TL; DR : sudo does not transmit signals sent by a process in a process group of a process since May 28, 2014 commit , released in sudo 1.8.11 - the python (sudo parent) process and the tcpdump (grandchild) process are by default in the same process group, and therefore sudo does not send a SIGTERM signal sent by .terminate() to the tcpdump process.
It shows the same behavior when running this code, being root, and being a regular user + sudo
Running as a regular user raises an OSError: [Errno 1] Operation not permitted exception OSError: [Errno 1] Operation not permitted in .terminate() (as expected).
Running as root reproduces the problem: sudo and tcpdump processes will not be killed on .terminate() , and the code is stuck on .communicate() in Ubuntu 15.10.
The same code kills both processes on Ubuntu 12.04.
tcpdump_process name is misleading, since the variable refers to the sudo process (child process), not tcpdump (grandson):
python ββ sudo tcpdump -w example.pcap -i eth0 -n icmp ββ tcpdump -w example.pcap -i eth0 -n icmp
As @ Mr.E pointed out in the comments , you do not need sudo here: you are already root (although you should not be - you can sniff the network without root ). If you drop sudo ; .terminate() works.
In the general case, .terminate() does not recursively kill the entire process tree, and therefore it is expected that the grandson process will continue. Although sudo is a special case, from the sudo (8) man page :
When a command is run as a child of the sudo process, sudo will relay the signals it receives in the command. emphasis is mine
ie, sudo should pass SIGTERM to tcpdump and tcpdump should stop capturing packets on SIGTERM , from the tcpdump (8) man page :
Tcpdump will, ..., continue to capture packets until it is interrupted by a SIGINT signal (generated, for example, by entering your interrupt character, usually control-C) or a SIGTERM signal (usually generated by kill (1));
those. expected behavior : tcpdump_process.terminate() sends SIGTERM to sudo , which sends a signal to tcpdump , which should stop the capture, and both processes terminate, and .communicate() returns tcpdump output stderr to a python script.
Note: in principle, a command can be run without creating a child process from the same sudo (8) manual page :
As a special case, if the policy plugin does not determine the closure of the function and pty is not required, sudo will execute the command directly instead of calling fork (2) first
and therefore .terminate() can directly send SIGTERM to the tcpdump process, although this is not an explanation: sudo tcpdump creates two processes on both Ubuntu 12.04 and 15.10 in my tests.
If I run sudo tcpdump -w example.pcap -i eth0 -n icmp in the shell, then kill -SIGTERM terminates both processes. This doesn't seem to be a Python issue (Python 2.7.3 (used on Ubuntu 12.04) behaves the same on Ubuntu 15.10. Python 3 also doesn't work here).
Associated with process groups (task management ): passing preexec_fn=os.setpgrp to subprocess.Popen() so that sudo in a new process group (task), where it is the leader, as in the shell, does tcpdump_process.terminate() in in this case.
What happened? It works with previous versions.
An explanation is given in the sudo source code :
Do not forward the signals sent by the process in the process of the command to the group , do not forward it, since we do not want the child to indirectly kill himself. For example, this may happen with some reboot versions that kill kill (-1, SIGTERM) kills all other processes. emphasis is mine
preexec_fn=os.setpgrp modifies the sudo process group. sudo descendants such as the tcpdump process inherit the group. python and tcpdump no longer in the same process group, so the signal sent by .terminate() is passed via sudo to tcpdump , and it exits.
Ubuntu 15.04 uses Sudo version 1.8.9p5 , where the code from the question works as it is.
Ubuntu 15.10 uses Sudo version 1.8.12 , which contains a commit .
sudo (8) the man page in wily (15.10) still only talks about the child process itself - no mention of the process group
As a special case, sudo will not transmit signals that were sent to the running command.
It should be instead of:
As a special case, sudo will not relay the signals that were sent by a process in the process group of the command in which it is running.
You may discover a problem with the documentation on the Ubuntu bug tracker and / or on tracking upstream errors .