Comment démoniser un script arbitraire sous Unix?


93

J'aimerais un démoniseur capable de transformer un script ou une commande arbitraire et générique en démon .

Je voudrais traiter deux cas courants:

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

  2. J'ai un script simple ou une commande de ligne de commande que j'aimerais continuer à exécuter à plusieurs reprises pour toujours (avec une courte pause entre les exécutions). Encore une fois, n'autorisez pas l'exécution simultanée de deux copies du script.

Bien sûr, il est trivial d'écrire une boucle «while (true)» autour du script dans le cas 2, puis d'appliquer une solution pour le cas 1, mais une solution plus générale résoudra simplement le cas 2 directement puisque cela s'applique au script dans le cas 1 comme eh bien (vous voudrez peut-être juste une pause plus courte ou pas de pause si le script n'est pas destiné à mourir (bien sûr, si le script ne meurt vraiment jamais, la pause n'a pas vraiment d'importance)).

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, j'aimerais un programme "daemonize" que je peux exécuter comme

% daemonize myscript arg1 arg2

ou, par exemple,

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

qui garderait une liste croissante de dates ajoutées à times.txt. (Notez que si le (s) argument (s) à démoniser est un script qui s'exécute pour toujours comme dans le cas 1 ci-dessus, alors daemonize fera toujours la bonne chose, en le redémarrant si nécessaire.) Je pourrais alors mettre une commande comme ci-dessus dans mon .login et / ou le cron toutes les heures ou toutes les minutes (en fonction de mon inquiétude à ce qu'il meure de façon inattendue).

NB: Le script daemonize devra se souvenir de la chaîne de commande qu'il démonise pour que si la même chaîne de commande est à nouveau démonisée, il ne lance pas une deuxième copie.

En outre, la solution devrait idéalement fonctionner à la fois sur OS X et Linux, mais les solutions pour l'un ou l'autre sont les bienvenues.

EDIT: C'est bien si vous devez l'invoquer avec sudo daemonize myscript myargs.

(Si je pense à tout cela mal ou qu'il existe des solutions partielles rapides et sales, j'aimerais aussi l'entendre.)


PS: Au cas où cela serait utile, voici une question similaire spécifique à python.

Et cette réponse à une question similaire a ce qui semble être un idiome utile pour une diabolisation rapide et sale d'un script arbitraire:


1
Voir serverfault.com/questions/311593/… pour une version pure shell
w00t

Réponses:


90

Vous pouvez démoniser n'importe quel 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 que cela ne tue votre script, tandis que le & place votre script en arrière-plan afin que vous obteniez une invite du shell pour continuer votre session. Le seul problème mineur avec ceci est la sortie standard et l'erreur standard toutes deux envoyées à ./nohup.out, donc si vous démarrez plusieurs scripts dans ce manoir, leur sortie sera entrelacée. Une meilleure commande serait:

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

Cela enverra le standard vers le fichier de votre choix et l'erreur standard vers un autre fichier de votre choix. Si vous souhaitez utiliser un seul fichier pour la sortie standard et l'erreur standard, vous pouvez utiliser ceci:

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

Le 2> & 1 indique au shell de rediriger l'erreur standard (descripteur de fichier 2) vers le même fichier que la sortie standard (descripteur de fichier 1).

Pour exécuter une commande une seule fois et la redémarrer 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 deuxième argument est la commande. Et tous les autres arguments sont les arguments de la commande.

Si vous nommez ce script restart.sh, voici comment vous l'appelleriez:

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

Impressionnant; Merci. Je me demande s'il devrait également avoir une option pour un retard dans le redémarrage. Ou peut-être mieux de l'utiliser en conjonction avec ceci: stackoverflow.com/questions/555116/…
dreeves

4
Cela ne gère que SIGHUP, il y a d'autres signaux fatals (normalement) qui doivent être traités.
Tim Post

Une autre façon d'améliorer ce script est probablement de lui proposer un bon emplacement pour placer le $ PIDFILE seul plutôt que d'exiger qu'il soit spécifié comme argument. Il ne nettoie même pas après lui-même! (ce qui devrait être simple avec a trap EXIT)
Steven Lu

Considérez également que l'utilisation de <dans testest une comparaison ASCII et non une comparaison entière. Cela peut toujours fonctionner, mais peut entraîner des bogues.
Steven Lu du

J'ai publié mes correctifs pour ce script ici .
Steven Lu

33

Je m'excuse pour la longue réponse (veuillez voir les commentaires sur la façon dont ma réponse correspond aux spécifications). J'essaie d'être compréhensif pour que vous ayez une longueur d'avance aussi bonne que possible. :-)

Si vous êtes en mesure d'installer des programmes (avec un accès root) et que vous êtes prêt à effectuer un travail ponctuel pour configurer votre script pour l'exécution du démon (c'est-à-dire plus compliqué que de simplement spécifier les arguments de ligne de commande à exécuter sur la ligne de commande, mais ne devant être fait qu'une fois par service), j'ai un moyen plus robuste.

Cela implique l'utilisation de daemontools . Le reste de l'article décrit comment configurer des services à l'aide de daemontools.

La configuration initiale

  1. Suivez les instructions de la section Comment installer daemontools . Certaines distributions (par exemple, Debian, Ubuntu) ont déjà des packages pour cela, alors utilisez-le simplement.
  2. Créez un répertoire appelé /service. Le programme d'installation devrait déjà avoir fait cela, mais il suffit de vérifier, ou si l'installation est manuelle. Si vous n'aimez pas cet emplacement, vous pouvez le changer dans votre svscanbootscript, bien que la plupart des utilisateurs de daemontools soient habitués à l'utiliser /serviceet seront confus si vous ne l'utilisez pas.
  3. Si vous utilisez Ubuntu ou une autre distribution qui n'utilise pas standard init(c'est-à-dire qui n'utilise pas /etc/inittab), vous devrez utiliser le pré-installé inittabcomme base pour organiser svscanbootl'appel par init. Ce n'est pas difficile, mais vous devez savoir comment configurer le initque votre système d'exploitation utilise. svscanbootest un script qui appelle svscan, qui effectue le travail principal de recherche de services; il est appelé à partir de celui-ci init, initil s'arrangera pour le redémarrer s'il meurt pour une raison quelconque.

Configuration par service

  1. Chaque service a besoin d'un répertoire de services , qui stocke des informations de gestion interne sur le service. Vous pouvez également créer un emplacement pour héberger ces répertoires de services afin qu'ils soient tous au même endroit; J'utilise habituellement /var/lib/svscan, mais tout nouvel emplacement conviendra.
  2. J'utilise généralement un script pour configurer le répertoire de services, pour économiser beaucoup de travail manuel répétitif. par exemple,

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

    some-service-nameest le nom que vous souhaitez donner à votre service, userest l'utilisateur sous lequel exécuter ce service et loguserest l'utilisateur sous lequel exécuter le journal. (La journalisation est expliquée dans quelques instants.)

  3. Votre service doit s'exécuter au premier plan . Si votre programme est en arrière-plan par défaut, mais qu'il a une option pour le désactiver, alors faites-le. Si votre programme est en arrière-plan sans moyen de le désactiver, lisez la suite fghack, bien que cela vienne à un compromis: vous ne pouvez plus contrôler le programme en utilisant svc.
  4. Modifiez le runscript pour vous assurer qu'il fait ce que vous voulez. Vous devrez peut-être passer un sleepappel en haut, si vous prévoyez que votre service se termine fréquemment.
  5. Lorsque tout est correctement configuré, créez un lien symbolique /servicepointant vers votre répertoire de services. (Ne mettez pas de répertoires de services directement à l'intérieur /service; cela rend plus difficile la suppression du service de svscanla montre de.)

Enregistrement

  1. La méthode de journalisation de daemontools consiste à demander au service d'écrire des messages de journal sur la sortie standard (ou erreur standard, si vous utilisez des scripts générés avec mkservice); svscanprend en charge l'envoi des messages de journal au service de journalisation.
  2. Le service de journalisation prend les messages du journal à partir de l'entrée standard. Le script du service de journalisation généré par mkservicecrée des fichiers journaux horodatés à rotation automatique dans le log/mainrépertoire. Le fichier journal actuel est appelé current.
  3. Le service de journalisation peut être démarré et arrêté indépendamment du service principal.
  4. Le transfert des fichiers journaux tai64nlocaltraduira les horodatages dans un format lisible par l'homme. (TAI64N est un horodatage atomique 64 bits avec un nombre de nanosecondes.)

Services de contrôle

  1. Utilisez svstatpour obtenir le statut d'un service. Notez que le service de journalisation est indépendant et a son propre statut.
  2. Vous contrôlez votre service (démarrage, arrêt, redémarrage, etc.) à l'aide de svc. Par exemple, pour redémarrer votre service, utilisez svc -t /service/some-service-name; -tsignifie «envoyer SIGTERM».
  3. Les autres signaux disponibles incluent -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2) et -k( SIGKILL).
  4. Pour arrêter le service, utilisez -d. Vous pouvez également empêcher un service de démarrer automatiquement au démarrage en créant un fichier nommé downdans le répertoire du service.
  5. Pour démarrer le service, utilisez -u. Cela n'est pas nécessaire sauf si vous l'avez arrêté précédemment (ou configuré pour ne pas démarrer automatiquement).
  6. Pour demander au superviseur de quitter, utilisez -x; généralement utilisé avec -dpour mettre fin au service. C'est la manière habituelle d'autoriser la suppression d'un service, mais vous devez d'abord dissocier le service /service, sinon vous svscanredémarrerez le superviseur. De plus, si vous avez créé votre service avec un service de journalisation ( mkservice -l), n'oubliez pas de quitter également le superviseur de journalisation (par exemple svc -dx /var/lib/svscan/some-service-name/log) avant de supprimer le répertoire de services.

Résumé

Avantages:

  1. daemontools fournit un moyen infaillible pour créer et gérer des services. Je l'utilise pour mes serveurs et je le recommande vivement.
  2. Son système de journalisation est très robuste, tout comme la fonction de redémarrage automatique du service.
  3. Comme il démarre les services avec un script shell que vous écrivez / réglez, vous pouvez personnaliser votre service comme vous le souhaitez.
  4. De puissants outils de contrôle de service: vous pouvez envoyer la plupart des signaux à un service et faire monter et descendre les services de manière fiable.
  5. Vos services sont garantis un environnement d'exécution propre: ils s'exécuteront avec le même environnement, les mêmes limites de processus, etc., que ce que initfournit.

Les inconvénients:

  1. Chaque service nécessite un peu de configuration. Heureusement, cela ne nécessite qu'une seule fois par service.
  2. Les services doivent être configurés pour s'exécuter au premier plan. En outre, pour de meilleurs résultats, ils doivent être configurés pour se connecter à la sortie standard / erreur standard, plutôt que syslog ou d'autres fichiers.
  3. Une courbe d'apprentissage abrupte si vous êtes nouveau dans la façon de faire des daemontools. Vous devez redémarrer les services en utilisant svcet ne pouvez pas exécuter les scripts d'exécution directement (car ils ne seraient alors pas sous le contrôle du superviseur).
  4. Beaucoup de fichiers de gestion interne et de nombreux processus de gestion interne. Chaque service a besoin de son propre annuaire de services, et chaque service utilise un processus de supervision pour redémarrer automatiquement le service s'il meurt. (Si vous avez de nombreux services, vous verrez beaucoup de superviseprocessus dans votre table de processus.)

Dans l'ensemble, je pense que daemontools est un excellent système pour vos besoins. Je suis ravi de toute question sur la manière de le configurer et de le maintenir.


Comment ma réponse cloue la spécification: 1. Vous devez configurer des services, donc tant que vous ne configurez pas de doublons (et tant que votre service ne se met pas en arrière-plan), aucun doublon ne se produira. 2. supervise, le superviseur, se charge de redémarrer tout service qui sort. Il attend une seconde entre les restars; si ce n'est pas assez de temps pour vous, mettez en veille en haut du script d'exécution du service.
Chris Jester-Young

2a. superviseest lui-même soutenu par svscan, donc si un superviseur meurt, il sera redémarré. 2b. svscanest soutenu par init, qui redémarrera automatiquement svscansi nécessaire. 2c. Si votre initmeurt pour une raison quelconque, vous êtes foutu de toute façon. :-P
Chris Jester-Young

Pour répondre à d'autres questions sur l'entretien ménager, le système daemontools n'utilise pas de fichiers PID, car ils peuvent devenir obsolètes. Au lieu de cela, toutes les informations de processus sont conservées par le superviseur qui soutient un service donné. Le superviseur maintient un tas de fichiers (et FIFO) dans le répertoire de service que les outils aiment svstatet svcpeuvent utiliser.
Chris Jester-Young

3
Nous devrions avoir plus de messages comme celui-ci dans SO et sur le net en général. Pas seulement une recette sur la façon d'obtenir l'effet désiré, mais une recette qui prend la peine d'expliquer les recettes. Pourquoi ne puis-je pas voter pour cela plus d'une fois? : |
skytreader

12

Je pense que vous voudrez peut-être essayer start-stop-daemon(8). Consultez les scripts dans /etc/init.dn'importe quelle distribution Linux pour des exemples. Il peut trouver les processus démarrés par ligne de commande invoquée ou par fichier PID, de sorte qu'il correspond à toutes vos exigences, sauf en tant que chien de garde pour votre script. Mais vous pouvez toujours démarrer un autre script de surveillance du démon qui redémarre simplement votre script si nécessaire.


Il n'y a pas encore de démon start-stop dans Fedora, donc les scripts qui en dépendent ne sont pas portables. Voir: fedoraproject.org/wiki/Features/start-stop-daemon
Bengt

Juste un avertissement pour les utilisateurs d'OSX: non start-stop-daemonlà non plus (à partir de 10.9).
mklement0

@ mklement0 Eh bien ... beaucoup de choses ont changé en près de 5 ans.
Alex B

Mon, comme le temps passe vite. start-stop-daemonest toujours en vie sur Linux, cependant; mais après avoir lu réponse stackoverflow.com/a/525406/45375 je réalisais que OSX fait sa propre chose: launchd.
mklement0

12

Vous devriez jeter un œil à daemonize . Il permet de détecter une seconde copie (mais il utilise un mécanisme de verrouillage de fichier). Il fonctionne également sur différentes distributions UNIX et Linux.

Si vous avez besoin de démarrer automatiquement votre application en tant que démon, vous devez créer un 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

1
On dirait un candidat pour la bonne réponse. Surtout compte tenu de sa "vérification d'instance unique".
Martin Wickman

C'est peut-être la meilleure réponse possible - je ne suis pas sûr - mais si vous pensez que c'est le cas, pouvez-vous également expliquer pourquoi la spécification que j'ai donnée dans la question est mal orientée?
dreeves

Je n'aime pas la killprocpartie d'arrêt: si vous aviez un processus qui, disons, a été exécuté java, killproccela entraînera la destruction de tous les autres processus Java.
Chris Jester-Young

1
Depuis /etc/rc.d/init.d/functions, daemonize démarre juste le binaire à partir d'un nouveau shell: $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*Donc je doute que ça démonise quoi que ce soit ...
mbonnin

1
Je sais que c'est vieux, mais pour quiconque trouvera ça plus tard ... c'est correct. "daemon" tel que défini dans /etc/init.d/functions ne démonise pas réellement pour vous. C'est juste un wrapper pour faire des groupes de contrôle, vérifier si le processus est déjà en cours d'exécution, définir un utilisateur, définir des valeurs nice et ulimit, etc. Cela ne démonise pas le processus pour vous. C'est toujours votre propre travail. :)
jakem

7

Comme alternative au déjà mentionné daemonizeet daemontools, il y a le démon commande du paquet libslack.

daemon est assez configurable et se soucie de toutes les tâches fastidieuses du démon telles que le redémarrage automatique, la journalisation ou la gestion des fichiers pid.


5

Si vous utilisez spécifiquement OS X, je vous suggère de jeter un œil au fonctionnement de launchd. Il vérifiera automatiquement que votre script est en cours d'exécution et le relancera si nécessaire. Il comprend également toutes sortes de fonctionnalités de planification, etc. Il doit satisfaire à la fois les exigences 1 et 2.

Pour vous assurer qu'une seule copie de votre script peut s'exécuter, vous devez utiliser un fichier PID. En général, j'écris un fichier dans /var/run/.pid qui contient un PID de l'instance en cours d'exécution. si le fichier existe lorsque le programme s'exécute, il vérifie si le PID dans le fichier est réellement en cours d'exécution (le programme peut avoir planté ou oublié de supprimer le fichier PID). Si c'est le cas, abandonnez. Sinon, lancez l'exécution et écrasez le fichier PID.


5

Daemontools ( http://cr.yp.to/daemontools.html ) est un ensemble d'utilitaires assez complexes utilisés pour faire cela, écrits par dj bernstein. J'ai utilisé ceci avec un certain succès. La partie ennuyeuse à ce sujet est qu'aucun des scripts ne renvoie de résultats visibles lorsque vous les exécutez - juste des codes de retour invisibles. Mais une fois qu'il fonctionne, il est à l'épreuve des balles.


Oui, j'allais écrire une entrée qui utilise aussi daemontools. J'écrirai mon propre article, car j'espère être beaucoup plus complet avec ma réponse, et j'espère obtenir la prime de cette façon. Nous verrons. :-)
Chris Jester-Young

3

Obtenez d'abord createDaemon()sur http://code.activestate.com/recipes/278731/

Puis le code principal:

import subprocess
import sys
import time

createDaemon()

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

Ooh, merci! Voulez-vous le rendre un peu plus général pour pouvoir faire "daemonize foo arg1 arg2" ainsi que "daemonize 'foo arg1 arg2'"?
dreeves

Ok, il joindra les arguments maintenant - mais vous devrez le changer si vous voulez un jour avoir des espaces dans vos arguments.
Douglas Leeder

Merci Douglas! Il y a cependant un gros défaut: exécuter "daemonize foo" deux fois démarre deux copies de foo en cours d'exécution.
dreeves

Vous pouvez ajouter du code d'enregistrement PID, mais il serait peut-être préférable d'exécuter le script une seule fois ...
Douglas Leeder

Je pense que c'est fondamental pour tout le concept de wrapper "démoniser". (Par exemple, je pourrais alors le croner toutes les heures ou toutes les minutes pour m'assurer qu'il fonctionnait toujours.) Est-ce que je pense mal à cela? CreateDaemon garantit-il déjà cela d'une manière ou d'une autre? Qu'en est-il après le redémarrage?
dreeves le

1

Ceci est une version de travail complète 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 , File :: Spec , File :: Pid et IPC :: System: : Simple - tous assez standard et sont fortement recommandés pour tout pirate informatique: vous pouvez tous les installer en même temps 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";

Vous pouvez maintenant invoquer l'exemple ci-dessus avec: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3et le fichier pidfilesera 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

Je pense que ce n'est pas tout à fait conforme aux spécifications, si je comprends bien. Dans votre solution (merci, btw!), Le programme que vous souhaitez démoniser doit être modifié pour écrire son PID dans le fichier PID. J'espère un utilitaire capable de démoniser un script arbitraire.
dreeves

@dreeves: oui, mais il y a deux façons de contourner cela: 1. le script invoqué par keepAlive.pl (par exemple example.pl) pourrait simplement être un wrapper pour exécuter le programme réel, ou 2. keepAlive.pl pourrait analyser la table de processus système actif (avec Proc :: ProcessTable du CPAN) pour tenter de trouver le processus pertinent et son pid).
Ether

1

Vous pouvez également essayer Monit . Monit est un service qui surveille et rend compte des autres services. Bien qu'il soit principalement utilisé pour notifier (par e-mail et sms) les problèmes d'exécution, il peut également faire ce que la plupart des autres suggestions ici recommandent. Il peut (re) démarrer et arrêter automatiquement des programmes, envoyer des e-mails, lancer d'autres scripts et maintenir un journal de sortie que vous pouvez récupérer. De plus, j'ai trouvé qu'il était facile à installer et à maintenir car il existe une documentation solide.


1

Vous pouvez essayer d' immortaliser. C'est un superviseur multiplateforme * nix (indépendant du système d'exploitation).

Pour un essai rapide sur macOS:

brew install immortal

Si vous utilisez FreeBSD depuis les ports ou en utilisant pkg:

pkg install immortal

Pour Linux en téléchargeant les binaires précompilés ou depuis la source: https://immortal.run/source/

Vous pouvez soit l'utiliser comme ceci:

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

Ou par un fichier de configuration 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

0

J'ai apporté une série d'améliorations sur l' autre réponse .

  1. stdout hors de ce script est purement constitué de stdout provenant de son enfant à moins qu'il ne se ferme en raison de la détection que la commande est déjà en cours d'exécution
  2. nettoie après son pidfile une fois terminé
  3. Délai d'expiration configurable facultatif (accepte tout argument numérique positif, envoie à sleep)
  4. invite d'utilisation activée -h
  5. exécution de commande arbitraire, plutôt qu'une exécution de commande unique. Le dernier argument OU les arguments restants (si plus d'un dernier argument) sont envoyés à eval, vous pouvez donc construire n'importe quelle sorte de script shell sous forme de chaîne à envoyer à ce script en tant que dernier argument (ou arguments de fin) pour qu'il démonise
  6. nombre d'arguments comparaisons effectuées avec -ltau 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

Usage:

$ 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

Sachez que si vous exécutez ce script à partir de répertoires différents, il peut utiliser différents fichiers pid et ne détecter aucune instance en cours d'exécution existante. Puisqu'il est conçu pour exécuter et redémarrer des commandes éphémères fournies via un argument, il n'y a aucun moyen de savoir si quelque chose a déjà été commencé, car qui doit dire s'il s'agit de la même commande ou non? Pour améliorer cette application de l'exécution d'une seule instance de quelque chose, une solution spécifique à la situation est nécessaire.

De plus, pour qu'il fonctionne comme un démon approprié, vous devez utiliser (au minimum) nohup comme le mentionne l'autre réponse. Je n'ai fait aucun effort pour fournir une résistance aux signaux que le processus peut recevoir.

Un autre point à noter est que tuer ce script (s'il a été appelé à partir d'un autre script qui est tué, ou avec un signal) peut ne pas réussir à tuer l'enfant, surtout si l'enfant est encore un autre script. Je ne sais pas pourquoi, mais cela semble être lié à la façon dont evalfonctionne, ce qui est mystérieux pour moi. Il peut donc être prudent de remplacer cette ligne par quelque chose qui n'accepte qu'une seule commande comme dans l'autre réponse.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.