version 1.1.1.2, 2012/05/29 12:26:49
|
version 1.1.1.4, 2014/06/15 16:12:54
|
Line 1
|
Line 1
|
/* |
/* |
* Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com> | * Copyright (c) 2011-2014 Todd C. Miller <Todd.Miller@courtesan.com> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
Line 17
|
Line 17
|
#include <config.h> |
#include <config.h> |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> |
|
#include <stdio.h> |
#include <stdio.h> |
#ifdef STDC_HEADERS |
#ifdef STDC_HEADERS |
# include <stdlib.h> |
# include <stdlib.h> |
Line 36
|
Line 35
|
#ifdef HAVE_STRINGS_H |
#ifdef HAVE_STRINGS_H |
# include <strings.h> |
# include <strings.h> |
#endif /* HAVE_STRINGS_H */ |
#endif /* HAVE_STRINGS_H */ |
|
#include <errno.h> |
|
#include <signal.h> |
#include <termios.h> |
#include <termios.h> |
|
#include <unistd.h> |
|
|
#include "missing.h" |
#include "missing.h" |
#include "sudo_debug.h" |
#include "sudo_debug.h" |
|
#include "sudo_util.h" |
|
|
#ifndef TCSASOFT |
#ifndef TCSASOFT |
# define TCSASOFT 0 |
# define TCSASOFT 0 |
Line 64
|
Line 67
|
|
|
static struct termios term, oterm; |
static struct termios term, oterm; |
static int changed; |
static int changed; |
|
|
|
/* tgetpass() needs to know the erase and kill chars for cbreak mode. */ |
int term_erase; |
int term_erase; |
int term_kill; |
int term_kill; |
|
|
int | static volatile sig_atomic_t got_sigttou; |
term_restore(int fd, int flush) | |
| /* |
| * 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) |
debug_decl(term_restore, SUDO_DEBUG_UTIL) |
|
|
if (changed) { |
if (changed) { |
int flags = TCSASOFT; | const int flags = flush ? (TCSASOFT|TCSAFLUSH) : (TCSASOFT|TCSADRAIN); |
flags |= flush ? TCSAFLUSH : TCSADRAIN; | if (tcsetattr_nobg(fd, flags, &oterm) != 0) |
if (tcsetattr(fd, flags, &oterm) != 0) | debug_return_bool(false); |
debug_return_int(0); | |
changed = 0; |
changed = 0; |
} |
} |
debug_return_int(1); | debug_return_bool(true); |
} |
} |
|
|
int | /* |
| * Disable terminal echo. |
| * Returns true on success or false on failure. |
| */ |
| bool |
term_noecho(int fd) |
term_noecho(int fd) |
{ |
{ |
debug_decl(term_noecho, SUDO_DEBUG_UTIL) |
debug_decl(term_noecho, SUDO_DEBUG_UTIL) |
|
|
|
again: |
if (!changed && tcgetattr(fd, &oterm) != 0) |
if (!changed && tcgetattr(fd, &oterm) != 0) |
debug_return_int(0); | debug_return_bool(false); |
(void) memcpy(&term, &oterm, sizeof(term)); |
(void) memcpy(&term, &oterm, sizeof(term)); |
CLR(term.c_lflag, ECHO|ECHONL); |
CLR(term.c_lflag, ECHO|ECHONL); |
#ifdef VSTATUS |
#ifdef VSTATUS |
term.c_cc[VSTATUS] = _POSIX_VDISABLE; |
term.c_cc[VSTATUS] = _POSIX_VDISABLE; |
#endif |
#endif |
if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) { | if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) { |
changed = 1; |
changed = 1; |
debug_return_int(1); | debug_return_bool(true); |
} |
} |
debug_return_int(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) |
term_raw(int fd, int isig) |
{ |
{ |
struct termios term; |
struct termios term; |
debug_decl(term_raw, SUDO_DEBUG_UTIL) |
debug_decl(term_raw, SUDO_DEBUG_UTIL) |
|
|
|
again: |
if (!changed && tcgetattr(fd, &oterm) != 0) |
if (!changed && tcgetattr(fd, &oterm) != 0) |
return 0; |
return 0; |
(void) memcpy(&term, &oterm, sizeof(term)); |
(void) memcpy(&term, &oterm, sizeof(term)); |
Line 118 term_raw(int fd, int isig)
|
Line 180 term_raw(int fd, int isig)
|
CLR(term.c_lflag, ECHO | ICANON | ISIG | IEXTEN); |
CLR(term.c_lflag, ECHO | ICANON | ISIG | IEXTEN); |
if (isig) |
if (isig) |
SET(term.c_lflag, ISIG); |
SET(term.c_lflag, ISIG); |
if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) { | if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) { |
changed = 1; |
changed = 1; |
debug_return_int(1); | debug_return_bool(true); |
} |
} |
debug_return_int(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) |
term_cbreak(int fd) |
{ |
{ |
debug_decl(term_cbreak, SUDO_DEBUG_UTIL) |
debug_decl(term_cbreak, SUDO_DEBUG_UTIL) |
|
|
|
again: |
if (!changed && tcgetattr(fd, &oterm) != 0) |
if (!changed && tcgetattr(fd, &oterm) != 0) |
return 0; |
return 0; |
(void) memcpy(&term, &oterm, sizeof(term)); |
(void) memcpy(&term, &oterm, sizeof(term)); |
/* Set terminal to half-cooked mode */ |
/* Set terminal to half-cooked mode */ |
term.c_cc[VMIN] = 1; |
term.c_cc[VMIN] = 1; |
term.c_cc[VTIME] = 0; |
term.c_cc[VTIME] = 0; |
|
/* cppcheck-suppress redundantAssignment */ |
CLR(term.c_lflag, ECHO | ECHONL | ICANON | IEXTEN); |
CLR(term.c_lflag, ECHO | ECHONL | ICANON | IEXTEN); |
|
/* cppcheck-suppress redundantAssignment */ |
SET(term.c_lflag, ISIG); |
SET(term.c_lflag, ISIG); |
#ifdef VSTATUS |
#ifdef VSTATUS |
term.c_cc[VSTATUS] = _POSIX_VDISABLE; |
term.c_cc[VSTATUS] = _POSIX_VDISABLE; |
#endif |
#endif |
if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) { | if (tcsetattr_nobg(fd, TCSADRAIN|TCSASOFT, &term) == 0) { |
term_erase = term.c_cc[VERASE]; |
term_erase = term.c_cc[VERASE]; |
term_kill = term.c_cc[VKILL]; |
term_kill = term.c_cc[VKILL]; |
changed = 1; |
changed = 1; |
debug_return_int(1); | debug_return_bool(true); |
} |
} |
debug_return_int(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) |
term_copy(int src, int dst) |
{ |
{ |
struct termios tt; |
struct termios tt; |
debug_decl(term_copy, SUDO_DEBUG_UTIL) |
debug_decl(term_copy, SUDO_DEBUG_UTIL) |
|
|
|
again: |
if (tcgetattr(src, &tt) != 0) |
if (tcgetattr(src, &tt) != 0) |
debug_return_int(0); | debug_return_bool(false); |
if (tcsetattr(dst, TCSANOW|TCSASOFT, &tt) != 0) | if (tcsetattr_nobg(dst, TCSANOW|TCSASOFT, &tt) == 0) |
debug_return_int(0); | debug_return_bool(true); |
debug_return_int(1); | if (got_sigttou) { |
| /* We were in the background, so oterm is probably bogus. */ |
| kill(getpid(), SIGTTOU); |
| goto again; |
| } |
| debug_return_bool(false); |
} |
} |