|
|
1.1 ! root 1: #include <stdio.h> ! 2: #include <ctype.h> ! 3: #include <signal.h> ! 4: #include <sysexits.h> ! 5: #include "string.h" ! 6: #include "smtp.h" ! 7: #include "mail.h" ! 8: #include <sys/stat.h> ! 9: ! 10: /* ! 11: * names of the limit files ! 12: */ ! 13: #define CON ".smtpscheds" ! 14: #define NCON ".nsmtpscheds" ! 15: ! 16: #define OLD 1*3600L /* old C file -> try less often */ ! 17: #define VOLD 6*3600L /* very old C file -> try much less often */ ! 18: #define OLDW 1*3600L /* wait time between tries at old files */ ! 19: #define VOLDW 4*3600L /* wait time between tries at very old files */ ! 20: ! 21: extern char *UPASROOT; ! 22: int warn = -1; ! 23: int removedays = -1; ! 24: int cleanup; ! 25: int verbose; ! 26: int testmode; ! 27: int Xonly; ! 28: int Conly; ! 29: string *replyaddr; ! 30: string *dest; ! 31: int mypid; ! 32: char **getcmd(); ! 33: ! 34: #define MAXDEST 50 /* # destinations remembered */ ! 35: #define MAXTPERD 5*60 /* total time allowed before skipping dests */ ! 36: struct { ! 37: string *dest; ! 38: time_t time; ! 39: } destlist[MAXDEST]; /* time consumed by unsuccessful tries at dest */ ! 40: int ndest = 0; ! 41: ! 42: int debug; ! 43: ! 44: ! 45: /* ! 46: * actions to take when locking ! 47: */ ! 48: #define BLOCK 0 /* wait for 5 minutes if the directory is locked */ ! 49: #define SKIP 1 /* skip the directory if it is locked */ ! 50: #define IGNORE 2 /* don't lock the directory or care if it is */ ! 51: ! 52: /* ! 53: * If called with arguments, those arguments are spool directories. Descend ! 54: * each one processing the control files in it (C.* and X.*). ! 55: * ! 56: * If called without arguments, descend all spool directories. ! 57: * ! 58: * -s #scheds specifies a maximum number of concurrent smtpscheds. ! 59: * -w #days causes users to be warned if their mail files are older ! 60: * than #days days ! 61: * -r #days causes mail older than #days days to be returned to sender ! 62: * -c cleanup empty directories ! 63: * -D debug ! 64: * -L level logging level ! 65: */ ! 66: usage() ! 67: { ! 68: fprintf(stderr, "smtpsched [-cvtDL] [-w #days] [-r #days] [-s #scheds] [dir]\n"); ! 69: exit(1); ! 70: } ! 71: ! 72: main(ac, av) ! 73: int ac; ! 74: char *av[]; ! 75: { ! 76: DIR *dirp; ! 77: Direct *dp; ! 78: int max=0; ! 79: int c; ! 80: extern int optind; ! 81: extern char *optarg; ! 82: ! 83: umask(2); ! 84: ! 85: mypid = getpid(); ! 86: ! 87: /* ! 88: * avoid annoying distractions ! 89: */ ! 90: signal(SIGPIPE, SIG_IGN); ! 91: signal(SIGHUP, SIG_IGN); ! 92: Openlog("smtpsched", LOG_PID, LOG_SMTPSCHED); ! 93: setlogmask(LOG_UPTO(LOG_INFO)); ! 94: ! 95: while ((c = getopt(ac, av, "XCDcvtr:w:s:L:")) != EOF) ! 96: switch (c) { ! 97: case 'X': Xonly++; break; ! 98: case 'C': Conly++; break; ! 99: case 't': testmode++; break; ! 100: case 'v': verbose++; break; ! 101: case 'c': cleanup++; break; ! 102: case 'r': removedays = atoi(optarg); break; ! 103: case 's': max = atoi(optarg); break; ! 104: case 'w': warn = atoi(optarg); break; ! 105: case 'L': setloglevel(optarg); break; ! 106: case 'D': debug++; break; ! 107: default: usage(); ! 108: } ! 109: ! 110: /* ! 111: * go to top spool directory ! 112: */ ! 113: if(chdir(SMTPQROOT)<0){ ! 114: Syslog(LOG_ALERT, "can't chdir to %s\n", SMTPQROOT); ! 115: exit(1); ! 116: } ! 117: ! 118: /* ! 119: * if there are too many running exit ! 120: */ ! 121: if(max && toomany(max)<0) ! 122: exit(0); ! 123: ! 124: /* ! 125: * If specific directories, do just them. Keep running the directory till there ! 126: * is no change. ! 127: */ ! 128: if(optind!=ac){ ! 129: for(; optind<ac; optind++) ! 130: while(dodir(av[optind], SKIP) && !warn && !removedays) ! 131: ; ! 132: return 0; ! 133: } ! 134: ! 135: /* ! 136: * walk through all directories in top directory. the lock is ! 137: * non-blocking (if neither r nor w options specified) to let ! 138: * different instances of smtpsched skip over each other. ! 139: */ ! 140: dirp = opendir("."); ! 141: if(dirp==NULL){ ! 142: Syslog(LOG_ALERT, "couldn't read %s\n", SMTPQROOT); ! 143: exit(1); ! 144: } ! 145: while(dp = readdir(dirp)){ ! 146: if(strcmp(dp->d_name, ".")!=0 && strcmp(dp->d_name, "..")!=0) ! 147: dodir(dp->d_name, (warn>=0 || removedays>=0) ? IGNORE : SKIP); ! 148: } ! 149: closedir(dirp); ! 150: return 0; ! 151: } ! 152: ! 153: /* ! 154: * do both directions in a directory ! 155: */ ! 156: dodir(dname, action) ! 157: char *dname; ! 158: { ! 159: int i, err; ! 160: static string *ds; ! 161: struct stat buf; ! 162: ! 163: if ((err=stat(dname, &buf)) < 0) ! 164: return 0; ! 165: ! 166: if (!(buf.st_mode & S_IFDIR)) ! 167: return 0; ! 168: ! 169: ds = s_reset(ds); ! 170: s_append(ds, dname); ! 171: ! 172: if (debug) ! 173: fprintf(stderr, "Checking %s\n", dname); ! 174: i = dodirdir(s_to_c(ds), action, "X."); ! 175: i += dodirdir(s_to_c(ds), action, "C."); ! 176: return i; ! 177: } ! 178: ! 179: /* ! 180: * walk through all entries in this directory. process any ! 181: * not starting with '.'. lock the directory before proceeding. ! 182: */ ! 183: dodirdir(dname, action, direction) ! 184: char *dname; ! 185: char *direction; ! 186: { ! 187: DIR *dirp; ! 188: Direct *dp; ! 189: int i; ! 190: int changed=0; ! 191: int ents=0, files=0; ! 192: static string *ls; ! 193: extern int errno; ! 194: ! 195: ls = s_reset(ls); ! 196: s_append(ls, direction); ! 197: s_append(ls, dname); ! 198: ! 199: /* ! 200: * lock the directory. the lock is in the parent directory. ! 201: */ ! 202: switch(action){ ! 203: case BLOCK: ! 204: for(i=0; i<3; i++){ ! 205: if(lock(s_to_c(ls))==0) ! 206: break; ! 207: if (debug) ! 208: Syslog(LOG_DEBUG, "pausing for lock"); ! 209: sleep(5); ! 210: } ! 211: if(i==3) ! 212: return changed; ! 213: break; ! 214: case SKIP: ! 215: if(lock(s_to_c(ls))<0){ ! 216: Syslog(LOG_DEBUG, "couldn't lock %s\n", dname); ! 217: return changed; ! 218: } ! 219: break; ! 220: case IGNORE: ! 221: break; ! 222: } ! 223: ! 224: /* ! 225: * descend into the directory. if it isn't a directory, ! 226: * this will fail. ! 227: */ ! 228: if(chdir(dname)<0){ ! 229: if(action != IGNORE) ! 230: unlock(s_to_c(ls)); ! 231: return changed; ! 232: } ! 233: ! 234: /* ! 235: * walk through the entries ! 236: */ ! 237: dirp = opendir("."); ! 238: if(dirp==NULL){ ! 239: Syslog(LOG_INFO, "couldn't read directory %s\n", dname); ! 240: if(chdir(SMTPQROOT)<0){ ! 241: Syslog(LOG_ALERT, "can't chdir back to SMTPQROOT\n"); ! 242: exit(1); ! 243: } ! 244: if(action != IGNORE) ! 245: unlock(s_to_c(ls)); ! 246: return changed; ! 247: } ! 248: while(dp = readdir(dirp)){ ! 249: if(strcmp(dp->d_name, ".")==0 || strcmp(dp->d_name, "..")==0) ! 250: continue; ! 251: files++; ! 252: if (cleanup) ! 253: continue; ! 254: if (dp->d_name[0] == *direction) { ! 255: switch(dofile(dname, dp->d_name)){ ! 256: case 0: ! 257: /* file removed */ ! 258: changed = 1; ! 259: break; ! 260: case 1: ! 261: /* file left alone */ ! 262: ents += 1; ! 263: break; ! 264: } ! 265: } ! 266: } ! 267: closedir(dirp); ! 268: ! 269: /* ! 270: * go back up. symbolic links could be painful!!!! ! 271: */ ! 272: if(chdir(SMTPQROOT)<0){ ! 273: Syslog(LOG_ALERT, "Can't chdir back to SMTPQROOT\n"); ! 274: exit(1); ! 275: } ! 276: ! 277: /* ! 278: * cleanup empty directories ! 279: */ ! 280: if(cleanup && files==0){ ! 281: Syslog(LOG_DEBUG, "%s empty\n", dname); ! 282: if(rmdir(dname)<0) ! 283: Syslog(LOG_ALERT, "can't unlink: %d\n", errno); ! 284: } ! 285: ! 286: if(action != IGNORE) ! 287: unlock(s_to_c(ls)); ! 288: return changed; ! 289: } ! 290: ! 291: /* ! 292: * process a spool control file. control file names start with C. or ! 293: * X. all error goes into an error file. ! 294: * ! 295: * return 0 if file removed, 1 otherwise. ! 296: */ ! 297: dofile(dname, name) ! 298: char *dname; ! 299: char *name; ! 300: { ! 301: int rv; ! 302: int fd, ofd; ! 303: char *ef; ! 304: extern char *fileoftype(); ! 305: struct stat sb; ! 306: time_t now, Edate, Cdate; ! 307: ! 308: rv = -1; ! 309: ! 310: /* ! 311: * if the file is inconsistent, remove it ! 312: */ ! 313: if(cleanup && inconsistent(name)){ ! 314: Syslog(LOG_NOTICE, "%s/%s inconsistent\n", dname, name); ! 315: unlink(name); ! 316: return 0; ! 317: } ! 318: ! 319: /* ! 320: * if this is not a control file, ignore it ! 321: */ ! 322: if(name[1]!='.' || (name[0]!='C' && name[0]!='X')) ! 323: return 1; ! 324: ! 325: /* ! 326: * if file is too old, warn user and remove it. if checking age, ! 327: * don't run the control file. ! 328: */ ! 329: if(warn>=0 || removedays>=0) { ! 330: if(checkage(name)==0) { ! 331: Syslog(LOG_NOTICE, "%s/%s too old\n", dname, name); ! 332: doremove(name); ! 333: return 0; ! 334: } ! 335: return 1; ! 336: } ! 337: ! 338: /* ! 339: * don't run control file when cleaning up ! 340: */ ! 341: if(cleanup) ! 342: return 1; ! 343: ! 344: /* ! 345: * Backoff scheme: don't try old C files very often ! 346: */ ! 347: ef = fileoftype('E', name); ! 348: if (name[0]=='C') { ! 349: now = time((time_t *)0); ! 350: Cdate = now; ! 351: Edate = now-VOLDW-1; ! 352: if (stat(name, &sb)==0) ! 353: Cdate = sb.st_ctime; ! 354: if (stat(ef, &sb)==0) ! 355: Edate = sb.st_mtime; ! 356: if (now-Cdate>VOLD && now-Edate<VOLDW ! 357: || now-Cdate>OLD && now-Edate<OLDW) { ! 358: if (verbose) ! 359: Syslog(LOG_DEBUG, "ignore %s/%s: not time yet\n", dname, name); ! 360: if (debug==0) ! 361: return 1; ! 362: } ! 363: } ! 364: ! 365: /* ! 366: * in test mode, just return ! 367: */ ! 368: if (testmode) { ! 369: Syslog(LOG_DEBUG, "would process %s/%s\n", dname, name); ! 370: return 1; ! 371: } ! 372: /* ! 373: * redirect output to an error file ! 374: */ ! 375: ofd = dup(2); ! 376: close(2); ! 377: fd = open(ef, 1); ! 378: if(fd<0) ! 379: fd = creat(ef, 0666); ! 380: if(fd>=0){ ! 381: lseek(fd, 0l, 2); ! 382: ! 383: /* ! 384: * process the file ! 385: */ ! 386: if(name[0]=='C') { ! 387: rv = dosmtp(dname, name); ! 388: } else if(name[0]=='X') { ! 389: rv = dormail(dname, name); ! 390: } ! 391: ! 392: /* ! 393: * get old error file back ! 394: */ ! 395: close(2); ! 396: (void) dup(ofd); ! 397: close(ofd); ! 398: } ! 399: ! 400: /* ! 401: * if processing was successful, remove the spool files ! 402: */ ! 403: if(rv==0) { ! 404: doremove(name); ! 405: return 0; ! 406: } ! 407: return 1; ! 408: } ! 409: ! 410: /* ! 411: * remove the control file, data file, and error file ! 412: */ ! 413: doremove(ctl) ! 414: char *ctl; ! 415: { ! 416: fflush(stdout); ! 417: unlink(fileoftype('E', ctl)); ! 418: unlink(ctl); ! 419: unlink(fileoftype('D', ctl)); ! 420: } ! 421: ! 422: /* ! 423: * run rmail. rmail takes care of its own errors, so if rmail fails, ! 424: * just don't remove the files. ! 425: */ ! 426: dormail(dname, ctl) ! 427: char *dname; ! 428: char *ctl; ! 429: { ! 430: char **av; ! 431: int rc; ! 432: ! 433: /* ! 434: * fork off the command ! 435: */ ! 436: if ((av = getcmd(ctl, "/bin/rmail")) == NULL) { ! 437: Syslog(LOG_WARNING, "Could not get rmail params for %s", ctl); ! 438: return -1; ! 439: } ! 440: ! 441: if ((rc = docmd(ctl, av)) == 0){ ! 442: Syslog(LOG_DEBUG, "success"); ! 443: return 0; ! 444: } else { ! 445: Syslog(LOG_DEBUG, "failed, rc = %d", rc); ! 446: return -1; ! 447: } ! 448: } ! 449: ! 450: /* ! 451: * run smtp. if an error occurs, determine its importance and send ! 452: * a error mail message if it is fatal. ! 453: */ ! 454: dosmtp(dname, ctl) ! 455: char *dname; ! 456: char *ctl; ! 457: { ! 458: static string *cmd; ! 459: int status, i; ! 460: char **av; ! 461: time_t t0, t1; ! 462: ! 463: /* ! 464: * fork off the command ! 465: */ ! 466: cmd = s_reset(cmd); ! 467: s_append(cmd, UPASROOT); ! 468: s_append(cmd, "smtp"); ! 469: av = getcmd(ctl, s_to_c(cmd)); ! 470: if (av==NULL) { ! 471: Syslog(LOG_WARNING, "Could not get smtp params for %s", ctl); ! 472: return -1; ! 473: } ! 474: /* ! 475: * Check whether unsuccessful attempts at this destination ! 476: * have taken too much time. If so, pass over the file. ! 477: */ ! 478: for (i=0; i<ndest; i++) { ! 479: if (strcmp(s_to_c(dest), s_to_c(destlist[i].dest))==0) { ! 480: if (destlist[i].time > MAXTPERD) { ! 481: Syslog(LOG_DEBUG, "passed %s (%d sec)\n", s_to_c(dest), destlist[i].time); ! 482: fprintf(stderr, "can't contact destination\n"); ! 483: return -1; ! 484: } ! 485: break; ! 486: } ! 487: } ! 488: if (i==ndest) { ! 489: if (ndest<MAXDEST) ! 490: ndest++; ! 491: else ! 492: i = 0; /* loses storage */ ! 493: /* ! 494: * the following s_copy died on a malformed `C' file. The ! 495: * contents of these files should be checked more carefully. ! 496: */ ! 497: destlist[i].dest = s_copy(s_to_c(dest)); ! 498: destlist[i].time = 0; ! 499: } ! 500: time(&t0); ! 501: switch(status=docmd(ctl, av)){ ! 502: case 0: /* it worked */ ! 503: Syslog(LOG_DEBUG, "success\n"); ! 504: destlist[i].time = 0; ! 505: return 0; ! 506: ! 507: case EX_UNAVAILABLE: /* service unavailable */ ! 508: case EX_NOPERM: /* permission denied */ ! 509: case EX_NOUSER: /* rejected by the other side */ ! 510: case EX_NOHOST: /* host name unknown */ ! 511: case EX_DATAERR: /* data format error */ ! 512: case EX_USAGE: /* command line usage error */ ! 513: Syslog(LOG_INFO, "failed with status %d\n", status); ! 514: returnmail(ctl, 1); /*permanant failure*/ ! 515: destlist[i].time = 0; ! 516: return 0; ! 517: ! 518: case EX_CANTCREAT: /* can't create (user) output file */ ! 519: case EX_IOERR: /* input/output error */ ! 520: case EX_OSERR: /* system error (e.g., can't fork) */ ! 521: case EX_OSFILE: /* critical OS file missing */ ! 522: case EX_SOFTWARE: /* internal software error */ ! 523: case EX_NOINPUT: /* cannot open input */ ! 524: case EX_PROTOCOL: /* remote error in protocol */ ! 525: /* gauss is having flakey datakit errors that confuse the ! 526: * SMTP protocol. EX_PROTOCOL is a temporary error for gauss-ches*/ ! 527: case EX_TEMPFAIL: /* temp failure; user is invited to retry */ ! 528: Syslog(LOG_INFO, "temp fail with status %d\n", status); ! 529: time(&t1); /*temporary failure*/ ! 530: destlist[i].time += t1-t0; ! 531: return -1; ! 532: ! 533: default: /* possibly a temporary problem */ ! 534: Syslog(LOG_WARNING, "unknown fail with status %d\n", status); ! 535: time(&t1); ! 536: destlist[i].time += t1-t0; ! 537: return -1; ! 538: } ! 539: } ! 540: ! 541: /* ! 542: * open a control file and parse the first line. It contains ! 543: * the reply address and the destination (for returning the mail). ! 544: * ! 545: * It leaves the control file open and returns the fp. ! 546: */ ! 547: FILE * ! 548: parseline1(ctl) ! 549: char *ctl; ! 550: { ! 551: FILE *fp; ! 552: static string *line; ! 553: ! 554: fp = fopen(ctl, "r"); ! 555: if(fp==NULL) ! 556: return NULL; ! 557: ! 558: /* ! 559: * get reply address and destination ! 560: */ ! 561: line = s_reset(line); ! 562: if(s_read_line(fp, line)==NULL){ ! 563: fprintf(stderr, "smtpsched: error reading ctl file %s: %s\n", ctl, ! 564: s_to_c(line)); ! 565: fclose(fp); ! 566: return NULL; ! 567: } ! 568: replyaddr = s_parse(s_restart(line), s_reset(replyaddr)); ! 569: if(replyaddr==NULL){ ! 570: fprintf(stderr, "smtpsched: error reading ctl file replyaddr %s\n", ! 571: ctl); ! 572: fclose(fp); ! 573: return NULL; ! 574: } ! 575: dest = s_parse(line, s_reset(dest)); ! 576: if(dest==NULL){ ! 577: fprintf(stderr, "smtpsched: error reading ctl file dest %s\n", ! 578: ctl); ! 579: fclose(fp); ! 580: return NULL; ! 581: } ! 582: return fp; ! 583: } ! 584: ! 585: /* ! 586: * Read control file to get arguments for command. Leave dest and replyaddr ! 587: * available. The control file has two lines. The first is reply address ! 588: * and recipients. the second is the arguments for the command. ! 589: */ ! 590: char ** ! 591: getcmd(ctl, cmd) ! 592: char *ctl; ! 593: char *cmd; ! 594: { ! 595: static string *args; ! 596: FILE *fp; ! 597: static char *av[1024]; ! 598: int ac=0; ! 599: char *cp; ! 600: ! 601: fp = parseline1(ctl); ! 602: if (fp==NULL) ! 603: return NULL; ! 604: ! 605: /* ! 606: * make command line ! 607: */ ! 608: av[ac++] = cmd; ! 609: args = s_reset(args); ! 610: if(s_read_line(fp, args)==NULL){ ! 611: fprintf(stderr, "smtpsched: error reading ctl file %s\n", ctl); ! 612: fclose(fp); ! 613: return NULL; ! 614: } ! 615: fclose(fp); ! 616: cp = s_to_c(args); ! 617: cp[strlen(cp) - 1] = '\0'; /*zap the newline*/ ! 618: Syslog(LOG_INFO, "%s <%s", cmd, ctl); ! 619: for(cp = s_to_c(args); *cp && ac<1023;){ ! 620: av[ac++] = cp++; ! 621: while(*cp && !isspace(*cp)) ! 622: cp++; ! 623: while(isspace(*cp)) ! 624: *cp++ = 0; ! 625: } ! 626: av[ac] = 0; ! 627: return av; ! 628: } ! 629: ! 630: /* ! 631: * execute a command, put standard error in the error file. ! 632: */ ! 633: docmd(ctl, av) ! 634: char *ctl; ! 635: char **av; ! 636: { ! 637: int fd; ! 638: int pid, status; ! 639: int n; ! 640: ! 641: /* ! 642: * fork off the command ! 643: */ ! 644: switch(pid = fork()){ ! 645: case -1: ! 646: return -1; ! 647: case 0: ! 648: /* ! 649: * make data file standard input ! 650: */ ! 651: close(0); ! 652: fd = open(fileoftype('D', ctl), 0); ! 653: if(fd<0){ ! 654: perror("smtpsched: error reading data file:\n"); ! 655: exit(1); ! 656: } ! 657: ! 658: /* ! 659: * make error file standard output ! 660: */ ! 661: close(1); ! 662: fd = dup(2); ! 663: ! 664: /* ! 665: * start the command ! 666: */ ! 667: execvp(av[0], av); ! 668: exit(-2); ! 669: default: ! 670: /* ! 671: * wait for the command to terminate ! 672: */ ! 673: while((n = wait(&status))>=0) ! 674: if(n == pid) ! 675: break; ! 676: if(status&0xff) ! 677: return -2; ! 678: else ! 679: return (status>>8)&0xff; ! 680: } ! 681: ! 682: } ! 683: ! 684: /* ! 685: * see if the number of consumers has been exceeded. if not, add this process ! 686: * to the list. ! 687: * ! 688: * returns 0 if there were the number was not exceeded, -1 otherwise ! 689: */ ! 690: toomany(max) ! 691: int max; ! 692: { ! 693: FILE *ifp=NULL; ! 694: FILE *ofp=NULL; ! 695: int cur=0; ! 696: int pid; ! 697: ! 698: /* ! 699: * lock consumers file ! 700: */ ! 701: if(lock(CON)<0) ! 702: return -1; ! 703: ! 704: /* ! 705: * open old and new consumer files ! 706: */ ! 707: ofp = fopen(NCON, "w"); ! 708: if(ofp==NULL){ ! 709: fprintf(stderr, "can't open %s\n", NCON); ! 710: goto error; ! 711: } ! 712: ifp = fopen(CON, "r"); ! 713: if(ifp!=NULL){ ! 714: /* ! 715: * see how many consumers are still around ! 716: */ ! 717: while(fscanf(ifp, "%d", &pid)==1){ ! 718: if(kill(pid, 0) == 0){ ! 719: cur++; ! 720: if(fprintf(ofp, "%d\n", pid)<0){ ! 721: fprintf(stderr, "error writing %s\n", NCON); ! 722: goto error; ! 723: } ! 724: } ! 725: } ! 726: if(cur >= max) ! 727: goto error; ! 728: } ! 729: ! 730: /* ! 731: * add us to the group of consumers ! 732: */ ! 733: if(fprintf(ofp, "%d\n", getpid())<0){ ! 734: fprintf(stderr, "error writing %s\n", NCON); ! 735: goto error; ! 736: } ! 737: if(ifp!=NULL) ! 738: fclose(ifp); ! 739: if(fclose(ofp)==EOF) ! 740: goto error; ! 741: unlink(CON); ! 742: if(link(NCON, CON)<0) ! 743: fprintf(stderr, "can't link %s to %s file\n", CON, NCON); ! 744: unlink(NCON); ! 745: unlock(CON); ! 746: return 0; ! 747: error: ! 748: /* ! 749: * too many consumers or we can't make a new consumer file ! 750: */ ! 751: if(ifp!=NULL) ! 752: fclose(ifp); ! 753: if(ofp!=NULL) ! 754: fclose(ofp); ! 755: unlink(NCON); ! 756: unlock(CON); ! 757: return -1; ! 758: } ! 759: ! 760: /* ! 761: * return true if the file is inconsistent. The following are inconsistent: ! 762: * - a control file without a datafile ! 763: * - an error file without a datafile ! 764: * - a day old data file without a control file ! 765: * - a limit file of any kind ! 766: */ ! 767: inconsistent(file) ! 768: char *file; ! 769: { ! 770: struct stat s; ! 771: int days; ! 772: ! 773: /* ! 774: * switch on file type ! 775: */ ! 776: switch(file[0]){ ! 777: case 'C': ! 778: case 'X': ! 779: /* ! 780: * if no data file, control file is inconsistent ! 781: */ ! 782: if(stat(fileoftype('D', file), &s)<0) ! 783: return 1; ! 784: break; ! 785: case 'E': ! 786: /* ! 787: * if no control file, error file is inconsistent ! 788: */ ! 789: if(stat(fileoftype('X', file), &s)<0 ! 790: && stat(fileoftype('C', file), &s)<0) ! 791: return 1; ! 792: ! 793: /* ! 794: * if no data file, error file is inconsistent ! 795: */ ! 796: if(stat(fileoftype('D', file), &s)<0) ! 797: return 1; ! 798: break; ! 799: case 'D': ! 800: /* ! 801: * look for a control file ! 802: */ ! 803: if(stat(fileoftype('X', file), &s)==0 ! 804: || stat(fileoftype('C', file), &s)==0) ! 805: break; ! 806: ! 807: /* ! 808: * no control file, data file inconsistent if >=1 day old ! 809: */ ! 810: if(stat(file, &s)<0) ! 811: return 0; ! 812: days = (time((long *)0) - s.st_ctime)/(24*60*60); ! 813: if(days>0) ! 814: return 1; ! 815: break; ! 816: default: ! 817: break; ! 818: } ! 819: return 0; ! 820: } ! 821: ! 822: /* ! 823: * check the age of a file. if it is greater than warn or remove, tell the ! 824: * sender. return 0 if the file is to be removed, -1 otherwise. ! 825: */ ! 826: checkage(ctl) ! 827: char *ctl; ! 828: { ! 829: struct stat s; ! 830: int days; ! 831: char buf[256]; ! 832: FILE *fp; ! 833: ! 834: /* ! 835: * get the file's age ! 836: */ ! 837: if(stat(ctl, &s)<0) ! 838: return -1; ! 839: days = (time((long *)0) - s.st_ctime)/(24*60*60); ! 840: ! 841: /* ! 842: * check for removal ! 843: */ ! 844: if(removedays>=0 && days>=removedays){ ! 845: fp = parseline1(ctl); ! 846: if(fp==NULL) ! 847: return -1; ! 848: fclose(fp); ! 849: ! 850: Syslog(LOG_INFO,"returning mail to %s orig to %s after %d days", ! 851: s_to_c(replyaddr), s_to_c(dest), days); ! 852: return returnmail(ctl, 1); ! 853: } ! 854: ! 855: /* ! 856: * check for warning ! 857: */ ! 858: if(warn>=0 && days>=warn){ ! 859: fp = parseline1(ctl); ! 860: if(fp==NULL) ! 861: return -1; ! 862: fclose(fp); ! 863: ! 864: Syslog(LOG_INFO, "warning %s about %s after %d days", ! 865: s_to_c(replyaddr), s_to_c(dest), days); ! 866: returnmail(ctl, 0); ! 867: } ! 868: ! 869: return -1; ! 870: } ! 871: ! 872: /* ! 873: * return a piece of mail with a reason for the return ! 874: */ ! 875: returnmail(ctl, warn) ! 876: char *ctl; ! 877: { ! 878: int pid, status; ! 879: string *cmd; ! 880: int pfd[2]; ! 881: char buf[132]; ! 882: int fd, n; ! 883: int reads; ! 884: FILE *fp; ! 885: FILE *ifp; ! 886: long now; ! 887: char asct[27]; ! 888: ! 889: if(pipe(pfd)<0) ! 890: return -1; ! 891: ! 892: switch(pid=fork()){ ! 893: case -1: ! 894: close(pfd[0]); ! 895: close(pfd[1]); ! 896: return -1; ! 897: case 0: ! 898: /* ! 899: * start up the mailer to take the refusal message ! 900: */ ! 901: close(0); ! 902: dup(pfd[0]); ! 903: close(pfd[1]); ! 904: execl("/bin/rmail", "/bin/rmail", s_to_c(replyaddr), 0); ! 905: exit(1); ! 906: default: ! 907: /* ! 908: * pipe the refusal message to the mailer ! 909: */ ! 910: close(pfd[0]); ! 911: fp = fdopen(pfd[1], "w"); ! 912: if(fp==NULL) { ! 913: close(pfd[1]); ! 914: break; ! 915: } ! 916: ! 917: /* ! 918: * the From line ! 919: */ ! 920: now = time((long *)0); ! 921: strcpy(asct, ctime(&now)); ! 922: asct[24] = 0; ! 923: fprintf(fp, "From postmaster %s remote from \n", asct); ! 924: ! 925: /* ! 926: * the refusal message ! 927: */ ! 928: if(warn) { ! 929: fprintf(fp, "Subject: smtp mail failed\n\n"); ! 930: fprintf(fp, "Your mail to %s is undeliverable.\n", ! 931: s_to_c(dest)); ! 932: } else { ! 933: fprintf(fp, "Subject: smtp mail warning\n\n"); ! 934: fprintf(fp, "Your mail to %s is not yet delivered.\n", ! 935: s_to_c(dest)); ! 936: fprintf(fp, "Delivery attempts continue.\n"); ! 937: } ! 938: ! 939: /* ! 940: * then diagnosis of error ! 941: */ ! 942: fprintf(fp, "---------- diagnosis ----------\n"); ! 943: ifp = fopen(fileoftype('E', ctl), "r"); ! 944: if(ifp!=NULL){ ! 945: for(reads=0; reads<20; reads++) { ! 946: if(fgets(buf, sizeof(buf), ifp)==NULL) ! 947: break; ! 948: fputs(buf, fp); ! 949: } ! 950: fclose(ifp); ! 951: } ! 952: ! 953: /* ! 954: * finally the message itself ! 955: */ ! 956: fprintf(fp, "---------- unsent mail ----------\n"); ! 957: ifp = fopen(fileoftype('D', ctl), "r"); ! 958: if(ifp!=NULL){ ! 959: for(reads=0; reads<50; reads++) { ! 960: if(fgets(buf, sizeof(buf), ifp)==NULL) ! 961: break; ! 962: fputs(buf, fp); ! 963: } ! 964: fclose(ifp); ! 965: } ! 966: fclose(fp); ! 967: ! 968: /* ! 969: * wait for the warning to finish ! 970: */ ! 971: while((n = wait(&status))>=0) ! 972: if(n == pid) ! 973: break; ! 974: return status ? -1 : 0; ! 975: } ! 976: close(pfd[1]); ! 977: return -1; ! 978: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.