6 typedef struct HttpObj HttpObj;
7 extern QLock memdrawlock;
17 char name[ObjNameSize];
21 static HttpObj objs[MaxObjs];
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 hicacheflush(HConnect *c);
32 static int hdcacheflush(HConnect *c);
33 static int notfound(HConnect *c);
34 static int httpdobj(char *name, int (*f)(HConnect*));
35 static int xgraph(HConnect *c);
36 static int xset(HConnect *c);
37 static int fromwebdir(HConnect *c);
40 httpdinit(char *address, char *dir)
42 fmtinstall('D', hdatefmt);
43 /* fmtinstall('H', httpfmt); */
44 fmtinstall('U', hurlfmt);
47 address = "tcp!*!http";
50 httpdobj("/stats", estats);
51 httpdobj("/index", dindex);
52 httpdobj("/storage", sindex);
53 httpdobj("/xindex", xindex);
54 httpdobj("/flushicache", hicacheflush);
55 httpdobj("/flushdcache", hdcacheflush);
56 httpdobj("/graph/", xgraph);
57 httpdobj("/set/", xset);
58 httpdobj("/log", xlog);
59 httpdobj("/log/", xlog);
61 if(vtproc(listenproc, address) < 0)
67 httpdobj(char *name, int (*f)(HConnect*))
71 if(name == nil || strlen(name) >= ObjNameSize)
73 for(i = 0; i < MaxObjs; i++){
74 if(objs[i].name[0] == '\0'){
75 strcpy(objs[i].name, name);
79 if(strcmp(objs[i].name, name) == 0)
90 c = mallocz(sizeof(HConnect), 1);
92 sysfatal("out of memory");
102 listenproc(void *vaddress)
105 char *address, ndir[NETPATHLEN], dir[NETPATHLEN];
108 //sleep(1000); /* let strace find us */
111 ctl = announce(address, dir);
113 fprint(2, "venti: httpd can't announce on %s: %r\n", address);
117 if(0) print("announce ctl %d dir %s\n", ctl, dir);
120 * wait for a call (or an error)
122 nctl = listen(dir, ndir);
123 if(0) print("httpd listen %d %s...\n", nctl, ndir);
125 fprint(2, "venti: httpd can't listen on %s: %r\n", address);
129 data = accept(ctl, ndir);
130 if(0) print("httpd accept %d...\n", data);
132 fprint(2, "venti: httpd accept: %r\n");
136 if(0) print("httpd close nctl %d\n", nctl);
139 hinit(&c->hin, data, Hread);
140 hinit(&c->hout, data, Hwrite);
151 //sleep(1000); /* let strace find us */
156 * No timeout because the signal appears to hit every
159 if(hparsereq(c, 0) < 0)
162 for(i = 0; i < MaxObjs && objs[i].name[0]; i++){
163 n = strlen(objs[i].name);
164 if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0)
165 || (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){
166 ok = (*objs[i].f)(c);
185 percent(long v, long total)
190 return (v * 100) / total;
200 if(hparseheaders(c, 0) < 0)
202 if(strcmp(c->req.meth, "GET") != 0
203 && strcmp(c->req.meth, "HEAD") != 0)
204 return hunallowed(c, "GET, HEAD");
205 if(c->head.expectother || c->head.expectcont)
206 return hfail(c, HExpectFail, nil);
211 preqtype(HConnect *c, char *type)
223 hprint(hout, "Content-type: %s\r\n", type);
225 hprint(hout, "Transfer-Encoding: chunked\r\n");
226 hprint(hout, "\r\n");
237 preqtext(HConnect *c)
239 return preqtype(c, "text/plain");
243 notfound(HConnect *c)
250 return hfail(c, HNotFound, c->req.uri);
257 ".html", "text/html",
258 ".txt", "text/plain",
266 fromwebdir(HConnect *c)
268 char buf[4096], *p, *ext, *type;
269 int i, fd, n, defaulted;
272 if(webroot == nil || strstr(c->req.uri, ".."))
274 snprint(buf, sizeof buf-20, "%s/%s", webroot, c->req.uri+1);
277 if((fd = open(buf, OREAD)) < 0)
287 strcat(buf, "/index.html");
297 type = "application/octet-stream";
298 for(i=0; exttab[i].ext; i++){
300 if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){
301 type = exttab[i].type;
305 if(preqtype(c, type) < 0){
309 while((n = read(fd, buf, sizeof buf)) > 0)
310 if(hwrite(&c->hout, buf, n) < 0)
323 "compress", &compressblocks,
324 "devnull", &writestodevnull,
325 "logging", &ventilogging,
326 "stats", &collectstats,
327 "icachesleeptime", &icachesleeptime,
328 "arenasumsleeptime", &arenasumsleeptime,
338 s = estrdup(c->req.uri);
339 nf = getfields(s+strlen("/set/"), f, nelem(f), 1, "/");
343 for(i=0; namedints[i].name; i++){
344 if(strcmp(f[0], namedints[i].name) == 0){
346 *namedints[i].p = atoi(f[1]);
350 hprint(&c->hout, "%s = %d\n", f[0], *namedints[i].p);
371 hprint(hout, "lump writes=%,ld\n", stats.lumpwrites);
372 hprint(hout, "lump reads=%,ld\n", stats.lumpreads);
373 hprint(hout, "lump cache read hits=%,ld\n", stats.lumphit);
374 hprint(hout, "lump cache read misses=%,ld\n", stats.lumpmiss);
376 hprint(hout, "clump disk writes=%,ld\n", stats.clumpwrites);
377 hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpbwrites);
378 hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpbcomp);
379 hprint(hout, "clump disk reads=%,ld\n", stats.clumpreads);
380 hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpbreads);
381 hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpbuncomp);
383 hprint(hout, "clump directory disk writes=%,ld\n", stats.ciwrites);
384 hprint(hout, "clump directory disk reads=%,ld\n", stats.cireads);
386 hprint(hout, "index disk writes=%,ld\n", stats.indexwrites);
387 hprint(hout, "index disk reads=%,ld\n", stats.indexreads);
388 hprint(hout, "index disk bloom filter hits=%,ld %d%% falsemisses=%,ld %d%%\n",
389 stats.indexbloomhits,
390 percent(stats.indexbloomhits, stats.indexreads),
391 stats.indexbloomfalsemisses,
392 percent(stats.indexbloomfalsemisses, stats.indexreads));
393 hprint(hout, "bloom filter bits=%,ld of %,ld %d%%\n",
394 stats.bloomones, stats.bloombits, percent(stats.bloomones, stats.bloombits));
395 hprint(hout, "index disk reads for modify=%,ld\n", stats.indexwreads);
396 hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexareads);
397 hprint(hout, "index block splits=%,ld\n", stats.indexsplits);
399 hprint(hout, "index cache lookups=%,ld\n", stats.iclookups);
400 hprint(hout, "index cache hits=%,ld %d%%\n", stats.ichits,
401 percent(stats.ichits, stats.iclookups));
402 hprint(hout, "index cache fills=%,ld %d%%\n", stats.icfills,
403 percent(stats.icfills, stats.iclookups));
404 hprint(hout, "index cache inserts=%,ld\n", stats.icinserts);
406 hprint(hout, "disk cache hits=%,ld\n", stats.pchit);
407 hprint(hout, "disk cache misses=%,ld\n", stats.pcmiss);
408 hprint(hout, "disk cache reads=%,ld\n", stats.pcreads);
409 hprint(hout, "disk cache bytes read=%,lld\n", stats.pcbreads);
411 hprint(hout, "disk cache writes=%,ld\n", stats.dirtydblocks);
412 hprint(hout, "disk cache writes absorbed=%,ld %d%%\n", stats.absorbedwrites,
413 percent(stats.absorbedwrites, stats.dirtydblocks));
415 hprint(hout, "disk cache flushes=%,ld\n", stats.dcacheflushes);
416 hprint(hout, "disk cache flush writes=%,ld (%,ld per flush)\n",
417 stats.dcacheflushwrites,
418 stats.dcacheflushwrites/(stats.dcacheflushes ? stats.dcacheflushes : 1));
420 hprint(hout, "disk writes=%,ld\n", stats.diskwrites);
421 hprint(hout, "disk bytes written=%,lld\n", stats.diskbwrites);
422 hprint(hout, "disk reads=%,ld\n", stats.diskreads);
423 hprint(hout, "disk bytes read=%,lld\n", stats.diskbreads);
436 vlong clumps, cclumps, uncsize, used, size;
446 hprint(hout, "index=%s\n", ix->name);
454 for(i = 0; i < ix->narenas; i++){
455 arena = ix->arenas[i];
456 if(arena != nil && arena->memstats.clumps != 0){
458 clumps += arena->memstats.clumps;
459 cclumps += arena->memstats.cclumps;
460 uncsize += arena->memstats.uncsize;
461 used += arena->memstats.used;
465 hprint(hout, "total arenas=%,d active=%,d\n", ix->narenas, active);
466 hprint(hout, "total space=%,lld used=%,lld\n", size, used + clumps * ClumpInfoSize);
467 hprint(hout, "clumps=%,lld compressed clumps=%,lld data=%,lld compressed data=%,lld\n",
468 clumps, cclumps, uncsize, used - clumps * ClumpSize);
474 darena(Hio *hout, Arena *arena)
476 hprint(hout, "arena='%s' on %s at [%lld,%lld)\n\tversion=%d created=%d modified=%d",
477 arena->name, arena->part->name, arena->base, arena->base + arena->size + 2 * arena->blocksize,
478 arena->version, arena->ctime, arena->wtime);
479 if(arena->memstats.sealed)
480 hprint(hout, " mem=sealed");
481 if(arena->diskstats.sealed)
482 hprint(hout, " disk=sealed");
484 if(scorecmp(zeroscore, arena->score) != 0)
485 hprint(hout, "\tscore=%V\n", arena->score);
487 hprint(hout, "\tmem: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n",
488 arena->memstats.clumps, arena->memstats.cclumps, arena->memstats.uncsize,
489 arena->memstats.used - arena->memstats.clumps * ClumpSize,
490 arena->memstats.used + arena->memstats.clumps * ClumpInfoSize);
491 hprint(hout, "\tdisk: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n",
492 arena->diskstats.clumps, arena->diskstats.cclumps, arena->diskstats.uncsize,
493 arena->diskstats.used - arena->diskstats.clumps * ClumpSize,
494 arena->diskstats.used + arena->diskstats.clumps * ClumpInfoSize);
498 hicacheflush(HConnect *c)
509 hprint(hout, "flushed icache\n");
515 hdcacheflush(HConnect *c)
526 hprint(hout, "flushed dcache\n");
545 hprint(hout, "index=%s version=%d blocksize=%d tabsize=%d\n",
546 ix->name, ix->version, ix->blocksize, ix->tabsize);
547 hprint(hout, "\tbuckets=%d div=%d\n", ix->buckets, ix->div);
548 for(i = 0; i < ix->nsects; i++)
549 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);
550 for(i = 0; i < ix->narenas; i++){
551 if(ix->arenas[i] != nil && ix->arenas[i]->memstats.clumps != 0){
552 hprint(hout, "arena=%s at index [%lld,%lld)\n\t", ix->amap[i].name, ix->amap[i].start, ix->amap[i].stop);
553 darena(hout, ix->arenas[i]);
560 typedef struct Arg Arg;
568 rawgraph(Stats *s, Stats *t, void *va)
573 return t->n[a->index];
577 diffgraph(Stats *s, Stats *t, void *va)
582 return t->n[a->index] - s->n[a->index];
586 pctgraph(Stats *s, Stats *t, void *va)
591 return percent(t->n[a->index], t->n[a->index2]);
595 pctdiffgraph(Stats *s, Stats *t, void *va)
600 return percent(t->n[a->index]-s->n[a->index], t->n[a->index2]-s->n[a->index2]);
609 return n[StatRpcReadBytes]+n[StatRpcWriteBytes]; /* not exactly right */
618 return n[StatApartReadBytes]+n[StatApartWriteBytes]
619 + n[StatIsectReadBytes]+n[StatIsectWriteBytes]
620 + n[StatSumReadBytes];
626 return netbw(s)+diskbw(s);
630 diskgraph(Stats *s, Stats *t, void *va)
633 return diskbw(t)-diskbw(s);
637 netgraph(Stats *s, Stats *t, void *va)
640 return netbw(t)-netbw(s);
644 iograph(Stats *s, Stats *t, void *va)
647 return iobw(t)-iobw(s);
651 static char* graphname[] =
662 "rpcreaduncachedtime",
733 for(i=0; i<nelem(graphname); i++)
734 if(strcmp(graphname[i], s) == 0)
736 fprint(2, "no name '%s'\n", s);
741 dotextbin(Hio *io, Graph *g)
744 Statbin *b, bin[2000]; /* 32 kB, but whack is worse */
746 needstack(8192); /* double check that bin didn't kill us */
748 binstats(g->fn, g->arg, g->t0, g->t1, bin, nbin);
750 hprint(io, "stats\n\n");
751 for(i=0; i<nbin; i++){
753 hprint(io, "%d: nsamp=%d min=%d max=%d avg=%d\n",
754 i, b->nsamp, b->min, b->max, b->avg);
768 s = estrdup(c->req.uri);
769 if(0) fprint(2, "graph %s\n" ,s);
770 memset(&g, 0, sizeof g);
771 nf = getfields(s+strlen("/graph/"), f, nelem(f), 1, "/");
774 if((arg.index = findname(f[0])) == -1 && strcmp(f[0], "*") != 0)
787 if(strncmp(f[i], "t0=", 3) == 0)
789 else if(strncmp(f[i], "t1=", 3) == 0)
791 else if(strncmp(f[i], "min=", 4) == 0)
792 g.min = atoi(f[i]+4);
793 else if(strncmp(f[i], "max=", 4) == 0)
794 g.max = atoi(f[i]+4);
795 else if(strncmp(f[i], "pct=", 4) == 0){
796 if((arg.index2 = findname(f[i]+4)) == -1)
801 }else if(strncmp(f[i], "pctdiff=", 8) == 0){
802 if((arg.index2 = findname(f[i]+8)) == -1)
807 }else if(strcmp(f[i], "diff") == 0)
809 else if(strcmp(f[i], "text") == 0)
811 else if(strncmp(f[i], "wid=", 4) == 0)
812 g.wid = atoi(f[i]+4);
813 else if(strncmp(f[i], "ht=", 3) == 0)
815 else if(strncmp(f[i], "fill=", 5) == 0)
816 g.fill = atoi(f[i]+5);
817 else if(strcmp(f[i], "diskbw") == 0)
819 else if(strcmp(f[i], "iobw") == 0)
821 else if(strcmp(f[i], "netbw") == 0)
825 preqtype(c, "text/plain");
826 dotextbin(&c->hout, &g);
835 if(preqtype(c, "image/png") < 0)
841 qunlock(&memdrawlock);
852 xloglist(HConnect *c)
854 if(preqtype(c, "text/html") < 0)
856 vtloghlist(&c->hout);
867 if(strcmp(c->req.uri, "/log") == 0 || strcmp(c->req.uri, "/log/") == 0)
869 if(strncmp(c->req.uri, "/log/", 5) != 0)
871 name = c->req.uri + strlen("/log/");
872 l = vtlogopen(name, 0);
875 if(preqtype(c, "text/html") < 0){
879 vtloghdump(&c->hout, l);
888 if(preqtype(c, "text/xml") < 0)
890 xmlindex(&c->hout, mainindex, "index", 0);
896 xmlindent(Hio *hout, int indent)
900 for(i = 0; i < indent; i++)
905 xmlaname(Hio *hout, char *v, char *tag)
907 hprint(hout, " %s=\"%s\"", tag, v);
911 xmlscore(Hio *hout, u8int *v, char *tag)
913 if(scorecmp(zeroscore, v) == 0)
915 hprint(hout, " %s=\"%V\"", tag, v);
919 xmlsealed(Hio *hout, int v, char *tag)
923 hprint(hout, " %s=\"yes\"", tag);
927 xmlu32int(Hio *hout, u32int v, char *tag)
929 hprint(hout, " %s=\"%ud\"", tag, v);
933 xmlu64int(Hio *hout, u64int v, char *tag)
935 hprint(hout, " %s=\"%llud\"", tag, v);
939 vtloghdump(Hio *h, VtLog *l)
945 name = l ? l->name : "<nil>";
947 fprint(2, "hdump xfer %d\n", h->xferenc);
948 hprint(h, "<html><head>\n");
949 hprint(h, "<title>Venti Server Log: %s</title>\n", name);
950 hprint(h, "</head><body>\n");
951 hprint(h, "<b>Venti Server Log: %s</b>\n<p>\n", name);
955 for(i=0; i<l->nchunk; i++){
956 if(++c == l->chunk+l->nchunk)
958 hwrite(h, c->p, c->wp-c->p);
961 hprint(h, "</body></html>\n");
965 strpcmp(const void *va, const void *vb)
967 return strcmp(*(char**)va, *(char**)vb);
976 hprint(h, "<html><head>\n");
977 hprint(h, "<title>Venti Server Logs</title>\n");
978 hprint(h, "</head><body>\n");
979 hprint(h, "<b>Venti Server Logs</b>\n<p>\n");
982 qsort(p, n, sizeof(p[0]), strpcmp);
984 hprint(h, "<a href=\"/log/%s\">%s</a><br>\n", p[i], p[i]);
986 hprint(h, "</body></html>\n");