Лучший способ сделать демон сценария оболочки?


Мне интересно, есть ли лучший способ сделать демона, который ждет чего-то, используя только sh, чем:

#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
  echo "doing stuff"
}

while true; do
  sleep 1000
done

в частности, мне интересно, есть ли какой-либо способ избавиться от петли и все еще есть вещь, которая слушает сигналы.

11 64

11 ответов:

используйте средство демона вашей системы, например start-stop-daemon.

в противном случае, да, где-то должен быть цикл.

просто фон вашего скрипта (./myscript &) не демонизировать его. См.http://www.faqs.org/faqs/unix-faq/programmer/faq/, раздел 1.7, который описывает, что необходимо, чтобы стать демоном. Вы должны отключить его от терминала, чтобы SIGHUP не убивать его. Вы можете использовать ярлык, чтобы сценарий выглядел как демон;

nohup ./myscript 0<&- &>/dev/null &

будет делать эту работу. Или, чтобы захватить оба stderr и stdout в файл:

nohup ./myscript 0<&- &> my.admin.log.file &

однако, там может будьте далее важными аспектами, которые вам нужно рассмотреть. Например:

  • у вас все еще будет файловый дескриптор, открытый для скрипта, что означает, что каталог, в котором он установлен, будет немонтируемым. Чтобы быть истинным демоном, вы должны chdir("/") (или cd / внутри вашего скрипта), и вилка так, что родительский выход, и, таким образом, исходный дескриптор закрыт.
  • может быть umask 0. Возможно, вы не захотите зависеть от umask вызывающего объекта демон.

пример сценария, который учитывает все эти аспекты, см. В разделе Майк S ' ответ.

# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981 
(./program.sh &) & 

в некоторых из ответов top-upoted здесь отсутствуют некоторые важные части того, что делает демона демоном, а не просто фоновым процессом или фоновым процессом, отделенным от оболочки.

это http://www.faqs.org/faqs/unix-faq/programmer/faq/ описывает, что необходимо, чтобы быть демоном. А это запустите скрипт bash как демон реализует setsid, хотя он пропускает chdir для root.

изначально вопрос был на самом деле более конкретно, чем "как создать процесс демона с помощью bash?", но поскольку тема и ответы обсуждают демонизирующие сценарии оболочки в целом, я думаю, что важно указать на это (для таких злоумышленников, как я, изучающих мелкие детали создания демона).

вот мое представление сценария оболочки, который будет вести себя в соответствии с FAQ. Набор отладки true чтобы увидеть довольно выход (но он также выходит сразу, а не цикл бесконечно):

#!/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=""; tty=""
    [[ "$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 )
cd /

#### CHILD HERE --------------------------------------------------------------------->
if [ "" = "child" ] ; then   # 2. We are the child. We need to fork again.
    shift; tty=""; 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 [ "" != "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=""; 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 man." >/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.

вывод выглядит так, когда DEBUG установлено значение true. Обратите внимание, как меняются номера идентификаторов групп сеансов и процессов (SESS, PGID):

<shell_prompt>$ bash blahd

*** PARENT, PID 5180
  PID  SESS  PGID
 5180  1708  5180
/dev/pts/6
PARENT OUT
<shell_prompt>$ 
*** CHILD, NEW SESSION, NEW PGID, PID 5188
  PID  SESS  PGID
 5188  5188  5188
not a tty
CHILD OUT

*** DAEMON, PID 5198
  PID  SESS  PGID
 5198  5188  5188
not a tty
NOT A REAL DAEMON. NOT RUNNING WHILE LOOP.
DAEMON OUT

это действительно зависит от того, что будет делать сам двоичный файл.

например, я хочу создать какой-то слушатель.

запуск демона-это простая задача:

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 "" in
      start)
            echo -n "Starting Listener Deamon .... "
            startproc -f -p $PIDFILE $LISTENER_BIN
            echo "running"
            ;;
          *)
            echo "Usage:  start"
            exit 1
            ;;
esac

вот как мы запускаем демон (общий способ для всех /etc / init.д/ сотрудников)

теперь что касается самого слушателя, Это должен быть какой-то цикл / предупреждение, иначе это вызовет скрипт делать то, что ты хочешь. Например, если вы хотите, чтобы ваш скрипт спал 10 мин и проснуться и спросить вас, как вы делаете вы будете делать это с

while true ; do sleep 600 ; echo "How are u ? " ; done

вот простой слушатель, что вы можете сделать, что будет слушать для вашего команды с удаленного компьютера и выполнять их на локальном :

слушатель :

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

Итак, чтобы запустить его :/tmp/deamon_test / listener start

и отправить команды из оболочки (или обернуть его в скрипт):

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)

надеюсь, что это поможет.

посмотрите на инструмент демона из пакета libslack:

http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script/

в Mac OS X Используйте сценарий launchd для демона оболочки.

если бы у меня был script.sh и я хотел выполнить его из bash и оставить его работать, даже когда я хочу закрыть свою сессию bash, тогда я бы объединил nohup и & в конце.

пример: nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &

inputFile.txt может быть любой файл. Если ваш файл не имеет ввода, то мы обычно используем /dev/null. Так что команда будет:

nohup ./script.sh < /dev/null > ./logFile 2>&1 &

после этого закройте сеанс bash, откройте другой терминал и выполните:ps -aux | egrep "script.sh" и вы увидите, что ваш скрипт все еще работает в фоновом режиме. Конечно, если вы хотите остановить его, то выполните ту же команду (ps) и kill -9 <PID-OF-YOUR-SCRIPT>

посмотреть Bash Service Manager проект:https://github.com/reduardo7/bash-service-manager

пример реализации

#!/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="" # 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="" # Action

  echo "* Starting with $action"
}

after-finish() {
  local action="" # Action
  local serviceExitCode= # Service exit code

  echo "* Finish with $action. Exit code: $serviceExitCode"
}

action=""
serviceName="Example Service"

serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish

пример использования

$ ./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

$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &

как и многие ответы, это не "настоящая" демонизация, а скорее альтернатива nohup подход.

echo "script.sh" | at now

есть очевидные отличия от использования nohup. Для одного нет отрыва от родителя в первую очередь. Также "script.sh-не наследует родительское окружение.

ни в коем случае это лучшая альтернатива. Это просто другой (и несколько ленивый) способ запуска процессов в фоновом режиме.

P. S. Я лично поддержанный ответ Карло, как кажется, самый элегантный и работает как с терминала, так и внутри скриптов

попробуйте выполнить с помощью & если вы сохраните этот файл как program.sh

можно использовать

$. program.sh &