xref: /plan9/sys/src/cmd/venti/srv/httpd.c (revision 23566e0c4b9b5ce80121325d240a2f6e83a70a8f)
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
httpdinit(char * address,char * dir)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
httpdobj(char * name,int (* f)(HConnect *))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*
mkconnect(void)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
listenproc(void * vaddress)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
httpproc(void * v)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*
hargstr(HConnect * c,char * name,char * def)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
hargint(HConnect * c,char * name,vlong def)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
percent(ulong v,ulong total)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
preq(HConnect * c)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
hsettype(HConnect * c,char * type)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
hsethtml(HConnect * c)269 hsethtml(HConnect *c)
270 {
271 	return hsettype(c, "text/html; charset=utf-8");
272 }
273 
274 int
hsettext(HConnect * c)275 hsettext(HConnect *c)
276 {
277 	return hsettype(c, "text/plain; charset=utf-8");
278 }
279 
280 static int
herror(HConnect * c)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
hnotfound(HConnect * c)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
fromwebdir(HConnect * c)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 	0
400 };
401 
402 static int
xset(HConnect * c)403 xset(HConnect *c)
404 {
405 	int i, old;
406 	char *name, *value;
407 
408 	if(hsettext(c) < 0)
409 		return -1;
410 
411 	if((name = hargstr(c, "name", nil)) == nil || name[0] == 0){
412 		for(i=0; namedints[i].name; i++)
413 			hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p);
414 		hflush(&c->hout);
415 		return 0;
416 	}
417 
418 	for(i=0; namedints[i].name; i++)
419 		if(strcmp(name, namedints[i].name) == 0)
420 			break;
421 	if(!namedints[i].name){
422 		hprint(&c->hout, "%s not found\n", name);
423 		hflush(&c->hout);
424 		return 0;
425 	}
426 
427 	if((value = hargstr(c, "value", nil)) == nil || value[0] == 0){
428 		hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p);
429 		hflush(&c->hout);
430 		return 0;
431 	}
432 
433 	old = *namedints[i].p;
434 	*namedints[i].p = atoll(value);
435 	hprint(&c->hout, "%s = %d (was %d)\n", name, *namedints[i].p, old);
436 	hflush(&c->hout);
437 	return 0;
438 }
439 
440 static int
estats(HConnect * c)441 estats(HConnect *c)
442 {
443 	Hio *hout;
444 	int r;
445 
446 	r = hsettext(c);
447 	if(r < 0)
448 		return r;
449 
450 
451 	hout = &c->hout;
452 /*
453 	hprint(hout, "lump writes=%,ld\n", stats.lumpwrites);
454 	hprint(hout, "lump reads=%,ld\n", stats.lumpreads);
455 	hprint(hout, "lump cache read hits=%,ld\n", stats.lumphit);
456 	hprint(hout, "lump cache read misses=%,ld\n", stats.lumpmiss);
457 
458 	hprint(hout, "clump disk writes=%,ld\n", stats.clumpwrites);
459 	hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpbwrites);
460 	hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpbcomp);
461 	hprint(hout, "clump disk reads=%,ld\n", stats.clumpreads);
462 	hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpbreads);
463 	hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpbuncomp);
464 
465 	hprint(hout, "clump directory disk writes=%,ld\n", stats.ciwrites);
466 	hprint(hout, "clump directory disk reads=%,ld\n", stats.cireads);
467 
468 	hprint(hout, "index disk writes=%,ld\n", stats.indexwrites);
469 	hprint(hout, "index disk reads=%,ld\n", stats.indexreads);
470 	hprint(hout, "index disk bloom filter hits=%,ld %d%% falsemisses=%,ld %d%%\n",
471 		stats.indexbloomhits,
472 		percent(stats.indexbloomhits, stats.indexreads),
473 		stats.indexbloomfalsemisses,
474 		percent(stats.indexbloomfalsemisses, stats.indexreads));
475 	hprint(hout, "bloom filter bits=%,ld of %,ld %d%%\n",
476 		stats.bloomones, stats.bloombits, percent(stats.bloomones, stats.bloombits));
477 	hprint(hout, "index disk reads for modify=%,ld\n", stats.indexwreads);
478 	hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexareads);
479 	hprint(hout, "index block splits=%,ld\n", stats.indexsplits);
480 
481 	hprint(hout, "index cache lookups=%,ld\n", stats.iclookups);
482 	hprint(hout, "index cache hits=%,ld %d%%\n", stats.ichits,
483 		percent(stats.ichits, stats.iclookups));
484 	hprint(hout, "index cache fills=%,ld %d%%\n", stats.icfills,
485 		percent(stats.icfills, stats.iclookups));
486 	hprint(hout, "index cache inserts=%,ld\n", stats.icinserts);
487 
488 	hprint(hout, "disk cache hits=%,ld\n", stats.pchit);
489 	hprint(hout, "disk cache misses=%,ld\n", stats.pcmiss);
490 	hprint(hout, "disk cache reads=%,ld\n", stats.pcreads);
491 	hprint(hout, "disk cache bytes read=%,lld\n", stats.pcbreads);
492 
493 	hprint(hout, "disk cache writes=%,ld\n", stats.dirtydblocks);
494 	hprint(hout, "disk cache writes absorbed=%,ld %d%%\n", stats.absorbedwrites,
495 		percent(stats.absorbedwrites, stats.dirtydblocks));
496 
497 	hprint(hout, "disk cache flushes=%,ld\n", stats.dcacheflushes);
498 	hprint(hout, "disk cache flush writes=%,ld (%,ld per flush)\n",
499 		stats.dcacheflushwrites,
500 		stats.dcacheflushwrites/(stats.dcacheflushes ? stats.dcacheflushes : 1));
501 
502 	hprint(hout, "disk writes=%,ld\n", stats.diskwrites);
503 	hprint(hout, "disk bytes written=%,lld\n", stats.diskbwrites);
504 	hprint(hout, "disk reads=%,ld\n", stats.diskreads);
505 	hprint(hout, "disk bytes read=%,lld\n", stats.diskbreads);
506 */
507 
508 	hflush(hout);
509 	return 0;
510 }
511 
512 static int
sindex(HConnect * c)513 sindex(HConnect *c)
514 {
515 	Hio *hout;
516 	Index *ix;
517 	Arena *arena;
518 	vlong clumps, cclumps, uncsize, used, size;
519 	int i, r, active;
520 
521 	r = hsettext(c);
522 	if(r < 0)
523 		return r;
524 	hout = &c->hout;
525 
526 	ix = mainindex;
527 
528 	hprint(hout, "index=%s\n", ix->name);
529 
530 	active = 0;
531 	clumps = 0;
532 	cclumps = 0;
533 	uncsize = 0;
534 	used = 0;
535 	size = 0;
536 	for(i = 0; i < ix->narenas; i++){
537 		arena = ix->arenas[i];
538 		if(arena != nil && arena->memstats.clumps != 0){
539 			active++;
540 			clumps += arena->memstats.clumps;
541 			cclumps += arena->memstats.cclumps;
542 			uncsize += arena->memstats.uncsize;
543 			used += arena->memstats.used;
544 		}
545 		size += arena->size;
546 	}
547 	hprint(hout, "total arenas=%,d active=%,d\n", ix->narenas, active);
548 	hprint(hout, "total space=%,lld used=%,lld\n", size, used + clumps * ClumpInfoSize);
549 	hprint(hout, "clumps=%,lld compressed clumps=%,lld data=%,lld compressed data=%,lld\n",
550 		clumps, cclumps, uncsize, used - clumps * ClumpSize);
551 	hflush(hout);
552 	return 0;
553 }
554 
555 static void
darena(Hio * hout,Arena * arena)556 darena(Hio *hout, Arena *arena)
557 {
558 	hprint(hout, "arena='%s' on %s at [%lld,%lld)\n\tversion=%d created=%d modified=%d",
559 		arena->name, arena->part->name, arena->base, arena->base + arena->size + 2 * arena->blocksize,
560 		arena->version, arena->ctime, arena->wtime);
561 	if(arena->memstats.sealed)
562 		hprint(hout, " mem=sealed");
563 	if(arena->diskstats.sealed)
564 		hprint(hout, " disk=sealed");
565 	hprint(hout, "\n");
566 	if(scorecmp(zeroscore, arena->score) != 0)
567 		hprint(hout, "\tscore=%V\n", arena->score);
568 
569 	hprint(hout, "\twritten: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n",
570 		arena->memstats.clumps, arena->memstats.cclumps, arena->memstats.uncsize,
571 		arena->memstats.used - arena->memstats.clumps * ClumpSize,
572 		arena->memstats.used + arena->memstats.clumps * ClumpInfoSize);
573 	hprint(hout, "\tindexed: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n",
574 		arena->diskstats.clumps, arena->diskstats.cclumps, arena->diskstats.uncsize,
575 		arena->diskstats.used - arena->diskstats.clumps * ClumpSize,
576 		arena->diskstats.used + arena->diskstats.clumps * ClumpInfoSize);
577 }
578 
579 static int
hempty(HConnect * c)580 hempty(HConnect *c)
581 {
582 	Hio *hout;
583 	int r;
584 
585 	r = hsettext(c);
586 	if(r < 0)
587 		return r;
588 	hout = &c->hout;
589 
590 	emptylumpcache();
591 	emptydcache();
592 	emptyicache();
593 	hprint(hout, "emptied all caches\n");
594 	hflush(hout);
595 	return 0;
596 }
597 
598 static int
hlcacheempty(HConnect * c)599 hlcacheempty(HConnect *c)
600 {
601 	Hio *hout;
602 	int r;
603 
604 	r = hsettext(c);
605 	if(r < 0)
606 		return r;
607 	hout = &c->hout;
608 
609 	emptylumpcache();
610 	hprint(hout, "emptied lumpcache\n");
611 	hflush(hout);
612 	return 0;
613 }
614 
615 static int
hicacheempty(HConnect * c)616 hicacheempty(HConnect *c)
617 {
618 	Hio *hout;
619 	int r;
620 
621 	r = hsettext(c);
622 	if(r < 0)
623 		return r;
624 	hout = &c->hout;
625 
626 	emptyicache();
627 	hprint(hout, "emptied icache\n");
628 	hflush(hout);
629 	return 0;
630 }
631 
632 static int
hdcacheempty(HConnect * c)633 hdcacheempty(HConnect *c)
634 {
635 	Hio *hout;
636 	int r;
637 
638 	r = hsettext(c);
639 	if(r < 0)
640 		return r;
641 	hout = &c->hout;
642 
643 	emptydcache();
644 	hprint(hout, "emptied dcache\n");
645 	hflush(hout);
646 	return 0;
647 }
648 static int
hicachekick(HConnect * c)649 hicachekick(HConnect *c)
650 {
651 	Hio *hout;
652 	int r;
653 
654 	r = hsettext(c);
655 	if(r < 0)
656 		return r;
657 	hout = &c->hout;
658 
659 	kickicache();
660 	hprint(hout, "kicked icache\n");
661 	hflush(hout);
662 	return 0;
663 }
664 
665 static int
hdcachekick(HConnect * c)666 hdcachekick(HConnect *c)
667 {
668 	Hio *hout;
669 	int r;
670 
671 	r = hsettext(c);
672 	if(r < 0)
673 		return r;
674 	hout = &c->hout;
675 
676 	kickdcache();
677 	hprint(hout, "kicked dcache\n");
678 	hflush(hout);
679 	return 0;
680 }
681 static int
hicacheflush(HConnect * c)682 hicacheflush(HConnect *c)
683 {
684 	Hio *hout;
685 	int r;
686 
687 	r = hsettext(c);
688 	if(r < 0)
689 		return r;
690 	hout = &c->hout;
691 
692 	flushicache();
693 	hprint(hout, "flushed icache\n");
694 	hflush(hout);
695 	return 0;
696 }
697 
698 static int
hdcacheflush(HConnect * c)699 hdcacheflush(HConnect *c)
700 {
701 	Hio *hout;
702 	int r;
703 
704 	r = hsettext(c);
705 	if(r < 0)
706 		return r;
707 	hout = &c->hout;
708 
709 	flushdcache();
710 	hprint(hout, "flushed dcache\n");
711 	hflush(hout);
712 	return 0;
713 }
714 
715 static int
dindex(HConnect * c)716 dindex(HConnect *c)
717 {
718 	Hio *hout;
719 	Index *ix;
720 	int i, r;
721 
722 	r = hsettext(c);
723 	if(r < 0)
724 		return r;
725 	hout = &c->hout;
726 
727 
728 	ix = mainindex;
729 	hprint(hout, "index=%s version=%d blocksize=%d tabsize=%d\n",
730 		ix->name, ix->version, ix->blocksize, ix->tabsize);
731 	hprint(hout, "\tbuckets=%d div=%d\n", ix->buckets, ix->div);
732 	for(i = 0; i < ix->nsects; i++)
733 		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);
734 	for(i = 0; i < ix->narenas; i++){
735 		if(ix->arenas[i] != nil && ix->arenas[i]->memstats.clumps != 0){
736 			hprint(hout, "arena=%s at index [%lld,%lld)\n\t", ix->amap[i].name, ix->amap[i].start, ix->amap[i].stop);
737 			darena(hout, ix->arenas[i]);
738 		}
739 	}
740 	hflush(hout);
741 	return 0;
742 }
743 
744 typedef struct Arg Arg;
745 struct Arg
746 {
747 	int index;
748 	int index2;
749 };
750 
751 static long
rawgraph(Stats * s,Stats * t,void * va)752 rawgraph(Stats *s, Stats *t, void *va)
753 {
754 	Arg *a;
755 
756 	USED(s);
757 	a = va;
758 	return t->n[a->index];
759 }
760 
761 static long
diffgraph(Stats * s,Stats * t,void * va)762 diffgraph(Stats *s, Stats *t, void *va)
763 {
764 	Arg *a;
765 
766 	a = va;
767 	return t->n[a->index] - s->n[a->index];
768 }
769 
770 static long
pctgraph(Stats * s,Stats * t,void * va)771 pctgraph(Stats *s, Stats *t, void *va)
772 {
773 	Arg *a;
774 
775 	USED(s);
776 	a = va;
777 	return percent(t->n[a->index], t->n[a->index2]);
778 }
779 
780 static long
pctdiffgraph(Stats * s,Stats * t,void * va)781 pctdiffgraph(Stats *s, Stats *t, void *va)
782 {
783 	Arg *a;
784 
785 	a = va;
786 	return percent(t->n[a->index]-s->n[a->index], t->n[a->index2]-s->n[a->index2]);
787 }
788 
789 static long
xdiv(long a,long b)790 xdiv(long a, long b)
791 {
792 	if(b == 0)
793 		b++;
794 	return a/b;
795 }
796 
797 static long
divdiffgraph(Stats * s,Stats * t,void * va)798 divdiffgraph(Stats *s, Stats *t, void *va)
799 {
800 	Arg *a;
801 
802 	a = va;
803 	return xdiv(t->n[a->index] - s->n[a->index], t->n[a->index2] - s->n[a->index2]);
804 }
805 
806 static long
netbw(Stats * s)807 netbw(Stats *s)
808 {
809 	ulong *n;
810 
811 	n = s->n;
812 	return n[StatRpcReadBytes]+n[StatRpcWriteBytes];	/* not exactly right */
813 }
814 
815 static long
diskbw(Stats * s)816 diskbw(Stats *s)
817 {
818 	ulong *n;
819 
820 	n = s->n;
821 	return n[StatApartReadBytes]+n[StatApartWriteBytes]
822 		+ n[StatIsectReadBytes]+n[StatIsectWriteBytes]
823 		+ n[StatSumReadBytes];
824 }
825 
826 static long
iobw(Stats * s)827 iobw(Stats *s)
828 {
829 	return netbw(s)+diskbw(s);
830 }
831 
832 static long
diskgraph(Stats * s,Stats * t,void * va)833 diskgraph(Stats *s, Stats *t, void *va)
834 {
835 	USED(va);
836 	return diskbw(t)-diskbw(s);
837 }
838 
839 static long
netgraph(Stats * s,Stats * t,void * va)840 netgraph(Stats *s, Stats *t, void *va)
841 {
842 	USED(va);
843 	return netbw(t)-netbw(s);
844 }
845 
846 static long
iograph(Stats * s,Stats * t,void * va)847 iograph(Stats *s, Stats *t, void *va)
848 {
849 	USED(va);
850 	return iobw(t)-iobw(s);
851 }
852 
853 
854 static char* graphname[] =
855 {
856 	"rpctotal",
857 	"rpcread",
858 	"rpcreadok",
859 	"rpcreadfail",
860 	"rpcreadbyte",
861 	"rpcreadtime",
862 	"rpcreadcached",
863 	"rpcreadcachedtime",
864 	"rpcreaduncached",
865 	"rpcreaduncachedtime",
866 	"rpcwrite",
867 	"rpcwritenew",
868 	"rpcwriteold",
869 	"rpcwritefail",
870 	"rpcwritebyte",
871 	"rpcwritetime",
872 	"rpcwritenewtime",
873 	"rpcwriteoldtime",
874 
875 	"lcachehit",
876 	"lcachemiss",
877 	"lcachelookup",
878 	"lcachewrite",
879 	"lcachesize",
880 	"lcachestall",
881 	"lcachelookuptime",
882 
883 	"dcachehit",
884 	"dcachemiss",
885 	"dcachelookup",
886 	"dcacheread",
887 	"dcachewrite",
888 	"dcachedirty",
889 	"dcachesize",
890 	"dcacheflush",
891 	"dcachestall",
892 	"dcachelookuptime",
893 
894 	"dblockstall",
895 	"lumpstall",
896 
897 	"icachehit",
898 	"icachemiss",
899 	"icacheread",
900 	"icachewrite",
901 	"icachefill",
902 	"icacheprefetch",
903 	"icachedirty",
904 	"icachesize",
905 	"icacheflush",
906 	"icachestall",
907 	"icachelookuptime",
908 	"icachelookup",
909 	"scachehit",
910 	"scacheprefetch",
911 
912 	"bloomhit",
913 	"bloommiss",
914 	"bloomfalsemiss",
915 	"bloomlookup",
916 	"bloomones",
917 	"bloombits",
918 
919 	"apartread",
920 	"apartreadbyte",
921 	"apartwrite",
922 	"apartwritebyte",
923 
924 	"isectread",
925 	"isectreadbyte",
926 	"isectwrite",
927 	"isectwritebyte",
928 
929 	"sumread",
930 	"sumreadbyte",
931 
932 	"cigload",
933 	"cigloadtime",
934 };
935 
936 static int
findname(char * s)937 findname(char *s)
938 {
939 	int i;
940 
941 	for(i=0; i<nelem(graphname); i++)
942 		if(strcmp(graphname[i], s) == 0)
943 			return i;
944 	return -1;
945 }
946 
947 static void
dotextbin(Hio * io,Graph * g)948 dotextbin(Hio *io, Graph *g)
949 {
950 	int i, nbin;
951 	Statbin *b, bin[2000];	/* 32 kB, but whack is worse */
952 
953 	needstack(8192);	/* double check that bin didn't kill us */
954 	nbin = 100;
955 	binstats(g->fn, g->arg, g->t0, g->t1, bin, nbin);
956 
957 	hprint(io, "stats\n\n");
958 	for(i=0; i<nbin; i++){
959 		b = &bin[i];
960 		hprint(io, "%d: nsamp=%d min=%d max=%d avg=%d\n",
961 			i, b->nsamp, b->min, b->max, b->avg);
962 	}
963 }
964 
965 static int
xgraph(HConnect * c)966 xgraph(HConnect *c)
967 {
968 	char *name;
969 	Hio *hout;
970 	Memimage *m;
971 	int dotext;
972 	Graph g;
973 	Arg arg;
974 	char *graph, *a;
975 
976 	name = hargstr(c, "arg", "");
977 	if((arg.index = findname(name)) == -1 && strcmp(name, "*") != 0){
978 		werrstr("unknown name %s", name);
979 		goto error;
980 	}
981 	a = hargstr(c, "arg2", "");
982 	if(a[0] && (arg.index2 = findname(a)) == -1){
983 		werrstr("unknown name %s", a);
984 		goto error;
985 	}
986 
987 	g.arg = &arg;
988 	g.t0 = hargint(c, "t0", -120);
989 	g.t1 = hargint(c, "t1", 0);
990 	g.min = hargint(c, "min", -1);
991 	g.max = hargint(c, "max", -1);
992 	g.wid = hargint(c, "wid", -1);
993 	g.ht = hargint(c, "ht", -1);
994 	dotext = hargstr(c, "text", "")[0] != 0;
995 	g.fill = hargint(c, "fill", -1);
996 
997 	graph = hargstr(c, "graph", "raw");
998 	if(strcmp(graph, "raw") == 0)
999 		g.fn = rawgraph;
1000 	else if(strcmp(graph, "diskbw") == 0)
1001 		g.fn = diskgraph;
1002 	else if(strcmp(graph, "iobw") == 0)
1003 		g.fn = iograph;
1004 	else if(strcmp(graph, "netbw") == 0)
1005 		g.fn = netgraph;
1006 	else if(strcmp(graph, "diff") == 0)
1007 		g.fn = diffgraph;
1008 	else if(strcmp(graph, "pct") == 0)
1009 		g.fn = pctgraph;
1010 	else if(strcmp(graph, "pctdiff") == 0)
1011 		g.fn = pctdiffgraph;
1012 	else if(strcmp(graph, "divdiff") == 0)
1013 		g.fn = divdiffgraph;
1014 	else{
1015 		werrstr("unknown graph %s", graph);
1016 		goto error;
1017 	}
1018 
1019 	if(dotext){
1020 		hsettype(c, "text/plain");
1021 		dotextbin(&c->hout, &g);
1022 		hflush(&c->hout);
1023 		return 0;
1024 	}
1025 
1026 	m = statgraph(&g);
1027 	if(m == nil)
1028 		goto error;
1029 
1030 	if(hsettype(c, "image/png") < 0)
1031 		return -1;
1032 	hout = &c->hout;
1033 	writepng(hout, m);
1034 	qlock(&memdrawlock);
1035 	freememimage(m);
1036 	qunlock(&memdrawlock);
1037 	hflush(hout);
1038 	return 0;
1039 
1040 error:
1041 	return herror(c);
1042 }
1043 
1044 static int
xloglist(HConnect * c)1045 xloglist(HConnect *c)
1046 {
1047 	if(hsettype(c, "text/html") < 0)
1048 		return -1;
1049 	vtloghlist(&c->hout);
1050 	hflush(&c->hout);
1051 	return 0;
1052 }
1053 
1054 static int
xlog(HConnect * c)1055 xlog(HConnect *c)
1056 {
1057 	char *name;
1058 	VtLog *l;
1059 
1060 	name = hargstr(c, "log", "");
1061 	if(!name[0])
1062 		return xloglist(c);
1063 	l = vtlogopen(name, 0);
1064 	if(l == nil)
1065 		return hnotfound(c);
1066 	if(hsettype(c, "text/html") < 0){
1067 		vtlogclose(l);
1068 		return -1;
1069 	}
1070 	vtloghdump(&c->hout, l);
1071 	vtlogclose(l);
1072 	hflush(&c->hout);
1073 	return 0;
1074 }
1075 
1076 static int
xindex(HConnect * c)1077 xindex(HConnect *c)
1078 {
1079 	if(hsettype(c, "text/xml") < 0)
1080 		return -1;
1081 	xmlindex(&c->hout, mainindex, "index", 0);
1082 	hflush(&c->hout);
1083 	return 0;
1084 }
1085 
1086 void
xmlindent(Hio * hout,int indent)1087 xmlindent(Hio *hout, int indent)
1088 {
1089 	int i;
1090 
1091 	for(i = 0; i < indent; i++)
1092 		hputc(hout, '\t');
1093 }
1094 
1095 void
xmlaname(Hio * hout,char * v,char * tag)1096 xmlaname(Hio *hout, char *v, char *tag)
1097 {
1098 	hprint(hout, " %s=\"%s\"", tag, v);
1099 }
1100 
1101 void
xmlscore(Hio * hout,u8int * v,char * tag)1102 xmlscore(Hio *hout, u8int *v, char *tag)
1103 {
1104 	if(scorecmp(zeroscore, v) == 0)
1105 		return;
1106 	hprint(hout, " %s=\"%V\"", tag, v);
1107 }
1108 
1109 void
xmlsealed(Hio * hout,int v,char * tag)1110 xmlsealed(Hio *hout, int v, char *tag)
1111 {
1112 	if(!v)
1113 		return;
1114 	hprint(hout, " %s=\"yes\"", tag);
1115 }
1116 
1117 void
xmlu32int(Hio * hout,u32int v,char * tag)1118 xmlu32int(Hio *hout, u32int v, char *tag)
1119 {
1120 	hprint(hout, " %s=\"%ud\"", tag, v);
1121 }
1122 
1123 void
xmlu64int(Hio * hout,u64int v,char * tag)1124 xmlu64int(Hio *hout, u64int v, char *tag)
1125 {
1126 	hprint(hout, " %s=\"%llud\"", tag, v);
1127 }
1128 
1129 void
vtloghdump(Hio * h,VtLog * l)1130 vtloghdump(Hio *h, VtLog *l)
1131 {
1132 	int i;
1133 	VtLogChunk *c;
1134 	char *name;
1135 
1136 	name = l ? l->name : "&lt;nil&gt;";
1137 
1138 	hprint(h, "<html><head>\n");
1139 	hprint(h, "<title>Venti Server Log: %s</title>\n", name);
1140 	hprint(h, "</head><body>\n");
1141 	hprint(h, "<b>Venti Server Log: %s</b>\n<p>\n", name);
1142 
1143 	if(l){
1144 		c = l->w;
1145 		for(i=0; i<l->nchunk; i++){
1146 			if(++c == l->chunk+l->nchunk)
1147 				c = l->chunk;
1148 			hwrite(h, c->p, c->wp-c->p);
1149 		}
1150 	}
1151 	hprint(h, "</body></html>\n");
1152 }
1153 
1154 static int
strpcmp(const void * va,const void * vb)1155 strpcmp(const void *va, const void *vb)
1156 {
1157 	return strcmp(*(char**)va, *(char**)vb);
1158 }
1159 
1160 void
vtloghlist(Hio * h)1161 vtloghlist(Hio *h)
1162 {
1163 	char **p;
1164 	int i, n;
1165 
1166 	hprint(h, "<html><head>\n");
1167 	hprint(h, "<title>Venti Server Logs</title>\n");
1168 	hprint(h, "</head><body>\n");
1169 	hprint(h, "<b>Venti Server Logs</b>\n<p>\n");
1170 
1171 	p = vtlognames(&n);
1172 	qsort(p, n, sizeof(p[0]), strpcmp);
1173 	for(i=0; i<n; i++)
1174 		hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]);
1175 	vtfree(p);
1176 	hprint(h, "</body></html>\n");
1177 }
1178