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