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 16 */ 17 18 #if !defined(lint) && !defined(LINT) 19 static const char rcsid[] = 20 "$FreeBSD$"; 21 #endif 22 23 #define MAIN_PROGRAM 24 25 26 #include "cron.h" 27 #include <sys/mman.h> 28 #include <sys/signal.h> 29 #if SYS_TIME_H 30 # include <sys/time.h> 31 #else 32 # include <time.h> 33 #endif 34 35 36 static void usage(void), 37 run_reboot_jobs(cron_db *), 38 cron_tick(cron_db *, int), 39 cron_sync(int), 40 cron_sleep(cron_db *, int), 41 cron_clean(cron_db *), 42 #ifdef USE_SIGCHLD 43 sigchld_handler(int), 44 #endif 45 sighup_handler(int), 46 parse_args(int c, char *v[]); 47 48 static int run_at_secres(cron_db *); 49 static void find_interval_entry(pid_t); 50 51 static cron_db database; 52 static time_t last_time = 0; 53 static int dst_enabled = 0; 54 static int dont_daemonize = 0; 55 struct pidfh *pfh; 56 57 static void 58 usage() { 59 #if DEBUGGING 60 char **dflags; 61 #endif 62 63 fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] " 64 "[-m mailto] [-n] [-s] [-o] [-x debugflag[,...]]\n"); 65 #if DEBUGGING 66 fprintf(stderr, "\ndebugflags: "); 67 68 for(dflags = DebugFlagNames; *dflags; dflags++) { 69 fprintf(stderr, "%s ", *dflags); 70 } 71 fprintf(stderr, "\n"); 72 #endif 73 74 exit(ERROR_EXIT); 75 } 76 77 static void 78 open_pidfile(void) 79 { 80 char pidfile[MAX_FNAME]; 81 char buf[MAX_TEMPSTR]; 82 int otherpid; 83 84 (void) snprintf(pidfile, sizeof(pidfile), PIDFILE, PIDDIR); 85 pfh = pidfile_open(pidfile, 0600, &otherpid); 86 if (pfh == NULL) { 87 if (errno == EEXIST) { 88 snprintf(buf, sizeof(buf), 89 "cron already running, pid: %d", otherpid); 90 } else { 91 snprintf(buf, sizeof(buf), 92 "can't open or create %s: %s", pidfile, 93 strerror(errno)); 94 } 95 log_it("CRON", getpid(), "DEATH", buf); 96 errx(ERROR_EXIT, "%s", buf); 97 } 98 } 99 100 int 101 main(argc, argv) 102 int argc; 103 char *argv[]; 104 { 105 int runnum; 106 int secres1, secres2; 107 struct tm *tm; 108 109 ProgramName = argv[0]; 110 111 #if defined(BSD) 112 setlinebuf(stdout); 113 setlinebuf(stderr); 114 #endif 115 116 parse_args(argc, argv); 117 118 #ifdef USE_SIGCHLD 119 (void) signal(SIGCHLD, sigchld_handler); 120 #else 121 (void) signal(SIGCLD, SIG_IGN); 122 #endif 123 (void) signal(SIGHUP, sighup_handler); 124 125 open_pidfile(); 126 set_cron_uid(); 127 set_cron_cwd(); 128 129 #if defined(POSIX) 130 setenv("PATH", _PATH_DEFPATH, 1); 131 #endif 132 133 /* if there are no debug flags turned on, fork as a daemon should. 134 */ 135 # if DEBUGGING 136 if (DebugFlags) { 137 # else 138 if (0) { 139 # endif 140 (void) fprintf(stderr, "[%d] cron started\n", getpid()); 141 } else if (dont_daemonize == 0) { 142 if (daemon(1, 0) == -1) { 143 pidfile_remove(pfh); 144 log_it("CRON",getpid(),"DEATH","can't become daemon"); 145 exit(0); 146 } 147 } 148 149 if (madvise(NULL, 0, MADV_PROTECT) != 0) 150 log_it("CRON", getpid(), "WARNING", "madvise() failed"); 151 152 pidfile_write(pfh); 153 database.head = NULL; 154 database.tail = NULL; 155 database.mtime = (time_t) 0; 156 load_database(&database); 157 secres1 = secres2 = run_at_secres(&database); 158 cron_sync(secres1); 159 run_reboot_jobs(&database); 160 runnum = 0; 161 while (TRUE) { 162 # if DEBUGGING 163 /* if (!(DebugFlags & DTEST)) */ 164 # endif /*DEBUGGING*/ 165 cron_sleep(&database, secres1); 166 167 if (secres1 == 0 || runnum % 60 == 0) { 168 load_database(&database); 169 secres2 = run_at_secres(&database); 170 if (secres2 != secres1) { 171 secres1 = secres2; 172 if (secres1 != 0) { 173 runnum = 0; 174 } else { 175 /* 176 * Going from 1 sec to 60 sec res. If we 177 * are already at minute's boundary, so 178 * let it run, otherwise schedule for the 179 * next minute. 180 */ 181 tm = localtime(&TargetTime); 182 if (tm->tm_sec > 0) { 183 cron_sync(secres2); 184 continue; 185 } 186 } 187 } 188 } 189 190 /* do this iteration 191 */ 192 cron_tick(&database, secres1); 193 194 /* sleep 1 or 60 seconds 195 */ 196 TargetTime += (secres1 != 0) ? 1 : 60; 197 runnum += 1; 198 } 199 } 200 201 202 static void 203 run_reboot_jobs(db) 204 cron_db *db; 205 { 206 register user *u; 207 register entry *e; 208 209 for (u = db->head; u != NULL; u = u->next) { 210 for (e = u->crontab; e != NULL; e = e->next) { 211 if (e->flags & WHEN_REBOOT) { 212 job_add(e, u); 213 } 214 if (e->flags & INTERVAL) { 215 e->lastexit = TargetTime; 216 } 217 } 218 } 219 (void) job_runqueue(); 220 } 221 222 223 static void 224 cron_tick(cron_db *db, int secres) 225 { 226 static struct tm lasttm; 227 static time_t diff = 0, /* time difference in seconds from the last offset change */ 228 difflimit = 0; /* end point for the time zone correction */ 229 struct tm otztm; /* time in the old time zone */ 230 int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; 231 register struct tm *tm = localtime(&TargetTime); 232 register int second, minute, hour, dom, month, dow; 233 register user *u; 234 register entry *e; 235 236 /* make 0-based values out of these so we can use them as indices 237 */ 238 second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND; 239 minute = tm->tm_min -FIRST_MINUTE; 240 hour = tm->tm_hour -FIRST_HOUR; 241 dom = tm->tm_mday -FIRST_DOM; 242 month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; 243 dow = tm->tm_wday -FIRST_DOW; 244 245 Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n", 246 getpid(), second, minute, hour, dom, month, dow)) 247 248 if (dst_enabled && last_time != 0 249 && TargetTime > last_time /* exclude stepping back */ 250 && tm->tm_gmtoff != lasttm.tm_gmtoff ) { 251 252 diff = tm->tm_gmtoff - lasttm.tm_gmtoff; 253 254 if ( diff > 0 ) { /* ST->DST */ 255 /* mark jobs for an earlier run */ 256 difflimit = TargetTime + diff; 257 for (u = db->head; u != NULL; u = u->next) { 258 for (e = u->crontab; e != NULL; e = e->next) { 259 e->flags &= ~NOT_UNTIL; 260 if ( e->lastrun >= TargetTime ) 261 e->lastrun = 0; 262 /* not include the ends of hourly ranges */ 263 if ( e->lastrun < TargetTime - 3600 ) 264 e->flags |= RUN_AT; 265 else 266 e->flags &= ~RUN_AT; 267 } 268 } 269 } else { /* diff < 0 : DST->ST */ 270 /* mark jobs for skipping */ 271 difflimit = TargetTime - diff; 272 for (u = db->head; u != NULL; u = u->next) { 273 for (e = u->crontab; e != NULL; e = e->next) { 274 e->flags |= NOT_UNTIL; 275 e->flags &= ~RUN_AT; 276 } 277 } 278 } 279 } 280 281 if (diff != 0) { 282 /* if the time was reset of the end of special zone is reached */ 283 if (last_time == 0 || TargetTime >= difflimit) { 284 /* disable the TZ switch checks */ 285 diff = 0; 286 difflimit = 0; 287 for (u = db->head; u != NULL; u = u->next) { 288 for (e = u->crontab; e != NULL; e = e->next) { 289 e->flags &= ~(RUN_AT|NOT_UNTIL); 290 } 291 } 292 } else { 293 /* get the time in the old time zone */ 294 time_t difftime = TargetTime + tm->tm_gmtoff - diff; 295 gmtime_r(&difftime, &otztm); 296 297 /* make 0-based values out of these so we can use them as indices 298 */ 299 otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND; 300 otzminute = otztm.tm_min -FIRST_MINUTE; 301 otzhour = otztm.tm_hour -FIRST_HOUR; 302 otzdom = otztm.tm_mday -FIRST_DOM; 303 otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; 304 otzdow = otztm.tm_wday -FIRST_DOW; 305 } 306 } 307 308 /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the 309 * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* 310 * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this 311 * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre. 312 * like many bizarre things, it's the standard. 313 */ 314 for (u = db->head; u != NULL; u = u->next) { 315 for (e = u->crontab; e != NULL; e = e->next) { 316 Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n", 317 env_get("LOGNAME", e->envp), 318 e->uid, e->gid, e->cmd)) 319 320 if (e->flags & INTERVAL) { 321 if (e->lastexit > 0 && 322 TargetTime >= e->lastexit + e->interval) 323 job_add(e, u); 324 continue; 325 } 326 327 if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) { 328 if (bit_test(e->second, otzsecond) 329 && bit_test(e->minute, otzminute) 330 && bit_test(e->hour, otzhour) 331 && bit_test(e->month, otzmonth) 332 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) 333 ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom)) 334 : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom)) 335 ) 336 ) { 337 if ( e->flags & RUN_AT ) { 338 e->flags &= ~RUN_AT; 339 e->lastrun = TargetTime; 340 job_add(e, u); 341 continue; 342 } else 343 e->flags &= ~NOT_UNTIL; 344 } else if ( e->flags & NOT_UNTIL ) 345 continue; 346 } 347 348 if (bit_test(e->second, second) 349 && bit_test(e->minute, minute) 350 && bit_test(e->hour, hour) 351 && bit_test(e->month, month) 352 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) 353 ? (bit_test(e->dow,dow) && bit_test(e->dom,dom)) 354 : (bit_test(e->dow,dow) || bit_test(e->dom,dom)) 355 ) 356 ) { 357 e->flags &= ~RUN_AT; 358 e->lastrun = TargetTime; 359 job_add(e, u); 360 } 361 } 362 } 363 364 last_time = TargetTime; 365 lasttm = *tm; 366 } 367 368 369 /* the task here is to figure out how long it's going to be until :00 of the 370 * following minute and initialize TargetTime to this value. TargetTime 371 * will subsequently slide 60 seconds at a time, with correction applied 372 * implicitly in cron_sleep(). it would be nice to let cron execute in 373 * the "current minute" before going to sleep, but by restarting cron you 374 * could then get it to execute a given minute's jobs more than once. 375 * instead we have the chance of missing a minute's jobs completely, but 376 * that's something sysadmin's know to expect what with crashing computers.. 377 */ 378 static void 379 cron_sync(int secres) { 380 struct tm *tm; 381 382 TargetTime = time((time_t*)0); 383 if (secres != 0) { 384 TargetTime += 1; 385 } else { 386 tm = localtime(&TargetTime); 387 TargetTime += (60 - tm->tm_sec); 388 } 389 } 390 391 static void 392 timespec_subtract(struct timespec *result, struct timespec *x, 393 struct timespec *y) 394 { 395 *result = *x; 396 result->tv_sec -= y->tv_sec; 397 result->tv_nsec -= y->tv_nsec; 398 if (result->tv_nsec < 0) { 399 result->tv_sec--; 400 result->tv_nsec += 1000000000; 401 } 402 } 403 404 static void 405 cron_sleep(cron_db *db, int secres) 406 { 407 int seconds_to_wait; 408 int rval; 409 struct timespec ctime, ttime, stime, remtime; 410 411 /* 412 * Loop until we reach the top of the next minute, sleep when possible. 413 */ 414 415 for (;;) { 416 clock_gettime(CLOCK_REALTIME, &ctime); 417 ttime.tv_sec = TargetTime; 418 ttime.tv_nsec = 0; 419 timespec_subtract(&stime, &ttime, &ctime); 420 421 /* 422 * If the seconds_to_wait value is insane, jump the cron 423 */ 424 425 if (stime.tv_sec < -600 || stime.tv_sec > 600) { 426 cron_clean(db); 427 cron_sync(secres); 428 continue; 429 } 430 431 seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : 432 stime.tv_sec; 433 434 Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", 435 getpid(), (long)TargetTime, seconds_to_wait)) 436 437 /* 438 * If we've run out of wait time or there are no jobs left 439 * to run, break 440 */ 441 442 if (stime.tv_sec < 0) 443 break; 444 if (job_runqueue() == 0) { 445 Debug(DSCH, ("[%d] sleeping for %d seconds\n", 446 getpid(), seconds_to_wait)) 447 448 for (;;) { 449 rval = nanosleep(&stime, &remtime); 450 if (rval == 0 || errno != EINTR) 451 break; 452 stime.tv_sec = remtime.tv_sec; 453 stime.tv_nsec = remtime.tv_nsec; 454 } 455 } 456 } 457 } 458 459 460 /* if the time was changed abruptly, clear the flags related 461 * to the daylight time switch handling to avoid strange effects 462 */ 463 464 static void 465 cron_clean(db) 466 cron_db *db; 467 { 468 user *u; 469 entry *e; 470 471 last_time = 0; 472 473 for (u = db->head; u != NULL; u = u->next) { 474 for (e = u->crontab; e != NULL; e = e->next) { 475 e->flags &= ~(RUN_AT|NOT_UNTIL); 476 } 477 } 478 } 479 480 #ifdef USE_SIGCHLD 481 static void 482 sigchld_handler(int x) 483 { 484 WAIT_T waiter; 485 PID_T pid; 486 487 for (;;) { 488 #ifdef POSIX 489 pid = waitpid(-1, &waiter, WNOHANG); 490 #else 491 pid = wait3(&waiter, WNOHANG, (struct rusage *)0); 492 #endif 493 switch (pid) { 494 case -1: 495 Debug(DPROC, 496 ("[%d] sigchld...no children\n", getpid())) 497 return; 498 case 0: 499 Debug(DPROC, 500 ("[%d] sigchld...no dead kids\n", getpid())) 501 return; 502 default: 503 find_interval_entry(pid); 504 Debug(DPROC, 505 ("[%d] sigchld...pid #%d died, stat=%d\n", 506 getpid(), pid, WEXITSTATUS(waiter))) 507 } 508 } 509 } 510 #endif /*USE_SIGCHLD*/ 511 512 513 static void 514 sighup_handler(int x) 515 { 516 log_close(); 517 } 518 519 520 static void 521 parse_args(argc, argv) 522 int argc; 523 char *argv[]; 524 { 525 int argch; 526 char *endp; 527 528 while ((argch = getopt(argc, argv, "j:J:m:nosx:")) != -1) { 529 switch (argch) { 530 case 'j': 531 Jitter = strtoul(optarg, &endp, 10); 532 if (*optarg == '\0' || *endp != '\0' || Jitter > 60) 533 errx(ERROR_EXIT, 534 "bad value for jitter: %s", optarg); 535 break; 536 case 'J': 537 RootJitter = strtoul(optarg, &endp, 10); 538 if (*optarg == '\0' || *endp != '\0' || RootJitter > 60) 539 errx(ERROR_EXIT, 540 "bad value for root jitter: %s", optarg); 541 break; 542 case 'm': 543 defmailto = optarg; 544 break; 545 case 'n': 546 dont_daemonize = 1; 547 break; 548 case 'o': 549 dst_enabled = 0; 550 break; 551 case 's': 552 dst_enabled = 1; 553 break; 554 case 'x': 555 if (!set_debug_flags(optarg)) 556 usage(); 557 break; 558 default: 559 usage(); 560 } 561 } 562 } 563 564 static int 565 run_at_secres(cron_db *db) 566 { 567 user *u; 568 entry *e; 569 570 for (u = db->head; u != NULL; u = u->next) { 571 for (e = u->crontab; e != NULL; e = e->next) { 572 if ((e->flags & (SEC_RES | INTERVAL)) != 0) 573 return 1; 574 } 575 } 576 return 0; 577 } 578 579 static void 580 find_interval_entry(pid_t pid) 581 { 582 user *u; 583 entry *e; 584 585 for (u = database.head; u != NULL; u = u->next) { 586 for (e = u->crontab; e != NULL; e = e->next) { 587 if ((e->flags & INTERVAL) && e->child == pid) { 588 e->lastexit = time(NULL); 589 e->child = 0; 590 break; 591 } 592 } 593 } 594 } 595