Schreiben eines Dienstes, der von Xorg abhängt

30

Ich versuche, einen Dienst auf Benutzerebene für zu schreiben redshift, und er muss warten, bis Xorg betriebsbereit ist. Meine aktuelle Servicedatei sieht folgendermaßen aus:

[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

Es scheint jedoch, dass es versucht zu starten, bevor Xorg gestartet wird, und ich muss den Dienst anschließend manuell starten. Ich schätze, ich benutze das falsche After=Ziel. Irgendwelche Hinweise?

mkaito
quelle

Antworten:

20

Ich habe dies untersucht und die Antwort von Grawity scheint veraltet zu sein. Sie können jetzt Benutzerdienste mit systemd einrichten, die als Teil der Benutzersitzung ausgeführt werden. Sie können DISPLAY und XAUTHORITY setzen (derzeit in Arch und Debian Stretch).

Dies ist gegenüber den vorherigen Empfehlungen zur Verwendung von Desktop-Autostart-Dateien sinnvoll, da Sie die Prozessverwaltung genau wie bei einer App auf Systemebene (Neustart usw.) erhalten.

Die derzeit besten Dokumente sind das Arch-Wiki. Systemd / Benutzer

TLDR-Version;

  1. Erstellen Sie die gewünschte * .service-Datei in ~/.config/systemd/user/
  2. Ausführen systemctl --user enable [service](ohne .service-Suffix)
  3. Optional können Sie systemctl --user start [service]jetzt starten
  4. Hiermit können Sie systemctl --user status [service]überprüfen, wie es funktioniert

Ein paar andere nützliche Befehle.

  • systemctl --user list-unit-files - Alle Benutzereinheiten anzeigen
  • s ystemctl --user daemon-reload- Wenn Sie eine .service-Datei bearbeiten

-- Später...

Ich habe die meisten meiner Sitzungs-Daemons aktualisiert und in systemd .service-Dateien konvertiert. So kann ich ein paar zusätzliche Notizen hinzufügen.

Es gab keinen Standard-Hook zum Ausführen der Dienste bei der Anmeldung. Sie müssen ihn daher selbst auslösen. Ich mache es aus meiner ~ / .xsession-Datei.

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

Die erste Zeile importiert einige Umgebungsvariablen in die systemd-Benutzersitzung und die zweite Zeile startet das Ziel. Meine xsession.target-Datei;

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

Mein xbindkeys.service als Beispiel.

[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
quelle
2
Wenn Sie eine Beispiel-Gerätedatei bereitstellen und erläutern können, wie das Gerät DISPLAY und XAUTHORITY verwenden kann, schalte ich die akzeptierte Antwort gerne um.
Mkaito
@mkaito Ich werde das untersuchen, sobald Debian Stretch veröffentlicht. Ich leite Debian Stable und habe bis dahin darauf gewartet, mehr damit zu spielen.
John Eikenberry
@mkaito Unter github.com/systemd/systemd/blob/v219/NEWS#L194 heißt es: "Ein X11-Sitzungsskriptlet wird jetzt ausgeliefert, das $ DISPLAY und $ XAUTHORITY in die Umgebung des systemd --user-Daemons hochlädt, wenn eine Sitzung beginnt. Dies sollte die Kompatibilität mit X11-fähigen Anwendungen verbessern, die als systemd-Benutzerdienste ausgeführt werden. "
josch
Ich würde immer noch gerne ein Beispiel für eine Unit-Datei sehen, um zu verdeutlichen, ob etwas Besonderes benötigt wird.
Mkaito
11

Der übliche Hinweis ist "nicht". redshiftEs handelt sich nicht um einen systemweiten Dienst, da für jede Sitzung eine eigene Instanz vorhanden ist. Außerdem muss bekannt sein, wie eine Verbindung zum Xorg dieser bestimmten Sitzung hergestellt werden kann.

(Xorg ist auch kein Systemdienst, sondern nur der Display-Manager. Außerdem wird für jede Sitzung ein separater Xorg gestartet. // graphical.targetZeigt an, wann der Display-Manager bereit ist, sagt aber nichts darüber aus, wann der DM tatsächlich den startet erste - oder alle - Anzeigen.)

Es DISPLAY=:0reicht nicht aus, es beim Booten mit zu starten , da es keine Garantie dafür gibt, dass es zu einem bestimmten Zeitpunkt genau eine Anzeige gibt, und dass dies auch immer der :0Fall ist (zum Beispiel, wenn Xorg abstürzt und eine veraltete Sperrdatei hinterlässt, würde die nächste so laufen, :1wie sie ist) würde denken :0ist noch besetzt); Sie müssen auch den Pfad zu Ihrer XAUTHORITYDatei festlegen, da X11 eine Authentifizierung erfordert. und stellen Sie sicher, dass der redshiftComputer neu gestartet wird, wenn Sie sich abmelden oder erneut anmelden.

Also, wie fange ich an? In der Desktop-Umgebung gibt es fast immer verschiedene Methoden zum Starten eigener Sitzungsdienste . Sehen Sie sich einen älteren Beitrag an, der bereits die beiden üblichen beschreibt. das ~/.xprofileDrehbuch und den ~/.config/autostart/*.desktopOrt.

Wenn Sie startx verwenden , können Sie ~/.xinitrcsolche Dinge starten. Eigenständige Fenstermanager haben häufig ihre eigenen Start- / Init-Skripte. zB ~/.config/openbox/autostartfür Openbox.

Allen diesen Methoden ist gemeinsam, dass das Programm innerhalb der Sitzung gestartet wird, wodurch alle oben aufgeführten Probleme vermieden werden.

Grawity
quelle
Während Redshift in vielen Fällen kein systemweiter Dienst ist, ist es sinnvoll, ein Benutzerdienst zu sein, der genau das ist, was das OP versucht.
Simotek
5

Folgendes habe ich gerade als Workaround für das noch nicht verfügbare graphical-session.target(auf meinem Kubuntu 16.04-System) erstellt:

  1. Erstellen Sie eine Pseudo-System-Benutzereinheit, die das Ziel "graphical-session.target" auf und ab bringt.

Erstellen Sie ~/.config/systemd/user/xsession.targetmit folgenden Inhalten:

[Einheit]
Beschreibung = Xsession läuft
BindsTo = graphical-session.target

Erzählen Sie systemd von dieser neuen Einheit:

$> systemctl --user daemon-reload
  1. Erstellen Sie Autostart- und Shutdown-Skripte , die xsession.targetüber die aktuell verfügbaren Mechaniken des Ubuntu 16.04-Desktops steuern .

Erstellen Sie ~/.config/autostart-scripts/xsession.target-login.shmit folgenden Inhalten:

#! / bin / bash

ob ! systemctl --user is-active xsession.target &> / dev / null
dann
  / bin / systemctl --user import-environment XAUTHORITY ANZEIGEN
  / bin / systemctl --user start xsession.target
fi

Erstellen Sie ~/.config/plasma-workspace/shutdown/xsession.target-logout.shmit folgenden Inhalten:

#! / bin / bash

if systemctl --user is-active xsession.target &> / dev / null
dann
  / bin / systemctl --user stop xsession.target
fi

Machen Sie die Skripte ausführbar:

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

Hinweis: Diese beiden Dateien werden dort abgelegt, wo sie von KDE zum automatischen Starten und Herunterfahren abgerufen werden. Die Dateien können für andere Desktop-Umgebungen (z. B. Gnome) an einem anderen Ort abgelegt werden - aber ich kenne diese Umgebungen nicht.

Hinweis: Diese Problemumgehung unterstützt keine Multi-Desktop-Sitzungen. Es wird nur dann graphical-session.targetrichtig gehandhabt, wenn nur eine aktive X11-Sitzung auf einem Computer ausgeführt wird (dies ist jedoch bei den meisten Linux-Benutzern der Fall).

  1. Erstellen Sie Ihre eigenen systemd-Benutzereinheiten, von denen Sie abhängig sind graphical-session.targetund die ordnungsgemäß ausgeführt werden, während Sie auf Ihrem Desktop angemeldet sind.

Als Beispiel sollte @ mkaitos Einheit so aussehen:

[Einheit]
Beschreibung = Rotverschiebung
PartOf = graphical-session.target

[Bedienung]
ExecStart = / bin / redshift -l 28: -13 -t 5300: 3300 -b 0,80: 0,91 -m randr
Neustart = immer

(Vergiss nicht, daemon-reloadnach dem Bearbeiten deiner Einheiten eine zu machen!)

  1. Starten Sie Ihren Computer neu, melden Sie sich an und vergewissern Sie sich, dass Ihre Geräte wie erwartet gestartet wurden
$> systemctl --user status graphical-session.target
● graphical-session.target - Aktuelle grafische Benutzersitzung
   Loaded: loaded (/usr/lib/systemd/user/graphical-session.target; static; Herstellervoreinstellung: enabled)
   Aktiv: Aktiv seit Don 2017-01-05 15:08:42 CET; Vor 47min
     Docs: man: systemd.special (7)
$> systemctl --user status your-unit ...

Irgendwann in der Zukunft (wird es Ubuntu 17.04 sein?) Ist meine Problemumgehung veraltet, da das System das graphical-session.targetProblem selbst richtig handhaben wird. Entfernen Sie an diesem Tag einfach das Autostart- und Shutdown-Skript und auch die xsession.targetbenutzerdefinierten Benutzereinheiten.

gue
quelle
Ich weiß, dass dies ein alter Kommentar ist, aber Sie können Startskripte / Anmeldeskripte auch über die App Systemeinstellungen unter Arbeitsbereich> Starten und Herunterfahren> Autostart hinzufügen. Wenn Sie diese Skripte an einem Ort ablegen möchten, werden Sie sich an sie erinnern.
AmbientCyan
2

Diese Lösung macht genau das, was der Autor der Frage fragt:

Es muss warten, bis Xorg läuft

Es gibt zwar bessere Möglichkeiten, dies zu tun, als dies bereits von anderen Benutzern beantwortet wurde, dies ist jedoch eine andere Herangehensweise an dieses Problem.

Es ähnelt dem systemd-networkd-wait-online.service von systemd, der blockiert, bis bestimmte Kriterien erfüllt sind. Andere Dienste, die davon abhängen, werden gestartet, sobald dieser Dienst erfolgreich gestartet wurde oder das Timeout abgelaufen ist.

Gemäß dem Handbuch (Abschnitt "Dateien") erstellt der X-Server einen UNIX-Socket /tmp/.X11-unix/Xn(wobei nes sich um eine Anzeigenummer handelt).

Indem wir das Vorhandensein dieses Sockets überwachen, können wir feststellen, dass der Server für eine bestimmte Anzeige gestartet wurde.

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

Aktivieren Sie nun, um x_server_started.servicegleichzeitig mit dem X-Server zu starten.

Stellen Sie sicher, dass andere Dienste (für die der X-Server gestartet werden muss) abhängig sind x_server_started.service

abhängige Einheit:

[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

Wenn der X-Server problemlos x_server_started.servicegestartet wird , wird er fast sofort gestartet und systemd fährt mit dem Starten aller Einheiten fort, von denen abhängig ist x_server_started.service.

VL-80
quelle
Das funktioniert gut. Der zusätzliche Service ist eine nette Geste. Sie können ExecStartPre auch einfach in Ihrem Zieldienst verwenden. Ich musste jedoch vor "exit 0" eine zusätzliche "sleep 1" hinzufügen, anscheinend war es ein bisschen zu schnell, um nur zu versuchen, X sofort zu fangen.
TTimo