diff --git a/CHANGES b/CHANGES index 3c44bae..1f770fd 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/xio-readline.c b/xio-readline.c index 24ee757..0fbbb88 100644 --- a/xio-readline.c +++ b/xio-readline.c @@ -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); diff --git a/xio-termios.c b/xio-termios.c index 70f1831..2845247 100644 --- a/xio-termios.c +++ b/xio-termios.c @@ -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; } diff --git a/xio-termios.h b/xio-termios.h index 8e39839..a288a2f 100644 --- a/xio-termios.h +++ b/xio-termios.h @@ -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) */ diff --git a/xioopts.c b/xioopts.c index 9ea6ce4..014c7d9 100644 --- a/xioopts.c +++ b/xioopts.c @@ -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 */