1"""Substitute for the forkpty system call, to support Solaris. 2""" 3import os 4import errno 5 6from pty import (STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, CHILD) 7from .util import PtyProcessError 8 9def fork_pty(): 10 '''This implements a substitute for the forkpty system call. This 11 should be more portable than the pty.fork() function. Specifically, 12 this should work on Solaris. 13 14 Modified 10.06.05 by Geoff Marshall: Implemented __fork_pty() method to 15 resolve the issue with Python's pty.fork() not supporting Solaris, 16 particularly ssh. Based on patch to posixmodule.c authored by Noah 17 Spurrier:: 18 19 http://mail.python.org/pipermail/python-dev/2003-May/035281.html 20 21 ''' 22 23 parent_fd, child_fd = os.openpty() 24 if parent_fd < 0 or child_fd < 0: 25 raise OSError("os.openpty() failed") 26 27 pid = os.fork() 28 if pid == CHILD: 29 # Child. 30 os.close(parent_fd) 31 pty_make_controlling_tty(child_fd) 32 33 os.dup2(child_fd, STDIN_FILENO) 34 os.dup2(child_fd, STDOUT_FILENO) 35 os.dup2(child_fd, STDERR_FILENO) 36 37 else: 38 # Parent. 39 os.close(child_fd) 40 41 return pid, parent_fd 42 43def pty_make_controlling_tty(tty_fd): 44 '''This makes the pseudo-terminal the controlling tty. This should be 45 more portable than the pty.fork() function. Specifically, this should 46 work on Solaris. ''' 47 48 child_name = os.ttyname(tty_fd) 49 50 # Disconnect from controlling tty, if any. Raises OSError of ENXIO 51 # if there was no controlling tty to begin with, such as when 52 # executed by a cron(1) job. 53 try: 54 fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) 55 os.close(fd) 56 except OSError as err: 57 if err.errno != errno.ENXIO: 58 raise 59 60 os.setsid() 61 62 # Verify we are disconnected from controlling tty by attempting to open 63 # it again. We expect that OSError of ENXIO should always be raised. 64 try: 65 fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) 66 os.close(fd) 67 raise PtyProcessError("OSError of errno.ENXIO should be raised.") 68 except OSError as err: 69 if err.errno != errno.ENXIO: 70 raise 71 72 # Verify we can open child pty. 73 fd = os.open(child_name, os.O_RDWR) 74 os.close(fd) 75 76 # Verify we now have a controlling tty. 77 fd = os.open("/dev/tty", os.O_WRONLY) 78 os.close(fd) 79