#include "stdinc.h"#include "dat.h"#include "fns.h"#include "httpd.h"#include "xml.h" typedef struct HttpObj HttpObj; enum{ ObjNameSize = 64, MaxObjs = 16}; struct HttpObj{ char name[ObjNameSize]; int (*f)(HConnect*);}; static HttpObj objs[MaxObjs]; static void dolisten(void*);static int estats(HConnect *c);static int darenas(HConnect *c);static int dindex(HConnect *c);static int xindex(HConnect *c);static int sindex(HConnect *c);static int notfound(HConnect *c);static int httpdObj(char *name, int (*f)(HConnect*)); inthttpdInit(char *address){ fmtinstall('D', hdatefmt); fmtinstall('H', httpfmt); fmtinstall('U', hurlfmt); if(address == nil) address = "tcp!*!http"; httpdObj("/stats", estats); httpdObj("/index", dindex); httpdObj("/storage", sindex); httpdObj("/xindex", xindex); if(vtThread(dolisten, address) < 0) return 0; return 1;} static inthttpdObj(char *name, int (*f)(HConnect*)){ int i; if(name == nil || strlen(name) >= ObjNameSize) return 0; for(i = 0; i < MaxObjs; i++){ if(objs[i].name[0] == '\0'){ strcpy(objs[i].name, name); objs[i].f = f; return 1; } if(strcmp(objs[i].name, name) == 0) return 0; } return 0;} static HConnect*mkconnect(void){ HConnect *c; c = mallocz(sizeof(HConnect), 1); if(c == nil) sysfatal("out of memory"); c->replog = nil; c->hpos = c->header; c->hstop = c->header; return c;} static voiddolisten(void *vaddress){ HConnect *c; char *address, ndir[NETPATHLEN], dir[NETPATHLEN]; int ctl, nctl, data, ok, t, i; address = vaddress; ctl = announce(address, dir); if(ctl < 0){ fprint(2, "venti: httpd can't announce on %s: %r\n", address); return; } for(;;){ /* * wait for a call (or an error) */ nctl = listen(dir, ndir); if(nctl < 0){ fprint(2, "venti: httpd can't listen on %s: %r\m", address); return; } /* * start a process for the service */ switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG|RFMEM)){ case -1: close(nctl); continue; case 0: /* * see if we know the service requested */ data = accept(ctl, ndir); if(data < 0){ fprint(2, "venti: httpd can't open %s/data: %r", ndir); exits(nil); } close(ctl); close(nctl); c = mkconnect(); hinit(&c->hin, data, Hread); hinit(&c->hout, data, Hwrite); for(t = 15*60*1000; ; t = 15*1000){ if(hparsereq(c, t) <= 0) break; ok = -1; for(i = 0; i < MaxObjs && objs[i].name[0]; i++){ if(strcmp(c->req.uri, objs[i].name) == 0){ ok = (*objs[i].f)(c); break; } } if(i == MaxObjs) ok = notfound(c); if(c->head.closeit) ok = -1; hreqcleanup(c); if(ok < 0) break; } hreqcleanup(c); free(c); exits(nil); default: close(nctl); break; } }} static intpercent(long v, long total){ if(total == 0) total = 1; if(v < 1000*1000) return (v * 100) / total; total /= 100; if(total == 0) total = 1; return v / total;} static intpreq(HConnect *c){ if(hparseheaders(c, 15*60*1000) < 0) return -1; if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0) return hunallowed(c, "GET, HEAD"); if(c->head.expectother || c->head.expectcont) return hfail(c, HExpectFail, nil); return 1;} static intpreqtext(HConnect *c){ Hio *hout; int r; r = preq(c); if(r <= 0) return r; hout = &c->hout; if(c->req.vermaj){ hokheaders(c); hprint(hout, "Content-type: text/plain\r\n"); if(http11(c)) hprint(hout, "Transfer-Encoding: chunked\r\n"); hprint(hout, "\r\n"); } if(http11(c)) hxferenc(hout, 1); else c->head.closeit = 1; return 1;} staticnotfound(HConnect *c){ int r; r = preq(c); if(r <= 0) return r; return hfail(c, HNotFound, c->req.uri);} static intestats(HConnect *c){ Hio *hout; int r; r = preqtext(c); if(r <= 0) return r; hout = &c->hout; hprint(hout, "lump writes=%,ld\n", stats.lumpWrites); hprint(hout, "lump reads=%,ld\n", stats.lumpReads); hprint(hout, "lump cache read hits=%,ld\n", stats.lumpHit); hprint(hout, "lump cache read misses=%,ld\n", stats.lumpMiss); hprint(hout, "clump disk writes=%,ld\n", stats.clumpWrites); hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpBWrites); hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpBComp); hprint(hout, "clump disk reads=%,ld\n", stats.clumpReads); hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpBReads); hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpBUncomp); hprint(hout, "clump directory disk writes=%,ld\n", stats.ciWrites); hprint(hout, "clump directory disk reads=%,ld\n", stats.ciReads); hprint(hout, "index disk writes=%,ld\n", stats.indexWrites); hprint(hout, "index disk reads=%,ld\n", stats.indexReads); hprint(hout, "index disk reads for modify=%,ld\n", stats.indexWReads); hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexAReads); hprint(hout, "index cache lookups=%,ld\n", stats.icLookups); hprint(hout, "index cache hits=%,ld %d%%\n", stats.icHits, percent(stats.icHits, stats.icLookups)); hprint(hout, "index cache fills=%,ld %d%%\n", stats.icFills, percent(stats.icFills, stats.icLookups)); hprint(hout, "index cache inserts=%,ld\n", stats.icInserts); hprint(hout, "disk cache hits=%,ld\n", stats.pcHit); hprint(hout, "disk cache misses=%,ld\n", stats.pcMiss); hprint(hout, "disk cache reads=%,ld\n", stats.pcReads); hprint(hout, "disk cache bytes read=%,lld\n", stats.pcBReads); hprint(hout, "disk writes=%,ld\n", stats.diskWrites); hprint(hout, "disk bytes written=%,lld\n", stats.diskBWrites); hprint(hout, "disk reads=%,ld\n", stats.diskReads); hprint(hout, "disk bytes read=%,lld\n", stats.diskBReads); hflush(hout); return 1;} static intsindex(HConnect *c){ Hio *hout; Index *ix; Arena *arena; vlong clumps, cclumps, uncsize, used, size; int i, r, active; r = preqtext(c); if(r <= 0) return r; hout = &c->hout; ix = mainIndex; hprint(hout, "index=%s\n", ix->name); active = 0; clumps = 0; cclumps = 0; uncsize = 0; used = 0; size = 0; for(i = 0; i < ix->narenas; i++){ arena = ix->arenas[i]; if(arena != nil && arena->clumps != 0){ active++; clumps += arena->clumps; cclumps += arena->cclumps; uncsize += arena->uncsize; used += arena->used; } size += arena->size; } hprint(hout, "total arenas=%d active=%d\n", ix->narenas, active); hprint(hout, "total space=%lld used=%lld\n", size, used + clumps * ClumpInfoSize); hprint(hout, "clumps=%lld compressed clumps=%lld data=%lld compressed data=%lld\n", clumps, cclumps, uncsize, used - clumps * ClumpSize); hflush(hout); return 1;} static voiddArena(Hio *hout, Arena *arena){ hprint(hout, "arena='%s' on %s at [%lld,%lld)\n\tversion=%d created=%d modified=%d", arena->name, arena->part->name, arena->base, arena->base + arena->size + 2 * arena->blockSize, arena->version, arena->ctime, arena->wtime); if(arena->sealed) hprint(hout, " sealed\n"); else hprint(hout, "\n"); if(!scoreEq(zeroScore, arena->score)) hprint(hout, "\tscore=%V\n", arena->score); hprint(hout, "\tclumps=%d compressed clumps=%d data=%lld compressed data=%lld disk storage=%lld\n", arena->clumps, arena->cclumps, arena->uncsize, arena->used - arena->clumps * ClumpSize, arena->used + arena->clumps * ClumpInfoSize);} static intdindex(HConnect *c){ Hio *hout; Index *ix; int i, r; r = preqtext(c); if(r <= 0) return r; hout = &c->hout; ix = mainIndex; hprint(hout, "index=%s version=%d blockSize=%d tabSize=%d\n", ix->name, ix->version, ix->blockSize, ix->tabSize); hprint(hout, "\tbuckets=%d div=%d\n", ix->buckets, ix->div); for(i = 0; i < ix->nsects; i++) hprint(hout, "\tsect=%s for buckets [%lld,%lld)\n", ix->smap[i].name, ix->smap[i].start, ix->smap[i].stop); for(i = 0; i < ix->narenas; i++){ if(ix->arenas[i] != nil && ix->arenas[i]->clumps != 0){ hprint(hout, "arena=%s at index [%lld,%lld)\n\t", ix->amap[i].name, ix->amap[i].start, ix->amap[i].stop); dArena(hout, ix->arenas[i]); } } hflush(hout); return 1;} static intxindex(HConnect *c){ Hio *hout; int r; r = preq(c); if(r <= 0) return r; hout = &c->hout; if(c->req.vermaj){ hokheaders(c); hprint(hout, "Content-type: text/xml\r\n"); if(http11(c)) hprint(hout, "Transfer-Encoding: chunked\r\n"); hprint(hout, "\r\n"); } if(http11(c)) hxferenc(hout, 1); else c->head.closeit = 1; xmlIndex(hout, mainIndex, "index", 0); hflush(hout); return 1;} voidxmlIndent(Hio *hout, int indent){ int i; for(i = 0; i < indent; i++) hputc(hout, '\t');} voidxmlAName(Hio *hout, char *v, char *tag){ hprint(hout, " %s=\"%s\"", tag, v);} voidxmlScore(Hio *hout, u8int *v, char *tag){ if(scoreEq(zeroScore, v)) return; hprint(hout, " %s=\"%V\"", tag, v);} voidxmlSealed(Hio *hout, int v, char *tag){ if(!v) return; hprint(hout, " %s=\"yes\"", tag);} voidxmlU32int(Hio *hout, u32int v, char *tag){ hprint(hout, " %s=\"%ud\"", tag, v);} voidxmlU64int(Hio *hout, u64int v, char *tag){ hprint(hout, " %s=\"%llud\"", tag, v);}
#include "stdinc.h"
#include "dat.h"
#include "fns.h"
#include "httpd.h"
#include "xml.h"
typedef struct HttpObj HttpObj;
enum
{
ObjNameSize = 64,
MaxObjs = 16
};
struct HttpObj
{
char name[ObjNameSize];
int (*f)(HConnect*);
};
static HttpObj objs[MaxObjs];
static void dolisten(void*);
static int estats(HConnect *c);
static int darenas(HConnect *c);
static int dindex(HConnect *c);
static int xindex(HConnect *c);
static int sindex(HConnect *c);
static int notfound(HConnect *c);
static int httpdObj(char *name, int (*f)(HConnect*));
int
httpdInit(char *address)
{
fmtinstall('D', hdatefmt);
fmtinstall('H', httpfmt);
fmtinstall('U', hurlfmt);
if(address == nil)
address = "tcp!*!http";
httpdObj("/stats", estats);
httpdObj("/index", dindex);
httpdObj("/storage", sindex);
httpdObj("/xindex", xindex);
if(vtThread(dolisten, address) < 0)
return 0;
return 1;
}
static int
httpdObj(char *name, int (*f)(HConnect*))
{
int i;
if(name == nil || strlen(name) >= ObjNameSize)
return 0;
for(i = 0; i < MaxObjs; i++){
if(objs[i].name[0] == '\0'){
strcpy(objs[i].name, name);
objs[i].f = f;
return 1;
}
if(strcmp(objs[i].name, name) == 0)
return 0;
}
return 0;
}
static HConnect*
mkconnect(void)
{
HConnect *c;
c = mallocz(sizeof(HConnect), 1);
if(c == nil)
sysfatal("out of memory");
c->replog = nil;
c->hpos = c->header;
c->hstop = c->header;
return c;
}
static void
dolisten(void *vaddress)
{
HConnect *c;
char *address, ndir[NETPATHLEN], dir[NETPATHLEN];
int ctl, nctl, data, ok, t, i;
address = vaddress;
ctl = announce(address, dir);
if(ctl < 0){
fprint(2, "venti: httpd can't announce on %s: %r\n", address);
return;
}
for(;;){
/*
* wait for a call (or an error)
*/
nctl = listen(dir, ndir);
if(nctl < 0){
fprint(2, "venti: httpd can't listen on %s: %r\m", address);
return;
}
/*
* start a process for the service
*/
switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFNAMEG|RFMEM)){
case -1:
close(nctl);
continue;
case 0:
/*
* see if we know the service requested
*/
data = accept(ctl, ndir);
if(data < 0){
fprint(2, "venti: httpd can't open %s/data: %r", ndir);
exits(nil);
}
close(ctl);
close(nctl);
c = mkconnect();
hinit(&c->hin, data, Hread);
hinit(&c->hout, data, Hwrite);
for(t = 15*60*1000; ; t = 15*1000){
if(hparsereq(c, t) <= 0)
break;
ok = -1;
for(i = 0; i < MaxObjs && objs[i].name[0]; i++){
if(strcmp(c->req.uri, objs[i].name) == 0){
ok = (*objs[i].f)(c);
break;
}
}
if(i == MaxObjs)
ok = notfound(c);
if(c->head.closeit)
ok = -1;
hreqcleanup(c);
if(ok < 0)
break;
}
hreqcleanup(c);
free(c);
exits(nil);
default:
close(nctl);
break;
}
}
}
static int
percent(long v, long total)
{
if(total == 0)
total = 1;
if(v < 1000*1000)
return (v * 100) / total;
total /= 100;
if(total == 0)
total = 1;
return v / total;
}
static int
preq(HConnect *c)
{
if(hparseheaders(c, 15*60*1000) < 0)
return -1;
if(strcmp(c->req.meth, "GET") != 0
&& strcmp(c->req.meth, "HEAD") != 0)
return hunallowed(c, "GET, HEAD");
if(c->head.expectother || c->head.expectcont)
return hfail(c, HExpectFail, nil);
return 1;
}
static int
preqtext(HConnect *c)
{
Hio *hout;
int r;
r = preq(c);
if(r <= 0)
return r;
hout = &c->hout;
if(c->req.vermaj){
hokheaders(c);
hprint(hout, "Content-type: text/plain\r\n");
if(http11(c))
hprint(hout, "Transfer-Encoding: chunked\r\n");
hprint(hout, "\r\n");
}
if(http11(c))
hxferenc(hout, 1);
else
c->head.closeit = 1;
return 1;
}
static
notfound(HConnect *c)
{
int r;
r = preq(c);
if(r <= 0)
return r;
return hfail(c, HNotFound, c->req.uri);
}
static int
estats(HConnect *c)
{
Hio *hout;
int r;
r = preqtext(c);
if(r <= 0)
return r;
hout = &c->hout;
hprint(hout, "lump writes=%,ld\n", stats.lumpWrites);
hprint(hout, "lump reads=%,ld\n", stats.lumpReads);
hprint(hout, "lump cache read hits=%,ld\n", stats.lumpHit);
hprint(hout, "lump cache read misses=%,ld\n", stats.lumpMiss);
hprint(hout, "clump disk writes=%,ld\n", stats.clumpWrites);
hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpBWrites);
hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpBComp);
hprint(hout, "clump disk reads=%,ld\n", stats.clumpReads);
hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpBReads);
hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpBUncomp);
hprint(hout, "clump directory disk writes=%,ld\n", stats.ciWrites);
hprint(hout, "clump directory disk reads=%,ld\n", stats.ciReads);
hprint(hout, "index disk writes=%,ld\n", stats.indexWrites);
hprint(hout, "index disk reads=%,ld\n", stats.indexReads);
hprint(hout, "index disk reads for modify=%,ld\n", stats.indexWReads);
hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexAReads);
hprint(hout, "index cache lookups=%,ld\n", stats.icLookups);
hprint(hout, "index cache hits=%,ld %d%%\n", stats.icHits,
percent(stats.icHits, stats.icLookups));
hprint(hout, "index cache fills=%,ld %d%%\n", stats.icFills,
percent(stats.icFills, stats.icLookups));
hprint(hout, "index cache inserts=%,ld\n", stats.icInserts);
hprint(hout, "disk cache hits=%,ld\n", stats.pcHit);
hprint(hout, "disk cache misses=%,ld\n", stats.pcMiss);
hprint(hout, "disk cache reads=%,ld\n", stats.pcReads);
hprint(hout, "disk cache bytes read=%,lld\n", stats.pcBReads);
hprint(hout, "disk writes=%,ld\n", stats.diskWrites);
hprint(hout, "disk bytes written=%,lld\n", stats.diskBWrites);
hprint(hout, "disk reads=%,ld\n", stats.diskReads);
hprint(hout, "disk bytes read=%,lld\n", stats.diskBReads);
hflush(hout);
return 1;
}
static int
sindex(HConnect *c)
{
Hio *hout;
Index *ix;
Arena *arena;
vlong clumps, cclumps, uncsize, used, size;
int i, r, active;
r = preqtext(c);
if(r <= 0)
return r;
hout = &c->hout;
ix = mainIndex;
hprint(hout, "index=%s\n", ix->name);
active = 0;
clumps = 0;
cclumps = 0;
uncsize = 0;
used = 0;
size = 0;
for(i = 0; i < ix->narenas; i++){
arena = ix->arenas[i];
if(arena != nil && arena->clumps != 0){
active++;
clumps += arena->clumps;
cclumps += arena->cclumps;
uncsize += arena->uncsize;
used += arena->used;
}
size += arena->size;
}
hprint(hout, "total arenas=%d active=%d\n", ix->narenas, active);
hprint(hout, "total space=%lld used=%lld\n", size, used + clumps * ClumpInfoSize);
hprint(hout, "clumps=%lld compressed clumps=%lld data=%lld compressed data=%lld\n",
clumps, cclumps, uncsize, used - clumps * ClumpSize);
hflush(hout);
return 1;
}
static void
dArena(Hio *hout, Arena *arena)
{
hprint(hout, "arena='%s' on %s at [%lld,%lld)\n\tversion=%d created=%d modified=%d",
arena->name, arena->part->name, arena->base, arena->base + arena->size + 2 * arena->blockSize,
arena->version, arena->ctime, arena->wtime);
if(arena->sealed)
hprint(hout, " sealed\n");
else
hprint(hout, "\n");
if(!scoreEq(zeroScore, arena->score))
hprint(hout, "\tscore=%V\n", arena->score);
hprint(hout, "\tclumps=%d compressed clumps=%d data=%lld compressed data=%lld disk storage=%lld\n",
arena->clumps, arena->cclumps, arena->uncsize,
arena->used - arena->clumps * ClumpSize,
arena->used + arena->clumps * ClumpInfoSize);
}
static int
dindex(HConnect *c)
{
Hio *hout;
Index *ix;
int i, r;
r = preqtext(c);
if(r <= 0)
return r;
hout = &c->hout;
ix = mainIndex;
hprint(hout, "index=%s version=%d blockSize=%d tabSize=%d\n",
ix->name, ix->version, ix->blockSize, ix->tabSize);
hprint(hout, "\tbuckets=%d div=%d\n", ix->buckets, ix->div);
for(i = 0; i < ix->nsects; i++)
hprint(hout, "\tsect=%s for buckets [%lld,%lld)\n", ix->smap[i].name, ix->smap[i].start, ix->smap[i].stop);
for(i = 0; i < ix->narenas; i++){
if(ix->arenas[i] != nil && ix->arenas[i]->clumps != 0){
hprint(hout, "arena=%s at index [%lld,%lld)\n\t", ix->amap[i].name, ix->amap[i].start, ix->amap[i].stop);
dArena(hout, ix->arenas[i]);
}
}
hflush(hout);
return 1;
}
static int
xindex(HConnect *c)
{
Hio *hout;
int r;
r = preq(c);
if(r <= 0)
return r;
hout = &c->hout;
if(c->req.vermaj){
hokheaders(c);
hprint(hout, "Content-type: text/xml\r\n");
if(http11(c))
hprint(hout, "Transfer-Encoding: chunked\r\n");
hprint(hout, "\r\n");
}
if(http11(c))
hxferenc(hout, 1);
else
c->head.closeit = 1;
xmlIndex(hout, mainIndex, "index", 0);
hflush(hout);
return 1;
}
void
xmlIndent(Hio *hout, int indent)
{
int i;
for(i = 0; i < indent; i++)
hputc(hout, '\t');
}
void
xmlAName(Hio *hout, char *v, char *tag)
{
hprint(hout, " %s=\"%s\"", tag, v);
}
void
xmlScore(Hio *hout, u8int *v, char *tag)
{
if(scoreEq(zeroScore, v))
return;
hprint(hout, " %s=\"%V\"", tag, v);
}
void
xmlSealed(Hio *hout, int v, char *tag)
{
if(!v)
return;
hprint(hout, " %s=\"yes\"", tag);
}
void
xmlU32int(Hio *hout, u32int v, char *tag)
{
hprint(hout, " %s=\"%ud\"", tag, v);
}
void
xmlU64int(Hio *hout, u64int v, char *tag)
{
hprint(hout, " %s=\"%llud\"", tag, v);
}