在Mac OS X,Windows上用Python获取root对话框?

时间:2011-04-21 16:28:33

标签: python windows macos wxpython root

如何在我的Python应用程序中弹出特权提升对话框?我想要Windows上的UAC对话框和Mac上的密码验证对话框。

基本上,我需要部分应用程序的root权限,我需要通过GUI获得这些权限。我正在使用wxPython。有什么想法吗?

3 个答案:

答案 0 :(得分:4)

在Windows上,如果不启动新进程,就无法获得UAC对话框,甚至无法使用CreateProcess启动该进程。

可以通过运行具有相应清单文件的另一个应用程序来实现UAC对话框 - 有关如何使用py2exe执行此操作的示例,请参阅Running compiled python (py2exe) as administrator in Vista

你也可以使用win32 api ShellExecute http://msdn.microsoft.com/en-us/library/bb762153(v=vs.85).aspx以编程方式使用runas动词 - 你可以使用ctypes http://python.net/crew/theller/ctypes/来调用它,它是python 2.5+ iirc上标准库的一部分。

抱歉不了解Mac。如果您提供有关您希望在Windows上完成的内容的更多详细信息,我可以提供更具体的帮助。

答案 1 :(得分:2)

我知道帖子有点旧,但我写了以下内容作为我的问题的解决方案(在Linux和OS X上以root身份运行python脚本)。

我编写了以下bash脚本来执行具有管理员权限的bash / python脚本(适用于Linux和OS X系统):

#!/bin/bash

if [ -z "$1" ]; then
    echo "Specify executable"
    exit 1
fi

EXE=$1

available(){
    which $1 >/dev/null 2>&1
}

platform=`uname`

if [ "$platform" == "Darwin" ]; then
    MESSAGE="Please run $1 as root with sudo or install osascript (should be installed by default)"
else
    MESSAGE="Please run $1 as root with sudo or install gksu / kdesudo!"
fi

if [ `whoami` != "root" ]; then

    if [ "$platform" == "Darwin" ]; then
        # Apple
        if available osascript
        then
            SUDO=`which osascript`
        fi

    else # assume Linux
        # choose either gksudo or kdesudo
        # if both are avilable check whoch desktop is running
        if available gksudo
        then
            SUDO=`which gksudo`
        fi
        if available kdesudo
        then
            SUDO=`which kdesudo`
        fi
        if ( available gksudo && available kdesudo )
        then
            if [ $XDG_CURRENT_DESKTOP = "KDE" ]; then
                SUDO=`which kdesudo`;
            else
                SUDO=`which gksudo`
            fi
        fi

        # prefer polkit if available
        if available pkexec
        then
           SUDO=`which pkexec`
        fi

    fi

    if [ -z $SUDO ]; then
        if available zenity; then
            zenity --info --text "$MESSAGE"
            exit 0
        elif available notify-send; then
            notify-send "$MESSAGE"
            exit 0
        elif available xmessage notify-send; then
            xmessage -buttons Ok:0 "$MESSAGE"
            exit 0
        else
            echo "$MESSAGE"
        fi
    fi

fi

if [ "$platform" == "Darwin" ]
then
    $SUDO -e "do shell script \"$*\" with administrator privileges"
else
    $SUDO $@
fi

基本上,我设置系统的方式是将子文件夹保存在bin目录中(例如/ usr / local / bin中的/ usr / local / bin / pyscripts),并创建指向可执行文件的符号链接。这对我有三个好处:

(1)如果我有不同的版本,我可以通过更改符号链接轻松切换执行哪个版本,并使bin目录保持清洁(例如/usr/local/bin/gcc-versions/4.9/,/ usr /local/bin/gcc-versions/4.8/,/ usr / local / bin / gcc - > gcc-versions / 4.8 / gcc)

(2)我可以使用它们的扩展名存储脚本(有助于IDE中的语法高亮显示),但可执行文件不包含它们,因为我喜欢它(例如svn-tools - > pyscripts / svn-tools的.py)

(3)我将在下面展示的原因:

我将脚本命名为“run-as-root-wrapper”并将其放在一个非常常见的路径中(例如/ usr / local / bin),因此python不需要任何特殊的东西来定位它。然后我有以下run_command.py模块:

import os
import sys
from distutils.spawn import find_executable

#===========================================================================#

def wrap_to_run_as_root(exe_install_path, true_command, expand_path = True):
    run_as_root_path = find_executable("run-as-root-wrapper")

    if(not run_as_root_path):
        return False
    else:
        if(os.path.exists(exe_install_path)):
            os.unlink(exe_install_path)

        if(expand_path):
            true_command = os.path.realpath(true_command)
            true_command = os.path.abspath(true_command)
            true_command = os.path.normpath(true_command)

        f = open(exe_install_path, 'w')
        f.write("#!/bin/bash\n\n")
        f.write(run_as_root_path + " " + true_command + " $@\n\n")
        f.close()
        os.chmod(exe_install_path, 0755)

        return True

在我的实际python脚本中,我有以下功能:

def install_cmd(args):
    exe_install_path = os.path.join(args.prefix, 
                                    os.path.join("bin", args.name))

    if(not run_command.wrap_to_run_as_root(exe_install_path, sys.argv[0])):
        os.symlink(os.path.realpath(sys.argv[0]), exe_install_path)

因此,如果我有一个名为TrackingBlocker.py的脚本(我用来修改/ etc / hosts文件以将已知跟踪域重新路由到127.0.0.1的实际脚本),当我调用“sudo / usr / local / bin”时/pyscripts/TrackingBlocker.py --prefix / usr / local --name ModifyTrackingBlocker install“(通过argparse模块处理的参数),它安装”/ usr / local / bin / ModifyTrackingBlocker“,这是一个执行的bash脚本

/usr/local/bin/run-as-root-wrapper /usr/local/bin/pyscripts/TrackingBlocker.py [args]

e.g。

ModifyTrackingBlocker add tracker.ads.com

执行:

/usr/local/bin/run-as-root-wrapper /usr/local/bin/pyscripts/TrackingBlocker.py add tracker.ads.com

然后显示获取添加权限所需的身份验证对话框:

127.0.0.1  tracker.ads.com

到我的hosts文件(只能由超级用户写入)。

如果您想简化/修改它以仅以root身份运行某些命令,您只需将其添加到您的脚本中(使用上面提到的必要导入+ import subprocess):

def run_as_root(command, args, expand_path = True):
    run_as_root_path = find_executable("run-as-root-wrapper")

    if(not run_as_root_path):
        return 1
    else:
        if(expand_path):
            command = os.path.realpath(command)
            command = os.path.abspath(command)
            command = os.path.normpath(command)

        cmd = []
        cmd.append(run_as_root_path)
        cmd.append(command)
        cmd.extend(args)

        return subprocess.call(' '.join(cmd), shell=True)

使用上面的(在run_command模块中):

>>> ret = run_command.run_as_root("/usr/local/bin/pyscripts/TrackingBlocker.py", ["status", "display"])
>>> /etc/hosts is blocking approximately 16147 domains

答案 2 :(得分:1)

我在Mac OS X上遇到同样的问题。我有一个可行的解决方案,但它并不是最佳的。我将在这里解释我的解决方案并继续寻找更好的解决方案。

在程序开始时,我通过执行

检查我是否是root
def _elevate():
    """Elevate user permissions if needed"""
    if platform.system() == 'Darwin':
        try:
            os.setuid(0)
        except OSError:
            _mac_elevate()
如果我还不是root用户,那么

os.setuid(0)将会失败,并且会触发_mac_elevate(),它会在管理员的帮助下从osascript开始重新启动我的程序。 osascript可用于执行applescript和其他东西。我这样用它:

def _mac_elevate():
    """Relaunch asking for root privileges."""
    print "Relaunching with root permissions"
    applescript = ('do shell script "./my_program" '
                   'with administrator privileges')
    exit_code = subprocess.call(['osascript', '-e', applescript])
    sys.exit(exit_code)

这个问题是如果我使用subprocess.call,我保持当前进程运行,我的应用程序运行的两个实例将提供两个停靠图标。如果我改为使用subprocess.Popen并让非特权进程立即死亡,我就无法使用退出代码,也无法获取stdout / stderr流并从原始进程传播到终端。