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