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 : 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 !

La grande migration, Épisode II

Magazine
Marque
Linux Pratique
Numéro
138
Mois de parution
juillet 2023
Spécialité(s)
Résumé

Dans l’épisode précédent [1], nous avons posé les premières briques d’une infrastructure d’auto-hébergement : vm-bhyve comme solution de virtualisation, sous FreeBSD donc, Wireguard comme gestionnaire de tunnel, une petite instance t4g.nano, 2 cœurs ARM64 et 512M de RAM chez AWS et un premier succès de déplacement de machine virtuelle hébergeant un simple serveur web d’un serveur dédié vers notre infrastructure personnelle. Nous allons voir maintenant comment migrer en douceur une machine virtuelle concentrant les services de base d’une architecture à l’autre.

La grande migration, Épisode I

Magazine
Marque
Linux Pratique
Numéro
137
Mois de parution
mai 2023
Spécialité(s)
Résumé

Il arrive parfois un jour où, ça y est, vous avez pris votre décision, le courage et l’envie sont là, c’est le moment : on va migrer. D’une ancienne technologie à une plus récente, par impératif professionnel, personnel, parce que c’est plus cohérent dans vos nouvelles coutumes, vous voyez exactement de quoi je parle, on frappe dans ses mains, on regarde le chantier, et on dit à voix haute : c’est parti.

Les derniers articles Premiums

Les derniers articles Premium

De la scytale au bit quantique : l’avenir de la cryptographie

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

Imaginez un monde où nos données seraient aussi insaisissables que le célèbre chat de Schrödinger : à la fois sécurisées et non sécurisées jusqu'à ce qu'un cryptographe quantique décide d’y jeter un œil. Cet article nous emmène dans les méandres de la cryptographie quantique, où la physique quantique n'est pas seulement une affaire de laboratoires, mais la clé d'un futur numérique très sécurisé. Entre principes quantiques mystérieux, défis techniques, et applications pratiques, nous allons découvrir comment cette technologie s'apprête à encoder nos données dans une dimension où même les meilleurs cryptographes n’y pourraient rien faire.

Les nouvelles menaces liées à l’intelligence artificielle

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

Sommes-nous proches de la singularité technologique ? Peu probable. Même si l’intelligence artificielle a fait un bond ces dernières années (elle est étudiée depuis des dizaines d’années), nous sommes loin d’en perdre le contrôle. Et pourtant, une partie de l’utilisation de l’intelligence artificielle échappe aux analystes. Eh oui ! Comme tout système, elle est utilisée par des acteurs malveillants essayant d’en tirer profit pécuniairement. Cet article met en exergue quelques-unes des applications de l’intelligence artificielle par des acteurs malveillants et décrit succinctement comment parer à leurs attaques.

Migration d’une collection Ansible à l’aide de fqcn_migration

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

Distribuer du contenu Ansible réutilisable (rôle, playbooks) par l’intermédiaire d’une collection est devenu le standard dans l’écosystème de l’outil d’automatisation. Pour éviter tout conflit de noms, ces collections sont caractérisées par un nom unique, formé d’une espace de nom, qui peut-être employé par plusieurs collections (tel qu'ansible ou community) et d’un nom plus spécifique à la fonction de la collection en elle-même. Cependant, il arrive parfois qu’il faille migrer une collection d’un espace de noms à un autre, par exemple une collection personnelle ou communautaire qui passe à un espace de noms plus connus ou certifiés. De même, le nom même de la collection peut être amené à changer, si elle dépasse son périmètre d’origine ou que le produit qu’elle concerne est lui-même renommé.

Les listes de lecture

8 article(s) - ajoutée le 01/07/2020
Découvrez notre sélection d'articles pour faire vos premiers pas avec les conteneurs, apprendre à les configurer et les utiliser au quotidien.
11 article(s) - ajoutée le 02/07/2020
Si vous recherchez quels sont les outils du DevOps et comment les utiliser, cette liste est faite pour vous.
8 article(s) - ajoutée le 02/07/2020
Il est essentiel d'effectuer des sauvegardes régulières de son travail pour éviter de perdre toutes ses données bêtement. De nombreux outils sont disponibles pour nous assister dans cette tâche.
Voir les 58 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous