Die Jagd nach dem Schnatz
Das Problem
Neulich las ich bei einem meiner ALIX-Rechnern nach dem Start eine Zeile auf der Konsole, die mich stutzen ließ:
Remounting / as read-write ... Done.
Removing /etc/nologin ... Done.
Remounting / as read-only ... mount: / is busy
Debian GNU/Linux 6.0 voyage ttyS0
Gleich meldete ich mich an, um mir die nötige Gewissheit zu verschaffen:
voyage login: root
Password:
Last login: Tue Dec 13 21:00:06 CET 2011 on ttyS0
...
< http://linux.voyage.hk > Version: 0.7 (Build Date 20110628)
root@voyage:~# mount
...
/dev/hda2 on / type ext2 (rw,noatime,errors=continue)
Das war keineswegs, was ich mir von diesem Rechner erhofft hatte.
Ich habe mittlerweile einige Rechner mit Voyage Linux aufgesetzt, um neben seinen anderen Vorzügen insbesondere die nur-lesend eingehängte Root-Partition einzusetzen, in der Hoffnung, damit die Lebensdauer der CompactFlash-Karten, auf denen selbiges gespeichert ist, etwas zu verlängern. Ein Vorzug von Flash-Speichern gegenüber Festplatten ist das Fehlen beweglicher Teile, womit eine ganze Klasse von Ausfallrisiken entfällt. Diesem Vorteil steht jedoch ein Nachteil gegenüber, nämlich das diese - insbesondere die MLC-Speicher mit höherer Kapazität - nur eine begrenzte Anzahl von Schreiboperationen erlauben. Aus diesem Grund habe ich verschiedene Verfahren ausprobiert, um diese Rechner mit nur lesend eingehängtem permanenten Speicher zu betreiben.
Diese Rechner sollen als hochverfügbare Geräte eingesetzt werden. Eingebaut und vergessen, weil immer funktionierend. Doch mit beschreibbarer CompactFlash-Karte kann ich nicht sicher sein, dass diese nicht doch eines Tages ausfällt.
Die Geräte sollten Crash-Only betrieben werden können. Kein Schalter zum Herunterfahren, nur einfach ausschalten. Wenn das Dateisystem beschreibbar eingehängt ist, dann ist beim nächsten Boot ein Dateisystem-Check fällig. Da die Geräte nur eine serielle Konsole haben, bemerkt am Ende niemand, das der Rechner auf ein Eingabe wartet, um das Dateisystem zu überprüfen.
Die Jagd beginnt
Also brachte ich meine Ausrüstung in Stellung und begann mit der Jagd.
Die erste Spur war mount: / is busy. Irgendein Prozess hatte eine Datei zum Schreiben geöffnet und verhindert damit, dass der Kernel das Dateisystem nur-lesend umhängt. Aber welcher? Um das herauszubekommen nehme ich fuser:
root@voyage:~# fuser -vm /
USER PID ACCESS COMMAND
/: root kernel mount /
...
root 1467 Frce. dhclient
...
Hier hatte ich schon den Übeltäter. Prozess 1467 war der einzige, mit Zugriffsmodus F und das Programm, das lief, war dhclient. Doch war es das wirklich?
root@voyage:~# remountro
mount: / is busy
root@voyage:~# kill 1467
root@voyage:~# remountro
root@voyage:~# mount
...
/dev/hda2 on / type ext2 (ro,relatime,errors=continue)
...
Kein Zweifel. Der DHCP-Client war es. Operation gelungen, Patient tot.
Natürlich sollte dieser Rechner via DHCP konfiguriert werden, aber trotzdem wollte ich die Root-Partition nur-lesend einhängen.
Also besser noch einmal nachschauen, ob sich da etwas machen lässt. Zunächst erstmal: neuer Start, neues Glück.
root@voyage:~# fuser -vm /
USER PID ACCESS COMMAND
/: root kernel mount /
...
root 1431 Frce. dhclient
...
Welche Datei wollte er denn nun eigentlich beschreiben? Das sagt mir ein anderes Werkzeug: lsof.
root@voyage:~# lsof -p 1431
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
dhclient 1431 root cwd DIR 3,2 4096 2 /
dhclient 1431 root rtd DIR 3,2 4096 2 /
dhclient 1431 root txt REG 3,2 440660 26949 /sbin/dhclient
dhclient 1431 root mem REG 3,2 42572 25691 /lib/libnss_files-2.11.2.so
dhclient 1431 root mem REG 3,2 1319176 25666 /lib/libc-2.11.2.so
dhclient 1431 root mem REG 3,2 113964 25656 /lib/ld-2.11.2.so
dhclient 1431 root 0u CHR 1,3 0t0 11 /dev/null
dhclient 1431 root 1u CHR 1,3 0t0 11 /dev/null
dhclient 1431 root 2u CHR 1,3 0t0 11 /dev/null
dhclient 1431 root 3w REG 3,2 1587 5385 /var/lib/dhcp/dhclient.eth0.leases
dhclient 1431 root 4u pack 3803 0t0 ALL type=SOCK_PACKET
dhclient 1431 root 5u IPv4 3807 0t0 UDP *:bootpc
Die Datei /var/lib/dhcp/dhclient.eth0.leases enthält die Daten, die der Client vom DHCP-Server bekommen hat. Nach einem Neustart versucht der DHCP-Client die IP-Adresse vom Server zu bekommen, die in dieser Datei steht.
Versuch einer Lösung
Es gibt bei Voyage Linux in der Konfigurationsdatei /etc/default/voyage-util eine Variable VOYAGE_SYNC_DIRS, in der man Verzeichnisse eintragen kann, bei denen während des Betriebes ein tmpfs eingehängt werden soll. Die Verzeichnisse werden beim ordnungsgemäßen Hinunterfahren des Rechners gesichert und beim Neustart aus den Sicherungen befüllt. Das wäre doch traumhaft. Zwei Fliegen mit einer Klappe. Also schnell
VOYAGE_SYNC_DIRS="var/lib/dhcp"
dort eingetragen, gespeichert, Neustart, und
aus der Traum.
Remounting / as read-write ... Done.
Removing /etc/nologin ... Done.
Remounting / as read-only ... mount: / is busy
Debian GNU/Linux 6.0 voyage ttyS0
Anmelden und kontrollieren:
voyage login: root
Password:
Last login: Tue Dec 13 22:57:08 CET 2011 on ttyS0
...
root@voyage:~# mount
rootfs on / type rootfs (rw)
...
/dev/hda2 on / type ext2 (rw,noatime,errors=continue)
...
tmpfs on /var/lib/dhcp type tmpfs (rw,nosuid,relatime,mode=755)
Da musste doch noch etwas anderes sein.
root@voyage:~# fuser -vm /
USER PID ACCESS COMMAND
/: root kernel mount /
...
root 1440 Frce. dhclient
...
root@voyage:~# lsof -p 1440
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
dhclient 1440 root cwd DIR 3,2 4096 2 /
dhclient 1440 root rtd DIR 3,2 4096 2 /
dhclient 1440 root txt REG 3,2 440660 26949 /sbin/dhclient
dhclient 1440 root mem REG 3,2 42572 25691 /lib/libnss_files-2.11.2.so
dhclient 1440 root mem REG 3,2 1319176 25666 /lib/libc-2.11.2.so
dhclient 1440 root mem REG 3,2 113964 25656 /lib/ld-2.11.2.so
dhclient 1440 root 0u CHR 1,3 0t0 11 /dev/null
dhclient 1440 root 1u CHR 1,3 0t0 11 /dev/null
dhclient 1440 root 2u CHR 1,3 0t0 11 /dev/null
dhclient 1440 root 3w REG 3,2 1008 5385 /var/lib/dhcp/dhclient.eth0.leases
dhclient 1440 root 4u pack 3830 0t0 ALL type=SOCK_PACKET
dhclient 1440 root 5u IPv4 3834 0t0 UDP *:bootpc
Da war immer noch die Datei /var/lib/dhcp/dhclient.eth0.leases auf dem Root-Dateisystem (DEVICE 3,2) offen. Die müsste doch eigentlich auf dem tmpfs liegen, das unter /var/lib/dhcp eingehängt ist.
Wie kann das sein, dass eine Datei in einem Dateisystem geöffnet wird, wenn ihr Pfad auf ein anderes Dateisystem verweist?
Es geht genau dann, wenn die Datei geöffnet wird, bevor das tmpfs in dem Verzeichnis eingehängt wird.
Wann wird das Verzeichnis eingehängt und wann der DHCP-Client gestartet?
root@voyage:~# ls /etc/rcS.d/
README S07module-init-tools S12procps
S01mountkernfs.sh S07mtab.sh S12udev-mtab
S02udev S08checkfs.sh S12urandom
S03mountdevsubfs.sh S09ifupdown S12voyage-sync
S04bootlogd S09mountall.sh S13networking
S05hostname.sh S10mountall-bootclean.sh S14portmap
S05hwclockfirst.sh S11mountoverflowtmp S15mountnfs.sh
S06checkroot.sh S12ebtables S16mountnfs-bootclean.sh
S07hwclock.sh S12iptables-persistent S17bootmisc.sh
S07ifupdown-clean S12pcmciautils
S18stop-bootlogd-single
Die Dateien in den rc?.d/ Verzeichnissen werden traditionell in der lexikalischen Reihenfolge ihrer Namen abgearbeitet. Das heisst S12voyage-sync sollte vor S13networking ausgeführt werden. Was war denn da los. Nun sah ich mir die Bootmeldungen weiter oben nochmal genauer an:
INIT: version 2.88 booting
Using makefile-style concurrent boot in runlevel S.
Oha.
Nebenläufig. Um die Wette sozusagen. Nun ja, makefile-style machte mir noch etwas Hoffnung. Da geht es doch um Abhängigkeiten. Kurz nachgesehen:
root@voyage:~# grep Required-Start /etc/init.d/voyage-sync
# Required-Start: $local_fs
root@voyage:~# grep Required-Start /etc/init.d/networking
# Required-Start: mountkernfs $local_fs
Beide haben ähnliche Abhängigkeiten, könnten also ungefähr gleichzeitig starten. Und dann geht es darum, wer schneller ist.
Die Lösung
Ich änderte die Zeile in /etc/init.d/networking ab in
# Required-Start: mountkernfs $local_fs voyage-sync
und machte das dem System bekannt:
root@voyage:~# update-rc.d networking defaults
update-rc.d: using dependency based boot sequencing
update-rc.d: warning: networking start runlevel arguments (2 3 4 5) do not match LSB Default-Start values (S)
update-rc.d: warning: networking stop runlevel arguments (0 1 6) do not match LSB Default-Stop values (0 6)
Neustart und nun:
Remounting / as read-write ... Done.
Removing /etc/nologin ... Done.
Remounting / as read-only ... Done.
Bingo.
Nebenbei: Normalerweise ist bei Voyage Linux /var/lib/dhcp ein symbolischer Link auf /lib/init/rw/var/lib/dhcp (/lib/init/rw/ wird schon sehr früh als tmpfs eingebunden). Ich weiss nicht, warum das bei diesem Rechner anders war, bei einem frisch aufgesetzten Rechner wäre das Problem nicht aufgetreten. Immerhin war es eine gute Gelegenheit, das Vorgehen bei derartigen Problemen zu erläutern.
Posted 2011-12-23