14632Seric # include "sendmail.h" 24632Seric # include <sys/stat.h> 38348Seric # include <dir.h> 44634Seric # include <signal.h> 54632Seric # include <errno.h> 64632Seric 75182Seric # ifndef QUEUE 8*9377Seric SCCSID(@(#)queue.c 3.54 11/28/82 (no queueing)); 95182Seric # else QUEUE 104632Seric 11*9377Seric SCCSID(@(#)queue.c 3.54 11/28/82); 125182Seric 134632Seric /* 14*9377Seric ** Work queue. 15*9377Seric */ 16*9377Seric 17*9377Seric struct work 18*9377Seric { 19*9377Seric char *w_name; /* name of control file */ 20*9377Seric long w_pri; /* priority of message, see below */ 21*9377Seric struct work *w_next; /* next in queue */ 22*9377Seric }; 23*9377Seric 24*9377Seric typedef struct work WORK; 25*9377Seric 26*9377Seric WORK *WorkQ; /* queue of things to be done */ 27*9377Seric /* 284632Seric ** QUEUEUP -- queue a message up for future transmission. 294632Seric ** 304632Seric ** Parameters: 316980Seric ** e -- the envelope to queue up. 326999Seric ** queueall -- if TRUE, queue all addresses, rather than 336999Seric ** just those with the QQUEUEUP flag set. 34*9377Seric ** announce -- if TRUE, tell when you are queueing up. 354632Seric ** 364632Seric ** Returns: 374632Seric ** none. 384632Seric ** 394632Seric ** Side Effects: 40*9377Seric ** The current request are saved in a control file. 414632Seric */ 424632Seric 43*9377Seric queueup(e, queueall, announce) 446980Seric register ENVELOPE *e; 456999Seric bool queueall; 46*9377Seric bool announce; 474632Seric { 487812Seric char *tf; 497812Seric char *qf; 507763Seric char buf[MAXLINE]; 517812Seric register FILE *tfp; 524632Seric register HDR *h; 535007Seric register ADDRESS *q; 544632Seric 555037Seric /* 565037Seric ** Create control file. 575037Seric */ 584632Seric 597812Seric tf = newstr(queuename(e, 't')); 607812Seric tfp = fopen(tf, "w"); 617812Seric if (tfp == NULL) 624632Seric { 637812Seric syserr("queueup: cannot create temp file %s", tf); 644632Seric return; 654632Seric } 669048Seric (void) chmod(tf, FileMode); 674632Seric 684632Seric # ifdef DEBUG 697677Seric if (tTd(40, 1)) 707812Seric printf("queueing in %s\n", tf); 714632Seric # endif DEBUG 724632Seric 734632Seric /* 746980Seric ** If there is no data file yet, create one. 756980Seric */ 766980Seric 776980Seric if (e->e_df == NULL) 786980Seric { 796980Seric register FILE *dfp; 806980Seric 817812Seric e->e_df = newstr(queuename(e, 'd')); 826980Seric dfp = fopen(e->e_df, "w"); 836980Seric if (dfp == NULL) 846980Seric { 856980Seric syserr("queueup: cannot create %s", e->e_df); 867812Seric (void) fclose(tfp); 876980Seric return; 886980Seric } 899048Seric (void) chmod(e->e_df, FileMode); 90*9377Seric (*e->e_putbody)(dfp, ProgMailer, FALSE); 917009Seric (void) fclose(dfp); 926980Seric } 936980Seric 946980Seric /* 954632Seric ** Output future work requests. 96*9377Seric ** Priority should be first, since it is read by orderq. 974632Seric */ 984632Seric 99*9377Seric /* output message priority */ 100*9377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 101*9377Seric 1024632Seric /* output name of data file */ 1037812Seric fprintf(tfp, "D%s\n", e->e_df); 1044632Seric 1054632Seric /* output name of sender */ 1067812Seric fprintf(tfp, "S%s\n", e->e_from.q_paddr); 1074632Seric 1087860Seric /* output creation time */ 1097860Seric fprintf(tfp, "T%ld\n", e->e_ctime); 1104632Seric 1116980Seric /* output message class */ 1127812Seric fprintf(tfp, "C%d\n", e->e_class); 1136980Seric 1144632Seric /* output list of recipient addresses */ 1156980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1164632Seric { 1175037Seric # ifdef DEBUG 1187677Seric if (tTd(40, 1)) 1195037Seric { 1205037Seric printf("queueing "); 1215037Seric printaddr(q, FALSE); 1225037Seric } 1235037Seric # endif DEBUG 1247763Seric if (queueall ? !bitset(QDONTSEND, q->q_flags) : 1257763Seric bitset(QQUEUEUP, q->q_flags)) 1268245Seric { 1277812Seric fprintf(tfp, "R%s\n", q->q_paddr); 128*9377Seric if (announce) 129*9377Seric { 130*9377Seric e->e_to = q->q_paddr; 131*9377Seric message(Arpa_Info, "queued"); 132*9377Seric if (LogLevel > 4) 133*9377Seric logdelivery("queued"); 134*9377Seric e->e_to = NULL; 135*9377Seric } 1368245Seric } 1374632Seric } 1384632Seric 139*9377Seric /* 140*9377Seric ** Output headers for this message. 141*9377Seric ** Expand macros completely here. Queue run will deal with 142*9377Seric ** everything as absolute headers. 143*9377Seric ** All headers that must be relative to the recipient 144*9377Seric ** can be cracked later. 145*9377Seric */ 146*9377Seric 147*9377Seric define('g', "$f", e); 1486980Seric for (h = e->e_header; h != NULL; h = h->h_link) 1494632Seric { 1504632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 1514632Seric continue; 1527812Seric fprintf(tfp, "H"); 1534632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 1547812Seric mfdecode(h->h_mflags, tfp); 1557763Seric if (bitset(H_DEFAULT, h->h_flags)) 1567763Seric { 1577763Seric (void) expand(h->h_value, buf, &buf[sizeof buf], e); 1588236Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 1597763Seric } 1608245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 1619348Seric { 1629348Seric commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 1639348Seric (MAILER *) NULL); 1649348Seric } 1657763Seric else 1668245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 1674632Seric } 1684632Seric 1694632Seric /* 1704632Seric ** Clean up. 1714632Seric */ 1724632Seric 1737812Seric (void) fclose(tfp); 1747812Seric qf = queuename(e, 'q'); 175*9377Seric holdsigs(); 1767812Seric (void) unlink(qf); 1777812Seric if (link(tf, qf) < 0) 1787812Seric syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df); 1796980Seric else 1807812Seric (void) unlink(tf); 181*9377Seric rlsesigs(); 1827391Seric 1837677Seric # ifdef LOG 1847677Seric /* save log info */ 1857878Seric if (LogLevel > 15) 1867878Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 1877677Seric # endif LOG 1884632Seric } 1894632Seric /* 1904632Seric ** RUNQUEUE -- run the jobs in the queue. 1914632Seric ** 1924632Seric ** Gets the stuff out of the queue in some presumably logical 1934632Seric ** order and processes them. 1944632Seric ** 1954632Seric ** Parameters: 1964632Seric ** none. 1974632Seric ** 1984632Seric ** Returns: 1994632Seric ** none. 2004632Seric ** 2014632Seric ** Side Effects: 2024632Seric ** runs things in the mail queue. 2034632Seric */ 2044632Seric 2054639Seric runqueue(forkflag) 2064639Seric bool forkflag; 2074632Seric { 2087466Seric /* 2097466Seric ** See if we want to go off and do other useful work. 2107466Seric */ 2114639Seric 2124639Seric if (forkflag) 2134639Seric { 2147943Seric int pid; 2157943Seric 2167943Seric pid = dofork(); 2177943Seric if (pid != 0) 2184639Seric { 2197943Seric /* parent -- pick up intermediate zombie */ 220*9377Seric (void) waitfor(pid); 2217690Seric if (QueueIntvl != 0) 2229348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 2234639Seric return; 2244639Seric } 2257943Seric /* child -- double fork */ 2267943Seric if (fork() != 0) 2277943Seric exit(EX_OK); 2284639Seric } 2297876Seric # ifdef LOG 2307876Seric if (LogLevel > 11) 2317943Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 2327876Seric # endif LOG 2334639Seric 2347466Seric /* 2357466Seric ** Start making passes through the queue. 2367466Seric ** First, read and sort the entire queue. 2377466Seric ** Then, process the work in that order. 2387466Seric ** But if you take too long, start over. 2397466Seric */ 2407466Seric 2417943Seric /* order the existing work requests */ 2427943Seric orderq(); 2437690Seric 2447943Seric /* process them once at a time */ 2457943Seric while (WorkQ != NULL) 2464639Seric { 2477943Seric WORK *w = WorkQ; 2487881Seric 2497943Seric WorkQ = WorkQ->w_next; 2507943Seric dowork(w); 2517943Seric free(w->w_name); 2527943Seric free((char *) w); 2534639Seric } 2547943Seric finis(); 2554634Seric } 2564634Seric /* 2574632Seric ** ORDERQ -- order the work queue. 2584632Seric ** 2594632Seric ** Parameters: 2604632Seric ** none. 2614632Seric ** 2624632Seric ** Returns: 2634632Seric ** none. 2644632Seric ** 2654632Seric ** Side Effects: 2664632Seric ** Sets WorkQ to the queue of available work, in order. 2674632Seric */ 2684632Seric 2694632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2704632Seric 2714632Seric orderq() 2724632Seric { 2736625Sglickman register struct direct *d; 2744632Seric register WORK *w; 2754632Seric register WORK **wp; /* parent of w */ 2766625Sglickman DIR *f; 2774632Seric register int i; 2784632Seric WORK wlist[WLSIZE]; 2794632Seric int wn = 0; 2804632Seric extern workcmpf(); 2814632Seric 2824632Seric /* clear out old WorkQ */ 2834632Seric for (w = WorkQ; w != NULL; ) 2844632Seric { 2854632Seric register WORK *nw = w->w_next; 2864632Seric 2874632Seric WorkQ = nw; 2884632Seric free(w->w_name); 2894632Seric free((char *) w); 2904632Seric w = nw; 2914632Seric } 2924632Seric 2934632Seric /* open the queue directory */ 2948148Seric f = opendir("."); 2954632Seric if (f == NULL) 2964632Seric { 2978148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 2984632Seric return; 2994632Seric } 3004632Seric 3014632Seric /* 3024632Seric ** Read the work directory. 3034632Seric */ 3044632Seric 3056625Sglickman while (wn < WLSIZE && (d = readdir(f)) != NULL) 3064632Seric { 307*9377Seric FILE *cf; 3084632Seric char lbuf[MAXNAME]; 3094632Seric 3104632Seric /* is this an interesting entry? */ 3117812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 3124632Seric continue; 3134632Seric 3148148Seric /* yes -- open control file */ 3158148Seric cf = fopen(d->d_name, "r"); 3164632Seric if (cf == NULL) 3174632Seric { 3187055Seric /* this may be some random person sending hir msgs */ 3197055Seric /* syserr("orderq: cannot open %s", cbuf); */ 3207055Seric errno = 0; 3214632Seric continue; 3224632Seric } 3238148Seric wlist[wn].w_name = newstr(d->d_name); 3244632Seric 3254632Seric /* extract useful information */ 3264632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3274632Seric { 328*9377Seric if (lbuf[0] == 'P') 3294632Seric { 3305037Seric (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 3314632Seric break; 3324632Seric } 3334632Seric } 3344632Seric wn++; 3354632Seric (void) fclose(cf); 3364632Seric } 3376625Sglickman (void) closedir(f); 3384632Seric 3394632Seric /* 3404632Seric ** Sort the work directory. 3414632Seric */ 3424632Seric 3434632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3444632Seric 3454632Seric /* 3464632Seric ** Convert the work list into canonical form. 347*9377Seric ** Should be turning it into a list of envelopes here perhaps. 3484632Seric */ 3494632Seric 3504632Seric wp = &WorkQ; 3514632Seric for (i = 0; i < wn; i++) 3524632Seric { 3534632Seric w = (WORK *) xalloc(sizeof *w); 3544632Seric w->w_name = wlist[i].w_name; 3554632Seric w->w_pri = wlist[i].w_pri; 3564632Seric w->w_next = NULL; 3574632Seric *wp = w; 3584632Seric wp = &w->w_next; 3594632Seric } 3604632Seric 3614632Seric # ifdef DEBUG 3627677Seric if (tTd(40, 1)) 3634632Seric { 3644632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3655037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 3664632Seric } 3674632Seric # endif DEBUG 3684632Seric } 3694632Seric /* 3707677Seric ** WORKCMPF -- compare function for ordering work. 3714632Seric ** 3724632Seric ** Parameters: 3734632Seric ** a -- the first argument. 3744632Seric ** b -- the second argument. 3754632Seric ** 3764632Seric ** Returns: 3774632Seric ** -1 if a < b 3784632Seric ** 0 if a == b 3794632Seric ** 1 if a > b 3804632Seric ** 3814632Seric ** Side Effects: 3824632Seric ** none. 3834632Seric */ 3844632Seric 3854632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3864632Seric 3874632Seric workcmpf(a, b) 3885037Seric register WORK *a; 3895037Seric register WORK *b; 3904632Seric { 3915037Seric if (a->w_pri == b->w_pri) 3924632Seric return (0); 3935037Seric else if (a->w_pri > b->w_pri) 3944632Seric return (1); 3954632Seric else 3964632Seric return (-1); 3974632Seric } 3984632Seric /* 3994632Seric ** DOWORK -- do a work request. 4004632Seric ** 4014632Seric ** Parameters: 4024632Seric ** w -- the work request to be satisfied. 4034632Seric ** 4044632Seric ** Returns: 4054632Seric ** none. 4064632Seric ** 4074632Seric ** Side Effects: 4084632Seric ** The work request is satisfied if possible. 4094632Seric */ 4104632Seric 4114632Seric dowork(w) 4124632Seric register WORK *w; 4134632Seric { 4144632Seric register int i; 4154632Seric 4164632Seric # ifdef DEBUG 4177677Seric if (tTd(40, 1)) 4185037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 4194632Seric # endif DEBUG 4204632Seric 4214632Seric /* 4224632Seric ** Fork for work. 4234632Seric */ 4244632Seric 4254632Seric i = fork(); 4264632Seric if (i < 0) 4274632Seric { 4284632Seric syserr("dowork: cannot fork"); 4294632Seric return; 4304632Seric } 4314632Seric 4324632Seric if (i == 0) 4334632Seric { 4344632Seric /* 4354632Seric ** CHILD 4368148Seric ** Lock the control file to avoid duplicate deliveries. 4378148Seric ** Then run the file as though we had just read it. 4387350Seric ** We save an idea of the temporary name so we 4397350Seric ** can recover on interrupt. 4404632Seric */ 4414632Seric 4427763Seric /* set basic modes, etc. */ 4437356Seric (void) alarm(0); 4449338Seric CurEnv->e_flags &= ~EF_FATALERRS; 4454632Seric QueueRun = TRUE; 446*9377Seric ErrorMode = EM_MAIL; 4478148Seric CurEnv->e_id = &w->w_name[2]; 4487876Seric # ifdef LOG 4497876Seric if (LogLevel > 11) 4507881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 4517881Seric getpid()); 4527876Seric # endif LOG 4537763Seric 4547763Seric /* don't use the headers from sendmail.cf... */ 4557763Seric CurEnv->e_header = NULL; 4569348Seric (void) chompheader("from: $q", TRUE); 4577763Seric 4587763Seric /* create the link to the control file during processing */ 4597812Seric if (link(w->w_name, queuename(CurEnv, 'l')) < 0) 4606980Seric { 4617812Seric /* being processed by another queuer */ 4627881Seric # ifdef LOG 4637881Seric if (LogLevel > 4) 4647881Seric syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id); 4657881Seric # endif LOG 4666980Seric exit(EX_OK); 4676980Seric } 4686980Seric 4696980Seric /* create ourselves a transcript file */ 4704634Seric openxscrpt(); 4716980Seric 4726980Seric /* do basic system initialization */ 4734632Seric initsys(); 4746980Seric 4756980Seric /* read the queue control file */ 476*9377Seric readqf(CurEnv); 4779338Seric CurEnv->e_flags |= EF_INQUEUE; 478*9377Seric eatheader(CurEnv); 4796980Seric 4806980Seric /* do the delivery */ 4819338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 4829282Seric sendall(CurEnv, SM_DELIVER); 4836980Seric 4846980Seric /* if still not sent, perhaps we should time out.... */ 4854634Seric # ifdef DEBUG 4867677Seric if (tTd(40, 3)) 4877886Seric printf("curtime=%ld, TimeOut=%ld\n", curtime(), 4887860Seric CurEnv->e_ctime + TimeOut); 4894634Seric # endif DEBUG 4909338Seric if (curtime() > CurEnv->e_ctime + TimeOut) 4919338Seric CurEnv->e_flags |= EF_TIMEOUT; 4926980Seric 4936980Seric /* finish up and exit */ 4944632Seric finis(); 4954632Seric } 4964632Seric 4974632Seric /* 4984632Seric ** Parent -- pick up results. 4994632Seric */ 5004632Seric 5014632Seric errno = 0; 502*9377Seric (void) waitfor(i); 5034632Seric } 5044632Seric /* 5054632Seric ** READQF -- read queue file and set up environment. 5064632Seric ** 5074632Seric ** Parameters: 508*9377Seric ** e -- the envelope of the job to run. 5094632Seric ** 5104632Seric ** Returns: 5114632Seric ** none. 5124632Seric ** 5134632Seric ** Side Effects: 5144632Seric ** cf is read and created as the current job, as though 5154632Seric ** we had been invoked by argument. 5164632Seric */ 5174632Seric 518*9377Seric readqf(e) 519*9377Seric register ENVELOPE *e; 5204632Seric { 5214632Seric register FILE *f; 5227785Seric char buf[MAXFIELD]; 5239348Seric extern char *fgetfolded(); 524*9377Seric register char *p; 5254632Seric 5264632Seric /* 5274632Seric ** Open the file created by queueup. 5284632Seric */ 5294632Seric 530*9377Seric p = queuename(e, 'q'); 531*9377Seric f = fopen(p, "r"); 5324632Seric if (f == NULL) 5334632Seric { 534*9377Seric syserr("readqf: no control file %s", p); 5354632Seric return; 5364632Seric } 537*9377Seric FileName = p; 538*9377Seric LineNumber = 0; 5394632Seric 5404632Seric /* 5414632Seric ** Read and process the file. 5424632Seric */ 5434632Seric 5447356Seric if (Verbose) 545*9377Seric printf("\nRunning %s\n", e->e_id); 5467785Seric while (fgetfolded(buf, sizeof buf, f) != NULL) 5474632Seric { 5484632Seric switch (buf[0]) 5494632Seric { 5504632Seric case 'R': /* specify recipient */ 551*9377Seric sendto(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 5524632Seric break; 5534632Seric 5544632Seric case 'H': /* header */ 5554632Seric (void) chompheader(&buf[1], FALSE); 5564632Seric break; 5574632Seric 5584632Seric case 'S': /* sender */ 5594634Seric setsender(newstr(&buf[1])); 5604632Seric break; 5614632Seric 5624632Seric case 'D': /* data file name */ 563*9377Seric e->e_df = newstr(&buf[1]); 564*9377Seric TempFile = fopen(e->e_df, "r"); 5654632Seric if (TempFile == NULL) 566*9377Seric syserr("readqf: cannot open %s", e->e_df); 5674632Seric break; 5684632Seric 5697860Seric case 'T': /* init time */ 570*9377Seric (void) sscanf(&buf[1], "%ld", &e->e_ctime); 5714632Seric break; 5724632Seric 5734634Seric case 'P': /* message priority */ 574*9377Seric (void) sscanf(&buf[1], "%ld", &e->e_msgpriority); 5755037Seric 5765037Seric /* make sure that big things get sent eventually */ 577*9377Seric e->e_msgpriority -= WKTIMEFACT; 5784634Seric break; 5794634Seric 5805902Seric case 'M': /* define macro */ 581*9377Seric define(buf[1], newstr(&buf[2]), e); 5825902Seric break; 5835902Seric 5844632Seric default: 585*9377Seric syserr("readqf(%s): bad line \"%s\"", e->e_id, buf); 5864632Seric break; 5874632Seric } 5884632Seric } 589*9377Seric 590*9377Seric FileName = NULL; 5914632Seric } 5924632Seric /* 5934632Seric ** TIMEOUT -- process timeout on queue file. 5944632Seric ** 5954632Seric ** Parameters: 5969338Seric ** e -- the envelope that timed out. 5974632Seric ** 5984632Seric ** Returns: 5994632Seric ** none. 6004632Seric ** 6014632Seric ** Side Effects: 6024632Seric ** Returns a message to the sender saying that this 6034632Seric ** message has timed out. 6044632Seric */ 6054632Seric 6069338Seric timeout(e) 6079338Seric register ENVELOPE *e; 6084632Seric { 6097369Seric char buf[MAXLINE]; 6107860Seric extern char *pintvl(); 6117369Seric 6124634Seric # ifdef DEBUG 6137677Seric if (tTd(40, 3)) 6149338Seric printf("timeout(%s)\n", e->e_id); 6154634Seric # endif DEBUG 6169338Seric e->e_to = NULL; 6176991Seric message(Arpa_Info, "Message has timed out"); 6184634Seric 6194634Seric /* return message to sender */ 6207860Seric (void) sprintf(buf, "Cannot send mail for %s", pintvl(TimeOut, FALSE)); 6219338Seric (void) returntosender(buf, &e->e_from, TRUE); 6224634Seric 6234634Seric /* arrange to remove files from queue */ 6249338Seric e->e_flags |= EF_CLRQUEUE; 6254632Seric } 6265182Seric 6275182Seric # endif QUEUE 628