![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
||||||||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|||||||
![]() |
|||||||
![]() |
|
||||||
![]() |
Daemonen starten und stoppenRauf und RunterMichael Schilli |
![]() |
S |
apachectl
, das Tool um den Apache zu starten, macht's so: Wird es mit einem
Parameter start
aufgerufen, fährt es den Webserver hoch und legt die PID des Hauptprozesses
in einer Datei
http.pid
im log
-Verzeichnis ab. Ruft man anschließend
apachectl stop
auf, wird in http.pid
nachgesehen, welche Prozessnummer der Apache hat und ein kill
-Befehl darauf angesetzt.
Das Skript in Listing pc
(für
Process Control) macht genau dasselbe mit
beliebigen Prozessen:
pc start myproc
startet das Programm myproc
als
Hintergrundprozess und legt die Prozess-ID in
myproc.pid
ab. Ein anschließendes
pc stop myproc
liest myproc.pid
aus und sendet dem
Prozess ein SIGTERM
-Signal, das diesen
beendet, falls er es nicht bewusst abfängt und
ignoriert. Im Notfall lässt sich mit
pc -s KILL stop myproc
auch das SIGKILL
-Signal einstellen, das
kein Prozess abfangen kann und das ihn unweigerlich vom
Himmel holt. Wem die Geschwätzigkeit von
pc
zuviel wird, kann es mit der Option
-q
zum Schweigen bringen:
pc -q start myproc
sagt nicht Starting myproc ... started, sondern gar nichts, falls alles glatt ging. Soll vor dem Starten in ein anderes Verzeichnis gewechselt werden, lässt sich das mit
pc -d /my/directory start myproc
bewerkstelligen. Soll nur die Datei, in der die PID
gespeichert wird, in einem anderen Verzeichnis landen,
kommt die l
-Option zum Einsatz:
pc -l /tmp start myproc
legt myproc.pid
in /tmp
ab. Bei relativen Pfadangaben ist darauf zu achten,
dass diese vom gegenwärtigen Verzeichnis aus
gelten, oder von dem mittels der -d
-Option
aus festgelegten.
Das Skript pc
überprüft genau,
ob es auch mit dem
start
/stop
-Befehl und
mindestens einem Programmnamen aufgerufen
wurde. Andernfalls bricht es ab und gibt die
Aufrufsyntax aus:
pc [-d dir] [-l logdir] [-s signal] [-q] start|stop proc
Der Aufruf des Programms, das pc
starten
soll, kann auch zusätzliche Parameter
enthalten. So startet zum Beispiel
pc start sleep 60
einen 60 Sekunden währenden
sleep
-Prozess im Hintergrund und legt die
PID in der Datei sleep.pid
ab. Den Reigen
beendet
pc stop sleep
(mit oder ohne Parameterangabe 60
) abrupt
wieder.
pc
funktioniert
Das Getopt::Std
-Modul, das Zeile 7 in Listing pc
hereinzieht, behandelt die Kommandozeilenoptionen und setzt entsprechende
Einträge im Hash %opts
. Der Befehl
getopts('d:l:s:q', \%opts);
legt fest, dass die Optionen -d
, -l
und -s
, falls sie gesetzt sind, jeweils ein Argument erwarten. -q
steht für sich selbst, ist also ein boolean Flag. Wird pc
mit
pc -s KILL -q stop sleep
aufgerufen, ist $opts{s}
auf "KILL"
gesetzt und $opts{q}
auf 1
.
Zeile 10 definiert den Prototyp der usage
-Funktion und legt fest, dass sie einen Skalar als Argument erwartet.
Danach kann usage
auch ohne Klammern aufgerufen werden, wie z.B. in Zeile 14. Dort wird
überprüft, ob die Anzahl der Parameter, die übrigbleiben, nachdem
getopt
alle definierten Optionen bearbeitet und entfernt hat, zwei unterschreitet.
In diesem Fall fehlt entweder eine Aktion oder das Skript, das pc
starten oder stoppen soll. Zeile 15 prüft, ob
start
oder stop
vergessen wurde.
Zeigt die -d
-Option an, dass pc
zunächst in ein bestimmtes Verzeichnis wechseln soll, führt Zeile 18 dies
durch und bricht ab, falls die Aktion aus irgend welchen Gründen
fehlschlägt. Wie alle Fehlermeldungen wird auch diese über die usage
-Funktion mit einer kleinen Hilfsanweisung mit den gültigen
Aufrufparametern von pc
begleitet, bevor das Programm abbricht.
Zeile 21 definiert den Namen der Datei, in der die Prozess-ID eines
gestarteten Programms abgelegt wird. Wurde mit -l
ein anderes Verzeichnis spezifiziert, hängt Zeile 22 den Dateinamen hinter
den angegebenen Pfad. Das Modul File::Spec
tut dies betriebssystemunabhängig, unter Unix kommt dies auf's selbe heraus
wie "$opt{l}/$pidf"
.
Die if
-else
-Logik ab Zeile 24 verzweigt zu start_proc
oder
stop_proc
, je nachdem ob wir einen Prozess starten oder stoppen wollen. Im Startfall
übergibt Zeile 25 der Funktion start_proc
nicht nur die Pfadangabe zur PID-Datei, sondern fügt auch noch den Namen
des zu startenden Prozesses mit allen angegebenen Parametern bei.
@ARGV[1..$#ARGV]
ist ein sogenanntes Array-Slice, das alle Argumente aus @ARGV
enthält, außer dem ersten, das auf
start
oder stop
gesetzt ist und beim eigentlichen Starten des Prozesses nichts verloren
hat.
start_proc
ab Zeile 31 entpuffert zunächst die Standard-Ausgabe, so dass es die mit print
geschriebenen Meldungen auch dann gleich anzeigt, wenn noch kein
Newline-Zeichen angegeben wurde. $| = 1
tut genau dies, und um die Einstellung nicht global durchzuführen, wird es
mit local
ausgezeichnet -- lexikalischen Scope mit my
kann man leider nur mit ``normalen'' Variaben, $|
ist ein Spezialteil.
Stellt Zeile 38 fest, dass eine PID-Datei schon existiert, bricht
pc
den Vorgang mit der Meldung "Already running"
ab, denn es geht davon aus, dass der Prozess schon gestartet wurde und noch
läuft und erst mit pc stop
gestoppt werden muss.
Zeile 43 erzeugt mit fork
einen Sohnprozess. Der Vater, der normal weiterläuft erhält in $pid
die Prozess-ID des Sohnes zugewiesen, während im Sohn-Universum $pid
auf 0
gesetzt ist.
Der Vater schreibt dann in Zeile 46 schnell die Sohn-PID in die PID-Datei
und kehrt nach dem if
-Block mit einer Meldung zurück, falls pc
nicht gerade mit der -q
-Option zum Schweigen verdonnert wurde.
Der Sohn überlädt in Zeile 51 mit exec
den gegenwärtigen Prozess mit einem externen Programm, dessen Name und
Kommandozeilenparamter in @proc
liegen. exec
ist ein schwarzes Loch, aus dem niemand jemals lebend zurückkehrt -- außer
es ging etwas grob schief, wie wenn etwa der Prozess zum Überladen nicht
gefunden wurde. In diesem Fall springt Zeile 52 ein und bricht mit einer
Fehlermeldung ab.
stop_proc
ab Zeile 59 versucht, einen laufenden Prozess durch das Senden eines
Signals zu beenden. Hierzu versucht Zeile 66, die Prozess-ID aus der
PID-Datei zu extrahieren. Existiert diese nicht, liegt der Schluß nahe,
dass der Prozess nie mit pc
gestartet wurde und Zeile 68 bricht den Vorgang ab.
Andernfalls sendet Zeile 74 dem Prozess mit dem kill
-Signal das Signal Nummer 0
, was auf den Prozess keine Auswirkungen hat, aber schiefgeht, falls der
Prozess nicht existiert. In diesem Fall denkt pc
, der Prozess wäre schon gestoppt, löscht die PID-Datei und bricht rasch
mit einer Fehlermeldung ab.
Geht Zeile 74 gut, existiert der Prozess mit der angegebenen PID und das
Konstrukt zwischen 80 und 89 versucht zehn Mal im Sekundentakt, den Prozess
mit einem SIGTERM-Signal zum Beenden zu überreden. Falls pc
mit der Option -s
und einem Signalnamen wie KILL
, HUP
, INT
etc. aufgerufen wurde, nimmt der kill
-Befehl in Zeile 81 diesen stattdessen. Alle verfügbaren Signal definiert
die include
-Datei
/usr/include/asm/signal.h
).
Zeile 82 prüft hinterher immer, ob der Prozess immer noch herumhängt, und
falls ja, geht's in die nächste Runde. Nach zehn ist endgültig Schluss und pc
gibt auf.
pc -s KILL stop myapp
holt eine vorher gestartete Applikation myapp
aber garantiert auf den Boden der Tatsachen zurück, denn das SIGKILL
-Signal mit der Nummer 9 bedeutet das unweigerliche Ende, ohne dass der
Prozess auch noch eine Chance zum Aufräumen hätte. Stellt pc
fest, dass der Prozess gekillt wurde, löscht es in Zeile 84 die PID-Datei
und beendet sich.
Die usage
-Funktion ab Zeile 95 extrahiert aus $0
, das den Programmpfad enthält (z. B. ./myapp
) den Programmnamen (myapp
) und gibt die unterstützten Optionen aus.
Startet fleissig und stoppt selten! Viel Spass damit! (hmi)
Listing: pc |
001 #!/usr/bin/perl 002 ################################################## 003 # pc [-d dir] [-l logdir] [-s signal] [-q] 004 # start|stop proc 005 ################################################## 006 007 use Getopt::Std; 008 use File::Spec; 009 010 sub usage($); 011 012 getopts('d:l:s:q', \%opts); 013 014 usage "Wrong argument count" if @ARGV < 2; 015 usage "No action" unless $ARGV[0] eq "start" || 016 $ARGV[0] eq "stop"; 017 018 chdir($d) or usage "Cannot chdir to $d" if 019 exists $opts{d}; 020 021 my $pidf = "$ARGV[1].pid"; 022 $pidf = File::Spec->catfile($opts{l}, 023 $pidf) if $opts{l}; 024 if($ARGV[0] eq "start") { 025 start_proc($pidf, @ARGV[1..$#ARGV]); 026 } else { 027 stop_proc($pidf); 028 } 029 030 ################################################## 031 sub start_proc { 032 ################################################## 033 my ($pidf, @proc) = @_; 034 local $| = 1; 035 036 print "Starting @proc ... " unless $opts{q}; 037 038 if(-f $pidf) { 039 print "Already running\n"; 040 exit 0; 041 } 042 043 defined(my $pid = fork()) or die "Fork failed"; 044 045 if($pid != 0) { # Father 046 open LOG, ">$pidf" or 047 usage "Cannot open $pidf"; 048 print LOG "$pid\n"; 049 close LOG; 050 } else { # Son 051 exec @proc; 052 usage "Cannot start \"$proc[0]\""; 053 } 054 055 print "started\n" unless $opts{q}; 056 } 057 058 ################################################## 059 sub stop_proc { 060 ################################################## 061 my $pidf = shift; 062 local $| = 1; 063 064 print "Stopping ... " unless $opts{q}; 065 066 if(! open LOG, "<$pidf") { 067 print "No instance running\n"; 068 exit 0; 069 } 070 071 my $pid = <LOG>; 072 close LOG; 073 074 if(! kill 0, $pid) { 075 print "Already Stopped\n"; 076 unlink $pidf or die "Cannot unlink $pidf"; 077 return; 078 } 079 080 foreach (1..10) { 081 kill $opts{s} || "TERM", $pid; 082 if(! kill 0, $pid) { 083 print "stopped\n" unless $opts{q}; 084 unlink $pidf or 085 die "Cannot unlink $pidf"; 086 return; 087 } 088 sleep(1); 089 } 090 091 print "Can't stop it - giving up.\n"; 092 } 093 094 ################################################## 095 sub usage($) { 096 ################################################## 097 (my $prog = $0) =~ s#.*/##g; 098 print "$prog: $_[0].\n"; 099 print "usage: $prog [-d dir] [-l logdir] " . 100 "proc start|stop\n"; 101 exit 1; 102 } |
Der Autor |
Michael Schilli arbeitet als Web-Engineer für AOL/Netscape in Mountain View, Kalifornien. Er ist Autor des 1998 bei Addison-Wesley erschienenen (und 1999 für den englischsprachigen Markt als "Perl Power" herausgekommenen) Buches "GoTo Perl 5" und unter michael@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com |
Copyright © 2000 Linux New Media AG