14632Seric # include "sendmail.h" 24632Seric # include <sys/stat.h> 34632Seric # include <sys/dir.h> 44634Seric # include <signal.h> 54632Seric # include <errno.h> 64632Seric 7*5007Seric static char SccsId[] = "@(#)queue.c 3.5 11/21/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; 35*5007Seric register ADDRESS *q; 364632Seric 374632Seric /* create control file name from data file name */ 384632Seric strcpy(cf, df); 394632Seric p = rindex(cf, '/'); 404632Seric if (p == NULL || *++p != 'd') 414632Seric { 424632Seric syserr("queueup: bad df name %s", df); 434632Seric return; 444632Seric } 454632Seric *p = 'c'; 464632Seric 474632Seric /* create control file */ 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 */ 744634Seric fprintf(f, "P%d\n", MsgPriority); 754634Seric 764632Seric /* output list of recipient addresses */ 77*5007Seric for (q = SendQueue; q != NULL; q = q->q_next) 784632Seric { 79*5007Seric if (!bitset(QQUEUEUP, q->q_flags)) 80*5007Seric continue; 81*5007Seric fprintf(f, "R%s\n", q->q_paddr); 824632Seric } 834632Seric 844632Seric /* output headers for this message */ 854632Seric for (h = Header; h != NULL; h = h->h_link) 864632Seric { 874632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 884632Seric continue; 894632Seric fprintf(f, "H"); 904632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 914632Seric mfdecode(h->h_mflags, f); 924632Seric fprintf(f, "%s: ", h->h_field); 934632Seric if (bitset(H_DEFAULT, h->h_flags)) 944632Seric { 954632Seric char buf[MAXLINE]; 964632Seric 974632Seric (void) expand(h->h_value, buf, &buf[sizeof buf]); 984632Seric fprintf(f, "%s\n", buf); 994632Seric } 1004632Seric else 1014632Seric fprintf(f, "%s\n", h->h_value); 1024632Seric } 1034632Seric 1044632Seric /* 1054632Seric ** Clean up. 1064632Seric */ 1074632Seric 1084632Seric (void) fclose(f); 1094632Seric } 1104632Seric /* 1114632Seric ** RUNQUEUE -- run the jobs in the queue. 1124632Seric ** 1134632Seric ** Gets the stuff out of the queue in some presumably logical 1144632Seric ** order and processes them. 1154632Seric ** 1164632Seric ** Parameters: 1174632Seric ** none. 1184632Seric ** 1194632Seric ** Returns: 1204632Seric ** none. 1214632Seric ** 1224632Seric ** Side Effects: 1234632Seric ** runs things in the mail queue. 1244632Seric */ 1254632Seric 1264639Seric bool ReorderQueue; /* if set, reorder the send queue */ 1274639Seric int QueuePid; /* pid of child running queue */ 1284634Seric 1294639Seric runqueue(forkflag) 1304639Seric bool forkflag; 1314632Seric { 1324634Seric extern reordersig(); 1334632Seric 1344639Seric if (QueueIntvl != 0) 1354639Seric { 1364836Seric (void) signal(SIGALRM, reordersig); 1374836Seric (void) alarm((unsigned) QueueIntvl); 1384639Seric } 1394639Seric 1404639Seric if (forkflag) 1414639Seric { 1424639Seric QueuePid = dofork(); 1434639Seric if (QueuePid > 0) 1444639Seric { 1454639Seric /* parent */ 1464639Seric return; 1474639Seric } 1484639Seric else 1494836Seric (void) alarm((unsigned) 0); 1504639Seric } 1514639Seric 1524634Seric for (;;) 1534634Seric { 1544634Seric /* 1554634Seric ** Order the existing work requests. 1564634Seric */ 1574632Seric 1584634Seric orderq(); 1594632Seric 1604634Seric if (WorkQ == NULL) 1614634Seric { 1624634Seric /* no work? well, maybe later */ 1634634Seric if (QueueIntvl == 0) 1644634Seric break; 1654639Seric pause(); 1664634Seric continue; 1674634Seric } 1684632Seric 1694634Seric ReorderQueue = FALSE; 1704634Seric 1714634Seric /* 1724634Seric ** Process them once at a time. 1734634Seric ** The queue could be reordered while we do this to take 1744634Seric ** new requests into account. If so, the existing job 1754634Seric ** will be finished but the next thing taken off WorkQ 1764634Seric ** may be something else. 1774634Seric */ 1784634Seric 1794634Seric while (WorkQ != NULL) 1804634Seric { 1814634Seric WORK *w = WorkQ; 1824634Seric 1834634Seric WorkQ = WorkQ->w_next; 1844634Seric dowork(w); 1854634Seric free(w->w_name); 1864634Seric free((char *) w); 1874634Seric if (ReorderQueue) 1884634Seric break; 1894634Seric } 1904634Seric 1914634Seric if (QueueIntvl == 0) 1924634Seric break; 1934632Seric } 1944632Seric } 1954632Seric /* 1964634Seric ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 1974634Seric ** 1984634Seric ** Parameters: 1994634Seric ** none. 2004634Seric ** 2014634Seric ** Returns: 2024634Seric ** none. 2034634Seric ** 2044634Seric ** Side Effects: 2054634Seric ** sets the "reorder work queue" flag. 2064634Seric */ 2074634Seric 2084634Seric reordersig() 2094634Seric { 2104639Seric if (QueuePid == 0) 2114639Seric { 2124639Seric /* we are in a child doing queueing */ 2134639Seric ReorderQueue = TRUE; 2144639Seric } 2154639Seric else 2164639Seric { 2174639Seric /* we are in a parent -- poke child or start new one */ 2184639Seric if (kill(QueuePid, SIGALRM) < 0) 2194639Seric { 2204639Seric /* no child -- get zombie & start new one */ 2214639Seric static int st; 2224639Seric 2234836Seric (void) wait(&st); 2244639Seric QueuePid = dofork(); 2254639Seric if (QueuePid == 0) 2264639Seric { 2274639Seric /* new child; run queue */ 2284836Seric runqueue(FALSE); 2294639Seric finis(); 2304639Seric } 2314639Seric } 2324639Seric } 2334639Seric 2344639Seric /* 2354639Seric ** Arrange to get this signal again. 2364639Seric */ 2374639Seric 2384836Seric (void) alarm((unsigned) QueueIntvl); 2394634Seric } 2404634Seric /* 2414632Seric ** ORDERQ -- order the work queue. 2424632Seric ** 2434632Seric ** Parameters: 2444632Seric ** none. 2454632Seric ** 2464632Seric ** Returns: 2474632Seric ** none. 2484632Seric ** 2494632Seric ** Side Effects: 2504632Seric ** Sets WorkQ to the queue of available work, in order. 2514632Seric */ 2524632Seric 2534632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2544632Seric 2554632Seric orderq() 2564632Seric { 2574632Seric struct direct d; 2584632Seric register WORK *w; 2594632Seric register WORK **wp; /* parent of w */ 2604632Seric register FILE *f; 2614632Seric register int i; 2624632Seric struct stat st; 2634632Seric WORK wlist[WLSIZE]; 2644632Seric int wn = 0; 2654632Seric extern workcmpf(); 2664632Seric extern char *QueueDir; 2674632Seric 2684632Seric /* clear out old WorkQ */ 2694632Seric for (w = WorkQ; w != NULL; ) 2704632Seric { 2714632Seric register WORK *nw = w->w_next; 2724632Seric 2734632Seric WorkQ = nw; 2744632Seric free(w->w_name); 2754632Seric free((char *) w); 2764632Seric w = nw; 2774632Seric } 2784632Seric 2794632Seric /* open the queue directory */ 2804632Seric f = fopen(QueueDir, "r"); 2814632Seric if (f == NULL) 2824632Seric { 2834632Seric syserr("orderq: cannot open %s", QueueDir); 2844632Seric return; 2854632Seric } 2864632Seric 2874632Seric /* 2884632Seric ** Read the work directory. 2894632Seric */ 2904632Seric 2914632Seric while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1) 2924632Seric { 2934632Seric char cbuf[MAXNAME]; 2944632Seric char lbuf[MAXNAME]; 2954632Seric FILE *cf; 2964632Seric register char *p; 2974632Seric 2984632Seric /* is this an interesting entry? */ 2994632Seric if (d.d_ino == 0 || d.d_name[0] != 'c') 3004632Seric continue; 3014632Seric 3024632Seric /* yes -- find the control file location */ 3034632Seric strcpy(cbuf, QueueDir); 3044632Seric strcat(cbuf, "/"); 3054632Seric p = &cbuf[strlen(cbuf)]; 3064632Seric strncpy(p, d.d_name, DIRSIZ); 3074632Seric p[DIRSIZ] = '\0'; 3084632Seric 3094632Seric /* open control file */ 3104632Seric cf = fopen(cbuf, "r"); 3114632Seric if (cf == NULL) 3124632Seric { 3134632Seric syserr("orderq: cannot open %s", cbuf); 3144632Seric continue; 3154632Seric } 3164632Seric 3174632Seric /* extract useful information */ 3184632Seric wlist[wn].w_pri = PRI_NORMAL; 3194632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3204632Seric { 3214632Seric fixcrlf(lbuf, TRUE); 3224632Seric 3234632Seric switch (lbuf[0]) 3244632Seric { 3254632Seric case 'D': /* data file name */ 3264632Seric if (stat(&lbuf[1], &st) < 0) 3274632Seric { 3284632Seric syserr("orderq: cannot stat %s", &lbuf[1]); 3294632Seric (void) fclose(cf); 3304632Seric (void) unlink(cbuf); 3314632Seric wn--; 3324632Seric continue; 3334632Seric } 3344632Seric wlist[wn].w_name = newstr(cbuf); 3354632Seric wlist[wn].w_size = st.st_size; 3364632Seric break; 3374632Seric 3384632Seric case 'P': /* message priority */ 3394632Seric wlist[wn].w_pri = atoi(&lbuf[1]); 3404632Seric break; 3414632Seric } 3424632Seric } 3434632Seric wn++; 3444632Seric (void) fclose(cf); 3454632Seric } 3464632Seric (void) fclose(f); 3474632Seric 3484632Seric /* 3494632Seric ** Sort the work directory. 3504632Seric */ 3514632Seric 3524632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3534632Seric 3544632Seric /* 3554632Seric ** Convert the work list into canonical form. 3564632Seric */ 3574632Seric 3584632Seric wp = &WorkQ; 3594632Seric for (i = 0; i < wn; i++) 3604632Seric { 3614632Seric w = (WORK *) xalloc(sizeof *w); 3624632Seric w->w_name = wlist[i].w_name; 3634632Seric w->w_size = wlist[i].w_size; 3644632Seric w->w_pri = wlist[i].w_pri; 3654632Seric w->w_next = NULL; 3664632Seric *wp = w; 3674632Seric wp = &w->w_next; 3684632Seric } 3694632Seric 3704632Seric # ifdef DEBUG 3714632Seric if (Debug) 3724632Seric { 3734632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3744634Seric printf("%32s: pri=%-2d sz=%ld\n", w->w_name, w->w_pri, 3754634Seric w->w_size); 3764632Seric } 3774632Seric # endif DEBUG 3784632Seric } 3794632Seric /* 3804632Seric ** WORKCMPF -- compare function for ordering work. 3814632Seric ** 3824632Seric ** Parameters: 3834632Seric ** a -- the first argument. 3844632Seric ** b -- the second argument. 3854632Seric ** 3864632Seric ** Returns: 3874632Seric ** -1 if a < b 3884632Seric ** 0 if a == b 3894632Seric ** 1 if a > b 3904632Seric ** 3914632Seric ** Side Effects: 3924632Seric ** none. 3934632Seric */ 3944632Seric 3954632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 3964632Seric 3974632Seric workcmpf(a, b) 3984632Seric WORK *a; 3994632Seric WORK *b; 4004632Seric { 4014632Seric register long aval; 4024632Seric register long bval; 4034632Seric 4044632Seric aval = a->w_size - PRIFACT * a->w_pri; 4054632Seric bval = b->w_size - PRIFACT * b->w_pri; 4064632Seric 4074632Seric if (aval == bval) 4084632Seric return (0); 4094632Seric else if (aval > bval) 4104632Seric return (1); 4114632Seric else 4124632Seric return (-1); 4134632Seric } 4144632Seric /* 4154632Seric ** DOWORK -- do a work request. 4164632Seric ** 4174632Seric ** Parameters: 4184632Seric ** w -- the work request to be satisfied. 4194632Seric ** 4204632Seric ** Returns: 4214632Seric ** none. 4224632Seric ** 4234632Seric ** Side Effects: 4244632Seric ** The work request is satisfied if possible. 4254632Seric */ 4264632Seric 4274632Seric dowork(w) 4284632Seric register WORK *w; 4294632Seric { 4304632Seric register int i; 4314632Seric auto int xstat; 4324632Seric 4334632Seric # ifdef DEBUG 4344632Seric if (Debug) 4354632Seric printf("dowork: %s size %ld pri %d\n", w->w_name, 4364632Seric w->w_size, w->w_pri); 4374632Seric # endif DEBUG 4384632Seric 4394632Seric /* 4404632Seric ** Fork for work. 4414632Seric */ 4424632Seric 4434632Seric i = fork(); 4444632Seric if (i < 0) 4454632Seric { 4464632Seric syserr("dowork: cannot fork"); 4474632Seric return; 4484632Seric } 4494632Seric 4504632Seric if (i == 0) 4514632Seric { 4524632Seric /* 4534632Seric ** CHILD 4544632Seric */ 4554632Seric 4564632Seric QueueRun = TRUE; 4574634Seric openxscrpt(); 4584632Seric initsys(); 4594632Seric readqf(w->w_name); 4604632Seric sendall(FALSE); 4614634Seric # ifdef DEBUG 4624634Seric if (Debug > 2) 4634634Seric printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 4644634Seric # endif DEBUG 4654634Seric if (QueueUp && CurTime > TimeOut) 4664634Seric timeout(w); 4674632Seric if (!QueueUp) 4684632Seric (void) unlink(w->w_name); 4694632Seric finis(); 4704632Seric } 4714632Seric 4724632Seric /* 4734632Seric ** Parent -- pick up results. 4744632Seric */ 4754632Seric 4764632Seric errno = 0; 4774632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 4784632Seric { 4794632Seric if (errno == EINTR) 4804632Seric { 4814632Seric errno = 0; 4824632Seric } 4834632Seric } 4844632Seric } 4854632Seric /* 4864632Seric ** READQF -- read queue file and set up environment. 4874632Seric ** 4884632Seric ** Parameters: 4894632Seric ** cf -- name of queue control file. 4904632Seric ** 4914632Seric ** Returns: 4924632Seric ** none. 4934632Seric ** 4944632Seric ** Side Effects: 4954632Seric ** cf is read and created as the current job, as though 4964632Seric ** we had been invoked by argument. 4974632Seric */ 4984632Seric 4994632Seric readqf(cf) 5004632Seric char *cf; 5014632Seric { 5024632Seric register FILE *f; 5034632Seric char buf[MAXLINE]; 5044632Seric 5054632Seric /* 5064632Seric ** Open the file created by queueup. 5074632Seric */ 5084632Seric 5094632Seric f = fopen(cf, "r"); 5104632Seric if (f == NULL) 5114632Seric { 5124632Seric syserr("readqf: no cf file %s", cf); 5134632Seric return; 5144632Seric } 5154632Seric 5164632Seric /* 5174632Seric ** Read and process the file. 5184632Seric */ 5194632Seric 5204632Seric while (fgets(buf, sizeof buf, f) != NULL) 5214632Seric { 5224632Seric fixcrlf(buf, TRUE); 5234632Seric 5244632Seric switch (buf[0]) 5254632Seric { 5264632Seric case 'R': /* specify recipient */ 527*5007Seric sendto(&buf[1], 1, (ADDRESS *) NULL, &SendQueue); 5284632Seric break; 5294632Seric 5304632Seric case 'H': /* header */ 5314632Seric (void) chompheader(&buf[1], FALSE); 5324632Seric break; 5334632Seric 5344632Seric case 'S': /* sender */ 5354634Seric setsender(newstr(&buf[1])); 5364632Seric break; 5374632Seric 5384632Seric case 'D': /* data file name */ 5394632Seric InFileName = newstr(&buf[1]); 5404632Seric TempFile = fopen(InFileName, "r"); 5414632Seric if (TempFile == NULL) 5424632Seric syserr("readqf: cannot open %s", InFileName); 5434632Seric break; 5444632Seric 5454632Seric case 'T': /* timeout */ 5464632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 5474632Seric break; 5484632Seric 5494634Seric case 'P': /* message priority */ 5504634Seric MsgPriority = atoi(&buf[1]); 5514634Seric break; 5524634Seric 5534632Seric default: 5544632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5554632Seric break; 5564632Seric } 5574632Seric } 5584632Seric } 5594632Seric /* 5604632Seric ** TIMEOUT -- process timeout on queue file. 5614632Seric ** 5624632Seric ** Parameters: 5634632Seric ** w -- pointer to work request that timed out. 5644632Seric ** 5654632Seric ** Returns: 5664632Seric ** none. 5674632Seric ** 5684632Seric ** Side Effects: 5694632Seric ** Returns a message to the sender saying that this 5704632Seric ** message has timed out. 5714632Seric */ 5724632Seric 5734632Seric timeout(w) 5744632Seric register WORK *w; 5754632Seric { 5764634Seric # ifdef DEBUG 5774634Seric if (Debug > 0) 5784634Seric printf("timeout(%s)\n", w->w_name); 5794634Seric # endif DEBUG 5804634Seric 5814634Seric /* return message to sender */ 5824634Seric (void) returntosender("Cannot send mail for three days"); 5834634Seric 5844634Seric /* arrange to remove files from queue */ 5854634Seric QueueUp = FALSE; 5864632Seric } 587