mxf-send.c (19274B)
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <sys/time.h> 4 #include "readwrite.h" 5 #include "sig.h" 6 #include "direntry.h" 7 #include "control.h" 8 #include "select.h" 9 #include "open.h" 10 #include "seek.h" 11 #include "exit.h" 12 #include "lock.h" 13 #include "ndelay.h" 14 #include "now.h" 15 #include "getln.h" 16 17 #ifdef USING_SKALIBS 18 #include <skalibs/buffer.h> 19 #include <skalibs/iopause.h> 20 #include <skalibs/stralloc.h> 21 #include <skalibs/tai.h> 22 #else 23 #include "substdio.h" 24 #include "stralloc.h" 25 #endif 26 27 #include "error.h" 28 29 #include "str.h" 30 #include "byte.h" 31 32 #include "fmt.h" 33 #include "scan.h" 34 #include "alloc.h" 35 36 #include "case.h" 37 #include "auto_qmail.h" 38 #include "trigger.h" 39 #include "newfield.h" 40 #include "quote.h" 41 #include "qmail.h" 42 #include "qsutil.h" 43 #include "prioq.h" 44 #include "constmap.h" 45 #include "fmtqfn.h" 46 #include "readsubdir.h" 47 48 /* critical timing feature #1: if not triggered, do not busy-loop */ 49 /* critical timing feature #2: if triggered, respond within fixed time */ 50 /* important timing feature: when triggered, respond instantly */ 51 #define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ 52 #define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ 53 #define SLEEP_FOREVER 86400 /* absolute maximum time spent in iopause() */ 54 #define SLEEP_CLEANUP 76431 /* time between cleanups */ 55 #define SLEEP_SYSFAIL 123 56 #define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 57 * hours) */ 58 59 int lifetime = 604800; 60 61 stralloc percenthack = {0}; 62 struct constmap mappercenthack; 63 stralloc locals = {0}; 64 struct constmap maplocals; 65 stralloc vdoms = {0}; 66 struct constmap mapvdoms; 67 stralloc envnoathost = {0}; 68 stralloc bouncefrom = {0}; 69 stralloc bouncehost = {0}; 70 stralloc doublebounceto = {0}; 71 stralloc doublebouncehost = {0}; 72 73 char strnum2[FMT_ULONG]; 74 char strnum3[FMT_ULONG]; 75 76 #define CHANNELS 2 77 char *chanaddr[CHANNELS] = {"local/", "remote/"}; 78 char *chanstatusmsg[CHANNELS] = {" local ", " remote "}; 79 char *tochan[CHANNELS] = {" to local ", " to remote "}; 80 int chanfdout[CHANNELS] = {1, 3}; /* hardcoded FDs as set up by -start are a crock. */ 81 int chanfdin[CHANNELS] = {2, 4}; /* see above. */ 82 int chanskip[CHANNELS] = {10, 20}; /* add this many seconds to quadr. retry */ 83 84 int flagexitasap = 0; 85 void 86 sigterm() 87 { 88 flagexitasap = 1; 89 } 90 int flagrunasap = 0; 91 void 92 sigalrm() 93 { 94 flagrunasap = 1; 95 } 96 int flagreadasap = 0; 97 void 98 sighup() 99 { 100 flagreadasap = 1; 101 } 102 103 void cleandied() { 104 log1("alert: the clean channel driver exited. to ensure that it is restarted, I will too.\n"); 105 flagexitasap = 1; 106 } 107 108 int flagspawnalive[CHANNELS]; 109 void spawndied(int c){ 110 log1("alert: one or more delivery channel drivers exited. to ensure that it is restarted, I will too.\n"); 111 flagspawnalive[c] = 0; 112 flagexitasap = 1; 113 } 114 115 #define REPORTMAX 10000 116 117 datetime_sec recent; 118 119 120 /* this file is too long ----------------------------------------- FILENAMES 121 122 stralloc fn = {0}; 123 stralloc fn2 = {0}; 124 char fnmake_strnum[FMT_ULONG]; 125 126 void 127 fnmake_init() 128 { 129 while (!stralloc_ready(&fn, FMTQFN)) 130 nomem(); 131 while (!stralloc_ready(&fn2, FMTQFN)) 132 nomem(); 133 } 134 135 void fnmake_info(unsigned long id){ 136 fn.len = fmtqfn(fn.s, "info/", id, 1); 137 } 138 void fnmake_todo(unsigned long id){ 139 fn.len = fmtqfn(fn.s, "todo/", id, 0); 140 } 141 void fnmake_mess(unsigned long id){ 142 fn.len = fmtqfn(fn.s, "mess/", id, 1); 143 } 144 void fnmake_foop(unsigned long id){ 145 fn.len = fmtqfn(fn.s, "foop/", id, 0); 146 } 147 void fnmake_split(unsigned long id){ 148 fn.len = fmtqfn(fn.s, "", id, 1); 149 } 150 void 151 fnmake2_bounce(unsigned long id) 152 { 153 fn2.len = fmtqfn(fn2.s, "bounce/", id, 0); 154 } 155 void 156 fnmake_chanaddr(unsigned long id, int c) 157 { 158 fn.len = fmtqfn(fn.s, chanaddr[c], id, 1); 159 } 160 */ 161 162 /* this file is too long ----------------------------------------- REWRITING */ 163 /* the future is here! the rewriting routines have been moved to rewrite.c. */ 164 165 /* this file is too long ---------------------------------------------- INFO */ 166 167 int 168 getinfo(stralloc * sa, datetime_sec * dt, unsigned long id) 169 { 170 int fdinfo; 171 struct stat st; 172 static stralloc line = { 173 0 174 }; 175 int match; 176 substdio ss; 177 char buf[128]; 178 179 fnmake_info(id, &fn); 180 fdinfo = open_read(fn.s); 181 if (fdinfo == -1) 182 return 0; 183 if (fstat(fdinfo, &st) == -1) { 184 close(fdinfo); 185 return 0; 186 } 187 substdio_fdbuf(&ss, read, fdinfo, buf, sizeof(buf)); 188 if (getln(&ss, &line, &match, '\0') == -1) { 189 close(fdinfo); 190 return 0; 191 } 192 close(fdinfo); 193 if (!match) 194 return 0; 195 if (line.s[0] != 'F') 196 return 0; 197 198 *dt = st.st_mtime; 199 while (!stralloc_copys(sa, line.s + 1)) 200 nomem(); 201 while (!stralloc_0(sa)) 202 nomem(); 203 return 1; 204 } 205 206 207 /* this file is too long ----------------------------------- PRIORITY QUEUES */ 208 209 prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */ 210 prioq pqchan[CHANNELS] = {{0}, {0}}; 211 /* pqchan 0: -todo +info +local ?remote */ 212 /* pqchan 1: -todo +info ?local +remote */ 213 prioq pqfail = {0}; /* stat() failure; has to be pqadded again */ 214 215 void 216 pqadd(unsigned long id) 217 { 218 struct prioq_elt pe; 219 struct prioq_elt pechan[CHANNELS]; 220 int flagchan[CHANNELS]; 221 struct stat st; 222 int c; 223 224 #define CHECKSTAT if (errno != error_noent) goto fail; 225 226 fnmake_info(id, &fn); 227 if (stat(fn.s, &st) == -1) { 228 CHECKSTAT 229 return; /* someone yanking our chain */ 230 } 231 232 fnmake_todo(id, &fn); 233 if (stat(fn.s, &st) != -1) 234 return; /* look, ma, dad crashed writing info! */ 235 CHECKSTAT 236 237 for (c = 0; c < CHANNELS; ++c) { 238 fnmake_chanaddr(id, c, &fn); 239 if (stat(fn.s, &st) == -1) { 240 flagchan[c] = 0; 241 CHECKSTAT 242 } else { 243 flagchan[c] = 1; 244 pechan[c].id = id; 245 pechan[c].dt = st.st_mtime; 246 } 247 } 248 249 for (c = 0; c < CHANNELS; ++c) 250 if (flagchan[c]) 251 while (!prioq_insert(&pqchan[c], &pechan[c])) 252 nomem(); 253 254 for (c = 0; c < CHANNELS; ++c) 255 if (flagchan[c]) 256 break; 257 if (c == CHANNELS) { 258 pe.id = id; 259 pe.dt = now(); 260 while (!prioq_insert(&pqdone, &pe)) 261 nomem(); 262 } 263 264 return; 265 266 fail: 267 log3("warning: unable to stat ", fn.s, "; will try again later\n"); 268 pe.id = id; 269 pe.dt = now() + SLEEP_SYSFAIL; 270 while (!prioq_insert(&pqfail, &pe)) 271 nomem(); 272 } 273 274 void 275 pqstart() 276 { 277 readsubdir rs; 278 int x; 279 unsigned long id; 280 281 readsubdir_init(&rs, "info", pausedir); 282 283 while ((x = readsubdir_next(&rs, &id))) 284 if (x > 0) 285 pqadd(id); 286 } 287 288 void 289 pqfinish() 290 { 291 int c; 292 struct prioq_elt pe; 293 struct timeval ut[2] = {0}; 294 295 for (c = 0; c < CHANNELS; ++c) 296 while (prioq_min(&pqchan[c], &pe)) { 297 prioq_delmin(&pqchan[c]); 298 fnmake_chanaddr(pe.id, c, &fn); 299 ut[0].tv_sec = ut[1].tv_sec = pe.dt; 300 if (utimes(fn.s, ut) == -1) 301 log3("warning: unable to utime ", fn.s, "; message will be retried too soon\n"); 302 } 303 } 304 305 void 306 pqrun() 307 { 308 int c; 309 unsigned int i; 310 for (c = 0; c < CHANNELS; ++c) 311 if (pqchan[c].p) 312 if (pqchan[c].len) 313 for (i = 0; i < pqchan[c].len; ++i) 314 pqchan[c].p[i].dt = recent; 315 } 316 317 318 /* this file is too long ---------------------------------------------- JOBS */ 319 320 struct job { 321 int refs; /* if 0, this struct is unused */ 322 unsigned long id; 323 int channel; 324 datetime_sec retry; 325 stralloc sender; 326 int numtodo; 327 int flaghiteof; 328 int flagdying; 329 } 330 ; 331 332 int numjobs; 333 struct job *jo; 334 335 void 336 job_init() 337 { 338 int j; 339 while (!(jo = (struct job *)alloc(numjobs * sizeof(struct job)))) 340 nomem(); 341 for (j = 0; j < numjobs; ++j) { 342 jo[j].refs = 0; 343 jo[j].sender.s = 0; 344 } 345 } 346 347 int 348 job_avail() 349 { 350 int j; 351 for (j = 0; j < numjobs; ++j) 352 if (!jo[j].refs) 353 return 1; 354 return 0; 355 } 356 357 int 358 job_open(unsigned long id, int channel) 359 { 360 int j; 361 for (j = 0; j < numjobs; ++j) 362 if (!jo[j].refs) 363 break; 364 if (j == numjobs) 365 return -1; 366 jo[j].refs = 1; 367 jo[j].id = id; 368 jo[j].channel = channel; 369 jo[j].numtodo = 0; 370 jo[j].flaghiteof = 0; 371 return j; 372 } 373 374 void 375 job_close(int j) 376 { 377 struct prioq_elt pe; 378 struct stat st; 379 380 if (0 < --jo[j].refs) 381 return; 382 383 pe.id = jo[j].id; 384 pe.dt = jo[j].retry; 385 if (jo[j].flaghiteof && !jo[j].numtodo) { 386 fnmake_chanaddr(jo[j].id, jo[j].channel, &fn); 387 if (unlink(fn.s) == -1) { 388 log3("warning: unable to unlink ", fn.s, "; will try again later\n"); 389 pe.dt = now() + SLEEP_SYSFAIL; 390 } else { 391 int c; 392 for (c = 0; c < CHANNELS; ++c) 393 if (c != jo[j].channel) { 394 fnmake_chanaddr(jo[j].id, c, &fn); 395 if (stat(fn.s, &st) == 0) 396 return; /* more channels going */ 397 if (errno != error_noent) { 398 log3("warning: unable to stat ", fn.s, "\n"); 399 break; /* this is the only 400 * reason for HOPEFULLY */ 401 } 402 } 403 pe.dt = now(); 404 while (!prioq_insert(&pqdone, &pe)) 405 nomem(); 406 return; 407 } 408 } 409 410 while (!prioq_insert(&pqchan[jo[j].channel], &pe)) 411 nomem(); 412 } 413 414 415 /* this file is too long ------------------------------------------- BOUNCES */ 416 417 char * 418 stripvdomprepend(char *recip) 419 { 420 unsigned int i; 421 char *domain; 422 unsigned int domainlen; 423 char *prepend; 424 425 i = str_rchr(recip, '@'); 426 if (!recip[i]) 427 return recip; 428 domain = recip + i + 1; 429 domainlen = str_len(domain); 430 431 for (i = 0; i <= domainlen; ++i) 432 if ((i == 0) || (i == domainlen) || (domain[i] == '.')) 433 if ((prepend = constmap(&mapvdoms, domain + i, domainlen - i))) { 434 if (!*prepend) 435 break; 436 i = str_len(prepend); 437 if (str_diffn(recip, prepend, i)) 438 break; 439 if (recip[i] != '-') 440 break; 441 return recip + i + 1; 442 } 443 return recip; 444 } 445 446 stralloc bouncetext = {0}; 447 448 void 449 addbounce(unsigned long id, char *recip, char *report) 450 { 451 int fd; 452 unsigned int pos; 453 int w; 454 while (!stralloc_copys(&bouncetext, "<")) 455 nomem(); 456 while (!stralloc_cats(&bouncetext, stripvdomprepend(recip))) 457 nomem(); 458 for (pos = 0; pos < bouncetext.len; ++pos) 459 if (bouncetext.s[pos] == '\n') 460 bouncetext.s[pos] = '_'; 461 while (!stralloc_cats(&bouncetext, ">:\n")) 462 nomem(); 463 while (!stralloc_cats(&bouncetext, report)) 464 nomem(); 465 if (report[0]) 466 if (report[str_len(report) - 1] != '\n') 467 while (!stralloc_cats(&bouncetext, "\n")) 468 nomem(); 469 for (pos = bouncetext.len - 2; pos > 0; --pos) 470 if (bouncetext.s[pos] == '\n') 471 if (bouncetext.s[pos - 1] == '\n') 472 bouncetext.s[pos] = '/'; 473 while (!stralloc_cats(&bouncetext, "\n")) 474 nomem(); 475 fnmake_bounce(id, &fn2); 476 for (;;) { 477 fd = open_append(fn2.s); 478 if (fd != -1) 479 break; 480 log1("alert: unable to append to bounce message; HELP! sleeping...\n"); 481 sleep(10); 482 } 483 pos = 0; 484 while (pos < bouncetext.len) { 485 w = write(fd, bouncetext.s + pos, bouncetext.len - pos); 486 if (w == 0 || w == -1) { 487 log1("alert: unable to append to bounce message; HELP! sleeping...\n"); 488 sleep(10); 489 } else 490 pos += w; 491 } 492 close(fd); 493 } 494 495 int 496 injectbounce(unsigned long id) 497 { 498 struct qmail qqt; 499 struct stat st; 500 char *bouncesender; 501 char *bouncerecip; 502 int r; 503 int fd; 504 substdio ssread; 505 char buf[128]; 506 char inbuf[128]; 507 static stralloc sender = { 508 0 509 }; 510 static stralloc quoted = { 511 0 512 }; 513 datetime_sec birth; 514 unsigned long qp; 515 516 if (!getinfo(&sender, &birth, id)) 517 return 0; /* XXX: print warning */ 518 519 /* owner-@host-@[] -> owner-@host */ 520 if (sender.len >= 5) 521 if (str_equal(sender.s + sender.len - 5, "-@[]")) { 522 sender.len -= 4; 523 sender.s[sender.len - 1] = 0; 524 } 525 526 fnmake_bounce(id, &fn2); 527 fnmake_mess(id, &fn); 528 if (stat(fn2.s, &st) == -1) { 529 if (errno == error_noent) 530 return 1; 531 log3("warning: unable to stat ", fn2.s, "\n"); 532 return 0; 533 } 534 if (str_equal(sender.s, "#@[]")) 535 log3("triple bounce: discarding ", fn2.s, "\n"); 536 else { 537 if (qmail_open(&qqt) == -1) { 538 log1("warning: unable to start qmail-queue, will try later\n"); 539 return 0; 540 } 541 qp = qmail_qp(&qqt); 542 543 if (*sender.s) { 544 bouncesender = ""; 545 bouncerecip = sender.s; 546 } else { 547 bouncesender = "#@[]"; 548 bouncerecip = doublebounceto.s; 549 } 550 551 while (!newfield_datemake(now())) 552 nomem(); 553 qmail_put(&qqt, newfield_date.s, newfield_date.len); 554 qmail_puts(&qqt, "From: "); 555 while (!quote("ed, &bouncefrom)) 556 nomem(); 557 qmail_put(&qqt, quoted.s, quoted.len); 558 qmail_puts(&qqt, "@"); 559 qmail_put(&qqt, bouncehost.s, bouncehost.len); 560 qmail_puts(&qqt, "\nTo: "); 561 while (!quote2("ed, bouncerecip)) 562 nomem(); 563 /* MAYBE TODO: import Manvendra's MIME bounce handling code? */ 564 qmail_put(&qqt, quoted.s, quoted.len); 565 qmail_puts(&qqt, "\n\ 566 Subject: Failure Notice\n\ 567 \n\ 568 Hi. This is the qmail-send (NightmareMail queue manager) program at "); 569 qmail_put(&qqt, bouncehost.s, bouncehost.len); 570 qmail_puts(&qqt, *sender.s ? ".\n\ 571 I'm afraid I wasn't able to deliver your message to the following addresses.\n\ 572 This is a permanent error. I've given up. Sorry it didn't work out.\n\ 573 For further assistance, mail the postmaster.\n\ 574 \n\ 575 " : ".\n\ 576 I tried to deliver a bounce message to this address, but the bounce bounced!\n\ 577 \n\ 578 "); 579 580 fd = open_read(fn2.s); 581 if (fd == -1) 582 qmail_fail(&qqt); 583 else { 584 substdio_fdbuf(&ssread, read, fd, inbuf, sizeof(inbuf)); 585 while ((r = substdio_get(&ssread, buf, sizeof(buf))) > 0) 586 qmail_put(&qqt, buf, r); 587 close(fd); 588 if (r == -1) 589 qmail_fail(&qqt); 590 } 591 592 qmail_puts(&qqt, *sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n"); 593 qmail_puts(&qqt, "Return-Path: <"); 594 while (!quote2("ed, sender.s)) 595 nomem(); 596 qmail_put(&qqt, quoted.s, quoted.len); 597 qmail_puts(&qqt, ">\n"); 598 599 fd = open_read(fn.s); 600 if (fd == -1) 601 qmail_fail(&qqt); 602 else { 603 substdio_fdbuf(&ssread, read, fd, inbuf, sizeof(inbuf)); 604 while ((r = substdio_get(&ssread, buf, sizeof(buf))) > 0) 605 qmail_put(&qqt, buf, r); 606 close(fd); 607 if (r == -1) 608 qmail_fail(&qqt); 609 } 610 611 qmail_from(&qqt, bouncesender); 612 qmail_to(&qqt, bouncerecip); 613 if (*qmail_close(&qqt)) { 614 log1("warning: trouble injecting bounce message, will try later\n"); 615 return 0; 616 } 617 618 strnum2[fmt_ulong(strnum2, id)] = 0; 619 qslog2("bounce msg ", strnum2); 620 strnum2[fmt_ulong(strnum2, qp)] = 0; 621 log3(" qp ", strnum2, "\n"); 622 } 623 if (unlink(fn2.s) == -1) { 624 log3("warning: unable to unlink ", fn2.s, "\n"); 625 return 0; 626 } 627 return 1; 628 } 629 630 /* this file is too long ---------------------------------------------- MAIN */ 631 632 int 633 getcontrols() 634 { 635 if (control_init() == -1) 636 return 0; 637 if (control_readint(&lifetime, "control/queuelifetime") == -1) 638 return 0; 639 if (control_readint(&concurrency[0], "control/concurrencylocal") == -1) 640 return 0; 641 if (control_readint(&concurrency[1], "control/concurrencyremote") == -1) 642 return 0; 643 if (control_rldef(&envnoathost, "control/envnoathost", 1, "envnoathost") != 1) 644 return 0; 645 if (control_rldef(&bouncefrom, "control/bouncefrom", 0, "MAILER-DAEMON") != 1) 646 return 0; 647 if (control_rldef(&bouncehost, "control/bouncehost", 1, "bouncehost") != 1) 648 return 0; 649 if (control_rldef(&doublebouncehost, "control/doublebouncehost", 1, "doublebouncehost") != 1) 650 return 0; 651 if (control_rldef(&doublebounceto, "control/doublebounceto", 0, "postmaster") != 1) 652 return 0; 653 if (!stralloc_cats(&doublebounceto, "@")) 654 return 0; 655 if (!stralloc_cat(&doublebounceto, &doublebouncehost)) 656 return 0; 657 if (!stralloc_0(&doublebounceto)) 658 return 0; 659 if (control_readfile(&locals, "control/locals", 1) != 1) 660 return 0; 661 if (!constmap_init(&maplocals, locals.s, locals.len, 0)) 662 return 0; 663 switch (control_readfile(&percenthack, "control/percenthack", 0)) { 664 case -1: 665 return 0; 666 case 0: 667 if (!constmap_init(&mappercenthack, "", 0, 0)) 668 return 0; 669 break; 670 case 1: 671 if (!constmap_init(&mappercenthack, percenthack.s, percenthack.len, 0)) 672 return 0; 673 break; 674 } 675 switch (control_readfile(&vdoms, "control/virtualdomains", 0)) { 676 case -1: 677 return 0; 678 case 0: 679 if (!constmap_init(&mapvdoms, "", 0, 1)) 680 return 0; 681 break; 682 case 1: 683 if (!constmap_init(&mapvdoms, vdoms.s, vdoms.len, 1)) 684 return 0; 685 break; 686 } 687 return 1; 688 } 689 690 stralloc newlocals = {0}; 691 stralloc newvdoms = {0}; 692 693 void 694 regetcontrols() 695 { 696 int r; 697 698 if (control_readfile(&newlocals, "control/locals", 1) != 1) { 699 log1("alert: unable to reread control/locals\n"); 700 return; 701 } 702 r = control_readfile(&newvdoms, "control/virtualdomains", 0); 703 if (r == -1) { 704 log1("alert: unable to reread control/virtualdomains\n"); 705 return; 706 } 707 708 constmap_free(&maplocals); 709 constmap_free(&mapvdoms); 710 711 while (!stralloc_copy(&locals, &newlocals)) 712 nomem(); 713 while (!constmap_init(&maplocals, locals.s, locals.len, 0)) 714 nomem(); 715 716 if (r) { 717 while (!stralloc_copy(&vdoms, &newvdoms)) 718 nomem(); 719 while (!constmap_init(&mapvdoms, vdoms.s, vdoms.len, 1)) 720 nomem(); 721 } else 722 while (!constmap_init(&mapvdoms, "", 0, 1)) 723 nomem(); 724 } 725 726 void 727 reread() 728 { 729 if (chdir(auto_qmail) == -1) { 730 log1("alert: unable to reread controls: unable to switch to home directory\n"); 731 return; 732 } 733 regetcontrols(); 734 while (chdir("queue") == -1) { 735 log1("alert: unable to switch back to queue directory; HELP! sleeping...\n"); 736 sleep(10); 737 } 738 } 739 740 int 741 main(void) 742 { 743 int fd; 744 datetime_sec wakeup; 745 fd_set rfds; 746 fd_set wfds; 747 int nfds; 748 struct timeval tv; 749 int c; 750 751 /* Bit of a brainage here I guess: 752 * for use under qmail-start-np, should we accept, on our argv, paths to 753 * our channelspawns' inputs and outputs? 754 */ 755 if (chdir(auto_qmail) == -1) { 756 log1("alert: cannot start: unable to switch to home directory\n"); 757 _exit(111); 758 } 759 if (!getcontrols()) { 760 log1("alert: cannot start: unable to read controls\n"); 761 _exit(111); 762 } 763 if (chdir("queue") == -1) { 764 log1("alert: cannot start: unable to switch to queue directory\n"); 765 _exit(111); 766 } 767 sig_pipeignore(); 768 sig_termcatch(sigterm); 769 sig_alarmcatch(sigalrm); 770 sig_hangupcatch(sighup); 771 sig_childdefault(); 772 umask(077); 773 774 fd = open_write("lock/sendmutex"); 775 if (fd == -1) { 776 log1("alert: cannot start: unable to open mutex\n"); 777 _exit(111); 778 } 779 if (lock_exnb(fd) == -1) { 780 log1("alert: cannot start: qmail-send is already running\n"); 781 _exit(111); 782 } 783 784 numjobs = 0; 785 for (c = 0; c < CHANNELS; ++c) { 786 char ch; 787 int u; 788 int r; 789 do 790 r = read(chanfdin[c], &ch, 1); 791 while ((r == -1) && (errno == error_intr)); 792 if (r < 1) { 793 log1("alert: cannot start: hath the daemon spawn no fire? one of my delivery channels didn't start correctly.\n"); 794 _exit(111); 795 } 796 u = (unsigned int)(unsigned char)ch; 797 if (concurrency[c] > u) 798 concurrency[c] = u; 799 numjobs += concurrency[c]; 800 } 801 802 fnmake_init(); 803 804 comm_init(); 805 806 pqstart(); 807 job_init(); 808 del_init(); 809 pass_init(); 810 todo_init(); 811 cleanup_init(); 812 813 while (!flagexitasap || !del_canexit()) { 814 recent = now(); 815 816 if (flagrunasap) { 817 flagrunasap = 0; 818 pqrun(); 819 } 820 if (flagreadasap) { 821 flagreadasap = 0; 822 reread(); 823 } 824 825 wakeup = recent + SLEEP_FOREVER; 826 FD_ZERO(&rfds); 827 FD_ZERO(&wfds); 828 nfds = 1; 829 830 comm_selprep(&nfds, &wfds); /* this isn't arcane at all. /s */ 831 del_selprep(&nfds, &rfds); 832 pass_selprep(&wakeup); 833 todo_selprep(&nfds, &rfds, &wakeup); 834 cleanup_selprep(&wakeup); 835 /* Lightning brainage: 836 * So here's what all that actually does. 837 * It prepares the writability for communication queues. 838 * It prepares the readability for delivery report FDs. 839 * It adjusts the earliest time the loop will wake up for the passes. 840 * It adjusts wakeup and prepares readability for the todo. 841 * and it adjusts wakeup for cleanup. 842 */ 843 844 if (wakeup <= recent) 845 tv.tv_sec = 0; 846 else 847 tv.tv_sec = wakeup - recent + SLEEP_FUZZ; 848 tv.tv_usec = 0; 849 850 if (select(nfds, &rfds, &wfds, NULL, &tv) == -1) 851 if (errno == error_intr) 852 ; 853 else 854 log1("warning: trouble in select\n"); 855 else { 856 recent = now(); 857 858 //XXX: should have a queue structure 859 comm_do(&wfds); 860 del_do(&rfds); 861 todo_do(&rfds); 862 pass_do(); 863 cleanup_do(); 864 } 865 } 866 pqfinish(); 867 log1("status: exiting\n"); 868 _exit(0); 869 };