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