sudo игнорирует SIGTERM, отправленный из того же скрипта

Я пытаюсь понять, почему это не работает:

#!/bin/bash

sudo sleep 60 &
sudo_pid=$!
sudo kill $sudo_pid

Я ожидаю, что после kill команда sudo и ее дочерний процесс sleep будут завершены, но это не так, как показано в этом скрипте:

#!/bin/bash

sudo sleep 60 &
sudo_pid=$!
sudo kill $sudo_pid

if ps -p $sudo_pid > /dev/null; then
    sudo kill $sudo_pid
else
    echo "No sudo process running"
    exit 1
fi

if ps -p $sudo_pid > /dev/null; then
    echo "sudo (pid $sudo_pid) is still running"
    ps -F $sudo_pid
else
    echo "sudo successfully killed"
fi

Что дает этот вывод, когда я запускаю его (с кэшированными кредитами sudo):

jon@ubuntu:~$ ./so.sh 
sudo (pid 46199) is still running
UID         PID   PPID  C    SZ   RSS PSR STIME TTY      STAT   TIME CMD
root      46199  46198  0 14764  3984   3 13:37 pts/0    S+     0:00 sudo sleep 60

После завершения скрипта (и sudo sleep 60 все еще работает) его можно убить идентичной командой:

jon@ubuntu:~$ ps -F 46199
UID         PID   PPID  C    SZ   RSS PSR STIME TTY      STAT   TIME CMD
root      46199      1  0 14764  3984   3 13:37 pts/0    S      0:00 sudo sleep 60

jon@ubuntu:~$ sudo kill 46199

jon@ubuntu:~$ ps -F 46199
UID         PID   PPID  C    SZ   RSS PSR STIME TTY      STAT   TIME CMD

jon@ubuntu:~$

Я заметил, что после завершения сценария so.sh идентификатор родительского процесса для sudo sleep 60 изменился со сценария на процесс init, что я считаю важным. Также возможно успешно завершить процесс sudo sleep 60 из другой оболочки, пока скрипт so.sh все еще работает.

Я также заметил, что использование sudo kill -ABRT в сценарии (в отличие от kill по умолчанию SIGTERM) успешно убивает процесс sudo, поэтому я предполагаю, что это как-то связано с тем, как sudo обрабатывает SIGTERM. Однако, основываясь на справочной странице, я не думаю, что это должно делать что-то особенное:

Signal handling
  When the command is run as a child of the sudo process, sudo will relay
  signals it receives to the command.  The SIGINT and SIGQUIT signals are
  only relayed when the command is being run in a new pty or when the sig‐
  nal was sent by a user process, not the kernel.  This prevents the com‐
  mand from receiving SIGINT twice each time the user enters control-C.

Единственная специальная обработка SIGTERM, упомянутая на странице руководства, касается signals that were sent by the command it is running; здесь не так. Кроме того, я изменил ps -F в приведенном выше сценарии на ps -o blocked,caught,ignored,pending, и он вывел

sudo (pid 46429) is still running
         BLOCKED           CAUGHT          IGNORED          PENDING
0000000000000000 00000001800b7a07 0000000000000000 0000000000000000

Что, по-видимому, указывает на то, что SIGTERM не блокируется и не игнорируется, так почему же процесс sudo sleep 60 не завершается?

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

Если это важно:

jon@ubuntu:~$ uname -a
Linux ubuntu 4.13.0-16-generic #19-Ubuntu SMP Wed Oct 11 18:35:14 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
jon@ubuntu:~$ sudo --version
Sudo version 1.8.20p2
Sudoers policy plugin version 1.8.20p2
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.20p2

person Jon    schedule 29.12.2017    source источник


Ответы (2)


Бит маски сигнала для любого данного сигнала:

(1ULL << ((signal_number) - 1))

(во всяком случае, для стандартных сигналов в диапазоне 1-32; дополнительные сигналы находятся в наборах сигналов «реального времени» и обрабатываются несколько иначе, хотя применяется та же концепция). Итак, интересная часть маски CAUGHT:

...7a07

то есть (+ за пойманный, - за не пойманный, в расширенной части):

xxx7: signals 1, 2, 3, but not 4: +SIGHUP  +SIGINT  +SIGQUIT -SIGILL
xx0x: not 5-8:                    -SIGTRAP -SIGABRT -SIGBUS  -SIGFPE
xaxx: not 9, 10, not 11, 12:      -SIGKILL +SIGUSR1 -SIGSEGV +SIGUSR2
7xxx: 13, 14, 15, not 16:         +SIGPIPE +SIGALRM +SIGTERM -SIGSTKFLT

(вы можете продолжить декодирование остальных, если хотите; см. /usr/include/asm-generic/signal.h для номеров сигналов, специфичных для Linux; обратите внимание, что числовые определения различаются в OSX и BSD, но метод одинаков: пойманный или заблокированный или любые другие сигналы представлены как 1-битные в маске).

Итак, это означает, что sudo ловит SIGTERM, но не ловит SIGABRT. Тот факт, что SIGTERM не передается, должен иметь какое-то отношение к самому коду sudo. Исходный код (apt-get source sudo) имеет довольно сложный код для обработки сигналов, включая некоторые интересные приемы отладки, которые вы можете включить в файле конфигурации sudo, чтобы помочь вам отслеживать, что происходит.

person torek    schedule 29.12.2017
comment
Спасибо за дополнительную информацию о сигналах! Имеет смысл, что sudo перехватывает SIGTERM, поскольку он не распространяет этот сигнал, когда он исходит от дочернего процесса sudo. Я думаю, мне придется копаться в источнике (спасибо за этот меткий указатель), если я действительно хочу знать. - person Jon; 30.12.2017

Я нашел следующие комментарии в исходном коде sudo

  /*  
 * Do not forward signals sent by a process in the command's process
 * group, as we don't want the command to indirectly kill itself.
 * For example, this can happen with some versions of reboot that
 * call kill(-1, SIGTERM) to kill all other processes.
 */
person user7219054    schedule 11.02.2019
comment
Привет! Большое спасибо за этот ответ. Это заметно отсутствует в документах. - person Jon; 22.02.2019