plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 : "&lt;nil&gt;";
   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 }