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