Ric*_*son 6 python ubuntu sudo subprocess ubuntu-15.10
我刚刚更新到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()
Run Code Online (Sandbox Code Playgroud)
发生了什么?它适用于以前的版本.
jfs*_*jfs 10
TL; DR:sudo不转发自2014年5月28日发布的命令进程组中的进程发送的信号sudo 1.8.11- python进程(sudo的父进程)和tcpdump进程(grandchild)默认位于同一进程组中,因此sudo不会将SIGTERM发送的信号转发.terminate()给tcpdump进程.
当作为root用户运行该代码并且作为常规用户+ sudo时,它显示相同的行为
以普通用户身份运行会引发OSError: [Errno 1] Operation not permitted异常.terminate()(如预期的那样).
运行时会root重现问题:sudo并且tcpdump不会终止进程.terminate()并且代码会停留.communicate()在Ubuntu 15.10上.
相同的代码会杀死Ubuntu 12.04上的两个进程.
tcpdump_processname是误导性的,因为变量引用sudo进程(子进程),而不是tcpdump(孙进程):
python
?? sudo tcpdump -w example.pcap -i eth0 -n icmp
?? tcpdump -w example.pcap -i eth0 -n icmp
Run Code Online (Sandbox Code Playgroud)
正如@E先生在评论中指出的那样,你不需要sudo在这里:你已经是根(虽然你不应该 - 你可以在没有root的情况下嗅探网络).如果你掉下来sudo; .terminate()作品.
通常,.terminate()不会递归地杀死整个进程树,因此预计孙子进程会幸存.虽然sudo是一个特例,但是从sudo(8)手册页:
当命令作为
sudo进程的子进程运行时,sudo会将 收到的信号中继到命令.重点是我的
即,sudo应该传达SIGTERM到tcpdump和tcpdump应该停止抓包SIGTERM,tcpdump的距离(8)手册页:
Tcpdump将继续捕获数据包,直到被SIGINT信号(例如,通过键入中断字符,通常是control-C生成)或SIGTERM信号(通常使用kill(1)命令生成)中断为止. ;
即,预期的行为是:tcpdump_process.terminate()发送SIGTERM,继续捕获应该停止捕获sudo的信号,tcpdump并且两个进程都退出并将stderr输出.communicate()返回tcpdump到python脚本.
注意:原则上,可以在不创建子进程的情况下运行命令,来自同一个sudo(8)手册页:
作为特殊情况,如果策略插件没有定义close函数并且不需要pty,
sudo则将直接执行命令而不是首先调用fork(2)
因此.terminate()可以tcpdump直接将SIGTERM发送到进程 - 虽然它不是解释:sudo tcpdump在我的测试中在Ubuntu 12.04和15.10上创建两个进程.
如果我sudo tcpdump -w example.pcap -i eth0 -n icmp在shell中运行,则kill -SIGTERM终止两个进程.它看起来不像Python问题(Python 2.7.3(在Ubuntu 12.04上使用)在Ubuntu 15.10上表现相同.Python 3也在这里失败).
它是处理组(相关作业控制):通过preexec_fn=os.setpgrp对subprocess.Popen()使sudo会在它的领导者在shell一个新的进程组(作业),使tcpdump_process.terminate()工作在这种情况下.
发生了什么?它适用于以前的版本.
解释是在sudo的源代码中:
不要转发命令进程组中进程发送的信号,不要转发它,因为我们不希望子进程间接自杀.例如,某些版本的reboot可以调用kill(-1,SIGTERM)来杀死所有其他进程.重点是我的
preexec_fn=os.setpgrp变更sudo的过程组.sudo像tcpdump进程这样的后代继承了这个组.python和tcpdump是相同的处理组中不再并因此通过发送的信号.terminate()是通过中继sudo到tcpdump和它退出.
Ubuntu 15.04使用Sudo version 1.8.9p5问题代码的工作原理.
Ubuntu 15.10使用Sudo version 1.8.12包含提交的内容.
sudo(8)wily中的手册页(15.10)仍然只讨论子进程本身 - 没有提到进程组:
作为一种特殊情况,sudo不会中继由其运行的命令发送的信号.
它应该是:
作为一种特殊情况,sudo不会中继由正在运行的命令的进程组中的进程发送的信号.
您可以在Ubuntu的错误跟踪器和/或上游错误跟踪器上打开文档问题.
| 归档时间: |
|
| 查看次数: |
1672 次 |
| 最近记录: |