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