xref: /plan9/sys/src/libsunrpc/client.c (revision 6ff5e9135082ce281d25c68a5544eef8249d480c)
134e04225SDavid du Colombier /*
234e04225SDavid du Colombier  * Sun RPC client.
334e04225SDavid du Colombier  */
434e04225SDavid du Colombier #include <u.h>
534e04225SDavid du Colombier #include <libc.h>
634e04225SDavid du Colombier #include <thread.h>
734e04225SDavid du Colombier #include <sunrpc.h>
834e04225SDavid du Colombier 
934e04225SDavid du Colombier typedef struct Out Out;
1034e04225SDavid du Colombier struct Out
1134e04225SDavid du Colombier {
1234e04225SDavid du Colombier 	char err[ERRMAX];	/* error string */
1334e04225SDavid du Colombier 	Channel *creply;	/* send to finish rpc */
1434e04225SDavid du Colombier 	uchar *p;			/* pending request packet */
1534e04225SDavid du Colombier 	int n;				/* size of request */
1634e04225SDavid du Colombier 	ulong tag;			/* flush tag of pending request */
1734e04225SDavid du Colombier 	ulong xid;			/* xid of pending request */
1834e04225SDavid du Colombier 	ulong st;			/* first send time */
1934e04225SDavid du Colombier 	ulong t;			/* resend time */
2034e04225SDavid du Colombier 	int nresend;		/* number of resends */
2134e04225SDavid du Colombier 	SunRpc rpc;		/* response rpc */
2234e04225SDavid du Colombier };
2334e04225SDavid du Colombier 
2434e04225SDavid du Colombier static void
udpThread(void * v)2534e04225SDavid du Colombier udpThread(void *v)
2634e04225SDavid du Colombier {
2734e04225SDavid du Colombier 	uchar *p, *buf;
2834e04225SDavid du Colombier 	Ioproc *io;
2934e04225SDavid du Colombier 	int n;
3034e04225SDavid du Colombier 	SunClient *cli;
3134e04225SDavid du Colombier 	enum { BufSize = 65536 };
3234e04225SDavid du Colombier 
3334e04225SDavid du Colombier 	cli = v;
3434e04225SDavid du Colombier 	buf = emalloc(BufSize);
3534e04225SDavid du Colombier 	io = ioproc();
3634e04225SDavid du Colombier 	p = nil;
3734e04225SDavid du Colombier 	for(;;){
3834e04225SDavid du Colombier 		n = ioread(io, cli->fd, buf, BufSize);
3934e04225SDavid du Colombier 		if(n <= 0)
4034e04225SDavid du Colombier 			break;
4134e04225SDavid du Colombier 		p = emalloc(4+n);
4234e04225SDavid du Colombier 		memmove(p+4, buf, n);
4334e04225SDavid du Colombier 		p[0] = n>>24;
4434e04225SDavid du Colombier 		p[1] = n>>16;
4534e04225SDavid du Colombier 		p[2] = n>>8;
4634e04225SDavid du Colombier 		p[3] = n;
4734e04225SDavid du Colombier 		if(sendp(cli->readchan, p) == 0)
4834e04225SDavid du Colombier 			break;
4934e04225SDavid du Colombier 		p = nil;
5034e04225SDavid du Colombier 	}
5134e04225SDavid du Colombier 	free(p);
5234e04225SDavid du Colombier 	closeioproc(io);
5334e04225SDavid du Colombier 	while(send(cli->dying, nil) == -1)
5434e04225SDavid du Colombier 		;
5534e04225SDavid du Colombier }
5634e04225SDavid du Colombier 
5734e04225SDavid du Colombier static void
netThread(void * v)5834e04225SDavid du Colombier netThread(void *v)
5934e04225SDavid du Colombier {
6034e04225SDavid du Colombier 	uchar *p, buf[4];
6134e04225SDavid du Colombier 	Ioproc *io;
6234e04225SDavid du Colombier 	uint n, tot;
6334e04225SDavid du Colombier 	int done;
6434e04225SDavid du Colombier 	SunClient *cli;
6534e04225SDavid du Colombier 
6634e04225SDavid du Colombier 	cli = v;
6734e04225SDavid du Colombier 	io = ioproc();
6834e04225SDavid du Colombier 	tot = 0;
6934e04225SDavid du Colombier 	p = nil;
7034e04225SDavid du Colombier 	for(;;){
7134e04225SDavid du Colombier 		n = ioreadn(io, cli->fd, buf, 4);
7234e04225SDavid du Colombier 		if(n != 4)
7334e04225SDavid du Colombier 			break;
7434e04225SDavid du Colombier 		n = (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3];
7534e04225SDavid du Colombier 		if(cli->chatty)
7634e04225SDavid du Colombier 			fprint(2, "%.8ux...", n);
7734e04225SDavid du Colombier 		done = n&0x80000000;
7834e04225SDavid du Colombier 		n &= ~0x80000000;
7934e04225SDavid du Colombier 		if(tot == 0){
8034e04225SDavid du Colombier 			p = emalloc(4+n);
8134e04225SDavid du Colombier 			tot = 4;
8234e04225SDavid du Colombier 		}else
8334e04225SDavid du Colombier 			p = erealloc(p, tot+n);
8434e04225SDavid du Colombier 		if(ioreadn(io, cli->fd, p+tot, n) != n)
8534e04225SDavid du Colombier 			break;
8634e04225SDavid du Colombier 		tot += n;
8734e04225SDavid du Colombier 		if(done){
8834e04225SDavid du Colombier 			p[0] = tot>>24;
8934e04225SDavid du Colombier 			p[1] = tot>>16;
9034e04225SDavid du Colombier 			p[2] = tot>>8;
9134e04225SDavid du Colombier 			p[3] = tot;
9234e04225SDavid du Colombier 			if(sendp(cli->readchan, p) == 0)
9334e04225SDavid du Colombier 				break;
9434e04225SDavid du Colombier 			p = nil;
9534e04225SDavid du Colombier 			tot = 0;
9634e04225SDavid du Colombier 		}
9734e04225SDavid du Colombier 	}
9834e04225SDavid du Colombier 	free(p);
9934e04225SDavid du Colombier 	closeioproc(io);
10034e04225SDavid du Colombier 	while(send(cli->dying, 0) == -1)
10134e04225SDavid du Colombier 		;
10234e04225SDavid du Colombier }
10334e04225SDavid du Colombier 
10434e04225SDavid du Colombier static void
timerThread(void * v)10534e04225SDavid du Colombier timerThread(void *v)
10634e04225SDavid du Colombier {
10734e04225SDavid du Colombier 	Ioproc *io;
10834e04225SDavid du Colombier 	SunClient *cli;
10934e04225SDavid du Colombier 
11034e04225SDavid du Colombier 	cli = v;
11134e04225SDavid du Colombier 	io = ioproc();
11234e04225SDavid du Colombier 	for(;;){
11334e04225SDavid du Colombier 		if(iosleep(io, 200) < 0)
11434e04225SDavid du Colombier 			break;
11534e04225SDavid du Colombier 		if(sendul(cli->timerchan, 0) == 0)
11634e04225SDavid du Colombier 			break;
11734e04225SDavid du Colombier 	}
11834e04225SDavid du Colombier 	closeioproc(io);
11934e04225SDavid du Colombier 	while(send(cli->dying, 0) == -1)
12034e04225SDavid du Colombier 		;
12134e04225SDavid du Colombier }
12234e04225SDavid du Colombier 
12334e04225SDavid du Colombier static ulong
msec(void)12434e04225SDavid du Colombier msec(void)
12534e04225SDavid du Colombier {
12634e04225SDavid du Colombier 	return nsec()/1000000;
12734e04225SDavid du Colombier }
12834e04225SDavid du Colombier 
12934e04225SDavid du Colombier static ulong
twait(ulong rtt,int nresend)13034e04225SDavid du Colombier twait(ulong rtt, int nresend)
13134e04225SDavid du Colombier {
13234e04225SDavid du Colombier 	ulong t;
13334e04225SDavid du Colombier 
13434e04225SDavid du Colombier 	t = rtt;
13534e04225SDavid du Colombier 	if(nresend <= 1)
13634e04225SDavid du Colombier 		{}
13734e04225SDavid du Colombier 	else if(nresend <= 3)
13834e04225SDavid du Colombier 		t *= 2;
13934e04225SDavid du Colombier 	else if(nresend <= 18)
14034e04225SDavid du Colombier 		t <<= nresend-2;
14134e04225SDavid du Colombier 	else
14234e04225SDavid du Colombier 		t = 60*1000;
14334e04225SDavid du Colombier 	if(t > 60*1000)
14434e04225SDavid du Colombier 		t = 60*1000;
14534e04225SDavid du Colombier 
14634e04225SDavid du Colombier 	return t;
14734e04225SDavid du Colombier }
14834e04225SDavid du Colombier 
14934e04225SDavid du Colombier static void
rpcMuxThread(void * v)15034e04225SDavid du Colombier rpcMuxThread(void *v)
15134e04225SDavid du Colombier {
15234e04225SDavid du Colombier 	uchar *buf, *p, *ep;
15334e04225SDavid du Colombier 	int i, n, nout, mout;
15434e04225SDavid du Colombier 	ulong t, xidgen, tag;
15534e04225SDavid du Colombier 	Alt a[5];
15634e04225SDavid du Colombier 	Out *o, **out;
15734e04225SDavid du Colombier 	SunRpc rpc;
15834e04225SDavid du Colombier 	SunClient *cli;
15934e04225SDavid du Colombier 
16034e04225SDavid du Colombier 	cli = v;
16134e04225SDavid du Colombier 	mout = 16;
16234e04225SDavid du Colombier 	nout = 0;
16334e04225SDavid du Colombier 	out = emalloc(mout*sizeof(out[0]));
16434e04225SDavid du Colombier 	xidgen = truerand();
16534e04225SDavid du Colombier 
16634e04225SDavid du Colombier 	a[0].op = CHANRCV;
16734e04225SDavid du Colombier 	a[0].c = cli->rpcchan;
16834e04225SDavid du Colombier 	a[0].v = &o;
16934e04225SDavid du Colombier 	a[1].op = CHANNOP;
17034e04225SDavid du Colombier 	a[1].c = cli->timerchan;
17134e04225SDavid du Colombier 	a[1].v = nil;
17234e04225SDavid du Colombier 	a[2].op = CHANRCV;
17334e04225SDavid du Colombier 	a[2].c = cli->flushchan;
17434e04225SDavid du Colombier 	a[2].v = &tag;
17534e04225SDavid du Colombier 	a[3].op = CHANRCV;
17634e04225SDavid du Colombier 	a[3].c = cli->readchan;
17734e04225SDavid du Colombier 	a[3].v = &buf;
17834e04225SDavid du Colombier 	a[4].op = CHANEND;
17934e04225SDavid du Colombier 
18034e04225SDavid du Colombier 	for(;;){
18134e04225SDavid du Colombier 		switch(alt(a)){
182*6ff5e913SDavid du Colombier 		case 0:	/* o = <-rpcchan */
18334e04225SDavid du Colombier 			if(o == nil)
18434e04225SDavid du Colombier 				goto Done;
18534e04225SDavid du Colombier 			cli->nsend++;
18634e04225SDavid du Colombier 			/* set xid */
18734e04225SDavid du Colombier 			o->xid = ++xidgen;
18834e04225SDavid du Colombier 			if(cli->needcount)
18934e04225SDavid du Colombier 				p = o->p+4;
19034e04225SDavid du Colombier 			else
19134e04225SDavid du Colombier 				p = o->p;
19234e04225SDavid du Colombier 			p[0] = xidgen>>24;
19334e04225SDavid du Colombier 			p[1] = xidgen>>16;
19434e04225SDavid du Colombier 			p[2] = xidgen>>8;
19534e04225SDavid du Colombier 			p[3] = xidgen;
19634e04225SDavid du Colombier 			if(write(cli->fd, o->p, o->n) != o->n){
19734e04225SDavid du Colombier 				free(o->p);
19834e04225SDavid du Colombier 				o->p = nil;
19934e04225SDavid du Colombier 				snprint(o->err, sizeof o->err, "write: %r");
20034e04225SDavid du Colombier 				sendp(o->creply, 0);
20134e04225SDavid du Colombier 				break;
20234e04225SDavid du Colombier 			}
20334e04225SDavid du Colombier 			if(nout >= mout){
20434e04225SDavid du Colombier 				mout *= 2;
20534e04225SDavid du Colombier 				out = erealloc(out, mout*sizeof(out[0]));
20634e04225SDavid du Colombier 			}
20734e04225SDavid du Colombier 			o->st = msec();
20834e04225SDavid du Colombier 			o->nresend = 0;
20934e04225SDavid du Colombier 			o->t = o->st + twait(cli->rtt.avg, 0);
2100b459c2cSDavid du Colombier if(cli->chatty) fprint(2, "send %lux %lud %lud\n", o->xid, o->st, o->t);
21134e04225SDavid du Colombier 			out[nout++] = o;
21234e04225SDavid du Colombier 			a[1].op = CHANRCV;
21334e04225SDavid du Colombier 			break;
21434e04225SDavid du Colombier 
215*6ff5e913SDavid du Colombier 		case 1:	/* <-timerchan */
21634e04225SDavid du Colombier 			t = msec();
21734e04225SDavid du Colombier 			for(i=0; i<nout; i++){
21834e04225SDavid du Colombier 				o = out[i];
21934e04225SDavid du Colombier 				if((int)(t - o->t) > 0){
2200b459c2cSDavid du Colombier if(cli->chatty) fprint(2, "resend %lux %lud %lud\n", o->xid, t, o->t);
22134e04225SDavid du Colombier 					if(cli->maxwait && t - o->st >= cli->maxwait){
22234e04225SDavid du Colombier 						free(o->p);
22334e04225SDavid du Colombier 						o->p = nil;
22434e04225SDavid du Colombier 						strcpy(o->err, "timeout");
22534e04225SDavid du Colombier 						sendp(o->creply, 0);
22634e04225SDavid du Colombier 						out[i--] = out[--nout];
22734e04225SDavid du Colombier 						continue;
22834e04225SDavid du Colombier 					}
22934e04225SDavid du Colombier 					cli->nresend++;
23034e04225SDavid du Colombier 					o->nresend++;
23134e04225SDavid du Colombier 					o->t = t + twait(cli->rtt.avg, o->nresend);
23234e04225SDavid du Colombier 					if(write(cli->fd, o->p, o->n) != o->n){
23334e04225SDavid du Colombier 						free(o->p);
23434e04225SDavid du Colombier 						o->p = nil;
23534e04225SDavid du Colombier 						snprint(o->err, sizeof o->err, "rewrite: %r");
23634e04225SDavid du Colombier 						sendp(o->creply, 0);
23734e04225SDavid du Colombier 						out[i--] = out[--nout];
23834e04225SDavid du Colombier 						continue;
23934e04225SDavid du Colombier 					}
24034e04225SDavid du Colombier 				}
24134e04225SDavid du Colombier 			}
24234e04225SDavid du Colombier 			/* stop ticking if no work; rpcchan will turn it back on */
24334e04225SDavid du Colombier 			if(nout == 0)
24434e04225SDavid du Colombier 				a[1].op = CHANNOP;
24534e04225SDavid du Colombier 			break;
24634e04225SDavid du Colombier 
247*6ff5e913SDavid du Colombier 		case 2:	/* tag = <-flushchan */
24834e04225SDavid du Colombier 			for(i=0; i<nout; i++){
24934e04225SDavid du Colombier 				o = out[i];
25034e04225SDavid du Colombier 				if(o->tag == tag){
25187f9f8c3SDavid du Colombier 					out[i--] = out[--nout];
25234e04225SDavid du Colombier 					strcpy(o->err, "flushed");
25334e04225SDavid du Colombier 					free(o->p);
25434e04225SDavid du Colombier 					o->p = nil;
25534e04225SDavid du Colombier 					sendp(o->creply, 0);
25634e04225SDavid du Colombier 				}
25734e04225SDavid du Colombier 			}
25834e04225SDavid du Colombier 			break;
25934e04225SDavid du Colombier 
260*6ff5e913SDavid du Colombier 		case 3:	/* buf = <-readchan */
26134e04225SDavid du Colombier 			p = buf;
26234e04225SDavid du Colombier 			n = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
26334e04225SDavid du Colombier 			p += 4;
26434e04225SDavid du Colombier 			ep = p+n;
26534e04225SDavid du Colombier 			if(sunRpcUnpack(p, ep, &p, &rpc) < 0){
2660b459c2cSDavid du Colombier 				fprint(2, "in: %.*H unpack failed\n", n, buf+4);
26734e04225SDavid du Colombier 				free(buf);
26834e04225SDavid du Colombier 				break;
26934e04225SDavid du Colombier 			}
27034e04225SDavid du Colombier 			if(cli->chatty)
2710b459c2cSDavid du Colombier 				fprint(2, "in: %B\n", &rpc);
27234e04225SDavid du Colombier 			if(rpc.iscall){
2730b459c2cSDavid du Colombier 				fprint(2, "did not get reply\n");
27434e04225SDavid du Colombier 				free(buf);
27534e04225SDavid du Colombier 				break;
27634e04225SDavid du Colombier 			}
277*6ff5e913SDavid du Colombier 			o = nil;
278*6ff5e913SDavid du Colombier 			for(i=0; i<nout; i++){
279*6ff5e913SDavid du Colombier 				o = out[i];
28034e04225SDavid du Colombier 				if(o->xid == rpc.xid)
28134e04225SDavid du Colombier 					break;
282*6ff5e913SDavid du Colombier 			}
28334e04225SDavid du Colombier 			if(i==nout){
2840b459c2cSDavid du Colombier 				if(cli->chatty) fprint(2, "did not find waiting request\n");
28534e04225SDavid du Colombier 				free(buf);
28634e04225SDavid du Colombier 				break;
28734e04225SDavid du Colombier 			}
28834e04225SDavid du Colombier 			out[i] = out[--nout];
28934e04225SDavid du Colombier 			free(o->p);
29062ec5179SDavid du Colombier 			o->p = nil;
29134e04225SDavid du Colombier 			if(rpc.status == SunSuccess){
29234e04225SDavid du Colombier 				o->p = buf;
29334e04225SDavid du Colombier 				o->rpc = rpc;
29434e04225SDavid du Colombier 			}else{
29534e04225SDavid du Colombier 				o->p = nil;
29634e04225SDavid du Colombier 				free(buf);
29734e04225SDavid du Colombier 				sunErrstr(rpc.status);
29834e04225SDavid du Colombier 				rerrstr(o->err, sizeof o->err);
29934e04225SDavid du Colombier 			}
30034e04225SDavid du Colombier 			sendp(o->creply, 0);
30134e04225SDavid du Colombier 			break;
30234e04225SDavid du Colombier 		}
30334e04225SDavid du Colombier 	}
30434e04225SDavid du Colombier Done:
30534e04225SDavid du Colombier 	free(out);
30634e04225SDavid du Colombier 	sendp(cli->dying, 0);
30734e04225SDavid du Colombier }
30834e04225SDavid du Colombier 
30934e04225SDavid du Colombier SunClient*
sunDial(char * address)31034e04225SDavid du Colombier sunDial(char *address)
31134e04225SDavid du Colombier {
31234e04225SDavid du Colombier 	int fd;
31334e04225SDavid du Colombier 	SunClient *cli;
31434e04225SDavid du Colombier 
31534e04225SDavid du Colombier 	if((fd = dial(address, 0, 0, 0)) < 0)
31634e04225SDavid du Colombier 		return nil;
31734e04225SDavid du Colombier 
31834e04225SDavid du Colombier 	cli = emalloc(sizeof(SunClient));
31934e04225SDavid du Colombier 	cli->fd = fd;
32034e04225SDavid du Colombier 	cli->maxwait = 15000;
32134e04225SDavid du Colombier 	cli->rtt.avg = 1000;
32234e04225SDavid du Colombier 	cli->dying = chancreate(sizeof(void*), 0);
32334e04225SDavid du Colombier 	cli->rpcchan = chancreate(sizeof(Out*), 0);
32434e04225SDavid du Colombier 	cli->timerchan = chancreate(sizeof(ulong), 0);
32534e04225SDavid du Colombier 	cli->flushchan = chancreate(sizeof(ulong), 0);
32634e04225SDavid du Colombier 	cli->readchan = chancreate(sizeof(uchar*), 0);
32734e04225SDavid du Colombier 	if(strstr(address, "udp!")){
32834e04225SDavid du Colombier 		cli->needcount = 0;
32934e04225SDavid du Colombier 		cli->nettid = threadcreate(udpThread, cli, SunStackSize);
33034e04225SDavid du Colombier 		cli->timertid = threadcreate(timerThread, cli, SunStackSize);
33134e04225SDavid du Colombier 	}else{
33234e04225SDavid du Colombier 		cli->needcount = 1;
33334e04225SDavid du Colombier 		cli->nettid = threadcreate(netThread, cli, SunStackSize);
33434e04225SDavid du Colombier 		/* assume reliable: don't need timer */
33534e04225SDavid du Colombier 		/* BUG: netThread should know how to redial */
33634e04225SDavid du Colombier 	}
33734e04225SDavid du Colombier 	threadcreate(rpcMuxThread, cli, SunStackSize);
33834e04225SDavid du Colombier 
33934e04225SDavid du Colombier 	return cli;
34034e04225SDavid du Colombier }
34134e04225SDavid du Colombier 
34234e04225SDavid du Colombier void
sunClientClose(SunClient * cli)34334e04225SDavid du Colombier sunClientClose(SunClient *cli)
34434e04225SDavid du Colombier {
34534e04225SDavid du Colombier 	int n;
34634e04225SDavid du Colombier 
34734e04225SDavid du Colombier 	/*
34834e04225SDavid du Colombier 	 * Threadints get you out of any stuck system calls
34934e04225SDavid du Colombier 	 * or thread rendezvouses, but do nothing if the thread
35034e04225SDavid du Colombier 	 * is in the ready state.  Keep interrupting until it takes.
35134e04225SDavid du Colombier 	 */
35234e04225SDavid du Colombier 	n = 0;
35334e04225SDavid du Colombier 	if(!cli->timertid)
35434e04225SDavid du Colombier 		n++;
35534e04225SDavid du Colombier 	while(n < 2){
35634e04225SDavid du Colombier 		threadint(cli->nettid);
35734e04225SDavid du Colombier 		if(cli->timertid)
35834e04225SDavid du Colombier 			threadint(cli->timertid);
35934e04225SDavid du Colombier 		yield();
36034e04225SDavid du Colombier 		while(nbrecv(cli->dying, nil) == 1)
36134e04225SDavid du Colombier 			n++;
36234e04225SDavid du Colombier 	}
36334e04225SDavid du Colombier 
36434e04225SDavid du Colombier 	sendp(cli->rpcchan, 0);
36534e04225SDavid du Colombier 	recvp(cli->dying);
36634e04225SDavid du Colombier 
36734e04225SDavid du Colombier 	/* everyone's gone: clean up */
36834e04225SDavid du Colombier 	close(cli->fd);
36934e04225SDavid du Colombier 	chanfree(cli->flushchan);
37034e04225SDavid du Colombier 	chanfree(cli->readchan);
37134e04225SDavid du Colombier 	chanfree(cli->timerchan);
37234e04225SDavid du Colombier 	free(cli);
37334e04225SDavid du Colombier }
37434e04225SDavid du Colombier 
37534e04225SDavid du Colombier void
sunClientFlushRpc(SunClient * cli,ulong tag)37634e04225SDavid du Colombier sunClientFlushRpc(SunClient *cli, ulong tag)
37734e04225SDavid du Colombier {
37834e04225SDavid du Colombier 	sendul(cli->flushchan, tag);
37934e04225SDavid du Colombier }
38034e04225SDavid du Colombier 
38134e04225SDavid du Colombier void
sunClientProg(SunClient * cli,SunProg * p)38234e04225SDavid du Colombier sunClientProg(SunClient *cli, SunProg *p)
38334e04225SDavid du Colombier {
38434e04225SDavid du Colombier 	if(cli->nprog%16 == 0)
38534e04225SDavid du Colombier 		cli->prog = erealloc(cli->prog, (cli->nprog+16)*sizeof(cli->prog[0]));
38634e04225SDavid du Colombier 	cli->prog[cli->nprog++] = p;
38734e04225SDavid du Colombier }
38834e04225SDavid du Colombier 
38934e04225SDavid du Colombier int
sunClientRpc(SunClient * cli,ulong tag,SunCall * tx,SunCall * rx,uchar ** tofree)39034e04225SDavid du Colombier sunClientRpc(SunClient *cli, ulong tag, SunCall *tx, SunCall *rx, uchar **tofree)
39134e04225SDavid du Colombier {
39234e04225SDavid du Colombier 	uchar *bp, *p, *ep;
39334e04225SDavid du Colombier 	int i, n1, n2, n, nn;
39434e04225SDavid du Colombier 	Out o;
39534e04225SDavid du Colombier 	SunProg *prog;
39634e04225SDavid du Colombier 	SunStatus ok;
39734e04225SDavid du Colombier 
39834e04225SDavid du Colombier 	for(i=0; i<cli->nprog; i++)
39934e04225SDavid du Colombier 		if(cli->prog[i]->prog == tx->rpc.prog && cli->prog[i]->vers == tx->rpc.vers)
40034e04225SDavid du Colombier 			break;
40134e04225SDavid du Colombier 	if(i==cli->nprog){
40234e04225SDavid du Colombier 		werrstr("unknown sun rpc program %d version %d", tx->rpc.prog, tx->rpc.vers);
40334e04225SDavid du Colombier 		return -1;
40434e04225SDavid du Colombier 	}
40534e04225SDavid du Colombier 	prog = cli->prog[i];
40634e04225SDavid du Colombier 
40734e04225SDavid du Colombier 	if(cli->chatty){
4080b459c2cSDavid du Colombier 		fprint(2, "out: %B\n", &tx->rpc);
4090b459c2cSDavid du Colombier 		fprint(2, "\t%C\n", tx);
41034e04225SDavid du Colombier 	}
41134e04225SDavid du Colombier 
41234e04225SDavid du Colombier 	n1 = sunRpcSize(&tx->rpc);
41334e04225SDavid du Colombier 	n2 = sunCallSize(prog, tx);
41434e04225SDavid du Colombier 
41534e04225SDavid du Colombier 	n = n1+n2;
41634e04225SDavid du Colombier 	if(cli->needcount)
41734e04225SDavid du Colombier 		n += 4;
41834e04225SDavid du Colombier 
41934e04225SDavid du Colombier 	bp = emalloc(n);
42034e04225SDavid du Colombier 	ep = bp+n;
42134e04225SDavid du Colombier 	p = bp;
42234e04225SDavid du Colombier 	if(cli->needcount){
42334e04225SDavid du Colombier 		nn = n-4;
42434e04225SDavid du Colombier 		p[0] = (nn>>24)|0x80;
42534e04225SDavid du Colombier 		p[1] = nn>>16;
42634e04225SDavid du Colombier 		p[2] = nn>>8;
42734e04225SDavid du Colombier 		p[3] = nn;
42834e04225SDavid du Colombier 		p += 4;
42934e04225SDavid du Colombier 	}
43034e04225SDavid du Colombier 	if((ok = sunRpcPack(p, ep, &p, &tx->rpc)) != SunSuccess
43134e04225SDavid du Colombier 	|| (ok = sunCallPack(prog, p, ep, &p, tx)) != SunSuccess){
43234e04225SDavid du Colombier 		sunErrstr(ok);
43334e04225SDavid du Colombier 		free(bp);
43434e04225SDavid du Colombier 		return -1;
43534e04225SDavid du Colombier 	}
43634e04225SDavid du Colombier 	if(p != ep){
43734e04225SDavid du Colombier 		werrstr("rpc: packet size mismatch");
43834e04225SDavid du Colombier 		free(bp);
43934e04225SDavid du Colombier 		return -1;
44034e04225SDavid du Colombier 	}
44134e04225SDavid du Colombier 
44234e04225SDavid du Colombier 	memset(&o, 0, sizeof o);
44334e04225SDavid du Colombier 	o.creply = chancreate(sizeof(void*), 0);
44434e04225SDavid du Colombier 	o.tag = tag;
44534e04225SDavid du Colombier 	o.p = bp;
44634e04225SDavid du Colombier 	o.n = n;
44734e04225SDavid du Colombier 
44834e04225SDavid du Colombier 	sendp(cli->rpcchan, &o);
44934e04225SDavid du Colombier 	recvp(o.creply);
45034e04225SDavid du Colombier 	chanfree(o.creply);
45134e04225SDavid du Colombier 
45234e04225SDavid du Colombier 	if(o.p == nil){
45334e04225SDavid du Colombier 		werrstr("%s", o.err);
45434e04225SDavid du Colombier 		return -1;
45534e04225SDavid du Colombier 	}
45634e04225SDavid du Colombier 
45734e04225SDavid du Colombier 	p = o.rpc.data;
45834e04225SDavid du Colombier 	ep = p+o.rpc.ndata;
45934e04225SDavid du Colombier 	rx->rpc = o.rpc;
46034e04225SDavid du Colombier 	rx->rpc.proc = tx->rpc.proc;
46134e04225SDavid du Colombier 	rx->rpc.prog = tx->rpc.prog;
46234e04225SDavid du Colombier 	rx->rpc.vers = tx->rpc.vers;
46334e04225SDavid du Colombier 	rx->type = (rx->rpc.proc<<1)|1;
46434e04225SDavid du Colombier 	if((ok = sunCallUnpack(prog, p, ep, &p, rx)) != SunSuccess){
46534e04225SDavid du Colombier 		sunErrstr(ok);
46634e04225SDavid du Colombier 		werrstr("unpack: %r");
46734e04225SDavid du Colombier 		free(o.p);
46834e04225SDavid du Colombier 		return -1;
46934e04225SDavid du Colombier 	}
47034e04225SDavid du Colombier 
47134e04225SDavid du Colombier 	if(cli->chatty){
4720b459c2cSDavid du Colombier 		fprint(2, "in: %B\n", &rx->rpc);
4730b459c2cSDavid du Colombier 		fprint(2, "in:\t%C\n", rx);
47434e04225SDavid du Colombier 	}
47534e04225SDavid du Colombier 
47634e04225SDavid du Colombier 	if(tofree)
47734e04225SDavid du Colombier 		*tofree = o.p;
47834e04225SDavid du Colombier 	else
47934e04225SDavid du Colombier 		free(o.p);
48034e04225SDavid du Colombier 
48134e04225SDavid du Colombier 	return 0;
48234e04225SDavid du Colombier }
483