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ąć:
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).
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:
Odpowiedzi:
Możesz zdemonizować dowolny plik wykonywalny w systemie Unix za pomocą nohup i operatora &:
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 &
źródło
trap EXIT
)<
wtest
jest porównaniem ASCII, a nie porównaniem liczb całkowitych. Może nadal działać, ale może prowadzić do błędów.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
/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 swoimsvscanboot
skrypcie, chociaż większość użytkowników daemontools jest do tego przyzwyczajona/service
i będą zdezorientowani, jeśli jej nie używasz.init
(czyli nie używa/etc/inittab
), trzeba będzie użyć preinstalowanyinittab
jako baza do aranżacjisvscanboot
być nazywany przezinit
. Nie jest to trudne, ale musisz wiedzieć, jak skonfigurowaćinit
używany przez system operacyjny.svscanboot
to skrypt, który wywołujesvscan
, który wykonuje główną pracę polegającą na wyszukiwaniu usług; jest wywoływany zinit
tegoinit
powodu, zorganizuje ponowne uruchomienie, jeśli z jakiegoś powodu umrze.Konfiguracja na usługę
/var/lib/svscan
, ale każda nowa lokalizacja będzie w porządku.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-name
to nazwa, którą chcesz nadać swojej usłudze,user
to użytkownik, który ma uruchamiać tę usługę, iloguser
jest użytkownikiem, jako którego ma uruchamiać program rejestrujący. (Rejestrowanie jest wyjaśnione trochę.)fghack
, chociaż wiąże się to z pewnym kompromisem: nie możesz już kontrolować programu za pomocąsvc
.run
skrypt, aby upewnić się, że robi to, co chcesz. Być może będziesz musiał wykonaćsleep
połączenie u góry, jeśli spodziewasz się, że Twoja usługa będzie często wychodzić./service
wskazujące na katalog usług. (Nie umieszczaj katalogów usług bezpośrednio w środku/service
; utrudnia to usunięcie usługi zsvscan
zegarka).Logowanie
mkservice
);svscan
dba o wysyłanie komunikatów dziennika do usługi logowania.mkservice
utworzy automatycznie obracane pliki dziennika ze znacznikami czasu wlog/main
katalogu. Bieżący plik dziennika nosi nazwęcurrent
.tai64nlocal
spowoduje przetłumaczenie sygnatur czasowych na format czytelny dla człowieka. (TAI64N to 64-bitowy atomowy znacznik czasu z liczbą nanosekund).Usługi kontrolne
svstat
do sprawdzania statusu usługi. Zauważ, że usługa logowania jest niezależna i ma swój własny status.svc
. Na przykład, aby zrestartować usługę, użyjsvc -t /service/some-service-name
;-t
oznacza „wyślijSIGTERM
”.-h
(SIGHUP
),-a
(SIGALRM
),-1
(SIGUSR1
),-2
(SIGUSR2
) i-k
(SIGKILL
).-d
. Można również zapobiec automatycznemu uruchamianiu usługi podczas rozruchu, tworząc plik o nazwiedown
w katalogu usługi.-u
. Nie jest to konieczne, chyba że wcześniej go wyłączyłeś (lub nie ustawiłeś automatycznego uruchamiania).-x
; zwykle używany również w-d
celu 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 raziesvscan
zrestartujesz 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:
init
zapewnia.Cons:
svc
i nie możesz bezpośrednio uruchamiać skryptów (ponieważ wtedy nie byłyby one pod kontrolą przełożonego).supervise
procesó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ć.
źródło
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.supervise
jest wspierany przezsvscan
, więc jeśli przełożony umrze, zostanie uruchomiony ponownie. 2b.svscan
jest wspierany przezinit
, którysvscan
w razie potrzeby zostanie automatycznie uruchomiony ponownie . 2c. Jeśliinit
umrzesz z jakiegokolwiek powodu, i tak masz przerąbane. :-Psvstat
iz którymisvc
mogą pracować.Myślę, że możesz spróbować
start-stop-daemon(8)
./etc/init.d
Przykł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.źródło
start-stop-daemon
tam też nie (od 10.9).start-stop-daemon
jednak 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
.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
źródło
killproc
część zatrzymująca: jeśli masz proces, który, powiedzmy, został uruchomionyjava
,killproc
spowoduje to również zabicie wszystkich innych procesów Java.$corelimit >/dev/null 2>&1 ; $*
Więc wątpię, żeby cokolwiek zdemonizował ...Alternatywą dla wspomnianego już
daemonize
idaemontools
jest 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.źródło
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.
źródło
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.
źródło
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)
źródło
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 3
a plikpidfile
zostanie utworzony, a zobaczysz wynik:Pid <random number here> no longer running; restarting ./example.pl 1 2 3 ./example.pl got arguments: 1 2 3
źródło
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.
źródło
Możesz spróbować nieśmiertelności. Jest to nadzorca wieloplatformowy * nix (niezależny od systemu operacyjnego).
Aby szybko wypróbować system macOS:
W przypadku, gdy używasz FreeBSD z portów lub używając pkg:
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
źródło
Dokonałem szeregu ulepszeń w drugiej odpowiedzi .
sleep
)-h
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-lt
zamiast<
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
eval
dział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.źródło