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*5902Seric SCCSID(@(#)queue.c 3.9 02/20/82 (no queueing)); 95182Seric # else QUEUE 104632Seric 11*5902Seric SCCSID(@(#)queue.c 3.9 02/20/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(); 39*5902Seric 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 76*5902Seric /* output macro definitions */ 77*5902Seric for (i = 0; i < 128; i++) 78*5902Seric { 79*5902Seric extern char *Macro[128]; 80*5902Seric register char *p = Macro[i]; 81*5902Seric 82*5902Seric if (p != NULL && i != (int) 'b') 83*5902Seric fprintf(f, "M%c%s\n", i, p); 84*5902Seric } 85*5902Seric 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); 108*5902Seric 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 } 2014632Seric } 2024632Seric /* 2034634Seric ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 2044634Seric ** 2054634Seric ** Parameters: 2064634Seric ** none. 2074634Seric ** 2084634Seric ** Returns: 2094634Seric ** none. 2104634Seric ** 2114634Seric ** Side Effects: 2124634Seric ** sets the "reorder work queue" flag. 2134634Seric */ 2144634Seric 2154634Seric reordersig() 2164634Seric { 2174639Seric if (QueuePid == 0) 2184639Seric { 2194639Seric /* we are in a child doing queueing */ 2204639Seric ReorderQueue = TRUE; 2214639Seric } 2224639Seric else 2234639Seric { 2244639Seric /* we are in a parent -- poke child or start new one */ 2254639Seric if (kill(QueuePid, SIGALRM) < 0) 2264639Seric { 2274639Seric /* no child -- get zombie & start new one */ 2284639Seric static int st; 2294639Seric 2304836Seric (void) wait(&st); 2314639Seric QueuePid = dofork(); 2324639Seric if (QueuePid == 0) 2334639Seric { 2344639Seric /* new child; run queue */ 2354836Seric runqueue(FALSE); 2364639Seric finis(); 2374639Seric } 2384639Seric } 2394639Seric } 2404639Seric 2414639Seric /* 2424639Seric ** Arrange to get this signal again. 2434639Seric */ 2444639Seric 2454836Seric (void) alarm((unsigned) QueueIntvl); 2464634Seric } 2474634Seric /* 2484632Seric ** ORDERQ -- order the work queue. 2494632Seric ** 2504632Seric ** Parameters: 2514632Seric ** none. 2524632Seric ** 2534632Seric ** Returns: 2544632Seric ** none. 2554632Seric ** 2564632Seric ** Side Effects: 2574632Seric ** Sets WorkQ to the queue of available work, in order. 2584632Seric */ 2594632Seric 2604632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2614632Seric 2624632Seric orderq() 2634632Seric { 2644632Seric struct direct d; 2654632Seric register WORK *w; 2664632Seric register WORK **wp; /* parent of w */ 2674632Seric register FILE *f; 2684632Seric register int i; 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 542*5902Seric case 'M': /* define macro */ 543*5902Seric define(buf[1], newstr(&buf[2])); 544*5902Seric break; 545*5902Seric 5464632Seric default: 5474632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5484632Seric break; 5494632Seric } 5504632Seric } 5514632Seric } 5524632Seric /* 5534632Seric ** TIMEOUT -- process timeout on queue file. 5544632Seric ** 5554632Seric ** Parameters: 5564632Seric ** w -- pointer to work request that timed out. 5574632Seric ** 5584632Seric ** Returns: 5594632Seric ** none. 5604632Seric ** 5614632Seric ** Side Effects: 5624632Seric ** Returns a message to the sender saying that this 5634632Seric ** message has timed out. 5644632Seric */ 5654632Seric 5664632Seric timeout(w) 5674632Seric register WORK *w; 5684632Seric { 5694634Seric # ifdef DEBUG 5704634Seric if (Debug > 0) 5714634Seric printf("timeout(%s)\n", w->w_name); 5724634Seric # endif DEBUG 5734634Seric 5744634Seric /* return message to sender */ 5754634Seric (void) returntosender("Cannot send mail for three days"); 5764634Seric 5774634Seric /* arrange to remove files from queue */ 5784634Seric QueueUp = FALSE; 5794632Seric } 5805182Seric 5815182Seric # endif QUEUE 582