2013-03-04 3 views
6

Я написал простой скрипт-оболочку для повторения команд, когда они не вызвали вызов retry.py. Однако, поскольку я хочу видеть вывод дочерней команды, мне пришлось вытащить некоторые трюки. Это работает нормально для таких программ, как rsync, но другие, такие как scp, применяют дополнительный тест для показа таких вещей, как их счетчик хода.Как настроить группу процессов переднего плана терминала для процесса, который я запускаю под pty?

Код УПП имеет тест, который широко:

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(), которая заканчивается как IOCTL на PTY Fd, но что приводит к -EINVAL для ptys. Я бы предпочел продолжать использовать подпроцесс Python, если это вообще возможно, или для этого требуется вручную fork/execve'ing?

ответ

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() оно было в фоновом процессе), что обычно останавливает процесс, поэтому в этом причина игнорирования SIGTTOU. Мы установили обработчик SIGTTOU обратно в прежнее состояние, чтобы свести к минимуму вероятность того, что подпроцесс запутался в неожиданной таблице сигналов.

Поскольку подпроцесс теперь находится в группе переднего плана для tty, его tcgetpgrp() и getpgrp() будут одинаковыми, а isatty (1) будет истинным (если предположить, что stdout, который он наследует от retry.py, на самом деле a tty). Вам не нужно прокси-трафик между подпроцессом и tty, который позволяет вам вырезать всю обработку событий select и настройку fcntl-неблокирования.

+0

Я дал эту попытку, и это не имеет никакого эффекта: > retry.py -v - ~/mysrc/retry.git/tty_tests command is ['/home/ajb/mysrc/retry.git/tty_tests '] isatty reports 1 pgrps are 28268 и -1 ребенок закончил: rc = 0 Ran command 1 раз – stsquad

+0

Не могли бы вы вставить код? –

+0

OH! Я только заметил, что вы дали ссылку на retry.py в своем вопросе. Я думал, что это просто stackoverflow, пытающийся быть полезным и создающий ссылку из чего-то похожего на имя хоста. Я взгляну. –