[BACK]Return to visudo.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

Annotation of src/usr.bin/sudo/visudo.c, Revision 1.13

1.1       millert     1: /*
1.13    ! millert     2:  * Copyright (c) 1996, 1998-2003 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       millert     3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  *
                      9:  * 1. Redistributions of source code must retain the above copyright
                     10:  *    notice, this list of conditions and the following disclaimer.
                     11:  *
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  *
                     16:  * 3. The name of the author may not be used to endorse or promote products
                     17:  *    derived from this software without specific prior written permission.
                     18:  *
                     19:  * 4. Products derived from this software may not be called "Sudo" nor
                     20:  *    may "Sudo" appear in their names without specific prior written
                     21:  *    permission from the author.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
                     24:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                     25:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
                     26:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     27:  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     28:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
                     29:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     30:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
                     31:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
                     32:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     33:  */
                     34:
                     35: /*
                     36:  * Lock the sudoers file for safe editing (ala vipw) and check for parse errors.
                     37:  */
                     38:
                     39: #include "config.h"
                     40:
1.11      millert    41: #include <sys/types.h>
                     42: #include <sys/param.h>
                     43: #include <sys/stat.h>
                     44: #include <sys/file.h>
                     45: #include <sys/wait.h>
1.1       millert    46: #include <stdio.h>
                     47: #ifdef STDC_HEADERS
1.11      millert    48: # include <stdlib.h>
                     49: # include <stddef.h>
                     50: #else
                     51: # ifdef HAVE_STDLIB_H
                     52: #  include <stdlib.h>
                     53: # endif
1.1       millert    54: #endif /* STDC_HEADERS */
1.11      millert    55: #ifdef HAVE_STRING_H
                     56: # include <string.h>
                     57: #else
                     58: # ifdef HAVE_STRINGS_H
                     59: #  include <strings.h>
                     60: # endif
                     61: #endif /* HAVE_STRING_H */
1.1       millert    62: #ifdef HAVE_UNISTD_H
                     63: #include <unistd.h>
                     64: #endif /* HAVE_UNISTD_H */
                     65: #include <ctype.h>
                     66: #include <pwd.h>
                     67: #include <time.h>
                     68: #include <signal.h>
                     69: #include <errno.h>
                     70: #include <fcntl.h>
                     71:
                     72: #include "sudo.h"
                     73: #include "version.h"
                     74:
                     75: #ifndef lint
1.13    ! millert    76: static const char rcsid[] = "$Sudo: visudo.c,v 1.149 2003/03/15 20:31:02 millert Exp $";
1.1       millert    77: #endif /* lint */
                     78:
                     79: /*
                     80:  * Function prototypes
                     81:  */
                     82: static void usage              __P((void));
                     83: static char whatnow            __P((void));
                     84: static RETSIGTYPE Exit         __P((int));
                     85: static void setup_signals      __P((void));
1.11      millert    86: static int run_command         __P((char *, char **));
                     87: static int check_syntax                __P((int));
1.1       millert    88: int command_matches            __P((char *, char *, char *, char *));
                     89: int addr_matches               __P((char *));
1.4       millert    90: int hostname_matches           __P((char *, char *, char *));
1.3       millert    91: int netgr_matches              __P((char *, char *, char *, char *));
1.1       millert    92: int usergr_matches             __P((char *, char *));
                     93: void init_parser               __P((void));
                     94: void yyrestart                 __P((FILE *));
                     95:
                     96: /*
                     97:  * External globals exported by the parser
                     98:  */
                     99: extern FILE *yyin, *yyout;
                    100: extern int errorlineno;
                    101: extern int pedantic;
1.11      millert   102: extern int quiet;
                    103:
                    104: /* For getopt(3) */
                    105: extern char *optarg;
                    106: extern int optind;
1.1       millert   107:
                    108: /*
                    109:  * Globals
                    110:  */
                    111: char **Argv;
                    112: char *sudoers = _PATH_SUDOERS;
                    113: char *stmp = _PATH_SUDOERS_TMP;
                    114: struct sudo_user sudo_user;
                    115: int parse_error = FALSE;
                    116:
                    117: int
                    118: main(argc, argv)
                    119:     int argc;
                    120:     char **argv;
                    121: {
                    122:     char buf[MAXPATHLEN*2];            /* buffer used for copying files */
1.4       millert   123:     char *Editor;                      /* editor to use */
1.11      millert   124:     char *UserEditor;                  /* editor user wants to use */
                    125:     char *EditorPath;                  /* colon-separated list of editors */
                    126:     char *av[4];                       /* argument vector for run_command */
                    127:     int checkonly;                     /* only check existing file? */
1.1       millert   128:     int sudoers_fd;                    /* sudoers file descriptor */
                    129:     int stmp_fd;                       /* stmp file descriptor */
                    130:     int n;                             /* length parameter */
1.11      millert   131:     int ch;                            /* getopt char */
1.1       millert   132:     time_t now;                                /* time now */
                    133:     struct stat stmp_sb, sudoers_sb;   /* to check for changes */
                    134:
                    135:     /* Warn about aliases that are used before being defined. */
                    136:     pedantic = 1;
                    137:
                    138:     /*
                    139:      * Parse command line options
                    140:      */
                    141:     Argv = argv;
                    142:
                    143:     /*
                    144:      * Arg handling.
                    145:      */
1.11      millert   146:     checkonly = 0;
                    147:     while ((ch = getopt(argc, argv, "Vcf:sq")) != -1) {
                    148:        switch (ch) {
                    149:            case 'V':
                    150:                (void) printf("visudo version %s\n", version);
                    151:                exit(0);
                    152:            case 'c':
                    153:                checkonly++;            /* check mode */
                    154:                break;
                    155:            case 'f':
                    156:                sudoers = optarg;       /* sudoers file path */
                    157:                easprintf(&stmp, "%s.tmp", optarg);
                    158:                break;
                    159:            case 's':
                    160:                pedantic++;             /* strict mode */
                    161:                break;
                    162:            case 'q':
                    163:                quiet++;                /* quiet mode */
                    164:                break;
                    165:            default:
                    166:                usage();
1.1       millert   167:        }
                    168:     }
1.11      millert   169:     argc -= optind;
                    170:     argv += optind;
                    171:     if (argc)
                    172:        usage();
1.1       millert   173:
                    174:     /* Mock up a fake sudo_user struct. */
                    175:     user_host = user_shost = user_cmnd = "";
                    176:     if ((sudo_user.pw = getpwuid(getuid())) == NULL) {
                    177:        (void) fprintf(stderr, "%s: Can't find you in the passwd database.\n",
                    178:            Argv[0]);
                    179:        exit(1);
                    180:     }
                    181:
1.4       millert   182:     /* Setup defaults data structures. */
                    183:     init_defaults();
1.1       millert   184:
1.11      millert   185:     if (checkonly)
                    186:        exit(check_syntax(quiet));
                    187:
1.1       millert   188:     /*
1.3       millert   189:      * Open sudoers, lock it and stat it.
                    190:      * sudoers_fd must remain open throughout in order to hold the lock.
1.1       millert   191:      */
1.6       millert   192:     sudoers_fd = open(sudoers, O_RDWR | O_CREAT, SUDOERS_MODE);
1.3       millert   193:     if (sudoers_fd == -1) {
                    194:        (void) fprintf(stderr, "%s: %s: %s\n", Argv[0], sudoers,
                    195:            strerror(errno));
1.10      millert   196:        exit(1);
1.1       millert   197:     }
1.3       millert   198:     if (!lock_file(sudoers_fd, SUDO_TLOCK)) {
1.1       millert   199:        (void) fprintf(stderr, "%s: sudoers file busy, try again later.\n",
                    200:            Argv[0]);
                    201:        exit(1);
                    202:     }
1.3       millert   203: #ifdef HAVE_FSTAT
                    204:     if (fstat(sudoers_fd, &sudoers_sb) == -1) {
1.1       millert   205: #else
1.3       millert   206:     if (stat(sudoers, &sudoers_sb) == -1) {
1.1       millert   207: #endif
1.3       millert   208:        (void) fprintf(stderr, "%s: can't stat %s: %s\n",
                    209:            Argv[0], sudoers, strerror(errno));
1.10      millert   210:        exit(1);
1.1       millert   211:     }
                    212:
1.3       millert   213:     /*
                    214:      * Open sudoers temp file.
                    215:      */
                    216:     stmp_fd = open(stmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
                    217:     if (stmp_fd < 0) {
                    218:        (void) fprintf(stderr, "%s: %s: %s\n", Argv[0], stmp, strerror(errno));
                    219:        exit(1);
                    220:     }
                    221:
1.1       millert   222:     /* Install signal handlers to clean up stmp if we are killed. */
                    223:     setup_signals();
                    224:
                    225:     /* Copy sudoers -> stmp and reset the mtime */
1.3       millert   226:     if (sudoers_sb.st_size) {
1.1       millert   227:        while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0)
                    228:            if (write(stmp_fd, buf, n) != n) {
                    229:                (void) fprintf(stderr, "%s: Write failed: %s\n", Argv[0],
                    230:                strerror(errno));
                    231:                Exit(-1);
                    232:            }
                    233:
1.13    ! millert   234:        /* Add missing newline at EOF if needed. */
        !           235:        if (n > 0 && buf[n - 1] != '\n') {
        !           236:            buf[0] = '\n';
        !           237:            write(stmp_fd, buf, 1);
        !           238:        }
        !           239:
1.3       millert   240:        (void) close(stmp_fd);
                    241:        (void) touch(stmp, sudoers_sb.st_mtime);
1.4       millert   242:
                    243:        /* Parse sudoers to pull in editor and env_editor conf values. */
                    244:        if ((yyin = fopen(stmp, "r"))) {
                    245:            yyout = stdout;
1.13    ! millert   246:            n = quiet;
        !           247:            quiet = 1;
1.4       millert   248:            init_parser();
                    249:            yyparse();
                    250:            parse_error = FALSE;
1.13    ! millert   251:            quiet = n;
1.4       millert   252:            fclose(yyin);
                    253:        }
1.3       millert   254:     } else
                    255:        (void) close(stmp_fd);
1.1       millert   256:
                    257:     /*
1.11      millert   258:      * Check EDITOR and VISUAL environment variables to see which editor
                    259:      * the user wants to use (we may not end up using it though).
                    260:      * If the path is not fully-qualified, make it so and check that
                    261:      * the specified executable actually exists.
1.4       millert   262:      */
1.11      millert   263:     if ((UserEditor = getenv("EDITOR")) == NULL || *UserEditor == '\0')
                    264:        UserEditor = getenv("VISUAL");
                    265:     if (UserEditor && *UserEditor == '\0')
                    266:        UserEditor = NULL;
                    267:     else if (UserEditor) {
                    268:        if (find_path(UserEditor, &Editor, getenv("PATH")) == FOUND) {
                    269:            UserEditor = Editor;
                    270:        } else {
                    271:            if (def_flag(I_ENV_EDITOR)) {
                    272:                /* If we are honoring $EDITOR this is a fatal error. */
                    273:                (void) fprintf(stderr,
                    274:                    "%s: specified editor (%s) doesn't exist!\n",
                    275:                    Argv[0], UserEditor);
                    276:                Exit(-1);
                    277:            } else {
                    278:                /* Otherwise, just ignore $EDITOR. */
                    279:                UserEditor = NULL;
                    280:            }
                    281:        }
                    282:     }
                    283:
                    284:     /*
                    285:      * See if we can use the user's choice of editors either because
                    286:      * we allow any $EDITOR or because $EDITOR is in the allowable list.
                    287:      */
1.12      millert   288:     Editor = EditorPath = NULL;
1.11      millert   289:     if (def_flag(I_ENV_EDITOR) && UserEditor)
                    290:        Editor = UserEditor;
                    291:     else if (UserEditor) {
                    292:        struct stat editor_sb;
                    293:        struct stat user_editor_sb;
                    294:        char *base, *userbase;
                    295:
                    296:        if (stat(UserEditor, &user_editor_sb) != 0) {
                    297:            /* Should never happen since we already checked above. */
                    298:            (void) fprintf(stderr, "%s: unable to stat editor (%s): %s\n",
                    299:                Argv[0], UserEditor, strerror(errno));
                    300:            Exit(-1);
                    301:        }
                    302:        EditorPath = estrdup(def_str(I_EDITOR));
                    303:        Editor = strtok(EditorPath, ":");
                    304:        do {
                    305:            /*
                    306:             * Both Editor and UserEditor should be fully qualified but
                    307:             * check anyway...
                    308:             */
                    309:            if ((base = strrchr(Editor, '/')) == NULL)
                    310:                continue;
                    311:            if ((userbase = strrchr(UserEditor, '/')) == NULL) {
                    312:                Editor = NULL;
                    313:                break;
                    314:            }
                    315:            base++, userbase++;
                    316:
                    317:            /*
                    318:             * We compare the basenames first and then use stat to match
                    319:             * for sure.
                    320:             */
                    321:            if (strcmp(base, userbase) == 0) {
                    322:                if (stat(Editor, &editor_sb) == 0 && S_ISREG(editor_sb.st_mode)
                    323:                    && (editor_sb.st_mode & 0000111) &&
                    324:                    editor_sb.st_dev == user_editor_sb.st_dev &&
                    325:                    editor_sb.st_ino == user_editor_sb.st_ino)
                    326:                    break;
                    327:            }
                    328:        } while ((Editor = strtok(NULL, ":")));
                    329:     }
                    330:
                    331:     /*
                    332:      * Can't use $EDITOR, try each element of I_EDITOR until we
                    333:      * find one that exists, is regular, and is executable.
                    334:      */
                    335:     if (Editor == NULL || *Editor == '\0') {
1.12      millert   336:        if (EditorPath != NULL)
                    337:            free(EditorPath);
1.11      millert   338:        EditorPath = estrdup(def_str(I_EDITOR));
                    339:        Editor = strtok(EditorPath, ":");
                    340:        do {
                    341:            if (sudo_goodpath(Editor))
                    342:                break;
                    343:        } while ((Editor = strtok(NULL, ":")));
                    344:
                    345:        /* Bleah, none of the editors existed! */
                    346:        if (Editor == NULL || *Editor == '\0') {
                    347:            (void) fprintf(stderr, "%s: no editor found (editor path = %s)\n",
                    348:                Argv[0], def_str(I_EDITOR));
                    349:            Exit(-1);
                    350:        }
                    351:     }
1.4       millert   352:
                    353:     /*
1.1       millert   354:      * Edit the temp file and parse it (for sanity checking)
                    355:      */
                    356:     do {
1.11      millert   357:        char linestr[64];
                    358:
                    359:        /* Build up argument vector for the command */
                    360:        if ((av[0] = strrchr(Editor, '/')) != NULL)
                    361:            av[0]++;
                    362:        else
                    363:            av[0] = Editor;
                    364:        n = 1;
                    365:        if (parse_error == TRUE) {
                    366:            (void) snprintf(linestr, sizeof(linestr), "+%d", errorlineno);
                    367:            av[n++] = linestr;
                    368:        }
                    369:        av[n++] = stmp;
                    370:        av[n++] = NULL;
                    371:
1.1       millert   372:        /*
1.11      millert   373:         * Do the edit:
                    374:         *  We cannot check the editor's exit value against 0 since
                    375:         *  XPG4 specifies that vi's exit value is a function of the
                    376:         *  number of errors during editing (?!?!).
1.1       millert   377:         */
                    378:        now = time(NULL);
1.11      millert   379:        if (run_command(Editor, av) != -1) {
1.1       millert   380:            /*
                    381:             * Sanity checks.
                    382:             */
                    383:            if (stat(stmp, &stmp_sb) < 0) {
                    384:                (void) fprintf(stderr,
                    385:                    "%s: Can't stat temporary file (%s), %s unchanged.\n",
                    386:                    Argv[0], stmp, sudoers);
                    387:                Exit(-1);
                    388:            }
                    389:            if (stmp_sb.st_size == 0) {
                    390:                (void) fprintf(stderr,
                    391:                    "%s: Zero length temporary file (%s), %s unchanged.\n",
                    392:                    Argv[0], stmp, sudoers);
                    393:                Exit(-1);
                    394:            }
                    395:
                    396:            /*
                    397:             * Passed sanity checks so reopen stmp file and check
                    398:             * for parse errors.
                    399:             */
                    400:            yyout = stdout;
1.13    ! millert   401:            yyin = fopen(stmp, "r+");
1.1       millert   402:            if (yyin == NULL) {
                    403:                (void) fprintf(stderr,
                    404:                    "%s: Can't re-open temporary file (%s), %s unchanged.\n",
                    405:                    Argv[0], stmp, sudoers);
                    406:                Exit(-1);
                    407:            }
                    408:
1.13    ! millert   409:            /* Add missing newline at EOF if needed. */
        !           410:            if (fseek(yyin, -1, SEEK_END) == 0 && (ch = fgetc(yyin)) != '\n')
        !           411:                fputc('\n', yyin);
        !           412:            rewind(yyin);
        !           413:
1.1       millert   414:            /* Clean slate for each parse */
1.5       millert   415:            user_runas = NULL;
1.1       millert   416:            init_defaults();
                    417:            init_parser();
                    418:
1.13    ! millert   419:            /* Parse the sudoers temp file */
        !           420:            yyrestart(yyin);
1.1       millert   421:            if (yyparse() && parse_error != TRUE) {
                    422:                (void) fprintf(stderr,
                    423:                    "%s: Failed to parse temporary file (%s), unknown error.\n",
                    424:                    Argv[0], stmp);
                    425:                parse_error = TRUE;
                    426:            }
1.13    ! millert   427:            fclose(yyin);
1.1       millert   428:        } else {
                    429:            (void) fprintf(stderr,
1.11      millert   430:                "%s: Editor (%s) failed, %s unchanged.\n", Argv[0],
                    431:                    Editor, sudoers);
1.1       millert   432:            Exit(-1);
                    433:        }
                    434:
                    435:        /*
                    436:         * Got an error, prompt the user for what to do now
                    437:         */
                    438:        if (parse_error == TRUE) {
                    439:            switch (whatnow()) {
1.7       millert   440:                case 'Q' :      parse_error = FALSE;    /* ignore parse error */
1.1       millert   441:                                break;
1.8       millert   442:                case 'x' :      if (sudoers_sb.st_size == 0)
                    443:                                    unlink(sudoers);
                    444:                                Exit(0);
1.1       millert   445:                                break;
                    446:            }
                    447:        }
                    448:     } while (parse_error == TRUE);
                    449:
                    450:     /*
                    451:      * If the user didn't change the temp file, just unlink it.
                    452:      */
                    453:     if (sudoers_sb.st_mtime != now && sudoers_sb.st_mtime == stmp_sb.st_mtime &&
                    454:        sudoers_sb.st_size == stmp_sb.st_size) {
                    455:        (void) fprintf(stderr, "%s: sudoers file unchanged.\n", Argv[0]);
                    456:        Exit(0);
                    457:     }
                    458:
                    459:     /*
                    460:      * Change mode and ownership of temp file so when
                    461:      * we move it to sudoers things are kosher.
                    462:      */
                    463:     if (chown(stmp, SUDOERS_UID, SUDOERS_GID)) {
                    464:        (void) fprintf(stderr,
                    465:            "%s: Unable to set (uid, gid) of %s to (%d, %d): %s\n",
                    466:            Argv[0], stmp, SUDOERS_UID, SUDOERS_GID, strerror(errno));
                    467:        Exit(-1);
                    468:     }
                    469:     if (chmod(stmp, SUDOERS_MODE)) {
                    470:        (void) fprintf(stderr,
                    471:            "%s: Unable to change mode of %s to %o: %s\n",
                    472:            Argv[0], stmp, SUDOERS_MODE, strerror(errno));
                    473:        Exit(-1);
                    474:     }
                    475:
                    476:     /*
                    477:      * Now that we have a sane stmp file (parses ok) it needs to be
                    478:      * rename(2)'d to sudoers.  If the rename(2) fails we try using
                    479:      * mv(1) in case stmp and sudoers are on different filesystems.
                    480:      */
                    481:     if (rename(stmp, sudoers)) {
                    482:        if (errno == EXDEV) {
                    483:            (void) fprintf(stderr,
                    484:              "%s: %s and %s not on the same filesystem, using mv to rename.\n",
                    485:              Argv[0], stmp, sudoers);
                    486:
1.11      millert   487:            /* Build up argument vector for the command */
                    488:            if ((av[0] = strrchr(_PATH_MV, '/')) != NULL)
                    489:                av[0]++;
                    490:            else
                    491:                av[0] = _PATH_MV;
                    492:            av[1] = stmp;
                    493:            av[2] = sudoers;
                    494:            av[3] = NULL;
1.1       millert   495:
1.11      millert   496:            /* And run it... */
                    497:            if (run_command(_PATH_MV, av)) {
1.1       millert   498:                (void) fprintf(stderr,
1.11      millert   499:                               "%s: Command failed: '%s %s %s', %s unchanged.\n",
                    500:                               Argv[0], _PATH_MV, stmp, sudoers, sudoers);
1.1       millert   501:                Exit(-1);
                    502:            }
                    503:        } else {
                    504:            (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: %s\n",
                    505:                                   Argv[0], stmp, sudoers, strerror(errno));
                    506:            Exit(-1);
                    507:        }
                    508:     }
                    509:
1.3       millert   510:     exit(0);
1.1       millert   511: }
                    512:
                    513: /*
                    514:  * Dummy *_matches routines.
                    515:  * These exist to allow us to use the same parser as sudo(8).
                    516:  */
                    517: int
                    518: command_matches(cmnd, cmnd_args, path, sudoers_args)
                    519:     char *cmnd;
                    520:     char *cmnd_args;
                    521:     char *path;
                    522:     char *sudoers_args;
                    523: {
                    524:     return(TRUE);
                    525: }
                    526:
                    527: int
                    528: addr_matches(n)
                    529:     char *n;
1.4       millert   530: {
                    531:     return(TRUE);
                    532: }
                    533:
                    534: int
                    535: hostname_matches(s, l, p)
                    536:     char *s, *l, *p;
1.1       millert   537: {
                    538:     return(TRUE);
                    539: }
                    540:
                    541: int
                    542: usergr_matches(g, u)
                    543:     char *g, *u;
                    544: {
                    545:     return(TRUE);
                    546: }
                    547:
                    548: int
1.3       millert   549: netgr_matches(n, h, sh, u)
                    550:     char *n, *h, *sh, *u;
1.1       millert   551: {
                    552:     return(TRUE);
1.2       millert   553: }
                    554:
                    555: void
                    556: set_fqdn()
                    557: {
                    558:     return;
1.1       millert   559: }
                    560:
1.11      millert   561: int
                    562: user_is_exempt()
                    563: {
                    564:     return(TRUE);
                    565: }
                    566:
                    567: void
                    568: init_envtables()
                    569: {
                    570:     return;
                    571: }
                    572:
1.1       millert   573: /*
                    574:  * Assuming a parse error occurred, prompt the user for what they want
                    575:  * to do now.  Returns the first letter of their choice.
                    576:  */
                    577: static char
                    578: whatnow()
                    579: {
                    580:     int choice, c;
                    581:
                    582:     for (;;) {
                    583:        (void) fputs("What now? ", stdout);
                    584:        choice = getchar();
                    585:        for (c = choice; c != '\n' && c != EOF;)
                    586:            c = getchar();
                    587:
1.3       millert   588:        switch (choice) {
                    589:            case EOF:
                    590:                choice = 'x';
                    591:                /* FALLTHROUGH */
                    592:            case 'e':
                    593:            case 'x':
                    594:            case 'Q':
                    595:                return(choice);
                    596:            default:
                    597:                (void) puts("Options are:");
                    598:                (void) puts("  (e)dit sudoers file again");
                    599:                (void) puts("  e(x)it without saving changes to sudoers file");
                    600:                (void) puts("  (Q)uit and save changes to sudoers file (DANGER!)\n");
1.1       millert   601:        }
                    602:     }
                    603: }
                    604:
                    605: /*
                    606:  * Install signal handlers for visudo.
                    607:  */
                    608: static void
                    609: setup_signals()
                    610: {
1.11      millert   611:        sigaction_t sa;
1.1       millert   612:
                    613:        /*
                    614:         * Setup signal handlers to cleanup nicely.
                    615:         */
1.11      millert   616:        sigemptyset(&sa.sa_mask);
                    617:        sa.sa_flags = SA_RESTART;
                    618:        sa.sa_handler = Exit;
                    619:        (void) sigaction(SIGTERM, &sa, NULL);
                    620:        (void) sigaction(SIGHUP, &sa, NULL);
                    621:        (void) sigaction(SIGINT, &sa, NULL);
                    622:        (void) sigaction(SIGQUIT, &sa, NULL);
                    623: }
                    624:
                    625: static int
                    626: run_command(path, argv)
                    627:     char *path;
                    628:     char **argv;
                    629: {
                    630:     int status;
                    631:     pid_t pid;
                    632:     sigset_t set, oset;
                    633:
                    634:     (void) sigemptyset(&set);
                    635:     (void) sigaddset(&set, SIGCHLD);
                    636:     (void) sigprocmask(SIG_BLOCK, &set, &oset);
                    637:
                    638:     switch (pid = fork()) {
                    639:        case -1:
                    640:            (void) fprintf(stderr,
                    641:                "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno));
                    642:            Exit(-1);
                    643:            break;      /* NOTREACHED */
                    644:        case 0:
                    645:            (void) sigprocmask(SIG_SETMASK, &oset, NULL);
                    646:            execv(path, argv);
                    647:            (void) fprintf(stderr,
                    648:                "%s: unable to run %s: %s\n", Argv[0], path, strerror(errno));
                    649:            _exit(127);
                    650:            break;      /* NOTREACHED */
                    651:     }
                    652:
                    653: #ifdef sudo_waitpid
                    654:     pid = sudo_waitpid(pid, &status, 0);
1.1       millert   655: #else
1.11      millert   656:     pid = wait(&status);
                    657: #endif
                    658:
                    659:     (void) sigprocmask(SIG_SETMASK, &oset, NULL);
                    660:
                    661:     /* XXX - should use WEXITSTATUS() */
                    662:     return(pid == -1 ? -1 : (status >> 8));
                    663: }
                    664:
                    665: static int
                    666: check_syntax(quiet)
                    667:     int quiet;
                    668: {
                    669:
                    670:     if ((yyin = fopen(sudoers, "r")) == NULL) {
                    671:        if (!quiet)
                    672:            (void) fprintf(stderr, "%s: unable to open %s: %s\n", Argv[0],
                    673:                sudoers, strerror(errno));
                    674:        exit(1);
                    675:     }
                    676:     yyout = stdout;
                    677:     init_parser();
                    678:     if (yyparse() && parse_error != TRUE) {
                    679:        if (!quiet)
                    680:            (void) fprintf(stderr,
                    681:                "%s: failed to parse %s file, unknown error.\n",
                    682:                Argv[0], sudoers);
                    683:        parse_error = TRUE;
                    684:     }
                    685:     if (!quiet){
                    686:        if (parse_error)
                    687:            (void) printf("parse error in %s near line %d\n", sudoers,
                    688:                errorlineno);
                    689:        else
                    690:            (void) printf("%s file parsed OK\n", sudoers);
                    691:     }
                    692:
                    693:     return(parse_error == TRUE);
1.1       millert   694: }
                    695:
                    696: /*
                    697:  * Unlink the sudoers temp file (if it exists) and exit.
                    698:  * Used in place of a normal exit() and as a signal handler.
1.10      millert   699:  * A positive parameter indicates we were called as a signal handler.
1.1       millert   700:  */
                    701: static RETSIGTYPE
                    702: Exit(sig)
                    703:     int sig;
                    704: {
1.10      millert   705:     char *emsg = " exiting due to signal.\n";
                    706:
1.1       millert   707:     (void) unlink(stmp);
                    708:
1.10      millert   709:     if (sig > 0) {
                    710:        write(STDERR_FILENO, Argv[0], strlen(Argv[0]));
                    711:        write(STDERR_FILENO, emsg, sizeof(emsg) - 1);
                    712:        _exit(-sig);
                    713:     }
                    714:     exit(-sig);
1.1       millert   715: }
                    716:
                    717: static void
                    718: usage()
                    719: {
1.11      millert   720:     (void) fprintf(stderr, "usage: %s [-c] [-f sudoers] [-q] [-s] [-V]\n",
                    721:        Argv[0]);
1.1       millert   722:     exit(1);
                    723: }