Вот мой замечательный сервис, написанный на скрипте оболочки:
$ cat ~/junk/demoapp
#! /bin/bash -eu
while true
do
echo "in shell"
( echo "in subshell" )
/usr/bin/echo "in subprocess"
sleep 1
done
Он выдает повторяющийся вывод:
$ ~/junk/demoapp
in shell
in subshell
in subprocess
in shell
in subshell
in subprocess
in shell
in subshell
in subprocess
in shell
in subshell
in subprocess
^C
Вот конфигурация пользовательской службы для этого:
$ cat ~/.config/systemd/user/demoapp.service
[Unit]
Description=Demo App
[Service]
Type=exec
ExecStart=/home/tomanderson/junk/demoapp
Но когда я запускаю эту службу с помощью systemd 239, в выводе журнала отсутствуют строки, созданные подоболочкой и подпроцессом:
$ systemctl --user daemon-reload
$ systemctl --user start demoapp
$ journalctl --user --unit demoapp
Sep 12 18:53:27 myhost systemd[539847]: Started Demo App.
Sep 12 18:53:27 myhost demoapp[559387]: in shell
Sep 12 18:53:28 myhost demoapp[559387]: in shell
Sep 12 18:53:29 myhost demoapp[559387]: in shell
Sep 12 18:53:30 myhost demoapp[559387]: in shell
Sep 12 18:53:31 myhost demoapp[559387]: in shell
Sep 12 18:53:32 myhost demoapp[559387]: in shell
Sep 12 18:53:33 myhost demoapp[559387]: in shell
Sep 12 18:53:34 myhost demoapp[559387]: in shell
Sep 12 18:53:35 myhost demoapp[559387]: in shell
Есть идеи, почему? Из чтения, похоже, что systemd обычно захватывает вывод из подпроцессов здесь. Это что-то конкретное, что делает оболочка, взаимодействующая с этим?
Погуглив, я вижу, что у людей возникают подобные проблемы с Python, где виновата буферизация, но я не понимаю, какое отношение это может иметь к данному случаю.
EDIT: Я вижу точно такое же поведение после того, как убрал скрипт оболочки из уравнения, используя две простые программы на C. Я не вижу такого поведения с простым родительским процессом, заменяющим systemd и собирающим вывод через канал. Это явно указывает на то, что systemd делает что-то странное. Смотрите:https://github.com/tomwhoiscontrary/child-stdout-demo
EDIT 2: Наблюдательный коллега, имеющий root, сообщает, что (a) вывод подпроцессаявляетсяв журнале это просто не связано с услугой, и (б) он видит это поведение только спользовательуслуга; если он устанавливаетсистемаслужба с тем же кодом, вывод подпроцесса связан с ней! Это, конечно, системный баг?
решение1
EDIT 2: Наблюдательный коллега, имеющий root, сообщает, что (a) вывод подпроцесса находится в журнале, просто он не связан со службой, и (b) он видит такое поведение только с пользовательской службой; если он настраивает системную службу с тем же кодом, вывод подпроцесса связан с ней! Это наверняка ошибка systemd?
Это известная, давно существующая проблема; проблема в том, что ядро не предоставляет достаточных средств для связывания сокет-клиента с cgroup (в отличие, например, от возможности извлечения PID клиента). Поэтому всякий раз, когда journald получает сообщение, он знает только PID отправителя, но долженасинхроннонайдите имя его модуля в /proc/<pid>/cgroup
. Если процесс очень кратковременный (например, подоболочка), вполне может быть, что он завершится еще до того, как journald будет разбужен, и к тому времени, когда его сообщение будет обработано, информация, необходимая для связывания его вывода со службой, уже не будет доступна.
Я немного не в курсе деталей, но, насколько я помню, в последних версиях systemd есть частичное решение, которое работает только в том случае, если «канал» stdout к journald (который на самом деле является парой сокетов) был настроен привилегированным процессом, тогда как ваши «пользовательские» службы настроены другим экземпляром systemd, имеющим только те же привилегии, что и вы.
решение2
Хотя я не исследовал основные технические детали, то же самое решение, которое работает для Python (отключить буферизацию), также работает и в этой ситуации. Если я использую этот файл модуля...
[Unit]
Description=Demo App
[Service]
Type=exec
ExecStart=/usr/bin/unbuffer %h/bin/demoapp
...затем ожидаемый результат регистрируется в журнале.
Команда unbuffer
является частью пакета expect
.
Это работает путем принудительного выполнения команды как интерактивного процесса, подключенного к устройству pty, что отключает обычную буферизацию.
Если у вас нет unbuffer
под рукой нужной команды, вы можете использовать script
следующую команду:
ExecStart=/usr/bin/script -c %h/bin/demoapp /dev/null