--- embedaddon/sudo/common/term.c 2012/02/21 16:23:02 1.1.1.1 +++ embedaddon/sudo/common/term.c 2014/06/15 16:12:54 1.1.1.4 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Todd C. Miller + * Copyright (c) 2011-2014 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,7 +17,6 @@ #include #include -#include #include #ifdef STDC_HEADERS # include @@ -36,9 +35,14 @@ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ +#include +#include #include +#include #include "missing.h" +#include "sudo_debug.h" +#include "sudo_util.h" #ifndef TCSASOFT # define TCSASOFT 0 @@ -63,44 +67,108 @@ static struct termios term, oterm; static int changed; + +/* tgetpass() needs to know the erase and kill chars for cbreak mode. */ int term_erase; int term_kill; -int -term_restore(int fd, int flush) +static volatile sig_atomic_t got_sigttou; + +/* + * SIGTTOU signal handler for term_restore that just sets a flag. + */ +static void sigttou(int signo) { + got_sigttou = 1; +} + +/* + * Like tcsetattr() but restarts on EINTR _except_ for SIGTTOU. + * Returns 0 on success or -1 on failure, setting errno. + * Sets got_sigttou on failure if interrupted by SIGTTOU. + */ +static int +tcsetattr_nobg(int fd, int flags, struct termios *tp) +{ + sigaction_t sa, osa; + int rc; + + /* + * If we receive SIGTTOU from tcsetattr() it means we are + * not in the foreground process group. + * This should be less racy than using tcgetpgrp(). + */ + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigttou; + got_sigttou = 0; + sigaction(SIGTTOU, &sa, &osa); + do { + rc = tcsetattr(fd, flags, tp); + } while (rc != 0 && errno == EINTR && !got_sigttou); + sigaction(SIGTTOU, &osa, NULL); + + return rc; +} + +/* + * Restore saved terminal settings if we are in the foreground process group. + * Returns true on success or false on failure. + */ +bool +term_restore(int fd, bool flush) +{ + debug_decl(term_restore, SUDO_DEBUG_UTIL) + if (changed) { - int flags = TCSASOFT; - flags |= flush ? TCSAFLUSH : TCSADRAIN; - if (tcsetattr(fd, flags, &oterm) != 0) - return 0; + const int flags = flush ? (TCSASOFT|TCSAFLUSH) : (TCSASOFT|TCSADRAIN); + if (tcsetattr_nobg(fd, flags, &oterm) != 0) + debug_return_bool(false); changed = 0; } - return 1; + debug_return_bool(true); } -int +/* + * Disable terminal echo. + * Returns true on success or false on failure. + */ +bool term_noecho(int fd) { + debug_decl(term_noecho, SUDO_DEBUG_UTIL) + +again: if (!changed && tcgetattr(fd, &oterm) != 0) - return 0; + debug_return_bool(false); (void) memcpy(&term, &oterm, sizeof(term)); CLR(term.c_lflag, ECHO|ECHONL); #ifdef VSTATUS term.c_cc[VSTATUS] = _POSIX_VDISABLE; #endif - if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) { + if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) { changed = 1; - return 1; + debug_return_bool(true); } - return 0; + if (got_sigttou) { + /* We were in the background, so oterm is probably bogus. */ + kill(getpid(), SIGTTOU); + goto again; + } + debug_return_bool(false); } -int +/* + * Set terminal to raw mode. + * Returns true on success or false on failure. + */ +bool term_raw(int fd, int isig) { struct termios term; + debug_decl(term_raw, SUDO_DEBUG_UTIL) +again: if (!changed && tcgetattr(fd, &oterm) != 0) return 0; (void) memcpy(&term, &oterm, sizeof(term)); @@ -112,44 +180,74 @@ term_raw(int fd, int isig) CLR(term.c_lflag, ECHO | ICANON | ISIG | IEXTEN); if (isig) SET(term.c_lflag, ISIG); - if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) { + if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) { changed = 1; - return 1; + debug_return_bool(true); } - return 0; + if (got_sigttou) { + /* We were in the background, so oterm is probably bogus. */ + kill(getpid(), SIGTTOU); + goto again; + } + debug_return_bool(false); } -int +/* + * Set terminal to cbreak mode. + * Returns true on success or false on failure. + */ +bool term_cbreak(int fd) { + debug_decl(term_cbreak, SUDO_DEBUG_UTIL) + +again: if (!changed && tcgetattr(fd, &oterm) != 0) return 0; (void) memcpy(&term, &oterm, sizeof(term)); /* Set terminal to half-cooked mode */ term.c_cc[VMIN] = 1; term.c_cc[VTIME] = 0; + /* cppcheck-suppress redundantAssignment */ CLR(term.c_lflag, ECHO | ECHONL | ICANON | IEXTEN); + /* cppcheck-suppress redundantAssignment */ SET(term.c_lflag, ISIG); #ifdef VSTATUS term.c_cc[VSTATUS] = _POSIX_VDISABLE; #endif - if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) { + if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) { term_erase = term.c_cc[VERASE]; term_kill = term.c_cc[VKILL]; changed = 1; - return 1; + debug_return_bool(true); } - return 0; + if (got_sigttou) { + /* We were in the background, so oterm is probably bogus. */ + kill(getpid(), SIGTTOU); + goto again; + } + debug_return_bool(false); } -int +/* + * Copy terminal settings from one descriptor to another. + * Returns true on success or false on failure. + */ +bool term_copy(int src, int dst) { struct termios tt; + debug_decl(term_copy, SUDO_DEBUG_UTIL) +again: if (tcgetattr(src, &tt) != 0) - return 0; - if (tcsetattr(dst, TCSANOW|TCSASOFT, &tt) != 0) - return 0; - return 1; + debug_return_bool(false); + if (tcsetattr_nobg(dst, TCSANOW|TCSASOFT, &tt) == 0) + debug_return_bool(true); + if (got_sigttou) { + /* We were in the background, so oterm is probably bogus. */ + kill(getpid(), SIGTTOU); + goto again; + } + debug_return_bool(false); }