Поймать и игнорировать сигнал - PullRequest
3 голосов
/ 14 июня 2011

Я пытаюсь написать программу, которая будет запускать программу на удаленном компьютере, используя ssh, и передавать ей сигналы SIGINT, не разрушая мое соединение ssh. Если бы я работал на терминале, это было бы легко с чем-то вроде

ssh -t -t host "command to run"

Я пробовал это с помощью следующего скрипта:

#!/bin/bash

ssh -t -t host "program $@"

и когда я запускаю его из терминала, он работает нормально, но когда корректор запускает этот скрипт и отправляет его SIGINT, он просто завершает работу программы (я думаю, он не может выделить терминал?).

У меня следующий беспорядок кода:

CTRL_C = "<CTRL-C>"

def endpoint(proc, handler):
    signal.signal(2, signal.SIG_IGN)

    inp = ""
    out = ""
    err = ""

    inp_from_fd = sys.stdin.fileno()
    inp_to_fd   = proc.stdin.fileno()
    out_from_fd = proc.stdout.fileno()
    out_to_fd   = sys.stdout.fileno()
    err_from_fd = proc.stderr.fileno()
    err_to_fd   = sys.stderr.fileno()

    while True:
       try:
           ins,outs,_ = select.select([inp_from_fd, out_from_fd, err_from_fd],
                                      [inp_to_fd, out_to_fd, err_to_fd],
                                      [])
           for fd in ins:
               if fd == inp_from_fd:
                   k   = os.read(fd, 1024)
                   if k == "":
                       os.close(proc.stdin.fileno())
                       return
                   inp = inp + k
               elif fd == out_from_fd:
                   k   = os.read(fd, 1024)
                   out = out + k
               elif fd == err_from_fd:
                   k   = os.read(fd, 1024)
                   err = err + k
               else:
                   assert False
           for fd in outs:
               if fd == inp_to_fd:
                   while CTRL_C in inp:
                       proc.send_signal(2)
                       p = inp.find(CTRL_C)
                       inp = inp[0:p] + inp[p+len(CTRL_C):]
                   k = os.write(fd, inp)
                   inp = inp[k:]
               elif fd == out_to_fd:
                   k = os.write(fd, out)
                   out = out[k:]
               elif fd == err_to_fd:
                   k = os.write(fd, err)
                   err = err[k:]
               else:
                   assert False
       except select.error:
           pass
       except KeyboardInterrupt:
           handler()
       except IOError, e:
           pass

def usage(args):
    print "must specify --client or --server" 

if __name__ == '__main__':
    if len(sys.argv) == 1:
        usage(sys.argv)
    elif sys.argv[1] == '--server':
        proc = subprocess.Popen(sys.argv[2:],
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        def INT():
            proc.stdin.write(CTRL_C)
            proc.stdin.flush()
        endpoint(proc, INT)
    elif '--client' in sys.argv:
        proc = subprocess.Popen(sys.argv[2:],
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        import time
        time.sleep(1)
        def INT():
            pass
        endpoint(proc, INT)
    else:
        usage(sys.argv)

который я вызываю, используя что-то вроде:

remote.py --client ssh -t -t host "remote.py --server <program-to-run> <args>"

Есть ли что-то, что я делаю здесь неправильно, чтобы обработать сигнал? Я попытался поместить отпечаток в обработчик сигнала 2, и он печатает его, но он также убивает ssh (на консоли выводится сообщение «Убито сигналом 2»). Питон передает сигнал своим детям? Есть ли способ обойти это? Есть ли более простой способ сделать это?

Спасибо за любые указатели.

1 Ответ

1 голос
/ 15 июля 2011

os.setpgrp (смотрите man-страницу setpgrp), в противном случае сигнал распространяется на детей

...