rcNG, l'anti systemd

Magazine
Marque
GNU/Linux Magazine
Numéro
156
Mois de parution
janvier 2013
Spécialité(s)


Résumé
À l'heure ou plusieurs distributions GNU/Linux sont sur le point de complexifier de façon irrémédiable (d'aucuns diront aberrante) leur processus d'amorce, les systèmes dérivés de BSD UNIX articulent leur mécanisme d'initialisation autour d'une cascade de scripts logiques, lisibles et contrôlables à souhait. Bien loin des usines à gaz que l'on voit germer de-ci [1] et de-là [2], rc(8) tel que nous le connaissons prend ses racines dans 4.0BSD (1980), et est devenu à travers les âges une machinerie bien huilée, devenu « rcNG » sous l'impulsion du Projet NetBSD.

Body

init-rc

/etc/rc en action. Pfff, c'est pourri ton truc, y'a même pas des [OK] verts fluo :(

1. Un peu d'histoire

Branchons la DeLorean sur 1980 et voyons à quoi ressemblait un /etc/rc dans 3BSD :

echo '' >/dev/console

date >/dev/console

echo entering rc >/dev/console

HOME=/

export HOME

echo clearing mtab >/dev/console

cp /dev/null /etc/mtab

echo mounting /usr on /dev/rp0g >/dev/console

/etc/mount /dev/rp0g /usr

echo preserving Ex temps and clearing /tmp >/dev/console

cd /tmp

(/usr/lib/ex3.2preserve -a; rm -f *)

cd /

echo starting update >/dev/console

/etc/update&

: echo clearing lock and starting printer >/dev/console

: rm -f /usr/lpd/lock

echo starting cron >/dev/console

/etc/cron&

: echo starting accounting >/dev/console

: /etc/accton /usr/adm/acct

: echo starting net daemon >/dev/console

: sh /usr/net/bin/start &

chmod 666 /etc/motd

echo leaving rc >/dev/console

*tousse* *tousse* ah ça oui, ça sent un peu le renfermé et on distingue mal les caractères à travers toutes ces toiles d'araignées. Mais tout de même, depuis ce script, 33 ans nous observent ! Et de confirmer que la voie était toute tracée en lisant le code source de init(8) d'alors, appelé init.vm et situé dans /etc :

[...]

char    shell[] = "/bin/sh";

char    getty[] = "/etc/getty.vm";

char    minus[] = "-";

char    runc[] = "/etc/rc";

[...]

runcom()

{

        register pid, f;

        pid = fork();

        if(pid == 0) {

                open("/", 0);

                dup(0);

                dup(0);

                execl(shell, shell, runc, (char *)0);

                exit(0);

        }

        while(wait((int *)0) != pid)

[...]

Nous y sommes, les prémices de rcNG.

En 1998, NetBSD apporte sa première pierre dans la rénovation des scripts de démarrage en introduisant le fichier /etc/rc.conf dans la version 1.3 de l'OS. Ce fichier ouvrait la voie à une plus fine configuration des services à démarrer, les conditions étant malgré tout réalisées dans /etc/rc, /etc/netstart et /etc/rc.lkm.

C'est en décembre 2000, 20 ans plus tard tout de même, que Luke Mewburn (oui, celui-là même à qui nous « devons » le drapeau orange...) révolutionne le design historiquement monolithique de rc hérité de 4.4BSD pour intégrer une petite révolution dans NetBSD 1.5 : rc.d(8)[3]. Cette nouvelle implémentation propose :

- Une mécanique d'ordre de démarrage des scripts indépendante des noms de fichiers (au contraire des SXXservice de l'init des UNIX System V).

- La possibilité d'intégrer des scripts tiers, issus par exemple de paquets.

- La manipulation de scripts autonomes pour gérer les services indépendamment.

- L'utilisation intensive du fichier /etc/rc.conf pour contrôler le comportement des services démarrés.

- Promouvoir la portabilité et la réutilisation du code (une vieille habitude chez NetBSD).

- Éviter les inutiles runlevels .

La nouvelle infrastructure est un véritable succès, simple mais souple, comme à son habitude, le Projet réalise une évolution dans la continuité et la philosophie propre aux UNIces BSD. Peu après cette release, ce sera au tour de FreeBSD d'intégrer ce nouveau dispositif à partir de sa version 5.0 en janvier 2003, sous le nom de rcNG[4].

2. Oil of Codaz

Les concepts et origines étant maintenant acquis, rentrons un peu plus dans le détail. Notre noyau s'en est remis à init(8) après avoir mis en place les briques essentielles au bon fonctionnement d'un système BSD UNIX multitâches. Nous articulerons notre explication sur des morceaux de code issus de NetBSD 6.0 :

Issu de sys/kern/init_main.c :

        /*

         * Create process 1 (init(8)). We do this now, as Unix has

         * historically had init be process 1, and changing this would

         * probably upset a lot of people.

         *

         * Note that process 1 won't immediately exec init(8), but will

         * wait for us to inform it that the root file system has been

         * mounted.

         */

        if (fork1(l, 0, SIGCHLD, NULL, 0, start_init, NULL, NULL, &initproc))

                panic("fork init");

init(8), dans le cas d'un démarrage en mode multi-utilisateurs, va invoquer notre fameux /etc/rc en argument de sh :

Issu de src/sbin/init/pathnames.h :

#define _PATH_RUNCOM    "/etc/rc"

Issu de src/sbin/init/init.c :

        switch ((pid = fork())) {

        case 0:

                (void)sigemptyset(&sa.sa_mask);

                sa.sa_flags = 0;

                sa.sa_handler = SIG_IGN;

                (void)sigaction(SIGTSTP, &sa, NULL);

                (void)sigaction(SIGHUP, &sa, NULL);

                setctty(_PATH_CONSOLE);

                argv[0] = "sh";

                argv[1] = _PATH_RUNCOM;

                argv[2] = (runcom_mode == AUTOBOOT ? "autoboot" : 0);

                argv[3] = 0;

La séquence d'initialisation va maintenant prendre la main ; en premier lieu, rc va interpréter le fichier /etc/rc.subr, qui regroupe les fonctions communes à la manipulation de scripts de démarrage. Comme exposé par Luke, la nouvelle infrastructure rc.d se doit d'être portable et de partager tout le code qui peut l'être, ainsi, tout script de contrôle de service doit s'assurer que rc.subr est bien chargé afin de ne pas réecrire inutilement du code factorisé et utilisé par l'ensemble de la chaîne.

Une fois les fonctions chargées, rc interprétera le fichier /etc/rc.conf qui indiquera la liste des services à démarrer ainsi que leurs éventuels paramètres. Cette vérification est réalisée par la fonction checkyesno, par exemple :

if ! checkyesno rc_configured; then

        echo "/etc/rc.conf is not configured. Multiuser boot aborted."

        exit 1

fi

Simple, efficace, élégant. Notez que toutes les opérations, succès comme échecs, sont enregistrées dans le fichier de log /var/run/rc.log, ce qui peut s'avérer fort pratique si vous avez activé la variable rc_silent apparue avec NetBSD 6.0 qui ordonne à rc de ne pas afficher de statut sur la sortie standard.

Les services de base bénéficient tous d'une configuration par défaut située dans /etc/defaults/rc.conf, ces variables sont bien entendu écrasées par les valeurs placées dans le fichier /etc/rc.conf, il est défendu de modifier des variables directement dans le fichier /etc/defaults/rc.conf !

Voici un exemple de rc.conf sur une machine de bureau, également utilisée pour mener quelques tests :

# $NetBSD: rc.conf,v 1.96 2000/10/14 17:01:29 wiz Exp $

#

# see rc.conf(5) for more information.

#

# Use program=YES to enable program, NO to disable it. program_flags are

# passed to the program on the command line.

#

# Load the defaults in from /etc/defaults/rc.conf (if it's readable).

# These can be overridden below.

#

if [ -r /etc/defaults/rc.conf ]; then

. /etc/defaults/rc.conf

fi

# If this is not set to YES, the system will drop into single-user mode.

#

rc_configured=YES

# Add local overrides below

#

wscons=YES

sshd=YES

pf=YES

powerd=YES

postfix=YES

dovecot=YES

dbus=YES

hal=YES

rpcbind=YES

famd=YES

avahidaemon=YES

xdm=YES

cupsd=YES

rpcbind=yes

nfs_client=yes

lockd=yes

statd=yes

nginx=YES

php_fpm=YES

mysqld=YES

estd=YES

estd_flags="-a -P -m 1500"

ntpdate=YES

ntpd=YES

smbd=YES

nmbd=YES

Bien évidemment, l'ordre des services dans cette configuration n'a aucune importance, car c'est l'exécutable rcorder(8) qui va avoir la charge d'ordonner le démarrage des services en analysant leurs interdépendances ; rcorder réalise cette tâche en se basant sur des mots-clés que nous trouvons dans les scripts de démarrage. Par exemple :

# PROVIDE: ppp

# REQUIRE: mountcritremote syslogd

# BEFORE: SERVERS

Dans ce cas, le rc-script fournit ppp et nécessite mountcritremote (montage des systèmes de fichiers possiblement distants) et syslog (démon syslogd lancé).

3. Anatomie d'un rc-script

À quel point l'infrastructure rcNG est-elle puissante, factorisée et facile à mettre en œuvre ? Jugez plutôt :

$ cat /etc/rc.d/apmd # apmd est un démon de monitoring de la gestion de l'énergie

#!/bin/sh

#

# $NetBSD: apmd,v 1.6 2004/08/13 18:08:03 mycroft Exp $

#

# PROVIDE: apmd

# REQUIRE: DAEMON

# BEFORE: LOGIN

$_rc_subr_loaded . /etc/rc.subr

name="apmd"

rcvar=$name

command="/usr/sbin/${name}"

load_rc_config $name

run_rc_command "$1"

Shocking, isn't it? Avec ces seules six lignes de shell, rc.d est capable des actions suivantes :

Usage: /etc/rc.d/apmd [fast|force|one](start stop restart rcvar status poll)

Mais quelle est cette sorcellerie ! Revoyons la scène au ralenti :

$_rc_subr_loaded . /etc/rc.subr

- On s'assure que les fonctions issues de rc.subr sont bien chargées, $_rc_subr_loaded vaut : lorsque c'est le cas, inhibant ainsi l'interprétation à suivre.

name="apmd"

- $name est la variable principale dans la mécanique rcNG, il s'agit du nom du script/service.

rcvar=$name

- La variable utilisée pour contrôler le script, elle peut avoir un nom différent de $name, mais c'est évidemment peu souhaitable pour des raisons de clarté

command="/usr/sbin/${name}"

- La commande effective, celle qui sera exécutée par le script

load_rc_config $name

- Chargement de la configuration liée au service dans /etc/rc.conf ou /etc/rc.conf.d/$name. Précisons que ces fichiers de configuration sont sourcés, il peut donc s'avérer pratique dans certains cas d'ajouter des commandes spécifiques à un service dans /etc/rc.conf.d/$name, comme une redéfinition de ulimit pour un service très gourmand en file descriptors

run_rc_command "$1"

- Exécution de la commande rc.d souhaitée.

Arrêtons-nous un instant sur cette dernière fonction, véritable centre de contrôle du service. La fonction run_rc_command comprend les actions standards start, stop, reload, restart, status, poll et rcvar par défaut, chacune de ces actions pouvant être surchargée d'un préfixe fast, force et one. Par exemple :

# /etc/rc.d/apmd onestart

aura pour effet de démarrer une fois le service même si ce dernier n'est pas explicitement activé dans le fichier /etc/rc.conf. Mais notre fonction est capable de gérer bien plus de situations, en effet, sans plus de programmation, run_rc_command sait tirer profit des variables suivantes :

- command_args, arguments optionnels à passer à la commande à exécuter ;

- command_interpreter, un nom d'interpréteur, typiquement lorsque le service est en réalité un script et que le PID à considérer est celui de l'interpréteur (python, perl , php...) ;

- extra_commands, la liste des commandes supplémentaires acceptées par le rc-script ;

- pidfile, rc.subr utilisera un fichier de PID pour vérifier l'existence du processus ;

- procname, si on souhaite donner un nom de processus à vérifier différent de $command ;

- ${name}_chroot, répertoire dans lequel on souhaite chroot'er le service ;

- ${name}_chdir, répertoire dans lequel on souhaite se déplacer avant d'exécuter $command ;

- ${name}_flags, des arguments supplémentaires à passer $command ;

- ${name}_env, variables d'environnement supplémentaires nécessaires à l'exécution de $command ;

- ${name}_nice, niveau de priorité (nice level) avec lequel démarrer $command ;

- ${name}_user, utilisateur avec lequel démarrer $command ;

- ${name}_group, groupe avec lequel démarrer $command ;

- ${name}_groups, groupes supplémentaires ;

- ${rc_arg}_cmd, surcharge le comportement par défaut pour la commande passée en argument au script, par exemple start_cmd=“start_service” appellera la fonction (à définir) start_service au lieu de simplement invoquer $commmand ;

- ${rc_arg}_precmd, commande à invoquer avant l'action passée en argument au script ;

- ${rc_arg}_postcmd, commande à invoquer après l'action passée en argument au script ;

- required_dirs, répertoires dont il faudra vérifier l'existence avant de démarrer le service ;

- required_files, fichiers dont il faudra vérifier l'existence avant de démarrer le service ;

- required_vars, variables dont il faudra vérifier l'existence avant de démarrer le service.

Ces variables sont toutes utilisables dans le rc-script et les variables ${name}_* sont en général définies dans /etc/rc.conf ; par exemple, pour signifier au démon named de chrooter dans /var/chroot/named avant de démarrer :

$ grep ^named /etc/rc.conf

named=YES

named_chrootdir="/var/chroot/named"

Autre exemple plus complet, l'initialisation de variables particulières dans le script de démarrage du démon mysqld :

# [...]

name="mysqld"

rcvar=${name}

command="/usr/pkg/bin/mysqld_safe"

procname="/usr/pkg/sbin/${name}"

: ${mysqld_user:=mysql}

: ${mysqld_group:=mysql}

: ${mysqld_datadir:=/var/mysql}

extra_commands="initdb"

initdb_cmd="mysqld_initdb"

start_precmd="mysqld_precmd"

start_cmd="mysqld_start"

# [...]

Ici, si on n'a pas spécifié d'utilisateur, groupe et répertoire de travail dans le fichier /etc/rc.conf, alors on renseigne mysqld_user, mysqld_group et mysqd_datadir par des valeurs par défaut.

On ajoute au script une fonction supplémentaire, initdb, réalisée par la fonction mysqld_initdb, on demande explicitement d'exécuter la fonction mysqld_precmd avant le démarrage du service et on écrase la fonction de démarrage avec la fonction mysqld_start.

À l'issue de ce chapitre, et munis de ces seuls exemples, vous disposez de toutes les connaissances nécessaires et suffisantes à créer à peu près n'importe quel type de script rcNG.

4. Portabiliquoi ?

Au contraire des bloatware [1] dont je vous entretenais au début de cet article, nous avons pu constater que l'infrastructure rc.d se veut hautement portable ; n'utilisant aucune spécificité de son système d'exploitation d'origine, ce mécanisme de démarrage peut s'intégrer dans n'importe quel OS disposant d'un shell « standard » [5]. L'unification de ce dispositif de démarrage nous assure que les services de base comme les logiciels tierce partie bénéficient du même séquencement et permet en outre de s'appuyer sur les très nombreux exemples disponibles dans le système initial.

L'argumentaire en vogue prétend que faire dépendre le démarrage de son système d'exploitation d'une cascade de scripts shell induit un temps de démarrage abominablement long ; mon poste de travail sous NetBSD 6.0[6] démarre en approximativement 30 secondes, depuis le bootloader jusqu'à xdm, je ne suis pas exactement friand des concours de ce type, mais le ratio flexibilité/temps d’exécution me semble parfaitement honorable.

Liens

[1] http://www.freedesktop.org/wiki/Software/systemd

[2] http://upstart.ubuntu.com/

[3] http://static.usenix.org/event/usenix01/freenix01/full_papers/mewburn/mewburn_html/

[4] http://www.freebsd.org/releases/5.0R/relnotes.html

[5] http://pubs.opengroup.org/onlinepubs/009695399/utilities/sh.html

[6] http://imil.net/wp/2012/08/22/6-month-later/




Article rédigé par

Par le(s) même(s) auteur(s)

smolBSD : UNIX façon « Ikea »

Magazine
Marque
Linux Pratique
Numéro
141
Mois de parution
janvier 2024
Spécialité(s)
Résumé

Dans les séries américaines, on appelle ce genre d’histoire un crossover, les premières occurrences ont démarré dans Linux Pratique, puis une partie plus profonde sur l’amincissement d’un noyau NetBSD, pas nécessairement utile pour la finalité de notre produit, a fait son apparition dans GNU/Linux Magazine. Aujourd’hui, nous allons apprendre à construire un système BSD UNIX, NetBSD, From Scratch.

SmolBSD : un système UNIX de 7 mégaoctets qui démarre en moins d’une seconde

Magazine
Marque
GNU/Linux Magazine
Numéro
265
Mois de parution
septembre 2023
Spécialité(s)
Résumé

Que de racolage en si peu de mots. Et pourtant si, c’est bien la promesse de cet article, comment parvenir à construire un système d’exploitation fonctionnel en moins de… 10 mégabits. Quelle est cette sorcellerie ? En utilisant une fonctionnalité prévue, mais pas utilisée à cet escient par le noyau NetBSD, nous allons lui faire subir un régime drastique !

Les derniers articles Premiums

Les derniers articles Premium

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 125 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous