int main(int argc, char *argv[]) { int exitstatus; Pid = getpid(); ProgramName = argv[0]; #if defined(POSIX) setlocale(LC_ALL, ""); #endif #if defined(BSD) setlinebuf(stderr); #endif parse_args(argc, argv); /* sets many globals, opens a file */ set_cron_uid(); set_cron_cwd(); if (!allowed(User)) { warnx("you (%s) are not allowed to use this program", User); log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); exit(ERROR_EXIT); } exitstatus = OK_EXIT; switch (Option) { case opt_list: list_cmd(); break; case opt_delete: delete_cmd(); break; case opt_edit: edit_cmd(); break; case opt_replace: if (replace_cmd() < 0) exitstatus = ERROR_EXIT; break; case opt_unknown: break; } exit(exitstatus); /*NOTREACHED*/ }
int main (int argc, char *argv[]) { cron_db database; char *config_file; /*Name of our configuration file; NULL if none */ struct sigaction my_sigaction; /* We need to put Program_Name in its own storage because later we will * modify argv[0] in an attempt to change the process name. */ ProcessName = argv[0]; ProgramName = strdup (argv[0]); #if HAVE_SETLINEBUF setlinebuf (stdout); setlinebuf (stderr); #endif /*HAVE_SETLINEBUF */ parse_args (argc, argv, &pass_environment, &config_file); read_config (config_file, &allow_only_root, &log_syslog, &allow_file, &deny_file, &crondir, &spool_dir, &log_file, &syscrontab, &lastrun_file, &pidfile, &mailprog, &mailargs); #ifdef USE_SIGCHLD memset (&my_sigaction, 0, sizeof (my_sigaction)); my_sigaction.sa_handler = sigchld_handler; my_sigaction.sa_flags |= SA_NOCLDSTOP | SA_NODEFER; if (sigaction (SIGCHLD, &my_sigaction, NULL)) perror ("sigaction"); #else (void) signal (SIGCHLD, SIG_IGN); #endif memset (&my_sigaction, 0, sizeof (my_sigaction)); my_sigaction.sa_handler = sigterm_handler; if (sigaction (SIGTERM, &my_sigaction, NULL)) perror ("sigaction"); memset (&my_sigaction, 0, sizeof (my_sigaction)); my_sigaction.sa_handler = sighup_handler; if (sigaction (SIGHUP, &my_sigaction, NULL)) perror ("sigaction"); acquire_daemonlock (0); set_cron_uid (); set_cron_cwd (); #if HAVE_SETENV if (!pass_environment) setenv ("PATH", _PATH_DEFPATH, 1); #endif /* if there are no debug flags turned on, fork as a daemon should. */ #if DEBUGGING if (DebugFlags) { #else if (0) { #endif (void) fprintf (stderr, "[%d] cron started\n", getpid ()); } else { switch (fork ()) { case -1: log_it ("CRON", getpid (), "DEATH", "can't fork"); exit (0); break; case 0: /* child process */ log_it ("CRON", getpid (), "STARTUP", "fork ok"); (void) setsid (); break; default: /* parent process should just die */ _exit (0); } } acquire_daemonlock (0); /* initialize waiting for busy disk */ init_diskload (); database.head = NULL; database.tail = NULL; database.mtime = (time_t) 0; Debug (DMISC, ("about to load database")); load_database (&database); Debug (DMISC, ("about to build catch up list")); build_cu_list (&database, &CatchUpList); Debug (DMISC, ("about to run reboot jobs")); run_reboot_jobs (&database); while (TRUE) { cron_sync (); # if DEBUGGING if (!(DebugFlags & DTEST)) # endif /*DEBUGGING*/ cron_sleep (); load_database (&database); /* first catch up any jobs that are on the CatchUpList */ if (CatchUpList && (!jhead)) CatchUpList = run_cu_list (CatchUpList); /* then run the regular jobs for this minute */ cron_tick (&database); } } static void run_reboot_jobs (cron_db * db) { register user *u; register entry *e; for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { if (e->flags & WHEN_REBOOT) { job_add (e, u); } } } (void) job_runqueue (); } static void cron_tick (cron_db * db) { register struct tm *tm = localtime (&TargetTime); register int minute, hour, dom, month, dow; register user *u; register entry *e; /* make 0-based values out of these so we can use them as indicies */ minute = tm->tm_min - FIRST_MINUTE; hour = tm->tm_hour - FIRST_HOUR; dom = tm->tm_mday - FIRST_DOM; month = tm->tm_mon + 1 /* 0..11 -> 1..12 */ - FIRST_MONTH; dow = tm->tm_wday - FIRST_DOW; Debug (DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n", getpid (), minute, hour, dom, month, dow)); /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre. * like many bizarre things, it's the standard. */ for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { Debug (DSCH | DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n", env_get ("LOGNAME", e->envp), e->uid, e->gid, e->cmd)); if (bit_test (e->minute, minute) && bit_test (e->hour, hour) && bit_test (e->month, month) && (((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) ? (bit_test (e->dow, dow) && bit_test (e->dom, dom)) : (bit_test (e->dow, dow) || bit_test (e->dom, dom)))) { job_add (e, u); } } } } /* the task here is to figure out how long it's going to be until :00 of the * following minute and initialize TargetTime to this value. TargetTime * will subsequently slide 60 seconds at a time, with correction applied * implicitly in cron_sleep(). it would be nice to let cron execute in * the "current minute" before going to sleep, but by restarting cron you * could then get it to execute a given minute's jobs more than once. * instead we have the chance of missing a minute's jobs completely, but * that's something sysadmin's know to expect what with crashing computers.. * * Patch from <*****@*****.**>: * Do cron_sync() before each cron_sleep(), to handle changes to the system * time. */ static void cron_sync (void) { register struct tm *tm; TargetTime = time ((time_t *) 0); tm = localtime (&TargetTime); TargetTime += (60 - tm->tm_sec); } static void cron_sleep (void) { register int seconds_to_wait; do { seconds_to_wait = (int) (TargetTime - time ((time_t *) 0)); Debug (DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", getpid (), TargetTime, seconds_to_wait)); /* if we intend to sleep, this means that it's finally * time to empty the job queue (execute it). * * if we run any jobs, we'll probably screw up our timing, * so go recompute. * * note that we depend here on the left-to-right nature * of &&, and the short-circuiting. */ } while (seconds_to_wait > 0 && job_runqueue ()); while (seconds_to_wait > 0) { Debug (DSCH, ("[%d] sleeping for %d seconds\n", getpid (), seconds_to_wait)); seconds_to_wait = (int) sleep ((unsigned int) seconds_to_wait); } } #ifdef USE_SIGCHLD static RETSIGTYPE sigchld_handler (int x) { WAIT_T waiter; pid_t pid; for (;;) { #if 1 pid = waitpid (-1, &waiter, WNOHANG); #else pid = wait3 (&waiter, WNOHANG, (struct rusage *) 0); #endif switch (pid) { case -1: Debug (DPROC, ("[%d] sigchld...no children\n", getpid ())); return; case 0: Debug (DPROC, ("[%d] sigchld...no dead kids\n", getpid ())); return; default: Debug (DPROC, ("[%d] sigchld...pid #%d died, stat=%d\n", getpid (), pid, WEXITSTATUS (waiter))); save_lastrun (CatchUpList); } } }
int main(int argc, char *argv[]) { struct sigaction sact; cron_db database; setprogname(argv[0]); (void)setlocale(LC_ALL, ""); (void)setvbuf(stdout, NULL, _IOLBF, 0); (void)setvbuf(stderr, NULL, _IOLBF, 0); NoFork = 0; parse_args(argc, argv); (void)memset(&sact, 0, sizeof sact); (void)sigemptyset(&sact.sa_mask); sact.sa_flags = 0; #ifdef SA_RESTART sact.sa_flags |= SA_RESTART; #endif sact.sa_handler = sigchld_handler; (void) sigaction(SIGCHLD, &sact, NULL); sact.sa_handler = sighup_handler; (void) sigaction(SIGHUP, &sact, NULL); sact.sa_handler = quit; (void) sigaction(SIGINT, &sact, NULL); (void) sigaction(SIGTERM, &sact, NULL); acquire_daemonlock(0); set_cron_uid(); set_cron_cwd(); if (setenv("PATH", _PATH_DEFPATH, 1) < 0) { log_it("CRON", getpid(), "DEATH", "can't malloc"); exit(1); } /* if there are no debug flags turned on, fork as a daemon should. */ if (DebugFlags) { #if DEBUGGING (void)fprintf(stderr, "[%ld] cron started\n", (long)getpid()); #endif } else if (NoFork == 0) { if (daemon(1, 0)) { log_it("CRON",getpid(),"DEATH","can't fork"); exit(1); } } acquire_daemonlock(0); database.head = NULL; database.tail = NULL; database.mtime = (time_t) 0; load_database(&database); set_time(TRUE); run_reboot_jobs(&database); timeRunning = virtualTime = clockTime; /* * Too many clocks, not enough time (Al. Einstein) * These clocks are in minutes since the epoch, adjusted for timezone. * virtualTime: is the time it *would* be if we woke up * promptly and nobody ever changed the clock. It is * monotonically increasing... unless a timejump happens. * At the top of the loop, all jobs for 'virtualTime' have run. * timeRunning: is the time we last awakened. * clockTime: is the time when set_time was last called. */ for (;;) { int timeDiff; enum timejump wakeupKind; /* ... wait for the time (in minutes) to change ... */ do { cron_sleep(timeRunning + 1); set_time(FALSE); } while (clockTime == timeRunning); timeRunning = clockTime; /* * Calculate how the current time differs from our virtual * clock. Classify the change into one of 4 cases. */ timeDiff = timeRunning - virtualTime; /* shortcut for the most common case */ if (timeDiff == 1) { virtualTime = timeRunning; find_jobs(virtualTime, &database, TRUE, TRUE); } else { if (timeDiff > (3*MINUTE_COUNT) || timeDiff < -(3*MINUTE_COUNT)) wakeupKind = large; else if (timeDiff > 5) wakeupKind = medium; else if (timeDiff > 0) wakeupKind = small; else wakeupKind = negative; switch (wakeupKind) { case small: /* * case 1: timeDiff is a small positive number * (wokeup late) run jobs for each virtual * minute until caught up. */ Debug(DSCH, ("[%ld], normal case %d minutes to go\n", (long)getpid(), timeDiff)); do { if (job_runqueue()) (void)sleep(10); virtualTime++; find_jobs(virtualTime, &database, TRUE, TRUE); } while (virtualTime < timeRunning); break; case medium: /* * case 2: timeDiff is a medium-sized positive * number, for example because we went to DST * run wildcard jobs once, then run any * fixed-time jobs that would otherwise be * skipped if we use up our minute (possible, * if there are a lot of jobs to run) go * around the loop again so that wildcard jobs * have a chance to run, and we do our * housekeeping. */ Debug(DSCH, ("[%ld], DST begins %d minutes to go\n", (long)getpid(), timeDiff)); /* run wildcard jobs for current minute */ find_jobs(timeRunning, &database, TRUE, FALSE); /* run fixed-time jobs for each minute missed */ do { if (job_runqueue()) (void)sleep(10); virtualTime++; find_jobs(virtualTime, &database, FALSE, TRUE); set_time(FALSE); } while (virtualTime < timeRunning && clockTime == timeRunning); break; case negative: /* * case 3: timeDiff is a small or medium-sized * negative num, eg. because of DST ending. * Just run the wildcard jobs. The fixed-time * jobs probably have already run, and should * not be repeated. Virtual time does not * change until we are caught up. */ Debug(DSCH, ("[%ld], DST ends %d minutes to go\n", (long)getpid(), timeDiff)); find_jobs(timeRunning, &database, TRUE, FALSE); break; default: /* * other: time has changed a *lot*, * jump virtual time, and run everything */ Debug(DSCH, ("[%ld], clock jumped\n", (long)getpid())); virtualTime = timeRunning; find_jobs(timeRunning, &database, TRUE, TRUE); } } /* Jobs to be run (if any) are loaded; clear the queue. */ (void)job_runqueue(); /* Check to see if we received a signal while running jobs. */ if (got_sighup) { got_sighup = 0; log_close(); } if (got_sigchld) { got_sigchld = 0; sigchld_reaper(); } load_database(&database); } }