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