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