Nous allons voir dans ce petit article comment utiliser systemd dans l’initrd. La construction d’un initrd étant spécifique à la distribution, nous verrons comment l’utiliser avec Arch Linux, mais avec un peu de travail cela devrait pouvoir donner le principe général de fonctionnement et être adaptable sur d’autres distributions.
Sommaire
- Quelques généralités sur l’initrd
- Configuration du système pour utiliser systemd
- La base
- Partition chiffrée
- LVM
- Résumé
- Plymouth
- Création de l’initrd
- Pour s’amuser un peu
- Conclusion
ATTENTION : nous allons modifier une partie très importante du démarrage de la machine, et il est probable qu’elle ne puisse plus démarrer. Prévoyez donc un live CD quelconque pour pouvoir réparer en cas de souci !
Quelques généralités sur l’initrd
L’initrd, c’est une archive cpio qui est chargée en mémoire vive par le noyau juste après son lancement. Elle est montée sur /
, puis le fichier /init
est exécuté. Ce dernier est censé s’occuper de lancer tout ce qui est nécessaire au montage de du vrai système de fichiers racine /
, et qui n’est pas en dur dans le noyau, comme le chargement de modules noyau : accès au LVM, le déchiffrement de partitions, montage de /usr
s’il est sur une partition séparée…
Traditionnellement, ce fichier init est un script shell construit par des outils spécifiques à la distribution.
La construction classique de l’initrd sous Arch Linux
Sous Arch Linux, la construction d’un initrd se fait à l’aide du logiciel mkinitcpio. Il se configure en utilisant le fichier /etc/mkinitcpio.conf
. La variable HOOKS
est celle qui nous intéresse présentement.
Pour chaque hook présent dans cette variable, deux fichiers seront utilisés, un script install
qui sera chargé d’ajouter les fichiers nécessaires à l’initrd lors de la construction, et un script hook
qui sera lancé par le script shell init
lors de l’amorçage du système. L’ordre d’apparition des hooks dans la variable HOOKS
est important, car c’est l’ordre utilisé pour lancer les scripts lors de l’amorçage.
L’utilisation de systemd dans l’initrd
Lorsqu’on utilise systemd dans l’initrd, le fichier /init
de l’initrd n’est plus un script shell, mais directement systemd.
Ainsi, lors de la construction de l’initrd avec mkinitcpio
, le script hook
n’a plus aucun effet, et le script install
est chargé, outre les fichiers nécessaires, tels les démons et modules noyaux, d’installer les unités systemd nécessaires à monter la vraie racine. L’ordre d’apparition des hooks dans la variable HOOKS
n’a plus d’importance (à une exception près que nous verrons plus bas), car ce sont les unités systemd qui détermineront l’ordre de lancement des différentes unités (et donc de tous les binaires lancés, car tout est lancé via systemd).
systemd dans l’initrd, ça sert à quoi ?
Il y a sûrement des avantages secondaires, comme la parallèlisation, une configuration de démarrage unique et centralisée, et peut‐être d’autres, mais l’avantage principal est quand même de faire rager les anti‐systemd en le mettant partout et à toutes les sauces.
Configuration du système pour utiliser systemd
Le problème principal est que le support d’Arch Linux pour systemd dans l’initrd est un peu balbutiant, et il faut donc mettre un peu les mains dans le cambouis pour que tout fonctionne. Nous verrons ici comment configurer un système utilisant une partition chiffrée contenant du LVM et Plymouth, puisque c’est ma configuration. L’ajout du RAID ne doit pas être très compliqué.
La base
Les hooks base
, usr
, udev
et timestamp
ne sont plus nécessaires et sont remplacés par le hook systemd
. Les hooks autodetect
, block
, filesystems
, btrfs
et keyboard
sont toujours nécessaires (au moins sur ma machine). Les autres hooks (lvm
, encrypt
, plymouth
, etc.) seront par la suite remplacés par des équivalents sd-machin
.
Les hooks sd-machin
doivent toujours être après le hook systemd
. En effet, ce hook ajoute une fonction utilisable dans les autres hooks, add_systemd_unit
. Cette fonction ajoute dans l’initrd l’unité passée en paramètre, ainsi que toutes ses dépendances.
Correctif 1 : unités dans /etc
Cette fonction a un problème : elle n’utilise que le dossier /usr/lib/systemd/system
et ignore donc totalement les unités créées par l’utilisateur, situées dans le dossier /etc/systemd/system
(voir le rapport de bogue).
Nous allons donc modifier le hook, en copiant /usr/lib/initcpio/install/systemd
vers /etc/initcpio/install/systemd
. Nous allons donc pouvoir effectuer nos modifications sans risquer de les voir écrasées par une mise à jour.
Le correctif est le suivant :
--- /usr/lib/initcpio/install/systemd 2014-09-02 00:11:28.000000000 +0630
+++ /etc/initcpio/install/systemd 2014-10-16 13:39:11.291460470 +0630
@@ -51,7 +51,7 @@
local unit= rule= entry= key= value= binary= dep=
- unit=$(PATH=/usr/lib/systemd/system:/lib/systemd/system type -P "$1")
+ unit=$(PATH=/etc/systemd/system:/usr/lib/systemd/system:/lib/systemd/system type -P "$1")
if [[ -z $unit ]]; then
# complain about not found unit file
return 1
@@ -78,18 +78,23 @@
done <"$unit"
# preserve reverse soft dependency
- for dep in {/usr,}/lib/systemd/system/*.wants/${unit##*/}; do
+ for dep in {{/usr,}/lib,/etc}/systemd/system/*.wants/${unit##*/}; do
if [[ -L $dep ]]; then
add_symlink "$dep"
fi
done
# add hard dependencies
- if [[ -d $unit.requires ]]; then
- for dep in "$unit".requires/*; do
- add_systemd_unit ${dep##*/}
- done
- fi
+ for dir in {{/usr,}/lib,/etc}/systemd/system/${unit##*/}.requires; do
+ if [[ -d "$dir" ]]; then
+ for dep in "$dir"/*; do
+ if [[ -L $dep ]]; then
+ add_symlink "$dep"
+ fi
+ add_systemd_unit ${dep##*/}
+ done
+ fi
+ done
}
build() {
Correctif 2 : emergency.target
Second problème de ce hook, il n’ajoute pas les utilitaires nécessaires au fonctionnement d’emergency.target
, une unité spéciale qui est lancée lorsque l’amorçage plante et donne un shell à l’utilisateur pour essayer de sauver les meubles (voir un rapport de bogue et un autre). On va donc le modifier pour rajouter sulogin
, ainsi que les utilitaires de busybox
.
--- /etc/initcpio/install/systemd.old 2014-10-16 13:44:23.135993657 +0630
+++ /etc/initcpio/install/systemd 2014-10-16 13:43:23.657715653 +0630
@@ -98,12 +98,22 @@
}
build() {
- local rules unit
+ local rules unit applet
# from base
add_binary /bin/mount
add_binary /usr/bin/kmod /usr/bin/modprobe
+ add_binary /usr/lib/initcpio/busybox /bin/busybox
+ for applet in $(/usr/lib/initcpio/busybox --list); do
+ add_symlink "/usr/bin/$applet" busybox
+ done
+
+ # sulogin is needed for emergency target
+ add_binary /sbin/sulogin
+ add_file /etc/shadow
+ add_file /etc/gshadow
+
# systemd
add_binary /usr/lib/systemd/systemd /init
add_binary /usr/bin/systemd-tmpfiles
À ce stade, si l’on a une partition racine simple, on a terminé.
Partition chiffrée
S’il y a une partition chiffrée nécessaire au démarrage, il faut ajouter le hook sd-encrypt
.
Attention, les paramètres de ligne de commande du noyau (probablement configurés dans /etc/default/grub
) pour indiquer les partitions et leurs options ont changé. Le paramètre cryptdevice
est remplacé par luks.machin
(man systemd-cryptsetup-generator
pour plus d’infos).
Cependant, plutôt que ces paramètres, il est plus utile d’utiliser le fichier /etc/crypttab.initramfs
, qui suit la syntaxe de /etc/crypttab
.
LVM
Il suffit d’ajouter le fichier sd-lvm
. Aucune configuration particulière, car un détection est lancée dès l’apparition d’un nouveau block device, et activé si celui‐ci est un LVM (donc on peut avoir une partition chiffrée dans un LVM et un LVM dans une partition chiffrée, sans avoir à préciser d’ordre particulier ; et même avoir une partition chiffrée dans un LVM dans une partition chiffrée).
Résumé
Il n’y a rien dans la version stable de systemd pour s’occuper de sortir le système de l’hibernation. C’est prévu pour la prochaine version, mais en attendant, il va falloir faire les choses à la main.
On crée donc une unité /etc/systemd/system/resume.target
:
[Unit]
Description=Resume from disk
Before=initrd-root-fs.target sysroot.mount
[Install]
WantedBy=initrd.target
Et on l’active avec systemctl enable resume.target
.
Puis une unité générique /etc/systemd/system/resume@.service
:
[Unit]
Description=Resume from disk using %I
Before=resume.target
DefaultDependencies=no
BindsTo=%i.device
After=%i.device
[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo $(mountpoint -x %I) > /sys/power/resume"
[Install]
RequiredBy=resume.target
On l’active en utilisant le chemin de sa partition d’échange (swap), dans mon cas : systemctl enable resume@dev-main-swap.service
.
On crée enfin le hook mkinitcpio /etc/initcpio/install/sd-resume
:
#!/bin/bash
build() {
add_systemd_unit resume.target
}
help() {
cat <<HELPEOF
This hook adds resume capabilities to the initramfs.
HELPEOF
}
# vim: set ft=sh ts=4 sw=4 et:
Et on l’active en ajoutant sd-resume
dans la variable HOOKS
.
Plymouth
Si l’on utilise le AUR de Plymouth, cela demande un peu de travail.
Création du hook
Il n’y a pas de hook sd-plymouth
, donc il va falloir en créer un à partir du hook plymouth
. On copie donc /usr/lib/initcpio/install/plymouth
vers /etc/initcpio/install/sd-plymouth
, puis on applique le correctif suivant pour ajouter les unités systemd :
--- /usr/lib/initcpio/install/plymouth 2014-10-16 09:38:06.000000000 +0630
+++ /etc/initcpio/install/sd-plymouth 2014-10-16 14:05:01.237713438 +0630
@@ -1,7 +1,6 @@
build() {
add_dir /dev/pts
add_dir /usr/share/plymouth/themes
- add_dir /var/run/plymouth
DATADIR="/usr/share"
PLYMOUTH_LOGO_FILE="${DATADIR}/plymouth/arch-logo.png"
@@ -49,7 +48,15 @@
add_binary "$(readlink -e /lib/libnss_files.so.2)"
add_file /lib/libnss_files.so.2
- add_runscript
+ add_systemd_unit systemd-ask-password-plymouth.path
+ add_systemd_unit systemd-ask-password-plymouth.service
+
+ map add_systemd_unit plymouth-switch-root.service \
+ plymouth-start.service \
+ plymouth-reboot.service \
+ plymouth-kexec.service \
+ plymouth-poweroff.service \
+ plymouth-halt.service
}
help() {
Utilisation avec systemd-ask-password
Lorsqu’on utilise Plymouth avec un système ayant besoin d’un mot de passe (comme une partition chiffrée), il faut que le mot de passe soit demandée via Plymouth et non sur la console. systemd possède un système plutôt malin pour gérer ce genre de choses, sauf que l’unité plymouth-start.service
est cassée, et donc la détection du l’utilisation de Plymouth ne marche pas. On copie donc l’unité /usr/lib/systemd/system/plymouth-start.service
vers /etc/systemd/system/plymouth-start.service
et on applique le correctif suivant pour corriger le chemin du fichier contenant l’identifiant de processus :
--- /usr/lib/systemd/system/plymouth-start.service 2014-10-16 14:15:55.005953827 +0630
+++ /etc/systemd/system/plymouth-start.service 2014-10-16 14:06:53.054169994 +0630
@@ -7,7 +7,8 @@
ConditionKernelCommandLine=!plymouth.enable=0
[Service]
-ExecStart=/usr/bin/plymouthd --mode=boot --pid-file=/var/run/plymouth/pid --attach-to-session
+ExecStartPre=/bin/mkdir /run/plymouth
+ExecStart=/usr/bin/plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session
ExecStartPost=-/usr/bin/plymouth show-splash
Type=forking
KillMode=none
Création de l’initrd
On crée l’initrd avec mkinitcpio -p linux
, on redémarre et on croise les doigts. :)
Pour s’amuser un peu
Pour s’amuser un peu, on peut créer des unités et supprimer de son système /etc/crypttab
, /etc/cryttab.initramfs
, /etc/fstab
et certains paramètres de ligne de commande du noyau (comme root
, par exemple).
Ainsi, toute la configuration de démarrage du système est centralisée dans des unités systemd, et non plus éparpillée dans différents endroits.
Suppression de /etc/crypttab
et des paramètres luks.machin
du noyau
Le plus simple est d’utiliser directement le générateur /usr/lib/systemd/system-generators/systemd-cryptsetup-generator /etc/systemd/system /etc/systemd/system /etc/systemd/system
, puis d’analyser les fichiers pour éventuellement les simplifier.
On peut ensuite déplacer /etc/crypttab.initramfs
vers /etc/crypttab
, puis relancer la commande. On peut maintenant se débarrasser des ces deux fichiers.
Pour les paramètres luks.machin
du noyau, le plus simple est de les transformer en une entrée de /etc/crypttab
avant de lancer le générateur.
Suppression de /etc/fstab
Même principe en utilisant le fichier /usr/lib/systemd/system-generators/systemd-fstab-generator
.
Suppression du paramètre root
du noyau
Cette partie est normalement gérée par le générateur fstab, mais celui‐ci génère l’unité correspondant au paramètre root
du noyau seulement s’il est lancé dans l’initrd. Il faudra donc créer l’unité à la main.
Cette unité doit s’appeler /etc/systemd/system/sysroot.mount
et doit contenir les options habituelles d’une unité [Mount]
(man systemd.mount
pour les détails). Cependant, les lignes suivantes sont indispensables :
[Install]
RequiredBy=initrd-root-fs.target
[Unit]
Before=initrd-root-fs.target
[Mount]
Where=/sysroot
Il faut bien sûr ajouter les lignes spécifiques à votre système, notamment What
et peut‐être Type
dans la section [Mount]
.
Ne pas oublier d’activer l’unité avec systemctl enable sysroot.mount
.
Conclusion
Cette solution de gestion de l’initrd, qui en est à ses débuts, sera sûrement une solution standard à l’avenir et par défaut sur les distributions majeures.
À vos trolls !
Aller plus loin
- Journal à l’origine de la dépêche (168 clics)
# /var/run -> /run
Posté par appzer0 (site web personnel) . Évalué à 4.
Pourquoi dois-tu patcher ceci :
… sachant que
/var/run
est un lien symbolique vers/run
?Et pourquoi donc supprimer cette ligne ?
<mode pour_vendredi>
Merci pour ton article, ça donne une vue d'ensemble plus précise de ce qu'il est nécessaire de savoir en plus pour mettre en place un initrd : savoir scripter + savoir alimenter initcpio via son vocabulaire + ajouter des unités via le vocabulaire systemd + patcher l'existant)
</mode>
C'est néanmoins très complet et ça prend en compte LVM + le chiffrement + Plymouth, merci donc.
[^] # Re: /var/run -> /run
Posté par Misc (site web personnel) . Évalué à 4.
Il est possible que le lien de /var/run vers /run ne soit pas encore en place à ce moment la ?
[^] # Re: /var/run -> /run
Posté par Jean-Philippe Garcia Ballester (site web personnel) . Évalué à 4.
Comme l'a supposé Misc au dessus, /var/run n'est un lien symbolique vers /run que plus tard.
La création du dossier /run/plymouth ne fonctionne pas dans le script install, car cela créé un dossier /run/plymouth dans l'initrd, mais un tmpfs est monté sur /run, donc ce qu'il y a dans /run dans l'initrd est caché.
Il faut donc créer le dossier au boot, donc dans l'unité systemd.
Il faut espérer que les bug reports soient corrigés et qu'utiliser systemd dans l'initrd se fasse en 5 minutes en changeant juste les hooks.
[^] # Re: /var/run -> /run
Posté par Cilyan Olowen (site web personnel) . Évalué à 1.
Et ça ne marche pas avec
RuntimeDirectory
?http://www.freedesktop.org/software/systemd/man/systemd.exec.html#RuntimeDirectory=
[^] # Re: /var/run -> /run
Posté par Jean-Philippe Garcia Ballester (site web personnel) . Évalué à 2.
Tout à fait, c'est beaucoup mieux, merci !
# Chargement de l'initrd
Posté par mathgl . Évalué à 5.
J'aurais plutôt dit que c'était chargé par le chargeur de démarrage. Sinon, je vois pas trop comment il fait pour la récupérer sur le disque.
[^] # Re: Chargement de l'initrd
Posté par Jean-Philippe Garcia Ballester (site web personnel) . Évalué à 2.
Tout à fait. D'après
man initrd
, le chargeur de démarrage charge l'initrd en mémoire dans/dev/initrd
, puis lors du démarrage du noyau, celui-ci décompresse et copie le contenu dans/dev/ram0
puis libère le contenu de la mémoire de/dev/initrd
.[^] # Re: Chargement de l'initrd
Posté par benoar . Évalué à 6.
Oui en fin ça c'est l'ancienne méthode. La « nouvelle » (depuis 10 ans peut-être ?) c'est l'initramfs, copié en RAM par le chargeur de démarrage également (il ne connaît pas la différence, d'ailleurs), qui est décompressé dans un
tmpfs
(indépendant du système de bloc, contrairement au/dev/ramX
).# Pour éviter le live-cd
Posté par Siosm (site web personnel) . Évalué à 5.
Pour éviter d'avoir à sortir le live-cd pendant les tests ou suite à une mise à jour fâcheuse d'une version du noyau, je conseille :
linux-lts
) ;Tout ceci est très facile à faire puisqu'il suffit de copier
/etc/mkinitcpio.conf
vers/etc/mkinitcpio-systemd.conf
par exemple, de faire les modifications désirées, puis de modifier les fichiers/etc/mkinitcpio.d/linux*.preset
pour ajouter un nouveauPRESET
utilisant la config systemd ;Un petit
mkinitcpio -p linux && mkinitcpio -p linux-lts
et hop, il ne reste plus qu'à créer les entrées dans GRUB/gummiboot pour avoir le choix au boot en cas d'imprévu.Seul inconvénient : il faut avoir de la place dans sa partition
/boot
.Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.