Annotation of embedaddon/sudo/src/exec_pty.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
        !             3:  *
        !             4:  * Permission to use, copy, modify, and distribute this software for any
        !             5:  * purpose with or without fee is hereby granted, provided that the above
        !             6:  * copyright notice and this permission notice appear in all copies.
        !             7:  *
        !             8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !             9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            15:  */
        !            16: 
        !            17: #include <config.h>
        !            18: 
        !            19: #include <sys/types.h>
        !            20: #include <sys/param.h>
        !            21: #ifdef HAVE_SYS_SYSMACROS_H
        !            22: # include <sys/sysmacros.h>
        !            23: #endif
        !            24: #include <sys/socket.h>
        !            25: #include <sys/time.h>
        !            26: #include <sys/wait.h>
        !            27: #include <sys/ioctl.h>
        !            28: #ifdef HAVE_SYS_SELECT_H
        !            29: # include <sys/select.h>
        !            30: #endif /* HAVE_SYS_SELECT_H */
        !            31: #include <stdio.h>
        !            32: #ifdef STDC_HEADERS
        !            33: # include <stdlib.h>
        !            34: # include <stddef.h>
        !            35: #else
        !            36: # ifdef HAVE_STDLIB_H
        !            37: #  include <stdlib.h>
        !            38: # endif
        !            39: #endif /* STDC_HEADERS */
        !            40: #ifdef HAVE_STRING_H
        !            41: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
        !            42: #  include <memory.h>
        !            43: # endif
        !            44: # include <string.h>
        !            45: #endif /* HAVE_STRING_H */
        !            46: #ifdef HAVE_STRINGS_H
        !            47: # include <strings.h>
        !            48: #endif /* HAVE_STRINGS_H */
        !            49: #ifdef HAVE_UNISTD_H
        !            50: # include <unistd.h>
        !            51: #endif /* HAVE_UNISTD_H */
        !            52: #if TIME_WITH_SYS_TIME
        !            53: # include <time.h>
        !            54: #endif
        !            55: #include <errno.h>
        !            56: #include <fcntl.h>
        !            57: #include <signal.h>
        !            58: #include <termios.h>
        !            59: 
        !            60: #include "sudo.h"
        !            61: #include "sudo_exec.h"
        !            62: #include "sudo_plugin.h"
        !            63: #include "sudo_plugin_int.h"
        !            64: 
        !            65: #define SFD_STDIN      0
        !            66: #define SFD_STDOUT     1
        !            67: #define SFD_STDERR     2
        !            68: #define SFD_MASTER     3
        !            69: #define SFD_SLAVE      4
        !            70: #define SFD_USERTTY    5
        !            71: 
        !            72: #define TERM_COOKED    0
        !            73: #define TERM_RAW       1
        !            74: 
        !            75: /* Compatibility with older tty systems. */
        !            76: #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
        !            77: # define TIOCGWINSZ    TIOCGSIZE
        !            78: # define TIOCSWINSZ    TIOCSSIZE
        !            79: # define winsize       ttysize
        !            80: #endif
        !            81: 
        !            82: struct io_buffer {
        !            83:     struct io_buffer *next;
        !            84:     int len; /* buffer length (how much produced) */
        !            85:     int off; /* write position (how much already consumed) */
        !            86:     int rfd;  /* reader (producer) */
        !            87:     int wfd; /* writer (consumer) */
        !            88:     int (*action)(const char *buf, unsigned int len);
        !            89:     char buf[16 * 1024];
        !            90: };
        !            91: 
        !            92: static char slavename[PATH_MAX];
        !            93: static int foreground;
        !            94: static int io_fds[6] = { -1, -1, -1, -1, -1, -1};
        !            95: static int pipeline = FALSE;
        !            96: static int tty_initialized;
        !            97: static int ttymode = TERM_COOKED;
        !            98: static pid_t ppgrp, child, child_pgrp;
        !            99: static sigset_t ttyblock;
        !           100: static struct io_buffer *iobufs;
        !           101: 
        !           102: static void flush_output(void);
        !           103: static int exec_monitor(struct command_details *details, int backchannel);
        !           104: static void exec_pty(struct command_details *detail);
        !           105: static void sigwinch(int s);
        !           106: static void sync_ttysize(int src, int dst);
        !           107: static void deliver_signal(pid_t pid, int signo);
        !           108: static int safe_close(int fd);
        !           109: 
        !           110: /*
        !           111:  * Cleanup hook for error()/errorx()
        !           112:  */
        !           113: void
        !           114: cleanup(int gotsignal)
        !           115: {
        !           116:     if (!tq_empty(&io_plugins))
        !           117:        term_restore(io_fds[SFD_USERTTY], 0);
        !           118: #ifdef HAVE_SELINUX
        !           119:     selinux_restore_tty();
        !           120: #endif
        !           121:     utmp_logout(slavename, 0); /* XXX - only if CD_SET_UTMP */
        !           122: }
        !           123: 
        !           124: /*
        !           125:  * Allocate a pty if /dev/tty is a tty.
        !           126:  * Fills in io_fds[SFD_USERTTY], io_fds[SFD_MASTER], io_fds[SFD_SLAVE]
        !           127:  * and slavename globals.
        !           128:  */
        !           129: void
        !           130: pty_setup(uid_t uid, const char *tty, const char *utmp_user)
        !           131: {
        !           132:     io_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
        !           133:     if (io_fds[SFD_USERTTY] != -1) {
        !           134:        if (!get_pty(&io_fds[SFD_MASTER], &io_fds[SFD_SLAVE],
        !           135:            slavename, sizeof(slavename), uid))
        !           136:            error(1, _("unable to allocate pty"));
        !           137:        /* Add entry to utmp/utmpx? */
        !           138:        if (utmp_user != NULL)
        !           139:            utmp_login(tty, slavename, io_fds[SFD_SLAVE], utmp_user);
        !           140:     }
        !           141: }
        !           142: 
        !           143: /* Call I/O plugin tty input log method. */
        !           144: static int
        !           145: log_ttyin(const char *buf, unsigned int n)
        !           146: {
        !           147:     struct plugin_container *plugin;
        !           148:     sigset_t omask;
        !           149:     int rval = TRUE;
        !           150: 
        !           151:     sigprocmask(SIG_BLOCK, &ttyblock, &omask);
        !           152: 
        !           153:     tq_foreach_fwd(&io_plugins, plugin) {
        !           154:        if (plugin->u.io->log_ttyin) {
        !           155:            if (!plugin->u.io->log_ttyin(buf, n)) {
        !           156:                rval = FALSE;
        !           157:                break;
        !           158:            }
        !           159:        }
        !           160:     }
        !           161: 
        !           162:     sigprocmask(SIG_SETMASK, &omask, NULL);
        !           163:     return rval;
        !           164: }
        !           165: 
        !           166: /* Call I/O plugin stdin log method. */
        !           167: static int
        !           168: log_stdin(const char *buf, unsigned int n)
        !           169: {
        !           170:     struct plugin_container *plugin;
        !           171:     sigset_t omask;
        !           172:     int rval = TRUE;
        !           173: 
        !           174:     sigprocmask(SIG_BLOCK, &ttyblock, &omask);
        !           175: 
        !           176:     tq_foreach_fwd(&io_plugins, plugin) {
        !           177:        if (plugin->u.io->log_stdin) {
        !           178:            if (!plugin->u.io->log_stdin(buf, n)) {
        !           179:                rval = FALSE;
        !           180:                break;
        !           181:            }
        !           182:        }
        !           183:     }
        !           184: 
        !           185:     sigprocmask(SIG_SETMASK, &omask, NULL);
        !           186:     return rval;
        !           187: }
        !           188: 
        !           189: /* Call I/O plugin tty output log method. */
        !           190: static int
        !           191: log_ttyout(const char *buf, unsigned int n)
        !           192: {
        !           193:     struct plugin_container *plugin;
        !           194:     sigset_t omask;
        !           195:     int rval = TRUE;
        !           196: 
        !           197:     sigprocmask(SIG_BLOCK, &ttyblock, &omask);
        !           198: 
        !           199:     tq_foreach_fwd(&io_plugins, plugin) {
        !           200:        if (plugin->u.io->log_ttyout) {
        !           201:            if (!plugin->u.io->log_ttyout(buf, n)) {
        !           202:                rval = FALSE;
        !           203:                break;
        !           204:            }
        !           205:        }
        !           206:     }
        !           207: 
        !           208:     sigprocmask(SIG_SETMASK, &omask, NULL);
        !           209:     return rval;
        !           210: }
        !           211: 
        !           212: /* Call I/O plugin stdout log method. */
        !           213: static int
        !           214: log_stdout(const char *buf, unsigned int n)
        !           215: {
        !           216:     struct plugin_container *plugin;
        !           217:     sigset_t omask;
        !           218:     int rval = TRUE;
        !           219: 
        !           220:     sigprocmask(SIG_BLOCK, &ttyblock, &omask);
        !           221: 
        !           222:     tq_foreach_fwd(&io_plugins, plugin) {
        !           223:        if (plugin->u.io->log_stdout) {
        !           224:            if (!plugin->u.io->log_stdout(buf, n)) {
        !           225:                rval = FALSE;
        !           226:                break;
        !           227:            }
        !           228:        }
        !           229:     }
        !           230: 
        !           231:     sigprocmask(SIG_SETMASK, &omask, NULL);
        !           232:     return rval;
        !           233: }
        !           234: 
        !           235: /* Call I/O plugin stderr log method. */
        !           236: static int
        !           237: log_stderr(const char *buf, unsigned int n)
        !           238: {
        !           239:     struct plugin_container *plugin;
        !           240:     sigset_t omask;
        !           241:     int rval = TRUE;
        !           242: 
        !           243:     sigprocmask(SIG_BLOCK, &ttyblock, &omask);
        !           244: 
        !           245:     tq_foreach_fwd(&io_plugins, plugin) {
        !           246:        if (plugin->u.io->log_stderr) {
        !           247:            if (!plugin->u.io->log_stderr(buf, n)) {
        !           248:                rval = FALSE;
        !           249:                break;
        !           250:            }
        !           251:        }
        !           252:     }
        !           253: 
        !           254:     sigprocmask(SIG_SETMASK, &omask, NULL);
        !           255:     return rval;
        !           256: }
        !           257: 
        !           258: /*
        !           259:  * Check whether we are running in the foregroup.
        !           260:  * Updates the foreground global and does lazy init of the
        !           261:  * the pty slave as needed.
        !           262:  */
        !           263: static void
        !           264: check_foreground(void)
        !           265: {
        !           266:     if (io_fds[SFD_USERTTY] != -1) {
        !           267:        foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;
        !           268:        if (foreground && !tty_initialized) {
        !           269:            if (term_copy(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE])) {
        !           270:                tty_initialized = 1;
        !           271:                sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
        !           272:            }
        !           273:        }
        !           274:     }
        !           275: }
        !           276: 
        !           277: /*
        !           278:  * Suspend sudo if the underlying command is suspended.
        !           279:  * Returns SIGCONT_FG if the child should be resume in the
        !           280:  * foreground or SIGCONT_BG if it is a background process.
        !           281:  */
        !           282: int
        !           283: suspend_parent(int signo)
        !           284: {
        !           285:     sigaction_t sa, osa;
        !           286:     int n, oldmode = ttymode, rval = 0;
        !           287: 
        !           288:     switch (signo) {
        !           289:     case SIGTTOU:
        !           290:     case SIGTTIN:
        !           291:        /*
        !           292:         * If we are the foreground process, just resume the child.
        !           293:         * Otherwise, re-send the signal with the handler disabled.
        !           294:         */
        !           295:        if (!foreground)
        !           296:            check_foreground();
        !           297:        if (foreground) {
        !           298:            if (ttymode != TERM_RAW) {
        !           299:                do {
        !           300:                    n = term_raw(io_fds[SFD_USERTTY], 0);
        !           301:                } while (!n && errno == EINTR);
        !           302:                ttymode = TERM_RAW;
        !           303:            }
        !           304:            rval = SIGCONT_FG; /* resume child in foreground */
        !           305:            break;
        !           306:        }
        !           307:        ttymode = TERM_RAW;
        !           308:        /* FALLTHROUGH */
        !           309:     case SIGSTOP:
        !           310:     case SIGTSTP:
        !           311:        /* Flush any remaining output before suspending. */
        !           312:        flush_output();
        !           313: 
        !           314:        /* Restore original tty mode before suspending. */
        !           315:        if (oldmode != TERM_COOKED) {
        !           316:            do {
        !           317:                n = term_restore(io_fds[SFD_USERTTY], 0);
        !           318:            } while (!n && errno == EINTR);
        !           319:        }
        !           320: 
        !           321:        /* Suspend self and continue child when we resume. */
        !           322:        sa.sa_handler = SIG_DFL;
        !           323:        sigaction(signo, &sa, &osa);
        !           324:        sudo_debug(8, "kill parent %d", signo);
        !           325:        if (killpg(ppgrp, signo) != 0)
        !           326:            warning("killpg(%d, %d)", (int)ppgrp, signo);
        !           327: 
        !           328:        /* Check foreground/background status on resume. */
        !           329:        check_foreground();
        !           330: 
        !           331:        /*
        !           332:         * Only modify term if we are foreground process and either
        !           333:         * the old tty mode was not cooked or child got SIGTT{IN,OU}
        !           334:         */
        !           335:        sudo_debug(8, "parent is in %s, ttymode %d -> %d",
        !           336:            foreground ? "foreground" : "background", oldmode, ttymode);
        !           337: 
        !           338:        if (ttymode != TERM_COOKED) {
        !           339:            if (foreground) {
        !           340:                /* Set raw mode. */
        !           341:                do {
        !           342:                    n = term_raw(io_fds[SFD_USERTTY], 0);
        !           343:                } while (!n && errno == EINTR);
        !           344:            } else {
        !           345:                /* Background process, no access to tty. */
        !           346:                ttymode = TERM_COOKED;
        !           347:            }
        !           348:        }
        !           349: 
        !           350:        sigaction(signo, &osa, NULL);
        !           351:        rval = ttymode == TERM_RAW ? SIGCONT_FG : SIGCONT_BG;
        !           352:        break;
        !           353:     }
        !           354: 
        !           355:     return rval;
        !           356: }
        !           357: 
        !           358: /*
        !           359:  * Kill child with increasing urgency.
        !           360:  */
        !           361: void
        !           362: terminate_child(pid_t pid, int use_pgrp)
        !           363: {
        !           364:     /*
        !           365:      * Note that SIGCHLD will interrupt the sleep()
        !           366:      */
        !           367:     if (use_pgrp) {
        !           368:        killpg(pid, SIGHUP);
        !           369:        killpg(pid, SIGTERM);
        !           370:        sleep(2);
        !           371:        killpg(pid, SIGKILL);
        !           372:     } else {
        !           373:        kill(pid, SIGHUP);
        !           374:        kill(pid, SIGTERM);
        !           375:        sleep(2);
        !           376:        kill(pid, SIGKILL);
        !           377:     }
        !           378: }
        !           379: 
        !           380: static struct io_buffer *
        !           381: io_buf_new(int rfd, int wfd, int (*action)(const char *, unsigned int),
        !           382:     struct io_buffer *head)
        !           383: {
        !           384:     struct io_buffer *iob;
        !           385: 
        !           386:     iob = emalloc(sizeof(*iob));
        !           387:     zero_bytes(iob, sizeof(*iob));
        !           388:     iob->rfd = rfd;
        !           389:     iob->wfd = wfd;
        !           390:     iob->action = action;
        !           391:     iob->next = head;
        !           392:     return iob;
        !           393: }
        !           394: 
        !           395: /*
        !           396:  * Read/write iobufs depending on fdsr and fdsw.
        !           397:  * Returns the number of errors.
        !           398:  */
        !           399: int
        !           400: perform_io(fd_set *fdsr, fd_set *fdsw, struct command_status *cstat)
        !           401: {
        !           402:     struct io_buffer *iob;
        !           403:     int n, errors = 0;
        !           404: 
        !           405:     for (iob = iobufs; iob; iob = iob->next) {
        !           406:        if (iob->rfd != -1 && FD_ISSET(iob->rfd, fdsr)) {
        !           407:            do {
        !           408:                n = read(iob->rfd, iob->buf + iob->len,
        !           409:                    sizeof(iob->buf) - iob->len);
        !           410:            } while (n == -1 && errno == EINTR);
        !           411:            switch (n) {
        !           412:                case -1:
        !           413:                    if (errno == EAGAIN)
        !           414:                        break;
        !           415:                    if (errno != ENXIO && errno != EBADF) {
        !           416:                        errors++;
        !           417:                        break;
        !           418:                    }
        !           419:                    /* FALLTHROUGH */
        !           420:                case 0:
        !           421:                    /* got EOF or pty has gone away */
        !           422:                    safe_close(iob->rfd);
        !           423:                    iob->rfd = -1;
        !           424:                    break;
        !           425:                default:
        !           426:                    if (!iob->action(iob->buf + iob->len, n))
        !           427:                        terminate_child(child, TRUE);
        !           428:                    iob->len += n;
        !           429:                    break;
        !           430:            }
        !           431:        }
        !           432:        if (iob->wfd != -1 && FD_ISSET(iob->wfd, fdsw)) {
        !           433:            do {
        !           434:                n = write(iob->wfd, iob->buf + iob->off,
        !           435:                    iob->len - iob->off);
        !           436:            } while (n == -1 && errno == EINTR);
        !           437:            if (n == -1) {
        !           438:                if (errno == EPIPE || errno == ENXIO || errno == EBADF) {
        !           439:                    /* other end of pipe closed or pty revoked */
        !           440:                    if (iob->rfd != -1) {
        !           441:                        safe_close(iob->rfd);
        !           442:                        iob->rfd = -1;
        !           443:                    }
        !           444:                    safe_close(iob->wfd);
        !           445:                    iob->wfd = -1;
        !           446:                    continue;
        !           447:                }
        !           448:                if (errno != EAGAIN)
        !           449:                    errors++;
        !           450:            } else {
        !           451:                iob->off += n;
        !           452:            }
        !           453:        }
        !           454:     }
        !           455:     if (errors && cstat != NULL) {
        !           456:        cstat->type = CMD_ERRNO;
        !           457:        cstat->val = errno;
        !           458:     }
        !           459:     return errors;
        !           460: }
        !           461: 
        !           462: /*
        !           463:  * Fork a monitor process which runs the actual command as its own child
        !           464:  * process with std{in,out,err} hooked up to the pty or pipes as appropriate.
        !           465:  * Returns the child pid.
        !           466:  */
        !           467: int
        !           468: fork_pty(struct command_details *details, int sv[], int *maxfd)
        !           469: {
        !           470:     struct command_status cstat;
        !           471:     struct io_buffer *iob;
        !           472:     int io_pipe[3][2], n;
        !           473:     sigaction_t sa;
        !           474:         
        !           475:     ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */
        !           476:      
        !           477:     zero_bytes(&sa, sizeof(sa));
        !           478:     sigemptyset(&sa.sa_mask);
        !           479:  
        !           480:     if (io_fds[SFD_USERTTY] != -1) {
        !           481:        sa.sa_flags = SA_RESTART;
        !           482:        sa.sa_handler = sigwinch;
        !           483:        sigaction(SIGWINCH, &sa, NULL);
        !           484:     }
        !           485: 
        !           486:     /* So we can block tty-generated signals */
        !           487:     sigemptyset(&ttyblock);
        !           488:     sigaddset(&ttyblock, SIGINT);
        !           489:     sigaddset(&ttyblock, SIGQUIT);
        !           490:     sigaddset(&ttyblock, SIGTSTP);
        !           491:     sigaddset(&ttyblock, SIGTTIN);
        !           492:     sigaddset(&ttyblock, SIGTTOU);
        !           493: 
        !           494:     /*
        !           495:      * Setup stdin/stdout/stderr for child, to be duped after forking.
        !           496:      * In background mode there is no stdin.
        !           497:      */
        !           498:     if (!ISSET(details->flags, CD_BACKGROUND))
        !           499:        io_fds[SFD_STDIN] = io_fds[SFD_SLAVE];
        !           500:     io_fds[SFD_STDOUT] = io_fds[SFD_SLAVE];
        !           501:     io_fds[SFD_STDERR] = io_fds[SFD_SLAVE];
        !           502: 
        !           503:     if (io_fds[SFD_USERTTY] != -1) {
        !           504:        /* Read from /dev/tty, write to pty master */
        !           505:        if (!ISSET(details->flags, CD_BACKGROUND)) {
        !           506:            iobufs = io_buf_new(io_fds[SFD_USERTTY], io_fds[SFD_MASTER],
        !           507:                log_ttyin, iobufs);
        !           508:        }
        !           509: 
        !           510:        /* Read from pty master, write to /dev/tty */
        !           511:        iobufs = io_buf_new(io_fds[SFD_MASTER], io_fds[SFD_USERTTY],
        !           512:            log_ttyout, iobufs);
        !           513: 
        !           514:        /* Are we the foreground process? */
        !           515:        foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;
        !           516:     }
        !           517: 
        !           518:     /*
        !           519:      * If either stdin, stdout or stderr is not a tty we use a pipe
        !           520:      * to interpose ourselves instead of duping the pty fd.
        !           521:      */
        !           522:     memset(io_pipe, 0, sizeof(io_pipe));
        !           523:     if (io_fds[SFD_STDIN] == -1 || !isatty(STDIN_FILENO)) {
        !           524:        pipeline = TRUE;
        !           525:        if (pipe(io_pipe[STDIN_FILENO]) != 0)
        !           526:            error(1, _("unable to create pipe"));
        !           527:        iobufs = io_buf_new(STDIN_FILENO, io_pipe[STDIN_FILENO][1],
        !           528:            log_stdin, iobufs);
        !           529:        io_fds[SFD_STDIN] = io_pipe[STDIN_FILENO][0];
        !           530:     }
        !           531:     if (io_fds[SFD_STDOUT] == -1 || !isatty(STDOUT_FILENO)) {
        !           532:        pipeline = TRUE;
        !           533:        if (pipe(io_pipe[STDOUT_FILENO]) != 0)
        !           534:            error(1, _("unable to create pipe"));
        !           535:        iobufs = io_buf_new(io_pipe[STDOUT_FILENO][0], STDOUT_FILENO,
        !           536:            log_stdout, iobufs);
        !           537:        io_fds[SFD_STDOUT] = io_pipe[STDOUT_FILENO][1];
        !           538:     }
        !           539:     if (io_fds[SFD_STDERR] == -1 || !isatty(STDERR_FILENO)) {
        !           540:        if (pipe(io_pipe[STDERR_FILENO]) != 0)
        !           541:            error(1, _("unable to create pipe"));
        !           542:        iobufs = io_buf_new(io_pipe[STDERR_FILENO][0], STDERR_FILENO,
        !           543:            log_stderr, iobufs);
        !           544:        io_fds[SFD_STDERR] = io_pipe[STDERR_FILENO][1];
        !           545:     }
        !           546: 
        !           547:     /* Job control signals to relay from parent to child. */
        !           548:     sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */
        !           549:     sa.sa_handler = handler;
        !           550:     sigaction(SIGTSTP, &sa, NULL);
        !           551: 
        !           552:     if (foreground) {
        !           553:        /* Copy terminal attrs from user tty -> pty slave. */
        !           554:        if (term_copy(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE])) {
        !           555:            tty_initialized = 1;
        !           556:            sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
        !           557:        }
        !           558: 
        !           559:        /* Start out in raw mode if we are not part of a pipeline. */
        !           560:        if (!pipeline) {
        !           561:            ttymode = TERM_RAW;
        !           562:            do {
        !           563:                n = term_raw(io_fds[SFD_USERTTY], 0);
        !           564:            } while (!n && errno == EINTR);
        !           565:            if (!n)
        !           566:                error(1, _("unable to set terminal to raw mode"));
        !           567:        }
        !           568:     }
        !           569: 
        !           570:     child = fork();
        !           571:     switch (child) {
        !           572:     case -1:
        !           573:        error(1, _("unable to fork"));
        !           574:        break;
        !           575:     case 0:
        !           576:        /* child */
        !           577:        close(sv[0]);
        !           578:        close(signal_pipe[0]);
        !           579:        close(signal_pipe[1]);
        !           580:        fcntl(sv[1], F_SETFD, FD_CLOEXEC);
        !           581:        if (exec_setup(details, slavename, io_fds[SFD_SLAVE]) == TRUE) {
        !           582:            /* Close the other end of the stdin/stdout/stderr pipes and exec. */
        !           583:            if (io_pipe[STDIN_FILENO][1])
        !           584:                close(io_pipe[STDIN_FILENO][1]);
        !           585:            if (io_pipe[STDOUT_FILENO][0])
        !           586:                close(io_pipe[STDOUT_FILENO][0]);
        !           587:            if (io_pipe[STDERR_FILENO][0])
        !           588:                close(io_pipe[STDERR_FILENO][0]);
        !           589:            exec_monitor(details, sv[1]);
        !           590:        }
        !           591:        cstat.type = CMD_ERRNO;
        !           592:        cstat.val = errno;
        !           593:        send(sv[1], &cstat, sizeof(cstat), 0);
        !           594:        _exit(1);
        !           595:     }
        !           596: 
        !           597:     /* Close the other end of the stdin/stdout/stderr pipes. */
        !           598:     if (io_pipe[STDIN_FILENO][0])
        !           599:        close(io_pipe[STDIN_FILENO][0]);
        !           600:     if (io_pipe[STDOUT_FILENO][1])
        !           601:        close(io_pipe[STDOUT_FILENO][1]);
        !           602:     if (io_pipe[STDERR_FILENO][1]) 
        !           603:        close(io_pipe[STDERR_FILENO][1]);
        !           604: 
        !           605:     for (iob = iobufs; iob; iob = iob->next) {
        !           606:        /* Determine maxfd */
        !           607:        if (iob->rfd > *maxfd)
        !           608:            *maxfd = iob->rfd;
        !           609:        if (iob->wfd > *maxfd)
        !           610:            *maxfd = iob->wfd;
        !           611: 
        !           612:        /* Set non-blocking mode. */
        !           613:        n = fcntl(iob->rfd, F_GETFL, 0);
        !           614:        if (n != -1 && !ISSET(n, O_NONBLOCK))
        !           615:            (void) fcntl(iob->rfd, F_SETFL, n | O_NONBLOCK);
        !           616:        n = fcntl(iob->wfd, F_GETFL, 0);
        !           617:        if (n != -1 && !ISSET(n, O_NONBLOCK))
        !           618:            (void) fcntl(iob->wfd, F_SETFL, n | O_NONBLOCK);
        !           619:     }
        !           620: 
        !           621:     return child;
        !           622: }
        !           623: 
        !           624: void
        !           625: pty_close(struct command_status *cstat)
        !           626: {
        !           627:     int n;
        !           628: 
        !           629:     /* Flush any remaining output (the plugin already got it) */
        !           630:     if (io_fds[SFD_USERTTY] != -1) {
        !           631:        n = fcntl(io_fds[SFD_USERTTY], F_GETFL, 0);
        !           632:        if (n != -1 && ISSET(n, O_NONBLOCK)) {
        !           633:            CLR(n, O_NONBLOCK);
        !           634:            (void) fcntl(io_fds[SFD_USERTTY], F_SETFL, n);
        !           635:        }
        !           636:     }
        !           637:     flush_output();
        !           638: 
        !           639:     if (io_fds[SFD_USERTTY] != -1) {
        !           640:        do {
        !           641:            n = term_restore(io_fds[SFD_USERTTY], 0);
        !           642:        } while (!n && errno == EINTR);
        !           643:     }
        !           644: 
        !           645:     /* If child was signalled, write the reason to stdout like the shell. */
        !           646:     if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
        !           647:        int signo = WTERMSIG(cstat->val);
        !           648:        if (signo && signo != SIGINT && signo != SIGPIPE) {
        !           649:            const char *reason = strsignal(signo);
        !           650:            n = io_fds[SFD_USERTTY] != -1 ?
        !           651:                io_fds[SFD_USERTTY] : STDOUT_FILENO;
        !           652:            if (write(n, reason, strlen(reason)) != -1) {
        !           653:                if (WCOREDUMP(cstat->val)) {
        !           654:                    if (write(n, " (core dumped)", 14) == -1)
        !           655:                        /* shut up glibc */;
        !           656:                }
        !           657:                if (write(n, "\n", 1) == -1)
        !           658:                    /* shut up glibc */;
        !           659:            }
        !           660:        }
        !           661:     }
        !           662:     utmp_logout(slavename, cstat->type == CMD_WSTATUS ? cstat->val : 0); /* XXX - only if CD_SET_UTMP */
        !           663: }
        !           664: 
        !           665: /*
        !           666:  * Fill in fdsr and fdsw based on the io buffers list.
        !           667:  * Called prior to select().
        !           668:  */
        !           669: void
        !           670: fd_set_iobs(fd_set *fdsr, fd_set *fdsw)
        !           671: {
        !           672:     struct io_buffer *iob;
        !           673: 
        !           674:     for (iob = iobufs; iob; iob = iob->next) {
        !           675:        if (iob->rfd == -1 && iob->wfd == -1)
        !           676:            continue;
        !           677:        if (iob->off == iob->len) {
        !           678:            iob->off = iob->len = 0;
        !           679:            /* Forward the EOF from reader to writer. */
        !           680:            if (iob->rfd == -1) {
        !           681:                safe_close(iob->wfd);
        !           682:                iob->wfd = -1;
        !           683:            }
        !           684:        }
        !           685:        /* Don't read/write /dev/tty if we are not in the foreground. */
        !           686:        if (iob->rfd != -1 &&
        !           687:            (ttymode == TERM_RAW || iob->rfd != io_fds[SFD_USERTTY])) {
        !           688:            if (iob->len != sizeof(iob->buf))
        !           689:                FD_SET(iob->rfd, fdsr);
        !           690:        }
        !           691:        if (iob->wfd != -1 &&
        !           692:            (foreground || iob->wfd != io_fds[SFD_USERTTY])) {
        !           693:            if (iob->len > iob->off)
        !           694:                FD_SET(iob->wfd, fdsw);
        !           695:        }
        !           696:     }
        !           697: }
        !           698: 
        !           699: static void
        !           700: deliver_signal(pid_t pid, int signo)
        !           701: {
        !           702:     int status;
        !           703: 
        !           704:     /* Handle signal from parent. */
        !           705:     sudo_debug(8, "signal %d from parent", signo);
        !           706:     switch (signo) {
        !           707:     case SIGALRM:
        !           708:        terminate_child(pid, TRUE);
        !           709:        break;
        !           710:     case SIGCONT_FG:
        !           711:        /* Continue in foreground, grant it controlling tty. */
        !           712:        do {
        !           713:            status = tcsetpgrp(io_fds[SFD_SLAVE], child_pgrp);
        !           714:        } while (status == -1 && errno == EINTR);
        !           715:        killpg(pid, SIGCONT);
        !           716:        break;
        !           717:     case SIGCONT_BG:
        !           718:        /* Continue in background, I take controlling tty. */
        !           719:        do {
        !           720:            status = tcsetpgrp(io_fds[SFD_SLAVE], getpid());
        !           721:        } while (status == -1 && errno == EINTR);
        !           722:        killpg(pid, SIGCONT);
        !           723:        break;
        !           724:     case SIGKILL:
        !           725:        _exit(1); /* XXX */
        !           726:        /* NOTREACHED */
        !           727:     default:
        !           728:        /* Relay signal to child. */
        !           729:        killpg(pid, signo);
        !           730:        break;
        !           731:     }
        !           732: }
        !           733: 
        !           734: /*
        !           735:  * Send status to parent over socketpair.
        !           736:  * Return value is the same as send(2).
        !           737:  */
        !           738: static int
        !           739: send_status(int fd, struct command_status *cstat)
        !           740: {
        !           741:     int n = -1;
        !           742: 
        !           743:     if (cstat->type != CMD_INVALID) {
        !           744:        do {
        !           745:            n = send(fd, cstat, sizeof(*cstat), 0);
        !           746:        } while (n == -1 && errno == EINTR);
        !           747:        if (n != sizeof(*cstat)) {
        !           748:            sudo_debug(8, "unable to send status to parent: %s",
        !           749:                strerror(errno));
        !           750:        } else {
        !           751:            sudo_debug(8, "sent status to parent");
        !           752:        }
        !           753:        cstat->type = CMD_INVALID; /* prevent re-sending */
        !           754:     }
        !           755:     return n;
        !           756: }
        !           757: 
        !           758: /*
        !           759:  * Wait for child status after receiving SIGCHLD.
        !           760:  * If the child was stopped, the status is send back to the parent.
        !           761:  * Otherwise, cstat is filled in but not sent.
        !           762:  * Returns TRUE if child is still alive, else FALSE.
        !           763:  */
        !           764: static int
        !           765: handle_sigchld(int backchannel, struct command_status *cstat)
        !           766: {
        !           767:     int status, alive = TRUE;
        !           768:     pid_t pid;
        !           769: 
        !           770:     /* read child status */
        !           771:     do {
        !           772:        pid = waitpid(child, &status, WUNTRACED|WNOHANG);
        !           773:     } while (pid == -1 && errno == EINTR);
        !           774:     if (pid == child) {
        !           775:        if (cstat->type != CMD_ERRNO) {
        !           776:            cstat->type = CMD_WSTATUS;
        !           777:            cstat->val = status;
        !           778:            if (WIFSTOPPED(status)) {
        !           779:                sudo_debug(8, "command stopped, signal %d", WSTOPSIG(status));
        !           780:                do {
        !           781:                    child_pgrp = tcgetpgrp(io_fds[SFD_SLAVE]);
        !           782:                } while (child_pgrp == -1 && errno == EINTR);
        !           783:                if (send_status(backchannel, cstat) == -1)
        !           784:                    return alive; /* XXX */
        !           785:            } else if (WIFSIGNALED(status)) {
        !           786:                sudo_debug(8, "command killed, signal %d", WTERMSIG(status));
        !           787:            } else {
        !           788:                sudo_debug(8, "command exited: %d", WEXITSTATUS(status));
        !           789:            }
        !           790:        }
        !           791:        if (!WIFSTOPPED(status))
        !           792:            alive = FALSE;
        !           793:     }
        !           794:     return alive;
        !           795: }
        !           796: 
        !           797: /*
        !           798:  * Monitor process that creates a new session with the controlling tty,
        !           799:  * resets signal handlers and forks a child to call exec_pty().
        !           800:  * Waits for status changes from the command and relays them to the
        !           801:  * parent and relays signals from the parent to the command.
        !           802:  * Returns an error if fork(2) fails, else calls _exit(2).
        !           803:  */
        !           804: static int
        !           805: exec_monitor(struct command_details *details, int backchannel)
        !           806: {
        !           807:     struct command_status cstat;
        !           808:     struct timeval tv;
        !           809:     fd_set *fdsr;
        !           810:     sigaction_t sa;
        !           811:     int errpipe[2], maxfd, n, status;
        !           812:     int alive = TRUE;
        !           813:     unsigned char signo;
        !           814: 
        !           815:     /* Close unused fds. */
        !           816:     if (io_fds[SFD_MASTER] != -1)
        !           817:        close(io_fds[SFD_MASTER]);
        !           818:     if (io_fds[SFD_USERTTY] != -1)
        !           819:        close(io_fds[SFD_USERTTY]);
        !           820: 
        !           821:     /*
        !           822:      * We use a pipe to atomically handle signal notification within
        !           823:      * the select() loop.
        !           824:      */
        !           825:     if (pipe_nonblock(signal_pipe) != 0)
        !           826:        error(1, _("unable to create pipe"));
        !           827: 
        !           828:     /* Reset SIGWINCH and SIGALRM. */
        !           829:     zero_bytes(&sa, sizeof(sa));
        !           830:     sigemptyset(&sa.sa_mask);
        !           831:     sa.sa_flags = SA_RESTART;
        !           832:     sa.sa_handler = SIG_DFL;
        !           833:     sigaction(SIGWINCH, &sa, NULL);
        !           834:     sigaction(SIGALRM, &sa, NULL);
        !           835: 
        !           836:     /* Ignore any SIGTTIN or SIGTTOU we get. */
        !           837:     sa.sa_handler = SIG_IGN;
        !           838:     sigaction(SIGTTIN, &sa, NULL);
        !           839:     sigaction(SIGTTOU, &sa, NULL);
        !           840: 
        !           841:     /* Note: HP-UX select() will not be interrupted if SA_RESTART set */
        !           842:     sa.sa_flags = SA_INTERRUPT;
        !           843:     sa.sa_handler = handler;
        !           844:     sigaction(SIGCHLD, &sa, NULL);
        !           845: 
        !           846:     /*
        !           847:      * Start a new session with the parent as the session leader
        !           848:      * and the slave pty as the controlling terminal.
        !           849:      * This allows us to be notified when the child has been suspended.
        !           850:      */
        !           851:     if (setsid() == -1) {
        !           852:        warning("setsid");
        !           853:        goto bad;
        !           854:     }
        !           855:     if (io_fds[SFD_SLAVE] != -1) {
        !           856: #ifdef TIOCSCTTY
        !           857:        if (ioctl(io_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
        !           858:            error(1, _("unable to set controlling tty"));
        !           859: #else
        !           860:        /* Set controlling tty by reopening slave. */
        !           861:        if ((n = open(slavename, O_RDWR)) >= 0)
        !           862:            close(n);
        !           863: #endif
        !           864:     }
        !           865: 
        !           866:     /*
        !           867:      * If stdin/stdout is not a tty, start command in the background
        !           868:      * since it might be part of a pipeline that reads from /dev/tty.
        !           869:      * In this case, we rely on the command receiving SIGTTOU or SIGTTIN
        !           870:      * when it needs access to the controlling tty.
        !           871:      */
        !           872:     if (pipeline)
        !           873:        foreground = 0;
        !           874: 
        !           875:     /* Start command and wait for it to stop or exit */
        !           876:     if (pipe(errpipe) == -1)
        !           877:        error(1, _("unable to create pipe"));
        !           878:     child = fork();
        !           879:     if (child == -1) {
        !           880:        warning(_("unable to fork"));
        !           881:        goto bad;
        !           882:     }
        !           883:     if (child == 0) {
        !           884:        /* We pass errno back to our parent via pipe on exec failure. */
        !           885:        close(backchannel);
        !           886:        close(signal_pipe[0]);
        !           887:        close(signal_pipe[1]);
        !           888:        close(errpipe[0]);
        !           889:        fcntl(errpipe[1], F_SETFD, FD_CLOEXEC);
        !           890:        restore_signals();
        !           891: 
        !           892:        /* setup tty and exec command */
        !           893:        exec_pty(details);
        !           894:        cstat.type = CMD_ERRNO;
        !           895:        cstat.val = errno;
        !           896:        if (write(errpipe[1], &cstat, sizeof(cstat)) == -1)
        !           897:            /* shut up glibc */;
        !           898:        _exit(1);
        !           899:     }
        !           900:     close(errpipe[1]);
        !           901: 
        !           902:     /* If any of stdin/stdout/stderr are pipes, close them in parent. */
        !           903:     if (io_fds[SFD_STDIN] != io_fds[SFD_SLAVE])
        !           904:        close(io_fds[SFD_STDIN]);
        !           905:     if (io_fds[SFD_STDOUT] != io_fds[SFD_SLAVE])
        !           906:        close(io_fds[SFD_STDOUT]);
        !           907:     if (io_fds[SFD_STDERR] != io_fds[SFD_SLAVE])
        !           908:        close(io_fds[SFD_STDERR]);
        !           909: 
        !           910:     /*
        !           911:      * Put child in its own process group.  If we are starting the command
        !           912:      * in the foreground, assign its pgrp to the tty.
        !           913:      */
        !           914:     child_pgrp = child;
        !           915:     setpgid(child, child_pgrp);
        !           916:     if (foreground) {
        !           917:        do {
        !           918:            status = tcsetpgrp(io_fds[SFD_SLAVE], child_pgrp);
        !           919:        } while (status == -1 && errno == EINTR);
        !           920:     }
        !           921: 
        !           922:     /* Wait for errno on pipe, signal on backchannel or for SIGCHLD */
        !           923:     maxfd = MAX(MAX(errpipe[0], signal_pipe[0]), backchannel);
        !           924:     fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
        !           925:     zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
        !           926:     zero_bytes(&cstat, sizeof(cstat));
        !           927:     tv.tv_sec = 0;
        !           928:     tv.tv_usec = 0;
        !           929:     for (;;) {
        !           930:        /* Check for signal on backchannel or errno on errpipe. */
        !           931:        FD_SET(backchannel, fdsr);
        !           932:        FD_SET(signal_pipe[0], fdsr);
        !           933:        if (errpipe[0] != -1)
        !           934:            FD_SET(errpipe[0], fdsr);
        !           935:        maxfd = MAX(MAX(errpipe[0], signal_pipe[0]), backchannel);
        !           936: 
        !           937:        /* If command exited we just poll, there may be data on errpipe. */
        !           938:        n = select(maxfd + 1, fdsr, NULL, NULL, alive ? NULL : &tv);
        !           939:        if (n <= 0) {
        !           940:            if (n == 0)
        !           941:                goto done;
        !           942:            if (errno == EINTR)
        !           943:                continue;
        !           944:            error(1, _("select failed"));
        !           945:        }
        !           946: 
        !           947:        if (FD_ISSET(signal_pipe[0], fdsr)) {
        !           948:            n = read(signal_pipe[0], &signo, sizeof(signo));
        !           949:            if (n == -1) {
        !           950:                if (errno == EINTR || errno == EAGAIN)
        !           951:                    continue;
        !           952:                warning(_("error reading from signal pipe"));
        !           953:                goto done;
        !           954:            }
        !           955:            /*
        !           956:             * Handle SIGCHLD specially and deliver other signals
        !           957:             * directly to the child.
        !           958:             */
        !           959:            if (signo == SIGCHLD)
        !           960:                alive = handle_sigchld(backchannel, &cstat);
        !           961:            else
        !           962:                deliver_signal(child, signo);
        !           963:            continue;
        !           964:        }
        !           965:        if (errpipe[0] != -1 && FD_ISSET(errpipe[0], fdsr)) {
        !           966:            /* read errno or EOF from command pipe */
        !           967:            n = read(errpipe[0], &cstat, sizeof(cstat));
        !           968:            if (n == -1) {
        !           969:                if (errno == EINTR)
        !           970:                    continue;
        !           971:                warning(_("error reading from pipe"));
        !           972:                goto done;
        !           973:            }
        !           974:            /* Got errno or EOF, either way we are done with errpipe. */
        !           975:            FD_CLR(errpipe[0], fdsr);
        !           976:            close(errpipe[0]);
        !           977:            errpipe[0] = -1;
        !           978:        }
        !           979:        if (FD_ISSET(backchannel, fdsr)) {
        !           980:            struct command_status cstmp;
        !           981: 
        !           982:            /* read command from backchannel, should be a signal */
        !           983:            n = recv(backchannel, &cstmp, sizeof(cstmp), 0);
        !           984:            if (n == -1) {
        !           985:                if (errno == EINTR)
        !           986:                    continue;
        !           987:                warning(_("error reading from socketpair"));
        !           988:                goto done;
        !           989:            }
        !           990:            if (cstmp.type != CMD_SIGNO) {
        !           991:                warningx(_("unexpected reply type on backchannel: %d"),
        !           992:                    cstmp.type);
        !           993:                continue;
        !           994:            }
        !           995:            deliver_signal(child, cstmp.val);
        !           996:        }
        !           997:     }
        !           998: 
        !           999: done:
        !          1000:     if (alive) {
        !          1001:        /* XXX An error occurred, should send an error back. */
        !          1002:        kill(child, SIGKILL);
        !          1003:     } else {
        !          1004:        /* Send parent status. */
        !          1005:        send_status(backchannel, &cstat);
        !          1006:     }
        !          1007:     _exit(1);
        !          1008: 
        !          1009: bad:
        !          1010:     return errno;
        !          1011: }
        !          1012: 
        !          1013: /*
        !          1014:  * Flush any output buffered in iobufs or readable from the fds.
        !          1015:  * Does not read from /dev/tty.
        !          1016:  */
        !          1017: static void
        !          1018: flush_output(void)
        !          1019: {
        !          1020:     struct io_buffer *iob;
        !          1021:     struct timeval tv;
        !          1022:     fd_set *fdsr, *fdsw;
        !          1023:     int nready, nwriters, maxfd = -1;
        !          1024: 
        !          1025:     /* Determine maxfd */
        !          1026:     for (iob = iobufs; iob; iob = iob->next) {
        !          1027:        if (iob->rfd > maxfd)
        !          1028:            maxfd = iob->rfd;
        !          1029:        if (iob->wfd > maxfd)
        !          1030:            maxfd = iob->wfd;
        !          1031:     }
        !          1032:     if (maxfd == -1)
        !          1033:        return;
        !          1034: 
        !          1035:     fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
        !          1036:     fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
        !          1037:     for (;;) {
        !          1038:        zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
        !          1039:        zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
        !          1040: 
        !          1041:        nwriters = 0;
        !          1042:        for (iob = iobufs; iob; iob = iob->next) {
        !          1043:            /* Don't read from /dev/tty while flushing. */
        !          1044:            if (io_fds[SFD_USERTTY] != -1 && iob->rfd == io_fds[SFD_USERTTY])
        !          1045:                continue;
        !          1046:            if (iob->rfd == -1 && iob->wfd == -1)
        !          1047:                continue;
        !          1048:            if (iob->off == iob->len) {
        !          1049:                iob->off = iob->len = 0;
        !          1050:                /* Forward the EOF from reader to writer. */
        !          1051:                if (iob->rfd == -1) {
        !          1052:                    safe_close(iob->wfd);
        !          1053:                    iob->wfd = -1;
        !          1054:                }
        !          1055:            }
        !          1056:            if (iob->rfd != -1) {
        !          1057:                if (iob->len != sizeof(iob->buf))
        !          1058:                    FD_SET(iob->rfd, fdsr);
        !          1059:            }
        !          1060:            if (iob->wfd != -1) {
        !          1061:                if (iob->len > iob->off) {
        !          1062:                    nwriters++;
        !          1063:                    FD_SET(iob->wfd, fdsw);
        !          1064:                }
        !          1065:            }
        !          1066:        }
        !          1067: 
        !          1068:        /* Don't sleep in select if there are no buffers that need writing. */
        !          1069:        tv.tv_sec = 0;
        !          1070:        tv.tv_usec = 0;
        !          1071:        nready = select(maxfd + 1, fdsr, fdsw, NULL, nwriters ? NULL : &tv);
        !          1072:        if (nready <= 0) {
        !          1073:            if (nready == 0)
        !          1074:                break; /* all I/O flushed */
        !          1075:            if (errno == EINTR)
        !          1076:                continue;
        !          1077:            error(1, _("select failed"));
        !          1078:        }
        !          1079:        if (perform_io(fdsr, fdsw, NULL) != 0)
        !          1080:            break;
        !          1081:     }
        !          1082:     efree(fdsr);
        !          1083:     efree(fdsw);
        !          1084: }
        !          1085: 
        !          1086: /*
        !          1087:  * Sets up std{in,out,err} and executes the actual command.
        !          1088:  * Returns only if execve() fails.
        !          1089:  */
        !          1090: static void
        !          1091: exec_pty(struct command_details *details)
        !          1092: {
        !          1093:     pid_t self = getpid();
        !          1094: 
        !          1095:     /* Set child process group here too to avoid a race. */
        !          1096:     setpgid(0, self);
        !          1097: 
        !          1098:     /* Wire up standard fds, note that stdout/stderr may be pipes. */
        !          1099:     if (dup2(io_fds[SFD_STDIN], STDIN_FILENO) == -1 ||
        !          1100:        dup2(io_fds[SFD_STDOUT], STDOUT_FILENO) == -1 ||
        !          1101:        dup2(io_fds[SFD_STDERR], STDERR_FILENO) == -1)
        !          1102:        error(1, "dup2");
        !          1103: 
        !          1104:     /* Wait for parent to grant us the tty if we are foreground. */
        !          1105:     if (foreground) {
        !          1106:        while (tcgetpgrp(io_fds[SFD_SLAVE]) != self)
        !          1107:            ; /* spin */
        !          1108:     }
        !          1109: 
        !          1110:     /* We have guaranteed that the slave fd is > 2 */
        !          1111:     if (io_fds[SFD_SLAVE] != -1)
        !          1112:        close(io_fds[SFD_SLAVE]);
        !          1113:     if (io_fds[SFD_STDIN] != io_fds[SFD_SLAVE])
        !          1114:        close(io_fds[SFD_STDIN]);
        !          1115:     if (io_fds[SFD_STDOUT] != io_fds[SFD_SLAVE])
        !          1116:        close(io_fds[SFD_STDOUT]);
        !          1117:     if (io_fds[SFD_STDERR] != io_fds[SFD_SLAVE])
        !          1118:        close(io_fds[SFD_STDERR]);
        !          1119: 
        !          1120:     if (details->closefrom >= 0)
        !          1121:        closefrom(details->closefrom);
        !          1122: #ifdef HAVE_SELINUX
        !          1123:     if (ISSET(details->flags, CD_RBAC_ENABLED))
        !          1124:        selinux_execve(details->command, details->argv, details->envp);
        !          1125:     else
        !          1126: #endif
        !          1127:        my_execve(details->command, details->argv, details->envp);
        !          1128: }
        !          1129: 
        !          1130: /*
        !          1131:  * Propagates tty size change signals to pty being used by the command.
        !          1132:  */
        !          1133: static void
        !          1134: sync_ttysize(int src, int dst)
        !          1135: {
        !          1136: #ifdef TIOCGWINSZ
        !          1137:     struct winsize wsize;
        !          1138:     pid_t pgrp;
        !          1139: 
        !          1140:     if (ioctl(src, TIOCGWINSZ, &wsize) == 0) {
        !          1141:            ioctl(dst, TIOCSWINSZ, &wsize);
        !          1142:            if ((pgrp = tcgetpgrp(dst)) != -1)
        !          1143:                killpg(pgrp, SIGWINCH);
        !          1144:     }
        !          1145: #endif
        !          1146: }
        !          1147: 
        !          1148: /*
        !          1149:  * Handler for SIGWINCH in parent.
        !          1150:  */
        !          1151: static void
        !          1152: sigwinch(int s)
        !          1153: {
        !          1154:     int serrno = errno;
        !          1155: 
        !          1156:     sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
        !          1157:     errno = serrno;
        !          1158: }
        !          1159: 
        !          1160: /*
        !          1161:  * Only close the fd if it is not /dev/tty or std{in,out,err}.
        !          1162:  * Return value is the same as send(2).
        !          1163:  */
        !          1164: static int
        !          1165: safe_close(int fd)
        !          1166: {
        !          1167:     /* Avoid closing /dev/tty or std{in,out,err}. */
        !          1168:     if (fd < 3 || fd == io_fds[SFD_USERTTY]) {
        !          1169:        errno = EINVAL;
        !          1170:        return -1;
        !          1171:     }
        !          1172:     return close(fd);
        !          1173: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>