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