14632Seric # include "sendmail.h" 24632Seric # include <sys/stat.h> 34632Seric # include <sys/dir.h> 44634Seric # include <signal.h> 54632Seric # include <errno.h> 64632Seric 7*5182Seric # ifndef QUEUE 8*5182Seric static char SccsId[] = "@(#)queue.c 3.7 12/05/81 (no queueing)"; 9*5182Seric # else QUEUE 104632Seric 11*5182Seric static char SccsId[] = "@(#)queue.c 3.7 12/05/81"; 12*5182Seric 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 int i; 374632Seric register HDR *h; 384632Seric register char *p; 395007Seric register ADDRESS *q; 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 764632Seric /* output list of recipient addresses */ 775007Seric for (q = SendQueue; q != NULL; q = q->q_next) 784632Seric { 795037Seric # ifdef DEBUG 805037Seric if (Debug > 0) 815037Seric { 825037Seric printf("queueing "); 835037Seric printaddr(q, FALSE); 845037Seric } 855037Seric # endif DEBUG 865037Seric if (bitset(QQUEUEUP, q->q_flags)) 875037Seric fprintf(f, "R%s\n", q->q_paddr); 884632Seric } 894632Seric 904632Seric /* output headers for this message */ 914632Seric for (h = Header; h != NULL; h = h->h_link) 924632Seric { 934632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 944632Seric continue; 954632Seric fprintf(f, "H"); 964632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 974632Seric mfdecode(h->h_mflags, f); 984632Seric fprintf(f, "%s: ", h->h_field); 994632Seric if (bitset(H_DEFAULT, h->h_flags)) 1004632Seric { 1014632Seric char buf[MAXLINE]; 1024632Seric 1034632Seric (void) expand(h->h_value, buf, &buf[sizeof buf]); 1044632Seric fprintf(f, "%s\n", buf); 1054632Seric } 1064632Seric else 1074632Seric fprintf(f, "%s\n", h->h_value); 1084632Seric } 1094632Seric 1104632Seric /* 1114632Seric ** Clean up. 1124632Seric */ 1134632Seric 1144632Seric (void) fclose(f); 1154632Seric } 1164632Seric /* 1174632Seric ** RUNQUEUE -- run the jobs in the queue. 1184632Seric ** 1194632Seric ** Gets the stuff out of the queue in some presumably logical 1204632Seric ** order and processes them. 1214632Seric ** 1224632Seric ** Parameters: 1234632Seric ** none. 1244632Seric ** 1254632Seric ** Returns: 1264632Seric ** none. 1274632Seric ** 1284632Seric ** Side Effects: 1294632Seric ** runs things in the mail queue. 1304632Seric */ 1314632Seric 1324639Seric bool ReorderQueue; /* if set, reorder the send queue */ 1334639Seric int QueuePid; /* pid of child running queue */ 1344634Seric 1354639Seric runqueue(forkflag) 1364639Seric bool forkflag; 1374632Seric { 1384634Seric extern reordersig(); 1394632Seric 1404639Seric if (QueueIntvl != 0) 1414639Seric { 1424836Seric (void) signal(SIGALRM, reordersig); 1434836Seric (void) alarm((unsigned) QueueIntvl); 1444639Seric } 1454639Seric 1464639Seric if (forkflag) 1474639Seric { 1484639Seric QueuePid = dofork(); 1494639Seric if (QueuePid > 0) 1504639Seric { 1514639Seric /* parent */ 1524639Seric return; 1534639Seric } 1544639Seric else 1554836Seric (void) alarm((unsigned) 0); 1564639Seric } 1574639Seric 1584634Seric for (;;) 1594634Seric { 1604634Seric /* 1614634Seric ** Order the existing work requests. 1624634Seric */ 1634632Seric 1644634Seric orderq(); 1654632Seric 1664634Seric if (WorkQ == NULL) 1674634Seric { 1684634Seric /* no work? well, maybe later */ 1694634Seric if (QueueIntvl == 0) 1704634Seric break; 1714639Seric pause(); 1724634Seric continue; 1734634Seric } 1744632Seric 1754634Seric ReorderQueue = FALSE; 1764634Seric 1774634Seric /* 1784634Seric ** Process them once at a time. 1794634Seric ** The queue could be reordered while we do this to take 1804634Seric ** new requests into account. If so, the existing job 1814634Seric ** will be finished but the next thing taken off WorkQ 1824634Seric ** may be something else. 1834634Seric */ 1844634Seric 1854634Seric while (WorkQ != NULL) 1864634Seric { 1874634Seric WORK *w = WorkQ; 1884634Seric 1894634Seric WorkQ = WorkQ->w_next; 1904634Seric dowork(w); 1914634Seric free(w->w_name); 1924634Seric free((char *) w); 1934634Seric if (ReorderQueue) 1944634Seric break; 1954634Seric } 1964634Seric 1974634Seric if (QueueIntvl == 0) 1984634Seric break; 1994632Seric } 2004632Seric } 2014632Seric /* 2024634Seric ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 2034634Seric ** 2044634Seric ** Parameters: 2054634Seric ** none. 2064634Seric ** 2074634Seric ** Returns: 2084634Seric ** none. 2094634Seric ** 2104634Seric ** Side Effects: 2114634Seric ** sets the "reorder work queue" flag. 2124634Seric */ 2134634Seric 2144634Seric reordersig() 2154634Seric { 2164639Seric if (QueuePid == 0) 2174639Seric { 2184639Seric /* we are in a child doing queueing */ 2194639Seric ReorderQueue = TRUE; 2204639Seric } 2214639Seric else 2224639Seric { 2234639Seric /* we are in a parent -- poke child or start new one */ 2244639Seric if (kill(QueuePid, SIGALRM) < 0) 2254639Seric { 2264639Seric /* no child -- get zombie & start new one */ 2274639Seric static int st; 2284639Seric 2294836Seric (void) wait(&st); 2304639Seric QueuePid = dofork(); 2314639Seric if (QueuePid == 0) 2324639Seric { 2334639Seric /* new child; run queue */ 2344836Seric runqueue(FALSE); 2354639Seric finis(); 2364639Seric } 2374639Seric } 2384639Seric } 2394639Seric 2404639Seric /* 2414639Seric ** Arrange to get this signal again. 2424639Seric */ 2434639Seric 2444836Seric (void) alarm((unsigned) QueueIntvl); 2454634Seric } 2464634Seric /* 2474632Seric ** ORDERQ -- order the work queue. 2484632Seric ** 2494632Seric ** Parameters: 2504632Seric ** none. 2514632Seric ** 2524632Seric ** Returns: 2534632Seric ** none. 2544632Seric ** 2554632Seric ** Side Effects: 2564632Seric ** Sets WorkQ to the queue of available work, in order. 2574632Seric */ 2584632Seric 2594632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2604632Seric 2614632Seric orderq() 2624632Seric { 2634632Seric struct direct d; 2644632Seric register WORK *w; 2654632Seric register WORK **wp; /* parent of w */ 2664632Seric register FILE *f; 2674632Seric register int i; 2684632Seric struct stat st; 2694632Seric WORK wlist[WLSIZE]; 2704632Seric int wn = 0; 2714632Seric extern workcmpf(); 2724632Seric extern char *QueueDir; 2734632Seric 2744632Seric /* clear out old WorkQ */ 2754632Seric for (w = WorkQ; w != NULL; ) 2764632Seric { 2774632Seric register WORK *nw = w->w_next; 2784632Seric 2794632Seric WorkQ = nw; 2804632Seric free(w->w_name); 2814632Seric free((char *) w); 2824632Seric w = nw; 2834632Seric } 2844632Seric 2854632Seric /* open the queue directory */ 2864632Seric f = fopen(QueueDir, "r"); 2874632Seric if (f == NULL) 2884632Seric { 2894632Seric syserr("orderq: cannot open %s", QueueDir); 2904632Seric return; 2914632Seric } 2924632Seric 2934632Seric /* 2944632Seric ** Read the work directory. 2954632Seric */ 2964632Seric 2974632Seric while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1) 2984632Seric { 2994632Seric char cbuf[MAXNAME]; 3004632Seric char lbuf[MAXNAME]; 3014632Seric FILE *cf; 3024632Seric register char *p; 3034632Seric 3044632Seric /* is this an interesting entry? */ 3054632Seric if (d.d_ino == 0 || d.d_name[0] != 'c') 3064632Seric continue; 3074632Seric 3084632Seric /* yes -- find the control file location */ 3094632Seric strcpy(cbuf, QueueDir); 3104632Seric strcat(cbuf, "/"); 3114632Seric p = &cbuf[strlen(cbuf)]; 3124632Seric strncpy(p, d.d_name, DIRSIZ); 3134632Seric p[DIRSIZ] = '\0'; 3144632Seric 3154632Seric /* open control file */ 3164632Seric cf = fopen(cbuf, "r"); 3174632Seric if (cf == NULL) 3184632Seric { 3194632Seric syserr("orderq: cannot open %s", cbuf); 3204632Seric continue; 3214632Seric } 3225037Seric wlist[wn].w_name = newstr(cbuf); 3234632Seric 3244632Seric /* extract useful information */ 3254632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3264632Seric { 3274632Seric fixcrlf(lbuf, TRUE); 3284632Seric 3294632Seric switch (lbuf[0]) 3304632Seric { 3314632Seric case 'P': /* message priority */ 3325037Seric (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 3334632Seric break; 3344632Seric } 3354632Seric } 3364632Seric wn++; 3374632Seric (void) fclose(cf); 3384632Seric } 3394632Seric (void) fclose(f); 3404632Seric 3414632Seric /* 3424632Seric ** Sort the work directory. 3434632Seric */ 3444632Seric 3454632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3464632Seric 3474632Seric /* 3484632Seric ** Convert the work list into canonical form. 3494632Seric */ 3504632Seric 3514632Seric wp = &WorkQ; 3524632Seric for (i = 0; i < wn; i++) 3534632Seric { 3544632Seric w = (WORK *) xalloc(sizeof *w); 3554632Seric w->w_name = wlist[i].w_name; 3564632Seric w->w_pri = wlist[i].w_pri; 3574632Seric w->w_next = NULL; 3584632Seric *wp = w; 3594632Seric wp = &w->w_next; 3604632Seric } 3614632Seric 3624632Seric # ifdef DEBUG 3634632Seric if (Debug) 3644632Seric { 3654632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3665037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 3674632Seric } 3684632Seric # endif DEBUG 3694632Seric } 3704632Seric /* 3714632Seric ** WORKCMPF -- compare function for ordering work. 3724632Seric ** 3734632Seric ** Parameters: 3744632Seric ** a -- the first argument. 3754632Seric ** b -- the second argument. 3764632Seric ** 3774632Seric ** Returns: 3784632Seric ** -1 if a < b 3794632Seric ** 0 if a == b 3804632Seric ** 1 if a > b 3814632Seric ** 3824632Seric ** Side Effects: 3834632Seric ** none. 3844632Seric */ 3854632Seric 3864632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3874632Seric 3884632Seric workcmpf(a, b) 3895037Seric register WORK *a; 3905037Seric register WORK *b; 3914632Seric { 3925037Seric if (a->w_pri == b->w_pri) 3934632Seric return (0); 3945037Seric else if (a->w_pri > b->w_pri) 3954632Seric return (1); 3964632Seric else 3974632Seric return (-1); 3984632Seric } 3994632Seric /* 4004632Seric ** DOWORK -- do a work request. 4014632Seric ** 4024632Seric ** Parameters: 4034632Seric ** w -- the work request to be satisfied. 4044632Seric ** 4054632Seric ** Returns: 4064632Seric ** none. 4074632Seric ** 4084632Seric ** Side Effects: 4094632Seric ** The work request is satisfied if possible. 4104632Seric */ 4114632Seric 4124632Seric dowork(w) 4134632Seric register WORK *w; 4144632Seric { 4154632Seric register int i; 4164632Seric auto int xstat; 4174632Seric 4184632Seric # ifdef DEBUG 4194632Seric if (Debug) 4205037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 4214632Seric # endif DEBUG 4224632Seric 4234632Seric /* 4244632Seric ** Fork for work. 4254632Seric */ 4264632Seric 4274632Seric i = fork(); 4284632Seric if (i < 0) 4294632Seric { 4304632Seric syserr("dowork: cannot fork"); 4314632Seric return; 4324632Seric } 4334632Seric 4344632Seric if (i == 0) 4354632Seric { 4364632Seric /* 4374632Seric ** CHILD 4384632Seric */ 4394632Seric 4404632Seric QueueRun = TRUE; 4414634Seric openxscrpt(); 4424632Seric initsys(); 4434632Seric readqf(w->w_name); 4444632Seric sendall(FALSE); 4454634Seric # ifdef DEBUG 4464634Seric if (Debug > 2) 4474634Seric printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 4484634Seric # endif DEBUG 4494634Seric if (QueueUp && CurTime > TimeOut) 4504634Seric timeout(w); 4515037Seric (void) unlink(w->w_name); 4524632Seric finis(); 4534632Seric } 4544632Seric 4554632Seric /* 4564632Seric ** Parent -- pick up results. 4574632Seric */ 4584632Seric 4594632Seric errno = 0; 4604632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 4614632Seric { 4624632Seric if (errno == EINTR) 4634632Seric { 4644632Seric errno = 0; 4654632Seric } 4664632Seric } 4674632Seric } 4684632Seric /* 4694632Seric ** READQF -- read queue file and set up environment. 4704632Seric ** 4714632Seric ** Parameters: 4724632Seric ** cf -- name of queue control file. 4734632Seric ** 4744632Seric ** Returns: 4754632Seric ** none. 4764632Seric ** 4774632Seric ** Side Effects: 4784632Seric ** cf is read and created as the current job, as though 4794632Seric ** we had been invoked by argument. 4804632Seric */ 4814632Seric 4824632Seric readqf(cf) 4834632Seric char *cf; 4844632Seric { 4854632Seric register FILE *f; 4864632Seric char buf[MAXLINE]; 4874632Seric 4884632Seric /* 4894632Seric ** Open the file created by queueup. 4904632Seric */ 4914632Seric 4924632Seric f = fopen(cf, "r"); 4934632Seric if (f == NULL) 4944632Seric { 4954632Seric syserr("readqf: no cf file %s", cf); 4964632Seric return; 4974632Seric } 4984632Seric 4994632Seric /* 5004632Seric ** Read and process the file. 5014632Seric */ 5024632Seric 5035037Seric if (Verbose) 5045037Seric message(Arpa_Info, "Running %s (from %s)", cf, From.q_paddr); 5055037Seric 5064632Seric while (fgets(buf, sizeof buf, f) != NULL) 5074632Seric { 5084632Seric fixcrlf(buf, TRUE); 5094632Seric 5104632Seric switch (buf[0]) 5114632Seric { 5124632Seric case 'R': /* specify recipient */ 5135007Seric sendto(&buf[1], 1, (ADDRESS *) NULL, &SendQueue); 5144632Seric break; 5154632Seric 5164632Seric case 'H': /* header */ 5174632Seric (void) chompheader(&buf[1], FALSE); 5184632Seric break; 5194632Seric 5204632Seric case 'S': /* sender */ 5214634Seric setsender(newstr(&buf[1])); 5224632Seric break; 5234632Seric 5244632Seric case 'D': /* data file name */ 5254632Seric InFileName = newstr(&buf[1]); 5264632Seric TempFile = fopen(InFileName, "r"); 5274632Seric if (TempFile == NULL) 5284632Seric syserr("readqf: cannot open %s", InFileName); 5294632Seric break; 5304632Seric 5314632Seric case 'T': /* timeout */ 5324632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 5334632Seric break; 5344632Seric 5354634Seric case 'P': /* message priority */ 5365037Seric (void) sscanf(&buf[1], "%ld", &MsgPriority); 5375037Seric 5385037Seric /* make sure that big things get sent eventually */ 5395037Seric MsgPriority -= WKTIMEFACT; 5404634Seric break; 5414634Seric 5424632Seric default: 5434632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5444632Seric break; 5454632Seric } 5464632Seric } 5474632Seric } 5484632Seric /* 5494632Seric ** TIMEOUT -- process timeout on queue file. 5504632Seric ** 5514632Seric ** Parameters: 5524632Seric ** w -- pointer to work request that timed out. 5534632Seric ** 5544632Seric ** Returns: 5554632Seric ** none. 5564632Seric ** 5574632Seric ** Side Effects: 5584632Seric ** Returns a message to the sender saying that this 5594632Seric ** message has timed out. 5604632Seric */ 5614632Seric 5624632Seric timeout(w) 5634632Seric register WORK *w; 5644632Seric { 5654634Seric # ifdef DEBUG 5664634Seric if (Debug > 0) 5674634Seric printf("timeout(%s)\n", w->w_name); 5684634Seric # endif DEBUG 5694634Seric 5704634Seric /* return message to sender */ 5714634Seric (void) returntosender("Cannot send mail for three days"); 5724634Seric 5734634Seric /* arrange to remove files from queue */ 5744634Seric QueueUp = FALSE; 5754632Seric } 576*5182Seric 577*5182Seric # endif QUEUE 578