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