crontab.c (e93f27e3aeb7c0778012b7732bc6376e20f80427) | crontab.c (fe590ffe40f49fe09d8275fbf29f0d46c5b99dc7) |
---|---|
1/* Copyright 1988,1990,1993,1994 by Paul Vixie 2 * All rights reserved | 1/* Copyright 1988,1990,1993,1994 by Paul Vixie 2 * All rights reserved |
3 */ 4 5/* 6 * Copyright (c) 1997 by Internet Software Consortium |
|
3 * | 7 * |
4 * Distribute freely, except: don't remove my name from the source or 5 * documentation (don't take credit for my work), mark your changes (don't 6 * get me blamed for your possible bugs), don't alter or remove this 7 * notice. May be sold if buildable source is provided to buyer. No 8 * warrantee of any kind, express or implied, is included with this 9 * software; use at your own risk, responsibility for damages (if any) to 10 * anyone resulting from the use of this software rests entirely with the 11 * user. | 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. |
12 * | 11 * |
13 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 14 * I'll try to keep a version up to date. I can be reached as follows: 15 * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 16 * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp | 12 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 13 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 14 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 15 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 18 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 19 * SOFTWARE. |
17 */ 18 19#if !defined(lint) && !defined(LINT) 20static const char rcsid[] = | 20 */ 21 22#if !defined(lint) && !defined(LINT) 23static const char rcsid[] = |
21 "$FreeBSD$"; | 24 "$Id: crontab.c,v 1.3 1998/08/14 00:32:38 vixie Exp $"; |
22#endif 23 24/* crontab - install and manage per-user crontab files 25 * vix 02may87 [RCS has the rest of the log] 26 * vix 26jan87 [original] 27 */ 28 29#define MAIN_PROGRAM 30 | 25#endif 26 27/* crontab - install and manage per-user crontab files 28 * vix 02may87 [RCS has the rest of the log] 29 * vix 26jan87 [original] 30 */ 31 32#define MAIN_PROGRAM 33 |
31#include <sys/param.h> | |
32#include "cron.h" | 34#include "cron.h" |
33#include <errno.h> 34#include <fcntl.h> | |
35#include <md5.h> | 35#include <md5.h> |
36#include <paths.h> 37#include <sys/file.h> 38#include <sys/stat.h> 39#ifdef USE_UTIMES 40# include <sys/time.h> 41#else 42# include <time.h> 43# include <utime.h> 44#endif 45#if defined(POSIX) 46# include <locale.h> 47#endif | |
48 49#define MD5_SIZE 33 50#define NHEADER_LINES 3 51 | 36 37#define MD5_SIZE 33 38#define NHEADER_LINES 3 39 |
52 | |
53enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; 54 55#if DEBUGGING 56static char *Options[] = { "???", "list", "delete", "edit", "replace" }; 57#endif 58 | 40enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; 41 42#if DEBUGGING 43static char *Options[] = { "???", "list", "delete", "edit", "replace" }; 44#endif 45 |
59 | |
60static PID_T Pid; 61static char User[MAXLOGNAME], RealUser[MAXLOGNAME]; 62static char Filename[MAX_FNAME]; 63static FILE *NewCrontab; 64static int CheckErrorCount; 65static enum opt_t Option; 66static int fflag; 67static struct passwd *pw; 68static void list_cmd(void), 69 delete_cmd(void), 70 edit_cmd(void), 71 poke_daemon(void), | 46static PID_T Pid; 47static char User[MAXLOGNAME], RealUser[MAXLOGNAME]; 48static char Filename[MAX_FNAME]; 49static FILE *NewCrontab; 50static int CheckErrorCount; 51static enum opt_t Option; 52static int fflag; 53static struct passwd *pw; 54static void list_cmd(void), 55 delete_cmd(void), 56 edit_cmd(void), 57 poke_daemon(void), |
72 check_error(char *), | 58 check_error(const char *), |
73 parse_args(int c, char *v[]); 74static int replace_cmd(void); 75 | 59 parse_args(int c, char *v[]); 60static int replace_cmd(void); 61 |
76 | |
77static void | 62static void |
78usage(char *msg) | 63usage(const char *msg) |
79{ 80 fprintf(stderr, "crontab: usage error: %s\n", msg); 81 fprintf(stderr, "%s\n%s\n", 82 "usage: crontab [-u user] file", 83 " crontab [-u user] { -l | -r [-f] | -e }"); 84 exit(ERROR_EXIT); 85} 86 | 64{ 65 fprintf(stderr, "crontab: usage error: %s\n", msg); 66 fprintf(stderr, "%s\n%s\n", 67 "usage: crontab [-u user] file", 68 " crontab [-u user] { -l | -r [-f] | -e }"); 69 exit(ERROR_EXIT); 70} 71 |
87 | |
88int 89main(int argc, char *argv[]) 90{ 91 int exitstatus; 92 93 Pid = getpid(); 94 ProgramName = argv[0]; 95 | 72int 73main(int argc, char *argv[]) 74{ 75 int exitstatus; 76 77 Pid = getpid(); 78 ProgramName = argv[0]; 79 |
96#if defined(POSIX) | |
97 setlocale(LC_ALL, ""); | 80 setlocale(LC_ALL, ""); |
98#endif | |
99 100#if defined(BSD) 101 setlinebuf(stderr); 102#endif 103 parse_args(argc, argv); /* sets many globals, opens a file */ 104 set_cron_uid(); 105 set_cron_cwd(); 106 if (!allowed(User)) { 107 warnx("you (%s) are not allowed to use this program", User); 108 log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); 109 exit(ERROR_EXIT); 110 } 111 exitstatus = OK_EXIT; 112 switch (Option) { | 81 82#if defined(BSD) 83 setlinebuf(stderr); 84#endif 85 parse_args(argc, argv); /* sets many globals, opens a file */ 86 set_cron_uid(); 87 set_cron_cwd(); 88 if (!allowed(User)) { 89 warnx("you (%s) are not allowed to use this program", User); 90 log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); 91 exit(ERROR_EXIT); 92 } 93 exitstatus = OK_EXIT; 94 switch (Option) { |
113 case opt_list: list_cmd(); 114 break; 115 case opt_delete: delete_cmd(); 116 break; 117 case opt_edit: edit_cmd(); 118 break; 119 case opt_replace: if (replace_cmd() < 0) 120 exitstatus = ERROR_EXIT; 121 break; | 95 case opt_list: 96 list_cmd(); 97 break; 98 case opt_delete: 99 delete_cmd(); 100 break; 101 case opt_edit: 102 edit_cmd(); 103 break; 104 case opt_replace: 105 if (replace_cmd() < 0) 106 exitstatus = ERROR_EXIT; 107 break; |
122 case opt_unknown: | 108 case opt_unknown: |
123 break; | 109 default: 110 abort(); |
124 } 125 exit(exitstatus); 126 /*NOTREACHED*/ 127} 128 | 111 } 112 exit(exitstatus); 113 /*NOTREACHED*/ 114} 115 |
129 | |
130static void 131parse_args(int argc, char *argv[]) 132{ | 116static void 117parse_args(int argc, char *argv[]) 118{ |
133 int argch; 134 char resolved_path[PATH_MAX]; | 119 int argch; 120 char resolved_path[PATH_MAX]; |
135 136 if (!(pw = getpwuid(getuid()))) 137 errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out"); 138 bzero(pw->pw_passwd, strlen(pw->pw_passwd)); 139 (void) strncpy(User, pw->pw_name, (sizeof User)-1); 140 User[(sizeof User)-1] = '\0'; 141 strcpy(RealUser, User); 142 Filename[0] = '\0'; --- 82 unchanged lines hidden (view full) --- 225 err(ERROR_EXIT, "swapping uids back"); 226 } 227 228 Debug(DMISC, ("user=%s, file=%s, option=%s\n", 229 User, Filename, Options[(int)Option])) 230} 231 232static void | 121 122 if (!(pw = getpwuid(getuid()))) 123 errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out"); 124 bzero(pw->pw_passwd, strlen(pw->pw_passwd)); 125 (void) strncpy(User, pw->pw_name, (sizeof User)-1); 126 User[(sizeof User)-1] = '\0'; 127 strcpy(RealUser, User); 128 Filename[0] = '\0'; --- 82 unchanged lines hidden (view full) --- 211 err(ERROR_EXIT, "swapping uids back"); 212 } 213 214 Debug(DMISC, ("user=%s, file=%s, option=%s\n", 215 User, Filename, Options[(int)Option])) 216} 217 218static void |
233copy_file(FILE *in, FILE *out) { 234 int x, ch; | 219copy_file(FILE *in, FILE *out) 220{ 221 int x, ch; |
235 236 Set_LineNum(1) 237 /* ignore the top few comments since we probably put them there. 238 */ | 222 223 Set_LineNum(1) 224 /* ignore the top few comments since we probably put them there. 225 */ |
239 for (x = 0; x < NHEADER_LINES; x++) { | 226 for (x = 0; x < NHEADER_LINES; x++) { |
240 ch = get_char(in); 241 if (EOF == ch) 242 break; 243 if ('#' != ch) { 244 putc(ch, out); 245 break; 246 } 247 while (EOF != (ch = get_char(in))) --- 8 unchanged lines hidden (view full) --- 256 if (EOF != ch) 257 while (EOF != (ch = get_char(in))) 258 putc(ch, out); 259} 260 261static void 262list_cmd(void) 263{ | 227 ch = get_char(in); 228 if (EOF == ch) 229 break; 230 if ('#' != ch) { 231 putc(ch, out); 232 break; 233 } 234 while (EOF != (ch = get_char(in))) --- 8 unchanged lines hidden (view full) --- 243 if (EOF != ch) 244 while (EOF != (ch = get_char(in))) 245 putc(ch, out); 246} 247 248static void 249list_cmd(void) 250{ |
264 char n[MAX_FNAME]; 265 FILE *f; | 251 char n[MAX_FNAME]; 252 FILE *f; |
266 267 log_it(RealUser, Pid, "LIST", User); 268 (void) snprintf(n, sizeof(n), CRON_TAB(User)); 269 if (!(f = fopen(n, "r"))) { 270 if (errno == ENOENT) 271 errx(ERROR_EXIT, "no crontab for %s", User); 272 else 273 err(ERROR_EXIT, "%s", n); 274 } 275 276 /* file is open. copy to stdout, close. 277 */ 278 copy_file(f, stdout); 279 fclose(f); 280} 281 | 253 254 log_it(RealUser, Pid, "LIST", User); 255 (void) snprintf(n, sizeof(n), CRON_TAB(User)); 256 if (!(f = fopen(n, "r"))) { 257 if (errno == ENOENT) 258 errx(ERROR_EXIT, "no crontab for %s", User); 259 else 260 err(ERROR_EXIT, "%s", n); 261 } 262 263 /* file is open. copy to stdout, close. 264 */ 265 copy_file(f, stdout); 266 fclose(f); 267} 268 |
282 | |
283static void 284delete_cmd(void) 285{ | 269static void 270delete_cmd(void) 271{ |
286 char n[MAX_FNAME]; | 272 char n[MAX_FNAME]; |
287 int ch, first; 288 289 if (!fflag && isatty(STDIN_FILENO)) { 290 (void)fprintf(stderr, "remove crontab for %s? ", User); 291 first = ch = getchar(); 292 while (ch != '\n' && ch != EOF) 293 ch = getchar(); 294 if (first != 'y' && first != 'Y') 295 return; 296 } 297 298 log_it(RealUser, Pid, "DELETE", User); | 273 int ch, first; 274 275 if (!fflag && isatty(STDIN_FILENO)) { 276 (void)fprintf(stderr, "remove crontab for %s? ", User); 277 first = ch = getchar(); 278 while (ch != '\n' && ch != EOF) 279 ch = getchar(); 280 if (first != 'y' && first != 'Y') 281 return; 282 } 283 284 log_it(RealUser, Pid, "DELETE", User); |
299 (void) snprintf(n, sizeof(n), CRON_TAB(User)); 300 if (unlink(n)) { | 285 if (snprintf(n, sizeof(n), CRON_TAB(User)) >= (int)sizeof(n)) 286 errx(ERROR_EXIT, "path too long"); 287 if (unlink(n) != 0) { |
301 if (errno == ENOENT) 302 errx(ERROR_EXIT, "no crontab for %s", User); 303 else 304 err(ERROR_EXIT, "%s", n); 305 } 306 poke_daemon(); 307} 308 | 288 if (errno == ENOENT) 289 errx(ERROR_EXIT, "no crontab for %s", User); 290 else 291 err(ERROR_EXIT, "%s", n); 292 } 293 poke_daemon(); 294} 295 |
309 | |
310static void | 296static void |
311check_error(char *msg) | 297check_error(const char *msg) |
312{ 313 CheckErrorCount++; 314 fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); 315} 316 | 298{ 299 CheckErrorCount++; 300 fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); 301} 302 |
317 | |
318static void 319edit_cmd(void) 320{ | 303static void 304edit_cmd(void) 305{ |
321 char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; 322 FILE *f; 323 int t; 324 struct stat statbuf, fsbuf; 325 WAIT_T waiter; 326 PID_T pid, xpid; 327 mode_t um; 328 int syntax_error = 0; 329 char orig_md5[MD5_SIZE]; 330 char new_md5[MD5_SIZE]; | 306 char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; 307 FILE *f; 308 int t; 309 struct stat statbuf, fsbuf; 310 WAIT_T waiter; 311 PID_T pid, xpid; 312 mode_t um; 313 int syntax_error = 0; 314 char orig_md5[MD5_SIZE]; 315 char new_md5[MD5_SIZE]; |
331 332 log_it(RealUser, Pid, "BEGIN EDIT", User); | 316 317 log_it(RealUser, Pid, "BEGIN EDIT", User); |
333 (void) snprintf(n, sizeof(n), CRON_TAB(User)); | 318 if (snprintf(n, sizeof(n), CRON_TAB(User)) >= (int)sizeof(n)) 319 errx(ERROR_EXIT, "path too long"); |
334 if (!(f = fopen(n, "r"))) { 335 if (errno != ENOENT) 336 err(ERROR_EXIT, "%s", n); 337 warnx("no crontab for %s - using an empty one", User); 338 if (!(f = fopen(_PATH_DEVNULL, "r"))) 339 err(ERROR_EXIT, _PATH_DEVNULL); 340 } 341 --- 26 unchanged lines hidden (view full) --- 368 warn("unable to fstat temp file"); 369 goto fatal; 370 } 371 again: 372 if (swap_uids() < OK) 373 err(ERROR_EXIT, "swapping uids"); 374 if (stat(Filename, &statbuf) < 0) { 375 warn("stat"); | 320 if (!(f = fopen(n, "r"))) { 321 if (errno != ENOENT) 322 err(ERROR_EXIT, "%s", n); 323 warnx("no crontab for %s - using an empty one", User); 324 if (!(f = fopen(_PATH_DEVNULL, "r"))) 325 err(ERROR_EXIT, _PATH_DEVNULL); 326 } 327 --- 26 unchanged lines hidden (view full) --- 354 warn("unable to fstat temp file"); 355 goto fatal; 356 } 357 again: 358 if (swap_uids() < OK) 359 err(ERROR_EXIT, "swapping uids"); 360 if (stat(Filename, &statbuf) < 0) { 361 warn("stat"); |
376 fatal: unlink(Filename); | 362 fatal: 363 unlink(Filename); |
377 exit(ERROR_EXIT); 378 } 379 if (swap_uids_back() < OK) 380 err(ERROR_EXIT, "swapping uids back"); 381 if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) 382 errx(ERROR_EXIT, "temp file must be edited in place"); 383 if (MD5File(Filename, orig_md5) == NULL) { 384 warn("MD5"); 385 goto fatal; 386 } 387 | 364 exit(ERROR_EXIT); 365 } 366 if (swap_uids_back() < OK) 367 err(ERROR_EXIT, "swapping uids back"); 368 if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) 369 errx(ERROR_EXIT, "temp file must be edited in place"); 370 if (MD5File(Filename, orig_md5) == NULL) { 371 warn("MD5"); 372 goto fatal; 373 } 374 |
388 if ((!(editor = getenv("VISUAL"))) 389 && (!(editor = getenv("EDITOR"))) 390 ) { | 375 if ((editor = getenv("VISUAL")) == NULL && 376 (editor = getenv("EDITOR")) == NULL) { |
391 editor = EDITOR; 392 } 393 394 /* we still have the file open. editors will generally rewrite the 395 * original file rather than renaming/unlinking it and starting a 396 * new one; even backup files are supposed to be made by copying 397 * rather than by renaming. if some editor does not support this, 398 * then don't use it. the security problems are more severe if we --- 100 unchanged lines hidden (view full) --- 499 500/* returns 0 on success 501 * -1 on syntax error 502 * -2 on install error 503 */ 504static int 505replace_cmd(void) 506{ | 377 editor = EDITOR; 378 } 379 380 /* we still have the file open. editors will generally rewrite the 381 * original file rather than renaming/unlinking it and starting a 382 * new one; even backup files are supposed to be made by copying 383 * rather than by renaming. if some editor does not support this, 384 * then don't use it. the security problems are more severe if we --- 100 unchanged lines hidden (view full) --- 485 486/* returns 0 on success 487 * -1 on syntax error 488 * -2 on install error 489 */ 490static int 491replace_cmd(void) 492{ |
507 char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; 508 FILE *tmp; 509 int ch, eof; 510 entry *e; 511 time_t now = time(NULL); 512 char **envp = env_init(); | 493 char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; 494 FILE *tmp; 495 int ch, eof; 496 entry *e; 497 time_t now = time(NULL); 498 char **envp = env_init(); |
513 514 if (envp == NULL) { 515 warnx("cannot allocate memory"); 516 return (-2); 517 } 518 519 (void) snprintf(n, sizeof(n), "tmp.%d", Pid); | 499 500 if (envp == NULL) { 501 warnx("cannot allocate memory"); 502 return (-2); 503 } 504 505 (void) snprintf(n, sizeof(n), "tmp.%d", Pid); |
520 (void) snprintf(tn, sizeof(tn), CRON_TAB(n)); | 506 if (snprintf(tn, sizeof(tn), CRON_TAB(n)) >= (int)sizeof(tn)) { 507 warnx("path too long"); 508 return (-2); 509 } |
521 522 if (!(tmp = fopen(tn, "w+"))) { 523 warn("%s", tn); 524 return (-2); 525 } 526 527 /* write a signature at the top of the file. 528 * --- 71 unchanged lines hidden (view full) --- 600 } 601 602 if (fclose(tmp) == EOF) { 603 warn("fclose"); 604 unlink(tn); 605 return (-2); 606 } 607 | 510 511 if (!(tmp = fopen(tn, "w+"))) { 512 warn("%s", tn); 513 return (-2); 514 } 515 516 /* write a signature at the top of the file. 517 * --- 71 unchanged lines hidden (view full) --- 589 } 590 591 if (fclose(tmp) == EOF) { 592 warn("fclose"); 593 unlink(tn); 594 return (-2); 595 } 596 |
608 (void) snprintf(n, sizeof(n), CRON_TAB(User)); | 597 if (snprintf(n, sizeof(n), CRON_TAB(User)) >= (int)sizeof(n)) { 598 warnx("path too long"); 599 unlink(tn); 600 return (-2); 601 } 602 |
609 if (rename(tn, n)) { 610 warn("error renaming %s to %s", tn, n); 611 unlink(tn); 612 return (-2); 613 } 614 615 log_it(RealUser, Pid, "REPLACE", User); 616 --- 6 unchanged lines hidden (view full) --- 623 */ 624 sleep(1); 625 626 poke_daemon(); 627 628 return (0); 629} 630 | 603 if (rename(tn, n)) { 604 warn("error renaming %s to %s", tn, n); 605 unlink(tn); 606 return (-2); 607 } 608 609 log_it(RealUser, Pid, "REPLACE", User); 610 --- 6 unchanged lines hidden (view full) --- 617 */ 618 sleep(1); 619 620 poke_daemon(); 621 622 return (0); 623} 624 |
631 | |
632static void 633poke_daemon(void) 634{ | 625static void 626poke_daemon(void) 627{ |
635#ifdef USE_UTIMES 636 struct timeval tvs[2]; 637 638 (void)gettimeofday(&tvs[0], NULL); 639 tvs[1] = tvs[0]; 640 if (utimes(SPOOL_DIR, tvs) < OK) { 641 warn("can't update mtime on spooldir %s", SPOOL_DIR); 642 return; 643 } 644#else | |
645 if (utime(SPOOL_DIR, NULL) < OK) { 646 warn("can't update mtime on spooldir %s", SPOOL_DIR); 647 return; 648 } | 628 if (utime(SPOOL_DIR, NULL) < OK) { 629 warn("can't update mtime on spooldir %s", SPOOL_DIR); 630 return; 631 } |
649#endif /*USE_UTIMES*/ | |
650} | 632} |