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*5978Seric SCCSID(@(#)queue.c 3.10 02/26/82 (no queueing)); 95182Seric # else QUEUE 104632Seric 11*5978Seric SCCSID(@(#)queue.c 3.10 02/26/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 } 201*5978Seric 202*5978Seric /* no work to do -- just exit */ 203*5978Seric 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 2484836Seric (void) alarm((unsigned) QueueIntvl); 2494634Seric } 2504634Seric /* 2514632Seric ** ORDERQ -- order the work queue. 2524632Seric ** 2534632Seric ** Parameters: 2544632Seric ** none. 2554632Seric ** 2564632Seric ** Returns: 2574632Seric ** none. 2584632Seric ** 2594632Seric ** Side Effects: 2604632Seric ** Sets WorkQ to the queue of available work, in order. 2614632Seric */ 2624632Seric 2634632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2644632Seric 2654632Seric orderq() 2664632Seric { 2674632Seric struct direct d; 2684632Seric register WORK *w; 2694632Seric register WORK **wp; /* parent of w */ 2704632Seric register FILE *f; 2714632Seric register int i; 2724632Seric WORK wlist[WLSIZE]; 2734632Seric int wn = 0; 2744632Seric extern workcmpf(); 2754632Seric extern char *QueueDir; 2764632Seric 2774632Seric /* clear out old WorkQ */ 2784632Seric for (w = WorkQ; w != NULL; ) 2794632Seric { 2804632Seric register WORK *nw = w->w_next; 2814632Seric 2824632Seric WorkQ = nw; 2834632Seric free(w->w_name); 2844632Seric free((char *) w); 2854632Seric w = nw; 2864632Seric } 2874632Seric 2884632Seric /* open the queue directory */ 2894632Seric f = fopen(QueueDir, "r"); 2904632Seric if (f == NULL) 2914632Seric { 2924632Seric syserr("orderq: cannot open %s", QueueDir); 2934632Seric return; 2944632Seric } 2954632Seric 2964632Seric /* 2974632Seric ** Read the work directory. 2984632Seric */ 2994632Seric 3004632Seric while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1) 3014632Seric { 3024632Seric char cbuf[MAXNAME]; 3034632Seric char lbuf[MAXNAME]; 3044632Seric FILE *cf; 3054632Seric register char *p; 3064632Seric 3074632Seric /* is this an interesting entry? */ 3084632Seric if (d.d_ino == 0 || d.d_name[0] != 'c') 3094632Seric continue; 3104632Seric 3114632Seric /* yes -- find the control file location */ 3124632Seric strcpy(cbuf, QueueDir); 3134632Seric strcat(cbuf, "/"); 3144632Seric p = &cbuf[strlen(cbuf)]; 3154632Seric strncpy(p, d.d_name, DIRSIZ); 3164632Seric p[DIRSIZ] = '\0'; 3174632Seric 3184632Seric /* open control file */ 3194632Seric cf = fopen(cbuf, "r"); 3204632Seric if (cf == NULL) 3214632Seric { 3224632Seric syserr("orderq: cannot open %s", cbuf); 3234632Seric continue; 3244632Seric } 3255037Seric wlist[wn].w_name = newstr(cbuf); 3264632Seric 3274632Seric /* extract useful information */ 3284632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3294632Seric { 3304632Seric fixcrlf(lbuf, TRUE); 3314632Seric 3324632Seric switch (lbuf[0]) 3334632Seric { 3344632Seric case 'P': /* message priority */ 3355037Seric (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 3364632Seric break; 3374632Seric } 3384632Seric } 3394632Seric wn++; 3404632Seric (void) fclose(cf); 3414632Seric } 3424632Seric (void) fclose(f); 3434632Seric 3444632Seric /* 3454632Seric ** Sort the work directory. 3464632Seric */ 3474632Seric 3484632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3494632Seric 3504632Seric /* 3514632Seric ** Convert the work list into canonical form. 3524632Seric */ 3534632Seric 3544632Seric wp = &WorkQ; 3554632Seric for (i = 0; i < wn; i++) 3564632Seric { 3574632Seric w = (WORK *) xalloc(sizeof *w); 3584632Seric w->w_name = wlist[i].w_name; 3594632Seric w->w_pri = wlist[i].w_pri; 3604632Seric w->w_next = NULL; 3614632Seric *wp = w; 3624632Seric wp = &w->w_next; 3634632Seric } 3644632Seric 3654632Seric # ifdef DEBUG 3664632Seric if (Debug) 3674632Seric { 3684632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3695037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 3704632Seric } 3714632Seric # endif DEBUG 3724632Seric } 3734632Seric /* 3744632Seric ** WORKCMPF -- compare function for ordering work. 3754632Seric ** 3764632Seric ** Parameters: 3774632Seric ** a -- the first argument. 3784632Seric ** b -- the second argument. 3794632Seric ** 3804632Seric ** Returns: 3814632Seric ** -1 if a < b 3824632Seric ** 0 if a == b 3834632Seric ** 1 if a > b 3844632Seric ** 3854632Seric ** Side Effects: 3864632Seric ** none. 3874632Seric */ 3884632Seric 3894632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3904632Seric 3914632Seric workcmpf(a, b) 3925037Seric register WORK *a; 3935037Seric register WORK *b; 3944632Seric { 3955037Seric if (a->w_pri == b->w_pri) 3964632Seric return (0); 3975037Seric else if (a->w_pri > b->w_pri) 3984632Seric return (1); 3994632Seric else 4004632Seric return (-1); 4014632Seric } 4024632Seric /* 4034632Seric ** DOWORK -- do a work request. 4044632Seric ** 4054632Seric ** Parameters: 4064632Seric ** w -- the work request to be satisfied. 4074632Seric ** 4084632Seric ** Returns: 4094632Seric ** none. 4104632Seric ** 4114632Seric ** Side Effects: 4124632Seric ** The work request is satisfied if possible. 4134632Seric */ 4144632Seric 4154632Seric dowork(w) 4164632Seric register WORK *w; 4174632Seric { 4184632Seric register int i; 4194632Seric auto int xstat; 4204632Seric 4214632Seric # ifdef DEBUG 4224632Seric if (Debug) 4235037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 4244632Seric # endif DEBUG 4254632Seric 4264632Seric /* 4274632Seric ** Fork for work. 4284632Seric */ 4294632Seric 4304632Seric i = fork(); 4314632Seric if (i < 0) 4324632Seric { 4334632Seric syserr("dowork: cannot fork"); 4344632Seric return; 4354632Seric } 4364632Seric 4374632Seric if (i == 0) 4384632Seric { 4394632Seric /* 4404632Seric ** CHILD 4414632Seric */ 4424632Seric 4434632Seric QueueRun = TRUE; 4444634Seric openxscrpt(); 4454632Seric initsys(); 4464632Seric readqf(w->w_name); 4474632Seric sendall(FALSE); 4484634Seric # ifdef DEBUG 4494634Seric if (Debug > 2) 4504634Seric printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 4514634Seric # endif DEBUG 4524634Seric if (QueueUp && CurTime > TimeOut) 4534634Seric timeout(w); 4545037Seric (void) unlink(w->w_name); 4554632Seric finis(); 4564632Seric } 4574632Seric 4584632Seric /* 4594632Seric ** Parent -- pick up results. 4604632Seric */ 4614632Seric 4624632Seric errno = 0; 4634632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 4644632Seric { 4654632Seric if (errno == EINTR) 4664632Seric { 4674632Seric errno = 0; 4684632Seric } 4694632Seric } 4704632Seric } 4714632Seric /* 4724632Seric ** READQF -- read queue file and set up environment. 4734632Seric ** 4744632Seric ** Parameters: 4754632Seric ** cf -- name of queue control file. 4764632Seric ** 4774632Seric ** Returns: 4784632Seric ** none. 4794632Seric ** 4804632Seric ** Side Effects: 4814632Seric ** cf is read and created as the current job, as though 4824632Seric ** we had been invoked by argument. 4834632Seric */ 4844632Seric 4854632Seric readqf(cf) 4864632Seric char *cf; 4874632Seric { 4884632Seric register FILE *f; 4894632Seric char buf[MAXLINE]; 4904632Seric 4914632Seric /* 4924632Seric ** Open the file created by queueup. 4934632Seric */ 4944632Seric 4954632Seric f = fopen(cf, "r"); 4964632Seric if (f == NULL) 4974632Seric { 4984632Seric syserr("readqf: no cf file %s", cf); 4994632Seric return; 5004632Seric } 5014632Seric 5024632Seric /* 5034632Seric ** Read and process the file. 5044632Seric */ 5054632Seric 5065037Seric if (Verbose) 5075037Seric message(Arpa_Info, "Running %s (from %s)", cf, From.q_paddr); 5085037Seric 5094632Seric while (fgets(buf, sizeof buf, f) != NULL) 5104632Seric { 5114632Seric fixcrlf(buf, TRUE); 5124632Seric 5134632Seric switch (buf[0]) 5144632Seric { 5154632Seric case 'R': /* specify recipient */ 5165007Seric sendto(&buf[1], 1, (ADDRESS *) NULL, &SendQueue); 5174632Seric break; 5184632Seric 5194632Seric case 'H': /* header */ 5204632Seric (void) chompheader(&buf[1], FALSE); 5214632Seric break; 5224632Seric 5234632Seric case 'S': /* sender */ 5244634Seric setsender(newstr(&buf[1])); 5254632Seric break; 5264632Seric 5274632Seric case 'D': /* data file name */ 5284632Seric InFileName = newstr(&buf[1]); 5294632Seric TempFile = fopen(InFileName, "r"); 5304632Seric if (TempFile == NULL) 5314632Seric syserr("readqf: cannot open %s", InFileName); 5324632Seric break; 5334632Seric 5344632Seric case 'T': /* timeout */ 5354632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 5364632Seric break; 5374632Seric 5384634Seric case 'P': /* message priority */ 5395037Seric (void) sscanf(&buf[1], "%ld", &MsgPriority); 5405037Seric 5415037Seric /* make sure that big things get sent eventually */ 5425037Seric MsgPriority -= WKTIMEFACT; 5434634Seric break; 5444634Seric 5455902Seric case 'M': /* define macro */ 5465902Seric define(buf[1], newstr(&buf[2])); 5475902Seric break; 5485902Seric 5494632Seric default: 5504632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5514632Seric break; 5524632Seric } 5534632Seric } 5544632Seric } 5554632Seric /* 5564632Seric ** TIMEOUT -- process timeout on queue file. 5574632Seric ** 5584632Seric ** Parameters: 5594632Seric ** w -- pointer to work request that timed out. 5604632Seric ** 5614632Seric ** Returns: 5624632Seric ** none. 5634632Seric ** 5644632Seric ** Side Effects: 5654632Seric ** Returns a message to the sender saying that this 5664632Seric ** message has timed out. 5674632Seric */ 5684632Seric 5694632Seric timeout(w) 5704632Seric register WORK *w; 5714632Seric { 5724634Seric # ifdef DEBUG 5734634Seric if (Debug > 0) 5744634Seric printf("timeout(%s)\n", w->w_name); 5754634Seric # endif DEBUG 5764634Seric 5774634Seric /* return message to sender */ 5784634Seric (void) returntosender("Cannot send mail for three days"); 5794634Seric 5804634Seric /* arrange to remove files from queue */ 5814634Seric QueueUp = FALSE; 5824632Seric } 5835182Seric 5845182Seric # endif QUEUE 585