14632Seric # include "sendmail.h" 24632Seric # include <sys/stat.h> 34632Seric # include <sys/dir.h> 44634Seric # include <signal.h> 54632Seric # include <errno.h> 64632Seric 7*5037Seric static char SccsId[] = "@(#)queue.c 3.6 11/22/81"; 84632Seric 94632Seric /* 104632Seric ** QUEUEUP -- queue a message up for future transmission. 114632Seric ** 124632Seric ** The queued message should already be in the correct place. 134632Seric ** This routine just outputs the control file as appropriate. 144632Seric ** 154632Seric ** Parameters: 164632Seric ** df -- location of the data file. The name will 174632Seric ** be transformed into a control file name. 184632Seric ** 194632Seric ** Returns: 204632Seric ** none. 214632Seric ** 224632Seric ** Side Effects: 234632Seric ** The current request (only unsatisfied addresses) 244632Seric ** are saved in a control file. 254632Seric */ 264632Seric 274632Seric queueup(df) 284632Seric char *df; 294632Seric { 304632Seric char cf[MAXNAME]; 314632Seric register FILE *f; 324632Seric register int i; 334632Seric register HDR *h; 344632Seric register char *p; 355007Seric register ADDRESS *q; 364632Seric 37*5037Seric /* 38*5037Seric ** Create control file. 39*5037Seric */ 404632Seric 41*5037Seric strcpy(cf, QueueDir); 42*5037Seric strcat(cf, "/cfXXXXXX"); 43*5037Seric (void) mktemp(cf); 444632Seric f = fopen(cf, "w"); 454632Seric if (f == NULL) 464632Seric { 474632Seric syserr("queueup: cannot create control file %s", cf); 484632Seric return; 494632Seric } 504632Seric 514632Seric # ifdef DEBUG 524632Seric if (Debug) 534632Seric printf("queued in %s\n", cf); 544632Seric # endif DEBUG 554632Seric 564632Seric /* 574632Seric ** Output future work requests. 584632Seric */ 594632Seric 604632Seric /* output name of data file */ 614632Seric fprintf(f, "D%s\n", df); 624632Seric 634632Seric /* output name of sender */ 644632Seric fprintf(f, "S%s\n", From.q_paddr); 654632Seric 664632Seric /* output timeout */ 674632Seric fprintf(f, "T%ld\n", TimeOut); 684632Seric 694634Seric /* output message priority */ 70*5037Seric fprintf(f, "P%ld\n", MsgPriority); 714634Seric 724632Seric /* output list of recipient addresses */ 735007Seric for (q = SendQueue; q != NULL; q = q->q_next) 744632Seric { 75*5037Seric # ifdef DEBUG 76*5037Seric if (Debug > 0) 77*5037Seric { 78*5037Seric printf("queueing "); 79*5037Seric printaddr(q, FALSE); 80*5037Seric } 81*5037Seric # endif DEBUG 82*5037Seric if (bitset(QQUEUEUP, q->q_flags)) 83*5037Seric fprintf(f, "R%s\n", q->q_paddr); 844632Seric } 854632Seric 864632Seric /* output headers for this message */ 874632Seric for (h = Header; h != NULL; h = h->h_link) 884632Seric { 894632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 904632Seric continue; 914632Seric fprintf(f, "H"); 924632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 934632Seric mfdecode(h->h_mflags, f); 944632Seric fprintf(f, "%s: ", h->h_field); 954632Seric if (bitset(H_DEFAULT, h->h_flags)) 964632Seric { 974632Seric char buf[MAXLINE]; 984632Seric 994632Seric (void) expand(h->h_value, buf, &buf[sizeof buf]); 1004632Seric fprintf(f, "%s\n", buf); 1014632Seric } 1024632Seric else 1034632Seric fprintf(f, "%s\n", h->h_value); 1044632Seric } 1054632Seric 1064632Seric /* 1074632Seric ** Clean up. 1084632Seric */ 1094632Seric 1104632Seric (void) fclose(f); 1114632Seric } 1124632Seric /* 1134632Seric ** RUNQUEUE -- run the jobs in the queue. 1144632Seric ** 1154632Seric ** Gets the stuff out of the queue in some presumably logical 1164632Seric ** order and processes them. 1174632Seric ** 1184632Seric ** Parameters: 1194632Seric ** none. 1204632Seric ** 1214632Seric ** Returns: 1224632Seric ** none. 1234632Seric ** 1244632Seric ** Side Effects: 1254632Seric ** runs things in the mail queue. 1264632Seric */ 1274632Seric 1284639Seric bool ReorderQueue; /* if set, reorder the send queue */ 1294639Seric int QueuePid; /* pid of child running queue */ 1304634Seric 1314639Seric runqueue(forkflag) 1324639Seric bool forkflag; 1334632Seric { 1344634Seric extern reordersig(); 1354632Seric 1364639Seric if (QueueIntvl != 0) 1374639Seric { 1384836Seric (void) signal(SIGALRM, reordersig); 1394836Seric (void) alarm((unsigned) QueueIntvl); 1404639Seric } 1414639Seric 1424639Seric if (forkflag) 1434639Seric { 1444639Seric QueuePid = dofork(); 1454639Seric if (QueuePid > 0) 1464639Seric { 1474639Seric /* parent */ 1484639Seric return; 1494639Seric } 1504639Seric else 1514836Seric (void) alarm((unsigned) 0); 1524639Seric } 1534639Seric 1544634Seric for (;;) 1554634Seric { 1564634Seric /* 1574634Seric ** Order the existing work requests. 1584634Seric */ 1594632Seric 1604634Seric orderq(); 1614632Seric 1624634Seric if (WorkQ == NULL) 1634634Seric { 1644634Seric /* no work? well, maybe later */ 1654634Seric if (QueueIntvl == 0) 1664634Seric break; 1674639Seric pause(); 1684634Seric continue; 1694634Seric } 1704632Seric 1714634Seric ReorderQueue = FALSE; 1724634Seric 1734634Seric /* 1744634Seric ** Process them once at a time. 1754634Seric ** The queue could be reordered while we do this to take 1764634Seric ** new requests into account. If so, the existing job 1774634Seric ** will be finished but the next thing taken off WorkQ 1784634Seric ** may be something else. 1794634Seric */ 1804634Seric 1814634Seric while (WorkQ != NULL) 1824634Seric { 1834634Seric WORK *w = WorkQ; 1844634Seric 1854634Seric WorkQ = WorkQ->w_next; 1864634Seric dowork(w); 1874634Seric free(w->w_name); 1884634Seric free((char *) w); 1894634Seric if (ReorderQueue) 1904634Seric break; 1914634Seric } 1924634Seric 1934634Seric if (QueueIntvl == 0) 1944634Seric break; 1954632Seric } 1964632Seric } 1974632Seric /* 1984634Seric ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 1994634Seric ** 2004634Seric ** Parameters: 2014634Seric ** none. 2024634Seric ** 2034634Seric ** Returns: 2044634Seric ** none. 2054634Seric ** 2064634Seric ** Side Effects: 2074634Seric ** sets the "reorder work queue" flag. 2084634Seric */ 2094634Seric 2104634Seric reordersig() 2114634Seric { 2124639Seric if (QueuePid == 0) 2134639Seric { 2144639Seric /* we are in a child doing queueing */ 2154639Seric ReorderQueue = TRUE; 2164639Seric } 2174639Seric else 2184639Seric { 2194639Seric /* we are in a parent -- poke child or start new one */ 2204639Seric if (kill(QueuePid, SIGALRM) < 0) 2214639Seric { 2224639Seric /* no child -- get zombie & start new one */ 2234639Seric static int st; 2244639Seric 2254836Seric (void) wait(&st); 2264639Seric QueuePid = dofork(); 2274639Seric if (QueuePid == 0) 2284639Seric { 2294639Seric /* new child; run queue */ 2304836Seric runqueue(FALSE); 2314639Seric finis(); 2324639Seric } 2334639Seric } 2344639Seric } 2354639Seric 2364639Seric /* 2374639Seric ** Arrange to get this signal again. 2384639Seric */ 2394639Seric 2404836Seric (void) alarm((unsigned) QueueIntvl); 2414634Seric } 2424634Seric /* 2434632Seric ** ORDERQ -- order the work queue. 2444632Seric ** 2454632Seric ** Parameters: 2464632Seric ** none. 2474632Seric ** 2484632Seric ** Returns: 2494632Seric ** none. 2504632Seric ** 2514632Seric ** Side Effects: 2524632Seric ** Sets WorkQ to the queue of available work, in order. 2534632Seric */ 2544632Seric 2554632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2564632Seric 2574632Seric orderq() 2584632Seric { 2594632Seric struct direct d; 2604632Seric register WORK *w; 2614632Seric register WORK **wp; /* parent of w */ 2624632Seric register FILE *f; 2634632Seric register int i; 2644632Seric struct stat st; 2654632Seric WORK wlist[WLSIZE]; 2664632Seric int wn = 0; 2674632Seric extern workcmpf(); 2684632Seric extern char *QueueDir; 2694632Seric 2704632Seric /* clear out old WorkQ */ 2714632Seric for (w = WorkQ; w != NULL; ) 2724632Seric { 2734632Seric register WORK *nw = w->w_next; 2744632Seric 2754632Seric WorkQ = nw; 2764632Seric free(w->w_name); 2774632Seric free((char *) w); 2784632Seric w = nw; 2794632Seric } 2804632Seric 2814632Seric /* open the queue directory */ 2824632Seric f = fopen(QueueDir, "r"); 2834632Seric if (f == NULL) 2844632Seric { 2854632Seric syserr("orderq: cannot open %s", QueueDir); 2864632Seric return; 2874632Seric } 2884632Seric 2894632Seric /* 2904632Seric ** Read the work directory. 2914632Seric */ 2924632Seric 2934632Seric while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1) 2944632Seric { 2954632Seric char cbuf[MAXNAME]; 2964632Seric char lbuf[MAXNAME]; 2974632Seric FILE *cf; 2984632Seric register char *p; 2994632Seric 3004632Seric /* is this an interesting entry? */ 3014632Seric if (d.d_ino == 0 || d.d_name[0] != 'c') 3024632Seric continue; 3034632Seric 3044632Seric /* yes -- find the control file location */ 3054632Seric strcpy(cbuf, QueueDir); 3064632Seric strcat(cbuf, "/"); 3074632Seric p = &cbuf[strlen(cbuf)]; 3084632Seric strncpy(p, d.d_name, DIRSIZ); 3094632Seric p[DIRSIZ] = '\0'; 3104632Seric 3114632Seric /* open control file */ 3124632Seric cf = fopen(cbuf, "r"); 3134632Seric if (cf == NULL) 3144632Seric { 3154632Seric syserr("orderq: cannot open %s", cbuf); 3164632Seric continue; 3174632Seric } 318*5037Seric wlist[wn].w_name = newstr(cbuf); 3194632Seric 3204632Seric /* extract useful information */ 3214632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3224632Seric { 3234632Seric fixcrlf(lbuf, TRUE); 3244632Seric 3254632Seric switch (lbuf[0]) 3264632Seric { 3274632Seric case 'P': /* message priority */ 328*5037Seric (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 3294632Seric break; 3304632Seric } 3314632Seric } 3324632Seric wn++; 3334632Seric (void) fclose(cf); 3344632Seric } 3354632Seric (void) fclose(f); 3364632Seric 3374632Seric /* 3384632Seric ** Sort the work directory. 3394632Seric */ 3404632Seric 3414632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3424632Seric 3434632Seric /* 3444632Seric ** Convert the work list into canonical form. 3454632Seric */ 3464632Seric 3474632Seric wp = &WorkQ; 3484632Seric for (i = 0; i < wn; i++) 3494632Seric { 3504632Seric w = (WORK *) xalloc(sizeof *w); 3514632Seric w->w_name = wlist[i].w_name; 3524632Seric w->w_pri = wlist[i].w_pri; 3534632Seric w->w_next = NULL; 3544632Seric *wp = w; 3554632Seric wp = &w->w_next; 3564632Seric } 3574632Seric 3584632Seric # ifdef DEBUG 3594632Seric if (Debug) 3604632Seric { 3614632Seric for (w = WorkQ; w != NULL; w = w->w_next) 362*5037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 3634632Seric } 3644632Seric # endif DEBUG 3654632Seric } 3664632Seric /* 3674632Seric ** WORKCMPF -- compare function for ordering work. 3684632Seric ** 3694632Seric ** Parameters: 3704632Seric ** a -- the first argument. 3714632Seric ** b -- the second argument. 3724632Seric ** 3734632Seric ** Returns: 3744632Seric ** -1 if a < b 3754632Seric ** 0 if a == b 3764632Seric ** 1 if a > b 3774632Seric ** 3784632Seric ** Side Effects: 3794632Seric ** none. 3804632Seric */ 3814632Seric 3824632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3834632Seric 3844632Seric workcmpf(a, b) 385*5037Seric register WORK *a; 386*5037Seric register WORK *b; 3874632Seric { 388*5037Seric if (a->w_pri == b->w_pri) 3894632Seric return (0); 390*5037Seric else if (a->w_pri > b->w_pri) 3914632Seric return (1); 3924632Seric else 3934632Seric return (-1); 3944632Seric } 3954632Seric /* 3964632Seric ** DOWORK -- do a work request. 3974632Seric ** 3984632Seric ** Parameters: 3994632Seric ** w -- the work request to be satisfied. 4004632Seric ** 4014632Seric ** Returns: 4024632Seric ** none. 4034632Seric ** 4044632Seric ** Side Effects: 4054632Seric ** The work request is satisfied if possible. 4064632Seric */ 4074632Seric 4084632Seric dowork(w) 4094632Seric register WORK *w; 4104632Seric { 4114632Seric register int i; 4124632Seric auto int xstat; 4134632Seric 4144632Seric # ifdef DEBUG 4154632Seric if (Debug) 416*5037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 4174632Seric # endif DEBUG 4184632Seric 4194632Seric /* 4204632Seric ** Fork for work. 4214632Seric */ 4224632Seric 4234632Seric i = fork(); 4244632Seric if (i < 0) 4254632Seric { 4264632Seric syserr("dowork: cannot fork"); 4274632Seric return; 4284632Seric } 4294632Seric 4304632Seric if (i == 0) 4314632Seric { 4324632Seric /* 4334632Seric ** CHILD 4344632Seric */ 4354632Seric 4364632Seric QueueRun = TRUE; 4374634Seric openxscrpt(); 4384632Seric initsys(); 4394632Seric readqf(w->w_name); 4404632Seric sendall(FALSE); 4414634Seric # ifdef DEBUG 4424634Seric if (Debug > 2) 4434634Seric printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 4444634Seric # endif DEBUG 4454634Seric if (QueueUp && CurTime > TimeOut) 4464634Seric timeout(w); 447*5037Seric (void) unlink(w->w_name); 4484632Seric finis(); 4494632Seric } 4504632Seric 4514632Seric /* 4524632Seric ** Parent -- pick up results. 4534632Seric */ 4544632Seric 4554632Seric errno = 0; 4564632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 4574632Seric { 4584632Seric if (errno == EINTR) 4594632Seric { 4604632Seric errno = 0; 4614632Seric } 4624632Seric } 4634632Seric } 4644632Seric /* 4654632Seric ** READQF -- read queue file and set up environment. 4664632Seric ** 4674632Seric ** Parameters: 4684632Seric ** cf -- name of queue control file. 4694632Seric ** 4704632Seric ** Returns: 4714632Seric ** none. 4724632Seric ** 4734632Seric ** Side Effects: 4744632Seric ** cf is read and created as the current job, as though 4754632Seric ** we had been invoked by argument. 4764632Seric */ 4774632Seric 4784632Seric readqf(cf) 4794632Seric char *cf; 4804632Seric { 4814632Seric register FILE *f; 4824632Seric char buf[MAXLINE]; 4834632Seric 4844632Seric /* 4854632Seric ** Open the file created by queueup. 4864632Seric */ 4874632Seric 4884632Seric f = fopen(cf, "r"); 4894632Seric if (f == NULL) 4904632Seric { 4914632Seric syserr("readqf: no cf file %s", cf); 4924632Seric return; 4934632Seric } 4944632Seric 4954632Seric /* 4964632Seric ** Read and process the file. 4974632Seric */ 4984632Seric 499*5037Seric if (Verbose) 500*5037Seric message(Arpa_Info, "Running %s (from %s)", cf, From.q_paddr); 501*5037Seric 5024632Seric while (fgets(buf, sizeof buf, f) != NULL) 5034632Seric { 5044632Seric fixcrlf(buf, TRUE); 5054632Seric 5064632Seric switch (buf[0]) 5074632Seric { 5084632Seric case 'R': /* specify recipient */ 5095007Seric sendto(&buf[1], 1, (ADDRESS *) NULL, &SendQueue); 5104632Seric break; 5114632Seric 5124632Seric case 'H': /* header */ 5134632Seric (void) chompheader(&buf[1], FALSE); 5144632Seric break; 5154632Seric 5164632Seric case 'S': /* sender */ 5174634Seric setsender(newstr(&buf[1])); 5184632Seric break; 5194632Seric 5204632Seric case 'D': /* data file name */ 5214632Seric InFileName = newstr(&buf[1]); 5224632Seric TempFile = fopen(InFileName, "r"); 5234632Seric if (TempFile == NULL) 5244632Seric syserr("readqf: cannot open %s", InFileName); 5254632Seric break; 5264632Seric 5274632Seric case 'T': /* timeout */ 5284632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 5294632Seric break; 5304632Seric 5314634Seric case 'P': /* message priority */ 532*5037Seric (void) sscanf(&buf[1], "%ld", &MsgPriority); 533*5037Seric 534*5037Seric /* make sure that big things get sent eventually */ 535*5037Seric MsgPriority -= WKTIMEFACT; 5364634Seric break; 5374634Seric 5384632Seric default: 5394632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5404632Seric break; 5414632Seric } 5424632Seric } 5434632Seric } 5444632Seric /* 5454632Seric ** TIMEOUT -- process timeout on queue file. 5464632Seric ** 5474632Seric ** Parameters: 5484632Seric ** w -- pointer to work request that timed out. 5494632Seric ** 5504632Seric ** Returns: 5514632Seric ** none. 5524632Seric ** 5534632Seric ** Side Effects: 5544632Seric ** Returns a message to the sender saying that this 5554632Seric ** message has timed out. 5564632Seric */ 5574632Seric 5584632Seric timeout(w) 5594632Seric register WORK *w; 5604632Seric { 5614634Seric # ifdef DEBUG 5624634Seric if (Debug > 0) 5634634Seric printf("timeout(%s)\n", w->w_name); 5644634Seric # endif DEBUG 5654634Seric 5664634Seric /* return message to sender */ 5674634Seric (void) returntosender("Cannot send mail for three days"); 5684634Seric 5694634Seric /* arrange to remove files from queue */ 5704634Seric QueueUp = FALSE; 5714632Seric } 572