在Ubuntu 15.10中无法终止使用python创建的sudo进程

我刚刚更新到Ubuntu 15.10并突然在 Python 2.7中我无法终止我在创建root时创建的进程.
例如,这不会终止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不转发由sudo 1.8.11中发布的命令进程组 since 28 May 2014 commit中的进程发送的信号 – python进程(sudo的父进程)和tcpdump进程(grandchild)认位于同一进程组中因此,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

以普通用户身份运行会引发OSError:[Errno 1] .terminate()上的操作不允许异常(如预期的那样).

以root身份运行会重现问题:sudo和tcpdump进程不会在.terminate()上被杀死,代码会在Ubuntu 15.10上停留在.communicate()上.

相同的代码会杀死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

作为@Mr.E pointed out in the comments,你在这里不需要sudo:你已经是root(虽然你不应该 – 你可以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,tcpdump应该停止捕获并且两个进程都退出并且.communicate()将tcpdump的stderr输出返回给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.

解释是在the sudo’s source code

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版本1.8.9p5,问题中的代码按原样运行.

Ubuntu 15.10使用包含the commit的Sudo版本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和/或the upstream bug tracker上打开文档问题.

相关文章

目录前言一、创建Hadoop用户二、更新apt和安装Vim编辑器三、...
原文连接:https://www.cnblogs.com/yasmi/p/5192694.html ...
电脑重启后,打开VirtualBox,发现一直用的虚拟机莫名的消失...
参见:https://blog.csdn.net/weixin_38883338/article/deta...
Ubuntu 18.04 LTS 已切换到 Netplan 来配置网络接口。Netpla...
介绍每个 Web 服务都可以通过特定的 URL 在 Internet 上访问...