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