1 /* Copyright 1988,1990,1993,1994 by Paul Vixie
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
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
18 #if !defined(lint) && !defined(LINT)
19 static const char rcsid[] =
28 #include <sys/signal.h>
30 # include <sys/time.h>
36 static void usage(void),
37 run_reboot_jobs(cron_db *),
38 cron_tick(cron_db *, int),
40 cron_sleep(cron_db *, int),
41 cron_clean(cron_db *),
46 parse_args(int c, char *v[]);
48 static int run_at_secres(cron_db *);
49 static void find_interval_entry(pid_t);
51 static cron_db database;
52 static time_t last_time = 0;
53 static int dst_enabled = 0;
54 static int dont_daemonize = 0;
63 fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] "
64 "[-m mailto] [-n] [-s] [-o] [-x debugflag[,...]]\n");
66 fprintf(stderr, "\ndebugflags: ");
68 for(dflags = DebugFlagNames; *dflags; dflags++) {
69 fprintf(stderr, "%s ", *dflags);
71 fprintf(stderr, "\n");
80 char pidfile[MAX_FNAME];
81 char buf[MAX_TEMPSTR];
84 (void) snprintf(pidfile, sizeof(pidfile), PIDFILE, PIDDIR);
85 pfh = pidfile_open(pidfile, 0600, &otherpid);
87 if (errno == EEXIST) {
88 snprintf(buf, sizeof(buf),
89 "cron already running, pid: %d", otherpid);
91 snprintf(buf, sizeof(buf),
92 "can't open or create %s: %s", pidfile,
95 log_it("CRON", getpid(), "DEATH", buf);
96 errx(ERROR_EXIT, "%s", buf);
106 int secres1, secres2;
109 ProgramName = argv[0];
116 parse_args(argc, argv);
119 (void) signal(SIGCHLD, sigchld_handler);
121 (void) signal(SIGCLD, SIG_IGN);
123 (void) signal(SIGHUP, sighup_handler);
130 setenv("PATH", _PATH_DEFPATH, 1);
133 /* if there are no debug flags turned on, fork as a daemon should.
140 (void) fprintf(stderr, "[%d] cron started\n", getpid());
141 } else if (dont_daemonize == 0) {
142 if (daemon(1, 0) == -1) {
144 log_it("CRON",getpid(),"DEATH","can't become daemon");
149 if (madvise(NULL, 0, MADV_PROTECT) != 0)
150 log_it("CRON", getpid(), "WARNING", "madvise() failed");
153 database.head = NULL;
154 database.tail = NULL;
155 database.mtime = (time_t) 0;
156 load_database(&database);
157 secres1 = secres2 = run_at_secres(&database);
159 run_reboot_jobs(&database);
163 /* if (!(DebugFlags & DTEST)) */
164 # endif /*DEBUGGING*/
165 cron_sleep(&database, secres1);
167 if (secres1 == 0 || runnum % 60 == 0) {
168 load_database(&database);
169 secres2 = run_at_secres(&database);
170 if (secres2 != secres1) {
176 * Going from 1 sec to 60 sec res. If we
177 * are already at minute's boundary, so
178 * let it run, otherwise schedule for the
181 tm = localtime(&TargetTime);
182 if (tm->tm_sec > 0) {
192 cron_tick(&database, secres1);
194 /* sleep 1 or 60 seconds
196 TargetTime += (secres1 != 0) ? 1 : 60;
209 for (u = db->head; u != NULL; u = u->next) {
210 for (e = u->crontab; e != NULL; e = e->next) {
211 if (e->flags & WHEN_REBOOT) {
214 if (e->flags & INTERVAL) {
215 e->lastexit = TargetTime;
219 (void) job_runqueue();
224 cron_tick(cron_db *db, int secres)
226 static struct tm lasttm;
227 static time_t diff = 0, /* time difference in seconds from the last offset change */
228 difflimit = 0; /* end point for the time zone correction */
229 struct tm otztm; /* time in the old time zone */
230 int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow;
231 register struct tm *tm = localtime(&TargetTime);
232 register int second, minute, hour, dom, month, dow;
236 /* make 0-based values out of these so we can use them as indices
238 second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND;
239 minute = tm->tm_min -FIRST_MINUTE;
240 hour = tm->tm_hour -FIRST_HOUR;
241 dom = tm->tm_mday -FIRST_DOM;
242 month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
243 dow = tm->tm_wday -FIRST_DOW;
245 Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n",
246 getpid(), second, minute, hour, dom, month, dow))
248 if (dst_enabled && last_time != 0
249 && TargetTime > last_time /* exclude stepping back */
250 && tm->tm_gmtoff != lasttm.tm_gmtoff ) {
252 diff = tm->tm_gmtoff - lasttm.tm_gmtoff;
254 if ( diff > 0 ) { /* ST->DST */
255 /* mark jobs for an earlier run */
256 difflimit = TargetTime + diff;
257 for (u = db->head; u != NULL; u = u->next) {
258 for (e = u->crontab; e != NULL; e = e->next) {
259 e->flags &= ~NOT_UNTIL;
260 if ( e->lastrun >= TargetTime )
262 /* not include the ends of hourly ranges */
263 if ( e->lastrun < TargetTime - 3600 )
269 } else { /* diff < 0 : DST->ST */
270 /* mark jobs for skipping */
271 difflimit = TargetTime - diff;
272 for (u = db->head; u != NULL; u = u->next) {
273 for (e = u->crontab; e != NULL; e = e->next) {
274 e->flags |= NOT_UNTIL;
282 /* if the time was reset of the end of special zone is reached */
283 if (last_time == 0 || TargetTime >= difflimit) {
284 /* disable the TZ switch checks */
287 for (u = db->head; u != NULL; u = u->next) {
288 for (e = u->crontab; e != NULL; e = e->next) {
289 e->flags &= ~(RUN_AT|NOT_UNTIL);
293 /* get the time in the old time zone */
294 time_t difftime = TargetTime + tm->tm_gmtoff - diff;
295 gmtime_r(&difftime, &otztm);
297 /* make 0-based values out of these so we can use them as indices
299 otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND;
300 otzminute = otztm.tm_min -FIRST_MINUTE;
301 otzhour = otztm.tm_hour -FIRST_HOUR;
302 otzdom = otztm.tm_mday -FIRST_DOM;
303 otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
304 otzdow = otztm.tm_wday -FIRST_DOW;
308 /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
309 * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
310 * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
311 * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
312 * like many bizarre things, it's the standard.
314 for (u = db->head; u != NULL; u = u->next) {
315 for (e = u->crontab; e != NULL; e = e->next) {
316 Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
317 env_get("LOGNAME", e->envp),
318 e->uid, e->gid, e->cmd))
320 if (e->flags & INTERVAL) {
321 if (e->lastexit > 0 &&
322 TargetTime >= e->lastexit + e->interval)
327 if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
328 if (bit_test(e->second, otzsecond)
329 && bit_test(e->minute, otzminute)
330 && bit_test(e->hour, otzhour)
331 && bit_test(e->month, otzmonth)
332 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
333 ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom))
334 : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom))
337 if ( e->flags & RUN_AT ) {
339 e->lastrun = TargetTime;
343 e->flags &= ~NOT_UNTIL;
344 } else if ( e->flags & NOT_UNTIL )
348 if (bit_test(e->second, second)
349 && bit_test(e->minute, minute)
350 && bit_test(e->hour, hour)
351 && bit_test(e->month, month)
352 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
353 ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
354 : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
358 e->lastrun = TargetTime;
364 last_time = TargetTime;
369 /* the task here is to figure out how long it's going to be until :00 of the
370 * following minute and initialize TargetTime to this value. TargetTime
371 * will subsequently slide 60 seconds at a time, with correction applied
372 * implicitly in cron_sleep(). it would be nice to let cron execute in
373 * the "current minute" before going to sleep, but by restarting cron you
374 * could then get it to execute a given minute's jobs more than once.
375 * instead we have the chance of missing a minute's jobs completely, but
376 * that's something sysadmin's know to expect what with crashing computers..
379 cron_sync(int secres) {
382 TargetTime = time((time_t*)0);
386 tm = localtime(&TargetTime);
387 TargetTime += (60 - tm->tm_sec);
392 timespec_subtract(struct timespec *result, struct timespec *x,
396 result->tv_sec -= y->tv_sec;
397 result->tv_nsec -= y->tv_nsec;
398 if (result->tv_nsec < 0) {
400 result->tv_nsec += 1000000000;
405 cron_sleep(cron_db *db, int secres)
409 struct timespec ctime, ttime, stime, remtime;
412 * Loop until we reach the top of the next minute, sleep when possible.
416 clock_gettime(CLOCK_REALTIME, &ctime);
417 ttime.tv_sec = TargetTime;
419 timespec_subtract(&stime, &ttime, &ctime);
422 * If the seconds_to_wait value is insane, jump the cron
425 if (stime.tv_sec < -600 || stime.tv_sec > 600) {
431 seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 :
434 Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
435 getpid(), (long)TargetTime, seconds_to_wait))
438 * If we've run out of wait time or there are no jobs left
442 if (stime.tv_sec < 0)
444 if (job_runqueue() == 0) {
445 Debug(DSCH, ("[%d] sleeping for %d seconds\n",
446 getpid(), seconds_to_wait))
449 rval = nanosleep(&stime, &remtime);
450 if (rval == 0 || errno != EINTR)
452 stime.tv_sec = remtime.tv_sec;
453 stime.tv_nsec = remtime.tv_nsec;
460 /* if the time was changed abruptly, clear the flags related
461 * to the daylight time switch handling to avoid strange effects
473 for (u = db->head; u != NULL; u = u->next) {
474 for (e = u->crontab; e != NULL; e = e->next) {
475 e->flags &= ~(RUN_AT|NOT_UNTIL);
482 sigchld_handler(int x)
489 pid = waitpid(-1, &waiter, WNOHANG);
491 pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
496 ("[%d] sigchld...no children\n", getpid()))
500 ("[%d] sigchld...no dead kids\n", getpid()))
503 find_interval_entry(pid);
505 ("[%d] sigchld...pid #%d died, stat=%d\n",
506 getpid(), pid, WEXITSTATUS(waiter)))
510 #endif /*USE_SIGCHLD*/
514 sighup_handler(int x)
521 parse_args(argc, argv)
528 while ((argch = getopt(argc, argv, "j:J:m:nosx:")) != -1) {
531 Jitter = strtoul(optarg, &endp, 10);
532 if (*optarg == '\0' || *endp != '\0' || Jitter > 60)
534 "bad value for jitter: %s", optarg);
537 RootJitter = strtoul(optarg, &endp, 10);
538 if (*optarg == '\0' || *endp != '\0' || RootJitter > 60)
540 "bad value for root jitter: %s", optarg);
555 if (!set_debug_flags(optarg))
565 run_at_secres(cron_db *db)
570 for (u = db->head; u != NULL; u = u->next) {
571 for (e = u->crontab; e != NULL; e = e->next) {
572 if ((e->flags & (SEC_RES | INTERVAL)) != 0)
580 find_interval_entry(pid_t pid)
585 for (u = database.head; u != NULL; u = u->next) {
586 for (e = u->crontab; e != NULL; e = e->next) {
587 if ((e->flags & INTERVAL) && e->child == pid) {
588 e->lastexit = time(NULL);