Мне нужен демонизатор, который может превратить произвольный общий сценарий или команду в демона .
Я бы хотел разобраться с двумя типичными случаями:
У меня есть сценарий, который должен работать вечно. Если он когда-нибудь умрет (или при перезагрузке), перезапустите его. Не допускайте одновременного запуска двух копий (определите, запущена ли уже копия, и не запускайте ее в этом случае).
У меня есть простой сценарий или команда командной строки, которую я бы хотел постоянно выполнять постоянно (с короткой паузой между запусками). Опять же, не позволяйте запускать две копии скрипта одновременно.
Конечно, тривиально написать цикл «while (true)» вокруг сценария в случае 2, а затем применить решение для случая 1, но более общее решение просто решит случай 2 напрямую, поскольку это применимо к сценарию в случае 1 как хорошо (вы можете просто хотите короче или нет паузы , если сценарий не предназначен когда - либо умереть (конечно , если сценарий действительно не делает не умрет , то пауза делает на самом деле не имеет значения)).
Обратите внимание, что решение не должно включать, скажем, добавление кода блокировки файла или записи PID к существующим скриптам.
В частности, мне нужна программа "daemonize", которую я могу запускать как
% daemonize myscript arg1 arg2
или, например,
% daemonize 'echo `date` >> /tmp/times.txt'
который будет хранить растущий список дат, добавленных к times.txt. (Обратите внимание, что если аргумент (ы) для daemonize - это скрипт, который запускается вечно, как в случае 1 выше, тогда daemonize все равно будет делать правильные вещи, перезагружая его при необходимости.) Затем я мог бы поместить команду, подобную выше, в свой .login и / или cron его ежечасно или поминутно (в зависимости от того, насколько я беспокоился о его неожиданной смерти).
NB: сценарий daemonize должен будет запомнить строку команды, которую он демонизирует, чтобы при повторной демонстрации той же строки команды не запускалась вторая копия.
Кроме того, решение в идеале должно работать как на OS X, так и на Linux, но приветствуются решения для одного или другого.
РЕДАКТИРОВАТЬ: это нормально, если вам нужно вызвать его с помощью sudo daemonize myscript myargs
.
(Если я думаю об этом неправильно или есть быстрые и грязные частичные решения, я тоже хотел бы это услышать.)
PS: Если это полезно, вот аналогичный вопрос, относящийся к python.
И этот ответ на аналогичный вопрос имеет полезную идиому для быстрой и грязной демонизации произвольного скрипта:
Ответы:
Вы можете демонизировать любой исполняемый файл в Unix, используя nohup и оператор &:
Команда nohup позволяет завершить сеанс оболочки, не убивая сценарий, в то время как & помещает сценарий в фоновый режим, чтобы вы получили приглашение оболочки для продолжения сеанса. Единственная небольшая проблема с этим - стандартный выход и стандартная ошибка, которые отправляются в ./nohup.out, поэтому, если вы запустите несколько сценариев в этом поместье, их вывод будет переплетен. Лучшая команда была бы:
nohup yourScript.sh script args >script.out 2>script.error&
Стандартное сообщение будет отправлено в выбранный вами файл, а стандартная ошибка - в другой файл по вашему выбору. Если вы хотите использовать только один файл как для стандартных ошибок, так и для стандартных ошибок, вы можете использовать это:
nohup yourScript.sh script args >script.out 2>&1 &
2> & 1 указывает оболочке перенаправить стандартную ошибку (дескриптор файла 2) в тот же файл, что и стандартный файл (дескриптор файла 1).
Чтобы запустить команду только один раз и перезапустить ее, если она умирает, вы можете использовать этот сценарий:
#!/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
Первый аргумент - это имя используемого файла pid. Второй аргумент - это команда. А все остальные аргументы являются аргументами команды.
Если вы назовете этот сценарий restart.sh, то вы бы назвали его так:
nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &
источник
trap EXIT
)<
вtest
- это сравнение ASCII, а не целочисленное сравнение. Он может работать, но может приводить к ошибкам.Прошу прощения за длинный ответ (см. Комментарии о том, как мой ответ соответствует спецификации). Я стараюсь быть исчерпывающим, чтобы у вас была как можно более сильная позиция. :-)
Если вы можете устанавливать программы (имеете root-доступ) и готовы выполнить одноразовую легкую работу по настройке вашего сценария для выполнения демона (т. Е. Более сложный процесс, чем просто указание аргументов командной строки для запуска в командной строке, но это нужно делать только один раз для каждой службы), у меня есть способ более надежный.
Это предполагает использование daemontools . Остальная часть сообщения описывает, как настроить службы с помощью daemontools.
Начальная настройка
/service
. Установщик уже должен был это сделать, но просто проверьте, или при установке вручную. Если вам не нравится это место, вы можете изменить его в своемsvscanboot
скрипте, хотя большинство пользователей daemontools привыкли к нему/service
и запутаются, если вы его не используете.init
(т.е. не используется/etc/inittab
), вам нужно будет использовать предварительно установленной вinittab
качестве основы для организации ,svscanboot
чтобы его называлиinit
. Это несложно, но вам нужно знать, как настроить то,init
что использует ваша ОС.svscanboot
это скрипт, который вызываетsvscan
, который выполняет основную работу по поиску услуг; он вызывается из,init
поэтомуinit
будет организован его перезапуск, если он умрет по какой-либо причине.Настройка для каждой услуги
/var/lib/svscan
, но любое новое место будет в порядке.Я обычно использую сценарий для настройки каталога служб, чтобы сэкономить много ручной повторяющейся работы. например,
sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"
где
some-service-name
имя, которое вы хотите дать своей службе,user
это пользователь, от имени которого запускается эта служба, иloguser
это пользователь, от имени которого запускается регистратор. (Ведение журнала объясняется совсем немного.)fghack
, хотя это требует компромисса: вы больше не можете управлять программой с помощьюsvc
.run
сценарий, чтобы убедиться, что он выполняет то, что вы хотите. Возможно, вам потребуется разместитьsleep
вызов вверху, если вы ожидаете, что ваша служба будет часто прекращать работу./service
указывающую на ваш служебный каталог. (Не помещайте каталоги служб непосредственно в них/service
; это затрудняет удаление службы изsvscan
часов.)логирование
mkservice
);svscan
заботится об отправке сообщений журнала службе ведения журнала.mkservice
, создаст автоматически вращающиеся файлы журнала с отметками времени вlog/main
каталоге . Текущий файл журнала называетсяcurrent
.tai64nlocal
метки времени переводятся в удобочитаемый формат. (TAI64N - это 64-битная атомная временная метка с наносекундным счетчиком.)Контролирующие услуги
svstat
для получения статуса услуги. Обратите внимание, что служба ведения журнала независима и имеет собственный статус.svc
. Например, чтобы перезапустить службу, используйтеsvc -t /service/some-service-name
;-t
означает «отправитьSIGTERM
».-h
(SIGHUP
),-a
(SIGALRM
),-1
(SIGUSR1
),-2
(SIGUSR2
) и-k
(SIGKILL
).-d
. Вы также можете запретить автоматический запуск службы при загрузке, создав файл с именемdown
в каталоге службы.-u
. В этом нет необходимости, если вы не отключили его ранее (или не настроили автоматический запуск).-x
; обычно также используется-d
для прекращения службы. Это обычный способ разрешить удаление службы, но вы должны/service
сначала отключить службу от нее , иначеsvscan
вы перезапустите супервизор. Кроме того, если вы создали свою службу с помощью службы ведения журнала (mkservice -l
), не забудьте также выйти из супервизора ведения журнала (например,svc -dx /var/lib/svscan/some-service-name/log
) перед удалением каталога службы.Резюме
Плюсы:
init
предоставляемые.Минусы:
svc
и не можете запускать сценарии запуска напрямую (так как в этом случае они не будут находиться под контролем супервизора).supervise
процессов в вашей таблице процессов.)В целом, я считаю, что daemontools - отличная система для ваших нужд. Я приветствую любые вопросы о том, как его настроить и поддерживать.
источник
supervise
Супервизор заботится о перезапуске любой завершающейся службы. Он ждет одну секунду между перезапусками; если вам этого недостаточно, переведите в спящий режим в верхней части сценария запуска службы.supervise
сам поддерживаетсяsvscan
, поэтому в случае смерти супервизора он будет перезапущен. 2b.svscan
поддерживаетсяinit
, который будет автоматически перезапускатьсяsvscan
при необходимости. 2c. Если вашinit
умрет по какой-либо причине, вы все равно облажались. :-Psvstat
иsvc
с которыми они могут работать.Думаю, вы можете попробовать
start-stop-daemon(8)
. Просмотрите сценарии в/etc/init.d
любом дистрибутиве Linux для примеров. Он может находить запущенные процессы по вызванной командной строке или PID-файлу, поэтому он соответствует всем вашим требованиям, кроме функции сторожевого таймера для вашего скрипта. Но вы всегда можете запустить другой скрипт сторожевого пса демона, который при необходимости просто перезапустит ваш скрипт.источник
start-stop-daemon
там тоже нет (по состоянию на 10.9).start-stop-daemon
тем не менее, все еще жив и здоров в Linux; но после прочтения ответа stackoverflow.com/a/525406/45375 я понял , что OSX делает свое дело:launchd
.Вам следует взглянуть на daemonize . Это позволяет обнаружить вторую копию (но использует механизм блокировки файлов). Также он работает в различных дистрибутивах UNIX и Linux.
Если вам нужно автоматически запускать ваше приложение как демон, вам необходимо создать соответствующий скрипт инициализации.
Вы можете использовать следующий шаблон:
#!/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
источник
killproc
остановка: если у вас есть процесс, который, скажем, запущенjava
, тоkillproc
все остальные процессы Java тоже будут убиты.$corelimit >/dev/null 2>&1 ; $*
Так что я сомневаюсь, что он что-то демонизирует ...В качестве альтернативы уже упомянутому
daemonize
иdaemontools
существует демон из пакета libslack.daemon
довольно настраиваемый и заботится обо всех утомительных вещах демона, таких как автоматический перезапуск, ведение журнала или обработка pidfile.источник
Если вы используете конкретно OS X, я предлагаю вам взглянуть на то, как работает launchd. Он автоматически проверит, работает ли ваш скрипт, и при необходимости перезапустит его. Он также включает в себя всевозможные функции планирования и т. Д. Он должен удовлетворять требованиям 1 и 2.
Что касается обеспечения возможности запуска только одной копии вашего скрипта, вам необходимо использовать файл PID. Обычно я записываю файл в /var/run/.pid, который содержит PID текущего запущенного экземпляра. если файл существует при запуске программы, она проверяет, действительно ли работает PID в файле (программа могла аварийно завершить работу или иным образом забыла удалить файл PID). Если это так, отменить. Если нет, запустите и перезапишите файл PID.
источник
Daemontools ( http://cr.yp.to/daemontools.html ) - это набор довольно жестких утилит, используемых для этого, написанный dj bernstein. Я использовал это с некоторым успехом. Раздражает то, что ни один из скриптов не возвращает видимых результатов, когда вы их запускаете - только невидимые коды возврата. Но когда он запущен, он пуленепробиваемый.
источник
Сначала получить
createDaemon()
от http://code.activestate.com/recipes/278731/Затем основной код:
import subprocess import sys import time createDaemon() while True: subprocess.call(" ".join(sys.argv[1:]),shell=True) time.sleep(10)
источник
Это рабочая версия с примером, который вы можете скопировать в пустой каталог и попробовать (после установки зависимостей CPAN, которыми являются Getopt :: Long , File :: Spec , File :: Pid и IPC :: System: : Simple - все довольно стандартно и настоятельно рекомендуется любому хакеру: вы можете установить их все сразу с помощью
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";
Теперь вы можете вызвать приведенный выше пример с помощью:
./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3
и файлpidfile
будет создан, и вы увидите результат:Pid <random number here> no longer running; restarting ./example.pl 1 2 3 ./example.pl got arguments: 1 2 3
источник
Вы также можете попробовать Monit . Мониторинг - это сервис, который отслеживает и сообщает о других сервисах. Хотя он в основном используется как способ уведомления (по электронной почте и sms) о проблемах во время выполнения, он также может делать то, что предлагается в большинстве других предложений здесь. Он может автоматически (повторно) запускать и останавливать программы, отправлять электронные письма, запускать другие сценарии и вести журнал вывода, который вы можете получить. Кроме того, я обнаружил, что его легко установить и поддерживать, поскольку есть надежная документация.
источник
Вы могли бы попробовать бессмертный Это * nix кроссплатформенный (независимый от ОС) супервизор.
Чтобы быстро попробовать macOS:
Если вы используете FreeBSD из портов или с помощью pkg:
Для Linux загрузив предварительно скомпилированные двоичные файлы или из источника: https://immortal.run/source/
Вы можете использовать его так:
immortal -l /var/log/date.log date
Или с помощью файла конфигурации YAML, который дает вам больше возможностей, например:
cmd: date log: file: /var/log/date.log age: 86400 # seconds num: 7 # int size: 1 # MegaBytes timestamp: true # will add timesamp to log
Если вы хотите сохранить стандартный вывод ошибок в отдельном файле, вы можете использовать что-то вроде:
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
источник
Я внес ряд улучшений в другой ответ .
sleep
)-h
eval
, поэтому вы можете создать любой сценарий оболочки в виде строки для отправки этому сценарию в качестве последнего аргумента (или конечных аргументов) для его демонизации-lt
вместо<
Вот сценарий:
#!/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
Применение:
$ 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
Помните, что если вы запустите этот сценарий из разных каталогов, он может использовать разные pid-файлы и не обнаруживать существующие запущенные экземпляры. Поскольку он предназначен для запуска и перезапуска эфемерных команд, предоставленных через аргумент, невозможно узнать, было ли что-то уже запущено, потому что кто должен сказать, является ли это той же командой или нет? Чтобы улучшить это принудительное выполнение только одного экземпляра чего-либо, требуется решение, специфичное для конкретной ситуации.
Кроме того, чтобы он функционировал как надлежащий демон, вы должны использовать (как минимум) nohup, как упоминается в другом ответе. Я не приложил никаких усилий, чтобы обеспечить устойчивость к сигналам, которые может получить процесс.
Еще один момент, на который следует обратить внимание, заключается в том, что уничтожение этого сценария (если он был вызван из еще одного сценария, который был уничтожен, или с сигналом) может не привести к уничтожению ребенка, особенно если ребенок - еще один сценарий. Я не уверен, почему это так, но, похоже, это связано с тем, как
eval
работает, что для меня загадочно. Поэтому может быть разумным заменить эту строку чем-то, что принимает только одну команду, как в другом ответе.источник