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