如何通过子进程模块调用ssh,以便它使用SSH_ASKPASS变量

gyi*_*yim 8 python ssh subprocess

我正在编写一个使用SSH命令的GUI.我尝试使用子进程模块来调用ssh并设置SSH_ASKPASS环境变量,以便我的应用程序可以弹出一个窗口,询问SSH密码.但是我无法使用给定的SSH_ASKPASS命令使ssh读取密码:它总是在终端窗口中提示它,无论我如何设置DISPLAY,SSH_ASKPASS,TERM环境变量或如何管道标准输入/输出.如何确保ssh与当前TTY分离并使用给定程序读取密码?

我的测试代码是:

#!/usr/bin/env python

import os
import subprocess

env = dict(os.environ)
env['DISPLAY'] = ':9999' # Fake value (trying in OS X and Windows)
del env['TERM']
env['SSH_ASKPASS'] = '/opt/local/libexec/git-core/git-gui--askpass'

p = subprocess.Popen(['ssh', '-T', '-v', 'user@myhost.com'],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    env=env
)
p.communicate()
Run Code Online (Sandbox Code Playgroud)

gyi*_*yim 15

SSH仅在进程与TTY分离时才使用SSH_ASKPASS变量(stdin重定向和设置环境变量是不够的).要从控制台分离进程,它应该fork并调用os.setsid().所以我发现的第一个解决方案是:

# Detach process
pid = os.fork()
if pid == 0:
    # Ensure that process is detached from TTY
    os.setsid()

    # call ssh from here
else:
    print "Waiting for ssh (pid %d)" % pid
    os.waitpid(pid, 0)    
    print "Done"
Run Code Online (Sandbox Code Playgroud)

使用子进程模块还有一种优雅的方法:在preexec_fn参数中,我们可以传递在执行外部命令之前在子进程中调用的Python函数.所以问题的解决方案是一个额外的线:

env = {'SSH_ASKPASS':'/path/to/myprog', 'DISPLAY':':9999'}
p = subprocess.Popen(['ssh', '-T', '-v', 'user@myhost.com'],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    env=env,
    preexec_fn=os.setsid
)
Run Code Online (Sandbox Code Playgroud)