Domanda

Vorrei una daemonizer che può trasformare una, script generico arbitraria o comando in un daemon.

Ci sono due casi comuni che vorrei affrontare:

  1. Ho uno script che dovrebbe correre per sempre. Se invece muore mai (o al riavvio), riavviarlo. Non lasciate mai esserci due copie in esecuzione in una sola volta (rilevare se una copia è già in esecuzione e non si lanciano in questo caso).

  2. Ho un semplice comando di script o riga comandi che mi piacerebbe continuare l'esecuzione più volte per sempre (con una breve pausa tra le esecuzioni). Anche in questo caso, non consentono a due copie della sceneggiatura per mai essere in esecuzione in una sola volta.

Naturalmente è banale scrivere un "while (true)" anello intorno allo script nel caso 2 e quindi applicare una soluzione per il caso 1, ma una soluzione più generale sarà solo risolvere il caso 2 direttamente da quel vale per lo script in caso 1 così (si può fare, una più breve o nessuna pausa se lo script non è destinato a morire mai (ovviamente se lo script davvero fa non muoiono mai, allora la pausa in realtà non importa)) .

Si noti che la soluzione non deve comportare, per esempio, l'aggiunta di codice di blocco dei file o la registrazione PID agli script esistenti.

In particolare, mi piacerebbe un programma di "demonizzare" che posso correre come

% daemonize myscript arg1 arg2

o, per esempio,

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

, che avrebbe mantenuto un crescente elenco di date allegati al times.txt. (Si noti che se l'argomento (s) per demonizzare è uno script che viene eseguito sempre come nel precedente caso 1, quindi demonizzare sarà ancora fare la cosa giusta, riavviandolo quando necessario.) Ho potuto poi mettere un comando come sopra nel mio .login e / o cron esso oraria o minuziosamente (a seconda di come ero preoccupato su di esso morire inaspettatamente).

. NB: Lo script demonizzare avrà bisogno di ricordare la stringa di comando è daemonizing in modo che se la stessa stringa di comando è daemonized ancora una volta non si avvia una seconda copia

Inoltre, la soluzione dovrebbe idealmente lavorare sia OS X e Linux, ma le soluzioni per uno o l'altro sono i benvenuti.

EDIT:. Va bene se si deve richiamare con sudo daemonize myscript myargs

(Se sto pensando a questo tutto sbagliato o ci sono soluzioni parziali rapide-e-sporca, mi piacerebbe sentire anche questo.)


PS: Nel caso in cui è utile, ecco un domanda simile specifica per python.

E questo rispondere a una domanda simile è quello che sembra essere un linguaggio utile per un rapido demonizzazione-and-dirty di uno script arbitrari:

È stato utile?

Soluzione

È possibile demonizzare qualsiasi eseguibile in UNIX utilizzando nohup e l'operatore &:

nohup yourScript.sh script args&

Il comando nohup permette di spegnere il sessione di shell senza uccidere lo script, mentre il & pone lo script in background in modo da ottenere un prompt della shell per continuare la sessione. L'unico piccolo problema con questo è standard e l'errore standard di ottenere sia inviato a ./nohup.out, quindi se si inizia a diversi script in questo maniero sarà intreccia la loro produzione. Un comando migliore potrebbe essere:

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

Questo invierà standard out al file di vostra scelta e lo standard error in un file diverso di vostra scelta. Se si desidera utilizzare un solo file per entrambi standard error fuori e standard potete noi questo:

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

Il 2> & 1 dice il guscio per reindirizzare lo standard error (descrittore di file 2) per lo stesso file come standard out (descrittore di file 1).

Per eseguire un comando solo una volta e riavviarlo se muore è possibile utilizzare questo script:

#!/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

Il primo argomento è il nome del file pid da utilizzare. Il secondo argomento è il comando. E tutti gli altri argomenti sono gli argomenti del comando.

Se il nome questo script restart.sh questo è come si potrebbe chiamare:

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

Altri suggerimenti

Mi scuso per la lunga risposta (vedi commenti su come la mia risposta unghie la spec). Sto cercando di essere completo, in modo da avere come bene di una gamba il più possibile. : -)

Se si è in grado di installare programmi (avere accesso root), e sono disposti a fare di una volta noia per impostare lo script per l'esecuzione daemon (cioè, più coinvolti che semplicemente specificare gli argomenti della riga di comando da eseguire sul riga di comando, ma solo bisogno di essere fatto una volta per servizio), ho un modo che è più robusto.

Si tratta di utilizzare daemontools . Il resto del post descrive come impostare servizi usando daemontools.

Impostazione iniziale

  1. Seguire le istruzioni riportate in Come installare daemontools . Alcune distribuzioni (ad esempio, Debian, Ubuntu) hanno già i pacchetti per esso, in modo che basta usare.
  2. Creare una directory denominata /service. L'installatore deve avere già fatto, ma solo verificare, o se l'installazione manuale. Se non ti piace questa posizione, si può cambiare nello script svscanboot, sebbene la maggior parte degli utenti daemontools sono abituati ad utilizzare /service e si confonderà se non ne fanno uso.
  3. Se si utilizza Ubuntu o altra distro che non utilizza init standard (ad esempio, non usa /etc/inittab), è necessario utilizzare il inittab preinstallato come base per organizzare svscanboot a essere chiamato da init . Non è difficile, ma è necessario sapere come configurare il init che il sistema operativo utilizza. svscanboot è uno script che chiama svscan, che fa il lavoro principale della ricerca di servizi; si chiama da init così init provvederà a ripartire se muore per qualsiasi motivo.

Impostazione Per-service

  1. Ogni servizio ha bisogno di un directory service , che memorizza le informazioni di pulizia sul servizio. È anche possibile fare un percorso per ospitare queste directory di servizio in modo che siano tutte in un unico luogo; di solito io uso /var/lib/svscan, ma qualsiasi nuova posizione andrà bene.
  2. Io di solito uso uno script per impostare la directory servizio, per risparmiare un sacco di lavoro ripetitivo manuale . per es.,

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

    dove some-service-name è il nome che volete dare al vostro servizio, user è l'utente ad eseguire tale servizio come, e loguser è l'utente di eseguire il logger come. (Registrazione è spiegato in appena un po '.)

  3. Il vostro servizio deve funzionare in primo piano . Se il programma sfondi di default, ma ha un'opzione per disabilitare che, poi farlo. Se il programma sfondi senza un modo per disattivarlo, leggere su fghack , anche se questo viene ad un trade-off:. non è più possibile controllare il programma utilizzando svc
  4. Modificare lo script run per assicurarsi che sta facendo ciò che si desidera. Potrebbe essere necessario effettuare una chiamata sleep in alto, se vi aspettate il vostro servizio per uscire frequentemente.
  5. Quando tutto è configurato correttamente, creare un link simbolico in /service che punta al servizio di directory. (Non mettere le directory di servizi direttamente all'interno /service;. Rende più difficile per rimuovere il servizio da orologio di svscan)

Accesso

  1. Il modo daemontools di registrazione è quello di avere i messaggi di log di servizio scrivere sullo standard output (o errore standard, se si sta utilizzando script generati con mkservice); svscan si prende cura di invio di messaggi di log per il servizio di registrazione.
  2. Il servizio di registrazione prende i messaggi di log provenienti da standard input. Il b script di servizio di registrazione generatoy mkservice creerà, i file di log con data e ora di auto-rotazione nella directory log/main. Il file di registro corrente si chiama current.
  3. Il servizio di registrazione può essere avviato e arrestato indipendentemente dal servizio principale.
  4. Tubazioni i file di log attraverso tai64nlocal si tradurrà i timestamp in un formato leggibile . (TAI64N è un timestamp atomica a 64 bit con un conteggio nanosecondo.)

servizi Controllo

  1. svstat per ottenere lo stato di un servizio. Si noti che il servizio di registrazione è indipendente, ed ha un proprio stato.
  2. È possibile controllare il servizio (start, stop, restart, ecc) utilizzando svc . Ad esempio, per riavviare il servizio, uso svc -t /service/some-service-name; -t significa "inviare SIGTERM".
  3. Altri segnali disponibili includono -h (SIGHUP), -a (SIGALRM), -1 (SIGUSR1), -2 (SIGUSR2), e -k (SIGKILL).
  4. Per giù il servizio, l'uso -d. È inoltre possibile impedire un servizio venga avviato automaticamente all'avvio con la creazione di un file denominato down nell'elenco del servizio.
  5. Per avviare il servizio, l'uso -u. Questo non è necessario se non hai abbattuto in precedenza (o impostare fino a non auto-start).
  6. Per richiedere il supervisore per uscire, l'uso -x; solitamente utilizzato con -d per terminare il servizio pure. Questo è il solito modo per consentire un servizio da rimuovere, ma è necessario scollegare il servizio da /service prima, altrimenti svscan riavvierà il supervisore. Inoltre, se avete creato il vostro servizio con un servizio di registrazione (mkservice -l), ricordarsi di chiudere anche il supervisore registrazione (ad esempio, svc -dx /var/lib/svscan/some-service-name/log) prima di rimuovere la directory di servizio.

Sommario

Pro:

  1. daemontools fornisce un modo a prova di proiettile per creare e gestire i servizi. Io lo uso per il mio server, e lo consiglio vivamente.
  2. Il suo sistema di registrazione è molto robusto, così come il centro di assistenza di riavvio automatico.
  3. Dato che avvia i servizi con uno script di shell che si scrive / melodia, è possibile personalizzare il vostro servizio come più vi piace.
  4. strumenti di controllo potente servizio: è possibile inviare la maggior parte qualsiasi segnale ad un servizio, e può portare servizi su e giù in modo affidabile
  5. .
  6. I tuoi servizi sono garantiti un ambiente di esecuzione pulita: essi saranno eseguiti con lo stesso ambiente, limiti di processo, ecc, come quello che fornisce init
  7. .

Contro:

  1. Ogni servizio richiede un po 'di messa a punto. Per fortuna, questo solo deve fare una volta per ogni servizio.
  2. I servizi devono essere configurati per l'esecuzione in primo piano. Inoltre, per ottenere i migliori risultati, devono essere impostati in modo da accedere a standard output / errore standard, piuttosto che file syslog o altro.
  3. ripida curva di apprendimento, se siete nuovi al modo daemontools di fare le cose. È necessario riavviare i servizi utilizzando svc, e non è possibile eseguire gli script eseguito direttamente (perché sarebbero allora non essere sotto il controllo del supervisore).
  4. Un sacco di file di pulizia, e un sacco di processi di pulizia. Ogni servizio ha bisogno di un proprio servizio di directory, e ogni servizio utilizza un processo di supervisore per l'auto-riavviare il servizio se muore. (Se si dispone di molti servizi, si vedrà un sacco di processi supervise nella vostra tabella dei processi.)

In equilibrio, penso daemontools è un ottimo sistema per le vostre esigenze. Accolgo con favore eventuali domande su come configurarlo e mantenerlo.

Si dovrebbe avere uno sguardo a demonizzare . Consente di rilevare seconda copia (ma utilizza meccanismo di blocco file). Inoltre funziona su diverse distribuzioni UNIX e Linux.

Se è necessario avviare automaticamente l'applicazione come demone, allora è necessario creare adeguate init-script.

È possibile utilizzare il seguente modello:

#!/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

Penso che si può provare a start-stop-daemon(8) . Scopri script in /etc/init.d in qualsiasi distro Linux per gli esempi. Può trovare i processi avviati da riga di comando invocato o il file PID, in modo che corrisponda a tutte le vostre esigenze, tranne di essere un cane da guardia per lo script. Ma si può sempre iniziare un altro script watchdog demone che appena si riavvia lo script, se necessario.

In alternativa alla daemonize già citato e daemontools, c'è il daemon comando del pacchetto libslack.

daemon è molto configurabile e fa la cura di tutte le cose daemon noiosa come riavvio automatico, la registrazione o la movimentazione pidfile.

Se si utilizza OS X specificamente, vi consiglio di dare un'occhiata a come funziona launchd. Controllerà automaticamente per garantire lo script è in esecuzione e rilanciare se necessario. Esso comprende anche tutta una serie di funzionalità di pianificazione, ecc Si dovrebbe soddisfare sia esigenze 1 e 2.

Per quanto riguarda assicurare solo una copia dello script può essere eseguito, è necessario utilizzare un file PID. In genere scrivo un file per /var/run/.pid che contiene un PID dell'istanza in esecuzione corrente. se il file esiste quando il programma viene eseguito, esso controlla se il PID nel file è effettivamente in esecuzione (il programma potrebbe essere andato in crash o comunque dimenticato di cancellare il file PID). Se lo è, abortire. In caso contrario, avviare l'esecuzione e sovrascrivere il file PID.

DaemonTools ( http://cr.yp.to/daemontools.html ) è un serie di utility piuttosto hard-core utilizzati per fare questo, scritto da dj Bernstein. Ho usato questo con un certo successo. La parte fastidiosa è che nessuno degli script prodotto risultati visibili quando vengono eseguiti - solo codici di ritorno invisibili. Ma una volta che è in esecuzione è a prova di proiettile.

In primo luogo ottenere createDaemon() da http://code.activestate.com/recipes/278731/

Quindi il codice principale:

import subprocess
import sys
import time

createDaemon()

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

Questa è una versione completa di lavoro con un esempio che è possibile copiare in una directory vuota e provare (dopo aver installato le dipendenze CPAN, che sono Getopt :: Long , File :: Spec , File :: Pid , e IPC :: System :: Simple - tutti abbastanza standard e sono altamente consigliato per qualsiasi hacker:. tutti è possibile installare in una volta con 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";

Ora è possibile richiamare l'esempio precedente con: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3 e il file pidfile sarà creato, e si vedrà l'output:

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

Si potrebbe anche provare Monit . Monit è un servizio che monitora e le relazioni su altri servizi. Mentre è utilizzato principalmente come un modo per comunicare (tramite e-mail e sms) sui problemi di esecuzione, si può anche fare ciò che la maggior parte degli altri suggerimenti qui hanno sostenuto. E 'in grado di auto (ri) avviare e arrestare i programmi, inviare e-mail, avviare altri script, e mantenere un registro di uscita che si possono raccogliere. Inoltre, ho trovato è facile da installare e mantenere perché non c'è documentazione solida.

Si potrebbe dare una prova di immortale Si tratta di un * nix multipiattaforma (OS agnostico) supervisore.

Per una rapida prova su MacOS:

brew install immortal

Nel caso in cui si utilizza FreeBSD dai porti o utilizzando pkg:

pkg install immortal

Linux scaricando i binari precompilati o dalla fonte: https://immortal.run/source/

È possibile utilizzare in questo modo:

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

oppure da un file di configurazione YAML che vi dà più opzioni, ad esempio:

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

Se si desidera mantenere anche l'output di errore standard in un file separato si potrebbe usare qualcosa come:

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

Ho fatto una serie di miglioramenti sul altra risposta .

  1. stdout su questo script è puramente costituito stdout proveniente dal suo bambino MENO esce dovuto rilevare che il comando è già in esecuzione
  2. pulisce dopo la sua pidfile caso di terminazione
  3. opzionale periodo di timeout configurabile (accetta qualsiasi argomento numerico positivo, manda a sleep)
  4. prompt di utilizzo su -h
  5. esecuzione di comandi arbitrari, anziché esecuzione del comando singolo. L'ultimo arg OR args rimanenti (se più di un ultimo arg) vengono inviati eval, in modo da poter realizzare qualsiasi tipo di script di shell come stringa da inviare a questo script come ultima arg (o args finali) affinchè come demone
  6. confronti di conteggio argomento fatto con -lt invece di <

Ecco lo script:

#!/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

Utilizzo:

$ 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

Attenzione che se si esegue questo script da diverse directory può utilizzare diversi pidfile e non rilevare eventuali istanze in esecuzione esistenti. Dal momento che è stato progettato per funzionare e riavviare i comandi effimeri forniti attraverso un argomento non c'è modo di sapere se è stato già avviato qualcosa, perché chi può dire se si tratta di lo stesso comando o no? Per migliorare su questo l'applicazione di solo l'esecuzione di una singola istanza di qualcosa, una soluzione specifica per la situazione è necessaria.

Inoltre, per poter funzionare come una vera e propria daemon, è necessario utilizzare (al minimo) nohup come l'altra risposta menziona. Ho fatto alcuno sforzo per fornire resilienza ai segnali il processo può ricevere.

Un altro punto di prendere atto di è che uccidere questo script (se è stato chiamato da un altro script che viene ucciso, o con un segnale) potrebbe non riuscire a uccidere il bambino, soprattutto se il bambino è ancora un'altra script. Sono incerto perché questo è, ma sembra di essere qualcosa legato alle opere eval modo, che è misterioso per me. Quindi può essere prudente per sostituire quella linea con qualcosa che accetta solo un singolo comando come in altra risposta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top