这个问题是关于 PyQt4 的,但我不关心可移植性。目标系统仅为 GNU/Linux。
我有一个小程序,应该使用 kdesudo/kdesu/gtksu 或类似的程序运行。
在某些时候,我需要启动一个新进程,假装是运行该命令的原始用户。
在文档(here 和 here)中,我读到我应该覆盖 setupChildProcess
方法。所以这是我的代码:
#!/usr/bin/python2
from os import setgroups, setuid, setgid, environ
from sys import argv, exit
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
testme = QtGui.QPushButton('\nTEST ME\n')
self.connect(testme, QtCore.SIGNAL('clicked()'), self.test)
self.setCentralWidget(testme)
def test(self):
sp = SandboxProcess()
sp.startDetached('id')
class SandboxProcess(QtCore.QProcess):
def __init__(self, *args, **kwargs):
super(SandboxProcess, self).__init__(*args, **kwargs)
env = environ.copy()
if 'SUDO_USER' in environ:
username = environ['SUDO_USER']
del env['SUDO_USER']
del env['SUDO_GID']
del env['SUDO_UID']
elif 'KDESU_USER' in environ:
username = environ['KDESU_USER']
del env['KDESU_USER']
else:
username = 'nobody'
env['USERNAME'] = username
env['LOGNAME'] = username
env['USER'] = username
env['MAIL'] = '/var/mail/' + username
env['HOME'] = '/home/' + username
qenv = QtCore.QProcessEnvironment()
for k, v in env.iteritems():
qenv.insert(k, v)
self.setProcessEnvironment(qenv)
def setupChildProcess(self):
super(SandboxProcess, self).setupChildProcess()
if 'SUDO_USER' in environ:
gid = environ['SUDO_GID']
uid = environ['SUDO_UID']
elif 'KDESU_USER' in environ:
gid = environ['KDE_SESSION_UID']
uid = environ['KDE_SESSION_UID']
else:
uid = 65534
gid = 65534
setgroups([])
setgid(int(gid))
setuid(int(uid))
if __name__ == '__main__':
app = QtGui.QApplication(argv)
main = MainWindow()
main.show()
exit(app.exec_())
首先,我知道我没有以尽可能最好的方式清洁环境,但这只是向您展示我的问题的概念证明。
我的问题是命令仍然以 root 身份执行。此外,似乎根本没有调用 setupChildProcess
。
我做错了什么?
最佳答案
我自己找到了答案。我会把它张贴在这里以供将来引用。
只有当您使用startDetached
而不是start
时,才会存在此问题中描述的问题。
原因是 startDetached
被实现为父类的一个静态方法,所以它永远不会看到我对 setupChildProcess
的覆盖。
为避免这种情况,您应该首先使用start
启动进程,然后您应该分离它。不幸的是,如您所见here ,目前这是不可能的。
我的解决方法是使用另一个脚本作为分离进程的“代理”:
分离.py:
#!/usr/bin/env python2
from sys import argv
from os import devnull
from subprocess import Popen
with open(devnull, "w") as fnull:
Popen(argv[1:], stdout = fnull, stderr = fnull, close_fds=True)
例子.py:
#!/usr/bin/python2
from os import setgroups, setuid, setgid, environ
from sys import argv, exit
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
testme = QtGui.QPushButton('\nTEST ME\n')
self.connect(testme, QtCore.SIGNAL('clicked()'), self.test)
self.setCentralWidget(testme)
def test(self):
sp = SandboxProcess()
sp.start('python2 ./detach.py firefox')
sp.waitForFinished()
print str(sp.readAllStandardOutput()).strip()
class SandboxProcess(QtCore.QProcess):
def __init__(self, *args, **kwargs):
super(SandboxProcess, self).__init__(*args, **kwargs)
env = environ.copy()
if 'SUDO_USER' in environ:
username = environ['SUDO_USER']
del env['SUDO_USER']
del env['SUDO_GID']
del env['SUDO_UID']
elif 'KDESU_USER' in environ:
username = environ['KDESU_USER']
del env['KDESU_USER']
else:
username = 'nobody'
env['USERNAME'] = username
env['LOGNAME'] = username
env['USER'] = username
env['MAIL'] = '/var/mail/' + username
env['HOME'] = '/home/' + username
#cleaning environment....
qenv = QtCore.QProcessEnvironment()
for k, v in env.iteritems():
qenv.insert(k, v)
self.setProcessEnvironment(qenv)
def setupChildProcess(self):
super(SandboxProcess, self).setupChildProcess()
if 'SUDO_USER' in environ:
gid = environ['SUDO_GID']
uid = environ['SUDO_UID']
elif 'KDESU_USER' in environ:
gid = environ['KDE_SESSION_UID']
uid = environ['KDE_SESSION_UID']
else:
uid = 65534
gid = 65534
setgroups([])
setgid(int(gid))
setuid(int(uid))
if __name__ == '__main__':
app = QtGui.QApplication(argv)
main = MainWindow()
main.show()
exit(app.exec_())
我还按照@mdurant 的建议将与环境相关的代码移到了 __init__
而不是 setupChildProcess
中。
关于python - 无法使用 PyQt4 中的 QProcess 放弃新进程的权限,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25083934/