【问题标题】:Get root dialog in Python on Mac OS X, Windows?在 Mac OS X、Windows 上使用 Python 获取根对话框?
【发布时间】:2011-08-10 11:02:24
【问题描述】:

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

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

【问题讨论】:

  • 不确定是否可以通过 OSX 中的 python 脚本来完成。在某些类 Unix 操作系统(包括 OS X)下,脚本(如 shell、perl、python 等)无法被授予调用 chown/chgrp 的权限,从而为您提供其他权限。

标签: python windows macos wxpython root


【解决方案1】:

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

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

您还可以通过 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 上完成什么,我可能会提供更具体的帮助。

【讨论】:

    【解决方案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/pyscripts 在 /usr/local/bin 中),并创建指向可执行文件的符号链接。这对我来说有三个好处:

    (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]
    

    例如

    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
    

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

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

    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
    

    【讨论】:

      【解决方案3】:

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

      在程序开始时,我通过执行检查我是否是root用户

      def _elevate():
          """Elevate user permissions if needed"""
          if platform.system() == 'Darwin':
              try:
                  os.setuid(0)
              except OSError:
                  _mac_elevate()
      

      os.setuid(0) 如果我还不是 root 将失败,这将触发 _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 流并传播到启动原始进程的终端。

      【讨论】:

        猜你喜欢
        • 2019-08-24
        • 1970-01-01
        • 2011-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-30
        相关资源
        最近更新 更多