Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Étude — systemd : qualités, défauts, alternatives ; et le comparatif avec le launchd d’Apple

Statut : note de travail. Ne fait autorité sur rien ; sert de matière à une décision ultérieure. Aucune décision n’est prise ici.

Pourquoi maintenant. ADR-005 (« Intégration systemd comme socle système, D-Bus optionnel ») fait de systemd une dépendance dure et nomme sd-bus, sd-event, sd-notify, sd-journal, sd-id128, libudev, logind et systemd --user. Dans les faits, Air a réimplémenté les protocoles en Rust pur sans jamais lier les bibliothèques C, et trois de ces intégrations (sd-bus, logind, systemd --user) ne sont pas commencées. Par ailleurs, le projet anticipe depuis longtemps un superviseur maison : le trait ServiceHost d’air-service est explicitement le seam v1 (systemd) → v2 (air-launchd), et ADR-041 (« Coexistence /etc ») renvoie l’enforcement dur à air-launchd. Avant de trancher quoi que ce soit, il faut savoir ce qu’on garde, ce qu’on rejette, et ce que les autres ont appris.


1. systemd — ce qu’il fait réellement bien

À charge de reconnaître ce qui, dans systemd, relève de l’avancée technique et non de la mode. Beaucoup de critiques portent sur son périmètre, rarement sur ces points-là.

1.1 La supervision par cgroups — le vrai saut

C’est le différenciateur, et de loin le plus sous-estimé. Un service = un cgroup. Le noyau, et non l’init, tient la liste des processus appartenant au service. Conséquences :

  • l’arrêt est fiable. Un démon qui double-forke, qui setsid(), qui laisse des enfants orphelins : tout est dans le cgroup, tout est tué. Les scripts SysV ne savaient pas faire — ils traquaient un PID dans un fichier, et se trompaient dès qu’un processus s’échappait ;
  • la comptabilité est exacte (CPU, mémoire, I/O par service) ;
  • les limites sont applicables sans coopération du service ;
  • l’attribution des logs est infalsifiable : _SYSTEMD_UNIT est renseigné par le noyau via le cgroup, un service ne peut pas mentir sur son identité dans le journal.

Aucune alternative citée en §3 n’offre l’équivalent avec la même rigueur.

1.2 Le hardening déclaratif

Sans doute la fonctionnalité la plus utile en pratique, et la moins discutée dans les polémiques. On durcit un service sans écrire une ligne de code : NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, PrivateDevices, RestrictAddressFamilies, CapabilityBoundingSet, SystemCallFilter (seccomp), MemoryDenyWriteExecute, DynamicUser, ProtectKernelModules, namespaces divers.

Un service tiers non coopératif devient confinable par configuration. C’est un modèle dont Air ferait bien de s’inspirer — d’autant qu’Air a déjà les briques (air-sandbox, ADR-112 : compilateur seccomp maison et Landlock, en Rust safe).

1.3 L’activation à la demande et le readiness typé

  • Socket activation : le socket est créé par l’init, le service ne démarre qu’au premier client, et les dépendances de démarrage s’effacent (le client bloque sur le socket au lieu d’échouer). Idée empruntée à launchd, ce que Poettering a reconnu explicitement dès « Rethinking PID 1 » (2010).
  • Type=notify : le service dit lui-même « je suis prêt » via sd_notify. Cela supprime la classe de bugs des sleep 5 et des courses sur fichier PID.
  • Watchdog : le service doit battre régulièrement, sinon il est redémarré.

1.4 Le modèle déclaratif et l’uniformité

Une unité est un fichier INI parsable, introspectable, diffable, générable. Un script shell ne l’est pas. Surtout, la même unité fonctionne sur toutes les distributions : c’est un effet de réseau considérable, et la principale raison de l’adoption — un éditeur livre une unité, pas sept scripts d’init.

1.5 Le reste, en bref

  • Ordonnancement explicite (Wants/Requires/After/Before) et parallélisation du démarrage.
  • Journal structuré : des champs, pas des lignes de texte à regexper ; corrélation par unité, par démarrage, par curseur.
  • Timers : dépendances, persistance (Persistent=true rattrape une exécution manquée), délais aléatoires — ce que cron ne sait pas faire.
  • systemd --user : les mêmes primitives pour la session utilisateur.
  • logind : sessions, sièges (seats), inhibiteurs — la base sur laquelle reposent tous les environnements de bureau modernes.
  • Introspection : systemd-analyze blame, critical-chain, systemctl status qui agrège état et logs.

2. systemd — ce qu’on lui reproche

Il faut séparer trois registres, souvent mélangés dans les débats.

2.1 Critiques techniques fondées

  • Périmètre. systemd a absorbé la résolution DNS (resolved), le réseau (networkd), l’horloge (timesyncd), les comptes (homed), le boot (systemd-boot), l’initrd… La qualité est inégale : systemd-resolved en particulier a accumulé les reproches (comportements de repli surprenants, gestion du DNSSEC). Chaque composant absorbé est une surface de plus.
  • Complexité sémantique du langage d’unité. Requires / Requisite / BindsTo / PartOf / Wants ont des sémantiques proches mais distinctes, et l’ordonnancement (After) est orthogonal à la dépendance (Requires) — confusion classique. Les cycles d’ordonnancement se diagnostiquent mal.
  • Journal binaire. Format indexé, donc rapide et structuré, mais corruptible, et inaccessible sans journalctl. Beaucoup d’exploitants rebranchent un syslog derrière, ce qui annule une partie du bénéfice.
  • Couplage de fait. Les composants sont modulaires en binaires, mais l’usage réel suppose D-Bus (le contrôle de PID 1 passe par sd-bus), journald, udev. « D-Bus est optionnel » est vrai sur le papier, pénible en pratique.
  • Surface de PID 1. Même si beaucoup de fonctions vivent hors du PID 1, le processus reste gros pour un composant dont la défaillance est fatale à la machine. L’historique de CVE n’est pas vide.
  • Debug du démarrage précoce (initrd, avant le journal) reste ingrat.
  • Adhérence Linux forte (cgroups, namespaces, epoll, signalfd) : non portable. Pour Air, ce point est neutre, voire favorable — ADR-004 assume Linux-only.

2.2 Ruptures de comportement mal vécues

Réelles, mais souvent défendables techniquement : KillUserProcesses=yes par défaut (systemd 230, 2016) qui a cassé nohup/screen ; les noms d’interfaces réseau « prévisibles » (utile, mais rupture brutale) ; tmpfiles.d qui efface des répertoires qu’on croyait à soi. Le grief n’est pas la décision, c’est la façon dont elle a été imposée.

2.3 Griefs culturels — à connaître, à ne pas surpondérer

  • « Viole la philosophie Unix. » Argument faible en l’état : systemd est bien découpé en binaires. Le reproche pertinent est le couplage, pas la monolithie.
  • Gouvernance perçue comme peu à l’écoute, et effet de verrouillage de l’écosystème (GNOME dépend de logind ; s’en passer coûte cher — cf. elogind, extraction de logind maintenue par les distributions sans systemd).

Ce qu’il faut en retenir pour une décision : les critiques sérieuses portent sur le périmètre et le couplage, presque jamais sur les mécanismes du §1. Un remplaçant crédible doit reprendre les mécanismes et refuser le périmètre.


3. Les alternatives (hors scripts rc.d)

3.1 Superviseurs purs — « faire une seule chose »

ProjetAuteur / distributionCaractère
daemontoolsD. J. BernsteinL’ancêtre (1990s). Un répertoire par service, run/log. Simplicité radicale, aujourd’hui historique.
runitVoid LinuxHéritier direct de daemontools, PID 1 inclus, ~ quelques milliers de lignes. Robuste, trivial à comprendre. Pas de socket activation, pas de cgroups, dépendances rudimentaires.
s6 / s6-rcLaurent Bercot (skarnet)La conception la plus rigoureuse du lot : composable, primitives minimales et orthogonales, s6-rc ajoute un graphe de dépendances compilé, s6-linux-init fait le PID 1. Courbe d’apprentissage raide, écosystème réduit, documentation dense mais exigeante. À lire absolument avant de concevoir un superviseur.
perp, noshConfidentiels. nosh est étonnamment complet et porte une culture BSD assumée.

3.2 Systèmes d’init complets

ProjetCe qu’il apporte / ce qui manque
OpenRCGentoo, AlpineDépendances, démarrage parallèle, portable (BSD). Scripts shell : lisible, mais on retrouve les limites de l’impératif. Supervision déléguée (supervise-daemon), suivi des processus sans cgroups par défaut.
DinitChimera LinuxInit et superviseur en C++, dépendances, conçu explicitement comme un systemd léger. Le plus crédible des « petits » aujourd’hui.
GNU ShepherdGuixÉcrit en Guile : la configuration est du code Scheme, donc programmable. Original et cohérent avec Guix ; adhérence forte à son écosystème.
finitEmbarquéLéger, orienté systèmes contraints.
procdOpenWrtSuperviseur + hotplug + ubus (leur bus léger, à la place de D-Bus). Intéressant comme preuve qu’on peut avoir un bus de contrôle sans D-Bus.
BusyBox initEmbarquéMinimal, sans supervision réelle.
UpstartUbuntu (abandonné)Modèle événementiel plutôt que dépendanciel. Mort, mais l’échec est instructif : l’événementiel pur rend l’état global difficile à raisonner.
SMFSolaris, aujourd’hui illumosService Management Facility (2005) : manifestes XML, graphe de dépendances, « restarters » enfichables, intégration au diagnostic matériel, instantanés de configuration avec retour arrière. Très en avance sur son temps ; à étudier pour ses idées, pas pour son ergonomie.

3.3 Le cas des conteneurs

tini, dumb-init, s6-overlay ne sont pas des inits système : ils existent parce qu’un PID 1 doit moissonner les zombies et propager les signaux, et que rien d’autre ne le fait dans un conteneur. Utile comme rappel du noyau irréductible du rôle.

3.4 Ce qu’aucune alternative n’offre

En l’état, aucune ne fournit simultanément : le hardening déclaratif par service (§1.2), la supervision par cgroups v2 (§1.1), un logind intégré, et l’uniformité inter-distributions. C’est précisément pourquoi systemd n’est pas délogé, indépendamment de ce qu’on pense de lui.


4. systemd contre launchd (celui d’Apple)

Levée d’ambiguïté — de quel launchd on parle. Dans toute cette section, « launchd » désigne le launchd d’Apple, le PID 1 de macOS. Il ne s’agit pas d’air-launchd, le futur superviseur d’Air, qui n’existe pas encore et dont le nom est seulement réservé (ADR-041 « Coexistence /etc », et le seam ServiceHost d’air-service). Le rapprochement des noms est un hommage, pas une équivalence : rien de ce qui suit ne préjuge de ce que fera air-launchd.

launchd (Apple, apparu avec Mac OS X 10.4 « Tiger », 2005, conçu par Dave Zarzycki) mérite l’examen : c’est l’ancêtre conceptuel de l’activation à la demande — systemd lui a explicitement emprunté la socket activation.

Réserve d’honnêteté. launchd a été publié sous APSL dans les premières versions de Darwin, mais les versions modernes ne le sont plus. Ce qui suit repose sur la documentation publique, les en-têtes et le comportement observable, pas sur le code actuel. À vérifier avant d’en tirer une décision fine.

4.1 Comparaison par axe

Axesystemdlaunchd
RôlePID 1 + une famille de démons satellitesPID 1 et gestionnaire des domaines utilisateur, un seul programme
DescriptionFichier INI (.service, .socket, .timer, .target…)plist (XML), un « job » unique pour tous les cas
Modèle mentalPlusieurs types d’unités, un graphe de dépendancesTout est un job — daemon, agent, tâche périodique : un seul concept
ActivationSocket, chemin, D-Bus, calendrier, périphériqueSocket, port Mach / XPC, chemin (WatchPaths, QueueDirectories), calendrier
Suivi des processuscgroups — attribution et arrêt fiablesPID + kqueue ; pas d’équivalent robuste au cgroup kill
ReadinessType=notify (sd_notify)Implicite (le service écoute son socket / son port Mach)
Sessions utilisateursystemd --user, monde partiellement distinctDomaines (system, user, gui) — modèle homogène et mieux pensé
Journalisationjournald, structuré, binaireASL puis unified logging (os_log) — composant séparé
ConfinementDéclaratif dans l’unité (seccomp, namespaces, capabilities)Hors du plist : profils sandbox (Seatbelt), entitlements, signature de code
Contrôlesd-bus / D-BusPorts Mach / XPC
PérimètreLarge et croissant (DNS, réseau, horloge, boot…)Étroit — launchd ne fait pas DNS ni réseau
OuvertureOpen source, écosystème vastePropriétaire de fait, spécifique à Apple

4.2 Ce que launchd fait mieux

  • L’unicité du modèle. Un seul concept — le job — couvre démons système, agents utilisateur, tâches périodiques et services à la demande. systemd multiplie les types d’unités et sépare imparfaitement le monde système du monde utilisateur.
  • L’activation par IPC, native. Un client ouvre une connexion XPC : le service est démarré par ce seul fait, sans qu’il ait à connaître launchd. C’est plus propre que l’activation D-Bus de systemd, parce que l’IPC et l’activation sont pensés ensemble plutôt qu’assemblés après coup.
  • Le service peut ne jamais tourner. Le design pousse le paresseux à l’extrême : démarrage à la première sollicitation, arrêt quand il n’y a plus rien à faire.
  • La retenue de périmètre. launchd ne s’est pas mis à faire du DNS.
  • L’ergonomie développeur : un service XPC s’embarque dans le bundle d’une application, avec son cycle de vie géré.

4.3 Ce que systemd fait mieux

  • Le suivi et l’arrêt des processus (cgroups) — l’écart est net et structurel.
  • Le confinement déclaratif : chez Apple, le durcissement vit ailleurs (profils sandbox, entitlements, signature). C’est plus fragmenté ; on peut lancer un job sans le confiner, et rien ne le rappelle.
  • La comptabilité et les limites de ressources.
  • Le journal structuré et corrélé à l’unité, infalsifiable via le cgroup.
  • Les timers persistants avec rattrapage.
  • L’ouverture : documentation, écosystème, réutilisabilité. launchd n’est pas réutilisable hors d’Apple.

4.4 Ce qu’on reproche à launchd

Diagnostic historiquement pauvre (messages d’erreur peu exploitables), rupture d’interface de launchctl (l’API historique dépréciée au profit d’un modèle par domaines, migration douloureuse), plist XML verbeux, et une documentation publique qui a longtemps été maigre.


5. Ce qu’Air peut en retenir

Rien n’est décidé ici ; ce sont les éléments à mettre sur la table.

À reprendre.

  1. Le suivi par cgroup v2, sans équivalent. C’est ce qui rend l’arrêt d’un service fiable et l’attribution des logs infalsifiable.
  2. Le confinement déclaratif par service — et Air part avec un avantage : air-sandbox (ADR-112) fournit déjà un compilateur seccomp maison et Landlock, en Rust safe. Là où systemd fait bien et launchd fait ailleurs, Air peut faire mieux et sûrement.
  3. Le readiness explicite plutôt que les courses sur fichier PID : air-service le fait déjà via le protocole sd_notify, sans lier libsystemd.
  4. L’activation à la demande pensée avec l’IPC, à la launchd plutôt qu’à la systemd. Air a les briques : capability = fd (ADR-010), AirCom en SEQPACKET avec passage de fd. Un service AirCom démarré par le fait qu’un client ouvre un canal serait l’équivalent propre de l’activation XPC.
  5. Le modèle homogène de launchd (un seul concept, des domaines) plutôt que la prolifération de types d’unités.

À éviter.

  1. L’absorption de périmètre. C’est le reproche le plus solide fait à systemd.
  2. Le journal binaire fragile comme unique voie.
  3. Un langage de description qui dérive vers des dizaines de directives à sémantique subtile. Air a déjà tranché en faveur de la configuration binaire (ADR-073), ce qui change la donne : le schéma est typé, versionné, et le codec texte n’est fourni que pour les autres.
  4. Le couplage à un bus généraliste pour le contrôle. procd/ubus sur OpenWrt montre qu’un bus de contrôle léger et dédié suffit.

Questions ouvertes, à trancher explicitement.

  • sd-bus : Air doit-il parler le protocole D-Bus pour piloter systemd, logind, NetworkManager ? Réimplémenter D-Bus en Rust pur pour cela, alors qu’ADR-001 refuse D-Bus comme transport natif, est une tension à assumer et non à subir.
  • logind : intégrer l’existant ou écrire l’équivalent Air ? Les distributions sans systemd maintiennent elogind, une extraction de logind — preuve que c’est faisable, et qu’aucune n’a voulu le réécrire.
  • air-launchd : quel périmètre exact ? Superviseur seul, ou superviseur + activation
    • confinement ? La réponse détermine si ADR-005 reste une intégration ou devient une transition.
  • Coexistence. Tant qu’Air tourne sur une distribution systemd, les deux superviseurs cohabitent. ADR-041 (« Coexistence /etc ») a déjà rencontré le problème.

Références à consulter avant décision

  • Lennart Poettering, « Rethinking PID 1 » (2010) — le texte fondateur, où l’emprunt à launchd est explicite.
  • Documentation skarnet de s6 — la réflexion la plus rigoureuse sur ce qu’est une primitive de supervision.
  • Documentation Apple sur les domaines launchd et le cycle de vie XPC.
  • SMF (illumos) pour ses instantanés de configuration et ses restarters enfichables.
  • elogind — ce que coûte réellement l’extraction de logind.

Licence du document : MPL 2.0 Statut : note de travail, sans autorité.