Question

Je voudrais un daemonizer qui peut transformer un script ou une commande générique arbitraire, dans un démon .

Il y a deux cas communs, je voudrais traiter:

  1. J'ai un script qui devrait fonctionner pour toujours. Si elle meurt jamais (ou redémarrage), redémarrez-le. Ne laissez jamais il y avoir deux copies en cours d'exécution à la fois (détecter si une copie est déjà en cours d'exécution et ne lancent pas dans ce cas).

  2. J'ai une simple commande de ligne de script ou une commande que je voudrais continuer d'exécuter de façon répétée pour toujours (avec une courte pause entre les courses). Encore une fois, ne permettent pas de deux copies du script à jamais en cours d'exécution à la fois.

Bien sûr, il est trivial d'écrire un « while (true) » boucle autour du script dans le cas 2, puis appliquer une solution pour le cas 1, mais une solution plus générale va juste résoudre le cas 2 directement depuis applique au script 1 cas aussi bien (vous pouvez juste une plus courte ou pas de pause si le script ne vise pas à jamais mourir (bien sûr, si le script vraiment ne ne meurent jamais alors la pause ne fait pas d'ailleurs)) .

Notez que la solution ne doit pas impliquer, par exemple, l'ajout de code de verrouillage de fichier ou d'enregistrement PID aux scripts existants.

Plus précisément, je voudrais un programme « daemonize » que je peux courir comme

% daemonize myscript arg1 arg2

ou, par exemple,

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

qui maintiendrait une liste croissante des dates annexées à times.txt. (Notez que si l'argument (s) à daemon est un script qui fonctionne toujours comme dans le cas 1 ci-dessus, daemonize va encore faire la bonne chose, de le redémarrer si nécessaire.) Je pourrais alors mettre une commande comme ci-dessus dans mon .login et / ou toutes les heures ou il cron minutieusement (selon la façon dont j'allais mourir de façon inattendue, il inquiet).

NB:. Le script daemonize devra se rappeler la chaîne de commande, il est daemonizing de sorte que si la même chaîne de commande est à nouveau daemon ne lance pas une seconde copie

En outre, la solution devrait idéalement travailler sur les deux OS X et Linux, mais des solutions pour l'un ou l'autre sont les bienvenus.

EDIT:. Il est très bien si vous devez invoquer avec sudo daemonize myscript myargs

(Si je pense à tout faux ou il existe des solutions partielles rapides et sale, j'aimerais entendre aussi.)


PS: Dans le cas où il est utile, est une question spécifique similaire à python.

Était-ce utile?

La solution

Vous pouvez daemonize tout exécutable sous Unix en utilisant nohup et l'opérateur &:

nohup yourScript.sh script args&

La commande nohup vous permet de fermer votre session shell sans elle tuer votre script, alors que le script et met en arrière-plan afin que vous obtenez une invite du shell pour continuer votre session. Le seul petit problème avec ceci est sortie standard et erreur standard à la fois obtenir envoyé à ./nohup.out, donc si vous démarrez plusieurs scripts dans ce manoir leur sortie sera étroitement liée. Une meilleure commande serait:

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

envoie la sortie standard dans le fichier de votre choix et l'erreur standard à un autre fichier de votre choix. Si vous voulez utiliser un seul fichier à la fois la sortie standard et l'erreur standard, vous pouvez nous ceci:

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

2> & 1 indique la coque pour rediriger l'erreur standard (descripteur de fichier 2) dans le même fichier en tant que sortie standard (descripteur de fichier 1).

Pour exécuter une commande qu'une seule fois et redémarrez-le si elle meurt, vous pouvez utiliser ce 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

Le premier argument est le nom du fichier pid à utiliser. Le second argument est la commande. Et tous les autres arguments sont les arguments de la commande.

Si vous nommez ce script restart.sh est ce que vous appelleriez:

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

Autres conseils

Je présente mes excuses pour la réponse longue (voir les commentaires s'il vous plaît sur la façon dont ma réponse ongles la spécification). Je suis en train d'être complète, vous devez donc aussi bien d'une jambe que possible. : -)

Si vous êtes en mesure d'installer des programmes (accès root), et sont prêts à faire un temps travail sur le terrain pour mettre en place votre script pour l'exécution du démon (soit plus impliqué que spécifiant simplement les arguments de ligne de commande à exécuter sur le ligne de commande, mais seulement besoin d'être fait une fois par service), j'ai une manière qui est plus robuste.

Il consiste à utiliser daemontools . Le reste du poste décrit comment mettre en place des services en utilisant daemontools.

Configuration initiale

  1. Suivez les instructions Comment installer daemontools. Certaines distributions (par exemple, Debian, Ubuntu) ont déjà des paquets pour, donc il suffit d'utiliser cela.
  2. un répertoire appelé /service. Le programme d'installation aurait déjà fait, mais simplement vérifier, ou en cas d'installation manuellement. Si vous n'aimez cet endroit, vous pouvez le modifier dans votre script svscanboot, bien que la plupart des utilisateurs de daemontools sont habitués à utiliser /service et se embrouille si vous ne l'utilisez pas.
  3. Si vous utilisez Ubuntu ou une autre distro qui n'utilise pas init standard (c.-à-ne pas utiliser /etc/inittab), vous devez utiliser le inittab pré-installé comme base pour l'organisation svscanboot à appeler par init . Il n'est pas difficile, mais vous devez savoir comment configurer le init que votre système d'exploitation utilise. svscanboot est un script qui appelle svscan, qui fait le travail principal de la recherche de services; il est appelé à partir init si init prendra les dispositions nécessaires pour le redémarrer si elle meurt pour une raison quelconque.

Configuration par service

  1. Chaque service a besoin d'un Répertoire des services , qui stocke des informations d'ordre administratif au sujet du service. Vous pouvez également faire un endroit pour héberger ces répertoires de services afin qu'ils soient en un seul endroit; J'utilise habituellement /var/lib/svscan, mais tout nouvel emplacement ira bien.
  2. J'utilise habituellement un script pour configurer le répertoire de services, pour économiser beaucoup de travail répétitif manuel . par ex.,

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

    some-service-name est le nom que vous voulez donner votre service, user est l'utilisateur d'exécuter ce service comme, et loguser est l'utilisateur d'exécuter l'enregistreur comme. (L'exploitation forestière est expliqué dans un peu.)

  3. Votre service doit fonctionner au premier plan . Si vos antécédents de programme par défaut, mais a une option pour désactiver cela, alors le faire. Si vos arrière-plans de programme sans moyen de le désactiver, lire sur fghack , bien que cela vient à un compromis. vous ne pouvez plus contrôler le programme en utilisant svc
  4. Modifier le script run pour assurer qu'il fait ce que vous voulez. Vous devrez peut-être passer un appel sleep en haut, si vous vous attendez votre service pour quitter fréquemment.
  5. Quand tout est mis en place à droite, créez un lien symbolique /service pointant vers votre répertoire de service. (Ne pas mettre les répertoires de service directement dans /service,. Il est plus difficile de supprimer le service de la montre de svscan)

Connexion

  1. La façon de l'exploitation forestière daemontools est d'avoir les messages du journal d'écriture de service à la sortie standard (ou une erreur standard, si vous utilisez des scripts générés avec mkservice); svscan se charge d'envoyer des messages journaux au service de l'exploitation forestière.
  2. Le service de journalisation prend les messages du journal de l'entrée standard. Le script de service d'enregistrement généré by mkservice va créer une rotation automatique, les fichiers journaux horodatés dans le répertoire log/main. Le fichier journal en cours est appelé current.
  3. Le service d'enregistrement peut être démarré et arrêté indépendamment du service principal.
  4. canalisant les fichiers journaux à travers tai64nlocal traduira les horodatages dans un format lisible par l'homme . (Tai64n est un horodatage atomique 64 bits avec un nombre de nanosecondes).

Services Contrôle

  1. svstat pour obtenir le statut d'un service. Notez que le service d'enregistrement est indépendant et a son propre statut.
  2. Vous contrôlez votre service (démarrage, arrêt, redémarrage, etc.) en utilisant svc . Par exemple, pour redémarrer votre service, l'utilisation svc -t /service/some-service-name; -t signifie "envoyer SIGTERM".
  3. D'autres signaux disponibles incluent -h (SIGHUP), -a (SIGALRM), -1 (SIGUSR1), -2 (SIGUSR2) et -k (SIGKILL).
  4. Pour descendre le service, l'utilisation -d. Vous pouvez également empêcher un service de démarrage automatique au démarrage du système en créant un fichier nommé down dans le répertoire de service.
  5. Pour démarrer le service, l'utilisation -u. Ce n'est pas nécessaire, à moins que vous avez avala précédemment (ou mis en place pas à démarrage automatique).
  6. Pour demander au superviseur de quitter, l'utilisation -x; habituellement utilisé avec -d de mettre fin au service aussi bien. Ceci est la façon habituelle pour permettre un service à supprimer, mais vous devez supprimer le lien entre le service de /service premier, ou bien svscan redémarre le superviseur. En outre, si vous avez créé votre service avec un service d'enregistrement (de mkservice -l), souvenez-vous de quitter aussi le superviseur de l'exploitation forestière (par exemple, svc -dx /var/lib/svscan/some-service-name/log) avant de retirer le répertoire de service.

Résumé

Avantages:

  1. daemontools fournit un moyen pare-balles pour créer et gérer des services. Je l'utilise pour mes serveurs, et je le recommande fortement.
  2. Son système d'enregistrement est très robuste, comme l'installation de redémarrage automatique du service.
  3. Parce qu'il démarre les services avec un script shell que vous écrivez / air, vous pouvez personnaliser votre service comme bon vous semble.
  4. outils de contrôle des services puissants: vous pouvez envoyer plus aucun signal à un service, et peut offrir des services de haut en bas de manière fiable
  5. .
  6. Vos services sont assurés d'un environnement d'exécution propre: ils exécuteront avec le même environnement, les limites de processus, etc., ce qui fournit init
  7. .

Moins:

  1. Chaque service prend un peu de configuration. Heureusement, cela doit faire seulement une fois par service.
  2. Les services doivent être mis en place pour fonctionner au premier plan. En outre, pour de meilleurs résultats, ils devraient être mis en place pour connecter la sortie standard / erreur standard, plutôt que des fichiers Syslog ou autres.
  3. courbe d'apprentissage si vous êtes nouveau à la façon daemontools de faire les choses. Vous devez redémarrer les services utilisant svc, et ne peut pas exécuter les scripts d'exécution directement (car ils seraient alors pas sous le contrôle du superviseur).
  4. Beaucoup de fichiers d'entretien ménager, et beaucoup de processus d'entretien ménager. Chaque service a besoin de son propre répertoire de services, et chaque service utilise un processus de superviseur pour le redémarrage automatique du service si elle meurt. (Si vous avez de nombreux services, vous verrez beaucoup des processus supervise dans votre table de processus.)

En équilibre, je pense que daemontools est un excellent système pour vos besoins. Je me réjouis des questions sur la façon de le configurer et de le maintenir.

Vous devriez jeter un oeil à daemonize . Il permet de détecter deuxième copie (mais il utilise un mécanisme de verrouillage de fichiers). En outre, il fonctionne sur les différentes distributions UNIX et Linux.

Si vous avez besoin pour démarrer automatiquement votre application en tant que démon, vous devez créer-script d'initialisation approprié.

Vous pouvez utiliser le modèle suivant:

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

Je pense que vous pouvez essayer start-stop-daemon(8) . Consultez les scripts dans /etc/init.d dans une distro Linux pour des exemples. Il peut trouver des processus a commencé par fichier ligne de commande appelée ou PID, de sorte qu'il corresponde à toutes vos exigences, sauf d'être un chien de garde pour votre script. Mais vous pouvez toujours commencer un autre script de chien de garde du démon qui vient redémarrer votre script si nécessaire.

Comme alternative à la daemonize et daemontools, il y a déjà mentionné la commande démon du paquet libslack.

daemon est tout à fait configurable et se soucie de tous les trucs démon fastidieux tels que le redémarrage automatique, l'enregistrement ou la manipulation de pidfile.

Si vous utilisez OS X spécifiquement, je vous suggère de jeter un oeil à la façon dont fonctionne launchd. Il vérifie automatiquement pour assurer votre script est en cours d'exécution et de le relancer si nécessaire. Il comprend également toutes sortes de fonctions de planification, etc. Il doit satisfaire à la fois l'exigence 1 et 2.

Quant à assurer une seule copie de votre script peut exécuter, vous devez utiliser un fichier PID. En général, j'écris un fichier à /var/run/.pid qui contient un PID de l'instance en cours d'exécution en cours. si le fichier existe lorsque le programme est exécuté, il vérifie si le PID dans le fichier est en fait en cours d'exécution (le programme peut-être écrasé ou autrement oublié de supprimer le fichier PID). Dans ce cas, abandonner. Sinon, commencer à courir et écraser le fichier PID.

daemontools ( http://cr.yp.to/daemontools.html ) est un ensemble d'utilitaires assez endurcis utilisés pour le faire, écrit par dj bernstein. Je l'ai utilisé cela avec un certain succès. La partie ennuyeux à ce sujet est que aucun des scripts aucun résultat visible lorsque vous les exécutez - seulement des codes de retour invisibles. Mais une fois qu'il est en cours d'exécution, il est l'épreuve des balles.

D'abord obtenir createDaemon() de http://code.activestate.com/recipes/278731/

Ensuite, le code principal:

import subprocess
import sys
import time

createDaemon()

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

Ceci est une version de travail avec un exemple que vous pouvez copier dans un répertoire vide et essayer (après avoir installé les dépendances CPAN, qui sont Getopt :: long, fichier :: Spec , File :: Pid et IPC :: System :: simple - tout à fait standard et sont fortement recommandés pour tout pirate. vous pouvez les installer à la fois avec 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";

Maintenant, vous pouvez invoquer l'exemple ci-dessus: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3 et le fichier pidfile sera créé, et vous verrez la sortie:

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

Vous pouvez également essayer Monit . Monit est un service qui surveille et rapports sur d'autres services. Bien qu'il soit principalement utilisé comme un moyen d'informer (par email et sms) sur les problèmes d'exécution, il peut aussi faire ce que la plupart des autres suggestions ici ont préconisé. Il peut automatiquement (re) démarrer et arrêter des programmes, envoyer des e-mails, lancer d'autres scripts, et de maintenir un journal de sortie que vous pouvez prendre. De plus, je l'ai trouvé, il est facile à installer et à entretenir car il y a une documentation solide.

Je l'ai fait une série d'améliorations sur le autre réponse .

  1. stdout de ce script est purement composé de stdout provenant de son enfant à moins qu'il sort en raison de la détection que la commande est déjà en cours d'exécution
  2. nettoie après son pidfile lorsque fin
  3. période de délai d'attente en option configurable (Accepte tout argument numérique positif, envoie à sleep)
  4. invite de l'utilisation sur -h
  5. exécution de commande arbitraire, plutôt que seule exécution de la commande. Le dernier arg OU args restant (si plus d'un dernier arg) sont envoyés à eval, de sorte que vous pouvez construire une sorte de script shell comme une chaîne à envoyer à ce script en dernier arg (ou args arrière) pour qu'il daemonize
  6. comparaisons de nombre d'arguments fait avec -lt au lieu de <

Voici le 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

Utilisation:

$ 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

Attention que si vous exécutez ce script à partir de répertoires différents, il peut utiliser différents .pid et ne pas détecter toutes les instances en cours d'exécution existants. Comme il est conçu pour fonctionner et redémarrer les commandes éphémères fournies par un argument il n'y a aucun moyen de savoir si quelque chose a déjà été commencé, car qui est-à-dire si elle est la même commande ou non? Pour améliorer cette application seulement en cours d'exécution d'une seule instance de quelque chose, une solution spécifique à la situation est nécessaire.

En outre, pour qu'il fonctionne comme un démon, vous devez impérativement utiliser (au minimum) nohup que l'autre réponse mentionne. J'ai fait aucun effort pour fournir une résistance aux signaux du processus peut recevoir.

Un autre point à prendre note est que tuer ce script (si elle a été appelée à partir encore un autre script qui est tué, ou avec un signal) peut ne pas réussir à tuer l'enfant, surtout si l'enfant est encore une autre script. Je ne suis pas certain pourquoi il en est, mais il semble être quelque chose lié à la façon dont fonctionne eval, ce qui est mystérieux pour moi. Donc, il peut être prudent de remplacer cette ligne avec quelque chose qui accepte une seule commande comme dans l'autre réponse.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top