Replace tty_mode global with term_raw flag in struct exec_closure.

The pty_cleanup hook needs access to the closure so add
pty_cleanup_init() to store a pointer to the closure for use
by pty_cleanup_hook().
This commit is contained in:
Todd C. Miller
2023-03-24 14:44:17 -06:00
parent b81c5e8dac
commit 778688d4fc
4 changed files with 93 additions and 57 deletions

View File

@@ -36,8 +36,6 @@
#include "sudo_plugin.h" #include "sudo_plugin.h"
#include "sudo_plugin_int.h" #include "sudo_plugin_int.h"
int ttymode = TERM_COOKED;
struct io_buffer_list iobufs = SLIST_HEAD_INITIALIZER(&iobufs); struct io_buffer_list iobufs = SLIST_HEAD_INITIALIZER(&iobufs);
int io_fds[6] = { -1, -1, -1, -1, -1, -1 }; int io_fds[6] = { -1, -1, -1, -1, -1, -1 };
@@ -144,7 +142,7 @@ io_buf_new(int rfd, int wfd,
* resuming from suspend. * resuming from suspend.
*/ */
void void
add_io_events(struct sudo_event_base *evbase) add_io_events(struct exec_closure *ec)
{ {
struct io_buffer *iob; struct io_buffer *iob;
debug_decl(add_io_events, SUDO_DEBUG_EXEC); debug_decl(add_io_events, SUDO_DEBUG_EXEC);
@@ -157,12 +155,12 @@ add_io_events(struct sudo_event_base *evbase)
SLIST_FOREACH(iob, &iobufs, entries) { SLIST_FOREACH(iob, &iobufs, entries) {
/* Don't read from /dev/tty if we are not in the foreground. */ /* Don't read from /dev/tty if we are not in the foreground. */
if (iob->revent != NULL && if (iob->revent != NULL &&
(ttymode == TERM_RAW || !USERTTY_EVENT(iob->revent))) { (ec->term_raw || !USERTTY_EVENT(iob->revent))) {
if (iob->len != sizeof(iob->buf)) { if (iob->len != sizeof(iob->buf)) {
sudo_debug_printf(SUDO_DEBUG_INFO, sudo_debug_printf(SUDO_DEBUG_INFO,
"added I/O revent %p, fd %d, events %d", "added I/O revent %p, fd %d, events %d",
iob->revent, iob->revent->fd, iob->revent->events); iob->revent, iob->revent->fd, iob->revent->events);
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1) if (sudo_ev_add(ec->evbase, iob->revent, NULL, false) == -1)
sudo_fatal("%s", U_("unable to add event to queue")); sudo_fatal("%s", U_("unable to add event to queue"));
} }
} }
@@ -172,7 +170,7 @@ add_io_events(struct sudo_event_base *evbase)
sudo_debug_printf(SUDO_DEBUG_INFO, sudo_debug_printf(SUDO_DEBUG_INFO,
"added I/O wevent %p, fd %d, events %d", "added I/O wevent %p, fd %d, events %d",
iob->wevent, iob->wevent->fd, iob->wevent->events); iob->wevent, iob->wevent->fd, iob->wevent->events);
if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1) if (sudo_ev_add(ec->evbase, iob->wevent, NULL, false) == -1)
sudo_fatal("%s", U_("unable to add event to queue")); sudo_fatal("%s", U_("unable to add event to queue"));
} }
} }

View File

@@ -1,7 +1,7 @@
/* /*
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* Copyright (c) 2009-2022 Todd C. Miller <Todd.Miller@sudo.ws> * Copyright (c) 2009-2023 Todd C. Miller <Todd.Miller@sudo.ws>
* *
* 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
@@ -680,7 +680,7 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
} }
/* Enable any I/O log events. */ /* Enable any I/O log events. */
add_io_events(ec.evbase); add_io_events(&ec);
/* Restore signal mask now that signal handlers are setup. */ /* Restore signal mask now that signal handlers are setup. */
sigprocmask(SIG_SETMASK, &oset, NULL); sigprocmask(SIG_SETMASK, &oset, NULL);

View File

@@ -64,24 +64,6 @@ static const char *utmp_user;
static void sync_ttysize(struct exec_closure *ec); static void sync_ttysize(struct exec_closure *ec);
static void schedule_signal(struct exec_closure *ec, int signo); static void schedule_signal(struct exec_closure *ec, int signo);
/*
* Cleanup hook for sudo_fatal()/sudo_fatalx()
*/
static void
pty_cleanup(void)
{
debug_decl(cleanup, SUDO_DEBUG_EXEC);
if (ttymode != TERM_COOKED) {
if (!sudo_term_restore(io_fds[SFD_USERTTY], false))
sudo_warn("%s", U_("unable to restore terminal settings"));
}
if (utmp_user != NULL)
utmp_logout(ptyname, 0);
debug_return;
}
/* /*
* Allocate a pty if /dev/tty is a tty. * Allocate a pty if /dev/tty is a tty.
* Fills in io_fds[SFD_USERTTY], io_fds[SFD_LEADER], io_fds[SFD_FOLLOWER] * Fills in io_fds[SFD_USERTTY], io_fds[SFD_LEADER], io_fds[SFD_FOLLOWER]
@@ -121,6 +103,68 @@ pty_setup(struct command_details *details, const char *tty)
debug_return_bool(true); debug_return_bool(true);
} }
/*
* Restore user's terminal settings and update utmp, as needed.
*/
static void
pty_cleanup_int(struct exec_closure *ec, int wstatus, bool init_only)
{
static struct exec_closure *saved_ec;
debug_decl(pty_cleanup, SUDO_DEBUG_EXEC);
/* If initializing, just store a pointer to the closure and return. */
if (init_only) {
saved_ec = ec;
debug_return;
}
/* Use the stored closure if one is not specified. */
if (ec == NULL) {
if (saved_ec == NULL)
debug_return;
ec = saved_ec;
}
/* Restore terminal settings. */
if (ec->term_raw) {
/* Only restore the terminal if sudo is the foreground process. */
const pid_t tcpgrp = tcgetpgrp(io_fds[SFD_USERTTY]);
if (tcpgrp == ec->ppgrp) {
if (sudo_term_restore(io_fds[SFD_USERTTY], false))
ec->term_raw = false;
else
sudo_warn("%s", U_("unable to restore terminal settings"));
}
}
/* Update utmp */
if (utmp_user != NULL)
utmp_logout(ptyname, wstatus);
debug_return;
}
static inline void
pty_cleanup(struct exec_closure *ec, int wstatus)
{
pty_cleanup_int(ec, wstatus, false);
}
static inline void
pty_cleanup_init(struct exec_closure *ec)
{
pty_cleanup_int(ec, 0, true);
}
/*
* Cleanup hook for sudo_fatal()/sudo_fatalx()
*/
static void
pty_cleanup_hook(void)
{
pty_cleanup_int(NULL, 0, false);
}
/* /*
* Make the tty follower the controlling tty. * Make the tty follower the controlling tty.
* This is only used by the monitor but ptyname[] is static. * This is only used by the monitor but ptyname[] is static.
@@ -184,17 +228,18 @@ resume_terminal(struct exec_closure *ec)
} }
sync_ttysize(ec); sync_ttysize(ec);
sudo_debug_printf(SUDO_DEBUG_INFO, "parent is in %s, ttymode %d -> %d", sudo_debug_printf(SUDO_DEBUG_INFO, "parent is in %s (%s -> %s)",
ec->foreground ? "foreground" : "background", ttymode, ec->foreground ? "foreground" : "background",
ec->foreground ? TERM_RAW : TERM_COOKED); ec->term_raw ? "raw" : "cooked",
ec->foreground ? "raw" : "cooked");
if (ec->foreground) { if (ec->foreground) {
/* Foreground process, set tty to raw mode. */ /* Foreground process, set tty to raw mode. */
if (sudo_term_raw(io_fds[SFD_USERTTY], 0)) if (sudo_term_raw(io_fds[SFD_USERTTY], 0))
ttymode = TERM_RAW; ec->term_raw = true;
} else { } else {
/* Background process, no access to tty. */ /* Background process, no access to tty. */
ttymode = TERM_COOKED; ec->term_raw = false;
} }
debug_return_bool(true); debug_return_bool(true);
@@ -244,9 +289,9 @@ suspend_sudo_pty(struct exec_closure *ec, int signo)
sudo_debug_printf(SUDO_DEBUG_INFO, sudo_debug_printf(SUDO_DEBUG_INFO,
"%s: command received SIG%s, parent running in the foregound", "%s: command received SIG%s, parent running in the foregound",
__func__, signame); __func__, signame);
if (ttymode != TERM_RAW) { if (!ec->term_raw) {
if (sudo_term_raw(io_fds[SFD_USERTTY], 0)) if (sudo_term_raw(io_fds[SFD_USERTTY], 0))
ttymode = TERM_RAW; ec->term_raw = true;
} }
ret = SIGCONT_FG; /* resume command in foreground */ ret = SIGCONT_FG; /* resume command in foreground */
break; break;
@@ -259,8 +304,10 @@ suspend_sudo_pty(struct exec_closure *ec, int signo)
del_io_events(true); del_io_events(true);
/* Restore original tty mode before suspending. */ /* Restore original tty mode before suspending. */
if (ttymode != TERM_COOKED) { if (ec->term_raw) {
if (!sudo_term_restore(io_fds[SFD_USERTTY], false)) if (sudo_term_restore(io_fds[SFD_USERTTY], false))
ec->term_raw = false;
else
sudo_warn("%s", U_("unable to restore terminal settings")); sudo_warn("%s", U_("unable to restore terminal settings"));
} }
@@ -317,7 +364,7 @@ suspend_sudo_pty(struct exec_closure *ec, int signo)
* command will be runnable. Otherwise, we can get into a * command will be runnable. Otherwise, we can get into a
* situation where the command will immediately suspend itself. * situation where the command will immediately suspend itself.
*/ */
ret = ttymode == TERM_RAW ? SIGCONT_FG : SIGCONT_BG; ret = ec->term_raw ? SIGCONT_FG : SIGCONT_BG;
break; break;
} }
@@ -512,7 +559,7 @@ write_callback(int fd, int what, void *v)
*/ */
if (iob->revent != NULL && iob->len != sizeof(iob->buf)) { if (iob->revent != NULL && iob->len != sizeof(iob->buf)) {
if (!USERTTY_EVENT(iob->revent) || if (!USERTTY_EVENT(iob->revent) ||
(ttymode == TERM_RAW && iob->ec->cmnd_pid != -1)) { (iob->ec->term_raw && iob->ec->cmnd_pid != -1)) {
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1) if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
sudo_fatal("%s", U_("unable to add event to queue")); sudo_fatal("%s", U_("unable to add event to queue"));
} }
@@ -542,19 +589,8 @@ pty_finish(struct exec_closure *ec, struct command_status *cstat)
del_io_events(false); del_io_events(false);
free_io_bufs(); free_io_bufs();
/* Restore terminal settings. */ /* Restore terminal settings and update utmp. */
if (ttymode != TERM_COOKED) { pty_cleanup(ec, cstat->type == CMD_WSTATUS ? cstat->val : 0);
/* Only restore the terminal if sudo is the foreground process. */
const pid_t tcpgrp = tcgetpgrp(io_fds[SFD_USERTTY]);
if (tcpgrp == ec->ppgrp) {
if (!sudo_term_restore(io_fds[SFD_USERTTY], false))
sudo_warn("%s", U_("unable to restore terminal settings"));
}
}
/* Update utmp */
if (utmp_user != NULL)
utmp_logout(ptyname, cstat->type == CMD_WSTATUS ? cstat->val : 0);
debug_return; debug_return;
} }
@@ -703,7 +739,7 @@ backchannel_cb(int fd, int what, void *v)
signo = suspend_sudo_pty(ec, WSTOPSIG(cstat.val)); signo = suspend_sudo_pty(ec, WSTOPSIG(cstat.val));
schedule_signal(ec, signo); schedule_signal(ec, signo);
/* Re-enable I/O events */ /* Re-enable I/O events */
add_io_events(ec->evbase); add_io_events(ec);
} else { } else {
/* Command exited or was killed, either way we are done. */ /* Command exited or was killed, either way we are done. */
sudo_debug_printf(SUDO_DEBUG_INFO, "command exited or was killed"); sudo_debug_printf(SUDO_DEBUG_INFO, "command exited or was killed");
@@ -798,7 +834,7 @@ handle_sigchld_pty(struct exec_closure *ec)
kill(pid, SIGCONT); kill(pid, SIGCONT);
schedule_signal(ec, n); schedule_signal(ec, n);
/* Re-enable I/O events */ /* Re-enable I/O events */
add_io_events(ec->evbase); add_io_events(ec);
} else { } else {
sudo_debug_printf(SUDO_DEBUG_WARN, sudo_debug_printf(SUDO_DEBUG_WARN,
"%s: unexpected wait status 0x%x for process (%d)", "%s: unexpected wait status 0x%x for process (%d)",
@@ -1076,7 +1112,8 @@ exec_pty(struct command_details *details, struct command_status *cstat)
debug_return_bool(false); debug_return_bool(false);
/* Register cleanup function */ /* Register cleanup function */
sudo_fatal_callback_register(pty_cleanup); pty_cleanup_init(&ec);
sudo_fatal_callback_register(pty_cleanup_hook);
/* /*
* We communicate with the monitor over a bi-directional pair of sockets. * We communicate with the monitor over a bi-directional pair of sockets.
@@ -1253,7 +1290,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
if (ec.foreground) { if (ec.foreground) {
if (!pipeline && !ISSET(details->flags, CD_EXEC_BG)) { if (!pipeline && !ISSET(details->flags, CD_EXEC_BG)) {
if (sudo_term_raw(io_fds[SFD_USERTTY], 0)) if (sudo_term_raw(io_fds[SFD_USERTTY], 0))
ttymode = TERM_RAW; ec.term_raw = true;
} }
} }
@@ -1289,7 +1326,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
close(io_pipe[STDERR_FILENO][0]); close(io_pipe[STDERR_FILENO][0]);
/* Only run the cleanup hook in the parent. */ /* Only run the cleanup hook in the parent. */
sudo_fatal_callback_deregister(pty_cleanup); sudo_fatal_callback_deregister(pty_cleanup_hook);
/* /*
* If stdin/stdout is not a tty, start command in the background * If stdin/stdout is not a tty, start command in the background
@@ -1372,7 +1409,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
* and pass output from leader to stdout and IO plugin. * and pass output from leader to stdout and IO plugin.
* Try to recover on ENXIO, it means the tty was revoked. * Try to recover on ENXIO, it means the tty was revoked.
*/ */
add_io_events(ec.evbase); add_io_events(&ec);
do { do {
if (sudo_ev_dispatch(ec.evbase) == -1) if (sudo_ev_dispatch(ec.evbase) == -1)
sudo_warn("%s", U_("error in event loop")); sudo_warn("%s", U_("error in event loop"));

View File

@@ -81,6 +81,7 @@ struct exec_closure {
short rows; short rows;
short cols; short cols;
bool foreground; bool foreground;
bool term_raw;
}; };
/* /*
@@ -201,7 +202,7 @@ void io_buf_new(int rfd, int wfd, bool (*action)(const char *, unsigned int, str
int safe_close(int fd); int safe_close(int fd);
void ev_free_by_fd(struct sudo_event_base *evbase, int fd); void ev_free_by_fd(struct sudo_event_base *evbase, int fd);
void free_io_bufs(void); void free_io_bufs(void);
void add_io_events(struct sudo_event_base *evbase); void add_io_events(struct exec_closure *ec);
void del_io_events(bool nonblocking); void del_io_events(bool nonblocking);
void init_ttyblock(void); void init_ttyblock(void);
extern struct io_buffer_list iobufs; extern struct io_buffer_list iobufs;