crontab.c revision 225736
1218822Sdim/* Copyright 1988,1990,1993,1994 by Paul Vixie 2218822Sdim * All rights reserved 3218822Sdim * 4218822Sdim * Distribute freely, except: don't remove my name from the source or 5218822Sdim * documentation (don't take credit for my work), mark your changes (don't 6218822Sdim * get me blamed for your possible bugs), don't alter or remove this 7218822Sdim * notice. May be sold if buildable source is provided to buyer. No 8218822Sdim * warrantee of any kind, express or implied, is included with this 9218822Sdim * software; use at your own risk, responsibility for damages (if any) to 10218822Sdim * anyone resulting from the use of this software rests entirely with the 11218822Sdim * user. 12218822Sdim * 13218822Sdim * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 14218822Sdim * I'll try to keep a version up to date. I can be reached as follows: 15218822Sdim * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 16218822Sdim * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp 17218822Sdim */ 18218822Sdim 19218822Sdim#if !defined(lint) && !defined(LINT) 20218822Sdimstatic const char rcsid[] = 21218822Sdim "$FreeBSD: stable/9/usr.sbin/cron/crontab/crontab.c 185041 2008-11-18 00:39:50Z matteo $"; 22218822Sdim#endif 23218822Sdim 24218822Sdim/* crontab - install and manage per-user crontab files 25218822Sdim * vix 02may87 [RCS has the rest of the log] 26218822Sdim * vix 26jan87 [original] 27218822Sdim */ 28218822Sdim 29218822Sdim#define MAIN_PROGRAM 30218822Sdim 31218822Sdim#include "cron.h" 32218822Sdim#include <errno.h> 33218822Sdim#include <fcntl.h> 34218822Sdim#include <md5.h> 35218822Sdim#include <paths.h> 36218822Sdim#include <sys/file.h> 37218822Sdim#include <sys/stat.h> 38218822Sdim#ifdef USE_UTIMES 39218822Sdim# include <sys/time.h> 40218822Sdim#else 41218822Sdim# include <time.h> 42218822Sdim# include <utime.h> 43218822Sdim#endif 44218822Sdim#if defined(POSIX) 45218822Sdim# include <locale.h> 46218822Sdim#endif 47218822Sdim 48218822Sdim#define MD5_SIZE 33 49218822Sdim#define NHEADER_LINES 3 50218822Sdim 51218822Sdim 52218822Sdimenum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; 53218822Sdim 54218822Sdim#if DEBUGGING 55218822Sdimstatic char *Options[] = { "???", "list", "delete", "edit", "replace" }; 56218822Sdim#endif 57218822Sdim 58218822Sdim 59218822Sdimstatic PID_T Pid; 60218822Sdimstatic char User[MAX_UNAME], RealUser[MAX_UNAME]; 61218822Sdimstatic char Filename[MAX_FNAME]; 62218822Sdimstatic FILE *NewCrontab; 63218822Sdimstatic int CheckErrorCount; 64218822Sdimstatic enum opt_t Option; 65218822Sdimstatic struct passwd *pw; 66218822Sdimstatic void list_cmd(void), 67218822Sdim delete_cmd(void), 68218822Sdim edit_cmd(void), 69218822Sdim poke_daemon(void), 70218822Sdim check_error(char *), 71218822Sdim parse_args(int c, char *v[]); 72218822Sdimstatic int replace_cmd(void); 73218822Sdim 74218822Sdim 75218822Sdimstatic void 76218822Sdimusage(char *msg) 77218822Sdim{ 78218822Sdim fprintf(stderr, "crontab: usage error: %s\n", msg); 79218822Sdim fprintf(stderr, "%s\n%s\n", 80218822Sdim "usage: crontab [-u user] file", 81218822Sdim " crontab [-u user] { -e | -l | -r }"); 82218822Sdim exit(ERROR_EXIT); 83218822Sdim} 84218822Sdim 85218822Sdim 86218822Sdimint 87218822Sdimmain(int argc, char *argv[]) 88218822Sdim{ 89218822Sdim int exitstatus; 90218822Sdim 91218822Sdim Pid = getpid(); 92218822Sdim ProgramName = argv[0]; 93218822Sdim 94218822Sdim#if defined(POSIX) 95218822Sdim setlocale(LC_ALL, ""); 96218822Sdim#endif 97218822Sdim 98218822Sdim#if defined(BSD) 99218822Sdim setlinebuf(stderr); 100218822Sdim#endif 101218822Sdim parse_args(argc, argv); /* sets many globals, opens a file */ 102218822Sdim set_cron_uid(); 103218822Sdim set_cron_cwd(); 104218822Sdim if (!allowed(User)) { 105218822Sdim warnx("you (%s) are not allowed to use this program", User); 106218822Sdim log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); 107218822Sdim exit(ERROR_EXIT); 108218822Sdim } 109218822Sdim exitstatus = OK_EXIT; 110218822Sdim switch (Option) { 111218822Sdim case opt_list: list_cmd(); 112218822Sdim break; 113218822Sdim case opt_delete: delete_cmd(); 114218822Sdim break; 115218822Sdim case opt_edit: edit_cmd(); 116218822Sdim break; 117218822Sdim case opt_replace: if (replace_cmd() < 0) 118218822Sdim exitstatus = ERROR_EXIT; 119218822Sdim break; 120218822Sdim case opt_unknown: 121218822Sdim break; 122218822Sdim } 123218822Sdim exit(exitstatus); 124218822Sdim /*NOTREACHED*/ 125218822Sdim} 126218822Sdim 127218822Sdim 128218822Sdimstatic void 129218822Sdimparse_args(argc, argv) 130218822Sdim int argc; 131218822Sdim char *argv[]; 132218822Sdim{ 133218822Sdim int argch; 134218822Sdim char resolved_path[PATH_MAX]; 135218822Sdim 136218822Sdim if (!(pw = getpwuid(getuid()))) 137218822Sdim errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out"); 138218822Sdim bzero(pw->pw_passwd, strlen(pw->pw_passwd)); 139218822Sdim (void) strncpy(User, pw->pw_name, (sizeof User)-1); 140218822Sdim User[(sizeof User)-1] = '\0'; 141218822Sdim strcpy(RealUser, User); 142218822Sdim Filename[0] = '\0'; 143218822Sdim Option = opt_unknown; 144218822Sdim while ((argch = getopt(argc, argv, "u:lerx:")) != -1) { 145218822Sdim switch (argch) { 146218822Sdim case 'x': 147218822Sdim if (!set_debug_flags(optarg)) 148218822Sdim usage("bad debug option"); 149218822Sdim break; 150218822Sdim case 'u': 151218822Sdim if (getuid() != ROOT_UID) 152218822Sdim errx(ERROR_EXIT, "must be privileged to use -u"); 153218822Sdim if (!(pw = getpwnam(optarg))) 154218822Sdim errx(ERROR_EXIT, "user `%s' unknown", optarg); 155218822Sdim bzero(pw->pw_passwd, strlen(pw->pw_passwd)); 156218822Sdim (void) strncpy(User, pw->pw_name, (sizeof User)-1); 157218822Sdim User[(sizeof User)-1] = '\0'; 158218822Sdim break; 159218822Sdim case 'l': 160218822Sdim if (Option != opt_unknown) 161218822Sdim usage("only one operation permitted"); 162218822Sdim Option = opt_list; 163218822Sdim break; 164218822Sdim case 'r': 165218822Sdim if (Option != opt_unknown) 166218822Sdim usage("only one operation permitted"); 167218822Sdim Option = opt_delete; 168218822Sdim break; 169218822Sdim case 'e': 170218822Sdim if (Option != opt_unknown) 171218822Sdim usage("only one operation permitted"); 172218822Sdim Option = opt_edit; 173218822Sdim break; 174218822Sdim default: 175218822Sdim usage("unrecognized option"); 176218822Sdim } 177218822Sdim } 178218822Sdim 179218822Sdim endpwent(); 180218822Sdim 181218822Sdim if (Option != opt_unknown) { 182218822Sdim if (argv[optind] != NULL) { 183218822Sdim usage("no arguments permitted after this option"); 184218822Sdim } 185218822Sdim } else { 186218822Sdim if (argv[optind] != NULL) { 187218822Sdim Option = opt_replace; 188218822Sdim (void) strncpy (Filename, argv[optind], (sizeof Filename)-1); 189218822Sdim Filename[(sizeof Filename)-1] = '\0'; 190218822Sdim 191218822Sdim } else { 192218822Sdim usage("file name must be specified for replace"); 193218822Sdim } 194218822Sdim } 195218822Sdim 196218822Sdim if (Option == opt_replace) { 197218822Sdim /* we have to open the file here because we're going to 198218822Sdim * chdir(2) into /var/cron before we get around to 199218822Sdim * reading the file. 200218822Sdim */ 201218822Sdim if (!strcmp(Filename, "-")) { 202218822Sdim NewCrontab = stdin; 203218822Sdim } else if (realpath(Filename, resolved_path) != NULL && 204218822Sdim !strcmp(resolved_path, SYSCRONTAB)) { 205218822Sdim err(ERROR_EXIT, SYSCRONTAB " must be edited manually"); 206218822Sdim } else { 207218822Sdim /* relinquish the setuid status of the binary during 208218822Sdim * the open, lest nonroot users read files they should 209218822Sdim * not be able to read. we can't use access() here 210218822Sdim * since there's a race condition. thanks go out to 211218822Sdim * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting 212218822Sdim * the race. 213218822Sdim */ 214218822Sdim 215218822Sdim if (swap_uids() < OK) 216218822Sdim err(ERROR_EXIT, "swapping uids"); 217218822Sdim if (!(NewCrontab = fopen(Filename, "r"))) 218218822Sdim err(ERROR_EXIT, "%s", Filename); 219218822Sdim if (swap_uids_back() < OK) 220218822Sdim err(ERROR_EXIT, "swapping uids back"); 221218822Sdim } 222218822Sdim } 223218822Sdim 224218822Sdim Debug(DMISC, ("user=%s, file=%s, option=%s\n", 225218822Sdim User, Filename, Options[(int)Option])) 226218822Sdim} 227218822Sdim 228218822Sdimstatic void 229218822Sdimcopy_file(FILE *in, FILE *out) { 230218822Sdim int x, ch; 231218822Sdim 232218822Sdim Set_LineNum(1) 233218822Sdim /* ignore the top few comments since we probably put them there. 234218822Sdim */ 235218822Sdim for (x = 0; x < NHEADER_LINES; x++) { 236218822Sdim ch = get_char(in); 237218822Sdim if (EOF == ch) 238218822Sdim break; 239218822Sdim if ('#' != ch) { 240218822Sdim putc(ch, out); 241218822Sdim break; 242218822Sdim } 243218822Sdim while (EOF != (ch = get_char(in))) 244218822Sdim if (ch == '\n') 245218822Sdim break; 246218822Sdim if (EOF == ch) 247218822Sdim break; 248218822Sdim } 249218822Sdim 250218822Sdim /* copy the rest of the crontab (if any) to the output file. 251218822Sdim */ 252218822Sdim if (EOF != ch) 253218822Sdim while (EOF != (ch = get_char(in))) 254218822Sdim putc(ch, out); 255218822Sdim} 256218822Sdim 257218822Sdimstatic void 258218822Sdimlist_cmd() { 259218822Sdim char n[MAX_FNAME]; 260218822Sdim FILE *f; 261218822Sdim 262218822Sdim log_it(RealUser, Pid, "LIST", User); 263218822Sdim (void) snprintf(n, sizeof(n), CRON_TAB(User)); 264218822Sdim if (!(f = fopen(n, "r"))) { 265218822Sdim if (errno == ENOENT) 266218822Sdim errx(ERROR_EXIT, "no crontab for %s", User); 267218822Sdim else 268218822Sdim err(ERROR_EXIT, "%s", n); 269218822Sdim } 270218822Sdim 271218822Sdim /* file is open. copy to stdout, close. 272218822Sdim */ 273218822Sdim copy_file(f, stdout); 274218822Sdim fclose(f); 275218822Sdim} 276218822Sdim 277218822Sdim 278218822Sdimstatic void 279218822Sdimdelete_cmd() { 280218822Sdim char n[MAX_FNAME]; 281218822Sdim int ch, first; 282218822Sdim 283218822Sdim if (isatty(STDIN_FILENO)) { 284218822Sdim (void)fprintf(stderr, "remove crontab for %s? ", User); 285218822Sdim first = ch = getchar(); 286218822Sdim while (ch != '\n' && ch != EOF) 287218822Sdim ch = getchar(); 288218822Sdim if (first != 'y' && first != 'Y') 289218822Sdim return; 290218822Sdim } 291218822Sdim 292218822Sdim log_it(RealUser, Pid, "DELETE", User); 293218822Sdim (void) snprintf(n, sizeof(n), CRON_TAB(User)); 294218822Sdim if (unlink(n)) { 295218822Sdim if (errno == ENOENT) 296218822Sdim errx(ERROR_EXIT, "no crontab for %s", User); 297218822Sdim else 298218822Sdim err(ERROR_EXIT, "%s", n); 299218822Sdim } 300218822Sdim poke_daemon(); 301218822Sdim} 302218822Sdim 303218822Sdim 304218822Sdimstatic void 305218822Sdimcheck_error(msg) 306218822Sdim char *msg; 307218822Sdim{ 308218822Sdim CheckErrorCount++; 309218822Sdim fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); 310218822Sdim} 311218822Sdim 312218822Sdim 313218822Sdimstatic void 314218822Sdimedit_cmd() { 315218822Sdim char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; 316218822Sdim FILE *f; 317218822Sdim int t; 318218822Sdim struct stat statbuf, fsbuf; 319218822Sdim WAIT_T waiter; 320218822Sdim PID_T pid, xpid; 321218822Sdim mode_t um; 322218822Sdim int syntax_error = 0; 323218822Sdim char orig_md5[MD5_SIZE]; 324218822Sdim char new_md5[MD5_SIZE]; 325218822Sdim 326218822Sdim log_it(RealUser, Pid, "BEGIN EDIT", User); 327218822Sdim (void) snprintf(n, sizeof(n), CRON_TAB(User)); 328218822Sdim if (!(f = fopen(n, "r"))) { 329218822Sdim if (errno != ENOENT) 330218822Sdim err(ERROR_EXIT, "%s", n); 331218822Sdim warnx("no crontab for %s - using an empty one", User); 332218822Sdim if (!(f = fopen(_PATH_DEVNULL, "r"))) 333218822Sdim err(ERROR_EXIT, _PATH_DEVNULL); 334218822Sdim } 335218822Sdim 336218822Sdim um = umask(077); 337218822Sdim (void) snprintf(Filename, sizeof(Filename), "/tmp/crontab.XXXXXXXXXX"); 338218822Sdim if ((t = mkstemp(Filename)) == -1) { 339218822Sdim warn("%s", Filename); 340218822Sdim (void) umask(um); 341218822Sdim goto fatal; 342218822Sdim } 343218822Sdim (void) umask(um); 344218822Sdim#ifdef HAS_FCHOWN 345218822Sdim if (fchown(t, getuid(), getgid()) < 0) { 346218822Sdim#else 347218822Sdim if (chown(Filename, getuid(), getgid()) < 0) { 348218822Sdim#endif 349218822Sdim warn("fchown"); 350218822Sdim goto fatal; 351218822Sdim } 352218822Sdim if (!(NewCrontab = fdopen(t, "r+"))) { 353218822Sdim warn("fdopen"); 354218822Sdim goto fatal; 355218822Sdim } 356218822Sdim 357218822Sdim copy_file(f, NewCrontab); 358218822Sdim fclose(f); 359218822Sdim if (fflush(NewCrontab)) 360218822Sdim err(ERROR_EXIT, "%s", Filename); 361218822Sdim if (fstat(t, &fsbuf) < 0) { 362218822Sdim warn("unable to fstat temp file"); 363218822Sdim goto fatal; 364218822Sdim } 365218822Sdim again: 366218822Sdim if (stat(Filename, &statbuf) < 0) { 367218822Sdim warn("stat"); 368218822Sdim fatal: unlink(Filename); 369218822Sdim exit(ERROR_EXIT); 370218822Sdim } 371218822Sdim if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) 372218822Sdim errx(ERROR_EXIT, "temp file must be edited in place"); 373218822Sdim if (MD5File(Filename, orig_md5) == NULL) { 374218822Sdim warn("MD5"); 375218822Sdim goto fatal; 376218822Sdim } 377218822Sdim 378218822Sdim if ((!(editor = getenv("VISUAL"))) 379218822Sdim && (!(editor = getenv("EDITOR"))) 380218822Sdim ) { 381218822Sdim editor = EDITOR; 382218822Sdim } 383218822Sdim 384218822Sdim /* we still have the file open. editors will generally rewrite the 385218822Sdim * original file rather than renaming/unlinking it and starting a 386218822Sdim * new one; even backup files are supposed to be made by copying 387218822Sdim * rather than by renaming. if some editor does not support this, 388218822Sdim * then don't use it. the security problems are more severe if we 389218822Sdim * close and reopen the file around the edit. 390218822Sdim */ 391218822Sdim 392218822Sdim switch (pid = fork()) { 393218822Sdim case -1: 394218822Sdim warn("fork"); 395218822Sdim goto fatal; 396218822Sdim case 0: 397218822Sdim /* child */ 398218822Sdim if (setuid(getuid()) < 0) 399218822Sdim err(ERROR_EXIT, "setuid(getuid())"); 400218822Sdim if (chdir("/tmp") < 0) 401218822Sdim err(ERROR_EXIT, "chdir(/tmp)"); 402218822Sdim if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR) 403218822Sdim errx(ERROR_EXIT, "editor or filename too long"); 404218822Sdim execlp(editor, editor, Filename, (char *)NULL); 405218822Sdim err(ERROR_EXIT, "%s", editor); 406218822Sdim /*NOTREACHED*/ 407218822Sdim default: 408218822Sdim /* parent */ 409218822Sdim break; 410218822Sdim } 411218822Sdim 412218822Sdim /* parent */ 413218822Sdim { 414218822Sdim void (*sig[3])(int signal); 415218822Sdim sig[0] = signal(SIGHUP, SIG_IGN); 416218822Sdim sig[1] = signal(SIGINT, SIG_IGN); 417218822Sdim sig[2] = signal(SIGTERM, SIG_IGN); 418218822Sdim xpid = wait(&waiter); 419218822Sdim signal(SIGHUP, sig[0]); 420218822Sdim signal(SIGINT, sig[1]); 421218822Sdim signal(SIGTERM, sig[2]); 422218822Sdim } 423218822Sdim if (xpid != pid) { 424218822Sdim warnx("wrong PID (%d != %d) from \"%s\"", xpid, pid, editor); 425218822Sdim goto fatal; 426218822Sdim } 427218822Sdim if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { 428218822Sdim warnx("\"%s\" exited with status %d", editor, WEXITSTATUS(waiter)); 429218822Sdim goto fatal; 430218822Sdim } 431218822Sdim if (WIFSIGNALED(waiter)) { 432218822Sdim warnx("\"%s\" killed; signal %d (%score dumped)", 433218822Sdim editor, WTERMSIG(waiter), WCOREDUMP(waiter) ?"" :"no "); 434218822Sdim goto fatal; 435218822Sdim } 436218822Sdim if (stat(Filename, &statbuf) < 0) { 437218822Sdim warn("stat"); 438218822Sdim goto fatal; 439218822Sdim } 440218822Sdim if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) 441218822Sdim errx(ERROR_EXIT, "temp file must be edited in place"); 442218822Sdim if (MD5File(Filename, new_md5) == NULL) { 443218822Sdim warn("MD5"); 444218822Sdim goto fatal; 445218822Sdim } 446218822Sdim if (strcmp(orig_md5, new_md5) == 0 && !syntax_error) { 447218822Sdim warnx("no changes made to crontab"); 448218822Sdim goto remove; 449218822Sdim } 450218822Sdim warnx("installing new crontab"); 451218822Sdim switch (replace_cmd()) { 452218822Sdim case 0: /* Success */ 453218822Sdim break; 454218822Sdim case -1: /* Syntax error */ 455218822Sdim for (;;) { 456218822Sdim printf("Do you want to retry the same edit? "); 457218822Sdim fflush(stdout); 458218822Sdim q[0] = '\0'; 459218822Sdim (void) fgets(q, sizeof q, stdin); 460218822Sdim switch (islower(q[0]) ? q[0] : tolower(q[0])) { 461218822Sdim case 'y': 462218822Sdim syntax_error = 1; 463218822Sdim goto again; 464218822Sdim case 'n': 465218822Sdim goto abandon; 466218822Sdim default: 467218822Sdim fprintf(stderr, "Enter Y or N\n"); 468218822Sdim } 469218822Sdim } 470218822Sdim /*NOTREACHED*/ 471218822Sdim case -2: /* Install error */ 472218822Sdim abandon: 473218822Sdim warnx("edits left in %s", Filename); 474218822Sdim goto done; 475218822Sdim default: 476218822Sdim warnx("panic: bad switch() in replace_cmd()"); 477218822Sdim goto fatal; 478218822Sdim } 479218822Sdim remove: 480218822Sdim unlink(Filename); 481218822Sdim done: 482218822Sdim log_it(RealUser, Pid, "END EDIT", User); 483218822Sdim} 484218822Sdim 485218822Sdim 486218822Sdim/* returns 0 on success 487218822Sdim * -1 on syntax error 488218822Sdim * -2 on install error 489218822Sdim */ 490218822Sdimstatic int 491218822Sdimreplace_cmd() { 492218822Sdim char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; 493218822Sdim FILE *tmp; 494218822Sdim int ch, eof; 495218822Sdim entry *e; 496218822Sdim time_t now = time(NULL); 497218822Sdim char **envp = env_init(); 498218822Sdim 499218822Sdim if (envp == NULL) { 500218822Sdim warnx("cannot allocate memory"); 501218822Sdim return (-2); 502218822Sdim } 503218822Sdim 504218822Sdim (void) snprintf(n, sizeof(n), "tmp.%d", Pid); 505218822Sdim (void) snprintf(tn, sizeof(tn), CRON_TAB(n)); 506218822Sdim 507218822Sdim if (!(tmp = fopen(tn, "w+"))) { 508218822Sdim warn("%s", tn); 509218822Sdim return (-2); 510218822Sdim } 511218822Sdim 512218822Sdim /* write a signature at the top of the file. 513218822Sdim * 514218822Sdim * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code. 515218822Sdim */ 516218822Sdim fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n"); 517218822Sdim fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now)); 518218822Sdim fprintf(tmp, "# (Cron version -- %s)\n", rcsid); 519218822Sdim 520218822Sdim /* copy the crontab to the tmp 521218822Sdim */ 522218822Sdim rewind(NewCrontab); 523218822Sdim Set_LineNum(1) 524218822Sdim while (EOF != (ch = get_char(NewCrontab))) 525218822Sdim putc(ch, tmp); 526218822Sdim ftruncate(fileno(tmp), ftell(tmp)); 527218822Sdim fflush(tmp); rewind(tmp); 528218822Sdim 529218822Sdim if (ferror(tmp)) { 530218822Sdim warnx("error while writing new crontab to %s", tn); 531218822Sdim fclose(tmp); unlink(tn); 532218822Sdim return (-2); 533218822Sdim } 534218822Sdim 535218822Sdim /* check the syntax of the file being installed. 536218822Sdim */ 537218822Sdim 538218822Sdim /* BUG: was reporting errors after the EOF if there were any errors 539218822Sdim * in the file proper -- kludged it by stopping after first error. 540218822Sdim * vix 31mar87 541218822Sdim */ 542218822Sdim Set_LineNum(1 - NHEADER_LINES) 543218822Sdim CheckErrorCount = 0; eof = FALSE; 544218822Sdim while (!CheckErrorCount && !eof) { 545218822Sdim switch (load_env(envstr, tmp)) { 546218822Sdim case ERR: 547218822Sdim eof = TRUE; 548218822Sdim break; 549218822Sdim case FALSE: 550218822Sdim e = load_entry(tmp, check_error, pw, envp); 551218822Sdim if (e) 552218822Sdim free(e); 553218822Sdim break; 554218822Sdim case TRUE: 555218822Sdim break; 556218822Sdim } 557218822Sdim } 558218822Sdim 559218822Sdim if (CheckErrorCount != 0) { 560218822Sdim warnx("errors in crontab file, can't install"); 561218822Sdim fclose(tmp); unlink(tn); 562218822Sdim return (-1); 563218822Sdim } 564218822Sdim 565218822Sdim#ifdef HAS_FCHOWN 566218822Sdim if (fchown(fileno(tmp), ROOT_UID, -1) < OK) 567218822Sdim#else 568218822Sdim if (chown(tn, ROOT_UID, -1) < OK) 569218822Sdim#endif 570218822Sdim { 571218822Sdim warn("chown"); 572218822Sdim fclose(tmp); unlink(tn); 573218822Sdim return (-2); 574218822Sdim } 575218822Sdim 576218822Sdim#ifdef HAS_FCHMOD 577218822Sdim if (fchmod(fileno(tmp), 0600) < OK) 578218822Sdim#else 579218822Sdim if (chmod(tn, 0600) < OK) 580218822Sdim#endif 581218822Sdim { 582218822Sdim warn("chown"); 583218822Sdim fclose(tmp); unlink(tn); 584218822Sdim return (-2); 585218822Sdim } 586218822Sdim 587218822Sdim if (fclose(tmp) == EOF) { 588218822Sdim warn("fclose"); 589218822Sdim unlink(tn); 590218822Sdim return (-2); 591218822Sdim } 592218822Sdim 593218822Sdim (void) snprintf(n, sizeof(n), CRON_TAB(User)); 594218822Sdim if (rename(tn, n)) { 595218822Sdim warn("error renaming %s to %s", tn, n); 596218822Sdim unlink(tn); 597218822Sdim return (-2); 598218822Sdim } 599218822Sdim 600218822Sdim log_it(RealUser, Pid, "REPLACE", User); 601218822Sdim 602218822Sdim poke_daemon(); 603218822Sdim 604218822Sdim return (0); 605218822Sdim} 606218822Sdim 607218822Sdim 608218822Sdimstatic void 609218822Sdimpoke_daemon() { 610218822Sdim#ifdef USE_UTIMES 611218822Sdim struct timeval tvs[2]; 612218822Sdim struct timezone tz; 613218822Sdim 614218822Sdim (void) gettimeofday(&tvs[0], &tz); 615218822Sdim tvs[1] = tvs[0]; 616218822Sdim if (utimes(SPOOL_DIR, tvs) < OK) { 617218822Sdim warn("can't update mtime on spooldir %s", SPOOL_DIR); 618218822Sdim return; 619218822Sdim } 620218822Sdim#else 621218822Sdim if (utime(SPOOL_DIR, NULL) < OK) { 622218822Sdim warn("can't update mtime on spooldir %s", SPOOL_DIR); 623218822Sdim return; 624218822Sdim } 625218822Sdim#endif /*USE_UTIMES*/ 626218822Sdim} 627218822Sdim