Annotation of embedaddon/sudo/common/term.c, revision 1.1.1.4

1.1       misho       1: /*
1.1.1.4 ! misho       2:  * Copyright (c) 2011-2014 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       misho       3:  *
                      4:  * Permission to use, copy, modify, and distribute this software for any
                      5:  * purpose with or without fee is hereby granted, provided that the above
                      6:  * copyright notice and this permission notice appear in all copies.
                      7:  *
                      8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                      9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     15:  */
                     16: 
                     17: #include <config.h>
                     18: 
                     19: #include <sys/types.h>
                     20: #include <stdio.h>
                     21: #ifdef STDC_HEADERS
                     22: # include <stdlib.h>
                     23: # include <stddef.h>
                     24: #else
                     25: # ifdef HAVE_STDLIB_H
                     26: #  include <stdlib.h>
                     27: # endif
                     28: #endif /* STDC_HEADERS */
                     29: #ifdef HAVE_STRING_H
                     30: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
                     31: #  include <memory.h>
                     32: # endif
                     33: # include <string.h>
                     34: #endif /* HAVE_STRING_H */
                     35: #ifdef HAVE_STRINGS_H
                     36: # include <strings.h>
                     37: #endif /* HAVE_STRINGS_H */
1.1.1.4 ! misho      38: #include <errno.h>
        !            39: #include <signal.h>
1.1       misho      40: #include <termios.h>
1.1.1.4 ! misho      41: #include <unistd.h>
1.1       misho      42: 
                     43: #include "missing.h"
1.1.1.2   misho      44: #include "sudo_debug.h"
1.1.1.4 ! misho      45: #include "sudo_util.h"
1.1       misho      46: 
                     47: #ifndef TCSASOFT
                     48: # define TCSASOFT      0
                     49: #endif
                     50: #ifndef ECHONL
                     51: # define ECHONL                0
                     52: #endif
                     53: #ifndef IEXTEN
                     54: # define IEXTEN                0
                     55: #endif
                     56: #ifndef IUCLC
                     57: # define IUCLC         0
                     58: #endif
                     59: 
                     60: #ifndef _POSIX_VDISABLE
                     61: # ifdef VDISABLE
                     62: #  define _POSIX_VDISABLE      VDISABLE
                     63: # else
                     64: #  define _POSIX_VDISABLE      0
                     65: # endif
                     66: #endif
                     67: 
                     68: static struct termios term, oterm;
                     69: static int changed;
1.1.1.4 ! misho      70: 
        !            71: /* tgetpass() needs to know the erase and kill chars for cbreak mode. */
1.1       misho      72: int term_erase;
                     73: int term_kill;
                     74: 
1.1.1.4 ! misho      75: static volatile sig_atomic_t got_sigttou;
        !            76: 
        !            77: /*
        !            78:  * SIGTTOU signal handler for term_restore that just sets a flag.
        !            79:  */
        !            80: static void sigttou(int signo)
        !            81: {
        !            82:     got_sigttou = 1;
        !            83: }
        !            84: 
        !            85: /*
        !            86:  * Like tcsetattr() but restarts on EINTR _except_ for SIGTTOU.
        !            87:  * Returns 0 on success or -1 on failure, setting errno.
        !            88:  * Sets got_sigttou on failure if interrupted by SIGTTOU.
        !            89:  */
        !            90: static int
        !            91: tcsetattr_nobg(int fd, int flags, struct termios *tp)
        !            92: {
        !            93:     sigaction_t sa, osa;
        !            94:     int rc;
        !            95: 
        !            96:     /*
        !            97:      * If we receive SIGTTOU from tcsetattr() it means we are
        !            98:      * not in the foreground process group.
        !            99:      * This should be less racy than using tcgetpgrp().
        !           100:      */
        !           101:     memset(&sa, 0, sizeof(sa));
        !           102:     sigemptyset(&sa.sa_mask);
        !           103:     sa.sa_handler = sigttou;
        !           104:     got_sigttou = 0;
        !           105:     sigaction(SIGTTOU, &sa, &osa);
        !           106:     do {
        !           107:        rc = tcsetattr(fd, flags, tp);
        !           108:     } while (rc != 0 && errno == EINTR && !got_sigttou);
        !           109:     sigaction(SIGTTOU, &osa, NULL);
        !           110: 
        !           111:     return rc;
        !           112: }
        !           113: 
        !           114: /*
        !           115:  * Restore saved terminal settings if we are in the foreground process group.
        !           116:  * Returns true on success or false on failure.
        !           117:  */
        !           118: bool
        !           119: term_restore(int fd, bool flush)
1.1       misho     120: {
1.1.1.2   misho     121:     debug_decl(term_restore, SUDO_DEBUG_UTIL)
                    122: 
1.1       misho     123:     if (changed) {
1.1.1.4 ! misho     124:        const int flags = flush ? (TCSASOFT|TCSAFLUSH) : (TCSASOFT|TCSADRAIN);
        !           125:        if (tcsetattr_nobg(fd, flags, &oterm) != 0)
        !           126:            debug_return_bool(false);
1.1       misho     127:        changed = 0;
                    128:     }
1.1.1.4 ! misho     129:     debug_return_bool(true);
1.1       misho     130: }
                    131: 
1.1.1.4 ! misho     132: /*
        !           133:  * Disable terminal echo.
        !           134:  * Returns true on success or false on failure.
        !           135:  */
        !           136: bool
1.1       misho     137: term_noecho(int fd)
                    138: {
1.1.1.2   misho     139:     debug_decl(term_noecho, SUDO_DEBUG_UTIL)
                    140: 
1.1.1.4 ! misho     141: again:
1.1       misho     142:     if (!changed && tcgetattr(fd, &oterm) != 0)
1.1.1.4 ! misho     143:        debug_return_bool(false);
1.1       misho     144:     (void) memcpy(&term, &oterm, sizeof(term));
                    145:     CLR(term.c_lflag, ECHO|ECHONL);
                    146: #ifdef VSTATUS
                    147:     term.c_cc[VSTATUS] = _POSIX_VDISABLE;
                    148: #endif
1.1.1.4 ! misho     149:     if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
1.1       misho     150:        changed = 1;
1.1.1.4 ! misho     151:        debug_return_bool(true);
1.1       misho     152:     }
1.1.1.4 ! misho     153:     if (got_sigttou) {
        !           154:        /* We were in the background, so oterm is probably bogus. */
        !           155:        kill(getpid(), SIGTTOU);
        !           156:        goto again;
        !           157:     }
        !           158:     debug_return_bool(false);
1.1       misho     159: }
                    160: 
1.1.1.4 ! misho     161: /*
        !           162:  * Set terminal to raw mode.
        !           163:  * Returns true on success or false on failure.
        !           164:  */
        !           165: bool
1.1       misho     166: term_raw(int fd, int isig)
                    167: {
                    168:     struct termios term;
1.1.1.2   misho     169:     debug_decl(term_raw, SUDO_DEBUG_UTIL)
1.1       misho     170: 
1.1.1.4 ! misho     171: again:
1.1       misho     172:     if (!changed && tcgetattr(fd, &oterm) != 0)
                    173:        return 0;
                    174:     (void) memcpy(&term, &oterm, sizeof(term));
                    175:     /* Set terminal to raw mode */
                    176:     term.c_cc[VMIN] = 1;
                    177:     term.c_cc[VTIME] = 0;
                    178:     CLR(term.c_iflag, ICRNL | IGNCR | INLCR | IUCLC | IXON);
                    179:     CLR(term.c_oflag, OPOST);
                    180:     CLR(term.c_lflag, ECHO | ICANON | ISIG | IEXTEN);
                    181:     if (isig)
                    182:        SET(term.c_lflag, ISIG);
1.1.1.4 ! misho     183:     if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
1.1       misho     184:        changed = 1;
1.1.1.4 ! misho     185:        debug_return_bool(true);
1.1       misho     186:     }
1.1.1.4 ! misho     187:     if (got_sigttou) {
        !           188:        /* We were in the background, so oterm is probably bogus. */
        !           189:        kill(getpid(), SIGTTOU);
        !           190:        goto again;
        !           191:     }
        !           192:     debug_return_bool(false);
1.1       misho     193: }
                    194: 
1.1.1.4 ! misho     195: /*
        !           196:  * Set terminal to cbreak mode.
        !           197:  * Returns true on success or false on failure.
        !           198:  */
        !           199: bool
1.1       misho     200: term_cbreak(int fd)
                    201: {
1.1.1.2   misho     202:     debug_decl(term_cbreak, SUDO_DEBUG_UTIL)
                    203: 
1.1.1.4 ! misho     204: again:
1.1       misho     205:     if (!changed && tcgetattr(fd, &oterm) != 0)
                    206:        return 0;
                    207:     (void) memcpy(&term, &oterm, sizeof(term));
                    208:     /* Set terminal to half-cooked mode */
                    209:     term.c_cc[VMIN] = 1;
                    210:     term.c_cc[VTIME] = 0;
1.1.1.4 ! misho     211:     /* cppcheck-suppress redundantAssignment */
1.1       misho     212:     CLR(term.c_lflag, ECHO | ECHONL | ICANON | IEXTEN);
1.1.1.4 ! misho     213:     /* cppcheck-suppress redundantAssignment */
1.1       misho     214:     SET(term.c_lflag, ISIG);
                    215: #ifdef VSTATUS
                    216:     term.c_cc[VSTATUS] = _POSIX_VDISABLE;
                    217: #endif
1.1.1.4 ! misho     218:     if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
1.1       misho     219:        term_erase = term.c_cc[VERASE];
                    220:        term_kill = term.c_cc[VKILL];
                    221:        changed = 1;
1.1.1.4 ! misho     222:        debug_return_bool(true);
        !           223:     }
        !           224:     if (got_sigttou) {
        !           225:        /* We were in the background, so oterm is probably bogus. */
        !           226:        kill(getpid(), SIGTTOU);
        !           227:        goto again;
1.1       misho     228:     }
1.1.1.4 ! misho     229:     debug_return_bool(false);
1.1       misho     230: }
                    231: 
1.1.1.4 ! misho     232: /*
        !           233:  * Copy terminal settings from one descriptor to another.
        !           234:  * Returns true on success or false on failure.
        !           235:  */
        !           236: bool
1.1       misho     237: term_copy(int src, int dst)
                    238: {
                    239:     struct termios tt;
1.1.1.2   misho     240:     debug_decl(term_copy, SUDO_DEBUG_UTIL)
1.1       misho     241: 
1.1.1.4 ! misho     242: again:
1.1       misho     243:     if (tcgetattr(src, &tt) != 0)
1.1.1.4 ! misho     244:        debug_return_bool(false);
        !           245:     if (tcsetattr_nobg(dst, TCSANOW|TCSASOFT, &tt) == 0)
        !           246:        debug_return_bool(true);
        !           247:     if (got_sigttou) {
        !           248:        /* We were in the background, so oterm is probably bogus. */
        !           249:        kill(getpid(), SIGTTOU);
        !           250:        goto again;
        !           251:     }
        !           252:     debug_return_bool(false);
1.1       misho     253: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>