Es gibt Dinge, die sollte im Grundsatz immer nur einer machen, etwa eine Datenbank anlegen oder einen Prozess starten. Ein Lock stellt die Solonummer sicher. Überraschend viele Implementierungen für Locks in Shellskripten sind jedoch falsch.
Wenn unterschiedliche Prozesse oder Threads eine Ressource zeitgleich verändern, kann das zu katastrophalen Folgen führen – für die Datenintegrität genauso wie für den Ablauf der betreffenden Prozesse (drohende Race Conditions). Daher verfügen Multitasking-Betriebssysteme seit langem über Mechanismen, die wechselseitige Zugriffe miteinander zu synchronisieren helfen.
Unter Linux gibt es dafür unter anderem so genannte Mutex-Semaphore (Mutex [1] steht für Mutual Exclusion, wechselseitiger Ausschluss). Die C-Bibliothek und die Systembibliotheken von Programmiersprachen machen den Zugriff auf Mutexe für Anwendungsprogramme verfügbar.
Leider bleibt Bash-Skripten der Zugang zu diesen Mechanismen verwehrt. Workarounds müssen darum her, um die Zugriffe zu serialisieren. Das Problem: Die meisten davon funktionieren – aber nicht sicher, wie die nächsten Abschnitte zeigen.
Open Suse nutzt zum Beispiel das Wrapper-Skript in Listing 1, das täglich den Logrotate-Dienst startet. Klar, dass gleichzeitig laufende Instanzen hier Unheil anrichten können. In Zeile 3 überprüft das Skript daher, ob schon eine Instanz läuft. Wenn nicht, startet das Skript in Zeile 10 den Dienst.
Listing 1
Start von Logrotate aus /etc/cron.daily
01 #!/bin/sh
02 # exit immediately if there is another instance running
03 if checkproc /usr/sbin/logrotate; then
04 /bin/logger -t logrotate "ALERT another instance of logrotate is running - exiting"
05 exit 1;
06 fi;
07
08 TMPF=`mktemp /tmp/logrotate.XXXXXXXXXX`
09 /usr/sbin/logrotate /etc/logrotate.conf 2>&1 | tee $TMPF
10 EXITVALUE=${PIPESTATUS[0]}
11 if [ $EXITVALUE != 0 ]; then
12 # wait a sec, we might just have restarted syslog
13 sleep 1
14 # tell what went wrong
15 /bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
16 /bin/lo gger -t logrotate -f $TMPF
17 fi
18 rm -f $TMPF
19 exit 0
Dumm nur, wenn Logrotate genau in diesem Zeitfenster losläuft. Normalerweise passiert so etwas nicht – aber wenn etwas schief gehen kann, dann passiert es eines Tages auch. Vergleichbar fehlerhafte Implementierungen finden sich in erstaunlich vielen Skripten: die Abfrage auf einen Prozess beispielsweise oder auf das Vorhandensein einer Prozess-PID-Datei.
Dass eigentlich Logrotate selbst dafür sorgen sollte, dass es nicht mehrfach zur selben Zeit mit identischer Konfigurationsdatei läuft, hilft nur bedingt weiter. Es bedarf vieler Wrapper-Skripte nur wegen der Unzulänglichkeiten der eigentlich damit gestarteten Programme.
Eigene Locks
Eine andere weitverbreitete Synchronisiertechnik arbeitet mit Lockdateien. Auch bei einem entsprechenden Beispiel in Listing 2 können zwei Instanzen des Skripts so unglücklich laufen, dass beide Instanzen die Existenz des Lockfiles zu einer Zeit abfragen, bevor das Lockfile erzeugt ist.
Es ist tragisch, dass gerade eine Programmieranleitung für Anfänger aus quasi offizieller Quelle [2] eine Technik demonstriert, die nichts taugt und den Anfänger verdirbt. Dies erscheint besonders unverständlich, weil die richtige Lösung nicht aufwändiger wäre.
Listing 2
Unsicheres Sperren mit einer Lockdatei
01 #!/bin/bash 02 LOCKFILE=/var/lock/makewhatis.lock 03 04 # Previous makewhatis should execute successfully: 05 [ -f $LOCKFILE ] && exit 0 06 [...] 07 08 touch $LOCKFILE 09 makewhatis -u -w 10 exit 0






