python - 无法使用 PyQt4 中的 QProcess 放弃新进程的权限

标签 python linux qt pyqt pyqt4

这个问题是关于 PyQt4 的,但我不关心可移植性。目标系统仅为 GNU/Linux。

我有一个小程序,应该使用 kdesudo/kdesu/gtksu 或类似的程序运行。 在某些时候,我需要启动一个新进程,假装是运行该命令的原始用户。 在文档(herehere)中,我读到我应该覆盖 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/

相关文章:

python - 如何获取所有子目录和文件的列表及其按大小排序的大小?

python - 如何计算 PDB 文件中所有原子之间的距离并从中创建距离矩阵

linux - 如何从现有脚本创建 shell 脚本?

c++ - 如何拦截 QtWebKit 中的 AJAX 请求?

c++ - QLineEdit 的完成者获取内存

python - 乘以 2 列,直到获得所需的值

python - 带有 UnboundLocalError 的本地和全局引用

linux - 检查目录树中文件时间戳的快速方法

linux - Bash:将 redis-benchmark 结果存储到 var 生成奇怪的字符串

python Qt : main widget scroll bar