Écrire un service qui dépend de Xorg

30

J'essaie d'écrire un service de niveau utilisateur redshiftet il doit attendre que Xorg soit opérationnel. Mon fichier de service actuel ressemble à ceci:

[Unit]
Description=Redshift
After=graphical.target

[Service]
Environment=DISPLAY=:0
ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr
Restart=always

[Install]
WantedBy=default.target

Cependant, il semble qu'il tente de démarrer avant que Xorg ne soit en place, et je dois ensuite démarrer manuellement le service. Je suppose que j'utilise la mauvaise After=cible. Des indices?

mkaito
la source

Réponses:

20

J'ai fait des recherches sur cela et la réponse de grawity semble obsolète. Vous pouvez maintenant configurer les services utilisateur avec systemd qui s'exécutent avec dans le cadre de la session de l'utilisateur. Ils peuvent avoir DISPLAY et XAUTHORITY définis (actuellement dans Arch et Debian Stretch).

Cela est logique par rapport aux recommandations précédentes d'utilisation des fichiers de démarrage automatique du bureau, car vous obtenez la gestion des processus comme vous le feriez pour une application de niveau système (redémarrage, etc.).

Le meilleur documentaire en ce moment est le wiki Arch; Systemd / User

Version TLDR;

  1. Créez le fichier * .service souhaité dans ~/.config/systemd/user/
  2. Exécuter systemctl --user enable [service](exclure le suffixe .service)
  3. Exécutez éventuellement systemctl --user start [service]pour commencer maintenant
  4. Utilisez systemctl --user status [service]pour vérifier comment ça se passe

Quelques autres commandes utiles.

  • systemctl --user list-unit-files - afficher toutes les unités utilisateur
  • s ystemctl --user daemon-reload- si vous modifiez un fichier .service

-- Plus tard...

J'ai mis à niveau et converti la plupart de mes démons de session en fichiers systemd .service. Je peux donc ajouter quelques notes supplémentaires.

Il n'y avait pas de hook par défaut pour exécuter les services à la connexion, vous devez donc le déclencher vous-même. Je le fais depuis mon fichier ~ / .xsession.

systemctl --user import-environment PATH DBUS_SESSION_BUS_ADDRESS
systemctl --no-block --user start xsession.target

La première ligne importe certaines variables d'environnement dans la session utilisateur systemd et la seconde lance la cible. Mon fichier xsession.target;

[Unit]
Description=Xsession running
BindsTo=graphical-session.target

Mon xbindkeys.service comme exemple.

[Unit]
Description=xbindkeys
PartOf=graphical-session.target

[Service]
ExecStart=/usr/bin/xbindkeys -n -f ${HOME}/projects/dotfiles/.xbindkeysrc
Restart=always

[Install]
WantedBy=xsession.target
John Eikenberry
la source
2
Si vous pouvez fournir un exemple de fichier d'unité et expliquer comment l'unité peut utiliser DISPLAY et XAUTHORITY, je serai heureux de changer la réponse acceptée.
mkaito
@mkaito J'examinerai cela une fois que Debian aura publié Stretch. J'utilise Debian stable et j'attendais jusque-là de jouer avec.
John Eikenberry
@mkaito Sur github.com/systemd/systemd/blob/v219/NEWS#L194, il est indiqué "Un scriptlet de session X11 est maintenant livré qui télécharge $ DISPLAY et $ XAUTHORITY dans l'environnement du démon systemd --user si une session commence. Cela devrait améliorer la compatibilité avec les applications compatibles X11 exécutées en tant que services utilisateur systemd. "
josch
J'aimerais toujours voir un exemple de fichier d'unité, juste pour qu'il soit clair s'il y a quelque chose de spécial nécessaire.
mkaito
11

L'allusion habituelle est «ne pas». redshiftn'est pas un service à l'échelle du système - il aurait une instance distincte pour chaque session , et il doit savoir comment se connecter au Xorg de cette session spécifique.

(Xorg n'est pas non plus un service système - seul le gestionnaire d'affichage l' est, et il lance également un Xorg distinct pour chaque session. // graphical.targetvous indiquera quand le gestionnaire d'affichage est prêt, mais il ne dit rien sur le moment où le DM démarre réellement le premier - ou tous - s'affiche.)

Il DISPLAY=:0ne suffit pas de le démarrer au démarrage , car il n'y a aucune garantie qu'il y ait exactement un écran à un moment donné, ni qu'il l'est toujours :0(par exemple, si Xorg se bloque en laissant un fichier de verrouillage périmé, le suivant fonctionnerait :1comme il penserait :0est encore occupé); vous devez également définir le chemin d'accès à votre XAUTHORITYfichier car X11 nécessite une authentification; et assurez-vous d' redshiftêtre redémarré si jamais vous vous déconnectez et vous reconnectez.

Alors comment le démarrer? Presque toujours, l'environnement de bureau dispose de plusieurs méthodes pour démarrer ses propres services de session . Voir un ancien article qui décrit déjà les deux habituels; le ~/.xprofilescript et l' ~/.config/autostart/*.desktopemplacement.

Si vous utilisez startx , vous pouvez utiliser ~/.xinitrcpour démarrer de telles choses. Les gestionnaires de fenêtres autonomes ont souvent leurs propres scripts de démarrage / init; par exemple ~/.config/openbox/autostartpour Openbox.

Ce qui est commun à toutes ces méthodes, c'est que le programme est démarré à partir de la session - en évitant tous les problèmes répertoriés ci-dessus.

grawity
la source
Bien que redshift ne soit pas un service à l'échelle du système dans de nombreux cas, il est logique d'être un service utilisateur qui est exactement ce que l'OP essaie de faire.
simotek
5

Voici ce que je viens de créer comme solution de contournement au non encore disponible graphical-session.target(sur mon système Kubuntu 16.04):

  1. Créez une unité utilisateur pseudo systemd qui fait monter et descendre le graphique-session.target.

Créez ~/.config/systemd/user/xsession.targetavec le contenu suivant:

[Unité]
Description = Xsession est opérationnel
BindsTo = graphical-session.target

Parlez à systemd de cette nouvelle unité:

$> systemctl --user daemon-reload
  1. Créez des scripts de démarrage et d'arrêt automatiques qui contrôlent xsession.targetvia les mécanismes actuellement disponibles du bureau Ubuntu 16.04.

Créez ~/.config/autostart-scripts/xsession.target-login.shavec le contenu suivant:

#! / bin / bash

si ! systemctl --user is-active xsession.target &> / dev / null
ensuite
  / bin / systemctl - environnement d'importation de l'utilisateur DISPLAY XAUTHORITY
  / bin / systemctl --user start xsession.target
Fi

Créez ~/.config/plasma-workspace/shutdown/xsession.target-logout.shavec le contenu suivant:

#! / bin / bash

si systemctl --user est actif xsession.target &> / dev / null
ensuite
  / bin / systemctl --user stop xsession.target
Fi

Rendez les scripts exécutables:

$> chmod + x ~ / .config / autostart-scripts / xsession.target-login.sh
$> chmod + x ~ / .config / plasma-workspace / shutdown / xsession.target-logout.sh

Remarque: ces deux fichiers sont placés là où KDE les récupérera pour le démarrage automatique et l'arrêt. Les fichiers peuvent être placés ailleurs pour d'autres environnements de bureau (par exemple Gnome) - mais je ne connais pas ces environnements.

Remarque: cette solution de contournement ne prend pas en charge les sessions multi-bureaux. Il ne gère graphical-session.targetcorrectement que tant qu'une seule session X11 active est exécutée sur une machine (mais c'est le cas pour la plupart d'entre nous utilisateurs Linux).

  1. Créez vos propres unités utilisateur systemd qui dépendent graphical-session.targetet faites-les fonctionner correctement tout en étant connecté sur votre bureau.

Comme exemple, l'unité de @ mkaito devrait ressembler à ceci:

[Unité]
Description = Redshift
PartOf = graphical-session.target

[Un service]
ExecStart = / bin / redshift -l 28: -13 -t 5300: 3300 -b 0,80: 0,91 -m randr
Redémarrer = toujours

(N'oubliez pas de faire un daemon-reloadaprès avoir édité vos unités!)

  1. Redémarrez votre machine, connectez-vous et vérifiez que vos unités sont démarrées comme prévu
$> systemctl --user status graphical-session.target
● graphical-session.target - Session utilisateur graphique en cours
   Chargé: chargé (/usr/lib/systemd/user/graphical-session.target; statique; préréglage fournisseur: activé)
   Actif: actif depuis Don 2017-01-05 15:08:42 CET; Il y a 47min
     Documents: man: systemd.special (7)
$> systemctl - état de l'utilisateur de votre unité ...

À un jour futur (sera-ce Ubuntu 17.04?), Ma solution de contournement deviendra obsolète car le système se débrouillera graphical-session.targetcorrectement lui-même. À ce jour, supprimez simplement le script de démarrage automatique et d'arrêt ainsi que le xsession.target- vos unités utilisateur personnalisées peuvent rester intactes et fonctionner.

gue
la source
Je sais que c'est un vieux commentaire, mais vous pouvez également ajouter des scripts de démarrage / connexion via l'application Paramètres système sous Espace de travail> Démarrage et arrêt> Démarrage automatique, si vous souhaitez placer ces scripts à un endroit dont vous vous souviendrez.
AmbientCyan
2

Cette solution fait exactement ce que l'auteur de la question demande:

il doit attendre que Xorg soit opérationnel

Bien qu'il puisse y avoir de meilleures façons de le faire, comme l'ont déjà répondu d'autres utilisateurs, c'est une autre approche de ce problème.

Il est similaire au service systemd -networkd-wait-online.service de systemd qui bloque jusqu'à ce que certains critères soient remplis. Les autres services qui en dépendent seront lancés dès que ce service démarre avec succès ou arrive à expiration.

Selon le manuel (section "Fichiers"), le serveur X créera un socket UNIX /tmp/.X11-unix/Xn(où nest un numéro d'affichage).

En surveillant la présence de ce socket, nous pouvons déterminer que le serveur pour un affichage particulier a démarré.

confirm_x_started.sh:

#!/bin/bash
COUNTER=0

while [ 1 ]
do
  # Check whether or not socket exists
  if [ -S /tmp/.X11-unix/X0 ]
  then
    exit 0
  fi

  ((++COUNTER))

  if [ $COUNTER -gt 20 ]
  then
    exit 1
  fi

  sleep 0.5
done

x_server_started.service:

[Unit]
Description=Monitor X server start

[Service]
Type=oneshot
ExecStart=/path/to/confirm_x_started.sh

[Install]
WantedBy=example.target

Maintenant, activez x_server_started.servicepour démarrer en même temps avec le serveur X.

Faire dépendre d'autres services (qui nécessitent le démarrage du serveur X) x_server_started.service

unité dépendante:

[Unit]
Description=Service that needs to have the X server started
Requires=x_server_started.service
After=x_server_started.service

[Service]
ExecStart=/path/to/binary

[Install]
WantedBy=example.target

Si le serveur X démarre sans problème, le x_server_started.servicedémarrera presque immédiatement et systemd commencera à démarrer toutes les unités qui en dépendent x_server_started.service.

VL-80
la source
Cela fonctionne bien. Le service supplémentaire est une belle touche. Vous pouvez également simplement utiliser ExecStartPre dans votre service cible. J'ai dû ajouter un «sommeil 1» supplémentaire avant la «sortie 0», mais il semble que c'était un peu trop rapide pour essayer de rattraper X tout de suite.
TTimo