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