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