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