/* * helper function to wait on process exit or background the job */ void queue_job(job_t *job) { siginfo_t si; if (job->bg) { job->state = RUNNING; job_add(&root, job); } else { fg = job; si.si_pid = 0; waitid(P_PID, job->pid, &si, WEXITED|WSTOPPED); tcsetpgrp(0, s_pgid); if (si.si_pid != 0) { switch (si.si_code) { case CLD_EXITED: job_rm(&root, fg); job_free(&fg); break; case CLD_STOPPED: job->state = STOPPED; job_add(&root, fg); fg = NULL; break; } } } }
static void run_reboot_jobs(cron_db *db) { user *u; 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, StartTime); } } (void) job_runqueue(); }
void* master_thread_routine(int *accept_fd) { int fd; while(1) { fd = server_accept(*accept_fd); if(fd != -1) { pthread_mutex_lock(&mutex); job_add(fd); pthread_mutex_unlock(&mutex); printf("master: added a job\n"); pthread_cond_signal(&cond); printf("master: signaled others\n"); } } return NULL; }
int cmd_if_shell_exec(struct cmd *self, struct cmd_ctx *ctx) { struct cmd_target_data *data = self->data; struct cmd_if_shell_data *cdata; struct job *job; cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(data->arg2); memcpy(&cdata->ctx, ctx, sizeof cdata->ctx); if (ctx->cmdclient != NULL) ctx->cmdclient->references++; if (ctx->curclient != NULL) ctx->curclient->references++; job = job_add(NULL, 0, NULL, data->arg, cmd_if_shell_callback, cmd_if_shell_free, cdata); job_run(job); return (1); /* don't let client exit */ }
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) { time_t now; /* Initialise route, runq and job classes */ now = time(NULL); route_init(NULL, 0); route_register(&rt_filea_method); route_register(&rt_fileov_method); route_register(&rt_stdin_method); route_register(&rt_stdout_method); route_register(&rt_stderr_method); if ( ! elog_init(1, "job test", NULL)) elog_die(FATAL, "didn't initialise elog\n"); sig_init(); callback_init(); runq_init(now); meth_init(); job_init(); /* Test should fail due to incorrect method */ elog_printf(DEBUG, "Expect a complaint! -> "); if (job_add(5, 5, 0, 1, "test1a1", "internal_test", "stdout", "stderr", 100, NULL, "echo \"Hello, world\"") != -1) { elog_die(FATAL, "[1a] Shouldn't be able to add\n"); } /* Single test in five seconds, never to run */ if (job_add(5, 5, 0, 1, "test1a2", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1a] Can't add\n"); } /* Attention: some white box testing */ itree_first(runq_event); if (itree_getkey(runq_event) != now+5) { elog_die(FATAL, "[1a] Queued at an incorrect time\n"); } job_clear(); if (!itree_empty(runq_event) || !itree_empty(runq_tab)) { elog_die(FATAL, "[1a] Trees not emptied. runq_events=%d, runq_tab=%d\n", itree_n(runq_event), itree_n(runq_tab)); } now = time(NULL); /* Two tests both in five seconds, never to run */ if (job_add(5, 5, 0, 1, "test1b1", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1b] Can't add first\n"); } if (job_add(5, 5, 0, 1, "test1b2", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1b] Can't add second\n"); } itree_first(runq_event); if (itree_getkey(runq_event) != now+5) { elog_die(FATAL, "[1b] First queued at an incorrect time\n"); } itree_next(runq_event); if (itree_getkey(runq_event) != now+5) { elog_die(FATAL, "[1b] Second queued at an incorrect time\n"); } job_clear(); if (!itree_empty(runq_event) || !itree_empty(runq_tab)) { elog_die(FATAL, "[1b] Trees not emptied. runq_events=%d, runq_tab=%d\n", itree_n(runq_event), itree_n(runq_tab)); } now = time(NULL); /* Two tests one in five seconds, the other in six, never to run */ if (job_add(6, 6, 0, 1, "test1c1", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1c] Can't add first\n"); } if (job_add(now+5, 5, 0, 1, "test1c2", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1c] Can't add second\n"); } itree_first(runq_event); if (itree_getkey(runq_event) != now+5) { elog_die(FATAL, "[1c] First queued at an incorrect time\n"); } itree_next(runq_event); if (itree_getkey(runq_event) != now+6) { elog_die(FATAL, "[1c] Second queued at an incorrect time\n"); } job_clear(); if (!itree_empty(runq_event) || !itree_empty(runq_tab)) { elog_die(FATAL, "[1c] Trees not emptied. runq_events=%d, runq_tab=%d\n", itree_n(runq_event), itree_n(runq_tab)); } now = time(NULL); /* Continuous single test supposed to start two seconds ago, * next run in three; never to run */ if (job_add(-2, 5, 0, 0, "test1d1", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1d] Can't add\n"); } itree_first(runq_event); if (itree_getkey(runq_event) != now+3) { elog_die(FATAL, "[1d] Event queued at an incorrect time: bad=%d good=%ld\n", itree_getkey(runq_event), now+3); } job_clear(); if (runq_nsched() > 0) { elog_die(FATAL, "[1d] Still active work scheduled. runq_events=%d, " "runq_tab=%d runq_nsched()=%d\n", itree_n(runq_event), itree_n(runq_tab), runq_nsched()); runq_dump(); } now = time(NULL); /* Two continous tests, starting two seconds ago, next next run in four; * never to run */ if (job_add(-2, 6, 0, 0, "test1e1", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1e] Can't add first\n"); } if (job_add(-3, 5, 0, 0, "test1e2", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1e] Can't add second\n"); } itree_first(runq_event); while (((struct runq_work*) itree_get(runq_event))->expired) itree_next(runq_event); if (itree_getkey(runq_event) != now+2) { elog_die(FATAL, "[1e] First queued at an incorrect time\n"); } itree_next(runq_event); while (((struct runq_work*) itree_get(runq_event))->expired) itree_next(runq_event); if (itree_getkey(runq_event) != now+4) { elog_die(FATAL, "[1e] Second queued at an incorrect time\n"); } job_clear(); if (runq_nsched() > 0) { elog_die(FATAL, "[1e] Still active work scheduled. runq_events=%d, " "runq_tab=%d runq_nsched()=%d\n", itree_n(runq_event), itree_n(runq_tab), runq_nsched()); runq_dump(); } now = time(NULL); /* Two 5 run jobs, scheduled to start 10 seconds ago, with periods * of 5 and 6 seconds; never to run */ if (job_add(-10, 6, 0, 5, "test1f1", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1f] Can't add first\n"); } if (job_add(-10, 5, 0, 5, "test1f2", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1f] Can't add second\n"); } itree_first(runq_event); while (((struct runq_work*) itree_get(runq_event))->expired) itree_next(runq_event); if (itree_getkey(runq_event) != now+2) { elog_die(FATAL, "[1f] First queued at an incorrect time\n"); } itree_next(runq_event); while (((struct runq_work*) itree_get(runq_event))->expired) itree_next(runq_event); if (itree_getkey(runq_event) != now+5) { elog_die(FATAL, "[1f] Second queued at an incorrect time\n"); } job_clear(); if (runq_nsched() > 0) { elog_die(FATAL, "[1f] Still active work scheduled. runq_events=%d, " "runq_tab=%d runq_nsched()=%d\n", itree_n(runq_event), itree_n(runq_tab), runq_nsched()); runq_dump(); } now = time(NULL); /* Two 5 run jobs, scheduled to start 100 seconds ago, with periods * of 5 and 6 seconds; they should never be scheduled */ if (job_add(-100, 6, 0, 5, "test1g1", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1g] Can't add first\n"); } if (job_add(-100, 5, 0, 5, "test1g2", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1g] Can't add second\n"); } if (runq_nsched() > 0) { elog_die(FATAL, "[1g] Still active work scheduled. runq_events=%d, " "runq_tab=%d runq_nsched()=%d\n", itree_n(runq_event), itree_n(runq_tab), runq_nsched()); runq_dump(); } job_clear(); now = time(NULL); /* Two five run tests, starting at different times in the past, * five runs each wittth different periods; they should both * run now */ if (job_add(-24, 6, 0, 5, "test1h1", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1h] Can't add first\n"); } if (job_add(-20, 5, 0, 5, "test1h2", "internal_test", "stdout", "stderr", 100, "exec", "echo \"Hello, world\"") == -1) { elog_die(FATAL, "[1h] Can't add second\n"); } if (runq_nsched() != 2) { elog_die(FATAL, "[1h] Two jobs should be scheduled not %d\n", runq_nsched()); runq_dump(); } sig_on(); sleep(6); /* let it run */ sleep(1); /* let it run */ sleep(1); /* let it run */ sleep(1); /* let it run */ sig_off(); if (runq_nsched() > 0) { elog_die(FATAL, "[1h] Still active work scheduled. runq_events=%d, " "runq_tab=%d runq_nsched()=%d\n", itree_n(runq_event), itree_n(runq_tab), runq_nsched()); runq_dump(); } job_clear(); #if 0 /* check all tables/lists are empty */ if (!itree_empty(runq_event) || !itree_empty(runq_tab)) { elog_die(FATAL, "[1i] Still entries in tables. runq_events=%d, " "runq_tab=%d runq_nsched()=%d\n", itree_n(runq_event), itree_n(runq_tab), runq_nsched()); runq_dump(); } #endif job_fini(); meth_fini(); runq_fini(); elog_fini(); route_fini(); callback_fini(); printf("%s: tests finished\n", argv[0]); exit(0); }
/* * Read in job definitions from the pseudo-url and add them to the * job class. If there is a problem with a job definition, it will not * be added to the job class, but other jobs will continue to be processed. * * File format: one line per job. magic string of 'job 1' at top * Fields/columns match the job_add() spec and are: * 1. start time (seconds) * 2. interval (currently seconds, but ought to have weekly or monthly based * intervals as well) * 3. phase (int; job order at each scheduled point) * 4. count (int; how many times to run; 0=indefinately) * 5. key (unique job id) * 6. origin (char *; no spaces) * 7. result (pseudo-url; where to send results) * 8. errors (pseudo-url; where to send errors) * 9. keep (number of results to keep in timestore or tablestore) * 10.method (char *; string representation of method) * 11.command (char *; command for method to run) * * The result url, error url and command are parsed for tokens of the * form %x, where x is a single letter. These are expanded into context * specific strings. Currently, %h is hostname and %j is jobname. See * route_expand() for more details. * * Returns the number of jobs added, or -1 for a major problem */ int job_loadroute(char *purl /* p-url location of file */ ) { long start, interval, phase, count, keep; char *key, *origin, *result, *error, *method, *command; char *endint; ITREE *jobdefs, *job; int r, njobdefs, jobsadded = 0; char key_t[60], result_t[256], error_t[256], command_t[1024]; /* read jobs into parse structure */ njobdefs = util_parseroute(purl, " \t", "job 1", &jobdefs); if (njobdefs < 1) return -1; util_parsedump(jobdefs); itree_traverse(jobdefs) { job = itree_get(jobdefs); /* check the size of each row */ if (itree_n(job) != 11) { elog_startprintf(ERROR, "%s row %d has %d fields, want 11 (", purl, itree_getkey(jobdefs)+1, itree_n(job) ); itree_traverse(job) elog_contprintf(ERROR, "%s ", (char *) itree_get(job)); elog_endprintf(ERROR, ")"); continue; } /* * validate each column * we dont need to strdup() any space because job_add() will * do that for itself */ /* column 1: start time */ itree_find(job, 0); start = strtol( itree_get(job), &endint, 10 ); if (start == LONG_MIN || start == LONG_MAX || itree_get(job) == endint) { elog_printf(ERROR, "%s row %d start time (column 1) is " "incorrect: '%s'; skipping", purl, itree_getkey(jobdefs), itree_get(job)); continue; } /* column 2: interval */ itree_find(job, 1); interval = strtol( itree_get(job), &endint, 10 ); if (interval == LONG_MIN || interval == LONG_MAX || itree_get(job) == endint) { elog_printf(ERROR, "%s row %d interval (column 2) is " "incorrect: '%s'; skipping", purl, itree_getkey(jobdefs), itree_get(job)); continue; } /* column 3: phase */ itree_find(job, 2); phase = strtol( itree_get(job), &endint, 10 ); if (phase == LONG_MIN || phase == LONG_MAX || itree_get(job) == endint) { elog_printf(ERROR, "%s row %d phase (column 3) is incorrect: " "'%s'; skipping", purl, itree_getkey(jobdefs), itree_get(job)); continue; } /* column 4: count */ itree_find(job, 3); count = strtol( itree_get(job), &endint, 10 ); if (count == LONG_MIN || count == LONG_MAX || itree_get(job) == endint) { elog_printf(ERROR, "%s row %d count (column 4) is incorrect: " "'%s'; skipping", purl, itree_getkey(jobdefs), itree_get(job)); continue; } /* column 5: key */ itree_find(job, 4); key = itree_get(job); if (route_expand(key_t, key, key, interval) != -1) key = key_t; /* column 6: origin */ itree_find(job, 5); origin = itree_get(job); /* column 7: result */ itree_find(job, 6); result = itree_get(job); if (route_expand(result_t, result, key, interval) != -1) result = result_t; /* column 8: error */ itree_find(job, 7); error = itree_get(job); if (route_expand(error_t, error, key, interval) != -1) error = error_t; /* column 9: keep */ itree_find(job, 8); keep = strtol( itree_get(job), &endint, 10 ); if (keep == LONG_MIN || keep == LONG_MAX || itree_get(job) == endint) { elog_printf(ERROR, "%s row %d keep (column 9) is incorrect: " "'%s'; skipping", purl, itree_getkey(jobdefs), itree_get(job)); continue; } /* column 10: method */ itree_find(job, 9); method = itree_get(job); /* column 11: command */ itree_find(job, 10); command = itree_get(job); if (route_expand(command_t, command, key, interval) != -1) command = command_t; elog_printf(DEBUG, "%s row %d read: start=%ld interval=%ld phase=%ld " "count=%ld key=%s origin=%s result=%s error=%s keep=%ld " "method=%s command=%s", purl, itree_getkey(jobdefs), start, interval, phase, count, key, origin, result, error, keep, method, command); if (meth_check(method)) { elog_printf(ERROR, "%s row %d method %s not loaded; skipping", purl, itree_getkey(jobdefs), method); continue; } /* insert into job class */ r = job_add(start, interval, phase, count, key, origin, result, error, keep, method, command); if (r == -1) elog_printf(ERROR, "%s row %d unable to add job; skipping", purl, itree_getkey(jobdefs)); else jobsadded++; } util_freeparse(jobdefs); return jobsadded; }
static void find_jobs(time_t vtime, cron_db *db, int doWild, int doNonWild) { time_t virtualSecond = vtime * SECONDS_PER_MINUTE; struct tm *tm; int minute, hour, dom, month, dow; user *u; entry *e; #ifndef CRON_LOCALTIME char *orig_tz, *job_tz; #define maketime(tz1, tz2) do { \ char *t = tz1; \ if (t != NULL && *t != '\0') \ setenv("TZ", t, 1); \ else if ((tz2) != NULL) \ setenv("TZ", (tz2), 1); \ else \ unsetenv("TZ"); \ tzset(); \ tm = localtime(&virtualSecond); \ 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; \ } while (/*CONSTCOND*/0) orig_tz = getenv("TZ"); maketime(NULL, orig_tz); #else tm = gmtime(&virtualSecond); #endif Debug(DSCH, ("[%ld] tick(%d,%d,%d,%d,%d) %s %s\n", (long)getpid(), minute, hour, dom, month, dow, doWild?" ":"No wildcard",doNonWild?" ":"Wildcard only")); /* 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) { #ifndef CRON_LOCALTIME job_tz = env_get("CRON_TZ", e->envp); maketime(job_tz, orig_tz); #else #define job_tz "N/A" #define orig_tz "N/A" #endif Debug(DSCH|DEXT, ("user [%s:%ld:%ld:...] " "[jobtz=%s, origtz=%s] " "tick(%s), cmd=\"%s\"\n", e->pwd->pw_name, (long)e->pwd->pw_uid, (long)e->pwd->pw_gid, job_tz, orig_tz, tick(e), 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)) ) ) { if ((doNonWild && !(e->flags & (MIN_STAR|HR_STAR))) || (doWild && (e->flags & (MIN_STAR|HR_STAR)))) job_add(e, u, StartTime); } } } #ifndef CRON_LOCALTIME if (orig_tz != NULL) setenv("TZ", orig_tz, 1); else unsetenv("TZ"); #endif }