xref: /plan9/sys/src/cmd/auth/factotum/rpc.c (revision 70b8e0103a6fc04254076ea6a1a33497e2b04a62)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * Factotum RPC
39a747e4fSDavid du Colombier  *
49a747e4fSDavid du Colombier  * Must be paired write/read cycles on /mnt/factotum/rpc.
59a747e4fSDavid du Colombier  * The format of a request is verb, single space, data.
69a747e4fSDavid du Colombier  * Data format is verb-dependent; in particular, it can be binary.
79a747e4fSDavid du Colombier  * The format of a response is the same.  The write only sets up
89a747e4fSDavid du Colombier  * the RPC.  The read tries to execute it.  If the /mnt/factotum/key
99a747e4fSDavid du Colombier  * file is open, we ask for new keys using that instead of returning
109a747e4fSDavid du Colombier  * an error in the RPC.  This means the read blocks.
119a747e4fSDavid du Colombier  * Textual arguments are parsed with tokenize, so rc-style quoting
129a747e4fSDavid du Colombier  * rules apply.
139a747e4fSDavid du Colombier  *
149a747e4fSDavid du Colombier  * Only authentication protocol messages go here.  Configuration
159a747e4fSDavid du Colombier  * is still via ctl (below).
169a747e4fSDavid du Colombier  *
179a747e4fSDavid du Colombier  * Return values are:
189a747e4fSDavid du Colombier  *	error message - an error happened.
199a747e4fSDavid du Colombier  *	ok [data] - success, possible data is request dependent.
209a747e4fSDavid du Colombier  *	needkey proto domain user - request aborted, get me this key and try again
219a747e4fSDavid du Colombier  *	badkey proto domain user - request aborted, this key might be bad
229a747e4fSDavid du Colombier  *	done [haveai] - authentication is done [haveai: you can get an ai with authinfo]
239a747e4fSDavid du Colombier  * Request RPCs are:
249a747e4fSDavid du Colombier  *	start attrs - initializes protocol for authentication, can fail.
259a747e4fSDavid du Colombier  *		returns "ok read" or "ok write" on success.
269a747e4fSDavid du Colombier  *	read - execute protocol read
279a747e4fSDavid du Colombier  *	write - execute protocol write
289a747e4fSDavid du Colombier  *	authinfo - if the protocol is finished, return the AI if any
299a747e4fSDavid du Colombier  *	attr - return protocol information
309a747e4fSDavid du Colombier  */
319a747e4fSDavid du Colombier 
329a747e4fSDavid du Colombier #include "dat.h"
339a747e4fSDavid du Colombier 
349a747e4fSDavid du Colombier Req *rpcwait;
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier typedef struct Verb Verb;
379a747e4fSDavid du Colombier struct Verb {
389a747e4fSDavid du Colombier 	char *verb;
399a747e4fSDavid du Colombier 	int iverb;
409a747e4fSDavid du Colombier };
419a747e4fSDavid du Colombier 
429a747e4fSDavid du Colombier enum {
439a747e4fSDavid du Colombier 	Vunknown = -1,
449a747e4fSDavid du Colombier 	Vauthinfo = 0,
459a747e4fSDavid du Colombier 	Vread,
469a747e4fSDavid du Colombier 	Vstart,
479a747e4fSDavid du Colombier 	Vwrite,
489a747e4fSDavid du Colombier 	Vattr,
499a747e4fSDavid du Colombier };
509a747e4fSDavid du Colombier 
519a747e4fSDavid du Colombier Verb rpctab[] = {
529a747e4fSDavid du Colombier 	"authinfo",	Vauthinfo,
539a747e4fSDavid du Colombier 	"read",		Vread,
549a747e4fSDavid du Colombier 	"start",		Vstart,
559a747e4fSDavid du Colombier 	"write",		Vwrite,
569a747e4fSDavid du Colombier 	"attr",		Vattr,
579a747e4fSDavid du Colombier };
589a747e4fSDavid du Colombier 
599a747e4fSDavid du Colombier static int
classify(char * s,Verb * verbtab,int nverbtab)609a747e4fSDavid du Colombier classify(char *s, Verb *verbtab, int nverbtab)
619a747e4fSDavid du Colombier {
629a747e4fSDavid du Colombier 	int i;
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier 	for(i=0; i<nverbtab; i++)
659a747e4fSDavid du Colombier 		if(strcmp(s, verbtab[i].verb) == 0)
669a747e4fSDavid du Colombier 			return verbtab[i].iverb;
679a747e4fSDavid du Colombier 	return Vunknown;
689a747e4fSDavid du Colombier }
699a747e4fSDavid du Colombier 
709a747e4fSDavid du Colombier void
rpcwrite(Req * r)719a747e4fSDavid du Colombier rpcwrite(Req *r)
729a747e4fSDavid du Colombier {
739a747e4fSDavid du Colombier 	Fsstate *fss;
749a747e4fSDavid du Colombier 
759a747e4fSDavid du Colombier 	if(r->ifcall.count >= Maxrpc){
769a747e4fSDavid du Colombier 		respond(r, Etoolarge);
779a747e4fSDavid du Colombier 		return;
789a747e4fSDavid du Colombier 	}
799a747e4fSDavid du Colombier 	fss = r->fid->aux;
809a747e4fSDavid du Colombier 	if(fss->pending){
819a747e4fSDavid du Colombier 		respond(r, "rpc already pending; read to clear");
829a747e4fSDavid du Colombier 		return;
839a747e4fSDavid du Colombier 	}
849a747e4fSDavid du Colombier 	memmove(fss->rpc.buf, r->ifcall.data, r->ifcall.count);
859a747e4fSDavid du Colombier 	fss->rpc.buf[r->ifcall.count] = '\0';
869a747e4fSDavid du Colombier 	fss->rpc.verb = fss->rpc.buf;
879a747e4fSDavid du Colombier 	if(fss->rpc.arg = strchr(fss->rpc.buf, ' ')){
889a747e4fSDavid du Colombier 		*fss->rpc.arg++ = '\0';
899a747e4fSDavid du Colombier 		fss->rpc.narg = r->ifcall.count - (fss->rpc.arg - fss->rpc.buf);
909a747e4fSDavid du Colombier 	}else{
919a747e4fSDavid du Colombier 		fss->rpc.arg = "";
929a747e4fSDavid du Colombier 		fss->rpc.narg = 0;
939a747e4fSDavid du Colombier 	}
949a747e4fSDavid du Colombier 	fss->rpc.iverb = classify(fss->rpc.verb, rpctab, nelem(rpctab));
959a747e4fSDavid du Colombier 	r->ofcall.count = r->ifcall.count;
969a747e4fSDavid du Colombier 	fss->pending = 1;
979a747e4fSDavid du Colombier 	respond(r, nil);
989a747e4fSDavid du Colombier }
999a747e4fSDavid du Colombier 
1009a747e4fSDavid du Colombier static void
retstring(Req * r,Fsstate * fss,char * s)1019a747e4fSDavid du Colombier retstring(Req *r, Fsstate *fss, char *s)
1029a747e4fSDavid du Colombier {
1039a747e4fSDavid du Colombier 	int n;
1049a747e4fSDavid du Colombier 
1059a747e4fSDavid du Colombier 	n = strlen(s);
1069a747e4fSDavid du Colombier 	if(n > r->ifcall.count)
1079a747e4fSDavid du Colombier 		n = r->ifcall.count;
1089a747e4fSDavid du Colombier 	memmove(r->ofcall.data, s, n);
1099a747e4fSDavid du Colombier 	r->ofcall.count = n;
1109a747e4fSDavid du Colombier 	fss->pending = 0;
1119a747e4fSDavid du Colombier 	respond(r, nil);
1129a747e4fSDavid du Colombier 	return;
1139a747e4fSDavid du Colombier }
1149a747e4fSDavid du Colombier 
1159a747e4fSDavid du Colombier static void
retrpc(Req * r,int ret,Fsstate * fss)1169a747e4fSDavid du Colombier retrpc(Req *r, int ret, Fsstate *fss)
1179a747e4fSDavid du Colombier {
1189a747e4fSDavid du Colombier 	switch(ret){
1199a747e4fSDavid du Colombier 	default:
1209a747e4fSDavid du Colombier 		snprint(fss->rpc.buf, Maxrpc, "internal error %d", ret);
1219a747e4fSDavid du Colombier 		retstring(r, fss, fss->rpc.buf);
1229a747e4fSDavid du Colombier 		return;
1239a747e4fSDavid du Colombier 	case RpcErrstr:
1249a747e4fSDavid du Colombier 		snprint(fss->rpc.buf, Maxrpc, "error %r");
1259a747e4fSDavid du Colombier 		retstring(r, fss, fss->rpc.buf);
1269a747e4fSDavid du Colombier 		return;
1279a747e4fSDavid du Colombier 	case RpcFailure:
1289a747e4fSDavid du Colombier 		snprint(fss->rpc.buf, Maxrpc, "error %s", fss->err);
1299a747e4fSDavid du Colombier 		retstring(r, fss, fss->rpc.buf);
1309a747e4fSDavid du Colombier 		return;
1319a747e4fSDavid du Colombier 	case RpcNeedkey:
1329a747e4fSDavid du Colombier 		if(needkeyqueue(r, fss) < 0){
1339a747e4fSDavid du Colombier 			snprint(fss->rpc.buf, Maxrpc, "needkey %s", fss->keyinfo);
1349a747e4fSDavid du Colombier 			retstring(r, fss, fss->rpc.buf);
1359a747e4fSDavid du Colombier 		}
1369a747e4fSDavid du Colombier 		return;
1379a747e4fSDavid du Colombier 	case RpcOk:
1389a747e4fSDavid du Colombier 		retstring(r, fss, "ok");
1399a747e4fSDavid du Colombier 		return;
1409a747e4fSDavid du Colombier 	case RpcToosmall:
1419a747e4fSDavid du Colombier 		snprint(fss->rpc.buf, Maxrpc, "toosmall %d", fss->rpc.nwant);
1429a747e4fSDavid du Colombier 		retstring(r, fss, fss->rpc.buf);
1439a747e4fSDavid du Colombier 		return;
1449a747e4fSDavid du Colombier 	case RpcPhase:
1459a747e4fSDavid du Colombier 		snprint(fss->rpc.buf, Maxrpc, "phase %r");
1469a747e4fSDavid du Colombier 		retstring(r, fss, fss->rpc.buf);
1479a747e4fSDavid du Colombier 		return;
1489a747e4fSDavid du Colombier 	case RpcConfirm:
1499a747e4fSDavid du Colombier 		confirmqueue(r, fss);
1509a747e4fSDavid du Colombier 		return;
1519a747e4fSDavid du Colombier 	}
1529a747e4fSDavid du Colombier }
1539a747e4fSDavid du Colombier 
1549a747e4fSDavid du Colombier int
rdwrcheck(Req * r,Fsstate * fss)1559a747e4fSDavid du Colombier rdwrcheck(Req *r, Fsstate *fss)
1569a747e4fSDavid du Colombier {
1579a747e4fSDavid du Colombier 	if(fss->ps == nil){
1589a747e4fSDavid du Colombier 		retstring(r, fss, "error no current protocol");
1599a747e4fSDavid du Colombier 		return -1;
1609a747e4fSDavid du Colombier 	}
1619a747e4fSDavid du Colombier 	if(fss->phase == Notstarted){
1629a747e4fSDavid du Colombier 		retstring(r, fss, "protocol not started");
1639a747e4fSDavid du Colombier 		return -1;
1649a747e4fSDavid du Colombier 	}
1659a747e4fSDavid du Colombier 	if(fss->phase == Broken){
1669a747e4fSDavid du Colombier 		snprint(fss->rpc.buf, Maxrpc, "error %s", fss->err);
1679a747e4fSDavid du Colombier 		retstring(r, fss, fss->rpc.buf);
1689a747e4fSDavid du Colombier 		return -1;
1699a747e4fSDavid du Colombier 	}
1709a747e4fSDavid du Colombier 	if(fss->phase == Established){
1719a747e4fSDavid du Colombier 		if(fss->haveai)
1729a747e4fSDavid du Colombier 			retstring(r, fss, "done haveai");
1739a747e4fSDavid du Colombier 		else
1749a747e4fSDavid du Colombier 			retstring(r, fss, "done");
1759a747e4fSDavid du Colombier 		return -1;
1769a747e4fSDavid du Colombier 	}
1779a747e4fSDavid du Colombier 	return 0;
1789a747e4fSDavid du Colombier }
1799a747e4fSDavid du Colombier 
1809a747e4fSDavid du Colombier static void
logret(char * pre,Fsstate * fss,int ret)1819a747e4fSDavid du Colombier logret(char *pre, Fsstate *fss, int ret)
1829a747e4fSDavid du Colombier {
1839a747e4fSDavid du Colombier 	switch(ret){
1849a747e4fSDavid du Colombier 	default:
1859a747e4fSDavid du Colombier 		flog("%s: code %d", pre, ret);
1869a747e4fSDavid du Colombier 		break;
1879a747e4fSDavid du Colombier 	case RpcErrstr:
1889a747e4fSDavid du Colombier 		flog("%s: error %r", pre);
1899a747e4fSDavid du Colombier 		break;
1909a747e4fSDavid du Colombier 	case RpcFailure:
1919a747e4fSDavid du Colombier 		flog("%s: failure %s", pre, fss->err);
1929a747e4fSDavid du Colombier 		break;
1939a747e4fSDavid du Colombier 	case RpcNeedkey:
1949a747e4fSDavid du Colombier 		flog("%s: needkey %s", pre, fss->keyinfo);
1959a747e4fSDavid du Colombier 		break;
1969a747e4fSDavid du Colombier 	case RpcOk:
1979a747e4fSDavid du Colombier 		flog("%s: ok", pre);
1989a747e4fSDavid du Colombier 		break;
1999a747e4fSDavid du Colombier 	case RpcToosmall:
2009a747e4fSDavid du Colombier 		flog("%s: toosmall %d", pre, fss->rpc.nwant);
2019a747e4fSDavid du Colombier 		break;
2029a747e4fSDavid du Colombier 	case RpcPhase:
2039a747e4fSDavid du Colombier 		flog("%s: phase: %r", pre);
2049a747e4fSDavid du Colombier 		break;
2059a747e4fSDavid du Colombier 	case RpcConfirm:
2069a747e4fSDavid du Colombier 		flog("%s: waiting for confirm", pre);
2079a747e4fSDavid du Colombier 		break;
2089a747e4fSDavid du Colombier 	}
2099a747e4fSDavid du Colombier }
2109a747e4fSDavid du Colombier 
2119a747e4fSDavid du Colombier void
rpcrdwrlog(Fsstate * fss,char * rdwr,uint n,int ophase,int ret)2129a747e4fSDavid du Colombier rpcrdwrlog(Fsstate *fss, char *rdwr, uint n, int ophase, int ret)
2139a747e4fSDavid du Colombier {
2149a747e4fSDavid du Colombier 	char buf0[40], buf1[40], pre[300];
2159a747e4fSDavid du Colombier 
2169a747e4fSDavid du Colombier 	if(!debug)
2179a747e4fSDavid du Colombier 		return;
2189a747e4fSDavid du Colombier 	snprint(pre, sizeof pre, "%d: %s %ud in phase %s yields phase %s",
2199a747e4fSDavid du Colombier 		fss->seqnum, rdwr, n, phasename(fss, ophase, buf0), phasename(fss, fss->phase, buf1));
2209a747e4fSDavid du Colombier 	logret(pre, fss, ret);
2219a747e4fSDavid du Colombier }
2229a747e4fSDavid du Colombier 
2239a747e4fSDavid du Colombier void
rpcstartlog(Attr * attr,Fsstate * fss,int ret)2249a747e4fSDavid du Colombier rpcstartlog(Attr *attr, Fsstate *fss, int ret)
2259a747e4fSDavid du Colombier {
2269a747e4fSDavid du Colombier 	char pre[300], tmp[40];
2279a747e4fSDavid du Colombier 
2289a747e4fSDavid du Colombier 	if(!debug)
2299a747e4fSDavid du Colombier 		return;
2309a747e4fSDavid du Colombier 	snprint(pre, sizeof pre, "%d: start %A yields phase %s", fss->seqnum,
2319a747e4fSDavid du Colombier 		attr, phasename(fss, fss->phase, tmp));
2329a747e4fSDavid du Colombier 	logret(pre, fss, ret);
2339a747e4fSDavid du Colombier }
2349a747e4fSDavid du Colombier 
2359a747e4fSDavid du Colombier int seqnum;
2369a747e4fSDavid du Colombier 
2379a747e4fSDavid du Colombier void
rpcread(Req * r)2389a747e4fSDavid du Colombier rpcread(Req *r)
2399a747e4fSDavid du Colombier {
2409a747e4fSDavid du Colombier 	Attr *attr;
2419a747e4fSDavid du Colombier 	char *p;
2429a747e4fSDavid du Colombier 	int ophase, ret;
2439a747e4fSDavid du Colombier 	uchar *e;
2449a747e4fSDavid du Colombier 	uint count;
2459a747e4fSDavid du Colombier 	Fsstate *fss;
2469a747e4fSDavid du Colombier 	Proto *proto;
2479a747e4fSDavid du Colombier 
2489a747e4fSDavid du Colombier 	if(r->ifcall.count < 64){
2499a747e4fSDavid du Colombier 		respond(r, "rpc read too small");
2509a747e4fSDavid du Colombier 		return;
2519a747e4fSDavid du Colombier 	}
2529a747e4fSDavid du Colombier 	fss = r->fid->aux;
2539a747e4fSDavid du Colombier 	if(!fss->pending){
2549a747e4fSDavid du Colombier 		respond(r, "no rpc pending");
2559a747e4fSDavid du Colombier 		return;
2569a747e4fSDavid du Colombier 	}
2579a747e4fSDavid du Colombier 	switch(fss->rpc.iverb){
2589a747e4fSDavid du Colombier 	default:
2599a747e4fSDavid du Colombier 	case Vunknown:
2609a747e4fSDavid du Colombier 		retstring(r, fss, "error unknown verb");
2619a747e4fSDavid du Colombier 		break;
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier 	case Vstart:
264d9306527SDavid du Colombier 		if(fss->phase != Notstarted){
265d9306527SDavid du Colombier 			flog("%d: implicit close due to second start; old attr '%A'", fss->seqnum, fss->attr);
266d9306527SDavid du Colombier 			if(fss->proto && fss->ps)
267d9306527SDavid du Colombier 				(*fss->proto->close)(fss);
268d9306527SDavid du Colombier 			fss->ps = nil;
269d9306527SDavid du Colombier 			fss->proto = nil;
270d9306527SDavid du Colombier 			_freeattr(fss->attr);
271d9306527SDavid du Colombier 			fss->attr = nil;
272d9306527SDavid du Colombier 			fss->phase = Notstarted;
273d9306527SDavid du Colombier 		}
2749a747e4fSDavid du Colombier 		attr = _parseattr(fss->rpc.arg);
2752ebbfa15SDavid du Colombier 		if((p = _strfindattr(attr, "proto")) == nil){
2769a747e4fSDavid du Colombier 			retstring(r, fss, "error did not specify proto");
277d9306527SDavid du Colombier 			_freeattr(attr);
2789a747e4fSDavid du Colombier 			break;
2799a747e4fSDavid du Colombier 		}
2809a747e4fSDavid du Colombier 		if((proto = findproto(p)) == nil){
2819a747e4fSDavid du Colombier 			snprint(fss->rpc.buf, Maxrpc, "error unknown protocol %q", p);
2829a747e4fSDavid du Colombier 			retstring(r, fss, fss->rpc.buf);
283d9306527SDavid du Colombier 			_freeattr(attr);
2849a747e4fSDavid du Colombier 			break;
2859a747e4fSDavid du Colombier 		}
2869a747e4fSDavid du Colombier 		fss->attr = attr;
2879a747e4fSDavid du Colombier 		fss->proto = proto;
2889a747e4fSDavid du Colombier 		fss->seqnum = ++seqnum;
2899a747e4fSDavid du Colombier 		ret = (*proto->init)(proto, fss);
2909a747e4fSDavid du Colombier 		rpcstartlog(attr, fss, ret);
2919a747e4fSDavid du Colombier 		if(ret != RpcOk){
2929a747e4fSDavid du Colombier 			_freeattr(fss->attr);
2939a747e4fSDavid du Colombier 			fss->attr = nil;
2949a747e4fSDavid du Colombier 			fss->phase = Notstarted;
2959a747e4fSDavid du Colombier 		}
2969a747e4fSDavid du Colombier 		retrpc(r, ret, fss);
2979a747e4fSDavid du Colombier 		break;
2989a747e4fSDavid du Colombier 
2999a747e4fSDavid du Colombier 	case Vread:
3009a747e4fSDavid du Colombier 		if(fss->rpc.arg && fss->rpc.arg[0]){
3019a747e4fSDavid du Colombier 			retstring(r, fss, "error read needs no parameters");
3029a747e4fSDavid du Colombier 			break;
3039a747e4fSDavid du Colombier 		}
3049a747e4fSDavid du Colombier 		if(rdwrcheck(r, fss) < 0)
3059a747e4fSDavid du Colombier 			break;
3069a747e4fSDavid du Colombier 		count = r->ifcall.count - 3;
3079a747e4fSDavid du Colombier 		ophase = fss->phase;
3089a747e4fSDavid du Colombier 		ret = fss->proto->read(fss, (uchar*)r->ofcall.data+3, &count);
3099a747e4fSDavid du Colombier 		rpcrdwrlog(fss, "read", count, ophase, ret);
3109a747e4fSDavid du Colombier 		if(ret == RpcOk){
3119a747e4fSDavid du Colombier 			memmove(r->ofcall.data, "ok ", 3);
3129a747e4fSDavid du Colombier 			if(count == 0)
3139a747e4fSDavid du Colombier 				r->ofcall.count = 2;
3149a747e4fSDavid du Colombier 			else
3159a747e4fSDavid du Colombier 				r->ofcall.count = 3+count;
3169a747e4fSDavid du Colombier 			fss->pending = 0;
3179a747e4fSDavid du Colombier 			respond(r, nil);
3189a747e4fSDavid du Colombier 		}else
3199a747e4fSDavid du Colombier 			retrpc(r, ret, fss);
3209a747e4fSDavid du Colombier 		break;
3219a747e4fSDavid du Colombier 
3229a747e4fSDavid du Colombier 	case Vwrite:
3239a747e4fSDavid du Colombier 		if(rdwrcheck(r, fss) < 0)
3249a747e4fSDavid du Colombier 			break;
3259a747e4fSDavid du Colombier 		ophase = fss->phase;
3269a747e4fSDavid du Colombier 		ret = fss->proto->write(fss, fss->rpc.arg, fss->rpc.narg);
3279a747e4fSDavid du Colombier 		rpcrdwrlog(fss, "write", fss->rpc.narg, ophase, ret);
3289a747e4fSDavid du Colombier 		retrpc(r, ret, fss);
3299a747e4fSDavid du Colombier 		break;
3309a747e4fSDavid du Colombier 
3319a747e4fSDavid du Colombier 	case Vauthinfo:
3329a747e4fSDavid du Colombier 		if(fss->phase != Established){
3339a747e4fSDavid du Colombier 			retstring(r, fss, "error authentication unfinished");
3349a747e4fSDavid du Colombier 			break;
3359a747e4fSDavid du Colombier 		}
3369a747e4fSDavid du Colombier 		if(!fss->haveai){
3379a747e4fSDavid du Colombier 			retstring(r, fss, "error no authinfo available");
3389a747e4fSDavid du Colombier 			break;
3399a747e4fSDavid du Colombier 		}
3409a747e4fSDavid du Colombier 		memmove(r->ofcall.data, "ok ", 3);
341d9306527SDavid du Colombier 		fss->ai.cap = mkcap(r->fid->uid, fss->ai.suid);
3429a747e4fSDavid du Colombier 		e = convAI2M(&fss->ai, (uchar*)r->ofcall.data+3, r->ifcall.count-3);
3439a747e4fSDavid du Colombier 		free(fss->ai.cap);
3449a747e4fSDavid du Colombier 		fss->ai.cap = nil;
3459a747e4fSDavid du Colombier 		if(e == nil){
3469a747e4fSDavid du Colombier 			retstring(r, fss, "error read too small");
3479a747e4fSDavid du Colombier 			break;
3489a747e4fSDavid du Colombier 		}
3499a747e4fSDavid du Colombier 		r->ofcall.count = e - (uchar*)r->ofcall.data;
3509a747e4fSDavid du Colombier 		fss->pending = 0;
3519a747e4fSDavid du Colombier 		respond(r, nil);
3529a747e4fSDavid du Colombier 		break;
3539a747e4fSDavid du Colombier 
3549a747e4fSDavid du Colombier 	case Vattr:
3559a747e4fSDavid du Colombier 		snprint(fss->rpc.buf, Maxrpc, "ok %A", fss->attr);
3569a747e4fSDavid du Colombier 		retstring(r, fss, fss->rpc.buf);
3579a747e4fSDavid du Colombier 		break;
3589a747e4fSDavid du Colombier 	}
3599a747e4fSDavid du Colombier }
3609a747e4fSDavid du Colombier 
3619a747e4fSDavid du Colombier enum {
3629a747e4fSDavid du Colombier 	Vdelkey,
3639a747e4fSDavid du Colombier 	Vaddkey,
3649a747e4fSDavid du Colombier 	Vdebug,
3659a747e4fSDavid du Colombier };
3669a747e4fSDavid du Colombier 
3679a747e4fSDavid du Colombier Verb ctltab[] = {
3689a747e4fSDavid du Colombier 	"delkey",		Vdelkey,
3699a747e4fSDavid du Colombier 	"key",		Vaddkey,
3709a747e4fSDavid du Colombier 	"debug",	Vdebug,
3719a747e4fSDavid du Colombier };
3729a747e4fSDavid du Colombier 
3739a747e4fSDavid du Colombier /*
3749a747e4fSDavid du Colombier  *	key attr=val... - add a key
3759a747e4fSDavid du Colombier  *		the attr=val pairs are protocol-specific.
3769a747e4fSDavid du Colombier  *		for example, both of these are valid:
3779a747e4fSDavid du Colombier  *			key p9sk1 gre cs.bell-labs.com mysecret
3789a747e4fSDavid du Colombier  *			key p9sk1 gre cs.bell-labs.com 11223344556677 fmt=des7hex
3799a747e4fSDavid du Colombier  *	delkey ... - delete a key
3809a747e4fSDavid du Colombier  *		if given, the attr=val pairs are used to narrow the search
3819a747e4fSDavid du Colombier  *		[maybe should require a password?]
3829a747e4fSDavid du Colombier  */
3839a747e4fSDavid du Colombier 
3849a747e4fSDavid du Colombier int
ctlwrite(char * a,int atzero)385*70b8e010SDavid du Colombier ctlwrite(char *a, int atzero)
3869a747e4fSDavid du Colombier {
3879a747e4fSDavid du Colombier 	char *p;
3889a747e4fSDavid du Colombier 	int i, nmatch, ret;
3899a747e4fSDavid du Colombier 	Attr *attr, **l, **lpriv, **lprotos, *pa, *priv, *protos;
3909a747e4fSDavid du Colombier 	Key *k;
3919a747e4fSDavid du Colombier 	Proto *proto;
3929a747e4fSDavid du Colombier 
3939a747e4fSDavid du Colombier 	if(a[0] == '#' || a[0] == '\0')
3949a747e4fSDavid du Colombier 		return 0;
3959a747e4fSDavid du Colombier 
396d9306527SDavid du Colombier 	/*
397d9306527SDavid du Colombier 	 * it would be nice to emit a warning of some sort here.
398d9306527SDavid du Colombier 	 * we ignore all but the first line of the write.  this helps
399d9306527SDavid du Colombier 	 * both with things like "echo delkey >/mnt/factotum/ctl"
400d9306527SDavid du Colombier 	 * and writes that (incorrectly) contain multiple key lines.
401d9306527SDavid du Colombier 	 */
402d9306527SDavid du Colombier 	if(p = strchr(a, '\n')){
403d9306527SDavid du Colombier 		if(p[1] != '\0'){
404d9306527SDavid du Colombier 			werrstr("multiline write not allowed");
405d9306527SDavid du Colombier 			return -1;
406d9306527SDavid du Colombier 		}
407d9306527SDavid du Colombier 		*p = '\0';
408d9306527SDavid du Colombier 	}
409d9306527SDavid du Colombier 
4109a747e4fSDavid du Colombier 	if((p = strchr(a, ' ')) == nil)
4119a747e4fSDavid du Colombier 		p = "";
4129a747e4fSDavid du Colombier 	else
4139a747e4fSDavid du Colombier 		*p++ = '\0';
4149a747e4fSDavid du Colombier 	switch(classify(a, ctltab, nelem(ctltab))){
4159a747e4fSDavid du Colombier 	default:
4169a747e4fSDavid du Colombier 	case Vunknown:
4179a747e4fSDavid du Colombier 		werrstr("unknown verb");
4189a747e4fSDavid du Colombier 		return -1;
4199a747e4fSDavid du Colombier 	case Vdebug:
4209a747e4fSDavid du Colombier 		debug ^= 1;
4219a747e4fSDavid du Colombier 		return 0;
4229a747e4fSDavid du Colombier 	case Vdelkey:
4239a747e4fSDavid du Colombier 		nmatch = 0;
4249a747e4fSDavid du Colombier 		attr = _parseattr(p);
425d9306527SDavid du Colombier 		for(pa=attr; pa; pa=pa->next){
4262ebbfa15SDavid du Colombier 			if(pa->type != AttrQuery && pa->name[0]=='!'){
427d9306527SDavid du Colombier 				werrstr("only !private? patterns are allowed for private fields");
428d9306527SDavid du Colombier 				_freeattr(attr);
429d9306527SDavid du Colombier 				return -1;
430d9306527SDavid du Colombier 			}
431d9306527SDavid du Colombier 		}
4329a747e4fSDavid du Colombier 		for(i=0; i<ring->nkey; ){
433d9306527SDavid du Colombier 			if(matchattr(attr, ring->key[i]->attr, ring->key[i]->privattr)){
4349a747e4fSDavid du Colombier 				nmatch++;
4359a747e4fSDavid du Colombier 				closekey(ring->key[i]);
4369a747e4fSDavid du Colombier 				ring->nkey--;
4379a747e4fSDavid du Colombier 				memmove(&ring->key[i], &ring->key[i+1], (ring->nkey-i)*sizeof(ring->key[0]));
4389a747e4fSDavid du Colombier 			}else
4399a747e4fSDavid du Colombier 				i++;
4409a747e4fSDavid du Colombier 		}
4419a747e4fSDavid du Colombier 		_freeattr(attr);
4429a747e4fSDavid du Colombier 		if(nmatch == 0){
4439a747e4fSDavid du Colombier 			werrstr("found no keys to delete");
4449a747e4fSDavid du Colombier 			return -1;
4459a747e4fSDavid du Colombier 		}
4469a747e4fSDavid du Colombier 		return 0;
4479a747e4fSDavid du Colombier 	case Vaddkey:
4489a747e4fSDavid du Colombier 		attr = _parseattr(p);
4499a747e4fSDavid du Colombier 		/* separate out proto= attributes */
4509a747e4fSDavid du Colombier 		lprotos = &protos;
4519a747e4fSDavid du Colombier 		for(l=&attr; (*l); ){
4522ebbfa15SDavid du Colombier 			if(strcmp((*l)->name, "proto") == 0){
4539a747e4fSDavid du Colombier 				*lprotos = *l;
4549a747e4fSDavid du Colombier 				lprotos = &(*l)->next;
4559a747e4fSDavid du Colombier 				*l = (*l)->next;
4569a747e4fSDavid du Colombier 			}else
4579a747e4fSDavid du Colombier 				l = &(*l)->next;
4589a747e4fSDavid du Colombier 		}
4599a747e4fSDavid du Colombier 		*lprotos = nil;
4609a747e4fSDavid du Colombier 		if(protos == nil){
4619a747e4fSDavid du Colombier 			werrstr("key without protos");
4629a747e4fSDavid du Colombier 			_freeattr(attr);
4639a747e4fSDavid du Colombier 			return -1;
4649a747e4fSDavid du Colombier 		}
4659a747e4fSDavid du Colombier 
4669a747e4fSDavid du Colombier 		/* separate out private attributes */
4679a747e4fSDavid du Colombier 		lpriv = &priv;
4689a747e4fSDavid du Colombier 		for(l=&attr; (*l); ){
4692ebbfa15SDavid du Colombier 			if((*l)->name[0] == '!'){
4709a747e4fSDavid du Colombier 				*lpriv = *l;
4719a747e4fSDavid du Colombier 				lpriv = &(*l)->next;
4729a747e4fSDavid du Colombier 				*l = (*l)->next;
4739a747e4fSDavid du Colombier 			}else
4749a747e4fSDavid du Colombier 				l = &(*l)->next;
4759a747e4fSDavid du Colombier 		}
4769a747e4fSDavid du Colombier 		*lpriv = nil;
4779a747e4fSDavid du Colombier 
4789a747e4fSDavid du Colombier 		/* add keys */
4799a747e4fSDavid du Colombier 		ret = 0;
4809a747e4fSDavid du Colombier 		for(pa=protos; pa; pa=pa->next){
4812ebbfa15SDavid du Colombier 			if((proto = findproto(pa->val)) == nil){
4822ebbfa15SDavid du Colombier 				werrstr("unknown proto %s", pa->val);
4839a747e4fSDavid du Colombier 				ret = -1;
4849a747e4fSDavid du Colombier 				continue;
4859a747e4fSDavid du Colombier 			}
4869a747e4fSDavid du Colombier 			if(proto->addkey == nil){
4879a747e4fSDavid du Colombier 				werrstr("proto %s doesn't take keys", proto->name);
4889a747e4fSDavid du Colombier 				ret = -1;
4899a747e4fSDavid du Colombier 				continue;
4909a747e4fSDavid du Colombier 			}
4919a747e4fSDavid du Colombier 			k = emalloc(sizeof(Key));
4929a747e4fSDavid du Colombier 			k->attr = _mkattr(AttrNameval, "proto", proto->name, _copyattr(attr));
4939a747e4fSDavid du Colombier 			k->privattr = _copyattr(priv);
4949a747e4fSDavid du Colombier 			k->ref = 1;
495d9306527SDavid du Colombier 			k->proto = proto;
496*70b8e010SDavid du Colombier 			if(proto->addkey(k, atzero) < 0){
4979a747e4fSDavid du Colombier 				ret = -1;
4989a747e4fSDavid du Colombier 				closekey(k);
4999a747e4fSDavid du Colombier 				continue;
5009a747e4fSDavid du Colombier 			}
5019a747e4fSDavid du Colombier 			closekey(k);
5029a747e4fSDavid du Colombier 		}
5039a747e4fSDavid du Colombier 		_freeattr(attr);
5049a747e4fSDavid du Colombier 		_freeattr(priv);
5059a747e4fSDavid du Colombier 		_freeattr(protos);
5069a747e4fSDavid du Colombier 		return ret;
5079a747e4fSDavid du Colombier 	}
5089a747e4fSDavid du Colombier }
509