xref: /openbsd/usr.sbin/cron/cron.c (revision 9d64f211)
1 /*	$OpenBSD: cron.c,v 1.61 2015/11/04 12:53:05 millert Exp $	*/
2 
3 /* Copyright 1988,1990,1993,1994 by Paul Vixie
4  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #define	MAIN_PROGRAM
21 
22 #include "cron.h"
23 
24 enum timejump { negative, small, medium, large };
25 
26 static	void	usage(void),
27 		run_reboot_jobs(cron_db *),
28 		find_jobs(time_t, cron_db *, int, int),
29 		set_time(int),
30 		cron_sleep(time_t, sigset_t *),
31 		sigchld_handler(int),
32 		sighup_handler(int),
33 		sigchld_reaper(void),
34 		parse_args(int c, char *v[]);
35 
36 static	int	open_socket(void);
37 
38 static	volatile sig_atomic_t	got_sighup, got_sigchld;
39 static	time_t			timeRunning, virtualTime, clockTime;
40 static	int			cronSock;
41 static	long			GMToff;
42 static	cron_db			database;
43 static	at_db			at_database;
44 static	double			batch_maxload = BATCH_MAXLOAD;
45 static	int			NoFork;
46 static	time_t			StartTime;
47 
48 static void
49 usage(void)
50 {
51 
52 	fprintf(stderr, "usage: %s [-n] [-l load_avg]\n", ProgramName);
53 	exit(EXIT_FAILURE);
54 }
55 
56 int
57 main(int argc, char *argv[])
58 {
59 	struct sigaction sact;
60 	sigset_t blocked, omask;
61 
62 	ProgramName = argv[0];
63 
64 	setlocale(LC_ALL, "");
65 
66 	setvbuf(stdout, NULL, _IOLBF, 0);
67 	setvbuf(stderr, NULL, _IOLBF, 0);
68 
69 	parse_args(argc, argv);
70 
71 	bzero((char *)&sact, sizeof sact);
72 	sigemptyset(&sact.sa_mask);
73 	sact.sa_flags = 0;
74 	sact.sa_flags |= SA_RESTART;
75 	sact.sa_handler = sigchld_handler;
76 	(void) sigaction(SIGCHLD, &sact, NULL);
77 	sact.sa_handler = sighup_handler;
78 	(void) sigaction(SIGHUP, &sact, NULL);
79 	sact.sa_handler = SIG_IGN;
80 	(void) sigaction(SIGPIPE, &sact, NULL);
81 
82 	set_cron_cwd();
83 
84 	if (pledge("stdio rpath wpath cpath fattr getpw unix flock id dns proc exec",
85 	    NULL) == -1)
86 		log_it("CRON", getpid(), "pledge", strerror(errno));
87 
88 	cronSock = open_socket();
89 
90 	if (putenv("PATH="_PATH_DEFPATH) < 0) {
91 		log_it("CRON", getpid(), "DEATH", "can't malloc");
92 		exit(EXIT_FAILURE);
93 	}
94 
95 	if (NoFork == 0) {
96 		if (daemon(1, 0) == -1) {
97 			log_it("CRON",getpid(),"DEATH","can't fork");
98 			exit(EXIT_FAILURE);
99 		}
100 		log_it("CRON",getpid(),"STARTUP",CRON_VERSION);
101 	}
102 
103 	database.head = NULL;
104 	database.tail = NULL;
105 	database.mtime = 0;
106 	load_database(&database);
107 	at_database.head = NULL;
108 	at_database.tail = NULL;
109 	at_database.mtime = 0;
110 	scan_atjobs(&at_database, NULL);
111 	set_time(TRUE);
112 	run_reboot_jobs(&database);
113 	timeRunning = virtualTime = clockTime;
114 
115 	/*
116 	 * We block SIGHUP and SIGCHLD while running jobs and receive them
117 	 * only while sleeping in ppoll().  This ensures no signal is lost.
118 	 */
119 	sigemptyset(&blocked);
120 	sigaddset(&blocked, SIGCHLD);
121 	sigaddset(&blocked, SIGHUP);
122 	sigprocmask(SIG_BLOCK, &blocked, &omask);
123 
124 	/*
125 	 * Too many clocks, not enough time (Al. Einstein)
126 	 * These clocks are in minutes since the epoch, adjusted for timezone.
127 	 * virtualTime: is the time it *would* be if we woke up
128 	 * promptly and nobody ever changed the clock. It is
129 	 * monotonically increasing... unless a timejump happens.
130 	 * At the top of the loop, all jobs for 'virtualTime' have run.
131 	 * timeRunning: is the time we last awakened.
132 	 * clockTime: is the time when set_time was last called.
133 	 */
134 	while (TRUE) {
135 		int timeDiff;
136 		enum timejump wakeupKind;
137 
138 		/* ... wait for the time (in minutes) to change ... */
139 		do {
140 			cron_sleep(timeRunning + 1, &omask);
141 			set_time(FALSE);
142 		} while (clockTime == timeRunning);
143 		timeRunning = clockTime;
144 
145 		/*
146 		 * Calculate how the current time differs from our virtual
147 		 * clock.  Classify the change into one of 4 cases.
148 		 */
149 		timeDiff = timeRunning - virtualTime;
150 
151 		/* shortcut for the most common case */
152 		if (timeDiff == 1) {
153 			virtualTime = timeRunning;
154 			find_jobs(virtualTime, &database, TRUE, TRUE);
155 		} else {
156 			if (timeDiff > (3*MINUTE_COUNT) ||
157 			    timeDiff < -(3*MINUTE_COUNT))
158 				wakeupKind = large;
159 			else if (timeDiff > 5)
160 				wakeupKind = medium;
161 			else if (timeDiff > 0)
162 				wakeupKind = small;
163 			else
164 				wakeupKind = negative;
165 
166 			switch (wakeupKind) {
167 			case small:
168 				/*
169 				 * case 1: timeDiff is a small positive number
170 				 * (wokeup late) run jobs for each virtual
171 				 * minute until caught up.
172 				 */
173 				do {
174 					if (job_runqueue())
175 						sleep(10);
176 					virtualTime++;
177 					find_jobs(virtualTime, &database,
178 					    TRUE, TRUE);
179 				} while (virtualTime < timeRunning);
180 				break;
181 
182 			case medium:
183 				/*
184 				 * case 2: timeDiff is a medium-sized positive
185 				 * number, for example because we went to DST
186 				 * run wildcard jobs once, then run any
187 				 * fixed-time jobs that would otherwise be
188 				 * skipped if we use up our minute (possible,
189 				 * if there are a lot of jobs to run) go
190 				 * around the loop again so that wildcard jobs
191 				 * have a chance to run, and we do our
192 				 * housekeeping.
193 				 */
194 				/* run wildcard jobs for current minute */
195 				find_jobs(timeRunning, &database, TRUE, FALSE);
196 
197 				/* run fixed-time jobs for each minute missed */
198 				do {
199 					if (job_runqueue())
200 						sleep(10);
201 					virtualTime++;
202 					find_jobs(virtualTime, &database,
203 					    FALSE, TRUE);
204 					set_time(FALSE);
205 				} while (virtualTime< timeRunning &&
206 				    clockTime == timeRunning);
207 				break;
208 
209 			case negative:
210 				/*
211 				 * case 3: timeDiff is a small or medium-sized
212 				 * negative num, eg. because of DST ending.
213 				 * Just run the wildcard jobs. The fixed-time
214 				 * jobs probably have already run, and should
215 				 * not be repeated.  Virtual time does not
216 				 * change until we are caught up.
217 				 */
218 				find_jobs(timeRunning, &database, TRUE, FALSE);
219 				break;
220 			default:
221 				/*
222 				 * other: time has changed a *lot*,
223 				 * jump virtual time, and run everything
224 				 */
225 				virtualTime = timeRunning;
226 				find_jobs(timeRunning, &database, TRUE, TRUE);
227 			}
228 		}
229 
230 		/* Jobs to be run (if any) are loaded; clear the queue. */
231 		job_runqueue();
232 
233 		/* Run any jobs in the at queue. */
234 		atrun(&at_database, batch_maxload,
235 		    timeRunning * SECONDS_PER_MINUTE - GMToff);
236 
237 		/* Reload jobs as needed. */
238 		load_database(&database);
239 		scan_atjobs(&at_database, NULL);
240 	}
241 }
242 
243 static void
244 run_reboot_jobs(cron_db *db)
245 {
246 	user *u;
247 	entry *e;
248 
249 	for (u = db->head; u != NULL; u = u->next) {
250 		for (e = u->crontab; e != NULL; e = e->next) {
251 			if (e->flags & WHEN_REBOOT)
252 				job_add(e, u);
253 		}
254 	}
255 	(void) job_runqueue();
256 }
257 
258 static void
259 find_jobs(time_t vtime, cron_db *db, int doWild, int doNonWild)
260 {
261 	time_t virtualSecond  = vtime * SECONDS_PER_MINUTE;
262 	struct tm *tm = gmtime(&virtualSecond);
263 	int minute, hour, dom, month, dow;
264 	user *u;
265 	entry *e;
266 
267 	/* make 0-based values out of these so we can use them as indices
268 	 */
269 	minute = tm->tm_min -FIRST_MINUTE;
270 	hour = tm->tm_hour -FIRST_HOUR;
271 	dom = tm->tm_mday -FIRST_DOM;
272 	month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
273 	dow = tm->tm_wday -FIRST_DOW;
274 
275 	/* the dom/dow situation is odd.  '* * 1,15 * Sun' will run on the
276 	 * first and fifteenth AND every Sunday;  '* * * * Sun' will run *only*
277 	 * on Sundays;  '* * 1,15 * *' will run *only* the 1st and 15th.  this
278 	 * is why we keep 'e->dow_star' and 'e->dom_star'.  yes, it's bizarre.
279 	 * like many bizarre things, it's the standard.
280 	 */
281 	for (u = db->head; u != NULL; u = u->next) {
282 		for (e = u->crontab; e != NULL; e = e->next) {
283 			if (bit_test(e->minute, minute) &&
284 			    bit_test(e->hour, hour) &&
285 			    bit_test(e->month, month) &&
286 			    ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
287 			      ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
288 			      : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
289 			    )
290 			   ) {
291 				if ((doNonWild &&
292 				    !(e->flags & (MIN_STAR|HR_STAR))) ||
293 				    (doWild && (e->flags & (MIN_STAR|HR_STAR))))
294 					job_add(e, u);
295 			}
296 		}
297 	}
298 }
299 
300 /*
301  * Set StartTime and clockTime to the current time.
302  * These are used for computing what time it really is right now.
303  * Note that clockTime is a unix wallclock time converted to minutes.
304  */
305 static void
306 set_time(int initialize)
307 {
308 	struct tm tm;
309 	static int isdst;
310 
311 	StartTime = time(NULL);
312 
313 	/* We adjust the time to GMT so we can catch DST changes. */
314 	tm = *localtime(&StartTime);
315 	if (initialize || tm.tm_isdst != isdst) {
316 		isdst = tm.tm_isdst;
317 		GMToff = get_gmtoff(&StartTime, &tm);
318 	}
319 	clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE;
320 }
321 
322 /*
323  * Try to just hit the next minute.
324  */
325 static void
326 cron_sleep(time_t target, sigset_t *mask)
327 {
328 	int fd, nfds;
329 	unsigned char poke;
330 	struct timespec t1, t2, timeout;
331 	struct sockaddr_un s_un;
332 	socklen_t sunlen;
333 	static struct pollfd pfd[1];
334 
335 	clock_gettime(CLOCK_REALTIME, &t1);
336 	t1.tv_sec += GMToff;
337 	timeout.tv_sec = (target * SECONDS_PER_MINUTE - t1.tv_sec) + 1;
338 	timeout.tv_nsec = 0;
339 
340 	pfd[0].fd = cronSock;
341 	pfd[0].events = POLLIN;
342 
343 	while (timespecisset(&timeout) && timeout.tv_sec < 65) {
344 		poke = RELOAD_CRON | RELOAD_AT;
345 
346 		/* Sleep until we time out, get a poke, or get a signal. */
347 		nfds = ppoll(pfd, 1, &timeout, mask);
348 		if (nfds == 0)
349 			break;		/* timer expired */
350 		if (nfds == -1 && errno != EINTR)
351 			break;		/* an error occurred */
352 		if (nfds > 0) {
353 			sunlen = sizeof(s_un);
354 			fd = accept4(cronSock, (struct sockaddr *)&s_un,
355 			    &sunlen, SOCK_NONBLOCK);
356 			if (fd >= 0) {
357 				(void) read(fd, &poke, 1);
358 				close(fd);
359 				if (poke & RELOAD_CRON) {
360 					database.mtime = 0;
361 					load_database(&database);
362 				}
363 				if (poke & RELOAD_AT) {
364 					/*
365 					 * We run any pending at jobs right
366 					 * away so that "at now" really runs
367 					 * jobs immediately.
368 					 */
369 					clock_gettime(CLOCK_REALTIME, &t2);
370 					at_database.mtime = 0;
371 					if (scan_atjobs(&at_database, &t2))
372 						atrun(&at_database,
373 						    batch_maxload, t2.tv_sec);
374 				}
375 			}
376 		} else {
377 			/* Interrupted by a signal. */
378 			if (got_sighup) {
379 				got_sighup = 0;
380 				log_close();
381 			}
382 			if (got_sigchld) {
383 				got_sigchld = 0;
384 				sigchld_reaper();
385 			}
386 		}
387 
388 		/* Adjust tv and continue where we left off.  */
389 		clock_gettime(CLOCK_REALTIME, &t2);
390 		t2.tv_sec += GMToff;
391 		timespecsub(&t2, &t1, &t1);
392 		timespecsub(&timeout, &t1, &timeout);
393 		memcpy(&t1, &t2, sizeof(t1));
394 		if (timeout.tv_sec < 0)
395 			timeout.tv_sec = 0;
396 		if (timeout.tv_nsec < 0)
397 			timeout.tv_nsec = 0;
398 	}
399 }
400 
401 /* int open_socket(void)
402  *	opens a UNIX domain socket that crontab uses to poke cron.
403  *	If the socket is already in use, return an error.
404  */
405 static int
406 open_socket(void)
407 {
408 	int		   sock, rc;
409 	mode_t		   omask;
410 	struct sockaddr_un s_un;
411 
412 	sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
413 	if (sock == -1) {
414 		fprintf(stderr, "%s: can't create socket: %s\n",
415 		    ProgramName, strerror(errno));
416 		log_it("CRON", getpid(), "DEATH", "can't create socket");
417 		exit(EXIT_FAILURE);
418 	}
419 	bzero(&s_un, sizeof(s_un));
420 	if (snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s/%s",
421 	      SPOOL_DIR, CRONSOCK) >= sizeof(s_un.sun_path)) {
422 		fprintf(stderr, "%s/%s: path too long\n", SPOOL_DIR, CRONSOCK);
423 		log_it("CRON", getpid(), "DEATH", "path too long");
424 		exit(EXIT_FAILURE);
425 	}
426 	s_un.sun_family = AF_UNIX;
427 
428 	if (connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == 0) {
429 		fprintf(stderr, "%s: already running\n", ProgramName);
430 		log_it("CRON", getpid(), "DEATH", "already running");
431 		exit(EXIT_FAILURE);
432 	}
433 	if (errno != ENOENT)
434 		unlink(s_un.sun_path);
435 
436 	omask = umask(007);
437 	rc = bind(sock, (struct sockaddr *)&s_un, sizeof(s_un));
438 	umask(omask);
439 	if (rc != 0) {
440 		fprintf(stderr, "%s: can't bind socket: %s\n",
441 		    ProgramName, strerror(errno));
442 		log_it("CRON", getpid(), "DEATH", "can't bind socket");
443 		exit(EXIT_FAILURE);
444 	}
445 	if (listen(sock, SOMAXCONN)) {
446 		fprintf(stderr, "%s: can't listen on socket: %s\n",
447 		    ProgramName, strerror(errno));
448 		log_it("CRON", getpid(), "DEATH", "can't listen on socket");
449 		exit(EXIT_FAILURE);
450 	}
451 	chmod(s_un.sun_path, 0660);
452 
453 	return(sock);
454 }
455 
456 static void
457 sighup_handler(int x)
458 {
459 	got_sighup = 1;
460 }
461 
462 static void
463 sigchld_handler(int x)
464 {
465 	got_sigchld = 1;
466 }
467 
468 static void
469 sigchld_reaper(void)
470 {
471 	int waiter;
472 	pid_t pid;
473 
474 	do {
475 		pid = waitpid(-1, &waiter, WNOHANG);
476 		switch (pid) {
477 		case -1:
478 			if (errno == EINTR)
479 				continue;
480 			break;
481 		case 0:
482 			break;
483 		default:
484 			break;
485 		}
486 	} while (pid > 0);
487 }
488 
489 static void
490 parse_args(int argc, char *argv[])
491 {
492 	int argch;
493 	char *ep;
494 
495 	while (-1 != (argch = getopt(argc, argv, "l:n"))) {
496 		switch (argch) {
497 		case 'l':
498 			errno = 0;
499 			batch_maxload = strtod(optarg, &ep);
500 			if (*ep != '\0' || ep == optarg || errno == ERANGE ||
501 			    batch_maxload < 0) {
502 				fprintf(stderr, "Illegal load average: %s\n",
503 				    optarg);
504 				usage();
505 			}
506 			break;
507 		case 'n':
508 			NoFork = 1;
509 			break;
510 		default:
511 			usage();
512 		}
513 	}
514 }
515