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