title: How to connect INET socket to PTY device in Python
body: <p>I am trying to duplicate the behavior of this <code>socat</code> command in Python:</p>
  
  <pre><code>socat PTY,link=/tmp/mypty tcp-listen:1234
  </code></pre>
  
  <p>A TCP connection to the server port 1234 will be connected to /tmp/mypty (a link to /dev/pts/N), and running <code>screen /tmp/mypty</code> will allow a user to interact with whatever is on the other end of that connection.  In other words, if this is used to connect to that listening port:</p>
  
  <pre><code>socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp-connect:172.16.30.3:1234
  </code></pre>
  
  <p>Then running <code>screen /tmp/mypty</code> will connect the user to the instance of <code>bash</code> running on the other end of the TCP connection.</p>
  
  <p>Getting the TCP socket is straightforward.  And I can get a PTY using os.openpty(), which yields me a pair of file descriptors, which are just integers.</p>
  
  <p>What I am unable to do is hook that socket and those file descriptors in a way that will seamlessly pass data back and forth.  If they were both sockets, it would be trivial; use a loop with select() to pass new <em>read</em> data to the other socket's <em>write</em>.</p>
  
  <p>What I have tried:</p>
  
  <ol>
  <li>Using socket.fromfd to build a socket using one of the PTY file descriptors.  This results in <code>OSError: [Errno 88] Socket operation on non-socket</code>.  Example of this code, where clisock is a valid socket:</li>
  </ol>
  
  <pre>
  
      def sockToPty(clisock,addr):
          (master, slave) = os.openpty()
          print('PTY: Opening {}'.format(os.ttyname(slave)))
          ptymsock = socket.fromfd(master, socket.AF_UNIX, socket.SOCK_STREAM)
          ptyssock = socket.fromfd(slave,  socket.AF_UNIX, socket.SOCK_STREAM)
          print('CLISOCK: {}'.format(clisock))
          print('PTYMSOCK: {}'.format(ptymsock))
          print('PTYSSOCK: {}'.format(ptyssock))
          peer[clisock] = ptymsock
          peer[ptyssock] = clisock
          while True:
              iready, oready, eready = select.select([clisock, ptyssock], [], [])
  
              for sock in iready:
                  data = sock.recv(4096)
                  if not len(data):
                      return
                  peer[sock].send(data)
  
  </pre>
  
  <p>which results in this error:</p>
  
  <pre>
  
      PTY: Opening /dev/pts/2
      CLISOCK: 
      PTYMSOCK: 
      PTYSSOCK: 
      Exception in thread Thread-1:
      Traceback (most recent call last):
        File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
          self.run()
        File "/usr/lib/python3.6/threading.py", line 864, in run
          self._target(*self._args, **self._kwargs)
        File "./tcpmux.py", line 62, in sockToPty
          peer[sock].send(data)
      OSError: [Errno 88] Socket operation on non-socket
  
  </pre>
  
  <p>and it makes no difference if I use AF_INET instead of AF_UNIX.</p>
  
  <ol start="2">
  <li>Using os.dup2() to map the SSLsocket's fileno() to stdin, stdout, and stderr and then running os.openpty() (a la <a href="https://github.com/infodox/python-pty-shells/blob/master/tcp_pty_bind.py" rel="nofollow noreferrer">tcp_pty_bind.py</a>).  This does not generate any errors, but there's no apparent data flow when I open the PTY using <code>screen</code>.</li>
  </ol>
  
  <p>Almost all of the Python PTY examples out there are focused on pty.spawn(), making it hard to find any example code of this (admittedly obscure) use of PTY.</p>
  
  <p>Side note: I'm using os.openpty instead of pty.openpty simply because there doesn't seem to be a point.  The pty module seems to be advertised for more portability, but not to have much more portability, and I'm working on Linux so portability isn't an issue.  And since I'm getting valid file descriptors to a correct os.ttyname(), I've no reason to question that openpty() isn't working properly.</p>