xref: /freebsd/usr.sbin/cron/cron/cron.c (revision d6b92ffa)
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(LINT)
19 static const char rcsid[] =
20   "$FreeBSD$";
21 #endif
22 
23 #define	MAIN_PROGRAM
24 
25 
26 #include "cron.h"
27 #include <sys/mman.h>
28 #include <sys/signal.h>
29 #if SYS_TIME_H
30 # include <sys/time.h>
31 #else
32 # include <time.h>
33 #endif
34 
35 
36 static	void	usage(void),
37 		run_reboot_jobs(cron_db *),
38 		cron_tick(cron_db *, int),
39 		cron_sync(int),
40 		cron_sleep(cron_db *, int),
41 		cron_clean(cron_db *),
42 #ifdef USE_SIGCHLD
43 		sigchld_handler(int),
44 #endif
45 		sighup_handler(int),
46 		parse_args(int c, char *v[]);
47 
48 static int	run_at_secres(cron_db *);
49 
50 static time_t	last_time = 0;
51 static int	dst_enabled = 0;
52 static int	dont_daemonize = 0;
53 struct pidfh *pfh;
54 
55 static void
56 usage() {
57 #if DEBUGGING
58     char **dflags;
59 #endif
60 
61 	fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] "
62 			"[-m mailto] [-n] [-s] [-o] [-x debugflag[,...]]\n");
63 #if DEBUGGING
64 	fprintf(stderr, "\ndebugflags: ");
65 
66         for(dflags = DebugFlagNames; *dflags; dflags++) {
67 		fprintf(stderr, "%s ", *dflags);
68 	}
69         fprintf(stderr, "\n");
70 #endif
71 
72 	exit(ERROR_EXIT);
73 }
74 
75 static void
76 open_pidfile(void)
77 {
78 	char	pidfile[MAX_FNAME];
79 	char	buf[MAX_TEMPSTR];
80 	int	otherpid;
81 
82 	(void) snprintf(pidfile, sizeof(pidfile), PIDFILE, PIDDIR);
83 	pfh = pidfile_open(pidfile, 0600, &otherpid);
84 	if (pfh == NULL) {
85 		if (errno == EEXIST) {
86 			snprintf(buf, sizeof(buf),
87 			    "cron already running, pid: %d", otherpid);
88 		} else {
89 			snprintf(buf, sizeof(buf),
90 			    "can't open or create %s: %s", pidfile,
91 			    strerror(errno));
92 		}
93 		log_it("CRON", getpid(), "DEATH", buf);
94 		errx(ERROR_EXIT, "%s", buf);
95 	}
96 }
97 
98 int
99 main(argc, argv)
100 	int	argc;
101 	char	*argv[];
102 {
103 	cron_db	database;
104 	int runnum;
105 	int secres1, secres2;
106 	struct tm *tm;
107 
108 	ProgramName = argv[0];
109 
110 #if defined(BSD)
111 	setlinebuf(stdout);
112 	setlinebuf(stderr);
113 #endif
114 
115 	parse_args(argc, argv);
116 
117 #ifdef USE_SIGCHLD
118 	(void) signal(SIGCHLD, sigchld_handler);
119 #else
120 	(void) signal(SIGCLD, SIG_IGN);
121 #endif
122 	(void) signal(SIGHUP, sighup_handler);
123 
124 	open_pidfile();
125 	set_cron_uid();
126 	set_cron_cwd();
127 
128 #if defined(POSIX)
129 	setenv("PATH", _PATH_DEFPATH, 1);
130 #endif
131 
132 	/* if there are no debug flags turned on, fork as a daemon should.
133 	 */
134 # if DEBUGGING
135 	if (DebugFlags) {
136 # else
137 	if (0) {
138 # endif
139 		(void) fprintf(stderr, "[%d] cron started\n", getpid());
140 	} else if (dont_daemonize == 0) {
141 		if (daemon(1, 0) == -1) {
142 			pidfile_remove(pfh);
143 			log_it("CRON",getpid(),"DEATH","can't become daemon");
144 			exit(0);
145 		}
146 	}
147 
148 	if (madvise(NULL, 0, MADV_PROTECT) != 0)
149 		log_it("CRON", getpid(), "WARNING", "madvise() failed");
150 
151 	pidfile_write(pfh);
152 	database.head = NULL;
153 	database.tail = NULL;
154 	database.mtime = (time_t) 0;
155 	load_database(&database);
156 	secres1 = secres2 = run_at_secres(&database);
157 	run_reboot_jobs(&database);
158 	cron_sync(secres1);
159 	runnum = 0;
160 	while (TRUE) {
161 # if DEBUGGING
162 	    /* if (!(DebugFlags & DTEST)) */
163 # endif /*DEBUGGING*/
164 			cron_sleep(&database, secres1);
165 
166 		if (secres1 == 0 || runnum % 60 == 0) {
167 			load_database(&database);
168 			secres2 = run_at_secres(&database);
169 			if (secres2 != secres1) {
170 				secres1 = secres2;
171 				if (secres1 != 0) {
172 					runnum = 0;
173 				} else {
174 					/*
175 					 * Going from 1 sec to 60 sec res. If we
176 					 * are already at minute's boundary, so
177 					 * let it run, otherwise schedule for the
178 					 * next minute.
179 					 */
180 					tm = localtime(&TargetTime);
181 					if (tm->tm_sec > 0)  {
182 						cron_sync(secres2);
183 						continue;
184 					}
185 				}
186 			}
187 		}
188 
189 		/* do this iteration
190 		 */
191 		cron_tick(&database, secres1);
192 
193 		/* sleep 1 or 60 seconds
194 		 */
195 		TargetTime += (secres1 != 0) ? 1 : 60;
196 		runnum += 1;
197 	}
198 }
199 
200 
201 static void
202 run_reboot_jobs(db)
203 	cron_db *db;
204 {
205 	register user		*u;
206 	register entry		*e;
207 
208 	for (u = db->head;  u != NULL;  u = u->next) {
209 		for (e = u->crontab;  e != NULL;  e = e->next) {
210 			if (e->flags & WHEN_REBOOT) {
211 				job_add(e, u);
212 			}
213 		}
214 	}
215 	(void) job_runqueue();
216 }
217 
218 
219 static void
220 cron_tick(cron_db *db, int secres)
221 {
222 	static struct tm	lasttm;
223 	static time_t	diff = 0, /* time difference in seconds from the last offset change */
224 		difflimit = 0; /* end point for the time zone correction */
225 	struct tm	otztm; /* time in the old time zone */
226 	int		otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow;
227  	register struct tm	*tm = localtime(&TargetTime);
228 	register int		second, minute, hour, dom, month, dow;
229 	register user		*u;
230 	register entry		*e;
231 
232 	/* make 0-based values out of these so we can use them as indices
233 	 */
234 	second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND;
235 	minute = tm->tm_min -FIRST_MINUTE;
236 	hour = tm->tm_hour -FIRST_HOUR;
237 	dom = tm->tm_mday -FIRST_DOM;
238 	month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
239 	dow = tm->tm_wday -FIRST_DOW;
240 
241 	Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n",
242 		getpid(), second, minute, hour, dom, month, dow))
243 
244 	if (dst_enabled && last_time != 0
245 	&& TargetTime > last_time /* exclude stepping back */
246 	&& tm->tm_gmtoff != lasttm.tm_gmtoff ) {
247 
248 		diff = tm->tm_gmtoff - lasttm.tm_gmtoff;
249 
250 		if ( diff > 0 ) { /* ST->DST */
251 			/* mark jobs for an earlier run */
252 			difflimit = TargetTime + diff;
253 			for (u = db->head;  u != NULL;  u = u->next) {
254 				for (e = u->crontab;  e != NULL;  e = e->next) {
255 					e->flags &= ~NOT_UNTIL;
256 					if ( e->lastrun >= TargetTime )
257 						e->lastrun = 0;
258 					/* not include the ends of hourly ranges */
259 					if ( e->lastrun < TargetTime - 3600 )
260 						e->flags |= RUN_AT;
261 					else
262 						e->flags &= ~RUN_AT;
263 				}
264 			}
265 		} else { /* diff < 0 : DST->ST */
266 			/* mark jobs for skipping */
267 			difflimit = TargetTime - diff;
268 			for (u = db->head;  u != NULL;  u = u->next) {
269 				for (e = u->crontab;  e != NULL;  e = e->next) {
270 					e->flags |= NOT_UNTIL;
271 					e->flags &= ~RUN_AT;
272 				}
273 			}
274 		}
275 	}
276 
277 	if (diff != 0) {
278 		/* if the time was reset of the end of special zone is reached */
279 		if (last_time == 0 || TargetTime >= difflimit) {
280 			/* disable the TZ switch checks */
281 			diff = 0;
282 			difflimit = 0;
283 			for (u = db->head;  u != NULL;  u = u->next) {
284 				for (e = u->crontab;  e != NULL;  e = e->next) {
285 					e->flags &= ~(RUN_AT|NOT_UNTIL);
286 				}
287 			}
288 		} else {
289 			/* get the time in the old time zone */
290 			time_t difftime = TargetTime + tm->tm_gmtoff - diff;
291 			gmtime_r(&difftime, &otztm);
292 
293 			/* make 0-based values out of these so we can use them as indices
294 			 */
295 			otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND;
296 			otzminute = otztm.tm_min -FIRST_MINUTE;
297 			otzhour = otztm.tm_hour -FIRST_HOUR;
298 			otzdom = otztm.tm_mday -FIRST_DOM;
299 			otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
300 			otzdow = otztm.tm_wday -FIRST_DOW;
301 		}
302 	}
303 
304 	/* the dom/dow situation is odd.  '* * 1,15 * Sun' will run on the
305 	 * first and fifteenth AND every Sunday;  '* * * * Sun' will run *only*
306 	 * on Sundays;  '* * 1,15 * *' will run *only* the 1st and 15th.  this
307 	 * is why we keep 'e->dow_star' and 'e->dom_star'.  yes, it's bizarre.
308 	 * like many bizarre things, it's the standard.
309 	 */
310 	for (u = db->head;  u != NULL;  u = u->next) {
311 		for (e = u->crontab;  e != NULL;  e = e->next) {
312 			Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
313 					  env_get("LOGNAME", e->envp),
314 					  e->uid, e->gid, e->cmd))
315 
316 			if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
317 				if (bit_test(e->second, otzsecond)
318 				 && bit_test(e->minute, otzminute)
319 				 && bit_test(e->hour, otzhour)
320 				 && bit_test(e->month, otzmonth)
321 				 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
322 					  ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom))
323 					  : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom))
324 					)
325 				   ) {
326 					if ( e->flags & RUN_AT ) {
327 						e->flags &= ~RUN_AT;
328 						e->lastrun = TargetTime;
329 						job_add(e, u);
330 						continue;
331 					} else
332 						e->flags &= ~NOT_UNTIL;
333 				} else if ( e->flags & NOT_UNTIL )
334 					continue;
335 			}
336 
337 			if (bit_test(e->second, second)
338 			 && bit_test(e->minute, minute)
339 			 && bit_test(e->hour, hour)
340 			 && bit_test(e->month, month)
341 			 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
342 			      ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
343 			      : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
344 			    )
345 			   ) {
346 				e->flags &= ~RUN_AT;
347 				e->lastrun = TargetTime;
348 				job_add(e, u);
349 			}
350 		}
351 	}
352 
353 	last_time = TargetTime;
354 	lasttm = *tm;
355 }
356 
357 
358 /* the task here is to figure out how long it's going to be until :00 of the
359  * following minute and initialize TargetTime to this value.  TargetTime
360  * will subsequently slide 60 seconds at a time, with correction applied
361  * implicitly in cron_sleep().  it would be nice to let cron execute in
362  * the "current minute" before going to sleep, but by restarting cron you
363  * could then get it to execute a given minute's jobs more than once.
364  * instead we have the chance of missing a minute's jobs completely, but
365  * that's something sysadmin's know to expect what with crashing computers..
366  */
367 static void
368 cron_sync(int secres) {
369  	struct tm *tm;
370 
371 	TargetTime = time((time_t*)0);
372 	if (secres != 0) {
373 		TargetTime += 1;
374 	} else {
375 		tm = localtime(&TargetTime);
376 		TargetTime += (60 - tm->tm_sec);
377 	}
378 }
379 
380 static void
381 timespec_subtract(struct timespec *result, struct timespec *x,
382     struct timespec *y)
383 {
384 	*result = *x;
385 	result->tv_sec -= y->tv_sec;
386 	result->tv_nsec -= y->tv_nsec;
387 	if (result->tv_nsec < 0) {
388 		result->tv_sec--;
389 		result->tv_nsec += 1000000000;
390 	}
391 }
392 
393 static void
394 cron_sleep(cron_db *db, int secres)
395 {
396 	int seconds_to_wait;
397 	int rval;
398 	struct timespec ctime, ttime, stime, remtime;
399 
400 	/*
401 	 * Loop until we reach the top of the next minute, sleep when possible.
402 	 */
403 
404 	for (;;) {
405 		clock_gettime(CLOCK_REALTIME, &ctime);
406 		ttime.tv_sec = TargetTime;
407 		ttime.tv_nsec = 0;
408 		timespec_subtract(&stime, &ttime, &ctime);
409 
410 		/*
411 		 * If the seconds_to_wait value is insane, jump the cron
412 		 */
413 
414 		if (stime.tv_sec < -600 || stime.tv_sec > 600) {
415 			cron_clean(db);
416 			cron_sync(secres);
417 			continue;
418 		}
419 
420 		seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 :
421 		    stime.tv_sec;
422 
423 		Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
424 			getpid(), (long)TargetTime, seconds_to_wait))
425 
426 		/*
427 		 * If we've run out of wait time or there are no jobs left
428 		 * to run, break
429 		 */
430 
431 		if (stime.tv_sec < 0)
432 			break;
433 		if (job_runqueue() == 0) {
434 			Debug(DSCH, ("[%d] sleeping for %d seconds\n",
435 				getpid(), seconds_to_wait))
436 
437 			for (;;) {
438 				rval = nanosleep(&stime, &remtime);
439 				if (rval == 0 || errno != EINTR)
440 					break;
441 				stime.tv_sec = remtime.tv_sec;
442 				stime.tv_nsec = remtime.tv_nsec;
443 			}
444 		}
445 	}
446 }
447 
448 
449 /* if the time was changed abruptly, clear the flags related
450  * to the daylight time switch handling to avoid strange effects
451  */
452 
453 static void
454 cron_clean(db)
455 	cron_db	*db;
456 {
457 	user		*u;
458 	entry		*e;
459 
460 	last_time = 0;
461 
462 	for (u = db->head;  u != NULL;  u = u->next) {
463 		for (e = u->crontab;  e != NULL;  e = e->next) {
464 			e->flags &= ~(RUN_AT|NOT_UNTIL);
465 		}
466 	}
467 }
468 
469 #ifdef USE_SIGCHLD
470 static void
471 sigchld_handler(int x)
472 {
473 	WAIT_T		waiter;
474 	PID_T		pid;
475 
476 	for (;;) {
477 #ifdef POSIX
478 		pid = waitpid(-1, &waiter, WNOHANG);
479 #else
480 		pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
481 #endif
482 		switch (pid) {
483 		case -1:
484 			Debug(DPROC,
485 				("[%d] sigchld...no children\n", getpid()))
486 			return;
487 		case 0:
488 			Debug(DPROC,
489 				("[%d] sigchld...no dead kids\n", getpid()))
490 			return;
491 		default:
492 			Debug(DPROC,
493 				("[%d] sigchld...pid #%d died, stat=%d\n",
494 				getpid(), pid, WEXITSTATUS(waiter)))
495 		}
496 	}
497 }
498 #endif /*USE_SIGCHLD*/
499 
500 
501 static void
502 sighup_handler(int x)
503 {
504 	log_close();
505 }
506 
507 
508 static void
509 parse_args(argc, argv)
510 	int	argc;
511 	char	*argv[];
512 {
513 	int	argch;
514 	char	*endp;
515 
516 	while ((argch = getopt(argc, argv, "j:J:m:nosx:")) != -1) {
517 		switch (argch) {
518 		case 'j':
519 			Jitter = strtoul(optarg, &endp, 10);
520 			if (*optarg == '\0' || *endp != '\0' || Jitter > 60)
521 				errx(ERROR_EXIT,
522 				     "bad value for jitter: %s", optarg);
523 			break;
524 		case 'J':
525 			RootJitter = strtoul(optarg, &endp, 10);
526 			if (*optarg == '\0' || *endp != '\0' || RootJitter > 60)
527 				errx(ERROR_EXIT,
528 				     "bad value for root jitter: %s", optarg);
529 			break;
530 		case 'm':
531 			defmailto = optarg;
532 			break;
533 		case 'n':
534 			dont_daemonize = 1;
535 			break;
536 		case 'o':
537 			dst_enabled = 0;
538 			break;
539 		case 's':
540 			dst_enabled = 1;
541 			break;
542 		case 'x':
543 			if (!set_debug_flags(optarg))
544 				usage();
545 			break;
546 		default:
547 			usage();
548 		}
549 	}
550 }
551 
552 static int
553 run_at_secres(cron_db *db)
554 {
555 	user *u;
556 	entry *e;
557 
558 	for (u = db->head;  u != NULL;  u = u->next) {
559 		for (e = u->crontab;  e != NULL;  e = e->next) {
560 			if ((e->flags & SEC_RES) != 0)
561 				return 1;
562 		}
563 	}
564 	return 0;
565 }
566