zur Übersicht: Linux: meine Software und Konfigurationstipps

emergency root shell

03.02.2006

Das Problem

Neulich flippte irgendein KDE-Prozess völlig aus, riss die gesamte I/O-Bandbreite des Systems an sich und brachte es damit quasi zum Stillstand. Ich konnte mich auch nicht mehr als root einloggen, um den Übeltäter zu beseitigen. Es dauerte nach der Eingabe des Logins schon etwa eine Minute, bis überhaupt der Passwortprompt erschien. Danach war dann Ende. Vermutlich wäre fünf Minuten später der Shellprompt da gewesen, aber so lange will ich nicht warten müssen.

der Lösungsansatz

Seit Kernel 2.6.13 gibt es den CFQ-I/O-Scheduler (siehe auch mein Programm ioniced). Damit ist es möglich, Prozessen eine höhere I/O-Priorität zuzuweisen als den anderen. Das Problem sollte zu lösen sein, indem man eine der virtuellen Konsolen mit maximaler I/O- und CPU-Priorität laufen lässt. Dann dauert lediglich der Wechsel vom X-Server auf die Konsole noch lange. Das Login sollte mit maximaler Geschwindigkeit ablaufen, und die aus dieser root-Shell gestarteten Prozesse sollten ebenfalls höchste Priorität haben. Auf diese Weise sollte es unproblematisch sein, mit top und ps den Übeltäter zu identifizieren und per ionice zu entschärfen oder gleich mit kill zu entsorgen. Das waren viele Konjunktive in diesem Absatz, was ich damit entschuldigen möchte, dass solche Situationen selten vorkommen und die hier vorgestellte Lösung daher noch nicht unter realistischen Bedingungen getestet werden konnte. Selbst ein find mit Echtzeit-I/O-Priorität beeinträchtigt mein System nicht; damit wollte ich einen Test vornehmen.

Implementierung

Ich habe mich für tty2 entschieden. Ich lasse den getty-Prozess dort mit maximaler Priorität laufen. Ursprünglich habe ich in den bash-Startdateien eine Abfrage gehabt, ob sich root einloggt, aber da ich bei meinem Problem ewig auf den Shellprompt warten musste, löst das womöglich das Problem nicht. Da sich auch User auf tty2 einloggen können, muss die gestartete Shell die Priorisierung in diesem Fall loswerden. Allerdings gilt das nur für Systeme, auf denen User mit lokaler Loginmöglichkeit wirklich Ärger machen könnten. Über DoS gehen deren Möglichkeiten ja nicht hinaus, und wer sich eingeloggt hat, loggt das System mit, bevor der Shellprompt kommt... Dieses Problem ist also eher theoretischer Natur, aber ich wollte darauf hinweisen.

/etc/inittab

Die getty-Prozesse werden von init über die Datei /etc/inittab gestartet. Es muss also das getty für tty2 mit der gewünschten höheren Priorität gestartet werden. Nach dem Editieren der Datei muss init mit Hilfe des Befehls telinit q dazu gebracht werden, die Datei neu einzulesen. Ansonsten wird die Änderung erst nach dem nächsten Reboot wirksam. Es ist also die folgende Änderung vorzunehmen (unter SuSE 10.0; bei anderen Distributionen mag die Zeile geringfügig anders aussehen, ist aber analog anzupassen:

vorher
2:2345:respawn:/sbin/mingetty tty2
nachher
2:2345:respawn:ionice -c1 nice --19 /sbin/mingetty tty2

Shell-Startdateien

Die Shells führen mehrere Startdateien aus, insbesondere systemglobale und vom jeweiligen User zu modifizierende. Um auszuschließen, dass schon in den Startdateien irgendwas ist, was durch den Amok laufenden Prozess blockiert wird, setze ich den entsprechenden Code ganz an den Anfang der systemglobalen Datei. Das bringt das Problem mit sich, dass sie bei jedem Update der Distribution Gefahr läuft, überschrieben zu werden. Die Änderungen sind dann also erneut vorzunehmen – was man nur allzu leicht vergisst, wenn der Rechner dies nicht prüft und einen daran erinnert.

Aus /etc/profile:

vorher
*/bash)    is=bash ;;
nachher
*/bash)     is=bash
	if [ $(/bin/ps -o tty -p $$ | tail -n 1) = tty2 ]
		then
		if [ "$UID" -eq 0 ]
			then
			renice -19 -p $$ &>/dev/null
			ionice -c1 -p $$ &>/dev/null
			echo "This is the emergency root shell: highest CPU and I/O priority."
			echo "Keep that in mind."
		else
			type sudo &>/dev/null && sudo /sbin/ionice-c2 &>/dev/null
			renice 0 -p $$ &>/dev/null
		fi
        fi
;;

Das muss ggf. auch für andere Shells gemacht werden.

Ergebnis

# /bin/ps a -o pid,tty,nice,args
  PID TT        NI COMMAND
 4895 tty7       0 /usr/X11R6/bin/X -br :0 vt7 -auth /var/lib/xdm/authdir/authfiles/A:0-eRE8qA
 6035 tty1       0 /sbin/mingetty --noclear tty1
 6039 tty2     -19 /sbin/mingetty tty2
 6043 tty3       0 /sbin/mingetty tty3
 6045 tty4       0 /sbin/mingetty tty4
 6047 tty5       0 /sbin/mingetty tty5
 6051 tty6       0 /sbin/mingetty tty6

# ionice -p 6039
realtime: prio 4

Und nach dem Login:

# /bin/ps -t tty2 -o pid,tty,nice,args
  PID TT        NI COMMAND
16391 tty2     -19 -bash
# ionice -p 16391
realtime: prio 4

Erweiterungen

Es könnte sinnvoll sein, auch den X-Server mit höherer Priorität laufen zu lassen, damit der die Umschaltung schnell hinbekommt.