2

I start socat on a terminal by executing socat - UNIX-listen:/tmp/sock

Then I go to another terminal and start a program (by python) in such a way, that it appears to run in the first terminal, here is an example:

s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect("/tmp/sock")
proc=subprocess.Popen(['prg'], stdin=s, stdout=s, stderr=s, shell=False)
proc.wait()

Programs with simple input/output like "cat" for example are working fine, but more complex ones (like "vim" or "bash") are not very happy, obviously. Beside of that, when I hit Cntrl-C on the first terminal, the socat is killed, while I would prefer the keyboard interrupt to be delivered to the prg program (started in the python code above).

My question is: if it's possible and how to tell it to socat to behave like the prg is the terminal owner and not socat itself.

PS. It might appear that this question is a duplicate of this one, but it's not the case, because in that question the prg is called directly by socat, so it is possible to use EXEC options.

2

Signals nor tty ownership cannot be 'magically' carried over a socket.

Over AF_UNIX, you could send the tty file descriptor itself (using sendmsg and recvmsg) and have the receiving side use it (and not the socket) as the subprocess' stdout/stderr. This cannot be done with plain socat, but this method is used internally by e.g. the tmux tool.


With plain socat, the most you can do is make it send a literal 0x03 byte (^C) when Ctrl+C is pressed (that's what telnet and ssh do). You can test this by pressing Ctrl+VCtrl+C to send it once, or by running stty raw before socat to enter "raw mode" permanently.

That won't be enough to make the receiving end actually understand the keypress, though. The most basic method would be to check each byte manually – send a SIGINT when it sees 0x03; write it to the subprocess' stdin otherwise.

What telnetd and sshd do, however, is add a 'pseudo-tty' in between the socket and the subprocess – in Python you can create one using os.openpty(); the 'slave' side needs to be given as the subprocess' stdout/stderr, and your script will need to run in a loop copying data between the socket and the 'master' side.

That's still not quite enough – a full-featured terminal login protocol, such as SSH or Telnet, would also have messages to update the pty's dimensions, and react to termios setting changes (e.g. the "local echo" setting and others seen in stty).

It would be useful to read the source code of the telnet client & telnetd server, e.g. from GNU inetutils, as they do exactly that.

  • Thank you for the answer! Alas it's not so easy as I hoped to. May it be that my problem will be easier to solve if I formulate it in a less general style? What I want to achieve is this: I have a "main" python program, which opens a new window in a given tmux session and start another program "xxx" in this new window, but the main python process must have full control over it (so "xxx" must be a "subprocess" instance or similar). So first I ask libtmux to open a new window with window_shell="socat - UNIX-listen:/tmp/sock" and then I connect as described in the question above. Will this help? – ilya Jul 13 '16 at 15:59
  • Could you ask libtmux to just give you the window's pty name or fd directly? – grawity Jul 14 '16 at 4:46
  • Yes, after I created a tmux window, I can see in window.panes[0]['pane_tty'] a pseudo-tty device name, for example /dev/pts/57 – ilya Jul 14 '16 at 8:43
  • Could you please elaborate your answer a bit: after I call (master, slave)=os.openpty() and then call subprocess.Popen(stdout=slave, stderr=slave....) what should I give as stdin parameter? Is it a good idea to open /dev/pts/XXX directly and give the descriptor as stdin to subprocess.Popen(...)? And the second question: what do you mean by copying data between the socket and the 'master' side? From where exactly should I read and where to write? – ilya Jul 14 '16 at 10:29
  • The pty slave is used both as input and output for the subprocess. Likewise, the pty master needs to be copied from and to the socket. (You might need select.poll() or a similar eventloop.) – grawity Jul 14 '16 at 11:29

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.