summaryrefslogtreecommitdiff
path: root/src/exec_pty.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exec_pty.c')
-rw-r--r--src/exec_pty.c745
1 files changed, 56 insertions, 689 deletions
diff --git a/src/exec_pty.c b/src/exec_pty.c
index e2fd9c60e..d8235fd85 100644
--- a/src/exec_pty.c
+++ b/src/exec_pty.c
@@ -48,82 +48,22 @@
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
-#ifndef __WALL
-# define __WALL 0
-#endif
-
-/* Evaluates to true if the event has /dev/tty as its fd. */
-#define USERTTY_EVENT(_ev) (sudo_ev_get_fd((_ev)) == io_fds[SFD_USERTTY])
-
-#define TERM_COOKED 0
-#define TERM_RAW 1
-
/* Tail queue of messages to send to the monitor. */
struct monitor_message {
TAILQ_ENTRY(monitor_message) entries;
struct command_status cstat;
};
TAILQ_HEAD(monitor_message_list, monitor_message);
-
-struct exec_closure_pty {
- struct command_details *details;
- struct sudo_event_base *evbase;
- struct sudo_event *backchannel_event;
- struct sudo_event *fwdchannel_event;
- struct sudo_event *sigint_event;
- struct sudo_event *sigquit_event;
- struct sudo_event *sigtstp_event;
- struct sudo_event *sigterm_event;
- struct sudo_event *sighup_event;
- struct sudo_event *sigalrm_event;
- struct sudo_event *sigusr1_event;
- struct sudo_event *sigusr2_event;
- struct sudo_event *sigchld_event;
- struct sudo_event *sigwinch_event;
- struct command_status *cstat;
- void *intercept;
- struct monitor_message_list monitor_messages;
- pid_t monitor_pid;
- pid_t cmnd_pid;
- pid_t ppgrp;
- short rows;
- short cols;
-};
-
-/*
- * I/O buffer with associated read/write events and a logging action.
- * Used to, e.g. pass data from the pty to the user's terminal
- * and any I/O logging plugins.
- */
-struct io_buffer;
-typedef bool (*sudo_io_action_t)(const char *, unsigned int, struct io_buffer *);
-struct io_buffer {
- SLIST_ENTRY(io_buffer) entries;
- struct exec_closure_pty *ec;
- struct sudo_event *revent;
- struct sudo_event *wevent;
- sudo_io_action_t action;
- int len; /* buffer length (how much produced) */
- int off; /* write position (how much already consumed) */
- char buf[64 * 1024];
-};
-SLIST_HEAD(io_buffer_list, io_buffer);
+static struct monitor_message_list monitor_messages =
+ TAILQ_HEAD_INITIALIZER(monitor_messages);
static char ptyname[PATH_MAX];
-int io_fds[6] = { -1, -1, -1, -1, -1, -1};
static bool foreground, pipeline;
-static int ttymode = TERM_COOKED;
-static sigset_t ttyblock;
-static struct io_buffer_list iobufs;
static const char *utmp_user;
-static void del_io_events(bool nonblocking);
-static void sync_ttysize(struct exec_closure_pty *ec);
-static int safe_close(int fd);
-static void ev_free_by_fd(struct sudo_event_base *evbase, int fd);
-static pid_t check_foreground(struct exec_closure_pty *ec);
-static void add_io_events(struct sudo_event_base *evbase);
-static void schedule_signal(struct exec_closure_pty *ec, int signo);
+static void sync_ttysize(struct exec_closure *ec);
+static pid_t check_foreground(struct exec_closure *ec);
+static void schedule_signal(struct exec_closure *ec, int signo);
/*
* Cleanup hook for sudo_fatal()/sudo_fatalx()
@@ -202,307 +142,6 @@ pty_make_controlling(void)
return 0;
}
-/* Call I/O plugin tty input log method. */
-static bool
-log_ttyin(const char *buf, unsigned int n, struct io_buffer *iob)
-{
- struct plugin_container *plugin;
- const char *errstr = NULL;
- sigset_t omask;
- bool ret = true;
- debug_decl(log_ttyin, SUDO_DEBUG_EXEC);
-
- sigprocmask(SIG_BLOCK, &ttyblock, &omask);
- TAILQ_FOREACH(plugin, &io_plugins, entries) {
- if (plugin->u.io->log_ttyin) {
- int rc;
-
- sudo_debug_set_active_instance(plugin->debug_instance);
- rc = plugin->u.io->log_ttyin(buf, n, &errstr);
- if (rc <= 0) {
- if (rc < 0) {
- /* Error: disable plugin's I/O function. */
- plugin->u.io->log_ttyin = NULL;
- audit_error(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("I/O plugin error"),
- iob->ec->details->info);
- } else {
- audit_reject(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("command rejected by I/O plugin"),
- iob->ec->details->info);
- }
- ret = false;
- break;
- }
- }
- }
- sudo_debug_set_active_instance(sudo_debug_instance);
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- debug_return_bool(ret);
-}
-
-/* Call I/O plugin stdin log method. */
-static bool
-log_stdin(const char *buf, unsigned int n, struct io_buffer *iob)
-{
- struct plugin_container *plugin;
- const char *errstr = NULL;
- sigset_t omask;
- bool ret = true;
- debug_decl(log_stdin, SUDO_DEBUG_EXEC);
-
- sigprocmask(SIG_BLOCK, &ttyblock, &omask);
- TAILQ_FOREACH(plugin, &io_plugins, entries) {
- if (plugin->u.io->log_stdin) {
- int rc;
-
- sudo_debug_set_active_instance(plugin->debug_instance);
- rc = plugin->u.io->log_stdin(buf, n, &errstr);
- if (rc <= 0) {
- if (rc < 0) {
- /* Error: disable plugin's I/O function. */
- plugin->u.io->log_stdin = NULL;
- audit_error(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("I/O plugin error"),
- iob->ec->details->info);
- } else {
- audit_reject(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("command rejected by I/O plugin"),
- iob->ec->details->info);
- }
- ret = false;
- break;
- }
- }
- }
- sudo_debug_set_active_instance(sudo_debug_instance);
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- debug_return_bool(ret);
-}
-
-/* Call I/O plugin tty output log method. */
-static bool
-log_ttyout(const char *buf, unsigned int n, struct io_buffer *iob)
-{
- struct plugin_container *plugin;
- const char *errstr = NULL;
- sigset_t omask;
- bool ret = true;
- debug_decl(log_ttyout, SUDO_DEBUG_EXEC);
-
- sigprocmask(SIG_BLOCK, &ttyblock, &omask);
- TAILQ_FOREACH(plugin, &io_plugins, entries) {
- if (plugin->u.io->log_ttyout) {
- int rc;
-
- sudo_debug_set_active_instance(plugin->debug_instance);
- rc = plugin->u.io->log_ttyout(buf, n, &errstr);
- if (rc <= 0) {
- if (rc < 0) {
- /* Error: disable plugin's I/O function. */
- plugin->u.io->log_ttyout = NULL;
- audit_error(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("I/O plugin error"),
- iob->ec->details->info);
- } else {
- audit_reject(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("command rejected by I/O plugin"),
- iob->ec->details->info);
- }
- ret = false;
- break;
- }
- }
- }
- sudo_debug_set_active_instance(sudo_debug_instance);
- if (!ret) {
- /*
- * I/O plugin rejected the output, delete the write event
- * (user's tty) so we do not display the rejected output.
- */
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: deleting and freeing devtty wevent %p", __func__, iob->wevent);
- sudo_ev_free(iob->wevent);
- iob->wevent = NULL;
- iob->off = iob->len = 0;
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- debug_return_bool(ret);
-}
-
-/* Call I/O plugin stdout log method. */
-static bool
-log_stdout(const char *buf, unsigned int n, struct io_buffer *iob)
-{
- struct plugin_container *plugin;
- const char *errstr = NULL;
- sigset_t omask;
- bool ret = true;
- debug_decl(log_stdout, SUDO_DEBUG_EXEC);
-
- sigprocmask(SIG_BLOCK, &ttyblock, &omask);
- TAILQ_FOREACH(plugin, &io_plugins, entries) {
- if (plugin->u.io->log_stdout) {
- int rc;
-
- sudo_debug_set_active_instance(plugin->debug_instance);
- rc = plugin->u.io->log_stdout(buf, n, &errstr);
- if (rc <= 0) {
- if (rc < 0) {
- /* Error: disable plugin's I/O function. */
- plugin->u.io->log_stdout = NULL;
- audit_error(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("I/O plugin error"),
- iob->ec->details->info);
- } else {
- audit_reject(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("command rejected by I/O plugin"),
- iob->ec->details->info);
- }
- ret = false;
- break;
- }
- }
- }
- sudo_debug_set_active_instance(sudo_debug_instance);
- if (!ret) {
- /*
- * I/O plugin rejected the output, delete the write event
- * (user's stdout) so we do not display the rejected output.
- */
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: deleting and freeing stdout wevent %p", __func__, iob->wevent);
- sudo_ev_free(iob->wevent);
- iob->wevent = NULL;
- iob->off = iob->len = 0;
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- debug_return_bool(ret);
-}
-
-/* Call I/O plugin stderr log method. */
-static bool
-log_stderr(const char *buf, unsigned int n, struct io_buffer *iob)
-{
- struct plugin_container *plugin;
- const char *errstr = NULL;
- sigset_t omask;
- bool ret = true;
- debug_decl(log_stderr, SUDO_DEBUG_EXEC);
-
- sigprocmask(SIG_BLOCK, &ttyblock, &omask);
- TAILQ_FOREACH(plugin, &io_plugins, entries) {
- if (plugin->u.io->log_stderr) {
- int rc;
-
- sudo_debug_set_active_instance(plugin->debug_instance);
- rc = plugin->u.io->log_stderr(buf, n, &errstr);
- if (rc <= 0) {
- if (rc < 0) {
- /* Error: disable plugin's I/O function. */
- plugin->u.io->log_stderr = NULL;
- audit_error(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("I/O plugin error"),
- iob->ec->details->info);
- } else {
- audit_reject(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("command rejected by I/O plugin"),
- iob->ec->details->info);
- }
- ret = false;
- break;
- }
- }
- }
- sudo_debug_set_active_instance(sudo_debug_instance);
- if (!ret) {
- /*
- * I/O plugin rejected the output, delete the write event
- * (user's stderr) so we do not display the rejected output.
- */
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: deleting and freeing stderr wevent %p", __func__, iob->wevent);
- sudo_ev_free(iob->wevent);
- iob->wevent = NULL;
- iob->off = iob->len = 0;
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- debug_return_bool(ret);
-}
-
-/* Call I/O plugin suspend log method. */
-static void
-log_suspend(struct exec_closure_pty *ec, int signo)
-{
- struct plugin_container *plugin;
- const char *errstr = NULL;
- sigset_t omask;
- debug_decl(log_suspend, SUDO_DEBUG_EXEC);
-
- sigprocmask(SIG_BLOCK, &ttyblock, &omask);
- TAILQ_FOREACH(plugin, &io_plugins, entries) {
- if (plugin->u.io->version < SUDO_API_MKVERSION(1, 13))
- continue;
- if (plugin->u.io->log_suspend) {
- int rc;
-
- sudo_debug_set_active_instance(plugin->debug_instance);
- rc = plugin->u.io->log_suspend(signo, &errstr);
- if (rc <= 0) {
- /* Error: disable plugin's I/O function. */
- plugin->u.io->log_suspend = NULL;
- audit_error(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("error logging suspend"),
- ec->details->info);
- break;
- }
- }
- }
- sudo_debug_set_active_instance(sudo_debug_instance);
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- debug_return;
-}
-
-/* Call I/O plugin window change log method. */
-static void
-log_winchange(struct exec_closure_pty *ec, unsigned int rows, unsigned int cols)
-{
- struct plugin_container *plugin;
- const char *errstr = NULL;
- sigset_t omask;
- debug_decl(log_winchange, SUDO_DEBUG_EXEC);
-
- sigprocmask(SIG_BLOCK, &ttyblock, &omask);
- TAILQ_FOREACH(plugin, &io_plugins, entries) {
- if (plugin->u.io->version < SUDO_API_MKVERSION(1, 12))
- continue;
- if (plugin->u.io->change_winsize) {
- int rc;
-
- sudo_debug_set_active_instance(plugin->debug_instance);
- rc = plugin->u.io->change_winsize(rows, cols, &errstr);
- if (rc <= 0) {
- /* Error: disable plugin's I/O function. */
- plugin->u.io->change_winsize = NULL;
- audit_error(plugin->name, SUDO_IO_PLUGIN,
- errstr ? errstr : _("error changing window size"),
- ec->details->info);
- break;
- }
- }
- }
- sudo_debug_set_active_instance(sudo_debug_instance);
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- debug_return;
-}
-
/*
* Check whether we are running in the foregroup.
* Updates the foreground global and updates the window size.
@@ -510,7 +149,7 @@ log_winchange(struct exec_closure_pty *ec, unsigned int rows, unsigned int cols)
* on success, or -1 on failure (tty revoked).
*/
static pid_t
-check_foreground(struct exec_closure_pty *ec)
+check_foreground(struct exec_closure *ec)
{
int ret = 0;
debug_decl(check_foreground, SUDO_DEBUG_EXEC);
@@ -532,7 +171,7 @@ check_foreground(struct exec_closure_pty *ec)
* foreground or SIGCONT_BG if it is a background process.
*/
static int
-suspend_sudo_pty(struct exec_closure_pty *ec, int signo)
+suspend_sudo_pty(struct exec_closure *ec, int signo)
{
char signame[SIG2STR_MAX];
struct sigaction sa, osa;
@@ -823,71 +462,25 @@ write_callback(int fd, int what, void *v)
debug_return;
}
-static void
-io_buf_new(int rfd, int wfd,
- bool (*action)(const char *, unsigned int, struct io_buffer *),
- struct exec_closure_pty *ec, struct io_buffer_list *head)
-{
- int n;
- struct io_buffer *iob;
- debug_decl(io_buf_new, SUDO_DEBUG_EXEC);
-
- /* Set non-blocking mode. */
- n = fcntl(rfd, F_GETFL, 0);
- if (n != -1 && !ISSET(n, O_NONBLOCK))
- (void) fcntl(rfd, F_SETFL, n | O_NONBLOCK);
- n = fcntl(wfd, F_GETFL, 0);
- if (n != -1 && !ISSET(n, O_NONBLOCK))
- (void) fcntl(wfd, F_SETFL, n | O_NONBLOCK);
-
- /* Allocate and add to head of list. */
- if ((iob = malloc(sizeof(*iob))) == NULL)
- sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- iob->ec = ec;
- iob->revent = sudo_ev_alloc(rfd, SUDO_EV_READ|SUDO_EV_PERSIST,
- read_callback, iob);
- iob->wevent = sudo_ev_alloc(wfd, SUDO_EV_WRITE|SUDO_EV_PERSIST,
- write_callback, iob);
- iob->len = 0;
- iob->off = 0;
- iob->action = action;
- iob->buf[0] = '\0';
- if (iob->revent == NULL || iob->wevent == NULL)
- sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- SLIST_INSERT_HEAD(head, iob, entries);
-
- debug_return;
-}
-
/*
* We already closed the follower so reads from the leader will not block.
*/
static void
pty_finish(struct command_status *cstat)
{
- struct io_buffer *iob;
- int n;
+ int flags;
debug_decl(pty_finish, SUDO_DEBUG_EXEC);
- /* Flush any remaining output (the plugin already got it). */
+ /* Flush any remaining output (the plugin already got it) and free bufs. */
if (io_fds[SFD_USERTTY] != -1) {
- n = fcntl(io_fds[SFD_USERTTY], F_GETFL, 0);
- if (n != -1 && ISSET(n, O_NONBLOCK)) {
- CLR(n, O_NONBLOCK);
- (void) fcntl(io_fds[SFD_USERTTY], F_SETFL, n);
+ flags = fcntl(io_fds[SFD_USERTTY], F_GETFL, 0);
+ if (flags != -1 && ISSET(flags, O_NONBLOCK)) {
+ CLR(flags, O_NONBLOCK);
+ (void) fcntl(io_fds[SFD_USERTTY], F_SETFL, flags);
}
}
del_io_events(false);
-
- /* Free I/O buffers. */
- while ((iob = SLIST_FIRST(&iobufs)) != NULL) {
- SLIST_REMOVE_HEAD(&iobufs, entries);
- if (iob->revent != NULL)
- sudo_ev_free(iob->revent);
- if (iob->wevent != NULL)
- sudo_ev_free(iob->wevent);
- free(iob);
- }
+ free_io_bufs();
/* Restore terminal settings. */
if (io_fds[SFD_USERTTY] != -1)
@@ -904,7 +497,7 @@ pty_finish(struct command_status *cstat)
* Send command status to the monitor (signal or window size change).
*/
static void
-send_command_status(struct exec_closure_pty *ec, int type, int val)
+send_command_status(struct exec_closure *ec, int type, int val)
{
struct monitor_message *msg;
debug_decl(send_command, SUDO_DEBUG_EXEC);
@@ -913,7 +506,7 @@ send_command_status(struct exec_closure_pty *ec, int type, int val)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
msg->cstat.type = type;
msg->cstat.val = val;
- TAILQ_INSERT_TAIL(&ec->monitor_messages, msg, entries);
+ TAILQ_INSERT_TAIL(&monitor_messages, msg, entries);
if (sudo_ev_add(ec->evbase, ec->fwdchannel_event, NULL, true) == -1)
sudo_fatal("%s", U_("unable to add event to queue"));
@@ -928,7 +521,7 @@ send_command_status(struct exec_closure_pty *ec, int type, int val)
* Schedule a signal to be forwarded.
*/
static void
-schedule_signal(struct exec_closure_pty *ec, int signo)
+schedule_signal(struct exec_closure *ec, int signo)
{
char signame[SIG2STR_MAX];
debug_decl(schedule_signal, SUDO_DEBUG_EXEC);
@@ -949,10 +542,27 @@ schedule_signal(struct exec_closure_pty *ec, int signo)
debug_return;
}
+/*
+ * Free any remaining monitor messages in the queue.
+ */
+static void
+flush_monitor_messages(void)
+{
+ struct monitor_message *msg;
+ debug_decl(flush_monitor_messages, SUDO_DEBUG_EXEC);
+
+ while ((msg = TAILQ_FIRST(&monitor_messages)) != NULL) {
+ TAILQ_REMOVE(&monitor_messages, msg, entries);
+ free(msg);
+ }
+
+ debug_return;
+}
+
static void
backchannel_cb(int fd, int what, void *v)
{
- struct exec_closure_pty *ec = v;
+ struct exec_closure *ec = v;
struct command_status cstat;
ssize_t nread;
debug_decl(backchannel_cb, SUDO_DEBUG_EXEC);
@@ -1060,7 +670,7 @@ backchannel_cb(int fd, int what, void *v)
* Handle changes to the monitors's status (SIGCHLD).
*/
static void
-handle_sigchld_pty(struct exec_closure_pty *ec)
+handle_sigchld_pty(struct exec_closure *ec)
{
int n, status;
pid_t pid;
@@ -1129,7 +739,7 @@ static void
signal_cb_pty(int signo, int what, void *v)
{
struct sudo_ev_siginfo_container *sc = v;
- struct exec_closure_pty *ec = sc->closure;
+ struct exec_closure *ec = sc->closure;
char signame[SIG2STR_MAX];
debug_decl(signal_cb_pty, SUDO_DEBUG_EXEC);
@@ -1180,13 +790,13 @@ signal_cb_pty(int signo, int what, void *v)
static void
fwdchannel_cb(int sock, int what, void *v)
{
- struct exec_closure_pty *ec = v;
+ struct exec_closure *ec = v;
char signame[SIG2STR_MAX];
struct monitor_message *msg;
ssize_t nsent;
debug_decl(fwdchannel_cb, SUDO_DEBUG_EXEC);
- while ((msg = TAILQ_FIRST(&ec->monitor_messages)) != NULL) {
+ while ((msg = TAILQ_FIRST(&monitor_messages)) != NULL) {
switch (msg->cstat.type) {
case CMD_SIGNO:
if (msg->cstat.val == SIGCONT_FG)
@@ -1209,7 +819,7 @@ fwdchannel_cb(int sock, int what, void *v)
msg->cstat.type, msg->cstat.val);
break;
}
- TAILQ_REMOVE(&ec->monitor_messages, msg, entries);
+ TAILQ_REMOVE(&monitor_messages, msg, entries);
nsent = send(sock, &msg->cstat, sizeof(msg->cstat), 0);
if (nsent != sizeof(msg->cstat)) {
if (errno == EPIPE) {
@@ -1217,10 +827,7 @@ fwdchannel_cb(int sock, int what, void *v)
"broken pipe writing to monitor over backchannel");
/* Other end of socket gone, empty out monitor_messages. */
free(msg);
- while ((msg = TAILQ_FIRST(&ec->monitor_messages)) != NULL) {
- TAILQ_REMOVE(&ec->monitor_messages, msg, entries);
- free(msg);
- }
+ flush_monitor_messages();
/* XXX - need new CMD_ type for monitor errors. */
ec->cstat->type = CMD_ERRNO;
ec->cstat->val = errno;
@@ -1238,10 +845,10 @@ fwdchannel_cb(int sock, int what, void *v)
* Forwarded signals on the backchannel are enabled on demand.
*/
static void
-fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
+fill_exec_closure(struct exec_closure *ec, struct command_status *cstat,
struct command_details *details, pid_t ppgrp, int backchannel)
{
- debug_decl(fill_exec_closure_pty, SUDO_DEBUG_EXEC);
+ debug_decl(fill_exec_closure, SUDO_DEBUG_EXEC);
/* Fill in the non-event part of the closure. */
ec->cmnd_pid = -1;
@@ -1250,7 +857,6 @@ fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
ec->details = details;
ec->rows = user_details.ts_rows;
ec->cols = user_details.ts_cols;
- TAILQ_INIT(&ec->monitor_messages);
/* Reset cstat for running the command. */
cstat->type = CMD_INVALID;
@@ -1353,40 +959,6 @@ fill_exec_closure_pty(struct exec_closure_pty *ec, struct command_status *cstat,
}
/*
- * Free the dynamically-allocated contents of the exec closure.
- */
-static void
-free_exec_closure_pty(struct exec_closure_pty *ec)
-{
- struct monitor_message *msg;
- debug_decl(free_exec_closure_pty, SUDO_DEBUG_EXEC);
-
- /* Free any remaining intercept resources. */
- intercept_cleanup();
-
- sudo_ev_base_free(ec->evbase);
- sudo_ev_free(ec->backchannel_event);
- sudo_ev_free(ec->fwdchannel_event);
- sudo_ev_free(ec->sigint_event);
- sudo_ev_free(ec->sigquit_event);
- sudo_ev_free(ec->sigtstp_event);
- sudo_ev_free(ec->sigterm_event);
- sudo_ev_free(ec->sighup_event);
- sudo_ev_free(ec->sigalrm_event);
- sudo_ev_free(ec->sigusr1_event);
- sudo_ev_free(ec->sigusr2_event);
- sudo_ev_free(ec->sigchld_event);
- sudo_ev_free(ec->sigwinch_event);
-
- while ((msg = TAILQ_FIRST(&ec->monitor_messages)) != NULL) {
- TAILQ_REMOVE(&ec->monitor_messages, msg, entries);
- free(msg);
- }
-
- debug_return;
-}
-
-/*
* Execute a command in a pty, potentially with I/O logging, and
* wait for it to finish.
* This is a little bit tricky due to how POSIX job control works and
@@ -1398,7 +970,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
int io_pipe[3][2] = { { -1, -1 }, { -1, -1 }, { -1, -1 } };
bool interpose[3] = { false, false, false };
int sv[2], intercept_sv[2] = { -1, -1 };
- struct exec_closure_pty ec = { 0 };
+ struct exec_closure ec = { 0 };
struct plugin_container *plugin;
int evloop_retries = -1;
sigset_t set, oset;
@@ -1408,14 +980,10 @@ exec_pty(struct command_details *details, struct command_status *cstat)
debug_decl(exec_pty, SUDO_DEBUG_EXEC);
/*
- * Allocate a pty.
+ * Allocate a pty if sudo is running in a terminal.
*/
- if (!pty_setup(details, user_details.tty)) {
- if (TAILQ_EMPTY(&io_plugins)) {
- /* Not logging I/O and didn't allocate a pty. */
- debug_return_bool(false);
- }
- }
+ if (!pty_setup(details, user_details.tty))
+ debug_return_bool(false);
/*
* We communicate with the monitor over a bi-directional pair of sockets.
@@ -1461,15 +1029,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
* Child will run the command in the pty, parent will pass data
* to and from pty.
*/
-
- /* So we can block tty-generated signals */
- sigemptyset(&ttyblock);
- sigaddset(&ttyblock, SIGINT);
- sigaddset(&ttyblock, SIGQUIT);
- sigaddset(&ttyblock, SIGTSTP);
- sigaddset(&ttyblock, SIGTTIN);
- sigaddset(&ttyblock, SIGTTOU);
-
+ init_ttyblock();
ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */
/* Determine whether any of std{in,out,err} should be logged. */
@@ -1495,12 +1055,12 @@ exec_pty(struct command_details *details, struct command_status *cstat)
/* Read from /dev/tty, write to pty leader */
if (!ISSET(details->flags, CD_BACKGROUND)) {
io_buf_new(io_fds[SFD_USERTTY], io_fds[SFD_LEADER],
- log_ttyin, &ec, &iobufs);
+ log_ttyin, read_callback, write_callback, &ec, &iobufs);
}
/* Read from pty leader, write to /dev/tty */
io_buf_new(io_fds[SFD_LEADER], io_fds[SFD_USERTTY],
- log_ttyout, &ec, &iobufs);
+ log_ttyout, read_callback, write_callback, &ec, &iobufs);
/* Are we the foreground process? */
foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;
@@ -1527,7 +1087,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
if (pipe2(io_pipe[STDIN_FILENO], O_CLOEXEC) != 0)
sudo_fatal("%s", U_("unable to create pipe"));
io_buf_new(STDIN_FILENO, io_pipe[STDIN_FILENO][1],
- log_stdin, &ec, &iobufs);
+ log_stdin, read_callback, write_callback, &ec, &iobufs);
io_fds[SFD_STDIN] = io_pipe[STDIN_FILENO][0];
}
}
@@ -1548,7 +1108,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
if (pipe2(io_pipe[STDOUT_FILENO], O_CLOEXEC) != 0)
sudo_fatal("%s", U_("unable to create pipe"));
io_buf_new(io_pipe[STDOUT_FILENO][0], STDOUT_FILENO,
- log_stdout, &ec, &iobufs);
+ log_stdout, read_callback, write_callback, &ec, &iobufs);
io_fds[SFD_STDOUT] = io_pipe[STDOUT_FILENO][1];
}
}
@@ -1568,7 +1128,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
if (pipe2(io_pipe[STDERR_FILENO], O_CLOEXEC) != 0)
sudo_fatal("%s", U_("unable to create pipe"));
io_buf_new(io_pipe[STDERR_FILENO][0], STDERR_FILENO,
- log_stderr, &ec, &iobufs);
+ log_stderr, read_callback, write_callback, &ec, &iobufs);
io_fds[SFD_STDERR] = io_pipe[STDERR_FILENO][1];
}
}
@@ -1676,7 +1236,7 @@ exec_pty(struct command_details *details, struct command_status *cstat)
* Fill in exec closure, allocate event base, signal events and
* the backchannel event.
*/
- fill_exec_closure_pty(&ec, cstat, details, ppgrp, sv[0]);
+ fill_exec_closure(&ec, cstat, details, ppgrp, sv[0]);
/* Create event and closure for intercept mode. */
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
@@ -1734,156 +1294,17 @@ exec_pty(struct command_details *details, struct command_status *cstat)
pty_finish(cstat);
/* Free things up. */
- free_exec_closure_pty(&ec);
+ free_exec_closure(&ec);
debug_return_bool(true);
}
/*
- * Schedule I/O events before starting the main event loop or
- * resuming from suspend.
- */
-static void
-add_io_events(struct sudo_event_base *evbase)
-{
- struct io_buffer *iob;
- debug_decl(add_io_events, SUDO_DEBUG_EXEC);
-
- /*
- * Schedule all readers as long as the buffer is not full.
- * Schedule writers that contain buffered data.
- * Normally, write buffers are added on demand when data is read.
- */
- SLIST_FOREACH(iob, &iobufs, entries) {
- /* Don't read from /dev/tty if we are not in the foreground. */
- if (iob->revent != NULL &&
- (ttymode == TERM_RAW || !USERTTY_EVENT(iob->revent))) {
- if (iob->len != sizeof(iob->buf)) {
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "added I/O revent %p, fd %d, events %d",
- iob->revent, iob->revent->fd, iob->revent->events);
- if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
- sudo_fatal("%s", U_("unable to add event to queue"));
- }
- }
- if (iob->wevent != NULL) {
- /* Enable writer if buffer is not empty. */
- if (iob->len > iob->off) {
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "added I/O wevent %p, fd %d, events %d",
- iob->wevent, iob->wevent->fd, iob->wevent->events);
- if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
- sudo_fatal("%s", U_("unable to add event to queue"));
- }
- }
- }
- debug_return;
-}
-
-/*
- * Flush any output buffered in iobufs or readable from fds other
- * than /dev/tty. Removes I/O events from the event base when done.
- */
-static void
-del_io_events(bool nonblocking)
-{
- struct io_buffer *iob;
- struct sudo_event_base *evbase;
- debug_decl(del_io_events, SUDO_DEBUG_EXEC);
-
- /* Remove iobufs from existing event base. */
- SLIST_FOREACH(iob, &iobufs, entries) {
- if (iob->revent != NULL) {
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "deleted I/O revent %p, fd %d, events %d",
- iob->revent, iob->revent->fd, iob->revent->events);
- sudo_ev_del(NULL, iob->revent);
- }
- if (iob->wevent != NULL) {
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "deleted I/O wevent %p, fd %d, events %d",
- iob->wevent, iob->wevent->fd, iob->wevent->events);
- sudo_ev_del(NULL, iob->wevent);
- }
- }
-
- /* Create temporary event base for flushing. */
- evbase = sudo_ev_base_alloc();
- if (evbase == NULL)
- sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-
- /* Avoid reading from /dev/tty, just flush existing data. */
- SLIST_FOREACH(iob, &iobufs, entries) {
- /* Don't read from /dev/tty while flushing. */
- if (iob->revent != NULL && !USERTTY_EVENT(iob->revent)) {
- if (iob->len != sizeof(iob->buf)) {
- if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
- sudo_fatal("%s", U_("unable to add event to queue"));
- }
- }
- /* Flush any write buffers with data in them. */
- if (iob->wevent != NULL) {
- if (iob->len > iob->off) {
- if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
- sudo_fatal("%s", U_("unable to add event to queue"));
- }
- }
- }
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: flushing remaining I/O buffers (nonblocking)", __func__);
- (void) sudo_ev_loop(evbase, SUDO_EVLOOP_NONBLOCK);
-
- /*
- * If not in non-blocking mode, make sure we flush write buffers.
- * We don't want to read from the pty or stdin since that might block
- * and the command is no longer running anyway.
- */
- if (!nonblocking) {
- /* Clear out iobufs from event base. */
- SLIST_FOREACH(iob, &iobufs, entries) {
- if (iob->revent != NULL && !USERTTY_EVENT(iob->revent))
- sudo_ev_del(evbase, iob->revent);
- if (iob->wevent != NULL)
- sudo_ev_del(evbase, iob->wevent);
- }
-
- SLIST_FOREACH(iob, &iobufs, entries) {
- /* Flush any write buffers with data in them. */
- if (iob->wevent != NULL) {
- if (iob->len > iob->off) {
- if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
- sudo_fatal("%s", U_("unable to add event to queue"));
- }
- }
- }
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: flushing remaining write buffers (blocking)", __func__);
- (void) sudo_ev_dispatch(evbase);
-
- /* We should now have flushed all write buffers. */
- SLIST_FOREACH(iob, &iobufs, entries) {
- if (iob->wevent != NULL) {
- if (iob->len > iob->off) {
- sudo_debug_printf(SUDO_DEBUG_ERROR,
- "unflushed data: wevent %p, fd %d, events %d",
- iob->wevent, iob->wevent->fd, iob->wevent->events);
- }
- }
- }
- }
-
- /* Free temporary event base, removing its events. */
- sudo_ev_base_free(evbase);
-
- debug_return;
-}
-
-/*
* Check for tty size changes.
* Passes the new window size to the I/O plugin and to the monitor.
*/
static void
-sync_ttysize(struct exec_closure_pty *ec)
+sync_ttysize(struct exec_closure *ec)
{
struct winsize wsize;
debug_decl(sync_ttysize, SUDO_DEBUG_EXEC);
@@ -1907,57 +1328,3 @@ sync_ttysize(struct exec_closure_pty *ec)
debug_return;
}
-
-/*
- * Remove and free any events associated with the specified
- * file descriptor present in the I/O buffers list.
- */
-static void
-ev_free_by_fd(struct sudo_event_base *evbase, int fd)
-{
- struct io_buffer *iob;
- debug_decl(ev_free_by_fd, SUDO_DEBUG_EXEC);
-
- /* Deschedule any users of the fd and free up the events. */
- SLIST_FOREACH(iob, &iobufs, entries) {
- if (iob->revent != NULL) {
- if (sudo_ev_get_fd(iob->revent) == fd) {
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: deleting and freeing revent %p with fd %d",
- __func__, iob->revent, fd);
- sudo_ev_free(iob->revent);
- iob->revent = NULL;
- }
- }
- if (iob->wevent != NULL) {
- if (sudo_ev_get_fd(iob->wevent) == fd) {
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: deleting and freeing wevent %p with fd %d",
- __func__, iob->wevent, fd);
- sudo_ev_free(iob->wevent);
- iob->wevent = NULL;
- }
- }
- }
- debug_return;
-}
-
-/*
- * Only close the fd if it is not /dev/tty or std{in,out,err}.
- * Return value is the same as close(2).
- */
-static int
-safe_close(int fd)
-{
- debug_decl(safe_close, SUDO_DEBUG_EXEC);
-
- /* Avoid closing /dev/tty or std{in,out,err}. */
- if (fd < 3 || fd == io_fds[SFD_USERTTY]) {
- sudo_debug_printf(SUDO_DEBUG_INFO,
- "%s: not closing fd %d (%s)", __func__, fd, _PATH_TTY);
- errno = EINVAL;
- debug_return_int(-1);
- }
- sudo_debug_printf(SUDO_DEBUG_INFO, "%s: closing fd %d", __func__, fd);
- debug_return_int(close(fd));
-}