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: }