Informationen sammeln |
Auch wenn UNIX-Maschinen normalerweise extrem zuverlässig sind, gibt es Stromausfälle, Programmierfehler, Anwenderfehler, Hardwareausfälle oder die gefürchteten Sonnenwinde und UFOs (Beides beliebte Administratorenausreden, wenn die Ursache einer Panne nur mit großem Aufwand feststellbar wäre), die einem Administrator das Leben schwer machen. In diesem Fall ist es wichtig, die Ursache der Zwangspause möglichst schnell zu ermitteln. Da in der Zeit dieser Suche normalerweise alle anderen Mitarbeiter nichts tun, außer die Gehaltskasse der Firma zu belasten, sollte man schnell und rational handeln.
Der erste Blick, noch bevor man sich einloggt, sollte der Konsole gelten, sofern man eine solche hat. Hier stehen gegebenfalls Meldungen, die weiterscrollen könnten und die, aus welchen Gründen auch immer, vielleicht nicht in die Protokolldateien einfließen. Stellt man tatsächlich fest, dass Informationen auf der Konsole nicht in die Protokolldateien einfließen, sollte man die Konfiguration des syslog Dämons genauer betrachten.
gaston# uname -a Linux gaston 2.2.16 #1 Wed Aug 2 20:22:26 GMT 2000 i686 unknown hpsrv$ uname -a HP-UX hpsrv B.10.10 A 9000/816 1962328252 two-user license note$ uname -a FreeBSD note.willemer.edu 4.4-RELEASE FreeBSD 4.4-RELEASE #0: Tue Sep 18 11:57:08 PDT 2001 murray@builder.FreeBSD.org:/usr /src/sys/compile/GENERIC i386 |
Flag | Bedeutung | Beispiel Linux | Beispiel HP | Beispiel FreeBSD |
---|---|---|---|---|
-s | Name des Betriebssystems | Linux | HP-UX | FreeBSD |
-n | nodename | gaston | hpsrv | note.willemer.edu |
-r | release | 2.2.16 | B.10.10 | 4.4-RELEASE |
-m | machine | i686 | 9000/816 | i386 |
Die Informationen von uname sollte man auch greifbar haben, wenn man eine Hotline kontaktiert. Die Wahrscheinlichkeit ist hoch, dass man nach diesen Informationen gefragt wird.
news.crit /var/log/news |
Das bedeutet, dass die Fehler, die vom Newssystem erzeugt werden, die mindestens kritisch sind, in die Datei /var/log/news abgestellt werden sollen.
Die Facility, also der Verursacher, kann folgende Werte annehmen:
Facility | Verursacher |
---|---|
kern | Kernel |
user | Anwenderprogramme |
sendmail und Kollegen | |
daemon | Systemhintergrundprozesse |
auth | Authorisierungsbefehle |
lpr | Das Drucksystem |
news | Der Newsserver |
uucp | UUCP |
cron | cron Dämon |
mark | Zeitstempel |
local0 - local7 | frei verfügbar |
* | alle |
Mit dem Level kann festgelegt werden, wie gravierend das Problem sein muss, damit es in der Protokolldatei erscheint. Für jede Facility kann ein eigener Level bestimmt werden, ab dem eine Meldung gemacht wird. Die folgende Tabelle zeigt die Level mit nach unten abnehmender Bedeutung.
Level | Bedeutung |
---|---|
emerg | Panik |
alert | Alarm |
crit | kritisch |
warning | Warnmeldungen |
notice | Hinweise |
info | informell |
debug | Ablaufnotizen von Programmen in der Entwicklung |
Normalerweise wird als Ausgabe eine Datei oder auch das Konsolendevice angegeben. Die entsprechenden Dateien müssen nach dem Eintrag angelegt werden. Der syslog-Dämon kann nämlich keine Dateien erzeugen. Es können auch mehrere Ziele angegeben werden, die durch Kommata getrennt werden. Die Dateien müssen mit vollem Pfadnamen angegeben werden. Steht dem Ziel ein @ voran, kann eine IP-Nummer oder ein Hostname angegeben werden. Dann wird die Fehlermeldung an den syslog-Dämon des betreffenden Rechners versandt.
# /etc/syslog.conf - Configuration file for syslogd(8) *.emerg * mail.* /var/log/mail *.info /var/log/messages |
Der Stern als Ziel, wie oben bei den Emergencynachrichten bedeutet, dass die Mitteilung sofort an alle angemeldeten Terminals versandt wird. In der nächsten Zeile werden alle Nachrichten, die Mails betreffen in die Datei mail eingetragen. Alle restlichen Meldungen, die mindestens den Level info haben, werden in der Datei messages abgestellt, also auch die emerg-Meldungen, die damit an zwei Ziele verteilt werden.
gaston# cd /var/log gaston# tail -f messages Jun 18 20:11:36 gaston pppd[2592]: sent [LCP TermReq id=0x4 "Us er request"] Jun 18 20:11:36 gaston pppd[2592]: rcvd [LCP TermAck id=0x4] Jun 18 20:11:36 gaston pppd[2592]: Connection terminated. Jun 18 20:11:36 gaston pppd[2592]: Connect time 0.4 minutes. Jun 18 20:11:36 gaston pppd[2592]: Sent 3333 bytes, received 25 062 bytes. Jun 18 20:11:36 gaston pppd[2592]: Waiting for 1 child processe s... Jun 18 20:11:36 gaston pppd[2592]: script /etc/ppp/ip-down, p id 2616 Jun 18 20:11:36 gaston pppd[2592]: Script /etc/ppp/ip-down fini shed (pid 2616), status = 0x0 Jun 18 20:11:36 gaston pppd[2592]: Exit. Jun 18 20:26:54 gaston su: (to root) arnold on /dev/pts/6 |
tail -2000 messages > guckmal |
Damit sind die letzten 2000 Zeilen in der Datei guckmal gelandet. Mit dieser Dateigröße lässt sich leicht arbeiten.
> /var/log/messages |
gaston# cd /var/log gaston# fuser messages messages: 378 |
Die Datei messages ist vom Prozess 378 im Zugriff. Vermutlich ist das der syslogd.
gaston# mv messages messages.$(date +%y%m%d) gaston# fuser messages.$(date +%y%m%d) messages.020224: 378 |
Die Datei wurde nun umbenannt. Immer noch ist sie im Zugriff von Prozess mit der PID 378, obwohl der Name der Datei sich geändert hat.
gaston# touch messages gaston:/var/log # l messages* -rw-r--r-- 1 root root 0 Feb 24 12:07 messages -rw-r----- 1 root root 2447648 Feb 24 12:06 messages.020224 gaston# chmod 640 messages gaston# l messages* -rw-r----- 1 root root 0 Feb 24 12:07 messages -rw-r----- 1 root root 2447648 Feb 24 12:06 messages.020224 |
Die neue Protokolldatei wurde erfolgreich angelegt und mit den Rechten der alten versehen. Auch der Eigentümer stimmt überein.
gaston# ps -ef | grep syslog root 378 1 0 10:24 ? 00:00:00 /sbin/syslogd root 1486 1465 0 12:09 pts/2 00:00:00 grep syslog gaston# kill -SIGHUP 378 |
Sicherheitshalber wird noch einmal geprüft, ob der Prozess 378 tatsächlich der syslogd ist. PID 1486 ist der eigene Prozess, der aus der Prozessliste herausfiltert. Nun wird mit dem kill der syslogd aufgefordert, die Konfiguration neu zu lesen und damit alle Dateien neu zu eröffnen.
gaston# fuser messages messages: 378 gaston# gzip messages.$(date +%y%m%d) |
Erwartungsgemäß hat der syslogd die Datei messages eröffnet und damit die gerade neu untergeschobene. Die alte Protokolldatei kann gepackt werden.
Mit Hilfe des Befehls \verb+date+ wird der neue Dateinamen mit dem aktuellen Datum gekennzeichnet. Die Konstruktion $(Kommando) bewirkt dasselbe wie mit den so genannten Backquotes `Kommando`, lässt sich aber etwas leichter lesen. Es wird das Ergebnis des eingeklammerten Kommandos ermittelt und in den Befehl integriert.
Da solche Arbeiten auf Produktionsmaschinen regelmäßig auftreten, ist es ein naheliegender Gedanke, sie in die crontab zu integrieren.
Einige Programme melden ihre Fehler nicht über den syslog-Dämon, sondern
verbreiten ihre Klagen per Mail. Prominentester Vertreter dieser Art ist der
Druckdämon, der fehlgegangene Drucke an den Benutzer sendet, der den Auftrag
gab. crontab und at senden ihre komplette Ausgabe an den
Besitzer. Darum gehört ein mindestens lokales Mailsystem zur
Standardinstallation. Dazu gehört auch das Standardtool mail.
Leider ist die Benutzung etwas antiquiert. Es ist darum eine gute Idee, ein
einfaches, aber übersichtliches Mailprogramm zu installieren wie elm
oder pine. Ein grafisches Tool braucht recht viele Ressourcen,
die bei einem Zusammenbruch des Servers vielleicht nicht zur Verfügung stehen.
Man kann und sollte die Mail, die an root geht,
weiterleiten.
Steht der Rechner nicht unter besonderer
Aufsicht, kann man sie sogar an einen fremden Rechner senden. Auf diese Weise
merkt man schnell, dass etwas schiefläuft. Dennoch sollte man ein Duplikat
auf dem Rechner lassen, so dass die Mail an Ort und Stelle ist, wo das
Problem ausgelöst wurde.
Briefe aus dem Nirvana
vi statt mail
Sollte ein Auslesen der Mail mit Hilfe der installierten Programme nicht
möglich sein, kann man als root natürlich auch die E-Mail
lesen, indem man mit dem Editor im Verzeichnis /var/spool/mail die
Datei root durchsieht.
gaston> uptime 5:11pm up 7:33, 1 user, load average: 0.06, 0.01, 0.00 gaston> uptime 11:11pm up 14:40, 1 user, load average: 0.95, 0.47, 0.28 |
Im Beispiel sind zwei unterschiedliche Aufrufe von uptime angezeigt. Die erste Meldung besagt, dass es 17:11 ist, der Rechner seit 7 Stunden und 33 Minuten läuft, dass ein Benutzer eingeloggt ist und dass es der Maschine richtig langweilig ist. Der zweite Aufruf wurde parallel zu einem Compilelauf kurz vor dessen Ende ausgeführt. Hier ist es 23:11, die Maschine läuft sein 14 Stunden, 40 Minuten, hat ebenfalls einen Benutzer und die Systemlast liegt bei 0.95. Andere Prozesse würden auf diesem System noch flüssig laufen, allerdings nicht ganz so schnell wie auf einer unbeschäftigten Maschine.
Befehl | System |
---|---|
ps -alx | BSD und Linux: zeigt auch PID und PPID |
ps -aux | BSD: zeigt auch Benutzer |
ps -elf | System V, beispielsweise SCO und HP-UX |
Ohne Parameter werden nur die Prozesse gezeigt, die von dieser Sitzung erzeugt wurden. In der ersten Tabelle sind die Optionen, wie sie BSD-Systeme verwenden.
Option | Anzeige |
---|---|
-a | alle Prozesse, nicht nur die eigenen |
-x | zeigt auch Prozesse, die keinem Terminal zugeordnet sind (z. B. Dämonen) |
-u | zeigt den Benutzer des Prozesses und die Startzeit |
-l | zeigt Prozess-ID des Vaterprozess und nice-Wert |
Auf den System V Maschinen verwendet man meist -elf. Die Bedeutung dieser Parameter sind:
Option | Anzeige |
---|---|
-e | zeigt alle auf dem System laufenden Prozesse |
-l | zeigt Größe, Status und Priorität jedes Prozesses |
-f | zeigt UserID, PID, PPID und Startzeit |
Daneben gibt es große Menge anderer Optionen, die diverse Informationen zu den Prozessen liefern. Die Manpage von ps liefert eine vollständige Liste für Ihr System.
Die Titel der Prozessliste geben Auskunft über die angezeigten Informationen.
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 448 208 ? S 09:37 0:04 init [5] root 2 0.0 0.0 0 0 ? SW 09:37 0:00 [keventd] root 3 0.0 0.0 0 0 ? SW 09:37 0:00 [kapm-idled] root 4 0.0 0.0 0 0 ? SWN 09:37 0:00 [ksoftirqd_CPU0] root 5 0.0 0.0 0 0 ? SW 09:37 0:00 [kswapd] root 6 0.0 0.0 0 0 ? SW 09:37 0:00 [bdflush] root 7 0.0 0.0 0 0 ? SW 09:37 0:00 [kupdated] root 8 0.0 0.0 0 0 ? SW< 09:37 0:00 [mdrecoveryd] root 11 0.0 0.0 0 0 ? SW 09:37 0:00 [scsi_eh_0] root 13 0.0 0.0 0 0 ? SW 09:37 0:00 [khubd] root 390 0.0 0.1 1396 628 ? S 09:39 0:00 /sbin/syslogd root 393 0.0 0.3 1772 992 ? S 09:39 0:00 /sbin/klogd -c 1 root 399 0.0 0.2 2308 828 ? S 09:39 0:00 /usr/sbin/sshd bin 452 0.0 0.1 1340 428 ? S 09:39 0:00 /sbin/portmap arnold 1249 0.0 19.5 108924 62496 ? R 10:01 0:15 /opt/office52/soffice arnold 2353 0.0 0.5 2840 1620 pts/4 S 17:11 0:00 /bin/bash arnold 2416 0.0 0.5 2692 1716 pts/4 R 17:32 0:00 ps aux arnold 2417 0.0 0.2 1976 824 pts/4 R 17:32 0:00 less |
Dieser Ausschnitt der Prozessliste zeigt in der ersten Zeile den Prozess init mit der PID 1. Die erste Zeile zeigt in den Überschriften, was die einzelnen Spalten anzeigen. Diese Liste ist durch den Befehl ps aux entstanden. Je nach System und Parametern sind die Spalten anders. Im folgenden sollen die wichtigsten Spalten beschrieben sein, die man durch ps anzeigen lassen kann. Welche Informationen durch welche Flags angezeigt werden, entnimmt man am besten der Manpage von ps auf dem jeweiligen System.
Kürzel | Beschreibung |
---|---|
USER | Benutzer, der den Prozess gestartet hat |
PID | Prozess ID, wird als Argument für kill verwandt |
PPID | Die Prozess ID des Elternprozesses |
PGID | Prozessgruppen ID |
SID | Session ID |
PRI | Priorität des Prozesses. Je niedriger, desto mehr Rechenzeit |
NI | Nice-Wert oder SY für Systemprozesse |
%CPU | Anteil an der CPU-Auslastung |
%MEM | Anteil an der Speicherauslastung |
VSZ | Virtuelle Prozessgröße |
RSS | Größe des residenten Speichers |
TTY | das Kontrollterminal des Prozesses |
STAT | Status des Prozesses, siehe unten |
SIZE oder SZ | Größe des Prozesses |
START oder STIME | Startzeitpunkt |
TIME | Die bisher verbrauchte CPU-Zeit |
WCHAN | Kernelfunktion, auf die der Prozess wartet |
COMMAND | Das Kommando, mit dem der Prozess gestartet wurde |
Der Prozessstatus STAT kann verschiedene Zeichen haben. R bedeutet runnable. Der Prozess ist also gerade aktiv. Im Beispiel oben sind dies beispielsweise der ps mit der PID 2416, der die Anzeige erzeugt hat. Das immer wieder auftauchende S zeigt an, dass der Prozess schläft, also auf ein Ereignis wartet, das ihn weckt.
Status | Bedeutung |
---|---|
R | ausführbar |
S | schlafend |
T | gestoppt |
D | auf der Festplatte wartend |
W | Prozess ist ausgelagert |
Der Befehl ps kann von jedem Anwender aufgerufen werden, nicht nur von root. Dementsprechend ist es ein massives Sicherheitsloch, wenn ein Programm ein Passwort als Parameter im Klartext entgegen nimmt.
11:32am up 3:04, 1 user, load average: 0.11, 0.16, 0.07 95 processes: 93 sleeping, 2 running, 0 zombie, 0 stopped CPU states: 1.7% user, 2.3% system, 0.0% nice, 95.8% idle Mem: 320136K av, 189100K used, 131036K free, 0K shrd, 5756K buff Swap: 128512K av, 0K used, 128512K free 118592K cached |
Mit h kann man sich die möglichen Kommandos anzeigen lassen. Aus top heraus kann man renice durch die Taste r oder einen kill (siehe unten) über die Taste k aufrufen. Anschließend fragt das Programm, welcher Prozess gemeint war und bittet um dessen PID. Mit der Taste q kann man das Programm wieder verlassen.
Das Programm top ist Bestandteil der Open Source Systeme wie Linux oder FreeBSD und damit auch MacOS X. Für die anderen Systeme kann es aber frei bezogen werden.
Quelle: http://www.groupsys.com/top/index.html
Nicht immer mit Tötungsabsicht: kill
Das Kommando kill sendet Signale an einen Prozess. Wie der Name schon
sagt,
führt dies meist zum Tod des Prozesses. Prozesse können aber viele Signale
fangen und verarbeiten. Dadurch ist eine Steuerung von außen möglich, was
gerade
für Hintergrundprozesse von Bedeutung ist. Auf diese Weise ist ein Booten
zum Neueinlesen der Konfiguration unter UNIX nicht erforderlich.
Damit ein Programm auf Signale reagiert, muss eine
explizite Behandlung der
Signale durch das Programm erfolgen.
Die Signale sind durchnummeriert, wenn auch nicht auf allen Systemen unbedingt
gleich und sie haben Namen. Beides kann als Parameter für kill mit
einem Minuszeichen gekennzeichnet zur Spezifikation verwendet werden.
Die folgende Tabelle zeigt die meist verwendeten Signale und deren Nummer,
sofern sie auf allen Systemen eindeutig ist.
Nr. | Bedeutung SIGHUP | 1 | Sitzungsende. Signal an Dämonen ihre Parameter-Datei neu lesen | SIGINT2 | Interrupt-Taste (Delete-Taste oder ctrl-C) | SIGKILL9 | Sofortiger Abschuss, kann vom Programm nicht abgefangen werden | SIGTERM15 | Aufforderung an den Prozess, sich regulär zu beenden. | SIGSTOPhält den Prozess an, ohne ihn zu beenden. | SIGCONTführt einen gestoppten Prozess fort |
SIGHUP
nohup unterbindet SIGHUP
Ursprünglich wurde das Signal SIGHUP versandt, wenn das Terminal ausgeschaltet
wurde, auf dem eine Anwendung gestartet worden war. Auch in den Hintergrund
gestellte Prozesse empfangen das Signal, wenn der Anwender, der sie gestartet
hat, sich abmeldet. Will man dieses Verhalten unterbinden, muss dem Kommando
der Befehl nohub vorangestellt werden. Das Signal kann zwar vom
Programm gefangen werden. Programme, die das Signal nicht explizit bearbeiten,
werden beendet.
SIGHUP fordert zum Neueinlesen der Konfiguration auf
Seine wichtigste Anwendung hat das Signal darin, dass die meisten Dämonen
auf ein kill -1 ihre Konfiguration wieder neu lesen und sich
entsprechend
neu initialisieren. Aber nicht alle Hintergrundprozesse reagieren auf
kill -1. So stirbt beispielsweise der lpd, wenn man ihm in
der Hoffnung,
er würde dann die /etc/printcap lesen, mit kill -1 anschießt.
In diesem Fall
ist das aber auch kein Drama, da man nach Änderungen der printcap
tatsächlich
den lpd abschießen und neu starten kann.
renice +5 987 |
kill -SIGSTOP 987 ... kill -SIGCONT 987 |
Terminieren
Erst wenn diese Möglichkeiten nicht mehr bestehen oder Sicherheit darüber
besteht, was der Prozess tut oder Gefahr in Verzug ist, wird man den Prozess
beenden. Um dies zu tun, verwendet man zunächst SIGTERM. Dies ist auch die
Grundeinstellung von kill, wenn man keinen Parameter angibt.
Das Signal
SIGTERM wird von den meisten UNIX-Programmen gefangen und führt dazu, dass
der Prozess seine Daten sichert und geregelt endet. Beim
Herunterfahren der Maschine bekommt jedes noch laufende Programm ein SIGTERM
zugesandt. Dann hat das Programm fünf Sekunden Zeit, die Daten zu sichern.
Anschließend kommt das SIGKILL, das auch dem störrigsten Prozess
die Lampe ausbläst.
gaston> fuser unix.ps unix.ps: 1176 1190 |
Wendet man fuser auf ein Verzeichnis an, kann man sehen, welche Prozesse unterhalb dieses Verzeichnisses gestartet worden sind. Das ist wichtig, wenn man beispielsweise ein Dateisystem per umount ausklinken möchte und die Meldung erscheint, dass das Gerät noch in Benutzung ist.
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME init 1 root mem REG 3,5 333780 178159 /sbin/init portmap 386 root mem REG 3,5 28184 178286 /sbin/portmap portmap 386 root mem REG 3,5 342535 275298 /lib/ld-2.1.3.so portmap 386 root mem REG 3,5 44729 275322 /lib/libutil.so.1 portmap 386 root mem REG 3,5 4070406 275303 /lib/libc.so.6 syslogd 404 root mem REG 3,5 29252 485827 /usr/sbin/syslogd syslogd 404 root mem REG 3,5 342535 275298 /lib/ld-2.1.3.so syslogd 404 root mem REG 3,5 4070406 275303 /lib/libc.so.6 |
In der zweiten Spalte steht die PID, die man leicht per grep oder besser per awk herausfiltern kann. Der folgende Aufruf liefert alle Dateien, die der Prozess mit der PID 719 offen hat.
lsof | awk '{if ( $2==719 ) print $9 }' |
Da nicht jeder Administrator auswendig den Syntax von awk zur Hand hat, funktioniert natürlich auch die einfachere Variante mit grep.
lsof | grep 719 |
Der Nachteil dieser unsportlichen Lösung ist natürlich, dass nun auch Zeilen herausgegriffen werden, die an anderer Stelle als in Spalte 2 die Zahl 719 haben.
Wenn man als Administrator eine Datei namens core findet, ist zunächst deren
Alter interessant. Das Alter gibt Auskunft über den Zeitpunkt des
Zusammenbruchs. Ist der verantwortliche Programmierer greifbar, wird er sich
für diese Datei interessieren. Ansonsten gehören core Dateien eher zum
lästigen Abfall, der bei der Entwicklung von Software nun einmal entsteht.
Darum steht bei einigen Maschinen ein Eintrag in der crontab, alle älteren
Dateien namens core zu suchen und zu löschen. Auf einer Produktionsmaschine
sollten core dumps nicht entstehen. Insofern wäre eine Beseitigung solcher
Spuren eines Zusammenbruchs nicht sehr sinnvoll.
Ein kernel panic ist ein Fehler, der so heftig ist, dass das Betriebssystem
alle Tätigkeiten einstellt. Die letzte Aktion besteht darin, einen
Speicherabzug in die Swappartition zu schreiben.
Als Ursache eines kernel panic kommen im Normalfall eigentlich nur Probleme
mit Hardware oder deren Treiber in Frage. Anwendungsprogramme haben keinen
Zugriff auf Bereiche, die das System derart außer Tritt bringen können.
Selbst Dämonen und Systemprozesse würden im Falle eines Amoklaufs auf die
Ausnahmebehandlung des Betriebssystems laufen und einen schnellen Tod finden.
Programmzusammenbrüche (core dump)
Todesstrafe für Fehlverhalten von Prozessen
Wenn ein Programm unter UNIX Dinge tut, die es nicht darf, dann wird ihm
vom System einfach die Betriebserlaubnis entzogen. Zu diesen unerlaubten Beschäftigungen
gehört das Dividieren durch Null oder der Griff in den Speicher anderer Prozesse.
UNIX teilt den Prozessen klar abgegrenzte Speicherbereiche zu und merkt, wenn
ein Prozess in die Systembereiche eindringen möchte. In einem solchen Fall
sendet UNIX dem Prozess ein Signal, im letzten Fall SIGSEGV, eine segment
violation. Man könnte das frei mit Grenzverletzung übersetzen.
core ist ein Speicherabzug
Wenn UNIX ein Programm auf diese Weise beendet, schreibt es den Speicherbereich
des Prozesses in eine Datei namens core in das aktuelle Arbeitsverzeichnis.
Da zum Speicher auch
der Stack gehört, kann ein Debugger
feststellen, in welcher Funktion der Zusammenbruch erfolgte.
Diese Information kann für den Programmierer eine große Hilfe sein.
Systemabsturz (kernel panic)
Diese Seite basiert auf Inhalten aus dem Buch
Arnold Willemer: Wie werde ich UNIX-Guru
Verlagsrechte bei galileo computing
Homepage |
(C) Copyright 2002 Arnold Willemer
|