mgr.c (18765B)
1 /* 2 * mirror manager. 3 * a work in progress. 4 * use at your own risk. 5 */ 6 7 #include "stdinc.h" 8 #include <regexp.h> 9 #include <bio.h> 10 #include "dat.h" 11 #include "fns.h" 12 13 #ifdef PLAN9PORT 14 #define sp s.sp 15 #define ep e.ep 16 #endif 17 18 void sendmail(char *content, char *subject, char *msg); 19 #define TIME "[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+" 20 21 char *mirrorregexp = 22 "^" TIME " (" 23 "([^ ]+ \\([0-9,]+-[0-9,]+\\))" 24 "|( copy [0-9,]+-[0-9,]+ (data|hole|directory|tail))" 25 "|( sha1 [0-9,]+-[0-9,]+)" 26 "|([^ ]+: [0-9,]+ used mirrored)" 27 "|([^ \\-]+-[^ \\-]+( mirrored| sealed| empty)+)" 28 ")$"; 29 Reprog *mirrorprog; 30 31 char *verifyregexp = 32 "^" TIME " (" 33 "([^ ]+: unsealed [0-9,]+ bytes)" 34 ")$"; 35 Reprog *verifyprog; 36 37 #undef pipe 38 enum 39 { 40 LogSize = 4*1024*1024 // TODO: make smaller 41 }; 42 43 VtLog *errlog; 44 45 typedef struct Mirror Mirror; 46 struct Mirror 47 { 48 char *src; 49 char *dst; 50 }; 51 52 typedef struct Conf Conf; 53 struct Conf 54 { 55 Mirror *mirror; 56 int nmirror; 57 char **verify; 58 int nverify; 59 char *httpaddr; 60 char *webroot; 61 char *smtp; 62 char *mailfrom; 63 char *mailto; 64 int mirrorfreq; 65 int verifyfreq; 66 }; 67 68 typedef struct Job Job; 69 struct Job 70 { 71 char *name; 72 QLock lk; 73 char *argv[10]; 74 int oldok; 75 int newok; 76 VtLog *oldlog; 77 VtLog *newlog; 78 int pid; 79 int pipe; 80 int nrun; 81 vlong freq; 82 vlong runstart; 83 vlong runend; 84 double offset; 85 int (*ok)(char*); 86 }; 87 88 Job *job; 89 int njob; 90 char *bin; 91 92 vlong time0; 93 Conf conf; 94 95 void 96 usage(void) 97 { 98 fprint(2, "usage: mgr [-s] [-b bin/venti/] venti.conf\n"); 99 threadexitsall(0); 100 } 101 102 int 103 rdconf(char *file, Conf *conf) 104 { 105 char *s, *line, *flds[10]; 106 int i, ok; 107 IFile f; 108 109 if(readifile(&f, file) < 0) 110 return -1; 111 memset(conf, 0, sizeof *conf); 112 ok = -1; 113 line = nil; 114 for(;;){ 115 s = ifileline(&f); 116 if(s == nil){ 117 ok = 0; 118 break; 119 } 120 line = estrdup(s); 121 i = getfields(s, flds, nelem(flds), 1, " \t\r"); 122 if(i <= 0 || strcmp(flds[0], "mgr") != 0) { 123 /* do nothing */ 124 }else if(i == 4 && strcmp(flds[1], "mirror") == 0) { 125 if(conf->nmirror%64 == 0) 126 conf->mirror = vtrealloc(conf->mirror, (conf->nmirror+64)*sizeof(conf->mirror[0])); 127 conf->mirror[conf->nmirror].src = vtstrdup(flds[2]); 128 conf->mirror[conf->nmirror].dst = vtstrdup(flds[3]); 129 conf->nmirror++; 130 }else if(i == 3 && strcmp(flds[1], "mirrorfreq") == 0) { 131 conf->mirrorfreq = atoi(flds[2]); 132 }else if(i == 3 && strcmp(flds[1], "verify") == 0) { 133 if(conf->nverify%64 == 0) 134 conf->verify = vtrealloc(conf->verify, (conf->nverify+64)*sizeof(conf->verify[0])); 135 conf->verify[conf->nverify++] = vtstrdup(flds[2]); 136 }else if(i == 3 && strcmp(flds[1], "verifyfreq") == 0) { 137 conf->verifyfreq = atoi(flds[2]); 138 }else if(i == 3 && strcmp(flds[1], "httpaddr") == 0){ 139 if(conf->httpaddr){ 140 seterr(EAdmin, "duplicate httpaddr lines in configuration file %s", file); 141 break; 142 } 143 conf->httpaddr = estrdup(flds[2]); 144 }else if(i == 3 && strcmp(flds[1], "webroot") == 0){ 145 if(conf->webroot){ 146 seterr(EAdmin, "duplicate webroot lines in configuration file %s", file); 147 break; 148 } 149 conf->webroot = estrdup(flds[2]); 150 }else if(i == 3 && strcmp(flds[1], "smtp") == 0) { 151 if(conf->smtp){ 152 seterr(EAdmin, "duplicate smtp lines in configuration file %s", file); 153 break; 154 } 155 conf->smtp = estrdup(flds[2]); 156 }else if(i == 3 && strcmp(flds[1], "mailfrom") == 0) { 157 if(conf->mailfrom){ 158 seterr(EAdmin, "duplicate mailfrom lines in configuration file %s", file); 159 break; 160 } 161 conf->mailfrom = estrdup(flds[2]); 162 }else if(i == 3 && strcmp(flds[1], "mailto") == 0) { 163 if(conf->mailto){ 164 seterr(EAdmin, "duplicate mailto lines in configuration file %s", file); 165 break; 166 } 167 conf->mailto = estrdup(flds[2]); 168 }else{ 169 seterr(EAdmin, "illegal line '%s' in configuration file %s", line, file); 170 break; 171 } 172 free(line); 173 line = nil; 174 } 175 free(line); 176 freeifile(&f); 177 return ok; 178 } 179 180 static QLock loglk; 181 static char *logbuf; 182 183 char* 184 logtext(VtLog *l) 185 { 186 int i; 187 char *p; 188 VtLogChunk *c; 189 190 p = logbuf; 191 c = l->w; 192 for(i=0; i<l->nchunk; i++) { 193 if(++c == l->chunk+l->nchunk) 194 c = l->chunk; 195 memmove(p, c->p, c->wp - c->p); 196 p += c->wp - c->p; 197 } 198 *p = 0; 199 return logbuf; 200 } 201 202 203 typedef struct HttpObj HttpObj; 204 205 static int fromwebdir(HConnect*); 206 207 enum 208 { 209 ObjNameSize = 64, 210 MaxObjs = 64 211 }; 212 213 struct HttpObj 214 { 215 char name[ObjNameSize]; 216 int (*f)(HConnect*); 217 }; 218 219 static HttpObj objs[MaxObjs]; 220 static void httpproc(void*); 221 222 static HConnect* 223 mkconnect(void) 224 { 225 HConnect *c; 226 227 c = mallocz(sizeof(HConnect), 1); 228 if(c == nil) 229 sysfatal("out of memory"); 230 c->replog = nil; 231 c->hpos = c->header; 232 c->hstop = c->header; 233 return c; 234 } 235 236 static int 237 preq(HConnect *c) 238 { 239 if(hparseheaders(c, 0) < 0) 240 return -1; 241 if(strcmp(c->req.meth, "GET") != 0 242 && strcmp(c->req.meth, "HEAD") != 0) 243 return hunallowed(c, "GET, HEAD"); 244 if(c->head.expectother || c->head.expectcont) 245 return hfail(c, HExpectFail, nil); 246 return 0; 247 } 248 249 int 250 hsettype(HConnect *c, char *type) 251 { 252 Hio *hout; 253 int r; 254 255 r = preq(c); 256 if(r < 0) 257 return r; 258 259 hout = &c->hout; 260 if(c->req.vermaj){ 261 hokheaders(c); 262 hprint(hout, "Content-type: %s\r\n", type); 263 if(http11(c)) 264 hprint(hout, "Transfer-Encoding: chunked\r\n"); 265 hprint(hout, "\r\n"); 266 } 267 268 if(http11(c)) 269 hxferenc(hout, 1); 270 else 271 c->head.closeit = 1; 272 return 0; 273 } 274 275 int 276 hsethtml(HConnect *c) 277 { 278 return hsettype(c, "text/html; charset=utf-8"); 279 } 280 281 int 282 hsettext(HConnect *c) 283 { 284 return hsettype(c, "text/plain; charset=utf-8"); 285 } 286 287 int 288 hnotfound(HConnect *c) 289 { 290 int r; 291 292 r = preq(c); 293 if(r < 0) 294 return r; 295 return hfail(c, HNotFound, c->req.uri); 296 } 297 298 static int 299 xloglist(HConnect *c) 300 { 301 if(hsettype(c, "text/html") < 0) 302 return -1; 303 vtloghlist(&c->hout); 304 hflush(&c->hout); 305 return 0; 306 } 307 308 static int 309 strpcmp(const void *va, const void *vb) 310 { 311 return strcmp(*(char**)va, *(char**)vb); 312 } 313 314 void 315 vtloghlist(Hio *h) 316 { 317 char **p; 318 int i, n; 319 320 hprint(h, "<html><head>\n"); 321 hprint(h, "<title>Venti Server Logs</title>\n"); 322 hprint(h, "</head><body>\n"); 323 hprint(h, "<b>Venti Server Logs</b>\n<p>\n"); 324 325 p = vtlognames(&n); 326 qsort(p, n, sizeof(p[0]), strpcmp); 327 for(i=0; i<n; i++) 328 hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]); 329 vtfree(p); 330 hprint(h, "</body></html>\n"); 331 } 332 333 void 334 vtloghdump(Hio *h, VtLog *l) 335 { 336 int i; 337 VtLogChunk *c; 338 char *name; 339 340 name = l ? l->name : "<nil>"; 341 342 hprint(h, "<html><head>\n"); 343 hprint(h, "<title>Venti Server Log: %s</title>\n", name); 344 hprint(h, "</head><body>\n"); 345 hprint(h, "<b>Venti Server Log: %s</b>\n<p>\n", name); 346 347 if(l){ 348 c = l->w; 349 for(i=0; i<l->nchunk; i++){ 350 if(++c == l->chunk+l->nchunk) 351 c = l->chunk; 352 hwrite(h, c->p, c->wp-c->p); 353 } 354 } 355 hprint(h, "</body></html>\n"); 356 } 357 358 359 char* 360 hargstr(HConnect *c, char *name, char *def) 361 { 362 HSPairs *p; 363 364 for(p=c->req.searchpairs; p; p=p->next) 365 if(strcmp(p->s, name) == 0) 366 return p->t; 367 return def; 368 } 369 370 static int 371 xlog(HConnect *c) 372 { 373 char *name; 374 VtLog *l; 375 376 name = hargstr(c, "log", ""); 377 if(!name[0]) 378 return xloglist(c); 379 l = vtlogopen(name, 0); 380 if(l == nil) 381 return hnotfound(c); 382 if(hsettype(c, "text/html") < 0){ 383 vtlogclose(l); 384 return -1; 385 } 386 vtloghdump(&c->hout, l); 387 vtlogclose(l); 388 hflush(&c->hout); 389 return 0; 390 } 391 392 static void 393 httpdproc(void *vaddress) 394 { 395 HConnect *c; 396 char *address, ndir[NETPATHLEN], dir[NETPATHLEN]; 397 int ctl, nctl, data; 398 399 address = vaddress; 400 ctl = announce(address, dir); 401 if(ctl < 0){ 402 sysfatal("announce %s: %r", address); 403 return; 404 } 405 406 if(0) print("announce ctl %d dir %s\n", ctl, dir); 407 for(;;){ 408 /* 409 * wait for a call (or an error) 410 */ 411 nctl = listen(dir, ndir); 412 if(0) print("httpd listen %d %s...\n", nctl, ndir); 413 if(nctl < 0){ 414 fprint(2, "mgr: httpd can't listen on %s: %r\n", address); 415 return; 416 } 417 418 data = accept(ctl, ndir); 419 if(0) print("httpd accept %d...\n", data); 420 if(data < 0){ 421 fprint(2, "mgr: httpd accept: %r\n"); 422 close(nctl); 423 continue; 424 } 425 if(0) print("httpd close nctl %d\n", nctl); 426 close(nctl); 427 c = mkconnect(); 428 hinit(&c->hin, data, Hread); 429 hinit(&c->hout, data, Hwrite); 430 vtproc(httpproc, c); 431 } 432 } 433 434 static void 435 httpproc(void *v) 436 { 437 HConnect *c; 438 int ok, i, n; 439 440 c = v; 441 442 for(;;){ 443 /* 444 * No timeout because the signal appears to hit every 445 * proc, not just us. 446 */ 447 if(hparsereq(c, 0) < 0) 448 break; 449 450 for(i = 0; i < MaxObjs && objs[i].name[0]; i++){ 451 n = strlen(objs[i].name); 452 if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0) 453 || (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){ 454 ok = (*objs[i].f)(c); 455 goto found; 456 } 457 } 458 ok = fromwebdir(c); 459 found: 460 hflush(&c->hout); 461 if(c->head.closeit) 462 ok = -1; 463 hreqcleanup(c); 464 465 if(ok < 0) 466 break; 467 } 468 hreqcleanup(c); 469 close(c->hin.fd); 470 free(c); 471 } 472 473 static int 474 httpdobj(char *name, int (*f)(HConnect*)) 475 { 476 int i; 477 478 if(name == nil || strlen(name) >= ObjNameSize) 479 return -1; 480 for(i = 0; i < MaxObjs; i++){ 481 if(objs[i].name[0] == '\0'){ 482 strcpy(objs[i].name, name); 483 objs[i].f = f; 484 return 0; 485 } 486 if(strcmp(objs[i].name, name) == 0) 487 return -1; 488 } 489 return -1; 490 } 491 492 493 struct { 494 char *ext; 495 char *type; 496 } exttab[] = { 497 ".html", "text/html", 498 ".txt", "text/plain", 499 ".xml", "text/xml", 500 ".png", "image/png", 501 ".gif", "image/gif", 502 0 503 }; 504 505 static int 506 fromwebdir(HConnect *c) 507 { 508 char buf[4096], *p, *ext, *type; 509 int i, fd, n, defaulted; 510 Dir *d; 511 512 if(conf.webroot == nil || strstr(c->req.uri, "..")) 513 return hnotfound(c); 514 snprint(buf, sizeof buf-20, "%s/%s", conf.webroot, c->req.uri+1); 515 defaulted = 0; 516 reopen: 517 if((fd = open(buf, OREAD)) < 0) 518 return hnotfound(c); 519 d = dirfstat(fd); 520 if(d == nil){ 521 close(fd); 522 return hnotfound(c); 523 } 524 if(d->mode&DMDIR){ 525 if(!defaulted){ 526 defaulted = 1; 527 strcat(buf, "/index.html"); 528 free(d); 529 close(fd); 530 goto reopen; 531 } 532 free(d); 533 return hnotfound(c); 534 } 535 free(d); 536 p = buf+strlen(buf); 537 type = "application/octet-stream"; 538 for(i=0; exttab[i].ext; i++){ 539 ext = exttab[i].ext; 540 if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){ 541 type = exttab[i].type; 542 break; 543 } 544 } 545 if(hsettype(c, type) < 0){ 546 close(fd); 547 return 0; 548 } 549 while((n = read(fd, buf, sizeof buf)) > 0) 550 if(hwrite(&c->hout, buf, n) < 0) 551 break; 552 close(fd); 553 hflush(&c->hout); 554 return 0; 555 } 556 557 static int 558 hmanager(HConnect *c) 559 { 560 Hio *hout; 561 int r; 562 int i, k; 563 Job *j; 564 VtLog *l; 565 VtLogChunk *ch; 566 567 r = hsethtml(c); 568 if(r < 0) 569 return r; 570 571 hout = &c->hout; 572 hprint(hout, "<html><head><title>venti mgr status</title></head>\n"); 573 hprint(hout, "<body><h2>venti mgr status</h2>\n"); 574 575 for(i=0; i<njob; i++) { 576 j = &job[i]; 577 hprint(hout, "<b>"); 578 if(j->nrun == 0) 579 hprint(hout, "----/--/-- --:--:--"); 580 else 581 hprint(hout, "%+T", (long)(j->runstart + time0)); 582 hprint(hout, " %s", j->name); 583 if(j->nrun > 0) { 584 if(j->newok == -1) { 585 hprint(hout, " (running)"); 586 } else if(!j->newok) { 587 hprint(hout, " <font color=\"#cc0000\">(FAILED)</font>"); 588 } 589 } 590 hprint(hout, "</b>\n"); 591 hprint(hout, "<font size=-1><pre>\n"); 592 l = j->newlog; 593 ch = l->w; 594 for(k=0; k<l->nchunk; k++){ 595 if(++ch == l->chunk+l->nchunk) 596 ch = l->chunk; 597 hwrite(hout, ch->p, ch->wp-ch->p); 598 } 599 hprint(hout, "</pre></font>\n"); 600 hprint(hout, "\n"); 601 } 602 hprint(hout, "</body></html>\n"); 603 hflush(hout); 604 return 0; 605 } 606 607 void 608 piper(void *v) 609 { 610 Job *j; 611 char buf[512]; 612 VtLog *l; 613 int n; 614 int fd; 615 char *p; 616 int ok; 617 618 j = v; 619 fd = j->pipe; 620 l = j->newlog; 621 while((n = read(fd, buf, 512-1)) > 0) { 622 buf[n] = 0; 623 if(l != nil) 624 vtlogprint(l, "%s", buf); 625 } 626 qlock(&loglk); 627 p = logtext(l); 628 ok = j->ok(p); 629 qunlock(&loglk); 630 j->newok = ok; 631 close(fd); 632 } 633 634 void 635 kickjob(Job *j) 636 { 637 int i; 638 int fd[3]; 639 int p[2]; 640 VtLog *l; 641 642 if((fd[0] = open("/dev/null", ORDWR)) < 0) { 643 vtlogprint(errlog, "%T open /dev/null: %r\n"); 644 return; 645 } 646 if(pipe(p) < 0) { 647 vtlogprint(errlog, "%T pipe: %r\n"); 648 close(fd[0]); 649 return; 650 } 651 qlock(&j->lk); 652 l = j->oldlog; 653 j->oldlog = j->newlog; 654 j->newlog = l; 655 qlock(&l->lk); 656 for(i=0; i<l->nchunk; i++) 657 l->chunk[i].wp = l->chunk[i].p; 658 qunlock(&l->lk); 659 j->oldok = j->newok; 660 j->newok = -1; 661 qunlock(&j->lk); 662 663 fd[1] = p[1]; 664 fd[2] = p[1]; 665 j->pid = threadspawn(fd, j->argv[0], j->argv); 666 if(j->pid < 0) { 667 vtlogprint(errlog, "%T exec %s: %r\n", j->argv[0]); 668 close(fd[0]); 669 close(fd[1]); 670 close(p[0]); 671 } 672 // fd[0], fd[1], fd[2] are closed now 673 j->pipe = p[0]; 674 j->nrun++; 675 vtproc(piper, j); 676 } 677 678 int 679 getline(Resub *text, Resub *line) 680 { 681 char *p; 682 683 if(text->sp >= text->ep) 684 return -1; 685 line->sp = text->sp; 686 p = memchr(text->sp, '\n', text->ep - text->sp); 687 if(p == nil) { 688 line->ep = text->ep; 689 text->sp = text->ep; 690 } else { 691 line->ep = p; 692 text->sp = p+1; 693 } 694 return 0; 695 } 696 697 int 698 verifyok(char *output) 699 { 700 Resub text, line, m; 701 702 text.sp = output; 703 text.ep = output+strlen(output); 704 while(getline(&text, &line) >= 0) { 705 *line.ep = 0; 706 memset(&m, 0, sizeof m); 707 if(!regexec(verifyprog, line.sp, nil, 0)) 708 return 0; 709 *line.ep = '\n'; 710 } 711 return 1; 712 } 713 714 int 715 mirrorok(char *output) 716 { 717 Resub text, line, m; 718 719 text.sp = output; 720 text.ep = output+strlen(output); 721 while(getline(&text, &line) >= 0) { 722 *line.ep = 0; 723 memset(&m, 0, sizeof m); 724 if(!regexec(mirrorprog, line.sp, nil, 0)) 725 return 0; 726 *line.ep = '\n'; 727 } 728 return 1; 729 } 730 731 void 732 mkjob(Job *j, ...) 733 { 734 int i; 735 char *p; 736 va_list arg; 737 738 memset(j, 0, sizeof *j); 739 i = 0; 740 va_start(arg, j); 741 while((p = va_arg(arg, char*)) != nil) { 742 j->argv[i++] = p; 743 if(i >= nelem(j->argv)) 744 sysfatal("job argv size too small"); 745 } 746 j->argv[i] = nil; 747 j->oldlog = vtlogopen(smprint("log%ld.0", j-job), LogSize); 748 j->newlog = vtlogopen(smprint("log%ld.1", j-job), LogSize); 749 va_end(arg); 750 } 751 752 void 753 manager(void *v) 754 { 755 int i; 756 Job *j; 757 vlong now; 758 759 USED(v); 760 for(;; sleep(1000)) { 761 for(i=0; i<njob; i++) { 762 now = time(0) - time0; 763 j = &job[i]; 764 if(j->pid > 0 || j->newok == -1) { 765 // still running 766 if(now - j->runstart > 2*j->freq) { 767 //TODO: log slow running j 768 } 769 continue; 770 } 771 if((j->nrun > 0 && now - j->runend > j->freq) 772 || (j->nrun == 0 && now > (vlong)(j->offset*j->freq))) { 773 j->runstart = now; 774 j->runend = 0; 775 kickjob(j); 776 } 777 } 778 } 779 } 780 781 void 782 waitproc(void *v) 783 { 784 Channel *c; 785 Waitmsg *w; 786 int i; 787 Job *j; 788 789 c = v; 790 for(;;) { 791 w = recvp(c); 792 for(i=0; i<njob; i++) { 793 j = &job[i]; 794 if(j->pid == w->pid) { 795 j->pid = 0; 796 j->runend = time(0) - time0; 797 break; 798 } 799 } 800 free(w); 801 } 802 } 803 804 void 805 threadmain(int argc, char **argv) 806 { 807 int i; 808 int nofork; 809 char *prog; 810 Job *j; 811 812 ventilogging = 1; 813 ventifmtinstall(); 814 #ifdef PLAN9PORT 815 bin = unsharp("#9/bin/venti"); 816 #else 817 bin = "/bin/venti"; 818 #endif 819 nofork = 0; 820 ARGBEGIN{ 821 case 'b': 822 bin = EARGF(usage()); 823 break; 824 case 's': 825 nofork = 1; 826 break; 827 default: 828 usage(); 829 }ARGEND 830 831 if(argc != 1) 832 usage(); 833 if(rdconf(argv[0], &conf) < 0) 834 sysfatal("reading config: %r"); 835 if(conf.httpaddr == nil) 836 sysfatal("config has no httpaddr"); 837 if(conf.smtp != nil && conf.mailfrom == nil) 838 sysfatal("config has smtp but no mailfrom"); 839 if(conf.smtp != nil && conf.mailto == nil) 840 sysfatal("config has smtp but no mailto"); 841 if((mirrorprog = regcomp(mirrorregexp)) == nil) 842 sysfatal("mirrorregexp did not complete"); 843 if((verifyprog = regcomp(verifyregexp)) == nil) 844 sysfatal("verifyregexp did not complete"); 845 if(conf.nverify > 0 && conf.verifyfreq == 0) 846 sysfatal("config has no verifyfreq"); 847 if(conf.nmirror > 0 && conf.mirrorfreq == 0) 848 sysfatal("config has no mirrorfreq"); 849 850 time0 = time(0); 851 // sendmail("startup", "mgr is starting\n"); 852 853 logbuf = vtmalloc(LogSize+1); // +1 for NUL 854 855 errlog = vtlogopen("errors", LogSize); 856 job = vtmalloc((conf.nmirror+conf.nverify)*sizeof job[0]); 857 prog = smprint("%s/mirrorarenas", bin); 858 for(i=0; i<conf.nmirror; i++) { 859 // job: /bin/venti/mirrorarenas -v src dst 860 // filter output 861 j = &job[njob++]; 862 mkjob(j, prog, "-v", conf.mirror[i].src, conf.mirror[i].dst, nil); 863 j->name = smprint("mirror %s %s", conf.mirror[i].src, conf.mirror[i].dst); 864 j->ok = mirrorok; 865 j->freq = conf.mirrorfreq; // 4 hours // TODO: put in config 866 j->offset = (double)i/conf.nmirror; 867 } 868 869 prog = smprint("%s/verifyarena", bin); 870 for(i=0; i<conf.nverify; i++) { 871 // job: /bin/venti/verifyarena -b 64M -s 1000 -v arena 872 // filter output 873 j = &job[njob++]; 874 mkjob(j, prog, "-b64M", "-s1000", conf.verify[i], nil); 875 j->name = smprint("verify %s", conf.verify[i]); 876 j->ok = verifyok; 877 j->freq = conf.verifyfreq; 878 j->offset = (double)i/conf.nverify; 879 } 880 881 httpdobj("/mgr", hmanager); 882 httpdobj("/log", xlog); 883 vtproc(httpdproc, conf.httpaddr); 884 vtproc(waitproc, threadwaitchan()); 885 if(nofork) 886 manager(nil); 887 else 888 vtproc(manager, nil); 889 } 890 891 892 void 893 qp(Biobuf *b, char *p) 894 { 895 int n, nspace; 896 897 nspace = 0; 898 n = 0; 899 for(; *p; p++) { 900 if(*p == '\n') { 901 if(nspace > 0) { 902 nspace = 0; 903 Bprint(b, "=\n"); 904 } 905 Bputc(b, '\n'); 906 n = 0; 907 continue; 908 } 909 if(n > 70) { 910 Bprint(b, "=\n"); 911 nspace = 0; 912 continue; 913 } 914 if(33 <= *p && *p <= 126 && *p != '=') { 915 Bputc(b, *p); 916 n++; 917 nspace = 0; 918 continue; 919 } 920 if(*p == ' ' || *p == '\t') { 921 Bputc(b, *p); 922 n++; 923 nspace++; 924 continue; 925 } 926 Bprint(b, "=%02X", (uchar)*p); 927 n += 3; 928 nspace = 0; 929 } 930 } 931 932 int 933 smtpread(Biobuf *b, int code) 934 { 935 char *p, *q; 936 int n; 937 938 while((p = Brdstr(b, '\n', 1)) != nil) { 939 n = strtol(p, &q, 10); 940 if(n == 0 || q != p+3) { 941 error: 942 vtlogprint(errlog, "sending mail: %s\n", p); 943 free(p); 944 return -1; 945 } 946 if(*q == ' ') { 947 if(n == code) { 948 free(p); 949 return 0; 950 } 951 goto error; 952 } 953 if(*q != '-') { 954 goto error; 955 } 956 } 957 return -1; 958 } 959 960 961 void 962 sendmail(char *content, char *subject, char *msg) 963 { 964 int fd; 965 Biobuf *bin, *bout; 966 967 if((fd = dial(conf.smtp, 0, 0, 0)) < 0) { 968 vtlogprint(errlog, "dial %s: %r\n", conf.smtp); 969 return; 970 } 971 bin = vtmalloc(sizeof *bin); 972 bout = vtmalloc(sizeof *bout); 973 Binit(bin, fd, OREAD); 974 Binit(bout, fd, OWRITE); 975 if(smtpread(bin, 220) < 0){ 976 error: 977 close(fd); 978 Bterm(bin); 979 Bterm(bout); 980 return; 981 } 982 983 Bprint(bout, "HELO venti-mgr\n"); 984 Bflush(bout); 985 if(smtpread(bin, 250) < 0) 986 goto error; 987 988 Bprint(bout, "MAIL FROM:<%s>\n", conf.mailfrom); 989 Bflush(bout); 990 if(smtpread(bin, 250) < 0) 991 goto error; 992 993 Bprint(bout, "RCPT TO:<%s>\n", conf.mailfrom); 994 Bflush(bout); 995 if(smtpread(bin, 250) < 0) 996 goto error; 997 998 Bprint(bout, "DATA\n"); 999 Bflush(bout); 1000 if(smtpread(bin, 354) < 0) 1001 goto error; 1002 1003 Bprint(bout, "From: \"venti mgr\" <%s>\n", conf.mailfrom); 1004 Bprint(bout, "To: <%s>\n", conf.mailto); 1005 Bprint(bout, "Subject: %s\n", subject); 1006 Bprint(bout, "MIME-Version: 1.0\n"); 1007 Bprint(bout, "Content-Type: %s; charset=\"UTF-8\"\n", content); 1008 Bprint(bout, "Content-Transfer-Encoding: quoted-printable\n"); 1009 Bprint(bout, "Message-ID: %08lux%08lux@venti.swtch.com\n", fastrand(), fastrand()); 1010 Bprint(bout, "\n"); 1011 qp(bout, msg); 1012 Bprint(bout, ".\n"); 1013 Bflush(bout); 1014 if(smtpread(bin, 250) < 0) 1015 goto error; 1016 1017 Bprint(bout, "QUIT\n"); 1018 Bflush(bout); 1019 Bterm(bin); 1020 Bterm(bout); 1021 close(fd); 1022 }