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*9387Seric SCCSID(@(#)queue.c 3.55 11/28/82 (no queueing)); 95182Seric # else QUEUE 104632Seric 11*9387Seric SCCSID(@(#)queue.c 3.55 11/28/82); 125182Seric 134632Seric /* 149377Seric ** Work queue. 159377Seric */ 169377Seric 179377Seric struct work 189377Seric { 199377Seric char *w_name; /* name of control file */ 209377Seric long w_pri; /* priority of message, see below */ 219377Seric struct work *w_next; /* next in queue */ 229377Seric }; 239377Seric 249377Seric typedef struct work WORK; 259377Seric 269377Seric WORK *WorkQ; /* queue of things to be done */ 279377Seric /* 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. 349377Seric ** announce -- if TRUE, tell when you are queueing up. 354632Seric ** 364632Seric ** Returns: 374632Seric ** none. 384632Seric ** 394632Seric ** Side Effects: 409377Seric ** The current request are saved in a control file. 414632Seric */ 424632Seric 439377Seric queueup(e, queueall, announce) 446980Seric register ENVELOPE *e; 456999Seric bool queueall; 469377Seric 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); 909377Seric (*e->e_putbody)(dfp, ProgMailer, FALSE); 917009Seric (void) fclose(dfp); 926980Seric } 936980Seric 946980Seric /* 954632Seric ** Output future work requests. 969377Seric ** Priority should be first, since it is read by orderq. 974632Seric */ 984632Seric 999377Seric /* output message priority */ 1009377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 1019377Seric 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 1114632Seric /* output list of recipient addresses */ 1126980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1134632Seric { 1147763Seric if (queueall ? !bitset(QDONTSEND, q->q_flags) : 1157763Seric bitset(QQUEUEUP, q->q_flags)) 1168245Seric { 1177812Seric fprintf(tfp, "R%s\n", q->q_paddr); 1189377Seric if (announce) 1199377Seric { 1209377Seric e->e_to = q->q_paddr; 1219377Seric message(Arpa_Info, "queued"); 1229377Seric if (LogLevel > 4) 1239377Seric logdelivery("queued"); 1249377Seric e->e_to = NULL; 1259377Seric } 126*9387Seric #ifdef DEBUG 127*9387Seric if (tTd(40, 1)) 128*9387Seric { 129*9387Seric printf("queueing "); 130*9387Seric printaddr(q, FALSE); 131*9387Seric } 132*9387Seric #endif DEBUG 1338245Seric } 1344632Seric } 1354632Seric 1369377Seric /* 1379377Seric ** Output headers for this message. 1389377Seric ** Expand macros completely here. Queue run will deal with 1399377Seric ** everything as absolute headers. 1409377Seric ** All headers that must be relative to the recipient 1419377Seric ** can be cracked later. 1429377Seric */ 1439377Seric 1449377Seric define('g', "$f", e); 1456980Seric for (h = e->e_header; h != NULL; h = h->h_link) 1464632Seric { 1474632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 1484632Seric continue; 1497812Seric fprintf(tfp, "H"); 1504632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 1517812Seric mfdecode(h->h_mflags, tfp); 1527763Seric if (bitset(H_DEFAULT, h->h_flags)) 1537763Seric { 1547763Seric (void) expand(h->h_value, buf, &buf[sizeof buf], e); 1558236Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 1567763Seric } 1578245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 1589348Seric { 1599348Seric commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 1609348Seric (MAILER *) NULL); 1619348Seric } 1627763Seric else 1638245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 1644632Seric } 1654632Seric 1664632Seric /* 1674632Seric ** Clean up. 1684632Seric */ 1694632Seric 1707812Seric (void) fclose(tfp); 1717812Seric qf = queuename(e, 'q'); 1729377Seric holdsigs(); 1737812Seric (void) unlink(qf); 1747812Seric if (link(tf, qf) < 0) 1757812Seric syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df); 1766980Seric else 1777812Seric (void) unlink(tf); 1789377Seric rlsesigs(); 1797391Seric 1807677Seric # ifdef LOG 1817677Seric /* save log info */ 1827878Seric if (LogLevel > 15) 1837878Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 1847677Seric # endif LOG 1854632Seric } 1864632Seric /* 1874632Seric ** RUNQUEUE -- run the jobs in the queue. 1884632Seric ** 1894632Seric ** Gets the stuff out of the queue in some presumably logical 1904632Seric ** order and processes them. 1914632Seric ** 1924632Seric ** Parameters: 1934632Seric ** none. 1944632Seric ** 1954632Seric ** Returns: 1964632Seric ** none. 1974632Seric ** 1984632Seric ** Side Effects: 1994632Seric ** runs things in the mail queue. 2004632Seric */ 2014632Seric 2024639Seric runqueue(forkflag) 2034639Seric bool forkflag; 2044632Seric { 2057466Seric /* 2067466Seric ** See if we want to go off and do other useful work. 2077466Seric */ 2084639Seric 2094639Seric if (forkflag) 2104639Seric { 2117943Seric int pid; 2127943Seric 2137943Seric pid = dofork(); 2147943Seric if (pid != 0) 2154639Seric { 2167943Seric /* parent -- pick up intermediate zombie */ 2179377Seric (void) waitfor(pid); 2187690Seric if (QueueIntvl != 0) 2199348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 2204639Seric return; 2214639Seric } 2227943Seric /* child -- double fork */ 2237943Seric if (fork() != 0) 2247943Seric exit(EX_OK); 2254639Seric } 2267876Seric # ifdef LOG 2277876Seric if (LogLevel > 11) 2287943Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 2297876Seric # endif LOG 2304639Seric 2317466Seric /* 2327466Seric ** Start making passes through the queue. 2337466Seric ** First, read and sort the entire queue. 2347466Seric ** Then, process the work in that order. 2357466Seric ** But if you take too long, start over. 2367466Seric */ 2377466Seric 2387943Seric /* order the existing work requests */ 2397943Seric orderq(); 2407690Seric 2417943Seric /* process them once at a time */ 2427943Seric while (WorkQ != NULL) 2434639Seric { 2447943Seric WORK *w = WorkQ; 2457881Seric 2467943Seric WorkQ = WorkQ->w_next; 2477943Seric dowork(w); 2487943Seric free(w->w_name); 2497943Seric free((char *) w); 2504639Seric } 2517943Seric finis(); 2524634Seric } 2534634Seric /* 2544632Seric ** ORDERQ -- order the work queue. 2554632Seric ** 2564632Seric ** Parameters: 2574632Seric ** none. 2584632Seric ** 2594632Seric ** Returns: 2604632Seric ** none. 2614632Seric ** 2624632Seric ** Side Effects: 2634632Seric ** Sets WorkQ to the queue of available work, in order. 2644632Seric */ 2654632Seric 2664632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2674632Seric 2684632Seric orderq() 2694632Seric { 2706625Sglickman register struct direct *d; 2714632Seric register WORK *w; 2724632Seric register WORK **wp; /* parent of w */ 2736625Sglickman DIR *f; 2744632Seric register int i; 2754632Seric WORK wlist[WLSIZE]; 2764632Seric int wn = 0; 2774632Seric extern workcmpf(); 2784632Seric 2794632Seric /* clear out old WorkQ */ 2804632Seric for (w = WorkQ; w != NULL; ) 2814632Seric { 2824632Seric register WORK *nw = w->w_next; 2834632Seric 2844632Seric WorkQ = nw; 2854632Seric free(w->w_name); 2864632Seric free((char *) w); 2874632Seric w = nw; 2884632Seric } 2894632Seric 2904632Seric /* open the queue directory */ 2918148Seric f = opendir("."); 2924632Seric if (f == NULL) 2934632Seric { 2948148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 2954632Seric return; 2964632Seric } 2974632Seric 2984632Seric /* 2994632Seric ** Read the work directory. 3004632Seric */ 3014632Seric 3026625Sglickman while (wn < WLSIZE && (d = readdir(f)) != NULL) 3034632Seric { 3049377Seric FILE *cf; 3054632Seric char lbuf[MAXNAME]; 3064632Seric 3074632Seric /* is this an interesting entry? */ 3087812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 3094632Seric continue; 3104632Seric 3118148Seric /* yes -- open control file */ 3128148Seric cf = fopen(d->d_name, "r"); 3134632Seric if (cf == NULL) 3144632Seric { 3157055Seric /* this may be some random person sending hir msgs */ 3167055Seric /* syserr("orderq: cannot open %s", cbuf); */ 3177055Seric errno = 0; 3184632Seric continue; 3194632Seric } 3208148Seric wlist[wn].w_name = newstr(d->d_name); 3214632Seric 3224632Seric /* extract useful information */ 3234632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3244632Seric { 3259377Seric if (lbuf[0] == 'P') 3264632Seric { 3275037Seric (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 3284632Seric break; 3294632Seric } 3304632Seric } 3314632Seric wn++; 3324632Seric (void) fclose(cf); 3334632Seric } 3346625Sglickman (void) closedir(f); 3354632Seric 3364632Seric /* 3374632Seric ** Sort the work directory. 3384632Seric */ 3394632Seric 3404632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3414632Seric 3424632Seric /* 3434632Seric ** Convert the work list into canonical form. 3449377Seric ** Should be turning it into a list of envelopes here perhaps. 3454632Seric */ 3464632Seric 3474632Seric wp = &WorkQ; 3484632Seric for (i = 0; i < wn; i++) 3494632Seric { 3504632Seric w = (WORK *) xalloc(sizeof *w); 3514632Seric w->w_name = wlist[i].w_name; 3524632Seric w->w_pri = wlist[i].w_pri; 3534632Seric w->w_next = NULL; 3544632Seric *wp = w; 3554632Seric wp = &w->w_next; 3564632Seric } 3574632Seric 3584632Seric # ifdef DEBUG 3597677Seric if (tTd(40, 1)) 3604632Seric { 3614632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3625037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 3634632Seric } 3644632Seric # endif DEBUG 3654632Seric } 3664632Seric /* 3677677Seric ** WORKCMPF -- compare function for ordering work. 3684632Seric ** 3694632Seric ** Parameters: 3704632Seric ** a -- the first argument. 3714632Seric ** b -- the second argument. 3724632Seric ** 3734632Seric ** Returns: 3744632Seric ** -1 if a < b 3754632Seric ** 0 if a == b 3764632Seric ** 1 if a > b 3774632Seric ** 3784632Seric ** Side Effects: 3794632Seric ** none. 3804632Seric */ 3814632Seric 3824632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3834632Seric 3844632Seric workcmpf(a, b) 3855037Seric register WORK *a; 3865037Seric register WORK *b; 3874632Seric { 3885037Seric if (a->w_pri == b->w_pri) 3894632Seric return (0); 3905037Seric else if (a->w_pri > b->w_pri) 3914632Seric return (1); 3924632Seric else 3934632Seric return (-1); 3944632Seric } 3954632Seric /* 3964632Seric ** DOWORK -- do a work request. 3974632Seric ** 3984632Seric ** Parameters: 3994632Seric ** w -- the work request to be satisfied. 4004632Seric ** 4014632Seric ** Returns: 4024632Seric ** none. 4034632Seric ** 4044632Seric ** Side Effects: 4054632Seric ** The work request is satisfied if possible. 4064632Seric */ 4074632Seric 4084632Seric dowork(w) 4094632Seric register WORK *w; 4104632Seric { 4114632Seric register int i; 4124632Seric 4134632Seric # ifdef DEBUG 4147677Seric if (tTd(40, 1)) 4155037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 4164632Seric # endif DEBUG 4174632Seric 4184632Seric /* 4194632Seric ** Fork for work. 4204632Seric */ 4214632Seric 4224632Seric i = fork(); 4234632Seric if (i < 0) 4244632Seric { 4254632Seric syserr("dowork: cannot fork"); 4264632Seric return; 4274632Seric } 4284632Seric 4294632Seric if (i == 0) 4304632Seric { 4314632Seric /* 4324632Seric ** CHILD 4338148Seric ** Lock the control file to avoid duplicate deliveries. 4348148Seric ** Then run the file as though we had just read it. 4357350Seric ** We save an idea of the temporary name so we 4367350Seric ** can recover on interrupt. 4374632Seric */ 4384632Seric 4397763Seric /* set basic modes, etc. */ 4407356Seric (void) alarm(0); 4419338Seric CurEnv->e_flags &= ~EF_FATALERRS; 4424632Seric QueueRun = TRUE; 443*9387Seric SendMode = SM_DELIVER; 4449377Seric ErrorMode = EM_MAIL; 4458148Seric CurEnv->e_id = &w->w_name[2]; 4467876Seric # ifdef LOG 4477876Seric if (LogLevel > 11) 4487881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 4497881Seric getpid()); 4507876Seric # endif LOG 4517763Seric 4527763Seric /* don't use the headers from sendmail.cf... */ 4537763Seric CurEnv->e_header = NULL; 4549348Seric (void) chompheader("from: $q", TRUE); 4557763Seric 4567763Seric /* create the link to the control file during processing */ 4577812Seric if (link(w->w_name, queuename(CurEnv, 'l')) < 0) 4586980Seric { 4597812Seric /* being processed by another queuer */ 4607881Seric # ifdef LOG 4617881Seric if (LogLevel > 4) 4627881Seric syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id); 4637881Seric # endif LOG 4646980Seric exit(EX_OK); 4656980Seric } 4666980Seric 4676980Seric /* create ourselves a transcript file */ 4684634Seric openxscrpt(); 4696980Seric 4706980Seric /* do basic system initialization */ 4714632Seric initsys(); 4726980Seric 4736980Seric /* read the queue control file */ 4749377Seric readqf(CurEnv); 4759338Seric CurEnv->e_flags |= EF_INQUEUE; 4769377Seric eatheader(CurEnv); 4776980Seric 4786980Seric /* do the delivery */ 4799338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 4809282Seric sendall(CurEnv, SM_DELIVER); 4816980Seric 4826980Seric /* if still not sent, perhaps we should time out.... */ 4834634Seric # ifdef DEBUG 4847677Seric if (tTd(40, 3)) 4857886Seric printf("curtime=%ld, TimeOut=%ld\n", curtime(), 4867860Seric CurEnv->e_ctime + TimeOut); 4874634Seric # endif DEBUG 4889338Seric if (curtime() > CurEnv->e_ctime + TimeOut) 4899338Seric CurEnv->e_flags |= EF_TIMEOUT; 4906980Seric 4916980Seric /* finish up and exit */ 4924632Seric finis(); 4934632Seric } 4944632Seric 4954632Seric /* 4964632Seric ** Parent -- pick up results. 4974632Seric */ 4984632Seric 4994632Seric errno = 0; 5009377Seric (void) waitfor(i); 5014632Seric } 5024632Seric /* 5034632Seric ** READQF -- read queue file and set up environment. 5044632Seric ** 5054632Seric ** Parameters: 5069377Seric ** e -- the envelope of the job to run. 5074632Seric ** 5084632Seric ** Returns: 5094632Seric ** none. 5104632Seric ** 5114632Seric ** Side Effects: 5124632Seric ** cf is read and created as the current job, as though 5134632Seric ** we had been invoked by argument. 5144632Seric */ 5154632Seric 5169377Seric readqf(e) 5179377Seric register ENVELOPE *e; 5184632Seric { 5194632Seric register FILE *f; 5207785Seric char buf[MAXFIELD]; 5219348Seric extern char *fgetfolded(); 5229377Seric register char *p; 5234632Seric 5244632Seric /* 5254632Seric ** Open the file created by queueup. 5264632Seric */ 5274632Seric 5289377Seric p = queuename(e, 'q'); 5299377Seric f = fopen(p, "r"); 5304632Seric if (f == NULL) 5314632Seric { 5329377Seric syserr("readqf: no control file %s", p); 5334632Seric return; 5344632Seric } 5359377Seric FileName = p; 5369377Seric LineNumber = 0; 5374632Seric 5384632Seric /* 5394632Seric ** Read and process the file. 5404632Seric */ 5414632Seric 5427356Seric if (Verbose) 5439377Seric printf("\nRunning %s\n", e->e_id); 5447785Seric while (fgetfolded(buf, sizeof buf, f) != NULL) 5454632Seric { 5464632Seric switch (buf[0]) 5474632Seric { 5484632Seric case 'R': /* specify recipient */ 5499377Seric sendto(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 5504632Seric break; 5514632Seric 5524632Seric case 'H': /* header */ 5534632Seric (void) chompheader(&buf[1], FALSE); 5544632Seric break; 5554632Seric 5564632Seric case 'S': /* sender */ 5574634Seric setsender(newstr(&buf[1])); 5584632Seric break; 5594632Seric 5604632Seric case 'D': /* data file name */ 5619377Seric e->e_df = newstr(&buf[1]); 5629377Seric TempFile = fopen(e->e_df, "r"); 5634632Seric if (TempFile == NULL) 5649377Seric syserr("readqf: cannot open %s", e->e_df); 5654632Seric break; 5664632Seric 5677860Seric case 'T': /* init time */ 5689377Seric (void) sscanf(&buf[1], "%ld", &e->e_ctime); 5694632Seric break; 5704632Seric 5714634Seric case 'P': /* message priority */ 5729377Seric (void) sscanf(&buf[1], "%ld", &e->e_msgpriority); 5735037Seric 5745037Seric /* make sure that big things get sent eventually */ 5759377Seric e->e_msgpriority -= WKTIMEFACT; 5764634Seric break; 5774634Seric 5785902Seric case 'M': /* define macro */ 5799377Seric define(buf[1], newstr(&buf[2]), e); 5805902Seric break; 5815902Seric 5824632Seric default: 5839377Seric syserr("readqf(%s): bad line \"%s\"", e->e_id, buf); 5844632Seric break; 5854632Seric } 5864632Seric } 5879377Seric 5889377Seric FileName = NULL; 5894632Seric } 5904632Seric /* 5914632Seric ** TIMEOUT -- process timeout on queue file. 5924632Seric ** 5934632Seric ** Parameters: 5949338Seric ** e -- the envelope that timed out. 5954632Seric ** 5964632Seric ** Returns: 5974632Seric ** none. 5984632Seric ** 5994632Seric ** Side Effects: 6004632Seric ** Returns a message to the sender saying that this 6014632Seric ** message has timed out. 6024632Seric */ 6034632Seric 6049338Seric timeout(e) 6059338Seric register ENVELOPE *e; 6064632Seric { 6077369Seric char buf[MAXLINE]; 6087860Seric extern char *pintvl(); 6097369Seric 6104634Seric # ifdef DEBUG 6117677Seric if (tTd(40, 3)) 6129338Seric printf("timeout(%s)\n", e->e_id); 6134634Seric # endif DEBUG 6149338Seric e->e_to = NULL; 6156991Seric message(Arpa_Info, "Message has timed out"); 6164634Seric 6174634Seric /* return message to sender */ 6187860Seric (void) sprintf(buf, "Cannot send mail for %s", pintvl(TimeOut, FALSE)); 6199338Seric (void) returntosender(buf, &e->e_from, TRUE); 6204634Seric 6214634Seric /* arrange to remove files from queue */ 6229338Seric e->e_flags |= EF_CLRQUEUE; 6234632Seric } 6245182Seric 6255182Seric # endif QUEUE 626