Portál AbcLinuxu, 9. května 2024 12:46
V
dnešním díle se zaměříme na .service
jednotky, což jsou jednotky spouštějící
démony, čili přímá náhrada za init skripty.
Systemd je nejnovější hvězda v tak poklidné a konzervativní oblasti, jako jsou
init systémy. V této sérii článků si probereme jeho vlastnosti.
.service
Jednotka .service
je jednotka sloužící ke spouštění, ukončování a kontrole
bežících procesů. Tolik oficiální dokumentace.
Každý typ jednotky má svoji stejnojmennou sekci, takže volby tohoto typu jsou v
sekci [Service]
. Ve výchozím stavu má každá jednotka implicitní závislosti
After=basic.target
a Requires=basic.target
a Conflicts=shutdown.target
a
Before=shutdown.target
. Těm, kteří se prokousali minulými díly, není
třeba vysvětlovat více, pro ostatní – vše podstatné, včetně toho, jak tyto
implicitní závislosti potlačit naleznete tam.
Mimo nativních jednotek systemd podporuje rovněž klasické skripty v
/etc/init.d
, dokonce včetně LSB hlaviček. Právě tato vlastnost usnadňuje přechod
z klasického sysvinit rozložení na systemd.
.service
jednotkyPřepis init skriptu na .service
jednotku je poměrně snadný a přímočarý úkol. A
výsledek je čitelnější než init skript, který je obvykle plný výplňového
kódu. Ten byl přesunut do samotného systemd
.
Toto je příklad /etc/init.d/bluetoothd
(známým občas pod jménem bluez
). Pomineme
skutečnost, že přinejmenším Fedora a openSUSE bluetooth démona spouštějí prostřednictvím
systému udev
.
[Unit] Description=Bluetooth Daemon Names=bluez.service After=syslog.target [Service] Type=forking ExecStart=/usr/sbin/bluetoothd PIDFile=/var/run/bluetooth.pid [Install] Alias=bluez.service WantedBy=bluetooth.target
Z několika desítek řádků init skriptu pro bluetooth démona jsme se dostali na 11 řádků
.service
jednotky. Navíc rozdělení jednotlivých voleb do sekcí dále
zjednodušuje orientaci – potřebujeme se podívat na to, jakým způsobem jednotka
startuje? Prostě se podíváme pouze na sekci [Service]
a zbytek ignorujeme.
Volba Description=
je popis jednotky. Sekce Names=
určuje alternativní
názvy pro jednotku a zaručuje, že příkaz systemclt bluez.service
bude rovněž
fungovat. Volba After=syslog.target
zajišťuje, že bude tato jednotka spuštěna
až po spuštění systémového logu. Ovšem protože volba After=
znamená pouze
pořadí a neříká nic o závislostech, neznamená to, že spuštění této služby spustí
i syslog. Volba After=
znamená, že pokud bude existovat požadavek na spuštění
syslog.target
a bluetooth.service
, bude prvně jmenovaná spuštěna dříve.
Sekce označená jako [Install]
říká, že bluez.service
je alternativní
název pro instalaci této služby. Obvykle je dobré mít obsah [Unit]/Names=
a
[Install]/Alias=
stejný, protože by rozdíly mezi instalačními a názvy za běhu
mohly být matoucí. Volba WantedBy=
říká, že tato jednotka bude vyžadována
jednotkou bluetooth.target
, čili při instalaci bude vytvořen symbolický odkaz
v bluetooth.target.wants
.
Prostřední sekcí [Service]
se bude zabývat následující text.
Nejdůležitějším parametrem je Type=, čili typ služby, kterou hodláme
spouštět. Systemd rozeznává 5 rozličných typů – simple
, forking
, oneshot
,
dbus
a notify
.
Výchozí hodnota simple
říká, že proces definovaný v ExecStart=
bude hlavním
procesem služby. V tomto režimu musí být komunikační kanály nastaveny před
vlastním spuštěním procesu. Tato volba je vlastní například /sbin/sulogin
v
emergency.target
a systemd
spouští závislé jednotky okamžitě.
Typ oneshot
je podobný typu simple
s tím rozdílem, že systemd
spustí
závislou jednotku až poté, co daný proces doběhne. Toto je klasický typ
různých služeb běžících při startu a ukončování systému jako například
fsck@.service
, clock.service
(odstraněna v systemd-28) nebo
quotaon.service
. Posledně jmenovaná má navíc nastaven parametr
RemainAfterExit=yes
, což znamená, že služba quotaon.service
, jinak
jednorázové spuštění příkazu /sbin/quotaon
, bude i po ukončení běhu procesu
považována za aktivní. Jinak by start každé závislé jednotky na
quotaon.service
vyvolal spuštění tohoto příkazu.
Dalším typem je dbus
, což opět obdoba typu simple
, ovšem zde se očekává, že
démon nakonec zabere svoje jméno na D-BUS sběrnici tak, jak určuje parametr
BusName=
. Systemd spustí závislé jednotky až poté, co dojde k onomu zabrání
jména. Tyto jednotky získávají implicitní závislost na dbus.target
.
Nastavení forking
je pro tradiční unixové démony, které volají fork(2)
.
Rodičovský proces skončí okamžikem, kdy se dokončí démonizace a nastavení
komunikačních kanálů. Démonizovaný proces potom běží dál samostatně. Pro
forking démony je vhodné uvést umístění souboru s číslem procesu parametrem
PIDFile=
. Další procesy jsou nastartovány až poté, so skončí rodičovský
proces. Typickým představitelem tohoto typu je acpid
.
Typ notify očekává aktivní notifikaci stavu spuštěného démona, k čemuž slouží
volání sd_notify(3)
(o sd_funkcích
se dozvíme v následujících částech), nebo
podobné volání. V tomto případě systemd
spouští závislé služby až potom, co
byl démonem upozorněn, že dokončil svůj start. Tato volba je určena pro démony,
které z nějakých důvodů nemohou svůj start notifikovat jinak, třeba připojením
se na sběrnici D-BUS. Představitelem je udev.service
.
NotifyAccess= pak specifikuje přístup k notifikačnímu socketu, kterým proces
komunikuje se systemd (viz sd_notify(3)
). Možnosti jsou none
– žádné zprávy
nejsou akceptovány, main
– pouze ty od hlavního procesu nebo all
– všechny
zprávy dané kontrolní skupiny jsou akceptovány. Tato volba má smysl, pokud
notifikaci provádíme příkazem /bin/systemd-notify
.
bluetooth.service
Přepis jednotek jedna k jedné je sice snadný a možný, ale nikterak nevyužívá
schopností systemd
, mezi něž patří především spouštění na požádání. Démon
bluetoothd
používá ke komunikaci sběrnici D-BUS, čili v tomto případě je lepší
použít typ dbus
.
Relevantní část se změní na
[Service] Type=dbus BusName=org.bluez ExecStart=/usr/sbin/bluetoothd -n
čili démon bluetoothd
bude spuštěn v případě, že démon dbus obdrží požadavek
na nějakou službu z org.bluez
. Připomínám, že dbus démon byl upraven tak, aby
dokázal požadavky na spuštění služeb přesměrovat na systemd
. To znamená, že
služby aktivované přes D-BUS jsou spuštěné stejným způsobem a mají k dispozici
stejné nastavení, jako ty ostatní.
ExecStart= určuje příkazový řádek, který má být vykonán pro start
této jednotky, a je povinný. První část argumentu musí být absolutní cesta k
příkazu (sbohem špatně nastavené PATH=
), následovaná argumenty pro tento
příkaz. Výše uvedená clock.service
má ExecStart=/sbin/hwclock --systz
.
Respektive měla, protože od verze systemd 28 byla tato jednotka zrušena.
Tato volba nesmí být uvedena více než jednou – s výjimkou typu oneshot
. V
tomto případě jsou příkazy spouštěny postupně v pořadí, ve kterém jsou uvedeny v
souboru.
V případě, že absolutní cesta začíná na @
, bude první část vynechána ze
seznamu argumentů daného programu, což znamená, že hodnota argv[0]
, která
označuje název programu, bude rovna druhému argumentu. Takže program spuštěný s
ExecStart=@/usr/bin/foo bar
bude mít v argv
hodnotu bar
.
Pokud je prvním prefixem znak -
, je ignorován návratový kód služby, takže je
spuštění považováno vždy za úspěšné. Příkladem je ExecPrefix=-/bin/sulogin
z
emerency.target
. Pokud je potřeba oba znaky zkombinovat, potom musí být ve
tvaru -@
.
S výjimkou typu forking
je vždy proces nastartovaný příkazem za ExecStart
považován za hlavní proces démona. V praxi to znamená, pokud spouštíme službu
pomocí nějakého shellového wrapperu, musíme démona spouštět pomocí exec
. V
případě typu forking
se systemd dívá na pid soubor.
Podporováno je i nahrazování proměnných prostředí, takže ${FOO}
bude
nahrazenou hodnotou proměnné stejného jména. Stejně tak i $FOO
může být uveden
jako samostatné slovo na příkazové řádce a v tom případě je nahrazeno hodnotou
rozdělenou bílými znaky. Ovšem jméno příkazu musí být stále absolutní cesta k
binárnímu souboru.
ExecStop= jsou příkazy spouštěné pro zastavení služby. Podporuje
${MAINPID}
jako ExecReaload=
a všechny ostatní vlastnosti uvedené výše.
Narozdíl od sysvinit skriptů je tato volba čistě volitelná a případné zbývající
procesy jsou ukončeny podle volby KillMode=
.
ExecStopPost= jsou příkazy spuštěné po ExecStop=
– využití je mizivé,
ovšem rescue.service
má ExecPostStop=/bin/systemctl default
.
Stejně jako se rozeznává několik typů procesů pro spouštění, existuje i
několik různých typů z hlediska vypínání. Ty určuje volba KillMode=
, jejíž
argumenty jsou control-group
, process-group
, process
, nebo none
.
Argument control-group
značí, že systemd po skončení příkazu ExecPost=
ukončí všechny zbývající procesy patřící do stejné kontrolní skupiny (cgroup)
jako hlavní proces. Volba process-group
značí, že budou ukončeny procesy
patřící do stejné skupiny procesů. Možnost process
znamená, že se má ukončit pouze hlavní
proces, a volba none
potlačí jakékoli vypínání procesů, takže bude
proveden pouze příkaz uvedený v ExecStop=
.
Schéma vypínání je následující: Nejprve je zaslán SIGTERM
(lze předefinovat
volbou KillSignal=
). Pokud po době specifikované parametrem TimeoutSec=
jsou
procesy stále naživu, je jim zaslán SIGKILL
. Výchozí hodnota je 60.
Další možností je použít argument -s příkazu systemctl kill, takže
# systemctl kill -s SIGKILL bluetoothd.service
Pošle procesům SIGKILL. Dalším parametrem můžete omezit počet procesů, kterým se signál posílá, takže požadavek na znovunahrání konfigurace je
# systemctl kill -s SIGHUP --kill-who=ḿain bluetoothd.service
Exec
ExecStartPre= a ExecStartPost= jsou příkazy spouštěné před, případně po
příkazu ExecStart=
. Příkazy mohou být odděleny středníkem, nebo může být
uvedeno více voleb po sobě, i když druhá forma může být nekompatibilní s
nastroji očekávající XDG .desktop
formát. Podporovány jsou všechny vlastnosti
zmíněné u ExecStart=
.
Příkladem použití je rescue.service
:
ExecStartPre=-/bin/plymouth --hide-splash ExecStartPre=-/bin/echo 'Welcome to rescue mode. Use „systemctl default“ or ^D to activate default mode.' ExecStart=-/sbin/sulogin
Volba ExecReload=
určuje způsob, jímž se službě říká, aby znovu načetla
konfiguraci. Jejím argumentem může být rovněž více příkazů jako u
ExecStartPre/Post
. Podporována je proměnná ${MAINPID}
. ExecReload=
pak
obvykle vypadá jako /bin/kill -HUP ${MAINPID}
, ale různé služby
lze požádat různě. Například dbus.service
vypadá takto:
ExecStartPre=/bin/dbus-uuidgen --ensure ExecStartPre=-/bin/rm -f /var/run/dbus/pid ExecStart=/bin/dbus-daemon --system --address=systemd: --nofork \ --systemd-activation ExecReload=/bin/dbus-send --print-reply --system --type=method_call \ --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig
Restart= určuje, zda má být hlavní proces služby restartován, pokud
skončí, nebo ne. Výchozí hodnotou je no
, čili služba nebude restartována. Při
on-success
bude restartována pouze v případě, že skončila s návratovým kódem
0 a při always
bude restartována vždy, bez ohledu na návratovou hodnotu.
Protože je poslední možnost alternativou k respawn
u klasického initu, není
překvapením, že je tato volba přítomná u getty@.service
.
RestartSec=
je interval mezi ukončením a opětovným spuštěním služby.
Výchozí hodnota je 100ms. U getty@.service
je nastavena na 0.
Tento díl nakousl praktičtější část problematiky systemd
, totiž .service
jednotky. Dozvěděli jsme se, které typy démonů systemd nativně podporuje, jak
spustit, zastavit, nebo požádat o znovunahrání konfigurace. Příští díl se bude
věnovat kontrole prostředí, v němž bude proces spuštěn.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.