Как я могу зарегистрировать стандартный вывод процесса, запущенного start-stop-daemon?

120

Я использую сценарий инициализации для запуска простого процесса, который начинается с:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
    --make-pidfile --pidfile $PIDFILE --background       \
    --exec $DAEMON $DAEMON_ARGS

Процесс с именем $ DAEMON обычно выводит информацию журнала на свой стандартный вывод. Насколько я могу судить, эти данные нигде не хранятся.

Я хотел бы записать или добавить stdout $ DAEMON где-нибудь в файл.

Единственное известное мне решение - это указать start-stop-daemon вызвать shellscript вместо $ DAEMON напрямую; затем сценарий вызывает $ DAEMON и записывает в файл журнала. Но для этого требуется дополнительный скрипт, который, как и изменение самого демона, кажется неправильным способом решения такой типичной задачи.

joeytwiddle
источник

Ответы:

127

Чтобы расширить ответ ypocat, поскольку он не позволяет мне комментировать:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
 --make-pidfile --pidfile $PIDFILE --background       \
 --startas /bin/bash -- -c "exec $DAEMON $DAEMON_ARGS > /var/log/some.log 2>&1"

Использование execдля запуска демона позволяет stop правильно остановить дочерний процесс, а не только родительский процесс bash.

Использование --startasвместо --execгарантирует, что процесс будет правильно обнаружен его pid и не будет ошибочно запускать несколько экземпляров демона, если start вызывается несколько раз. В противном случае start-stop-daemon будет искать процесс / bin / bash и игнорировать фактический дочерний процесс, на котором запущен демон.

stormbeta
источник
2
Это гораздо лучшее решение, чем решение @ypocat, главным образом потому, что снова закрытие демона путем замены --startна --stopдействительно работает.
aef
Я пробовал запустить эту команду из rc.local вместо init.d ... Кажется, не получаю тех же результатов. Однако при запуске его из оболочки через SSH он работает как шарм!
nemo
1
Как бы start-stop-daemon --test (...)выглядело сопровождение ?
Абдул
2
@MattClimbs Он перезаписывает файл после каждого запуска. используйте >>вместо >добавления.
Мяу
2
Прежде чем вы взбеситесь (как я) из-за того, что ваш журнал был пуст, имейте в виду, что это буферизуется! Вы можете использовать «exec stdbuf -oL -eL $ DAEMON $ DAEMONARGS> $ LOGFILE 2> & 1», чтобы заставить вывод сбрасывать каждую строку (из blog.lanyonm.org/articles/2015/01/11/… )
piers7
47

Вам необходимо сделать:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
    --make-pidfile --pidfile $PIDFILE --background       \
    --exec /bin/bash -- -c "$DAEMON $DAEMON_ARGS > /var/log/some.log 2>&1"

Также, если вы используете --chuidили --user, убедитесь, что пользователь может писать /var/logв существующий /var/log/some.log. Лучше всего, чтобы у этого пользователя был /var/log/subdir/хотя.

youurayy
источник
1
Прекрасно, спасибо ypocat. Сегодня, помимо сохранения журнала, мне нужно было запустить небинарный сценарий, который --exec не позволяет, но ваш трюк работает!
joeytwiddle
8
Обратная сторона ... остановка службы убивает bash, но не запускает дочерний процесс bash! (В моем случае DAEMON = кофе).
joeytwiddle
1
Я работал над этим, убивая все дочерние процессы процесса bash в верхней части do_stop. bashPID=$(cat $PIDFILE); [ -n "$bashPID" ] && pkill -P "$bashPID"
joeytwiddle
5
Полезно знать, и pkillрешение тоже. Интересно, что бы ... -c "exec $DAEMON..."(добавление "exec") сделать. У меня нет этого сейчас на тарелке, поэтому не могу попробовать.
youurayy
12
@ypocat Я только что убедился, что он работает с -c "exec $ DAEMON ...". Это означает, что никаких взломов pkill не требуется.
overthink
40

Кажется, теперь вы сможете использовать этот --no-closeпараметр при запуске start-stop-daemonзахвата вывода демона. Эта новая функция доступна в dpkgпакете, начиная с версии 1.16.5 в Debian:

Добавьте новую опцию --no-close, чтобы отключить закрытие файловых файлов на --background.

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

Stéphane
источник
8
Жаль, что он недоступен в Ubuntu 12.04 :(
Леон Рэдли
Кажется, я не могу понять это --no-close to work ... вывод все еще идет в оболочку, из которой я выполняю сценарий
init.d
+1 Отлично работает на Debian squeeze с демонизированной службой node.js.
Speaker 05
2
@stantonk Вы также перенаправили stdout / stderr в файл? Полная командная строка выглядит следующим образом. И убедитесь, что файл журнала может быть записан пользователем $ USER: start-stop-daemon --start --chuid $ USER --pidfile $ PIDFILE --background --no-close --make-pidfile --exec $ DAEMON - $ DAEMONARGS >> /var/log/xxxxx.log 2> & 1
nharrer 07
1
Это недоступно в openrc start-stop-daemon. Однако версия openrc имеет параметры -1и -2для перенаправления stdout и stderr соответственно.
маленький чувак
14

С openrc (который используется по умолчанию, например, в gentoo или alpine linux) start-stop-daemonесть параметры -1и -2:

-1, --stdout Перенаправить стандартный вывод в файл

-2, --stderr Перенаправить stderr в файл

Так что вы можете просто написать:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
    --make-pidfile --pidfile $PIDFILE --background       \
    --exec $DAEMON $DAEMON_ARGS -1 $LOGFILE -2 $LOGFILE
маленький чувак
источник
9

Захватить вывод демона и сохранить его в файл несложно:

start-stop-daemon --start --background \
  --pidfile $PIDFILE --make-pidfile \
  --chuid $DAEMON_USER \
  --startas $DAEMON --no-close \
  -- $DAEMON_ARGS >> $LOGFILE 2>&1

Однако это решение может быть неоптимальным для logrotate.

Возможно, лучше записать вывод в системный журнал. В Debian это соответствует поведению сервисов systemd. Следующая прямая попытка переписать приведенный выше пример неверна, потому что она оставляет после себя два родительских («зомби») процесса (регистратор и демон) после остановки демона, поскольку start-stop-daemonзавершает работу только его дочернего элемента, но не всех потомков:

## Do not use this!
start-stop-daemon --start --background \
  --pidfile $PIDFILE --make-pidfile \
  --chuid $DAEMON_USER \
  --startas /bin/sh \
  -- -c """exec $DAEMON $DAEMON_ARGS | /usr/bin/logger --tag $NAME"""

Чтобы он работал, нам нужна оболочка, которая завершает свои дочерние элементы при получении SIGTERMот start-stop-daemon. Вот такие:

дуэнде :
start-stop-daemon --start --background \
  --pidfile $PIDFILE \
  --startas /usr/sbin/duende \
  -- --pid $PIDFILE --chroot=/ --uid 65534 --ident $NAME \
  /bin/su --login $DAEMON_USER --shell /bin/sh --command """exec ${DAEMON} $DAEMON_ARGS"""

Примечание: uid=65534является пользователем nobody.

Плюсы : работает и относительно просто.
Минусы : 4 процесса (супервизор duende, его вилка с удаленными привилегиями (регистратор) suи сам демон); обязательный --chroot; Если демон сразу завершает свою работу (например, неверная команда), status_of_proc -p $PIDFILE "$DAEMON" "$NAME"сообщите, что он запущен успешно.

демон :
start-stop-daemon --start --pidfile $PIDFILE \
  --startas /usr/bin/daemon \
  -- --noconfig --name $NAME --stderr=syslog.info --stdout=syslog.info \
  -- /bin/su --login $DAEMON_USER --shell /bin/sh --command """exec $DAEMON $DAEMON_ARGS"""

Преимущества : 3 процессы (супервизор daemon, suи демон сам по себе).
Минусы : сложно управлять $PIDFILEиз-за запутанных параметров командной строки демона ; Если демон сразу завершает свою работу (например, неверная команда), status_of_proc -p $PIDFILE "$DAEMON" "$NAME"сообщите, что он запущен успешно.

pipexec ( победитель ):

start-stop-daemon --start --background \
  --pidfile $PIDFILE --make-pidfile \
  --chuid $DAEMON_USER \
  --startas /usr/bin/pipexec -- -k \
   -- [ D $DAEMON $DAEMON_ARGS ] [ L /usr/bin/logger --tag $NAME ] '{D:2>D:1}' '{D:1>L:0}'

Преимущества : 3 процессов (руководитель pipexec, loggerи демон сам по себе); Если демон немедленно завершает работу (например, неверная команда), status_of_proc -p $PIDFILE "$DAEMON" "$NAME"правильно сообщите об ошибке.
Минусы : нет.

Это победитель - самое простое и удобное решение, которое, кажется, работает хорошо.

Onlyjob
источник
7

Обычно start-stop-daemonзакрывает стандартные файловые дескрипторы при работе в фоновом режиме. На странице руководства start-stop-daemon:

-C, --no-close
Не закрывать дескриптор файла при переводе демона в фоновый режим. Используется в целях отладки для просмотра выходных данных процесса или для перенаправления файловых дескрипторов для регистрации выходных данных процесса. Актуально только при использовании --background.

Это сработало для меня:

    start-stop-daemon -b -C -o -c \ 
         $DAEMON_USER -S -x $DAEMON > $DAEMON_LOG 2>&1
Раашид Мухаммед
источник
4

Цитата из старого списка рассылки:

https://lists.ubuntu.com/archives/ubuntu-uk/2005-June/000037.html

Простой - и если вы хотите использовать start-stop-daemon, возможно, единственный - способ обойти это - создать небольшой скрипт, содержащий:

#!/bin/sh
exec /home/boinc/boinc/boinc > /home/boinc/log/boinc.log

а затем используйте этот сценарий в качестве аргумента для start-stop-daemon.

Однако, возможно, реальный вопрос заключается в том, действительно ли необходимо использовать start-stop-daemon в первую очередь?

joeytwiddle
источник
3

Я не уверен, что "$ DAEMON $ DAEMON_ARGS> /var/log/some.log 2> & 1" когда-либо закроет файловый дескриптор для файла журнала ... что означает, что если ваш демон будет работать вечно, я не уверен этот logrotate или другие механизмы для очистки дискового пространства будут работать. Поскольку это> вместо >>, предлагаемая команда также будет обрезать существующие журналы при перезапуске. Если вы хотите узнать, почему демон разбился и автоматически перезапустился, это может быть не очень полезно.

Другой вариант - «$ DAEMON | logger». logger - это команда, которая будет записывать в системный журнал (/ var / log / messages). Если вам тоже нужен stderr, я думаю, вы можете использовать "$ DAEMON 1> & 2 | logger"

nairbv
источник
Вы правы, использование >>обычно более подходит для демонов, хотя это подразумевает, что теперь вы должны создать правило logrotate!
joeytwiddle
Что касается дискового пространства, методы, которые усекают файл, немедленно возвращают пространство (по крайней мере, в файловых системах ext). Но будьте осторожны с методами, которые просто удаляют файл, в который все еще выполняется запись: пространство не будет освобождено, пока дескриптор не будет освобожден, и вы больше не сможете найти узел файла, чтобы вручную его усечь!
joeytwiddle
@joeytwiddle: часть моей точки зрения заключается в том, что бывают ситуации, когда logrotate не сможет повернуть журналы, если дескриптор файла никогда не закрывается.
nairbv 04
--no-close ... | loggerу меня не работает (Debian 7.3, start-stop-daemon 1.16.12). Скрипт start-stop-daemon не возвращается, хотя / var / log / messages заполнен :-). Пробовал с и без 1>&2.
hgoebl
hgoebl, вам нужно заключить в кавычки выражение «cmd | logger», чтобы интерпретатор знал, что это «cmd», которое вы передаете в logger, а не выражение start-stop-daemon.
Wexxor
2

Предполагая, что это bash (хотя некоторые другие оболочки также могут это допускать), строка:

exec >>/tmp/myDaemon.log

отправит весь будущий стандартный вывод в этот файл. Это потому, что execбез имени программы просто происходит некоторая магия перенаправления. На bashстранице руководства :

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

Разумеется, управление указанным файлом - это еще одна проблема.

paxdiablo
источник
можете уточнить, где предполагается разместить эту линию? Сразу после start-stop-daemonстроки, упомянутой в первоначальном вопросе?
Абдул,
1

Как насчет:

sudo -u myuser -i start-stop-daemon ...
jakub.piasecki
источник