Comment puis-je daemonize un script arbitraire dans unix?
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:
-
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).
-
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.
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
- Suivez les instructions Comment installer daemontools. Certaines distributions (par exemple, Debian, Ubuntu) ont déjà des paquets pour, donc il suffit d'utiliser cela.
- 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 scriptsvscanboot
, bien que la plupart des utilisateurs de daemontools sont habitués à utiliser/service
et se embrouille si vous ne l'utilisez pas. - Si vous utilisez Ubuntu ou une autre distro qui n'utilise pas
init
standard (c.-à-ne pas utiliser/etc/inittab
), vous devez utiliser leinittab
pré-installé comme base pour l'organisationsvscanboot
à appeler parinit
. Il n'est pas difficile, mais vous devez savoir comment configurer leinit
que votre système d'exploitation utilise.svscanboot
est un script qui appellesvscan
, qui fait le travail principal de la recherche de services; il est appelé à partirinit
siinit
prendra les dispositions nécessaires pour le redémarrer si elle meurt pour une raison quelconque.
Configuration par service
- 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. -
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"
où
some-service-name
est le nom que vous voulez donner votre service,user
est l'utilisateur d'exécuter ce service comme, etloguser
est l'utilisateur d'exécuter l'enregistreur comme. (L'exploitation forestière est expliqué dans un peu.) - 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 utilisantsvc
- Modifier le script
run
pour assurer qu'il fait ce que vous voulez. Vous devrez peut-être passer un appelsleep
en haut, si vous vous attendez votre service pour quitter fréquemment. - 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 desvscan
)
Connexion
- 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. - 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épertoirelog/main
. Le fichier journal en cours est appelécurrent
. - Le service d'enregistrement peut être démarré et arrêté indépendamment du service principal.
- 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
-
svstat
pour obtenir le statut d'un service. Notez que le service d'enregistrement est indépendant et a son propre statut. - Vous contrôlez votre service (démarrage, arrêt, redémarrage, etc.) en utilisant
svc
. Par exemple, pour redémarrer votre service, l'utilisationsvc -t /service/some-service-name
;-t
signifie "envoyerSIGTERM
". - D'autres signaux disponibles incluent
-h
(SIGHUP
),-a
(SIGALRM
),-1
(SIGUSR1
),-2
(SIGUSR2
) et-k
(SIGKILL
). - 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. - 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). - 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 biensvscan
redémarre le superviseur. En outre, si vous avez créé votre service avec un service d'enregistrement (demkservice -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:
- 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.
- Son système d'enregistrement est très robuste, comme l'installation de redémarrage automatique du service.
- Parce qu'il démarre les services avec un script shell que vous écrivez / air, vous pouvez personnaliser votre service comme bon vous semble.
- 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 .
- 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
.
Moins:
- Chaque service prend un peu de configuration. Heureusement, cela doit faire seulement une fois par service.
- 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.
- 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). - 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.
Vous pouvez donner un essai immortel Il est un * nix superviseur multi-plateforme (OS agnostique).
Pour un essai rapide sur macOS:
brew install immortal
Si vous utilisez FreeBSD des ports ou en utilisant pkg:
pkg install immortal
Linux en téléchargeant les binaires précompilés ou de la source: https://immortal.run/source/
Vous pouvez l'utiliser comme ceci:
immortal -l /var/log/date.log date
Ou par un fichier YAML qui vous donne plus d'options, par exemple:
cmd: date
log:
file: /var/log/date.log
age: 86400 # seconds
num: 7 # int
size: 1 # MegaBytes
timestamp: true # will add timesamp to log
Si vous souhaitez conserver également la sortie d'erreur standard dans un fichier séparé, vous pouvez utiliser quelque chose comme:
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
Je l'ai fait une série d'améliorations sur le autre réponse .
- 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
- nettoie après son pidfile lorsque fin
- période de délai d'attente en option configurable (Accepte tout argument numérique positif, envoie à
sleep
) - invite de l'utilisation sur
-h
- 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 - 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.