1: # include "sendmail.h" 2: # include <sys/stat.h> 3: # include <ndir.h> 4: # include <signal.h> 5: # include <errno.h> 6: 7: # ifndef QUEUE 8: SCCSID(@(#)queue.c 4.1 7/25/83 (no queueing)); 9: # else QUEUE 10: 11: SCCSID(@(#)queue.c 4.1 7/25/83); 12: 13: /* 14: ** Work queue. 15: */ 16: 17: struct work 18: { 19: char *w_name; /* name of control file */ 20: long w_pri; /* priority of message, see below */ 21: struct work *w_next; /* next in queue */ 22: }; 23: 24: typedef struct work WORK; 25: 26: WORK *WorkQ; /* queue of things to be done */ 27: /* 28: ** QUEUEUP -- queue a message up for future transmission. 29: ** 30: ** Parameters: 31: ** e -- the envelope to queue up. 32: ** queueall -- if TRUE, queue all addresses, rather than 33: ** just those with the QQUEUEUP flag set. 34: ** announce -- if TRUE, tell when you are queueing up. 35: ** 36: ** Returns: 37: ** none. 38: ** 39: ** Side Effects: 40: ** The current request are saved in a control file. 41: */ 42: 43: queueup(e, queueall, announce) 44: register ENVELOPE *e; 45: bool queueall; 46: bool announce; 47: { 48: char *tf; 49: char *qf; 50: char buf[MAXLINE]; 51: register FILE *tfp; 52: register HDR *h; 53: register ADDRESS *q; 54: MAILER nullmailer; 55: 56: /* 57: ** Create control file. 58: */ 59: 60: tf = newstr(queuename(e, 't')); 61: tfp = fopen(tf, "w"); 62: if (tfp == NULL) 63: { 64: syserr("queueup: cannot create temp file %s", tf); 65: return; 66: } 67: (void) chmod(tf, FileMode); 68: 69: # ifdef DEBUG 70: if (tTd(40, 1)) 71: printf("queueing in %s\n", tf); 72: # endif DEBUG 73: 74: /* 75: ** If there is no data file yet, create one. 76: */ 77: 78: if (e->e_df == NULL) 79: { 80: register FILE *dfp; 81: extern putbody(); 82: 83: e->e_df = newstr(queuename(e, 'd')); 84: dfp = fopen(e->e_df, "w"); 85: if (dfp == NULL) 86: { 87: syserr("queueup: cannot create %s", e->e_df); 88: (void) fclose(tfp); 89: return; 90: } 91: (void) chmod(e->e_df, FileMode); 92: (*e->e_putbody)(dfp, ProgMailer, e); 93: (void) fclose(dfp); 94: e->e_putbody = putbody; 95: } 96: 97: /* 98: ** Output future work requests. 99: ** Priority should be first, since it is read by orderq. 100: */ 101: 102: /* output message priority */ 103: fprintf(tfp, "P%ld\n", e->e_msgpriority); 104: 105: /* output creation time */ 106: fprintf(tfp, "T%ld\n", e->e_ctime); 107: 108: /* output name of data file */ 109: fprintf(tfp, "D%s\n", e->e_df); 110: 111: /* message from envelope, if it exists */ 112: if (e->e_message != NULL) 113: fprintf(tfp, "M%s\n", e->e_message); 114: 115: /* output name of sender */ 116: fprintf(tfp, "S%s\n", e->e_from.q_paddr); 117: 118: /* output list of recipient addresses */ 119: for (q = e->e_sendqueue; q != NULL; q = q->q_next) 120: { 121: if (queueall ? !bitset(QDONTSEND, q->q_flags) : 122: bitset(QQUEUEUP, q->q_flags)) 123: { 124: fprintf(tfp, "R%s\n", q->q_paddr); 125: if (announce) 126: { 127: e->e_to = q->q_paddr; 128: message(Arpa_Info, "queued"); 129: if (LogLevel > 4) 130: logdelivery("queued"); 131: e->e_to = NULL; 132: } 133: #ifdef DEBUG 134: if (tTd(40, 1)) 135: { 136: printf("queueing "); 137: printaddr(q, FALSE); 138: } 139: #endif DEBUG 140: } 141: } 142: 143: /* 144: ** Output headers for this message. 145: ** Expand macros completely here. Queue run will deal with 146: ** everything as absolute headers. 147: ** All headers that must be relative to the recipient 148: ** can be cracked later. 149: ** We set up a "null mailer" -- i.e., a mailer that will have 150: ** no effect on the addresses as they are output. 151: */ 152: 153: bzero((char *) &nullmailer, sizeof nullmailer); 154: nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; 155: nullmailer.m_eol = "\n"; 156: 157: define('g', "$f", e); 158: for (h = e->e_header; h != NULL; h = h->h_link) 159: { 160: extern bool bitzerop(); 161: 162: /* don't output null headers */ 163: if (h->h_value == NULL || h->h_value[0] == '\0') 164: continue; 165: 166: /* don't output resent headers on non-resent messages */ 167: if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 168: continue; 169: 170: /* output this header */ 171: fprintf(tfp, "H"); 172: 173: /* if conditional, output the set of conditions */ 174: if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 175: { 176: int j; 177: 178: putc('?', tfp); 179: for (j = '\0'; j <= '\177'; j++) 180: if (bitnset(j, h->h_mflags)) 181: putc(j, tfp); 182: putc('?', tfp); 183: } 184: 185: /* output the header: expand macros, convert addresses */ 186: if (bitset(H_DEFAULT, h->h_flags)) 187: { 188: (void) expand(h->h_value, buf, &buf[sizeof buf], e); 189: fprintf(tfp, "%s: %s\n", h->h_field, buf); 190: } 191: else if (bitset(H_FROM|H_RCPT, h->h_flags)) 192: { 193: commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 194: &nullmailer); 195: } 196: else 197: fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 198: } 199: 200: /* 201: ** Clean up. 202: */ 203: 204: (void) fclose(tfp); 205: qf = queuename(e, 'q'); 206: holdsigs(); 207: (void) unlink(qf); 208: if (link(tf, qf) < 0) 209: syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df); 210: else 211: (void) unlink(tf); 212: rlsesigs(); 213: 214: # ifdef LOG 215: /* save log info */ 216: if (LogLevel > 15) 217: syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 218: # endif LOG 219: } 220: /* 221: ** RUNQUEUE -- run the jobs in the queue. 222: ** 223: ** Gets the stuff out of the queue in some presumably logical 224: ** order and processes them. 225: ** 226: ** Parameters: 227: ** none. 228: ** 229: ** Returns: 230: ** none. 231: ** 232: ** Side Effects: 233: ** runs things in the mail queue. 234: */ 235: 236: runqueue(forkflag) 237: bool forkflag; 238: { 239: /* 240: ** See if we want to go off and do other useful work. 241: */ 242: 243: if (forkflag) 244: { 245: int pid; 246: 247: pid = dofork(); 248: if (pid != 0) 249: { 250: /* parent -- pick up intermediate zombie */ 251: (void) waitfor(pid); 252: if (QueueIntvl != 0) 253: (void) setevent(QueueIntvl, runqueue, TRUE); 254: return; 255: } 256: /* child -- double fork */ 257: if (fork() != 0) 258: exit(EX_OK); 259: } 260: # ifdef LOG 261: if (LogLevel > 11) 262: syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 263: # endif LOG 264: 265: /* 266: ** Release any resources used by the daemon code. 267: */ 268: 269: # ifdef DAEMON 270: clrdaemon(); 271: # endif DAEMON 272: 273: /* 274: ** Start making passes through the queue. 275: ** First, read and sort the entire queue. 276: ** Then, process the work in that order. 277: ** But if you take too long, start over. 278: */ 279: 280: /* order the existing work requests */ 281: (void) orderq(); 282: 283: /* process them once at a time */ 284: while (WorkQ != NULL) 285: { 286: WORK *w = WorkQ; 287: 288: WorkQ = WorkQ->w_next; 289: dowork(w); 290: free(w->w_name); 291: free((char *) w); 292: } 293: finis(); 294: } 295: /* 296: ** ORDERQ -- order the work queue. 297: ** 298: ** Parameters: 299: ** none. 300: ** 301: ** Returns: 302: ** The number of request in the queue (not necessarily 303: ** the number of requests in WorkQ however). 304: ** 305: ** Side Effects: 306: ** Sets WorkQ to the queue of available work, in order. 307: */ 308: 309: # define WLSIZE 120 /* max size of worklist per sort */ 310: 311: orderq() 312: { 313: register struct direct *d; 314: register WORK *w; 315: register WORK **wp; /* parent of w */ 316: DIR *f; 317: register int i; 318: WORK wlist[WLSIZE+1]; 319: int wn = -1; 320: extern workcmpf(); 321: 322: /* clear out old WorkQ */ 323: for (w = WorkQ; w != NULL; ) 324: { 325: register WORK *nw = w->w_next; 326: 327: WorkQ = nw; 328: free(w->w_name); 329: free((char *) w); 330: w = nw; 331: } 332: 333: /* open the queue directory */ 334: f = opendir("."); 335: if (f == NULL) 336: { 337: syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 338: return (0); 339: } 340: 341: /* 342: ** Read the work directory. 343: */ 344: 345: while ((d = readdir(f)) != NULL) 346: { 347: FILE *cf; 348: char lbuf[MAXNAME]; 349: 350: /* is this an interesting entry? */ 351: if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 352: continue; 353: 354: /* yes -- open control file (if not too many files) */ 355: if (++wn >= WLSIZE) 356: continue; 357: cf = fopen(d->d_name, "r"); 358: if (cf == NULL) 359: { 360: /* this may be some random person sending hir msgs */ 361: /* syserr("orderq: cannot open %s", cbuf); */ 362: #ifdef DEBUG 363: if (tTd(41, 2)) 364: printf("orderq: cannot open %s (%d)\n", 365: d->d_name, errno); 366: #endif DEBUG 367: errno = 0; 368: wn--; 369: continue; 370: } 371: wlist[wn].w_name = newstr(d->d_name); 372: 373: /* extract useful information */ 374: while (fgets(lbuf, sizeof lbuf, cf) != NULL) 375: { 376: if (lbuf[0] == 'P') 377: { 378: (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 379: break; 380: } 381: } 382: (void) fclose(cf); 383: } 384: (void) closedir(f); 385: wn++; 386: 387: /* 388: ** Sort the work directory. 389: */ 390: 391: qsort(wlist, min(wn, WLSIZE), sizeof *wlist, workcmpf); 392: 393: /* 394: ** Convert the work list into canonical form. 395: ** Should be turning it into a list of envelopes here perhaps. 396: */ 397: 398: wp = &WorkQ; 399: for (i = min(wn, WLSIZE); --i >= 0; ) 400: { 401: w = (WORK *) xalloc(sizeof *w); 402: w->w_name = wlist[i].w_name; 403: w->w_pri = wlist[i].w_pri; 404: w->w_next = NULL; 405: *wp = w; 406: wp = &w->w_next; 407: } 408: 409: # ifdef DEBUG 410: if (tTd(40, 1)) 411: { 412: for (w = WorkQ; w != NULL; w = w->w_next) 413: printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 414: } 415: # endif DEBUG 416: 417: return (wn); 418: } 419: /* 420: ** WORKCMPF -- compare function for ordering work. 421: ** 422: ** Parameters: 423: ** a -- the first argument. 424: ** b -- the second argument. 425: ** 426: ** Returns: 427: ** 1 if a < b 428: ** 0 if a == b 429: ** -1 if a > b 430: ** 431: ** Side Effects: 432: ** none. 433: */ 434: 435: workcmpf(a, b) 436: register WORK *a; 437: register WORK *b; 438: { 439: if (a->w_pri == b->w_pri) 440: return (0); 441: else if (a->w_pri > b->w_pri) 442: return (-1); 443: else 444: return (1); 445: } 446: /* 447: ** DOWORK -- do a work request. 448: ** 449: ** Parameters: 450: ** w -- the work request to be satisfied. 451: ** 452: ** Returns: 453: ** none. 454: ** 455: ** Side Effects: 456: ** The work request is satisfied if possible. 457: */ 458: 459: dowork(w) 460: register WORK *w; 461: { 462: register int i; 463: 464: # ifdef DEBUG 465: if (tTd(40, 1)) 466: printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 467: # endif DEBUG 468: 469: /* 470: ** Fork for work. 471: */ 472: 473: i = fork(); 474: if (i < 0) 475: { 476: syserr("dowork: cannot fork"); 477: return; 478: } 479: 480: if (i == 0) 481: { 482: /* 483: ** CHILD 484: ** Lock the control file to avoid duplicate deliveries. 485: ** Then run the file as though we had just read it. 486: ** We save an idea of the temporary name so we 487: ** can recover on interrupt. 488: */ 489: 490: /* set basic modes, etc. */ 491: (void) alarm(0); 492: closexscript(CurEnv); 493: CurEnv->e_flags &= ~EF_FATALERRS; 494: QueueRun = TRUE; 495: ErrorMode = EM_MAIL; 496: CurEnv->e_id = &w->w_name[2]; 497: # ifdef LOG 498: if (LogLevel > 11) 499: syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 500: getpid()); 501: # endif LOG 502: 503: /* don't use the headers from sendmail.cf... */ 504: CurEnv->e_header = NULL; 505: 506: /* create the link to the control file during processing */ 507: if (link(w->w_name, queuename(CurEnv, 'l')) < 0) 508: { 509: /* being processed by another queuer */ 510: # ifdef LOG 511: if (LogLevel > 4) 512: syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id); 513: # endif LOG 514: exit(EX_OK); 515: } 516: 517: /* do basic system initialization */ 518: initsys(); 519: 520: /* read the queue control file */ 521: readqf(CurEnv, TRUE); 522: CurEnv->e_flags |= EF_INQUEUE; 523: eatheader(CurEnv); 524: 525: /* do the delivery */ 526: if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 527: sendall(CurEnv, SM_DELIVER); 528: 529: /* finish up and exit */ 530: finis(); 531: } 532: 533: /* 534: ** Parent -- pick up results. 535: */ 536: 537: errno = 0; 538: (void) waitfor(i); 539: } 540: /* 541: ** READQF -- read queue file and set up environment. 542: ** 543: ** Parameters: 544: ** e -- the envelope of the job to run. 545: ** full -- if set, read in all information. Otherwise just 546: ** read in info needed for a queue print. 547: ** 548: ** Returns: 549: ** none. 550: ** 551: ** Side Effects: 552: ** cf is read and created as the current job, as though 553: ** we had been invoked by argument. 554: */ 555: 556: readqf(e, full) 557: register ENVELOPE *e; 558: bool full; 559: { 560: register FILE *f; 561: char buf[MAXFIELD]; 562: extern char *fgetfolded(); 563: register char *p; 564: 565: /* 566: ** Open the file created by queueup. 567: */ 568: 569: p = queuename(e, 'q'); 570: f = fopen(p, "r"); 571: if (f == NULL) 572: { 573: syserr("readqf: no control file %s", p); 574: return; 575: } 576: FileName = p; 577: LineNumber = 0; 578: 579: /* 580: ** Read and process the file. 581: */ 582: 583: if (Verbose && full) 584: printf("\nRunning %s\n", e->e_id); 585: while (fgetfolded(buf, sizeof buf, f) != NULL) 586: { 587: switch (buf[0]) 588: { 589: case 'R': /* specify recipient */ 590: sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 591: break; 592: 593: case 'H': /* header */ 594: if (full) 595: (void) chompheader(&buf[1], FALSE); 596: break; 597: 598: case 'M': /* message */ 599: e->e_message = newstr(&buf[1]); 600: break; 601: 602: case 'S': /* sender */ 603: setsender(newstr(&buf[1])); 604: break; 605: 606: case 'D': /* data file name */ 607: if (!full) 608: break; 609: e->e_df = newstr(&buf[1]); 610: e->e_dfp = fopen(e->e_df, "r"); 611: if (e->e_dfp == NULL) 612: syserr("readqf: cannot open %s", e->e_df); 613: break; 614: 615: case 'T': /* init time */ 616: (void) sscanf(&buf[1], "%ld", &e->e_ctime); 617: break; 618: 619: case 'P': /* message priority */ 620: (void) sscanf(&buf[1], "%ld", &e->e_msgpriority); 621: 622: /* make sure that big things get sent eventually */ 623: e->e_msgpriority -= WKTIMEFACT; 624: break; 625: 626: default: 627: syserr("readqf(%s): bad line \"%s\"", e->e_id, buf); 628: break; 629: } 630: } 631: 632: FileName = NULL; 633: } 634: /* 635: ** PRINTQUEUE -- print out a representation of the mail queue 636: ** 637: ** Parameters: 638: ** none. 639: ** 640: ** Returns: 641: ** none. 642: ** 643: ** Side Effects: 644: ** Prints a listing of the mail queue on the standard output. 645: */ 646: 647: printqueue() 648: { 649: register WORK *w; 650: FILE *f; 651: int nrequests; 652: char buf[MAXLINE]; 653: 654: /* 655: ** Read and order the queue. 656: */ 657: 658: nrequests = orderq(); 659: 660: /* 661: ** Print the work list that we have read. 662: */ 663: 664: /* first see if there is anything */ 665: if (nrequests <= 0) 666: { 667: printf("Mail queue is empty\n"); 668: return; 669: } 670: 671: printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 672: if (nrequests > WLSIZE) 673: printf(", only %d printed", WLSIZE); 674: printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 675: for (w = WorkQ; w != NULL; w = w->w_next) 676: { 677: struct stat st; 678: auto time_t submittime = 0; 679: long dfsize = -1; 680: char lf[20]; 681: char message[MAXLINE]; 682: 683: printf("%7s", w->w_name + 2); 684: strcpy(lf, w->w_name); 685: lf[0] = 'l'; 686: if (stat(lf, &st) >= 0) 687: printf("*"); 688: else 689: printf(" "); 690: errno = 0; 691: f = fopen(w->w_name, "r"); 692: if (f == NULL) 693: { 694: printf(" (finished)\n"); 695: errno = 0; 696: continue; 697: } 698: message[0] = '\0'; 699: while (fgets(buf, sizeof buf, f) != NULL) 700: { 701: fixcrlf(buf, TRUE); 702: switch (buf[0]) 703: { 704: case 'M': /* error message */ 705: strcpy(message, &buf[1]); 706: break; 707: 708: case 'S': /* sender name */ 709: printf("%8ld %.16s %.45s", dfsize, 710: ctime(&submittime), &buf[1]); 711: if (message[0] != '\0') 712: printf("\n\t\t\t\t (%.43s)", message); 713: break; 714: 715: case 'R': /* recipient name */ 716: printf("\n\t\t\t\t %.45s", &buf[1]); 717: break; 718: 719: case 'T': /* creation time */ 720: sscanf(&buf[1], "%ld", &submittime); 721: break; 722: 723: case 'D': /* data file name */ 724: if (stat(&buf[1], &st) >= 0) 725: dfsize = st.st_size; 726: break; 727: } 728: } 729: if (submittime == (time_t) 0) 730: printf(" (no control file)"); 731: printf("\n"); 732: fclose(f); 733: } 734: } 735: 736: # endif QUEUE