|
|
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.