runwhen von Paul Jarc ist eine praktische Cron-Alternative, die gegenüber klassischen Cronjobs viele Vorteile bietet:
Keine Kollisionen. Man kann diesen Punkt gar nicht oft genug betonen. Jeder, der schon einmal einen Cronjob verfasst hat, der z.B. alle fünf Minuten laufen sollte und der dann aber für einen Durchlauf länger als fünf Minuten braucht, hat gesehen, wie ärgerlich es ist, wenn mehrere Instanzen eines Cronjobs gleichzeitig laufen (nehmen wir zum Beispiel einen Job, der Newsletter verschickt, mit dem Versand aber eine Stunden beschäftigt ist und währenddessen alle fünf Minuten weitere Instanzen startet, die ebenfalls anfangen, den gleichen Newsletter an die gleichen Empfänger zu verschicken …).
Sekundengenaue Ausführung. Mit Cron können Jobs maximal minutengenau ausgeführt werden; runwhen arbeitet sekundengenau. Es ist also kein Problem, einen Job mehrmals in der Minute laufen zu lassen, beispielsweise, weil er die Verfügbarkeit eines Dienstes prüfen soll und Probleme nicht erst nach einer Minute bemerken soll.
Integriertes Logging. runwhen-Jobs laufen unter den
daemontools und können sich dessen Fähigkeit, sämtliche Ausgaben direkt in einen Logging-Prozess zu schicken, zunutze machen.
Auskunft, wann die nächste Ausführung stattfindet. Unmittelbar beim Start wird ins Logfile geschrieben, wann die nächste Ausführung des Scripts zu erwarten ist. Damit siehst du sofort, ob das von dir gewählte Zeitschema korrekt angegeben wurde.
„Mach's sofort“-Feature. Du hast jederzeit die Möglichkeit, einen Job jetzt sofort anzustoßen, ungeachtet der eigentlich geplanten Zeit. Dabei läuft der Job unter exakt der gleichen Umgebung wie sonst auch, was jeden freuen dürfte, der schon mal darüber gestolpert ist, dass unter Cron das Set an Umgebungsvariablen nicht völlig identisch zu dem der Login-Shell ist.
Temporäres Pausieren. Wie alle Jobs, die unter den Daemontools laufen, kannst du mit einem einzigen svc-Aufruf weitere Ausführungen temporär unterbinden und später jederzeit wieder aktivieren.
Aus Sicht eines Systemadministrators - also aus unserer Sicht - können wir noch ergänzen, dass es im Gegensatz zu Cron ohne ein setuid-Programm wie crontab auskommt und dass es keinen zentralen Daemon benötigt. Zurecht fragt der Autor nach dem Sinn von Konfigurationsdateien wie /etc/cron.allow, wenn doch ein zeitgesteuerter Job eigentlich nicht mehr ist als …
(sleep 1234 && do-some-command) &
… in entsprechend regelmäßigen Wiederholungen, was nun wirklich jeder ohne irgendeinen Daemon ausführen kann.
runwhen-Scripts laufen unter den daemontools und machen sich deren Eigenschaft zunutze, dass ein Job, der sich beendet, automatisch neu gestartet wird. Jener Job sieht insofern so aus:
Berechne die Anzahl der Sekunden, bis der nächste Zeitpunkt nach dem gewählten Zeitschema eintritt
Warte die Anzahl dieser Sekunden
Führe das gewünsche Programm aus
Beende dich, auf dass du von den
daemontools neu gestartet wirst
Einfach - aber effektiv! Die Hauptarbeit erledigt das Programm rw-match, das für die Berechnung der Wartedauer da ist. rw-sleep übernimmt das Warten und die anschließende Ausführung. Zusätzliche Programme ermöglichen noch eine feinere Beeinflussung der Wartezeit, um beispielsweise bestimmte Minimalwerte nicht zu unter- oder bestimmte Maximalwerte nicht zu überschreiten. Paul Jarc stellt ein komplexes Beispiel bereit. In der Regel reicht es aber, sich auf eine einfache Berechnung der Wartezeit zu beschränken.
Wir stellen dir das Script runwhen-conf bereit, das dir einen fertigen Job anlegt, in dem du nur noch dein Zeitschema einstellen musst und ihn dann in dein ~/service-Verzeichnis verlinken musst. Wenn du noch kein eigenes ~/service-Verzeichnis besitzt, so musst du dir zunächst eins anlegen. Du kannst es nicht nur für runwhen, sondern auch noch für viele andere Sachen benutzen. In aller Kürze reicht dafür ein Befehl:
[sandra@xenon ~]$ uberspace-setup-svscan
Erledigt? Prima, dann können wir weitermachen. Im konkreten Fall wollen wir das Programm ~/bin/reminder.pl einmal täglich ausführen lassen (bitte beachte, dass das Programm ausführbar sein muss, also ein chmod +x erfahren haben muss). Der Service soll den Namen reminder bekommen. Und so sieht das aus:
[sandra@xenon ~]$ runwhen-conf ~/etc/run-reminder ~/bin/reminder.pl
Creating the service directories... done
Creating the run script... done
Creating the log/run script... done
Setting restrictive permissions on directories and scripts... done
The service is now nearly set up, but two steps are missing:
* Set the time when you want to run the job through setting the variable
RUNWHEN within /home/sandra/etc/run-reminder/run
* Put a symlink pointing to the service directory into a directory that is
monitored by svscan, for example:
ln -s /home/sandra/etc/run-reminder /service
Entsprechend der Anweisungen bearbeiten wir nun das Script /home/sandra/etc/run-reminder/run mit einem beliebigen Texteditor. Im Kopf wird bereits die Variable RUNWHEN definiert, nur eben mit leerem Wert. Darunter befindet sich direkt eine kleine Dokumentation, welche Möglichkeiten hier bestehen.
Jede zeitliche Bedingung beginnt mit einem Komma (“,“), dann der Angabe der Einheit (z.B. “H“ für Stunde) und dann der Bedingung (z.B. “=9“ für „um 9 Uhr“ oder “/2“ für „alle 2 Stunden“). Für eine tägliche Ausführung um 9 Uhr müsstest du also setzen:
RUNWHEN=",H=9"
Kombinationen sind ebenfalls möglich - soll der Job um 9:30 Uhr laufen, kannst du Stunde und Minute definieren:
RUNWHEN=",H=9,M=30"
Du möchtest, dass der Job nur am 3., 6., 9. … eines Monats, jeweils um 9:30 Uhr läuft? Auch das ist kein Problem:
RUNWHEN=",d/3,H=9,M=30"
Oder vielleicht um Mitternacht des drittletzten Tags eines Monats?
RUNWHEN=",d-3"
Das Prinzip dürfte klar sein. Was dann noch fehlt, ist, den Service zu starten, in dem er per Symlink nach ~/service verlinkt wird:
ln -s ~/etc/run-reminder ~/service/reminder
Innerhalb von 5 Sekunden nimmt der Job seine Tätigkeit auf. Ein Blick ins Logfile verrät den Zeitpunkt der nächsten Ausführung (hier bei RUNWHEN=“,H=9,M=30“:
[sandra@xenon ~]$ tail -f ~/service/reminder/log/main/current | tai64nlocal
2011-03-04 00:18:14.130403500 next run time: 2011-03-04 09:30:00.000000000
Möchtest du den Job jetzt sofort starten, kannst du ihm mittels svc ein ALRM-Signal senden - rw-sleep nimmt das zum Anlass, die restliche Wartezeit unter den Tisch fallen zu lassen und den Job sofort zu starten:
[sandra@xenon ~]$ svc -a ~/service/reminder
Du kannst alle normalen svc-Kommandos auf deine runwhen-Jobs anwenden. Ein …
[sandra@xenon ~]$ svc -d ~/service/reminder
… beispielsweise schaltet den Job aus; er wird dann nicht mehr ausgeführt. Das ist beispielsweise bei häufig laufenen Jobs praktisch, wenn du zwischendurch Änderungen vornehmen möchtest und währenddessen nicht willst, dass der Job weiterhin läuft. Willst du ihn dann wieder starten, sendest du ihm ein up-Signal:
[sandra@xenon ~]$ svc -u ~/service/reminder
Möchtest du einen runwhen-Job stoppen und komplett entfernen, kannst du genauso vorgehen wie bei jedem anderen unter den daemontools laufenden Service - schau dort einfach in der Doku im Abschnitt Einen Daemon entfernen.