Merge tag 'debian/1.8.5p2-1' into squeeze
[debian/sudo] / exec_pty.c
diff --git a/exec_pty.c b/exec_pty.c
deleted file mode 100644 (file)
index 2d6b0e8..0000000
+++ /dev/null
@@ -1,1057 +0,0 @@
-/*
- * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <config.h>
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#ifdef HAVE_TERMIOS_H
-# include <termios.h>
-#else
-# include <termio.h>
-#endif /* HAVE_TERMIOS_H */
-#include <sys/ioctl.h>
-#ifdef HAVE_SYS_SELECT_H
-# include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H */
-#include <stdio.h>
-#ifdef STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-#  include <stdlib.h>
-# endif
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRING_H
-# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
-#  include <memory.h>
-# endif
-# include <string.h>
-#endif /* HAVE_STRING_H */
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif /* HAVE_STRINGS_H */
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#if TIME_WITH_SYS_TIME
-# include <time.h>
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#include "sudo.h"
-#include "sudo_exec.h"
-
-#define SFD_STDIN      0
-#define SFD_STDOUT     1
-#define SFD_STDERR     2
-#define SFD_MASTER     3
-#define SFD_SLAVE      4
-#define SFD_USERTTY    5
-
-#define TERM_COOKED    0
-#define TERM_RAW       1
-
-/* Compatibility with older tty systems. */
-#if !defined(TIOCGSIZE) && defined(TIOCGWINSZ)
-# define TIOCGSIZE     TIOCGWINSZ
-# define TIOCSSIZE     TIOCSWINSZ
-# define ttysize       winsize
-# define ts_cols       ws_col
-#endif
-
-struct io_buffer {
-    struct io_buffer *next;
-    int len; /* buffer length (how much produced) */
-    int off; /* write position (how much already consumed) */
-    int rfd;  /* reader (producer) */
-    int wfd; /* writer (consumer) */
-    int (*action) __P((const char *buf, unsigned int len));
-    char buf[16 * 1024];
-};
-
-static char slavename[PATH_MAX];
-static int foreground;
-static int io_fds[6] = { -1, -1, -1, -1, -1, -1};
-static int pipeline = FALSE;
-static int tty_initialized;
-static int ttymode = TERM_COOKED;
-static pid_t ppgrp, child;
-static struct io_buffer *iobufs;
-
-static void flush_output __P((void));
-static int exec_monitor __P((const char *path, char *argv[],
-    char *envp[], int, int));
-static void exec_pty __P((const char *path, char *argv[],
-    char *envp[], int));
-static void sigwinch __P((int s));
-static void sync_ttysize __P((int src, int dst));
-static void deliver_signal __P((pid_t pid, int signo));
-static int safe_close __P((int fd));
-
-/*
- * Allocate a pty if /dev/tty is a tty.
- * Fills in io_fds[SFD_USERTTY], io_fds[SFD_MASTER], io_fds[SFD_SLAVE]
- * and slavename globals.
- */
-void
-pty_setup(uid)
-    uid_t uid;
-{
-    io_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
-    if (io_fds[SFD_USERTTY] != -1) {
-       if (!get_pty(&io_fds[SFD_MASTER], &io_fds[SFD_SLAVE],
-           slavename, sizeof(slavename), uid))
-           error(1, "Can't get pty");
-    }
-}
-
-/*
- * Check whether we are running in the foregroup.
- * Updates the foreground global and does lazy init of the
- * the pty slave as needed.
- */
-static void
-check_foreground()
-{
-    if (io_fds[SFD_USERTTY] != -1) {
-       foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;
-       if (foreground && !tty_initialized) {
-           if (term_copy(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE])) {
-               tty_initialized = 1;
-               sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
-           }
-       }
-    }
-}
-
-/*
- * Suspend sudo if the underlying command is suspended.
- * Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2.
- */
-int
-suspend_parent(signo)
-    int signo;
-{
-    sigaction_t sa, osa;
-    int n, oldmode = ttymode, rval = 0;
-
-    switch (signo) {
-    case SIGTTOU:
-    case SIGTTIN:
-       /*
-        * If we are the foreground process, just resume the child.
-        * Otherwise, re-send the signal with the handler disabled.
-        */
-       if (!foreground)
-           check_foreground();
-       if (foreground) {
-           if (ttymode != TERM_RAW) {
-               do {
-                   n = term_raw(io_fds[SFD_USERTTY], 0);
-               } while (!n && errno == EINTR);
-               ttymode = TERM_RAW;
-           }
-           rval = SIGUSR1; /* resume child in foreground */
-           break;
-       }
-       ttymode = TERM_RAW;
-       /* FALLTHROUGH */
-    case SIGSTOP:
-    case SIGTSTP:
-       /* Flush any remaining output before suspending. */
-       flush_output();
-
-       /* Restore original tty mode before suspending. */
-       if (oldmode != TERM_COOKED) {
-           do {
-               n = term_restore(io_fds[SFD_USERTTY], 0);
-           } while (!n && errno == EINTR);
-       }
-
-       /* Suspend self and continue child when we resume. */
-       sa.sa_handler = SIG_DFL;
-       sigaction(signo, &sa, &osa);
-       if (killpg(ppgrp, signo) != 0)
-           warning("killpg(%d, %d)", ppgrp, signo);
-
-       /* Check foreground/background status on resume. */
-       check_foreground();
-
-       /*
-        * Only modify term if we are foreground process and either
-        * the old tty mode was not cooked or child got SIGTT{IN,OU}
-        */
-       if (ttymode != TERM_COOKED) {
-           if (foreground) {
-               /* Set raw mode. */
-               do {
-                   n = term_raw(io_fds[SFD_USERTTY], 0);
-               } while (!n && errno == EINTR);
-           } else {
-               /* Background process, no access to tty. */
-               ttymode = TERM_COOKED;
-           }
-       }
-
-       sigaction(signo, &osa, NULL);
-       rval = ttymode == TERM_RAW ? SIGUSR1 : SIGUSR2;
-       break;
-    }
-
-    return(rval);
-}
-
-/*
- * Kill child with increasing urgency.
- */
-static void
-terminate_child(pid, use_pgrp)
-    pid_t pid;
-    int use_pgrp;
-{
-    /*
-     * Note that SIGCHLD will interrupt the sleep()
-     */
-    if (use_pgrp) {
-       killpg(pid, SIGHUP);
-       killpg(pid, SIGTERM);
-       sleep(2);
-       killpg(pid, SIGKILL);
-    } else {
-       kill(pid, SIGHUP);
-       kill(pid, SIGTERM);
-       sleep(2);
-       kill(pid, SIGKILL);
-    }
-}
-
-/*
- * Allocate a new io_buffer struct and insert it at the head of the list.
- * Returns the new head element.
- */
-static struct io_buffer *
-io_buf_new(rfd, wfd, action, head)
-    int rfd;
-    int wfd;
-    int (*action) __P((const char *, unsigned int));
-    struct io_buffer *head;
-{
-    struct io_buffer *iob;
-
-    iob = emalloc(sizeof(*iob));
-    zero_bytes(iob, sizeof(*iob));
-    iob->rfd = rfd;
-    iob->wfd = wfd;
-    iob->action = action;
-    iob->next = head;
-    return iob;
-}
-
-/*
- * Read/write iobufs depending on fdsr and fdsw.
- * Fills in cstat on error.
- * Returns the number of errors.
- */
-int
-perform_io(fdsr, fdsw, cstat)
-    fd_set *fdsr;
-    fd_set *fdsw;
-    struct command_status *cstat;
-{
-    struct io_buffer *iob;
-    int n, errors = 0;
-
-    for (iob = iobufs; iob; iob = iob->next) {
-       if (iob->rfd != -1 && FD_ISSET(iob->rfd, fdsr)) {
-           do {
-               n = read(iob->rfd, iob->buf + iob->len,
-                   sizeof(iob->buf) - iob->len);
-           } while (n == -1 && errno == EINTR);
-           switch (n) {
-               case -1:
-                   if (errno == EAGAIN)
-                       break;
-                   if (errno != ENXIO && errno != EBADF) {
-                       errors++;
-                       break;
-                   }
-                   /* FALLTHROUGH */
-               case 0:
-                   /* got EOF or pty has gone away */
-                   safe_close(iob->rfd);
-                   iob->rfd = -1;
-                   break;
-               default:
-                   if (!iob->action(iob->buf + iob->len, n))
-                       terminate_child(child, TRUE);
-                   iob->len += n;
-                   break;
-           }
-       }
-       if (iob->wfd != -1 && FD_ISSET(iob->wfd, fdsw)) {
-           do {
-               n = write(iob->wfd, iob->buf + iob->off,
-                   iob->len - iob->off);
-           } while (n == -1 && errno == EINTR);
-           if (n == -1) {
-               if (errno == EPIPE || errno == ENXIO || errno == EBADF) {
-                   /* other end of pipe closed or pty revoked */
-                   if (iob->rfd != -1) {
-                       safe_close(iob->rfd);
-                       iob->rfd = -1;
-                   }
-                   safe_close(iob->wfd);
-                   iob->wfd = -1;
-                   continue;
-               }
-               if (errno != EAGAIN)
-                   errors++;
-           } else {
-               iob->off += n;
-           }
-       }
-    }
-    if (errors && cstat != NULL) {
-       cstat->type = CMD_ERRNO;
-       cstat->val = errno;
-    }
-    return errors;
-}
-
-/*
- * Fork a monitor process which runs the actual command as its own child
- * process with std{in,out,err} hooked up to the pty or pipes as appropriate.
- * Returns the child pid.
- */
-int
-fork_pty(path, argv, envp, sv, rbac_enabled, maxfd)
-    const char *path;
-    char *argv[];
-    char *envp[];
-    int sv[2];
-    int rbac_enabled;
-    int *maxfd;
-{
-    struct command_status cstat;
-    struct io_buffer *iob;
-    int io_pipe[3][2], n;
-    sigaction_t sa;
-
-    ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */
-
-    zero_bytes(&sa, sizeof(sa));
-    sigemptyset(&sa.sa_mask);
-
-    if (io_fds[SFD_USERTTY] != -1) {
-       sa.sa_flags = SA_RESTART;
-       sa.sa_handler = sigwinch;
-       sigaction(SIGWINCH, &sa, NULL);
-    }
-
-    /*
-     * Setup stdin/stdout/stderr for child, to be duped after forking.
-     */
-    io_fds[SFD_STDIN] = io_fds[SFD_SLAVE];
-    io_fds[SFD_STDOUT] = io_fds[SFD_SLAVE];
-    io_fds[SFD_STDERR] = io_fds[SFD_SLAVE];
-
-    /* Copy /dev/tty -> pty master */
-    if (io_fds[SFD_USERTTY] != -1) {
-       iobufs = io_buf_new(io_fds[SFD_USERTTY], io_fds[SFD_MASTER],
-           log_ttyin, iobufs);
-
-       /* Copy pty master -> /dev/tty */
-       iobufs = io_buf_new(io_fds[SFD_MASTER], io_fds[SFD_USERTTY],
-           log_ttyout, iobufs);
-
-       /* Are we the foreground process? */
-       foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;
-    }
-
-    /*
-     * If either stdin, stdout or stderr is not a tty we use a pipe
-     * to interpose ourselves instead of duping the pty fd.
-     */
-    memset(io_pipe, 0, sizeof(io_pipe));
-    if (io_fds[SFD_STDIN] == -1 || !isatty(STDIN_FILENO)) {
-       pipeline = TRUE;
-       if (pipe(io_pipe[STDIN_FILENO]) != 0)
-           error(1, "unable to create pipe");
-       iobufs = io_buf_new(STDIN_FILENO, io_pipe[STDIN_FILENO][1],
-           log_stdin, iobufs);
-       io_fds[SFD_STDIN] = io_pipe[STDIN_FILENO][0];
-    }
-    if (io_fds[SFD_STDOUT] == -1 || !isatty(STDOUT_FILENO)) {
-       pipeline = TRUE;
-       if (pipe(io_pipe[STDOUT_FILENO]) != 0)
-           error(1, "unable to create pipe");
-       iobufs = io_buf_new(io_pipe[STDOUT_FILENO][0], STDOUT_FILENO,
-           log_stdout, iobufs);
-       io_fds[SFD_STDOUT] = io_pipe[STDOUT_FILENO][1];
-    }
-    if (io_fds[SFD_STDERR] == -1 || !isatty(STDERR_FILENO)) {
-       if (pipe(io_pipe[STDERR_FILENO]) != 0)
-           error(1, "unable to create pipe");
-       iobufs = io_buf_new(io_pipe[STDERR_FILENO][0], STDERR_FILENO,
-           log_stderr, iobufs);
-       io_fds[SFD_STDERR] = io_pipe[STDERR_FILENO][1];
-    }
-
-    /* Job control signals to relay from parent to child. */
-    sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
-    sa.sa_handler = handler;
-    sigaction(SIGTSTP, &sa, NULL);
-
-    if (foreground) {
-       /* Copy terminal attrs from user tty -> pty slave. */
-       if (term_copy(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE])) {
-           tty_initialized = 1;
-           sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
-       }
-
-       /* Start out in raw mode if we are not part of a pipeline. */
-       if (!pipeline) {
-           ttymode = TERM_RAW;
-           do {
-               n = term_raw(io_fds[SFD_USERTTY], 0);
-           } while (!n && errno == EINTR);
-           if (!n)
-               error(1, "Can't set terminal to raw mode");
-       }
-    }
-
-    child = fork();
-    switch (child) {
-    case -1:
-       error(1, "fork");
-       break;
-    case 0:
-       /* child */
-       close(sv[0]);
-       fcntl(sv[1], F_SETFD, FD_CLOEXEC);
-       if (exec_setup(rbac_enabled, slavename, io_fds[SFD_SLAVE]) == TRUE) {
-           /* Close the other end of the stdin/stdout/stderr pipes and exec. */
-           if (io_pipe[STDIN_FILENO][1])
-               close(io_pipe[STDIN_FILENO][1]);
-           if (io_pipe[STDOUT_FILENO][0])
-               close(io_pipe[STDOUT_FILENO][0]);
-           if (io_pipe[STDERR_FILENO][0])
-               close(io_pipe[STDERR_FILENO][0]);
-           exec_monitor(path, argv, envp, sv[1], rbac_enabled);
-       }
-       cstat.type = CMD_ERRNO;
-       cstat.val = errno;
-       send(sv[1], &cstat, sizeof(cstat), 0);
-       _exit(1);
-    }
-
-    /* Close the other end of the stdin/stdout/stderr pipes. */
-    if (io_pipe[STDIN_FILENO][0])
-       close(io_pipe[STDIN_FILENO][0]);
-    if (io_pipe[STDOUT_FILENO][1])
-       close(io_pipe[STDOUT_FILENO][1]);
-    if (io_pipe[STDERR_FILENO][1])
-       close(io_pipe[STDERR_FILENO][1]);
-
-    for (iob = iobufs; iob; iob = iob->next) {
-       /* Adjust maxfd. */
-       if (iob->rfd > *maxfd)
-           *maxfd = iob->rfd;
-       if (iob->wfd > *maxfd)
-           *maxfd = iob->wfd;
-
-       /* Set non-blocking mode. */
-       n = fcntl(iob->rfd, F_GETFL, 0);
-       if (n != -1 && !ISSET(n, O_NONBLOCK))
-           (void) fcntl(iob->rfd, F_SETFL, n | O_NONBLOCK);
-       n = fcntl(iob->wfd, F_GETFL, 0);
-       if (n != -1 && !ISSET(n, O_NONBLOCK))
-           (void) fcntl(iob->wfd, F_SETFL, n | O_NONBLOCK);
-    }
-
-    return child;
-}
-
-/*
- * Flush any remaining output and restore /dev/tty to the way we found it.
- * If the command died due to a signal, writes the reason to stdout.
- */
-void
-pty_close(cstat)
-    struct command_status *cstat;
-{
-    int n;
-
-    /* Flush any remaining output (the plugin already got it) */
-    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);
-       }
-    }
-    flush_output();
-
-    if (io_fds[SFD_USERTTY] != -1) {
-       do {
-           n = term_restore(io_fds[SFD_USERTTY], 0);
-       } while (!n && errno == EINTR);
-    }
-
-    /* If child was signalled, write the reason to stdout like the shell. */
-    if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
-       int signo = WTERMSIG(cstat->val);
-       if (signo && signo != SIGINT && signo != SIGPIPE) {
-           const char *reason = strsignal(signo);
-           n = io_fds[SFD_USERTTY] != -1 ?
-               io_fds[SFD_USERTTY] : STDOUT_FILENO;
-           write(n, reason, strlen(reason));
-           if (WCOREDUMP(cstat->val))
-               write(n, " (core dumped)", 14);
-           write(n, "\n", 1);
-       }
-    }
-}
-
-
-/*
- * Fill in fdsr and fdsw based on the io buffers list.
- * Called prior to select().
- */
-void
-fd_set_iobs(fdsr, fdsw)
-    fd_set *fdsr;
-    fd_set *fdsw;
-{
-    struct io_buffer *iob;
-
-    for (iob = iobufs; iob; iob = iob->next) {
-       if (iob->rfd == -1 && iob->wfd == -1)
-           continue;
-       if (iob->off == iob->len) {
-           iob->off = iob->len = 0;
-           /* Forward the EOF from reader to writer. */
-           if (iob->rfd == -1) {
-               safe_close(iob->wfd);
-               iob->wfd = -1;
-           }
-       }
-       /* Don't read/write /dev/tty if we are not in the foreground. */
-       if (iob->rfd != -1 &&
-           (ttymode == TERM_RAW || iob->rfd != io_fds[SFD_USERTTY])) {
-           if (iob->len != sizeof(iob->buf))
-               FD_SET(iob->rfd, fdsr);
-       }
-       if (iob->wfd != -1 &&
-           (foreground || iob->wfd != io_fds[SFD_USERTTY])) {
-           if (iob->len > iob->off)
-               FD_SET(iob->wfd, fdsw);
-       }
-    }
-}
-
-/*
- * Deliver a relayed signal to the command.
- */
-static void
-deliver_signal(pid, signo)
-    pid_t pid;
-    int signo;
-{
-    int status;
-
-    /* Handle signal from parent. */
-    switch (signo) {
-    case SIGKILL:
-       _exit(1); /* XXX */
-       /* NOTREACHED */
-    case SIGPIPE:
-    case SIGHUP:
-    case SIGTERM:
-    case SIGINT:
-    case SIGQUIT:
-    case SIGTSTP:
-       /* relay signal to child */
-       killpg(pid, signo);
-       break;
-    case SIGALRM:
-       terminate_child(pid, TRUE);
-       break;
-    case SIGUSR1:
-       /* foreground process, grant it controlling tty. */
-       do {
-           status = tcsetpgrp(io_fds[SFD_SLAVE], pid);
-       } while (status == -1 && errno == EINTR);
-       killpg(pid, SIGCONT);
-       break;
-    case SIGUSR2:
-       /* background process, I take controlling tty. */
-       do {
-           status = tcsetpgrp(io_fds[SFD_SLAVE], getpid());
-       } while (status == -1 && errno == EINTR);
-       killpg(pid, SIGCONT);
-       break;
-    default:
-       warningx("unexpected signal from child: %d", signo);
-       break;
-    }
-}
-
-/*
- * Send status to parent over socketpair.
- * Return value is the same as send(2).
- */
-static int
-send_status(fd, cstat)
-    int fd;
-    struct command_status *cstat;
-{
-    int n = -1;
-
-    if (cstat->type != CMD_INVALID) {
-       do {
-           n = send(fd, cstat, sizeof(*cstat), 0);
-       } while (n == -1 && errno == EINTR);
-       cstat->type = CMD_INVALID; /* prevent re-sending */
-    }
-    return n;
-}
-
-/*
- * Wait for child status after receiving SIGCHLD.
- * If the child was stopped, the status is send back to the parent.
- * Otherwise, cstat is filled in but not sent.
- * Returns TRUE if child is still alive, else FALSE.
- */
-static int
-handle_sigchld(backchannel, cstat)
-    int backchannel;
-    struct command_status *cstat;
-{
-    int status, alive = TRUE;
-    pid_t pid;
-
-    /* read child status */
-    do {
-#ifdef sudo_waitpid
-       pid = sudo_waitpid(child, &status, WUNTRACED|WNOHANG);
-#else
-       pid = wait(&status);
-#endif
-    } while (pid == -1 && errno == EINTR);
-    if (pid == child) {
-       if (cstat->type != CMD_ERRNO) {
-           cstat->type = CMD_WSTATUS;
-           cstat->val = status;
-           if (WIFSTOPPED(status)) {
-               if (send_status(backchannel, cstat) == -1)
-                   return alive; /* XXX */
-           }
-       }
-       if (!WIFSTOPPED(status))
-           alive = FALSE;
-    }
-    return alive;
-}
-
-/*
- * Monitor process that creates a new session with the controlling tty,
- * resets signal handlers and forks a child to call exec_pty().
- * Waits for status changes from the command and relays them to the
- * parent and relays signals from the parent to the command.
- * Returns an error if fork(2) fails, else calls _exit(2).
- */
-static int
-exec_monitor(path, argv, envp, backchannel, rbac)
-    const char *path;
-    char *argv[];
-    char *envp[];
-    int backchannel;
-    int rbac;
-{
-    struct command_status cstat;
-    struct timeval tv;
-    fd_set *fdsr;
-    sigaction_t sa;
-    int errpipe[2], maxfd, n, status;
-    int alive = TRUE;
-
-    /* Close unused fds. */
-    if (io_fds[SFD_MASTER] != -1)
-       close(io_fds[SFD_MASTER]);
-    if (io_fds[SFD_USERTTY] != -1)
-       close(io_fds[SFD_USERTTY]);
-
-    /* Reset SIGWINCH and SIGALRM. */
-    zero_bytes(&sa, sizeof(sa));
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = SA_RESTART;
-    sa.sa_handler = SIG_DFL;
-    sigaction(SIGWINCH, &sa, NULL);
-    sigaction(SIGALRM, &sa, NULL);
-
-    /* Ignore any SIGTTIN or SIGTTOU we get. */
-    sa.sa_handler = SIG_IGN;
-    sigaction(SIGTTIN, &sa, NULL);
-    sigaction(SIGTTOU, &sa, NULL);
-
-    /* Note: HP-UX select() will not be interrupted if SA_RESTART set */
-    sa.sa_flags = SA_INTERRUPT;
-    sa.sa_handler = handler;
-    sigaction(SIGCHLD, &sa, NULL);
-
-    /*
-     * Start a new session with the parent as the session leader
-     * and the slave pty as the controlling terminal.
-     * This allows us to be notified when the child has been suspended.
-     */
-    if (setsid() == -1) {
-       warning("setsid");
-       goto bad;
-    }
-    if (io_fds[SFD_SLAVE] != -1) {
-#ifdef TIOCSCTTY
-       if (ioctl(io_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
-           error(1, "unable to set controlling tty");
-#else
-       /* Set controlling tty by reopening slave. */
-       if ((n = open(slavename, O_RDWR)) >= 0)
-           close(n);
-#endif
-    }
-
-    /*
-     * If stdin/stdout is not a tty, start command in the background
-     * since it might be part of a pipeline that reads from /dev/tty.
-     * In this case, we rely on the command receiving SIGTTOU or SIGTTIN
-     * when it needs access to the controlling tty.
-     */
-    if (pipeline)
-       foreground = 0;
-
-    /* Start command and wait for it to stop or exit */
-    if (pipe(errpipe) == -1)
-       error(1, "unable to create pipe");
-    child = fork();
-    if (child == -1) {
-       warning("Can't fork");
-       goto bad;
-    }
-    if (child == 0) {
-       /* We pass errno back to our parent via pipe on exec failure. */
-       close(backchannel);
-       close(errpipe[0]);
-       fcntl(errpipe[1], F_SETFD, FD_CLOEXEC);
-
-       /* setup tty and exec command */
-       exec_pty(path, argv, envp, rbac);
-       cstat.type = CMD_ERRNO;
-       cstat.val = errno;
-       write(errpipe[1], &cstat, sizeof(cstat));
-       _exit(1);
-    }
-    close(errpipe[1]);
-
-    /* If any of stdin/stdout/stderr are pipes, close them in parent. */
-    if (io_fds[SFD_STDIN] != io_fds[SFD_SLAVE])
-       close(io_fds[SFD_STDIN]);
-    if (io_fds[SFD_STDOUT] != io_fds[SFD_SLAVE])
-       close(io_fds[SFD_STDOUT]);
-    if (io_fds[SFD_STDERR] != io_fds[SFD_SLAVE])
-       close(io_fds[SFD_STDERR]);
-
-    /*
-     * Put child in its own process group.  If we are starting the command
-     * in the foreground, assign its pgrp to the tty.
-     */
-    setpgid(child, child);
-    if (foreground) {
-       do {
-           status = tcsetpgrp(io_fds[SFD_SLAVE], child);
-       } while (status == -1 && errno == EINTR);
-    }
-
-    /* Wait for errno on pipe, signal on backchannel or for SIGCHLD */
-    maxfd = MAX(errpipe[0], backchannel);
-    fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
-    zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
-    zero_bytes(&cstat, sizeof(cstat));
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-    for (;;) {
-       /* Read child status. */
-       if (recvsig[SIGCHLD]) {
-           recvsig[SIGCHLD] = FALSE;
-           alive = handle_sigchld(backchannel, &cstat);
-       }
-
-       /* Check for signal on backchannel or errno on errpipe. */
-       FD_SET(backchannel, fdsr);
-       if (errpipe[0] != -1)
-           FD_SET(errpipe[0], fdsr);
-       maxfd = MAX(errpipe[0], backchannel);
-
-       if (recvsig[SIGCHLD])
-           continue;
-       /* If command exited we just poll, there may be data on errpipe. */
-       n = select(maxfd + 1, fdsr, NULL, NULL, alive ? NULL : &tv);
-       if (n <= 0) {
-           if (n == 0)
-               goto done;
-           if (errno == EINTR)
-               continue;
-           error(1, "select failed");
-       }
-
-       if (errpipe[0] != -1 && FD_ISSET(errpipe[0], fdsr)) {
-           /* read errno or EOF from command pipe */
-           n = read(errpipe[0], &cstat, sizeof(cstat));
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               warning("error reading from pipe");
-               goto done;
-           }
-           /* Got errno or EOF, either way we are done with errpipe. */
-           FD_CLR(errpipe[0], fdsr);
-           close(errpipe[0]);
-           errpipe[0] = -1;
-       }
-       if (FD_ISSET(backchannel, fdsr)) {
-           struct command_status cstmp;
-
-           /* read command from backchannel, should be a signal */
-           n = recv(backchannel, &cstmp, sizeof(cstmp), 0);
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               warning("error reading from socketpair");
-               goto done;
-           }
-           if (cstmp.type != CMD_SIGNO) {
-               warningx("unexpected reply type on backchannel: %d", cstmp.type);
-               continue;
-           }
-           deliver_signal(child, cstmp.val);
-       }
-    }
-
-done:
-    if (alive) {
-       /* XXX An error occurred, should send an error back. */
-       kill(child, SIGKILL);
-    } else {
-       /* Send parent status. */
-       send_status(backchannel, &cstat);
-    }
-    _exit(1);
-
-bad:
-    return errno;
-}
-
-/*
- * Flush any output buffered in iobufs or readable from the fds.
- * Does not read from /dev/tty.
- */
-static void
-flush_output()
-{
-    struct io_buffer *iob;
-    struct timeval tv;
-    fd_set *fdsr, *fdsw;
-    int nready, nwriters, maxfd = -1;
-
-    /* Determine maxfd */
-    for (iob = iobufs; iob; iob = iob->next) {
-       if (iob->rfd > maxfd)
-           maxfd = iob->rfd;
-       if (iob->wfd > maxfd)
-           maxfd = iob->wfd;
-    }
-    if (maxfd == -1)
-       return;
-
-    fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
-    fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
-    for (;;) {
-       zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
-       zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
-
-       nwriters = 0;
-       for (iob = iobufs; iob; iob = iob->next) {
-           /* Don't read from /dev/tty while flushing. */
-           if (io_fds[SFD_USERTTY] != -1 && iob->rfd == io_fds[SFD_USERTTY])
-               continue;
-           if (iob->rfd == -1 && iob->wfd == -1)
-               continue;
-           if (iob->off == iob->len) {
-               iob->off = iob->len = 0;
-               /* Forward the EOF from reader to writer. */
-               if (iob->rfd == -1) {
-                   safe_close(iob->wfd);
-                   iob->wfd = -1;
-               }
-           }
-           if (iob->rfd != -1) {
-               if (iob->len != sizeof(iob->buf))
-                   FD_SET(iob->rfd, fdsr);
-           }
-           if (iob->wfd != -1) {
-               if (iob->len > iob->off) {
-                   nwriters++;
-                   FD_SET(iob->wfd, fdsw);
-               }
-           }
-       }
-
-       /* Don't sleep in select if there are no buffers that need writing. */
-       tv.tv_sec = 0;
-       tv.tv_usec = 0;
-       nready = select(maxfd + 1, fdsr, fdsw, NULL, nwriters ? NULL : &tv);
-       if (nready <= 0) {
-           if (nready == 0)
-               break; /* all I/O flushed */
-           if (errno == EINTR)
-               continue;
-           error(1, "select failed");
-       }
-       if (perform_io(fdsr, fdsw, NULL) != 0)
-           break;
-    }
-    efree(fdsr);
-    efree(fdsw);
-}
-
-/*
- * Sets up std{in,out,err} and executes the actual command.
- * Returns only if execve() fails.
- */
-static void
-exec_pty(path, argv, envp, rbac_enabled)
-    const char *path;
-    char *argv[];
-    char *envp[];
-    int rbac_enabled;
-{
-    sigaction_t sa;
-    pid_t self = getpid();
-
-    /* Reset signal handlers. */
-    zero_bytes(&sa, sizeof(sa));
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = SA_RESTART;
-    sa.sa_handler = SIG_DFL;
-    sigaction(SIGHUP, &sa, NULL);
-    sigaction(SIGTERM, &sa, NULL);
-    sigaction(SIGINT, &sa, NULL);
-    sigaction(SIGQUIT, &sa, NULL);
-    sigaction(SIGTSTP, &sa, NULL);
-    sigaction(SIGTTIN, &sa, NULL);
-    sigaction(SIGTTOU, &sa, NULL);
-    sigaction(SIGUSR1, &sa, NULL);
-    sigaction(SIGUSR2, &sa, NULL);
-    sigaction(SIGCHLD, &sa, NULL);
-
-    /* Set child process group here too to avoid a race. */
-    setpgid(0, self);
-
-    /* Wire up standard fds, note that stdout/stderr may be pipes. */
-    if (dup2(io_fds[SFD_STDIN], STDIN_FILENO) == -1 ||
-       dup2(io_fds[SFD_STDOUT], STDOUT_FILENO) == -1 ||
-       dup2(io_fds[SFD_STDERR], STDERR_FILENO) == -1)
-       error(1, "dup2");
-
-    /* Wait for parent to grant us the tty if we are foreground. */
-    if (foreground) {
-       while (tcgetpgrp(io_fds[SFD_SLAVE]) != self)
-           ; /* spin */
-    }
-
-    /* We have guaranteed that the slave fd is > 2 */
-    if (io_fds[SFD_SLAVE] != -1)
-       close(io_fds[SFD_SLAVE]);
-    if (io_fds[SFD_STDIN] != io_fds[SFD_SLAVE])
-       close(io_fds[SFD_STDIN]);
-    if (io_fds[SFD_STDOUT] != io_fds[SFD_SLAVE])
-       close(io_fds[SFD_STDOUT]);
-    if (io_fds[SFD_STDERR] != io_fds[SFD_SLAVE])
-       close(io_fds[SFD_STDERR]);
-
-    closefrom(def_closefrom);
-#ifdef HAVE_SELINUX
-    if (rbac_enabled)
-       selinux_execve(path, argv, envp);
-    else
-#endif
-       my_execve(path, argv, envp);
-}
-
-/*
- * Propagates tty size change signals to pty being used by the command.
- */
-static void
-sync_ttysize(src, dst)
-    int src;
-    int dst;
-{
-#ifdef TIOCGSIZE
-    struct ttysize tsize;
-    pid_t pgrp;
-
-    if (ioctl(src, TIOCGSIZE, &tsize) == 0) {
-           ioctl(dst, TIOCSSIZE, &tsize);
-           if ((pgrp = tcgetpgrp(dst)) != -1)
-               killpg(pgrp, SIGWINCH);
-    }
-#endif
-}
-
-/*
- * Handler for SIGWINCH in parent.
- */
-static void
-sigwinch(s)
-    int s;
-{
-    int serrno = errno;
-
-    sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
-    errno = serrno;
-}
-
-/*
- * Only close the fd if it is not /dev/tty or std{in,out,err}.
- * Return value is the same as send(2).
- */
-static int
-safe_close(fd)
-    int fd;
-{
-    /* Avoid closing /dev/tty or std{in,out,err}. */
-    if (fd < 3 || fd == io_fds[SFD_USERTTY]) {
-       errno = EINVAL;
-       return -1;
-    }
-    return close(fd);
-}