在 Ubuntu 15.10 中无法终止使用 python 创建的 sudo 进程
Can't terminate a sudo process created with python, in Ubuntu 15.10
我刚刚更新到 Ubuntu 15.10,突然在 Python 2.7 中,我无法 终止 我在 时创建的进程]根。
例如,这不会终止 tcpdump:
import subprocess, shlex, time
tcpdump_command = "sudo tcpdump -w example.pcap -i eth0 -n icmp"
tcpdump_process = subprocess.Popen(
shlex.split(tcpdump_command),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
time.sleep(1)
tcpdump_process.terminate()
tcpdump_out, tcpdump_err = tcpdump_process.communicate()
发生了什么事?它适用于以前的版本。
TL;DR: sudo
不转发命令进程组 since 28 May 2014 commit 中进程发送的信号,已在 sudo 1.8.11
中发布 - - 默认情况下,python 进程(sudo 的父进程)和 tcpdump 进程(孙进程)在同一个进程组中,因此 sudo
不会转发 .terminate()
发送的 SIGTERM
信号到 tcpdump
过程。
It shows the same behaviour when running that code while being the root user and while being a regular user + sudo
运行 作为普通用户在 .terminate()
上引发 OSError: [Errno 1] Operation not permitted
异常(如预期)。
运行 因为 root
重现了这个问题:sudo
和 tcpdump
进程没有在 .terminate()
上终止,代码卡在 [=23 上=] 在 Ubuntu 15.10。
相同的代码在 Ubuntu 12.04.
上终止了两个进程
tcpdump_process
名称具有误导性,因为变量指的是 sudo
进程(子进程),而不是 tcpdump
(孙进程):
python
└─ sudo tcpdump -w example.pcap -i eth0 -n icmp
└─ tcpdump -w example.pcap -i eth0 -n icmp
如, you don't need sudo
here: you're root already (though you shouldn't be -- you can sniff the network without root)。如果你放弃 sudo
; .terminate()
有效。
一般来说,.terminate()
不会递归地杀死整个进程树,因此预计孙进程会存活下来。虽然 sudo
是一个特例,但 from sudo(8) man page:
When the command is run as a child of the sudo
process, sudo
will
relay signals it receives to the command.emphasis is mine
即,sudo
应该将 SIGTERM
中继到 tcpdump
和 tcpdump
should stop capturing packets on SIGTERM
, from tcpdump(8) man page:
Tcpdump will, ..., continue capturing packets until it is
interrupted by a SIGINT signal (generated, for example, by typing your
interrupt character, typically control-C) or a SIGTERM signal
(typically generated with the kill(1) command);
即,预期行为是:tcpdump_process.terminate()
将 SIGTERM 发送到 sudo
,后者将信号中继到 tcpdump
,后者应停止捕获并两个进程都退出并且 .communicate()
returns tcpdump
的标准错误输出到 python 脚本。
注意:原则上命令可以运行不创建子进程,from the same sudo(8) man page:
As a special case, if the policy plugin does not define a close
function and no pty is required, sudo
will execute the command
directly instead of calling fork(2) first
因此 .terminate()
可能会直接将 SIGTERM 发送到 tcpdump
进程——尽管这不是解释:sudo tcpdump
在 Ubuntu 12.04 和15.10 在我的测试中。
如果我在 shell 中 运行 sudo tcpdump -w example.pcap -i eth0 -n icmp
然后 kill -SIGTERM
终止两个进程。它看起来不像 Python 问题(Python 2.7.3(用于 Ubuntu 12.04)在 Ubuntu 15.10 上表现相同。Python 3 在这里也失败).
它与进程组(job control)有关:将preexec_fn=os.setpgrp
传递给subprocess.Popen()
,这样sudo
就会在一个新的进程组(作业)中是 shell 中的领导者使得 tcpdump_process.terminate()
在这种情况下工作。
What happened? It works on previous versions.
Do not forward signals sent by a process in the command's process
group, do not forward it as we don't want the child to indirectly kill
itself. For example, this can happen with some versions of reboot
that call kill(-1, SIGTERM) to kill all other processes.emphasis is mine
preexec_fn=os.setpgrp
更改 sudo
的进程组。 sudo
的后代,例如 tcpdump
进程继承了该组。 python
和 tcpdump
不再在同一个进程组中,因此 .terminate()
发送的信号由 sudo
中继到 tcpdump
并退出。
Ubuntu 15.04 使用 Sudo version 1.8.9p5
问题中的代码按原样工作。
Ubuntu 15.10 使用包含 the commit.
的 Sudo version 1.8.12
sudo(8) man page in wily (15.10) 还是只讲子进程本身 -- 没有提到进程组:
As a special case, sudo will not relay signals that were sent by the
command it is running.
应该改为:
As a special case, sudo will not relay signals that were sent by a process in the process group of the command it is running.
您可以在 Ubuntu's bug tracker and/or on the upstream bug tracker 上打开文档问题。
我刚刚更新到 Ubuntu 15.10,突然在 Python 2.7 中,我无法 终止 我在 时创建的进程]根。 例如,这不会终止 tcpdump:
import subprocess, shlex, time
tcpdump_command = "sudo tcpdump -w example.pcap -i eth0 -n icmp"
tcpdump_process = subprocess.Popen(
shlex.split(tcpdump_command),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
time.sleep(1)
tcpdump_process.terminate()
tcpdump_out, tcpdump_err = tcpdump_process.communicate()
发生了什么事?它适用于以前的版本。
TL;DR: sudo
不转发命令进程组 since 28 May 2014 commit 中进程发送的信号,已在 sudo 1.8.11
中发布 - - 默认情况下,python 进程(sudo 的父进程)和 tcpdump 进程(孙进程)在同一个进程组中,因此 sudo
不会转发 .terminate()
发送的 SIGTERM
信号到 tcpdump
过程。
It shows the same behaviour when running that code while being the root user and while being a regular user + sudo
运行 作为普通用户在 .terminate()
上引发 OSError: [Errno 1] Operation not permitted
异常(如预期)。
运行 因为 root
重现了这个问题:sudo
和 tcpdump
进程没有在 .terminate()
上终止,代码卡在 [=23 上=] 在 Ubuntu 15.10。
相同的代码在 Ubuntu 12.04.
上终止了两个进程tcpdump_process
名称具有误导性,因为变量指的是 sudo
进程(子进程),而不是 tcpdump
(孙进程):
python
└─ sudo tcpdump -w example.pcap -i eth0 -n icmp
└─ tcpdump -w example.pcap -i eth0 -n icmp
如sudo
here: you're root already (though you shouldn't be -- you can sniff the network without root)。如果你放弃 sudo
; .terminate()
有效。
一般来说,.terminate()
不会递归地杀死整个进程树,因此预计孙进程会存活下来。虽然 sudo
是一个特例,但 from sudo(8) man page:
When the command is run as a child of the
sudo
process,sudo
will relay signals it receives to the command.emphasis is mine
即,sudo
应该将 SIGTERM
中继到 tcpdump
和 tcpdump
should stop capturing packets on SIGTERM
, from tcpdump(8) man page:
Tcpdump will, ..., continue capturing packets until it is interrupted by a SIGINT signal (generated, for example, by typing your interrupt character, typically control-C) or a SIGTERM signal (typically generated with the kill(1) command);
即,预期行为是:tcpdump_process.terminate()
将 SIGTERM 发送到 sudo
,后者将信号中继到 tcpdump
,后者应停止捕获并两个进程都退出并且 .communicate()
returns tcpdump
的标准错误输出到 python 脚本。
注意:原则上命令可以运行不创建子进程,from the same sudo(8) man page:
As a special case, if the policy plugin does not define a close function and no pty is required,
sudo
will execute the command directly instead of calling fork(2) first
因此 .terminate()
可能会直接将 SIGTERM 发送到 tcpdump
进程——尽管这不是解释:sudo tcpdump
在 Ubuntu 12.04 和15.10 在我的测试中。
如果我在 shell 中 运行 sudo tcpdump -w example.pcap -i eth0 -n icmp
然后 kill -SIGTERM
终止两个进程。它看起来不像 Python 问题(Python 2.7.3(用于 Ubuntu 12.04)在 Ubuntu 15.10 上表现相同。Python 3 在这里也失败).
它与进程组(job control)有关:将preexec_fn=os.setpgrp
传递给subprocess.Popen()
,这样sudo
就会在一个新的进程组(作业)中是 shell 中的领导者使得 tcpdump_process.terminate()
在这种情况下工作。
What happened? It works on previous versions.
Do not forward signals sent by a process in the command's process group, do not forward it as we don't want the child to indirectly kill itself. For example, this can happen with some versions of reboot that call kill(-1, SIGTERM) to kill all other processes.emphasis is mine
preexec_fn=os.setpgrp
更改 sudo
的进程组。 sudo
的后代,例如 tcpdump
进程继承了该组。 python
和 tcpdump
不再在同一个进程组中,因此 .terminate()
发送的信号由 sudo
中继到 tcpdump
并退出。
Ubuntu 15.04 使用 Sudo version 1.8.9p5
问题中的代码按原样工作。
Ubuntu 15.10 使用包含 the commit.
的Sudo version 1.8.12
sudo(8) man page in wily (15.10) 还是只讲子进程本身 -- 没有提到进程组:
As a special case, sudo will not relay signals that were sent by the command it is running.
应该改为:
As a special case, sudo will not relay signals that were sent by a process in the process group of the command it is running.
您可以在 Ubuntu's bug tracker and/or on the upstream bug tracker 上打开文档问题。