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

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