plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

conv.c (5286B)


      1 #include "std.h"
      2 #include "dat.h"
      3 
      4 Conv *conv;
      5 
      6 ulong taggen = 1;
      7 
      8 Conv*
      9 convalloc(char *sysuser)
     10 {
     11 	Conv *c;
     12 
     13 	c = mallocz(sizeof(Conv), 1);
     14 	if(c == nil)
     15 		return nil;
     16 	c->ref = 1;
     17 	c->tag = taggen++;
     18 	c->next = conv;
     19 	c->sysuser = estrdup(sysuser);
     20 	c->state = "nascent";
     21 	c->rpcwait = chancreate(sizeof(void*), 0);
     22 	c->keywait = chancreate(sizeof(void*), 0);
     23 	strcpy(c->err, "protocol has not started");
     24 	conv = c;
     25 	convreset(c);
     26 	return c;
     27 }
     28 
     29 void
     30 convreset(Conv *c)
     31 {
     32 	if(c->ref != 1){
     33 		c->hangup = 1;
     34 		nbsendp(c->rpcwait, 0);
     35 		while(c->ref > 1)
     36 			yield();
     37 		c->hangup = 0;
     38 	}
     39 	c->state = "nascent";
     40 	c->err[0] = '\0';
     41 	freeattr(c->attr);
     42 	c->attr = nil;
     43 	c->proto = nil;
     44 	c->rpc.op = 0;
     45 	c->active = 0;
     46 	c->done = 0;
     47 	c->hangup = 0;
     48 }
     49 
     50 void
     51 convhangup(Conv *c)
     52 {
     53 	c->hangup = 1;
     54 	c->rpc.op = 0;
     55 	(*c->kickreply)(c);
     56 	nbsendp(c->rpcwait, 0);
     57 }
     58 
     59 void
     60 convclose(Conv *c)
     61 {
     62 	Conv *p;
     63 
     64 	if(c == nil)
     65 		return;
     66 
     67 	if(--c->ref > 0)
     68 		return;
     69 
     70 	if(c == conv){
     71 		conv = c->next;
     72 		goto free;
     73 	}
     74 	for(p=conv; p && p->next!=c; p=p->next)
     75 		;
     76 	if(p == nil){
     77 		print("cannot find conv in list\n");
     78 		return;
     79 	}
     80 	p->next = c->next;
     81 
     82 free:
     83 	c->next = nil;
     84 	free(c);
     85 }
     86 
     87 static Rpc*
     88 convgetrpc(Conv *c, int want)
     89 {
     90 	for(;;){
     91 		if(c->hangup){
     92 			flog("convgetrpc: hangup");
     93 			werrstr("hangup");
     94 			return nil;
     95 		}
     96 		if(c->rpc.op == RpcUnknown){
     97 			recvp(c->rpcwait);
     98 			if(c->hangup){
     99 				flog("convgetrpc: hangup");
    100 				werrstr("hangup");
    101 				return nil;
    102 			}
    103 			if(c->rpc.op == RpcUnknown)
    104 				continue;
    105 		}
    106 		if(want < 0 || c->rpc.op == want)
    107 			return &c->rpc;
    108 		rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]);
    109 	}
    110 	/* not reached */
    111 }
    112 
    113 /* read until the done function tells us that's enough */
    114 int
    115 convreadfn(Conv *c, int (*done)(void*, int), char **ps)
    116 {
    117 	int n;
    118 	Rpc *r;
    119 	char *s;
    120 
    121 	for(;;){
    122 		r = convgetrpc(c, RpcWrite);
    123 		if(r == nil)
    124 			return -1;
    125 		n = (*done)(r->data, r->count);
    126 		if(n == r->count)
    127 			break;
    128 		rpcrespond(c, "toosmall %d", n);
    129 	}
    130 
    131 	s = emalloc(r->count+1);
    132 	memmove(s, r->data, r->count);
    133 	s[r->count] = 0;
    134 	*ps = s;
    135 	rpcrespond(c, "ok");
    136 	return r->count;
    137 }
    138 
    139 /*
    140  * read until we get a non-zero write.  assumes remote side
    141  * knows something about the protocol (is not auth_proxy).
    142  * the remote side typically won't bother with the zero-length
    143  * write to find out the length -- the loop is there only so the
    144  * test program can call auth_proxy on both sides of a pipe
    145  * to play a conversation.
    146  */
    147 int
    148 convreadm(Conv *c, char **ps)
    149 {
    150 	char *s;
    151 	Rpc *r;
    152 
    153 	for(;;){
    154 		r = convgetrpc(c, RpcWrite);
    155 		if(r == nil)
    156 			return -1;
    157 		if(r->count > 0)
    158 			break;
    159 		rpcrespond(c, "toosmall %d", AuthRpcMax);
    160 	}
    161 	s = emalloc(r->count+1);
    162 	memmove(s, r->data, r->count);
    163 	s[r->count] = 0;
    164 	*ps = s;
    165 	rpcrespond(c, "ok");
    166 	return r->count;
    167 }
    168 
    169 /* read exactly count bytes */
    170 int
    171 convread(Conv *c, void *data, int count)
    172 {
    173 	Rpc *r;
    174 
    175 	for(;;){
    176 		r = convgetrpc(c, RpcWrite);
    177 		if(r == nil)
    178 			return -1;
    179 		if(r->count == count)
    180 			break;
    181 		if(r->count < count)
    182 			rpcrespond(c, "toosmall %d", count);
    183 		else
    184 			rpcrespond(c, "error too much data; want %d got %d", count, r->count);
    185 	}
    186 	memmove(data, r->data, count);
    187 	rpcrespond(c, "ok");
    188 	return 0;
    189 }
    190 
    191 /* write exactly count bytes */
    192 int
    193 convwrite(Conv *c, void *data, int count)
    194 {
    195 	Rpc *r;
    196 
    197 	r = convgetrpc(c, RpcRead);
    198 	if(r == nil)
    199 		return -1;
    200 	rpcrespondn(c, "ok", data, count);
    201 	return 0;
    202 }
    203 
    204 /* print to the conversation */
    205 int
    206 convprint(Conv *c, char *fmt, ...)
    207 {
    208 	char *s;
    209 	va_list arg;
    210 	int ret;
    211 
    212 	va_start(arg, fmt);
    213 	s = vsmprint(fmt, arg);
    214 	va_end(arg);
    215 	if(s == nil)
    216 		return -1;
    217 	ret = convwrite(c, s, strlen(s));
    218 	free(s);
    219 	return ret;
    220 }
    221 
    222 /* ask for a key */
    223 int
    224 convneedkey(Conv *c, Attr *a)
    225 {
    226 	/*
    227 	 * Piggyback key requests in the usual RPC channel.
    228 	 * Wait for the next RPC and then send a key request
    229 	 * in response.  The keys get added out-of-band (via the
    230 	 * ctl file), so assume the key has been added when the
    231 	 * next request comes in.
    232 	 *
    233 	 * The convgetrpc seems dodgy, because we might be in
    234 	 * the middle of an rpc, and what about the one that comes
    235 	 * in later?  It's all actually okay: convgetrpc is idempotent
    236 	 * until rpcrespond is called, so if we're in the middle of an rpc,
    237 	 * the first convgetrpc is a no-op, the rpcrespond sends back
    238 	 * the needkey, and then the client repeats the rpc we're in
    239 	 * the middle of.  Otherwise, if we're not in the middle of an
    240 	 * rpc, the first convgetrpc waits for one, we respond needkey,
    241 	 * and then the second convgetrpc waits for another.  Because
    242 	 * there is no second response, eventually the caller will get
    243 	 * around to asking for an rpc itself, at which point the already
    244 	 * gotten rpc will be returned again.
    245 	 */
    246 	if(convgetrpc(c, -1) == nil)
    247 		return -1;
    248 	if(conv->proto)
    249 		a = addattrs(parseattr(c->proto->keyprompt), a);
    250 	flog("convneedkey %A", a);
    251 	rpcrespond(c, "needkey %A", a);
    252 	if(convgetrpc(c, -1) == nil)
    253 		return -1;
    254 	flog("convneedkey returning");
    255 	return 0;
    256 }
    257 
    258 /* ask for a replacement for a bad key*/
    259 int
    260 convbadkey(Conv *c, Key *k, char *msg, Attr *a)
    261 {
    262 	if(convgetrpc(c, -1) == nil)
    263 		return -1;
    264 	flog("convbadkey %A %N / %s / %A", k->attr, k->privattr, msg, a);
    265 	rpcrespond(c, "badkey %A %N\n%s\n%A",
    266 		k->attr, k->privattr, msg, a);
    267 	if(convgetrpc(c, -1) == nil)
    268 		return -1;
    269 	return 0;
    270 }