1

I'm trying to figure out how I can reliably loop a read on a pt master I have. I open the ptmx, grant and unlock it as per usual:

* ptmx stuff */
/* get the master (ptmx) */
int32_t masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
if(masterfd < 0){
    perror("open");
    exit(EXIT_FAILURE);
};

/* grant access to the slave */
if(grantpt(masterfd) < 0){
    perror("grantpt");
    exit(EXIT_FAILURE);
};

/* unlock the slave */
if(unlockpt(masterfd) < 0){
    perror("unlockpt");
    exit(EXIT_FAILURE);
};

comms_in->ptmx = masterfd;

Next I save the slave's name (yes I know sizeof(char) is always 1)

/* get the path to the slave */
char * slavepathPtr;
char * slavePath;
size_t slavepathLen;
if((slavepathPtr = ptsname(masterfd)) == NULL){
    perror("ptsname");
    exit(EXIT_FAILURE);
}else{
    slavepathLen = strlen(slavepathPtr);
    slavePath = (char *) malloc(sizeof(char) * (slavepathLen + 1));
    strcpy(slavePath, slavepathPtr);
};

I then create a predictably named symlink to the slave (/dev/pts/number) in /dev/custom/predictable (which was provided as an argument to this program using getopts) and verify that its permissions are safe using calls to access, lstat, readlink, symlink and confirm that the program can continue execution, otherwise it calls unlink on the symlink and terminates the thread.

Finally the program ends up in this loop

ssize_t read_result;
ssize_t write_result;
while(1){
    if((read_result = read(comms_in->ptmx, ptmxio_read_buffer, sizeof ptmxio_read_buffer)) <= 0){
        { /** calls thread ender routine */
            pthread_mutex_lock(&COMMS_MUTEX);
            comms_in->thread_statuses[PTMXIO_THREAD] = THREAD_FAILED;
            pthread_mutex_unlock(&COMMS_MUTEX);
            pthread_cond_signal(&SIG_PROGRAM_FINISHED);
            pthread_exit((void *) comms_in);
        }
    }else if((write_result = write(STDOUT_FILENO, ptmxio_read_buffer, read_result)) != read_result){
        {
            /** same as above */
        }
    };
};

On the system, I can run this program and all is swell. The read blocks. When the pts symlink is opened with cu or picocom then bytes are successfully read up to the buffer limits either on my end or the kernel's end, depending on who's lower. The problem comes when the slave is closed. At this point, the read returns -1 -> EIO with error text: Input/output error and will continue to do so, consuming a lot of cpu time if I choose to not terminate the thread and loop. When cu or picocom or even just an echo -en "some text" > /dev/pts/number, the read blocks again, until bytes are available. In the case of the redirection into the symlink, obviously if it fills less than a buffer, read just gets that one buffer and continues to return -1 -> EIO again.

What's going on? I need a method that doesn't consume a lot of CPU as this runs on a slow embedded application processor and allows me to re-establish reads without losing bytes.

I noticed a thread making a call to this: ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) and can't make much sense of what the 3 options are as they're not in my Linux headers anywhere.. Note that 3 is comms_in->ptmx / masterfd.

Here is an lstat on the symlink and some extra information, note that the st_mode is unchanged before and after successful and unsuccessful reads.

‘ptmxio_thread’ failed read (-1) on /dev/pts/13 /dev/pts/13: Input/output error
‘ptmxio_thread’ ptsNum (from ioctl) 13
‘ptmxio_thread’ st_dev: 6, st_ino: 451, st_mode: 0000A1FF, st_nlink: 1
‘ptmxio_thread’ st_uid: 000003E8, st_gid: 000003E8, st_rdev: 0, st_size: 11
‘ptmxio_thread’ st_blksize: 4096, st_blocks: 0, st_atime: 1540963806, st_mtime: 1540963798
‘ptmxio_thread’ st_ctime: 1540963798
3

It's very simple: you should open and keep open a handle to the slave side of the pty in the program handling the master side.

After you got the name with ptsname(3), open(2) it.

I noticed a thread making a call to this: ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) and can't make much sense of what the 3 options are as they're not in my Linux headers anywhere.. Note that 3 is comms_in->ptmx / masterfd.

ioctl(TCGETS) is tcgetattr(3), which is also called from isatty(3) and ptsname(3). It's defined in /usr/include/asm-generic/ioctls.h. As to the SND*, strace could be a little smarter; you're not going to do sound/oss operations on a pseudo-terminal.

int32_t masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);

There is no point in making your program needlessly unportable. Use posix_openpt(3) instead.

slavepathLen = strlen(slavepathPtr);
slavePath = (char *) malloc(sizeof(char) * (slavepathLen + 1));
strcpy(slavePath, slavepathPtr);

That's what strdup(3) is for ;-)

And you should also handle your read() being interrupted by a signal, unless you're absolutely sure you (and all the library functions you call) set all the signal handlers with SA_RESTART.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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