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)

Des bits comme s'il en pleuvait : une appliance SAN/NAS sous NetBSD

Magazine
Marque
GNU/Linux Magazine
Numéro
181
Mois de parution
avril 2015
Spécialité(s)
Résumé
À Noël, je suis le désespoir de ma famille. En effet, là où d'autres demandent des Blu-ray, de quoi s'habiller en hiver ou encore une nouvelle poêle à frire, moi je demande des composants. Des disques, des cartes, des trucs super pas excitants pour les personnes qui vous les offrent. Cette année, je voulais plus de place sur mon NAS [1], plein de bits pour y coller des documents à haute teneur multimédia.

Les différents BSD

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
74
Mois de parution
septembre 2014
Spécialité(s)
Résumé

« Salut, je voudrais installer *BSD, vous me conseillez quoi ? »Cette phrase, lue et relue des centaines, des milliers de fois, est fondamentalement incorrecte. *BSD n'existe pas. Plus important, lorsque l'on souhaite mettre le pied à l'étrier d'un système BSD, un minimum de contexte est nécessaire, car chacun des héritiers de BSD UNIX suit une philosophie bien définie, et si chacun d'entre eux s'avère éminemment versatile, certains seront plus adaptés et faciles d'accès fonction de l'utilisation souhaitée. Nous allons ici faire connaissance avec les trois systèmes BSD les plus connus, NetBSD, FreeBSD et OpenBSD et évoquer leurs forces et faiblesses afin de guider votre choix de façon la plus objective possible.

NetBSD, le système du futur du subjonctif

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
74
Mois de parution
septembre 2014
Spécialité(s)
Résumé
« The Original UNIX », c'est pour cette punchline que j'avais voté lorsque le groupe chargé du marketing chez NetBSD cherchait une approche différente du classique « of course it runs NetBSD » ; néanmoins, s'il est l'héritier le plus direct des travaux du CSRG [1] et l'un des plus grands défenseurs de la portabilité du code, le projet NetBSD n'est certainement pas un système d'exploitation du passé, loin s'en faut. Bénéficiant des avancées standards d'autres OS, NetBSD dispose en outre d'avantages uniques dont l'administrateur système aguerri saura faire usage. Dans cet article, nous allons monter un serveur web articulé autour de briques modernes, et allons dans notre périple découvrir l'ensemble des éléments nécessaires au déploiement d'un système aussi réactif que respectueux des standards.

Les derniers articles Premiums

Les derniers articles Premium

Game & Watch : utilisons judicieusement la mémoire

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

Au terme de l'article précédent [1] concernant la transformation de la console Nintendo Game & Watch en plateforme de développement, nous nous sommes heurtés à un problème : les 128 Ko de flash intégrés au microcontrôleur STM32 sont une ressource précieuse, car en quantité réduite. Mais heureusement pour nous, le STM32H7B0 dispose d'une mémoire vive de taille conséquente (~ 1,2 Mo) et se trouve être connecté à une flash externe QSPI offrant autant d'espace. Pour pouvoir développer des codes plus étoffés, nous devons apprendre à utiliser ces deux ressources.

Raspberry Pi Pico : PIO, DMA et mémoire flash

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

Le microcontrôleur RP2040 équipant la Pico est une petite merveille et malgré l'absence de connectivité wifi ou Bluetooth, l'étendue des fonctionnalités intégrées reste très impressionnante. Nous avons abordé le sujet du sous-système PIO dans un précédent article [1], mais celui-ci n'était qu'une découverte de la fonctionnalité. Il est temps à présent de pousser plus loin nos expérimentations en mêlant plusieurs ressources à notre disposition : PIO, DMA et accès à la flash QSPI.

Programmation des PIO de la Raspberry Pi Pico

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

La carte Pico de Raspberry Pi est appréciable à bien des égards. Ses ressources, son prix, ses deux cœurs ARM... Mais ce morceau de silicium qu'est le RP2040 renferme une fonctionnalité unique : des blocs PIO permettant de créer librement des périphériques supplémentaires qu'il s'agisse d'éléments standardisés comme SPI, UART ou i2c, ou des choses totalement exotiques et très spécifiques à un projet ou un environnement donné. Voyons ensemble comment prendre en main cette ressource et explorer le monde fantastique des huit machines à états de la Pico !

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 49 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous