12311Sjkh/* Copyright 1988,1990,1993,1994 by Paul Vixie
22311Sjkh * All rights reserved
32311Sjkh *
42311Sjkh * Distribute freely, except: don't remove my name from the source or
52311Sjkh * documentation (don't take credit for my work), mark your changes (don't
62311Sjkh * get me blamed for your possible bugs), don't alter or remove this
72311Sjkh * notice.  May be sold if buildable source is provided to buyer.  No
82311Sjkh * warrantee of any kind, express or implied, is included with this
92311Sjkh * software; use at your own risk, responsibility for damages (if any) to
102311Sjkh * anyone resulting from the use of this software rests entirely with the
112311Sjkh * user.
122311Sjkh *
132311Sjkh * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
142311Sjkh * I'll try to keep a version up to date.  I can be reached as follows:
152311Sjkh * Paul Vixie          <paul@vix.com>          uunet!decwrl!vixie!paul
162311Sjkh */
172311Sjkh
182311Sjkh#if !defined(lint) && !defined(LINT)
1929452Scharnierstatic const char rcsid[] =
2050479Speter  "$FreeBSD$";
212311Sjkh#endif
222311Sjkh
232311Sjkh#define	MAIN_PROGRAM
242311Sjkh
252311Sjkh
262311Sjkh#include "cron.h"
27199804Sattilio#include <sys/mman.h>
282311Sjkh#include <sys/signal.h>
292311Sjkh#if SYS_TIME_H
302311Sjkh# include <sys/time.h>
312311Sjkh#else
322311Sjkh# include <time.h>
332311Sjkh#endif
342311Sjkh
352311Sjkh
36173412Skevlostatic	void	usage(void),
37173412Skevlo		run_reboot_jobs(cron_db *),
38242101Ssobomax		cron_tick(cron_db *, int),
39242101Ssobomax		cron_sync(int),
40242101Ssobomax		cron_sleep(cron_db *, int),
41173412Skevlo		cron_clean(cron_db *),
422311Sjkh#ifdef USE_SIGCHLD
43173412Skevlo		sigchld_handler(int),
442311Sjkh#endif
45173412Skevlo		sighup_handler(int),
46173412Skevlo		parse_args(int c, char *v[]);
472311Sjkh
48242101Ssobomaxstatic int	run_at_secres(cron_db *);
49242101Ssobomax
5074010Sbabkinstatic time_t	last_time = 0;
5174010Sbabkinstatic int	dst_enabled = 0;
52149430Spjdstruct pidfh *pfh;
532311Sjkh
542311Sjkhstatic void
552311Sjkhusage() {
56241124Spluknet#if DEBUGGING
5716859Swosch    char **dflags;
58241124Spluknet#endif
5916859Swosch
60129280Syar	fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] "
61180096Smarck			"[-m mailto] [-s] [-o] [-x debugflag[,...]]\n");
62241124Spluknet#if DEBUGGING
6316859Swosch	fprintf(stderr, "\ndebugflags: ");
6416859Swosch
6516859Swosch        for(dflags = DebugFlagNames; *dflags; dflags++) {
6616859Swosch		fprintf(stderr, "%s ", *dflags);
6716859Swosch	}
6816859Swosch        fprintf(stderr, "\n");
69241124Spluknet#endif
7016859Swosch
712311Sjkh	exit(ERROR_EXIT);
722311Sjkh}
732311Sjkh
74149430Spjdstatic void
75149430Spjdopen_pidfile(void)
76149430Spjd{
77149430Spjd	char	pidfile[MAX_FNAME];
78149430Spjd	char	buf[MAX_TEMPSTR];
79149430Spjd	int	otherpid;
802311Sjkh
81149430Spjd	(void) snprintf(pidfile, sizeof(pidfile), PIDFILE, PIDDIR);
82150214Spjd	pfh = pidfile_open(pidfile, 0600, &otherpid);
83149430Spjd	if (pfh == NULL) {
84149430Spjd		if (errno == EEXIST) {
85149430Spjd			snprintf(buf, sizeof(buf),
86149430Spjd			    "cron already running, pid: %d", otherpid);
87149430Spjd		} else {
88149430Spjd			snprintf(buf, sizeof(buf),
89149430Spjd			    "can't open or create %s: %s", pidfile,
90149430Spjd			    strerror(errno));
91149430Spjd		}
92149430Spjd		log_it("CRON", getpid(), "DEATH", buf);
93149430Spjd		errx(ERROR_EXIT, "%s", buf);
94149430Spjd	}
95149430Spjd}
96149430Spjd
972311Sjkhint
982311Sjkhmain(argc, argv)
992311Sjkh	int	argc;
1002311Sjkh	char	*argv[];
1012311Sjkh{
1022311Sjkh	cron_db	database;
103242101Ssobomax	int runnum;
104242101Ssobomax	int secres1, secres2;
105242101Ssobomax	struct tm *tm;
10671407Sbabkin
1072311Sjkh	ProgramName = argv[0];
1082311Sjkh
1092311Sjkh#if defined(BSD)
1102311Sjkh	setlinebuf(stdout);
1112311Sjkh	setlinebuf(stderr);
1122311Sjkh#endif
1132311Sjkh
1142311Sjkh	parse_args(argc, argv);
1152311Sjkh
1162311Sjkh#ifdef USE_SIGCHLD
1172311Sjkh	(void) signal(SIGCHLD, sigchld_handler);
1182311Sjkh#else
1192311Sjkh	(void) signal(SIGCLD, SIG_IGN);
1202311Sjkh#endif
1212311Sjkh	(void) signal(SIGHUP, sighup_handler);
1222311Sjkh
123149430Spjd	open_pidfile();
1242311Sjkh	set_cron_uid();
1252311Sjkh	set_cron_cwd();
1262311Sjkh
1272311Sjkh#if defined(POSIX)
1282311Sjkh	setenv("PATH", _PATH_DEFPATH, 1);
1292311Sjkh#endif
1302311Sjkh
1312311Sjkh	/* if there are no debug flags turned on, fork as a daemon should.
1322311Sjkh	 */
1332311Sjkh# if DEBUGGING
1342311Sjkh	if (DebugFlags) {
1352311Sjkh# else
1362311Sjkh	if (0) {
1372311Sjkh# endif
1382311Sjkh		(void) fprintf(stderr, "[%d] cron started\n", getpid());
1392311Sjkh	} else {
14073955Speter		if (daemon(1, 0) == -1) {
141149430Spjd			pidfile_remove(pfh);
14273955Speter			log_it("CRON",getpid(),"DEATH","can't become daemon");
1432311Sjkh			exit(0);
1442311Sjkh		}
1452311Sjkh	}
1462311Sjkh
147199804Sattilio	if (madvise(NULL, 0, MADV_PROTECT) != 0)
148199804Sattilio		log_it("CRON", getpid(), "WARNING", "madvise() failed");
149199804Sattilio
150149430Spjd	pidfile_write(pfh);
1512311Sjkh	database.head = NULL;
1522311Sjkh	database.tail = NULL;
1532311Sjkh	database.mtime = (time_t) 0;
1542311Sjkh	load_database(&database);
155242101Ssobomax	secres1 = secres2 = run_at_secres(&database);
1562311Sjkh	run_reboot_jobs(&database);
157242101Ssobomax	cron_sync(secres1);
158242101Ssobomax	runnum = 0;
1592311Sjkh	while (TRUE) {
1602311Sjkh# if DEBUGGING
16116859Swosch	    /* if (!(DebugFlags & DTEST)) */
1622311Sjkh# endif /*DEBUGGING*/
163242101Ssobomax			cron_sleep(&database, secres1);
1642311Sjkh
165242101Ssobomax		if (secres1 == 0 || runnum % 60 == 0) {
166242101Ssobomax			load_database(&database);
167242101Ssobomax			secres2 = run_at_secres(&database);
168242101Ssobomax			if (secres2 != secres1) {
169242101Ssobomax				secres1 = secres2;
170242101Ssobomax				if (secres1 != 0) {
171242101Ssobomax					runnum = 0;
172242101Ssobomax				} else {
173242101Ssobomax					/*
174242101Ssobomax					 * Going from 1 sec to 60 sec res. If we
175242101Ssobomax					 * are already at minute's boundary, so
176242101Ssobomax					 * let it run, otherwise schedule for the
177242101Ssobomax					 * next minute.
178242101Ssobomax					 */
179242101Ssobomax					tm = localtime(&TargetTime);
180242101Ssobomax					if (tm->tm_sec > 0)  {
181242101Ssobomax						cron_sync(secres2);
182242101Ssobomax						continue;
183242101Ssobomax					}
184242101Ssobomax				}
185242101Ssobomax			}
186242101Ssobomax		}
1872311Sjkh
1882311Sjkh		/* do this iteration
1892311Sjkh		 */
190242101Ssobomax		cron_tick(&database, secres1);
1912311Sjkh
192242101Ssobomax		/* sleep 1 or 60 seconds
1932311Sjkh		 */
194242101Ssobomax		TargetTime += (secres1 != 0) ? 1 : 60;
195242101Ssobomax		runnum += 1;
1962311Sjkh	}
1972311Sjkh}
1982311Sjkh
1992311Sjkh
2002311Sjkhstatic void
2012311Sjkhrun_reboot_jobs(db)
2022311Sjkh	cron_db *db;
2032311Sjkh{
2042311Sjkh	register user		*u;
2052311Sjkh	register entry		*e;
2062311Sjkh
2072311Sjkh	for (u = db->head;  u != NULL;  u = u->next) {
2082311Sjkh		for (e = u->crontab;  e != NULL;  e = e->next) {
2092311Sjkh			if (e->flags & WHEN_REBOOT) {
2102311Sjkh				job_add(e, u);
2112311Sjkh			}
2122311Sjkh		}
2132311Sjkh	}
2142311Sjkh	(void) job_runqueue();
2152311Sjkh}
2162311Sjkh
2172311Sjkh
2182311Sjkhstatic void
219242101Ssobomaxcron_tick(cron_db *db, int secres)
2202311Sjkh{
22174010Sbabkin	static struct tm	lasttm;
22274010Sbabkin	static time_t	diff = 0, /* time difference in seconds from the last offset change */
22374010Sbabkin		difflimit = 0; /* end point for the time zone correction */
22474010Sbabkin	struct tm	otztm; /* time in the old time zone */
225242101Ssobomax	int		otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow;
2262311Sjkh 	register struct tm	*tm = localtime(&TargetTime);
227242101Ssobomax	register int		second, minute, hour, dom, month, dow;
2282311Sjkh	register user		*u;
2292311Sjkh	register entry		*e;
2302311Sjkh
2312311Sjkh	/* make 0-based values out of these so we can use them as indicies
2322311Sjkh	 */
233242101Ssobomax	second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND;
2342311Sjkh	minute = tm->tm_min -FIRST_MINUTE;
2352311Sjkh	hour = tm->tm_hour -FIRST_HOUR;
2362311Sjkh	dom = tm->tm_mday -FIRST_DOM;
2372311Sjkh	month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
2382311Sjkh	dow = tm->tm_wday -FIRST_DOW;
2392311Sjkh
240242101Ssobomax	Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n",
241242101Ssobomax		getpid(), second, minute, hour, dom, month, dow))
2422311Sjkh
24374010Sbabkin	if (dst_enabled && last_time != 0
24474010Sbabkin	&& TargetTime > last_time /* exclude stepping back */
24574010Sbabkin	&& tm->tm_gmtoff != lasttm.tm_gmtoff ) {
24674010Sbabkin
24774010Sbabkin		diff = tm->tm_gmtoff - lasttm.tm_gmtoff;
24874010Sbabkin
24974010Sbabkin		if ( diff > 0 ) { /* ST->DST */
25074010Sbabkin			/* mark jobs for an earlier run */
25174010Sbabkin			difflimit = TargetTime + diff;
25274010Sbabkin			for (u = db->head;  u != NULL;  u = u->next) {
25374010Sbabkin				for (e = u->crontab;  e != NULL;  e = e->next) {
25474010Sbabkin					e->flags &= ~NOT_UNTIL;
25574010Sbabkin					if ( e->lastrun >= TargetTime )
25674010Sbabkin						e->lastrun = 0;
25774010Sbabkin					/* not include the ends of hourly ranges */
25874010Sbabkin					if ( e->lastrun < TargetTime - 3600 )
25974010Sbabkin						e->flags |= RUN_AT;
26074010Sbabkin					else
26174010Sbabkin						e->flags &= ~RUN_AT;
26274010Sbabkin				}
26374010Sbabkin			}
26474010Sbabkin		} else { /* diff < 0 : DST->ST */
26574010Sbabkin			/* mark jobs for skipping */
26674010Sbabkin			difflimit = TargetTime - diff;
26774010Sbabkin			for (u = db->head;  u != NULL;  u = u->next) {
26874010Sbabkin				for (e = u->crontab;  e != NULL;  e = e->next) {
26974010Sbabkin					e->flags |= NOT_UNTIL;
27074010Sbabkin					e->flags &= ~RUN_AT;
27174010Sbabkin				}
27274010Sbabkin			}
27374010Sbabkin		}
27474010Sbabkin	}
27574010Sbabkin
27674010Sbabkin	if (diff != 0) {
27774010Sbabkin		/* if the time was reset of the end of special zone is reached */
27874010Sbabkin		if (last_time == 0 || TargetTime >= difflimit) {
27974010Sbabkin			/* disable the TZ switch checks */
28074010Sbabkin			diff = 0;
28174010Sbabkin			difflimit = 0;
28274010Sbabkin			for (u = db->head;  u != NULL;  u = u->next) {
28374010Sbabkin				for (e = u->crontab;  e != NULL;  e = e->next) {
28474010Sbabkin					e->flags &= ~(RUN_AT|NOT_UNTIL);
28574010Sbabkin				}
28674010Sbabkin			}
28774010Sbabkin		} else {
28874010Sbabkin			/* get the time in the old time zone */
28974010Sbabkin			time_t difftime = TargetTime + tm->tm_gmtoff - diff;
29074010Sbabkin			gmtime_r(&difftime, &otztm);
29174010Sbabkin
29274010Sbabkin			/* make 0-based values out of these so we can use them as indicies
29374010Sbabkin			 */
294242101Ssobomax			otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND;
29574010Sbabkin			otzminute = otztm.tm_min -FIRST_MINUTE;
29674010Sbabkin			otzhour = otztm.tm_hour -FIRST_HOUR;
29774010Sbabkin			otzdom = otztm.tm_mday -FIRST_DOM;
29874010Sbabkin			otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
29974010Sbabkin			otzdow = otztm.tm_wday -FIRST_DOW;
30074010Sbabkin		}
30174010Sbabkin	}
30274010Sbabkin
3032311Sjkh	/* the dom/dow situation is odd.  '* * 1,15 * Sun' will run on the
3042311Sjkh	 * first and fifteenth AND every Sunday;  '* * * * Sun' will run *only*
3052311Sjkh	 * on Sundays;  '* * 1,15 * *' will run *only* the 1st and 15th.  this
3062311Sjkh	 * is why we keep 'e->dow_star' and 'e->dom_star'.  yes, it's bizarre.
3072311Sjkh	 * like many bizarre things, it's the standard.
3082311Sjkh	 */
3092311Sjkh	for (u = db->head;  u != NULL;  u = u->next) {
3102311Sjkh		for (e = u->crontab;  e != NULL;  e = e->next) {
3112311Sjkh			Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
3122311Sjkh					  env_get("LOGNAME", e->envp),
3132311Sjkh					  e->uid, e->gid, e->cmd))
31474010Sbabkin
31574010Sbabkin			if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
316242101Ssobomax				if (bit_test(e->second, otzsecond)
317242101Ssobomax				 && bit_test(e->minute, otzminute)
31874010Sbabkin				 && bit_test(e->hour, otzhour)
31974010Sbabkin				 && bit_test(e->month, otzmonth)
32074010Sbabkin				 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
32174010Sbabkin					  ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom))
32274010Sbabkin					  : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom))
32374010Sbabkin					)
32474010Sbabkin				   ) {
32574010Sbabkin					if ( e->flags & RUN_AT ) {
32674010Sbabkin						e->flags &= ~RUN_AT;
32774010Sbabkin						e->lastrun = TargetTime;
32874010Sbabkin						job_add(e, u);
32974010Sbabkin						continue;
33074010Sbabkin					} else
33174010Sbabkin						e->flags &= ~NOT_UNTIL;
33274010Sbabkin				} else if ( e->flags & NOT_UNTIL )
33374010Sbabkin					continue;
33474010Sbabkin			}
33574010Sbabkin
336242101Ssobomax			if (bit_test(e->second, second)
337242101Ssobomax			 && bit_test(e->minute, minute)
3382311Sjkh			 && bit_test(e->hour, hour)
3392311Sjkh			 && bit_test(e->month, month)
3402311Sjkh			 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
3412311Sjkh			      ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
3422311Sjkh			      : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
3432311Sjkh			    )
3442311Sjkh			   ) {
34574010Sbabkin				e->flags &= ~RUN_AT;
34674010Sbabkin				e->lastrun = TargetTime;
3472311Sjkh				job_add(e, u);
3482311Sjkh			}
3492311Sjkh		}
3502311Sjkh	}
35174010Sbabkin
35274010Sbabkin	last_time = TargetTime;
35374010Sbabkin	lasttm = *tm;
3542311Sjkh}
3552311Sjkh
3562311Sjkh
3572311Sjkh/* the task here is to figure out how long it's going to be until :00 of the
3582311Sjkh * following minute and initialize TargetTime to this value.  TargetTime
3592311Sjkh * will subsequently slide 60 seconds at a time, with correction applied
3602311Sjkh * implicitly in cron_sleep().  it would be nice to let cron execute in
3612311Sjkh * the "current minute" before going to sleep, but by restarting cron you
3622311Sjkh * could then get it to execute a given minute's jobs more than once.
3632311Sjkh * instead we have the chance of missing a minute's jobs completely, but
3642311Sjkh * that's something sysadmin's know to expect what with crashing computers..
3652311Sjkh */
3662311Sjkhstatic void
367242101Ssobomaxcron_sync(int secres) {
368242101Ssobomax 	struct tm *tm;
3692311Sjkh
370241672Ssobomax	TargetTime = time((time_t*)0);
371242101Ssobomax	if (secres != 0) {
372242101Ssobomax		TargetTime += 1;
373242101Ssobomax	} else {
374242101Ssobomax		tm = localtime(&TargetTime);
375242101Ssobomax		TargetTime += (60 - tm->tm_sec);
376242101Ssobomax	}
3772311Sjkh}
3782311Sjkh
379242101Ssobomaxstatic int
380242101Ssobomaxtimespec_subtract(struct timespec *result, struct timespec *x,
381242101Ssobomax    struct timespec *y)
382242101Ssobomax{
383242101Ssobomax	time_t nsec;
3842311Sjkh
385242101Ssobomax	/* Perform the carry for the later subtraction by updating y. */
386242101Ssobomax	if (x->tv_nsec < y->tv_nsec) {
387242101Ssobomax		nsec = (y->tv_nsec - x->tv_nsec) / 10000000 + 1;
388242101Ssobomax		y->tv_nsec -= 1000000000 * nsec;
389242101Ssobomax		y->tv_sec += nsec;
390242101Ssobomax	}
391242101Ssobomax	if (x->tv_nsec - y->tv_nsec > 1000000000) {
392242101Ssobomax		nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
393242101Ssobomax		y->tv_nsec += 1000000000 * nsec;
394242101Ssobomax		y->tv_sec -= nsec;
395242101Ssobomax	}
396242101Ssobomax
397242101Ssobomax	/* tv_nsec is certainly positive. */
398242101Ssobomax	result->tv_sec = x->tv_sec - y->tv_sec;
399242101Ssobomax	result->tv_nsec = x->tv_nsec - y->tv_nsec;
400242101Ssobomax
401242101Ssobomax	/* Return True if result is negative. */
402242101Ssobomax	return (x->tv_sec < y->tv_sec);
403242101Ssobomax}
404242101Ssobomax
4052311Sjkhstatic void
406242101Ssobomaxcron_sleep(cron_db *db, int secres)
40774010Sbabkin{
408242101Ssobomax	int seconds_to_wait;
409242101Ssobomax	int rval;
410242101Ssobomax	struct timespec ctime, ttime, stime, remtime;
4112311Sjkh
41241723Sdillon	/*
41341723Sdillon	 * Loop until we reach the top of the next minute, sleep when possible.
41441723Sdillon	 */
41541723Sdillon
41641723Sdillon	for (;;) {
417242101Ssobomax		clock_gettime(CLOCK_REALTIME, &ctime);
418242101Ssobomax		ttime.tv_sec = TargetTime;
419242101Ssobomax		ttime.tv_nsec = 0;
420242101Ssobomax		timespec_subtract(&stime, &ttime, &ctime);
42141723Sdillon
42241723Sdillon		/*
42341723Sdillon		 * If the seconds_to_wait value is insane, jump the cron
42441723Sdillon		 */
42541723Sdillon
426242101Ssobomax		if (stime.tv_sec < -600 || stime.tv_sec > 600) {
42774010Sbabkin			cron_clean(db);
428242101Ssobomax			cron_sync(secres);
42941723Sdillon			continue;
43041723Sdillon		}
43141723Sdillon
432242101Ssobomax		seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 :
433242101Ssobomax		    stime.tv_sec;
434242101Ssobomax
4352311Sjkh		Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
43637450Sbde			getpid(), (long)TargetTime, seconds_to_wait))
4372311Sjkh
43841723Sdillon		/*
43941723Sdillon		 * If we've run out of wait time or there are no jobs left
44041723Sdillon		 * to run, break
4412311Sjkh		 */
4422311Sjkh
443242101Ssobomax		if (stime.tv_sec < 0)
44441723Sdillon			break;
44541723Sdillon		if (job_runqueue() == 0) {
44641723Sdillon			Debug(DSCH, ("[%d] sleeping for %d seconds\n",
44741723Sdillon				getpid(), seconds_to_wait))
44841723Sdillon
449242101Ssobomax			for (;;) {
450242101Ssobomax				rval = nanosleep(&stime, &remtime);
451242101Ssobomax				if (rval == 0 || errno != EINTR)
452242101Ssobomax					break;
453242101Ssobomax				stime.tv_sec = remtime.tv_sec;
454242101Ssobomax				stime.tv_nsec = remtime.tv_nsec;
455242101Ssobomax			}
45641723Sdillon		}
4572311Sjkh	}
4582311Sjkh}
4592311Sjkh
4602311Sjkh
46174010Sbabkin/* if the time was changed abruptly, clear the flags related
46274010Sbabkin * to the daylight time switch handling to avoid strange effects
46374010Sbabkin */
46474010Sbabkin
46574010Sbabkinstatic void
46674010Sbabkincron_clean(db)
46774010Sbabkin	cron_db	*db;
46874010Sbabkin{
46974010Sbabkin	user		*u;
47074010Sbabkin	entry		*e;
47174010Sbabkin
47274010Sbabkin	last_time = 0;
47374010Sbabkin
47474010Sbabkin	for (u = db->head;  u != NULL;  u = u->next) {
47574010Sbabkin		for (e = u->crontab;  e != NULL;  e = e->next) {
47674010Sbabkin			e->flags &= ~(RUN_AT|NOT_UNTIL);
47774010Sbabkin		}
47874010Sbabkin	}
47974010Sbabkin}
48074010Sbabkin
4812311Sjkh#ifdef USE_SIGCHLD
4822311Sjkhstatic void
483160521Sstefanfsigchld_handler(int x)
484160521Sstefanf{
4852311Sjkh	WAIT_T		waiter;
4862311Sjkh	PID_T		pid;
4872311Sjkh
4882311Sjkh	for (;;) {
4892311Sjkh#ifdef POSIX
4902311Sjkh		pid = waitpid(-1, &waiter, WNOHANG);
4912311Sjkh#else
4922311Sjkh		pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
4932311Sjkh#endif
4942311Sjkh		switch (pid) {
4952311Sjkh		case -1:
4962311Sjkh			Debug(DPROC,
4972311Sjkh				("[%d] sigchld...no children\n", getpid()))
4982311Sjkh			return;
4992311Sjkh		case 0:
5002311Sjkh			Debug(DPROC,
5012311Sjkh				("[%d] sigchld...no dead kids\n", getpid()))
5022311Sjkh			return;
5032311Sjkh		default:
5042311Sjkh			Debug(DPROC,
5052311Sjkh				("[%d] sigchld...pid #%d died, stat=%d\n",
5062311Sjkh				getpid(), pid, WEXITSTATUS(waiter)))
5072311Sjkh		}
5082311Sjkh	}
5092311Sjkh}
5102311Sjkh#endif /*USE_SIGCHLD*/
5112311Sjkh
5122311Sjkh
5132311Sjkhstatic void
514160521Sstefanfsighup_handler(int x)
515160521Sstefanf{
5162311Sjkh	log_close();
5172311Sjkh}
5182311Sjkh
5192311Sjkh
5202311Sjkhstatic void
5212311Sjkhparse_args(argc, argv)
5222311Sjkh	int	argc;
5232311Sjkh	char	*argv[];
5242311Sjkh{
5252311Sjkh	int	argch;
526129280Syar	char	*endp;
5272311Sjkh
528180096Smarck	while ((argch = getopt(argc, argv, "j:J:m:osx:")) != -1) {
5292311Sjkh		switch (argch) {
530129280Syar		case 'j':
531129280Syar			Jitter = strtoul(optarg, &endp, 10);
532129280Syar			if (*optarg == '\0' || *endp != '\0' || Jitter > 60)
533129280Syar				errx(ERROR_EXIT,
534129280Syar				     "bad value for jitter: %s", optarg);
535129280Syar			break;
536129280Syar		case 'J':
537129280Syar			RootJitter = strtoul(optarg, &endp, 10);
538129280Syar			if (*optarg == '\0' || *endp != '\0' || RootJitter > 60)
539129280Syar				errx(ERROR_EXIT,
540129280Syar				     "bad value for root jitter: %s", optarg);
541129280Syar			break;
542180096Smarck		case 'm':
543180096Smarck			defmailto = optarg;
544180096Smarck			break;
54574010Sbabkin		case 'o':
54674010Sbabkin			dst_enabled = 0;
54774010Sbabkin			break;
54874010Sbabkin		case 's':
54974010Sbabkin			dst_enabled = 1;
55074010Sbabkin			break;
5512311Sjkh		case 'x':
5522311Sjkh			if (!set_debug_flags(optarg))
5532311Sjkh				usage();
5542311Sjkh			break;
55516859Swosch		default:
55616859Swosch			usage();
5572311Sjkh		}
5582311Sjkh	}
5592311Sjkh}
56074010Sbabkin
561242101Ssobomaxstatic int
562242101Ssobomaxrun_at_secres(cron_db *db)
563242101Ssobomax{
564242101Ssobomax	user *u;
565242101Ssobomax	entry *e;
566242101Ssobomax
567242101Ssobomax	for (u = db->head;  u != NULL;  u = u->next) {
568242101Ssobomax		for (e = u->crontab;  e != NULL;  e = e->next) {
569242101Ssobomax			if ((e->flags & SEC_RES) != 0)
570242101Ssobomax				return 1;
571242101Ssobomax		}
572242101Ssobomax	}
573242101Ssobomax	return 0;
574242101Ssobomax}
575