1

I'm trying to create a simulated TTY connected target device that I can connect to via i.e. minicom. I'm using /dev/ptmx to create a pty and print out the slave name to be opened by i.e. minicom: Please connect to: /dev/pts/4. On the python side I then use os.read and os.write to do io and simulate my target:

import os, re, termios                                                                                                                                                                                                                                                         
from ctypes import *
class dev():
    def __init__(self):
        pass  
    def createpty(self):                                                                                                                                                                                                                                                       

        self.fd3 = os.open("/dev/ptmx", os.O_RDWR | os.O_NONBLOCK);                                                                                                                                                                                                            
        if self.fd3 < 0:                                                                                                                                                                                                                                                       
            print("Couldn't open output /dev/ptmx\n")                                                                                                                                                                                                                          
        libc = cdll.LoadLibrary("libc.so.6")                                                                                                                                                                                                                                   
        libc.grantpt(self.fd3);                                                                                                                                                                                                                                                
        libc.unlockpt(self.fd3);                                                                                                                                                                                                                                               
        libc.ptsname.restype = c_char_p                                                                                                                                                                                                                                        
        self.slave = libc.ptsname(self.fd3)                                                                                                                                                                                                                                    
        print("Please connect to:" + self.slave);                                                                                                                                                                                                                                                    

        self.old = termios.tcgetattr(self.fd3)                                                                                                                                                                                                                                 
        n = termios.tcgetattr(self.fd3)                                                                                                                                                                                                                                        

        n[3] = n[3] & ~(termios.ECHO|termios.ICANON) # c_lflag                                                                                                                                                                                                                 
        n[3] = n[3] & 0                                                                                                                                                                                                                                                        
        n[4+1] = n[4+1] & 0xffff0000;                                                                                                                                                                                                                                          

        termios.tcsetattr(self.fd3, termios.TCSANOW, n)           

The whole process is quite combersome. I would like to use select on the python side which forces my to use ctypes again. I cannot wrap the self.fd3 into a file using os.fdopen because I need to prevent close being called on self.fd3.

So I have two questions:

  • Does anyone know a readymade python library that handles pty creation and manipulation when implementing the pty master side in python?
  • If not: Is there a example somewhere that describes howto call libc.select via ctypes?
Konrad Eisele
  • 3,088
  • 20
  • 35
  • Side note: check https://stackoverflow.com/questions/49878901/how-does-ctypes-cdll-loadlibrarynone-work/49880922#49880922.. Also where are you using *select*? Are you talking about https://docs.python.org/3/library/select.html? – CristiFati Sep 14 '18 at 21:04
  • Im trying to call luv select via ctypes – Konrad Eisele Sep 14 '18 at 21:06
  • 1
    Where? And I'd recommend using the *Python* module (they both work with *fd*s) to avoid writing the *Python* *C* conversion code. – CristiFati Sep 14 '18 at 21:08
  • I think I tried with fd and that didn’t work but I‘ll try again. Thanks for the tip. – Konrad Eisele Sep 14 '18 at 21:10
  • @CristiFati : Tested it again and, yes actually pythons select works on self.fd3. So no need for ctypes select – Konrad Eisele Sep 15 '18 at 06:17
  • That's true. Also maybe other functions are exported by *Python* modules as well? Don't forget about `libc = cdll.LoadLibrary(None)` – CristiFati Sep 15 '18 at 07:45

1 Answers1

0

As @CristiFati stated Python's select can be used for FDs, however for the sake of completeness below is my dirty hack for using ctype's select. This implements an tty echo server. Connect to the pty slave that is printed out, i.e. minicom -D /dev/pty/4

import os, re, termios
import threading, os, re, sys, select, math, binascii, errno, time
from ctypes import *
import errno

class pty():
    def __init__(self, n="undef"):
        self.cmds = {}
        self.createpty()
        self.name = n
        self.goon = 1
        self.input = ""
        self.output = []

    def createpty(self):

        self.fd3 = os.open("/dev/ptmx", os.O_RDWR | os.O_NONBLOCK);
        if self.fd3 < 0:
            print("Couldn't open output /dev/ptmx\n")
        libc = cdll.LoadLibrary(None)
        libc.grantpt(self.fd3);
    libc.unlockpt(self.fd3);
        libc.ptsname.restype = c_char_p
        self.slave = libc.ptsname(self.fd3)
        print("Use: " + self.slave);

        self.old = termios.tcgetattr(self.fd3)
        n = termios.tcgetattr(self.fd3)

        n[3] = n[3] & ~(termios.ECHO|termios.ICANON) # c_lflag
        n[3] = n[3] & 0

#define VTIME 5
#define VMIN 6
#struct termios
#  {
#    tcflag_t c_iflag;      /* input mode flags */
#    tcflag_t c_oflag;      /* output mode flags */
#    tcflag_t c_cflag;      /* control mode flags */
#    tcflag_t c_lflag;      /* local mode flags */
#    cc_t c_line;           /* line discipline */
#    cc_t c_cc[NCCS];       /* control characters */
#    speed_t c_ispeed;      /* input speed */
#    speed_t c_ospeed;      /* output speed */
##define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
##define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
#  };

        n[4+1] = n[4+1] & 0xffff0000;
    #ioc.c_cc[VMIN] = 0;  # byte 6 + 7
    #ioc.c_cc[VTIME] = 0;

        termios.tcsetattr(self.fd3, termios.TCSANOW, n)

        #self.io = os.fdopen(self.fd3, "r+");


#       fd3io = fdopen (fd3, "r+");
#       fflush (fd3io);
#       setbuf (fd3io, NULL);
#     }


#typedef long int __fd_mask;
#define __NFDBITS   (8 * (int) sizeof (__fd_mask))
#define __FD_ELT(d) ((d) / __NFDBITS)
#define __FD_MASK(d)    ((__fd_mask) (1UL << ((d) % __NFDBITS)))

__FD_SETSIZE=1024

def fdmask():
    l = __FD_SETSIZE/64
    r = (c_ulong*l)()
    for i in range(l):
        r[i] = 0;
    return r

def fdset(a,b):
    a[b>>6] = a[b>>6] | (1 << (b % 64))

def fdisset(a,b):
    return (a[b>>6] & (1 << (b % 64)))

def setset(b, a):
    for i in a:
        fdset(b, i)

def setget(b, a):
    return [ i for i in a if fdisset(b,i) ]

class timeval(Structure):
    _fields_ = [("tv_sec", c_long), ("tv_usec", c_long)]

def cselect(r,w,x,timeout):

    libc = cdll.LoadLibrary(None)

    ra = fdmask();
    wa = fdmask();
    xa = fdmask();
    m = max(r+w+x)
    setset(ra, r),
    setset(wa, w),
    setset(xa, x),
    tv = timeval()
    tv.tv_sec = int(timeout)
    tv.tv_usec = int(1000000.0 * (timeout%1.0))
    ret = libc.select(m+1, ra, wa, xa, pointer(tv));
    if (ret < 0):
        print ("Error %d" %(ret));
    elif (ret == 0):
        return ([],[],[])
    else:
        return (setget(ra,r),setget(wa,w),setget(xa,x))

def main():
    if sizeof(c_ulong) != 8:
        raise("64bit os expected with long int = 64bit")
    a = pty();
    while True:
        try:
            (r,w,x) = cselect([a.fd3],[],[a.fd3],1)
            if (len(x) > 0):
                time.sleep(0.1);
                print("x");
                continue
            if (len(r) > 0):
                # implement an echo-server:
                v = os.read(a.fd3, 1)
                print (".");
                os.write(a.fd3,v)
        except pty as e:
            print (str(e));
            raise(e)

if __name__ == "__main__":
    main()
Konrad Eisele
  • 3,088
  • 20
  • 35