Lancer un script au démarrage sous Linux et BSD : quelle méthode pour quelle distribution ?

Rangez vos haches de guerre
Lancer un script au démarrage sous Linux et BSD : quelle méthode pour quelle distribution ?

Que l'on gère des serveurs sous Linux ou une machine plus classique, une question revient régulièrement : comment lancer un script au démarrage ? Mais du fait de la diversité des distributions, il n'y a pas de réponse simple à cette question. On distingue néanmoins quelques solutions communes. Tour d'horizon.

Créé il y a plus de 30 ans, le noyau Linux a donné naissance à un écosystème large et diversifié, allant de distributions ultra-légères pour systèmes embarqués à celles visant des serveurs, PC de bureau ou pour joueurs. Il y en a pour tous les goûts. Si toutes utilisent des briques communes, elles se distinguent par certains choix.

La diversité est une chance (quand on respecte l'autre)

L'un d'entre eux concerne le système d'initialisation (init) qui est le plus souvent un enchevêtrement d'outils legacy, utilisés de manière historique, et de décisions plus récentes. Avec un zeste de couche de compatibilité pour s'assurer que l'ensemble continue de fonctionner, sans perdre les administrateurs système et autres utilisateurs, qui ont leurs habitudes. Dès lors, il existe parfois différentes manières de lancer un script au démarrage.

Mais il n'est pas simple de savoir laquelle utiliser, dans quel but et avec quelles contraintes. Surtout que si l'on navigue entre différentes distributions, chacune aura ses propres règles. Sans parler de ceux qui livrent des conseils assez péremptoires en la matière. Car le sujet de « l'init » est devenu pour certains une quasi-guerre de religion, avec ses préceptes et ses adeptes. Au point de mener à Devuan, fork de Debian sans systemd.

C'est donc éloigné de ces considérations que nous avons cherché à comprendre comment lancer un script au lancement de différentes distributions. Car la diversité du monde Linux ne doit pas être une source de conflit, mais bien de complémentarité dans les approches, permettant de répondre à différents besoins.

Au commencement était System V

Tout comme Linux, init est issu du monde UNIX. C'est le premier programme lancé, prenant en charge toute la phase d'initialisation et ce qui en découle. Son fonctionnement, issu du Unix System V d'AT&T et ses niveaux d'exécution, est d'ailleurs l'un des points de divergence historique avec BSD (Berkley Standard Distribution).

L'approche « SysV » a longtemps été majoritaire au sein des différentes distributions Linux. Mais au fil des années, plusieurs initiatives ont été lancées pour le remplacer et/ou l'améliorer comme nous le verrons un peu plus loin. On trouve néanmoins toujours des traces de sa manière de fonctionner, plutôt basique.

Historiquement, la première chose que fait init (/sbin/init) est de lire le fichier /etc/inittab qui contient une série de scripts (run commands ou rc) à lancer selon le niveau d'exécution demandé puis passe à la phase de connexion ou du gestionnaire de fenêtres. Cette série de scripts est placée dans le dossier /etc/init.d avec des liens symboliques selon le niveau d'exécution du type /etc/rc0.d, /etc/rc1.d, /etc/rc2.d, etc. 

Pour connaître le niveau d'exécution (run level) de votre système vous pouvez utiliser l'un de ces commandes :

who -r
runlevel

rc.local : la vieille habitude

Dans la liste de scripts exécutés, il y en a un plutôt spécial : rc.local. Chargé après tous les autres il a été pensé pour permettre aux administrateurs système d'exécuter des actions une fois que le reste du système est prêt. On en trouve trace tant dans des documentations de distributions Linux que de BSD.

Son emplacement n'était d'ailleurs pas toujours le même, mais à travers le temps un (presque) consensus a émergé, consistant à le placer à la racine du dossier /etc. Il doit par contre toujours respecter quelques règles simples : être exécutable, commencer et finir comme un script.

Ainsi, il suffit le plus souvent de l'éditer ou de le créer (via nano, mais vous pouvez utiliser l'éditeur de votre choix) : 

sudo nano /etc/rc.local

Puis de l'enregistrer (CTRL+X). Dans notre exemple du jour, il ressemble à ça :

#!/bin/bash -e

date >> /var/log/boot_history.log

exit 0

Il nous permet de créer un fichier de log dans lequel nous enregistrons la date et l'heure de chaque démarrage de la machine. Notez que nous n'avons pas à utiliser sudo puisque ce script sera exécuté depuis l'utilisateur root. On peut d'ailleurs restreindre son accès au maximum et ne l'autoriser qu'à celui-ci :

chown root:root /etc/rc.local
chmod 700 /etc/rc.local

Droits utilisateurs avec chmod : quelques rappels

La première des deux commandes ci-dessus permet d'indiquer que le fichier appartient à l'utilisateur root et aux utilisateurs du groupe root. La seconde commande permet de définir les droits d'accès via trois chiffres : ceux de l'utilisateur propriétaire, du groupe, puis de tous les autres utilisateurs.

Ils sont le résultat de l'addition de trois chiffres : 

  • 4 : droits en lecture (r)
  • 2 : droits en écriture (w)
  • 1 : droits d'exécution (x)

Ainsi, l'attribut 700 donne tous les droits au propriétaire, aucun aux autres utilisateurs. On pourrait par exemple donner l'accès en lecture au groupe et aux autres utilisateurs (744) ou leur permettre également de l'exécuter (755).

Parfois le besoin de rester basique, simple

Si tout s'est bien passé, redémarrez le système (sudo reboot) vous devriez voir apparaitre le fichier de log qui sera désormais rempli avec une nouvelle ligne à chaque (re)démarrage :

cat /var/log/boot_history.log

Selon nos constatations cette méthode fonctionne avec la plupart des systèmes, même certains qui se sont éloignés de l'init de System V (nous verrons pourquoi ensuite), même lorsqu'ils ont fait le choix de systemd. Cette méthode est donc à privilégier quand vous avez simplement besoin d'effectuer une action, sans qu'elle n'ait à être maintenue dans le temps via un service avec un cycle complet (arrêt, redémarrage, analyse du statut, etc.)

Votre chemin, vous devez décider

Pour nombre de distributions, le mode de fonctionnement de l'init issu de System V n'était plus adapté. Elles ont donc cherché une solution plus « moderne », avec des avantages en termes de performance, de stabilité, de sécurité ou de gestion des dépendances. Comme souvent, Ubuntu s'est essayé à une solution maison avant de l'abandonner : Upstart. Elle est passée à System Deamon (systemd), désormais majoritaire.

Fedora et openSUSE avaient sauté le pas vers 2011, favorisant cette solution développée au départ par Lennart Poettering, alors développeur chez Red Hat. D'autres ont progressivement fait le même choix : Arch et ses principaux dérivés, puis Debian et Ubuntu. Tous le présentent comme une brique logicielle adaptée aux besoins des administrateurs, tout en conservant une compatibilité avec l'existant.

Pour ses détracteurs, systemd a surtout complexifié les choses en cherchant à éviter l'utilisation de scripts de lancement en devenant un élément central visant à simplifier les choses. Mais au point que de nombreux éléments dépendent de lui, dépassant le cadre qui devrait être celui d'un outil d'initialisation du système.

Certaines distributions se sont ainsi tournées vers l'OpenRC de Gentoo ou runit. Elles laissent même parfois le choix. C'est le cas du très modulaire Gentoo qui gère différents outils d'initialisation (dont SysVinit et systemd), mais aussi de Devuan qui est né de la volonté d'un Debian sans Systemd et défend « l'init freedom ». On peut ainsi y utiliser OpenRC, runit dans le choix proposé à l'installation, mais aussi sinit, s6 ou Sheperd issu de GNU/Hurd.

En 2015, Linux-FR avait fait un travail de synthèse des arguments pour ou contre systemd. Gentoo propose une comparaison des fonctionnalités de différents outils d'init dans sa documentation.

rc-local.service : opération compatibilité

Face à ce rejet d'une partie de la communauté et du bouleversement des habitudes, des solutions de compatibilité ont été mises en place, dont rc-local.service. Un service (ou démon) qui surveille si un script exécutable rc.local est présent au démarrage de la machine. Si c'est le cas, il est lancé. Sinon, le service est inactif.

Comme c'est trop souvent le cas, les différentes distributions ne se sont pas entendues sur l'emplacement du fichier à surveiller. C'est donc parfois /etc/rc.local. Parfois /etc/rc.d/rc.local. Pour savoir ce qu'il en est pour votre distribution, il suffit de demander le statut du service, dont la description précise cette information. 

Ainsi, sous Debian vous obtenez : 

$ systemctl status rc-local.service
● rc-local.service - /etc/rc.local Compatibility

Et sous Fedora :

$ systemctl status rc-local.service
○ rc-local.service - /etc/rc.d/rc.local Compatibility

Dans les deux cas, la méthode est la même que pour les distributions avec SysVinit : éditez le contenu du script, rendez-le exécutable. Vous n'avez rien d'autre à faire, à chaque démarrage il sera lancé automatiquement.

Créez un service pour systemd

Plutôt que des scripts, systemd introduit la notion d'unités, dont les services sont un type en particulier. Dans le cadre de cet article, nous ne nous intéresserons qu'à eux. Ils prennent la forme d'un fichier décrivant leur but et leur fonctionnement, ils sont stockés dans le dossier /etc/systemd/system/ et n'ont pas besoin d'être exécutables.

Pour lister tous les services du système (« q » pour quitter) :

systemctl list-units --type service --all

Pour disposer d'un équivalent à notre script rc.local ci-dessus, on peut ainsi créer un service simple :

sudo nano /etc/systemd/system/boot-logger.service

Notez que dans un billet de blog, Red Hat recommande de le placer dans /usr/local/lib/systemd/system et de créer un lien symbolique depuis /etc/systemd/system. Faites selon votre convenance

On y ajoute le contenu suivant puis on enregistre (CTRL+X) :

[Unit]
Description=Boot date & time logger

[Service]
Type=simple

ExecStart=/usr/bin/date
StandardOutput=append:/var/log/boot-date.log

[Install]
WantedBy=multi-user.target

Un service est toujours composé de ces trois sections. Ici, on décrit simplement ce à quoi il sert, l'exécutable à lancer, le fichier de log où ajouter le résultat. Notez que l'on utilise systématiquement des chemins complets pour les fichiers et exécutables. On peut bien entendu complexifier un service en décrivant des actions à exécuter lorsqu'il est arrêté, redémarré, un dossier d'exécution et un utilisateur à utiliser pour le lancer, etc.

La documentation de Red Hat Enterprise Linux (en français pour la version 7) est assez complète en la matière.  

Dépendances et conditions d'activation 

L'un des avantages de systemd étant sa gestion des dépendances, on pourrait par exemple attendre que le réseau soit actif pour lancer l'action. Pour cela, on ajoute une cible au paramètre After de la section Unit :

After=network.target

Pour lister toutes les cibles disponibles sur le système :

systemctl list-units --type target --all

On peut aussi vérifier qu'un fichier existe et est exécutable, si on compte l'utiliser dans le cadre du service :

ConditionFileIsExecutable=/usr/bin/local/script.sh

Une fois le fichier du service créé, on modifie les droits d'accès : 

sudo chmod 644 /etc/systemd/system/boot-logger.service 

On peut ensuite activer le service ou demander à connaitre son statut :

sudo systemctl enable boot-logger
sudo systemctl status boot-logger

Une fois activé il sera démarré à chaque lancement du système, l'action sera exécutée puis il sera mis en pause.

Crontab et @reboot : l'alternative simple

Si vous ne voulez pas dépendre de la méthode d'initialisation du système, vous pouvez opter pour cron. Pour rappel cet outil permet d'exécuter des tâches de manière récurrente, mais aussi à chaque démarrage. Pour cela, il faut lancer l'édition de son fichier de configuration (crontab), gérée par utilisateur :

crontab -e

Cela vous permet d'éditer celle de l'utilisateur actuel, mais si vous avez besoin d'exécuter des tâches avec les droits administrateur (root) comme c'est notre cas pour écrire dans /var/log/, vous pouvez utiliser sudo :

sudo crontab -e

Vous pourrez alors y ajouter la ligne suivante :

@reboot date >> /var/log/boot_history.log

Seul problème de cette solution : selon la distribution, elle s'exécutera plus ou moins tôt dans le démarrage, ce qui peut poser problème si vous avez besoin de certains éléments comme le réseau par exemple. Mais elle a l'avantage de fonctionner avec Arch et ses dérivées qui n'ont pas de service de compatibilité avec rc.local.

init alternatifs, FreeBSD : quelle solution choisir ?

Selon nos différents essais avec des distributions ne se reposant pas sur systemd et FreeBSD, il est toujours possible d'utiliser le fichier /etc/rc.local ou @reboot dans crontab pour lancer un script ou une commande au démarrage. Ce sont donc des solutions à privilégier pour des besoins simples.

Il est aussi possible de créer des services sous la forme de scripts RC, comme indiqué dans la documentation de FreeBSD. Gentoo détaille le fonctionnement d'OpenRC, Alpine donne l'exemple d'une création de service simple. Pour runit vous pouvez vous tourner vers la documentation de Void Linux qui l'utilise par défaut.

Lancer une application à la connexion de l'utilisateur

Il existe une dernière possibilité : lancer un script ou une application lorsque l'utilisateur se connecte via l'interface graphique ou un accès SSH par exemple. Dans le premier cas, cela dépendra de l'environnement. Cinnamon, GNOME, KDE, LXDE, MATE, XFCE et tous les autres proposent une telle fonctionnalité dans leurs paramètres.

Pour les scripts, cela dépend du shell. Bash permet par exemple d'éditer le fichier .bashrc pour y ajouter toutes les commandes de votre choix, et donc le lancement de script. C'est ici que l'on place par exemple les alias à créer comme nous l'avions déjà évoqué dans un précédent article. Par exemple :

alias fup="sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove"

Cette commande permet de lancer la mise à jour du système dans les distributions Linux exploitant APT via la commande fup. Le script .bash_logout sera pour sa part exécuté à la déconnexion. Pour ZSH, il y a un équivalent à la connexion avec le fichier .zshrc, etc. Vous pouvez aussi ne pas dépendre du shell utilisé avec le dossier /etc/profile.d/. Tout script exécutable qui y est placé est lancé à chaque chargement d'un shell, quel qu'il soit. Veillez par contre à ne pas préciser le shell à utiliser en tête de fichier (#/bin/bash par exemple).

Vous n'avez pas encore de notification

Page d'accueil
Options d'affichage
Actualités
Abonné
Des thèmes sont disponibles :
Thème de baseThème de baseThème sombreThème sombreThème yinyang clairThème yinyang clairThème yinyang sombreThème yinyang sombreThème orange mécanique clairThème orange mécanique clairThème orange mécanique sombreThème orange mécanique sombreThème rose clairThème rose clairThème rose sombreThème rose sombre

Vous n'êtes pas encore INpactien ?

Inscrivez-vous !