pty difficulties

J

Justin Dubs

I'm writing a utility that runs a program inside of a pseudo-tty and
then multiplexes both stdin and a fifo to the child's pty via a select
loop.

In other words, you run my program, tell it the name of the program to
run and the name of the fifo to use and then you can interact with the
program as per usual. However, you can also send data into the fifo
and the program will see it as input just as if you'd typed it on the
keyboard.

As a test I'm running sbcl (a lisp interpreter) through my python
program. It works like a charm in normal usage. However, if I dump
more than 2K or so of code into the FIFO, sbcl gets the first K fine,
but then seems to miss a chunk, get another character or two and then
freeze. After that I am unable to interact with it via either stdin
or the fifo even though the select loop is still running. The pty
just seems to be dead, eating input and giving nothing back. So, I
tried running a different Lisp interpreter (openmcl) instead, but the
problem persisted. So, I just ran vim through it and it handled the
2K of text just fine.

However, vim is just sending the text into a buffer where-as sbcl is
parsing and compiling it. If I add a sleep(1.0) after data is sent
from the fifo to the pty then the problem goes away. So, it seems
like if the child process can't pull things out of it's stdin as fast
as I'm putting it in, something goes awry.

Has anyone seen similar behavior? Does anyone have any idea why this
would be happening?

Any help would be greatly appreciated. Thanks everyone. The code is
below.

Justin Dubs


Here's the code:

from time import sleep
from pty import spawn, fork
from sys import stdin, stdout, stderr, exit, argv
from os import fdopen, open, read, write, O_RDONLY, O_NONBLOCK
from select import select
from termios import tcgetattr, tcsetattr, tcdrain, ECHO, TCSADRAIN
from tty import setraw

if (len(argv) != 3):
print "usage: vee.py program pipe"
exit(0)

pid, childID = fork()

if pid == 0:
spawn(argv[1])
else:
fifo = fdopen(open(argv[2], O_RDONLY | O_NONBLOCK), 'r')
child = fdopen(childID, 'r+')

attribs = tcgetattr(stdin)
attribs[3] = attribs[3] & ~ECHO
tcsetattr(stdin, TCSADRAIN, attribs)

setraw(stdin)

connections = { child : stdout, stdin : child, fifo : child }

try:
while True:
readable = select(connections.keys(), [], [])[0]
for f in readable:
data = read(f.fileno(), 1024)
connections[f].write(data)
connections[f].flush()
except (OSError, IOError):
exit(0)
 
J

Josiah Carlson

while True:
readable = select(connections.keys(), [], [])[0]
for f in readable:
data = read(f.fileno(), 1024)
connections[f].write(data)
connections[f].flush()

I believe your problem exists in the write and flush. All you seem to
be doing is checking to see if your reading file handles are capable of
reading, you never check to see if you can write to anything. Your lisp
interpreter showcases the fact that it is not ready to get a write while
it is interpreting, by failing. I believe the following should fix you up:

while True:
readable = select(connections.keys(), [], [])[0]
for f in readable:
if select([], [connections[f]], [], 0)[1]:
data = read(f.fileno(), 1024)
connections[f].write(data)
connections[f].flush()

- Josiah
 
J

Justin Dubs

Josiah Carlson said:
while True:
readable = select(connections.keys(), [], [])[0]
for f in readable:
data = read(f.fileno(), 1024)
connections[f].write(data)
connections[f].flush()

I believe your problem exists in the write and flush. All you seem to
be doing is checking to see if your reading file handles are capable of
reading, you never check to see if you can write to anything. Your lisp
interpreter showcases the fact that it is not ready to get a write while
it is interpreting, by failing. I believe the following should fix you up:

while True:
readable = select(connections.keys(), [], [])[0]
for f in readable:
if select([], [connections[f]], [], 0)[1]:
data = read(f.fileno(), 1024)
connections[f].write(data)
connections[f].flush()

Thanks for the suggestions, but this didn't help. I should have
mentioned that I had tried this before. Here's the code I had used:

while True:
readable, writeable, ignore = select(connections.keys(),
connections.values(), [])
for f in readable:
if connections[f] in writeable:
data = read(f.fileno(), 1024)
connections[f].write(data)
connections[f].flush()

I don't think checking for writeable status /should/ help because the
write call I am using blocks. So, if the stream isn't writeable, it
should just block until it is. I think.

Justin Dubs
 
J

Josiah Carlson

I don't think checking for writeable status /should/ help because the
write call I am using blocks. So, if the stream isn't writeable, it
should just block until it is. I think.

Hrm. Perhaps you should just leave the sleep in there. One second
seems a bit much, but maybe .01 or .001 seconds is sufficient. You
could reasonably still get 100K-1M/second. One would hope that would be
enough for most tasks.

- Josiah
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top