I want to kill a subprocess if the time of executing is too long.
I know I have to use os.kill or os.killpg.
However, the problems comes out when if I am not a root user. For example, in my designed GUI, I want to call subprocess, and os.kill or os.killpg to kill the subprocess. But my GUI is owned by apache. So when it comes to the command os.kill, I will get error:
[type:
exceptions.OSError value: [Errno 1] Operation not permitted
And besides, the version of my python is 2.4.3. so terminate()...can't be used.
Could anyone give me some ideas?
Thanks a lot!
P.S.
Related part of my code:
timeout=4
subp = subprocess.Popen('sudo %s'%commandtosend, shell=True,preexec_fn=os.setsid, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while subp.poll() is None:
time.sleep(0.1)
now = datetime.datetime.now()
if (now - start).seconds > timeout:
os.kill(subp.pid, signal.SIGKILL)
#os.killpg(subp.pid, signal.SIGKILL)
break
Remove sudo from the subprocess command if it's possible which you should do because you shouldn't run a subprocess in a sudo user from your GUI , it's definitely a security breach:
subprocess.Popen(commandtosend, shell=True,preexec_fn=os
^^
Here don't put sudo
Like this your subprocess will be launch with the www-data user(Apache user), and you can kill it with os.kill(subp.pid, signal.SIGKILL).
If it's not possible to remove the sudo (which is bad) from the subprocess you will have to execute the kill like this :
os.system("sudo kill %s" % (subp.pid, ))
Hope this can help :)
Your subprocess is running with superuser privileges (because you're starting it with sudo).
To kill it, you need to be superuser.
One option would be to not use os.kill but run 'sudo kill 5858' where 5858 would be the PID of the process spawned by subprocess.Popen.
It's also worth noting that if your program allows the user to control commandtosend you will give the user superuser rights to the entire machine.
Related
Consider this python script:
import subprocess
nc = subprocess.Popen(["/bin/bash"], stdin=subprocess.PIPE, text=True)
nc.stdin.write("nc localhost 2222\n")
nc.stdin.write("pwd\n")
When I listen with netcat as nc -lnvp 2222
I successfully connect and send the string pwd nothing more happens of course.
Now I get a non stable php reverse shell(Completely new event) and I connect through netcat successfully. I execute this script to upgrade shell and print current directory. By the way that listener is another Popen instance.
import subprocess
nc = subprocess.Popen(["/bin/bash"], stdin=subprocess.PIPE, text=True)
nc.stdin.write("nc localhost 2222\n")
nc.stdin.write('python3 -c "import pty;pty.spawn(\'/bin/bash\')"\n')
nc.stdin.write('pwd\n')
Now when I execute that python script, I expected the input will go through netcat, get executed in that new bash tty and spawn a stable shell and pass pwd to return current directory. But this script only works upto spawing stable shell and then stdin input doesn't go through nc or something else happens that I'm not aware of.
What's happening here?
Edit: I need to be able to run multiple commands. Using subprocess.communicate(input=<command>) causes deadlock and can't accept stdin.
I'm running a python subprocess using:
p = Popen(["sudo", "./a.out"])
where a.out is a C executable which runs continuously until a SIGINT or Ctrl+C signal is sent to it. I've had trouble with subprocess.Popen object functions such as send_signal() because Operation not permitted errors are raised due to the sudo nature of the executable. After this I tried to send a SIGINT to the subprocess via:
os.system(f"sudo kill -2 {p.pid}")
but this doesn't seem to target the process correctly. Running a quick sudo netstat -lpnt check shows the a.out process is still running on a pid which is different to the one which p.pid returned (usually by a few integers, i.e. p.pid returns 3031 but a.out is 3035). Anything that I've misunderstood?
You are actually getting pid of and killing sudo process (that forked your application process). Instead you should kill the whole process group with:
import subprocess, os
p = Popen(["sudo", "./a.out"])
pgid = os.getpgid(p.pid)
subprocess.check_output("sudo kill {}".format(pgid))
or with the help of pkill:
import subprocess
p = Popen(["sudo", "./a.out"])
subprocess.call(f"sudo pkill -2 -P {p.pid})
Specifically, I'm trying to use fabric to run some tests which rely on the existence of a MongoDB.
I have the following code:
db_cmd = 'mongod'
test_cmd = 'istanbul cover node_modules/mocha/bin/_mocha -- -R spec'
pid = os.spawnl(os.P_NOWAIT, db_cmd)
with shell_env(NODE_ENV='test'):
local(test_cmd)
I plan to use the PID to kill the process after the test_cmd has finished however I've not gotten that far yet.
The running of test_cmd results in an error suggesting that db_cmd has exited and that MongoDB is no longer available:
Uncaught Error: failed to connect to [localhost:27017]
However running mongod manually before running fabric causes test_cmd to run fine and interact with MongoDB.
I suspect I'm just not understanding os.spawnl. Note that this code needs to run on Windows / Linux and OSX so I think I'm somewhat restricted in which os.spawnxxx methods I can use. I'm also interested to know if there's a fabric method to achieve this as well though.
I successfully use:
os.killpg(process.pid, signal.SIGTERM)
Probably, you need to use subprocess module for that.
To run mongo in background use:
process = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
shell=True, preexec_fn=os.setsid
)
To kill it after tests, use command I 've written first.
command - is a string contained your mongo start code, for example :
mongod --host localhost --port 27018
It works fine for me. If you will have problems wit the code, please let me know it.
You can also do this in straight bash with jobs and traps:
#!/bin/bash
trap "kill %1" SIGINT SIGTERM EXIT
mongod --host localhost --port 27018 &
istanbul cover node_modules/mocha/bin/_mocha -- -R spec
exit 0
What this is doing:
Set a trap on signals, SIGINT SIGTERM EXIT, to kill the first backgrond job
Make a mongod instance, and throw it into the background (the first one)
Run the tests
trigger exit signal
So this will setup and tare down your mongod instance on completion, even on a term signal or exception.
I am starting my script locally via:
sudo python run.py remote
This script happens to also open a subprocess (if that matters)
webcam = subprocess.Popen('avconv -f video4linux2 -s 320x240 -r 20 -i /dev/video0 -an -metadata title="OfficeBot" -f flv rtmp://6f7528a4.fme.bambuser.com/b-fme/xxx', shell = True)
I want to know how to terminate this script when I SSH in.
I understand I can do:
sudo pkill -f "python run.py remote"
or use:
ps -f -C python
to find the process ID and kill it that way.
However none of these gracefully kill the process, I want to able to trigger the equilivent of CTRL/CMD C to register an exit command (I do lots of things on shutdown that aren't triggered when the process is simply killed).
Thank you!
You should use "signals" for it:
http://docs.python.org/2/library/signal.html
Example:
import signal, os
def handler(signum, frame):
print 'Signal handler called with signal', signum
signal.signal(signal.SIGINT, handler)
#do your stuff
then in terminal:
kill -INT $PID
or ctrl+c if your script is active in current shell
http://en.wikipedia.org/wiki/Unix_signal
also this might be useful:
How do you create a daemon in Python?
You can use signals for communicating with your process. If you want to emulate CTRL-C the signal is SIGINT (which you can raise by kill -INT and process id. You can also modify the behavior for SIGTERM which would make your program shut down cleanly under a broader range of circumstances.
I am writing a GUI which uses SSH commands. I tried to use the subprocess module to call ssh and set the SSH_ASKPASS environment variable so that my application can pop up a window asking for the SSH password. However I cannot make ssh read the password using the given SSH_ASKPASS command: it always prompts it in the terminal window, regardless how I set the DISPLAY, SSH_ASKPASS, TERM environment variables or how I pipe the standard input/output. How can I make sure that ssh is detached from the current TTY and use the given program to read password?
My test code was:
#!/usr/bin/env python
import os
import subprocess
env = dict(os.environ)
env['DISPLAY'] = ':9999' # Fake value (trying in OS X and Windows)
del env['TERM']
env['SSH_ASKPASS'] = '/opt/local/libexec/git-core/git-gui--askpass'
p = subprocess.Popen(['ssh', '-T', '-v', 'user#myhost.com'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env
)
p.communicate()
SSH uses the SSH_ASKPASS variable only if the process is really detached from TTY (stdin redirecting and setting environment variables is not enough). To detach a process from console it should fork and call os.setsid(). So the first solution I found was:
# Detach process
pid = os.fork()
if pid == 0:
# Ensure that process is detached from TTY
os.setsid()
# call ssh from here
else:
print "Waiting for ssh (pid %d)" % pid
os.waitpid(pid, 0)
print "Done"
There is also an elegant way to do this using the subprocess module: in the preexec_fn argument we can pass a Python function that is called in the subprocess before executing the external command. So the solution for the question is one extra line:
env = {'SSH_ASKPASS':'/path/to/myprog', 'DISPLAY':':9999'}
p = subprocess.Popen(['ssh', '-T', '-v', 'user#myhost.com'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
preexec_fn=os.setsid
)
Your problem is that SSH detects your TTY and talks to it directly (as is clearly stated in the man-page). You can try and run ssh without a terminal - the man page suggests it might be necessary to redirect stdin to /dev/null for ssh to think it has no terminal.
You can also use pexcept for this, it's known to work with SSH - example usage.
The Right Way (TM) to do what you're trying to do is either:
Use a library specifically for using SSH in python (for example twisted conch or paramiko)
Use public and private keys so that passwords will not be necessary
If you want a quick and dirty way of doing it for your own personal usage, you could enable passwordless login between these two machines by doing this in your terminal:
ssh-keygen -t rsa # generate a keypair (if you haven't done this already)
ssh-copy-id user#other_machine # copy your public key to the other machine
Then you can get ssh commands to go through (subprocess can't seem to accept ssh commands directly) by creating a script (remember to mark it executable, e.g. chmod 755 my_script.sh ) with the things you want, such as:
#!/bin/bash
ssh user#other_machine ls
and call it from your program:
import subprocess
response = subprocess.call("./my_script.sh")
print(response)
For production-use of apps that need to be deployed on other people's machines I'd go with abyx's approach of using an SSH library. Much simpler than messing with some environment variables.