1 /* Copyright 1988,1990,1993,1994 by Paul Vixie
6 * Copyright (c) 1997 by Internet Software Consortium
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
13 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
14 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
15 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
18 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
22 #if !defined(lint) && !defined(LINT)
23 static const char rcsid[] =
24 "$Id: cron.c,v 1.3 1998/08/14 00:32:36 vixie Exp $";
32 static void usage(void),
33 run_reboot_jobs(cron_db *),
34 cron_tick(cron_db *, int),
36 cron_sleep(cron_db *, int),
37 cron_clean(cron_db *),
40 parse_args(int c, char *v[]);
42 static int run_at_secres(cron_db *);
43 static void find_interval_entry(pid_t);
45 static cron_db database;
46 static time_t last_time = 0;
47 static int dst_enabled = 0;
48 static int dont_daemonize = 0;
58 fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] "
59 "[-m mailto] [-n] [-s] [-o] [-x debugflag[,...]]\n");
61 fprintf(stderr, "\ndebugflags: ");
63 for (dflags = DebugFlagNames; *dflags; dflags++) {
64 fprintf(stderr, "%s ", *dflags);
66 fprintf(stderr, "\n");
75 const char *pidfile = PIDDIR PIDFILE;
76 char buf[MAX_TEMPSTR];
79 pfh = pidfile_open(pidfile, 0600, &otherpid);
81 if (errno == EEXIST) {
82 snprintf(buf, sizeof(buf),
83 "cron already running, pid: %d", otherpid);
85 snprintf(buf, sizeof(buf),
86 "can't open or create %s: %s", pidfile,
89 log_it("CRON", getpid(), "DEATH", buf);
90 errx(ERROR_EXIT, "%s", buf);
95 main(int argc, char *argv[])
101 ProgramName = argv[0];
108 parse_args(argc, argv);
110 (void) signal(SIGCHLD, sigchld_handler);
111 (void) signal(SIGHUP, sighup_handler);
117 putenv("PATH="_PATH_DEFPATH);
119 /* if there are no debug flags turned on, fork as a daemon should.
126 (void) fprintf(stderr, "[%d] cron started\n", getpid());
127 } else if (dont_daemonize == 0) {
128 if (daemon(1, 0) == -1) {
130 log_it("CRON",getpid(),"DEATH","can't become daemon");
135 if (madvise(NULL, 0, MADV_PROTECT) != 0)
136 log_it("CRON", getpid(), "WARNING", "madvise() failed");
139 database.head = NULL;
140 database.tail = NULL;
141 database.mtime = (time_t) 0;
142 load_database(&database);
143 secres1 = secres2 = run_at_secres(&database);
145 run_reboot_jobs(&database);
149 /* if (!(DebugFlags & DTEST)) */
150 # endif /*DEBUGGING*/
151 cron_sleep(&database, secres1);
153 if (secres1 == 0 || runnum % 60 == 0) {
154 load_database(&database);
155 secres2 = run_at_secres(&database);
156 if (secres2 != secres1) {
162 * Going from 1 sec to 60 sec res. If we
163 * are already at minute's boundary, so
164 * let it run, otherwise schedule for the
167 tm = localtime(&TargetTime);
168 if (tm->tm_sec > 0) {
178 cron_tick(&database, secres1);
180 /* sleep 1 or 60 seconds
182 TargetTime += (secres1 != 0) ? 1 : 60;
188 run_reboot_jobs(cron_db *db)
193 for (u = db->head; u != NULL; u = u->next) {
194 for (e = u->crontab; e != NULL; e = e->next) {
195 if (e->flags & WHEN_REBOOT) {
198 if (e->flags & INTERVAL) {
199 e->lastexit = TargetTime;
203 (void) job_runqueue();
207 cron_tick(cron_db *db, int secres)
209 static struct tm lasttm;
210 /* time difference in seconds from the last offset change */
211 static time_t diff = 0;
212 /* end point for the time zone correction */
213 static time_t difflimit = 0;
214 /* time in the old time zone */
216 int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow;
217 struct tm *tm = localtime(&TargetTime);
218 int second, minute, hour, dom, month, dow;
222 /* make 0-based values out of these so we can use them as indices
224 second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND;
225 minute = tm->tm_min -FIRST_MINUTE;
226 hour = tm->tm_hour -FIRST_HOUR;
227 dom = tm->tm_mday -FIRST_DOM;
228 month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
229 dow = tm->tm_wday -FIRST_DOW;
231 Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n",
232 getpid(), second, minute, hour, dom, month, dow))
234 if (dst_enabled && last_time != 0
235 && TargetTime > last_time /* exclude stepping back */
236 && tm->tm_gmtoff != lasttm.tm_gmtoff ) {
238 diff = tm->tm_gmtoff - lasttm.tm_gmtoff;
240 if ( diff > 0 ) { /* ST->DST */
241 /* mark jobs for an earlier run */
242 difflimit = TargetTime + diff;
243 for (u = db->head; u != NULL; u = u->next) {
244 for (e = u->crontab; e != NULL; e = e->next) {
245 e->flags &= ~NOT_UNTIL;
246 if ( e->lastrun >= TargetTime )
248 /* not include the ends of hourly ranges */
249 if ( e->lastrun < TargetTime - 3600 )
255 } else { /* diff < 0 : DST->ST */
256 /* mark jobs for skipping */
257 difflimit = TargetTime - diff;
258 for (u = db->head; u != NULL; u = u->next) {
259 for (e = u->crontab; e != NULL; e = e->next) {
260 e->flags |= NOT_UNTIL;
268 /* if the time was reset of the end of special zone is reached */
269 if (last_time == 0 || TargetTime >= difflimit) {
270 /* disable the TZ switch checks */
273 for (u = db->head; u != NULL; u = u->next) {
274 for (e = u->crontab; e != NULL; e = e->next) {
275 e->flags &= ~(RUN_AT|NOT_UNTIL);
279 /* get the time in the old time zone */
280 time_t difftime = TargetTime + tm->tm_gmtoff - diff;
281 gmtime_r(&difftime, &otztm);
283 /* make 0-based values out of these so we can use them as indices
285 otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND;
286 otzminute = otztm.tm_min -FIRST_MINUTE;
287 otzhour = otztm.tm_hour -FIRST_HOUR;
288 otzdom = otztm.tm_mday -FIRST_DOM;
289 otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
290 otzdow = otztm.tm_wday -FIRST_DOW;
294 /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
295 * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
296 * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
297 * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
298 * like many bizarre things, it's the standard.
300 for (u = db->head; u != NULL; u = u->next) {
301 for (e = u->crontab; e != NULL; e = e->next) {
302 Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
303 env_get("LOGNAME", e->envp),
304 e->uid, e->gid, e->cmd))
306 if (e->flags & INTERVAL) {
307 if (e->lastexit > 0 &&
308 TargetTime >= e->lastexit + e->interval)
313 if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
314 if (bit_test(e->second, otzsecond) &&
315 bit_test(e->minute, otzminute) &&
316 bit_test(e->hour, otzhour) &&
317 bit_test(e->month, otzmonth) &&
318 ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
319 ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom))
320 : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom))
323 if ( e->flags & RUN_AT ) {
325 e->lastrun = TargetTime;
329 e->flags &= ~NOT_UNTIL;
330 } else if ( e->flags & NOT_UNTIL )
334 if (bit_test(e->second, second) &&
335 bit_test(e->minute, minute) &&
336 bit_test(e->hour, hour) &&
337 bit_test(e->month, month) &&
338 ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
339 ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
340 : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
344 e->lastrun = TargetTime;
350 last_time = TargetTime;
354 /* the task here is to figure out how long it's going to be until :00 of the
355 * following minute and initialize TargetTime to this value. TargetTime
356 * will subsequently slide 60 seconds at a time, with correction applied
357 * implicitly in cron_sleep(). it would be nice to let cron execute in
358 * the "current minute" before going to sleep, but by restarting cron you
359 * could then get it to execute a given minute's jobs more than once.
360 * instead we have the chance of missing a minute's jobs completely, but
361 * that's something sysadmin's know to expect what with crashing computers..
364 cron_sync(int secres) {
367 TargetTime = time((time_t*)0);
371 tm = localtime(&TargetTime);
372 TargetTime += (60 - tm->tm_sec);
377 timespec_subtract(struct timespec *result, struct timespec *x,
381 result->tv_sec -= y->tv_sec;
382 result->tv_nsec -= y->tv_nsec;
383 if (result->tv_nsec < 0) {
385 result->tv_nsec += 1000000000;
390 cron_sleep(cron_db *db, int secres)
394 struct timespec ctime, ttime, stime, remtime;
397 * Loop until we reach the top of the next minute, sleep when possible.
401 clock_gettime(CLOCK_REALTIME, &ctime);
402 ttime.tv_sec = TargetTime;
404 timespec_subtract(&stime, &ttime, &ctime);
407 * If the seconds_to_wait value is insane, jump the cron
410 if (stime.tv_sec < -600 || stime.tv_sec > 600) {
416 seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 :
419 Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
420 getpid(), (long)TargetTime, seconds_to_wait))
423 * If we've run out of wait time or there are no jobs left
427 if (stime.tv_sec < 0)
429 if (job_runqueue() == 0) {
430 Debug(DSCH, ("[%d] sleeping for %d seconds\n",
431 getpid(), seconds_to_wait))
434 rval = nanosleep(&stime, &remtime);
435 if (rval == 0 || errno != EINTR)
437 stime.tv_sec = remtime.tv_sec;
438 stime.tv_nsec = remtime.tv_nsec;
445 /* if the time was changed abruptly, clear the flags related
446 * to the daylight time switch handling to avoid strange effects
450 cron_clean(cron_db *db)
457 for (u = db->head; u != NULL; u = u->next) {
458 for (e = u->crontab; e != NULL; e = e->next) {
459 e->flags &= ~(RUN_AT|NOT_UNTIL);
465 sigchld_handler(int x)
471 pid = waitpid(-1, &waiter, WNOHANG);
475 ("[%d] sigchld...no children\n", getpid()))
479 ("[%d] sigchld...no dead kids\n", getpid()))
482 find_interval_entry(pid);
484 ("[%d] sigchld...pid #%d died, stat=%d\n",
485 getpid(), pid, WEXITSTATUS(waiter)))
491 sighup_handler(int x)
497 parse_args(int argc, char *argv[])
502 while ((argch = getopt(argc, argv, "j:J:m:nosx:")) != -1) {
505 Jitter = strtoul(optarg, &endp, 10);
506 if (*optarg == '\0' || *endp != '\0' || Jitter > 60)
508 "bad value for jitter: %s", optarg);
511 RootJitter = strtoul(optarg, &endp, 10);
512 if (*optarg == '\0' || *endp != '\0' || RootJitter > 60)
514 "bad value for root jitter: %s", optarg);
529 if (!set_debug_flags(optarg))
539 run_at_secres(cron_db *db)
544 for (u = db->head; u != NULL; u = u->next) {
545 for (e = u->crontab; e != NULL; e = e->next) {
546 if ((e->flags & (SEC_RES | INTERVAL)) != 0)
554 find_interval_entry(pid_t pid)
559 for (u = database.head; u != NULL; u = u->next) {
560 for (e = u->crontab; e != NULL; e = e->next) {
561 if ((e->flags & INTERVAL) && e->child == pid) {
562 e->lastexit = time(NULL);