解决 ssh 不转发信号的问题

Work around ssh does not forward signal

如何解决 ssh 不转发 SIGTERM 信号的问题?

我希望print-signal.py终止ssh root@localhost进程终止:

ssh root@localhost /root/print-signal.py

不幸的是,只有 ssh 进程本身得到信号,而不是远程命令 (print-signal.py)。远程命令不终止:-(

由于 openssh 不会将 SIGTERM 转发到远程命令,我正在寻找解决方法。

如果 ssh root@localhost ... 终止,如何终止 print-signal.py

这是以下问题的后续问题:Forwarding SIGTERM over ssh

您可以编写一个包装器,如果父进程是 init 进程则终止:

ssh root@localhost terminate-command-if-parent-is-lost print-signal.py

工具 terminate-command-if-parent-is-lost 需要执行此操作:

将 argv 作为子进程启动(在本例中 print-signal.py)。然后它每秒检查其父 pid 的状态(在 Python os.getppid())。

如果 ppid 为 1(init 进程),则 print-signal.py 进程已失去其父进程。这意味着 "ssh root@localhost ..." 已终止(或连接已关闭)。

现在 terminate-command-if-parent-is-lost 终止子进程。

免责声明:下面的答案不是针对 SIGTERM,而是针对 SIGINT。由于疏忽,这不是问题的答案。

您观察到的问题是由于缺少 tty 造成的,它应该控制您尝试 运行 的进程。如果没有可用的 tty,则 ssh 无法将信号发送到进程。当您对 ssh 命令使用选项 -t 时,它将强制分配伪终端,这使得通过 ssh 发送信号成为可能:

ssh -t root@localhost /root/print-signal.py

man ssh -t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.

Giles on unix.stackexchange.

很好地解释了如何以及为什么

在这里你可以看到它是如何工作的:

[terminal 1]% ssh server ./print_signal.py

在另一个终端上,您会看到 print_signal.pysshPID=26992 上 运行ning PID=26991 没有 tty(username@notty)

[terminal 2]% ssh server ps -f                                                                                                                                                                                                            
UID        PID  PPID  C STIME TTY          TIME CMD
username 26991 26989  0 17:06 ?        00:00:00 sshd: username@notty
username 26992 26991  0 17:06 ?        00:00:00 python ./print_signal.py
username 27347 27345  0 17:07 ?        00:00:00 sshd: username@notty
username 27348 27347  0 17:07 ?        00:00:00 ps -f

使用 kill 或 CTRL-C 终止 ssh 进程后,该进程仍然处于活动状态,但现在 运行s在 /sbin/init (PPID=1)

[terminal 2]% ssh server ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
username 26992     1  0 17:06 ?        00:00:00 python ./print_signal.py
username 27453 27451  0 17:08 ?        00:00:00 sshd: username@notty
username 27454 27453  5 17:08 ?        00:00:00 ps -f

使用 -t 标志可以很好地终止另一端的进程:

[terminal 1]% ssh -t server ./print_signal.py

在另一个终端上,您会看到 print_signal.pyssh 下的 PID=39277 上 运行ning,PID=39276 绑定到 tty(username@pts/10)

[terminal 2]% ssh server ps -U username -f
UID        PID  PPID  C STIME TTY          TIME CMD
username 39276 39274  0 17:22 ?        00:00:00 sshd: username@pts/10
username 39277 39276  1 17:22 pts/10   00:00:00 python ./print_signal.py
username 39317 39314  0 17:22 ?        00:00:00 sshd: username@notty
username 39318 39317  5 17:22 ?        00:00:00 ps -U username -f

杀掉ssh进程后

[terminal 1]% ssh -t server ./print_signal.py
My PID: 39277
^CCaught signal SIGINT (2), exiting.
Connection to server closed

该进程现在已在另一台服务器上明确终止

[terminal 2]% ssh server ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
username 39768 39765  0 17:26 ?        00:00:00 sshd: username@notty
username 39769 39768  6 17:26 ?        00:00:00 ps -U username -f

如果 shell 支持一些内置变量( $! ),它可以轻松帮助您解决此类问题。 这是一个基本的 shell 解决方案,将您的命令替换为长时间睡眠。

ssh server '(sleep 100000 & (MID=$!; A=n; while [ "$A" != y ];do echo "i am process $$ want kill "$MID" y/n?"; read A; done; kill -TERM $MID))'

命令中发送的脚本是远程主机上的运行。

我刚刚遇到了这个问题。虽然我还没有弄清楚确切的原因,但当 ssh 终止时发生的事情是您的进程被重新设置为 init。您可以告诉您的进程在父进程死亡时请求信号,而不是使用 prctl.

如果您使用 python-prctl,请将以下内容提前放在 /root/print-signal.py

import signal
import prctl

prctl.set_pdeathsig(signal.SIGTERM)