Annotation of researchv10no/cmd/nupas/smtp/smtpsched.c, revision 1.1.1.1

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: extern char *fileoftype();
                     23: int warn = -1;
                     24: int remove = -1;
                     25: int cleanup;
                     26: int verbose;
                     27: int testmode;
                     28: int Xonly;
                     29: int Conly;
                     30: string *replyaddr;
                     31: string *dest;
                     32: int mypid;
                     33: char **getcmd();
                     34: 
                     35: #define        MAXDEST 50              /* # destinations remembered */
                     36: #define        MAXTPERD 5*60           /* total time allowed before skipping dests */
                     37: struct {
                     38:        string  *dest;
                     39:        time_t  time;
                     40: } destlist[MAXDEST];           /* time consumed by unsuccessful tries at dest */
                     41: int    ndest = 0;
                     42: 
                     43: int    debug;
                     44: 
                     45: 
                     46: /*
                     47:  *  actions to take when locking
                     48:  */
                     49: #define BLOCK 0                /* wait for 5 minutes if the directory is locked */
                     50: #define SKIP 1         /* skip the directory if it is locked */
                     51: #define IGNORE 2       /* don't lock the directory or care if it is */
                     52: 
                     53: /*
                     54:  *  If called with arguments, those arguments are spool directories.  Descend
                     55:  *  each one processing the control files in it (C.* and X.*).
                     56:  *
                     57:  *  If called without arguments, descend all spool directories.
                     58:  *
                     59:  *  -s #scheds  specifies a maximum number of concurrent smtpscheds.
                     60:  *  -w #days   causes users to be warned if their mail files are older
                     61:  *             than #days days
                     62:  *  -r #days   causes mail older than #days days to be returned to sender
                     63:  *  -c         cleanup empty directories
                     64:  *  -D         debug
                     65:  *  -L level   logging level
                     66:  */
                     67: usage()
                     68: {
                     69:        fprintf(stderr, "smtpsched [-cvtDL] [-w #days] [-r #days] [-s #scheds] [dir]\n");
                     70:        exit(1);
                     71: }
                     72: 
                     73: main(ac, av)
                     74:        int ac;
                     75:        char *av[];
                     76: {
                     77:        DIR *dirp;
                     78:        Direct *dp;
                     79:        int max=0;
                     80:        int c;
                     81:        extern int optind;
                     82:        extern char *optarg;
                     83: 
                     84:        umask(2);
                     85: 
                     86:        mypid = getpid();
                     87: 
                     88:        /*
                     89:         *  avoid annoying distractions
                     90:         */
                     91:        signal(SIGPIPE, SIG_IGN);
                     92:        signal(SIGHUP, SIG_IGN);
                     93:        Openlog("smtpsched", LOG_PID, LOG_SMTPSCHED);
                     94:        setlogmask(LOG_UPTO(LOG_INFO));
                     95: 
                     96:        while ((c = getopt(ac, av, "XCDcvtr:w:s:L:")) != EOF)
                     97:                switch (c) {
                     98:                case 'X':       Xonly++;                break;
                     99:                case 'C':       Conly++;                break;
                    100:                case 't':       testmode++;             break;
                    101:                case 'v':       verbose++;              break;
                    102:                case 'c':       cleanup++;              break;
                    103:                case 'r':       remove = atoi(optarg);  break;
                    104:                case 's':       max = atoi(optarg);     break;
                    105:                case 'w':       warn = atoi(optarg);    break;
                    106:                case 'L':       setloglevel(optarg);    break;
                    107:                case 'D':       debug++;                break;
                    108:                default:        usage();
                    109:                }
                    110: 
                    111:        /*
                    112:         *  go to top spool directory
                    113:         */
                    114:        if(chdir(SMTPQROOT)<0){
                    115:                Syslog(LOG_ALERT, "can't chdir to %s\n", SMTPQROOT);
                    116:                exit(1);
                    117:        }
                    118: 
                    119:        /*
                    120:         *  if there are too many running exit
                    121:         */
                    122:        if(max && toomany(max)<0)
                    123:                exit(0);
                    124: 
                    125:        /*
                    126:         *  If specific directories, do just them.  Keep running the directory till there
                    127:         *  is no change.  
                    128:         */
                    129:        if(optind!=ac){
                    130:                for(; optind<ac; optind++)
                    131:                        while(dodir(av[optind], SKIP) && !warn && !remove)
                    132:                                ;
                    133:                return 0;
                    134:        }
                    135: 
                    136:        /*
                    137:         *  walk through all directories in top directory.  the lock is
                    138:         *  non-blocking (if neither r nor w options specified) to let
                    139:         *  different instances of smtpsched skip over each other.
                    140:         */
                    141:        dirp = opendir(".");
                    142:        if(dirp<(DIR *)0){
                    143:                Syslog(LOG_ALERT, "couldn't read %s\n", SMTPQROOT);
                    144:                exit(1);
                    145:        }
                    146:        while(dp = readdir(dirp)){
                    147:                if(strcmp(dp->d_name, ".")!=0 && strcmp(dp->d_name, "..")!=0)
                    148:                        dodir(dp->d_name, (warn>=0 || remove>=0) ? IGNORE : SKIP);
                    149:        }
                    150:        closedir(dirp);
                    151:        return 0;
                    152: }
                    153: 
                    154: /*
                    155:  *  do both directions in a directory
                    156:  */
                    157: dodir(dname, action)
                    158:        char *dname;
                    159: {
                    160:        int i, err;
                    161:        static string *ds;
                    162:        struct stat buf;
                    163: 
                    164:        if ((err=stat(dname, &buf)) < 0)
                    165:                return 0;
                    166: 
                    167:        if (!(buf.st_mode & S_IFDIR))
                    168:                return 0;
                    169: 
                    170:        ds = s_reset(ds);
                    171:        s_append(ds, dname);
                    172: 
                    173:        if (debug)
                    174:                fprintf(stderr, "Checking %s\n", dname);
                    175:        i = dodirdir(s_to_c(ds), action, "X.");
                    176:        i += dodirdir(s_to_c(ds), action, "C.");
                    177:        return i;
                    178: }
                    179: 
                    180: /*
                    181:  *  walk through all entries in this directory.  process any
                    182:  *  not starting with '.'.  lock the directory before proceeding.
                    183:  */
                    184: dodirdir(dname, action, direction)
                    185:        char *dname;
                    186:        char *direction;
                    187: {
                    188:        DIR *dirp;
                    189:        Direct *dp;
                    190:        int i;
                    191:        int changed=0;
                    192:        int ents=0, files=0;
                    193:        static string *ls;
                    194:        extern int errno;
                    195: 
                    196:        ls = s_reset(ls);
                    197:        s_append(ls, direction);
                    198:        s_append(ls, dname);
                    199: 
                    200:        /*
                    201:         *  lock the directory.  the lock is in the parent directory.
                    202:         */
                    203:        switch(action){
                    204:        case BLOCK:
                    205:                for(i=0; i<3; i++){
                    206:                        if(lock(s_to_c(ls))==0)
                    207:                                break;
                    208:                        if (debug)
                    209:                                Syslog(LOG_DEBUG, "pausing for lock");
                    210:                        sleep(5);
                    211:                }
                    212:                if(i==3)
                    213:                        return changed;
                    214:                break;
                    215:        case SKIP:
                    216:                if(lock(s_to_c(ls))<0){
                    217:                        Syslog(LOG_DEBUG, "couldn't lock %s\n", dname);
                    218:                        return changed;
                    219:                }
                    220:                break;
                    221:        case IGNORE:
                    222:                break;
                    223:        }
                    224: 
                    225:        /*
                    226:         *  descend into the directory.  if it isn't a directory,
                    227:         *  this will fail.
                    228:         */
                    229:        if(chdir(dname)<0){
                    230:                if(action != IGNORE)
                    231:                        unlock(s_to_c(ls));
                    232:                return changed;
                    233:        }
                    234: 
                    235:        /*
                    236:         *  walk through the entries
                    237:         */
                    238:        dirp = opendir(".");
                    239:        if(dirp<(DIR *)0){
                    240:                Syslog(LOG_INFO, "couldn't read directory %s\n", dname);
                    241:                if(chdir(SMTPQROOT)<0){
                    242:                        Syslog(LOG_ALERT, "can't chdir back to SMTPQROOT\n");
                    243:                        exit(1);
                    244:                }
                    245:                if(action != IGNORE)
                    246:                        unlock(s_to_c(ls));
                    247:                return changed;
                    248:        }
                    249:        while(dp = readdir(dirp)){
                    250:                if(strcmp(dp->d_name, ".")==0 || strcmp(dp->d_name, "..")==0)
                    251:                        continue;
                    252:                files++;
                    253:                if (cleanup)
                    254:                        continue;
                    255:                if (dp->d_name[0] == *direction) {
                    256:                        switch(dofile(dname, dp->d_name)){
                    257:                        case 0:
                    258:                                /* file removed */
                    259:                                changed = 1;
                    260:                                break;
                    261:                        case 1:
                    262:                                /* file left alone */
                    263:                                ents += 1;
                    264:                                break;
                    265:                        }
                    266:                }
                    267:        }
                    268:        closedir(dirp);
                    269: 
                    270:        /*
                    271:         *  go back up.  symbolic links could be painful!!!!
                    272:         */
                    273:        if(chdir(SMTPQROOT)<0){
                    274:                Syslog(LOG_ALERT, "Can't chdir back to SMTPQROOT\n");
                    275:                exit(1);
                    276:        }
                    277: 
                    278:        /*
                    279:         *  cleanup empty directories
                    280:         */
                    281:        if(cleanup && files==0){
                    282:                Syslog(LOG_DEBUG, "%s empty\n", dname);
                    283:                if(rmdir(dname)<0)
                    284:                        Syslog(LOG_ALERT, "can't unlink: %d\n", errno);
                    285:        }
                    286: 
                    287:        if(action != IGNORE)
                    288:                unlock(s_to_c(ls));
                    289:        return changed;
                    290: }
                    291: 
                    292: /*
                    293:  *  process a spool control file.  control file names start with C. or
                    294:  *  X.  all error goes into an error file.
                    295:  *
                    296:  *  return 0 if file removed, 1 otherwise.
                    297:  */
                    298: dofile(dname, name)
                    299:        char *dname;
                    300:        char *name;
                    301: {
                    302:        int rv;
                    303:        int fd, ofd;
                    304:        char *ef;
                    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 || remove>=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(remove>=0 && days>=remove){
                    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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.