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 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);
45 httpdinit(char *address, char *dir)
47 fmtinstall('D', hdatefmt);
48 /* fmtinstall('H', httpfmt); */
49 fmtinstall('U', hurlfmt);
52 address = "tcp!*!http";
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);
74 if(vtproc(listenproc, address) < 0)
80 httpdobj(char *name, int (*f)(HConnect*))
84 if(name == nil || strlen(name) >= ObjNameSize)
86 for(i = 0; i < MaxObjs; i++){
87 if(objs[i].name[0] == '\0'){
88 strcpy(objs[i].name, name);
92 if(strcmp(objs[i].name, name) == 0)
103 c = mallocz(sizeof(HConnect), 1);
105 sysfatal("out of memory");
108 c->hstop = c->header;
112 void httpproc(void*);
115 listenproc(void *vaddress)
118 char *address, ndir[NETPATHLEN], dir[NETPATHLEN];
122 ctl = announce(address, dir);
124 fprint(2, "venti: httpd can't announce on %s: %r\n", address);
128 if(0) print("announce ctl %d dir %s\n", ctl, dir);
131 * wait for a call (or an error)
133 nctl = listen(dir, ndir);
134 if(0) print("httpd listen %d %s...\n", nctl, ndir);
136 fprint(2, "venti: httpd can't listen on %s: %r\n", address);
140 data = accept(ctl, ndir);
141 if(0) print("httpd accept %d...\n", data);
143 fprint(2, "venti: httpd accept: %r\n");
147 if(0) print("httpd close nctl %d\n", nctl);
150 hinit(&c->hin, data, Hread);
151 hinit(&c->hout, data, Hwrite);
166 * No timeout because the signal appears to hit every
169 if(hparsereq(c, 0) < 0)
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);
196 hargstr(HConnect *c, char *name, char *def)
200 for(p=c->req.searchpairs; p; p=p->next)
201 if(strcmp(p->s, name) == 0)
207 hargint(HConnect *c, char *name, vlong def)
211 if((a = hargstr(c, name, nil)) == nil)
217 percent(ulong v, ulong total)
222 return (v * 100) / total;
232 if(hparseheaders(c, 0) < 0)
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);
243 hsettype(HConnect *c, char *type)
255 hprint(hout, "Content-type: %s\r\n", type);
257 hprint(hout, "Transfer-Encoding: chunked\r\n");
258 hprint(hout, "\r\n");
269 hsethtml(HConnect *c)
271 return hsettype(c, "text/html; charset=utf-8");
275 hsettext(HConnect *c)
277 return hsettype(c, "text/plain; charset=utf-8");
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);
294 hprint(hout, "Connection: close\r\n");
296 hprint(hout, "Connection: Keep-Alive\r\n");
297 hprint(hout, "\r\n");
299 if(c->req.meth == nil || strcmp(c->req.meth, "HEAD") != 0)
300 hwrite(hout, c->xferbuf, n);
306 hnotfound(HConnect *c)
313 return hfail(c, HNotFound, c->req.uri);
320 ".html", "text/html",
321 ".txt", "text/plain",
329 fromwebdir(HConnect *c)
331 char buf[4096], *p, *ext, *type;
332 int i, fd, n, defaulted;
335 if(webroot == nil || strstr(c->req.uri, ".."))
337 snprint(buf, sizeof buf-20, "%s/%s", webroot, c->req.uri+1);
340 if((fd = open(buf, OREAD)) < 0)
350 strcat(buf, "/index.html");
360 type = "application/octet-stream";
361 for(i=0; exttab[i].ext; i++){
363 if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){
364 type = exttab[i].type;
368 if(hsettype(c, type) < 0){
372 while((n = read(fd, buf, sizeof buf)) > 0)
373 if(hwrite(&c->hout, buf, n) < 0)
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,
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);
419 for(i=0; namedints[i].name; i++)
420 if(strcmp(name, namedints[i].name) == 0)
422 if(!namedints[i].name){
423 hprint(&c->hout, "%s not found\n", name);
428 if((value = hargstr(c, "value", nil)) == nil || value[0] == 0){
429 hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p);
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);
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);
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);
466 hprint(hout, "clump directory disk writes=%,ld\n", stats.ciwrites);
467 hprint(hout, "clump directory disk reads=%,ld\n", stats.cireads);
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);
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);
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);
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));
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));
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);
519 vlong clumps, cclumps, uncsize, used, size;
529 hprint(hout, "index=%s\n", ix->name);
537 for(i = 0; i < ix->narenas; i++){
538 arena = ix->arenas[i];
539 if(arena != nil && arena->memstats.clumps != 0){
541 clumps += arena->memstats.clumps;
542 cclumps += arena->memstats.cclumps;
543 uncsize += arena->memstats.uncsize;
544 used += arena->memstats.used;
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);
557 darena(Hio *hout, Arena *arena)
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");
567 hprint(hout, " inqueue");
569 if(scorecmp(zeroscore, arena->score) != 0)
570 hprint(hout, "\tscore=%V\n", arena->score);
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);
596 hprint(hout, "emptied all caches\n");
602 hlcacheempty(HConnect *c)
613 hprint(hout, "emptied lumpcache\n");
619 hicacheempty(HConnect *c)
630 hprint(hout, "emptied icache\n");
636 hdcacheempty(HConnect *c)
647 hprint(hout, "emptied dcache\n");
652 hicachekick(HConnect *c)
663 hprint(hout, "kicked icache\n");
669 hdcachekick(HConnect *c)
680 hprint(hout, "kicked dcache\n");
685 hicacheflush(HConnect *c)
696 hprint(hout, "flushed icache\n");
702 hdcacheflush(HConnect *c)
713 hprint(hout, "flushed dcache\n");
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]);
747 typedef struct Arg Arg;
755 rawgraph(Stats *s, Stats *t, void *va)
761 return t->n[a->index];
765 diffgraph(Stats *s, Stats *t, void *va)
770 return t->n[a->index] - s->n[a->index];
774 pctgraph(Stats *s, Stats *t, void *va)
780 return percent(t->n[a->index], t->n[a->index2]);
784 pctdiffgraph(Stats *s, Stats *t, void *va)
789 return percent(t->n[a->index]-s->n[a->index], t->n[a->index2]-s->n[a->index2]);
801 divdiffgraph(Stats *s, Stats *t, void *va)
806 return xdiv(t->n[a->index] - s->n[a->index], t->n[a->index2] - s->n[a->index2]);
815 return n[StatRpcReadBytes]+n[StatRpcWriteBytes]; /* not exactly right */
824 return n[StatApartReadBytes]+n[StatApartWriteBytes]
825 + n[StatIsectReadBytes]+n[StatIsectWriteBytes]
826 + n[StatSumReadBytes];
832 return netbw(s)+diskbw(s);
836 diskgraph(Stats *s, Stats *t, void *va)
839 return diskbw(t)-diskbw(s);
843 netgraph(Stats *s, Stats *t, void *va)
846 return netbw(t)-netbw(s);
850 iograph(Stats *s, Stats *t, void *va)
853 return iobw(t)-iobw(s);
857 static char* graphname[] =
868 "rpcreaduncachedtime",
944 for(i=0; i<nelem(graphname); i++)
945 if(strcmp(graphname[i], s) == 0)
951 dotextbin(Hio *io, Graph *g)
954 Statbin *b, bin[2000]; /* 32 kB, but whack is worse */
956 needstack(8192); /* double check that bin didn't kill us */
958 binstats(g->fn, g->arg, g->t0, g->t1, bin, nbin);
960 hprint(io, "stats\n\n");
961 for(i=0; i<nbin; i++){
963 hprint(io, "%d: nsamp=%d min=%d max=%d avg=%d\n",
964 i, b->nsamp, b->min, b->max, b->avg);
979 name = hargstr(c, "arg", "");
980 if((arg.index = findname(name)) == -1 && strcmp(name, "*") != 0){
981 werrstr("unknown name %s", name);
984 a = hargstr(c, "arg2", "");
985 if(a[0] && (arg.index2 = findname(a)) == -1){
986 werrstr("unknown name %s", a);
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);
1000 graph = hargstr(c, "graph", "raw");
1001 if(strcmp(graph, "raw") == 0)
1003 else if(strcmp(graph, "diskbw") == 0)
1005 else if(strcmp(graph, "iobw") == 0)
1007 else if(strcmp(graph, "netbw") == 0)
1009 else if(strcmp(graph, "diff") == 0)
1011 else if(strcmp(graph, "pct") == 0)
1013 else if(strcmp(graph, "pctdiff") == 0)
1014 g.fn = pctdiffgraph;
1015 else if(strcmp(graph, "divdiff") == 0)
1016 g.fn = divdiffgraph;
1018 werrstr("unknown graph %s", graph);
1023 hsettype(c, "text/plain");
1024 dotextbin(&c->hout, &g);
1033 if(hsettype(c, "image/png") < 0)
1037 qlock(&memdrawlock);
1039 qunlock(&memdrawlock);
1048 xloglist(HConnect *c)
1050 if(hsettype(c, "text/html") < 0)
1052 vtloghlist(&c->hout);
1063 name = hargstr(c, "log", "");
1066 l = vtlogopen(name, 0);
1068 return hnotfound(c);
1069 if(hsettype(c, "text/html") < 0){
1073 vtloghdump(&c->hout, l);
1082 if(hsettype(c, "text/xml") < 0)
1084 xmlindex(&c->hout, mainindex, "index", 0);
1090 xmlindent(Hio *hout, int indent)
1094 for(i = 0; i < indent; i++)
1099 xmlaname(Hio *hout, char *v, char *tag)
1101 hprint(hout, " %s=\"%s\"", tag, v);
1105 xmlscore(Hio *hout, u8int *v, char *tag)
1107 if(scorecmp(zeroscore, v) == 0)
1109 hprint(hout, " %s=\"%V\"", tag, v);
1113 xmlsealed(Hio *hout, int v, char *tag)
1117 hprint(hout, " %s=\"yes\"", tag);
1121 xmlu32int(Hio *hout, u32int v, char *tag)
1123 hprint(hout, " %s=\"%ud\"", tag, v);
1127 xmlu64int(Hio *hout, u64int v, char *tag)
1129 hprint(hout, " %s=\"%llud\"", tag, v);
1133 vtloghdump(Hio *h, VtLog *l)
1139 name = l ? l->name : "<nil>";
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);
1148 for(i=0; i<l->nchunk; i++){
1149 if(++c == l->chunk+l->nchunk)
1151 hwrite(h, c->p, c->wp-c->p);
1154 hprint(h, "</body></html>\n");
1158 strpcmp(const void *va, const void *vb)
1160 return strcmp(*(char**)va, *(char**)vb);
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");
1175 qsort(p, n, sizeof(p[0]), strpcmp);
1177 hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]);
1179 hprint(h, "</body></html>\n");