| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* Copyright 1988,1990,1993,1994 by Paul Vixie |
| 2 | * All rights reserved | |
| 3 | * | |
| 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. | |
| 12 | * | |
| 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 | |
| 1de703da | 16 | * |
| 984263bc | 17 | * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp |
| 1de703da | 18 | * $FreeBSD: src/usr.sbin/cron/crontab/crontab.c,v 1.12.2.4 2001/06/16 03:18:37 peter Exp $ |
| 71126e33 | 19 | * $DragonFly: src/usr.sbin/cron/crontab/crontab.c,v 1.5 2004/12/18 22:48:03 swildner Exp $ |
| 984263bc MD |
20 | */ |
| 21 | ||
| 984263bc MD |
22 | /* crontab - install and manage per-user crontab files |
| 23 | * vix 02may87 [RCS has the rest of the log] | |
| 24 | * vix 26jan87 [original] | |
| 25 | */ | |
| 26 | ||
| 27 | #define MAIN_PROGRAM | |
| 28 | ||
| 29 | #include "cron.h" | |
| 30 | #include <errno.h> | |
| 31 | #include <fcntl.h> | |
| 32 | #include <paths.h> | |
| 33 | #include <sys/file.h> | |
| 34 | #include <sys/stat.h> | |
| 35 | #ifdef USE_UTIMES | |
| 36 | # include <sys/time.h> | |
| 37 | #else | |
| 38 | # include <time.h> | |
| 39 | # include <utime.h> | |
| 40 | #endif | |
| 41 | #if defined(POSIX) | |
| 42 | # include <locale.h> | |
| 43 | #endif | |
| 44 | ||
| 45 | ||
| 46 | #define NHEADER_LINES 3 | |
| 47 | ||
| 48 | ||
| 49 | enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; | |
| 50 | ||
| 51 | #if DEBUGGING | |
| 52 | static char *Options[] = { "???", "list", "delete", "edit", "replace" }; | |
| 53 | #endif | |
| 54 | ||
| 55 | ||
| 56 | static PID_T Pid; | |
| 57 | static char User[MAX_UNAME], RealUser[MAX_UNAME]; | |
| 58 | static char Filename[MAX_FNAME]; | |
| 59 | static FILE *NewCrontab; | |
| 60 | static int CheckErrorCount; | |
| 61 | static enum opt_t Option; | |
| 62 | static struct passwd *pw; | |
| 2d8a3be7 EN |
63 | static void list_cmd(void), |
| 64 | delete_cmd(void), | |
| 65 | edit_cmd(void), | |
| 66 | poke_daemon(void), | |
| 67 | check_error(char *), | |
| 68 | parse_args(int c, char *v[]); | |
| 69 | static int replace_cmd(void); | |
| 984263bc MD |
70 | |
| 71 | ||
| 72 | static void | |
| 3a2e6dbb | 73 | usage(char *msg) |
| 984263bc MD |
74 | { |
| 75 | fprintf(stderr, "crontab: usage error: %s\n", msg); | |
| 76 | fprintf(stderr, "%s\n%s\n", | |
| 77 | "usage: crontab [-u user] file", | |
| 78 | " crontab [-u user] { -e | -l | -r }"); | |
| 79 | exit(ERROR_EXIT); | |
| 80 | } | |
| 81 | ||
| 82 | ||
| 83 | int | |
| 3a2e6dbb | 84 | main(int argc, char **argv) |
| 984263bc MD |
85 | { |
| 86 | int exitstatus; | |
| 87 | ||
| 88 | Pid = getpid(); | |
| 89 | ProgramName = argv[0]; | |
| 90 | ||
| 91 | #if defined(POSIX) | |
| 92 | setlocale(LC_ALL, ""); | |
| 93 | #endif | |
| 94 | ||
| 95 | #if defined(BSD) | |
| 96 | setlinebuf(stderr); | |
| 97 | #endif | |
| 98 | parse_args(argc, argv); /* sets many globals, opens a file */ | |
| 99 | set_cron_uid(); | |
| 100 | set_cron_cwd(); | |
| 101 | if (!allowed(User)) { | |
| 102 | warnx("you (%s) are not allowed to use this program", User); | |
| 103 | log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); | |
| 104 | exit(ERROR_EXIT); | |
| 105 | } | |
| 106 | exitstatus = OK_EXIT; | |
| 107 | switch (Option) { | |
| 108 | case opt_list: list_cmd(); | |
| 109 | break; | |
| 110 | case opt_delete: delete_cmd(); | |
| 111 | break; | |
| 112 | case opt_edit: edit_cmd(); | |
| 113 | break; | |
| 114 | case opt_replace: if (replace_cmd() < 0) | |
| 115 | exitstatus = ERROR_EXIT; | |
| 116 | break; | |
| 117 | case opt_unknown: | |
| 118 | break; | |
| 119 | } | |
| 120 | exit(0); | |
| 121 | /*NOTREACHED*/ | |
| 122 | } | |
| 123 | ||
| 124 | ||
| 125 | static void | |
| 3a2e6dbb | 126 | parse_args(int argc, char **argv) |
| 984263bc MD |
127 | { |
| 128 | int argch; | |
| 129 | ||
| 130 | if (!(pw = getpwuid(getuid()))) | |
| 131 | errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out"); | |
| 71126e33 | 132 | strncpy(User, pw->pw_name, (sizeof User)-1); |
| 984263bc MD |
133 | User[(sizeof User)-1] = '\0'; |
| 134 | strcpy(RealUser, User); | |
| 135 | Filename[0] = '\0'; | |
| 136 | Option = opt_unknown; | |
| 137 | while ((argch = getopt(argc, argv, "u:lerx:")) != -1) { | |
| 138 | switch (argch) { | |
| 139 | case 'x': | |
| 140 | if (!set_debug_flags(optarg)) | |
| 141 | usage("bad debug option"); | |
| 142 | break; | |
| 143 | case 'u': | |
| 144 | if (getuid() != ROOT_UID) | |
| 145 | errx(ERROR_EXIT, "must be privileged to use -u"); | |
| 146 | if (!(pw = getpwnam(optarg))) | |
| 147 | errx(ERROR_EXIT, "user `%s' unknown", optarg); | |
| 71126e33 | 148 | strncpy(User, pw->pw_name, (sizeof User)-1); |
| 984263bc MD |
149 | User[(sizeof User)-1] = '\0'; |
| 150 | break; | |
| 151 | case 'l': | |
| 152 | if (Option != opt_unknown) | |
| 153 | usage("only one operation permitted"); | |
| 154 | Option = opt_list; | |
| 155 | break; | |
| 156 | case 'r': | |
| 157 | if (Option != opt_unknown) | |
| 158 | usage("only one operation permitted"); | |
| 159 | Option = opt_delete; | |
| 160 | break; | |
| 161 | case 'e': | |
| 162 | if (Option != opt_unknown) | |
| 163 | usage("only one operation permitted"); | |
| 164 | Option = opt_edit; | |
| 165 | break; | |
| 166 | default: | |
| 167 | usage("unrecognized option"); | |
| 168 | } | |
| 169 | } | |
| 170 | ||
| 171 | endpwent(); | |
| 172 | ||
| 173 | if (Option != opt_unknown) { | |
| 174 | if (argv[optind] != NULL) { | |
| 175 | usage("no arguments permitted after this option"); | |
| 176 | } | |
| 177 | } else { | |
| 178 | if (argv[optind] != NULL) { | |
| 179 | Option = opt_replace; | |
| 71126e33 | 180 | strncpy (Filename, argv[optind], (sizeof Filename)-1); |
| 984263bc MD |
181 | Filename[(sizeof Filename)-1] = '\0'; |
| 182 | ||
| 183 | } else { | |
| 184 | usage("file name must be specified for replace"); | |
| 185 | } | |
| 186 | } | |
| 187 | ||
| 188 | if (Option == opt_replace) { | |
| 189 | /* we have to open the file here because we're going to | |
| 190 | * chdir(2) into /var/cron before we get around to | |
| 191 | * reading the file. | |
| 192 | */ | |
| 193 | if (!strcmp(Filename, "-")) { | |
| 194 | NewCrontab = stdin; | |
| 195 | } else { | |
| 196 | /* relinquish the setuid status of the binary during | |
| 197 | * the open, lest nonroot users read files they should | |
| 198 | * not be able to read. we can't use access() here | |
| 199 | * since there's a race condition. thanks go out to | |
| 200 | * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting | |
| 201 | * the race. | |
| 202 | */ | |
| 203 | ||
| 204 | if (swap_uids() < OK) | |
| 205 | err(ERROR_EXIT, "swapping uids"); | |
| 206 | if (!(NewCrontab = fopen(Filename, "r"))) | |
| 207 | err(ERROR_EXIT, "%s", Filename); | |
| 208 | if (swap_uids() < OK) | |
| 209 | err(ERROR_EXIT, "swapping uids back"); | |
| 210 | } | |
| 211 | } | |
| 212 | ||
| 213 | Debug(DMISC, ("user=%s, file=%s, option=%s\n", | |
| 214 | User, Filename, Options[(int)Option])) | |
| 215 | } | |
| 216 | ||
| 217 | ||
| 218 | static void | |
| 3a2e6dbb EN |
219 | list_cmd(void) |
| 220 | { | |
| 984263bc MD |
221 | char n[MAX_FNAME]; |
| 222 | FILE *f; | |
| 223 | int ch, x; | |
| 224 | ||
| 225 | log_it(RealUser, Pid, "LIST", User); | |
| 71126e33 | 226 | sprintf(n, CRON_TAB(User)); |
| 984263bc MD |
227 | if (!(f = fopen(n, "r"))) { |
| 228 | if (errno == ENOENT) | |
| 229 | errx(ERROR_EXIT, "no crontab for %s", User); | |
| 230 | else | |
| 231 | err(ERROR_EXIT, "%s", n); | |
| 232 | } | |
| 233 | ||
| 234 | /* file is open. copy to stdout, close. | |
| 235 | */ | |
| 236 | Set_LineNum(1) | |
| 237 | ||
| 238 | /* ignore the top few comments since we probably put them there. | |
| 239 | */ | |
| 240 | for (x = 0; x < NHEADER_LINES; x++) { | |
| 241 | ch = get_char(f); | |
| 242 | if (EOF == ch) | |
| 243 | break; | |
| 244 | if ('#' != ch) { | |
| 245 | putchar(ch); | |
| 246 | break; | |
| 247 | } | |
| 248 | while (EOF != (ch = get_char(f))) | |
| 249 | if (ch == '\n') | |
| 250 | break; | |
| 251 | if (EOF == ch) | |
| 252 | break; | |
| 253 | } | |
| 254 | ||
| 255 | while (EOF != (ch = get_char(f))) | |
| 256 | putchar(ch); | |
| 257 | fclose(f); | |
| 258 | } | |
| 259 | ||
| 260 | ||
| 261 | static void | |
| 3a2e6dbb EN |
262 | delete_cmd(void) |
| 263 | { | |
| 984263bc MD |
264 | char n[MAX_FNAME]; |
| 265 | int ch, first; | |
| 266 | ||
| 267 | if (isatty(STDIN_FILENO)) { | |
| 71126e33 | 268 | fprintf(stderr, "remove crontab for %s? ", User); |
| 984263bc MD |
269 | first = ch = getchar(); |
| 270 | while (ch != '\n' && ch != EOF) | |
| 271 | ch = getchar(); | |
| 272 | if (first != 'y' && first != 'Y') | |
| 273 | return; | |
| 274 | } | |
| 275 | ||
| 276 | log_it(RealUser, Pid, "DELETE", User); | |
| 71126e33 | 277 | sprintf(n, CRON_TAB(User)); |
| 984263bc MD |
278 | if (unlink(n)) { |
| 279 | if (errno == ENOENT) | |
| 280 | errx(ERROR_EXIT, "no crontab for %s", User); | |
| 281 | else | |
| 282 | err(ERROR_EXIT, "%s", n); | |
| 283 | } | |
| 284 | poke_daemon(); | |
| 285 | } | |
| 286 | ||
| 287 | ||
| 288 | static void | |
| 3a2e6dbb | 289 | check_error(char *msg) |
| 984263bc MD |
290 | { |
| 291 | CheckErrorCount++; | |
| 292 | fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); | |
| 293 | } | |
| 294 | ||
| 295 | ||
| 296 | static void | |
| 3a2e6dbb EN |
297 | edit_cmd(void) |
| 298 | { | |
| 984263bc MD |
299 | char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; |
| 300 | FILE *f; | |
| 301 | int ch, t, x; | |
| 302 | struct stat statbuf, fsbuf; | |
| 303 | time_t mtime; | |
| 304 | WAIT_T waiter; | |
| 305 | PID_T pid, xpid; | |
| 306 | mode_t um; | |
| 307 | ||
| 308 | log_it(RealUser, Pid, "BEGIN EDIT", User); | |
| 71126e33 | 309 | sprintf(n, CRON_TAB(User)); |
| 984263bc MD |
310 | if (!(f = fopen(n, "r"))) { |
| 311 | if (errno != ENOENT) | |
| 312 | err(ERROR_EXIT, "%s", n); | |
| 313 | warnx("no crontab for %s - using an empty one", User); | |
| 314 | if (!(f = fopen(_PATH_DEVNULL, "r"))) | |
| 315 | err(ERROR_EXIT, _PATH_DEVNULL); | |
| 316 | } | |
| 317 | ||
| 318 | um = umask(077); | |
| 71126e33 | 319 | sprintf(Filename, "/tmp/crontab.XXXXXXXXXX"); |
| 984263bc MD |
320 | if ((t = mkstemp(Filename)) == -1) { |
| 321 | warn("%s", Filename); | |
| 71126e33 | 322 | umask(um); |
| 984263bc MD |
323 | goto fatal; |
| 324 | } | |
| 71126e33 | 325 | umask(um); |
| 984263bc MD |
326 | #ifdef HAS_FCHOWN |
| 327 | if (fchown(t, getuid(), getgid()) < 0) { | |
| 328 | #else | |
| 329 | if (chown(Filename, getuid(), getgid()) < 0) { | |
| 330 | #endif | |
| 331 | warn("fchown"); | |
| 332 | goto fatal; | |
| 333 | } | |
| 334 | if (!(NewCrontab = fdopen(t, "r+"))) { | |
| 335 | warn("fdopen"); | |
| 336 | goto fatal; | |
| 337 | } | |
| 338 | ||
| 339 | Set_LineNum(1) | |
| 340 | ||
| 341 | /* ignore the top few comments since we probably put them there. | |
| 342 | */ | |
| 343 | for (x = 0; x < NHEADER_LINES; x++) { | |
| 344 | ch = get_char(f); | |
| 345 | if (EOF == ch) | |
| 346 | break; | |
| 347 | if ('#' != ch) { | |
| 348 | putc(ch, NewCrontab); | |
| 349 | break; | |
| 350 | } | |
| 351 | while (EOF != (ch = get_char(f))) | |
| 352 | if (ch == '\n') | |
| 353 | break; | |
| 354 | if (EOF == ch) | |
| 355 | break; | |
| 356 | } | |
| 357 | ||
| 358 | /* copy the rest of the crontab (if any) to the temp file. | |
| 359 | */ | |
| 360 | if (EOF != ch) | |
| 361 | while (EOF != (ch = get_char(f))) | |
| 362 | putc(ch, NewCrontab); | |
| 363 | fclose(f); | |
| 364 | if (fflush(NewCrontab)) | |
| 365 | err(ERROR_EXIT, "%s", Filename); | |
| 366 | if (fstat(t, &fsbuf) < 0) { | |
| 367 | warn("unable to fstat temp file"); | |
| 368 | goto fatal; | |
| 369 | } | |
| 370 | again: | |
| 371 | if (stat(Filename, &statbuf) < 0) { | |
| 372 | warn("stat"); | |
| 373 | fatal: unlink(Filename); | |
| 374 | exit(ERROR_EXIT); | |
| 375 | } | |
| 376 | if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) | |
| 377 | errx(ERROR_EXIT, "temp file must be edited in place"); | |
| 378 | mtime = statbuf.st_mtime; | |
| 379 | ||
| 380 | if ((!(editor = getenv("VISUAL"))) | |
| 381 | && (!(editor = getenv("EDITOR"))) | |
| 382 | ) { | |
| 383 | editor = EDITOR; | |
| 384 | } | |
| 385 | ||
| 386 | /* we still have the file open. editors will generally rewrite the | |
| 387 | * original file rather than renaming/unlinking it and starting a | |
| 388 | * new one; even backup files are supposed to be made by copying | |
| 389 | * rather than by renaming. if some editor does not support this, | |
| 390 | * then don't use it. the security problems are more severe if we | |
| 391 | * close and reopen the file around the edit. | |
| 392 | */ | |
| 393 | ||
| 394 | switch (pid = fork()) { | |
| 395 | case -1: | |
| 396 | warn("fork"); | |
| 397 | goto fatal; | |
| 398 | case 0: | |
| 399 | /* child */ | |
| 400 | if (setuid(getuid()) < 0) | |
| 401 | err(ERROR_EXIT, "setuid(getuid())"); | |
| 402 | if (chdir("/tmp") < 0) | |
| 403 | err(ERROR_EXIT, "chdir(/tmp)"); | |
| 404 | if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR) | |
| 405 | errx(ERROR_EXIT, "editor or filename too long"); | |
| 406 | execlp(editor, editor, Filename, NULL); | |
| 407 | err(ERROR_EXIT, "%s", editor); | |
| 408 | /*NOTREACHED*/ | |
| 409 | default: | |
| 410 | /* parent */ | |
| 411 | break; | |
| 412 | } | |
| 413 | ||
| 414 | /* parent */ | |
| 415 | { | |
| 416 | void (*f[4])(); | |
| 417 | f[0] = signal(SIGHUP, SIG_IGN); | |
| 418 | f[1] = signal(SIGINT, SIG_IGN); | |
| 419 | f[2] = signal(SIGTERM, SIG_IGN); | |
| 420 | xpid = wait(&waiter); | |
| 421 | signal(SIGHUP, f[0]); | |
| 422 | signal(SIGINT, f[1]); | |
| 423 | signal(SIGTERM, f[2]); | |
| 424 | } | |
| 425 | if (xpid != pid) { | |
| 426 | warnx("wrong PID (%d != %d) from \"%s\"", xpid, pid, editor); | |
| 427 | goto fatal; | |
| 428 | } | |
| 429 | if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { | |
| 430 | warnx("\"%s\" exited with status %d", editor, WEXITSTATUS(waiter)); | |
| 431 | goto fatal; | |
| 432 | } | |
| 433 | if (WIFSIGNALED(waiter)) { | |
| 434 | warnx("\"%s\" killed; signal %d (%score dumped)", | |
| 435 | editor, WTERMSIG(waiter), WCOREDUMP(waiter) ?"" :"no "); | |
| 436 | goto fatal; | |
| 437 | } | |
| 438 | if (stat(Filename, &statbuf) < 0) { | |
| 439 | warn("stat"); | |
| 440 | goto fatal; | |
| 441 | } | |
| 442 | if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) | |
| 443 | errx(ERROR_EXIT, "temp file must be edited in place"); | |
| 444 | if (mtime == statbuf.st_mtime) { | |
| 445 | warnx("no changes made to crontab"); | |
| 446 | goto remove; | |
| 447 | } | |
| 448 | warnx("installing new crontab"); | |
| 449 | switch (replace_cmd()) { | |
| 450 | case 0: | |
| 451 | break; | |
| 452 | case -1: | |
| 453 | for (;;) { | |
| 454 | printf("Do you want to retry the same edit? "); | |
| 455 | fflush(stdout); | |
| 456 | q[0] = '\0'; | |
| 71126e33 | 457 | fgets(q, sizeof q, stdin); |
| 984263bc MD |
458 | switch (islower(q[0]) ? q[0] : tolower(q[0])) { |
| 459 | case 'y': | |
| 460 | goto again; | |
| 461 | case 'n': | |
| 462 | goto abandon; | |
| 463 | default: | |
| 464 | fprintf(stderr, "Enter Y or N\n"); | |
| 465 | } | |
| 466 | } | |
| 467 | /*NOTREACHED*/ | |
| 468 | case -2: | |
| 469 | abandon: | |
| 470 | warnx("edits left in %s", Filename); | |
| 471 | goto done; | |
| 472 | default: | |
| 473 | warnx("panic: bad switch() in replace_cmd()"); | |
| 474 | goto fatal; | |
| 475 | } | |
| 476 | remove: | |
| 477 | unlink(Filename); | |
| 478 | done: | |
| 479 | log_it(RealUser, Pid, "END EDIT", User); | |
| 480 | } | |
| 481 | ||
| 482 | ||
| 483 | /* returns 0 on success | |
| 484 | * -1 on syntax error | |
| 485 | * -2 on install error | |
| 486 | */ | |
| 487 | static int | |
| 3a2e6dbb EN |
488 | replace_cmd(void) |
| 489 | { | |
| 984263bc MD |
490 | char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; |
| 491 | FILE *tmp; | |
| 492 | int ch, eof; | |
| 493 | entry *e; | |
| 494 | time_t now = time(NULL); | |
| 495 | char **envp = env_init(); | |
| 496 | ||
| 497 | if (envp == NULL) { | |
| 498 | warnx("cannot allocate memory"); | |
| 499 | return (-2); | |
| 500 | } | |
| 501 | ||
| 71126e33 SW |
502 | sprintf(n, "tmp.%d", Pid); |
| 503 | sprintf(tn, CRON_TAB(n)); | |
| 984263bc MD |
504 | if (!(tmp = fopen(tn, "w+"))) { |
| 505 | warn("%s", tn); | |
| 506 | return (-2); | |
| 507 | } | |
| 508 | ||
| 509 | /* write a signature at the top of the file. | |
| 510 | * | |
| 511 | * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code. | |
| 512 | */ | |
| 513 | fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n"); | |
| 514 | fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now)); | |
| 984263bc MD |
515 | |
| 516 | /* copy the crontab to the tmp | |
| 517 | */ | |
| 518 | rewind(NewCrontab); | |
| 519 | Set_LineNum(1) | |
| 520 | while (EOF != (ch = get_char(NewCrontab))) | |
| 521 | putc(ch, tmp); | |
| 522 | ftruncate(fileno(tmp), ftell(tmp)); | |
| 523 | fflush(tmp); rewind(tmp); | |
| 524 | ||
| 525 | if (ferror(tmp)) { | |
| 526 | warnx("error while writing new crontab to %s", tn); | |
| 527 | fclose(tmp); unlink(tn); | |
| 528 | return (-2); | |
| 529 | } | |
| 530 | ||
| 531 | /* check the syntax of the file being installed. | |
| 532 | */ | |
| 533 | ||
| 534 | /* BUG: was reporting errors after the EOF if there were any errors | |
| 535 | * in the file proper -- kludged it by stopping after first error. | |
| 536 | * vix 31mar87 | |
| 537 | */ | |
| 538 | Set_LineNum(1 - NHEADER_LINES) | |
| 539 | CheckErrorCount = 0; eof = FALSE; | |
| 540 | while (!CheckErrorCount && !eof) { | |
| 541 | switch (load_env(envstr, tmp)) { | |
| 542 | case ERR: | |
| 543 | eof = TRUE; | |
| 544 | break; | |
| 545 | case FALSE: | |
| 546 | e = load_entry(tmp, check_error, pw, envp); | |
| 547 | if (e) | |
| 548 | free(e); | |
| 549 | break; | |
| 550 | case TRUE: | |
| 551 | break; | |
| 552 | } | |
| 553 | } | |
| 554 | ||
| 555 | if (CheckErrorCount != 0) { | |
| 556 | warnx("errors in crontab file, can't install"); | |
| 557 | fclose(tmp); unlink(tn); | |
| 558 | return (-1); | |
| 559 | } | |
| 560 | ||
| 561 | #ifdef HAS_FCHOWN | |
| 562 | if (fchown(fileno(tmp), ROOT_UID, -1) < OK) | |
| 563 | #else | |
| 564 | if (chown(tn, ROOT_UID, -1) < OK) | |
| 565 | #endif | |
| 566 | { | |
| 567 | warn("chown"); | |
| 568 | fclose(tmp); unlink(tn); | |
| 569 | return (-2); | |
| 570 | } | |
| 571 | ||
| 572 | #ifdef HAS_FCHMOD | |
| 573 | if (fchmod(fileno(tmp), 0600) < OK) | |
| 574 | #else | |
| 575 | if (chmod(tn, 0600) < OK) | |
| 576 | #endif | |
| 577 | { | |
| 578 | warn("chown"); | |
| 579 | fclose(tmp); unlink(tn); | |
| 580 | return (-2); | |
| 581 | } | |
| 582 | ||
| 583 | if (fclose(tmp) == EOF) { | |
| 584 | warn("fclose"); | |
| 585 | unlink(tn); | |
| 586 | return (-2); | |
| 587 | } | |
| 588 | ||
| 71126e33 | 589 | sprintf(n, CRON_TAB(User)); |
| 984263bc MD |
590 | if (rename(tn, n)) { |
| 591 | warn("error renaming %s to %s", tn, n); | |
| 592 | unlink(tn); | |
| 593 | return (-2); | |
| 594 | } | |
| 595 | log_it(RealUser, Pid, "REPLACE", User); | |
| 596 | ||
| 597 | poke_daemon(); | |
| 598 | ||
| 599 | return (0); | |
| 600 | } | |
| 601 | ||
| 602 | ||
| 603 | static void | |
| 3a2e6dbb EN |
604 | poke_daemon(void) |
| 605 | { | |
| 984263bc MD |
606 | #ifdef USE_UTIMES |
| 607 | struct timeval tvs[2]; | |
| 608 | struct timezone tz; | |
| 609 | ||
| 71126e33 | 610 | gettimeofday(&tvs[0], &tz); |
| 984263bc MD |
611 | tvs[1] = tvs[0]; |
| 612 | if (utimes(SPOOL_DIR, tvs) < OK) { | |
| 613 | warn("can't update mtime on spooldir %s", SPOOL_DIR); | |
| 614 | return; | |
| 615 | } | |
| 616 | #else | |
| 617 | if (utime(SPOOL_DIR, NULL) < OK) { | |
| 618 | warn("can't update mtime on spooldir %s", SPOOL_DIR); | |
| 619 | return; | |
| 620 | } | |
| 621 | #endif /*USE_UTIMES*/ | |
| 622 | } |