Apply termios settings in a single system call (for ispeed,ospeed)

This commit is contained in:
Gerhard Rieger 2018-01-20 14:04:42 +01:00
parent b5be579187
commit ebbe704423
5 changed files with 255 additions and 249 deletions

View file

@ -31,6 +31,12 @@ corrections:
Test: UDP6MULTICAST_UNIDIR
Thanks to Angus Gratton for sending a patch.
Setting ispeed and ospeed failed for some serial devices because the
two settings were applied with two different get/set cycles, Thanks to
Alexandre Fenyo for providing an initial patch.
However, the actual fix is part of a conceptual change of the termios
module that aims for applying all changes in a single tcsetaddr call.
testing:
test.sh: Show a warning when phase-1 (insecure phase) of a security
test fails

View file

@ -123,8 +123,8 @@ static int xioopen_readline(int argc, const char *argv[], struct opt *opts,
Read_history(xfd->stream.para.readline.history_file);
}
#if _WITH_TERMIOS
xiotermios_clrflag(xfd->stream.fd, 3, ICANON);
xiotermios_clrflag(xfd->stream.fd, 3, ECHO);
xiotermios_clrflag(xfd->stream.fd, 3, ICANON|ECHO);
xiotermios_flush(xfd->stream.fd);
#endif /* _WITH_TERMIOS */
return _xio_openlate(&xfd->stream, opts);
}
@ -174,6 +174,7 @@ ssize_t xioread_readline(struct single *pipe, void *buff, size_t bufsiz) {
#if _WITH_TERMIOS
xiotermios_setflag(pipe->fd, 3, ECHO);
xiotermios_flush(pipe->fd);
#endif /* _WITH_TERMIOS */
if (pipe->para.readline.prompt || pipe->para.readline.dynprompt) {
/* we must carriage return, because readline will first print the
@ -201,6 +202,7 @@ ssize_t xioread_readline(struct single *pipe, void *buff, size_t bufsiz) {
}
#if _WITH_TERMIOS
xiotermios_clrflag(pipe->fd, 3, ECHO);
xiotermios_flush(pipe->fd);
#endif /* _WITH_TERMIOS */
Add_history(line);
bytes = strlen(line);

View file

@ -14,6 +14,7 @@
#if WITH_TERMIOS
const struct optdesc opt_tiocsctty={ "tiocsctty", "ctty",OPT_TIOCSCTTY, GROUP_TERMIOS, PH_LATE2, TYPE_BOOL, OFUNC_SPEC };
/* it is important for handling of these options that they have PH_FD */
const struct optdesc opt_brkint = { "brkint", NULL, OPT_BRKINT, GROUP_TERMIOS, PH_FD, TYPE_BOOL, OFUNC_TERMIOS_FLAG, 0, BRKINT };
const struct optdesc opt_icrnl = { "icrnl", NULL, OPT_ICRNL, GROUP_TERMIOS, PH_FD, TYPE_BOOL, OFUNC_TERMIOS_FLAG, 0, ICRNL };
const struct optdesc opt_ignbrk = { "ignbrk", NULL, OPT_IGNBRK, GROUP_TERMIOS, PH_FD, TYPE_BOOL, OFUNC_TERMIOS_FLAG, 0, IGNBRK };
@ -297,42 +298,237 @@ int xiotermiosflag_applyopt(int fd, struct opt *opt) {
#endif /* WITH_TERMIOS */
int xiotermios_setflag(int fd, int word, tcflag_t mask) {
union {
struct termios termarg;
tcflag_t flags[4];
} tdata;
bool _xiotermios_doit = false; /* _data has been retrieved and manipulated, set it later */
union {
struct termios termarg;
tcflag_t flags[4];
#ifdef HAVE_TERMIOS_ISPEED
speed_t speeds[sizeof(struct termios)/sizeof(speed_t)];
#endif
} _xiotermios_data;
if (Tcgetattr(fd, &tdata.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &tdata.termarg, strerror(errno));
return -1;
int xiotermios_setflag(int fd, int word, tcflag_t mask) {
if (!_xiotermios_doit) {
if (Tcgetattr(fd, &_xiotermios_data.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &_xiotermios_data.termarg, strerror(errno));
return -1;
}
_xiotermios_doit = true;
}
tdata.flags[word] |= mask;
if (Tcsetattr(fd, TCSADRAIN, &tdata.termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &tdata.termarg, strerror(errno));
_xiotermios_data.flags[word] |= mask;
return 0;
}
int xiotermios_clrflag(int fd, int word, tcflag_t mask) {
if (!_xiotermios_doit) {
if (Tcgetattr(fd, &_xiotermios_data.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &_xiotermios_data.termarg, strerror(errno));
return -1;
}
_xiotermios_doit = true;
}
_xiotermios_data.flags[word] &= ~mask;
return 0;
}
int xiotermios_value(int fd, int word, tcflag_t mask, tcflag_t value) {
if (!_xiotermios_doit) {
if (Tcgetattr(fd, &_xiotermios_data.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &_xiotermios_data.termarg, strerror(errno));
return -1;
}
_xiotermios_doit = true;
}
_xiotermios_data.flags[word] &= ~mask;
_xiotermios_data.flags[word] |= value;
return 0;
}
int xiotermios_char(int fd, int n, unsigned char c) {
if (!_xiotermios_doit) {
if (Tcgetattr(fd, &_xiotermios_data.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &_xiotermios_data.termarg, strerror(errno));
return -1;
}
_xiotermios_doit = true;
}
_xiotermios_data.termarg.c_cc[n] = c;
return 0;
}
#ifdef HAVE_TERMIOS_ISPEED
int xiotermios_speed(int fd, int n, unsigned int speed) {
if (!_xiotermios_doit) {
if (Tcgetattr(fd, &_xiotermios_data.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &_xiotermios_data.termarg, strerror(errno));
return -1;
}
_xiotermios_doit = true;
}
_xiotermios_data.speeds[n] = speed;
return 0;
}
#endif /* HAVE_TERMIOS_ISPEED */
int xiotermios_spec(int fd, int optcode) {
if (!_xiotermios_doit) {
if (Tcgetattr(fd, &_xiotermios_data.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &_xiotermios_data.termarg, strerror(errno));
return -1;
}
_xiotermios_doit = true;
}
switch (optcode) {
case OPT_RAW:
_xiotermios_data.termarg.c_iflag &=
~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF
#ifdef IUCLC
|IUCLC
#endif
|IXANY|IMAXBEL);
_xiotermios_data.termarg.c_iflag |= (0);
_xiotermios_data.termarg.c_oflag &= ~(OPOST);
_xiotermios_data.termarg.c_oflag |= (0);
_xiotermios_data.termarg.c_cflag &= ~(0);
_xiotermios_data.termarg.c_cflag |= (0);
_xiotermios_data.termarg.c_lflag &= ~(ISIG|ICANON
#ifdef XCASE
|XCASE
#endif
);
_xiotermios_data.termarg.c_lflag |= (0);
_xiotermios_data.termarg.c_cc[VMIN] = 1;
_xiotermios_data.termarg.c_cc[VTIME] = 0;
break;
case OPT_TERMIOS_RAWER:
_xiotermios_data.termarg.c_iflag = 0;
_xiotermios_data.termarg.c_oflag = 0;
_xiotermios_data.termarg.c_lflag = 0;
_xiotermios_data.termarg.c_cflag = (CS8);
_xiotermios_data.termarg.c_cc[VMIN] = 1;
_xiotermios_data.termarg.c_cc[VTIME] = 0;
break;
case OPT_SANE:
/* cread -ignbrk brkint -inlcr -igncr icrnl
-ixoff -iuclc -ixany imaxbel opost -olcuc -ocrnl
onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0
vt0 ff0 isig icanon iexten echo echoe echok -echonl
-noflsh -xcase -tostop -echoprt echoctl echoke, and
also sets all special characters to their default
values.
*/
_xiotermios_data.termarg.c_iflag &= ~(IGNBRK|INLCR|IGNCR|IXOFF
#ifdef IUCLC
|IUCLC
#endif
|IXANY);
_xiotermios_data.termarg.c_iflag |= (BRKINT|ICRNL|IMAXBEL);
_xiotermios_data.termarg.c_oflag &= ~(0 /* for canonical reasons */
#ifdef OLCUC
|OLCUC
#endif
#ifdef OCRNL
|OCRNL
#endif
#ifdef ONOCR
|ONOCR
#endif
#ifdef ONLRET
|ONLRET
#endif
#ifdef OFILL
|OFILL
#endif
#ifdef OFDEL
|OFDEL
#endif
#ifdef NLDLY
|NLDLY
#endif
#ifdef CRDLY
|CRDLY
#endif
#ifdef TABDLY
|TABDLY
#endif
#ifdef BSDLY
|BSDLY
#endif
#ifdef VTDLY
|VTDLY
#endif
#ifdef FFDLY
|FFDLY
#endif
);
_xiotermios_data.termarg.c_oflag |= (OPOST|ONLCR
#ifdef NL0
|NL0
#endif
#ifdef CR0
|CR0
#endif
#ifdef TAB0
|TAB0
#endif
#ifdef BS0
|BS0
#endif
#ifdef VT0
|VT0
#endif
#ifdef FF0
|FF0
#endif
);
_xiotermios_data.termarg.c_cflag &= ~(0);
_xiotermios_data.termarg.c_cflag |= (CREAD);
_xiotermios_data.termarg.c_lflag &= ~(ECHONL|NOFLSH
#ifdef XCASE
|XCASE
#endif
|TOSTOP
#ifdef ECHOPRT
|ECHOPRT
#endif
);
_xiotermios_data.termarg.c_lflag |= (ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE);
/*! "sets characters to their default values... - which? */
break;
case OPT_TERMIOS_CFMAKERAW:
#if HAVE_CFMAKERAW
cfmakeraw(&_xiotermios_data.termarg);
#else
/* these setting follow the Linux documenation of cfmakeraw */
_xiotermios_data.termarg.c_iflag &=
~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
_xiotermios_data.termarg.c_oflag &= ~(OPOST);
_xiotermios_data.termarg.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
_xiotermios_data.termarg.c_cflag &= ~(CSIZE|PARENB);
_xiotermios_data.termarg.c_cflag |= (CS8);
#endif
break;
default:
Error("TERMIOS option not handled - internal error?");
return -1;
}
return 0;
}
int xiotermios_clrflag(int fd, int word, tcflag_t mask) {
union {
struct termios termarg;
tcflag_t flags[4];
} tdata;
if (Tcgetattr(fd, &tdata.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &tdata.termarg, strerror(errno));
return -1;
}
tdata.flags[word] &= ~mask;
if (Tcsetattr(fd, TCSADRAIN, &tdata.termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &tdata.termarg, strerror(errno));
return -1;
int xiotermios_flush(int fd) {
if (_xiotermios_doit) {
if (Tcsetattr(fd, TCSADRAIN, &_xiotermios_data.termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &_xiotermios_data.termarg, strerror(errno));
return -1;
}
_xiotermios_doit = false;
}
return 0;
}

View file

@ -145,6 +145,13 @@ extern const struct optdesc opt_termios_cfmakeraw;
extern int xiotermios_setflag(int fd, int word, tcflag_t mask);
extern int xiotermios_clrflag(int fd, int word, tcflag_t mask);
extern int xiotermiosflag_applyopt(int fd, struct opt *opt);
extern int xiotermios_value(int fd, int word, tcflag_t mask, tcflag_t value);
extern int xiotermios_char(int fd, int n, unsigned char c);
#ifdef HAVE_TERMIOS_ISPEED
extern int xiotermios_speed(int fd, int n, unsigned int speed);
#endif
extern int xiotermios_spec(int fd, int optcode);
extern int xiotermios_flush(int fd);
#endif /* _WITH_TERMIOS */
#endif /* !defined(__xio_termios_h_included) */

231
xioopts.c
View file

@ -3509,252 +3509,41 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
#if WITH_TERMIOS
} else if (opt->desc->func == OFUNC_TERMIOS_FLAG) {
#if 0
union {
struct termios termarg;
tcflag_t flags[4];
} tdata;
if (Tcgetattr(fd, &tdata.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &tdata.termarg, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
}
if (opt->value.u_bool) {
tdata.flags[opt->desc->major] |= opt->desc->minor;
} else {
tdata.flags[opt->desc->major] &= ~opt->desc->minor;
}
if (Tcsetattr(fd, TCSADRAIN, &tdata.termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &tdata.termarg, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
}
#else
if (xiotermiosflag_applyopt(fd, opt) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
}
#endif
} else if (opt->desc->func == OFUNC_TERMIOS_VALUE) {
union {
struct termios termarg;
tcflag_t flags[4];
} tdata;
if (((opt->value.u_uint << opt->desc->arg3) & opt->desc->minor) !=
(opt->value.u_uint << opt->desc->arg3)) {
Error2("option %s: invalid value %u",
opt->desc->defname, opt->value.u_uint);
opt->desc = ODESC_ERROR; ++opt; continue;
}
if (Tcgetattr(fd, &tdata.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &tdata.termarg, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
}
tdata.flags[opt->desc->major] &= ~opt->desc->minor;
tdata.flags[opt->desc->major] |=
((opt->value.u_uint << opt->desc->arg3) & opt->desc->minor);
if (Tcsetattr(fd, TCSADRAIN, &tdata.termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &tdata.termarg, strerror(errno));
if (xiotermios_value(fd, opt->desc->major, opt->desc->minor,
(opt->value.u_uint << opt->desc->arg3) & opt->desc->minor) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
}
} else if (opt->desc->func == OFUNC_TERMIOS_PATTERN) {
union {
struct termios termarg;
tcflag_t flags[4];
} tdata;
if (Tcgetattr(fd, &tdata.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &tdata.termarg, strerror(errno));
if (xiotermios_value(fd, opt->desc->major, opt->desc->arg3, opt->desc->minor) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
}
tdata.flags[opt->desc->major] &= ~opt->desc->arg3;
tdata.flags[opt->desc->major] |= opt->desc->minor;
if (Tcsetattr(fd, TCSADRAIN, &tdata.termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &tdata.termarg, strerror(errno));
opt->desc = ODESC_ERROR;++opt; continue;
}
} else if (opt->desc->func == OFUNC_TERMIOS_CHAR) {
struct termios termarg;
if (Tcgetattr(fd, &termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &termarg, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
}
termarg.c_cc[opt->desc->major] = opt->value.u_byte;
if (Tcsetattr(fd, TCSADRAIN, &termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &termarg, strerror(errno));
if (xiotermios_char(fd, opt->desc->major, opt->value.u_byte) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
}
#ifdef HAVE_TERMIOS_ISPEED
} else if (opt->desc->func == OFUNC_TERMIOS_SPEED) {
union {
struct termios termarg;
speed_t speeds[sizeof(struct termios)/sizeof(speed_t)];
} tdata;
if (Tcgetattr(fd, &tdata.termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &tdata.termarg, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
}
tdata.speeds[opt->desc->major] = opt->value.u_uint;
if (Tcsetattr(fd, TCSADRAIN, &tdata.termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &tdata.termarg, strerror(errno));
if (xiotermios_speed(fd, opt->desc->major, opt->value.u_uint) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
}
#endif /* HAVE_TERMIOS_ISPEED */
} else if (opt->desc->func == OFUNC_TERMIOS_SPEC) {
struct termios termarg;
if (Tcgetattr(fd, &termarg) < 0) {
Error3("tcgetattr(%d, %p): %s",
fd, &termarg, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
}
switch (opt->desc->optcode) {
case OPT_RAW:
termarg.c_iflag &=
~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF
#ifdef IUCLC
|IUCLC
#endif
|IXANY|IMAXBEL);
termarg.c_iflag |= (0);
termarg.c_oflag &= ~(OPOST);
termarg.c_oflag |= (0);
termarg.c_cflag &= ~(0);
termarg.c_cflag |= (0);
termarg.c_lflag &= ~(ISIG|ICANON
#ifdef XCASE
|XCASE
#endif
);
termarg.c_lflag |= (0);
termarg.c_cc[VMIN] = 1;
termarg.c_cc[VTIME] = 0;
break;
case OPT_TERMIOS_RAWER:
termarg.c_iflag = 0;
termarg.c_oflag = 0;
termarg.c_lflag = 0;
termarg.c_cflag = (CS8);
termarg.c_cc[VMIN] = 1;
termarg.c_cc[VTIME] = 0;
break;
case OPT_SANE:
/* cread -ignbrk brkint -inlcr -igncr icrnl
-ixoff -iuclc -ixany imaxbel opost -olcuc -ocrnl
onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0
vt0 ff0 isig icanon iexten echo echoe echok -echonl
-noflsh -xcase -tostop -echoprt echoctl echoke, and
also sets all special characters to their default
values.
*/
termarg.c_iflag &= ~(IGNBRK|INLCR|IGNCR|IXOFF
#ifdef IUCLC
|IUCLC
#endif
|IXANY);
termarg.c_iflag |= (BRKINT|ICRNL|IMAXBEL);
termarg.c_oflag &= ~(0 /* for canonical reasons */
#ifdef OLCUC
|OLCUC
#endif
#ifdef OCRNL
|OCRNL
#endif
#ifdef ONOCR
|ONOCR
#endif
#ifdef ONLRET
|ONLRET
#endif
#ifdef OFILL
|OFILL
#endif
#ifdef OFDEL
|OFDEL
#endif
#ifdef NLDLY
|NLDLY
#endif
#ifdef CRDLY
|CRDLY
#endif
#ifdef TABDLY
|TABDLY
#endif
#ifdef BSDLY
|BSDLY
#endif
#ifdef VTDLY
|VTDLY
#endif
#ifdef FFDLY
|FFDLY
#endif
);
termarg.c_oflag |= (OPOST|ONLCR
#ifdef NL0
|NL0
#endif
#ifdef CR0
|CR0
#endif
#ifdef TAB0
|TAB0
#endif
#ifdef BS0
|BS0
#endif
#ifdef VT0
|VT0
#endif
#ifdef FF0
|FF0
#endif
);
termarg.c_cflag &= ~(0);
termarg.c_cflag |= (CREAD);
termarg.c_lflag &= ~(ECHONL|NOFLSH
#ifdef XCASE
|XCASE
#endif
|TOSTOP
#ifdef ECHOPRT
|ECHOPRT
#endif
);
termarg.c_lflag |= (ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE);
/*! "sets characters to their default values... - which? */
break;
case OPT_TERMIOS_CFMAKERAW:
#if HAVE_CFMAKERAW
cfmakeraw(&termarg);
#else
/* these setting follow the Linux documenation of cfmakeraw */
termarg.c_iflag &=
~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termarg.c_oflag &= ~(OPOST);
termarg.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termarg.c_cflag &= ~(CSIZE|PARENB);
termarg.c_cflag |= (CS8);
#endif
break;
default:
Error("TERMIOS option not handled - internal error?");
}
if (Tcsetattr(fd, TCSADRAIN, &termarg) < 0) {
Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
fd, &termarg, strerror(errno));
if (xiotermios_spec(fd, opt->desc->optcode) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
}
@ -3782,7 +3571,13 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
opt->desc = ODESC_DONE;
++opt;
}
return 0;
#if WITH_TERMIOS
if (phase == PH_FD) {
xiotermios_flush(fd);
}
#endif /* WITH_TERMIOS */
return 0;
}
/* applies to fd all options belonging to phases */