1 # include "sendmail.h" 2 # include <sys/stat.h> 3 # include <ndir.h> 4 # include <signal.h> 5 # include <errno.h> 6 7 # ifndef QUEUE 8 SCCSID(@(#)queue.c 3.26 07/27/82 (no queueing)); 9 # else QUEUE 10 11 SCCSID(@(#)queue.c 3.26 07/27/82); 12 13 /* 14 ** QUEUEUP -- queue a message up for future transmission. 15 ** 16 ** The queued message should already be in the correct place. 17 ** This routine just outputs the control file as appropriate. 18 ** 19 ** Parameters: 20 ** e -- the envelope to queue up. 21 ** queueall -- if TRUE, queue all addresses, rather than 22 ** just those with the QQUEUEUP flag set. 23 ** 24 ** Returns: 25 ** none. 26 ** 27 ** Side Effects: 28 ** The current request (only unsatisfied addresses) 29 ** are saved in a control file. 30 */ 31 32 queueup(e, queueall) 33 register ENVELOPE *e; 34 bool queueall; 35 { 36 char cf[MAXNAME]; 37 char buf[MAXNAME]; 38 register FILE *cfp; 39 register HDR *h; 40 register ADDRESS *q; 41 extern char *mktemp(); 42 register int i; 43 44 /* 45 ** Create control file. 46 */ 47 48 (void) strcpy(cf, QueueDir); 49 (void) strcat(cf, "/tfXXXXXX"); 50 (void) mktemp(cf); 51 cfp = fopen(cf, "w"); 52 if (cfp == NULL) 53 { 54 syserr("queueup: cannot create control file %s", cf); 55 return; 56 } 57 (void) chmod(cf, 0600); 58 59 # ifdef DEBUG 60 if (Debug) 61 printf("queueing in %s\n", cf); 62 # endif DEBUG 63 64 /* 65 ** If there is no data file yet, create one. 66 */ 67 68 if (e->e_df == NULL) 69 { 70 register FILE *dfp; 71 72 (void) strcpy(buf, QueueDir); 73 (void) strcat(buf, "/dfXXXXXX"); 74 e->e_df = newstr(mktemp(buf)); 75 dfp = fopen(e->e_df, "w"); 76 if (dfp == NULL) 77 { 78 syserr("queueup: cannot create %s", e->e_df); 79 (void) fclose(cfp); 80 return; 81 } 82 (void) chmod(e->e_df, 0600); 83 (*e->e_putbody)(dfp, Mailer[1], FALSE); 84 (void) fclose(dfp); 85 } 86 87 /* 88 ** Output future work requests. 89 */ 90 91 /* output name of data file */ 92 fprintf(cfp, "D%s\n", e->e_df); 93 94 /* output name of sender */ 95 fprintf(cfp, "S%s\n", e->e_from.q_paddr); 96 97 /* output timeout */ 98 fprintf(cfp, "T%ld\n", TimeOut); 99 100 /* output message priority */ 101 fprintf(cfp, "P%ld\n", e->e_msgpriority); 102 103 /* output message class */ 104 fprintf(cfp, "C%d\n", e->e_class); 105 106 /* output macro definitions */ 107 for (i = 0; i < 128; i++) 108 { 109 register char *p = e->e_macro[i]; 110 111 if (p != NULL && i != (int) 'b') 112 fprintf(cfp, "M%c%s\n", i, p); 113 } 114 115 /* output list of recipient addresses */ 116 for (q = e->e_sendqueue; q != NULL; q = q->q_next) 117 { 118 # ifdef DEBUG 119 if (Debug > 0) 120 { 121 printf("queueing "); 122 printaddr(q, FALSE); 123 } 124 # endif DEBUG 125 if (queueall || bitset(QQUEUEUP, q->q_flags)) 126 fprintf(cfp, "R%s\n", q->q_paddr); 127 } 128 129 /* output headers for this message */ 130 for (h = e->e_header; h != NULL; h = h->h_link) 131 { 132 if (h->h_value == NULL || h->h_value[0] == '\0') 133 continue; 134 fprintf(cfp, "H"); 135 if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags)) 136 mfdecode(h->h_mflags, cfp); 137 fprintf(cfp, "%s: %s\n", h->h_field, h->h_value); 138 } 139 140 /* 141 ** Clean up. 142 */ 143 144 (void) fclose(cfp); 145 (void) strcpy(buf, QueueDir); 146 (void) strcat(buf, "/cfXXXXXX"); 147 (void) mktemp(buf); 148 if (link(cf, buf) < 0) 149 syserr("cannot link(%s, %s), df=%s", cf, buf, e->e_df); 150 else 151 (void) unlink(cf); 152 153 /* disconnect this temp file from the job */ 154 e->e_df = NULL; 155 } 156 /* 157 ** RUNQUEUE -- run the jobs in the queue. 158 ** 159 ** Gets the stuff out of the queue in some presumably logical 160 ** order and processes them. 161 ** 162 ** Parameters: 163 ** none. 164 ** 165 ** Returns: 166 ** none. 167 ** 168 ** Side Effects: 169 ** runs things in the mail queue. 170 */ 171 172 bool ReorderQueue; /* if set, reorder the send queue */ 173 int QueuePid; /* pid of child running queue */ 174 175 runqueue(forkflag) 176 bool forkflag; 177 { 178 extern reordersig(); 179 180 /* 181 ** See if we want to go off and do other useful work. 182 */ 183 184 if (forkflag) 185 { 186 QueuePid = dofork(); 187 if (QueuePid > 0) 188 { 189 /* parent */ 190 return; 191 } 192 else 193 (void) alarm(0); 194 } 195 196 /* 197 ** Arrange to reorder the queue at polite intervals. 198 */ 199 200 if (QueueIntvl != 0) 201 { 202 (void) signal(SIGALRM, reordersig); 203 (void) alarm(QueueIntvl); 204 } 205 206 /* 207 ** Start making passes through the queue. 208 ** First, read and sort the entire queue. 209 ** Then, process the work in that order. 210 ** But if you take too long, start over. 211 ** There is a race condition at the end -- we could get 212 ** a reorder signal after finishing the queue. 213 ** In this case we will hang for one more queue 214 ** interval -- clearly a botch, but rare and 215 ** relatively innocuous. 216 */ 217 218 for (;;) 219 { 220 /* order the existing work requests */ 221 orderq(); 222 ReorderQueue = FALSE; 223 224 /* process them once at a time */ 225 while (WorkQ != NULL) 226 { 227 WORK *w = WorkQ; 228 229 WorkQ = WorkQ->w_next; 230 dowork(w); 231 free(w->w_name); 232 free((char *) w); 233 if (ReorderQueue) 234 break; 235 } 236 237 /* if we are just doing one pass, then we are done */ 238 if (QueueIntvl == 0) 239 break; 240 241 /* wait for work -- note (harmless) race condition here */ 242 if (!ReorderQueue) 243 pause(); 244 } 245 246 /* no work to do -- just exit */ 247 finis(); 248 } 249 /* 250 ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 251 ** 252 ** Parameters: 253 ** none. 254 ** 255 ** Returns: 256 ** none. 257 ** 258 ** Side Effects: 259 ** sets the "reorder work queue" flag. 260 */ 261 262 reordersig() 263 { 264 if (QueuePid == 0) 265 { 266 /* we are in a child doing queueing */ 267 ReorderQueue = TRUE; 268 } 269 else 270 { 271 /* we are in a parent -- poke child or start new one */ 272 if (kill(QueuePid, SIGALRM) < 0) 273 { 274 /* no child -- get zombie & start new one */ 275 static int st; 276 277 (void) wait(&st); 278 QueuePid = dofork(); 279 if (QueuePid == 0) 280 { 281 /* new child; run queue */ 282 runqueue(FALSE); 283 finis(); 284 } 285 } 286 } 287 288 /* 289 ** Arrange to get this signal again. 290 */ 291 292 (void) signal(SIGALRM, reordersig); 293 (void) alarm(QueueIntvl); 294 } 295 /* 296 ** ORDERQ -- order the work queue. 297 ** 298 ** Parameters: 299 ** none. 300 ** 301 ** Returns: 302 ** none. 303 ** 304 ** Side Effects: 305 ** Sets WorkQ to the queue of available work, in order. 306 */ 307 308 # define WLSIZE 120 /* max size of worklist per sort */ 309 310 orderq() 311 { 312 register struct direct *d; 313 register WORK *w; 314 register WORK **wp; /* parent of w */ 315 DIR *f; 316 register int i; 317 WORK wlist[WLSIZE]; 318 int wn = 0; 319 extern workcmpf(); 320 extern char *QueueDir; 321 322 /* clear out old WorkQ */ 323 for (w = WorkQ; w != NULL; ) 324 { 325 register WORK *nw = w->w_next; 326 327 WorkQ = nw; 328 free(w->w_name); 329 free((char *) w); 330 w = nw; 331 } 332 333 /* open the queue directory */ 334 f = opendir(QueueDir); 335 if (f == NULL) 336 { 337 syserr("orderq: cannot open %s", QueueDir); 338 return; 339 } 340 341 /* 342 ** Read the work directory. 343 */ 344 345 while (wn < WLSIZE && (d = readdir(f)) != NULL) 346 { 347 char cbuf[MAXNAME]; 348 char lbuf[MAXNAME]; 349 FILE *cf; 350 register char *p; 351 352 /* is this an interesting entry? */ 353 if (d->d_name[0] != 'c') 354 continue; 355 356 /* yes -- find the control file location */ 357 (void) strcpy(cbuf, QueueDir); 358 (void) strcat(cbuf, "/"); 359 p = &cbuf[strlen(cbuf)]; 360 (void) strcpy(p, d->d_name); 361 362 /* open control file */ 363 cf = fopen(cbuf, "r"); 364 if (cf == NULL) 365 { 366 /* this may be some random person sending hir msgs */ 367 /* syserr("orderq: cannot open %s", cbuf); */ 368 errno = 0; 369 continue; 370 } 371 wlist[wn].w_name = newstr(cbuf); 372 373 /* extract useful information */ 374 while (fgets(lbuf, sizeof lbuf, cf) != NULL) 375 { 376 fixcrlf(lbuf, TRUE); 377 378 switch (lbuf[0]) 379 { 380 case 'P': /* message priority */ 381 (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri); 382 break; 383 } 384 } 385 wn++; 386 (void) fclose(cf); 387 } 388 (void) closedir(f); 389 390 /* 391 ** Sort the work directory. 392 */ 393 394 qsort(wlist, wn, sizeof *wlist, workcmpf); 395 396 /* 397 ** Convert the work list into canonical form. 398 */ 399 400 wp = &WorkQ; 401 for (i = 0; i < wn; i++) 402 { 403 w = (WORK *) xalloc(sizeof *w); 404 w->w_name = wlist[i].w_name; 405 w->w_pri = wlist[i].w_pri; 406 w->w_next = NULL; 407 *wp = w; 408 wp = &w->w_next; 409 } 410 411 # ifdef DEBUG 412 if (Debug) 413 { 414 for (w = WorkQ; w != NULL; w = w->w_next) 415 printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 416 } 417 # endif DEBUG 418 } 419 /* 420 ** WORKCMPF -- compare function for ordering work. 421 ** 422 ** Parameters: 423 ** a -- the first argument. 424 ** b -- the second argument. 425 ** 426 ** Returns: 427 ** -1 if a < b 428 ** 0 if a == b 429 ** 1 if a > b 430 ** 431 ** Side Effects: 432 ** none. 433 */ 434 435 # define PRIFACT 1800 /* bytes each priority point is worth */ 436 437 workcmpf(a, b) 438 register WORK *a; 439 register WORK *b; 440 { 441 if (a->w_pri == b->w_pri) 442 return (0); 443 else if (a->w_pri > b->w_pri) 444 return (1); 445 else 446 return (-1); 447 } 448 /* 449 ** DOWORK -- do a work request. 450 ** 451 ** Parameters: 452 ** w -- the work request to be satisfied. 453 ** 454 ** Returns: 455 ** none. 456 ** 457 ** Side Effects: 458 ** The work request is satisfied if possible. 459 */ 460 461 dowork(w) 462 register WORK *w; 463 { 464 register int i; 465 auto int xstat; 466 467 # ifdef DEBUG 468 if (Debug) 469 printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 470 # endif DEBUG 471 472 /* 473 ** Fork for work. 474 */ 475 476 i = fork(); 477 if (i < 0) 478 { 479 syserr("dowork: cannot fork"); 480 return; 481 } 482 483 if (i == 0) 484 { 485 char buf[MAXNAME]; 486 487 /* 488 ** CHILD 489 ** Change the name of the control file to avoid 490 ** duplicate deliveries. Then run the file 491 ** as though we had just read it. 492 ** We save an idea of the temporary name so we 493 ** can recover on interrupt. 494 */ 495 496 (void) alarm(0); 497 FatalErrors = FALSE; 498 QueueRun = TRUE; 499 MailBack = TRUE; 500 (void) strcpy(buf, QueueDir); 501 (void) strcat(buf, "/tfXXXXXX"); 502 (void) mktemp(buf); 503 if (link(w->w_name, buf) < 0) 504 { 505 syserr("dowork: link(%s, %s)", w->w_name, buf); 506 507 /* it's ok to lie -- it will be run later */ 508 exit(EX_OK); 509 } 510 ControlFile = newstr(buf); 511 (void) unlink(w->w_name); 512 513 /* create ourselves a transcript file */ 514 openxscrpt(); 515 516 /* do basic system initialization */ 517 initsys(); 518 519 /* read the queue control file */ 520 readqf(buf); 521 522 /* do the delivery */ 523 if (!FatalErrors) 524 sendall(CurEnv, FALSE); 525 526 /* if still not sent, perhaps we should time out.... */ 527 # ifdef DEBUG 528 if (Debug > 2) 529 printf("CurTime=%ld, TimeOut=%ld\n", CurTime, TimeOut); 530 # endif DEBUG 531 if (CurEnv->e_queueup && CurTime > TimeOut) 532 timeout(w); 533 534 /* get rid of the temporary file -- a new cf will be made */ 535 ControlFile = NULL; 536 (void) unlink(buf); 537 538 /* finish up and exit */ 539 finis(); 540 } 541 542 /* 543 ** Parent -- pick up results. 544 */ 545 546 errno = 0; 547 while ((i = wait(&xstat)) > 0 && errno != EINTR) 548 { 549 if (errno == EINTR) 550 { 551 errno = 0; 552 } 553 } 554 } 555 /* 556 ** READQF -- read queue file and set up environment. 557 ** 558 ** Parameters: 559 ** cf -- name of queue control file. 560 ** 561 ** Returns: 562 ** none. 563 ** 564 ** Side Effects: 565 ** cf is read and created as the current job, as though 566 ** we had been invoked by argument. 567 */ 568 569 readqf(cf) 570 char *cf; 571 { 572 register FILE *f; 573 char buf[MAXLINE]; 574 575 /* 576 ** Open the file created by queueup. 577 */ 578 579 f = fopen(cf, "r"); 580 if (f == NULL) 581 { 582 syserr("readqf: no cf file %s", cf); 583 return; 584 } 585 586 /* 587 ** Read and process the file. 588 */ 589 590 if (Verbose) 591 printf("\nRunning %s\n", cf); 592 while (fgets(buf, sizeof buf, f) != NULL) 593 { 594 fixcrlf(buf, TRUE); 595 596 switch (buf[0]) 597 { 598 case 'R': /* specify recipient */ 599 sendto(&buf[1], 1, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 600 break; 601 602 case 'H': /* header */ 603 (void) chompheader(&buf[1], FALSE); 604 break; 605 606 case 'S': /* sender */ 607 setsender(newstr(&buf[1])); 608 break; 609 610 case 'D': /* data file name */ 611 CurEnv->e_df = newstr(&buf[1]); 612 TempFile = fopen(CurEnv->e_df, "r"); 613 if (TempFile == NULL) 614 syserr("readqf: cannot open %s", CurEnv->e_df); 615 break; 616 617 case 'T': /* timeout */ 618 (void) sscanf(&buf[1], "%ld", &TimeOut); 619 break; 620 621 case 'P': /* message priority */ 622 (void) sscanf(&buf[1], "%ld", &CurEnv->e_msgpriority); 623 624 /* make sure that big things get sent eventually */ 625 CurEnv->e_msgpriority -= WKTIMEFACT; 626 break; 627 628 case 'C': /* message class */ 629 (void) sscanf(&buf[1], "%hd", &CurEnv->e_class); 630 break; 631 632 case 'M': /* define macro */ 633 define(buf[1], newstr(&buf[2])); 634 break; 635 636 default: 637 syserr("readqf(%s): bad line \"%s\"", cf, buf); 638 break; 639 } 640 } 641 } 642 /* 643 ** TIMEOUT -- process timeout on queue file. 644 ** 645 ** Parameters: 646 ** w -- pointer to work request that timed out. 647 ** 648 ** Returns: 649 ** none. 650 ** 651 ** Side Effects: 652 ** Returns a message to the sender saying that this 653 ** message has timed out. 654 */ 655 656 timeout(w) 657 register WORK *w; 658 { 659 char buf[MAXLINE]; 660 extern char *TextTimeOut; 661 662 # ifdef DEBUG 663 if (Debug > 0) 664 printf("timeout(%s)\n", w->w_name); 665 # endif DEBUG 666 message(Arpa_Info, "Message has timed out"); 667 668 /* return message to sender */ 669 (void) sprintf(buf, "Cannot send mail for %s", TextTimeOut); 670 (void) returntosender(buf, &CurEnv->e_from, TRUE); 671 672 /* arrange to remove files from queue */ 673 CurEnv->e_queueup = FALSE; 674 } 675 676 # endif QUEUE 677