BEZEICHNUNG
daemon - Schreiben und Paketieren von System-Daemons
BESCHREIBUNG
Ein Daemon ist ein Diensteprozess, der im Hintergrund läuft und das System beaufsichtigt oder Funktionalitäten für andere Prozesse bereitstellt. Traditionell werden Daemons so implementiert, dass sie einem ursprünglich aus SysV-Unix stammenden Schema folgen. Moderne Daemons sollten einem einfacheren, aber dennoch leistungsfähigeren Schema folgen (hier »neuartige« Daemons genannt), wie es von systemd(1) implementiert wird. Diese Handbuchseite deckt beide Schemata ab und enthält insbesondere Empfehlungen für Daemons, die im Systemd-Init-System aufgenommen werden sollen.
SysV-Daemons
Wenn ein traditioneller SysV-Daemon startet, sollte er die
folgenden Schritte als Teil seiner Initialisierung
ausführen. Beachten Sie, dass diese Schritte von
neuartigen Daemons (siehe unten) nicht benötigt werden.
Sie sollten daher nur implementiert werden, wenn die
Kompatibilität zu SysV essenziell ist.
1. Alle offenen Dateideskriptoren außer der Standardeingabe, -ausgabe und -fehlerausgabe (d.h. den drei ersten Dateideskriptoren 0, 1, 2) werden geschlossen. Dies stellt sicher, dass kein versehentlich geöffneter Dateideskriptor im Daemon-Prozess verbleibt. Unter Linux wird dies am besten dadurch implementiert, dass durch /proc/self/fd iteriert wird, mit der Rückfalloption, von Dateideskriptor 3 bis zu dem von getrlimit() für RLIMIT_NOFILE zurückgelieferten Wert durchzuiterieren.
2. Alle Signal-Handler werden auf ihre Vorgaben zurückgesetzt. Dies erfolgt am besten durch Iteration durch alle verfügbaren Signale bis zur Grenze von _NSIG und dem Zurücksetzen dieser auf SIG_DFL.
3. Zurücksetzen der Signal-Maske mittels sigprocmask().
4. Bereinigen des Umgebungsblocks, Entfernen oder Zurücksetzen der Umgebungsvariablen, die einen negativen Einfluss auf die Laufzeit des Daemons haben könnten.
5. Aufrufen von fork(), um einen Hintergrundprozess zu erzeugen.
6. Aufruf von setsid() im Kind, um von jedem Terminal zu trennen und eine unabhängige Sitzung zu erzeugen.
7. Erneuter Aufruf von fork() im Kind, um sicherzustellen, dass der Daemon niemals wieder ein Terminal erlangen kann. (Dies ist relevant, falls das Programm und alle seine Abhängigkeiten nicht sorgfältig genug sind, um bei jedem »open()«-Aufruf, der möglicherweise einen TTY-Geräteknoten erlangen könnte, stets »O_NOCTTY« festzulegen.)
8. Aufruf von exit() im ersten Kind, so dass nur das zweite Kind (der eigentliche Daemon) verbleibt. Dies stellt sicher, dass der Daemon-Prozess PID 1 als neuen Elternprozess bekommt, wie das bei allen Daemons der Fall sein sollte.
9. Verbinden von /dev/null im Daemon-Prozess mit der Standardeingabe, -ausgabe und -fehlerausgabe.
10. Zurücksetzen der Umask auf 0 im Daemon-Prozess, so dass die an open(), mkdir() und ähnliche weitergegebenen Dateimodi direkt die Zugriffsmodi auf die erstellten Dateien und Verzeichnisse steuern.
11. Ändern des aktuellen Verzeichnisses im Daemon-Prozess auf das Wurzelverzeichnis (/), um zu vermeiden, dass der Daemon unfreiwillig die Einhängepunkte vor dem Aushängen blockiert.
12. Schreiben der Daemon-PID (wie von getpid() zurückgeliefert) im Daemon-Prozess in eine PID-Datei, beispielsweise /run/foobar.pid (für einen hypothetischen Daemon »foobar«), um sicherzustellen, dass der Daemon nicht mehr als einmal gestartet wird. Dies muss so implementiert werden, dass kein Ressourcenwettlauf möglich ist, so dass die PID-Datei nur aktualisiert wird, wenn gleichzeitig sichergestellt ist, dass die vorher in der PID-Datei gespeicherte PID nicht mehr existiert oder zu einem fremden Prozess gehört.
13. Abgeben der Privilegien im Daemon-Prozess, falls möglich und zutreffend.
14. Benachrichtigen des ursprünglich gestarteten Prozesses aus dem Daemon-Prozess, dass die Initialisierung abgeschlossen ist. Dies kann über eine unbenannte Pipe oder einen ähnlichen Kommunikationskanal, der vor dem ersten fork() erstellt wurde und daher in sowohl dem ursprünglichen als auch dem Daemon-Prozess verfügbar ist, erfolgen.
15. Aufrufen von exit() im ursprünglichen Prozess. Der Prozess, der den Daemon gestartet hat, muss in der Lage sein, sich darauf zu verlassen, dass dieser exit() passiert, nachdem die Initialisierung abgeschlossen und die externen Kommunikationskanäle etabliert und zugreifbar sind.
Die BSD-Funktion daemon() sollte nicht verwandt werden, da sie nur eine Teilmenge dieser Schritte implementiert.
Ein Daemon, der Kompatibilität mit SysV-Systemen bereitstellen muss, sollte das oben dargestellte Schema implementieren. Es wird allerdings empfohlen, dieses Verhalten über eine Befehlszeilenoption optional und konfigurierbar zu gestalten, um die Fehlersuche zu erleichtern und um die Integration in Systeme, die Systemd verwenden, zu erleichtern.
Neuartige
Daemons
Moderne Dienste für Linux sollten als neuartige Daemons
implementiert werden. Dies erleichtert die Beaufsichtigung
und Steuerung zur Laufzeit und vereinfacht ihre
Implementierung.
Zur Entwicklung eines neuartigen Daemons muss keiner der für SysV-Daemons empfohlenen Initialisierungsschritte implementiert werden. Bei neuartigen Init-Systemen wie Systemd sind diese alle redundant. Da einige dieser Schritte sogar die Prozessüberwachung, Dateideskriptorweitergabe und andere Funktionalitäten des Init-Systems stören, wird empfohlen, sie nicht auszuführen, wenn sie als neuartiger Dienst ausgeführt laufen.
Beachten Sie, dass neuartige Init-Systeme die Ausführung eines Daemon-Prozesses in einem sauberen Prozesskontext garantieren: Es wird garantiert, dass der Umgebungsblock bereinigt ist, dass Signal-Handler und -Masken zurückgesetzt sind und dass kein übrig gebliebener Dateideskriptor weitergegeben wird. Daemons werden in ihrer eigenen Sitzung ausgeführt, wobei die Standardeingabe mit /dev/null und die Standardausgabe/Standardfehlerausgabe mit dem Protokollierungsdienst systemd-journald.service(8) verbunden ist, außer dies ist anders konfiguriert. Die Umask ist zurückgesetzt.
Es wird empfohlen, dass neuartige Daemons folgendes implementieren:
1. Falls SIGTERM empfangen wird, soll der Daemon sich herunterfahren und sauber beenden.
2. Falls SIGHUP empfangen wird, sollen die Konfigurationsdateien, falls zutreffend, neu geladen werden.
3. Es soll ein korrekter Exit-Code vom Haupt-Daemon-Prozess bereitgestellt werden, da dieser vom Init-System benutzt wird, um Dienstefehler und Probleme zu erkennen. Es wird empfohlen, dem in LSB-Empfehlungen für SysV-Init-Skripte [1] definierten Exit-Code-System zu folgen.
4. Falls möglich und zutreffend sollte die Steuerschnittstelle mittels des D-Bus-IPC-Systems zugänglich gemacht und ein Bus-Name als letzten Schritt in der Initialisierung erlangt werden.
5. Stellen Sie eine .service-Unit-Datei zur Integration mit Systemd bereit, die Informationen zum Starten, Beenden und anderseitigen Verwalten des Daemons enthält. Siehe systemd.service(5) für Details.
6. Verlassen Sie sich soweit möglich auf die Funktionalitäten des Init-Systems, den Zugriff des Daemons auf Dateien, Dienste und andere Ressourcen zu begrenzen, d.h. im Falle von Systemd, verlassen Sie sich auf Systemds Ressourenbegrenzungssteuerung statt Ihre eigene zu implementieren, verlassen Sie sich auf den Privilegienabgabe-Code von Systemd, statt ihn in Ihrem Daemon zu implementieren und ähnliches. SIehe systemd.exec(5) für die verfügbaren Steuerungen.
7. Falls D-Bus verwandt wird, ermöglichen Sie die Bus-Aktivierung Ihres Daemons, indem Sie eine D-Bus-Dienste-Aktivierungsdatei bereitstellen. Dies hat eine Reihe von Vorteilen: Ihr Daemon kann erst bei Bedarf gestartet werden; er kann parallel zu anderen Daemons, die ihn benötigen, gestartet werden — wodurch die Parallelisierung und die Systemstartgeschwindigkeit maximiert wird; Ihr Daemon kann im Fehlerfall neu gestartet werden, ohne eine Bus-Anfrage zu verlieren, da die Bus-Warteschlagen aktivierbare Dienste erbitten. Siehe unten für Details.
8. Falls Ihr Daemon Dienste für andere lokale Prozesse oder Clients in der Ferne über ein Socket anbietet, sollte es gemäß des nachfolgend dargestellten Schemas Socket-aktivierbar sein. Ähnlich der D-Bus-Aktivierung ermöglicht dies bedarfsgesteuertes Starten von Diensten sowie verbesserte Parallelisierung beim Starten der Dienste. Für zustandslose Protokolle (wie Syslog, DNS) kann ein Daemon, der Socket-basierte Aktivierung implementiert, auch ohne Verlust einer einzelnen Anfrage neu gestartet werden. Siehe unten für Details.
9. Falls zutreffend sollte ein Daemon über den Abschluss des Startens oder Statusaktualisierungen mittels der Schnittstelle sd_notify(3) informieren.
10. Statt den Aufruf syslog() zum direkten Protokollieren in den Systemprotokolldienst zu verwenden, kann ein neuartiger Daemon einfach auf die Standardfehlerausgabe mittels fprintf() protokollieren, was dann durch das Init-System an Syslog weitergeleitet wird. Falls Protokollierungsstufen notwendig sind, können diese durch Voranstellen von Zeichenketten wie »<4>« (für Protokollierungsstufe 4 »WARNING« im Syslog-Prioritätsschema) vor individuellen Protokollierzeilen kodiert werden. Dies folgt einem ähnlichen Stil wie das printk()-Stufensystem des Linux-Kernels. Für Details siehe sd-daemon(3) und systemd.exec(5).
11. Da neuartige Daemons ohne ein steuerndes TTY (aber sie selbst als ihren eigenen Sitzungsleiter) aufgerufen werden, sollte sichergestellt werden, dass bei »open()«-Aufrufen, die möglicherweise einen TTY-Geräteknoten referenzieren, stets »O_NOCTTY« festgelegt wird, so dass nicht versehentlich ein steuerndes TTY erlangt wird.
Diese Empfehlungen sind ähnlich zu, aber nicht identisch mit den Apple-MacOS-X-Daemon-Anforderungen [2] .
AKTIVIERUNG
Neuartige Init-Systeme stellen eine Reihe von zusätzlichen Mechanismen bereit, um Dienste zu aktivieren. Details dazu weiter unten. Es ist typisch, dass Dienste konfiguriert wurden, über mehr als einen Mechanismus gleichzeitig aktiviert zu werden. Beispielsweise könnte in einem Systemd-System bluetoothd.service aktiviert werden, wenn eine Bluetooth-Hardware eingesteckt wird oder wenn eine Anwendung mittels D-Bus auf seine Programmierschnittstelle zugreift. Oder ein Drucker-Server-Daemon könnte aktiviert werden, wenn Daten auf einem IPP-Port ankommen oder wenn ein Drucker eingesteckt wird oder wenn eine Datei im Drucker-Spool-Verzeichnis in die Warteschlange kommt. Selbst für Dienste, die dazu gedacht sind, beim Systemstart bedingungslos zu starten, ist es eine gute Idee, einige der unten dargestellten verschiedenen Aktivierungsschemata zu implementieren, um die Parallelisierung zu maximieren. Falls ein Daemon einen D-Bus-Dienst implementiert oder an einem Socket auf Anfragen wartet, erlaubt die Implementierung des kompletten Bus- und Socket-Aktivierungsschematas das Starten des Daemons mit seinen Clients parallel durchzuführen (womit der Systemstart beschleunigt wird), da bereits alle Kommunikationskanäle etabliert sind und keine Anfrage verloren geht, da Client-Anfragen vom Bus-System in Warteschlangen gestellt werden (im Falle von D-Bus) oder durch den Kernel (im Falle von Sockets), bis die Aktivierung abgeschlossen ist.
Aktivierung
beim Systemstart
Daemons der alten Art werden typischerweise exklusiv beim
Systemstart (oder manuell durch den Systemadministrator)
mittels SysV-Init-Skripten aktiviert, wie dies in der
LSB Linux Standard Base
Kern-Spezifikation
[1]
dargestellt ist. Diese Methode wird
universell auf Linux-Init-Systemen unterstützt, sowohl
bei denen der alten Art als auch bei neuartigen. Unter
anderen Problemen haben die SysV-Init-Skripte den Nachteil,
Shell-Skripte im Systemstartprozess zu involvieren.
Neuartige Init-Systeme setzen im Allgemeinen aktualisierte
Versionen der Aktivierung ein, sowohl während des
Systemstarts als auch während der Laufzeit und
verwenden minimalere Dienstebeschreibungsdateien.
Falls der Entwickler oder Administrator in Systemd sicherstellen möchte, dass ein Dienst oder eine andere Unit automatisch beim Systemstart aktiviert ist, wird empfohlen, einen Symlink auf die Unit-Datei im Verzeichnis .wants/ von entweder multi-user.target oder graphical.target, die normalerweise als Systemstartziele beim Systemstart verwandt werden, zu legen. Siehe systemd.unit(5) für Details über die Verzeichnisse .wants/ und systemd.special(7) für Details über die zwei Systemstartziele.
Socket-basierte
Aktivierung
Um die Parallelisierung und Robustheit zu maximieren und die
Konfiguration und Entwicklung zu vereinfachen, wird für
alle neuartigen Daemons empfohlen, die über Warten an
Sockets kommunizieren, eine Socket-basierte Aktivierung
einzusetzen. In einem Socket-basierten Aktivierungsschema
wird die Erstellung und das Binden des Sockets, an dem
gewartet werden soll, als primären Kommunikationskanal
von Daemons mit lokalen (und manchmal fernen) Clients aus
dem Daemon-Code in das Init-System verschoben. Basierend auf
einer Daemon-basierten Konfiguration installiert das
Init-System die Sockets und gibt sie an die erstellten
Prozesse, sobald der respektive Daemon gestartet wird.
Optional kann die Aktivierung des Dienstes verzögert
werden, bis der erste eingehende Verkehr auf dem Socket
eintrifft, um eine bedarfsgesteuerte Aktivierung von Daemons
zu realisieren. Allerdings ist der Hauptvorteil dieses
Schemas, dass alle Anbieter und Konsumenten des Sockets
parallel gestartet werden können, sobald alle Sockets
etabliert sind. Zusätzlich können Daemons
neugestartet werden und dabei nur eine minimale Anzahl an
Client-Transaktionen verlieren oder sogar überhaupt
keine Client-Anfrage (Letzteres stimmt insbesondere für
zustandslose Protokolle wie DNS oder Syslog), da das Socket
während des Neustarts angebunden und zugreifbar
verbleibt und alle Anfragen in einer Warteschlange
verbleiben, während der Daemon sie nicht verarbeiten
kann.
Neuartige Daemons, die Socket-Aktivierung unterstützen, müssen in der Lage sein, Sockets vom Init-System zu erhalten, statt sie selbst zu erstellen und sich daran zu binden. Für Details über die Programmierschnittstellen für dieses von Systemd bereitgestellte Schema, siehe sd_listen_fds(3) und sd-daemon(3). Für Details über die Portierung bestehender Daemons zu Socket-basierter Aktivierung, siehe unten. Mit minimalem Einsatz ist es möglich, Socket-basierte Aktivierung zusätzlich zu traditioneller interner Socket-Erstellung in der gleichen Codebasis zu erstellen, um sowohl neuartige als auch Init-Systeme der alten Art im gleichen Daemon-Programm zu unterstützen.
Systemd implementiert Socket-basierte Aktivierung mittels .socket-Units, die in systemd.socket(5) beschrieben sind. Wenn Socket-Units für Socket-basierte Aktivierung konfiguriert werden, ist es wesentlich, dass alle Sockets, bei denen auf Anfragen gewartet wird, durch eine spezielle Ziel-Unit sockets.target hereingezogen werden. Es wird empfohlen, eine Anweisung WantedBy=sockets.target in den Abschnitt »[Install]« zu setzen, um eine solche Abhängigkeit automatisch bei der Installation einer Socket-Unit hinzuzufügen. Die notwendigen Ordnungs-Abhängigkeiten werden für alle Socket-Units implizit erstellt, falls DefaultDependencies=no nicht gesetzt ist. Für weitere Informationen über sockets.target, siehe systemd.special(7). Es ist nicht notwendig oder empfohlen, zusätzliche Abhängigkeiten auf Socket-Units abzulegen (zum Beispiel von multi-user.target oder ähnlichem), wenn eine in sockets.target installiert ist.
Bus-basierte
Aktivierung
Wenn das D-Bus-IPC-System für die Kommunikation mit
Clients verwandt wird, sollten neuartige Daemons
Bus-Aktivierung einsetzen, so dass sie automatisch aktiviert
werden, wenn eine Client-Anwendung auf ihre
IPC-Schnittstelle zugreift. Dies wird in
D-Bus-Dienste-Dateien (die nicht mit Systemds
Dienste-Unit-Dateien durcheinandergebracht werden
dürfen) konfiguriert. Um sicherzustellen, dass D-Bus
Systemd verwendet, um den Daemon zu starten und zu
verwalten, verwenden Sie die Anweisung
SystemdService= in diesen Dienste-Dateien, um den
passenden Systemd-Dienst für einen D-Bus-Dienst zu
konfigurieren. Stellen Sie beispielsweise für einen
D-Bus-Dienst, dessen D-Bus-Aktivierungsdatei
org.freedesktop.RealtimeKit.service heißt, sicher,
dass SystemdService=rtkit-daemon.service in dieser
Datei gesetzt ist, um ihn an den Systemd-Dienst
rtkit-daemon.service zu binden. Dies wird benötigt, um
sicherzustellen, dass der Daemon ohne
Ressourcenwettläufe gestartet wird, wenn er über
mehrere Mechanismen simultan aktiviert wird.
Geräte-basierte
Aktivierung
Oft sollen Daemons, die einen besonderen Hardwaretyp
verwalten, nur aktiviert werden, wenn die Hardware der
entsprechenden Art eingesteckt oder andernfalls
verfügbar gemacht wird. In einem neuartigen Init-System
ist es möglich, die Aktivierung an
Hardware-Einsteck-/Aussteck-Ereignisse zu binden. In Systemd
können Kernel-Geräte, die in dem Gerätebaum
sysfs/udev auftauchen, als Units offengelegt werden, falls
sie mit der Zeichenkette »systemd« markiert
sind. Wie andere Arten von Units können sie dann andere
Units bei der Aktivierung (z.B. beim Einstecken)
hereinziehen, und daher Geräte-basierte Aktivierung
implementieren. Systemd-Abhängigkeiten können in
der Udev-Datenbank mittels der Eigenschaft
SYSTEMD_WANTS= kodiert werden. Siehe
systemd.device(5) für Details. Oft ist es
schöner, einen Dienst von Geräten nur indirekt
über dedizierte Ziele hereinzuziehen. Beispiel: Statt
bluetoothd.service von alle den verschiedenen
Bluetooth-Dongles und anderen verfügbaren Hardwaren
hereinzuziehen, ziehen Sie von ihnen bluetooth.target herein
und dann bluetoothd.service von diesem Ziel. Dies stellt
eine nettere Abstraktion bereit und ermöglicht
Administratoren die Option, bluetoothd.service über
einen Symlink bluetooth.target.wants/ uniform mit einem
Befehl der Art enable von systemctl(1) zu
steuern, statt den Udev-Regelsatz zu bearbeiten.
Pfadbasierte
Aktivierung
Oft können Laufzeit-Daemons, die Spool-Dateien oder
-Verzeichnisse (wie bei einem Drucksystem) verarbeiten,
verzögert werden, bis diese Dateisystemobjekte den
Zustand ändern oder nicht mehr leer sind. Neuartige
Init-Systeme stellen einen Weg bereit, die
Diensteaktivierung an Dateisystemänderungen zu binden.
Systemd implementiert dieses Schema mittels in .path-Units
konfigurierter pfadbasierter Aktivierung, wie in
systemd.path(5) skizziert.
Zeitgeber-basierte
Aktivierung
Einige Daemons, die Aufräumaufgaben erledigen, die
regelmäßig ausgeführt werden sollen, ziehen
aus Zeitgeber-basierter Aktivierung einen Nutzen. In Systemd
ist dies mittels .timer-Units implementiert, wie in
systemd.timer(5) beschrieben.
Andere
Formen der Aktivierung
Es wurden andere Formen der Aktivierung vorgeschlagen und in
einigen Systemen implementiert. Allerdings gibt es oft
einfachere und bessere Alternativen oder sie können als
Kombination der obigen Schemata zusammengestellt werden.
Beispiel: Manchmal erscheint es nützlich, Daemons oder
.socket-Units zu starten, wenn eine bestimmte IP-Adresse an
einer Netzwerkschnittstelle konfiguriert ist, da
Netzwerk-Sockets an diese Adresse gebunden werden sollen.
Eine Alternative zur Implementierung hiervon ist die
Verwendung der Socket-Option IP_FREEBIND von Linux,
die mittels FreeBind=yes in Systemd-Socket-Dateien
erreichbar ist (siehe systemd.socket(5) für
Details). Diese Option erlaubt Sockets, wenn sie aktiviert
ist, an nichtlokale, nichtkonfigurierte IP-Adressen gebunden
zu werden und erlaubt damit Anbindungen an bestimmte
IP-Adressen, bevor diese tatsächlich verfügbar
werden; dies macht eine solche explizite Abhängigkeit
auf die konfigurierte Adresse redundant. Ein anderer oft
vorgeschlagener Auslöser für Dienste-Aktivierung
ist niedrige Systemlast. Auch hier könnte allerdings
ein überzeugender Ansatz sein, die
Funktionalitäten des Betriebssystems korrekt zu
benutzen, insbesondere des CPU- oder E/A-Schedulers von
Linux. Anstatt Aufträge vom Anwenderbereich, basierend
auf der Überwachung des Betriebssystem-Schedulers
einzuplanen, ist es empfehlenswert, die Einplanung von
Prozessen dem Betriebssystem-Scheduler selbst zu
überlassen. Systemd stellt einen feingranularen Zugriff
auf die CPU- und E/A-Scheduler bereit. Falls ein vom
Init-System ausgeführter Prozess den Umfang der
für andere Prozesse verfügbaren CPU- oder
E/A-Bandbreite nicht negativ beeinflussen soll, sollte er
mit CPUSchedulingPolicy=idle und/oder
IOSchedulingClass=idle konfiguriert werden. Optional
kann dies mit Zeitgeber-basierter Aktivierung kombiniert
werden, um Hintergrund-Aufträge während der
Laufzeit und mit minimalen Auswirkungen auf das System
einzuplanen und ihn aus der Systemstartphase selbst zu
entfernen.
INTEGRATION MIT SYSTEMD
Schreiben
von Systemd-Unit-Dateien
Beim Schreiben von Systemd-Unit-Dateien wird empfohlen, die
folgenden Vorschläge zu
berücksichtigen:
1. Falls möglich, nutzen Sie die Einstellung Type=forking in Dienstedateien nicht. Falls Sie es aber machen, stellen Sie sicher, den PID-Dateipfad mit PIDFile= zu setzen. Siehe systemd.service(5) für Details.
2. Falls Ihr Daemon einen D-Bus-Namen auf dem Bus registriert, stellen Sie sicher, falls möglich Type=dbus in der Dienstedatei zu verwenden.
3. Setzen Sie eine gut lesbare Beschreibung in Description=.
4. Deaktivieren Sie DefaultDependencies= nicht, außer Sie wissen genau, was Sie tun und Ihre Unit ist in der frühen Systemstartphase oder im späten Herunterfahren beteiligt.
5. Normalerweise werden wenige, falls überhaupt, Abhängigkeiten explizit definiert werden müssen. Falls Sie allerdings explizite Abhängigkeiten definieren, beziehen Sie sich nur auf Units, die in systemd.special(7) aufgeführt sind oder auf Namen, die von Ihrem eigenen Paket bekanntgegeben sind, um Ihre Unit-Datei Betriebssystem-unabhängig zu halten.
6. Nehmen Sie einen Abschnitt »[Install]« auf, der die Installationsinformation für die Unit-Datei enthält. Siehe systemd.unit(5) für Details. Um Ihren Dienst beim Systemstart zu aktivieren, fügen Sie eine Anweisung WantedBy=multi-user.target oder WantedBy=graphical.target hinzu. Um Ihr Socket beim Systemstart zu aktivieren, fügen Sie WantedBy=sockets.target hinzu. Normalerweise wollen Sie auch sicherstellen, dass bei der Installation Ihres Dienstes auch Ihr Socket installiert wird, daher fügen Sie Also=foo.socket in Ihrer Dienstedatei foo.service für ein hypothetisches Programm foo hinzu.
Systemd-Dienstedateien
installieren
Zum Bauinstallationszeitpunkt (d.h. make install
während des Paketbaus) wird empfohlen, dass Pakete ihre
Systemd-Unit-Dateien im Verzeichnis, das von pkg-config
systemd --variable=systemdsystemunitdir (für
Systemdienste) oder pkg-config systemd
--variable=systemduserunitdir (für Benutzerdienste)
zurückgeliefert wird, zu installieren. Damit wird der
Dienst im System für explizite Anfragen verfügbar
gemacht, aber nicht beim Systemstart automatisch aktiviert.
Optional sollten während der Paketinstallation (z.B.
rpm -i durch den Administrator) Symlinks in den
Systemd-Konfigurationsverzeichnissen mit dem Befehl
enable des Werkzeuges systemctl(1) erstellt
werden, um sie beim Systemstart automatisch zu
aktivieren.
Für Pakete, die autoconf(1) verwenden, wird empfohlen, einen Configure-Skript-Auszug wie den nachfolgenden zu verwenden, um den Unit-Installationspfad während der Quellkonfiguration zu ermitteln:
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
[Directory for systemd service files])],,
[with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" =
"xyes" -o "x$with_systemdsystemunitdir"
= "xauto"], [
def_systemdsystemunitdir=$($PKG_CONFIG
--variable=systemdsystemunitdir systemd)
AS_IF([test
"x$def_systemdsystemunitdir" = "x"],
[AS_IF([test "x$with_systemdsystemunitdir" =
"xyes"],
[AC_MSG_ERROR([systemd support requested but pkg-config
unable to query systemd package])])
with_systemdsystemunitdir=no],
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" !=
"xno"],
[AC_SUBST([systemdsystemunitdir],
[$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test
"x$with_systemdsystemunitdir" !=
"xno"])
Dieser Schnipsel ermöglicht die automatische Installation der Unit-Dateien auf Systemd-Maschinen und erlaubt optional ihre Installation selbst auf Maschinen, denen Systemd fehlt. (Die Anpassung des Schnipsels auf das Benutzer-Unit-Verzeichnis bleibt als Aufgabe für den Leser.)
Damit make distcheck weiterhin funktioniert, wird auf automake(1) basierenden Projekten zusätzlich empfohlen, folgendes zur Datei Makefile.am auf oberster Ebene hinzuzufügen:
AM_DISTCHECK_CONFIGURE_FLAGS
= \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
Schließlich sollten Unit-Dateien im System mit einem Automake-Ausschnitt der folgenden Art installiert werden:
if
HAVE_SYSTEMD
systemdsystemunit_DATA = \
foobar.socket \
foobar.service
endif
In der .spec-Datei von rpm(8) verwenden Sie Schnipsel der folgenden Art, um Dienste während der Installation/Deinstallation zu aktivieren/zu deaktivieren. Dies verwendet RPM-Makros, die zusammen mit Systemd ausgeliefert werden. Ziehen Sie die Paketierungsrichtlinien Ihrer Distribution für Details und die Äquivalente anderer Paketverwalter hinzu.
Am Anfang der Datei:
BuildRequires:
systemd
%{?systemd_requires}
Und als Skriptteile, weiter unten:
%post
%systemd_post foobar.service foobar.socket
%preun
%systemd_preun foobar.service foobar.socket
%postun
%systemd_postun
Falls der Dienst während der Aktualisierung neu gestartet werden soll, ersetzen Sie den Skriptteil »%postun« mit nachfolgendem:
%postun
%systemd_postun_with_restart foobar.service
Beachten Sie, dass »%systemd_post« und »%systemd_preun« die Namen sämtlicher zu installierender/entfernender Units als durch Leerzeichen getrennte Argumente erwarten. »%systemd_postun« erwartet keine Argumente. »%systemd_postun_with_restart« erwartet die neu zu startenden Units als Argumente.
Um Aktualisierungen von einer Paketversion, die nur SysV-Init-Skripte auslieferte, zu einer Paketversion, die sowohl SysV-Init-Skripte als auch eine native Systemd-Dienstedatei ausliefert, zu unterstützen, verwenden Sie Fragmente der folgenden Art:
%triggerun
-- foobar < 0.47.11-1
if /sbin/chkconfig --level 5 foobar ; then
/bin/systemctl --no-reload enable foobar.service
foobar.socket >/dev/null 2>&1 || :
fi
Wobei 0.47.11-1 die erste Paketversion ist, die die native Unit-Datei enthält. Dieses Fragment bewirkt, dass bei der erstmaligen Installation der Unit-Datei diese nur aktiviert wird, falls das SysV-Init-Skript aktiviert ist und stellt damit sicher, dass der Aktivierungsstatus sich nicht ändert. Beachten Sie, dass der Befehl chkconfig, der zum Überprüfen des Aktivitätsstandes eines SysV-Init-Skriptes verwandt werden kann, Fedora-spezifisch ist. Hier werden andere Betriebssysteme andere Befehle verwenden müssen.
EXISTIERENDE DAEMONS PORTIEREN
Da neuartige Init-Systeme wie Systemd zu traditionellen SysV-Init-Systemen kompatibel sind, ist es strenggenommen nicht notwendig, Daemons auf die neue Art zu portieren. Erfolgt dies, ermöglicht es aber, zusätzliche Funktionalitäten zum Daemon hinzuzufügen, sowie die Integration in das neuartige Init-System zu vereinfachen.
Um einen bestehenden, SysV-kompatiblen Daemon zu portieren, werden die folgenden Schritte empfohlen:
1. Falls noch nicht implementiert, fügen Sie einen zusätzlichen Befehlszeilenschalter zu dem Daemon hinzu, um die Daemonisierung zu deaktivieren. Dies ist nicht nur für den Einsatz in neuartigen Init-Systemen, sondern auch zur Erleichterung der Fehlersuche nützlich.
2. Falls der Daemon zu anderer, auf dem lokalen System laufenden Software Schnittstellen mittels lokaler AF_UNIX-Sockets anbietet, sollte eine Socket-basierte Aktivierung (siehe oben) überlegt werden. Normalerweise reicht ein minimaler Patch, um dies zu implementieren: Erweitern Sie die Socket-Erstellung im Daemon-Code, so dass zuerst sd_listen_fds(3) auf bereits übergebene Sockets geprüft wird. Falls Sockets übergeben wurden (d.h. wenn sd_listen_fds() einen positiven Wert liefert), überspringen Sie den Socket-Erstellungsschritt und verwenden Sie die übergebenen Sockets. Zweitens sollten die System-Socket-Knoten für lokale, in der Socket-basierten Aktivierung verwandten AF_UNIX-Sockets nicht entfernt werden, wenn der Daemon heruntergefahren wird, falls Sockets übergeben wurden. Falls der Daemon normalerweise alle verbliebenen offenen Dateideskriptoren als Teil seiner Initialisierung schließt, müssen drittens die vom Init-System übergebenen Sockets verschont werden. Da neuartige Init-Systeme garantieren, dass keine verbliebenen Dateideskriptoren an ausgeführte Prozesse übergeben werden, könnte es eine gute Idee sein, einfach das Schließen aller verbliebenen offenen Dateideskriptoren zu überspringen, falls Sockets übergeben wurden.
3. Schreiben und installieren Sie eine Systemd-Unit-Datei für den Dienst (und die Sockets, falls Socket-basierte Aktivierung verwandt wird, sowie eine Pfad-Unit-Datei, falls der Daemon ein Spool-Verzeichnis verarbeitet), siehe oben für Details.
4. Falls der Daemon Schnittstellen mittels D-Bus offenlegt, schreiben Sie eine D-Bus-Aktivierungsdatei für den Dienst, siehe oben für Details.
ABLAGE VON DAEMON-DATEN
Es wird empfohlen, den allgemeinen Richtlinien, beschrieben in file-hierarchy(7), für die Ablage von Paketdateien zu folgen.
SIEHE AUCH
systemd(1), sd-daemon(3), sd_listen_fds(3), sd_notify(3), daemon(3), systemd.service(5), file-hierarchy(7)
ANMERKUNGEN
1. |
LSB-Empfehlungen für SysV-Init-Skripte |
http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
2. |
Apple-MacOS-X-Daemon-Anforderungen |
ÜBERSETZUNG
Die deutsche Übersetzung dieser Handbuchseite wurde von Helge Kreutzmann <debian [AT] helgefjell.de> erstellt.
Diese Übersetzung ist Freie Dokumentation; lesen Sie die GNU General Public License Version 3 oder neuer bezüglich der Copyright-Bedingungen. Es wird KEINE HAFTUNG übernommen.
Wenn Sie Fehler in der Übersetzung dieser Handbuchseite finden, schicken Sie bitte eine E-Mail an <debian-l10n-german [AT] lists.org>.