xref: /plan9/sys/src/cmd/webfs/client.c (revision 360053c830964336c451d94d3711af93cb0045f0)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <ip.h>
59a747e4fSDavid du Colombier #include <plumb.h>
69a747e4fSDavid du Colombier #include <thread.h>
79a747e4fSDavid du Colombier #include <fcall.h>
89a747e4fSDavid du Colombier #include <9p.h>
99a747e4fSDavid du Colombier #include "dat.h"
109a747e4fSDavid du Colombier #include "fns.h"
119a747e4fSDavid du Colombier 
129a747e4fSDavid du Colombier int nclient;
139a747e4fSDavid du Colombier Client **client;
149a747e4fSDavid du Colombier 
159a747e4fSDavid du Colombier static void clientthread(void*);
169a747e4fSDavid du Colombier int
newclient(int plumbed)179a747e4fSDavid du Colombier newclient(int plumbed)
189a747e4fSDavid du Colombier {
199a747e4fSDavid du Colombier 	int i;
209a747e4fSDavid du Colombier 	Client *c;
219a747e4fSDavid du Colombier 
229a747e4fSDavid du Colombier 	for(i=0; i<nclient; i++)
239a747e4fSDavid du Colombier 		if(client[i]->ref==0)
249a747e4fSDavid du Colombier 			return i;
259a747e4fSDavid du Colombier 
269a747e4fSDavid du Colombier 	c = emalloc(sizeof(Client));
279a747e4fSDavid du Colombier 	c->plumbed = plumbed;
289a747e4fSDavid du Colombier 	c->creq = chancreate(sizeof(Req*), 8);
299a747e4fSDavid du Colombier 	threadcreate(clientthread, c, STACK);
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	c->io = ioproc();
329a747e4fSDavid du Colombier 	c->num = nclient;
339a747e4fSDavid du Colombier 	c->ctl = globalctl;
349a747e4fSDavid du Colombier 	clonectl(&c->ctl);
359a747e4fSDavid du Colombier 	if(nclient%16 == 0)
369a747e4fSDavid du Colombier 		client = erealloc(client, (nclient+16)*sizeof(client[0]));
379a747e4fSDavid du Colombier 	client[nclient++] = c;
389a747e4fSDavid du Colombier 	return nclient-1;
399a747e4fSDavid du Colombier }
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier void
closeclient(Client * c)429a747e4fSDavid du Colombier closeclient(Client *c)
439a747e4fSDavid du Colombier {
449a747e4fSDavid du Colombier 	if(--c->ref == 0){
456b6b9ac8SDavid du Colombier 		if(c->bodyopened){
469a747e4fSDavid du Colombier 			if(c->url && c->url->close)
479a747e4fSDavid du Colombier 				(*c->url->close)(c);
486b6b9ac8SDavid du Colombier 			c->bodyopened = 0;
496b6b9ac8SDavid du Colombier 		}
509a747e4fSDavid du Colombier 		free(c->contenttype);
519a747e4fSDavid du Colombier 		c->contenttype = nil;
529a747e4fSDavid du Colombier 		free(c->postbody);
539a747e4fSDavid du Colombier 		c->postbody = nil;
549a747e4fSDavid du Colombier 		freeurl(c->url);
559a747e4fSDavid du Colombier 		c->url = nil;
569a747e4fSDavid du Colombier 		free(c->redirect);
579a747e4fSDavid du Colombier 		c->redirect = nil;
589dfc0cb2SDavid du Colombier 		free(c->authenticate);
599dfc0cb2SDavid du Colombier 		c->authenticate = nil;
609a747e4fSDavid du Colombier 		c->npostbody = 0;
619a747e4fSDavid du Colombier 		c->havepostbody = 0;
629a747e4fSDavid du Colombier 		c->bodyopened = 0;
639a747e4fSDavid du Colombier 	}
649a747e4fSDavid du Colombier }
659a747e4fSDavid du Colombier 
669a747e4fSDavid du Colombier void
clonectl(Ctl * c)679a747e4fSDavid du Colombier clonectl(Ctl *c)
689a747e4fSDavid du Colombier {
699a747e4fSDavid du Colombier 	if(c->useragent)
709a747e4fSDavid du Colombier 		c->useragent = estrdup(c->useragent);
719a747e4fSDavid du Colombier }
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier void
clientbodyopen(Client * c,Req * r)749a747e4fSDavid du Colombier clientbodyopen(Client *c, Req *r)
759a747e4fSDavid du Colombier {
769a747e4fSDavid du Colombier 	char e[ERRMAX], *next;
77*360053c8SDavid du Colombier 	int i, nauth;
789a747e4fSDavid du Colombier 	Url *u;
799a747e4fSDavid du Colombier 
80*360053c8SDavid du Colombier 	nauth = 0;
819a747e4fSDavid du Colombier 	next = nil;
829a747e4fSDavid du Colombier 	for(i=0; i<=c->ctl.redirectlimit; i++){
83d9306527SDavid du Colombier 		if(c->url == nil){
84d9306527SDavid du Colombier 			werrstr("nil url");
85d9306527SDavid du Colombier 			goto Error;
86d9306527SDavid du Colombier 		}
87d9306527SDavid du Colombier 		if(c->url->open == nil){
88d9306527SDavid du Colombier 			werrstr("unsupported url type");
89d9306527SDavid du Colombier 			goto Error;
90d9306527SDavid du Colombier 		}
919a747e4fSDavid du Colombier 		if(fsdebug)
929a747e4fSDavid du Colombier 			fprint(2, "try %s\n", c->url->url);
939a747e4fSDavid du Colombier 		if(c->url->open(c, c->url) < 0){
949a747e4fSDavid du Colombier 		Error:
959a747e4fSDavid du Colombier 			if(next)
969a747e4fSDavid du Colombier 				fprint(2, "next %s (but for error)\n", next);
979a747e4fSDavid du Colombier 			free(next);
989a747e4fSDavid du Colombier 			rerrstr(e, sizeof e);
999a747e4fSDavid du Colombier 			c->iobusy = 0;
1009a747e4fSDavid du Colombier 			if(r != nil)
1019a747e4fSDavid du Colombier 				r->fid->omode = -1;
1029a747e4fSDavid du Colombier 			closeclient(c);	/* not opening */
1039a747e4fSDavid du Colombier 			if(r != nil)
1049a747e4fSDavid du Colombier 				respond(r, e);
1059a747e4fSDavid du Colombier 			return;
1069a747e4fSDavid du Colombier 		}
107*360053c8SDavid du Colombier 		if (c->authenticate && nauth++ < 1)
1089dfc0cb2SDavid du Colombier 				continue;
1099a747e4fSDavid du Colombier 		if(!c->redirect)
1109a747e4fSDavid du Colombier 			break;
1119a747e4fSDavid du Colombier 		next = c->redirect;
1129a747e4fSDavid du Colombier 		c->redirect = nil;
1139a747e4fSDavid du Colombier 		if(i==c->ctl.redirectlimit){
1149a747e4fSDavid du Colombier 			werrstr("redirect limit reached");
1159a747e4fSDavid du Colombier 			goto Error;
1169a747e4fSDavid du Colombier 		}
1179a747e4fSDavid du Colombier 		if((u = parseurl(next, c->url)) == nil)
1189a747e4fSDavid du Colombier 			goto Error;
1199a747e4fSDavid du Colombier 		if(urldebug)
1209a747e4fSDavid du Colombier 			fprint(2, "parseurl %s got scheme %d\n", next, u->ischeme);
1219a747e4fSDavid du Colombier 		if(u->ischeme == USunknown){
1229a747e4fSDavid du Colombier 			werrstr("redirect with unknown URL scheme");
1239a747e4fSDavid du Colombier 			goto Error;
1249a747e4fSDavid du Colombier 		}
1259a747e4fSDavid du Colombier 		if(u->ischeme == UScurrent){
1269a747e4fSDavid du Colombier 			werrstr("redirect to URL relative to current document");
1279a747e4fSDavid du Colombier 			goto Error;
1289a747e4fSDavid du Colombier 		}
1299a747e4fSDavid du Colombier 		freeurl(c->url);
1309a747e4fSDavid du Colombier 		c->url = u;
1319a747e4fSDavid du Colombier 	}
1329a747e4fSDavid du Colombier 	free(next);
1339a747e4fSDavid du Colombier 	c->iobusy = 0;
1349a747e4fSDavid du Colombier 	if(r != nil)
1359a747e4fSDavid du Colombier 		respond(r, nil);
1369a747e4fSDavid du Colombier }
1379a747e4fSDavid du Colombier 
1389a747e4fSDavid du Colombier void
plumburl(char * url,char * base)1399a747e4fSDavid du Colombier plumburl(char *url, char *base)
1409a747e4fSDavid du Colombier {
1419a747e4fSDavid du Colombier 	int i;
1429a747e4fSDavid du Colombier 	Client *c;
1435e91980fSDavid du Colombier 	Url *ubase, *uurl;
1449a747e4fSDavid du Colombier 
1455e91980fSDavid du Colombier 	ubase = nil;
1465e91980fSDavid du Colombier 	if(base){
1475e91980fSDavid du Colombier 		ubase = parseurl(base, nil);
1485e91980fSDavid du Colombier 		if(ubase == nil)
1495e91980fSDavid du Colombier 			return;
1505e91980fSDavid du Colombier 	}
1515e91980fSDavid du Colombier 	uurl = parseurl(url, ubase);
1525e91980fSDavid du Colombier 	if(uurl == nil){
1535e91980fSDavid du Colombier 		freeurl(ubase);
1545e91980fSDavid du Colombier 		return;
1555e91980fSDavid du Colombier 	}
1569a747e4fSDavid du Colombier 	i = newclient(1);
1579a747e4fSDavid du Colombier 	c = client[i];
1589a747e4fSDavid du Colombier 	c->ref++;
1595e91980fSDavid du Colombier 	c->baseurl = ubase;
1605e91980fSDavid du Colombier 	c->url = uurl;
1619a747e4fSDavid du Colombier 	sendp(c->creq, nil);
1629a747e4fSDavid du Colombier }
1639a747e4fSDavid du Colombier 
1649a747e4fSDavid du Colombier void
clientbodyread(Client * c,Req * r)1659a747e4fSDavid du Colombier clientbodyread(Client *c, Req *r)
1669a747e4fSDavid du Colombier {
1679a747e4fSDavid du Colombier 	char e[ERRMAX];
1689a747e4fSDavid du Colombier 
169d9306527SDavid du Colombier 	if(c->url->read == nil){
170d9306527SDavid du Colombier 		respond(r, "unsupported url type");
171d9306527SDavid du Colombier 		return;
172d9306527SDavid du Colombier 	}
1739a747e4fSDavid du Colombier 	if(c->url->read(c, r) < 0){
1749a747e4fSDavid du Colombier 		rerrstr(e, sizeof e);
1759a747e4fSDavid du Colombier 		c->iobusy = 0;
1769a747e4fSDavid du Colombier 		respond(r, e);
177d9306527SDavid du Colombier 		return;
1789a747e4fSDavid du Colombier 	}
1799a747e4fSDavid du Colombier 	c->iobusy = 0;
1809a747e4fSDavid du Colombier 	respond(r, nil);
1819a747e4fSDavid du Colombier }
1829a747e4fSDavid du Colombier 
1839a747e4fSDavid du Colombier static void
clientthread(void * a)1849a747e4fSDavid du Colombier clientthread(void *a)
1859a747e4fSDavid du Colombier {
1869a747e4fSDavid du Colombier 	Client *c;
1879a747e4fSDavid du Colombier 	Req *r;
1889a747e4fSDavid du Colombier 
1899a747e4fSDavid du Colombier 	c = a;
1909a747e4fSDavid du Colombier 	if(c->plumbed) {
1919a747e4fSDavid du Colombier 		recvp(c->creq);
1925e91980fSDavid du Colombier 		if(c->url == nil){
1935e91980fSDavid du Colombier 			fprint(2, "bad url got plumbed\n");
1945e91980fSDavid du Colombier 			return;
1955e91980fSDavid du Colombier 		}
1969a747e4fSDavid du Colombier 		clientbodyopen(c, nil);
1979a747e4fSDavid du Colombier 		replumb(c);
1989a747e4fSDavid du Colombier 	}
1999a747e4fSDavid du Colombier 	while((r = recvp(c->creq)) != nil){
2009a747e4fSDavid du Colombier 		if(fsdebug)
2019a747e4fSDavid du Colombier 			fprint(2, "clientthread %F\n", &r->ifcall);
2029a747e4fSDavid du Colombier 		switch(r->ifcall.type){
2039a747e4fSDavid du Colombier 		case Topen:
2049a747e4fSDavid du Colombier 			if(c->plumbed) {
2059a747e4fSDavid du Colombier 				c->plumbed = 0;
2069a747e4fSDavid du Colombier 				c->ref--;			/* from plumburl() */
2079a747e4fSDavid du Colombier 				respond(r, nil);
2089a747e4fSDavid du Colombier 			}
2099a747e4fSDavid du Colombier 			else
2109a747e4fSDavid du Colombier 				clientbodyopen(c, r);
2119a747e4fSDavid du Colombier 			break;
2129a747e4fSDavid du Colombier 		case Tread:
2139a747e4fSDavid du Colombier 			clientbodyread(c, r);
2149a747e4fSDavid du Colombier 			break;
2159a747e4fSDavid du Colombier 		case Tflush:
2169a747e4fSDavid du Colombier 			respond(r, nil);
2179a747e4fSDavid du Colombier 		}
2189a747e4fSDavid du Colombier 		if(fsdebug)
2199a747e4fSDavid du Colombier 			fprint(2, "clientthread finished req\n");
2209a747e4fSDavid du Colombier 	}
2219a747e4fSDavid du Colombier }
2229a747e4fSDavid du Colombier 
2239a747e4fSDavid du Colombier enum
2249a747e4fSDavid du Colombier {
2259a747e4fSDavid du Colombier 	Bool,
2269a747e4fSDavid du Colombier 	Int,
2279a747e4fSDavid du Colombier 	String,
2289a747e4fSDavid du Colombier 	XUrl,
2299a747e4fSDavid du Colombier 	Fn,
2309a747e4fSDavid du Colombier };
2319a747e4fSDavid du Colombier 
2329a747e4fSDavid du Colombier typedef struct Ctab Ctab;
2339a747e4fSDavid du Colombier struct Ctab {
2349a747e4fSDavid du Colombier 	char *name;
2359a747e4fSDavid du Colombier 	int type;
2369a747e4fSDavid du Colombier 	void *offset;
2379a747e4fSDavid du Colombier };
2389a747e4fSDavid du Colombier 
2399a747e4fSDavid du Colombier Ctab ctltab[] = {
2409a747e4fSDavid du Colombier 	"acceptcookies",	Bool,		(void*)offsetof(Ctl, acceptcookies),
2419a747e4fSDavid du Colombier 	"sendcookies",		Bool,		(void*)offsetof(Ctl, sendcookies),
2429a747e4fSDavid du Colombier 	"redirectlimit",		Int,		(void*)offsetof(Ctl, redirectlimit),
2439a747e4fSDavid du Colombier 	"useragent",		String,	(void*)offsetof(Ctl, useragent),
2449a747e4fSDavid du Colombier };
2459a747e4fSDavid du Colombier 
2469a747e4fSDavid du Colombier Ctab globaltab[] = {
2479a747e4fSDavid du Colombier 	"chatty9p",		Int,		&chatty9p,
2489a747e4fSDavid du Colombier 	"fsdebug",		Int,		&fsdebug,
2499a747e4fSDavid du Colombier 	"cookiedebug",		Int,		&cookiedebug,
2509a747e4fSDavid du Colombier 	"urldebug",		Int,		&urldebug,
2519a747e4fSDavid du Colombier 	"httpdebug",		Int,		&httpdebug,
2529a747e4fSDavid du Colombier };
2539a747e4fSDavid du Colombier 
2549a747e4fSDavid du Colombier Ctab clienttab[] = {
2559a747e4fSDavid du Colombier 	"baseurl",			XUrl,		(void*)offsetof(Client, baseurl),
2569a747e4fSDavid du Colombier 	"url",				XUrl,		(void*)offsetof(Client, url),
2579a747e4fSDavid du Colombier };
2589a747e4fSDavid du Colombier 
2599a747e4fSDavid du Colombier static Ctab*
findcmd(char * cmd,Ctab * tab,int ntab)2609a747e4fSDavid du Colombier findcmd(char *cmd, Ctab *tab, int ntab)
2619a747e4fSDavid du Colombier {
2629a747e4fSDavid du Colombier 	int i;
2639a747e4fSDavid du Colombier 
2649a747e4fSDavid du Colombier 	for(i=0; i<ntab; i++)
2659a747e4fSDavid du Colombier 		if(strcmp(tab[i].name, cmd) == 0)
2669a747e4fSDavid du Colombier 			return &tab[i];
2679a747e4fSDavid du Colombier 	return nil;
2689a747e4fSDavid du Colombier }
2699a747e4fSDavid du Colombier 
2709a747e4fSDavid du Colombier static void
parseas(Req * r,char * arg,int type,void * a)2719a747e4fSDavid du Colombier parseas(Req *r, char *arg, int type, void *a)
2729a747e4fSDavid du Colombier {
2739a747e4fSDavid du Colombier 	Url *u;
2749a747e4fSDavid du Colombier 	char e[ERRMAX];
2759a747e4fSDavid du Colombier 
2769a747e4fSDavid du Colombier 	switch(type){
2779a747e4fSDavid du Colombier 	case Bool:
2789a747e4fSDavid du Colombier 		if(strcmp(arg, "on")==0 || strcmp(arg, "1")==0)
2799a747e4fSDavid du Colombier 			*(int*)a = 1;
2809a747e4fSDavid du Colombier 		else
2819a747e4fSDavid du Colombier 			*(int*)a = 0;
2829a747e4fSDavid du Colombier 		break;
2839a747e4fSDavid du Colombier 	case String:
2849a747e4fSDavid du Colombier 		free(*(char**)a);
2859a747e4fSDavid du Colombier 		*(char**)a = estrdup(arg);
2869a747e4fSDavid du Colombier 		break;
2879a747e4fSDavid du Colombier 	case XUrl:
2889a747e4fSDavid du Colombier 		u = parseurl(arg, nil);
2899a747e4fSDavid du Colombier 		if(u == nil){
2909a747e4fSDavid du Colombier 			snprint(e, sizeof e, "parseurl: %r");
2919a747e4fSDavid du Colombier 			respond(r, e);
2929a747e4fSDavid du Colombier 			return;
2939a747e4fSDavid du Colombier 		}
2949a747e4fSDavid du Colombier 		freeurl(*(Url**)a);
2959a747e4fSDavid du Colombier 		*(Url**)a = u;
2969a747e4fSDavid du Colombier 		break;
2979a747e4fSDavid du Colombier 	case Int:
2989a747e4fSDavid du Colombier 		if(strcmp(arg, "on")==0)
2999a747e4fSDavid du Colombier 			*(int*)a = 1;
3009a747e4fSDavid du Colombier 		else
3019a747e4fSDavid du Colombier 			*(int*)a = atoi(arg);
3029a747e4fSDavid du Colombier 		break;
3039a747e4fSDavid du Colombier 	}
3049a747e4fSDavid du Colombier 	respond(r, nil);
3059a747e4fSDavid du Colombier }
3069a747e4fSDavid du Colombier 
3079a747e4fSDavid du Colombier int
ctlwrite(Req * r,Ctl * ctl,char * cmd,char * arg)3089a747e4fSDavid du Colombier ctlwrite(Req *r, Ctl *ctl, char *cmd, char *arg)
3099a747e4fSDavid du Colombier {
3109a747e4fSDavid du Colombier 	void *a;
3119a747e4fSDavid du Colombier 	Ctab *t;
3129a747e4fSDavid du Colombier 
3139a747e4fSDavid du Colombier 	if((t = findcmd(cmd, ctltab, nelem(ctltab))) == nil)
3149a747e4fSDavid du Colombier 		return 0;
3159dfc0cb2SDavid du Colombier 	a = (void*)((uintptr)ctl+(uintptr)t->offset);
3169a747e4fSDavid du Colombier 	parseas(r, arg, t->type, a);
3179a747e4fSDavid du Colombier 	return 1;
3189a747e4fSDavid du Colombier }
3199a747e4fSDavid du Colombier 
3209a747e4fSDavid du Colombier int
clientctlwrite(Req * r,Client * c,char * cmd,char * arg)3219a747e4fSDavid du Colombier clientctlwrite(Req *r, Client *c, char *cmd, char *arg)
3229a747e4fSDavid du Colombier {
3239a747e4fSDavid du Colombier 	void *a;
3249a747e4fSDavid du Colombier 	Ctab *t;
3259a747e4fSDavid du Colombier 
3269a747e4fSDavid du Colombier 	if((t = findcmd(cmd, clienttab, nelem(clienttab))) == nil)
3279a747e4fSDavid du Colombier 		return 0;
3289dfc0cb2SDavid du Colombier 	a = (void*)((uintptr)c+(uintptr)t->offset);
3299a747e4fSDavid du Colombier 	parseas(r, arg, t->type, a);
3309a747e4fSDavid du Colombier 	return 1;
3319a747e4fSDavid du Colombier }
3329a747e4fSDavid du Colombier 
3339a747e4fSDavid du Colombier int
globalctlwrite(Req * r,char * cmd,char * arg)3349a747e4fSDavid du Colombier globalctlwrite(Req *r, char *cmd, char *arg)
3359a747e4fSDavid du Colombier {
3369a747e4fSDavid du Colombier 	void *a;
3379a747e4fSDavid du Colombier 	Ctab *t;
3389a747e4fSDavid du Colombier 
3399a747e4fSDavid du Colombier 	if((t = findcmd(cmd, globaltab, nelem(globaltab))) == nil)
3409a747e4fSDavid du Colombier 		return 0;
3419a747e4fSDavid du Colombier 	a = t->offset;
3429a747e4fSDavid du Colombier 	parseas(r, arg, t->type, a);
3439a747e4fSDavid du Colombier 	return 1;
3449a747e4fSDavid du Colombier }
3459a747e4fSDavid du Colombier 
3469a747e4fSDavid du Colombier static void
ctlfmt(Ctl * c,char * s)3479a747e4fSDavid du Colombier ctlfmt(Ctl *c, char *s)
3489a747e4fSDavid du Colombier {
3499a747e4fSDavid du Colombier 	int i;
3509a747e4fSDavid du Colombier 	void *a;
3519a747e4fSDavid du Colombier 	char *t;
3529a747e4fSDavid du Colombier 
3539a747e4fSDavid du Colombier 	for(i=0; i<nelem(ctltab); i++){
3549dfc0cb2SDavid du Colombier 		a = (void*)((uintptr)c+(uintptr)ctltab[i].offset);
3559a747e4fSDavid du Colombier 		switch(ctltab[i].type){
3569a747e4fSDavid du Colombier 		case Bool:
3579a747e4fSDavid du Colombier 			s += sprint(s, "%s %s\n", ctltab[i].name, *(int*)a ? "on" : "off");
3589a747e4fSDavid du Colombier 			break;
3599a747e4fSDavid du Colombier 		case Int:
3609a747e4fSDavid du Colombier 			s += sprint(s, "%s %d\n", ctltab[i].name, *(int*)a);
3619a747e4fSDavid du Colombier 			break;
3629a747e4fSDavid du Colombier 		case String:
3639a747e4fSDavid du Colombier 			t = *(char**)a;
3649a747e4fSDavid du Colombier 			if(t != nil)
3659a747e4fSDavid du Colombier 				s += sprint(s, "%s %.*s%s\n", ctltab[i].name, utfnlen(t, 100), t, strlen(t)>100 ? "..." : "");
3669a747e4fSDavid du Colombier 			break;
3679a747e4fSDavid du Colombier 		}
3689a747e4fSDavid du Colombier 	}
3699a747e4fSDavid du Colombier }
3709a747e4fSDavid du Colombier 
3719a747e4fSDavid du Colombier void
ctlread(Req * r,Client * c)3729a747e4fSDavid du Colombier ctlread(Req *r, Client *c)
3739a747e4fSDavid du Colombier {
3749a747e4fSDavid du Colombier 	char buf[1024];
3759a747e4fSDavid du Colombier 
3769a747e4fSDavid du Colombier 	sprint(buf, "%11d \n", c->num);
3779a747e4fSDavid du Colombier 	ctlfmt(&c->ctl, buf+strlen(buf));
3789a747e4fSDavid du Colombier 	readstr(r, buf);
3799a747e4fSDavid du Colombier 	respond(r, nil);
3809a747e4fSDavid du Colombier }
3819a747e4fSDavid du Colombier 
3829a747e4fSDavid du Colombier void
globalctlread(Req * r)3839a747e4fSDavid du Colombier globalctlread(Req *r)
3849a747e4fSDavid du Colombier {
3859a747e4fSDavid du Colombier 	char buf[1024], *s;
3869a747e4fSDavid du Colombier 	int i;
3879a747e4fSDavid du Colombier 
3889a747e4fSDavid du Colombier 	s = buf;
3899a747e4fSDavid du Colombier 	for(i=0; i<nelem(globaltab); i++)
3909a747e4fSDavid du Colombier 		s += sprint(s, "%s %d\n", globaltab[i].name, *(int*)globaltab[i].offset);
3919a747e4fSDavid du Colombier 	ctlfmt(&globalctl, s);
3929a747e4fSDavid du Colombier 	readstr(r, buf);
3939a747e4fSDavid du Colombier 	respond(r, nil);
3949a747e4fSDavid du Colombier }
395