_fork_pty.py 2.3 KB

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