14632Seric # include "sendmail.h" 24632Seric # include <sys/stat.h> 34632Seric # include <sys/dir.h> 44634Seric # include <signal.h> 54632Seric # include <errno.h> 64632Seric 7*4836Seric static char SccsId[] = "@(#)queue.c 3.4 11/08/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; 354632Seric 364632Seric /* create control file name from data file name */ 374632Seric strcpy(cf, df); 384632Seric p = rindex(cf, '/'); 394632Seric if (p == NULL || *++p != 'd') 404632Seric { 414632Seric syserr("queueup: bad df name %s", df); 424632Seric return; 434632Seric } 444632Seric *p = 'c'; 454632Seric 464632Seric /* create control file */ 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 */ 734634Seric fprintf(f, "P%d\n", MsgPriority); 744634Seric 754632Seric /* output list of recipient addresses */ 764632Seric for (i = 0; Mailer[i] != NULL; i++) 774632Seric { 784632Seric register ADDRESS *q; 794632Seric 804632Seric for (q = Mailer[i]->m_sendq; q != NULL; q = q->q_next) 814632Seric { 824632Seric if (!bitset(QQUEUEUP, q->q_flags)) 834632Seric continue; 844632Seric fprintf(f, "R%s\n", q->q_paddr); 854632Seric } 864632Seric } 874632Seric 884632Seric /* output headers for this message */ 894632Seric for (h = Header; h != NULL; h = h->h_link) 904632Seric { 914632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 924632Seric continue; 934632Seric fprintf(f, "H"); 944632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 954632Seric mfdecode(h->h_mflags, f); 964632Seric fprintf(f, "%s: ", h->h_field); 974632Seric if (bitset(H_DEFAULT, h->h_flags)) 984632Seric { 994632Seric char buf[MAXLINE]; 1004632Seric 1014632Seric (void) expand(h->h_value, buf, &buf[sizeof buf]); 1024632Seric fprintf(f, "%s\n", buf); 1034632Seric } 1044632Seric else 1054632Seric fprintf(f, "%s\n", h->h_value); 1064632Seric } 1074632Seric 1084632Seric /* 1094632Seric ** Clean up. 1104632Seric */ 1114632Seric 1124632Seric (void) fclose(f); 1134632Seric } 1144632Seric /* 1154632Seric ** RUNQUEUE -- run the jobs in the queue. 1164632Seric ** 1174632Seric ** Gets the stuff out of the queue in some presumably logical 1184632Seric ** order and processes them. 1194632Seric ** 1204632Seric ** Parameters: 1214632Seric ** none. 1224632Seric ** 1234632Seric ** Returns: 1244632Seric ** none. 1254632Seric ** 1264632Seric ** Side Effects: 1274632Seric ** runs things in the mail queue. 1284632Seric */ 1294632Seric 1304639Seric bool ReorderQueue; /* if set, reorder the send queue */ 1314639Seric int QueuePid; /* pid of child running queue */ 1324634Seric 1334639Seric runqueue(forkflag) 1344639Seric bool forkflag; 1354632Seric { 1364634Seric extern reordersig(); 1374632Seric 1384639Seric if (QueueIntvl != 0) 1394639Seric { 140*4836Seric (void) signal(SIGALRM, reordersig); 141*4836Seric (void) alarm((unsigned) QueueIntvl); 1424639Seric } 1434639Seric 1444639Seric if (forkflag) 1454639Seric { 1464639Seric QueuePid = dofork(); 1474639Seric if (QueuePid > 0) 1484639Seric { 1494639Seric /* parent */ 1504639Seric return; 1514639Seric } 1524639Seric else 153*4836Seric (void) alarm((unsigned) 0); 1544639Seric } 1554639Seric 1564634Seric for (;;) 1574634Seric { 1584634Seric /* 1594634Seric ** Order the existing work requests. 1604634Seric */ 1614632Seric 1624634Seric orderq(); 1634632Seric 1644634Seric if (WorkQ == NULL) 1654634Seric { 1664634Seric /* no work? well, maybe later */ 1674634Seric if (QueueIntvl == 0) 1684634Seric break; 1694639Seric pause(); 1704634Seric continue; 1714634Seric } 1724632Seric 1734634Seric ReorderQueue = FALSE; 1744634Seric 1754634Seric /* 1764634Seric ** Process them once at a time. 1774634Seric ** The queue could be reordered while we do this to take 1784634Seric ** new requests into account. If so, the existing job 1794634Seric ** will be finished but the next thing taken off WorkQ 1804634Seric ** may be something else. 1814634Seric */ 1824634Seric 1834634Seric while (WorkQ != NULL) 1844634Seric { 1854634Seric WORK *w = WorkQ; 1864634Seric 1874634Seric WorkQ = WorkQ->w_next; 1884634Seric dowork(w); 1894634Seric free(w->w_name); 1904634Seric free((char *) w); 1914634Seric if (ReorderQueue) 1924634Seric break; 1934634Seric } 1944634Seric 1954634Seric if (QueueIntvl == 0) 1964634Seric break; 1974632Seric } 1984632Seric } 1994632Seric /* 2004634Seric ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 2014634Seric ** 2024634Seric ** Parameters: 2034634Seric ** none. 2044634Seric ** 2054634Seric ** Returns: 2064634Seric ** none. 2074634Seric ** 2084634Seric ** Side Effects: 2094634Seric ** sets the "reorder work queue" flag. 2104634Seric */ 2114634Seric 2124634Seric reordersig() 2134634Seric { 2144639Seric if (QueuePid == 0) 2154639Seric { 2164639Seric /* we are in a child doing queueing */ 2174639Seric ReorderQueue = TRUE; 2184639Seric } 2194639Seric else 2204639Seric { 2214639Seric /* we are in a parent -- poke child or start new one */ 2224639Seric if (kill(QueuePid, SIGALRM) < 0) 2234639Seric { 2244639Seric /* no child -- get zombie & start new one */ 2254639Seric static int st; 2264639Seric 227*4836Seric (void) wait(&st); 2284639Seric QueuePid = dofork(); 2294639Seric if (QueuePid == 0) 2304639Seric { 2314639Seric /* new child; run queue */ 232*4836Seric runqueue(FALSE); 2334639Seric finis(); 2344639Seric } 2354639Seric } 2364639Seric } 2374639Seric 2384639Seric /* 2394639Seric ** Arrange to get this signal again. 2404639Seric */ 2414639Seric 242*4836Seric (void) alarm((unsigned) QueueIntvl); 2434634Seric } 2444634Seric /* 2454632Seric ** ORDERQ -- order the work queue. 2464632Seric ** 2474632Seric ** Parameters: 2484632Seric ** none. 2494632Seric ** 2504632Seric ** Returns: 2514632Seric ** none. 2524632Seric ** 2534632Seric ** Side Effects: 2544632Seric ** Sets WorkQ to the queue of available work, in order. 2554632Seric */ 2564632Seric 2574632Seric # define WLSIZE 120 /* max size of worklist per sort */ 2584632Seric 2594632Seric orderq() 2604632Seric { 2614632Seric struct direct d; 2624632Seric register WORK *w; 2634632Seric register WORK **wp; /* parent of w */ 2644632Seric register FILE *f; 2654632Seric register int i; 2664632Seric struct stat st; 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 } 3204632Seric 3214632Seric /* extract useful information */ 3224632Seric wlist[wn].w_pri = PRI_NORMAL; 3234632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 3244632Seric { 3254632Seric fixcrlf(lbuf, TRUE); 3264632Seric 3274632Seric switch (lbuf[0]) 3284632Seric { 3294632Seric case 'D': /* data file name */ 3304632Seric if (stat(&lbuf[1], &st) < 0) 3314632Seric { 3324632Seric syserr("orderq: cannot stat %s", &lbuf[1]); 3334632Seric (void) fclose(cf); 3344632Seric (void) unlink(cbuf); 3354632Seric wn--; 3364632Seric continue; 3374632Seric } 3384632Seric wlist[wn].w_name = newstr(cbuf); 3394632Seric wlist[wn].w_size = st.st_size; 3404632Seric break; 3414632Seric 3424632Seric case 'P': /* message priority */ 3434632Seric wlist[wn].w_pri = atoi(&lbuf[1]); 3444632Seric break; 3454632Seric } 3464632Seric } 3474632Seric wn++; 3484632Seric (void) fclose(cf); 3494632Seric } 3504632Seric (void) fclose(f); 3514632Seric 3524632Seric /* 3534632Seric ** Sort the work directory. 3544632Seric */ 3554632Seric 3564632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 3574632Seric 3584632Seric /* 3594632Seric ** Convert the work list into canonical form. 3604632Seric */ 3614632Seric 3624632Seric wp = &WorkQ; 3634632Seric for (i = 0; i < wn; i++) 3644632Seric { 3654632Seric w = (WORK *) xalloc(sizeof *w); 3664632Seric w->w_name = wlist[i].w_name; 3674632Seric w->w_size = wlist[i].w_size; 3684632Seric w->w_pri = wlist[i].w_pri; 3694632Seric w->w_next = NULL; 3704632Seric *wp = w; 3714632Seric wp = &w->w_next; 3724632Seric } 3734632Seric 3744632Seric # ifdef DEBUG 3754632Seric if (Debug) 3764632Seric { 3774632Seric for (w = WorkQ; w != NULL; w = w->w_next) 3784634Seric printf("%32s: pri=%-2d sz=%ld\n", w->w_name, w->w_pri, 3794634Seric w->w_size); 3804632Seric } 3814632Seric # endif DEBUG 3824632Seric } 3834632Seric /* 3844632Seric ** WORKCMPF -- compare function for ordering work. 3854632Seric ** 3864632Seric ** Parameters: 3874632Seric ** a -- the first argument. 3884632Seric ** b -- the second argument. 3894632Seric ** 3904632Seric ** Returns: 3914632Seric ** -1 if a < b 3924632Seric ** 0 if a == b 3934632Seric ** 1 if a > b 3944632Seric ** 3954632Seric ** Side Effects: 3964632Seric ** none. 3974632Seric */ 3984632Seric 3994632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 4004632Seric 4014632Seric workcmpf(a, b) 4024632Seric WORK *a; 4034632Seric WORK *b; 4044632Seric { 4054632Seric register long aval; 4064632Seric register long bval; 4074632Seric 4084632Seric aval = a->w_size - PRIFACT * a->w_pri; 4094632Seric bval = b->w_size - PRIFACT * b->w_pri; 4104632Seric 4114632Seric if (aval == bval) 4124632Seric return (0); 4134632Seric else if (aval > bval) 4144632Seric return (1); 4154632Seric else 4164632Seric return (-1); 4174632Seric } 4184632Seric /* 4194632Seric ** DOWORK -- do a work request. 4204632Seric ** 4214632Seric ** Parameters: 4224632Seric ** w -- the work request to be satisfied. 4234632Seric ** 4244632Seric ** Returns: 4254632Seric ** none. 4264632Seric ** 4274632Seric ** Side Effects: 4284632Seric ** The work request is satisfied if possible. 4294632Seric */ 4304632Seric 4314632Seric dowork(w) 4324632Seric register WORK *w; 4334632Seric { 4344632Seric register int i; 4354632Seric auto int xstat; 4364632Seric 4374632Seric # ifdef DEBUG 4384632Seric if (Debug) 4394632Seric printf("dowork: %s size %ld pri %d\n", w->w_name, 4404632Seric w->w_size, w->w_pri); 4414632Seric # endif DEBUG 4424632Seric 4434632Seric /* 4444632Seric ** Fork for work. 4454632Seric */ 4464632Seric 4474632Seric i = fork(); 4484632Seric if (i < 0) 4494632Seric { 4504632Seric syserr("dowork: cannot fork"); 4514632Seric return; 4524632Seric } 4534632Seric 4544632Seric if (i == 0) 4554632Seric { 4564632Seric /* 4574632Seric ** CHILD 4584632Seric */ 4594632Seric 4604632Seric QueueRun = TRUE; 4614634Seric openxscrpt(); 4624632Seric initsys(); 4634632Seric readqf(w->w_name); 4644632Seric sendall(FALSE); 4654634Seric # ifdef DEBUG 4664634Seric if (Debug > 2) 4674634Seric printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 4684634Seric # endif DEBUG 4694634Seric if (QueueUp && CurTime > TimeOut) 4704634Seric timeout(w); 4714632Seric if (!QueueUp) 4724632Seric (void) unlink(w->w_name); 4734632Seric finis(); 4744632Seric } 4754632Seric 4764632Seric /* 4774632Seric ** Parent -- pick up results. 4784632Seric */ 4794632Seric 4804632Seric errno = 0; 4814632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 4824632Seric { 4834632Seric if (errno == EINTR) 4844632Seric { 4854632Seric errno = 0; 4864632Seric } 4874632Seric } 4884632Seric } 4894632Seric /* 4904632Seric ** READQF -- read queue file and set up environment. 4914632Seric ** 4924632Seric ** Parameters: 4934632Seric ** cf -- name of queue control file. 4944632Seric ** 4954632Seric ** Returns: 4964632Seric ** none. 4974632Seric ** 4984632Seric ** Side Effects: 4994632Seric ** cf is read and created as the current job, as though 5004632Seric ** we had been invoked by argument. 5014632Seric */ 5024632Seric 5034632Seric readqf(cf) 5044632Seric char *cf; 5054632Seric { 5064632Seric register FILE *f; 5074632Seric char buf[MAXLINE]; 5084632Seric 5094632Seric /* 5104632Seric ** Open the file created by queueup. 5114632Seric */ 5124632Seric 5134632Seric f = fopen(cf, "r"); 5144632Seric if (f == NULL) 5154632Seric { 5164632Seric syserr("readqf: no cf file %s", cf); 5174632Seric return; 5184632Seric } 5194632Seric 5204632Seric /* 5214632Seric ** Read and process the file. 5224632Seric */ 5234632Seric 5244632Seric while (fgets(buf, sizeof buf, f) != NULL) 5254632Seric { 5264632Seric fixcrlf(buf, TRUE); 5274632Seric 5284632Seric switch (buf[0]) 5294632Seric { 5304632Seric case 'R': /* specify recipient */ 5314632Seric sendto(&buf[1], 1, (ADDRESS *) NULL); 5324632Seric break; 5334632Seric 5344632Seric case 'H': /* header */ 5354632Seric (void) chompheader(&buf[1], FALSE); 5364632Seric break; 5374632Seric 5384632Seric case 'S': /* sender */ 5394634Seric setsender(newstr(&buf[1])); 5404632Seric break; 5414632Seric 5424632Seric case 'D': /* data file name */ 5434632Seric InFileName = newstr(&buf[1]); 5444632Seric TempFile = fopen(InFileName, "r"); 5454632Seric if (TempFile == NULL) 5464632Seric syserr("readqf: cannot open %s", InFileName); 5474632Seric break; 5484632Seric 5494632Seric case 'T': /* timeout */ 5504632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 5514632Seric break; 5524632Seric 5534634Seric case 'P': /* message priority */ 5544634Seric MsgPriority = atoi(&buf[1]); 5554634Seric break; 5564634Seric 5574632Seric default: 5584632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 5594632Seric break; 5604632Seric } 5614632Seric } 5624632Seric } 5634632Seric /* 5644632Seric ** TIMEOUT -- process timeout on queue file. 5654632Seric ** 5664632Seric ** Parameters: 5674632Seric ** w -- pointer to work request that timed out. 5684632Seric ** 5694632Seric ** Returns: 5704632Seric ** none. 5714632Seric ** 5724632Seric ** Side Effects: 5734632Seric ** Returns a message to the sender saying that this 5744632Seric ** message has timed out. 5754632Seric */ 5764632Seric 5774632Seric timeout(w) 5784632Seric register WORK *w; 5794632Seric { 5804634Seric # ifdef DEBUG 5814634Seric if (Debug > 0) 5824634Seric printf("timeout(%s)\n", w->w_name); 5834634Seric # endif DEBUG 5844634Seric 5854634Seric /* return message to sender */ 5864634Seric (void) returntosender("Cannot send mail for three days"); 5874634Seric 5884634Seric /* arrange to remove files from queue */ 5894634Seric QueueUp = FALSE; 5904632Seric } 591