xref: /original-bsd/usr.sbin/cron/cron.c (revision 210ce081)
1 #ifndef lint
2 static char *sccsid = "@(#)cron.c	4.16 (Berkeley) 06/29/90";
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 	daemon(0, 0);
83 	(void) signal(SIGHUP, SIG_IGN);
84 	(void) signal(SIGINT, SIG_IGN);
85 	(void) signal(SIGQUIT, SIG_IGN);
86 	(void) signal(SIGCHLD, reapchild);
87 	(void) time(&itime);
88 	itime -= localtime(&itime)->tm_sec;
89 
90 	for (;; itime+=60, slp()) {
91 		struct stat cstat, lcstat;
92 		int newcron, newloc;
93 
94 		newcron = 0;
95 		if (stat(crontab, &cstat) < 0)
96 		    cstat.st_mtime = 1;
97 		if (cstat.st_mtime != filetime) {
98 			filetime = cstat.st_mtime;
99 			newcron++;
100 		}
101 
102 		newloc  = 0;
103 		if (stat(loc_crontab, &lcstat) < 0)
104 		    lcstat.st_mtime = 1;
105 		if (lcstat.st_mtime != lfiletime) {
106 			lfiletime = lcstat.st_mtime;
107 			newloc++;
108 		}
109 
110 		if (newcron || newloc) {
111 			init();
112 			append(crontab);
113 			append(loc_crontab);
114 			*listend++ = EOS;
115 			*listend++ = EOS;
116 		}
117 
118 		loct = localtime(&itime);
119 		loct->tm_mon++;		 /* 1-12 for month */
120 		if (loct->tm_wday == 0)
121 			loct->tm_wday = 7;	/* sunday is 7, not 0 */
122 		for(cp = list; *cp != EOS;) {
123 			flag = 0;
124 			cp = cmp(cp, loct->tm_min);
125 			cp = cmp(cp, loct->tm_hour);
126 			cp = cmp(cp, loct->tm_mday);
127 			cp = cmp(cp, loct->tm_mon);
128 			cp = cmp(cp, loct->tm_wday);
129 			if(flag == 0)
130 				ex(cp);
131 			while(*cp++ != 0)
132 				;
133 		}
134 	}
135 }
136 
137 char *
138 cmp(p, v)
139 char *p;
140 {
141 	register char *cp;
142 
143 	cp = p;
144 	switch(*cp++) {
145 
146 	case EXACT:
147 		if (*cp++ != v)
148 			flag++;
149 		return(cp);
150 
151 	case ANY:
152 		return(cp);
153 
154 	case LIST:
155 		while(*cp != LIST)
156 			if(*cp++ == v) {
157 				while(*cp++ != LIST)
158 					;
159 				return(cp);
160 			}
161 		flag++;
162 		return(cp+1);
163 
164 	case RANGE:
165 		if(*cp > v || cp[1] < v)
166 			flag++;
167 		return(cp+2);
168 	}
169 	if(cp[-1] != v)
170 		flag++;
171 	return(cp);
172 }
173 
174 slp()
175 {
176 	register i;
177 	time_t t;
178 
179 	(void) time(&t);
180 	i = itime - t;
181 	if(i < -60 * 60 || i > 60 * 60) {
182 		itime = t;
183 		i = 60 - localtime(&itime)->tm_sec;
184 		itime += i;
185 	}
186 	if(i > 0)
187 		sleep((u_int)i);
188 }
189 
190 ex(s)
191 char *s;
192 {
193 	register struct passwd *pwd;
194 	char user[BUFSIZ];
195 	char *c = user;
196 	int pid;
197 
198 	switch (fork()) {
199 	case 0:
200 		break;
201 	case -1:
202 		syslog(LOG_ERR, "cannot fork: %m (running %.40s%s)",
203 			s, strlen(s) > 40 ? "..." : "");
204 		/*FALLTHROUGH*/
205 	default:
206 		return;
207 	}
208 	pid = getpid();
209 	while(*s != ' ' && *s != '\t')
210 		*c++ = *s++;
211 	*c = '\0';
212 	s++;
213 	if ((pwd = getpwnam(user)) == NULL) {
214 		syslog(LOG_ERR, "invalid user name \"%s\"", user);
215 		dprintf(debug, "%d: cannot find %s\n", pid, user),
216 			fflush(debug);
217 		exit(1);
218 	}
219 	(void) setgid(pwd->pw_gid);
220 	(void) initgroups(pwd->pw_name, pwd->pw_gid);
221 	(void) setuid(pwd->pw_uid);
222 	(void) freopen("/", "r", stdin);
223 	closelog();
224 	dprintf(debug, "%d: executing %s", pid, s), fflush (debug);
225 	execl(_PATH_BSHELL, "sh", "-c", s, 0);
226 	syslog(LOG_ERR, "cannot exec %s: %m");
227 	dprintf(debug, "%d: cannot execute sh\n", pid), fflush (debug);
228 	exit(0);
229 }
230 
231 init()
232 {
233 	/*
234 	 * Don't free in case was longer than LISTS.  Trades off
235 	 * the rare case of crontab shrinking vs. the common case of
236 	 * extra realloc's needed in append() for a large crontab.
237 	 */
238 	if (list == 0) {
239 		list = malloc(LISTS);
240 		listsize = LISTS;
241 	}
242 	listend = list;
243 }
244 
245 append(fn)
246 char *fn;
247 {
248 	register i, c;
249 	register char *cp;
250 	register char *ocp;
251 	register int n;
252 
253 	if (freopen(fn, "r", stdin) == NULL)
254 		return;
255 	cp = listend;
256 loop:
257 	if(cp > list+listsize-MAXLIN) {
258 		int length = cp - list;
259 
260 		listsize += LISTS;
261 		list = realloc(list, listsize);
262 		cp = list + length;
263 	}
264 	ocp = cp;
265 	for(i=0;; i++) {
266 		do
267 			c = getchar();
268 		while(c == ' ' || c == '\t')
269 			;
270 		if(c == EOF || c == '\n')
271 			goto ignore;
272 		if(i == 5)
273 			break;
274 		if(c == '*') {
275 			*cp++ = ANY;
276 			continue;
277 		}
278 		if ((n = number(c)) < 0)
279 			goto ignore;
280 		c = getchar();
281 		if(c == ',')
282 			goto mlist;
283 		if(c == '-')
284 			goto mrange;
285 		if(c != '\t' && c != ' ')
286 			goto ignore;
287 		*cp++ = EXACT;
288 		*cp++ = n;
289 		continue;
290 
291 	mlist:
292 		*cp++ = LIST;
293 		*cp++ = n;
294 		do {
295 			if ((n = number(getchar())) < 0)
296 				goto ignore;
297 			*cp++ = n;
298 			c = getchar();
299 		} while (c==',');
300 		if(c != '\t' && c != ' ')
301 			goto ignore;
302 		*cp++ = LIST;
303 		continue;
304 
305 	mrange:
306 		*cp++ = RANGE;
307 		*cp++ = n;
308 		if ((n = number(getchar())) < 0)
309 			goto ignore;
310 		c = getchar();
311 		if(c != '\t' && c != ' ')
312 			goto ignore;
313 		*cp++ = n;
314 	}
315 	while(c != '\n') {
316 		if(c == EOF)
317 			goto ignore;
318 		if(c == '%')
319 			c = '\n';
320 		*cp++ = c;
321 		c = getchar();
322 	}
323 	*cp++ = '\n';
324 	*cp++ = 0;
325 	goto loop;
326 
327 ignore:
328 	cp = ocp;
329 	while(c != '\n') {
330 		if(c == EOF) {
331 			(void) fclose(stdin);
332 			listend = cp;
333 			return;
334 		}
335 		c = getchar();
336 	}
337 	goto loop;
338 }
339 
340 number(c)
341 register c;
342 {
343 	register n = 0;
344 
345 	while (isdigit(c)) {
346 		n = n*10 + c - '0';
347 		c = getchar();
348 	}
349 	(void) ungetc(c, stdin);
350 	if (n>=100)
351 		return(-1);
352 	return(n);
353 }
354 
355 reapchild()
356 {
357 	union wait status;
358 	int pid;
359 
360 	while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0)
361 		dprintf(debug, "%d: child exits with signal %d status %d\n",
362 			pid, status.w_termsig, status.w_retcode),
363 			fflush (debug);
364 }
365