0

I have the following problem:

I need my Python script run a bash script. In case the bash script is running more than let's say 10 seconds, I need to kill it. This is what I have so far:

cmd = ["bash", "script.sh", self.get_script_path()]
process = subprocess.Popen(cmd)

time.sleep(10)  # process running here...

procinfo = psutil.Process(process.pid)
children = procinfo.children(recursive=True)
for child in children:
    os.kill(child.pid, signal.SIGKILL)

The thing I am afraid of is this scenario: The bash script finishes in 1 second, frees its PID and the system passes the PID to another process. After 10 seconds, I kill the PID which I think it belongs to my script but it is not true and I kill some other process. The script needs to be run as root because I require chroot in it.

Any ideas?

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
karlosss
  • 2,816
  • 7
  • 26
  • 42
  • If the subprocess was created without `shell=True` then you should be able to simply use [`process.kill()`](https://docs.python.org/2/library/subprocess.html#subprocess.Popen.kill). Otherwise, refer to [this question](http://stackoverflow.com/questions/4789837/how-to-terminate-a-python-subprocess-launched-with-shell-true) – NullUserException Aug 28 '16 at 07:24

3 Answers3

2

Since you are already using psutil I suggest you replace the calls to the subprocess module with calls to psutil.Popen. This class has the same interface of subprocess.Popen but provides all the functionality of psutil.Process.

Also note that the psutil library pre-emptively checks for PID reuse already, at least for a number of methods including terminate and kill (just read the documentation for Process).

This means that the following code:

cmd = ["bash", "script.sh", self.get_script_path()]
process = psutil.Popen(cmd)

time.sleep(10)  # process running here...

children = process.children(recursive=True)
for child in children:
    child.terminate()   # try to close the process "gently" first
    child.kill()

Note that the documentation for children says:

children(recursive=False)

Return the children of this process as a list of Process objects, preemptively checking whether PID has been reused.

In summary this means that:

  1. When you call children the psutil library checks that you want the children of the correct process and not one that happen to have the same pid
  2. When you call terminate or kill the library makes sure that you are killing your child process and not a random process with the same pid.
Bakuriu
  • 98,325
  • 22
  • 197
  • 231
0

I think the timeout command is perfect for you. From the doc page:

Synopsis

timeout [OPTION] NUMBER[SUFFIX] COMMAND [ARG]...
timeout [OPTION]


Description

Start COMMAND, and kill it if still running after NUMBER seconds. SUFFIX may be 's' for seconds (the default), 'm' for minutes, 'h' for hours or 'd' for days.

-s, --signal=SIGNAL
specify the signal to be sent on timeout.
        SIGNAL may be a name like 'HUP' or a number.
        See 'kill -l' for a list of signals

By depending on timeout, you don't have to worry about the messy details of PID reuse, race conditions, etc. Those concerns are nicely encapsulated in this standard Unix utility. Another benefit is your script will resume execution immediately upon early termination by the subprocess, rather than needlessly sleeping for the full 10 seconds.

Demo in bash:

timeout -s9 10 sleep 11; echo $?;
## Killed
## 137
timeout -s9 10 sleep 3; echo $?;
## 0

Demo in python:

import subprocess;
subprocess.Popen(['timeout','-s9','10','sleep','11']).wait();
## -9
subprocess.Popen(['timeout','-s9','10','sleep','3']).wait();
## 0
bgoldst
  • 34,190
  • 6
  • 38
  • 64
  • Thanks, this might be useful. In this case, I would prefer a native python solution. Thanks anyway for `timeout` introduction :) – karlosss Aug 28 '16 at 09:24
-3

I am using command stop process_name on ubuntu for stop my process. May it will helpful.

dev verma
  • 671
  • 1
  • 9
  • 18
  • Sorry, I need a solution which works for all linux distros. I said that the script is run as root, so I do not understand the `sudo` here. And finally, the process name is bash so I cannot kill it by name. – karlosss Aug 28 '16 at 09:26