Pythonで学ぶ「詳解 UNIXプログラミング」(その16) 第19章 擬似端末
19.1 〜 19.3
Pythonのpty.fork()
でptyのオープンとforkを行うことができる。pty.fork()
はこのサンプルと同等の機能が実装されており、Linux/BSD/Solaris/Cygwin等の各プラットフォーム対応のコードが入っている。ただし、子プロセス側でのtermio
とウィンドウサイズの設定は行われない。
19.4 ptyプログラムのmain関数
import os, sys, atexit, termios, pty, socket from optparse import OptionParser STDIN_FILENO = 0 STDOUT_FILENO = 1 def main(): parser = OptionParser() parser.add_option("-d", dest="driver", help="driver for stdin/stdout") parser.add_option("-e", dest="noecho", action="store_true", help="necho for slave pty's line discipline") parser.add_option("-i", dest="ignoreeof", action="store_true", help="ignore EOF on standard input") parser.add_option("-n", dest="interactive", action="store_false", default=os.isatty(STDIN_FILENO), help="not interactive") (options, args) = parser.parse_args() pid, fdm = pty.fork() if not pid: # child if options.noecho: set_noecho(STDIN_FILENO) # stdin is slave pty os.execvp(args[0], args) # parent if options.interactive and not options.driver: tty_raw(STDIN_FILENO) # user's tty to raw mode atexit.register(tty_exit) # reset user's tty on exit if options.driver: do_driver(options.driver) # changes our stdin/stdout loop(fdm, options.ignoreeof) # copies stdin -> ptym, ptym -> stdout def set_noecho(fd): # turn echo off sterm = termios.tcgetattr(fd) sterm[2] &= ~(termios.ECHO | termios.ECHOE | termios.ECHOK | termios.ECHONL) sterm[1] &= ~(termios.ONLCR) # also turn off NL to CR/NL mapping on output termios.tcsetattr(fd, termios.TCSANOW, sterm) if __name__ == '__main__': main()
19.5 loop関数
BUFFSIZE = 512 sigcaught = 0 def loop(ptym, ignoreeof): child = os.fork() if child == 0: # child # child copies stdin to ptym while True: try: buff = os.read(STDIN_FILENO, BUFFSIZE) except OSError: break if not buff: # EOF on stdin means we're done break n = os.write(ptym, buff) # We always terminate when we encounter an EOF on stdin, # but we anly notify the parent if ignoreeof is 0 if not ignoreeof: os.kill(os.getppid(), signal.SIGTERM) # notify parent # parent copies ptym to stdout signal.signal(signal.SIGTERM, sig_term) while True: try: buff = os.read(ptym, BUFFSIZE) except OSError, e: break if not buff: break os.write(STDOUT_FILENO, buff) # There are three ways to get here: sig_term() below caught the # SIGTERM from the child, we read an EOF on the pty master (which # means we have to signal the child to stop), or an error. if not sigcaught: os.kill(child, signal.SIGTERM) # The child sends us a SIGTERM when it receives an EOF on # the pty slave or encounter a read() error. def sig_term(signo, frame): global sigcaught sigcaught = 1
19.6 ptyプログラム用のdo_driver関数
def do_driver(driver): # create a stream pipe to communicate with the driver pipe = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) child = os.fork() if child == 0: # child pipe[1].close() os.dup2(pipe[0].fileno(), 0) # stdin for driver os.dup2(pipe[0].fileno(), 1) # stdout for driver pipe[0].close() os.execlp(driver, driver) pipe[0].close() os.dup2(pipe[1].fileno(), 0) os.dup2(pipe[1].fileno(), 1) pipe[1].close() # Parent returns, but with stdin and stdout connected # to the driver.