1*4632Seric # include "sendmail.h" 2*4632Seric # include <sys/stat.h> 3*4632Seric # include <sys/dir.h> 4*4632Seric # include <errno.h> 5*4632Seric 6*4632Seric static char SccsId[] = "@(#)queue.c 3.1 10/27/81"; 7*4632Seric 8*4632Seric /* 9*4632Seric ** QUEUEUP -- queue a message up for future transmission. 10*4632Seric ** 11*4632Seric ** The queued message should already be in the correct place. 12*4632Seric ** This routine just outputs the control file as appropriate. 13*4632Seric ** 14*4632Seric ** Parameters: 15*4632Seric ** df -- location of the data file. The name will 16*4632Seric ** be transformed into a control file name. 17*4632Seric ** 18*4632Seric ** Returns: 19*4632Seric ** none. 20*4632Seric ** 21*4632Seric ** Side Effects: 22*4632Seric ** The current request (only unsatisfied addresses) 23*4632Seric ** are saved in a control file. 24*4632Seric */ 25*4632Seric 26*4632Seric queueup(df) 27*4632Seric char *df; 28*4632Seric { 29*4632Seric char cf[MAXNAME]; 30*4632Seric register FILE *f; 31*4632Seric register int i; 32*4632Seric register HDR *h; 33*4632Seric register char *p; 34*4632Seric 35*4632Seric /* create control file name from data file name */ 36*4632Seric strcpy(cf, df); 37*4632Seric p = rindex(cf, '/'); 38*4632Seric if (p == NULL || *++p != 'd') 39*4632Seric { 40*4632Seric syserr("queueup: bad df name %s", df); 41*4632Seric return; 42*4632Seric } 43*4632Seric *p = 'c'; 44*4632Seric 45*4632Seric /* create control file */ 46*4632Seric f = fopen(cf, "w"); 47*4632Seric if (f == NULL) 48*4632Seric { 49*4632Seric syserr("queueup: cannot create control file %s", cf); 50*4632Seric return; 51*4632Seric } 52*4632Seric 53*4632Seric # ifdef DEBUG 54*4632Seric if (Debug) 55*4632Seric printf("queued in %s\n", cf); 56*4632Seric # endif DEBUG 57*4632Seric 58*4632Seric /* 59*4632Seric ** Output future work requests. 60*4632Seric */ 61*4632Seric 62*4632Seric /* output name of data file */ 63*4632Seric fprintf(f, "D%s\n", df); 64*4632Seric 65*4632Seric /* output name of sender */ 66*4632Seric fprintf(f, "S%s\n", From.q_paddr); 67*4632Seric 68*4632Seric /* output timeout */ 69*4632Seric fprintf(f, "T%ld\n", TimeOut); 70*4632Seric 71*4632Seric /* output list of recipient addresses */ 72*4632Seric for (i = 0; Mailer[i] != NULL; i++) 73*4632Seric { 74*4632Seric register ADDRESS *q; 75*4632Seric 76*4632Seric for (q = Mailer[i]->m_sendq; q != NULL; q = q->q_next) 77*4632Seric { 78*4632Seric if (!bitset(QQUEUEUP, q->q_flags)) 79*4632Seric continue; 80*4632Seric fprintf(f, "R%s\n", q->q_paddr); 81*4632Seric } 82*4632Seric } 83*4632Seric 84*4632Seric /* output headers for this message */ 85*4632Seric for (h = Header; h != NULL; h = h->h_link) 86*4632Seric { 87*4632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 88*4632Seric continue; 89*4632Seric fprintf(f, "H"); 90*4632Seric if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 91*4632Seric mfdecode(h->h_mflags, f); 92*4632Seric fprintf(f, "%s: ", h->h_field); 93*4632Seric if (bitset(H_DEFAULT, h->h_flags)) 94*4632Seric { 95*4632Seric char buf[MAXLINE]; 96*4632Seric 97*4632Seric (void) expand(h->h_value, buf, &buf[sizeof buf]); 98*4632Seric fprintf(f, "%s\n", buf); 99*4632Seric } 100*4632Seric else 101*4632Seric fprintf(f, "%s\n", h->h_value); 102*4632Seric } 103*4632Seric 104*4632Seric /* 105*4632Seric ** Clean up. 106*4632Seric */ 107*4632Seric 108*4632Seric (void) fclose(f); 109*4632Seric } 110*4632Seric /* 111*4632Seric ** RUNQUEUE -- run the jobs in the queue. 112*4632Seric ** 113*4632Seric ** Gets the stuff out of the queue in some presumably logical 114*4632Seric ** order and processes them. 115*4632Seric ** 116*4632Seric ** Parameters: 117*4632Seric ** none. 118*4632Seric ** 119*4632Seric ** Returns: 120*4632Seric ** none. 121*4632Seric ** 122*4632Seric ** Side Effects: 123*4632Seric ** runs things in the mail queue. 124*4632Seric */ 125*4632Seric 126*4632Seric runqueue() 127*4632Seric { 128*4632Seric /* 129*4632Seric ** Order the existing work requests. 130*4632Seric */ 131*4632Seric 132*4632Seric orderq(); 133*4632Seric 134*4632Seric /* 135*4632Seric ** Process them once at a time. 136*4632Seric ** The queue could be reordered while we do this to take 137*4632Seric ** new requests into account. If so, the existing job 138*4632Seric ** will be finished but the next thing taken off WorkQ 139*4632Seric ** may be something else. 140*4632Seric */ 141*4632Seric 142*4632Seric while (WorkQ != NULL) 143*4632Seric { 144*4632Seric WORK *w = WorkQ; 145*4632Seric 146*4632Seric WorkQ = WorkQ->w_next; 147*4632Seric dowork(w); 148*4632Seric free(w->w_name); 149*4632Seric free((char *) w); 150*4632Seric } 151*4632Seric } 152*4632Seric /* 153*4632Seric ** ORDERQ -- order the work queue. 154*4632Seric ** 155*4632Seric ** Parameters: 156*4632Seric ** none. 157*4632Seric ** 158*4632Seric ** Returns: 159*4632Seric ** none. 160*4632Seric ** 161*4632Seric ** Side Effects: 162*4632Seric ** Sets WorkQ to the queue of available work, in order. 163*4632Seric */ 164*4632Seric 165*4632Seric # define WLSIZE 120 /* max size of worklist per sort */ 166*4632Seric 167*4632Seric orderq() 168*4632Seric { 169*4632Seric struct direct d; 170*4632Seric register WORK *w; 171*4632Seric register WORK **wp; /* parent of w */ 172*4632Seric register FILE *f; 173*4632Seric register int i; 174*4632Seric struct stat st; 175*4632Seric WORK wlist[WLSIZE]; 176*4632Seric int wn = 0; 177*4632Seric extern workcmpf(); 178*4632Seric extern char *QueueDir; 179*4632Seric 180*4632Seric /* clear out old WorkQ */ 181*4632Seric for (w = WorkQ; w != NULL; ) 182*4632Seric { 183*4632Seric register WORK *nw = w->w_next; 184*4632Seric 185*4632Seric WorkQ = nw; 186*4632Seric free(w->w_name); 187*4632Seric free((char *) w); 188*4632Seric w = nw; 189*4632Seric } 190*4632Seric 191*4632Seric /* open the queue directory */ 192*4632Seric f = fopen(QueueDir, "r"); 193*4632Seric if (f == NULL) 194*4632Seric { 195*4632Seric syserr("orderq: cannot open %s", QueueDir); 196*4632Seric return; 197*4632Seric } 198*4632Seric 199*4632Seric /* 200*4632Seric ** Read the work directory. 201*4632Seric */ 202*4632Seric 203*4632Seric while (wn < WLSIZE && fread((char *) &d, sizeof d, 1, f) == 1) 204*4632Seric { 205*4632Seric char cbuf[MAXNAME]; 206*4632Seric char lbuf[MAXNAME]; 207*4632Seric FILE *cf; 208*4632Seric register char *p; 209*4632Seric 210*4632Seric /* is this an interesting entry? */ 211*4632Seric if (d.d_ino == 0 || d.d_name[0] != 'c') 212*4632Seric continue; 213*4632Seric 214*4632Seric /* yes -- find the control file location */ 215*4632Seric strcpy(cbuf, QueueDir); 216*4632Seric strcat(cbuf, "/"); 217*4632Seric p = &cbuf[strlen(cbuf)]; 218*4632Seric strncpy(p, d.d_name, DIRSIZ); 219*4632Seric p[DIRSIZ] = '\0'; 220*4632Seric 221*4632Seric /* open control file */ 222*4632Seric cf = fopen(cbuf, "r"); 223*4632Seric if (cf == NULL) 224*4632Seric { 225*4632Seric syserr("orderq: cannot open %s", cbuf); 226*4632Seric continue; 227*4632Seric } 228*4632Seric 229*4632Seric /* extract useful information */ 230*4632Seric wlist[wn].w_pri = PRI_NORMAL; 231*4632Seric while (fgets(lbuf, sizeof lbuf, cf) != NULL) 232*4632Seric { 233*4632Seric fixcrlf(lbuf, TRUE); 234*4632Seric 235*4632Seric switch (lbuf[0]) 236*4632Seric { 237*4632Seric case 'D': /* data file name */ 238*4632Seric if (stat(&lbuf[1], &st) < 0) 239*4632Seric { 240*4632Seric syserr("orderq: cannot stat %s", &lbuf[1]); 241*4632Seric (void) fclose(cf); 242*4632Seric (void) unlink(cbuf); 243*4632Seric wn--; 244*4632Seric continue; 245*4632Seric } 246*4632Seric wlist[wn].w_name = newstr(cbuf); 247*4632Seric wlist[wn].w_size = st.st_size; 248*4632Seric break; 249*4632Seric 250*4632Seric case 'P': /* message priority */ 251*4632Seric wlist[wn].w_pri = atoi(&lbuf[1]); 252*4632Seric break; 253*4632Seric } 254*4632Seric } 255*4632Seric wn++; 256*4632Seric (void) fclose(cf); 257*4632Seric } 258*4632Seric (void) fclose(f); 259*4632Seric 260*4632Seric /* 261*4632Seric ** Sort the work directory. 262*4632Seric */ 263*4632Seric 264*4632Seric qsort(wlist, wn, sizeof *wlist, workcmpf); 265*4632Seric 266*4632Seric /* 267*4632Seric ** Convert the work list into canonical form. 268*4632Seric */ 269*4632Seric 270*4632Seric wp = &WorkQ; 271*4632Seric for (i = 0; i < wn; i++) 272*4632Seric { 273*4632Seric w = (WORK *) xalloc(sizeof *w); 274*4632Seric w->w_name = wlist[i].w_name; 275*4632Seric w->w_size = wlist[i].w_size; 276*4632Seric w->w_pri = wlist[i].w_pri; 277*4632Seric w->w_next = NULL; 278*4632Seric *wp = w; 279*4632Seric wp = &w->w_next; 280*4632Seric } 281*4632Seric 282*4632Seric # ifdef DEBUG 283*4632Seric if (Debug) 284*4632Seric { 285*4632Seric for (w = WorkQ; w != NULL; w = w->w_next) 286*4632Seric printf("%32s: sz=%ld\n", w->w_name, w->w_size); 287*4632Seric } 288*4632Seric # endif DEBUG 289*4632Seric } 290*4632Seric /* 291*4632Seric ** WORKCMPF -- compare function for ordering work. 292*4632Seric ** 293*4632Seric ** Parameters: 294*4632Seric ** a -- the first argument. 295*4632Seric ** b -- the second argument. 296*4632Seric ** 297*4632Seric ** Returns: 298*4632Seric ** -1 if a < b 299*4632Seric ** 0 if a == b 300*4632Seric ** 1 if a > b 301*4632Seric ** 302*4632Seric ** Side Effects: 303*4632Seric ** none. 304*4632Seric */ 305*4632Seric 306*4632Seric # define PRIFACT 1800 /* bytes each priority point is worth */ 307*4632Seric 308*4632Seric workcmpf(a, b) 309*4632Seric WORK *a; 310*4632Seric WORK *b; 311*4632Seric { 312*4632Seric register long aval; 313*4632Seric register long bval; 314*4632Seric 315*4632Seric aval = a->w_size - PRIFACT * a->w_pri; 316*4632Seric bval = b->w_size - PRIFACT * b->w_pri; 317*4632Seric 318*4632Seric if (aval == bval) 319*4632Seric return (0); 320*4632Seric else if (aval > bval) 321*4632Seric return (1); 322*4632Seric else 323*4632Seric return (-1); 324*4632Seric } 325*4632Seric /* 326*4632Seric ** DOWORK -- do a work request. 327*4632Seric ** 328*4632Seric ** Parameters: 329*4632Seric ** w -- the work request to be satisfied. 330*4632Seric ** 331*4632Seric ** Returns: 332*4632Seric ** none. 333*4632Seric ** 334*4632Seric ** Side Effects: 335*4632Seric ** The work request is satisfied if possible. 336*4632Seric */ 337*4632Seric 338*4632Seric dowork(w) 339*4632Seric register WORK *w; 340*4632Seric { 341*4632Seric register int i; 342*4632Seric auto int xstat; 343*4632Seric 344*4632Seric # ifdef DEBUG 345*4632Seric if (Debug) 346*4632Seric printf("dowork: %s size %ld pri %d\n", w->w_name, 347*4632Seric w->w_size, w->w_pri); 348*4632Seric # endif DEBUG 349*4632Seric 350*4632Seric /* 351*4632Seric ** Fork for work. 352*4632Seric */ 353*4632Seric 354*4632Seric i = fork(); 355*4632Seric if (i < 0) 356*4632Seric { 357*4632Seric syserr("dowork: cannot fork"); 358*4632Seric return; 359*4632Seric } 360*4632Seric 361*4632Seric if (i == 0) 362*4632Seric { 363*4632Seric /* 364*4632Seric ** CHILD 365*4632Seric */ 366*4632Seric 367*4632Seric QueueRun = TRUE; 368*4632Seric initsys(); 369*4632Seric readqf(w->w_name); 370*4632Seric sendall(FALSE); 371*4632Seric if (!QueueUp) 372*4632Seric (void) unlink(w->w_name); 373*4632Seric else if (CurTime > TimeOut) 374*4632Seric timeout(w); 375*4632Seric finis(); 376*4632Seric } 377*4632Seric 378*4632Seric /* 379*4632Seric ** Parent -- pick up results. 380*4632Seric */ 381*4632Seric 382*4632Seric errno = 0; 383*4632Seric while ((i = wait(&xstat)) > 0 && errno != EINTR) 384*4632Seric { 385*4632Seric if (errno == EINTR) 386*4632Seric { 387*4632Seric errno = 0; 388*4632Seric } 389*4632Seric } 390*4632Seric } 391*4632Seric /* 392*4632Seric ** READQF -- read queue file and set up environment. 393*4632Seric ** 394*4632Seric ** Parameters: 395*4632Seric ** cf -- name of queue control file. 396*4632Seric ** 397*4632Seric ** Returns: 398*4632Seric ** none. 399*4632Seric ** 400*4632Seric ** Side Effects: 401*4632Seric ** cf is read and created as the current job, as though 402*4632Seric ** we had been invoked by argument. 403*4632Seric */ 404*4632Seric 405*4632Seric readqf(cf) 406*4632Seric char *cf; 407*4632Seric { 408*4632Seric register FILE *f; 409*4632Seric char buf[MAXLINE]; 410*4632Seric 411*4632Seric /* 412*4632Seric ** Open the file created by queueup. 413*4632Seric */ 414*4632Seric 415*4632Seric f = fopen(cf, "r"); 416*4632Seric if (f == NULL) 417*4632Seric { 418*4632Seric syserr("readqf: no cf file %s", cf); 419*4632Seric return; 420*4632Seric } 421*4632Seric 422*4632Seric /* 423*4632Seric ** Read and process the file. 424*4632Seric */ 425*4632Seric 426*4632Seric while (fgets(buf, sizeof buf, f) != NULL) 427*4632Seric { 428*4632Seric fixcrlf(buf, TRUE); 429*4632Seric 430*4632Seric switch (buf[0]) 431*4632Seric { 432*4632Seric case 'R': /* specify recipient */ 433*4632Seric sendto(&buf[1], 1, (ADDRESS *) NULL); 434*4632Seric break; 435*4632Seric 436*4632Seric case 'H': /* header */ 437*4632Seric (void) chompheader(&buf[1], FALSE); 438*4632Seric break; 439*4632Seric 440*4632Seric case 'S': /* sender */ 441*4632Seric setsender(&buf[1]); 442*4632Seric break; 443*4632Seric 444*4632Seric case 'D': /* data file name */ 445*4632Seric InFileName = newstr(&buf[1]); 446*4632Seric TempFile = fopen(InFileName, "r"); 447*4632Seric if (TempFile == NULL) 448*4632Seric syserr("readqf: cannot open %s", InFileName); 449*4632Seric break; 450*4632Seric 451*4632Seric case 'T': /* timeout */ 452*4632Seric (void) sscanf(&buf[1], "%ld", &TimeOut); 453*4632Seric break; 454*4632Seric 455*4632Seric default: 456*4632Seric syserr("readqf(%s): bad line \"%s\"", cf, buf); 457*4632Seric break; 458*4632Seric } 459*4632Seric } 460*4632Seric } 461*4632Seric /* 462*4632Seric ** TIMEOUT -- process timeout on queue file. 463*4632Seric ** 464*4632Seric ** Parameters: 465*4632Seric ** w -- pointer to work request that timed out. 466*4632Seric ** 467*4632Seric ** Returns: 468*4632Seric ** none. 469*4632Seric ** 470*4632Seric ** Side Effects: 471*4632Seric ** Returns a message to the sender saying that this 472*4632Seric ** message has timed out. 473*4632Seric */ 474*4632Seric 475*4632Seric timeout(w) 476*4632Seric register WORK *w; 477*4632Seric { 478*4632Seric printf("timeout(%s)\n", w->w_name); 479*4632Seric } 480