httpd.c (22933B)
1 #include "stdinc.h" 2 #include "dat.h" 3 #include "fns.h" 4 #include "xml.h" 5 6 typedef struct HttpObj HttpObj; 7 extern QLock memdrawlock; 8 9 enum 10 { 11 ObjNameSize = 64, 12 MaxObjs = 64 13 }; 14 15 struct HttpObj 16 { 17 char name[ObjNameSize]; 18 int (*f)(HConnect*); 19 }; 20 21 static HttpObj objs[MaxObjs]; 22 23 static char *webroot; 24 25 static void listenproc(void*); 26 static int estats(HConnect *c); 27 static int dindex(HConnect *c); 28 static int xindex(HConnect *c); 29 static int xlog(HConnect *c); 30 static int sindex(HConnect *c); 31 static int hempty(HConnect *c); 32 static int hlcacheempty(HConnect *c); 33 static int hdcacheempty(HConnect *c); 34 static int hicacheempty(HConnect *c); 35 static int hicachekick(HConnect *c); 36 static int hdcachekick(HConnect *c); 37 static int hicacheflush(HConnect *c); 38 static int hdcacheflush(HConnect *c); 39 static int httpdobj(char *name, int (*f)(HConnect*)); 40 static int xgraph(HConnect *c); 41 static int xset(HConnect *c); 42 static int fromwebdir(HConnect *c); 43 44 int 45 httpdinit(char *address, char *dir) 46 { 47 fmtinstall('D', hdatefmt); 48 /* fmtinstall('H', httpfmt); */ 49 fmtinstall('U', hurlfmt); 50 51 if(address == nil) 52 address = "tcp!*!http"; 53 webroot = dir; 54 55 httpdobj("/stats", estats); 56 httpdobj("/index", dindex); 57 httpdobj("/storage", sindex); 58 httpdobj("/xindex", xindex); 59 httpdobj("/flushicache", hicacheflush); 60 httpdobj("/flushdcache", hdcacheflush); 61 httpdobj("/kickicache", hicachekick); 62 httpdobj("/kickdcache", hdcachekick); 63 httpdobj("/graph", xgraph); 64 httpdobj("/set", xset); 65 httpdobj("/log", xlog); 66 httpdobj("/empty", hempty); 67 httpdobj("/emptyicache", hicacheempty); 68 httpdobj("/emptylumpcache", hlcacheempty); 69 httpdobj("/emptydcache", hdcacheempty); 70 httpdobj("/disk", hdisk); 71 httpdobj("/debug", hdebug); 72 httpdobj("/proc/", hproc); 73 74 if(vtproc(listenproc, address) < 0) 75 return -1; 76 return 0; 77 } 78 79 static int 80 httpdobj(char *name, int (*f)(HConnect*)) 81 { 82 int i; 83 84 if(name == nil || strlen(name) >= ObjNameSize) 85 return -1; 86 for(i = 0; i < MaxObjs; i++){ 87 if(objs[i].name[0] == '\0'){ 88 strcpy(objs[i].name, name); 89 objs[i].f = f; 90 return 0; 91 } 92 if(strcmp(objs[i].name, name) == 0) 93 return -1; 94 } 95 return -1; 96 } 97 98 static HConnect* 99 mkconnect(void) 100 { 101 HConnect *c; 102 103 c = mallocz(sizeof(HConnect), 1); 104 if(c == nil) 105 sysfatal("out of memory"); 106 c->replog = nil; 107 c->hpos = c->header; 108 c->hstop = c->header; 109 return c; 110 } 111 112 void httpproc(void*); 113 114 static void 115 listenproc(void *vaddress) 116 { 117 HConnect *c; 118 char *address, ndir[NETPATHLEN], dir[NETPATHLEN]; 119 int ctl, nctl, data; 120 121 address = vaddress; 122 ctl = announce(address, dir); 123 if(ctl < 0){ 124 fprint(2, "venti: httpd can't announce on %s: %r\n", address); 125 return; 126 } 127 128 if(0) print("announce ctl %d dir %s\n", ctl, dir); 129 for(;;){ 130 /* 131 * wait for a call (or an error) 132 */ 133 nctl = listen(dir, ndir); 134 if(0) print("httpd listen %d %s...\n", nctl, ndir); 135 if(nctl < 0){ 136 fprint(2, "venti: httpd can't listen on %s: %r\n", address); 137 return; 138 } 139 140 data = accept(ctl, ndir); 141 if(0) print("httpd accept %d...\n", data); 142 if(data < 0){ 143 fprint(2, "venti: httpd accept: %r\n"); 144 close(nctl); 145 continue; 146 } 147 if(0) print("httpd close nctl %d\n", nctl); 148 close(nctl); 149 c = mkconnect(); 150 hinit(&c->hin, data, Hread); 151 hinit(&c->hout, data, Hwrite); 152 vtproc(httpproc, c); 153 } 154 } 155 156 void 157 httpproc(void *v) 158 { 159 HConnect *c; 160 int ok, i, n; 161 162 c = v; 163 164 for(;;){ 165 /* 166 * No timeout because the signal appears to hit every 167 * proc, not just us. 168 */ 169 if(hparsereq(c, 0) < 0) 170 break; 171 172 for(i = 0; i < MaxObjs && objs[i].name[0]; i++){ 173 n = strlen(objs[i].name); 174 if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0) 175 || (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){ 176 ok = (*objs[i].f)(c); 177 goto found; 178 } 179 } 180 ok = fromwebdir(c); 181 found: 182 hflush(&c->hout); 183 if(c->head.closeit) 184 ok = -1; 185 hreqcleanup(c); 186 187 if(ok < 0) 188 break; 189 } 190 hreqcleanup(c); 191 close(c->hin.fd); 192 free(c); 193 } 194 195 char* 196 hargstr(HConnect *c, char *name, char *def) 197 { 198 HSPairs *p; 199 200 for(p=c->req.searchpairs; p; p=p->next) 201 if(strcmp(p->s, name) == 0) 202 return p->t; 203 return def; 204 } 205 206 vlong 207 hargint(HConnect *c, char *name, vlong def) 208 { 209 char *a; 210 211 if((a = hargstr(c, name, nil)) == nil) 212 return def; 213 return atoll(a); 214 } 215 216 static int 217 percent(ulong v, ulong total) 218 { 219 if(total == 0) 220 total = 1; 221 if(v < 1000*1000) 222 return (v * 100) / total; 223 total /= 100; 224 if(total == 0) 225 total = 1; 226 return v / total; 227 } 228 229 static int 230 preq(HConnect *c) 231 { 232 if(hparseheaders(c, 0) < 0) 233 return -1; 234 if(strcmp(c->req.meth, "GET") != 0 235 && strcmp(c->req.meth, "HEAD") != 0) 236 return hunallowed(c, "GET, HEAD"); 237 if(c->head.expectother || c->head.expectcont) 238 return hfail(c, HExpectFail, nil); 239 return 0; 240 } 241 242 int 243 hsettype(HConnect *c, char *type) 244 { 245 Hio *hout; 246 int r; 247 248 r = preq(c); 249 if(r < 0) 250 return r; 251 252 hout = &c->hout; 253 if(c->req.vermaj){ 254 hokheaders(c); 255 hprint(hout, "Content-type: %s\r\n", type); 256 if(http11(c)) 257 hprint(hout, "Transfer-Encoding: chunked\r\n"); 258 hprint(hout, "\r\n"); 259 } 260 261 if(http11(c)) 262 hxferenc(hout, 1); 263 else 264 c->head.closeit = 1; 265 return 0; 266 } 267 268 int 269 hsethtml(HConnect *c) 270 { 271 return hsettype(c, "text/html; charset=utf-8"); 272 } 273 274 int 275 hsettext(HConnect *c) 276 { 277 return hsettype(c, "text/plain; charset=utf-8"); 278 } 279 280 static int 281 herror(HConnect *c) 282 { 283 int n; 284 Hio *hout; 285 286 hout = &c->hout; 287 n = snprint(c->xferbuf, HBufSize, "<html><head><title>Error</title></head>\n<body><h1>Error</h1>\n<pre>%r</pre>\n</body></html>"); 288 hprint(hout, "%s %s\r\n", hversion, "400 Bad Request"); 289 hprint(hout, "Date: %D\r\n", time(nil)); 290 hprint(hout, "Server: Venti\r\n"); 291 hprint(hout, "Content-Type: text/html\r\n"); 292 hprint(hout, "Content-Length: %d\r\n", n); 293 if(c->head.closeit) 294 hprint(hout, "Connection: close\r\n"); 295 else if(!http11(c)) 296 hprint(hout, "Connection: Keep-Alive\r\n"); 297 hprint(hout, "\r\n"); 298 299 if(c->req.meth == nil || strcmp(c->req.meth, "HEAD") != 0) 300 hwrite(hout, c->xferbuf, n); 301 302 return hflush(hout); 303 } 304 305 int 306 hnotfound(HConnect *c) 307 { 308 int r; 309 310 r = preq(c); 311 if(r < 0) 312 return r; 313 return hfail(c, HNotFound, c->req.uri); 314 } 315 316 struct { 317 char *ext; 318 char *type; 319 } exttab[] = { 320 ".html", "text/html", 321 ".txt", "text/plain", 322 ".xml", "text/xml", 323 ".png", "image/png", 324 ".gif", "image/gif", 325 0 326 }; 327 328 static int 329 fromwebdir(HConnect *c) 330 { 331 char buf[4096], *p, *ext, *type; 332 int i, fd, n, defaulted; 333 Dir *d; 334 335 if(webroot == nil || strstr(c->req.uri, "..")) 336 return hnotfound(c); 337 snprint(buf, sizeof buf-20, "%s/%s", webroot, c->req.uri+1); 338 defaulted = 0; 339 reopen: 340 if((fd = open(buf, OREAD)) < 0) 341 return hnotfound(c); 342 d = dirfstat(fd); 343 if(d == nil){ 344 close(fd); 345 return hnotfound(c); 346 } 347 if(d->mode&DMDIR){ 348 if(!defaulted){ 349 defaulted = 1; 350 strcat(buf, "/index.html"); 351 free(d); 352 close(fd); 353 goto reopen; 354 } 355 free(d); 356 return hnotfound(c); 357 } 358 free(d); 359 p = buf+strlen(buf); 360 type = "application/octet-stream"; 361 for(i=0; exttab[i].ext; i++){ 362 ext = exttab[i].ext; 363 if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){ 364 type = exttab[i].type; 365 break; 366 } 367 } 368 if(hsettype(c, type) < 0){ 369 close(fd); 370 return 0; 371 } 372 while((n = read(fd, buf, sizeof buf)) > 0) 373 if(hwrite(&c->hout, buf, n) < 0) 374 break; 375 close(fd); 376 hflush(&c->hout); 377 return 0; 378 } 379 380 static struct 381 { 382 char *name; 383 int *p; 384 } namedints[] = 385 { 386 "compress", &compressblocks, 387 "devnull", &writestodevnull, 388 "logging", &ventilogging, 389 "stats", &collectstats, 390 "icachesleeptime", &icachesleeptime, 391 "minicachesleeptime", &minicachesleeptime, 392 "arenasumsleeptime", &arenasumsleeptime, 393 "l0quantum", &l0quantum, 394 "l1quantum", &l1quantum, 395 "manualscheduling", &manualscheduling, 396 "ignorebloom", &ignorebloom, 397 "syncwrites", &syncwrites, 398 "icacheprefetch", &icacheprefetch, 399 "bootstrap", &bootstrap, 400 0 401 }; 402 403 static int 404 xset(HConnect *c) 405 { 406 int i, old; 407 char *name, *value; 408 409 if(hsettext(c) < 0) 410 return -1; 411 412 if((name = hargstr(c, "name", nil)) == nil || name[0] == 0){ 413 for(i=0; namedints[i].name; i++) 414 hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p); 415 hflush(&c->hout); 416 return 0; 417 } 418 419 for(i=0; namedints[i].name; i++) 420 if(strcmp(name, namedints[i].name) == 0) 421 break; 422 if(!namedints[i].name){ 423 hprint(&c->hout, "%s not found\n", name); 424 hflush(&c->hout); 425 return 0; 426 } 427 428 if((value = hargstr(c, "value", nil)) == nil || value[0] == 0){ 429 hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p); 430 hflush(&c->hout); 431 return 0; 432 } 433 434 old = *namedints[i].p; 435 *namedints[i].p = atoll(value); 436 hprint(&c->hout, "%s = %d (was %d)\n", name, *namedints[i].p, old); 437 hflush(&c->hout); 438 return 0; 439 } 440 441 static int 442 estats(HConnect *c) 443 { 444 Hio *hout; 445 int r; 446 447 r = hsettext(c); 448 if(r < 0) 449 return r; 450 451 452 hout = &c->hout; 453 /* 454 hprint(hout, "lump writes=%,ld\n", stats.lumpwrites); 455 hprint(hout, "lump reads=%,ld\n", stats.lumpreads); 456 hprint(hout, "lump cache read hits=%,ld\n", stats.lumphit); 457 hprint(hout, "lump cache read misses=%,ld\n", stats.lumpmiss); 458 459 hprint(hout, "clump disk writes=%,ld\n", stats.clumpwrites); 460 hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpbwrites); 461 hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpbcomp); 462 hprint(hout, "clump disk reads=%,ld\n", stats.clumpreads); 463 hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpbreads); 464 hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpbuncomp); 465 466 hprint(hout, "clump directory disk writes=%,ld\n", stats.ciwrites); 467 hprint(hout, "clump directory disk reads=%,ld\n", stats.cireads); 468 469 hprint(hout, "index disk writes=%,ld\n", stats.indexwrites); 470 hprint(hout, "index disk reads=%,ld\n", stats.indexreads); 471 hprint(hout, "index disk bloom filter hits=%,ld %d%% falsemisses=%,ld %d%%\n", 472 stats.indexbloomhits, 473 percent(stats.indexbloomhits, stats.indexreads), 474 stats.indexbloomfalsemisses, 475 percent(stats.indexbloomfalsemisses, stats.indexreads)); 476 hprint(hout, "bloom filter bits=%,ld of %,ld %d%%\n", 477 stats.bloomones, stats.bloombits, percent(stats.bloomones, stats.bloombits)); 478 hprint(hout, "index disk reads for modify=%,ld\n", stats.indexwreads); 479 hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexareads); 480 hprint(hout, "index block splits=%,ld\n", stats.indexsplits); 481 482 hprint(hout, "index cache lookups=%,ld\n", stats.iclookups); 483 hprint(hout, "index cache hits=%,ld %d%%\n", stats.ichits, 484 percent(stats.ichits, stats.iclookups)); 485 hprint(hout, "index cache fills=%,ld %d%%\n", stats.icfills, 486 percent(stats.icfills, stats.iclookups)); 487 hprint(hout, "index cache inserts=%,ld\n", stats.icinserts); 488 489 hprint(hout, "disk cache hits=%,ld\n", stats.pchit); 490 hprint(hout, "disk cache misses=%,ld\n", stats.pcmiss); 491 hprint(hout, "disk cache reads=%,ld\n", stats.pcreads); 492 hprint(hout, "disk cache bytes read=%,lld\n", stats.pcbreads); 493 494 hprint(hout, "disk cache writes=%,ld\n", stats.dirtydblocks); 495 hprint(hout, "disk cache writes absorbed=%,ld %d%%\n", stats.absorbedwrites, 496 percent(stats.absorbedwrites, stats.dirtydblocks)); 497 498 hprint(hout, "disk cache flushes=%,ld\n", stats.dcacheflushes); 499 hprint(hout, "disk cache flush writes=%,ld (%,ld per flush)\n", 500 stats.dcacheflushwrites, 501 stats.dcacheflushwrites/(stats.dcacheflushes ? stats.dcacheflushes : 1)); 502 503 hprint(hout, "disk writes=%,ld\n", stats.diskwrites); 504 hprint(hout, "disk bytes written=%,lld\n", stats.diskbwrites); 505 hprint(hout, "disk reads=%,ld\n", stats.diskreads); 506 hprint(hout, "disk bytes read=%,lld\n", stats.diskbreads); 507 */ 508 509 hflush(hout); 510 return 0; 511 } 512 513 static int 514 sindex(HConnect *c) 515 { 516 Hio *hout; 517 Index *ix; 518 Arena *arena; 519 vlong clumps, cclumps, uncsize, used, size; 520 int i, r, active; 521 522 r = hsettext(c); 523 if(r < 0) 524 return r; 525 hout = &c->hout; 526 527 ix = mainindex; 528 529 hprint(hout, "index=%s\n", ix->name); 530 531 active = 0; 532 clumps = 0; 533 cclumps = 0; 534 uncsize = 0; 535 used = 0; 536 size = 0; 537 for(i = 0; i < ix->narenas; i++){ 538 arena = ix->arenas[i]; 539 if(arena != nil && arena->memstats.clumps != 0){ 540 active++; 541 clumps += arena->memstats.clumps; 542 cclumps += arena->memstats.cclumps; 543 uncsize += arena->memstats.uncsize; 544 used += arena->memstats.used; 545 } 546 size += arena->size; 547 } 548 hprint(hout, "total arenas=%,d active=%,d\n", ix->narenas, active); 549 hprint(hout, "total space=%,lld used=%,lld\n", size, used + clumps * ClumpInfoSize); 550 hprint(hout, "clumps=%,lld compressed clumps=%,lld data=%,lld compressed data=%,lld\n", 551 clumps, cclumps, uncsize, used - clumps * ClumpSize); 552 hflush(hout); 553 return 0; 554 } 555 556 static void 557 darena(Hio *hout, Arena *arena) 558 { 559 hprint(hout, "arena='%s' on %s at [%lld,%lld)\n\tversion=%d created=%d modified=%d", 560 arena->name, arena->part->name, arena->base, arena->base + arena->size + 2 * arena->blocksize, 561 arena->version, arena->ctime, arena->wtime); 562 if(arena->memstats.sealed) 563 hprint(hout, " mem=sealed"); 564 if(arena->diskstats.sealed) 565 hprint(hout, " disk=sealed"); 566 if(arena->inqueue) 567 hprint(hout, " inqueue"); 568 hprint(hout, "\n"); 569 if(scorecmp(zeroscore, arena->score) != 0) 570 hprint(hout, "\tscore=%V\n", arena->score); 571 572 hprint(hout, "\twritten: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n", 573 arena->memstats.clumps, arena->memstats.cclumps, arena->memstats.uncsize, 574 arena->memstats.used - arena->memstats.clumps * ClumpSize, 575 arena->memstats.used + arena->memstats.clumps * ClumpInfoSize); 576 hprint(hout, "\tindexed: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n", 577 arena->diskstats.clumps, arena->diskstats.cclumps, arena->diskstats.uncsize, 578 arena->diskstats.used - arena->diskstats.clumps * ClumpSize, 579 arena->diskstats.used + arena->diskstats.clumps * ClumpInfoSize); 580 } 581 582 static int 583 hempty(HConnect *c) 584 { 585 Hio *hout; 586 int r; 587 588 r = hsettext(c); 589 if(r < 0) 590 return r; 591 hout = &c->hout; 592 593 emptylumpcache(); 594 emptydcache(); 595 emptyicache(); 596 hprint(hout, "emptied all caches\n"); 597 hflush(hout); 598 return 0; 599 } 600 601 static int 602 hlcacheempty(HConnect *c) 603 { 604 Hio *hout; 605 int r; 606 607 r = hsettext(c); 608 if(r < 0) 609 return r; 610 hout = &c->hout; 611 612 emptylumpcache(); 613 hprint(hout, "emptied lumpcache\n"); 614 hflush(hout); 615 return 0; 616 } 617 618 static int 619 hicacheempty(HConnect *c) 620 { 621 Hio *hout; 622 int r; 623 624 r = hsettext(c); 625 if(r < 0) 626 return r; 627 hout = &c->hout; 628 629 emptyicache(); 630 hprint(hout, "emptied icache\n"); 631 hflush(hout); 632 return 0; 633 } 634 635 static int 636 hdcacheempty(HConnect *c) 637 { 638 Hio *hout; 639 int r; 640 641 r = hsettext(c); 642 if(r < 0) 643 return r; 644 hout = &c->hout; 645 646 emptydcache(); 647 hprint(hout, "emptied dcache\n"); 648 hflush(hout); 649 return 0; 650 } 651 static int 652 hicachekick(HConnect *c) 653 { 654 Hio *hout; 655 int r; 656 657 r = hsettext(c); 658 if(r < 0) 659 return r; 660 hout = &c->hout; 661 662 kickicache(); 663 hprint(hout, "kicked icache\n"); 664 hflush(hout); 665 return 0; 666 } 667 668 static int 669 hdcachekick(HConnect *c) 670 { 671 Hio *hout; 672 int r; 673 674 r = hsettext(c); 675 if(r < 0) 676 return r; 677 hout = &c->hout; 678 679 kickdcache(); 680 hprint(hout, "kicked dcache\n"); 681 hflush(hout); 682 return 0; 683 } 684 static int 685 hicacheflush(HConnect *c) 686 { 687 Hio *hout; 688 int r; 689 690 r = hsettext(c); 691 if(r < 0) 692 return r; 693 hout = &c->hout; 694 695 flushicache(); 696 hprint(hout, "flushed icache\n"); 697 hflush(hout); 698 return 0; 699 } 700 701 static int 702 hdcacheflush(HConnect *c) 703 { 704 Hio *hout; 705 int r; 706 707 r = hsettext(c); 708 if(r < 0) 709 return r; 710 hout = &c->hout; 711 712 flushdcache(); 713 hprint(hout, "flushed dcache\n"); 714 hflush(hout); 715 return 0; 716 } 717 718 static int 719 dindex(HConnect *c) 720 { 721 Hio *hout; 722 Index *ix; 723 int i, r; 724 725 r = hsettext(c); 726 if(r < 0) 727 return r; 728 hout = &c->hout; 729 730 731 ix = mainindex; 732 hprint(hout, "index=%s version=%d blocksize=%d tabsize=%d\n", 733 ix->name, ix->version, ix->blocksize, ix->tabsize); 734 hprint(hout, "\tbuckets=%d div=%d\n", ix->buckets, ix->div); 735 for(i = 0; i < ix->nsects; i++) 736 hprint(hout, "\tsect=%s for buckets [%lld,%lld) buckmax=%d\n", ix->smap[i].name, ix->smap[i].start, ix->smap[i].stop, ix->sects[i]->buckmax); 737 for(i = 0; i < ix->narenas; i++){ 738 if(ix->arenas[i] != nil && ix->arenas[i]->memstats.clumps != 0){ 739 hprint(hout, "arena=%s at index [%lld,%lld)\n\t", ix->amap[i].name, ix->amap[i].start, ix->amap[i].stop); 740 darena(hout, ix->arenas[i]); 741 } 742 } 743 hflush(hout); 744 return 0; 745 } 746 747 typedef struct Arg Arg; 748 struct Arg 749 { 750 int index; 751 int index2; 752 }; 753 754 static long 755 rawgraph(Stats *s, Stats *t, void *va) 756 { 757 Arg *a; 758 759 USED(s); 760 a = va; 761 return t->n[a->index]; 762 } 763 764 static long 765 diffgraph(Stats *s, Stats *t, void *va) 766 { 767 Arg *a; 768 769 a = va; 770 return t->n[a->index] - s->n[a->index]; 771 } 772 773 static long 774 pctgraph(Stats *s, Stats *t, void *va) 775 { 776 Arg *a; 777 778 USED(s); 779 a = va; 780 return percent(t->n[a->index], t->n[a->index2]); 781 } 782 783 static long 784 pctdiffgraph(Stats *s, Stats *t, void *va) 785 { 786 Arg *a; 787 788 a = va; 789 return percent(t->n[a->index]-s->n[a->index], t->n[a->index2]-s->n[a->index2]); 790 } 791 792 static long 793 xdiv(long a, long b) 794 { 795 if(b == 0) 796 b++; 797 return a/b; 798 } 799 800 static long 801 divdiffgraph(Stats *s, Stats *t, void *va) 802 { 803 Arg *a; 804 805 a = va; 806 return xdiv(t->n[a->index] - s->n[a->index], t->n[a->index2] - s->n[a->index2]); 807 } 808 809 static long 810 netbw(Stats *s) 811 { 812 ulong *n; 813 814 n = s->n; 815 return n[StatRpcReadBytes]+n[StatRpcWriteBytes]; /* not exactly right */ 816 } 817 818 static long 819 diskbw(Stats *s) 820 { 821 ulong *n; 822 823 n = s->n; 824 return n[StatApartReadBytes]+n[StatApartWriteBytes] 825 + n[StatIsectReadBytes]+n[StatIsectWriteBytes] 826 + n[StatSumReadBytes]; 827 } 828 829 static long 830 iobw(Stats *s) 831 { 832 return netbw(s)+diskbw(s); 833 } 834 835 static long 836 diskgraph(Stats *s, Stats *t, void *va) 837 { 838 USED(va); 839 return diskbw(t)-diskbw(s); 840 } 841 842 static long 843 netgraph(Stats *s, Stats *t, void *va) 844 { 845 USED(va); 846 return netbw(t)-netbw(s); 847 } 848 849 static long 850 iograph(Stats *s, Stats *t, void *va) 851 { 852 USED(va); 853 return iobw(t)-iobw(s); 854 } 855 856 857 static char* graphname[] = 858 { 859 "rpctotal", 860 "rpcread", 861 "rpcreadok", 862 "rpcreadfail", 863 "rpcreadbyte", 864 "rpcreadtime", 865 "rpcreadcached", 866 "rpcreadcachedtime", 867 "rpcreaduncached", 868 "rpcreaduncachedtime", 869 "rpcwrite", 870 "rpcwritenew", 871 "rpcwriteold", 872 "rpcwritefail", 873 "rpcwritebyte", 874 "rpcwritetime", 875 "rpcwritenewtime", 876 "rpcwriteoldtime", 877 878 "lcachehit", 879 "lcachemiss", 880 "lcachelookup", 881 "lcachewrite", 882 "lcachesize", 883 "lcachestall", 884 "lcachelookuptime", 885 886 "dcachehit", 887 "dcachemiss", 888 "dcachelookup", 889 "dcacheread", 890 "dcachewrite", 891 "dcachedirty", 892 "dcachesize", 893 "dcacheflush", 894 "dcachestall", 895 "dcachelookuptime", 896 897 "dblockstall", 898 "lumpstall", 899 900 "icachehit", 901 "icachemiss", 902 "icacheread", 903 "icachewrite", 904 "icachefill", 905 "icacheprefetch", 906 "icachedirty", 907 "icachesize", 908 "icacheflush", 909 "icachestall", 910 "icachelookuptime", 911 "icachelookup", 912 "scachehit", 913 "scacheprefetch", 914 915 "bloomhit", 916 "bloommiss", 917 "bloomfalsemiss", 918 "bloomlookup", 919 "bloomones", 920 "bloombits", 921 922 "apartread", 923 "apartreadbyte", 924 "apartwrite", 925 "apartwritebyte", 926 927 "isectread", 928 "isectreadbyte", 929 "isectwrite", 930 "isectwritebyte", 931 932 "sumread", 933 "sumreadbyte", 934 935 "cigload", 936 "cigloadtime", 937 }; 938 939 static int 940 findname(char *s) 941 { 942 int i; 943 944 for(i=0; i<nelem(graphname); i++) 945 if(strcmp(graphname[i], s) == 0) 946 return i; 947 return -1; 948 } 949 950 static void 951 dotextbin(Hio *io, Graph *g) 952 { 953 int i, nbin; 954 Statbin *b, bin[2000]; /* 32 kB, but whack is worse */ 955 956 needstack(8192); /* double check that bin didn't kill us */ 957 nbin = 100; 958 binstats(g->fn, g->arg, g->t0, g->t1, bin, nbin); 959 960 hprint(io, "stats\n\n"); 961 for(i=0; i<nbin; i++){ 962 b = &bin[i]; 963 hprint(io, "%d: nsamp=%d min=%d max=%d avg=%d\n", 964 i, b->nsamp, b->min, b->max, b->avg); 965 } 966 } 967 968 static int 969 xgraph(HConnect *c) 970 { 971 char *name; 972 Hio *hout; 973 Memimage *m; 974 int dotext; 975 Graph g; 976 Arg arg; 977 char *graph, *a; 978 979 name = hargstr(c, "arg", ""); 980 if((arg.index = findname(name)) == -1 && strcmp(name, "*") != 0){ 981 werrstr("unknown name %s", name); 982 goto error; 983 } 984 a = hargstr(c, "arg2", ""); 985 if(a[0] && (arg.index2 = findname(a)) == -1){ 986 werrstr("unknown name %s", a); 987 goto error; 988 } 989 990 g.arg = &arg; 991 g.t0 = hargint(c, "t0", -120); 992 g.t1 = hargint(c, "t1", 0); 993 g.min = hargint(c, "min", -1); 994 g.max = hargint(c, "max", -1); 995 g.wid = hargint(c, "wid", -1); 996 g.ht = hargint(c, "ht", -1); 997 dotext = hargstr(c, "text", "")[0] != 0; 998 g.fill = hargint(c, "fill", -1); 999 1000 graph = hargstr(c, "graph", "raw"); 1001 if(strcmp(graph, "raw") == 0) 1002 g.fn = rawgraph; 1003 else if(strcmp(graph, "diskbw") == 0) 1004 g.fn = diskgraph; 1005 else if(strcmp(graph, "iobw") == 0) 1006 g.fn = iograph; 1007 else if(strcmp(graph, "netbw") == 0) 1008 g.fn = netgraph; 1009 else if(strcmp(graph, "diff") == 0) 1010 g.fn = diffgraph; 1011 else if(strcmp(graph, "pct") == 0) 1012 g.fn = pctgraph; 1013 else if(strcmp(graph, "pctdiff") == 0) 1014 g.fn = pctdiffgraph; 1015 else if(strcmp(graph, "divdiff") == 0) 1016 g.fn = divdiffgraph; 1017 else{ 1018 werrstr("unknown graph %s", graph); 1019 goto error; 1020 } 1021 1022 if(dotext){ 1023 hsettype(c, "text/plain"); 1024 dotextbin(&c->hout, &g); 1025 hflush(&c->hout); 1026 return 0; 1027 } 1028 1029 m = statgraph(&g); 1030 if(m == nil) 1031 goto error; 1032 1033 if(hsettype(c, "image/png") < 0) 1034 return -1; 1035 hout = &c->hout; 1036 writepng(hout, m); 1037 qlock(&memdrawlock); 1038 freememimage(m); 1039 qunlock(&memdrawlock); 1040 hflush(hout); 1041 return 0; 1042 1043 error: 1044 return herror(c); 1045 } 1046 1047 static int 1048 xloglist(HConnect *c) 1049 { 1050 if(hsettype(c, "text/html") < 0) 1051 return -1; 1052 vtloghlist(&c->hout); 1053 hflush(&c->hout); 1054 return 0; 1055 } 1056 1057 static int 1058 xlog(HConnect *c) 1059 { 1060 char *name; 1061 VtLog *l; 1062 1063 name = hargstr(c, "log", ""); 1064 if(!name[0]) 1065 return xloglist(c); 1066 l = vtlogopen(name, 0); 1067 if(l == nil) 1068 return hnotfound(c); 1069 if(hsettype(c, "text/html") < 0){ 1070 vtlogclose(l); 1071 return -1; 1072 } 1073 vtloghdump(&c->hout, l); 1074 vtlogclose(l); 1075 hflush(&c->hout); 1076 return 0; 1077 } 1078 1079 static int 1080 xindex(HConnect *c) 1081 { 1082 if(hsettype(c, "text/xml") < 0) 1083 return -1; 1084 xmlindex(&c->hout, mainindex, "index", 0); 1085 hflush(&c->hout); 1086 return 0; 1087 } 1088 1089 void 1090 xmlindent(Hio *hout, int indent) 1091 { 1092 int i; 1093 1094 for(i = 0; i < indent; i++) 1095 hputc(hout, '\t'); 1096 } 1097 1098 void 1099 xmlaname(Hio *hout, char *v, char *tag) 1100 { 1101 hprint(hout, " %s=\"%s\"", tag, v); 1102 } 1103 1104 void 1105 xmlscore(Hio *hout, u8int *v, char *tag) 1106 { 1107 if(scorecmp(zeroscore, v) == 0) 1108 return; 1109 hprint(hout, " %s=\"%V\"", tag, v); 1110 } 1111 1112 void 1113 xmlsealed(Hio *hout, int v, char *tag) 1114 { 1115 if(!v) 1116 return; 1117 hprint(hout, " %s=\"yes\"", tag); 1118 } 1119 1120 void 1121 xmlu32int(Hio *hout, u32int v, char *tag) 1122 { 1123 hprint(hout, " %s=\"%ud\"", tag, v); 1124 } 1125 1126 void 1127 xmlu64int(Hio *hout, u64int v, char *tag) 1128 { 1129 hprint(hout, " %s=\"%llud\"", tag, v); 1130 } 1131 1132 void 1133 vtloghdump(Hio *h, VtLog *l) 1134 { 1135 int i; 1136 VtLogChunk *c; 1137 char *name; 1138 1139 name = l ? l->name : "<nil>"; 1140 1141 hprint(h, "<html><head>\n"); 1142 hprint(h, "<title>Venti Server Log: %s</title>\n", name); 1143 hprint(h, "</head><body>\n"); 1144 hprint(h, "<b>Venti Server Log: %s</b>\n<p>\n", name); 1145 1146 if(l){ 1147 c = l->w; 1148 for(i=0; i<l->nchunk; i++){ 1149 if(++c == l->chunk+l->nchunk) 1150 c = l->chunk; 1151 hwrite(h, c->p, c->wp-c->p); 1152 } 1153 } 1154 hprint(h, "</body></html>\n"); 1155 } 1156 1157 static int 1158 strpcmp(const void *va, const void *vb) 1159 { 1160 return strcmp(*(char**)va, *(char**)vb); 1161 } 1162 1163 void 1164 vtloghlist(Hio *h) 1165 { 1166 char **p; 1167 int i, n; 1168 1169 hprint(h, "<html><head>\n"); 1170 hprint(h, "<title>Venti Server Logs</title>\n"); 1171 hprint(h, "</head><body>\n"); 1172 hprint(h, "<b>Venti Server Logs</b>\n<p>\n"); 1173 1174 p = vtlognames(&n); 1175 qsort(p, n, sizeof(p[0]), strpcmp); 1176 for(i=0; i<n; i++) 1177 hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]); 1178 vtfree(p); 1179 hprint(h, "</body></html>\n"); 1180 }