xref: /openbsd-src/usr.sbin/cron/cron.c (revision 9c847fa89616307ef9664ddfa4e7537e642b50bc)
1*9c847fa8Smillert /*	$OpenBSD: cron.c,v 1.82 2022/07/08 20:47:24 millert Exp $	*/
2e134e629Smillert 
3df930be7Sderaadt /* Copyright 1988,1990,1993,1994 by Paul Vixie
4a5198fa1Smillert  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5f454ebdeSmillert  * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
6df930be7Sderaadt  *
7f454ebdeSmillert  * Permission to use, copy, modify, and distribute this software for any
8f454ebdeSmillert  * purpose with or without fee is hereby granted, provided that the above
9f454ebdeSmillert  * copyright notice and this permission notice appear in all copies.
10df930be7Sderaadt  *
11a5198fa1Smillert  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12a5198fa1Smillert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a5198fa1Smillert  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
14a5198fa1Smillert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a5198fa1Smillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a5198fa1Smillert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17a5198fa1Smillert  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18df930be7Sderaadt  */
19df930be7Sderaadt 
20fa575ea2Smillert #include <sys/types.h>
21fa575ea2Smillert #include <sys/socket.h>
22fa575ea2Smillert #include <sys/stat.h>
2395f2b4e2Smillert #include <sys/time.h>
24fa575ea2Smillert #include <sys/un.h>
25fa575ea2Smillert #include <sys/wait.h>
26fa575ea2Smillert 
27fa575ea2Smillert #include <bitstring.h>
28b8c5c73dSmillert #include <err.h>
29fa575ea2Smillert #include <errno.h>
3088959323Smillert #include <grp.h>
31fa575ea2Smillert #include <poll.h>
32fa575ea2Smillert #include <signal.h>
33fa575ea2Smillert #include <stdio.h>
34fa575ea2Smillert #include <stdlib.h>
35fa575ea2Smillert #include <string.h>
36a38a3395Smillert #include <syslog.h>
37fa575ea2Smillert #include <time.h>
38fa575ea2Smillert #include <unistd.h>
39fa575ea2Smillert 
40fa575ea2Smillert #include "config.h"
41fa575ea2Smillert #include "pathnames.h"
42fa575ea2Smillert #include "macros.h"
43fa575ea2Smillert #include "structs.h"
44fa575ea2Smillert #include "funcs.h"
45fa575ea2Smillert #include "globals.h"
46df930be7Sderaadt 
47dc59ecf4Smillert enum timejump { negative, small, medium, large };
48dc59ecf4Smillert 
49f454ebdeSmillert static	void	usage(void),
50f454ebdeSmillert 		run_reboot_jobs(cron_db *),
511e5c894eSderaadt 		find_jobs(time_t, cron_db *, int, int),
52c72b5b24Smillert 		set_time(int),
53f3ce7c4eSmillert 		cron_sleep(time_t, sigset_t *),
54f454ebdeSmillert 		sigchld_handler(int),
55f454ebdeSmillert 		sigchld_reaper(void),
56f454ebdeSmillert 		parse_args(int c, char *v[]);
57df930be7Sderaadt 
5858a24ea1Smillert static	int	open_socket(void);
5958a24ea1Smillert 
60a38a3395Smillert static	volatile sig_atomic_t	got_sigchld;
611e5c894eSderaadt static	time_t			timeRunning, virtualTime, clockTime;
6240616ceaSmillert static	long			GMToff;
638a6f8ff3Smillert static	cron_db			*database;
648a6f8ff3Smillert static	at_db			*at_database;
652b139a93Smillert static	double			batch_maxload = BATCH_MAXLOAD;
669d64f211Smillert static	int			NoFork;
679d64f211Smillert static	time_t			StartTime;
68a9e807deSmillert 	gid_t			cron_gid;
690a66a1feSjca 	int			cronSock;
70df930be7Sderaadt 
71df930be7Sderaadt static void
usage(void)72785ecd14Stedu usage(void)
73785ecd14Stedu {
743d61ea5dSderaadt 
754a3a0dd4Smillert 	fprintf(stderr, "usage: %s [-n] [-l load_avg]\n", __progname);
7610ee6820Smillert 	exit(EXIT_FAILURE);
77df930be7Sderaadt }
78df930be7Sderaadt 
79df930be7Sderaadt int
main(int argc,char * argv[])80785ecd14Stedu main(int argc, char *argv[])
81785ecd14Stedu {
82f454ebdeSmillert 	struct sigaction sact;
83f3ce7c4eSmillert 	sigset_t blocked, omask;
84a9e807deSmillert 	struct group *grp;
85df930be7Sderaadt 
86b9a887b6Smillert 	setvbuf(stdout, NULL, _IOLBF, 0);
87b9a887b6Smillert 	setvbuf(stderr, NULL, _IOLBF, 0);
88df930be7Sderaadt 
89df930be7Sderaadt 	parse_args(argc, argv);
90df930be7Sderaadt 
91f454ebdeSmillert 	bzero((char *)&sact, sizeof sact);
92f454ebdeSmillert 	sigemptyset(&sact.sa_mask);
9349ff01e1Smillert 	sact.sa_flags = SA_RESTART;
94f454ebdeSmillert 	sact.sa_handler = sigchld_handler;
95f454ebdeSmillert 	(void) sigaction(SIGCHLD, &sact, NULL);
96e134e629Smillert 	sact.sa_handler = SIG_IGN;
97a38a3395Smillert 	(void) sigaction(SIGHUP, &sact, NULL);
98e134e629Smillert 	(void) sigaction(SIGPIPE, &sact, NULL);
99df930be7Sderaadt 
100a38a3395Smillert 	openlog(__progname, LOG_PID, LOG_CRON);
101a38a3395Smillert 
102d31b1261Smillert 	if (pledge("stdio rpath wpath cpath fattr getpw unix id dns proc exec",
103e8d6cae4Sderaadt 	    NULL) == -1) {
104b8c5c73dSmillert 		warn("pledge");
105a38a3395Smillert 		syslog(LOG_ERR, "(CRON) PLEDGE (%m)");
106e8d6cae4Sderaadt 		exit(EXIT_FAILURE);
107e8d6cae4Sderaadt 	}
1082da949c6Sderaadt 
109a9e807deSmillert 	if ((grp = getgrnam(CRON_GROUP)) == NULL) {
110a9e807deSmillert 		warnx("can't find cron group %s", CRON_GROUP);
111a9e807deSmillert 		syslog(LOG_ERR, "(CRON) DEATH (can't find cron group)");
112a9e807deSmillert 		exit(EXIT_FAILURE);
113a9e807deSmillert 	}
114a9e807deSmillert 	cron_gid = grp->gr_gid;
115a9e807deSmillert 
11652c15c8aSmillert 	cronSock = open_socket();
11752c15c8aSmillert 
118e134e629Smillert 	if (putenv("PATH="_PATH_DEFPATH) < 0) {
119b8c5c73dSmillert 		warn("putenv");
120a38a3395Smillert 		syslog(LOG_ERR, "(CRON) DEATH (%m)");
12110ee6820Smillert 		exit(EXIT_FAILURE);
1222c20747dSderaadt 	}
123df930be7Sderaadt 
124585d9be6Stedu 	if (NoFork == 0) {
12588959323Smillert 		if (daemon(0, 0) == -1) {
126a38a3395Smillert 			syslog(LOG_ERR, "(CRON) DEATH (%m)");
12710ee6820Smillert 			exit(EXIT_FAILURE);
12814852038Smillert 		}
129a38a3395Smillert 		syslog(LOG_INFO, "(CRON) STARTUP (%s)", CRON_VERSION);
130df930be7Sderaadt 	}
131df930be7Sderaadt 
132df930be7Sderaadt 	load_database(&database);
1332b139a93Smillert 	scan_atjobs(&at_database, NULL);
134e134e629Smillert 	set_time(TRUE);
1358a6f8ff3Smillert 	run_reboot_jobs(database);
136f326c411Sderaadt 	timeRunning = virtualTime = clockTime;
137f326c411Sderaadt 
138f326c411Sderaadt 	/*
139f3ce7c4eSmillert 	 * We block SIGHUP and SIGCHLD while running jobs and receive them
140f3ce7c4eSmillert 	 * only while sleeping in ppoll().  This ensures no signal is lost.
141f3ce7c4eSmillert 	 */
142f3ce7c4eSmillert 	sigemptyset(&blocked);
143f3ce7c4eSmillert 	sigaddset(&blocked, SIGCHLD);
144f3ce7c4eSmillert 	sigaddset(&blocked, SIGHUP);
145f3ce7c4eSmillert 	sigprocmask(SIG_BLOCK, &blocked, &omask);
146f3ce7c4eSmillert 
147f3ce7c4eSmillert 	/*
148f454ebdeSmillert 	 * Too many clocks, not enough time (Al. Einstein)
14994db05edSmillert 	 * These clocks are in minutes since the epoch, adjusted for timezone.
150f326c411Sderaadt 	 * virtualTime: is the time it *would* be if we woke up
151f326c411Sderaadt 	 * promptly and nobody ever changed the clock. It is
152f326c411Sderaadt 	 * monotonically increasing... unless a timejump happens.
153f326c411Sderaadt 	 * At the top of the loop, all jobs for 'virtualTime' have run.
154f326c411Sderaadt 	 * timeRunning: is the time we last awakened.
155f326c411Sderaadt 	 * clockTime: is the time when set_time was last called.
156f326c411Sderaadt 	 */
157df930be7Sderaadt 	while (TRUE) {
15894db05edSmillert 		int timeDiff;
159dc59ecf4Smillert 		enum timejump wakeupKind;
160df930be7Sderaadt 
161f326c411Sderaadt 		/* ... wait for the time (in minutes) to change ... */
162f326c411Sderaadt 		do {
163f3ce7c4eSmillert 			cron_sleep(timeRunning + 1, &omask);
164e134e629Smillert 			set_time(FALSE);
165f326c411Sderaadt 		} while (clockTime == timeRunning);
166f326c411Sderaadt 		timeRunning = clockTime;
167df930be7Sderaadt 
168f326c411Sderaadt 		/*
169f454ebdeSmillert 		 * Calculate how the current time differs from our virtual
170f454ebdeSmillert 		 * clock.  Classify the change into one of 4 cases.
171df930be7Sderaadt 		 */
172f326c411Sderaadt 		timeDiff = timeRunning - virtualTime;
173f326c411Sderaadt 
174f326c411Sderaadt 		/* shortcut for the most common case */
175f326c411Sderaadt 		if (timeDiff == 1) {
176f326c411Sderaadt 			virtualTime = timeRunning;
1778a6f8ff3Smillert 			find_jobs(virtualTime, database, TRUE, TRUE);
178f326c411Sderaadt 		} else {
179dc59ecf4Smillert 			if (timeDiff > (3*MINUTE_COUNT) ||
180dc59ecf4Smillert 			    timeDiff < -(3*MINUTE_COUNT))
181dc59ecf4Smillert 				wakeupKind = large;
182dc59ecf4Smillert 			else if (timeDiff > 5)
183dc59ecf4Smillert 				wakeupKind = medium;
184dc59ecf4Smillert 			else if (timeDiff > 0)
185dc59ecf4Smillert 				wakeupKind = small;
186dc59ecf4Smillert 			else
187dc59ecf4Smillert 				wakeupKind = negative;
188f326c411Sderaadt 
189f326c411Sderaadt 			switch (wakeupKind) {
190dc59ecf4Smillert 			case small:
191f326c411Sderaadt 				/*
192f326c411Sderaadt 				 * case 1: timeDiff is a small positive number
193f454ebdeSmillert 				 * (wokeup late) run jobs for each virtual
194f454ebdeSmillert 				 * minute until caught up.
195f326c411Sderaadt 				 */
196f326c411Sderaadt 				do {
197f326c411Sderaadt 					if (job_runqueue())
198f326c411Sderaadt 						sleep(10);
199f326c411Sderaadt 					virtualTime++;
2008a6f8ff3Smillert 					find_jobs(virtualTime, database,
201f454ebdeSmillert 					    TRUE, TRUE);
202f326c411Sderaadt 				} while (virtualTime < timeRunning);
203f326c411Sderaadt 				break;
204f326c411Sderaadt 
205dc59ecf4Smillert 			case medium:
206f326c411Sderaadt 				/*
207f454ebdeSmillert 				 * case 2: timeDiff is a medium-sized positive
208f454ebdeSmillert 				 * number, for example because we went to DST
209f454ebdeSmillert 				 * run wildcard jobs once, then run any
210f454ebdeSmillert 				 * fixed-time jobs that would otherwise be
211f454ebdeSmillert 				 * skipped if we use up our minute (possible,
212f454ebdeSmillert 				 * if there are a lot of jobs to run) go
213f454ebdeSmillert 				 * around the loop again so that wildcard jobs
214f454ebdeSmillert 				 * have a chance to run, and we do our
215f454ebdeSmillert 				 * housekeeping.
216f326c411Sderaadt 				 */
217f326c411Sderaadt 				/* run wildcard jobs for current minute */
2188a6f8ff3Smillert 				find_jobs(timeRunning, database, TRUE, FALSE);
219f326c411Sderaadt 
220f326c411Sderaadt 				/* run fixed-time jobs for each minute missed */
221f326c411Sderaadt 				do {
222f326c411Sderaadt 					if (job_runqueue())
223f326c411Sderaadt 						sleep(10);
224f326c411Sderaadt 					virtualTime++;
2258a6f8ff3Smillert 					find_jobs(virtualTime, database,
226f454ebdeSmillert 					    FALSE, TRUE);
227e134e629Smillert 					set_time(FALSE);
228f326c411Sderaadt 				} while (virtualTime< timeRunning &&
229f326c411Sderaadt 				    clockTime == timeRunning);
230f326c411Sderaadt 				break;
231f326c411Sderaadt 
232dc59ecf4Smillert 			case negative:
233f326c411Sderaadt 				/*
234f326c411Sderaadt 				 * case 3: timeDiff is a small or medium-sized
23594db05edSmillert 				 * negative num, eg. because of DST ending.
23694db05edSmillert 				 * Just run the wildcard jobs. The fixed-time
23794db05edSmillert 				 * jobs probably have already run, and should
23894db05edSmillert 				 * not be repeated.  Virtual time does not
23994db05edSmillert 				 * change until we are caught up.
240f326c411Sderaadt 				 */
2418a6f8ff3Smillert 				find_jobs(timeRunning, database, TRUE, FALSE);
242f326c411Sderaadt 				break;
243f326c411Sderaadt 			default:
244f326c411Sderaadt 				/*
245f326c411Sderaadt 				 * other: time has changed a *lot*,
246f326c411Sderaadt 				 * jump virtual time, and run everything
247f326c411Sderaadt 				 */
248f326c411Sderaadt 				virtualTime = timeRunning;
2498a6f8ff3Smillert 				find_jobs(timeRunning, database, TRUE, TRUE);
250f326c411Sderaadt 			}
251f326c411Sderaadt 		}
252f454ebdeSmillert 
253f454ebdeSmillert 		/* Jobs to be run (if any) are loaded; clear the queue. */
254f326c411Sderaadt 		job_runqueue();
2552b139a93Smillert 
2562b139a93Smillert 		/* Run any jobs in the at queue. */
2578a6f8ff3Smillert 		atrun(at_database, batch_maxload,
2582b139a93Smillert 		    timeRunning * SECONDS_PER_MINUTE - GMToff);
2592b139a93Smillert 
260f3ce7c4eSmillert 		/* Reload jobs as needed. */
2612b139a93Smillert 		load_database(&database);
2622b139a93Smillert 		scan_atjobs(&at_database, NULL);
263df930be7Sderaadt 	}
264df930be7Sderaadt }
265df930be7Sderaadt 
266df930be7Sderaadt static void
run_reboot_jobs(cron_db * db)267785ecd14Stedu run_reboot_jobs(cron_db *db)
268785ecd14Stedu {
269f454ebdeSmillert 	user *u;
270f454ebdeSmillert 	entry *e;
271df930be7Sderaadt 
2728a6f8ff3Smillert 	TAILQ_FOREACH(u, &db->users, entries) {
2738a6f8ff3Smillert 		SLIST_FOREACH(e, &u->crontab, entries) {
274f454ebdeSmillert 			if (e->flags & WHEN_REBOOT)
275df930be7Sderaadt 				job_add(e, u);
276df930be7Sderaadt 		}
277df930be7Sderaadt 	}
278df930be7Sderaadt 	(void) job_runqueue();
279df930be7Sderaadt }
280df930be7Sderaadt 
281df930be7Sderaadt static void
find_jobs(time_t vtime,cron_db * db,int doWild,int doNonWild)282785ecd14Stedu find_jobs(time_t vtime, cron_db *db, int doWild, int doNonWild)
283785ecd14Stedu {
284f326c411Sderaadt 	time_t virtualSecond  = vtime * SECONDS_PER_MINUTE;
28594db05edSmillert 	struct tm *tm = gmtime(&virtualSecond);
286f454ebdeSmillert 	int minute, hour, dom, month, dow;
287f454ebdeSmillert 	user *u;
288f454ebdeSmillert 	entry *e;
289df930be7Sderaadt 
290cb354146Smiod 	/* make 0-based values out of these so we can use them as indices
291df930be7Sderaadt 	 */
292df930be7Sderaadt 	minute = tm->tm_min -FIRST_MINUTE;
293df930be7Sderaadt 	hour = tm->tm_hour -FIRST_HOUR;
294df930be7Sderaadt 	dom = tm->tm_mday -FIRST_DOM;
295df930be7Sderaadt 	month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
296df930be7Sderaadt 	dow = tm->tm_wday -FIRST_DOW;
297df930be7Sderaadt 
298df930be7Sderaadt 	/* the dom/dow situation is odd.  '* * 1,15 * Sun' will run on the
299df930be7Sderaadt 	 * first and fifteenth AND every Sunday;  '* * * * Sun' will run *only*
300df930be7Sderaadt 	 * on Sundays;  '* * 1,15 * *' will run *only* the 1st and 15th.  this
301df930be7Sderaadt 	 * is why we keep 'e->dow_star' and 'e->dom_star'.  yes, it's bizarre.
302df930be7Sderaadt 	 * like many bizarre things, it's the standard.
303df930be7Sderaadt 	 */
3048a6f8ff3Smillert 	TAILQ_FOREACH(u, &db->users, entries) {
3058a6f8ff3Smillert 		SLIST_FOREACH(e, &u->crontab, entries) {
306f326c411Sderaadt 			if (bit_test(e->minute, minute) &&
307f326c411Sderaadt 			    bit_test(e->hour, hour) &&
308f326c411Sderaadt 			    bit_test(e->month, month) &&
309f326c411Sderaadt 			    ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
310df930be7Sderaadt 			      ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
311f454ebdeSmillert 			      : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
312f454ebdeSmillert 			    )
313bb0cdb18Smillert 			   ) {
314bb0cdb18Smillert 				if ((doNonWild &&
315bb0cdb18Smillert 				    !(e->flags & (MIN_STAR|HR_STAR))) ||
316bb0cdb18Smillert 				    (doWild && (e->flags & (MIN_STAR|HR_STAR))))
317df930be7Sderaadt 					job_add(e, u);
318df930be7Sderaadt 			}
319df930be7Sderaadt 		}
320df930be7Sderaadt 	}
321bb0cdb18Smillert }
322df930be7Sderaadt 
323f326c411Sderaadt /*
324f454ebdeSmillert  * Set StartTime and clockTime to the current time.
325f454ebdeSmillert  * These are used for computing what time it really is right now.
326f454ebdeSmillert  * Note that clockTime is a unix wallclock time converted to minutes.
327df930be7Sderaadt  */
328df930be7Sderaadt static void
set_time(int initialize)329785ecd14Stedu set_time(int initialize)
330785ecd14Stedu {
331e134e629Smillert 	struct tm tm;
33294db05edSmillert 	static int isdst;
333f454ebdeSmillert 
33494db05edSmillert 	StartTime = time(NULL);
33594db05edSmillert 
33694db05edSmillert 	/* We adjust the time to GMT so we can catch DST changes. */
337e134e629Smillert 	tm = *localtime(&StartTime);
338e134e629Smillert 	if (initialize || tm.tm_isdst != isdst) {
339e134e629Smillert 		isdst = tm.tm_isdst;
340e134e629Smillert 		GMToff = get_gmtoff(&StartTime, &tm);
34194db05edSmillert 	}
34240616ceaSmillert 	clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE;
343f326c411Sderaadt }
344f326c411Sderaadt 
345f326c411Sderaadt /*
346f454ebdeSmillert  * Try to just hit the next minute.
347f326c411Sderaadt  */
348f326c411Sderaadt static void
cron_sleep(time_t target,sigset_t * mask)349f3ce7c4eSmillert cron_sleep(time_t target, sigset_t *mask)
350785ecd14Stedu {
35115237702Smillert 	int fd, nfds;
3522b139a93Smillert 	unsigned char poke;
353f3ce7c4eSmillert 	struct timespec t1, t2, timeout;
354e134e629Smillert 	struct sockaddr_un s_un;
35515237702Smillert 	socklen_t sunlen;
35612c65670Sderaadt 	static struct pollfd pfd[1];
357b636719cSmillert 
358f3ce7c4eSmillert 	clock_gettime(CLOCK_REALTIME, &t1);
35915237702Smillert 	t1.tv_sec += GMToff;
360f3ce7c4eSmillert 	timeout.tv_sec = (target * SECONDS_PER_MINUTE - t1.tv_sec) + 1;
361*9c847fa8Smillert 	if (timeout.tv_sec < 0)
362*9c847fa8Smillert 		timeout.tv_sec = 0;
363f3ce7c4eSmillert 	timeout.tv_nsec = 0;
364df930be7Sderaadt 
36512c65670Sderaadt 	pfd[0].fd = cronSock;
36612c65670Sderaadt 	pfd[0].events = POLLIN;
367b636719cSmillert 
368f3ce7c4eSmillert 	while (timespecisset(&timeout) && timeout.tv_sec < 65) {
3691a16ac32Smillert 		poke = RELOAD_CRON | RELOAD_AT;
37012c65670Sderaadt 
3712b139a93Smillert 		/* Sleep until we time out, get a poke, or get a signal. */
372f3ce7c4eSmillert 		nfds = ppoll(pfd, 1, &timeout, mask);
373be425821Smillert 		switch (nfds) {
374be425821Smillert 		case -1:
375139e0acfSjca 			if (errno != EINTR && errno != EAGAIN) {
376139e0acfSjca 				syslog(LOG_ERR, "(CRON) DEATH (ppoll failure: %m)");
377139e0acfSjca 				exit(EXIT_FAILURE);
378139e0acfSjca 			}
379be425821Smillert 			if (errno == EINTR) {
380be425821Smillert 				if (got_sigchld) {
381be425821Smillert 					got_sigchld = 0;
382be425821Smillert 					sigchld_reaper();
383be425821Smillert 				}
384be425821Smillert 			}
385be425821Smillert 			break;
386be425821Smillert 		case 0:
387be425821Smillert 			/* done sleeping */
388be425821Smillert 			return;
389be425821Smillert 		default:
39070805688Smillert 			sunlen = sizeof(s_un);
3915ce06057Smillert 			fd = accept4(cronSock, (struct sockaddr *)&s_un,
3925ce06057Smillert 			    &sunlen, SOCK_NONBLOCK);
3935ce06057Smillert 			if (fd >= 0) {
3942b139a93Smillert 				(void) read(fd, &poke, 1);
39515237702Smillert 				close(fd);
39669386091Smillert 				if (poke & RELOAD_CRON) {
39795f2b4e2Smillert 					timespecclear(&database->mtime);
3982b139a93Smillert 					load_database(&database);
39969386091Smillert 				}
4002b139a93Smillert 				if (poke & RELOAD_AT) {
4012b139a93Smillert 					/*
4022b139a93Smillert 					 * We run any pending at jobs right
4032b139a93Smillert 					 * away so that "at now" really runs
4042b139a93Smillert 					 * jobs immediately.
4052b139a93Smillert 					 */
406f3ce7c4eSmillert 					clock_gettime(CLOCK_REALTIME, &t2);
40795f2b4e2Smillert 					timespecclear(&at_database->mtime);
4082b139a93Smillert 					if (scan_atjobs(&at_database, &t2))
4098a6f8ff3Smillert 						atrun(at_database,
4102b139a93Smillert 						    batch_maxload, t2.tv_sec);
4112b139a93Smillert 				}
4122b139a93Smillert 			}
41315237702Smillert 		}
41415237702Smillert 
4152b139a93Smillert 		/* Adjust tv and continue where we left off.  */
416f3ce7c4eSmillert 		clock_gettime(CLOCK_REALTIME, &t2);
41715237702Smillert 		t2.tv_sec += GMToff;
418f3ce7c4eSmillert 		timespecsub(&t2, &t1, &t1);
419f3ce7c4eSmillert 		timespecsub(&timeout, &t1, &timeout);
42015237702Smillert 		memcpy(&t1, &t2, sizeof(t1));
421f3ce7c4eSmillert 		if (timeout.tv_sec < 0)
422*9c847fa8Smillert 			timespecclear(&timeout);
42315237702Smillert 	}
424df930be7Sderaadt }
425df930be7Sderaadt 
42658a24ea1Smillert /* int open_socket(void)
42758a24ea1Smillert  *	opens a UNIX domain socket that crontab uses to poke cron.
42858a24ea1Smillert  *	If the socket is already in use, return an error.
42958a24ea1Smillert  */
43058a24ea1Smillert static int
open_socket(void)43158a24ea1Smillert open_socket(void)
43258a24ea1Smillert {
43358a24ea1Smillert 	int		   sock, rc;
43458a24ea1Smillert 	mode_t		   omask;
43558a24ea1Smillert 	struct sockaddr_un s_un;
43658a24ea1Smillert 
43758a24ea1Smillert 	sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
43858a24ea1Smillert 	if (sock == -1) {
439b8c5c73dSmillert 		warn("socket");
440a38a3395Smillert 		syslog(LOG_ERR, "(CRON) DEATH (can't create socket)");
44158a24ea1Smillert 		exit(EXIT_FAILURE);
44258a24ea1Smillert 	}
44358a24ea1Smillert 	bzero(&s_un, sizeof(s_un));
44488959323Smillert 	if (strlcpy(s_un.sun_path, _PATH_CRON_SOCK, sizeof(s_un.sun_path))
4456b36bf07Smillert 	    >= sizeof(s_un.sun_path)) {
446b8c5c73dSmillert 		warnc(ENAMETOOLONG, _PATH_CRON_SOCK);
447a38a3395Smillert 		syslog(LOG_ERR, "(CRON) DEATH (socket path too long)");
44858a24ea1Smillert 		exit(EXIT_FAILURE);
44958a24ea1Smillert 	}
45058a24ea1Smillert 	s_un.sun_family = AF_UNIX;
45158a24ea1Smillert 
45258a24ea1Smillert 	if (connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == 0) {
453b8c5c73dSmillert 		warnx("already running");
454a38a3395Smillert 		syslog(LOG_ERR, "(CRON) DEATH (already running)");
45558a24ea1Smillert 		exit(EXIT_FAILURE);
45658a24ea1Smillert 	}
45758a24ea1Smillert 	if (errno != ENOENT)
45858a24ea1Smillert 		unlink(s_un.sun_path);
45958a24ea1Smillert 
46058a24ea1Smillert 	omask = umask(007);
46158a24ea1Smillert 	rc = bind(sock, (struct sockaddr *)&s_un, sizeof(s_un));
46258a24ea1Smillert 	umask(omask);
46358a24ea1Smillert 	if (rc != 0) {
464b8c5c73dSmillert 		warn("bind");
465a38a3395Smillert 		syslog(LOG_ERR, "(CRON) DEATH (can't bind socket)");
46658a24ea1Smillert 		exit(EXIT_FAILURE);
46758a24ea1Smillert 	}
46858a24ea1Smillert 	if (listen(sock, SOMAXCONN)) {
469b8c5c73dSmillert 		warn("listen");
470a38a3395Smillert 		syslog(LOG_ERR, "(CRON) DEATH (can't listen on socket)");
47158a24ea1Smillert 		exit(EXIT_FAILURE);
47258a24ea1Smillert 	}
473a9e807deSmillert 
47488959323Smillert 	/* pledge won't let us change files to a foreign group. */
475a9e807deSmillert 	if (setegid(cron_gid) == 0) {
476a9e807deSmillert 		chown(s_un.sun_path, -1, cron_gid);
47788959323Smillert 		(void)setegid(getgid());
47888959323Smillert 	}
479a100c690Smillert 	chmod(s_un.sun_path, 0660);
48058a24ea1Smillert 
48158a24ea1Smillert 	return(sock);
48258a24ea1Smillert }
48358a24ea1Smillert 
484df930be7Sderaadt static void
sigchld_handler(int x)485785ecd14Stedu sigchld_handler(int x)
486785ecd14Stedu {
487f454ebdeSmillert 	got_sigchld = 1;
488f454ebdeSmillert }
489f454ebdeSmillert 
490f454ebdeSmillert static void
sigchld_reaper(void)491785ecd14Stedu sigchld_reaper(void)
492785ecd14Stedu {
493afc409ecSmillert 	int waiter;
494afc409ecSmillert 	pid_t pid;
495df930be7Sderaadt 
496f454ebdeSmillert 	do {
497df930be7Sderaadt 		pid = waitpid(-1, &waiter, WNOHANG);
498df930be7Sderaadt 		switch (pid) {
499df930be7Sderaadt 		case -1:
5007b12bc57Smillert 			if (errno == EINTR)
5017b12bc57Smillert 				continue;
502f454ebdeSmillert 			break;
503df930be7Sderaadt 		case 0:
504f454ebdeSmillert 			break;
505df930be7Sderaadt 		default:
5062e6cac80Smillert 			job_exit(pid);
507fd579382Smillert 			break;
508df930be7Sderaadt 		}
509f454ebdeSmillert 	} while (pid > 0);
510df930be7Sderaadt }
511df930be7Sderaadt 
512df930be7Sderaadt static void
parse_args(int argc,char * argv[])513785ecd14Stedu parse_args(int argc, char *argv[])
514785ecd14Stedu {
515df930be7Sderaadt 	int argch;
5162b139a93Smillert 	char *ep;
517df930be7Sderaadt 
518585d9be6Stedu 	while (-1 != (argch = getopt(argc, argv, "l:n"))) {
519df930be7Sderaadt 		switch (argch) {
5202b139a93Smillert 		case 'l':
5212b139a93Smillert 			errno = 0;
5222b139a93Smillert 			batch_maxload = strtod(optarg, &ep);
5232b139a93Smillert 			if (*ep != '\0' || ep == optarg || errno == ERANGE ||
5242b139a93Smillert 			    batch_maxload < 0) {
525b8c5c73dSmillert 				warnx("illegal load average: %s", optarg);
526df930be7Sderaadt 				usage();
5272b139a93Smillert 			}
5282b139a93Smillert 			break;
52913ae079dSmillert 		case 'n':
53013ae079dSmillert 			NoFork = 1;
53113ae079dSmillert 			break;
5322b139a93Smillert 		default:
5332b139a93Smillert 			usage();
534df930be7Sderaadt 		}
535df930be7Sderaadt 	}
536df930be7Sderaadt }
537