2

我用 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 线程的一个简单误解......

4

2 回答 2

3

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

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

此外,出于安全考虑,我建议您以 root 身份创建 bash 脚本,将其放在任何方便的地方,不授予用户对其的写入权限,最后设置 sudo 以在不需要密码的情况下运行它。这将消除将密码存储在此 python 脚本中的需要。

于 2012-10-12T22:04:31.187 回答
2

TL;博士;

处理这个问题的一种更优雅的方法是使用 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”才能真正起作用,我设计了一个模仿用户行为的解决方案,它就像一个魅力!

于 2016-08-23T21:04:08.543 回答