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