xref: /netbsd-src/external/bsd/cron/dist/cron.c (revision cd1156524477b27e01412687e65720a208f78651)
1*cd115652Schristos /*	$NetBSD: cron.c,v 1.11 2020/04/18 19:32:19 christos Exp $	*/
2032a4398Schristos 
30061c6a5Schristos /* Copyright 1988,1990,1993,1994 by Paul Vixie
40061c6a5Schristos  * All rights reserved
50061c6a5Schristos  */
60061c6a5Schristos 
70061c6a5Schristos /*
80061c6a5Schristos  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
90061c6a5Schristos  * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
100061c6a5Schristos  *
110061c6a5Schristos  * Permission to use, copy, modify, and distribute this software for any
120061c6a5Schristos  * purpose with or without fee is hereby granted, provided that the above
130061c6a5Schristos  * copyright notice and this permission notice appear in all copies.
140061c6a5Schristos  *
150061c6a5Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
160061c6a5Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
170061c6a5Schristos  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
180061c6a5Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
190061c6a5Schristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
200061c6a5Schristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
210061c6a5Schristos  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
220061c6a5Schristos  */
23032a4398Schristos #include <sys/cdefs.h>
240061c6a5Schristos #if !defined(lint) && !defined(LINT)
25032a4398Schristos #if 0
260061c6a5Schristos static char rcsid[] = "Id: cron.c,v 1.12 2004/01/23 18:56:42 vixie Exp";
27032a4398Schristos #else
28*cd115652Schristos __RCSID("$NetBSD: cron.c,v 1.11 2020/04/18 19:32:19 christos Exp $");
29032a4398Schristos #endif
300061c6a5Schristos #endif
310061c6a5Schristos 
320061c6a5Schristos #define	MAIN_PROGRAM
330061c6a5Schristos 
340061c6a5Schristos #include "cron.h"
350061c6a5Schristos 
360061c6a5Schristos enum timejump { negative, small, medium, large };
370061c6a5Schristos 
385f34e14bSjoerg static	void	usage(void) __dead,
390061c6a5Schristos 		run_reboot_jobs(cron_db *),
40c809afbcSchristos 		find_jobs(time_t, cron_db *, int, int),
410061c6a5Schristos 		set_time(int),
42ae5dea0aSchristos 		cron_sleep(time_t),
430061c6a5Schristos 		sigchld_handler(int),
440061c6a5Schristos 		sighup_handler(int),
450061c6a5Schristos 		sigchld_reaper(void),
465f34e14bSjoerg 		quit(int) __dead,
470061c6a5Schristos 		parse_args(int c, char *v[]);
480061c6a5Schristos 
490061c6a5Schristos static	volatile sig_atomic_t	got_sighup, got_sigchld;
50c809afbcSchristos static	time_t			timeRunning, virtualTime, clockTime;
510061c6a5Schristos static	long			GMToff;
520061c6a5Schristos 
530061c6a5Schristos static void
usage(void)540061c6a5Schristos usage(void) {
550061c6a5Schristos 	const char **dflags;
560061c6a5Schristos 
57032a4398Schristos 	(void)fprintf(stderr, "usage:  %s [-n] [-x [", getprogname());
580061c6a5Schristos 	for (dflags = DebugFlagNames; *dflags; dflags++)
59032a4398Schristos 		(void)fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]");
60032a4398Schristos 	(void)fprintf(stderr, "]\n");
610061c6a5Schristos 	exit(ERROR_EXIT);
620061c6a5Schristos }
630061c6a5Schristos 
640061c6a5Schristos int
main(int argc,char * argv[])650061c6a5Schristos main(int argc, char *argv[]) {
660061c6a5Schristos 	struct sigaction sact;
670061c6a5Schristos 	cron_db	database;
680061c6a5Schristos 
69032a4398Schristos 	setprogname(argv[0]);
70032a4398Schristos 	(void)setlocale(LC_ALL, "");
710061c6a5Schristos 
72032a4398Schristos 	(void)setvbuf(stdout, NULL, _IOLBF, 0);
73032a4398Schristos 	(void)setvbuf(stderr, NULL, _IOLBF, 0);
740061c6a5Schristos 
750061c6a5Schristos 	NoFork = 0;
760061c6a5Schristos 	parse_args(argc, argv);
770061c6a5Schristos 
78032a4398Schristos 	(void)memset(&sact, 0, sizeof sact);
79032a4398Schristos 	(void)sigemptyset(&sact.sa_mask);
800061c6a5Schristos 	sact.sa_flags = 0;
810061c6a5Schristos #ifdef SA_RESTART
820061c6a5Schristos 	sact.sa_flags |= SA_RESTART;
830061c6a5Schristos #endif
840061c6a5Schristos 	sact.sa_handler = sigchld_handler;
850061c6a5Schristos 	(void) sigaction(SIGCHLD, &sact, NULL);
860061c6a5Schristos 	sact.sa_handler = sighup_handler;
870061c6a5Schristos 	(void) sigaction(SIGHUP, &sact, NULL);
880061c6a5Schristos 	sact.sa_handler = quit;
890061c6a5Schristos 	(void) sigaction(SIGINT, &sact, NULL);
900061c6a5Schristos 	(void) sigaction(SIGTERM, &sact, NULL);
910061c6a5Schristos 
920061c6a5Schristos 	acquire_daemonlock(0);
930061c6a5Schristos 	set_cron_uid();
940061c6a5Schristos 	set_cron_cwd();
950061c6a5Schristos 
96c4f7aae5Stron 	if (setenv("PATH", _PATH_DEFPATH, 1) < 0) {
970061c6a5Schristos 		log_it("CRON", getpid(), "DEATH", "can't malloc");
980061c6a5Schristos 		exit(1);
990061c6a5Schristos 	}
1000061c6a5Schristos 
1010061c6a5Schristos 	/* if there are no debug flags turned on, fork as a daemon should.
1020061c6a5Schristos 	 */
1030061c6a5Schristos 	if (DebugFlags) {
1040061c6a5Schristos #if DEBUGGING
1050061c6a5Schristos 		(void)fprintf(stderr, "[%ld] cron started\n", (long)getpid());
1060061c6a5Schristos #endif
1070061c6a5Schristos 	} else if (NoFork == 0) {
108032a4398Schristos 		if (daemon(1, 0)) {
1090061c6a5Schristos 			log_it("CRON",getpid(),"DEATH","can't fork");
110032a4398Schristos 			exit(1);
1110061c6a5Schristos 		}
1120061c6a5Schristos 	}
1130061c6a5Schristos 
1140061c6a5Schristos 	acquire_daemonlock(0);
1150061c6a5Schristos 	database.head = NULL;
1160061c6a5Schristos 	database.tail = NULL;
1170061c6a5Schristos 	database.mtime = (time_t) 0;
1180061c6a5Schristos 	load_database(&database);
1190061c6a5Schristos 	set_time(TRUE);
1200061c6a5Schristos 	run_reboot_jobs(&database);
1210061c6a5Schristos 	timeRunning = virtualTime = clockTime;
1220061c6a5Schristos 
1230061c6a5Schristos 	/*
1240061c6a5Schristos 	 * Too many clocks, not enough time (Al. Einstein)
1250061c6a5Schristos 	 * These clocks are in minutes since the epoch, adjusted for timezone.
1260061c6a5Schristos 	 * virtualTime: is the time it *would* be if we woke up
1270061c6a5Schristos 	 * promptly and nobody ever changed the clock. It is
1280061c6a5Schristos 	 * monotonically increasing... unless a timejump happens.
1290061c6a5Schristos 	 * At the top of the loop, all jobs for 'virtualTime' have run.
1300061c6a5Schristos 	 * timeRunning: is the time we last awakened.
1310061c6a5Schristos 	 * clockTime: is the time when set_time was last called.
1320061c6a5Schristos 	 */
133032a4398Schristos 	for (;;) {
134ae5dea0aSchristos 		time_t timeDiff;
1350061c6a5Schristos 		enum timejump wakeupKind;
1360061c6a5Schristos 
1370061c6a5Schristos 		/* ... wait for the time (in minutes) to change ... */
1380061c6a5Schristos 		do {
1390061c6a5Schristos 			cron_sleep(timeRunning + 1);
1400061c6a5Schristos 			set_time(FALSE);
1410061c6a5Schristos 		} while (clockTime == timeRunning);
1420061c6a5Schristos 		timeRunning = clockTime;
1430061c6a5Schristos 
1440061c6a5Schristos 		/*
1450061c6a5Schristos 		 * Calculate how the current time differs from our virtual
1460061c6a5Schristos 		 * clock.  Classify the change into one of 4 cases.
1470061c6a5Schristos 		 */
1480061c6a5Schristos 		timeDiff = timeRunning - virtualTime;
1490061c6a5Schristos 
1500061c6a5Schristos 		/* shortcut for the most common case */
1510061c6a5Schristos 		if (timeDiff == 1) {
1520061c6a5Schristos 			virtualTime = timeRunning;
1530061c6a5Schristos 			find_jobs(virtualTime, &database, TRUE, TRUE);
1540061c6a5Schristos 		} else {
1550061c6a5Schristos 			if (timeDiff > (3*MINUTE_COUNT) ||
1560061c6a5Schristos 			    timeDiff < -(3*MINUTE_COUNT))
1570061c6a5Schristos 				wakeupKind = large;
1580061c6a5Schristos 			else if (timeDiff > 5)
1590061c6a5Schristos 				wakeupKind = medium;
1600061c6a5Schristos 			else if (timeDiff > 0)
1610061c6a5Schristos 				wakeupKind = small;
1620061c6a5Schristos 			else
1630061c6a5Schristos 				wakeupKind = negative;
1640061c6a5Schristos 
1650061c6a5Schristos 			switch (wakeupKind) {
1660061c6a5Schristos 			case small:
1670061c6a5Schristos 				/*
1680061c6a5Schristos 				 * case 1: timeDiff is a small positive number
1690061c6a5Schristos 				 * (wokeup late) run jobs for each virtual
1700061c6a5Schristos 				 * minute until caught up.
1710061c6a5Schristos 				 */
172ae5dea0aSchristos 				Debug(DSCH, ("[%jd], normal case %jd minutes to go\n",
173ae5dea0aSchristos 				    (intmax_t)getpid(), (intmax_t)timeDiff));
1740061c6a5Schristos 				do {
1750061c6a5Schristos 					if (job_runqueue())
176032a4398Schristos 						(void)sleep(10);
1770061c6a5Schristos 					virtualTime++;
1780061c6a5Schristos 					find_jobs(virtualTime, &database,
1790061c6a5Schristos 					    TRUE, TRUE);
1800061c6a5Schristos 				} while (virtualTime < timeRunning);
1810061c6a5Schristos 				break;
1820061c6a5Schristos 
1830061c6a5Schristos 			case medium:
1840061c6a5Schristos 				/*
1850061c6a5Schristos 				 * case 2: timeDiff is a medium-sized positive
1860061c6a5Schristos 				 * number, for example because we went to DST
1870061c6a5Schristos 				 * run wildcard jobs once, then run any
1880061c6a5Schristos 				 * fixed-time jobs that would otherwise be
1890061c6a5Schristos 				 * skipped if we use up our minute (possible,
1900061c6a5Schristos 				 * if there are a lot of jobs to run) go
1910061c6a5Schristos 				 * around the loop again so that wildcard jobs
1920061c6a5Schristos 				 * have a chance to run, and we do our
1930061c6a5Schristos 				 * housekeeping.
1940061c6a5Schristos 				 */
195ae5dea0aSchristos 				Debug(DSCH, ("[%jd], DST begins %jd minutes to go\n",
196ae5dea0aSchristos 				    (intmax_t)getpid(), (intmax_t)timeDiff));
1970061c6a5Schristos 				/* run wildcard jobs for current minute */
1980061c6a5Schristos 				find_jobs(timeRunning, &database, TRUE, FALSE);
1990061c6a5Schristos 
2000061c6a5Schristos 				/* run fixed-time jobs for each minute missed */
2010061c6a5Schristos 				do {
2020061c6a5Schristos 					if (job_runqueue())
203032a4398Schristos 						(void)sleep(10);
2040061c6a5Schristos 					virtualTime++;
2050061c6a5Schristos 					find_jobs(virtualTime, &database,
2060061c6a5Schristos 					    FALSE, TRUE);
2070061c6a5Schristos 					set_time(FALSE);
2080061c6a5Schristos 				} while (virtualTime < timeRunning &&
2090061c6a5Schristos 				    clockTime == timeRunning);
2100061c6a5Schristos 				break;
2110061c6a5Schristos 
2120061c6a5Schristos 			case negative:
2130061c6a5Schristos 				/*
2140061c6a5Schristos 				 * case 3: timeDiff is a small or medium-sized
2150061c6a5Schristos 				 * negative num, eg. because of DST ending.
2160061c6a5Schristos 				 * Just run the wildcard jobs. The fixed-time
2170061c6a5Schristos 				 * jobs probably have already run, and should
2180061c6a5Schristos 				 * not be repeated.  Virtual time does not
2190061c6a5Schristos 				 * change until we are caught up.
2200061c6a5Schristos 				 */
221ae5dea0aSchristos 				Debug(DSCH, ("[%jd], DST ends %jd minutes to go\n",
222ae5dea0aSchristos 				    (intmax_t)getpid(), (intmax_t)timeDiff));
2230061c6a5Schristos 				find_jobs(timeRunning, &database, TRUE, FALSE);
2240061c6a5Schristos 				break;
2250061c6a5Schristos 			default:
2260061c6a5Schristos 				/*
2270061c6a5Schristos 				 * other: time has changed a *lot*,
2280061c6a5Schristos 				 * jump virtual time, and run everything
2290061c6a5Schristos 				 */
2300061c6a5Schristos 				Debug(DSCH, ("[%ld], clock jumped\n",
231032a4398Schristos 				    (long)getpid()));
2320061c6a5Schristos 				virtualTime = timeRunning;
2330061c6a5Schristos 				find_jobs(timeRunning, &database, TRUE, TRUE);
2340061c6a5Schristos 			}
2350061c6a5Schristos 		}
2360061c6a5Schristos 
2370061c6a5Schristos 		/* Jobs to be run (if any) are loaded; clear the queue. */
238032a4398Schristos 		(void)job_runqueue();
2390061c6a5Schristos 
2400061c6a5Schristos 		/* Check to see if we received a signal while running jobs. */
2410061c6a5Schristos 		if (got_sighup) {
2420061c6a5Schristos 			got_sighup = 0;
2430061c6a5Schristos 			log_close();
2440061c6a5Schristos 		}
2450061c6a5Schristos 		if (got_sigchld) {
2460061c6a5Schristos 			got_sigchld = 0;
2470061c6a5Schristos 			sigchld_reaper();
2480061c6a5Schristos 		}
2490061c6a5Schristos 		load_database(&database);
2500061c6a5Schristos 	}
2510061c6a5Schristos }
2520061c6a5Schristos 
2530061c6a5Schristos static void
run_reboot_jobs(cron_db * db)2540061c6a5Schristos run_reboot_jobs(cron_db *db) {
2550061c6a5Schristos 	user *u;
2560061c6a5Schristos 	entry *e;
2570061c6a5Schristos 
2580061c6a5Schristos 	for (u = db->head; u != NULL; u = u->next) {
2590061c6a5Schristos 		for (e = u->crontab; e != NULL; e = e->next) {
2600061c6a5Schristos 			if (e->flags & WHEN_REBOOT)
261032a4398Schristos 				job_add(e, u, StartTime);
2620061c6a5Schristos 		}
2630061c6a5Schristos 	}
2640061c6a5Schristos 	(void) job_runqueue();
2650061c6a5Schristos }
2660061c6a5Schristos 
26771a5eeb0Schristos static const char *
bitprint(const bitstr_t * b,size_t l)26871a5eeb0Schristos bitprint(const bitstr_t *b, size_t l)
26971a5eeb0Schristos {
27071a5eeb0Schristos 	size_t i, set, clear;
27171a5eeb0Schristos 	static char result[1024];
27271a5eeb0Schristos 	char tmp[1024];
27371a5eeb0Schristos 
27471a5eeb0Schristos 	result[0] = '\0';
27571a5eeb0Schristos 	for (i = 0;;) {
27671a5eeb0Schristos 		for (; i < l; i++)
27771a5eeb0Schristos 			if (bit_test(b, i))
27871a5eeb0Schristos 				break;
27971a5eeb0Schristos 		if (i == l)
28071a5eeb0Schristos 			return result;
28171a5eeb0Schristos 		set = i;
28271a5eeb0Schristos 		for (; i < l; i++)
28371a5eeb0Schristos 			if (!bit_test(b, i))
28471a5eeb0Schristos 				break;
28571a5eeb0Schristos 		clear = i;
28671a5eeb0Schristos 		if (set == 0 && clear == l) {
28771a5eeb0Schristos 			snprintf(result, sizeof(result), "*");
28871a5eeb0Schristos 			return result;
28971a5eeb0Schristos 		}
29071a5eeb0Schristos 		if (clear == l || set == clear - 1)
29171a5eeb0Schristos 			snprintf(tmp, sizeof(tmp), ",%zu", set);
29271a5eeb0Schristos 		else
29371a5eeb0Schristos 			snprintf(tmp, sizeof(tmp), ",%zu-%zu", set, clear - 1);
29471a5eeb0Schristos 		if (result[0] == '\0')
29571a5eeb0Schristos 			strlcpy(result, tmp + 1, sizeof(result));
29671a5eeb0Schristos 		else
29771a5eeb0Schristos 			strlcat(result, tmp, sizeof(result));
29871a5eeb0Schristos 	}
29971a5eeb0Schristos }
30071a5eeb0Schristos 
30171a5eeb0Schristos static const char *
flagsprint(int flags)30271a5eeb0Schristos flagsprint(int flags)
30371a5eeb0Schristos {
30471a5eeb0Schristos 	static char result[1024];
30571a5eeb0Schristos 
30671a5eeb0Schristos 	result[0] = '\0';
30771a5eeb0Schristos 	if (flags & MIN_STAR)
30871a5eeb0Schristos 		strlcat(result, ",min", sizeof(result));
30971a5eeb0Schristos 	if (flags & HR_STAR)
31071a5eeb0Schristos 		strlcat(result, ",hr", sizeof(result));
31171a5eeb0Schristos 	if (flags & DOM_STAR)
31271a5eeb0Schristos 		strlcat(result, ",dom", sizeof(result));
31371a5eeb0Schristos 	if (flags & DOW_STAR)
31471a5eeb0Schristos 		strlcat(result, ",dow", sizeof(result));
31571a5eeb0Schristos 	if (flags & WHEN_REBOOT)
31671a5eeb0Schristos 		strlcat(result, ",reboot", sizeof(result));
31771a5eeb0Schristos 	if (flags & DONT_LOG)
31871a5eeb0Schristos 		strlcat(result, ",nolog", sizeof(result));
31971a5eeb0Schristos 
32071a5eeb0Schristos 	return result + (result[0] == ',');
32171a5eeb0Schristos }
32271a5eeb0Schristos 
32371a5eeb0Schristos static void
printone(char * res,size_t len,const char * b)32471a5eeb0Schristos printone(char *res, size_t len, const char *b)
32571a5eeb0Schristos {
32671a5eeb0Schristos 	int comma = strchr(b, ',') != NULL;
32771a5eeb0Schristos 	if (comma)
32871a5eeb0Schristos 		strlcat(res, "[", len);
32971a5eeb0Schristos 	strlcat(res, b, len);
33071a5eeb0Schristos 	strlcat(res, "]," + (comma ? 0 : 1), len);
33171a5eeb0Schristos }
33271a5eeb0Schristos 
33371a5eeb0Schristos static const char *
tick(const entry * e)33471a5eeb0Schristos tick(const entry *e)
33571a5eeb0Schristos {
33671a5eeb0Schristos 	static char result[1024];
33771a5eeb0Schristos 
33871a5eeb0Schristos 	result[0] = '\0';
33971a5eeb0Schristos 	printone(result, sizeof(result), bitprint(e->minute, MINUTE_COUNT));
34071a5eeb0Schristos 	printone(result, sizeof(result), bitprint(e->hour, HOUR_COUNT));
34171a5eeb0Schristos 	printone(result, sizeof(result), bitprint(e->dom, DOM_COUNT));
34271a5eeb0Schristos 	printone(result, sizeof(result), bitprint(e->month, MONTH_COUNT));
34371a5eeb0Schristos 	printone(result, sizeof(result), bitprint(e->dow, DOW_COUNT));
34471a5eeb0Schristos 	strlcat(result, "flags=", sizeof(result));
34571a5eeb0Schristos 	strlcat(result, flagsprint(e->flags), sizeof(result));
34671a5eeb0Schristos 	return result;
34771a5eeb0Schristos }
34871a5eeb0Schristos 
3490061c6a5Schristos static void
find_jobs(time_t vtime,cron_db * db,int doWild,int doNonWild)350c809afbcSchristos find_jobs(time_t vtime, cron_db *db, int doWild, int doNonWild) {
3510061c6a5Schristos 	time_t virtualSecond = vtime * SECONDS_PER_MINUTE;
35271a5eeb0Schristos 	struct tm *tm;
3530061c6a5Schristos 	int minute, hour, dom, month, dow;
3540061c6a5Schristos 	user *u;
3550061c6a5Schristos 	entry *e;
35671a5eeb0Schristos #ifndef CRON_LOCALTIME
357c809afbcSchristos 	char *orig_tz, *job_tz;
3580061c6a5Schristos 
359c809afbcSchristos #define maketime(tz1, tz2) do { \
360c809afbcSchristos 	char *t = tz1; \
361c809afbcSchristos 	if (t != NULL && *t != '\0') \
362c809afbcSchristos 		setenv("TZ", t, 1); \
363c809afbcSchristos 	else if ((tz2) != NULL) \
364c809afbcSchristos 		setenv("TZ", (tz2), 1); \
365c809afbcSchristos 	else \
366c809afbcSchristos 		unsetenv("TZ"); \
36771a5eeb0Schristos 	tzset(); \
368c809afbcSchristos 	tm = localtime(&virtualSecond); \
369c809afbcSchristos 	minute = tm->tm_min -FIRST_MINUTE; \
370c809afbcSchristos 	hour = tm->tm_hour -FIRST_HOUR; \
371c809afbcSchristos 	dom = tm->tm_mday -FIRST_DOM; \
372c809afbcSchristos 	month = tm->tm_mon + 1 /* 0..11 -> 1..12 */ -FIRST_MONTH; \
373c809afbcSchristos 	dow = tm->tm_wday -FIRST_DOW; \
374c809afbcSchristos 	} while (/*CONSTCOND*/0)
375c809afbcSchristos 
376c809afbcSchristos 	orig_tz = getenv("TZ");
377c809afbcSchristos 	maketime(NULL, orig_tz);
37871a5eeb0Schristos #else
37971a5eeb0Schristos 	tm = gmtime(&virtualSecond);
38071a5eeb0Schristos #endif
3810061c6a5Schristos 
3820061c6a5Schristos 	Debug(DSCH, ("[%ld] tick(%d,%d,%d,%d,%d) %s %s\n",
3830061c6a5Schristos 		     (long)getpid(), minute, hour, dom, month, dow,
384032a4398Schristos 		     doWild?" ":"No wildcard",doNonWild?" ":"Wildcard only"));
3850061c6a5Schristos 
3860061c6a5Schristos 	/* the dom/dow situation is odd.  '* * 1,15 * Sun' will run on the
3870061c6a5Schristos 	 * first and fifteenth AND every Sunday;  '* * * * Sun' will run *only*
3880061c6a5Schristos 	 * on Sundays;  '* * 1,15 * *' will run *only* the 1st and 15th.  this
3890061c6a5Schristos 	 * is why we keep 'e->dow_star' and 'e->dom_star'.  yes, it's bizarre.
3900061c6a5Schristos 	 * like many bizarre things, it's the standard.
3910061c6a5Schristos 	 */
3920061c6a5Schristos 	for (u = db->head; u != NULL; u = u->next) {
3930061c6a5Schristos 		for (e = u->crontab; e != NULL; e = e->next) {
39471a5eeb0Schristos #ifndef CRON_LOCALTIME
395c809afbcSchristos 			job_tz = env_get("CRON_TZ", e->envp);
396c809afbcSchristos 			maketime(job_tz, orig_tz);
39771a5eeb0Schristos #else
39871a5eeb0Schristos #define job_tz "N/A"
39971a5eeb0Schristos #define orig_tz "N/A"
40071a5eeb0Schristos #endif
40171a5eeb0Schristos 			Debug(DSCH|DEXT, ("user [%s:%ld:%ld:...] "
40271a5eeb0Schristos 			    "[jobtz=%s, origtz=%s] "
40371a5eeb0Schristos 			    "tick(%s), cmd=\"%s\"\n",
40471a5eeb0Schristos 			    e->pwd->pw_name, (long)e->pwd->pw_uid,
40571a5eeb0Schristos 			    (long)e->pwd->pw_gid, job_tz, orig_tz,
40671a5eeb0Schristos 			    tick(e), e->cmd));
4070061c6a5Schristos 			if (bit_test(e->minute, minute) &&
4080061c6a5Schristos 			    bit_test(e->hour, hour) &&
4090061c6a5Schristos 			    bit_test(e->month, month) &&
4100061c6a5Schristos 			    ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
4110061c6a5Schristos 			      ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
4120061c6a5Schristos 			      : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
4130061c6a5Schristos 			    )
4140061c6a5Schristos 			   ) {
4150061c6a5Schristos 				if ((doNonWild &&
4160061c6a5Schristos 				    !(e->flags & (MIN_STAR|HR_STAR))) ||
4170061c6a5Schristos 				    (doWild && (e->flags & (MIN_STAR|HR_STAR))))
418c809afbcSchristos 					job_add(e, u, StartTime);
4190061c6a5Schristos 			}
4200061c6a5Schristos 		}
4210061c6a5Schristos 	}
42271a5eeb0Schristos #ifndef CRON_LOCALTIME
42332d0172fSchristos 	if (orig_tz != NULL)
42432d0172fSchristos 		setenv("TZ", orig_tz, 1);
42532d0172fSchristos 	else
42632d0172fSchristos 		unsetenv("TZ");
42771a5eeb0Schristos #endif
4280061c6a5Schristos }
4290061c6a5Schristos 
4300061c6a5Schristos /*
4310061c6a5Schristos  * Set StartTime and clockTime to the current time.
4320061c6a5Schristos  * These are used for computing what time it really is right now.
4330061c6a5Schristos  * Note that clockTime is a unix wallclock time converted to minutes.
4340061c6a5Schristos  */
4350061c6a5Schristos static void
set_time(int initialize)4360061c6a5Schristos set_time(int initialize) {
43771a5eeb0Schristos 
43871a5eeb0Schristos #ifdef CRON_LOCALTIME
4390061c6a5Schristos 	struct tm tm;
4400061c6a5Schristos 	static int isdst;
4410061c6a5Schristos 
4420061c6a5Schristos 	StartTime = time(NULL);
4430061c6a5Schristos 	/* We adjust the time to GMT so we can catch DST changes. */
4440061c6a5Schristos 	tm = *localtime(&StartTime);
4450061c6a5Schristos 	if (initialize || tm.tm_isdst != isdst) {
4460061c6a5Schristos 		isdst = tm.tm_isdst;
4470061c6a5Schristos 		GMToff = get_gmtoff(&StartTime, &tm);
4480061c6a5Schristos 		Debug(DSCH, ("[%ld] GMToff=%ld\n",
449032a4398Schristos 		    (long)getpid(), (long)GMToff));
4500061c6a5Schristos 	}
45171a5eeb0Schristos #else
45271a5eeb0Schristos 	StartTime = time(NULL);
45371a5eeb0Schristos #endif
454c809afbcSchristos 	clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE;
4550061c6a5Schristos }
4560061c6a5Schristos 
4570061c6a5Schristos /*
4580061c6a5Schristos  * Try to just hit the next minute.
4590061c6a5Schristos  */
4600061c6a5Schristos static void
cron_sleep(time_t target)461ae5dea0aSchristos cron_sleep(time_t target) {
4620061c6a5Schristos 	time_t t1, t2;
4630061c6a5Schristos 	int seconds_to_wait;
4640061c6a5Schristos 
4650061c6a5Schristos 	t1 = time(NULL) + GMToff;
4666611a4beSchristos 	seconds_to_wait = (int)(target * SECONDS_PER_MINUTE - t1);
467032a4398Schristos 	Debug(DSCH, ("[%ld] Target time=%lld, sec-to-wait=%d\n",
468032a4398Schristos 	    (long)getpid(), (long long)target*SECONDS_PER_MINUTE,
469032a4398Schristos 	    seconds_to_wait));
4700061c6a5Schristos 
4710061c6a5Schristos 	while (seconds_to_wait > 0 && seconds_to_wait < 65) {
472032a4398Schristos 		(void)sleep((unsigned int) seconds_to_wait);
4730061c6a5Schristos 
4740061c6a5Schristos 		/*
4750061c6a5Schristos 		 * Check to see if we were interrupted by a signal.
4760061c6a5Schristos 		 * If so, service the signal(s) then continue sleeping
4770061c6a5Schristos 		 * where we left off.
4780061c6a5Schristos 		 */
4790061c6a5Schristos 		if (got_sighup) {
4800061c6a5Schristos 			got_sighup = 0;
4810061c6a5Schristos 			log_close();
4820061c6a5Schristos 		}
4830061c6a5Schristos 		if (got_sigchld) {
4840061c6a5Schristos 			got_sigchld = 0;
4850061c6a5Schristos 			sigchld_reaper();
4860061c6a5Schristos 		}
4870061c6a5Schristos 		t2 = time(NULL) + GMToff;
4880061c6a5Schristos 		seconds_to_wait -= (int)(t2 - t1);
4890061c6a5Schristos 		t1 = t2;
4900061c6a5Schristos 	}
4910061c6a5Schristos }
4920061c6a5Schristos 
493032a4398Schristos /*ARGSUSED*/
4940061c6a5Schristos static void
sighup_handler(int x __unused)495032a4398Schristos sighup_handler(int x __unused) {
4960061c6a5Schristos 	got_sighup = 1;
4970061c6a5Schristos }
4980061c6a5Schristos 
499032a4398Schristos /*ARGSUSED*/
5000061c6a5Schristos static void
sigchld_handler(int x __unused)501032a4398Schristos sigchld_handler(int x __unused) {
5020061c6a5Schristos 	got_sigchld = 1;
5030061c6a5Schristos }
5040061c6a5Schristos 
505032a4398Schristos /*ARGSUSED*/
5060061c6a5Schristos static void
quit(int x __unused)507032a4398Schristos quit(int x __unused) {
5080061c6a5Schristos 	(void) unlink(_PATH_CRON_PID);
5090061c6a5Schristos 	_exit(0);
5100061c6a5Schristos }
5110061c6a5Schristos 
5120061c6a5Schristos static void
sigchld_reaper(void)5130061c6a5Schristos sigchld_reaper(void) {
514065057e6Schristos 	for (;;) {
5150061c6a5Schristos 		WAIT_T waiter;
516065057e6Schristos 		PID_T pid = waitpid(-1, &waiter, WNOHANG);
5170061c6a5Schristos 
5180061c6a5Schristos 		switch (pid) {
5190061c6a5Schristos 		case -1:
5200061c6a5Schristos 			if (errno == EINTR)
5210061c6a5Schristos 				continue;
5220061c6a5Schristos 			Debug(DPROC,
5230061c6a5Schristos 			      ("[%ld] sigchld...no children\n",
524032a4398Schristos 			       (long)getpid()));
525065057e6Schristos 			return;
5260061c6a5Schristos 		case 0:
5270061c6a5Schristos 			Debug(DPROC,
5280061c6a5Schristos 			      ("[%ld] sigchld...no dead kids\n",
529032a4398Schristos 			       (long)getpid()));
530065057e6Schristos 			return;
5310061c6a5Schristos 		default:
5320061c6a5Schristos 			Debug(DPROC,
5330061c6a5Schristos 			      ("[%ld] sigchld...pid #%ld died, stat=%d\n",
534032a4398Schristos 			       (long)getpid(), (long)pid, WEXITSTATUS(waiter)));
535*cd115652Schristos 			job_exit(pid);
5360061c6a5Schristos 			break;
5370061c6a5Schristos 		}
538065057e6Schristos 	}
5390061c6a5Schristos }
5400061c6a5Schristos 
5410061c6a5Schristos static void
parse_args(int argc,char * argv[])5420061c6a5Schristos parse_args(int argc, char *argv[]) {
5430061c6a5Schristos 	int argch;
5440061c6a5Schristos 
5450061c6a5Schristos 	while (-1 != (argch = getopt(argc, argv, "nx:"))) {
5460061c6a5Schristos 		switch (argch) {
5470061c6a5Schristos 		default:
5480061c6a5Schristos 			usage();
549032a4398Schristos 			break;
5500061c6a5Schristos 		case 'x':
5510061c6a5Schristos 			if (!set_debug_flags(optarg))
5520061c6a5Schristos 				usage();
5530061c6a5Schristos 			break;
5540061c6a5Schristos 		case 'n':
5550061c6a5Schristos 			NoFork = 1;
5560061c6a5Schristos 			break;
5570061c6a5Schristos 		}
5580061c6a5Schristos 	}
5590061c6a5Schristos }
560