I'm studying Linux TTY. And there is a phenomenon that I cannot understand:
Set PARENB into c_cflag , tcsetattr() will return -1 with errno is EINVAL.
Why PARENB can cause EINVAL? Where return -1.
I check this with GDB on Linux kernel source code (linux-5.15.30), and I noticed that tty_ioctl return 0, not -1.
So will C lib return -1? I also try to check it in glibc source code, but I did not find anywhere could return -1, in my opinion.
The PARENB means: "Enable parity generation on output and parity checking for input."
I can understand some TTY would not support it. But how does the system know it's unsupported, even if the linux kernel tty_ioctl() return 0?
My test code is:
#include <stdio.h>
#include <pty.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int ret = 0;
int m = 0;
int s = 0;
struct termios t;
ret = openpty(&m, &s, NULL, NULL, NULL);
if (0 != ret) {
printf("Error: openpty failed\n");
return -1;
}
tcgetattr(s, &t);
printf("> termios->c_cflag = %#x\n", t.c_cflag);
t.c_cflag |= PARENB;
printf(">>> will tcsetattr(), c_cflag = %#x, TCSETS = %d, TCSANOW = %d\n",
t.c_cflag, TCSETS, TCSANOW);
ret = tcsetattr(s, TCSANOW, &t);
if (0 != ret) {
printf("Error: tcsetattr failed, ret = %d, errno = %d\n", ret, errno);
}
close(m);
close(s);
return 0;
}
The result is:
gcc test_attr.c -lutil
./a.out
> termios->c_cflag = 0xbf
>>> will tcsetattr(), c_cflag = 0x1bf, TCSETS = 21506, TCSANOW = 0
Error: tcsetattr failed, ret = -1, errno = 22
I wanna know where returns -1, and how does it can know PARENB is unsupported.
The observed behavior is due to a Debian-specific patch (also used by Debian-derived distros such as Ubuntu) applied to the glibc sources for the libc6 packages. The patch for 2.37-7 is shown below, but this patch has been used since 2003, related to Debian bug #218181:
https://sources.debian.org/patches/glibc/2.37-7/any/local-tcsetaddr.diff/
The patched code sandwiches the
TCSETSioctlcall between a pair ofTCGETSioctlcalls and if all the calls were successful it does a 3-way difference between the original termios settings got from the firstTCGETS, the replacement termios settings sent toTCSETS, and the final termios settings got from the secondTCGETS. The patch is specifically looking for the case when changes to thePARENB,CREAD, andCSIZEbits ofc_cflaghave been silently ignored and no other changes have been made. The function will return an error (witherrnoset toEINVAL) in that case. (I am a bit puzzled by the(PARENB & CREAD & CSIZE)subexpression which it seems would always evaluate to 0. I think that is actually a harmless bug and the subexpression should be(PARENB | CREAD | CSIZE). The bug is harmless if the TTY driver either handles the requested changes to thec_cflagbits masked by(PARENB | CREAD | CSIZE)in theTCSETSioctlcall or always sets them to the to the same values for theTCGETSioctlcall.)As described by the author of the patch, Jeff Licquia in bug 218131 message 30:
The relevant part from the POSIX description of
tcsetattris as follows: