Vous êtes-vous déjà demandé comment modifier un script systemd sous Debian et ces dérivés ?
Cela se fait à l’aide de la commande systemctl edit
qui assure le redémarrage, et ne modifie pas le code venant du paquet du mainteneur. Cela vous évitera bien des questions lors de la mise à jour de votre système. De plus, cela met en évidence vos modifications.
systemctl edit NOM_DU_SERVICE
Prenons le script BIND9
systemd comme exemple. Un brouillon des meilleures pratiques chez ISC stipule que, de par sa conception et pour des raisons de sécurité, le mode de défaillance le plus courant pour BIND est la fin intentionnelle du processus lorsque celui-ci rencontre un état incohérent. Un processus d’alerte automatique capable de redémarrer intelligemment BIND est recommandé…
Suite à cette recommandation, on veut s’assurer que BIND est redémarré automatiquement en cas d’échec, ce qui n’est pas le cas par défaut. Pour que systemd redémarre un service de manière automatique, vous devez ajouter la directive Restart=
au fichier de configuration du service.
Pour comprendre il faut s’attarder sur la documentation de 5ᵉ niveau de systemd :
man 5 systemd.service
On y trouve ce tableau très intéressant à propos de la clause Restart=
:
Comme le DNS est très important dans mon infrastructure, j’ai choisi Restart=always
ce qui permet d’assurer un redémarrage du service même s’il est sorti avec succès.
Notez que le service ne redémarrera pas automatiquement si vous utilisez service bind9 stop
ou systemctl stop bind9
.
Pour ajouter Restart=
au problème de fichier de service, utilisons la solution prévue par systemd
:
sudo systemctl edit bind9.service
et dans l’éditeur qui a été ainsi ouvert, il suffit d’écrire :
[Service]
Restart=always
Enregistrez et quittez le fichier. Jetez un coup d’œil au service à l’aide de :
systemctl status bind9
● bind9.service - BIND Domain Name Server
Loaded: loaded (/lib/systemd/system/bind9.service; enabled; vendor preset: enabled)
Drop-In: /etc/systemd/system/bind9.service.d
└─override.conf
Active: active (running) since Tue 2019-07-30 11:20:42 CEST; 23s ago
Docs: man:named(8)
Process: 9453 ExecStop=/usr/sbin/rndc stop (code=exited, status=0/SUCCESS)
Main PID: 9458 (named)
Tasks: 7 (limit: 4915)
CGroup: /system.slice/bind9.service
└─9458 /usr/sbin/named -f -4 -u bind
Vous remarquerez que la section Drop-In contient une référence vers override.conf. Choisissons le PID 9458 (tache named) et tuons le service à l’aide de :
sudo kill -9 9458
Ensuite vérifions l’état du service avec systemctl status bind9
:
● bind9.service - BIND Domain Name Server
Loaded: loaded (/lib/systemd/system/bind9.service; enabled; vendor preset: enabled)
Drop-In: /etc/systemd/system/bind9.service.d
└─override.conf
Active: active (running) since Tue 2019-07-30 11:22:39 CEST; 34s ago
Docs: man:named(8)
Process: 11403 ExecStop=/usr/sbin/rndc stop (code=exited, status=1/FAILURE)
Main PID: 11408 (named)
Tasks: 7 (limit: 4915)
CGroup: /system.slice/bind9.service
└─11408 /usr/sbin/named -f -4 -u bind
Il a maintenant un nouveau PID.
La commande journalctl -xe
donne un peu plus de détails :
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Main process exited, code=killed, status=9/KILL
Jul 30 11:22:39 framboise10 rndc[11403]: rndc: connect failed: 127.0.0.1#953: connection refused
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Control process exited, code=exited status=1
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Unit entered failed state.
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Failed with result 'signal'.
Jul 30 11:22:39 framboise10 systemd[1]: bind9.service: Service hold-off time over, scheduling restart.
Jul 30 11:22:39 framboise10 systemd[1]: Stopped BIND Domain Name Server.
-- Subject: Unit bind9.service has finished shutting down
-- Defined-By: systemd
-- Support: https://www.debian.org/support
--
-- Unit bind9.service has finished shutting down.
Jul 30 11:22:39 framboise10 systemd[1]: Started BIND Domain Name Server.
-- Subject: Unit bind9.service has finished start-up
-- Defined-By: systemd
-- Support: https://www.debian.org/support
--
-- Unit bind9.service has finished starting up.
--
-- The start-up result is done.
Jul 30 11:22:39 framboise10 named[11408]: starting BIND 9.10.3-P4-Raspbian <id:ebd72b3> -f -4 -u bind
Notez que systemd a remarqué que BIND a été tué et l’a redémarré.
Bon c’est bien mais comment scripter cela ?
La solution est étonnamment simple…
tmpfile=$( mktemp )
cat <<EOF >"${tmpfile}"
[Service]
Restart=always
EOF
sudo env SYSTEMD_EDITOR="cp ${tmpfile}" systemctl edit bind9.service
Wouhai, bon avec quelques explications se sera plus simple :
Première étape, création d’un fichier temporaire, et on récupère le nom dans la variable tmpfile
:
tmpfile=$( mktemp )
La syntaxe $( xxx )
permet d’exécuter la commande xxx et prend comme résultat la sortie standard. On parle de sortie standard pour différencier les affichages qui ne sont pas des messages d’erreurs et qui ne seront pas pris en compte.
Ensuite on initialise le fichier, avec le contenu souhaité :
cat <<EOF >"${tmpfile}"
[Service]
Restart=always
EOF
Ici, la syntaxe cat <<EOF … EOF
permet de définir une zone de texte qui sera considérée comme une entrée standard, que l’on redirige vers le fichier à l’aide de >"${tmpfile}"
. La zone de texte commande après la ligne contenant <<EOF
et fini ajuste avant la ligne contenant EOF
Ensuite se trouve l’astuce. Pour comprendre, il faut savoir que systemctl
va regarder le contenu de la variable SYSTEMD_EDITOR
pour choisir le programme qui sera utilisé pour éditer le fichier. Le lancement de l’éditeur se fera à l’aide : commande_editeur nom_du_fichier_a_editer
.
En utilisant la syntaxe SYSTEMD_EDITOR="cp ${tmpfile}"
, on indique le l’éditeur est la commande cp ${tmpfile}
, commande qui sera complétée par le nom du fichier qu’ajoutera systemctl edit
.
sudo env SYSTEMD_EDITOR="cp ${tmpfile}" systemctl edit bind9.service
Pour être exhaustif sur la syntaxe de la ligne :
- La commande
sudo
indique que l’on souhaite avoir les privilèges administrateurs, - ensuite
env
précise que l’on définit une variable d’environnement (qui n’existera que pour la commande suivante), - Après on trouve la définition de la variable, suivant de la commande qui sera effectuée.
Concrètement j’utilise ce code à travers une fonction que j’ai nommée patch_systemd comme suit :
function patch_systemd {
local tmpfile=
tmpfile="$( mktemp )"
cat <<EOF >"${tmpfile}"
[Service]
Restart=always
EOF
sudo env SYSTEMD_EDITOR="cp ${tmpfile}" systemctl edit bind9.service
}
Il suffit d’appeler la fonction pour mettre à jour la configuration du service.
Références
- Documentation en français de Bind9 sur debian.org,
- Inspiré de [Overriding BIND systemd script in Debian]
https://jeanbruenn.info/2017/06/05/overriding-bind-systemd-script-in-debian/
(lien cassé), - Question stackexchange Using systemctl edit via bash script? (stackoverflow)
ᦿ