subprocces.Popen, kill process started with sudo

Question:

I am trying to start and later kill a process that requires sudo via a python-script. Even if the python script itself is run with sudo and kill() does not give any permission errors the process is not killed (and never receives SIGKILL).

Investigating this, i found out that Popen() returns the the process id of the sudo process, i assume at least, rather than the process i want to control. So when i correctly kill it later the underlying process keeps running. (Although if i kill the python program before killing the sudo process in python code the underlying process is also killed, so i guess there must be a way to do this manually, too).

I know it might be an option to use pgrep or pidof to search for the correct process, but as the processes name might not be unique it seems unnescessarly error prone (it might also occur that a process with the same name is started around the same time, so taking the latest one might not help).

Is there any solution to get reliably the pid of the underlying process started with sudo in python?

Using Python3.

My code for conducting the tests, taken slightly modified from https://stackoverflow.com/a/43417395/1171541:

import subprocess, time

cmd = ["sudo", "testscript.sh"]
def myfunction(action, process=None):
    if action === "start":
        process = subprocess.Popen(cmd)
        return process
    if action === "stop"
        # kill() and send_signal(signal.SIGTERM) do not work either
        process.terminate()
        
process = myfunction("start")
time.sleep(5)
myfunction("stop", process);
Asked By: patman

||

Answers:

Okay, i can answer my own question here (which i found on https://izziswift.com/how-to-terminate-a-python-subprocess-launched-with-shelltrue/). The trick was to open the process with:

subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)

and then kill it:

os.killpg(os.getpgid(process.pid), signal.SIGTERM)

This time i use a shell to open and use the os to kill all the processes in the process group.

Answered By: patman

There are two problems:

  1. sudo does not propagate SIGKILL, see man sudo. Solution: use the default signal SIGTERM. SIGKILL is bad practice anyway.
  2. sudo does not propagate signals sent from its own Progress Group ID out of fear of "accidentally kill itself", see man sudo. Solution: use start_new_session=True which (indirectly) gives a different PGID.

Demonstration of issue 1:

$ sudo sleep 15 &
$ kill  $! # sudo propagates the TERM signal and sleep is terminated

$ sudo sleep 15 & sudoPID=$!
$  ps  xfao pid,ppid,pgid,sid,comm | grep -C 5 -e PID -e sleep -e sudo
# As documented, sudo does NOT propagate the KILL signal and sleep is STILL running!
$ kill -KILL $sudoPID
$  ps  xfao pid,ppid,pgid,sid,comm | grep -C 5 -e PID -e sleep -e sudo

Demonstration of issue 2

# signal from different PGID is propagated and sleep is terminated
sudo sleep 15 & kill $! 

# Signal sent from the same PGID is NOT propagated, sleep is still running!
sudo bash -c 'sleep 15 & killall sudo'
Answered By: MarcH
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.