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