1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  *
22*0Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
23*0Sstevel@tonic-gate  * Use is subject to license terms.
24*0Sstevel@tonic-gate  */
25*0Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26*0Sstevel@tonic-gate /* All Rights Reserved */
27*0Sstevel@tonic-gate /*
28*0Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley
29*0Sstevel@tonic-gate  * 4.3 BSD under license from the Regents of the University of
30*0Sstevel@tonic-gate  * California.
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate /*
36*0Sstevel@tonic-gate  * clnt_raw.c
37*0Sstevel@tonic-gate  *
38*0Sstevel@tonic-gate  * Memory based rpc for simple testing and timing.
39*0Sstevel@tonic-gate  * Interface to create an rpc client and server in the same process.
40*0Sstevel@tonic-gate  * This lets us similate rpc and get round trip overhead, without
41*0Sstevel@tonic-gate  * any interference from the kernel.
42*0Sstevel@tonic-gate  */
43*0Sstevel@tonic-gate #include "mt.h"
44*0Sstevel@tonic-gate #include "rpc_mt.h"
45*0Sstevel@tonic-gate #include <rpc/rpc.h>
46*0Sstevel@tonic-gate #include <rpc/trace.h>
47*0Sstevel@tonic-gate #include <rpc/raw.h>
48*0Sstevel@tonic-gate #include <syslog.h>
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate extern mutex_t	clntraw_lock;
51*0Sstevel@tonic-gate #define	MCALL_MSG_SIZE 24
52*0Sstevel@tonic-gate #ifndef UDPMSGSIZE
53*0Sstevel@tonic-gate #define	UDPMSGSIZE 8800
54*0Sstevel@tonic-gate #endif
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate /*
57*0Sstevel@tonic-gate  * This is the "network" we will be moving stuff over.
58*0Sstevel@tonic-gate  */
59*0Sstevel@tonic-gate static struct clnt_raw_private {
60*0Sstevel@tonic-gate 	CLIENT	client_object;
61*0Sstevel@tonic-gate 	XDR	xdr_stream;
62*0Sstevel@tonic-gate 	char	*raw_buf;	/* should be shared with server handle */
63*0Sstevel@tonic-gate 	char	mashl_callmsg[MCALL_MSG_SIZE];
64*0Sstevel@tonic-gate 	uint_t	mcnt;
65*0Sstevel@tonic-gate } *clnt_raw_private;
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate static struct clnt_ops *clnt_raw_ops();
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate extern char	*calloc();
70*0Sstevel@tonic-gate extern void	free();
71*0Sstevel@tonic-gate extern void svc_getreq_common(int);
72*0Sstevel@tonic-gate extern bool_t xdr_opaque_auth();
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate /*
75*0Sstevel@tonic-gate  * Create a client handle for memory based rpc.
76*0Sstevel@tonic-gate  */
77*0Sstevel@tonic-gate CLIENT *
78*0Sstevel@tonic-gate clnt_raw_create(rpcprog_t prog, rpcvers_t vers)
79*0Sstevel@tonic-gate {
80*0Sstevel@tonic-gate 	struct clnt_raw_private *clp;
81*0Sstevel@tonic-gate 	struct rpc_msg call_msg;
82*0Sstevel@tonic-gate 	XDR *xdrs;
83*0Sstevel@tonic-gate 	CLIENT *client;
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate /* VARIABLES PROTECTED BY clntraw_lock: clp */
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate 	trace3(TR_clnt_raw_create, 0, prog, vers);
88*0Sstevel@tonic-gate 	mutex_lock(&clntraw_lock);
89*0Sstevel@tonic-gate 	clp = clnt_raw_private;
90*0Sstevel@tonic-gate 	if (clp == NULL) {
91*0Sstevel@tonic-gate /* LINTED pointer alignment */
92*0Sstevel@tonic-gate 		clp = (struct clnt_raw_private *)calloc(1, sizeof (*clp));
93*0Sstevel@tonic-gate 		if (clp == NULL) {
94*0Sstevel@tonic-gate 			mutex_unlock(&clntraw_lock);
95*0Sstevel@tonic-gate 			trace3(TR_clnt_raw_create, 1, prog, vers);
96*0Sstevel@tonic-gate 			return ((CLIENT *)NULL);
97*0Sstevel@tonic-gate 		}
98*0Sstevel@tonic-gate 		if (_rawcombuf == NULL) {
99*0Sstevel@tonic-gate 			_rawcombuf = (char *)calloc(UDPMSGSIZE, sizeof (char));
100*0Sstevel@tonic-gate 			if (_rawcombuf == NULL) {
101*0Sstevel@tonic-gate 				syslog(LOG_ERR, "clnt_raw_create: "
102*0Sstevel@tonic-gate 					"out of memory.");
103*0Sstevel@tonic-gate 				if (clp)
104*0Sstevel@tonic-gate 					free(clp);
105*0Sstevel@tonic-gate 				mutex_unlock(&clntraw_lock);
106*0Sstevel@tonic-gate 				trace3(TR_clnt_raw_create, 1, prog, vers);
107*0Sstevel@tonic-gate 				return ((CLIENT *)NULL);
108*0Sstevel@tonic-gate 			}
109*0Sstevel@tonic-gate 		}
110*0Sstevel@tonic-gate 		clp->raw_buf = _rawcombuf; /* Share it with the server */
111*0Sstevel@tonic-gate 		clnt_raw_private = clp;
112*0Sstevel@tonic-gate 	}
113*0Sstevel@tonic-gate 	xdrs = &clp->xdr_stream;
114*0Sstevel@tonic-gate 	client = &clp->client_object;
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 	/*
117*0Sstevel@tonic-gate 	 * pre-serialize the static part of the call msg and stash it away
118*0Sstevel@tonic-gate 	 */
119*0Sstevel@tonic-gate 	call_msg.rm_direction = CALL;
120*0Sstevel@tonic-gate 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
121*0Sstevel@tonic-gate 	call_msg.rm_call.cb_prog = prog;
122*0Sstevel@tonic-gate 	call_msg.rm_call.cb_vers = vers;
123*0Sstevel@tonic-gate 	xdrmem_create(xdrs, clp->mashl_callmsg, MCALL_MSG_SIZE, XDR_ENCODE);
124*0Sstevel@tonic-gate 	if (! xdr_callhdr(xdrs, &call_msg))
125*0Sstevel@tonic-gate 		(void) syslog(LOG_ERR,
126*0Sstevel@tonic-gate 			(const char *) "clnt_raw_create :  \
127*0Sstevel@tonic-gate 			Fatal header serialization error.");
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 	clp->mcnt = XDR_GETPOS(xdrs);
130*0Sstevel@tonic-gate 	XDR_DESTROY(xdrs);
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate 	/*
133*0Sstevel@tonic-gate 	 * Set xdrmem for client/server shared buffer
134*0Sstevel@tonic-gate 	 */
135*0Sstevel@tonic-gate 	xdrmem_create(xdrs, clp->raw_buf, UDPMSGSIZE, XDR_FREE);
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	/*
138*0Sstevel@tonic-gate 	 * create client handle
139*0Sstevel@tonic-gate 	 */
140*0Sstevel@tonic-gate 	client->cl_ops = clnt_raw_ops();
141*0Sstevel@tonic-gate 	client->cl_auth = authnone_create();
142*0Sstevel@tonic-gate 	mutex_unlock(&clntraw_lock);
143*0Sstevel@tonic-gate 	trace3(TR_clnt_raw_create, 1, prog, vers);
144*0Sstevel@tonic-gate 	return (client);
145*0Sstevel@tonic-gate }
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate /*ARGSUSED*/
148*0Sstevel@tonic-gate static enum clnt_stat
149*0Sstevel@tonic-gate clnt_raw_call(CLIENT *h, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp,
150*0Sstevel@tonic-gate 	xdrproc_t xresults, caddr_t resultsp, struct timeval timeout)
151*0Sstevel@tonic-gate {
152*0Sstevel@tonic-gate 	struct clnt_raw_private *clp;
153*0Sstevel@tonic-gate 	XDR *xdrs;
154*0Sstevel@tonic-gate 	struct rpc_msg msg;
155*0Sstevel@tonic-gate 	enum clnt_stat status;
156*0Sstevel@tonic-gate 	struct rpc_err error;
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	trace3(TR_clnt_raw_call, 0, h, proc);
159*0Sstevel@tonic-gate 	mutex_lock(&clntraw_lock);
160*0Sstevel@tonic-gate 	clp = clnt_raw_private;
161*0Sstevel@tonic-gate 	xdrs = &clp->xdr_stream;
162*0Sstevel@tonic-gate 	if (clp == NULL) {
163*0Sstevel@tonic-gate 		mutex_unlock(&clntraw_lock);
164*0Sstevel@tonic-gate 		trace3(TR_clnt_raw_call, 1, h, proc);
165*0Sstevel@tonic-gate 		return (RPC_FAILED);
166*0Sstevel@tonic-gate 	}
167*0Sstevel@tonic-gate 	mutex_unlock(&clntraw_lock);
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate call_again:
170*0Sstevel@tonic-gate 	/*
171*0Sstevel@tonic-gate 	 * send request
172*0Sstevel@tonic-gate 	 */
173*0Sstevel@tonic-gate 	xdrs->x_op = XDR_ENCODE;
174*0Sstevel@tonic-gate 	XDR_SETPOS(xdrs, 0);
175*0Sstevel@tonic-gate /* LINTED pointer alignment */
176*0Sstevel@tonic-gate 	((struct rpc_msg *)clp->mashl_callmsg)->rm_xid++;
177*0Sstevel@tonic-gate 	if ((! XDR_PUTBYTES(xdrs, clp->mashl_callmsg, clp->mcnt)) ||
178*0Sstevel@tonic-gate 	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
179*0Sstevel@tonic-gate 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
180*0Sstevel@tonic-gate 	    (! (*xargs)(xdrs, argsp))) {
181*0Sstevel@tonic-gate 		trace3(TR_clnt_raw_call, 1, h, proc);
182*0Sstevel@tonic-gate 		return (RPC_CANTENCODEARGS);
183*0Sstevel@tonic-gate 	}
184*0Sstevel@tonic-gate 	(void) XDR_GETPOS(xdrs);  /* called just to cause overhead */
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	/*
187*0Sstevel@tonic-gate 	 * We have to call server input routine here because this is
188*0Sstevel@tonic-gate 	 * all going on in one process.
189*0Sstevel@tonic-gate 	 * By convention using FD_SETSIZE as the psuedo file descriptor.
190*0Sstevel@tonic-gate 	 */
191*0Sstevel@tonic-gate 	svc_getreq_common(FD_SETSIZE);
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	/*
194*0Sstevel@tonic-gate 	 * get results
195*0Sstevel@tonic-gate 	 */
196*0Sstevel@tonic-gate 	xdrs->x_op = XDR_DECODE;
197*0Sstevel@tonic-gate 	XDR_SETPOS(xdrs, 0);
198*0Sstevel@tonic-gate 	msg.acpted_rply.ar_verf = _null_auth;
199*0Sstevel@tonic-gate 	msg.acpted_rply.ar_results.where = resultsp;
200*0Sstevel@tonic-gate 	msg.acpted_rply.ar_results.proc = xresults;
201*0Sstevel@tonic-gate 	if (! xdr_replymsg(xdrs, &msg)) {
202*0Sstevel@tonic-gate 		trace3(TR_clnt_raw_call, 1, h, proc);
203*0Sstevel@tonic-gate 		return (RPC_CANTDECODERES);
204*0Sstevel@tonic-gate 	}
205*0Sstevel@tonic-gate 	if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
206*0Sstevel@tonic-gate 		    (msg.acpted_rply.ar_stat == SUCCESS))
207*0Sstevel@tonic-gate 		status = RPC_SUCCESS;
208*0Sstevel@tonic-gate 	else {
209*0Sstevel@tonic-gate 		__seterr_reply(&msg, &error);
210*0Sstevel@tonic-gate 		status = error.re_status;
211*0Sstevel@tonic-gate 	}
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate 	if (status == RPC_SUCCESS) {
214*0Sstevel@tonic-gate 		if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) {
215*0Sstevel@tonic-gate 			status = RPC_AUTHERROR;
216*0Sstevel@tonic-gate 		}
217*0Sstevel@tonic-gate 		/* end successful completion */
218*0Sstevel@tonic-gate 	} else {
219*0Sstevel@tonic-gate 		if (AUTH_REFRESH(h->cl_auth, &msg))
220*0Sstevel@tonic-gate 			goto call_again;
221*0Sstevel@tonic-gate 		/* end of unsuccessful completion */
222*0Sstevel@tonic-gate 	}
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate 	if (status == RPC_SUCCESS) {
225*0Sstevel@tonic-gate 		if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) {
226*0Sstevel@tonic-gate 			status = RPC_AUTHERROR;
227*0Sstevel@tonic-gate 		}
228*0Sstevel@tonic-gate 		if (msg.acpted_rply.ar_verf.oa_base != NULL) {
229*0Sstevel@tonic-gate 			xdrs->x_op = XDR_FREE;
230*0Sstevel@tonic-gate 			(void) xdr_opaque_auth(xdrs,
231*0Sstevel@tonic-gate 					&(msg.acpted_rply.ar_verf));
232*0Sstevel@tonic-gate 		}
233*0Sstevel@tonic-gate 	}
234*0Sstevel@tonic-gate 	trace3(TR_clnt_raw_call, 1, h, proc);
235*0Sstevel@tonic-gate 	return (status);
236*0Sstevel@tonic-gate }
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate /*ARGSUSED*/
239*0Sstevel@tonic-gate static enum clnt_stat
240*0Sstevel@tonic-gate clnt_raw_send(CLIENT *h, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp)
241*0Sstevel@tonic-gate {
242*0Sstevel@tonic-gate 	struct clnt_raw_private *clp;
243*0Sstevel@tonic-gate 	XDR *xdrs;
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate 	trace3(TR_clnt_raw_send, 0, h, proc);
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 	mutex_lock(&clntraw_lock);
248*0Sstevel@tonic-gate 	clp = clnt_raw_private;
249*0Sstevel@tonic-gate 	xdrs = &clp->xdr_stream;
250*0Sstevel@tonic-gate 	if (clp == NULL) {
251*0Sstevel@tonic-gate 		mutex_unlock(&clntraw_lock);
252*0Sstevel@tonic-gate 		trace3(TR_clnt_raw_send, 1, h, proc);
253*0Sstevel@tonic-gate 		return (RPC_FAILED);
254*0Sstevel@tonic-gate 	}
255*0Sstevel@tonic-gate 	mutex_unlock(&clntraw_lock);
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	/*
258*0Sstevel@tonic-gate 	 * send request
259*0Sstevel@tonic-gate 	 */
260*0Sstevel@tonic-gate 	xdrs->x_op = XDR_ENCODE;
261*0Sstevel@tonic-gate 	XDR_SETPOS(xdrs, 0);
262*0Sstevel@tonic-gate /* LINTED pointer alignment */
263*0Sstevel@tonic-gate 	((struct rpc_msg *)clp->mashl_callmsg)->rm_xid++;
264*0Sstevel@tonic-gate 	if ((! XDR_PUTBYTES(xdrs, clp->mashl_callmsg, clp->mcnt)) ||
265*0Sstevel@tonic-gate 	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
266*0Sstevel@tonic-gate 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
267*0Sstevel@tonic-gate 	    (! (*xargs)(xdrs, argsp))) {
268*0Sstevel@tonic-gate 		trace3(TR_clnt_raw_send, 1, h, proc);
269*0Sstevel@tonic-gate 		return (RPC_CANTENCODEARGS);
270*0Sstevel@tonic-gate 	}
271*0Sstevel@tonic-gate 	(void) XDR_GETPOS(xdrs);  /* called just to cause overhead */
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	/*
274*0Sstevel@tonic-gate 	 * We have to call server input routine here because this is
275*0Sstevel@tonic-gate 	 * all going on in one process.
276*0Sstevel@tonic-gate 	 * By convention using FD_SETSIZE as the psuedo file descriptor.
277*0Sstevel@tonic-gate 	 */
278*0Sstevel@tonic-gate 	svc_getreq_common(FD_SETSIZE);
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 	return (RPC_SUCCESS);
281*0Sstevel@tonic-gate }
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate /*ARGSUSED*/
284*0Sstevel@tonic-gate static void
285*0Sstevel@tonic-gate clnt_raw_geterr(CLIENT *cl, struct rpc_err *errp)
286*0Sstevel@tonic-gate {
287*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_geterr, 0);
288*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_geterr, 1);
289*0Sstevel@tonic-gate }
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate /*ARGSUSED*/
292*0Sstevel@tonic-gate static bool_t
293*0Sstevel@tonic-gate clnt_raw_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
294*0Sstevel@tonic-gate {
295*0Sstevel@tonic-gate 	struct clnt_raw_private *clp;
296*0Sstevel@tonic-gate 	XDR *xdrs;
297*0Sstevel@tonic-gate 	static bool_t dummy;
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate 	trace2(TR_clnt_raw_freeres, 0, cl);
300*0Sstevel@tonic-gate 	mutex_lock(&clntraw_lock);
301*0Sstevel@tonic-gate 	clp = clnt_raw_private;
302*0Sstevel@tonic-gate 	xdrs = &clp->xdr_stream;
303*0Sstevel@tonic-gate 	if (clp == NULL) {
304*0Sstevel@tonic-gate 		mutex_unlock(&clntraw_lock);
305*0Sstevel@tonic-gate 		trace2(TR_clnt_raw_freeres, 1, cl);
306*0Sstevel@tonic-gate 		return (FALSE);
307*0Sstevel@tonic-gate 	}
308*0Sstevel@tonic-gate 	mutex_unlock(&clntraw_lock);
309*0Sstevel@tonic-gate 	xdrs->x_op = XDR_FREE;
310*0Sstevel@tonic-gate 	dummy  = (*xdr_res)(xdrs, res_ptr);
311*0Sstevel@tonic-gate 	trace2(TR_clnt_raw_freeres, 1, cl);
312*0Sstevel@tonic-gate 	return (dummy);
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate /*ARGSUSED*/
316*0Sstevel@tonic-gate static void
317*0Sstevel@tonic-gate clnt_raw_abort(CLIENT *cl, struct rpc_err *errp)
318*0Sstevel@tonic-gate {
319*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_abort, 0);
320*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_abort, 1);
321*0Sstevel@tonic-gate }
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate /*ARGSUSED*/
324*0Sstevel@tonic-gate static bool_t
325*0Sstevel@tonic-gate clnt_raw_control(CLIENT *cl, int request, char *info)
326*0Sstevel@tonic-gate {
327*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_control, 0);
328*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_control, 1);
329*0Sstevel@tonic-gate 	return (FALSE);
330*0Sstevel@tonic-gate }
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate /*ARGSUSED*/
333*0Sstevel@tonic-gate static void
334*0Sstevel@tonic-gate clnt_raw_destroy(CLIENT *cl)
335*0Sstevel@tonic-gate {
336*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_destroy, 0);
337*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_destroy, 1);
338*0Sstevel@tonic-gate }
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate static struct clnt_ops *
341*0Sstevel@tonic-gate clnt_raw_ops(void)
342*0Sstevel@tonic-gate {
343*0Sstevel@tonic-gate 	static struct clnt_ops ops;
344*0Sstevel@tonic-gate 	extern mutex_t	ops_lock;
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	/* VARIABLES PROTECTED BY ops_lock: ops */
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_ops, 0);
349*0Sstevel@tonic-gate 	mutex_lock(&ops_lock);
350*0Sstevel@tonic-gate 	if (ops.cl_call == NULL) {
351*0Sstevel@tonic-gate 		ops.cl_call = clnt_raw_call;
352*0Sstevel@tonic-gate 		ops.cl_send = clnt_raw_send;
353*0Sstevel@tonic-gate 		ops.cl_abort = clnt_raw_abort;
354*0Sstevel@tonic-gate 		ops.cl_geterr = clnt_raw_geterr;
355*0Sstevel@tonic-gate 		ops.cl_freeres = clnt_raw_freeres;
356*0Sstevel@tonic-gate 		ops.cl_destroy = clnt_raw_destroy;
357*0Sstevel@tonic-gate 		ops.cl_control = clnt_raw_control;
358*0Sstevel@tonic-gate 	}
359*0Sstevel@tonic-gate 	mutex_unlock(&ops_lock);
360*0Sstevel@tonic-gate 	trace1(TR_clnt_raw_ops, 1);
361*0Sstevel@tonic-gate 	return (&ops);
362*0Sstevel@tonic-gate }
363