1

I am trying to set a read timeout on a file descriptor representing a PTY. I have set VMIN = 0 and VTIME = 10 in termios, which I expect to return when a character is available, or after a second if no characters are available. However, my program sits forever in the read call.

Is there something special about PTY that makes this not work? Are there other TERMIOS settings that cause this to work? I tried this same configuration on the stdin file descriptor and it worked as expected.

#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>

#define debug(...) fprintf (stderr, __VA_ARGS__)

static void set_term (int fd)
{
    struct termios termios;
    int res;

    res = tcgetattr (fd, &termios);
    if (res) {
        debug ("TERM get error\n");
        return;
    }

    cfmakeraw (&termios);
    termios.c_lflag &= ~(ICANON);
    termios.c_cc[VMIN] = 0;
    termios.c_cc[VTIME] = 10;        /* One second */

    res = tcsetattr (fd, TCSANOW, &termios);
    if (res) {
        debug ("TERM set error\n");
        return;
    }

}

int get_term (void)
{
    int fd;
    int res;
    char *name;

    fd = posix_openpt (O_RDWR);
    if (fd < 0) {
        debug ("Error opening PTY\n");
        exit (1);
    }

    res = grantpt (fd);
    if (res) {
        debug ("Error granting PTY\n");
        exit (1);
    }

    res = unlockpt (fd);
    if (res) {
        debug ("Error unlocking PTY\n");
        exit (1);
    }

    name = ptsname (fd);
    debug ("Attach terminal on %s\n", name);

    return fd;
}

int main (int argc, char **argv)
{
    int read_fd;
    int bytes;
    char c;

    read_fd = get_term ();

    set_term (read_fd);
    bytes = read (read_fd, &c, 1);
    debug ("Read returned\n");

    return 0;
}
FazJaxton
  • 7,544
  • 6
  • 26
  • 32

1 Answers1

0

From the linux pty (7) manpage (italics are mine):

A pseudoterminal (sometimes abbreviated "pty") is a pair of virtual character devices that provide a bidirectional communication channel. One end of the channel is called the master; the other end is called the slave. The slave end of the pseudoterminal provides an interface that behaves exactly like a classical terminal

Your program, however, is reading from the master, which cannot be expected to behave exactly like a terminal device

If you change/expand the last few lines of get_term thusly ...

int slave_fd =  open (name, O_RDWR); /* now open the slave end..*/
if (slave_fd < 0) {
   debug ("Error opening slave PTY\n");
   exit (1);
}

return slave_fd; /* ... and use it instead of the master..*/

... your example program will work as expected.

Hans Lub
  • 5,513
  • 1
  • 23
  • 43
  • Of course, this begs the question why the tcsetattr() on the *master* in Faz' original program succeeds and what exactly it achieves. I believe (but cannot find this documented anywhere) that it sets the *slave* terminal attributes. In any case, which of the two file descriptors one uses for the tcsetattr() in the example progam doesn't make any difference in its behaviour, as long as the reading is done at the slave end. – Hans Lub May 26 '12 at 22:35
  • More on the difference between master and slave on Solaris and Linux [here](https://blogs.oracle.com/weixue/entry/tip_differece_master_pty_regards) – Hans Lub May 27 '12 at 11:23