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)
19static const char rcsid[] =
20  "$FreeBSD: stable/10/usr.sbin/cron/cron/cron.c 321237 2017-07-19 19:41:13Z ngie $";
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
36static	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
48static int	run_at_secres(cron_db *);
49
50static time_t	last_time = 0;
51static int	dst_enabled = 0;
52static int	dont_daemonize = 0;
53struct pidfh *pfh;
54
55static void
56usage() {
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
75static void
76open_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
98int
99main(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
201static void
202run_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
219static void
220cron_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 indicies
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 indicies
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 */
367static void
368cron_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
380static void
381timespec_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
393static void
394cron_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
453static void
454cron_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
470static void
471sigchld_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
501static void
502sighup_handler(int x)
503{
504	log_close();
505}
506
507
508static void
509parse_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
552static int
553run_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