Les services utilisateurs avec OpenRC
-------------------------------------------------
[03/05/2025] - ~9mins - #linux #adminsys #gentoo
-------------------------------------------------
<details>je vais dire du bien de Systemd !
Systemd permet de définir des services utilisateurs depuis longtemps.
Mais voilà **OpenRC** (le magnifique) depuis la version 0.60 (sorti fin février 2025) possède maintenant un équivalent et c'est dispo sur Gentoo !
Service utilisateur ?!
Bon les services classiques sont appelés services systèmes.
Ils démarrent des logiciels qui tournent en fond et qui le font souvent en root ou par un utilisateur "système" (non humain quoi).
Ça peut être un serveur web, un daemon cron etc, bref ça doit être lancé via une commande root.
Mais il y a des services pour l'utilisateur, donc des commandes lancées par l'utilisateur et qui tournent en fond.
Par exemple, dans mon cas, je fais tourner **mpd** qui est un lecteur audio en mode serveur.
Donc dès que je me loggue, je veux que ce logiciel soit démarré et que lorsque je me déloggue, il soit coupé.
Le but est donc tout simplement de lancer des logiciels plus ou moins automatiquement.
Et quel meilleur logiciel pour en lancer d'autres qu'un gestionnaire d'initialisation ?
Donc voilà qu'**OpenRC** s'y est mis.
Mais il existe également **superd** qui est plus proche de systemd dans son approche et son comportement.
Je ne l'utilise que sur mon téléphone vu que c'est une des briques de **sxmo**.
Mais sur mon ordi de bureau je passais par la technique un peu crasseuse de lancer ça avec **sway**.
La plupart (tous ?) des utilisateurs de sway ont dans leur config de multiples lignes avec **exec** pour lancer des logiciels.
C'était la technique normale, mais c'est un peu crade d'avoir un fichier de conf qui se retrouve à définir le comportement d'un logiciel et aussi à définir quoi démarrer.
Et puis c'est très basique dans son fonctionnement.
Avantages de passer par OpenRC
Lancer via Sway ça fonctionne mais c'est tout.
Avec OpenRC, on peut ajouter quelques ptites fonctionnalités plutôt cool.
La première est l'utilisation d'un superviseur.
C'est la ptite brique supplémentaire qui permet de relancer en cas de crash.
Bien entendu, aucun logiciel ne crash sous Linux, mais bon, sait-on jamais.
Un autre avantage de passer par ça plutôt que par **sway** c'est de pouvoir lancer des services même quand on ne lance pas **sway**.
C'est rare mais il arrive parfois que je ne lance pas de session graphique et que je reste en console quand c'est juste pour deux trois trucs.
Bha même dans ce cas, ça fonctionne.
Ça découple complètement de **sway**.
Et puis si un jour je passe à autre chose que **sway**, la migration sera d'autant plus aisée.
Ha et ça veut aussi dire qu'on peut facilement attribuer des limitations de cgroups m'enfin je ne me suis pas penché sur le sujet.
Prérequis
- Le truc chiant c'est d'avoir $XDG_RUNTIME_DIR de défini au préalable* (alors que ce serait le bon endroit pour le faire ^__^).
Bon bha du coup j'ai feinté et j'ai modifié /etc/init.d/user pour inclure le ptit bout qui va bien.
<summary>/etc/init.d/user
{{}}
!/sbin/openrc-run
Copyright (c) 2017 The OpenRC Authors.
See the Authors file at the top-level directory of this distribution and
https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
#
This file is part of OpenRC. It is subject to the license terms in
the LICENSE file found in the top-level directory of this
distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
This file may not be copied, modified, propagated, or distributed
except according to the terms contained in the LICENSE file.
supervisor=supervise-daemon
description="starts an openrc session for an user"
user="${RC_SVCNAME#*.}"
command="/usr/libexec/rc/bin/openrc-user"
command_args="$user"
notify="fd:3"
If it's rapidly failing, usually due to XDG_RUNTIME_DIR being unset,
we should just quit fast.
respawn_max=3
respawn_period=1
start_pre() {
if [ "$user" = "$RC_SVCNAME" ]; then
eerror "${RC_SVCNAME} cannot be started directly. You must create"
eerror "symbolic links to it for the users you want to start"
return 1
fi
#PERSO POUR CRÉER LE XDG_RUNTIME_DIR
if test -z "${XDG_RUNTIME_DIR}"; then
export XDG_RUNTIME_DIR=/tmp/$user-runtime-dir
if ! test -d "${XDG_RUNTIME_DIR}"; then
mkdir "${XDG_RUNTIME_DIR}"
chmod 0700 "${XDG_RUNTIME_DIR}"
chown $user "${XDG_RUNTIME_DIR}"
fi
fi
}
{{}}
Comme ça on crée le dossier dédié dans /tmp avec les bons droits et on exporte la variable.
On en profite pour créer un lien pour se faire une session pour l'utilisateur ln -s /etc/init.d/user /etc/init.d/user.lord et voilà.
On a une base fonctionnelle.
Créer un service utilisateur
On se rend dans */etc/user/init.d/* où l'on va créer notre premier service (pour en créer vraiment que pour votre utilisateur vous pouvez le foutre dans ~/.config/rc/init.d).
C'est possible que l'on y trouve déjà un peu de monde, petit à petit, les paquets adoptent ce système et sont mis à jour pour proposer le service qui va bien :-)
Pour notre cas on va se faire un service basique pour **mpd**.
<summary>/etc/users/init.d/mpd
{{}}
!/sbin/openrc-run
depend(){
need pipewire
}
description="Music Player Daemon"
supervisor=supervise-daemon
command="/usr/bin/mpd"
command_args="--no-daemon"
{{}}
(je mets d'autres exemples en fin d'articles)
Rien de bien méchant.
On définit une dépendance à **pipewire** (oui sinon pas de son ce qui est dommage pour mpd).
Il vous faut donc avoir également un service pipewire (fourni par gentoo dans mon cas).
On met un supervisor pour le relancer en cas de problème.
On pense bien à rendre ce script executable avec chmod +x /etc/users/init.d/mpd et on est prêt à le lancer.
Donc avec votre utilisateur (et pas en root, c'est tout le but de la manœuvre je rapelle), on fait un ptit rc-service --user mpd start et hop !
En gros toutes les commandes **rc-\*** ont désormais le flag **--user** pour agir en tant qu'utilisateur.
Démarrer automatiquement ces services
Bon bha comme pour les services systèmes, on a des *runlevels* qui permettent de grouper de nombreux services pour les démarrer/stopper en masse.
Il y a celui par défaut qui est automatiquement démarré.
Et dans le cas des services utilisateurs c'est pareil, le runlevel default est chargé automatiquement au login.
Il suffit donc d'ajouter les services dans ce runlevel.
Donc pour ajouter notre service mpd au login notre utilisateur lance un ptit rc-update --user add mpd default et c'est tout.
Pour voir vos runlevels un ptit rc-update --user show et c'est tout bon.
Cas des applications graphiques
Bon, c'est cool mais bon, au moment de vous logguer, bha vous n'avez pas encore de session wayland disponible (arrêtons de parler de X11 please, on est en 2025).
Du coup, vos applications graphiques vont pas pouvoir être lancées.
Dans mon cas j'ai donc fait un découpage en 2 runlevels distincts.
Celui par default et un nommé "graphical" dédié à toutes les applis graphiques.
Du coup au login, **pam** déclenche le lancement du runlevel default.
Et j'ai configuré **sway** pour qu'il passe au runlevel graphical.
Rapelez-vous qu'on peut "stacker" des runlevels pour signifier que "graphical" va étendre "default" et non le remplacer.
Prérequis
Par contre il y a une ptite subtilité, les applis graphiques s'appuient sur la variable d'environnement *$WAYLAND_DISPLAY* pour fonctionner.
Par défaut, **OpenRC** ne va pas transmettre l'environnement.
Il faut lui dire d'autoriser le passage de certaines variables.
Pour cela il faut éditer la conf utilisateur.
<summary>~/.config/rc/rc.conf
rc_env_allow="WAYLAND_DISPLAY SWAYSOCK DISPLAY"
La seconde variable est spécifique à **sway** pour ses ipc.
Ça permet le fonctionnement de **swaymsg** mais aussi pour les barres notament **yambar** qui trouve le socket avec cette variable pour vous afficher vos workspaces.
Avec ça, les applications graphiques devraient plus faire chier.
Le dernier détail à régler c'est le fait que ça ne fork pas trop.
Les applications graphiques ne sont pas des daemons et donc ne tournent pas en arrière plan.
Il faut donc adapter un peu les fichiers pour que OpenRC râle pas.
Il faut ajouter l'option pour indiquer ça mais il faut aussi définir un fichier pid pour qu'il puisse s'en démerder et suivre l'état.
<summary>exemple de lignes à ajouter au service
command_background="true"
pidfile="$XDG_RUNTIME_DIR/nom_du_service.pid"
création et utilisation d'un runlevel dédié
Il suffit de créer un dossier du nom de votre runlevel dans *~/.config/rc/runlevels*
Ensuite vous le définissez comme runlevel stacké à celui par défaut (comme ça, quand vous passez sur celui-là, vous restez tout de même dans default) avec rc-update --user --stack default graphical.
Ensuite vous attribuez des services à ce runlevel : rc-update --user add foot graphical et le tour est joué.
Le dernier élément à faire c'est de demander à **sway** de basculer sur ce runlevel quand il démarre :
<summary>extrait de ~/.config/sway/config
exec openrc --user graphical
Et si vous avez bien fait tout votre boulot ça devrait être le seul et unique **exec** de votre config.
Profit !
Bon bha voilà, on a un système un peu plus propre pour lancer nos services utilisateurs.
Bon par contre c'est vrai que lorsqu'on liste les processus, bha, ça fait un peu de monde en plus et ça titille mon toc sur les processus [1] mais bon c'est moderne.
Exemples de fichiers services
- *Foot** est un émulateur de terminal pour wayland.
Il possède un mode serveur pour bouffer moins de ram et démarrer plus vite.
Pas besoin de le surperviser, si ça plante ça va dégager tous les terminaux… le mal sera fait.
<summary>/etc/user/ini.d/foot
{{}}
!/sbin/openrc-run
description="Foot terminal server mode"
supervisor=supervise-daemon
command="/usr/bin/foot"
command_args="--server"
command_background="true"
pidfile="$XDG_RUNTIME_DIR/foot-server.pid"
start_pre() {
if [ -z "$WAYLAND_DISPLAY" ]; then
eerror "$WAYLAND_DISPLAY unset, can't proceed."
return 1
fi
}
{{}}
- *Easyeffects** est un logiciel pour appliquer des effets sonores.
Ça peut être de l'équalizer ou des trucs plus poussé que ce soit en entrée ou en sortie.
Le truc c'est que c'est une application en gtk pas trop faite pour être utilisé sans interface graphique mais on peut le tordre pour que ça passe.
<summary>/etc/user/ini.d/easyeffects
{{}}
!/sbin/openrc-run
depend(){
need pipewire
}
description="Easy Effects"
supervisor=supervise-daemon
command="/usr/bin/easyeffects"
command_args="--gapplication-service"
command_background="true"
pidfile="$XDG_RUNTIME_DIR/easyeffects.pid"
{{}}
Bon **mpd** c'est mon lecteur audio mais c'est un serveur sans interface graphique donc plus simple.
Le supervisor est pas indispensable puisque même si le daemon est coupé, il est automatiquement lancé par les commandes pour interagir avec lui.
<summary>/etc/user/ini.d/mpd
{{}}
!/sbin/openrc-run
depend(){
need pipewire
}
description="Music Player Daemon"
supervisor=supervise-daemon
command="/usr/bin/mpd"
command_args="--no-daemon"
{{}}
- *tsmpcd** est mon ptit script pour générer de la playlist en continue pour **mpd**.
Il fait en sorte que ça ne se tarisse jamais.
<summary>/etc/user/ini.d/tsmpcd
{{}}
!/sbin/openrc-run
description="Tiny Shuffler Music Player Client Daemon"
supervisor=supervise-daemon
command="/usr/local/bin/tsmpcdaemon"
depend() {
need mpd
}
{{}}
- *yambar** est la barre que j'utilise sur **sway**.
Elle plante parfois en sortie de veille et du coup là le supervisor va relancer le bousin tout seul comme un grand.
<summary>/etc/user/ini.d/yambar
{{}}
!/sbin/openrc-run
description="Yambar"
supervisor=supervise-daemon
command="/usr/bin/yambar"
command_background="true"
pidfile="$XDG_RUNTIME_DIR/yambar.pid"
start_pre() {
if [ -z "$WAYLAND_DISPLAY" ]; then
eerror "$WAYLAND_DISPLAY unset, can't proceed."
return 1
fi
}
{{}}
Liens
------------------------------------
------------------------------------
[03/05/2025] - #linux #adminsys #gentoo
------------------------------------