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