Přeskočit na obsah

Bash signal trap čte $? — po SIGTERM je 0 a rollback se přeskočí

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

Fail-closed orchestrační skript (změna credentialu: ALTER v DB → výměna configu → restart → verify) měl trap na ERR INT TERM EXIT a v handleru větvení „rc≠0 → rollback”. Testy poruch (vstříknuté selhání příkazu) procházely. Ale při SIGTERM/SIGINT uprostřed okna skript skončil exit 0, bez rollbacku, bez cleanupu — pomocné soubory se secrety zůstaly na disku a systém zůstal v půlce přepnutí. Flaky: projevilo se jen někdy, podle toho, kam signál dopadl.

Terminál
trap 'rc=$?; if [[ $rc -ne 0 ]]; then rollback; cleanup; fi; exit $rc' ERR INT TERM EXIT

Při selhání příkazu (ERR) je $? nenulové — větev funguje. Ale při signálu bash doručí trap až po dokončení právě běžícího příkazu a $? nese status toho posledního dokončeného — typicky úspěšného — příkazu, tedy 0. Handler vyhodnotí „úspěch”, rollback přeskočí a exit 0 zamaskuje, že běh byl zabit uprostřed kritického okna.

Druhá vrstva téže pasti: cesty něco || die (kde die volá exit 1) nefirují ERR trap (selhání je „ošetřené”) — bez EXIT trapu skončí selhání za point-of-no-return také bez rollbacku.

Oddělené handlery — signál je VŽDY přerušení, bez ohledu na $?:

Terminál
# INT/TERM: nikdy nečíst $? — vždy rollback, konvenční exit 143
trap 'trap - ERR INT TERM EXIT
rollback || true; cleanup || true
exit 143' INT TERM
# ERR + EXIT: tady $? smysl má (kryje i `něco || die` cesty)
trap 'rc=$?; trap - ERR INT TERM EXIT
if [[ $rc -ne 0 ]]; then rollback || true; cleanup || true; fi
exit $rc' ERR EXIT

Rollback musí být idempotentní (guard flag), protože handler může proběhnout vícekrát (explicitní volání + trap).

Jak se tomu vyvarovat v jiných systémech

Sekce “Jak se tomu vyvarovat v jiných systémech”
  • Detection: grep -n "trap 'rc=\$?" *.sh + zkontrolovat, zda signály sdílejí handler s ERR/EXIT. Testovací scénář: poslat skriptu SIGTERM uprostřed kritické fáze a ověřit, že rollback proběhl a exit ≠ 0.
  • Anti-pattern: jeden trap string pro ERR INT TERM EXIT s větvením podle $?; absence EXIT trapu u skriptů s cmd || die vzorem.
  • Lepší přístup: rehearsal s fault-injection musí obsahovat i signálový scénář (kill v každé fázi), ne jen selhání příkazů — právě signálová větev se jinak nikdy neotestuje.

Sister bugs / související

Sekce “Sister bugs / související”
  • Příbuzná past: set -e je uvnitř funkce volané v if/|| kontextu vypnuté — selhání uprostřed funkce pokračuje dál.
  • IFS=$'\n\t' rozbije word-splitting víceslovných konfigurací (CMD="sudo -u postgres psql" je pak jedno slovo) — smyčky a wrappery potřebují lokální IFS=' '.
Přidal aiarchitekt.cz · 4. 8. 2026 2:00
Provozuje aiarchitekt.cz