Как демонизировать произвольный скрипт в unix?

Я бы хотел daemonizer, которые могут превратить произвольное, универсального сценария или команды в демон.

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

  1. у меня есть скрипт, который должен работать вечно. Если он когда-либо умрет (или перезагрузится), перезапустите его. Не позволяйте двум копиям работать одновременно (определите, запущена ли копия, и не запускайте ее в этом случае).

  2. у меня есть простой скрипт или команду команда строки, которую я хотел бы продолжать выполнять повторно навсегда (с короткой паузой между запусками). Опять же, не позволяйте двум копиям сценария работать одновременно.

конечно, тривиально написать цикл "while (true)" вокруг скрипта в случае 2, а затем применить решение для случая 1, но более общее решение просто решит случай 2 напрямую, так как это относится и к скрипту в случае 1 (Вы можете просто захотеть более короткую или нет паузы, если сценарий не является намеревался когда-нибудь умереть (конечно, если скрипт действительно тут никогда не умирайте, тогда пауза на самом деле не имеет значения)).

обратите внимание, что решение не должно включать, скажем, добавление кода блокировки файлов или записи PID в существующие скрипты.

более конкретно, я хотел бы программу "демонизировать", которую я могу запустить как

% daemonize myscript arg1 arg2

или, например,

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

который будет держать растущий список дат, добавленных к времени.формат txt. (Обратите внимание, что если аргумент(ы) для демонизации-это скрипт, который работает вечно, как в случае 1 выше, тогда daemonize все равно будет делать правильные вещи, перезапуская его при необходимости.) Затем я мог бы поместить команду, как указано выше в моем .вход и / или cron это ежечасно или минутно (в зависимости от того, как я беспокоился о том, что он неожиданно умирает).

NB: сценарий daemonize должен будет запомнить командную строку, которая является демонизирующей, так что если та же командная строка снова демонизируется, она не запускает вторую копировать.

кроме того, решение должно идеально работать как на OS X, так и на linux, но решения для одного или другого приветствуются.

EDIT: это нормально, если вам нужно вызвать его с помощью sudo daemonize myscript myargs.

(Если я думаю об этом все неправильно или есть быстрые-и-грязные частичные решения, я хотел бы узнать.)


PS: В случае, если это полезно, вот!--5--> аналогичный вопрос, специфичный для python.

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

12 ответов


вы можете демонизировать любой исполняемый файл в Unix с помощью nohup и оператора&:

nohup yourScript.sh script args&

команда 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=
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  already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=
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 вот как вы это называете:--6-->

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

я прошу прощения за длинный ответ (см. комментарии о том, как мой ответ гвозди спецификации). Я пытаюсь быть всесторонним, так что у вас есть как можно более хорошая нога. :-)

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

он включает в себя использование сайтов. Остальная часть сообщения описывает, как настроить службы с помощью daemontools.

основная настройка

  1. следуйте инструкциям в Как установить daemontools. Некоторые дистрибутивы (например, Debian, Ubuntu) уже имеют пакеты для него, поэтому просто используйте это.
  2. сделать каталог под названием /service. Установщик должен был уже сделать это, но просто проверьте или установите вручную. Если вам не нравится это место, вы можете изменить его в вашем svscanboot скрипт, хотя большинство пользователей daemontools привыкли использовать /service и будет запутаться, если вы не используете его.
  3. если вы используете Ubuntu или другой дистрибутив, который не использует standard init (т. е., не использовать /etc/inittab), вам нужно будет использовать предварительно установленный inittab в качестве основы для организации svscanboot называть init. Это не сложно, но нужно знать, как настроить init что ваша ОС использует. svscanboot - это скрипт, который вызывает svscan, который выполняет основную работу по поиску услуг; он называется от init так init организует перезапуск, если он умрет по какой-либо причине.

настройка на обслуживание

  1. каждая служба нуждается в служба каталогов, в котором хранится информация о службе уборки номеров. Вы также можете сделать место для размещения этих каталогов служб, чтобы они все были в одном месте; обычно я использую /var/lib/svscan, но любое новое место будет в порядке.
  2. я обычно использую скрипт чтобы настроить Каталог услуг, чтобы сохранить много ручной повторяющейся работы. например,

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

    здесь some-service-name это имя, которое вы хотите дать вашей службе,user пользователь для запуска этой службы как и loguser пользователь для запуска регистратора как. (Ведение журнала объясняется немного.)

  3. ваша служба должна работать in на переднем плане!--95-->. Если ваши фоны программы по умолчанию, но есть возможность отключить это, то сделайте это. Если ваша программа фоны без способа отключить его, прочитайте на fghack, хотя это происходит в компромиссе: вы больше не можете управлять программой с помощью svc.
  4. редактировать run скрипт чтобы убедиться, что он делает то, что вы хотите. Возможно, Вам потребуется разместить sleep позвоните сверху, если вы ожидаете, что ваша служба выйдет часто.
  5. когда все настроено правильно, создайте символическую ссылку в /service указывая на ваш каталог услуг. (Не помещайте каталоги служб непосредственно в /service; это затрудняет удаление службы из svscan'ы часы.)

лесозаготовки

  1. способ ведения журнала daemontools заключается в том, чтобы служба записывала сообщения журнала в стандартный вывод (или стандартную ошибку, если вы используете скрипты, созданные с помощью mkservice); svscan принимает забота об отправке сообщений журнала в службу ведения журнала.
  2. служба ведения журнала принимает сообщения журнала из стандартного ввода. Сценарий службы ведения журнала, созданный mkservice создаст автоматически повернутые, помеченные временем файлы журнала в . Текущий файл журнала называется current.
  3. служба регистрации может быть запущена и остановлена независимо от основной службы.
  4. передача файлов журнала через tai64nlocal будет переводить метки времени в удобочитаемом формате. (TAI64N-это 64-разрядная атомная метка времени с количеством наносекунд.)

контролирующие службы

  1. использовать svstat чтобы получить статус сервиса. Обратите внимание, что служба ведения журнала независима и имеет свой собственный статус.
  2. вы контролируете свою службу (запуск, остановка, перезапуск и т. д.) с помощью svc. Например, чтобы перезапустить службу, используйте svc -t /service/some-service-name; -t означает "отправить SIGTERM".
  3. другие сигналы включают в себя -h (SIGHUP), -a (SIGALRM), -1 (SIGUSR1), -2 (SIGUSR2), и -k (SIGKILL).
  4. чтобы отключить службу, используйте -d. Вы также можете предотвратить автоматический запуск службы при загрузке, создав файл с именем down в каталоге сервиса.
  5. чтобы запустить службу, используйте -u. Это не обязательно, если вы не сбили его ранее (или не настроили его не для автоматического запуска).
  6. чтобы попросить супервизора выйти, используйте -x; обычно употребляется с -d прекратить обслуживание, а также. Это обычный способ разрешить удаление службы, но вы должны отключить службу от /service во-первых, иначе svscan перезагрузится руководителя. Кроме того, если вы создали свою службу с помощью службы ведения журнала (mkservice -l), не забудьте также выйти из супервизора ведения журнала (например,svc -dx /var/lib/svscan/some-service-name/log) перед удалением службы справочник.

резюме

плюсы:

  1. daemontools обеспечивает пуленепробиваемый способ создания и управления услугами. Я использую его для своих серверов, и я очень рекомендую его.
  2. своя система регистрации очень робастна, как средство автоматическ-рестарта обслуживания.
  3. поскольку он запускает службы со скриптом оболочки, который вы пишете / настраиваете, вы можете настроить свою службу так, как вам нравится.
  4. мощные инструменты управления службами : вы можете послать большинств любой сигнал к обслуживанию, и можете принести обслуживания вверх и вниз надежно.
  5. вашим службам гарантируется чистая среда выполнения: они будут выполняться с той же средой, ограничениями процесса и т. д., так что init обеспечивает.

плюсы:

  1. каждая служба требует немного настройки. К счастью, это нужно делать только один раз за услугу.
  2. услуги должны быть настроены для запуска на переднем плане. Кроме того, для лучшего результаты, они должны быть настроены для входа в стандартный вывод / Стандартная ошибка, а не системный журнал или другие файлы.
  3. крутая кривая обучения, если вы новичок в daemontools способ делать вещи. Вы должны перезапустить службы с помощью svc, и не может запускать сценарии запуска напрямую (поскольку они тогда не будут находиться под контролем супервизора).
  4. много служебных файлах, и много уборка процессов. Каждая служба нуждается в собственном каталоге служб, и каждая служба использует один процесс супервизора для автоматического перезапуска службы, если она умирает. (Если у вас есть много услуг, вы увидите много of supervise процессы в таблице процессов.)

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


вы должны взглянуть на демонизации. Он позволяет обнаружить вторую копию (но использует механизм блокировки файлов). Также он работает на различных дистрибутивах UNIX и Linux.

Если вам нужно автоматически запустить приложение как демон, вам нужно создать соответствующий init-скрипт.

вы можете использовать следующий шаблон:

#!/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 "" 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)
     stop
     start
    ;;

  *)
    echo $"Usage:  {start|stop|status|restart}"
    exit 1
esac

exit 0

Я думаю, вы можете попробовать start-stop-daemon(8). Проверьте скрипты в /etc/init.d в любом дистрибутиве Linux для примеров. Он может найти запущенные процессы с помощью командной строки или файла PID, поэтому он соответствует всем вашим требованиям, кроме сторожевого пса для вашего скрипта. Но вы всегда можете запустить другой сценарий сторожевого пса демона, который просто перезапустит ваш скрипт, если это необходимо.


в качестве альтернативы уже упомянутым daemonize и daemontools, есть демон команда пакета libslack.

daemon вполне настраивается и заботится обо всех утомительных вещах демона, таких как автоматический перезапуск, ведение журнала или обработка pidfile.


Если вы используете OS X конкретно, я предлагаю вам взглянуть на то, как работает launchd. Он автоматически проверяет, работает ли ваш скрипт, и при необходимости перезапускает его. Он также включает в себя всевозможные функции планирования и т. д. Она должна удовлетворять оба требования 1 и 2.

Что касается обеспечения запуска только одной копии вашего скрипта, вам нужно использовать PID-файл. Обычно я пишу файл в /var/run/.pid, содержащий PID текущего запущенного экземпляра. если файл существует когда программа запускается, она проверяет, действительно ли PID в файле запущен (возможно, программа разбилась или иным образом забыла удалить файл PID). Если это так, прервите. Если нет, запустите и перезапишите PID-файл.


демоны (http://cr.yp.to/daemontools.html)-это набор довольно жестких утилит, используемых для этого, написанный dj bernstein. Я использовал это с некоторым успехом. Раздражает то, что ни один из сценариев не возвращает видимых результатов при их запуске - просто невидимые коды возврата. Но когда он работает, он пуленепробиваемый.


сначала сделать createDaemon() от http://code.activestate.com/recipes/278731/

затем основной код:

import subprocess
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:  --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 " 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) о проблемах во время выполнения, он также может делать то, что большинство других предложений здесь выступали. Он может автоматически (повторно)запускать и останавливать программы, отправлять электронные письма, инициировать другие сценарии и поддерживать журнал вывода, который вы можете забрать. Кроме того, я обнаружил, что его легко установить и поддерживать, так как есть solid документация.


вы могли бы попробовать Бессмертный это кросс-платформенный (агностический) супервизор * nix.

для быстрой попытки на macOS:

brew install immortal

в случае, если вы используете FreeBSD из портов или с помощью pkg:

pkg install immortal

на 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

Я сделал ряд улучшений на другого ответа.

  1. stdout из этого скрипта состоит исключительно из stdout, исходящего от его дочернего элемента, если он не выходит из-за обнаружения того, что команда уже выполняется
  2. очищает после его pidfile при завершении
  3. дополнительный настраиваемый период ожидания (принимает любой положительный числовой аргумент, отправляет в sleep)
  4. приглашение на использование -h
  5. произвольное выполнение команды, а не выполнение одной команды. Последние arg или оставшиеся args (если более одного последнего arg) отправляются в eval, поэтому вы можете создать любой сценарий оболочки в виде строки для отправки в этот скрипт в качестве последнего arg (или конечных args) для его демонизации
  6. сравнение количества аргументов сделано с -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 [ "" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=
shift

TIMEOUT=1
if [[  =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=
        [ $# -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

остерегайтесь, что если вы запустите этот скрипт из разных каталогов, он может использовать разные pidfiles и не обнаруживать существующие запущенные экземпляры. Поскольку он предназначен для запуска и перезапуска эфемерных команд, предоставляемых с помощью аргумента, невозможно узнать, было ли что-то уже запущено, потому что кто может сказать, является ли это команду или нет? Чтобы улучшить это принудительное выполнение только одного экземпляра чего-либо, решение требуется конкретизация ситуации.

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

еще один момент, чтобы принять к сведению, что убийство этого сценария (если он был вызван из еще одного сценария, который убит, или с сигналом) может не преуспеть в убийстве ребенка, особенно если ребенок еще другой сценарий. Я не уверен, почему это так, но, похоже, это что-то связанное с образом eval работает, что для меня загадочно. Поэтому может быть разумно заменить эту строку чем-то, что принимает только одну команду, как в другом ответе.