TIOCPKT mode of PTY is broken if ONLCR bit is cleared.

Takashi Yano takashi.yano@nifty.ne.jp
Sat Feb 28 12:16:00 GMT 2015


Package: cygwin
Version: 1.7.34-6

[Problem 1]

TIOCKPT mode for PTY does not work properly when ONLCR flag is
unset. When read() is called from master side, it seems working
correctly only if the buffer size specified to read() is larger
than the size of available data, otherwise it causes an internal
error.

To reproduce this problem, compile the following C source (Test
Case 1) and execute it.

The expected result is:
00 30 31 32 33 34 35 36 37 38 39
00 30 31 32 33 34
00 35 36 37 38 39

In cygwin 1.7.34-6, however, the result is:
00 30 31 32 33 34 35 36 37 38 39
      0 [main] test_pty 3872 fhandler_pty_master::process_slave_output: internal error: 0 unexpected characters
00 30 31 32 33 34
0a
00 36 37 38 39

Furthermore, in the case that OPOST flag is unset, the result is:
00 30 31 32 33 34 35 36 37 38 39
00 30 31 32 33 34 35
00 36 37 38 39

In the latter case, length of data read out exceeds that specified
to read().


I was looking into cygwin source code and found the PTY fails
to count TIOCPKT control byte (TIOCPKT_DATA) into buffer length
properly in fhandler_pty_master::process_slave_output().


[Problem 2]

Moreover, I found another problem of TTY while testing the case.

tcsetattr() affects data already written to TTY even if it is used
with TCSADRAIN option.

To reproduce this problem, compile the following C source (Test
Case 2) and execute it.

The expected result is:
0123456789
          0123456789
because the first write() is without OPOST flag and the second
write() is with OPOST flag.

In cygwin 1.7.34-6, however, the result is:
0123456789
0123456789

This means that the second tcsetattr() is applied before the
post-processing for the first write() is done.

If usleep() currently commented-out is enabled, the result
will be as expected.

I was looking into cygwin source code again and found that
this problem is caused since the processing for OPOST is done
in a function fhandler_pty_master::process_slave_output()
which is called by read-process.

To fix this problem, the processing for OPOST should be done
in write-process instead of read-process.


Both problem above is essentially independent, but the
patches for them are not independent. In other words, the
patches for these problems touch same function. Therefore
the bug reports for both problem are in this mail together.


To fix these problems, I have made a patch (cygwin.patch)
attached. I have confirmed both problem mentioned above
have been gone with this patch.

Please consider of adopting the patch I have proposed in the
future release.



/***************/
/* Test Case 1 */
/***************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <pty.h>

int main()
{
        int master, slave;
        struct termios tt;
        int val = 1;
        int pkt = 0;
        char buf[BUFSIZ];
        int len;

        if ( openpty(&master, &slave, NULL, NULL, NULL) < 0) {
                perror("openpty()");
                exit(EXIT_FAILURE);
        }

        /* setup master */
        if ( ioctl(master, FIONBIO, &val) < 0) {
                perror("ioctl(FIONBIO)");
                exit(EXIT_FAILURE);
        }
        if ( ioctl(master, TIOCPKT, &val) < 0) {
                perror("ioctl(TIOCPKT)");
                exit(EXIT_FAILURE);
        } else {
                pkt = 1;
        }

        /* setup slave */
        tcgetattr(slave, &tt);
        tt.c_oflag &= ~ONLCR;
//      tt.c_oflag &= ~OPOST;
        tcsetattr(slave, TCSANOW, &tt);

        /* Case 1: No Error */
        len = write(slave, "0123456789", 10);
        while (len>0) {
                int n = read(master, buf, 10+pkt);
                int i;
                if (n<=0) break;
                for (i=0; i<n; i++) {
                        printf("%02x ", buf[i]);
                }
                printf("\n");
                len -= n - pkt;
        }

        /* Case 2: Error */
        len = write(slave, "0123456789", 10);
        while (len>0) {
                int n = read(master, buf, 5+pkt);
                int i;
                if (n<=0) break;
                for (i=0; i<n; i++) {
                        printf("%02x ", buf[i]);
                }
                printf("\n");
                len -= n - pkt;
        }

        return 0;
}



/***************/
/* Test Case 2 */
/***************/
#include <unistd.h>
#include <termios.h>

int main()
{
        struct termios tt;

        tcgetattr(STDOUT_FILENO, &tt);

        tt.c_oflag &= ~OPOST;
        tcsetattr(STDOUT_FILENO, TCSANOW, &tt);

        write(STDOUT_FILENO, "0123456789\n", 11);

        /* usleep(100000); */

        tt.c_oflag |= OPOST;
        tcsetattr(STDOUT_FILENO, TCSADRAIN, &tt);

        write(STDOUT_FILENO, "0123456789\n", 11);

        return 0;
}


-- 
Takashi Yano <takashi.yano@nifty.ne.jp>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: cygcheck.out
Type: application/octet-stream
Size: 100058 bytes
Desc: not available
URL: <http://cygwin.com/pipermail/cygwin/attachments/20150228/eeae311d/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: cygwin.patch
Type: application/octet-stream
Size: 9636 bytes
Desc: not available
URL: <http://cygwin.com/pipermail/cygwin/attachments/20150228/eeae311d/attachment-0001.obj>
-------------- next part --------------
--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple


More information about the Cygwin mailing list