Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pseudoterminal master reads what it has just written

Tags:

python

pty

I'm working on a project that interfaces "virtual devices" (python processes) that use serial port connections with real devices that also use serial ports, and I'm using pseudoterminals to connect several(more than 2) of these serial-port communications processes (modeling serial devices) together, and I've hit a bit of a snag.

I've got a python process that generates pseudoterminals, symlinks the slave end of the pty to a file (so the processes can create a pyserial object to the filename), while the master ends are kept by my pty generating process and read; when data comes in on one master, the data is logged and then written to the other masters. This approach works if the listening process is always there.

The problem is when the virtual device dies or is never started (which is a valid use case for this project). On my system, it seems, that if data is written to a master end of a pty, if there is nothing listening to the slave end, calling read on that master will return the data that was just written! This means that devices receive the same data more than once -- not good!

Example:

>>master, slave = pty.openpty()
>>os.write(master,"Hello!")
6
>>os.read(master,6)
'Hello!'

I would prefer that the call to read() block until the slave sends data. In fact, this is the behavior of the slave device -- it can write, and then os.read(slave,1) will block until the master writes data.

My "virtual devices" need to be able to pass a filename to open a serial port object; I've attempted to symlink the master end, but that causes my virtual devices to open /dev/ptmx, which creates a new pseudoterminal pair instead of linking back to the slaves that already exist!

Is there any way to change the behavior of the master? Or even just get a filename to the master that corresponds to a slave device (not just /dev/ptmx)?

Thanks in advance!

like image 543
bradreaves Avatar asked May 19 '11 16:05

bradreaves


2 Answers

I'm pretty sure this is because echoing is on by default. To borrow from the Python termios docs, you could do:

master, slave = os.openpty()    # It's preferred to use os.openpty()
old_settings = termios.tcgetattr(master)
new_settings = termios.tcgetattr(master)   # Does this to avoid modifying a reference that also modifies old_settings
new_settings[3] = new_settings[3] & ~termios.ECHO
termios.tcsetattr(master, termios.TCSADRAIN, new_settings)

You can use the following to restore the old settings:

termios.tcsetattr(master, termios.TCSADRAIN, old_settings)
like image 178
John Szakmeister Avatar answered Nov 20 '22 04:11

John Szakmeister


In case someone finds this question, and jszakmeister's answer doesn't work, here is what worked for me.

openpty seems to create pty's in canonical mode with echo turned on. This is not what one might expect. You can change the mode using the tty.setraw function, like in this example of a simple openpty echo server:

master, slave = os.openpty()
tty.setraw(master, termios.TCSANOW)
print("Connect to:", os.ttyname(slave))

while True:
    try:
        data = os.read(master, 10000)
    except OSError:
        break
    if not data:
        break
    os.write(master, data)
like image 31
Vasily G Avatar answered Nov 20 '22 05:11

Vasily G