| #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 listenproc(void*); |
| static int estats(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(vtproc(listenproc, address) < 0) |
| return -1; |
| return 0; |
| } |
| |
| static int |
| httpdobj(char *name, int (*f)(HConnect*)) |
| { |
| int i; |
| |
| if(name == nil || strlen(name) >= ObjNameSize) |
| return -1; |
| for(i = 0; i < MaxObjs; i++){ |
| if(objs[i].name[0] == '\0'){ |
| strcpy(objs[i].name, name); |
| objs[i].f = f; |
| return 0; |
| } |
| if(strcmp(objs[i].name, name) == 0) |
| return -1; |
| } |
| return -1; |
| } |
| |
| 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; |
| } |
| |
| void httpproc(void*); |
| |
| static void |
| listenproc(void *vaddress) |
| { |
| HConnect *c; |
| char *address, ndir[NETPATHLEN], dir[NETPATHLEN]; |
| int ctl, nctl, data; |
| |
| //sleep(1000); /* let strace find us */ |
| |
| address = vaddress; |
| ctl = announce(address, dir); |
| if(ctl < 0){ |
| fprint(2, "venti: httpd can't announce on %s: %r\n", address); |
| return; |
| } |
| |
| print("announce ctl %d dir %s\n", ctl, dir); |
| for(;;){ |
| /* |
| * wait for a call (or an error) |
| */ |
| nctl = listen(dir, ndir); |
| print("httpd listen %d %s...\n", nctl, ndir); |
| if(nctl < 0){ |
| fprint(2, "venti: httpd can't listen on %s: %r\n", address); |
| return; |
| } |
| |
| data = accept(ctl, ndir); |
| print("httpd accept %d...\n", data); |
| if(data < 0){ |
| fprint(2, "venti: httpd accept: %r\n"); |
| close(nctl); |
| continue; |
| } |
| print("httpd close nctl %d\n", nctl); |
| close(nctl); |
| c = mkconnect(); |
| hinit(&c->hin, data, Hread); |
| hinit(&c->hout, data, Hwrite); |
| vtproc(httpproc, c); |
| } |
| } |
| |
| void |
| httpproc(void *v) |
| { |
| HConnect *c; |
| int ok, t, i; |
| |
| //sleep(1000); /* let strace find us */ |
| c = v; |
| |
| 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; |
| } |
| print("httpd cleanup %d\n", c->hin.fd); |
| hreqcleanup(c); |
| print("close %d\n", c->hin.fd); |
| close(c->hin.fd); |
| free(c); |
| } |
| |
| 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 0; |
| } |
| |
| 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 0; |
| } |
| |
| static int |
| 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 0; |
| } |
| |
| 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 0; |
| } |
| |
| 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(scorecmp(zeroscore, arena->score) != 0) |
| 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 0; |
| } |
| |
| 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 0; |
| } |
| |
| 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(scorecmp(zeroscore, v) == 0) |
| 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); |
| } |