| /* |
| * Copyright (c) 2009, Sun Microsystems, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * - Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * - Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * - Neither the name of Sun Microsystems, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * clnt_raw.c |
| * |
| * Copyright (C) 1984, Sun Microsystems, Inc. |
| * |
| * Memory based rpc for simple testing and timing. |
| * Interface to create an rpc client and server in the same process. |
| * This lets us similate rpc and get round trip overhead, without |
| * any interference from the kernel. |
| */ |
| #include <config.h> |
| #include <pthread.h> |
| #include <reentrant.h> |
| #include <assert.h> |
| #include <err.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <rpc/rpc.h> |
| #include <rpc/raw.h> |
| |
| extern mutex_t clntraw_lock; /* XXXX does it need to be extern? */ |
| |
| #define MCALL_MSG_SIZE 24 |
| |
| /* |
| * This is the "network" we will be moving stuff over. |
| */ |
| static struct clntraw_private { |
| CLIENT client_object; |
| XDR xdr_stream; |
| char *_raw_buf; |
| union { |
| struct rpc_msg mashl_rpcmsg; |
| char mashl_callmsg[MCALL_MSG_SIZE]; |
| } u; |
| u_int mcnt; |
| } *clntraw_private; |
| |
| static enum clnt_stat clnt_raw_call(CLIENT *, AUTH *, rpcproc_t, xdrproc_t, |
| void *, xdrproc_t, void *, struct timeval); |
| static void clnt_raw_geterr(CLIENT *, struct rpc_err *); |
| static bool clnt_raw_freeres(CLIENT *, xdrproc_t, void *); |
| static void clnt_raw_abort(CLIENT *); |
| static bool clnt_raw_control(CLIENT *, u_int, void *); |
| static void clnt_raw_destroy(CLIENT *); |
| static struct clnt_ops *clnt_raw_ops(void); |
| |
| /* |
| * Create a client handle for memory based rpc. |
| */ |
| CLIENT * |
| clnt_raw_ncreate(rpcprog_t prog, rpcvers_t vers) |
| { |
| struct clntraw_private *clp; |
| struct rpc_msg call_msg; |
| XDR *xdrs; |
| CLIENT *client; |
| |
| mutex_lock(&clntraw_lock); |
| clp = clntraw_private; |
| if (clp == NULL) { |
| clp = (struct clntraw_private *)mem_zalloc(sizeof(*clp)); |
| if (__rpc_rawcombuf == NULL) |
| __rpc_rawcombuf = |
| (char *)mem_zalloc(UDPMSGSIZE); |
| clp->_raw_buf = __rpc_rawcombuf; |
| clntraw_private = clp; |
| } |
| xdrs = &clp->xdr_stream; |
| client = &clp->client_object; |
| /* |
| * pre-serialize the static part of the call msg and stash it away |
| */ |
| call_msg.rm_direction = CALL; |
| call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; |
| /* XXX: prog and vers have been long historically :-( */ |
| call_msg.rm_call.cb_prog = (u_int32_t) prog; |
| call_msg.rm_call.cb_vers = (u_int32_t) vers; |
| xdrmem_create(xdrs, clp->u.mashl_callmsg, MCALL_MSG_SIZE, XDR_ENCODE); |
| if (!xdr_callhdr(xdrs, &call_msg)) |
| __warnx(TIRPC_DEBUG_FLAG_CLNT_RAW, |
| "clntraw_create - Fatal header serialization error."); |
| clp->mcnt = XDR_GETPOS(xdrs); |
| XDR_DESTROY(xdrs); |
| |
| /* |
| * Set xdrmem for client/server shared buffer |
| */ |
| xdrmem_create(xdrs, clp->_raw_buf, UDPMSGSIZE, XDR_FREE); |
| |
| /* |
| * create client handle |
| */ |
| client->cl_ops = clnt_raw_ops(); |
| mutex_unlock(&clntraw_lock); |
| return (client); |
| } |
| |
| /* ARGSUSED */ |
| static enum clnt_stat |
| clnt_raw_call(CLIENT *h, AUTH *auth, rpcproc_t proc, |
| xdrproc_t xargs, void *argsp, |
| xdrproc_t xresults, void *resultsp, |
| struct timeval timeout) |
| { |
| struct clntraw_private *clp = clntraw_private; |
| XDR *xdrs = &clp->xdr_stream; |
| struct rpc_msg msg; |
| enum clnt_stat status; |
| struct rpc_err error; |
| |
| assert(h != NULL); |
| |
| mutex_lock(&clntraw_lock); |
| if (clp == NULL) { |
| mutex_unlock(&clntraw_lock); |
| return (RPC_FAILED); |
| } |
| mutex_unlock(&clntraw_lock); |
| |
| call_again: |
| /* |
| * send request |
| */ |
| xdrs->x_op = XDR_ENCODE; |
| XDR_SETPOS(xdrs, 0); |
| clp->u.mashl_rpcmsg.rm_xid++; |
| if ((!XDR_PUTBYTES(xdrs, clp->u.mashl_callmsg, clp->mcnt)) |
| || (!XDR_PUTINT32(xdrs, (int32_t *) &proc)) |
| || (!AUTH_MARSHALL(auth, xdrs)) || (!(*xargs) (xdrs, argsp))) { |
| return (RPC_CANTENCODEARGS); |
| } |
| (void)XDR_GETPOS(xdrs); /* called just to cause overhead */ |
| |
| /* |
| * We have to call server input routine here because this is |
| * all going on in one process. Yuk. |
| */ |
| svc_getreq_common(FD_SETSIZE); |
| |
| /* |
| * get results |
| */ |
| xdrs->x_op = XDR_DECODE; |
| XDR_SETPOS(xdrs, 0); |
| msg.acpted_rply.ar_verf = _null_auth; |
| msg.acpted_rply.ar_results.where = resultsp; |
| msg.acpted_rply.ar_results.proc = xresults; |
| if (!xdr_replymsg(xdrs, &msg)) { |
| /* |
| * It's possible for xdr_replymsg() to fail partway |
| * through its attempt to decode the result from the |
| * server. If this happens, it will leave the reply |
| * structure partially populated with dynamically |
| * allocated memory. (This can happen if someone uses |
| * clntudp_bufcreate() to create a CLIENT handle and |
| * specifies a receive buffer size that is too small.) |
| * This memory must be free()ed to avoid a leak. |
| */ |
| int op = xdrs->x_op; |
| xdrs->x_op = XDR_FREE; |
| (void) xdr_replymsg(xdrs, &msg); |
| xdrs->x_op = op; |
| return (RPC_CANTDECODERES); |
| } |
| _seterr_reply(&msg, &error); |
| status = error.re_status; |
| |
| if (status == RPC_SUCCESS) { |
| if (!AUTH_VALIDATE(auth, &msg.acpted_rply.ar_verf)) |
| status = RPC_AUTHERROR; |
| } /* end successful completion */ |
| else { |
| if (AUTH_REFRESH(auth, &msg)) |
| goto call_again; |
| } /* end of unsuccessful completion */ |
| |
| if (status == RPC_SUCCESS) { |
| if (!AUTH_VALIDATE(auth, &msg.acpted_rply.ar_verf)) |
| status = RPC_AUTHERROR; |
| if (msg.acpted_rply.ar_verf.oa_base != NULL) { |
| xdrs->x_op = XDR_FREE; |
| (void)xdr_opaque_auth(xdrs, &(msg.acpted_rply.ar_verf)); |
| } |
| } |
| |
| return (status); |
| } |
| |
| /*ARGSUSED*/ |
| static void clnt_raw_geterr(CLIENT *cl, struct rpc_err *err) |
| { |
| } |
| |
| /* ARGSUSED */ |
| static bool |
| clnt_raw_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr) |
| { |
| struct clntraw_private *clp = clntraw_private; |
| XDR *xdrs = &clp->xdr_stream; |
| bool rval; |
| |
| mutex_lock(&clntraw_lock); |
| if (clp == NULL) { |
| rval = (bool) RPC_FAILED; |
| mutex_unlock(&clntraw_lock); |
| return (rval); |
| } |
| mutex_unlock(&clntraw_lock); |
| xdrs->x_op = XDR_FREE; |
| return ((*xdr_res) (xdrs, res_ptr)); |
| } |
| |
| /*ARGSUSED*/ |
| static void |
| clnt_raw_abort(CLIENT *cl) |
| { |
| } |
| |
| /*ARGSUSED*/ |
| static bool |
| clnt_raw_control(CLIENT *cl, u_int ui, void *str) |
| { |
| return (false); |
| } |
| |
| /*ARGSUSED*/ |
| static void clnt_raw_destroy(CLIENT *cl) |
| { |
| } |
| |
| static struct clnt_ops * |
| clnt_raw_ops(void) |
| { |
| static struct clnt_ops ops; |
| extern mutex_t ops_lock; /* XXXX does it need to be extern? */ |
| |
| /* VARIABLES PROTECTED BY ops_lock: ops */ |
| |
| mutex_lock(&ops_lock); |
| if (ops.cl_call == NULL) { |
| ops.cl_call = clnt_raw_call; |
| ops.cl_abort = clnt_raw_abort; |
| ops.cl_geterr = clnt_raw_geterr; |
| ops.cl_freeres = clnt_raw_freeres; |
| ops.cl_destroy = clnt_raw_destroy; |
| ops.cl_control = clnt_raw_control; |
| } |
| mutex_unlock(&ops_lock); |
| return (&ops); |
| } |