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