clnt_vc.c revision ac9418faa9bc5d2e2b8fdb33f434b05183528026
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* Implements a connectionful client side RPC.
*
* Connectionful RPC supports 'batched calls'.
* A sequence of calls may be batched-up in a send buffer. The rpc call
* return immediately to the client even though the call was not necessarily
* sent. The batching occurs if the results' xdr routine is NULL (0) AND
* the rpc timeout value is zero (see clnt.h, rpc).
*
* Clients should NOT casually batch calls that in fact return results; that
* is the server side should be aware that a call is batched and not produce
* any return message. Batched calls that produce many result messages can
* deadlock (netlock) the client and the server....
*/
#include "rac_private.h"
#include <syslog.h>
#include <errno.h>
#include <sys/byteorder.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <tiuser.h>
#define MCALL_MSG_SIZE 24
/*
* Private data structure
*/
struct ct_data {
/* per-CLIENT information */
int ct_fd; /* connection's file descriptor */
char ct_mcallproto[MCALL_MSG_SIZE];
/* prototype marshalled callmsg */
void *ct_pollinfo; /* private data for pkt_vc_poll() */
};
struct callinfo {
#define CI_ASYNC 1
#define CI_SYNC 2
void *ci_readinfo; /* private data for pkt_vc_read() */
};
struct rpc_msg *);
static struct clnt_ops *clnt_vc_ops(void);
struct ct_data *);
static void free_callinfo(struct callinfo *);
static const char clnt_vc_errstr[] = "%s : %s";
static const char clnt_vc_str[] = "clnt_vc_create";
static const char __no_mem_str[] = "out of memory";
/*
* Create a client handle for a connection.
* Default options are set, which the user can change using clnt_control()'s.
* must pick send and receive buffer sizes, 0 => use the default.
* NB: fd is copied into a private area.
* NB: The rpch->cl_auth is set null authentication. Caller may wish to
* set this something more useful.
*
* fd should be open and bound.
*/
CLIENT *
{
#ifdef PRINTFS
#endif
#ifdef PRINTFS
#endif
goto err;
}
goto err;
/*
* Set up other members of private data struct
*/
ct->ct_pollinfo = (void *)0;
/*
* The actual value will be set by clnt_call or clnt_control
*/
/*
* By default, closeit is always FALSE. It is users responsibility
* to do a t_close on it, else the user may use clnt_control
*/
/*
* Initialize call message
*/
/*
* pre-serialize the static part of the call msg and stash it away
*/
goto err;
}
goto err;
} else {
/*
* Find the receive and the send size
*/
}
return (cl);
err:
if (cl) {
if (ct) {
#ifdef PRINTFS
#endif
}
#ifdef PRINTFS
#endif
}
}
static bool_t
{
int state;
if (state == -1) {
return (FALSE);
}
#ifdef PRINTFS
#endif
switch (state) {
case T_IDLE:
return (FALSE);
}
/*
* Connect only if state is IDLE and svcaddr known
*/
return (FALSE);
}
/*
* Even NULL could have sufficed for rcvcall, because
* the address returned is same for all cases except
* for the gateway case, and hence required.
*/
else
} else {
}
#ifdef PRINTFS
#endif
return (FALSE);
}
/* Free old area if allocated */
/* So that address buf does not get freed */
break;
case T_DATAXFER:
case T_OUTCON:
/*
* svcaddr could also be NULL in cases where the
* client is already bound and connected.
*/
} else {
return (FALSE);
}
}
break;
default:
return (FALSE);
}
return (TRUE);
}
static int readhack = 0;
typedef int (* recrw_fn_t)(void *, caddr_t, int);
static enum clnt_stat
{
return (RPC_SYSTEMERROR);
/*
* Create a client handle which uses xdrrec for serialization
* and authnone for authentication.
*/
return (RPC_SYSTEMERROR);
}
}
ci->ci_readinfo = (void *)0;
#ifdef PRINTFS
printf("clnt_vc_call: ci_readinfo <- 0\n");
#endif
if (!ct->ct_waitset) {
/* If time is not within limits, we ignore it. */
}
#ifdef PRINTFS
printf("clnt_vc_call: call_again\n");
#endif
#ifdef PRINTFS
printf("clnt_vc_call returning error %d\n",
#endif
}
#ifdef PRINTFS
else
printf("clnt_vc_call: vc_send succeeded\n");
#endif
if (! shipnow) {
#ifdef PRINTFS
printf("clnt_vc_call: returning RPC_SUCCESS\n");
#endif
return (RPC_SUCCESS);
}
/*
* Hack to provide rpc-based message passing
*/
if (!timerisset(&timeout)) {
#ifdef PRINTFS
printf("clnt_vc_call: returning RPC_TIMEDOUT\n");
#endif
}
#ifdef PRINTFS
#endif
if (ci->ci_readinfo) {
#ifdef PRINTFS
printf("clnt_vc_call: ci_readinfo <- 0\n");
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
printf("clnt_vc_call: ci_readinfo = 0\n");
#endif
}
#ifdef PRINTFS
else
printf("clnt_vc_call: vc_recv returned OK\n");
#endif
case (int)XS_CALLAGAIN:
#ifdef PRINTFS
printf("vc_xdrin returned XS_CALLAGAIN\n");
#endif
if (ci->ci_readinfo) {
#ifdef PRINTFS
printf("clnt_vc_call: ci_readinfo <- 0\n");
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
printf("clnt_vc_call: ci_readinfo = 0\n");
#endif
goto call_again;
case (int)XS_ERROR:
case (int)XS_OK:
default:
#ifdef PRINTFS
#endif
if (ci->ci_readinfo) {
#ifdef PRINTFS
printf("clnt_vc_call: ci_readinfo <- 0\n");
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
printf("clnt_vc_call: ci_readinfo = 0\n");
#endif
}
/* NOTREACHED */
}
static enum clnt_stat
{
#ifdef PRINTFS
#endif
}
return (RPC_SUCCESS);
}
static enum clnt_stat
{
/*
* Keep receiving until we get a valid transaction id
*/
/* CONSTCOND */
while (TRUE) {
if (! xdrrec_skiprecord(xdrs)) {
#ifdef PRINTFS
printf("vc_recv: returning %d after xdrrec_skiprecord\n",
#endif
if (ci->ci_readinfo) {
#ifdef PRINTFS
printf("vc_recv: ci_readinfo <- 0\n");
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
printf("vc_recv: ci_readinfo = 0\n");
#endif
}
else
readhack = 0;
/* now decode and validate the response header */
continue;
#ifdef PRINTFS
printf("vc_recv: returning %d after xdr_replymsg\n",
#endif
if (ci->ci_readinfo) {
#ifdef PRINTFS
printf("vc_recv: ci_readinfo <- 0\n");
#endif
ci->ci_readinfo = (void *)0;
}
}
return (RPC_SUCCESS);
#ifdef PRINTFS
else
printf("vc_recv: reply_msg->rm_xid %d, ci->ci_xid %d\n",
#endif
}
/* NOTREACHED */
}
static enum vc_xdrin_status
{
/*
* process header
*/
}
/* free verifier ... */
(void) xdr_opaque_auth(xdrs,
}
} /* end successful completion */
else {
/* maybe our credentials need to be refreshed ... */
if (ci->ci_nrefreshes-- &&
return (XS_CALLAGAIN);
else
return (XS_ERROR);
} /* end of unsuccessful completion */
return (XS_OK);
}
/*
* The action of this function is not well-defined in the face of
* asynchronous calls. We do the best we can by first trying to
* find a synchronous callinfo structure and if none is found,
* taking the first call in the chain.
*/
static void
{
return;
}
}
/*
* The action of this function is not well-defined in the face of
* asynchronous calls. We do the best we can by first trying to
* find a synchronous callinfo structure and if none is found,
* taking the first call in the chain.
*/
static bool_t
{
break;
}
if (xdrs) {
} else
return (FALSE);
}
static void
{
}
static bool_t
{
switch (request) {
case CLSET_FD_CLOSE:
return (TRUE);
case CLSET_FD_NCLOSE:
return (TRUE);
}
/* for other requests which use info */
return (FALSE);
switch (request) {
case CLSET_TIMEOUT:
return (FALSE);
break;
case CLGET_TIMEOUT:
break;
case CLGET_SERVER_ADDR: /* For compatibility only */
break;
case CLGET_FD:
break;
case CLGET_SVC_ADDR:
/* The caller should not free this memory area */
break;
case CLSET_SVC_ADDR: /* set to new address */
return (FALSE);
case CLGET_XID:
/*
* use the knowledge that xid is the
* first element in the call structure
* This will get the xid of the PREVIOUS call
*/
break;
case CLSET_XID:
/* This will set the xid of the NEXT call */
/* increment by 1 as clnt_vc_call() decrements once */
break;
case CLGET_VERS:
/*
* This RELIES on the information that, in the call body,
* the version number field is the fifth field from the
* begining of the RPC header. MUST be changed if the
* call_struct is changed
*/
4 * BYTES_PER_XDR_UNIT));
break;
case CLSET_VERS:
break;
case CLGET_PROG:
/*
* This RELIES on the information that, in the call body,
* the program number field is the fourth field from the
* begining of the RPC header. MUST be changed if the
* call_struct is changed
*/
3 * BYTES_PER_XDR_UNIT));
break;
case CLSET_PROG:
break;
case CLRAC_DROP:
break;
case CLRAC_POLL:
case CLRAC_RECV:
case CLRAC_SEND:
break;
default:
return (FALSE);
}
return (TRUE);
}
static void
{
if (ct->ct_closeit)
}
#ifdef PRINTFS
#endif
}
#ifdef PRINTFS
#endif
#ifdef PRINTFS
#endif
}
#ifdef PRINTFS
#endif
}
#ifdef PRINTFS
#endif
}
/*
* Interface between xdr serializer and vc connection.
* Behaves like the system calls, read & write, but keeps some error state
* around for the rpc level.
*/
static int
{
register void *ri;
register struct callinfo *h;
#ifdef PRINTFS
#endif
if (len == 0) {
if (ci->ci_readinfo) {
#ifdef PRINTFS
printf("read_vc: ci_readinfo <- 0\n");
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
printf("read_vc: ci_readinfo = 0\n");
#endif
return (0);
}
if (!ci->ci_readinfo) {
#ifdef PRINTFS
printf("read_vc: ci->ci_readinfo == 0\np");
#endif
while (!ci->ci_readinfo) {
#ifdef PRINTFS
printf("read_vc: calling poll\n");
#endif
case 0:
#ifdef PRINTFS
printf("read_vc: poll returned 0\n");
#endif
if (readhack == 0)
return (-1);
case -1:
#ifdef PRINTFS
printf("read_vc: poll returned -1 (errno %d, fd %d)\n",
#endif
continue;
return (-1);
default:
#ifdef PRINTFS
printf("read_vc: poll returned > 0 - calling pkt_vc_poll()\n");
#endif
#ifdef PRINTFS
#endif
if (ri == (void *)0)
continue;
#ifdef PRINTFS
#endif
#ifdef PRINTFS
#endif
} else {
#ifdef PRINTFS
#endif
if (h && (h->ci_readinfo
== (void *)0)) {
h->ci_readinfo = ri;
#ifdef PRINTFS
#endif
} else
}
break;
}
}
}
#ifdef PRINTFS
printf("read_vc: calling pkt_vc_read()\n");
#endif
case 0:
#ifdef PRINTFS
printf("read_vc: pkt_vc_read() returned 0\n");
#endif
/* premature eof */
if (ci->ci_readinfo) {
#ifdef PRINTFS
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
printf("read_vc: ci_readinfo = 0\n");
#endif
break;
case -1:
#ifdef PRINTFS
printf("read_vc: pkt_vc_read() returned -1\n");
#endif
if (ci->ci_readinfo) {
#ifdef PRINTFS
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
printf("read_vc: ci_readinfo = 0\n");
#endif
break;
default:
}
#ifdef PRINTFS
#endif
return (len);
}
static int
{
register int i, cnt;
int flag;
int maxsz;
i = 0;
return (-1);
}
}
return (len);
}
/*
* This for those transports which have a max size for data.
*/
flag)) == -1) {
return (-1);
}
}
return (len);
}
static struct clnt_ops *
{
}
return (&ops);
}
/*
* Make sure that the time is not garbage. -1 value is disallowed.
* Note this is different from time_not_ok in clnt_dg.c
*/
static bool_t
time_not_ok(struct timeval *t)
{
}
static struct callinfo *
{
#ifdef PRINTFS
printf("alloc_callinfo: malloc callinfo struct returning 0x%p\n",
ci);
#endif
} else {
}
return (ci);
}
static void
{
#ifdef PRINTFS
#endif
}
static struct callinfo *
{
return (ci);
return ((struct callinfo *)0);
}
static void
{
else {
}
}
}
static void
{
else {
}
if (ci->ci_readinfo) {
#ifdef PRINTFS
printf("rac_vc_drop: calling free_pkt(0x%p)\n",
ci->ci_readinfo);
#endif
#ifdef PRINTFS
#endif
ci->ci_readinfo = (void *)0;
}
#ifdef PRINTFS
else
#endif
if (ct->ct_pollinfo) {
#ifdef PRINTFS
#endif
#ifdef PRINTFS
#endif
ct->ct_pollinfo = (void *)0;
}
#ifdef PRINTFS
else
#endif
#ifdef PRINTFS
#endif
return;
} else
#ifdef PRINTFS
#endif
}
static enum clnt_stat
{
register void *ri;
if (rachandle_is_valid(cl, h)) {
if (h->ci_readinfo) {
#ifdef PRINTFS
printf("rac_vc_poll: 0x%p->ci_readinfo 0x%p, returning RPC_SUCCESS\n",
h, h->ci_readinfo);
#endif
return (RPC_SUCCESS);
}
#ifdef PRINTFS
printf("rac_vc_poll: got my reply (xid %d), returning RPC_SUCCESS\n",
xid);
#endif
h->ci_readinfo = ri;
return (RPC_SUCCESS);
} else {
#ifdef PRINTFS
#endif
#ifdef PRINTFS
#endif
} else {
#ifdef PRINTFS
printf("rac_vc_poll: no ci, or ci_readinfo already set for ci 0x%p\n",
ci);
#endif
}
}
}
#ifdef PRINTFS
printf("returning RPC_INPROGRESS\n");
#endif
return (RPC_INPROGRESS);
} else
return (RPC_STALERACHANDLE);
}
static enum clnt_stat
{
if (rachandle_is_valid(cl, h)) {
#ifdef PRINTFS
printf("rac_vc_recv(CLIENT 0x%p, callinfo 0x%p): "
#endif
rac_vc_drop(cl, h);
return (status);
}
case (int)XS_CALLAGAIN:
rac_vc_drop(cl, h);
return (RPC_AUTHERROR);
} else {
rac_vc_drop(cl, h);
return (status);
} else
goto recv_again;
}
/* NOTREACHED */
case (int)XS_ERROR:
case (int)XS_OK:
default:
rac_vc_drop(cl, h);
return (status);
}
/* NOTREACHED */
} else
return (RPC_STALERACHANDLE);
}
static void *
{
return ((void *)NULL);
}
#ifdef PRINTFS
#endif
ci->ci_readinfo = (void *)0;
if (!ct->ct_waitset)
/*
* Create a client handle which uses xdrrec for serialization
* and authnone for authentication.
*/
return ((void *)NULL);
}
#ifdef PRINTFS
printf("rac_vc_send: vc_send failed (error %d)\n",
#endif
return ((void *)NULL);
}
#ifdef PRINTFS
else
printf("rac_vc_send: vc_send succeeded\n");
#endif
return ((void *)ci);
}
static bool_t
{
return (TRUE);
}
return (FALSE);
}
static struct callinfo *
{
return (ci);
}