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