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
main(argc,argv)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 *
cmp(p,v)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
slp()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
ex(s)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
init()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
append(fn)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
number(c)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
reapchild()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