Nebojte se systemd 3: vytváření jednotek

Minule jsme se naučili jednotky ovládat pomocí příkazu systemctl. Dnes si ukážeme jejich vytváření, formát konfiguračních souborů a nastavení stejné pro všechny typy jednotek.

Formát konfiguračních souborů

Jednotky, které jsou instalovány z repozitářů, a tedy by je uživatel neměl měnit, sídlí v /usr/lib/systemd/system, resp. uživatelské v /usr/lib/systemd/user. Vlastní systémové jsou standardně v /etc/systemd/system. Uživatelské jsou v /etc/systemd/user, které jsou přístupné pro všechny uživatele, a v ~/.systemd/user  pro každého uživatele zvlášť.

 

Konfigurační soubory jsou inspirovány soubory s příponou .desktop, které nalezneme například v GNOME (pozor, systemd na rozdíl od těchto souborů povoluje specifikovat hodnotu vícekrát, standardní parsery tedy nemusí fungovat). Tento formát je snadno čitelný jak pro uživatele, tak pro počítače. Soubor je rozdělen na pojmenované sekce uvozené řádkem [jméno]. V každé sekci je pak seznam hodnot ve formátu jméno parametru=hodnota. Řádka uvozená # popř ; je komentář.

Zde si ještě doplňme minulý díl. Pokud chceme načíst novou konfiguraci nějaké jednotky, stačí spustit příkaz systemctl daemon-reload, který aktualizuje všechny jednotky. Příkaz, který by aktualizoval jen jednu konkrétní, neexistuje. Pozor na záměnu se systemctl reload jednotka, který aktualizuje konfigurační soubory týkající se té jednotky, ne konfigurace systemd. Vše vysvětlí příklad. Reload jednotky Apache přečte httpd.conf, apache.service se aktualizovat nebude. Oproti tomu daemon-reload přečte konfiguraci souboru apache.service, tedy např. změní závislosti.

Pokud je hodnotou seznam, rozumí se oddělený mezerami. Také je možné specifikovat tuto hodnotu na několika řádcích, výsledek je pak sjednocení těchto seznamů. Pokud jako hodnotu předáme prázdný řetězec, dojde k vynulování aktuálního seznamu. Tedy v následujícím příkladu bude v parametru Documentation seznam „man:ls man:systemd“.

Documentation=file:/home/knezi/ahoj man:od Documentation= Documentation=man:ls Documentation=man:systemd

Jednotky obecně mají sekci [Unit], ve které nalezneme parametry nezávisející na typu, tedy např. popis či závislosti. Dále [Install], ve které jsou informace pro instalaci jednotky. Tato sekce není interpretována systemd za běhu, ale až při použití příkazů enable/disable, které slouží pro povolení/zakázání spouštění jednotky po startu počítače. Poslední sekce je již závislá na typu konkrétní jednotky a představíme si ji později.

Příklad jednotky typu služba mající všechny tři sekce:

[Unit] Description=Console Shell Documentation=man:sulogin(8) After=systemd-user-sessions.service plymouth-quit-wait.service Before=getty.target  [Service] Environment=HOME=/root WorkingDirectory=-/root ExecStart=-/usr/bin/sulogin ExecStopPost=-/usr/bin/systemctl poweroff Type=idle StandardInput=tty-force StandardOutput=inherit StandardError=inherit KillMode=process IgnoreSIGPIPE=no SendSIGHUP=yes  [Install] WantedBy=getty.target

Pro snažší psaní konfiguračních souborů se hodí zvýrazňování syntaxe. Pro textový editor VIM stačí stáhnout již hotové zvýrazňování syntaxe. V Arch Linuxu je přímo balíček vim-systemd. Zdrojové soubory jsou na adrese fedorapeople.org/cgit/wwo­ods/public_git/vim-scripts.git (přímý odkaz do repozitáře git://fedorapeople.org/ho­me/fedora/wwoods/public_git/vim-scripts.git). Stačí zkopírovat soubory ftdetect/systemd.vim, ftdetect/udev.vim do adresáře /usr/share/vim/vimfiles/ftdetect a syntax/systemd.vim, syntax/udev.vim do /usr/share/vim/vimfiles/syntax. Pro zapnutí zvýrazňování stačí ve VIMu pustit příkaz  :syntax on.

Sekce [Unit]

Základním parametrem je Description, což je popis, který se objeví například ve výpisu všech jednotek. Všimněme si, že zde není parametr pro jméno. To se odvozuje od jména konfiguračního souboru. Parametr Documentation obsahuje seznam URI oddělených mezerami. URI mohou být typu http[s]://, file:, man:, info:. Přičemž příkaz systemctl help jednotka zobrazí první dostupnou manuálovou stránku (tedy file:, http[s]:// přeskakuje). Celý parametr Documentation se objeví ve výpisu příkazu  systemctl status jednotka.

Závislosti

Řešení a spouštění závislostí řeší tzv. minimální transakční systém. Kdykoliv je jednotka aktivována či deaktivována, je přidána spolu se všemi rekurzivními závislostmi do dočasné transakce. Poté se otestuje, jestli transakce obsahuje cykly. Pokud ano, systemd odstraní všechny postradatelné závislosti a také odstraní ty, které by mohly aktivovanou jednotku zastavit. Poté se zkontroluje, jestli není transakce v konfliktu s nějakou právě prováděnou a nakonec, pokud je vše v pořádku, je přidána do spouštěcí fronty. Speciálně pokud není možné transakci vyřešit, je odmítnuta ještě před jejím prováděním a nezůstane tak v žádném mezistavu.

V systemd se doporučuje používat automatické závislosti všude, kde je to možné. K tomuto účelu se používá mechanismus socket-based activation. Princip si představíme na pulseaudio.service. Jednotka může komunikovat s ostatními pomocí speciálních souborů (tzv. socket). K této jednotce vytvoříme další jednotku typu socket (tedy pulseaudio.socket), kterou nastartujeme po startu počítače. Ve chvíli, kdy potřebujeme pulseaudio.service použít, přistoupíme k socket souboru jednotky pulseaudio.socket, která při prvním přístupu spustí  pulseaudio.service.

Pokud z nějakého důvodu automatické závislosti použít nemůžeme nebo nechceme, má systemd propracovaný systém explicitních závislostí. Vše přehledně shrnuje následující seznam. Jen poznamejme, že všechny parametry jsou seznamy hodnot, tedy pro ně platí chování popsané výše.

Requires
Při aktivaci se aktivují všechny jednotky specifikované v tomto seznamu. V případě že selže aktivace některé jednotky ze seznamu nebo nějaká selže za běhu, bude deaktivována i tato. Tento parametr nedefinuje pořadí spouštění jednotek, spuštěny budou všechny najednou, pokud to neurčí jiný parametr.
Wants
Slabší varianta Requires. Funguje stejně, jen nevadí, pokud nějaká závislost selže. Dokumentace tuto variantu doporučuje.
After,Before
Jednotka je aktivována až po startu nebo před startem definovaných jednotek. Tento parametr nedefinuje explicitní závislost, jen určuje pořadí spouštění. Více viz příklad.
Conflicts
Když je tato jednotka aktivována, jsou zastaveny všechny konfliktní a naopak.

Ukážeme si modelový příklad. Pokud spustíme jednotku tlp příkazem systemctl start tlp.service, spustí se jednotky bluetooth.service a NetworkManager.service. Řádka After=multi-user.target bluetooth.service NetworkManager.service zajistí, že se tyto jednotky nejdříve spustí a teprve až když jsou spuštěné, jednotka tlp.service se začne spouštět. Díky tomu, že závislosti jsou definované parametrem Wants=, spouštění bude pokračovat i pokud by spouštění některé závislosti selhalo. Vidíme, že pro vhodné nastavení používání závislosti je nutné použít kombinaci Requires/Wants a After/Before.

[Unit] Description=TLP system startup/shutdown Wants=bluetooth.service NetworkManager.service After=multi-user.target bluetooth.service NetworkManager.service Before=shutdown.target  [Service] Type=simple RemainAfterExit=yes ExecStart=/usr/bin/tlp init start ExecStop=/usr/bin/tlp init stop  [Install] WantedBy=multi-user.target

Ještě poznamenejme, že výše uvedené nastavování závislostí Requires a Wants je možné nastavit i bez nutnosti úpravy konfiguračních souborů. Pokud bychom chtěli přidat závislost jednotce tlp, můžeme vytvořit adresář /etc/systemd/system/tlp.service.wants a symbolický odkaz z tohoto adresáře na jednotku, kterou chceme mít jako závislost tlp. Stejný mechanismus funguje i s Require. Tohoto faktu se využívá při instalaci jednotek. Viz níže.

V krátkosti ještě zmíníme několik dalších parametrů, další podrobnosti může čtenář nalézt v manuálových stránkách man systemd.unit. Pokud jednotka selže, spustí se příkaz v parametru OnFailure. Tento parametr je vhodné použít například pro upozornění administrátora v případě selhání jednotky důležité pro běh systému.

Pokud jednotka potřebuje ke svému běhu nějaký adresář či soubor na jiném oddíle, nemusí být tento oddíl při startu ještě připojen. To vyřeší parametr RequiresMountsFor, který určuje, že jednotka může být spuštěna až ve chvíli, kdy je daná cesta připojena do souborového systém. Také je možné specifikovat podmínky, za kterých může být jednotka spuštěna. Jsou to parametry začínající Condition, popř. Assert, kterými je možno například určit, že jednotku je možné spustit pouze na některých architekturách nebo že počítač musí být napájen z elektrické sítě.

Sekce [Install]

Nejdůležitějšími parametry jsou WantedBy a RequiredBy, které říkají, kam se mají přidat závislosti při příkazu systemctl enable. Pokud chceme nějakou jednotku spouštět po startu, typicky nastavíme WantedBy=default.target, popř. WantedBy=multi-user.target. Při systemctl enable netctl.service se nastaví závislost multi-user.target na netctl.service a díky tomu se netctl spustí po startu počítače. Závislosti se nastavují pomocí symbolických odkazů, takže není nutné editovat žádné soubory. Při disable se zase odkazy odstraní.

Úprava jednotek

Pokud potřebujeme upravit nějakou jednotku, která je dodávaná v repozitářích, nemůžeme upravit její konfigurační soubor, protože by byl při příští aktualizaci přepsán. Jednou možností je zkopírovat jednotku do adresáře /etc/systemd/system, který má vyšší prioritu. Tím se ale připravíme o aktualizace. V systemd existuje mechanismus umožňující přepsat jen některé parametry. Pro každou jednotku můžeme vytvořit adresář  jmeno.d. Jednotka je načtena standardně a poté jsou aplikovány všechny soubory s příponou .conf v této složce. Soubor je standardně rozdělen na sekce (jen neobsahuje sekci [Install]) a řádky jsou přidány do původní konfigurace.

Pokud tedy například chceme kompletně předefinovat jednotky, které budou spuštěny před tlp.service, parametr After vymažeme parametr a nadefinujeme vlastní:

/etc/systemd/system/tlp.d/change.conf

After= After=jednotky...

Šablony

Často je potřeba použít nějakou jednotku v několika instancích (kupříkladu terminály). K tomu se dají použít šablony, které mají stejné konfigurační soubory jako běžné jednotky. Odlišeny jsou zavináčem na konci jména. Pokud v systemd pracujeme s jednotkou obsahující zavináč (ve tvaru jmeno@parametr.typ), je hledán soubor jmeno@.typ a pro odlišení konkrétní instance se použije parametr. V systemd je standardně šablona getty@.service a pro třetí terminál slouží jednotka odvozená z této šablony pojmenovaná  getty@tty3.service.

V šablonách je ještě proti běžným jednotkám možné použít meta informace o konkrétní jednotce. Tedy např. %i je identifikátor označující parametr. V našem případě je nahrazen tty3. Více v man systemd.unit  v sekci  SPECIFIERS.

Šablonami jsme vyčerpali dnešní téma. Na shledanou u následujících dílů, ve kterých si představíme konkrétní typy jednotek.

 

Zdroj: https://www.root.cz/clanky/nebojte-se-systemd-vytvareni-jednotek/