在 Ubuntu 15.10 中无法终止使用 python 创建的 sudo 进程 [英] Can't terminate a sudo process created with python, in Ubuntu 15.10

查看:29
本文介绍了在 Ubuntu 15.10 中无法终止使用 python 创建的 sudo 进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚更新到 Ubuntu 15.10,突然在 Python 2.7 中我无法终止我在 root 时创建的进程.例如,这不会终止 tcpdump:

I just updated to Ubuntu 15.10 and suddenly in Python 2.7 I am not able to terminate a process I created when being root. For example, this doesn't terminate 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()

发生了什么?它适用于以前的版本.

What happened? It works on previous versions.

推荐答案

TL;DR: sudo 不转发由命令的进程组中的进程发送的信号 自 2014 年 5 月 28 日提交,在 sudo 1.8.11 -- 默认情况下,python 进程(sudo 的父进程)和 tcpdump 进程(孙子进程)在同一个进程组中,因此 sudo 不会转发 SIGTERM 发送的信号 SIGTERMcode>.terminate() 到 tcpdump 进程.

TL;DR: sudo does not forward signals sent by a process in the command's process group since 28 May 2014 commit released in sudo 1.8.11 -- the python process (sudo's parent) and the tcpdump process (grandchild) are in the same process group by default and therefore sudo does not forward SIGTERM signal sent by .terminate() to the tcpdump process.

在以 root 用户和普通用户 + sudo 运行该代码时,它显示相同的行为

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 allowed 异常(如预期).

Running as a regular user raises OSError: [Errno 1] Operation not permitted exception on .terminate() (as expected).

root 身份运行会重现该问题:sudotcpdump 进程不会在 .terminate() 上终止并且代码停留在 Ubuntu 15.10 上的 .communicate() 上.

Running as root reproduces the issue: sudo and tcpdump processes are not killed on .terminate() and the code is stuck on .communicate() on Ubuntu 15.10.

相同的代码会杀死 Ubuntu 12.04 上的两个进程.

The same code kills both processes on Ubuntu 12.04.

tcpdump_process 名称具有误导性,因为该变量指的是 sudo 进程(子进程),而不是 tcpdump(孙进程):

tcpdump_process name is misleading because the variable refers to the sudo process (the child process), not tcpdump (grandchild):

python
└─ sudo tcpdump -w example.pcap -i eth0 -n icmp
   └─ tcpdump -w example.pcap -i eth0 -n icmp          

作为 @Mr.E在评论中指出,你不需要 sudo 在这里:你已经是 root(虽然你不应该 - 你可以 无需 root 即可嗅探网络).如果你删除 sudo;.terminate() 有效.

As @Mr.E pointed out in the comments, you don't need sudo here: you're root already (though you shouldn't be -- you can sniff the network without root). If you drop sudo; .terminate() works.

一般来说,.terminate() 不会递归地杀死整个进程树,因此预计孙子进程会存活下来.尽管 sudo 是一个特例,来自 sudo(8) 手册页:

In general, .terminate() does not kill the whole process tree recursively and therefore it is expected that a grandchild process survives. Though sudo is a special case, from sudo(8) man page:

当命令作为 sudo 进程的子进程运行时,sudo中继信号它接收到命令.重点是我的

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 中继到 tcpdumptcpdump 应该停止在 SIGTERM 上捕获数据包,来自 tcpdump(8) 手册页:

i.e., sudo should relay SIGTERM to tcpdump and tcpdump should stop capturing packets on SIGTERM, from tcpdump(8) man page:

Tcpdump 将,...,继续捕获数据包,直到它被被 SIGINT 信号中断(例如,通过键入您的中断字符,通常是 control-C) 或 SIGTERM 信号(通常由 kill(1) 命令生成);

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()tcpdump 的 stderr 输出返回给 python 脚本.

i.e., the expected behavior is: tcpdump_process.terminate() sends SIGTERM to sudo which relays the signal to tcpdump which should stop capturing and both processes exit and .communicate() returns tcpdump's stderr output to the python script.

注意:原则上该命令可以在不创建子进程的情况下运行,来自同一个 sudo(8) 手册页:

Note: in principle the command may be run without creating a child process, from the same sudo(8) man page:

作为一种特殊情况,如果策略插件没有定义关闭function 并且不需要 pty,sudo 将执行命令直接调用而不是先调用 fork(2)

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 上有两个进程.

and therefore .terminate() may send SIGTERM to the tcpdump process directly -- though it is not the explanation: sudo tcpdump creates two processes on both Ubuntu 12.04 and 15.10 in my tests.

如果我在 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 在这里也失败了).

If I run sudo tcpdump -w example.pcap -i eth0 -n icmp in the shell then kill -SIGTERM terminates both processes. It does not look like Python issue (Python 2.7.3 (used on Ubuntu 12.04) behaves the same on Ubuntu 15.10. Python 3 also fails here).

与进程组有关(作业控制):将 preexec_fn=os.setpgrp 传递给 subprocess.Popen()这样 sudo 将在一个新的进程组(作业)中,它是领导者,因为在 shell 中使 tcpdump_process.terminate() 在这种情况下工作.

It is related to process groups (job control): passing preexec_fn=os.setpgrp to subprocess.Popen() so that sudo will be in a new process group (job) where it is the leader as in the shell makes tcpdump_process.terminate() work in this case.

发生了什么?它适用于以前的版本.

What happened? It works on previous versions.

解释在sudo的源代码:

不要转发命令进程中某个进程发送的信号group,不要转发,因为我们不想孩子间接杀人本身.例如,某些版本的重启可能会发生这种情况调用 kill(-1, SIGTERM) 来杀死所有其他进程.重点是我的

preexec_fn=os.setpgrp 更改 sudo 的进程组.sudo 的子进程如 tcpdump 进程继承了组.pythontcpdump 不再在同一个进程组中,因此 .terminate() 发送的信号由 sudo<中继/code> 到 tcpdump 并退出.

preexec_fn=os.setpgrp changes sudo's process group. sudo's descendants such as tcpdump process inherit the group. python and tcpdump are no longer in the same process group and therefore the signal sent by .terminate() is relayed by sudo to tcpdump and it exits.

Ubuntu 15.04 使用 Sudo 版本 1.8.9p5,问题中的代码按原样运行.

Ubuntu 15.04 uses Sudo version 1.8.9p5 where the code from the question works as is.

Ubuntu 15.10 使用 Sudo 版本 1.8.12,其中包含 提交.

Ubuntu 15.10 uses Sudo version 1.8.12 that contains the commit.

wily (15.10) 中的 sudo(8) 手册页 仍然只讨论子进程本身——没有提到进程组:

sudo(8) man page in wily (15.10) still talks only about the child process itself -- no mention of the process group:

作为一种特殊情况,sudo 不会中继由命令它正在运行.

As a special case, sudo will not relay signals that were sent by the command it is running.

应该改为:

作为一种特殊情况,sudo 不会中继它正在运行的命令的进程组中的进程发送的信号.

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 的错误跟踪器 和/或 上游错误跟踪器.

You could open a documentation issue on Ubuntu's bug tracker and/or on the upstream bug tracker.

这篇关于在 Ubuntu 15.10 中无法终止使用 python 创建的 sudo 进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆