如何为我在pty下运行的进程设置终端前台进程组?

时间:2013-03-04 11:40:02

标签: python posix subprocess pty posix-api

我编写了一个简单的包装脚本,用于在命令retry.py失败时重复命令。但是,由于我想看到子命令的输出,我不得不拉一些pty技巧。这适用于像rsync这样的程序,但是像scp这样的其他人会应用额外的测试来显示像进度表这样的东西。

scp代码有一个广泛的测试:

getpgrp() == tcgetpgrp(STDOUT_FILENO);

当我运行包装器脚本时失败了。正如您在我的简单tty_test.c测试用例中看到的那样:

./tty_tests
isatty reports 1
pgrps are 13619 and 13619

./retry.py -v -- ./tty_tests
command is ['./tty_tests']
isatty reports 1
pgrps are 13614 and -1
child finished: rc = 0
Ran command 1 times

我尝试过使用tcsetpgrp(),它最终会作为pty fd上的IOCTL,但这会导致ptys的-EINVAL。如果可能的话,我宁愿继续使用Python子进程机器,还是需要手动fork / execve'ing呢?

1 个答案:

答案 0 :(得分:9)

如果您不需要为子流程提供全新的pty,我相信您可以将您的程序削减到此:

from argparse import ArgumentParser
import os
import signal
import subprocess
import itertools

# your argumentparser stuff goes here

def become_tty_fg():
    os.setpgrp()
    hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN)
    tty = os.open('/dev/tty', os.O_RDWR)
    os.tcsetpgrp(tty, os.getpgrp())
    signal.signal(signal.SIGTTOU, hdlr)

if __name__ == "__main__":
    args = parser.parse_args()

    if args.verbose: print "command is %s" % (args.command)
    if args.invert and args.limit==None:
        sys.exit("You must define a limit if you have inverted the return code test")

    for run_count in itertools.count():
        return_code = subprocess.call(args.command, close_fds=True,
                                      preexec_fn=become_tty_fg)
        if args.test == True: break
        if run_count >= args.limit: break
        if args.invert and return_code != 0: break
        elif not args.invert and return_code == 0: break

    print "Ran command %d times" % (run_count)

setpgrp()调用在同一会话中创建一个新进程组,以便新进程将从用户接收任何ctrl-c / ctrl-z / etc,而您的重试脚本则不会。然后tcsetpgrp()使新进程组成为控制tty的前台进程组。当发生这种情况时,新进程会获得SIGTTOU(因为setpgrp(),因为它已经在后台进程组中),这通常会使进程停止,因此这是忽略{{1}的原因}}。我们将SIGTTOU处理程序设置回原来的状态,以最大限度地减少子进程被意外信号表混淆的可能性。

由于子进程现在位于tty的前台组中,因此其tcgetpgrp()和getpgrp()将相同,并且isatty(1)将为true(假设它从retry.py继承的stdout实际上是TTY)。您不需要在子流程和tty之间代理流量,这样您就可以放弃所有SIGTTOU事件处理和fcntl-nonblocking-setting。