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(DOSCCS) 19: static char sccsid[] = "@(#)cron.c 2.11.1 (2.11BSD) 1999/08/05"; 20: #endif 21: 22: #define MAIN_PROGRAM 23: 24: #include "cron.h" 25: #include <sys/signal.h> 26: #include <sys/time.h> 27: 28: static void usage __P((void)), 29: run_reboot_jobs __P((cron_db *)), 30: cron_tick __P((cron_db *)), 31: cron_sync __P((void)), 32: cron_sleep __P((void)), 33: sigchld_handler __P((int)), 34: sighup_handler __P((int)), 35: parse_args __P((int c, char *v[])); 36: 37: 38: static void 39: usage() { 40: fprintf(stderr, "usage: %s [-x debugflag[,...]]\n", ProgramName); 41: exit(ERROR_EXIT); 42: } 43: 44: 45: int 46: main(argc, argv) 47: int argc; 48: char *argv[]; 49: { 50: cron_db database; 51: 52: ProgramName = argv[0]; 53: 54: setlinebuf(stdout); 55: setlinebuf(stderr); 56: 57: parse_args(argc, argv); 58: 59: (void) signal(SIGCHLD, sigchld_handler); 60: (void) signal(SIGHUP, sighup_handler); 61: 62: acquire_daemonlock(0); 63: set_cron_uid(); 64: set_cron_cwd(); 65: 66: setenv("PATH", _PATH_DEFPATH, 1); 67: 68: /* if there are no debug flags turned on, fork as a daemon should. 69: */ 70: # if DEBUGGING 71: if (DebugFlags) { 72: # else 73: if (0) { 74: # endif 75: (void) fprintf(stderr, "[%d] cron started\n", getpid()); 76: } else { 77: switch (fork()) { 78: case -1: 79: log_it("CRON",getpid(),"DEATH","can't fork"); 80: exit(0); 81: break; 82: case 0: 83: /* child process */ 84: log_it("CRON",getpid(),"STARTUP","fork ok"); 85: (void) setsid(); 86: break; 87: default: 88: /* parent process should just die */ 89: _exit(0); 90: } 91: } 92: 93: acquire_daemonlock(0); 94: database.head = NULL; 95: database.tail = NULL; 96: database.mtime = (time_t) 0; 97: load_database(&database); 98: run_reboot_jobs(&database); 99: cron_sync(); 100: while (TRUE) { 101: # if DEBUGGING 102: if (!(DebugFlags & DTEST)) 103: # endif /*DEBUGGING*/ 104: cron_sleep(); 105: 106: load_database(&database); 107: 108: /* do this iteration 109: */ 110: cron_tick(&database); 111: 112: /* sleep 1 minute 113: */ 114: TargetTime += 60; 115: } 116: } 117: 118: 119: static void 120: run_reboot_jobs(db) 121: cron_db *db; 122: { 123: register user *u; 124: register entry *e; 125: 126: for (u = db->head; u != NULL; u = u->next) { 127: for (e = u->crontab; e != NULL; e = e->next) { 128: if (e->flags & WHEN_REBOOT) { 129: job_add(e, u); 130: } 131: } 132: } 133: (void) job_runqueue(); 134: } 135: 136: 137: static void 138: cron_tick(db) 139: cron_db *db; 140: { 141: register struct tm *tm = localtime(&TargetTime); 142: register int minute, hour, dom, month, dow; 143: register user *u; 144: register entry *e; 145: 146: /* make 0-based values out of these so we can use them as indicies 147: */ 148: minute = tm->tm_min -FIRST_MINUTE; 149: hour = tm->tm_hour -FIRST_HOUR; 150: dom = tm->tm_mday -FIRST_DOM; 151: month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; 152: dow = tm->tm_wday -FIRST_DOW; 153: 154: Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n", 155: getpid(), minute, hour, dom, month, dow)) 156: 157: /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the 158: * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* 159: * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this 160: * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre. 161: * like many bizarre things, it's the standard. 162: */ 163: for (u = db->head; u != NULL; u = u->next) { 164: for (e = u->crontab; e != NULL; e = e->next) { 165: Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n", 166: env_get("LOGNAME", e->envp), 167: e->uid, e->gid, e->cmd)) 168: if (bit_test(e->minute, minute) 169: && bit_test(e->hour, hour) 170: && bit_test(e->month, month) 171: && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) 172: ? (bit_test(e->dow,dow) && bit_test(e->dom,dom)) 173: : (bit_test(e->dow,dow) || bit_test(e->dom,dom)) 174: ) 175: ) { 176: job_add(e, u); 177: } 178: } 179: } 180: } 181: 182: 183: /* the task here is to figure out how long it's going to be until :00 of the 184: * following minute and initialize TargetTime to this value. TargetTime 185: * will subsequently slide 60 seconds at a time, with correction applied 186: * implicitly in cron_sleep(). it would be nice to let cron execute in 187: * the "current minute" before going to sleep, but by restarting cron you 188: * could then get it to execute a given minute's jobs more than once. 189: * instead we have the chance of missing a minute's jobs completely, but 190: * that's something sysadmin's know to expect what with crashing computers.. 191: */ 192: static void 193: cron_sync() { 194: register struct tm *tm; 195: 196: TargetTime = time((time_t*)0); 197: tm = localtime(&TargetTime); 198: TargetTime += (60 - tm->tm_sec); 199: } 200: 201: 202: static void 203: cron_sleep() { 204: register int seconds_to_wait; 205: 206: do { 207: seconds_to_wait = (int) (TargetTime - time((time_t*)0)); 208: Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", 209: getpid(), TargetTime, seconds_to_wait)) 210: 211: /* if we intend to sleep, this means that it's finally 212: * time to empty the job queue (execute it). 213: * 214: * if we run any jobs, we'll probably screw up our timing, 215: * so go recompute. 216: * 217: * note that we depend here on the left-to-right nature 218: * of &&, and the short-circuiting. 219: */ 220: } while (seconds_to_wait > 0 && job_runqueue()); 221: 222: while (seconds_to_wait > 0) { 223: Debug(DSCH, ("[%d] sleeping for %d seconds\n", 224: getpid(), seconds_to_wait)) 225: seconds_to_wait = (int) sleep((unsigned int) seconds_to_wait); 226: } 227: } 228: 229: 230: static void 231: sigchld_handler(x) { 232: WAIT_T waiter; 233: PID_T pid; 234: 235: for (;;) { 236: pid = waitpid(-1, &waiter, WNOHANG); 237: switch (pid) { 238: case -1: 239: Debug(DPROC, 240: ("[%d] sigchld...no children\n", getpid())) 241: return; 242: case 0: 243: Debug(DPROC, 244: ("[%d] sigchld...no dead kids\n", getpid())) 245: return; 246: default: 247: Debug(DPROC, 248: ("[%d] sigchld...pid #%d died, stat=%d\n", 249: getpid(), pid, WEXITSTATUS(waiter))) 250: } 251: } 252: } 253: 254: static void 255: sighup_handler(x) { 256: log_close(); 257: } 258: 259: 260: static void 261: parse_args(argc, argv) 262: int argc; 263: char *argv[]; 264: { 265: int argch; 266: 267: while (EOF != (argch = getopt(argc, argv, "x:"))) { 268: switch (argch) { 269: default: 270: usage(); 271: case 'x': 272: if (!set_debug_flags(optarg)) 273: usage(); 274: break; 275: } 276: } 277: }