[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.14

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