Zastanawiam się, czy jest lepszy sposób na stworzenie demona, który czeka na coś używając tylko sh niż:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
W szczególności zastanawiam się, czy istnieje sposób, aby pozbyć się pętli i nadal mieć urządzenie nasłuchujące sygnałów.
Odpowiedzi:
Użyj funkcji demona systemu, takiej jak start-stop-daemon .
W przeciwnym razie tak, gdzieś musi być pętla.
źródło
Samo umieszczenie skryptu w tle (
./myscript &
) nie daemonizuje go. Zobacz http://www.faqs.org/faqs/unix-faq/programmer/faq/ , sekcja 1.7, w której opisano, co jest konieczne, aby zostać demonem. Musisz go odłączyć od terminala, żebySIGHUP
go nie zabić. Możesz użyć skrótu, aby skrypt wyglądał jak demon;wykona robotę. Lub, aby przechwycić stderr i stdout do pliku:
Jednak mogą istnieć inne ważne aspekty, które należy wziąć pod uwagę. Na przykład:
chdir("/")
(lubcd /
wewnątrz swojego skryptu) i rozwidlić, aby rodzic zakończył działanie, a tym samym oryginalny deskryptor został zamknięty.umask 0
. Możesz nie chcieć polegać na umasce wywołującego demona.Na przykład skrypt, który ma wszystkie te aspekty pod uwagę, patrz odpowiedź Mike S” .
źródło
0<&-
? Nie jest oczywiste, co osiąga ta sekwencja znaków.0<&-
ma robić. Znalazłem ten link, który to wyjaśnia.0<&-
zamyka stdin (fd 0). W ten sposób, jeśli twój proces przypadkowo odczyta ze standardowego wejścia (łatwe do zrobienia), otrzyma błąd zamiast zawieszać się wiecznie, czekając na pojawienie się danych.W niektórych odpowiedziach, które zostały tutaj zarzucone, brakuje niektórych ważnych części tego, co sprawia, że demon jest demonem, w przeciwieństwie do samego procesu w tle lub procesu w tle odłączonego od powłoki.
Ten http://www.faqs.org/faqs/unix-faq/programmer/faq/ opisuje, co jest niezbędne, aby być demonem. I ten skrypt Run bash jako demon implementuje id seta, chociaż pomija chdir do roota.
Pytanie pierwotnego autora było w rzeczywistości bardziej szczegółowe niż „Jak utworzyć proces demona przy użyciu basha?”, Ale ponieważ temat i odpowiedzi omawiają generalnie skrypty powłoki demonizujące, myślę, że ważne jest, aby je wskazać (dla intruzów takich jak ja drobne szczegóły tworzenia demona).
Oto moja wersja skryptu powłoki, który zachowywałby się zgodnie z FAQ. Ustaw DEBUG, aby
true
zobaczył ładny wynik (ale również kończy się natychmiast, zamiast zapętlać się w nieskończoność):#!/bin/bash DEBUG=false # This part is for fun, if you consider shell scripts fun- and I do. trap process_USR1 SIGUSR1 process_USR1() { echo 'Got signal USR1' echo 'Did you notice that the signal was acted upon only after the sleep was done' echo 'in the while loop? Interesting, yes? Yes.' exit 0 } # End of fun. Now on to the business end of things. print_debug() { whatiam="$1"; tty="$2" [[ "$tty" != "not a tty" ]] && { echo "" >$tty echo "$whatiam, PID $$" >$tty ps -o pid,sess,pgid -p $$ >$tty tty >$tty } } me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" me_FILE=$(basename $0) cd / #### CHILD HERE ---------------------------------------------------------------------> if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again. shift; tty="$1"; shift $DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty" umask 0 $me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$@" </dev/null >/dev/null 2>/dev/null & $DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty exit 0 fi ##### ENTRY POINT HERE --------------------------------------------------------------> if [ "$1" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts. tty=$(tty) $DEBUG && print_debug "*** PARENT" "$tty" setsid $me_DIR/$me_FILE child "$tty" "$@" & $DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty exit 0 fi ##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace --------------> # 3. We have been reforked. Go to work. exec >/tmp/outfile exec 2>/tmp/errfile exec 0</dev/null shift; tty="$1"; shift $DEBUG && print_debug "*** DAEMON" "$tty" # The real stuff goes here. To exit, see fun (above) $DEBUG && [[ "$tty" != "not a tty" ]] && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty $DEBUG || { while true; do echo "Change this loop, so this silly no-op goes away." >/dev/null echo "Do something useful with your life, young padawan." >/dev/null sleep 10 done } $DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$tty exit # This may never run. Why is it here then? It's pretty. # Kind of like, "The End" at the end of a movie that you # already know is over. It's always nice.
Wynik wygląda tak, gdy
DEBUG
jest ustawiony natrue
. Zwróć uwagę, jak zmieniają się numery sesji i grup procesów (SESS, PGID):źródło
fork()' so the parent can exit, this returns control to the command line or shell invoking your program. ... 2.
setsid ()", aby zostać grupą procesów i liderem grupy sesji ... nasz proces nie ma teraz terminala sterującego, którym jest Dobra Rzecz dla demonów… 3. „fork ()” ponownie, aby rodzic… mógł wyjść. Oznacza to, że my, jako lider grupy bez sesji, nigdy nie odzyskamy kontrolującego terminala. "# double background your script to have it detach from the tty # cf. http://www.linux-mag.com/id/5981 (./program.sh &) &
źródło
stdin
,stdout
,stderr
. Przynajmniej nie zsh
.To naprawdę zależy od tego, co zrobi sam plik binarny.
Na przykład chcę stworzyć słuchacza.
Uruchomienie demona to proste zadanie:
lis_deamon:
#!/bin/bash # We will start the listener as Deamon process # LISTENER_BIN=/tmp/deamon_test/listener test -x $LISTENER_BIN || exit 5 PIDFILE=/tmp/deamon_test/listener.pid case "$1" in start) echo -n "Starting Listener Deamon .... " startproc -f -p $PIDFILE $LISTENER_BIN echo "running" ;; *) echo "Usage: $0 start" exit 1 ;; esac
w ten sposób uruchamiamy demona (wspólny sposób dla całego personelu /etc/init.d/)
teraz, jeśli chodzi o samego słuchacza, to musi to być jakiś rodzaj pętli / ostrzeżenia, bo inaczej skrypt będzie robił to, co chcesz. Na przykład, jeśli chcesz, aby twój skrypt spał 10 minut i obudził się i zapytał, jak się masz, zrobisz to za pomocą
while true ; do sleep 600 ; echo "How are u ? " ; done
Oto prosty słuchacz, który możesz zrobić, który będzie nasłuchiwał poleceń ze zdalnej maszyny i wykonywał je lokalnie:
słuchacz:
#!/bin/bash # Starting listener on some port # we will run it as deamon and we will send commands to it. # IP=$(hostname --ip-address) PORT=1024 FILE=/tmp/backpipe count=0 while [ -a $FILE ] ; do #If file exis I assume that it used by other program FILE=$FILE.$count count=$(($count + 1)) done # Now we know that such file do not exist, # U can write down in deamon it self the remove for those files # or in different part of program mknod $FILE p while true ; do netcat -l -s $IP -p $PORT < $FILE |/bin/bash > $FILE done rm $FILE
Aby go uruchomić: / tmp / deamon_test / listener start
i do wysyłania poleceń z powłoki (lub zawijania do skryptu):
test_host#netcat 10.184.200.22 1024 uptime 20:01pm up 21 days 5:10, 44 users, load average: 0.62, 0.61, 0.60 date Tue Jan 28 20:02:00 IST 2014 punt! (Cntrl+C)
Mam nadzieję, że to pomoże.
źródło
$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &
źródło
Spójrz na narzędzie demona z pakietu libslack:
http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script/
W systemie Mac OS X użyj skryptu launchd dla demona powłoki.
źródło
Gdybym miał
script.sh
i chciałbym wykonać to z basha i zostawić go uruchomionego nawet wtedy, gdy chcę zamknąć moją sesję basha, połączyłbym gonohup
i&
na końcu.przykład:
nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &
inputFile.txt
może być dowolnym plikiem. Jeśli twój plik nie ma danych wejściowych, zwykle używamy/dev/null
. Więc polecenie byłoby następujące:nohup ./script.sh < /dev/null > ./logFile 2>&1 &
Następnie zamknij sesję basha, otwórz inny terminal i wykonaj:
ps -aux | egrep "script.sh"
a zobaczysz, że twój skrypt nadal działa w tle. Oczywiście, jeśli chcesz go zatrzymać, wykonaj to samo polecenie (ps) ikill -9 <PID-OF-YOUR-SCRIPT>
źródło
Zobacz projekt Bash Service Manager : https://github.com/reduardo7/bash-service-manager
Przykład realizacji
#!/usr/bin/env bash export PID_FILE_PATH="/tmp/my-service.pid" export LOG_FILE_PATH="/tmp/my-service.log" export LOG_ERROR_FILE_PATH="/tmp/my-service.error.log" . ./services.sh run-script() { local action="$1" # Action while true; do echo "@@@ Running action '${action}'" echo foo echo bar >&2 [ "$action" = "run" ] && return 0 sleep 5 [ "$action" = "debug" ] && exit 25 done } before-start() { local action="$1" # Action echo "* Starting with $action" } after-finish() { local action="$1" # Action local serviceExitCode=$2 # Service exit code echo "* Finish with $action. Exit code: $serviceExitCode" } action="$1" serviceName="Example Service" serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish
Przykład użycia
$ ./example-service # Actions: [start|stop|restart|status|run|debug|tail(-[log|error])] $ ./example-service start # Starting Example Service service... $ ./example-service status # Serive Example Service is runnig with PID 5599 $ ./example-service stop # Stopping Example Service... $ ./example-service status # Service Example Service is not running
źródło
Jak wiele odpowiedzi, ta nie jest „prawdziwą” demonizacją, ale raczej alternatywą dla
nohup
podejścia.echo "script.sh" | at now
Istnieją oczywiście różnice w używaniu
nohup
. Po pierwsze, nie ma oderwania się od rodzica. Również "script.sh" nie dziedziczy środowiska rodzica.W żadnym wypadku nie jest to lepsza alternatywa. To po prostu inny (i nieco leniwy) sposób uruchamiania procesów w tle.
PS Osobiście głosowałem za odpowiedzią Carlo, ponieważ wydaje się najbardziej elegancka i działa zarówno z poziomu terminala, jak i wewnątrz skryptów
źródło
Oto minimalna zmiana w pierwotnej propozycji stworzenia prawidłowego demona w powłoce Bourne'a (lub Bash):
#!/bin/sh if [ "$1" != "__forked__" ]; then setsid "$0" __forked__ "$@" & exit else shift fi trap 'siguser1=true' SIGUSR1 trap 'echo "Clean up and exit"; kill $sleep_pid; exit' SIGTERM exec > outfile exec 2> errfile exec 0< /dev/null while true; do (sleep 30000000 &>/dev/null) & sleep_pid=$! wait kill $sleep_pid &>/dev/null if [ -n "$siguser1" ]; then siguser1='' echo "Wait was interrupted by SIGUSR1, do things here." fi done
Wyjaśnienie:
Chyba nie ma nic prostszego.
źródło
spróbuj wykonać za pomocą &, jeśli zapiszesz ten plik jako program.sh
możesz użyć
źródło