Přeskočit na obsah

systemctl --dry-run není simulace pro všechny podpříkazy

import { Aside } from ‘@astrojs/starlight/components’;

Během read-only inventury (příprava plánu změny, nic se nemělo měnit) padl dotaz „co přesně by enable udělal?” a odpověď se hledala příkazem s --dry-run. Výstup vypadal jako popis plánu:

Created symlink /etc/systemd/system/multi-user.target.wants/<unit>
→ /usr/local/lib/<app>/<konkrétní-verze>/<unit>

O dvacet minut později se při další kontrole ukázalo, že symlink skutečně existuje, is-enabled se změnil z linked na enabled a jednotka by nastartovala při příštím bootu. Žádné potvrzení se nevyžadovalo, žádné varování se nevypsalo.

--dry-run není globální vlastnost systemctl. Podporuje ho jen část podpříkazů (typicky ty pro řízení stavu systému). U enable/disable v ověřované verzii příznak neplatí a je tiše ignorován — příkaz proběhne ostře.

Zrádné je hlášení: Created symlink … je v systemd stejná věta pro reálné provedení i pro simulaci. Čtenář v režimu „dry-run” ji přirozeně přečte jako budoucí čas („vytvořil by”), přitom popisuje hotový fakt. Že šlo o reálný zápis, prozradí až:

Terminál
# časové razítko odpovídá okamžiku "simulace"
find /etc/systemd -newermt "<dnešek>" -printf '%TY-%Tm-%Td %TH:%TM:%TS %p -> %l\n'
journalctl --since "<čas>" | grep Reloading # systemd[1]: Reloading.

Druhý, tišší problém: symlink, který enable vytvoří, míří na kanonizovanou cestu (rozřešený symlink), tedy na konkrétní verzi balíku — ne na stabilní přepínací symlink. U verzovaných instalací s atomickým přepínáním current → verze-N to znamená, že po příštím přepnutí by boot spouštěl jednotku staré verze, případně by odkaz osiřel.

Zjišťování bez mutace. [Install] sekce plně určuje, co enable udělá — stačí ji přečíst:

Terminál
sed -n '/\[Install\]/,$p' /etc/systemd/system/<unit> # např. jen WantedBy=multi-user.target
systemctl show <unit> -p UnitFileState -p WantedBy -p NeedDaemonReload

Zápis persistence bez enable. Když [Install] obsahuje jen WantedBy=, je celý efekt jeden symlink — dá se vytvořit atomicky a se stabilním cílem:

Terminál
W=/etc/systemd/system/multi-user.target.wants
TMP="$W/.<unit>.$$-$RANDOM.tmp" # běhově unikátní jméno
ln -s /usr/local/lib/<app>/current/<unit> "$TMP" # BEZ -f: existující objekt = chyba, ne tichý přepis
[ "$(readlink "$TMP")" = /usr/local/lib/<app>/current/<unit> ] || exit 1
mv -T "$TMP" "$W/<unit>" # jediná publikační mutace (atomický rename)

Cíl je stabilní current/, takže persistence se přepíná spolu s balíkem.

Kontrola rezidua musí počítat se skrytým jménem. Dočasné jméno začíná tečkou, takže běžný shell glob ho mine:

Terminál
# ŠPATNĚ – skryté položky nevidí
ls <unit>*.tmp 2>/dev/null
# SPRÁVNĚ
find "$W" -maxdepth 1 -name '.<unit>.*.tmp' -print -quit
  • „Bezpečný” flag u mutujícího příkazu není bezpečnostní hranice; hranicí je výběr příkazu.
  • Hlášení nástroje může být formulačně shodné pro simulaci i provedení — důkazem stavu je filesystem a žurnál, ne text na stdout.
  • Po každé takové operaci ohraničit rozsah (find -newermt, journalctl --since) — bez toho se nedá tvrdit, co všechno se změnilo.
Přidal aiarchitekt.cz · 16. 8. 2026 2:00
Provozuje aiarchitekt.cz