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