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 SCCSID(@(#)queue.c 3.9 02/20/82 (no queueing)); 9 # else QUEUE 10 11 SCCSID(@(#)queue.c 3.9 02/20/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 /* 203 ** REORDERSIG -- catch the alarm signal and tell sendmail to reorder queue. 204 ** 205 ** Parameters: 206 ** none. 207 ** 208 ** Returns: 209 ** none. 210 ** 211 ** Side Effects: 212 ** sets the "reorder work queue" flag. 213 */ 214 215 reordersig() 216 { 217 if (QueuePid == 0) 218 { 219 /* we are in a child doing queueing */ 220 ReorderQueue = TRUE; 221 } 222 else 223 { 224 /* we are in a parent -- poke child or start new one */ 225 if (kill(QueuePid, SIGALRM) < 0) 226 { 227 /* no child -- get zombie & start new one */ 228 static int st; 229 230 (void) wait(&st); 231 QueuePid = dofork(); 232 if (QueuePid == 0) 233 { 234 /* new child; run queue */ 235 runqueue(FALSE); 236 finis(); 237 } 238 } 239 } 240 241 /* 242 ** Arrange to get this signal again. 243 */ 244 245 (void) alarm((unsigned) QueueIntvl); 246 } 247 /* 248 ** ORDERQ -- order the work queue. 249 ** 250 ** Parameters: 251 ** none. 252 ** 253 ** Returns: 254 ** none. 255 ** 256 ** Side Effects: 257 ** Sets WorkQ to the queue of available work, in order. 258 */ 259 260 # define WLSIZE 120 /* max size of worklist per sort */ 261 262 orderq() 263 { 264 struct direct d; 265 register WORK *w; 266 register WORK **wp; /* parent of w */ 267 register FILE *f; 268 register int i; 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 case 'M': /* define macro */ 543 define(buf[1], newstr(&buf[2])); 544 break; 545 546 default: 547 syserr("readqf(%s): bad line \"%s\"", cf, buf); 548 break; 549 } 550 } 551 } 552 /* 553 ** TIMEOUT -- process timeout on queue file. 554 ** 555 ** Parameters: 556 ** w -- pointer to work request that timed out. 557 ** 558 ** Returns: 559 ** none. 560 ** 561 ** Side Effects: 562 ** Returns a message to the sender saying that this 563 ** message has timed out. 564 */ 565 566 timeout(w) 567 register WORK *w; 568 { 569 # ifdef DEBUG 570 if (Debug > 0) 571 printf("timeout(%s)\n", w->w_name); 572 # endif DEBUG 573 574 /* return message to sender */ 575 (void) returntosender("Cannot send mail for three days"); 576 577 /* arrange to remove files from queue */ 578 QueueUp = FALSE; 579 } 580 581 # endif QUEUE 582