Systemd - Unit-Datei

systemd unit-Datei

Die grundlegenden und einführenden Informationen zu Systemd enthält die Handbuchseite Systemd-Start
In der vorliegenden Handbuchseite erklären wir den Aufbau der Unit-Dateien und die generischen Sektionen “[Unit]” und “[Install]”.

Die Unit-Datei ist eine reine Textdatei im INI-Format. Sie enthält Konfigurationsanweisungen von der Art “Schlüssel=Wert” in verschiedenen Sektionen. Leere Zeilen und solche, die mit “#” oder “;” beginnen, werden ignoriert. Alle Unit-Dateien müssen eine Sektion entsprechend des Unit Typ enthalten. Die generischen Sektionen “[Unit]” am Beginn und “[Install]” am Ende der Datei sind optional, wobei die Sektion “[Unit]” dringend empfohlen wird.

Ladepfad der Unit-Dateien

Die Ausgabe zeigt die Reihenfolge der Verzeichnisse, aus denen die Unit-Dateien geladen werden.

# systemd-analyze unit-paths
/etc/systemd/system.control
/run/systemd/system.control
/run/systemd/transient
/run/systemd/generator.early
/etc/systemd/system
/etc/systemd/system.attached
/run/systemd/system
/run/systemd/system.attached
/run/systemd/generator
/usr/local/lib/systemd/system
/lib/systemd/system
/usr/lib/systemd/system
/run/systemd/generator.late

Unit-Dateien, die in früher aufgeführten Verzeichnissen gefunden werden, setzen Dateien mit dem gleichen Namen in Verzeichnissen, die weiter unten in der Liste aufgeführt sind, außer Kraft. So hat eine Datei in “/etc/systemd/system” Vorrang vor der gleichnamigen in “/lib/systemd/system”.

Nur ein Teil der zuvor aufgeführten Verzeichnisse existiert per default in siduction. Die Verzeichnisse

Wir empfehlen eigene Unit-Dateien in /usr/local/lib/systemd/system/ abzulegen.

Aktivierung der Unit-Datei

Um systemd die Konfiguration einer Unit zugänglich zu machen, muss die Unit-Datei aktiviert werden. Dies geschieht mit dem Aufruf:

# systemctl daemon-reload
# systemctl enable --now <UNIT_DATEI>

Der erste Befehl lädt die komplette Daemon-Konfiguration neu, der zweite startet die Unit sofort (Option “–now”) und gliedert sie in systemd ein, sodass sie bei jedem Neustart des PC ausgeführt wird.
Der Befehl

# systemctl disable <UNIT_DATEI>

bewirkt, dass sie nicht mehr bei jedem Neustart des PC ausgeführt wird. Sie kann aber weiterhin manuell mit dem Befehl systemctl start <UNIT_DATEI> gestartet und mit systemctl stop <UNIT_DATEI> gestopt werden.
Falls eine Unit-Datei leer ist (d.h. die Größe 0 hat) oder ein Symlink auf /dev/null ist, wird ihre Konfiguration nicht geladen und sie erscheint mit einem Ladezustand “masked” und kann nicht aktiviert werden. Dies ist eine wirksame Methode um eine Unit komplett zu deaktivieren und es auch unmöglich zu machen, sie manuell zu starten.

Sektionen der Unit-Datei

Die Unit-Datei besteht in der Regel aus der Sektionen [Unit], der Typ-spezifischen Sektion und der Sektion [Install]. Die Typ-spezifische Sektion fließt als Suffix in den Dateinamen ein. So besitzt zum Beispiel eine Unit-Datei, die einen Zeitgeber konfiguriert, immer die Endung “.timer” und muss “[Timer]” als Typ-spezifische Sektion enthalten.

Sektion Unit

Diese Sektion enhält allgemeine Informationen über die Unit, definiert Abhängigkeiten zu anderen Units, wertet Bedingungen aus und sorgt für die Einreihung in den Bootprozess.

  1. Allgemeine Optionen

    1. Description=
      Identifiziert die Unit durch einen menschenlesbaren Namen, der von systemd als Bezeichnung für die Unit verwandt wird und somit im systemjournal erscheint (“Starting description…”) und dort als Suchmuster verwandt werden kann.

    2. Documentation=
      Ein Verweis auf eine Datei oder Webseite, die Dokumentation für diese Unit oder ihre Konfiguration referenzieren. Z. B.: “Documentation=man:cupsd(8)” oder “Documentation=http://www.cups.org/doc/man-cupsd.html”.

  2. Bindungsabhängigkeiten zu anderen Units

    1. Wants=
      Hier aufgeführte Units werden mit der konfigurierten Unit gestartet.

    2. Requires=
      Ähnlich zu Wants=, erklärt aber eine stärkerere Bindung an die aufgeführten Units.
      Wenn diese Unit aktiviert wird, werden die aufgeführten Units ebenfalls aktiviert.
      Schlägt die Aktivierung einer der anderen Units fehl und die Ordnungsabhängigkeit After= ist auf die fehlgeschlagene Unit gesetzt, dann wird diese Unit nicht gestartet.
      Falls eine der anderen Units inaktiv wird, bleibt diese Unit aktiv, nur wenn eine der anderen Units gestoppt wird, wird diese Unit auch gestoppt.

    3. Requisite=
      Ähnlich zu Requires=. Der Start dieser Unit wird sofort fehlschlagen, wenn die hier aufgeführten Units noch nicht gestartet wurden. Requisite= sollte mit der Ordnungsabhängigkeit After= kombiniert werden, um sicherzustellen, dass diese Unit nicht vor der anderen Unit gestartet wird.

    4. BindsTo=
      BindsTo= ist der stärkste Abhängigkeitstyp: Es bewirkt zusätzlich zu den Eigenschaften von Requires=, dass die gebundene Unit im aktiven Status sein muss, damit diese Unit auch aktiv sein kann.
      Beim Stoppen oder inaktivem Zustand der gebundenen Unit wird diese Unit immer gestoppt.
      Um zu verhindern, dass der Start dieser Unit fehlschlägt, wenn die gebundene Unit nicht, oder noch nicht in einem aktiven Zustand ist, sollte BindsTo= am besten mit der Ordnungsabhängigkeit After= kombiniert werden.

    5. PartOf=
      Ähnlich zu Requires=, aber begrenzt auf das Stoppen und Neustarten von Units.
      Wenn Systemd die hier aufgeführten Units stoppt oder neustartet, wird die Aktion zu dieser Unit weitergeleitet.
      Das ist eine Einwege-Abhängigkeit. Änderungen an dieser Unit betreffen nicht die aufgeführten Units.

    6. Conflicts=
      Deklariert negative Anforderungs-Abhängigkeiten. Die Angabe einer durch Leerzeichen getrennten Liste ist möglich.
      Conflicts= bewirkt, dass die aufgeführte Unit gestoppt wird, wenn diese Unit startet und umgekehrt.
      Da Conflicts= keine Ordnungs-Abhängigkeit beinhaltet, muss eine Abhängigkeit After= oder Before= erklärt werden, um sicherzustellen, dass die in Konflikt stehende Unit gestoppt wird, bevor die andere Unit gestartet wird.

  3. Ordnungsabhängigkeiten zu anderen Units

    1. Before=
      Diese Einstellung konfiguriert Ordnungsabhängigkeiten zwischen Units. Before= stellt sicher, dass die aufgeführte Unit erst mit dem Starten beginnt, nachdem der Start der konfigurierten Unit abgeschlossen ist.
      Die Angabe einer durch Leerzeichen getrennten Liste ist möglich.

    2. After=
      Diese Einstellung stellt das Gegenteil von Before= sicher. Die aufgeführte Unit muss vollständig gestartet sein, bevor die konfigurierte Unit gestartet wird.

    3. OnFailure=
      Units, die aktiviert werden, wenn diese Unit den Zustand »failed« einnimmt.

  4. Bedingungen
    Unit-Dateien können auch eine Reihe von Bedingungen enthalten.
    Bevor die Unit gestartet wird, wird Systemd nachweisen, dass die festgelegten Bedingungen wahr sind. Falls nicht, wird das Starten der Unit (fast ohne Ausgabe) übersprungen.
    Fehlschlagende Bedingungen führen nicht dazu, dass die Unit in den Zustand »failed« überführt wird.
    Falls mehrere Bedingungen festgelegt sind, wird die Unit ausgeführt, falls alle von ihnen zutreffen.
    In diesem Abschnitt führen wir nur Bedingungen auf, die uns für selbst erstellte Units hilfreich erscheinen, denn viele Bedingungen dienen dazu, um Units zu überspringen, die auf dem lokalen System nicht zutreffen.
    Der Befehl systemd-analyze verify <UNIT_DATEI> kann zum Testen von Bedingungen verwandt werden.

    1. ConditionVirtualization=
      Prüft, ob das System in einer virtualisierten Umgebung ausgeführt wird und testet optional, ob es eine bestimmte Implementierung ist.

    2. ConditionACPower=
      Prüft, ob das System zum Zeitpunkt der Aktivierung der Unit am Netz hängt oder ausschließlich über Akku läuft.

    3. ConditionPathExists=
      Prüft auf die Existenz einer Datei. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    4. ConditionPathExistsGlob=
      Wie zuvor, nur dass ein Suchmuster angegeben wird. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    5. ConditionPathIsDirectory=
      Prüft auf die Existenz eines Verzeichnisses. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    6. ConditionPathIsSymbolicLink=
      Überprüft ob ein bestimmter Pfad existiert und ein symbolischer Link ist. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    7. ConditionPathIsMountPoint=
      Überprüft ob ein bestimmter Pfad existiert und ein Einhängepunkt ist. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    8. ConditionPathIsReadWrite=
      Überprüft ob das zugrundeliegende Dateisystem les- und schreibbar ist. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    9. ConditionDirectoryNotEmpty=
      Überprüft ob ein bestimmter Pfad existiert und ein nicht leeres Verzeichnis ist. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    10. ConditionFileNotEmpty=
      Überprüft ob ein bestimmter Pfad existiert und sich auf eine normale Datei mit einer von Null verschiedenen Größe bezieht. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

    11. ConditionFileIsExecutable=
      Überprüft ob ein bestimmter Pfad existiert und sich auf eine normale, als ausführbar gekennzeichnete Datei bezieht. Mit einem Ausrufezeichen (“!”) vor dem Pfad wird der Test negiert.

Die vollständige Dokumentation zu allen Optionen der Sektion “[Unit]” bitte in der Deutschen Manpage, systemd.unit nachlesen.

Typ-spezifische Sektion

Diese Sektion enthält die speziellen Optionen der elf möglichen Typen. Ausführliche Beschreibungen enthalten die verlinkten Handbuchseiten, oder ersatzweise die jeweilige deutsche Manpage.

Sektion Install

Unit-Dateien können diese Sektion enthalten.
Die Optionen der [Install]-Sektion werden von den Befehlen systemctl enable <UNIT_DATEI> und systemctl disable <UNIT_DATEI> während der Installation einer Unit verwandt.
Unit-Dateien ohne [Install]-Sektion lassen sich manuell mit dem Befehl systemctl start <UNIT_DATEI>, oder von einer anderen Unit-Datei starten.

Beschreibung der Optionen:

Hinweis: Um die Konfiguration einer Unit-Datei zu prüfen, eignet sich der Befehl systemd-analyze verify <UNIT_DATEI>.

Beispiel cupsd

Der cupsd, Auftragsplaner (Scheduler) für das Common UNIX Printing System, wird von systemd mit seinen drei Unit Dateien “cups.socket”, “cups.service” und “cups.path” gesteuert und eignet sich gut, um die Abhängigkeiten zu verdeutlichen.
Hier die drei Dateien.

Datei /lib/systemd/system/cups.service:

[Unit]
Description=CUPS Scheduler
Documentation=man:cupsd(8)
After=network.target sssd.service ypbind.service nslcd.service
Requires=cups.socket
    After=cups.socket (nicht in der Datei, da implizit vorhanden.)
    After=cups.path   (nicht in der Datei, da implizit vorhanden.)

[Service]
ExecStart=/usr/sbin/cupsd -l
Type=notify
Restart=on-failure

[Install]
Also=cups.socket cups.path
WantedBy=printer.target
Datei /lib/systemd/system/cups.path:

[Unit]
Description=CUPS Scheduler
PartOf=cups.service
    Before=cups.service (nicht in der Datei, da implizit vorhanden.)

[Path]
PathExists=/var/cache/cups/org.cups.cupsd

[Install]
WantedBy=multi-user.target
Datei /lib/systemd/system/cups.socket:

[Unit]
Description=CUPS Scheduler
PartOf=cups.service
    Before=cups.service (nicht in der Datei, da implizit vorhanden.)

[Socket]
ListenStream=/run/cups/cups.sock

[Install]
WantedBy=sockets.target

Die Sektion [Unit]
enthält für alle drei Dateien die gleiche Beschreibung. Die Dateien cups.path und cups.socket enthalten zusätzlich die Bindungsabhängigkeit PartOf=cups.service, was bedeutet, dass diese zwei Units abhängig von cups.service gestoppt oder neu gestartet werden.
Die socket-Unit ebenso wie die path-Unit schließen die Ordnungsabhängigkeit “Before=” zu ihrer namensgleichen Service-Unit ein. Deshalb ist es nicht notwendig in der cups.service-Unit die Ordnungs-Abhängigkeiten “After=cups.socket” und “After=cups.path” einzutragen. (Siehe unten die Ausgabe von “systemd-analyze dump” mit dem Vermerk “destination-implicit”.) Beide Abhängigkeiten gemeinsam bewirken, dass unabhängig davon, welche Unit zuerst startet, immer alle drei Units starten und die cups.service-Unit erst, nachdem der Start der cups.path-Unit und der cups.socket-Unit erfolgreich abgeschlossen wurde.

Die vollständige Konfiguration der Units erhalten wir mit dem Befehl systemd-analyze dump, der eine sehr, sehr lange Liste ( > 32000 Zeilen) des systemd Serverstatus ausgibt.

# systemd-analyze dump
[...]
-> Unit cups.service:
    Description: CUPS Scheduler.service
    [...]
    WantedBy: printer.target (destination-file)
    ConsistsOf: cups.socket (destination-file)
    ConsistsOf: cups.path (destination-file)
    Before: printer.target (destination-default)
    After: cups.socket (destination-implicit)
    After: cups.path (destination-implicit)
[...]
-> Unit printer.target:
    Description: Printer
    [...]
    Wants: cups.service (origin-file)
    After: cups.service (origin-default)
[...]

Die Sektion [Install]
der cups.service-Unit enthält mit der Option “Also=cups.socket cups.path” die Anweisung, diese beiden Units auch zu installieren und alle drei Units haben unterschiedliche “WantedBy=” Optionen:

Um zu verstehen, warum unterschiedliche Werte für die Option “WantedBy=” Verwendung finden, benötigen wir zusätzliche Informationen, die wir mit den Befehlen systemd-analyze dot und systemd-analyze plot erhalten.

$ systemd-analyze dot --to-pattern='*.target' --from-pattern=\
    '*.target' | dot -Tsvg > targets.svg

$ systemd-analyze plot > bootup.svg

Der erste liefert uns ein Flussdiagramm mit den Abhängigkeiten der verschiedenen Targets zueinander und der zweite eine graphisch aufbereitete Auflistung des Bootprozesses mit den Zeitpunkten wann ein Prozess gestartet wurde, welche Zeit er beanspruchte und seinen Aktivitätszustand.

Der targets.svg und der bootup.svg entnehmen wir, dass

  1. sysinit.target
    aktiviert wird und

  2. basic.target
    erst startet, wenn sysinit.target erreicht wurde.

    1. sockets.target
      von basic.target angefordert wird,

      1. cups.socket
        und alle weiteren .socket-Units von sockets.target hereingeholt werden.
    2. paths.target
      von basic.target angefordert wird,

      1. cups.path
        und alle weiteren .path-Units von paths.target hereingeholt werden.
  3. network.target
    erst startet, wenn basic.target erreicht wurde.

  4. cups.service
    erst startet, wenn network.target erreicht wurde.

  5. multi-user.target
    erst startet, wenn network.target erreicht wurde.

  6. multi-user.target
    erst dann erreicht wird, wenn cups.service erfolgreich gestartet wurde.
    (Genau genommen liegt es daran, dass der cups-browsed.service, der vom
    cups.service abhängt, erfolgreich gestartet sein muss.)

  7. printer.target
    wird erst aktiv, wenn Systemd dynamisch Geräte-Units für die Drucker generiert.
    Dazu müssen die Drucker angeschlossen und eingeschaltet sein.

Weiter oben stellten wir fest, dass der Start einer cups.xxx-Unit ausreicht, um alle drei Units hereinzuholen. Betrachten wir noch einmal die “WantedBy=”-Optionen in der [Install]-Sektion, so haben wir die cups.socket-Unit, die über das sockets.target bereits während des basic.target hereingeholt wird, die cups.path-Unit, die während des multi-user.target hereingeholt wird und den cups.service, der vom printer.target hereingeholt wird.
Während des gesamten Bootprozesses werden die drei cups.xxx-Units wiederholt bei systemd zur Aktivierung angefordert. Das härtet den cupsd gegen unvorhergesehene Fehler, spielt für systemd aber keine Rolle, denn es ist unerheblich wie oft ein Service angefordert wird, wenn er sich in der Warteschlange befindet.
Zusätzlich fordert immer dann das printer.target den cups.service an, wenn ein Drucker neu von systemd erkannt wird.

Werkzeuge

Systemd beinhaltet einige nützliche Werkzeuge für die Analyse, Prüfung und Bearbeitung der Unit-Dateien.
Bitte auch die Manpages systemd-analyze und systemctl zu Rate ziehen.

Die hier genannten Hilfsmittel stellen nur einen Teil der mit systemd ausgelieferten Werkzeuge dar. Bitte entnehme den man-Pages die vollständige Dokumentation.

Quellen systemd-unit-Datei

Deutsche Manpage, systemd.unit
Deutsche Manpage, systemd.syntax
Deutsche Manpage, systemd.device
Deutsche Manpage, systemd.scope
Deutsche Manpage, systemd.slice
Deutsche Manpage, systemd.socket
Deutsche Manpage, systemd.swap
Deutsche Manpage, systemd-analyze
Deutsche Manpage, systemctl

Dank an Helge Kreuzmann für die deutschen Übersetzungen.

Seite zuletzt aktualisert 2021-05-05