|
|
1.1 root 1: /*
2: * The information contained herein is a trade secret of Mark Williams
3: * Company, and is confidential information. It is provided under a
4: * license agreement, and may be copied or disclosed only under the
5: * terms of that agreement. Any reproduction or disclosure of this
6: * material without the express written authorization of Mark Williams
7: * Company or persuant to the license agreement is unlawful.
8: *
9: * An unpublished work by Mark Williams Company, Chicago.
10: * All rights reserved.
11: */
12:
13: /* cron.c
14: *
15: * Execute commands stored in /usr/lib/crontab if it exist (3.2.0 compatibility)
16: * as daemon.
17: * Otherwise execute crontabs from /usr/spool/cron/crontabs.
18: * It sets uid and gid to user which crontab is going to be executed.
19: *
20: * $ 12-10-1991 vlad (Vladimir Smelyansky)
21: *
22: */
23: #include <stdio.h>
24: #include <time.h>
25: #include <signal.h>
26: #include <sys/msig.h>
27: #include <errno.h>
28: #include <ctype.h>
29: #include <pwd.h>
30: #include <sys/types.h>
31: #include <dirent.h>
32: #include "cron.h"
33:
34: /*
35: * Time field types.
36: */
37: #define MIN 0
38: #define HOUR 1
39: #define MDAY 2
40: #define MON 3
41: #define WDAY 4
42:
43: /*
44: * (Finite) States in valid().
45: */
46: #define START 0
47: #define INT 1
48: #define INTDASH 2
49: #define RANGE 3
50: #define STR 4
51: #define STRPLUS 5
52: #define ERR 6
53: #define END 7
54: #define GLOB ('*')
55:
56: /*
57: * Tokens returned by gettoken(). Tokens are integers. EOF is also a token.
58: * INT, STR and GLOB above are also tokens.
59: */
60: #define DASH ('-')
61: #define COMMA (',')
62: #define WS (' ')
63: #define PERCENT (-2)
64: #define NEWLINE (-3)
65:
66: extern DIR *opendir();
67: extern struct dirent *readdir();
68: extern char *realloc();
69:
70: extern int set_uid(); /* Set UID */
71: extern FILE *fpOpenTable(); /* Open crintable */
72: extern int lock(); /* Write a lock file */
73: extern FILE *cronpipe(); /* Popen for cron */
74:
75: extern void mail_entry(); /* Send mail to user if command failed */
76: extern child_id *add_entry(), /* Add an new entry to a link list */
77: *find_entry(), /* Find an entry in the link list */
78: *del_entry(); /* Remove an entry from the link list */
79:
80: child_id *current; /* Pointer to the current structure */
81: char acRealUser[MAX_UNAME]; /* Real user name */
82:
83: int mailFlag = TRUE;
84: int lock_flag = FALSE;
85: int flag320 = FALSE;
86:
87: struct tm *tm;
88: time_t clock;
89: int tmfield[5];
90: FILE *f;
91: extern int errno;
92:
93: int ugtokflag = FALSE;
94: int ugtoken;
95: int ufetflag = FALSE;
96: int ufetval;
97: int set_uid_flag = FALSE;
98:
99: char *tokbuf;
100: int buflen = MAX_STR_LEN;
101: char crontab[] = "/usr/lib/crontab";
102: DIR *dirp = NULL;
103: struct dirent *dp;
104:
105: main()
106: {
107: int n;
108: int pid;
109: int child_pid;
110:
111: sigsetup();
112: #if !DEBUG
113: /* Disassociate from controling terminal and process
114: * group. Close all open files.
115: */
116: bedaemon();
117: #endif
118:
119: tokbuf = malloc(buflen);
120:
121: /* Check if crond chould be into cron 320 mode */
122: if ((f = fopen(crontab, "r")) != NULL) {
123: fclose(f);
124: chdir("/bin"); /* Under 3.2.0 cron started from /bin. */
125: flag320 = TRUE;
126: }
127: time(&clock);
128: tm = localtime(&clock);
129: alarm(61 - tm->tm_sec);
130:
131: Dprint("Time is %s\n", ctime(&clock));
132: for (;;) {
133: /* Put the time values into tmfield[] for easy reference.
134: * localtime() gives tm_mon in the range 0-11, tm_wday, 0-6
135: * (0=sunday). These are adjusted for cron's syntax.
136: */
137: tmfield[MIN] = tm->tm_min;
138: tmfield[HOUR] = tm->tm_hour;
139: tmfield[MDAY] = tm->tm_mday;
140: tmfield[MON] = tm->tm_mon + 1;
141: tmfield[WDAY] = tm->tm_wday;
142:
143: if (flag320 == TRUE) { /* Run 320 mode */
144: if ((f = fopen(crontab, "r")) != NULL) {
145: Dprint("File pointer is 0x%x\n", f);
146: strcpy(acRealUser, DAEMON);
147: while (tex() != EOF)
148: ;
149: fclose(f);
150: while (wait(&n) != -1)
151: ;
152: if (errno == ECHILD)
153: pause();
154: } else
155: fprintf(stderr, "crond: cannot open %s\n",
156: crontab);
157: } else { /* Run SV mode */
158: if (lock_flag == FALSE) {/* We didn't lock yet */
159: /* Check if cron was fired. Do it only for SV cron. */
160: if (lock(F_LOCK) == FALSE) {
161: fprintf(stderr, "crond: locked.\n");
162: exit(1);
163: }
164: lock_flag = TRUE;
165: }
166: if (dirp == NULL) /* Open dirp only once */
167: if ((dirp = opendir(D_SPOOL)) == NULL) {
168: fprintf(stderr,
169: "crond: cannot open directory %s.\n",
170: D_SPOOL);
171: /*exit(1);*/
172: }
173: while ((dp = readdir(dirp)) != NULL) {
174: /* Skip '.' and '..' */
175: if (!strcmp(dp->d_name, ".") ||
176: !strcmp(dp->d_name, ".."))
177: continue;
178:
179: strcpy(acRealUser, dp->d_name);
180: Dprint("User name is %s\n", acRealUser);
181: set_uid_flag = FALSE;
182:
183: if ((pid = fork()) < 0) {
184: fprintf(stderr, "crond: cannot fork\n");
185: /*exit(1);*/
186: }
187: if (pid == 0) { /* Child */
188: if ((f = fpOpenTable("r")) == NULL) {
189: fprintf(stderr,
190: "crond: cannot open table '%s'\n", acRealUser);
191: exit(1);
192: }
193: while (tex() != EOF)
194: ;
195: fclose(f);
196: /* wait for grandchildren */
197: while ((child_pid = wait(&n)) != -1) {
198: static child_id *pstDone;
199:
200: Dprint("\nmain: child id %d",
201: child_pid);
202: Dprint("\treturn is %d\n", n);
203: pstDone = find_entry(child_pid);
204: if (n && (mailFlag == TRUE))
205: mail_entry(pstDone);
206: current = del_entry(pstDone);
207: } /* while wait */
208: exit(0);
209: } /*if child*/
210: } /* while readdir */
211: rewinddir(dirp);
212: /* Wait for the children */
213: while (wait(&n) != -1)
214: ;
215: if (errno == ECHILD)
216: pause();
217:
218: } /* if 320 */
219: } /* for */
220: } /* main */
221:
222: /*
223: * Test and Execute: tests a crontab entry against the time fields in `tm' to
224: * see if it should be executed, if so it executes. f is left pointing to the
225: * next entry (line). Returns EOF when encountered, something else otherwise.
226: */
227: tex()
228: {
229: register int fieldnum;
230:
231: for (fieldnum = MIN; fieldnum <= WDAY; ++fieldnum)
232: if (!valid(fieldnum))
233: return (skip_it());
234: return (do_it());
235: }
236:
237: /*
238: * Valid(fieldnum) parses the next time field from the current line in crontab
239: * and checks whether tmfield[fieldnum] satisfies the constraints of that time
240: * field. Returns TRUE if so, FALSE if not. Leaves f at the next field.
241: * Detects syntax errors in the time fields.
242: */
243: valid(fieldnum)
244: int fieldnum;
245: {
246: register int t;
247: register int ival;
248: register int state = START;
249: int ival2;
250: int tm_val = tmfield[fieldnum];
251:
252: if (fieldnum == MIN) {
253: while ((t = gettoken()) == WS || t == NEWLINE)
254: ;
255: ungettoken(t);
256: }
257:
258: for (;;) {
259: t = gettoken();
260: switch (state) {
261: case START:
262: switch (t) {
263: case INT:
264: if (strlen(tokbuf) <= 2) {
265: ival = atoi(tokbuf);
266: state = INT;
267: }
268: else
269: state = ERR;
270: break;
271: case GLOB:
272: state = GLOB;
273: break;
274: case WS:
275: state = END;
276: break;
277: default:
278: state = ERR;
279: break;
280: }
281: break;
282: case INT:
283: if (t == COMMA || t == WS) {
284: if (ival == tm_val) {
285: while (t != WS)
286: t = gettoken();
287: return (TRUE);
288: }
289: state = (t == WS) ? END : START;
290: }
291: else if (t == DASH)
292: state = INTDASH;
293: else
294: state = ERR;
295: break;
296: case INTDASH:
297: if (t == INT && strlen(tokbuf) <= 2) {
298: ival2 = atoi(tokbuf);
299: state = RANGE;
300: }
301: else
302: state = ERR;
303: break;
304: case RANGE:
305: if (t == COMMA || t == WS) {
306: if (ival <= tm_val && tm_val <= ival2) {
307: while (t != WS)
308: t = gettoken();
309: return (TRUE);
310: }
311: state = (t == WS) ? END : START;
312: }
313: else
314: state = ERR;
315: break;
316: case GLOB:
317: if (t == COMMA || t == WS) {
318: while (t != WS)
319: t = gettoken();
320: return (TRUE);
321: }
322: else
323: state = ERR;
324: break;
325: } /* End switch on state */
326:
327: if (state == END)
328: return (FALSE);
329: if (state == ERR) {
330: ungettoken(t);
331: ungettoken(skip_it());
332: return (FALSE);
333: }
334: }
335: }
336:
337:
338: ungettoken(t)
339: int t;
340: {
341: ugtokflag = TRUE;
342: ugtoken = t;
343: }
344:
345: gettoken()
346: {
347: register int c;
348: register char *sp = tokbuf;
349: register char *mark;
350: int posn;
351:
352: if (ugtokflag) {
353: ugtokflag = FALSE;
354: return (ugtoken);
355: }
356:
357: switch (c = fetch()) {
358: case NEWLINE:
359: case EOF:
360: case PERCENT:
361: return (c);
362: case ' ':
363: case '\t':
364: while ((c = fetch()) == ' ' || c == '\t')
365: ;
366: unfetch(c);
367: return (WS);
368: case '-':
369: return (DASH);
370: case ',':
371: return (COMMA);
372: case '*':
373: return (GLOB);
374: }
375:
376:
377: /*
378: * Case of INT. Place ascii digit string into tokbuf.
379: */
380: if (isdigit(c)) {
381: *sp++ = c;
382: while (isdigit(c = fetch()))
383: *sp++ = c;
384: unfetch(c);
385: *sp = '\0';
386: return (INT);
387: }
388:
389: /*
390: * The only remaining possibility is the token STR or STRPLUS.
391: */
392: *sp++ = c;
393: mark = tokbuf + buflen - 2;
394: while ((c = fetch()) != EOF && c != NEWLINE && c != PERCENT) {
395: if (sp == mark) {
396: posn = sp - tokbuf;
397: tokbuf = realloc(tokbuf, (buflen += 128));
398: sp = tokbuf + posn;
399: mark = sp + buflen;
400: }
401: *sp++ = c;
402: }
403: *sp = '\0';
404: if (c == PERCENT)
405: return (STRPLUS);
406: unfetch(c);
407: return (STR);
408: }
409:
410:
411: unfetch(c)
412: int c;
413: {
414: ufetval = c;
415: ufetflag = TRUE;
416: }
417:
418: fetch()
419: {
420: register int c;
421: register int c2;
422:
423: if (ufetflag) {
424: ufetflag = FALSE;
425: return (ufetval);
426: }
427:
428: for (;;)
429: switch (c = getc(f)) {
430: case '%':
431: return (PERCENT);
432: case '\n':
433: return (NEWLINE);
434: case '\\':
435: if ((c2 = getc(f)) == '%')
436: return ('%');
437: else if (c2 == '\n')
438: continue;
439: else {
440: ungetc(c2, f);
441: return ('\\');
442: }
443: default:
444: return (c);
445: }
446: }
447:
448:
449: skip_it()
450: {
451: register int t;
452:
453: while ((t = gettoken()) != EOF && t != NEWLINE)
454: ;
455: return (t);
456: }
457:
458: do_it()
459: {
460: register int c;
461: register FILE *fp;
462:
463: if ((c = gettoken()) != STR && c != STRPLUS) {
464: ungettoken(c);
465: return (skip_it());
466: }
467: /* Set uid to user which crontab is going to be executed.
468: * In case of COHERENT3.2.0 set uid to daemon (set_uid_flag is TRUE).
469: */
470: Dprint("Set user ID to '%s'\n", acRealUser);
471: if (set_uid_flag == FALSE) {
472: /* Check do user want to have a mail messages.
473: * We have to do it right now, while we still superuser.
474: */
475: char *cBuf;
476: int fd; /* Descriptor of the mail lock file */
477:
478: if ((cBuf = malloc(sizeof(D_MAIN) + strlen(acRealUser) + 1))
479: == NULL) {
480: fprintf(stderr, "crond: out of memory\n");
481: return; /* We don't want exit here. Things can change */
482: }
483: sprintf(cBuf, "%s/%s", D_MAIN, acRealUser);
484:
485: /* Does user want to receive mail. */
486: if ((fd = open(cBuf, 0)) != -1) {
487: close(fd);
488: mailFlag = FALSE;
489: } else
490: mailFlag = TRUE;
491:
492: /* If file name is not user name ignore it */
493: if ((set_uid_flag = set_uid(acRealUser)) != TRUE) {
494: fprintf(stderr, "crond: cannot find/set user '%s'\n",
495: acRealUser);
496: return(EOF);
497: }
498: }
499:
500: Dprint("Tokken is %s\n", tokbuf);
501:
502: if ((fp = cronpipe(tokbuf, "w")) == NULL) {
503: fprintf(stderr, "crond:\tCould not popen: %s\n\t\
504: errno = %d: %s.\n", tokbuf, errno, perror());
505: return (skip_it());
506: }
507:
508: if (c == STR) {
509: fclose(fp);
510: return (gettoken());
511: }
512: while ((c = fetch()) != NEWLINE && c != EOF)
513: if (c == PERCENT)
514: putc('\n', fp);
515: else
516: putc(c, fp);
517: putc('\n', fp);
518: fclose(fp);
519: return(c);
520: }
521:
522: /*
523: * We have to catch all signals.
524: * Ignore signals INT, HUP, & PIPE.
525: * Reset time on ALRM.
526: * Remove lock file and take default action on the rest of them.
527: */
528: sigsetup()
529: {
530: register int i;
531: int catchalarm(); /* Catch alarm signal */
532: int catchsignals(); /* Catch all signals */
533:
534: for (i = 1; i < NSIG; i++)
535: signal(i, catchsignals);
536:
537: signal(SIGINT, SIG_IGN);
538: signal(SIGHUP, SIG_IGN);
539: signal(SIGPIPE, SIG_IGN);
540: signal(SIGALRM, catchalarm);
541: }
542:
543: /*
544: * Remove lock file. Restore default action. Send the catched signal to itself.
545: */
546: int catchsignals(sig)
547: int sig;
548: {
549: unlink(F_LOCK); /* Remove lock FIFO. Do not care if unlink */
550: /* failed (case cron 3.2.0) */
551: Dprint("Remove lock file %s\n", F_LOCK);
552: signal(sig, SIG_DFL); /* Reset to default */
553: kill(getpid(), sig); /* Send catched signal */
554: wait();
555: }
556:
557: /*
558: * Catch alarm.
559: */
560: int catchalarm()
561: {
562: signal(SIGALRM, catchalarm);
563: time(&clock);
564: tm = localtime(&clock);
565: alarm(61 - tm->tm_sec);
566: }
567:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.