systemd : Créer des services, timers (unités)

systemd




Introduction



Dans cet article, on va voir comment créer une unité systemd et comment se compose celle-ci.


Généralités



Rappels systemd



Un service est un programme qui est exécuté en tache de fond (sans interaction directe avec l'utilisateur). A noter qu'un service peut être appelé également démon.
Chez systemd, on ne parle pas de service, mais d'unité.
Il existe des unités service, comme on l'entend avec OpenRC ou du temps de sysVinit, mais il existe d'autres unités telles que "automount", "path", "mount", "target", "timer", "wants" et "socket".

Les unités du système sont créées dans /usr/lib/systemd/system
Les unités créées personnelles seront à placer dans /etc/systemd/system

Une fois une unité créée ou modifiée, il faut relancer le démon systemd :

Code BASH :
systemctl --system daemon-reload


Types d'unités



J'ai listé ci-dessus les différentes unités possibles. Il y a donc :
- service : pour un service/démon
- socket : pour une socket réseau (de tous types : UNIX, fichier ...)
- mount : pour un système de fichiers (exemple : home.mount), /etc/fstab est aussi utilisé sur le système
- swap : comme mount mais pour les partitions d'échanges
- automount : comme mount mais pour un système de fichiers monté à la demande
- device : pour un périphérique
- timer : pour l'activation basée sur une date
- path : pour l'activation basée sur des fichiers ou des répertoires ;
- target : macro-unité qui permet de grouper plusieurs unités (exemple : multi-user.target pour définir une cible) : Ce sont les niveaux d'exécutions de l'ancien système d'init.

Les services



Types de services



Du côté des services, il existe plusieurs types de service. C'est défini dans le fichier de configuration par Type= avec comme valeur possible : simple, forking, oneshot, dbus, notify.

Nous allons rester simple et ne traiter que les cas suivants :

- Service simple : C'est le type par défaut. Il lance un processus principal. Le créateur du service doit s'assurer de créer les canaux de communication ou de lancer les processus avant le lancement du dit service. Systemd ne se préoccupe pas de la fin de l'exécution du service pour traiter d'autres unités.
- Service oneshot : Le fonctionnement est similaire au type simple. Cependant, systemd attend que le processus soit terminé avant de continuer ses traitement. (Fonctionnement similaire des services sysVinit, au rc.local)
- Service forking : Ce service lance un processus père, qui créera un processus fils. Le processus parent s'arrête une fois le service complètement démarré (canaux de communication inclus). Le processus fils tourne tant que le service est démarré. Systemd traite les autres unités une fois le processus père précédemment décrit est terminé. Par analogie, ce sont ainsi que fonctionnent les scripts unix traditionnels.

Il existe d'autres types, tel que notify, et dbus par exemple non abordés ici.


Syntaxe d'un service



Pour les services voici une structure classique, avec quelques options facultatives aussi :

Code BASH :
[Unit]
Description=
After=
ConditionPathExists=
 
[Service]
Type= 
ExecStartPre=
ExecStart=
ExecStop=
ExecStopPost=
RemainAfterExit=
Restart=
 
[Install]
WantedBy=
 


On a donc 3 sections :
- La section Unit permet de décrire le service, de vérifier des prérequis.
- La section Service définit le type de service, et ce qu'il va faire
- La section Install s'occupe des circonstances et des déclencheurs

Dans la section Unit, on retrouve :
- Description : Description du service
- After : Doit se lancer après un processus donné (exemple : After=network.target)
- ConditionPathExists : Sera utilisé si le chemin existe (facultatif)

Dans la section Service, on retrouve :
- Type : Le type de service (simple, forking, oneshot, ...)
- ExecStartPre : Commande à exécuter avant le démarrage du service (facutatif)
- ExecStart : Commande de service lorsque start est invoqué
- ExecStop : Commande de service lorsque stop est invoqué (facultatif)
- ExecStopPost : Commande à exécuter après l'arrêt du service (facultatif)
- RemainAfterExit : yes = Quand ExecStart est terminée, le service est considéré comme toujours lancé. (utile pour oneshot) / no = Ne doit pas considérer que le service est actif lorsque le processus neo4j s'est terminé normalement. (facultatif)
- Restart : Permet de redémarre le service (on-failure = après un plantage) (facultatif)

Dans la section Install, on retrouve :
- WantedBy : Dans quel cadre le service s'exécute ( multi-user.target = mode multi-utilisateurs) (facultatif)


Exemples de services



Exemple de script permettant de restaurer la fonction du fichier rc.local :

Code BASH :
[Unit]
Description=Exécuter le fichier rc.local
ConditionFileIsExecutable=/etc/rc.local
After=network.target
[Service]
Type=oneshot
ExecStart=/etc/rc.local
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target


Voici un exemple de service utilisé pour gérer un script de parefeu :

Code BASH :
[Unit]
Description=Exécuter le fichier rc.local
ConditionFileIsExecutable=/etc/rc.local
After=network.target
[Service]
Type=oneshot
ExecStart=/root/iptables-perso.sh
ExecStop=/root/iptables-flush.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target



Services modèles



Parfois, vous pourrie avoir besoin de créer des services modèles. Un service modèle est un service qui peut recevoir un paramètre en argument.
Ils seront nommés sur cette syntaxe : leservice@.service

Lorsque vous démarrez le service, ce qui est derrière l'arobase est un argument passé au service.

Il s'écrit de la même manière qu'un service classique, l'argument passé à l'arobase est récupéré par la variable %i

Exemple avec un service vnc@.service

Dans la ligne ExecStart on va passer l'argument %i
Code BASH :
ExecStart=/usr/bin/vncserver %i


Ainsi, quand je ferai
Code BASH :
systemctl start vnc@:1.service


La commande lancée sera :
Code BASH :
/usr/bin/vncserver :1


Et me lancera donc un serveur VNC sur l'affichage :1.

Vous pouvez regarder les services composés ainsi sur votre système via

Code BASH :
find /usr/lib/systemd/system -iname '*@*.service'


Les timers ou services périodiques



Qu'est ce qu'un timer



Les timers sont des fichiers de programmation qui vont se charger de lancer des services à intervalles réguliers.

Quand vous créez un timer, il doit avoir l'extension .timer. Un service .service du même nom doit exister. (le service est souvent un service minimaliste, de type oneshot, permettant de lancer une commande)

Les timers «pourraient presque» remplacer le planificateur de tâches cron.

Syntaxe d'un timer



Un timer possède une section Unit et Install comme un service.
Cependant la section Service n'existe pas et s'appelle Timer :

Code BASH :
[Unit]
Description=
[Timer]
OnBootSec=
OnUnitActiveSec=
OnCalendar=
Persistent=
[Install]
WantedBy=timers.target


Dans la section Timer, on peut retrouver :
- OnBootSec : Démarre le service du même nom que le timer après X secondes
- OnUnitActiveSec : Répétition toutes les X secondes tant que le système est en fonctionnement
- OnCalendar : Quand lancer le timer (voir ci-dessous)
- Persistent : yes = Si le système était à l'arrêt pendant le déclenchement, le lancement raté est rattrapé (utilisé avec OnCalendar)

Pour les valeurs en Secondes, on peut utiliser des suffixes tels que min, h, d et w pour minutes, heures, jours et semaines.

Pour la valeur OnCalendar :
- daily = à minuit chaque jour
- weekly = tous les lundis à minuit
- DayOfWeek Year-Month-Day Hour:Minute:Second Au format attendu

Exemples :
- OnCalendar=*-*-* 1:00:00 : Tous les jours à 1h du matin
- OnCalendar=Sun *-*-* 12:00:00 : Tous les dimanches à midi
- OnCalendar=*-*-1 00:20:00 : Tous les premiers du mois à 00h20
- OnCalendar=Mon *-*-1..7 6:00:00 ; Du 1 au 7 du mois, qui soit un lundi, à 6h (traduisez le premier lundi du mois à 6h)

Côté syntaxe :
- * : Toutes les occurrences
- 1 : Occurrence 1
- 1..7 : Occurrences de 1 à 7
- 0,12 : Occurrences 0 et 12

Plus d'exemples avec les OnCalendar des services installés sur le système :

Code BASH :
grep OnCalendar -r /usr/lib/systemd/system/


Exemples de timers



Voici un exemple du timer logrotate.timer (qui va lancer à intervalle régulier logrotate.service) :

Code BASH :
[Unit]
Description=Daily rotation of log files
[Timer]
OnCalendar=daily
AccuracySec=1h
Persistent=true
[Install]
WantedBy=timers.target