如何杀死用Popen用sudo调用的shell脚本?

时间:2012-10-12 22:02:34

标签: python

我在Python中编写了一个GTK GUI应用程序来调用shell脚本并将调用参数从GUI传递给脚本,并在文本窗口中返回脚本结果。

现在我想允许用户从GUI取消正在运行的shell脚本。该脚本以Popen开头。如果我以普通用户的身份调用它,它可以正常工作(在下面的代码中标记sudo = False)。当我使用sudo来调用脚本时,我无法再取消它。我发现如果我使用shell = True,报告kill的人将无效,所以我用shell = False测试它,但它也不起作用。

我将我的问题删除到以下代码段:

import os
import signal
import subprocess
import time
import sys
import shlex

bashScriptFileName="t.sh"

f = open(bashScriptFileName,'w')

f.write("""#!/bin/bash
on_die()
{
    echo "Dying..."
    exit 0
}  

trap 'on_die' TERM

SEC=0
while true ; do
    sleep 1
    SEC=$((SEC+1))
    echo "I'm PID# $$, and I'm alive for $SEC seconds now!"
done

exit 0""")
f.close()
os.chmod(bashScriptFileName, 0755)

shell=True      # flag to enable/disable shell invocation of Popen
sudo=True       # flag to invoke command with sudo

if sudo:
    commandToExecute='sudo -S '+ bashScriptFileName
else:
    commandToExecute='./' + bashScriptFileName

if not shell:
    commandToExecute = shlex.split(commandToExecute)

print "Command: %s" % (commandToExecute)                                

proc = subprocess.Popen(commandToExecute, stdin=subprocess.PIPE, close_fds=False,shell=shell, preexec_fn=os.setsid)
print >> proc.stdin, "secretPassword"                
print 'PARENT      : Pausing before sending signal to child %s...' % proc.pid
sys.stdout.flush()
time.sleep(5)
print 'PARENT      : Signaling child %s' % proc.pid
sys.stdout.flush()
os.killpg(proc.pid, signal.SIGTERM)
time.sleep(3)
print "Done"

我实际上已经遇到了使用os.kill将脚本作为普通用户取消的问题,但后来发现我必须使用os.setuid和killpg来杀死正在运行的shell。我是线程新手,可能只是一个简单的Linux线程误解了我...

2 个答案:

答案 0 :(得分:3)

如果你用sudo启动它,你必须拥有root权限才能杀死它。基本上,你必须这样做:

os.system("sudo kill %d"%(pid))

另外,出于安全考虑,我建议您以root身份创建bash脚本,将其放在任何方便的地方,不要让用户对它进行写入访问,最后设置sudo来运行它而不需要密码。这将无需在此python脚本中存储密码。

答案 1 :(得分:1)

TL; DR;

处理此问题的一种更优雅的方法是使用pyexpect而不是“subprocess.Popen”启动该过程。

spawnProcess = pexpect.spawn(command)

然后,当你想要“杀死”这个过程时,你只需要:

spawnProcess.sendcontrol("c")

说明:

从os.system运行sudo kill是一个肮脏的黑客,需要用户再次输入密码或要求你存储root密码只是为了让你杀死一个子进程。在我的情况下,它更糟糕,因为该进程不是以sudo启动的,它是setuid root可执行文件,因此目标用户实际上没有sudo访问权限。

事实证明,来自bash的“CTRL + C”与kill -INT不同,因为很多人都会让你相信。你可以“CTRL + C”一个以sudo或setuid root开头的进程,但是你不能将SIGINT发送给进程。我在这里找到了解释:

https://www.reddit.com/r/linux/comments/2av912/how_does_sudo_get_signals_from_the_controlling/ciz574v

知道只有“CTRL + C”真的有用,我设计了一个模仿用户会做什么的解决方案,它就像一个魅力!