Dans le doute tu rebootes, pas si facile avec kexec !
Ça vous est forcement arrivé, ce moment de solitude, mais si, vous savez, celui qui vous rend perplexe lorsque vous exécutez un simple init 6 ou reboot. Celui où vous vous attendez à voir le BIOS (qui met quinze plombes à charger) de votre serveur apparaître à l’écran alors qu’en fait ce n’est qu’une séquence de démarrage de votre noyau.
C’est bon, ça vous revient à l’esprit ? Vous vous souvenez donc ce que vous avez fait par la suite, un reboot -f bien moche qui passe outre l’init (d’ici, j’arrive à voir la honte sur votre visage). Vous n’êtes quand même pas allés jusqu’au reset matériel ?
J’avoue, je l’ai fait… Ne me regardez pas comme ça ! 😯
L’explication du pourquoi du comment
Pourquoi les commandes init 6, reboot et shutdown -r now ne fonctionnent pas alors que la commande reboot -f permet, elle de redémarrer le système ?
- La commande reboot appelle la commande shutdown avec l’option -r, shutdown envoie un signal à init pour lui demander de changer de niveau (runlevel)
- L’appel système shutdown(8) est utilisé
- /sbin/reboot est un lien symbolique de la commande /sbin/halt
- La commande init 6 appelle la commande init qui exécute tous les scripts présents dans /etc/rcX.d/ (via /etc/init.d/rc) pour ensuite terminer avec la commande shutdown -r
- L’appel système shutdown(8) est utilisé
En soit, il n’y a quasiment pas de différence entre ces trois commandes, elles appellent toutes les trois les scripts de gestion des services.
Pour ce qui est de la commande reboot -f, c’est un peu différent. L’option -f indique au système de ne pas passer par l’init, ce qui signifie que les processus seront arrêtés de manière brutale via un SIGKILL. Qui dit SIGKILL dit possible perte de données, imaginez un SIGKILL sur une base de données MySQL en InnoDB… j’espère que vous avez une sauvegarde.
L’appel système utilisé par la commande reboot -f est reboot(2).
Un début de piste fait son apparition, l’init
Je travaille sous Debian GNU/Linux Wheezy (au travail ainsi qu’à la maison), je vais donc prendre cette distribution comme exemple.
Comme vu plus haut, lorsque la commande reboot est exécutée, elle appelle shutdown qui envoie un signal (6 dans notre cas) à l’init. Jusque là rien de nouveau allez-vous me dire, certes, certes…
L’init définit le runlevel dans lequel il se trouve puis lance /etc/init.d/rc avec le bon runlevel en paramètre pour que ce dernier puisse effectuer des tâches différentes, en fonction de l’état dans lequel se trouve le système. Si l’on regarde le script rc d’un peu plus près on peut voir ceci :
# # Check if we are able to use make like booting. It require the # insserv package to be enabled. Boot concurrency also requires # startpar to be installed. # CONCURRENCY=makefile test -s /etc/init.d/.depend.boot || CONCURRENCY="none" test -s /etc/init.d/.depend.start || CONCURRENCY="none" test -s /etc/init.d/.depend.stop || CONCURRENCY="none" <...> startup() { eval "$(startpar -p 4 -t 20 -T 3 -M $1 -P $previous -R $runlevel)" <...> case "$runlevel" in 0|6) ACTION=stop ;; S) ACTION=start ;; *) ACTION=start ;; esac
La commande startpar fait partie du paquet sysvinit-utils. startpar permet de paralléliser le lancement des services au démarrage (ainsi qu’à l’arrêt) du serveur. Pour ce faire, rc vérifie qu’au moins un des trois fichiers suivants existe et ne soit pas vide :
- /etc/init.d/.depend.boot
- /etc/init.d/.depend.start
- /etc/init.d/.depend.stop
Le fichier .depend.stop nous confirme que la dernière action exécutée par startpar est kexec :
TARGETS = kexec-load urandom virtualbox-guest-utils apache2 exim4 atd yum-updatesd isc-dhcp-server tftpd-hpa sendsigs rsyslog umountnfs.sh rpcbind nfs-common hwclock.sh networking umountfs umountroot halt kexec reboot sendsigs: virtualbox-guest-utils exim4 atd apache2 isc-dhcp-server kexec-load rsyslog: sendsigs exim4 atd yum-updatesd apache2 isc-dhcp-server tftpd-hpa umountnfs.sh: virtualbox-guest-utils rsyslog sendsigs exim4 atd apache2 isc-dhcp-server kexec-load rpcbind: umountnfs.sh nfs-common: umountnfs.sh hwclock.sh: rsyslog atd nfs-common networking: umountnfs.sh rpcbind exim4 yum-updatesd apache2 isc-dhcp-server umountfs: virtualbox-guest-utils umountnfs.sh rpcbind exim4 networking atd yum-updatesd urandom hwclock.sh apache2 isc-dhcp-server kexec-load umountroot: umountfs halt: umountroot kexec: umountroot kexec-load reboot: umountroot kexec
Une solution \o/
Pour empêcher l’exécution de kexec, il faut indiquer au paquet kexec-tools de ne pas gérer le redémarrage du serveur. Si on regarde le code qui gère le service kexec-load, on peut voir ceci :
#! /bin/sh ### BEGIN INIT INFO # Provides: kexec-load # Required-Start: # Required-Stop: $local_fs $remote_fs kexec # Should-Stop: autofs # Default-Start: # Default-Stop: 6 # Short-Description: Load kernel image with kexec # Description: ### END INIT INFO <...> test -r /etc/default/kexec && . /etc/default/kexec <...> do_stop () { test "$LOAD_KEXEC" = "true" || exit 0 test -x /sbin/kexec || exit 0 test "x`cat /sys/kernel/kexec_loaded`y" = "x1y" && exit 0 if [ -f $NOKEXECFILE ] then /bin/rm -f $NOKEXECFILE exit 0 fi test "$USE_GRUB_CONFIG" = "true" && get_grub_kernel REAL_APPEND="$APPEND" test -z "$REAL_APPEND" && REAL_APPEND="`cat /proc/cmdline`" log_action_begin_msg "Loading new kernel image into memory" if [ -z "$INITRD" ] then kexec -l "$KERNEL_IMAGE" --append="$REAL_APPEND" else kexec -l "$KERNEL_IMAGE" --initrd="$INITRD" --append="$REAL_APPEND" fi log_action_end_msg $? }
Le fichier /etc/default/kexec est sourcé au début du fichier, dans ce dernier se trouve la variable LOAD_KEXEC. Si cette dernière est définie à true alors kexec sera exécuté lors de l’arrêt du service. Pour passer cette valeur à false, soit on édite directement le fichier soit on passe par dpkg-reconfigure. Passons par la bonne méthode :
# dpkg-reconfigure kexec-tools
Voici ce que debconf vous posera comme question (répondez No) :
Essayez, allez, faites-le, je sais que vous en mourrez d’envie !
# reboot
En théorie c’est censé fonctionner. Vous aurez l’immense joie de voir apparaître le magnifique logo de votre constructeur…
Pas envie de commenter cette foutue ligne ?
Commenter cette ligne est une solution plus que correcte mais elle peut ne pas convenir à tout le monde. L’autre solution serait de faire l’équivalent de la commande init 6 mais à la main. Ça donnerait quelque chose comme ceci (à adapter en fonction des services présents sur le serveur bien sûr) :
# /etc/init.d/apache2 stop # /etc/init.d/mysql stop # /etc/init.d/libvirt-guest stop # sync ; sync # reboot -f
Le sync est très important, il permet de synchroniser le cache (buffer) sur le disque et donc de s’assurer de la consistance des données. C’est chiant, c’est long mais ça fonctionne…
Édite du 28/01/2014
Sous Debian GNU/Linux il existe la commande coldreboot (qui n’est qu’un script shell), cette dernière passe outre kexec :
#!/bin/sh NOKEXECFILE=/tmp/no-kexec-reboot /bin/rm -f $NOKEXECFILE touch $NOKEXECFILE /sbin/reboot $*
Le man coldboot donne la description suivante :
DESCRIPTION
coldreboot is a script that forces a cold reboot regardless of whether kexec is enabled or not in /etc/default/kexec. coldreboot takes the same arguments as /sbin/reboot and passes them on to /sbin/reboot later.
Liens
- http://man7.org/linux/man-pages/man2/syscalls.2.html
- http://manpages.ubuntu.com/manpages/precise/man8/startpar.8.html
- http://wiki.incloudus.com/display/DOC/Debian+-+Kdump
Gaëtan Trellu (goldyfruit)
Derniers articles parGaëtan Trellu (goldyfruit) (voir tous)
- Qinling, let’s the journey begin! - 23 mai 2019
- systemd-networkd, l’âge de la maturité ? - 13 mars 2018
- Hyper-V, Nova, VxLAN et Open vSwitch, enfin une belle histoire ! - 31 décembre 2017
Kexec et l’appel système (syscall) shutdown(8) par Gaëtan Trellu (goldyfruit) est sous Licence Creative Commons Internationale Attribution 4.0.