xref: /original-bsd/usr.sbin/cron/cron.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1986, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1986, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)cron.c	8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17 
18 #include <sys/types.h>
19 #include <sys/signal.h>
20 #include <sys/time.h>
21 #include <sys/stat.h>
22 #include <sys/wait.h>
23 #include <sys/ioctl.h>
24 #include <sys/file.h>
25 #include <sys/resource.h>
26 #include <pwd.h>
27 #include <fcntl.h>
28 #include <syslog.h>
29 #include <stdio.h>
30 #include <ctype.h>
31 #include "pathnames.h"
32 
33 #define	LISTS	(2*BUFSIZ)
34 #define	MAXLIN	BUFSIZ
35 
36 #define	EXACT	100
37 #define	ANY	101
38 #define	LIST	102
39 #define	RANGE	103
40 #define	EOS	104
41 
42 char	crontab[]	= _PATH_CRON;
43 char	loc_crontab[]   = _PATH_LCRON;
44 time_t	itime, time();
45 struct	tm *loct;
46 struct	tm *localtime();
47 char	*malloc();
48 char	*realloc();
49 void	reapchild();
50 int	flag;
51 char	*list;
52 char	*listend;
53 unsigned listsize;
54 
55 FILE	*debug;
56 #define dprintf if (debug) fprintf
57 
58 main(argc, argv)
59 	int argc;
60 	char **argv;
61 {
62 	register char *cp;
63 	char *cmp();
64 	time_t filetime = 0;
65 	time_t lfiletime = 0;
66 	char c;
67 	extern char *optarg;
68 
69 	if (geteuid()) {
70 		fprintf(stderr, "cron: NOT super-user\n");
71 		exit(1);
72 	}
73 
74 	openlog("cron", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_DAEMON);
75 	switch (fork()) {
76 
77 	case -1:
78 		syslog(LOG_ERR, "fork: %m");
79 		exit(1);
80 		/* NOTREACHED */
81 	case 0:
82 		break;
83 	default:
84 		exit(0);
85 		/* NOTREACHED */
86 	}
87 
88 	c = getopt(argc, argv, "d:");
89 	if (c == 'd') {
90 		debug = fopen(optarg, "w");
91 		if (debug == NULL)
92 			exit(1);
93 		(void) fcntl(fileno(debug), F_SETFL, FAPPEND);
94 	}
95 	daemon(0, 0);
96 	(void) signal(SIGHUP, SIG_IGN);
97 	(void) signal(SIGINT, SIG_IGN);
98 	(void) signal(SIGQUIT, SIG_IGN);
99 	(void) signal(SIGCHLD, reapchild);
100 	(void) time(&itime);
101 	itime -= localtime(&itime)->tm_sec;
102 
103 	for (;; itime+=60, slp()) {
104 		struct stat cstat, lcstat;
105 		int newcron, newloc;
106 
107 		newcron = 0;
108 		if (stat(crontab, &cstat) < 0)
109 		    cstat.st_mtime = 1;
110 		if (cstat.st_mtime != filetime) {
111 			filetime = cstat.st_mtime;
112 			newcron++;
113 		}
114 
115 		newloc  = 0;
116 		if (stat(loc_crontab, &lcstat) < 0)
117 		    lcstat.st_mtime = 1;
118 		if (lcstat.st_mtime != lfiletime) {
119 			lfiletime = lcstat.st_mtime;
120 			newloc++;
121 		}
122 
123 		if (newcron || newloc) {
124 			init();
125 			append(crontab);
126 			append(loc_crontab);
127 			*listend++ = EOS;
128 			*listend++ = EOS;
129 		}
130 
131 		loct = localtime(&itime);
132 		loct->tm_mon++;		 /* 1-12 for month */
133 		if (loct->tm_wday == 0)
134 			loct->tm_wday = 7;	/* sunday is 7, not 0 */
135 		for(cp = list; *cp != EOS;) {
136 			flag = 0;
137 			cp = cmp(cp, loct->tm_min);
138 			cp = cmp(cp, loct->tm_hour);
139 			cp = cmp(cp, loct->tm_mday);
140 			cp = cmp(cp, loct->tm_mon);
141 			cp = cmp(cp, loct->tm_wday);
142 			if(flag == 0)
143 				ex(cp);
144 			while(*cp++ != 0)
145 				;
146 		}
147 	}
148 }
149 
150 char *
151 cmp(p, v)
152 char *p;
153 {
154 	register char *cp;
155 
156 	cp = p;
157 	switch(*cp++) {
158 
159 	case EXACT:
160 		if (*cp++ != v)
161 			flag++;
162 		return(cp);
163 
164 	case ANY:
165 		return(cp);
166 
167 	case LIST:
168 		while(*cp != LIST)
169 			if(*cp++ == v) {
170 				while(*cp++ != LIST)
171 					;
172 				return(cp);
173 			}
174 		flag++;
175 		return(cp+1);
176 
177 	case RANGE:
178 		if(*cp > v || cp[1] < v)
179 			flag++;
180 		return(cp+2);
181 	}
182 	if(cp[-1] != v)
183 		flag++;
184 	return(cp);
185 }
186 
187 slp()
188 {
189 	register i;
190 	time_t t;
191 
192 	(void) time(&t);
193 	i = itime - t;
194 	if(i < -60 * 60 || i > 60 * 60) {
195 		itime = t;
196 		i = 60 - localtime(&itime)->tm_sec;
197 		itime += i;
198 	}
199 	if(i > 0)
200 		sleep((u_int)i);
201 }
202 
203 ex(s)
204 char *s;
205 {
206 	register struct passwd *pwd;
207 	char user[BUFSIZ];
208 	char *c = user;
209 	int pid;
210 
211 	switch (fork()) {
212 	case 0:
213 		break;
214 	case -1:
215 		syslog(LOG_ERR, "cannot fork: %m (running %.40s%s)",
216 			s, strlen(s) > 40 ? "..." : "");
217 		/*FALLTHROUGH*/
218 	default:
219 		return;
220 	}
221 	pid = getpid();
222 	while(*s != ' ' && *s != '\t')
223 		*c++ = *s++;
224 	*c = '\0';
225 	s++;
226 	if ((pwd = getpwnam(user)) == NULL) {
227 		syslog(LOG_ERR, "invalid user name \"%s\"", user);
228 		dprintf(debug, "%d: cannot find %s\n", pid, user),
229 			fflush(debug);
230 		exit(1);
231 	}
232 	(void) setgid(pwd->pw_gid);
233 	(void) initgroups(pwd->pw_name, pwd->pw_gid);
234 	(void) setuid(pwd->pw_uid);
235 	(void) freopen("/", "r", stdin);
236 	closelog();
237 	dprintf(debug, "%d: executing %s", pid, s), fflush (debug);
238 	execl(_PATH_BSHELL, "sh", "-c", s, 0);
239 	syslog(LOG_ERR, "cannot exec %s: %m");
240 	dprintf(debug, "%d: cannot execute sh\n", pid), fflush (debug);
241 	exit(0);
242 }
243 
244 init()
245 {
246 	/*
247 	 * Don't free in case was longer than LISTS.  Trades off
248 	 * the rare case of crontab shrinking vs. the common case of
249 	 * extra realloc's needed in append() for a large crontab.
250 	 */
251 	if (list == 0) {
252 		list = malloc(LISTS);
253 		listsize = LISTS;
254 	}
255 	listend = list;
256 }
257 
258 append(fn)
259 char *fn;
260 {
261 	register i, c;
262 	register char *cp;
263 	register char *ocp;
264 	register int n;
265 
266 	if (freopen(fn, "r", stdin) == NULL)
267 		return;
268 	cp = listend;
269 loop:
270 	if(cp > list+listsize-MAXLIN) {
271 		int length = cp - list;
272 
273 		listsize += LISTS;
274 		list = realloc(list, listsize);
275 		cp = list + length;
276 	}
277 	ocp = cp;
278 	for(i=0;; i++) {
279 		do
280 			c = getchar();
281 		while(c == ' ' || c == '\t')
282 			;
283 		if(c == EOF || c == '\n')
284 			goto ignore;
285 		if(i == 5)
286 			break;
287 		if(c == '*') {
288 			*cp++ = ANY;
289 			continue;
290 		}
291 		if ((n = number(c)) < 0)
292 			goto ignore;
293 		c = getchar();
294 		if(c == ',')
295 			goto mlist;
296 		if(c == '-')
297 			goto mrange;
298 		if(c != '\t' && c != ' ')
299 			goto ignore;
300 		*cp++ = EXACT;
301 		*cp++ = n;
302 		continue;
303 
304 	mlist:
305 		*cp++ = LIST;
306 		*cp++ = n;
307 		do {
308 			if ((n = number(getchar())) < 0)
309 				goto ignore;
310 			*cp++ = n;
311 			c = getchar();
312 		} while (c==',');
313 		if(c != '\t' && c != ' ')
314 			goto ignore;
315 		*cp++ = LIST;
316 		continue;
317 
318 	mrange:
319 		*cp++ = RANGE;
320 		*cp++ = n;
321 		if ((n = number(getchar())) < 0)
322 			goto ignore;
323 		c = getchar();
324 		if(c != '\t' && c != ' ')
325 			goto ignore;
326 		*cp++ = n;
327 	}
328 	while(c != '\n') {
329 		if(c == EOF)
330 			goto ignore;
331 		if(c == '%')
332 			c = '\n';
333 		*cp++ = c;
334 		c = getchar();
335 	}
336 	*cp++ = '\n';
337 	*cp++ = 0;
338 	goto loop;
339 
340 ignore:
341 	cp = ocp;
342 	while(c != '\n') {
343 		if(c == EOF) {
344 			(void) fclose(stdin);
345 			listend = cp;
346 			return;
347 		}
348 		c = getchar();
349 	}
350 	goto loop;
351 }
352 
353 number(c)
354 register c;
355 {
356 	register n = 0;
357 
358 	while (isdigit(c)) {
359 		n = n*10 + c - '0';
360 		c = getchar();
361 	}
362 	(void) ungetc(c, stdin);
363 	if (n>=100)
364 		return(-1);
365 	return(n);
366 }
367 
368 void
369 reapchild()
370 {
371 	union wait status;
372 	int pid;
373 
374 	while ((pid = wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0)
375 		dprintf(debug, "%d: child exits with signal %d status %d\n",
376 			pid, status.w_termsig, status.w_retcode),
377 			fflush (debug);
378 }
379