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*5199Seric SCCSID(@(#)queue.c 3.8 12/06/81 (no queueing)); 95182Seric # else QUEUE 104632Seric 11*5199Seric SCCSID(@(#)queue.c 3.8 12/06/81); 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; 38*5199Seric extern char *mktemp(); 394632Seric 405037Seric /* 415037Seric ** Create control file. 425037Seric */ 434632Seric 445037Seric strcpy(cf, QueueDir); 455037Seric strcat(cf, "/cfXXXXXX"); 465037Seric (void) mktemp(cf); 474632Seric f = fopen(cf, "w"); 484632Seric if (f == NULL) 494632Seric { 504632Seric syserr("queueup: cannot create control file %s", cf); 514632Seric return; 524632Seric } 534632Seric 544632Seric # ifdef DEBUG 554632Seric if (Debug) 564632Seric printf("queued in %s\n", cf); 574632Seric # endif DEBUG 584632Seric 594632Seric /* 604632Seric ** Output future work requests. 614632Seric */ 624632Seric 634632Seric /* output name of data file */ 644632Seric fprintf(f, "D%s\n", df); 654632Seric 664632Seric /* output name of sender */ 674632Seric fprintf(f, "S%s\n", From.q_paddr); 684632Seric 694632Seric /* output timeout */ 704632Seric fprintf(f, "T%ld\n", TimeOut); 714632Seric 724634Seric /* output message priority */ 735037Seric fprintf(f, "P%ld\n", MsgPriority); 744634Seric 754632Seric /* output list of recipient addresses */ 765007Seric for (q = SendQueue; q != NULL; q = q->q_next) 774632Seric { 785037Seric # ifdef DEBUG 795037Seric if (Debug > 0) 805037Seric { 815037Seric printf("queueing "); 825037Seric printaddr(q, FALSE); 835037Seric } 845037Seric # endif DEBUG 855037Seric if (bitset(QQUEUEUP, q->q_flags)) 865037Seric fprintf(f, "R%s\n", q->q_paddr); 874632Seric } 884632Seric 894632Seric /* output headers for this message */ 904632Seric for (h = Header; h != NULL; h = h->h_link) 914632Seric { 924632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 934632Seric continue; 944632Seric fprintf(f, "H"); 954632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 964632Seric mfdecode(h->h_mflags, f); 974632Seric fprintf(f, "%s: ", h->h_field); 984632Seric if (bitset(H_DEFAULT, h->h_flags)) 994632Seric { 1004632Seric char buf[MAXLINE]; 1014632Seric 1024632Seric (void) expand(h->h_value, buf, &buf[sizeof buf]); 1034632Seric fprintf(f, "%s\n", buf); 1044632Seric } 1054632Seric else 1064632Seric fprintf(f, "%s\n", h->h_value); 1074632Seric } 1084632Seric 1094632Seric /* 1104632Seric ** Clean up. 1114632Seric */ 1124632Seric 1134632Seric (void) fclose(f); 1144632Seric } 1154632Seric /* 1164632Seric ** RUNQUEUE -- run the jobs in the queue. 1174632Seric ** 1184632Seric ** Gets the stuff out of the queue in some presumably logical 1194632Seric ** order and processes them. 1204632Seric ** 1214632Seric ** Parameters: 1224632Seric ** none. 1234632Seric ** 1244632Seric ** Returns: 1254632Seric ** none. 1264632Seric ** 1274632Seric ** Side Effects: 1284632Seric ** runs things in the mail queue. 1294632Seric */ 1304632Seric 1314639Seric bool ReorderQueue; /* if set, reorder the send queue */ 1324639Seric int QueuePid; /* pid of child running queue */ 1334634Seric 1344639Seric runqueue(forkflag) 1354639Seric bool forkflag; 1364632Seric { 1374634Seric extern reordersig(); 1384632Seric 1394639Seric if (QueueIntvl != 0) 1404639Seric { 1414836Seric (void) signal(SIGALRM, reordersig); 1424836Seric (void) alarm((unsigned) QueueIntvl); 1434639Seric } 1444639Seric 1454639Seric if (forkflag) 1464639Seric { 1474639Seric QueuePid = dofork(); 1484639Seric if (QueuePid > 0) 1494639Seric { 1504639Seric /* parent */ 1514639Seric return; 1524639Seric } 1534639Seric else 1544836Seric (void) alarm((unsigned) 0); 1554639Seric } 1564639Seric 1574634Seric for (;;) 1584634Seric { 1594634Seric /* 1604634Seric ** Order the existing work requests. 1614634Seric */ 1624632Seric 1634634Seric orderq(); 1644632Seric 1654634Seric if (WorkQ == NULL) 1664634Seric { 1674634Seric /* no work? well, maybe later */ 1684634Seric if (QueueIntvl == 0) 1694634Seric break; 1704639Seric pause(); 1714634Seric continue; 1724634Seric } 1734632Seric 1744634Seric ReorderQueue = FALSE; 1754634Seric 1764634Seric /* 1774634Seric ** Process them once at a time. 1784634Seric ** The queue could be reordered while we do this to take 1794634Seric ** new requests into account. If so, the existing job 1804634Seric ** will be finished but the next thing taken off WorkQ 1814634Seric ** may be something else. 1824634Seric */ 1834634Seric 1844634Seric while (WorkQ != NULL) 1854634Seric { 1864634Seric WORK *w = WorkQ; 1874634Seric 1884634Seric WorkQ = WorkQ->w_next; 1894634Seric dowork(w); 1904634Seric free(w->w_name); 1914634Seric free((char *) w); 1924634Seric if (ReorderQueue) 1934634Seric break; 1944634Seric } 1954634Seric 1964634Seric if (QueueIntvl == 0) 1974634Seric break; 1984632Seric } 1994632Seric } 2004632Seric /* 2014634Seric ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 2024634Seric ** 2034634Seric ** Parameters: 2044634Seric ** none. 2054634Seric ** 2064634Seric ** Returns: 2074634Seric ** none. 2084634Seric ** 2094634Seric ** Side Effects: 2104634Seric ** sets the "reorder work queue" flag. 2114634Seric */ 2124634Seric 2134634Seric reordersig() 2144634Seric { 2154639Seric if (QueuePid == 0) 2164639Seric { 2174639Seric /* we are in a child doing queueing */ 2184639Seric ReorderQueue = TRUE; 2194639Seric } 2204639Seric else 2214639Seric { 2224639Seric /* we are in a parent -- poke child or start new one */ 2234639Seric if (kill(QueuePid, SIGALRM) < 0) 2244639Seric { 2254639Seric /* no child -- get zombie & start new one */ 2264639Seric static int st; 2274639Seric 2284836Seric (void) wait(&st); 2294639Seric QueuePid = dofork(); 2304639Seric if (QueuePid == 0) 2314639Seric { 2324639Seric /* new child; run queue */ 2334836Seric runqueue(FALSE); 2344639Seric finis(); 2354639Seric } 2364639Seric } 2374639Seric } 2384639Seric 2394639Seric /* 2404639Seric ** Arrange to get this signal again. 2414639Seric */ 2424639Seric 2434836Seric (void) alarm((unsigned) QueueIntvl); 2444634Seric } 2454634Seric /* 2464632Seric ** ORDERQ -- order the work queue. 2474632Seric ** 2484632Seric ** Parameters: 2494632Seric ** none. 2504632Seric ** 2514632Seric ** Returns: 2524632Seric ** none. 2534632Seric ** 2544632Seric ** Side Effects: 2554632Seric ** Sets WorkQ to the queue of available work, in order. 2564632Seric */ 2574632Seric 2584632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2594632Seric 2604632Seric orderq() 2614632Seric { 2624632Seric struct direct d; 2634632Seric register WORK *w; 2644632Seric register WORK **wp; /* parent of w */ 2654632Seric register FILE *f; 2664632Seric register int i; 2674632Seric WORK wlist[WLSIZE]; 2684632Seric int wn = 0; 2694632Seric extern workcmpf(); 2704632Seric extern char *QueueDir; 2714632Seric 2724632Seric /* clear out old WorkQ */ 2734632Seric for (w = WorkQ; w != NULL; ) 2744632Seric { 2754632Seric register WORK *nw = w->w_next; 2764632Seric 2774632Seric WorkQ = nw; 2784632Seric free(w->w_name); 2794632Seric free((char *) w); 2804632Seric w = nw; 2814632Seric } 2824632Seric 2834632Seric /* open the queue directory */ 2844632Seric f = fopen(QueueDir, "r"); 2854632Seric if (f == NULL) 2864632Seric { 2874632Seric syserr("orderq: cannot open %s", QueueDir); 2884632Seric return; 2894632Seric } 2904632Seric 2914632Seric /* 2924632Seric ** Read the work directory. 2934632Seric */ 2944632Seric 2954632Seric while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1) 2964632Seric { 2974632Seric char cbuf[MAXNAME]; 2984632Seric char lbuf[MAXNAME]; 2994632Seric FILE *cf; 3004632Seric register char *p; 3014632Seric 3024632Seric /* is this an interesting entry? */ 3034632Seric if (d.d_ino == 0 || d.d_name[0] != 'c') 3044632Seric continue; 3054632Seric 3064632Seric /* yes -- find the control file location */ 3074632Seric strcpy(cbuf, QueueDir); 3084632Seric strcat(cbuf, "/"); 3094632Seric p = &cbuf[strlen(cbuf)]; 3104632Seric strncpy(p, d.d_name, DIRSIZ); 3114632Seric p[DIRSIZ] = '\0'; 3124632Seric 3134632Seric /* open control file */ 3144632Seric cf = fopen(cbuf, "r"); 3154632Seric if (cf == NULL) 3164632Seric { 3174632Seric syserr("orderq: cannot open %s", cbuf); 3184632Seric continue; 3194632Seric } 3205037Seric wlist[wn].w_name = newstr(cbuf); 3214632Seric 3224632Seric /* extract useful information */ 3234632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3244632Seric { 3254632Seric fixcrlf(lbuf, TRUE); 3264632Seric 3274632Seric switch (lbuf[0]) 3284632Seric { 3294632Seric case 'P': /* message priority */ 3305037Seric (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 3314632Seric break; 3324632Seric } 3334632Seric } 3344632Seric wn++; 3354632Seric (void) fclose(cf); 3364632Seric } 3374632Seric (void) fclose(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. 3474632Seric */ 3484632Seric 3494632Seric wp = &WorkQ; 3504632Seric for (i = 0; i < wn; i++) 3514632Seric { 3524632Seric w = (WORK *) xalloc(sizeof *w); 3534632Seric w->w_name = wlist[i].w_name; 3544632Seric w->w_pri = wlist[i].w_pri; 3554632Seric w->w_next = NULL; 3564632Seric *wp = w; 3574632Seric wp = &w->w_next; 3584632Seric } 3594632Seric 3604632Seric # ifdef DEBUG 3614632Seric if (Debug) 3624632Seric { 3634632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3645037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 3654632Seric } 3664632Seric # endif DEBUG 3674632Seric } 3684632Seric /* 3694632Seric ** WORKCMPF -- compare function for ordering work. 3704632Seric ** 3714632Seric ** Parameters: 3724632Seric ** a -- the first argument. 3734632Seric ** b -- the second argument. 3744632Seric ** 3754632Seric ** Returns: 3764632Seric ** -1 if a < b 3774632Seric ** 0 if a == b 3784632Seric ** 1 if a > b 3794632Seric ** 3804632Seric ** Side Effects: 3814632Seric ** none. 3824632Seric */ 3834632Seric 3844632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3854632Seric 3864632Seric workcmpf(a, b) 3875037Seric register WORK *a; 3885037Seric register WORK *b; 3894632Seric { 3905037Seric if (a->w_pri == b->w_pri) 3914632Seric return (0); 3925037Seric else if (a->w_pri > b->w_pri) 3934632Seric return (1); 3944632Seric else 3954632Seric return (-1); 3964632Seric } 3974632Seric /* 3984632Seric ** DOWORK -- do a work request. 3994632Seric ** 4004632Seric ** Parameters: 4014632Seric ** w -- the work request to be satisfied. 4024632Seric ** 4034632Seric ** Returns: 4044632Seric ** none. 4054632Seric ** 4064632Seric ** Side Effects: 4074632Seric ** The work request is satisfied if possible. 4084632Seric */ 4094632Seric 4104632Seric dowork(w) 4114632Seric register WORK *w; 4124632Seric { 4134632Seric register int i; 4144632Seric auto int xstat; 4154632Seric 4164632Seric # ifdef DEBUG 4174632Seric if (Debug) 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 4364632Seric */ 4374632Seric 4384632Seric QueueRun = TRUE; 4394634Seric openxscrpt(); 4404632Seric initsys(); 4414632Seric readqf(w->w_name); 4424632Seric sendall(FALSE); 4434634Seric # ifdef DEBUG 4444634Seric if (Debug > 2) 4454634Seric printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 4464634Seric # endif DEBUG 4474634Seric if (QueueUp && CurTime > TimeOut) 4484634Seric timeout(w); 4495037Seric (void) unlink(w->w_name); 4504632Seric finis(); 4514632Seric } 4524632Seric 4534632Seric /* 4544632Seric ** Parent -- pick up results. 4554632Seric */ 4564632Seric 4574632Seric errno = 0; 4584632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 4594632Seric { 4604632Seric if (errno == EINTR) 4614632Seric { 4624632Seric errno = 0; 4634632Seric } 4644632Seric } 4654632Seric } 4664632Seric /* 4674632Seric ** READQF -- read queue file and set up environment. 4684632Seric ** 4694632Seric ** Parameters: 4704632Seric ** cf -- name of queue control file. 4714632Seric ** 4724632Seric ** Returns: 4734632Seric ** none. 4744632Seric ** 4754632Seric ** Side Effects: 4764632Seric ** cf is read and created as the current job, as though 4774632Seric ** we had been invoked by argument. 4784632Seric */ 4794632Seric 4804632Seric readqf(cf) 4814632Seric char *cf; 4824632Seric { 4834632Seric register FILE *f; 4844632Seric char buf[MAXLINE]; 4854632Seric 4864632Seric /* 4874632Seric ** Open the file created by queueup. 4884632Seric */ 4894632Seric 4904632Seric f = fopen(cf, "r"); 4914632Seric if (f == NULL) 4924632Seric { 4934632Seric syserr("readqf: no cf file %s", cf); 4944632Seric return; 4954632Seric } 4964632Seric 4974632Seric /* 4984632Seric ** Read and process the file. 4994632Seric */ 5004632Seric 5015037Seric if (Verbose) 5025037Seric message(Arpa_Info, "Running %s (from %s)", cf, From.q_paddr); 5035037Seric 5044632Seric while (fgets(buf, sizeof buf, f) != NULL) 5054632Seric { 5064632Seric fixcrlf(buf, TRUE); 5074632Seric 5084632Seric switch (buf[0]) 5094632Seric { 5104632Seric case 'R': /* specify recipient */ 5115007Seric sendto(&buf[1], 1, (ADDRESS *) NULL, &SendQueue); 5124632Seric break; 5134632Seric 5144632Seric case 'H': /* header */ 5154632Seric (void) chompheader(&buf[1], FALSE); 5164632Seric break; 5174632Seric 5184632Seric case 'S': /* sender */ 5194634Seric setsender(newstr(&buf[1])); 5204632Seric break; 5214632Seric 5224632Seric case 'D': /* data file name */ 5234632Seric InFileName = newstr(&buf[1]); 5244632Seric TempFile = fopen(InFileName, "r"); 5254632Seric if (TempFile == NULL) 5264632Seric syserr("readqf: cannot open %s", InFileName); 5274632Seric break; 5284632Seric 5294632Seric case 'T': /* timeout */ 5304632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 5314632Seric break; 5324632Seric 5334634Seric case 'P': /* message priority */ 5345037Seric (void) sscanf(&buf[1], "%ld", &MsgPriority); 5355037Seric 5365037Seric /* make sure that big things get sent eventually */ 5375037Seric MsgPriority -= WKTIMEFACT; 5384634Seric break; 5394634Seric 5404632Seric default: 5414632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5424632Seric break; 5434632Seric } 5444632Seric } 5454632Seric } 5464632Seric /* 5474632Seric ** TIMEOUT -- process timeout on queue file. 5484632Seric ** 5494632Seric ** Parameters: 5504632Seric ** w -- pointer to work request that timed out. 5514632Seric ** 5524632Seric ** Returns: 5534632Seric ** none. 5544632Seric ** 5554632Seric ** Side Effects: 5564632Seric ** Returns a message to the sender saying that this 5574632Seric ** message has timed out. 5584632Seric */ 5594632Seric 5604632Seric timeout(w) 5614632Seric register WORK *w; 5624632Seric { 5634634Seric # ifdef DEBUG 5644634Seric if (Debug > 0) 5654634Seric printf("timeout(%s)\n", w->w_name); 5664634Seric # endif DEBUG 5674634Seric 5684634Seric /* return message to sender */ 5694634Seric (void) returntosender("Cannot send mail for three days"); 5704634Seric 5714634Seric /* arrange to remove files from queue */ 5724634Seric QueueUp = FALSE; 5734632Seric } 5745182Seric 5755182Seric # endif QUEUE 576