Jak zdemonizować dowolny skrypt w systemie UNIX?

94

Chciałbym demonizator, który może zamienić dowolny, ogólny skrypt lub polecenie w demona .

Są dwa typowe przypadki, którymi chciałbym się zająć:

  1. Mam skrypt, który powinien działać wiecznie. Jeśli kiedykolwiek umrze (lub przy ponownym uruchomieniu), uruchom go ponownie. Nie pozwól, aby dwie kopie działały jednocześnie (sprawdź, czy kopia już działa i nie uruchamiaj jej w takim przypadku).

  2. Mam prosty skrypt lub polecenie wiersza poleceń, które chciałbym wykonywać wielokrotnie w nieskończoność (z krótką przerwą między uruchomieniami). Ponownie, nie pozwól, aby dwie kopie skryptu działały jednocześnie.

Oczywiście to trywialne jest napisanie pętli "while (prawda)" wokół skryptu w przypadku 2, a następnie zastosowanie rozwiązania dla przypadku 1, ale bardziej ogólne rozwiązanie po prostu rozwiąże bezpośrednio przypadek 2, ponieważ dotyczy to skryptu w przypadku 1 jako dobrze (być może po prostu nie krótszy lub pauzy, jeśli skrypt nie ma nigdy umrzeć (oczywiście jeśli skrypt naprawdę nie nigdy nie umierają wtedy pauza nie faktycznie sprawa)).

Zwróć uwagę, że rozwiązanie nie powinno obejmować, powiedzmy, dodawania kodu blokującego pliki lub nagrywania PID do istniejących skryptów.

Dokładniej, chciałbym program "demonizujący", który mógłbym uruchomić

% daemonize myscript arg1 arg2

lub na przykład

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

co pozwoliłoby na utrzymanie rosnącej listy dat dołączanych do times.txt. (Zauważ, że jeśli argumentem (argumentami) do demonizacji jest skrypt, który działa wiecznie, jak w przypadku 1 powyżej, wtedy daemonize nadal będzie działać właściwie, uruchamiając go ponownie w razie potrzeby.) Mogę wtedy umieścić polecenie jak powyżej w moim .login i / lub cron go co godzinę lub co minutę (w zależności od tego, jak bardzo się martwiłem, że to nieoczekiwanie umrze).

NB: Skrypt daemonize będzie musiał zapamiętać ciąg poleceń, które demonizuje, aby w przypadku ponownego zdemonizowania tego samego ciągu poleceń nie uruchamiał drugiej kopii.

Ponadto rozwiązanie powinno idealnie działać zarówno na OS X, jak i na Linuksie, ale rozwiązania dla jednego lub drugiego są mile widziane.

EDYCJA: W porządku, jeśli musisz go wywołać sudo daemonize myscript myargs.

(Jeśli myślę o tym źle lub są szybkie i brudne rozwiązania częściowe, też chciałbym to usłyszeć.)


PS: Na wypadek, gdyby było to przydatne, oto podobne pytanie dotyczące Pythona.

A ta odpowiedź na podobne pytanie ma coś, co wydaje się być użytecznym idiomem do szybkiej i brudnej demonizacji dowolnego scenariusza:

dreeves
źródło
1
Zobacz serverfault.com/questions/311593/… aby uzyskać czystą wersję powłoki
w00t

Odpowiedzi:

93

Możesz zdemonizować dowolny plik wykonywalny w systemie Unix za pomocą nohup i operatora &:

nohup yourScript.sh script args&

Polecenie nohup pozwala na zamknięcie sesji powłoki bez zabijania skryptu, podczas gdy & umieszcza skrypt w tle, aby uzyskać monit powłoki, aby kontynuować sesję. Jedynym drobnym problemem jest to, że standardowe wyjście i standardowy błąd są wysyłane do ./nohup.out, więc jeśli uruchomisz kilka skryptów w tym dworku, ich wyjście zostanie splecione. Lepszym poleceniem byłoby:

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

Spowoduje to wysłanie standardu do wybranego pliku, a standardowego błędu do innego wybranego pliku. Jeśli chcesz używać tylko jednego pliku zarówno dla standardowego wyjścia, jak i standardowego błędu, możesz to zrobić:

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

2> & 1 nakazuje powłoce przekierowanie błędu standardowego (deskryptor pliku 2) do tego samego pliku co wyjście standardowe (deskryptor pliku 1).

Aby uruchomić polecenie tylko raz i zrestartować je, jeśli umrze, możesz użyć tego skryptu:

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

Pierwszym argumentem jest nazwa pliku pid, który ma zostać użyty. Drugi argument to polecenie. A wszystkie inne argumenty są argumentami polecenia.

Jeśli nazwiesz ten skrypt restart.sh tak to nazwałbyś:

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &
Robert Menteer
źródło
Niesamowite; Dziękuję Ci. Zastanawiam się, czy powinien mieć też opcję opóźnienia w ponownym uruchomieniu. A może lepiej po prostu użyć go w połączeniu z tym: stackoverflow.com/questions/555116/ ...
dreeves
4
Obsługuje tylko SIGHUP, istnieją inne (zwykle) fatalne sygnały, które należy obsłużyć.
Tim Post
Innym sposobem na ulepszenie tego skryptu jest prawdopodobnie wymyślenie przez niego dobrej lokalizacji do samodzielnego umieszczenia $ PIDFILE, zamiast wymagania, aby był określony jako argument. Nawet po sobie nie czyści! (co powinno być proste z a trap EXIT)
Steven Lu
Weź również pod uwagę, że użycie znaku <w testjest porównaniem ASCII, a nie porównaniem liczb całkowitych. Może nadal działać, ale może prowadzić do błędów.
Steven Lu,
I zostały zaksięgowane moje poprawki do tego skryptu tutaj .
Steven Lu,
34

Przepraszam za długą odpowiedź (proszę zobaczyć komentarze o tym, jak moja odpowiedź spełnia wymagania). Staram się być wszechstronny, żebyś miał jak największą nogę. :-)

Jeśli jesteś w stanie instalować programy (masz uprawnienia roota) i chcesz wykonać jednorazową legalną pracę, aby skonfigurować swój skrypt do wykonywania demona (tj. Bardziej skomplikowane niż po prostu określanie argumentów wiersza poleceń do uruchomienia w wierszu poleceń, ale trzeba to zrobić tylko raz na usługę), mam sposób, który jest solidniejszy.

Polega na użyciu daemontools . W pozostałej części postu opisano, jak skonfigurować usługi przy użyciu narzędzi demonów.

Początkowe ustawienia

  1. Postępuj zgodnie z instrukcjami w Jak zainstalować daemontools . Niektóre dystrybucje (np. Debian, Ubuntu) mają już pakiety do tego, więc po prostu użyj tego.
  2. Utwórz katalog o nazwie /service. Instalator powinien już to zrobić, ale po prostu zweryfikuj, lub jeśli instalujesz ręcznie. Jeśli nie podoba ci się ta lokalizacja, możesz ją zmienić w swoim svscanbootskrypcie, chociaż większość użytkowników daemontools jest do tego przyzwyczajona/service i będą zdezorientowani, jeśli jej nie używasz.
  3. Jeśli używasz Ubuntu lub inną dystrybucję, które nie wykorzystują standardowe init(czyli nie używa /etc/inittab), trzeba będzie użyć preinstalowany inittabjako baza do aranżacji svscanbootbyć nazywany przez init. Nie jest to trudne, ale musisz wiedzieć, jak skonfigurować initużywany przez system operacyjny. svscanbootto skrypt, który wywołuje svscan, który wykonuje główną pracę polegającą na wyszukiwaniu usług; jest wywoływany z inittego initpowodu, zorganizuje ponowne uruchomienie, jeśli z jakiegoś powodu umrze.

Konfiguracja na usługę

  1. Każda usługa wymaga katalogu usług , w którym przechowywane są informacje porządkowe dotyczące usługi. Możesz również utworzyć lokalizację dla tych katalogów usług, aby wszystkie były w jednym miejscu; zwykle używam /var/lib/svscan, ale każda nowa lokalizacja będzie w porządku.
  2. Zwykle używam skryptu do skonfigurowania katalogu usług, aby zaoszczędzić wiele ręcznej, powtarzalnej pracy. na przykład,

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

    gdzie some-service-nameto nazwa, którą chcesz nadać swojej usłudze, userto użytkownik, który ma uruchamiać tę usługę, i loguserjest użytkownikiem, jako którego ma uruchamiać program rejestrujący. (Rejestrowanie jest wyjaśnione trochę.)

  3. Twoja usługa musi działać na pierwszym planie . Jeśli twój program domyślnie działa jako tło, ale ma opcję wyłączenia tego, zrób to. Jeśli twój program działa w tle bez możliwości jego wyłączenia, czytaj dalej fghack, chociaż wiąże się to z pewnym kompromisem: nie możesz już kontrolować programu za pomocą svc.
  4. Edytuj runskrypt, aby upewnić się, że robi to, co chcesz. Być może będziesz musiał wykonać sleeppołączenie u góry, jeśli spodziewasz się, że Twoja usługa będzie często wychodzić.
  5. Kiedy wszystko jest ustawione prawidłowo, utwórz łącze symboliczne /servicewskazujące na katalog usług. (Nie umieszczaj katalogów usług bezpośrednio w środku /service; utrudnia to usunięcie usługi z svscanzegarka).

Logowanie

  1. Sposób rejestrowania przez narzędzia demonów polega na zapisywaniu przez usługę komunikatów dziennika na standardowe wyjście (lub standardowy błąd, jeśli używasz skryptów wygenerowanych za pomocą mkservice); svscandba o wysyłanie komunikatów dziennika do usługi logowania.
  2. Usługa rejestrowania pobiera komunikaty dziennika ze standardowego wejścia. Skrypt usługi rejestrowania wygenerowany przez mkserviceutworzy automatycznie obracane pliki dziennika ze znacznikami czasu w log/mainkatalogu. Bieżący plik dziennika nosi nazwę current.
  3. Usługę logowania można uruchamiać i zatrzymywać niezależnie od usługi głównej.
  4. Przepuszczenie plików dziennika potokiem tai64nlocalspowoduje przetłumaczenie sygnatur czasowych na format czytelny dla człowieka. (TAI64N to 64-bitowy atomowy znacznik czasu z liczbą nanosekund).

Usługi kontrolne

  1. Służy svstatdo sprawdzania statusu usługi. Zauważ, że usługa logowania jest niezależna i ma swój własny status.
  2. Kontrolujesz swoją usługę (uruchamianie, zatrzymywanie, restartowanie itp.) Za pomocą svc. Na przykład, aby zrestartować usługę, użyj svc -t /service/some-service-name; -toznacza „wyślij SIGTERM”.
  3. Inne dostępne sygnały to -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2) i -k( SIGKILL).
  4. Aby wyłączyć usługę, użyj -d. Można również zapobiec automatycznemu uruchamianiu usługi podczas rozruchu, tworząc plik o nazwie downw katalogu usługi.
  5. Aby uruchomić usługę, użyj -u. Nie jest to konieczne, chyba że wcześniej go wyłączyłeś (lub nie ustawiłeś automatycznego uruchamiania).
  6. Aby poprosić przełożonego o wyjście, użyj -x; zwykle używany również w -dcelu zakończenia usługi. Jest to zwykły sposób zezwalania na usunięcie usługi, ale musisz najpierw odłączyć usługę /service, w przeciwnym razie svscanzrestartujesz nadzorcę. Ponadto, jeśli utworzyłeś swoją usługę za pomocą usługi logowania ( mkservice -l), pamiętaj, aby również zamknąć nadzorcę logowania (np. svc -dx /var/lib/svscan/some-service-name/log) Przed usunięciem katalogu usługi.

Podsumowanie

Plusy:

  1. daemontools zapewnia niezawodny sposób tworzenia usług i zarządzania nimi. Używam go na moich serwerach i bardzo go polecam.
  2. Jego system logowania jest bardzo solidny, podobnie jak funkcja automatycznego restartu usługi.
  3. Ponieważ uruchamia usługi za pomocą skryptu powłoki, który piszesz / dostrajasz, możesz dostosować usługę, jak chcesz.
  4. Potężne narzędzia do kontroli usług: możesz wysłać prawie każdy sygnał do serwisu i niezawodnie włączać i wyłączać usługi.
  5. Twoje usługi mają zagwarantowane czyste środowisko wykonawcze: będą działać z tym samym środowiskiem, ograniczeniami procesów itp., Co initzapewnia.

Cons:

  1. Każda usługa wymaga trochę konfiguracji. Na szczęście wystarczy to zrobić tylko raz na usługę.
  2. Usługi muszą być skonfigurowane do działania na pierwszym planie. Ponadto, aby uzyskać najlepsze wyniki, należy skonfigurować je tak, aby logowały się do standardowego wyjścia / standardowego błędu, a nie do syslog lub innych plików.
  3. Stroma krzywa uczenia się, jeśli jesteś nowy w daemontools sposób robienia rzeczy. Musisz ponownie uruchomić usługi przy użyciu svci nie możesz bezpośrednio uruchamiać skryptów (ponieważ wtedy nie byłyby one pod kontrolą przełożonego).
  4. Wiele plików porządkowych i wiele procesów porządkowych. Każda usługa potrzebuje własnego katalogu usług, a każda usługa używa jednego procesu nadzorczego do automatycznego restartowania usługi, jeśli ta umrze. (Jeśli masz wiele usług, widać wiele z superviseprocesów w tabeli procesowej).

Podsumowując, uważam, że daemontools to doskonały system dla Twoich potrzeb. Z zadowoleniem przyjmuję wszelkie pytania dotyczące tego, jak go skonfigurować i utrzymać.

Chris Jester-Young
źródło
Jak moja odpowiedź określa specyfikację: 1. Musisz skonfigurować usługi, tak długo, jak nie skonfigurujesz duplikatów (i tak długo, jak Twoja usługa nie działa w tle), nie wystąpią żadne duplikaty. 2. supervise, przełożony dba o ponowne uruchomienie każdej usługi, która zostanie zakończona. Czeka jedną sekundę między restarami; jeśli to dla ciebie za mało czasu, uśpij u góry skryptu uruchamiania usługi.
Chris Jester-Young
2a. supervisejest wspierany przezsvscan , więc jeśli przełożony umrze, zostanie uruchomiony ponownie. 2b. svscanjest wspierany przez init, który svscanw razie potrzeby zostanie automatycznie uruchomiony ponownie . 2c. Jeśli initumrzesz z jakiegokolwiek powodu, i tak masz przerąbane. :-P
Chris Jester-Young
Aby odpowiedzieć na inne pytania dotyczące porządkowania, system daemontools nie używa plików PID, ponieważ mogą się one zestarzeć. Zamiast tego wszystkie informacje o procesach są przechowywane przez przełożonego wspierającego daną usługę. Nadzorca przechowuje w katalogu usług kilka plików (i FIFO), które narzędzia lubią svstatiz którymi svcmogą pracować.
Chris Jester-Young
3
Powinniśmy mieć więcej takich postów w SO i ogólnie w sieci. Nie tylko przepis na to, jak osiągnąć pożądany efekt, ale taki, który zadaje sobie trud wyjaśniania przepisów. Dlaczego nie mogę zagłosować za tym więcej niż raz? : |
skytreader
12

Myślę, że możesz spróbować start-stop-daemon(8). /etc/init.dPrzykłady można znaleźć w skryptach w dowolnej dystrybucji Linuksa. Może znaleźć uruchomione procesy za pomocą wywoływanego wiersza poleceń lub pliku PID, więc spełnia wszystkie twoje wymagania, z wyjątkiem bycia strażnikiem dla twojego skryptu. Ale zawsze możesz uruchomić inny skrypt nadzorujący demona, który w razie potrzeby po prostu zrestartuje skrypt.

Alex B.
źródło
Nie ma jeszcze demona start-stop-w Fedorze, więc skrypty zależne od niego nie są przenośne. Zobacz: fedoraproject.org/wiki/Features/start-stop-daemon
Bengt
Tylko ostrzeżenie dla użytkowników OSX: start-stop-daemontam też nie (od 10.9).
mklement0
@ mklement0 Cóż ... wiele się zmieniło w ciągu prawie 5 lat.
Alex B
Mój, jak czas leci. start-stop-daemonjednak wciąż żyje i działa na Linuksie; ale po przeczytaniu odpowiedzi stackoverflow.com/a/525406/45375 zdałem sobie sprawę, że OSX ma swoje własne rzeczy: launchd.
mklement0
12

Powinieneś rzucić okiem na daemonize . Pozwala wykryć drugą kopię (ale wykorzystuje mechanizm blokowania plików). Działa również w różnych dystrybucjach UNIX i Linux.

Jeśli chcesz automatycznie uruchamiać aplikację jako demon, musisz utworzyć odpowiedni skrypt init.

Możesz użyć następującego szablonu:

#!/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
uthark
źródło
1
Wygląda na kandydata do poprawnej odpowiedzi. Zwłaszcza biorąc pod uwagę jego „sprawdzanie pojedynczych instancji”.
Martin Wickman,
To może być najlepsza możliwa odpowiedź - nie jestem pewien - ale jeśli myślisz, że tak, czy możesz również dołączyć wyjaśnienie, dlaczego specyfikacja, którą podałem w pytaniu, jest błędna?
dreeves
Nie podoba mi się killprocczęść zatrzymująca: jeśli masz proces, który, powiedzmy, został uruchomiony java, killprocspowoduje to również zabicie wszystkich innych procesów Java.
Chris Jester-Young
1
Z /etc/rc.d/init.d/functions, daemonize właśnie uruchamia plik binarny z nowej powłoki: $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*Więc wątpię, żeby cokolwiek zdemonizował ...
mbonnin
1
Wiem, że to jest stare, ale dla każdego, kto znajdzie to później ... to jest poprawne. "demon" zdefiniowany w /etc/init.d/functions w rzeczywistości nie demonizuje dla ciebie. To tylko wrapper zrobić cgroups sprawdzić, czy proces jest już uruchomiony, ustawiony przez użytkownika, ustaw ładne i limitów ulimit wartości itd to nie nie daemonize proces dla Ciebie. To nadal twoja własna praca. :)
jakem
7

Alternatywą dla wspomnianego już daemonizei daemontoolsjest polecenie demona pakietu libslack.

daemon jest dość konfigurowalny i dba o wszystkie żmudne rzeczy związane z demonami, takie jak automatyczny restart, logowanie lub obsługa plików pidfile.

jefz
źródło
5

Jeśli używasz w szczególności OS X, proponuję przyjrzeć się, jak działa launchd. Automatycznie sprawdzi, czy skrypt działa, iw razie potrzeby uruchomi go ponownie. Zawiera również wszelkiego rodzaju funkcje planowania itp. Powinien spełniać wymagania 1 i 2.

Aby zapewnić działanie tylko jednej kopii skryptu, musisz użyć pliku PID. Ogólnie piszę plik do /var/run/.pid, który zawiera PID aktualnie działającej instancji. jeśli plik istnieje podczas działania programu, sprawdza, czy identyfikator PID w pliku faktycznie działa (program mógł ulec awarii lub w inny sposób zapomniał usunąć plik PID). Jeśli tak, przerwij. Jeśli nie, zacznij działać i nadpisz plik PID.

Kamil Kisiel
źródło
5

Daemontools ( http://cr.yp.to/daemontools.html ) to zestaw dość solidnych narzędzi używanych do tego, napisany przez dj bernstein. Użyłem tego z pewnym sukcesem. Irytujące jest to, że żaden ze skryptów nie zwraca żadnych widocznych wyników po ich uruchomieniu - tylko niewidoczne kody zwrotne. Ale kiedy już działa, jest kuloodporny.

szybkie mnożenie
źródło
Tak, miałem zamiar napisać wpis, który również używa daemontools. Napiszę własny post, ponieważ mam nadzieję, że moja odpowiedź będzie znacznie bardziej wyczerpująca i mam nadzieję, że w ten sposób zdobędę nagrodę. Zobaczymy. :-)
Chris Jester-Young
3

Najpierw skorzystaj createDaemon()z http://code.activestate.com/recipes/278731/

Następnie główny kod:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)
Douglas Leeder
źródło
Ooh, dzięki! Chcesz uczynić to nieco bardziej ogólnym, aby móc wykonać „demonizację foo arg1 arg2” oraz „daemonizację 'foo arg1 arg2'”?
dreeves
Ok, teraz połączy argumenty - jednak będziesz musiał to zmienić, jeśli kiedykolwiek zechcesz mieć spacje w swoich argumentach.
Douglas Leeder
Dzięki, Douglas! Jest jednak duża wada: dwukrotne uruchomienie "daemonize foo" powoduje uruchomienie dwóch kopii foo.
dreeves
Mógłbyś dodać kod do nagrywania PID, ale najlepiej byłoby uruchomić skrypt tylko raz ...
Douglas Leeder
Myślę o tym jako o fundamentalnym znaczeniu dla całej koncepcji „demonizowania” opakowania. (Np. Mógłbym wtedy cronować go co godzinę lub co minutę, aby upewnić się, że zawsze działa.) Czy myślę o tym źle? Czy createDaemon już w jakiś sposób to gwarantuje? A co po restarcie?
dreeves
1

To jest działająca wersja wraz z przykładem, który można skopiować do pustego katalogu i wypróbować (po zainstalowaniu zależności CPAN, którymi są Getopt :: Long , File :: Spec , File :: Pid i IPC :: System: : Prosty - wszystkie są dość standardowe i są wysoce zalecane dla każdego hakera: możesz zainstalować je wszystkie naraz za pomocą 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";

Teraz możesz wywołać powyższy przykład za pomocą: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3a plik pidfilezostanie utworzony, a zobaczysz wynik:

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3
Eter
źródło
Uważam, że nie jest to do końca do specyfikacji, jeśli dobrze rozumiem. W twoim rozwiązaniu (dzięki, przy okazji!) Program, który chcesz zdemonizować, musi zostać zmodyfikowany, aby zapisywał swój PID w pliku PID. Mam nadzieję na narzędzie, które daemonizuje dowolny skrypt.
dreeves
@dreeves: tak, ale można to obejść na dwa sposoby: 1. skrypt wywołany przez keepAlive.pl (np. example.pl) może być po prostu opakowaniem do wykonania prawdziwego programu, lub 2. keepAlive.pl mógłby parsować tabelę aktywne procesy systemowe (z CPAN's Proc :: ProcessTable), aby spróbować znaleźć odpowiedni proces i jego pid).
Ether
1

Możesz także wypróbować Monit . Monit to usługa, która monitoruje i raportuje inne usługi. Chociaż jest używany głównie jako sposób powiadamiania (przez e-mail i sms) o problemach z uruchomieniem, może również robić to, co zalecała większość innych sugestii. Może automatycznie (ponownie) uruchamiać i zatrzymywać programy, wysyłać e-maile, inicjować inne skrypty i utrzymywać dziennik wyników, który można odebrać. Ponadto stwierdziłem, że jest łatwy w instalacji i utrzymaniu, ponieważ istnieje solidna dokumentacja.

Adestin
źródło
1

Możesz spróbować nieśmiertelności. Jest to nadzorca wieloplatformowy * nix (niezależny od systemu operacyjnego).

Aby szybko wypróbować system macOS:

brew install immortal

W przypadku, gdy używasz FreeBSD z portów lub używając pkg:

pkg install immortal

W przypadku systemu Linux pobierając wstępnie skompilowane pliki binarne lub ze źródła: https://immortal.run/source/

Możesz go użyć w ten sposób:

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

Lub przez plik konfiguracyjny YAML , który daje więcej opcji, na przykład:

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

Jeśli chciałbyś zachować standardowe wyjście błędów w oddzielnym pliku, możesz użyć czegoś takiego:

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
nbari
źródło
0

Dokonałem szeregu ulepszeń w drugiej odpowiedzi .

  1. stdout z tego skryptu składa się wyłącznie z wyjścia standardowego pochodzącego od jego dziecka podrzędnego, JEŚLI nie wychodzi z powodu wykrycia, że ​​polecenie jest już wykonywane
  2. czyści po swoim pliku pid po zakończeniu
  3. opcjonalny konfigurowalny limit czasu (akceptuje każdy dodatni argument liczbowy, wysyła do sleep)
  4. włączony monit o użycie -h
  5. wykonanie dowolnego polecenia, a nie wykonanie pojedynczego polecenia. Ostatni argument LUB pozostałe argumenty (jeśli więcej niż jeden ostatni argument) są wysyłane do eval, więc możesz skonstruować dowolny rodzaj skryptu powłoki jako ciąg znaków do wysłania do tego skryptu jako ostatni argument (lub końcowe argumenty), aby zdemonizował go
  6. porównania liczby argumentów wykonane z -ltzamiast<

Oto skrypt:

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

Stosowanie:

$ 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

Uważaj, jeśli uruchomisz ten skrypt z różnych katalogów, może on używać różnych plików pid i nie wykryć żadnych istniejących uruchomionych instancji. Ponieważ jest przeznaczony do uruchamiania i ponownego uruchamiania poleceń efemerycznych dostarczonych przez argument, nie ma możliwości sprawdzenia, czy coś zostało już uruchomione, ponieważ kto ma powiedzieć, czy jest to to samo polecenie czy nie? Aby ulepszyć wymuszanie uruchamiania tylko jednej instancji czegoś, wymagane jest rozwiązanie dostosowane do sytuacji.

Ponadto, aby działał jako właściwy demon, musisz użyć (co najmniej) nohup, jak wspomina druga odpowiedź. Nie starałem się zapewnić odporności na sygnały, które może otrzymać proces.

Jeszcze jedną kwestią, na którą należy zwrócić uwagę, jest to, że zabicie tego skryptu (jeśli został wywołany z innego skryptu, który został zabity, lub z sygnałem) może nie udać się zabić dziecka, zwłaszcza jeśli dziecko jest kolejnym skryptem. Nie jestem pewien, dlaczego tak jest, ale wydaje się, że jest to coś związanego ze sposobem evaldziałania, co jest dla mnie tajemnicze. Dlatego rozsądnie może być zastąpienie tego wiersza czymś, co akceptuje tylko jedno polecenie, jak w drugiej odpowiedzi.

Steven Lu
źródło