Handle receipt of SIGTTIN/SIGTTOU when reading/writing from/to the tty.
We can't use a signal event for these since that would restart the system call after the signal was handled and the callback would not get a chance to run. Fixes running a command in the background that write to the tty when the TOSTOP terminal flag is set.
This commit is contained in:
@@ -110,6 +110,7 @@ static int safe_close(int fd);
|
|||||||
static void ev_free_by_fd(struct sudo_event_base *evbase, int fd);
|
static void ev_free_by_fd(struct sudo_event_base *evbase, int fd);
|
||||||
static void check_foreground(pid_t ppgrp);
|
static void check_foreground(pid_t ppgrp);
|
||||||
static void add_io_events(struct sudo_event_base *evbase);
|
static void add_io_events(struct sudo_event_base *evbase);
|
||||||
|
static void schedule_signal(struct exec_closure_pty *ec, int signo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cleanup hook for sudo_fatal()/sudo_fatalx()
|
* Cleanup hook for sudo_fatal()/sudo_fatalx()
|
||||||
@@ -517,6 +518,17 @@ suspend_sudo(int signo, pid_t ppgrp)
|
|||||||
debug_return_int(ret);
|
debug_return_int(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SIGTTIN signal handler for read_callback that just sets a flag.
|
||||||
|
*/
|
||||||
|
static volatile sig_atomic_t got_sigttin;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigttin(int signo)
|
||||||
|
{
|
||||||
|
got_sigttin = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read an iobuf that is ready.
|
* Read an iobuf that is ready.
|
||||||
*/
|
*/
|
||||||
@@ -524,17 +536,36 @@ static void
|
|||||||
read_callback(int fd, int what, void *v)
|
read_callback(int fd, int what, void *v)
|
||||||
{
|
{
|
||||||
struct io_buffer *iob = v;
|
struct io_buffer *iob = v;
|
||||||
struct sudo_event_base *evbase;
|
struct sudo_event_base *evbase = sudo_ev_get_base(iob->revent);
|
||||||
int n;
|
struct sigaction sa, osa;
|
||||||
|
int saved_errno;
|
||||||
|
ssize_t n;
|
||||||
debug_decl(read_callback, SUDO_DEBUG_EXEC);
|
debug_decl(read_callback, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
evbase = sudo_ev_get_base(iob->revent);
|
/*
|
||||||
do {
|
* We ignore SIGTTIN by default but we need to handle it when reading
|
||||||
n = read(fd, iob->buf + iob->len, sizeof(iob->buf) - iob->len);
|
* from the terminal. A signal event won't work here because the
|
||||||
} while (n == -1 && errno == EINTR);
|
* read() would be restarted, preventing the callback from running.
|
||||||
|
*/
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_handler = sigttin;
|
||||||
|
got_sigttin = 0;
|
||||||
|
sigaction(SIGTTIN, &sa, &osa);
|
||||||
|
n = read(fd, iob->buf + iob->len, sizeof(iob->buf) - iob->len);
|
||||||
|
saved_errno = errno;
|
||||||
|
sigaction(SIGTTIN, &osa, NULL);
|
||||||
|
errno = saved_errno;
|
||||||
|
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case -1:
|
case -1:
|
||||||
if (errno == EAGAIN)
|
if (got_sigttin) {
|
||||||
|
/* Schedule SIGTTIN to be forwared to the command. */
|
||||||
|
schedule_signal(iob->ec, SIGTTIN);
|
||||||
|
/* Restart event loop to service signal immediately. */
|
||||||
|
sudo_ev_loopcontinue(evbase);
|
||||||
|
}
|
||||||
|
if (errno == EAGAIN || errno == EINTR)
|
||||||
break;
|
break;
|
||||||
/* treat read error as fatal and close the fd */
|
/* treat read error as fatal and close the fd */
|
||||||
sudo_debug_printf(SUDO_DEBUG_ERROR,
|
sudo_debug_printf(SUDO_DEBUG_ERROR,
|
||||||
@@ -557,7 +588,7 @@ read_callback(int fd, int what, void *v)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO,
|
sudo_debug_printf(SUDO_DEBUG_INFO,
|
||||||
"read %d bytes from fd %d", n, fd);
|
"read %zd bytes from fd %d", n, fd);
|
||||||
if (!iob->action(iob->buf + iob->len, n, iob)) {
|
if (!iob->action(iob->buf + iob->len, n, iob)) {
|
||||||
terminate_command(iob->ec->cmnd_pid, true);
|
terminate_command(iob->ec->cmnd_pid, true);
|
||||||
iob->ec->cmnd_pid = -1;
|
iob->ec->cmnd_pid = -1;
|
||||||
@@ -577,6 +608,17 @@ read_callback(int fd, int what, void *v)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SIGTTOU signal handler for write_callback that just sets a flag.
|
||||||
|
*/
|
||||||
|
static volatile sig_atomic_t got_sigttou;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigttou(int signo)
|
||||||
|
{
|
||||||
|
got_sigttou = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write an iobuf that is ready.
|
* Write an iobuf that is ready.
|
||||||
*/
|
*/
|
||||||
@@ -584,14 +626,27 @@ static void
|
|||||||
write_callback(int fd, int what, void *v)
|
write_callback(int fd, int what, void *v)
|
||||||
{
|
{
|
||||||
struct io_buffer *iob = v;
|
struct io_buffer *iob = v;
|
||||||
struct sudo_event_base *evbase;
|
struct sudo_event_base *evbase = sudo_ev_get_base(iob->wevent);
|
||||||
int n;
|
struct sigaction sa, osa;
|
||||||
|
int saved_errno;
|
||||||
|
ssize_t n;
|
||||||
debug_decl(write_callback, SUDO_DEBUG_EXEC);
|
debug_decl(write_callback, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
evbase = sudo_ev_get_base(iob->wevent);
|
/*
|
||||||
do {
|
* We ignore SIGTTOU by default but we need to handle it when writing
|
||||||
n = write(fd, iob->buf + iob->off, iob->len - iob->off);
|
* to the terminal. A signal event won't work here because the
|
||||||
} while (n == -1 && errno == EINTR);
|
* write() would be restarted, preventing the callback from running.
|
||||||
|
*/
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_handler = sigttou;
|
||||||
|
got_sigttou = 0;
|
||||||
|
sigaction(SIGTTOU, &sa, &osa);
|
||||||
|
n = write(fd, iob->buf + iob->off, iob->len - iob->off);
|
||||||
|
saved_errno = errno;
|
||||||
|
sigaction(SIGTTOU, &osa, NULL);
|
||||||
|
errno = saved_errno;
|
||||||
|
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case EPIPE:
|
case EPIPE:
|
||||||
@@ -610,6 +665,14 @@ write_callback(int fd, int what, void *v)
|
|||||||
safe_close(fd);
|
safe_close(fd);
|
||||||
ev_free_by_fd(evbase, fd);
|
ev_free_by_fd(evbase, fd);
|
||||||
break;
|
break;
|
||||||
|
case EINTR:
|
||||||
|
if (got_sigttou) {
|
||||||
|
/* Schedule SIGTTOU to be forwared to the command. */
|
||||||
|
schedule_signal(iob->ec, SIGTTOU);
|
||||||
|
/* Restart event loop to service signal immediately. */
|
||||||
|
sudo_ev_loopcontinue(evbase);
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
case EAGAIN:
|
case EAGAIN:
|
||||||
/* not an error */
|
/* not an error */
|
||||||
break;
|
break;
|
||||||
@@ -624,7 +687,7 @@ write_callback(int fd, int what, void *v)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO,
|
sudo_debug_printf(SUDO_DEBUG_INFO,
|
||||||
"wrote %d bytes to fd %d", n, fd);
|
"wrote %zd bytes to fd %d", n, fd);
|
||||||
iob->off += n;
|
iob->off += n;
|
||||||
/* Reset buffer if fully consumed. */
|
/* Reset buffer if fully consumed. */
|
||||||
if (iob->off == iob->len) {
|
if (iob->off == iob->len) {
|
||||||
|
Reference in New Issue
Block a user