2

Python で GTK GUI アプリケーションを作成して、シェル スクリプトを呼び出し、呼び出しパラメーターを GUI からスクリプトに渡し、スクリプトの結果をテキスト ウィンドウに返すようにしました。

ここで、ユーザーが実行中のシェル スクリプトを GUI からキャンセルできるようにしたいと考えています。スクリプトは 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 を使用する必要があることがわかりました。私はスレッディングに慣れていません。それは、私の側から見た単純な Linux スレッディングの誤解にすぎない可能性があります ...

4

2 に答える 2

3

sudoで開始した場合、それを強制終了するにはroot権限が必要です。基本的に、次のことを行う必要があります。

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

また、セキュリティ上の理由から、bashスクリプトをrootとして作成し、ユーザーに書き込みアクセスを許可せずに、都合のよい場所に配置し、最後にパスワードを必要とせずに実行するようにsudoを設定することをお勧めします。これにより、このpythonスクリプトにパスワードを保存する必要がなくなります。

于 2012-10-12T22:04:31.187 に答える
2

TL;DR;

この問題を処理するはるかに洗練された方法は、「subprocess.Popen」の代わりに pyexpect を使用してプロセスを開始することです。

spawnProcess = pexpect.spawn(command)

次に、プロセスを「強制終了」したい場合は、次のようにする必要があります。

spawnProcess.sendcontrol("c")

説明:

os.system から sudo kill を実行するのは汚いハックであり、ユーザーがパスワードを再度入力する必要があるか、サブプロセスを強制終了できるようにするためだけに root パスワードを保存する必要があります。私の場合、プロセスはsudoで開始されていないため、さらに悪いことに、setuidルート実行可能ファイルであるため、ターゲットユーザーは実際にはsudoアクセスを持っていません。

結局のところ、多くの人が信じているように、bash からの「CTRL+C」は kill -INT と同じではありません。sudo または setuid root で開始したプロセスを「CTRL+C」することはできますが、そのプロセスに 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 に答える