Wie dämonisiere ich ein beliebiges Skript unter Unix?


93

Ich hätte gerne einen Daemonizer, der aus einem beliebigen, generischen Skript oder Befehl einen Daemon machen kann .

Es gibt zwei häufige Fälle, mit denen ich mich befassen möchte:

  1. Ich habe ein Skript, das für immer laufen sollte. Wenn es jemals stirbt (oder beim Neustart), starten Sie es neu. Lassen Sie niemals zu, dass zwei Kopien gleichzeitig ausgeführt werden (erkennen Sie, ob eine Kopie bereits ausgeführt wird, und starten Sie sie in diesem Fall nicht).

  2. Ich habe ein einfaches Skript oder einen Befehlszeilenbefehl, den ich für immer wiederholt ausführen möchte (mit einer kurzen Pause zwischen den Läufen). Lassen Sie auch hier nicht zu, dass zwei Kopien des Skripts gleichzeitig ausgeführt werden.

Natürlich ist es trivial, in Fall 2 eine "while (true)" - Schleife um das Skript zu schreiben und dann eine Lösung für Fall 1 anzuwenden, aber eine allgemeinere Lösung löst Fall 2 direkt, da dies für das Skript in Fall 1 als gilt gut (Sie möchten vielleicht nur eine kürzere oder keine Pause, wenn das Skript nicht jemals sterben soll (natürlich, wenn das Skript es wirklich tut) die Pause keine Rolle, nie stirbt).

Beachten Sie, dass die Lösung beispielsweise nicht das Hinzufügen von Dateisperrcode oder PID-Aufzeichnung zu den vorhandenen Skripten umfassen sollte.

Insbesondere möchte ich ein Programm "daemonize", das ich gerne ausführen kann

% daemonize myscript arg1 arg2

oder zum Beispiel

% daemonize 'echo `date` >> /tmp/times.txt'

Dadurch würde eine wachsende Liste von Daten an times.txt angehängt. (Beachten Sie, dass wenn das zu dämonisierende Argument ein Skript ist, das für immer wie in Fall 1 oben ausgeführt wird, Daemonize immer noch das Richtige tut und es bei Bedarf neu startet.) Ich könnte dann einen Befehl wie oben in mein .login einfügen und / oder cron es stündlich oder minutiös (je nachdem, wie besorgt ich war, dass es unerwartet sterben würde).

NB: Das Daemonize-Skript muss sich die Befehlszeichenfolge merken, die es dämonisiert, damit beim erneuten Dämonisieren derselben Befehlszeichenfolge keine zweite Kopie gestartet wird.

Außerdem sollte die Lösung idealerweise sowohl unter OS X als auch unter Linux funktionieren, aber Lösungen für das eine oder andere sind willkommen.

EDIT: Es ist in Ordnung, wenn Sie es mit aufrufen müssen sudo daemonize myscript myargs.

(Wenn ich daran denke, dass alles falsch ist oder es schnelle und schmutzige Teillösungen gibt, würde ich das auch gerne hören.)


PS: Falls es nützlich ist, hier ist eine ähnliche Frage speziell für Python.

Und diese Antwort auf eine ähnliche Frage scheint eine nützliche Redewendung für eine schnelle und schmutzige Dämonisierung eines beliebigen Skripts zu sein:


1
Siehe serverfault.com/questions/311593/… für eine reine Shell-Version
w00t

Antworten:


90

Sie können jede ausführbare Datei in Unix mit nohup und dem Operator & dämonisieren:

nohup yourScript.sh script args&

Mit dem Befehl nohup können Sie Ihre Shell-Sitzung beenden, ohne dass Ihr Skript beendet wird, während & Ihr Skript in den Hintergrund stellt, sodass Sie eine Shell-Eingabeaufforderung erhalten, um Ihre Sitzung fortzusetzen. Das einzige kleinere Problem dabei ist, dass Standardausgang und Standardfehler an ./nohup.out gesendet werden. Wenn Sie also mehrere Skripte in diesem Manor starten, wird deren Ausgabe miteinander verflochten. Ein besserer Befehl wäre:

nohup yourScript.sh script args >script.out 2>script.error&

Dadurch wird Standard an die Datei Ihrer Wahl und Standardfehler an eine andere Datei Ihrer Wahl gesendet. Wenn Sie nur eine Datei für Standardausgang und Standardfehler verwenden möchten, können Sie uns Folgendes tun:

nohup yourScript.sh script args >script.out 2>&1 &

2> & 1 weist die Shell an, den Standardfehler (Dateideskriptor 2) in dieselbe Datei wie den Standardausgang (Dateideskriptor 1) umzuleiten.

Um einen Befehl nur einmal auszuführen und neu zu starten, wenn er stirbt, können Sie dieses Skript verwenden:

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

Das erste Argument ist der Name der zu verwendenden PID-Datei. Das zweite Argument ist der Befehl. Alle anderen Argumente sind die Argumente des Befehls.

Wenn Sie dieses Skript restart.sh nennen, würden Sie es so nennen:

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &

Genial; Danke. Ich frage mich, ob es auch eine Option für eine Verzögerung beim Neustart geben sollte. Oder vielleicht besser, um es einfach in Verbindung damit zu verwenden: stackoverflow.com/questions/555116/…
dreeves

4
Dies behandelt nur SIGHUP, es gibt andere (normalerweise) schwerwiegende Signale, die behandelt werden sollten.
Tim Post

Eine andere Möglichkeit, dieses Skript zu verbessern, besteht wahrscheinlich darin, einen guten Speicherort für die Platzierung der $ PIDFILE zu finden, anstatt die Angabe als Argument zu verlangen. Es räumt nicht einmal nach sich selbst auf! (was mit einem einfach sein sollte trap EXIT)
Steven Lu

Beachten Sie auch, dass die Verwendung von <in testein ASCII-Vergleich und kein ganzzahliger Vergleich ist. Es kann immer noch funktionieren, kann aber zu Fehlern führen.
Steven Lu

Ich habe meine Korrekturen zu diesem Skript hier veröffentlicht .
Steven Lu

33

Ich entschuldige mich für die lange Antwort (siehe Kommentare darüber, wie meine Antwort der Spezifikation entspricht). Ich versuche, umfassend zu sein, damit Sie so gut wie möglich sind. :-)

Wenn Sie in der Lage sind, Programme zu installieren (über Root-Zugriff zu verfügen) und bereit sind, einmalige Schritte zu unternehmen, um Ihr Skript für die Daemon-Ausführung einzurichten (dh mehr als nur die Befehlszeilenargumente anzugeben, die in der Befehlszeile ausgeführt werden sollen, Aber ich muss nur einmal pro Service arbeiten. Ich habe einen Weg, der robuster ist.

Es beinhaltet die Verwendung von Daemontools . Der Rest des Beitrags beschreibt, wie Sie Dienste mit Daemontools einrichten.

Ersteinrichtung

  1. Befolgen Sie die Anweisungen unter Installieren von Daemontools . Einige Distributionen (z. B. Debian, Ubuntu) haben bereits Pakete dafür, verwenden Sie diese also einfach.
  2. Machen Sie ein Verzeichnis namens /service. Das Installationsprogramm sollte dies bereits getan haben, aber nur überprüfen oder manuell installieren. Wenn Sie diesen Speicherort nicht mögen, können Sie ihn in Ihrem svscanbootSkript ändern , obwohl die meisten Daemontools-Benutzer daran gewöhnt sind/service und verwirrt werden, wenn Sie ihn nicht verwenden.
  3. Wenn Sie Ubuntu oder eine andere Distribution verwenden, die keinen Standard verwendet init(dh nicht verwendet /etc/inittab), müssen Sie die vorinstallierte inittabals Basis für die Anordnung verwenden svscanboot, von der aufgerufen werden soll init. Es ist nicht schwer, aber Sie müssen wissen, wie Sie das konfigurieren, das initIhr Betriebssystem verwendet. svscanbootist ein Skript, das aufruft svscanund die Hauptarbeit bei der Suche nach Diensten erledigt. Es wird von aufgerufen init, initum einen Neustart zu veranlassen, wenn es aus irgendeinem Grund stirbt.

Service-Setup

  1. Jeder Dienst benötigt ein Dienstverzeichnis , in dem Reinigungsinformationen über den Dienst gespeichert sind. Sie können auch einen Speicherort für diese Serviceverzeichnisse festlegen, damit sie alle an einem Ort gespeichert sind. Normalerweise benutze ich /var/lib/svscan, aber jeder neue Ort wird in Ordnung sein.
  2. Normalerweise verwende ich ein Skript , um das Dienstverzeichnis einzurichten und viel manuelle Wiederholungsarbeit zu sparen. z.B,

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"

    Wo some-service-nameist der Name, den Sie Ihrem Dienst geben möchten, userist der Benutzer, unter dem dieser Dienst ausgeführt wird, und loguserder Benutzer, unter dem der Logger ausgeführt wird. (Die Protokollierung wird in Kürze erklärt.)

  3. Ihr Dienst muss im Vordergrund laufen . Wenn Ihr Programm standardmäßig einen Hintergrund hat, dies jedoch deaktiviert werden kann, tun Sie dies. Wenn Ihr Programmhintergrund keine Möglichkeit zum Deaktivieren bietet, lesen Sie weiter fghack, obwohl dies einen Kompromiss darstellt: Sie können das Programm nicht mehr mit steuern svc.
  4. Bearbeiten Sie das runSkript, um sicherzustellen, dass es das tut, was Sie möchten. Möglicherweise müssen Sie sleepoben einen Anruf tätigen, wenn Sie erwarten, dass Ihr Dienst häufig beendet wird.
  5. Wenn alles richtig eingerichtet ist, erstellen Sie einen Symlink /service, der auf Ihr Dienstverzeichnis verweist. (Legen Sie keine Dienstverzeichnisse direkt in das Verzeichnis ein. Dadurch /servicewird es schwieriger, den Dienst von svscander Uhr zu entfernen .)

Protokollierung

  1. Die Daemontools-Protokollierungsmethode besteht darin, dass der Dienst Protokollnachrichten in die Standardausgabe schreibt (oder einen Standardfehler, wenn Sie mit Skripten generierte Skripts verwenden mkservice). svscankümmert sich um das Senden von Protokollnachrichten an den Protokollierungsdienst.
  2. Der Protokollierungsdienst übernimmt die Protokollnachrichten von der Standardeingabe. Das von generierte Protokollierungsdienstskript mkserviceerstellt automatisch gedrehte Protokolldateien mit Zeitstempel im log/mainVerzeichnis. Die aktuelle Protokolldatei wird aufgerufen current.
  3. Der Protokollierungsdienst kann unabhängig vom Hauptdienst gestartet und gestoppt werden.
  4. Durch das Durchleiten der Protokolldateien tai64nlocalwerden die Zeitstempel in ein für Menschen lesbares Format übersetzt. (TAI64N ist ein 64-Bit-Atomzeitstempel mit einer Nanosekundenzahl.)

Dienstleistungen kontrollieren

  1. Verwenden Sie svstatdiese Option , um den Status eines Dienstes abzurufen. Beachten Sie, dass der Protokollierungsdienst unabhängig ist und einen eigenen Status hat.
  2. Sie steuern Ihren Dienst (Start, Stopp, Neustart usw.) mit svc. Verwenden Sie beispielsweise Folgendes, um Ihren Dienst neu zu starten svc -t /service/some-service-name. -tbedeutet "senden SIGTERM".
  3. Andere verfügbare Signale umfassen -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2) und -k( SIGKILL).
  4. Verwenden Sie zum Herunterfahren des Dienstes -d. Sie können auch verhindern, dass ein Dienst beim Start automatisch gestartet wird, indem Sie eine Datei mit dem Namen downim Dienstverzeichnis erstellen .
  5. Verwenden Sie zum Starten des Dienstes -u. Dies ist nur erforderlich, wenn Sie es zuvor heruntergefahren haben (oder so eingerichtet haben, dass es nicht automatisch startet).
  6. Verwenden Sie Folgendes, um den Supervisor zum Beenden aufzufordern -x. Wird normalerweise auch verwendet, -dum den Dienst zu beenden. Dies ist die übliche Methode, um das Entfernen eines Dienstes zuzulassen. Sie müssen jedoch zuerst die Verknüpfung des Dienstes aufheben /service, da sonst svscander Supervisor neu gestartet wird. Wenn Sie Ihren Dienst mit einem Protokollierungsdienst ( mkservice -l) erstellt haben, müssen Sie auch den Protokollierungs-Supervisor (z. B. svc -dx /var/lib/svscan/some-service-name/log) beenden , bevor Sie das Dienstverzeichnis entfernen.

Zusammenfassung

Vorteile:

  1. daemontools bietet eine kugelsichere Möglichkeit zum Erstellen und Verwalten von Diensten. Ich benutze es für meine Server und kann es nur empfehlen.
  2. Das Protokollierungssystem ist sehr robust, ebenso wie die automatische Neustartfunktion des Dienstes.
  3. Da Dienste mit einem Shell-Skript gestartet werden, das Sie schreiben / optimieren, können Sie Ihren Dienst nach Ihren Wünschen anpassen.
  4. Leistungsstarke Tools zur Servicesteuerung: Sie können fast jedes Signal an einen Service senden und Services zuverlässig hoch- und runterfahren.
  5. Ihren Diensten wird eine saubere Ausführungsumgebung garantiert: Sie werden mit derselben Umgebung, denselben Prozessgrenzen usw. ausgeführt, wie sie bereitgestellt initwerden.

Nachteile:

  1. Jeder Dienst erfordert einige Einstellungen. Zum Glück muss dies nur einmal pro Service durchgeführt werden.
  2. Dienste müssen so eingerichtet sein, dass sie im Vordergrund ausgeführt werden. Um die besten Ergebnisse zu erzielen, sollten sie so eingerichtet werden, dass sie bei Standardausgabe / Standardfehler und nicht bei Syslog oder anderen Dateien protokolliert werden.
  3. Steile Lernkurve, wenn Sie neu in der Daemontools-Arbeitsweise sind. Sie müssen Dienste mit neu starten svcund können die Ausführungsskripte nicht direkt ausführen (da sie dann nicht unter der Kontrolle des Supervisors stehen würden).
  4. Viele Housekeeping-Dateien und viele Housekeeping-Prozesse. Jeder Dienst benötigt ein eigenes Dienstverzeichnis, und jeder Dienst verwendet einen Supervisor-Prozess, um den Dienst automatisch neu zu starten, wenn er stirbt. (Wenn Sie viele Dienstleistungen haben, werden Sie sehen , viele von superviseProzessen in Ihrer Prozesstabelle.)

Alles in allem denke ich, dass Daemontools ein hervorragendes System für Ihre Bedürfnisse ist. Ich freue mich über alle Fragen zur Einrichtung und Wartung.


Wie meine Antwort die Spezifikation nagelt: 1. Sie müssen Dienste einrichten. Solange Sie keine Duplikate einrichten (und solange Ihr Dienst selbst keinen Hintergrund hat), treten keine Duplikate auf. 2. superviseDer Supervisor kümmert sich um den Neustart aller Dienste, die beendet werden. Es wartet eine Sekunde zwischen den Restars; Wenn dies nicht genug Zeit für Sie ist, legen Sie oben im Service-Run-Skript einen Ruhezustand fest.
Chris Jester-Young

2a. supervisewird selbst unterstützt von svscan. Wenn ein Supervisor stirbt, wird er neu gestartet. 2b. svscanwird von unterstützt init, das bei svscanBedarf automatisch neu gestartet wird. 2c. Wenn Ihr initaus irgendeinem Grund stirbt, sind Sie trotzdem geschraubt. :-P
Chris Jester-Young

Um andere Fragen zur Haushaltsführung zu beantworten, verwendet das daemontools-System keine PID-Dateien, da diese veraltet sein können. Stattdessen werden alle Prozessinformationen vom Supervisor gespeichert, der einen bestimmten Dienst unterstützt. Der Supervisor unterhält eine Reihe von Dateien (und FIFOs) im Service - Verzeichnis , dass Werkzeuge wie svstatund svcarbeiten können.
Chris Jester-Young

3
Wir sollten mehr Beiträge wie diesen in SO und im Netz im Allgemeinen haben. Nicht nur ein Rezept, wie man den gewünschten Effekt erzielt, sondern eines, das sich die Mühe macht, die Rezepte zu erklären. Warum kann ich das nicht mehr als einmal positiv bewerten? : |
Skytreader

12

Ich denke, Sie möchten es vielleicht versuchen start-stop-daemon(8). Beispiele finden Sie in den Skripten /etc/init.deiner Linux-Distribution. Es kann gestartete Prozesse über die aufgerufene Befehlszeile oder die PID-Datei finden, sodass es allen Ihren Anforderungen entspricht, außer dass es ein Watchdog für Ihr Skript ist. Sie können jedoch jederzeit ein anderes Daemon-Watchdog-Skript starten, das Ihr Skript bei Bedarf neu startet.


In Fedora gibt es noch keinen Start-Stopp-Daemon, sodass Skripte, die davon abhängen, nicht portierbar sind. Siehe: fedoraproject.org/wiki/Features/start-stop-daemon
Bengt

Nur ein Heads-up für OSX-Benutzer: Nein start-stop-daemon, auch nicht (ab 10.9).
mklement0

@ mklement0 Nun ... in fast 5 Jahren hat sich viel geändert.
Alex B

Meine Güte, wie die Zeit vergeht. start-stop-daemonist aber noch am Leben und tritt unter Linux an; Aber nachdem ich die Antwort stackoverflow.com/a/525406/45375 gelesen hatte, wurde mir klar, dass OSX sein eigenes Ding macht : launchd.
mklement0

12

Sie sollten sich daemonize ansehen . Es ermöglicht das Erkennen einer zweiten Kopie (verwendet jedoch den Dateisperrmechanismus). Es funktioniert auch auf verschiedenen UNIX- und Linux-Distributionen.

Wenn Sie Ihre Anwendung automatisch als Daemon starten müssen, müssen Sie ein entsprechendes Init-Skript erstellen.

Sie können die folgende Vorlage verwenden:

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0

1
Sieht aus wie ein Kandidat für die richtige Antwort. Besonders in Anbetracht seiner "Einzelinstanzprüfung".
Martin Wickman

Dies ist vielleicht die bestmögliche Antwort - ich bin mir nicht sicher -, aber wenn Sie glauben, dass dies der Fall ist, können Sie auch eine Erklärung hinzufügen, warum die von mir in der Frage angegebene Spezifikation falsch ist?
Dreeves

Ich mag das killprocim Stopp-Teil nicht: Wenn Sie einen Prozess hatten java, der beispielsweise ausgeführt wurde, killprocwerden auch alle anderen Java-Prozesse beendet.
Chris Jester-Young

1
Von /etc/rc.d/init.d/functions startet daemonize die Binärdatei nur von einer neuen Shell aus: $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*Ich bezweifle, dass es irgendetwas
daemonisieren wird

1
Ich weiß, dass dies alt ist, aber für jeden, der dies später findet ... ist dies richtig. "Daemon", wie in /etc/init.d/functions definiert, wird für Sie nicht wirklich daemonisiert . Es ist nur ein Wrapper cgroups zu tun, zu überprüfen , ob der Prozess bereits ausgeführt wird , stellen Sie einen Benutzer, Set schön und ulimit - Werte, usw. Es ist nicht das Verfahren für Sie daemonize. Das ist immer noch dein eigener Job. :)
jakem

7

Alternativ zu dem bereits erwähnten daemonizeund daemontoolsgibt es den Daemon- Befehl des libslack-Pakets.

daemon ist ziemlich konfigurierbar und kümmert sich um all die mühsamen Daemon-Dinge wie automatischen Neustart, Protokollierung oder Pidfile-Behandlung.


5

Wenn Sie OS X speziell verwenden, sollten Sie sich die Funktionsweise von launchd ansehen. Es wird automatisch überprüft, ob Ihr Skript ausgeführt wird, und es gegebenenfalls neu gestartet. Es enthält auch alle Arten von Planungsfunktionen usw. Es sollte sowohl die Anforderungen 1 als auch 2 erfüllen.

Um sicherzustellen, dass nur eine Kopie Ihres Skripts ausgeführt werden kann, müssen Sie eine PID-Datei verwenden. Im Allgemeinen schreibe ich eine Datei in /var/run/.pid, die eine PID der aktuell ausgeführten Instanz enthält. Wenn die Datei beim Ausführen des Programms vorhanden ist, wird überprüft, ob die PID in der Datei tatsächlich ausgeführt wird (das Programm ist möglicherweise abgestürzt oder hat auf andere Weise vergessen, die PID-Datei zu löschen). Wenn ja, brechen Sie ab. Wenn nicht, starten Sie die Ausführung und überschreiben Sie die PID-Datei.


5

Daemontools ( http://cr.yp.to/daemontools.html ) ist eine Reihe von Hardcore-Dienstprogrammen, die von DJ Bernstein geschrieben wurden. Ich habe dies mit einigem Erfolg verwendet. Das ärgerliche daran ist, dass keines der Skripte beim Ausführen sichtbare Ergebnisse zurückgibt - nur unsichtbare Rückkehrcodes. Aber sobald es läuft, ist es kugelsicher.


Ja, ich wollte einen Eintrag schreiben, der auch Daemontools verwendet. Ich werde meinen eigenen Beitrag schreiben, weil ich hoffe, mit meiner Antwort viel umfassender zu sein und das Kopfgeld auf diese Weise zu bekommen. Wir werden sehen. :-)
Chris Jester-Young

3

Holen Sie sich zuerst createDaemon()von http://code.activestate.com/recipes/278731/

Dann der Hauptcode:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)

Ooh, danke! Möchten Sie es etwas allgemeiner gestalten, damit Sie sowohl "foo arg1 arg2 daemonisieren" als auch "foo arg1 arg2 'daemonisieren" können?
Dreeves

Ok, es wird jetzt Argumente verbinden - Sie müssen es jedoch ändern, wenn Sie jemals Leerzeichen in Ihren Argumenten haben möchten.
Douglas Leeder

Danke Douglas! Es gibt jedoch einen großen Fehler: Wenn Sie "daemonize foo" zweimal ausführen, werden zwei Kopien von foo ausgeführt.
Dreeves

Sie könnten einen PID-Aufzeichnungscode hinzufügen, aber es ist möglicherweise am besten, das Skript nur einmal auszuführen ...
Douglas Leeder

Ich halte das für grundlegend für das gesamte Wrapper-Konzept "Daemonize". (ZB könnte ich es dann stündlich oder minutiös cronen, um sicherzustellen, dass es immer läuft.) Denke ich daran falsch? Garantiert createDaemon das schon irgendwie? Was ist nach dem Neustart?
Dreeves

1

Dies ist eine Arbeitsversion mit einem Beispiel, das Sie in ein leeres Verzeichnis kopieren und ausprobieren können (nach der Installation der CPAN-Abhängigkeiten Getopt :: Long , File :: Spec , File :: Pid und IPC :: System: : Einfach - alles ziemlich normal und für jeden Hacker sehr zu empfehlen: Sie können sie alle gleichzeitig installieren mit cpan <modulename> <modulename> ...).


keepAlive.pl:

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

example.pl:

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

Jetzt können Sie das obige Beispiel aufrufen mit: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3 und die Datei pidfilewird erstellt, und Sie sehen die Ausgabe:

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3

Ich glaube, das ist nicht ganz genau, wenn ich es richtig verstehe. In Ihrer Lösung (danke übrigens!) Muss das Programm, das Sie dämonisieren möchten, geändert werden, um seine PID in die PID-Datei zu schreiben. Ich hoffe auf ein Dienstprogramm, das ein beliebiges Skript dämonisieren kann.
Dreeves

@dreeves: Ja, aber es gibt zwei Möglichkeiten, dies zu umgehen: 1. Das von keepAlive.pl aufgerufene Skript (z. B. example.pl) könnte einfach ein Wrapper sein, um das eigentliche Programm auszuführen, oder 2. keepAlive.pl könnte die Tabelle von analysieren aktive Systemprozesse (mit Proc :: ProcessTable von CPAN), um zu versuchen, den relevanten Prozess und seine PID zu finden).
Ether

1

Sie können auch Monit ausprobieren . Monit ist ein Dienst, der andere Dienste überwacht und darüber berichtet. Während es hauptsächlich verwendet wird, um (per E-Mail und SMS) über Laufzeitprobleme zu informieren, kann es auch das tun, was die meisten anderen Vorschläge hier befürwortet haben. Es kann Programme automatisch (neu) starten und stoppen, E-Mails senden, andere Skripte initiieren und ein Ausgabeprotokoll führen, das Sie abrufen können. Außerdem habe ich festgestellt, dass es einfach zu installieren und zu warten ist, da es eine solide Dokumentation gibt.


1

Sie könnten versuchen, unsterblich Es handelt sich um einen plattformübergreifenden * nix-Supervisor (OS Agnostic).

Für einen schnellen Versuch unter macOS:

brew install immortal

Wenn Sie FreeBSD über die Ports oder pkg verwenden:

pkg install immortal

Für Linux durch Herunterladen der vorkompilierten Binärdateien oder von der Quelle: https://immortal.run/source/

Sie können es entweder so verwenden:

immortal -l /var/log/date.log date

Oder durch eine Konfigurations-YAML- Datei, die Ihnen weitere Optionen bietet, zum Beispiel:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

Wenn Sie auch die Standardfehlerausgabe in einer separaten Datei speichern möchten, können Sie Folgendes verwenden:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
stderr:
    file: /var/log/date-error.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

0

Ich habe eine Reihe von Verbesserungen an der anderen Antwort vorgenommen .

  1. stdout aus diesem Skript besteht ausschließlich aus stdout, das von seinem untergeordneten Element stammt, es sei denn, es wird beendet, weil festgestellt wird, dass der Befehl bereits ausgeführt wird
  2. Bereinigt nach der PID-Datei, wenn sie beendet wird
  3. optional konfigurierbare Zeitüberschreitung (Akzeptiert alle positiven numerischen Argumente, sendet an sleep )
  4. Eingabeaufforderung für die Verwendung aktiviert -h
  5. willkürliche Befehlsausführung statt Einzelbefehlsausführung. Das letzte Argument ODER die verbleibenden Argumente (wenn mehr als ein letztes Argument vorhanden sind) werden an gesendeteval , sodass Sie jede Art von Shell-Skript als Zeichenfolge erstellen können, die als letztes Argument (oder nachfolgende Argumente) an dieses Skript , damit es dämonisiert werden kann
  6. Vergleiche der Argumentanzahl mit -ltstatt<

Hier ist das Skript:

#!/bin/sh

# this script builds a mini-daemon, which isn't a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.

if [ "$1" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=$1
shift

TIMEOUT=1
if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=$1
        [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
        shift
fi

echo "Checking pid in file ${PIDFILE}." >&2

#Check to see if process running.
if [ -f "$PIDFILE" ]; then
    PID=$(< $PIDFILE)
    if [ $? = 0 ]; then
        ps -p $PID >/dev/null 2>&1
        if [ $? = 0 ]; then
            echo "This script is (probably) already running as PID ${PID}."
            exit
        fi
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

cleanup() {
        rm $PIDFILE
}
trap cleanup EXIT

# Run command until we're killed.
while true; do
    eval "$@"
    echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
    sleep $TIMEOUT
done

Verwendung:

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C

Beachten Sie, dass beim Ausführen dieses Skripts aus verschiedenen Verzeichnissen möglicherweise unterschiedliche PID-Dateien verwendet werden und keine vorhandenen laufenden Instanzen erkannt werden. Da es so konzipiert ist, dass es kurzlebige Befehle ausführt und neu startet, die über ein Argument bereitgestellt werden, kann nicht festgestellt werden, ob bereits etwas gestartet wurde, denn wer soll sagen, ob es sich um denselben Befehl handelt ? oder nicht? Um diese Durchsetzung zu verbessern, indem nur eine einzige Instanz von etwas ausgeführt wird, ist eine situationsspezifische Lösung erforderlich.

Damit es als richtiger Daemon fungiert, müssen Sie (mindestens) nohup verwenden, wie in der anderen Antwort erwähnt. Ich habe keine Anstrengungen unternommen, um den Signalen, die der Prozess möglicherweise empfängt, eine gewisse Widerstandsfähigkeit zu verleihen.

Ein weiterer Punkt, den Sie beachten sollten, ist, dass das Töten dieses Skripts (wenn es von einem anderen Skript aufgerufen wurde, das getötet wurde, oder mit einem Signal) möglicherweise nicht erfolgreich ist, um das Kind zu töten, insbesondere wenn das Kind noch ein anderes Skript ist. Ich bin mir nicht sicher, warum das so ist, aber es scheint etwas mit der Funktionsweise zu evaltun zu haben, was für mich mysteriös ist. Es kann daher ratsam sein, diese Zeile durch etwas zu ersetzen, das nur einen einzigen Befehl akzeptiert, wie in der anderen Antwort.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.