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