14632Seric # include "sendmail.h" 24632Seric # include <sys/stat.h> 34632Seric # include <sys/dir.h> 44634Seric # include <signal.h> 54632Seric # include <errno.h> 64632Seric 75182Seric # ifndef QUEUE 8*6065Seric SCCSID(@(#)queue.c 3.11 03/06/82 (no queueing)); 95182Seric # else QUEUE 104632Seric 11*6065Seric SCCSID(@(#)queue.c 3.11 03/06/82); 125182Seric 134632Seric /* 144632Seric ** QUEUEUP -- queue a message up for future transmission. 154632Seric ** 164632Seric ** The queued message should already be in the correct place. 174632Seric ** This routine just outputs the control file as appropriate. 184632Seric ** 194632Seric ** Parameters: 204632Seric ** df -- location of the data file. The name will 214632Seric ** be transformed into a control file name. 224632Seric ** 234632Seric ** Returns: 244632Seric ** none. 254632Seric ** 264632Seric ** Side Effects: 274632Seric ** The current request (only unsatisfied addresses) 284632Seric ** are saved in a control file. 294632Seric */ 304632Seric 314632Seric queueup(df) 324632Seric char *df; 334632Seric { 344632Seric char cf[MAXNAME]; 354632Seric register FILE *f; 364632Seric register HDR *h; 375007Seric register ADDRESS *q; 385199Seric extern char *mktemp(); 395902Seric register int i; 404632Seric 415037Seric /* 425037Seric ** Create control file. 435037Seric */ 444632Seric 455037Seric strcpy(cf, QueueDir); 465037Seric strcat(cf, "/cfXXXXXX"); 475037Seric (void) mktemp(cf); 484632Seric f = fopen(cf, "w"); 494632Seric if (f == NULL) 504632Seric { 514632Seric syserr("queueup: cannot create control file %s", cf); 524632Seric return; 534632Seric } 544632Seric 554632Seric # ifdef DEBUG 564632Seric if (Debug) 574632Seric printf("queued in %s\n", cf); 584632Seric # endif DEBUG 594632Seric 604632Seric /* 614632Seric ** Output future work requests. 624632Seric */ 634632Seric 644632Seric /* output name of data file */ 654632Seric fprintf(f, "D%s\n", df); 664632Seric 674632Seric /* output name of sender */ 684632Seric fprintf(f, "S%s\n", From.q_paddr); 694632Seric 704632Seric /* output timeout */ 714632Seric fprintf(f, "T%ld\n", TimeOut); 724632Seric 734634Seric /* output message priority */ 745037Seric fprintf(f, "P%ld\n", MsgPriority); 754634Seric 765902Seric /* output macro definitions */ 775902Seric for (i = 0; i < 128; i++) 785902Seric { 795902Seric extern char *Macro[128]; 805902Seric register char *p = Macro[i]; 815902Seric 825902Seric if (p != NULL && i != (int) 'b') 835902Seric fprintf(f, "M%c%s\n", i, p); 845902Seric } 855902Seric 864632Seric /* output list of recipient addresses */ 875007Seric for (q = SendQueue; q != NULL; q = q->q_next) 884632Seric { 895037Seric # ifdef DEBUG 905037Seric if (Debug > 0) 915037Seric { 925037Seric printf("queueing "); 935037Seric printaddr(q, FALSE); 945037Seric } 955037Seric # endif DEBUG 965037Seric if (bitset(QQUEUEUP, q->q_flags)) 975037Seric fprintf(f, "R%s\n", q->q_paddr); 984632Seric } 994632Seric 1004632Seric /* output headers for this message */ 1014632Seric for (h = Header; h != NULL; h = h->h_link) 1024632Seric { 1034632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 1044632Seric continue; 1054632Seric fprintf(f, "H"); 1064632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 1074632Seric mfdecode(h->h_mflags, f); 1085902Seric fprintf(f, "%s: %s\n", h->h_field, h->h_value); 1094632Seric } 1104632Seric 1114632Seric /* 1124632Seric ** Clean up. 1134632Seric */ 1144632Seric 1154632Seric (void) fclose(f); 1164632Seric } 1174632Seric /* 1184632Seric ** RUNQUEUE -- run the jobs in the queue. 1194632Seric ** 1204632Seric ** Gets the stuff out of the queue in some presumably logical 1214632Seric ** order and processes them. 1224632Seric ** 1234632Seric ** Parameters: 1244632Seric ** none. 1254632Seric ** 1264632Seric ** Returns: 1274632Seric ** none. 1284632Seric ** 1294632Seric ** Side Effects: 1304632Seric ** runs things in the mail queue. 1314632Seric */ 1324632Seric 1334639Seric bool ReorderQueue; /* if set, reorder the send queue */ 1344639Seric int QueuePid; /* pid of child running queue */ 1354634Seric 1364639Seric runqueue(forkflag) 1374639Seric bool forkflag; 1384632Seric { 1394634Seric extern reordersig(); 1404632Seric 1414639Seric if (QueueIntvl != 0) 1424639Seric { 1434836Seric (void) signal(SIGALRM, reordersig); 1444836Seric (void) alarm((unsigned) QueueIntvl); 1454639Seric } 1464639Seric 1474639Seric if (forkflag) 1484639Seric { 1494639Seric QueuePid = dofork(); 1504639Seric if (QueuePid > 0) 1514639Seric { 1524639Seric /* parent */ 1534639Seric return; 1544639Seric } 1554639Seric else 1564836Seric (void) alarm((unsigned) 0); 1574639Seric } 1584639Seric 1594634Seric for (;;) 1604634Seric { 1614634Seric /* 1624634Seric ** Order the existing work requests. 1634634Seric */ 1644632Seric 1654634Seric orderq(); 1664632Seric 1674634Seric if (WorkQ == NULL) 1684634Seric { 1694634Seric /* no work? well, maybe later */ 1704634Seric if (QueueIntvl == 0) 1714634Seric break; 1724639Seric pause(); 1734634Seric continue; 1744634Seric } 1754632Seric 1764634Seric ReorderQueue = FALSE; 1774634Seric 1784634Seric /* 1794634Seric ** Process them once at a time. 1804634Seric ** The queue could be reordered while we do this to take 1814634Seric ** new requests into account. If so, the existing job 1824634Seric ** will be finished but the next thing taken off WorkQ 1834634Seric ** may be something else. 1844634Seric */ 1854634Seric 1864634Seric while (WorkQ != NULL) 1874634Seric { 1884634Seric WORK *w = WorkQ; 1894634Seric 1904634Seric WorkQ = WorkQ->w_next; 1914634Seric dowork(w); 1924634Seric free(w->w_name); 1934634Seric free((char *) w); 1944634Seric if (ReorderQueue) 1954634Seric break; 1964634Seric } 1974634Seric 1984634Seric if (QueueIntvl == 0) 1994634Seric break; 2004632Seric } 2015978Seric 2025978Seric /* no work to do -- just exit */ 2035978Seric finis(); 2044632Seric } 2054632Seric /* 2064634Seric ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 2074634Seric ** 2084634Seric ** Parameters: 2094634Seric ** none. 2104634Seric ** 2114634Seric ** Returns: 2124634Seric ** none. 2134634Seric ** 2144634Seric ** Side Effects: 2154634Seric ** sets the "reorder work queue" flag. 2164634Seric */ 2174634Seric 2184634Seric reordersig() 2194634Seric { 2204639Seric if (QueuePid == 0) 2214639Seric { 2224639Seric /* we are in a child doing queueing */ 2234639Seric ReorderQueue = TRUE; 2244639Seric } 2254639Seric else 2264639Seric { 2274639Seric /* we are in a parent -- poke child or start new one */ 2284639Seric if (kill(QueuePid, SIGALRM) < 0) 2294639Seric { 2304639Seric /* no child -- get zombie & start new one */ 2314639Seric static int st; 2324639Seric 2334836Seric (void) wait(&st); 2344639Seric QueuePid = dofork(); 2354639Seric if (QueuePid == 0) 2364639Seric { 2374639Seric /* new child; run queue */ 2384836Seric runqueue(FALSE); 2394639Seric finis(); 2404639Seric } 2414639Seric } 2424639Seric } 2434639Seric 2444639Seric /* 2454639Seric ** Arrange to get this signal again. 2464639Seric */ 2474639Seric 248*6065Seric (void) signal(SIGALRM, reordersig); 2494836Seric (void) alarm((unsigned) QueueIntvl); 2504634Seric } 2514634Seric /* 2524632Seric ** ORDERQ -- order the work queue. 2534632Seric ** 2544632Seric ** Parameters: 2554632Seric ** none. 2564632Seric ** 2574632Seric ** Returns: 2584632Seric ** none. 2594632Seric ** 2604632Seric ** Side Effects: 2614632Seric ** Sets WorkQ to the queue of available work, in order. 2624632Seric */ 2634632Seric 2644632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2654632Seric 2664632Seric orderq() 2674632Seric { 2684632Seric struct direct d; 2694632Seric register WORK *w; 2704632Seric register WORK **wp; /* parent of w */ 2714632Seric register FILE *f; 2724632Seric register int i; 2734632Seric WORK wlist[WLSIZE]; 2744632Seric int wn = 0; 2754632Seric extern workcmpf(); 2764632Seric extern char *QueueDir; 2774632Seric 2784632Seric /* clear out old WorkQ */ 2794632Seric for (w = WorkQ; w != NULL; ) 2804632Seric { 2814632Seric register WORK *nw = w->w_next; 2824632Seric 2834632Seric WorkQ = nw; 2844632Seric free(w->w_name); 2854632Seric free((char *) w); 2864632Seric w = nw; 2874632Seric } 2884632Seric 2894632Seric /* open the queue directory */ 2904632Seric f = fopen(QueueDir, "r"); 2914632Seric if (f == NULL) 2924632Seric { 2934632Seric syserr("orderq: cannot open %s", QueueDir); 2944632Seric return; 2954632Seric } 2964632Seric 2974632Seric /* 2984632Seric ** Read the work directory. 2994632Seric */ 3004632Seric 3014632Seric while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1) 3024632Seric { 3034632Seric char cbuf[MAXNAME]; 3044632Seric char lbuf[MAXNAME]; 3054632Seric FILE *cf; 3064632Seric register char *p; 3074632Seric 3084632Seric /* is this an interesting entry? */ 3094632Seric if (d.d_ino == 0 || d.d_name[0] != 'c') 3104632Seric continue; 3114632Seric 3124632Seric /* yes -- find the control file location */ 3134632Seric strcpy(cbuf, QueueDir); 3144632Seric strcat(cbuf, "/"); 3154632Seric p = &cbuf[strlen(cbuf)]; 3164632Seric strncpy(p, d.d_name, DIRSIZ); 3174632Seric p[DIRSIZ] = '\0'; 3184632Seric 3194632Seric /* open control file */ 3204632Seric cf = fopen(cbuf, "r"); 3214632Seric if (cf == NULL) 3224632Seric { 3234632Seric syserr("orderq: cannot open %s", cbuf); 3244632Seric continue; 3254632Seric } 3265037Seric wlist[wn].w_name = newstr(cbuf); 3274632Seric 3284632Seric /* extract useful information */ 3294632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3304632Seric { 3314632Seric fixcrlf(lbuf, TRUE); 3324632Seric 3334632Seric switch (lbuf[0]) 3344632Seric { 3354632Seric case 'P': /* message priority */ 3365037Seric (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 3374632Seric break; 3384632Seric } 3394632Seric } 3404632Seric wn++; 3414632Seric (void) fclose(cf); 3424632Seric } 3434632Seric (void) fclose(f); 3444632Seric 3454632Seric /* 3464632Seric ** Sort the work directory. 3474632Seric */ 3484632Seric 3494632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3504632Seric 3514632Seric /* 3524632Seric ** Convert the work list into canonical form. 3534632Seric */ 3544632Seric 3554632Seric wp = &WorkQ; 3564632Seric for (i = 0; i < wn; i++) 3574632Seric { 3584632Seric w = (WORK *) xalloc(sizeof *w); 3594632Seric w->w_name = wlist[i].w_name; 3604632Seric w->w_pri = wlist[i].w_pri; 3614632Seric w->w_next = NULL; 3624632Seric *wp = w; 3634632Seric wp = &w->w_next; 3644632Seric } 3654632Seric 3664632Seric # ifdef DEBUG 3674632Seric if (Debug) 3684632Seric { 3694632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3705037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 3714632Seric } 3724632Seric # endif DEBUG 3734632Seric } 3744632Seric /* 3754632Seric ** WORKCMPF -- compare function for ordering work. 3764632Seric ** 3774632Seric ** Parameters: 3784632Seric ** a -- the first argument. 3794632Seric ** b -- the second argument. 3804632Seric ** 3814632Seric ** Returns: 3824632Seric ** -1 if a < b 3834632Seric ** 0 if a == b 3844632Seric ** 1 if a > b 3854632Seric ** 3864632Seric ** Side Effects: 3874632Seric ** none. 3884632Seric */ 3894632Seric 3904632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3914632Seric 3924632Seric workcmpf(a, b) 3935037Seric register WORK *a; 3945037Seric register WORK *b; 3954632Seric { 3965037Seric if (a->w_pri == b->w_pri) 3974632Seric return (0); 3985037Seric else if (a->w_pri > b->w_pri) 3994632Seric return (1); 4004632Seric else 4014632Seric return (-1); 4024632Seric } 4034632Seric /* 4044632Seric ** DOWORK -- do a work request. 4054632Seric ** 4064632Seric ** Parameters: 4074632Seric ** w -- the work request to be satisfied. 4084632Seric ** 4094632Seric ** Returns: 4104632Seric ** none. 4114632Seric ** 4124632Seric ** Side Effects: 4134632Seric ** The work request is satisfied if possible. 4144632Seric */ 4154632Seric 4164632Seric dowork(w) 4174632Seric register WORK *w; 4184632Seric { 4194632Seric register int i; 4204632Seric auto int xstat; 4214632Seric 4224632Seric # ifdef DEBUG 4234632Seric if (Debug) 4245037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 4254632Seric # endif DEBUG 4264632Seric 4274632Seric /* 4284632Seric ** Fork for work. 4294632Seric */ 4304632Seric 4314632Seric i = fork(); 4324632Seric if (i < 0) 4334632Seric { 4344632Seric syserr("dowork: cannot fork"); 4354632Seric return; 4364632Seric } 4374632Seric 4384632Seric if (i == 0) 4394632Seric { 4404632Seric /* 4414632Seric ** CHILD 4424632Seric */ 4434632Seric 4444632Seric QueueRun = TRUE; 4454634Seric openxscrpt(); 4464632Seric initsys(); 4474632Seric readqf(w->w_name); 4484632Seric sendall(FALSE); 4494634Seric # ifdef DEBUG 4504634Seric if (Debug > 2) 4514634Seric printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 4524634Seric # endif DEBUG 4534634Seric if (QueueUp && CurTime > TimeOut) 4544634Seric timeout(w); 4555037Seric (void) unlink(w->w_name); 4564632Seric finis(); 4574632Seric } 4584632Seric 4594632Seric /* 4604632Seric ** Parent -- pick up results. 4614632Seric */ 4624632Seric 4634632Seric errno = 0; 4644632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 4654632Seric { 4664632Seric if (errno == EINTR) 4674632Seric { 4684632Seric errno = 0; 4694632Seric } 4704632Seric } 4714632Seric } 4724632Seric /* 4734632Seric ** READQF -- read queue file and set up environment. 4744632Seric ** 4754632Seric ** Parameters: 4764632Seric ** cf -- name of queue control file. 4774632Seric ** 4784632Seric ** Returns: 4794632Seric ** none. 4804632Seric ** 4814632Seric ** Side Effects: 4824632Seric ** cf is read and created as the current job, as though 4834632Seric ** we had been invoked by argument. 4844632Seric */ 4854632Seric 4864632Seric readqf(cf) 4874632Seric char *cf; 4884632Seric { 4894632Seric register FILE *f; 4904632Seric char buf[MAXLINE]; 4914632Seric 4924632Seric /* 4934632Seric ** Open the file created by queueup. 4944632Seric */ 4954632Seric 4964632Seric f = fopen(cf, "r"); 4974632Seric if (f == NULL) 4984632Seric { 4994632Seric syserr("readqf: no cf file %s", cf); 5004632Seric return; 5014632Seric } 5024632Seric 5034632Seric /* 5044632Seric ** Read and process the file. 5054632Seric */ 5064632Seric 5075037Seric if (Verbose) 5085037Seric message(Arpa_Info, "Running %s (from %s)", cf, From.q_paddr); 5095037Seric 5104632Seric while (fgets(buf, sizeof buf, f) != NULL) 5114632Seric { 5124632Seric fixcrlf(buf, TRUE); 5134632Seric 5144632Seric switch (buf[0]) 5154632Seric { 5164632Seric case 'R': /* specify recipient */ 5175007Seric sendto(&buf[1], 1, (ADDRESS *) NULL, &SendQueue); 5184632Seric break; 5194632Seric 5204632Seric case 'H': /* header */ 5214632Seric (void) chompheader(&buf[1], FALSE); 5224632Seric break; 5234632Seric 5244632Seric case 'S': /* sender */ 5254634Seric setsender(newstr(&buf[1])); 5264632Seric break; 5274632Seric 5284632Seric case 'D': /* data file name */ 5294632Seric InFileName = newstr(&buf[1]); 5304632Seric TempFile = fopen(InFileName, "r"); 5314632Seric if (TempFile == NULL) 5324632Seric syserr("readqf: cannot open %s", InFileName); 5334632Seric break; 5344632Seric 5354632Seric case 'T': /* timeout */ 5364632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 5374632Seric break; 5384632Seric 5394634Seric case 'P': /* message priority */ 5405037Seric (void) sscanf(&buf[1], "%ld", &MsgPriority); 5415037Seric 5425037Seric /* make sure that big things get sent eventually */ 5435037Seric MsgPriority -= WKTIMEFACT; 5444634Seric break; 5454634Seric 5465902Seric case 'M': /* define macro */ 5475902Seric define(buf[1], newstr(&buf[2])); 5485902Seric break; 5495902Seric 5504632Seric default: 5514632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5524632Seric break; 5534632Seric } 5544632Seric } 5554632Seric } 5564632Seric /* 5574632Seric ** TIMEOUT -- process timeout on queue file. 5584632Seric ** 5594632Seric ** Parameters: 5604632Seric ** w -- pointer to work request that timed out. 5614632Seric ** 5624632Seric ** Returns: 5634632Seric ** none. 5644632Seric ** 5654632Seric ** Side Effects: 5664632Seric ** Returns a message to the sender saying that this 5674632Seric ** message has timed out. 5684632Seric */ 5694632Seric 5704632Seric timeout(w) 5714632Seric register WORK *w; 5724632Seric { 5734634Seric # ifdef DEBUG 5744634Seric if (Debug > 0) 5754634Seric printf("timeout(%s)\n", w->w_name); 5764634Seric # endif DEBUG 5774634Seric 5784634Seric /* return message to sender */ 5794634Seric (void) returntosender("Cannot send mail for three days"); 5804634Seric 5814634Seric /* arrange to remove files from queue */ 5824634Seric QueueUp = FALSE; 5834632Seric } 5845182Seric 5855182Seric # endif QUEUE 586