clnt_dg.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 connectionless client side RPC.
*/
#include "rac_private.h"
#ifndef NDEBUG
#include <stdio.h>
#endif
#include <assert.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <tiuser.h>
#define MCALL_MSG_SIZE 24
/*
* Private data kept per client handle
*/
struct cu_data {
/* per-CLIENT information */
int cu_fd; /* connections fd */
char cu_mcallproto[MCALL_MSG_SIZE];
/* prototype marshalled callmsg */
struct t_unitdata *cu_tr_data;
/* recv. buffer for sync calls */
};
struct callinfo {
#define CI_ASYNC 1
#define CI_SYNC 2
int ci_nrefreshes; /* times to refresh cred */
/* First timeout associated with this call */
char *ci_outbuf; /* per-call output buffer */
struct t_unitdata *ci_trdata;
};
static struct clnt_ops *clnt_dg_ops(void);
int);
static void free_callinfo(struct callinfo *);
extern int __rpc_timeval_to_msec(struct timeval *);
/*
* Connectionless client creation returns with client handle parameters.
* Default options are set, which the user can change using clnt_control().
* fd should be open and bound.
* NB: The cl->cl_auth is initialized to null authentication.
* Caller may wish to set this something more useful.
*
* sendsz and recvsz are the maximum allowable packet sizes that can be
* sent and received. Normally they are the same, but they can be
* changed to improve the program efficiency and buffer allocation.
* If they are 0, use the transport default.
*
* If svcaddr is NULL, returns NULL.
*/
CLIENT *
{
struct t_unitdata *tr_data;
}
goto err1;
goto err1;
goto err1;
}
/* Other values can also be set through clnt_control() */
/* XID is set in clnt_dg_marshall() */
goto err2;
}
goto err2;
}
} else {
/*
* Find the receive and the send size
*/
}
/*
* Should be multiple of 4 for XDR.
*/
/*
* By default, closeit is always FALSE. It is users responsibility
* to do a t_close on it, else the user may use clnt_control
*/
goto err1;
}
return (cl);
err1:
err2:
if (cl) {
if (cu) {
}
}
}
static enum clnt_stat
{
return (RPC_SYSTEMERROR);
} else {
}
#ifdef PRINTFS
printf("clnt_dg_call: call_again\n");
#endif
}
#ifdef PRINTFS
else
printf("clnt_dg_call: clnt_dg_marshall succeeded\n");
#endif
}
#ifdef PRINTFS
else
printf("clnt_dg_call: clnt_dg_send succeeded\n");
#endif
/*
* Hack to provide rpc-based message passing
*/
case (int)RS_RESEND:
#ifdef PRINTFS
printf("clnt_dg_receive returned RS_RESEND\n");
#endif
goto send_again;
/* NOTREACHED */
case (int)RS_ERROR:
#ifdef PRINTFS
printf("clnt_dg_receive returned error %d\n",
#endif
/* NOTREACHED */
case (int)RS_OK:
#ifdef PRINTFS
printf("clnt_dg_receive returned OK\n");
#endif
break;
}
case (int)XS_CALLAGAIN:
#ifdef PRINTFS
printf("clnt_dg_xdrin returned CALL_AGAIN\n");
#endif
goto call_again;
case (int)XS_ERROR:
case (int)XS_OK:
default:
#ifdef PRINTFS
#endif
}
/* NOTREACHED */
}
static enum clnt_stat
{
/*
* the transaction id is the first thing in the output buffer
*/
#ifdef PRINTFS
#endif
else
return (RPC_SUCCESS);
}
static enum clnt_stat
{
struct t_unitdata tu_data;
} else
return (RPC_SUCCESS);
}
static enum clnt_dg_receive_stat
{
int res; /* result of operations */
static int nfds = 0;
return (-1);
}
nfds = 1;
}
for (;;) {
extern void (*_svc_getreqset_proc)();
int fds;
}
/*
* This provides for callback support. When a client
* recv's a call from another client on the server fd's,
* it calls _svc_getreqset(&readfds) which would return
* after serving all the server requests. Also look under
* svc.c
*/
if (_svc_getreqset_proc) {
/* ``+ 1'' because of pfdp[0] */
}
sizeof (pollfd_t) * svc_max_pollfd);
} else {
}
case 0:
}
/* update the time to next retransmission */
}
}
return (RS_RESEND);
else {
return (RS_ERROR);
}
case -1:
return (RS_ERROR);
}
errno = 0; /* reset it */
continue;
}
if (ci->ci_firsttimeout) {
/*
* Could have done gettimeofday before clnt_call
* but that means 1 more system call per each
* clnt_call, so do it after first time out
*/
if (gettimeofday(&startime,
errno = 0;
continue;
}
ci->ci_firsttimeout = 0;
errno = 0;
continue;
};
if (gettimeofday(&curtime,
errno = 0;
continue;
};
};
}
return (RS_ERROR);
}
errno = 0; /* reset it */
continue;
}
/* must be for server side of the house */
continue; /* do poll again */
/*
* Note: we're faking errno here because we
* previously would have expected select() to
* return -1 with errno EBADF. Poll(BA_OS)
* returns 0 and sets the POLLNVAL revents flag
* instead.
*/
return (RS_ERROR);
}
/* We have some data now */
T_ALL);
return (RS_ERROR);
}
} else
do {
int moreflag; /* flag indicating more data */
moreflag = 0;
#ifdef PRINTFS
printf("clnt_dg_receive: moreflag %d, udata.len %d, recvsz %d\n",
#endif
/*
* Drop this packet. I ain't got any
* more space.
*/
res = -1;
/* I should not really be doing this */
errno = 0;
/*
* XXX: Not really Buffer overflow in the
* sense of TLI.
*/
}
if (res < 0) {
#ifdef sun
#else
#endif
continue;
int old;
continue;
else
} else {
}
return (RS_ERROR);
}
/* tmp_trdata is freed at the top of the for loop */
continue;
/*
* If the returned XID happens to match ours, we're in
* luck. If not, we have to search for an in-progress
* call to which the reply should be attached.
*/
register struct callinfo *p;
#ifdef PRINTFS
printf("clnt_dg_receive: pktxid (%x) != ci_xid (%x)\n",
#endif
/* don't overwrite a previous reply */
p->ci_trdata = tmp_trdata;
/*
* Prevent a t_free on the
* packet we gave away
*/
} else {
/* copy t_unitdata structure */
p->ci_trdata = (struct t_unitdata *)
T_ADDR)) {
(void) t_free(
(char *)p->ci_trdata,
return (RS_ERROR);
}
T_OPT)) {
(void) t_free(
(char *)p->ci_trdata,
return (RS_ERROR);
}
T_DATA)) {
(void) t_free(
(char *)p->ci_trdata,
return (RS_ERROR);
}
}
}
/*
* Else, tmp_trdata is freed at the top of the
* for loop
*/
continue;
}
#ifdef PRINTFS
else
printf("clnt_dg_receive: xid match\n");
#endif
/* we now assume we have the proper reply */
break;
}
return (RS_OK);
}
static enum clnt_dg_xdrin_stat
{
/*
* now decode and validate the response
*/
else
}
/* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
if (ok) {
else
}
(void) xdr_opaque_auth(xdrs,
}
} /* end successful completion */
/*
* If unsuccesful AND error is an authentication error
* then refresh credentials and try again, else break
*/
/* maybe our credentials need to be refreshed ... */
if (ci->ci_nrefreshes > 0 &&
ci->ci_nrefreshes--;
return (XS_CALLAGAIN);
}
/* end of valid reply message */
} else {
return (XS_ERROR);
}
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. Finally, we assume that
* the error must have been from a rac_send() failure and look in
* the rac_senderr structure.
*/
static void
{
return;
}
else
/*
* No calls in progress at all - assume this was a
* rac_send failure.
*/
*errp = rac_senderr;
}
/*
* 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);
}
/* ARGSUSED */
static void
clnt_dg_abort(CLIENT *h)
{
}
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: /* Give him the fd address */
/* Now obsolete. Only for backword compatibility */
break;
case CLSET_RETRY_TIMEOUT:
return (FALSE);
break;
case CLGET_RETRY_TIMEOUT:
break;
case CLGET_FD:
break;
case CLGET_SVC_ADDR:
break;
case CLSET_SVC_ADDR: /* set to new address */
return (FALSE);
}
break;
case CLGET_XID:
/*
* This will get the xid seed value
*/
break;
case CLSET_XID:
/* This will set the xid seed of the NEXT call */
/* decrement by 1 as clnt_dg_call() increments 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 (cu->cu_closeit)
/*
* Don't destroy the one allocated synchronous callinfo
* structure.
*
* if ((ci->ci_flags & CI_SYNC) == 0)
*/
}
}
static struct clnt_ops *
{
}
return (&ops);
}
/*
* Make sure that the time is not garbage. -1 value is allowed.
*/
static bool_t
time_not_ok(struct timeval *t)
{
}
static struct callinfo *
{
/*
* Memory is arranged as:
*
* ------------------------------------
* | callinfo structure | send buffer |
* ------------------------------------
*
* with the receive buffer allocated via t_alloc()
*/
} else {
}
return (ci);
}
static void
{
}
static struct callinfo *
{
return (ci);
return ((struct callinfo *)0);
}
static void
{
else {
}
}
}
static void
{
else {
}
return;
} else
}
static enum clnt_stat
{
int polltimeout = 0;
int res; /* result of operations */
#ifdef PRINTFS
#endif
if (rachandle_is_valid(cl, h))
else
return (RPC_STALERACHANDLE);
assert(h);
/*
* If a packet's already been received (possibly by someone else
* doing a poll), return success immediately.
*/
#ifdef PRINTFS
printf("rac_dg_poll: packet waiting for handle %p\n", h);
#endif
return (RPC_SUCCESS);
}
case 0:
/*
* Compute the time difference between now and the time
* we last called clnt_dg_send().
*/
}
/*
* ``Delta1'' is the time we've waited since the last call
* to clnt_dg_send(); ``h->ci_timewaited'' contains the
* cumulative wait from the first send to the most recent.
*
* ``Delta2'' will contain the total waiting time. If it
* exceeds the total call timeout, give up.
*/
}
rac_dg_drop(cl, h);
return (RPC_TIMEDOUT);
}
/*
* Nothing there for us, but the call hasn't timed out.
* If ``delta1'' is greater than the retransmit timeout,
* retransmit the packet for the user and recompute the
* retransmit time.
*/
h->ci_timewaited = delta1;
/* remember time waited so far */
#ifdef PRINTFS
if (clnt_dg_send(h) != RPC_SUCCESS)
printf("rac_dg_poll: clnt_dg_send failed\n");
else
printf("rac_dg_poll: clnt_dg_send succeeded\n");
#else
(void) clnt_dg_send(h);
#endif
h->ci_rexmittime.tv_sec++;
}
}
}
return (RPC_INPROGRESS);
case -1:
} else {
errno = 0; /* reset it */
return (RPC_INPROGRESS);
}
/* NOTREACHED */
}
/*
* poll says we have a packet. Receive it. If it`s for us,
* great! If it's for some other customer of this CLIENT handle,
* figure out who and hang the packet off his callinfo structure.
*/
/*
* Note: we're faking errno here because we
* previously would have expected select() to
* return -1 with errno EBADF. Poll(BA_OS)
* returns 0 and sets the POLLNVAL revents flag
* instead.
*/
}
}
do {
int moreflag; /* flag indicating more data */
moreflag = 0;
/*
* Must make sure errno was not already
* EINTR in case t_rcvudata() returns -1.
* This way will only stay in the loop
* if getmsg() sets errno to EINTR.
*/
errno = 0;
}
#ifdef PRINTFS
printf("rac_dg_poll: moreflag %d, udata.len %d, recvsz %d\n",
#endif
/*
* Drop this packet. I ain't got any
* more space.
*/
res = -1;
/* I should not really be doing this */
errno = 0;
/*
* XXX: Not really Buffer overflow in the
* sense of TLI.
*/
}
if (res < 0) {
#ifdef sun
#else
#endif
return (RPC_INPROGRESS);
int old;
return (RPC_INPROGRESS);
else
} else {
}
}
return (RPC_INPROGRESS);
}
/*
* If the returned XID happens to match ours, we're in luck.
* If not, we have to search for an in-progress call to which
* the reply should be attached.
*/
#ifdef PRINTFS
printf("rac_dg_poll: xid match\n");
#endif
h->ci_trdata = tmp_trdata;
return (RPC_SUCCESS);
} else {
#ifdef PRINTFS
printf("rac_dg_poll: pktxid (%x) != ci_xid (%x)\n",
#endif
/* don't overwrite a previous reply */
#ifdef PRINTFS
printf("rac_dg_poll: found owner of xid %d: handle %p\n",
#endif
} else
/* no owner found or reply already present - drop it */
return (RPC_INPROGRESS);
}
/* NOTREACHED */
}
static enum clnt_stat
{
if (!rachandle_is_valid(cl, h))
return (RPC_STALERACHANDLE);
/*
* If a packet has been received (indicated by a non-NULL
* ci_trdata field), XDR it. Otherwise, we act just like
* normal, blocking, RPC.
*/
case (int)XS_CALLAGAIN:
#ifdef PRINTFS
printf("rac_dg_recv: clnt_dg_xdrin returned CALL_AGAIN\n");
#endif
rac_dg_drop(cl, h);
return (RPC_AUTHERROR);
case (int)XS_ERROR:
case (int)XS_OK:
default:
#ifdef PRINTFS
printf("rac_dg_recv: clnt_dg_xdrin returned %d\n",
#endif
rac_dg_drop(cl, h);
}
/* NOTREACHED */
} else {
switch ((int)clnt_dg_receive(h, CI_ASYNC)) {
case (int)RS_RESEND:
#ifdef PRINTFS
printf("rac_dg_recv: clnt_dg_receive returned RS_RESEND\n");
#endif
goto send_again;
/* NOTREACHED */
case (int)RS_ERROR:
#ifdef PRINTFS
printf("rac_dg_recv: clnt_dg_receive returned error %d\n",
#endif
rac_dg_drop(cl, h);
/* NOTREACHED */
case (int)RS_OK:
#ifdef PRINTFS
printf("rac_dg_recv: clnt_dg_receive returned OK\n");
#endif
break;
}
case (int)XS_CALLAGAIN:
#ifdef PRINTFS
printf("rac_dg_recv: clnt_dg_xdrin returned CALL_AGAIN\n");
#endif
break;
case (int)XS_ERROR:
case (int)XS_OK:
#ifdef PRINTFS
printf("rac_dg_recv: clnt_dg_xdrin returned %d\n",
#endif
rac_dg_drop(cl, h);
}
#ifdef PRINTFS
printf("rac_dg_recv: call_again\n");
#endif
rac_dg_drop(cl, h);
}
#ifdef PRINTFS
else
printf("rac_dg_recv: clnt_dg_marshall succeeded\n");
#endif
if (clnt_dg_send(h) != RPC_SUCCESS) {
rac_dg_drop(cl, h);
}
#ifdef PRINTFS
else
printf("rac_dg_recv: clnt_dg_send succeeded\n");
#endif
goto receive_again;
}
/* NOTREACHED */
}
static void *
{
return ((void *)NULL);
}
return ((void *)NULL);
#ifdef PRINTFS
printf("rac_dg_send: calling clnt_dg_send\n");
#endif
return ((void *)NULL);
}
#ifdef PRINTFS
else
#endif
return ((void *)ci);
}
static bool_t
{
return (TRUE);
}
return (FALSE);
}
static struct callinfo *
{
return (ci);
}
static int
{
if (!from)
return (0);
return (0);
} else { /* for T_DATA, also allocate memory */
}
return (0);
return (1);
}