clnt_dg.c revision 92905
174462Salfred/* $NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $ */ 274462Salfred/* $FreeBSD: head/lib/libc/rpc/clnt_dg.c 92905 2002-03-21 22:49:10Z obrien $ */ 374462Salfred 474462Salfred/* 574462Salfred * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 674462Salfred * unrestricted use provided that this legend is included on all tape 774462Salfred * media and as a part of the software program in whole or part. Users 874462Salfred * may copy or modify Sun RPC without charge, but are not authorized 974462Salfred * to license or distribute it to anyone else except as part of a product or 1074462Salfred * program developed by the user. 1174462Salfred * 1274462Salfred * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 1374462Salfred * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 1474462Salfred * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 1574462Salfred * 1674462Salfred * Sun RPC is provided with no support and without any obligation on the 1774462Salfred * part of Sun Microsystems, Inc. to assist in its use, correction, 1874462Salfred * modification or enhancement. 1974462Salfred * 2074462Salfred * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 2174462Salfred * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 2274462Salfred * OR ANY PART THEREOF. 2374462Salfred * 2474462Salfred * In no event will Sun Microsystems, Inc. be liable for any lost revenue 2574462Salfred * or profits or other special, indirect and consequential damages, even if 2674462Salfred * Sun has been advised of the possibility of such damages. 2774462Salfred * 2874462Salfred * Sun Microsystems, Inc. 2974462Salfred * 2550 Garcia Avenue 3074462Salfred * Mountain View, California 94043 3174462Salfred */ 3274462Salfred/* 3374462Salfred * Copyright (c) 1986-1991 by Sun Microsystems Inc. 3474462Salfred */ 3574462Salfred 3674462Salfred/* #ident "@(#)clnt_dg.c 1.23 94/04/22 SMI" */ 3774462Salfred 3874462Salfred#if 0 3974462Salfred#if !defined(lint) && defined(SCCSIDS) 4074462Salfredstatic char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro"; 4174462Salfred#endif 4274462Salfred#endif 4374462Salfred 4474462Salfred/* 4574462Salfred * Implements a connectionless client side RPC. 4674462Salfred */ 4774462Salfred 4875094Siedowse#include "namespace.h" 4974462Salfred#include "reentrant.h" 5074462Salfred#include <sys/poll.h> 5174462Salfred#include <sys/types.h> 5274462Salfred#include <sys/time.h> 5374462Salfred#include <sys/socket.h> 5474462Salfred#include <sys/ioctl.h> 5590868Smike#include <arpa/inet.h> 5674462Salfred#include <rpc/rpc.h> 5774462Salfred#include <errno.h> 5874462Salfred#include <stdlib.h> 5974462Salfred#include <string.h> 6074462Salfred#include <signal.h> 6174462Salfred#include <unistd.h> 6274462Salfred#include <err.h> 6374462Salfred#include "un-namespace.h" 6474462Salfred#include "rpc_com.h" 6574462Salfred 6674462Salfred 6774462Salfred#define RPC_MAX_BACKOFF 30 /* seconds */ 6874462Salfred 6974462Salfred 7092905Sobrienstatic struct clnt_ops *clnt_dg_ops(void); 7192905Sobrienstatic bool_t time_not_ok(struct timeval *); 7274462Salfredstatic enum clnt_stat clnt_dg_call __P((CLIENT *, rpcproc_t, xdrproc_t, caddr_t, 7374462Salfred xdrproc_t, caddr_t, struct timeval)); 7492905Sobrienstatic void clnt_dg_geterr(CLIENT *, struct rpc_err *); 7592905Sobrienstatic bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, caddr_t); 7692905Sobrienstatic void clnt_dg_abort(CLIENT *); 7792905Sobrienstatic bool_t clnt_dg_control(CLIENT *, u_int, char *); 7892905Sobrienstatic void clnt_dg_destroy(CLIENT *); 7992905Sobrienstatic int __rpc_timeval_to_msec(struct timeval *); 8074462Salfred 8174462Salfred 8274462Salfred 8374462Salfred 8474462Salfred/* 8574462Salfred * This machinery implements per-fd locks for MT-safety. It is not 8674462Salfred * sufficient to do per-CLIENT handle locks for MT-safety because a 8774462Salfred * user may create more than one CLIENT handle with the same fd behind 8874462Salfred * it. Therfore, we allocate an array of flags (dg_fd_locks), protected 8974462Salfred * by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables 9074462Salfred * similarly protected. Dg_fd_lock[fd] == 1 => a call is activte on some 9174462Salfred * CLIENT handle created for that fd. 9274462Salfred * The current implementation holds locks across the entire RPC and reply, 9374462Salfred * including retransmissions. Yes, this is silly, and as soon as this 9474462Salfred * code is proven to work, this should be the first thing fixed. One step 9574462Salfred * at a time. 9674462Salfred */ 9774462Salfredstatic int *dg_fd_locks; 9874462Salfredextern mutex_t clnt_fd_lock; 9974462Salfredstatic cond_t *dg_cv; 10074462Salfred#define release_fd_lock(fd, mask) { \ 10174462Salfred mutex_lock(&clnt_fd_lock); \ 10275144Siedowse dg_fd_locks[fd] = 0; \ 10374462Salfred mutex_unlock(&clnt_fd_lock); \ 10474462Salfred thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL); \ 10574462Salfred cond_signal(&dg_cv[fd]); \ 10674462Salfred} 10774462Salfred 10874462Salfredstatic const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory"; 10974462Salfred 11074462Salfred/* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */ 11174462Salfred 11274462Salfred/* 11374462Salfred * Private data kept per client handle 11474462Salfred */ 11574462Salfredstruct cu_data { 11674462Salfred int cu_fd; /* connections fd */ 11774462Salfred bool_t cu_closeit; /* opened by library */ 11874462Salfred struct sockaddr_storage cu_raddr; /* remote address */ 11974462Salfred int cu_rlen; 12074462Salfred struct timeval cu_wait; /* retransmit interval */ 12174462Salfred struct timeval cu_total; /* total time for the call */ 12274462Salfred struct rpc_err cu_error; 12374462Salfred XDR cu_outxdrs; 12474462Salfred u_int cu_xdrpos; 12574462Salfred u_int cu_sendsz; /* send size */ 12674462Salfred char *cu_outbuf; 12774462Salfred u_int cu_recvsz; /* recv size */ 12874462Salfred struct pollfd pfdp; 12974879Swpaul int cu_async; 13078678Siedowse int cu_connect; /* Use connect(). */ 13178678Siedowse int cu_connected; /* Have done connect(). */ 13274462Salfred char cu_inbuf[1]; 13374462Salfred}; 13474462Salfred 13574462Salfred/* 13674462Salfred * Connection less client creation returns with client handle parameters. 13774462Salfred * Default options are set, which the user can change using clnt_control(). 13874462Salfred * fd should be open and bound. 13974462Salfred * NB: The rpch->cl_auth is initialized to null authentication. 14074462Salfred * Caller may wish to set this something more useful. 14174462Salfred * 14274462Salfred * sendsz and recvsz are the maximum allowable packet sizes that can be 14374462Salfred * sent and received. Normally they are the same, but they can be 14474462Salfred * changed to improve the program efficiency and buffer allocation. 14574462Salfred * If they are 0, use the transport default. 14674462Salfred * 14774462Salfred * If svcaddr is NULL, returns NULL. 14874462Salfred */ 14974462SalfredCLIENT * 15074462Salfredclnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz) 15174462Salfred int fd; /* open file descriptor */ 15274462Salfred const struct netbuf *svcaddr; /* servers address */ 15374462Salfred rpcprog_t program; /* program number */ 15474462Salfred rpcvers_t version; /* version number */ 15574462Salfred u_int sendsz; /* buffer recv size */ 15674462Salfred u_int recvsz; /* buffer send size */ 15774462Salfred{ 15874462Salfred CLIENT *cl = NULL; /* client handle */ 15974462Salfred struct cu_data *cu = NULL; /* private data */ 16074462Salfred struct timeval now; 16174462Salfred struct rpc_msg call_msg; 16274462Salfred sigset_t mask; 16374462Salfred sigset_t newmask; 16474462Salfred struct __rpc_sockinfo si; 16574462Salfred int one = 1; 16674462Salfred 16774462Salfred sigfillset(&newmask); 16874462Salfred thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 16974462Salfred mutex_lock(&clnt_fd_lock); 17074462Salfred if (dg_fd_locks == (int *) NULL) { 17174462Salfred int cv_allocsz; 17274462Salfred size_t fd_allocsz; 17374462Salfred int dtbsize = __rpc_dtbsize(); 17474462Salfred 17574462Salfred fd_allocsz = dtbsize * sizeof (int); 17674462Salfred dg_fd_locks = (int *) mem_alloc(fd_allocsz); 17774462Salfred if (dg_fd_locks == (int *) NULL) { 17874462Salfred mutex_unlock(&clnt_fd_lock); 17974462Salfred thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 18074462Salfred goto err1; 18174462Salfred } else 18274462Salfred memset(dg_fd_locks, '\0', fd_allocsz); 18374462Salfred 18474462Salfred cv_allocsz = dtbsize * sizeof (cond_t); 18574462Salfred dg_cv = (cond_t *) mem_alloc(cv_allocsz); 18674462Salfred if (dg_cv == (cond_t *) NULL) { 18774462Salfred mem_free(dg_fd_locks, fd_allocsz); 18874462Salfred dg_fd_locks = (int *) NULL; 18974462Salfred mutex_unlock(&clnt_fd_lock); 19074462Salfred thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 19174462Salfred goto err1; 19274462Salfred } else { 19374462Salfred int i; 19474462Salfred 19574462Salfred for (i = 0; i < dtbsize; i++) 19674462Salfred cond_init(&dg_cv[i], 0, (void *) 0); 19774462Salfred } 19874462Salfred } 19974462Salfred 20074462Salfred mutex_unlock(&clnt_fd_lock); 20174462Salfred thr_sigsetmask(SIG_SETMASK, &(mask), NULL); 20274462Salfred 20374462Salfred if (svcaddr == NULL) { 20474462Salfred rpc_createerr.cf_stat = RPC_UNKNOWNADDR; 20574462Salfred return (NULL); 20674462Salfred } 20774462Salfred 20874462Salfred if (!__rpc_fd2sockinfo(fd, &si)) { 20974462Salfred rpc_createerr.cf_stat = RPC_TLIERROR; 21074462Salfred rpc_createerr.cf_error.re_errno = 0; 21174462Salfred return (NULL); 21274462Salfred } 21374462Salfred /* 21474462Salfred * Find the receive and the send size 21574462Salfred */ 21674462Salfred sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz); 21774462Salfred recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz); 21874462Salfred if ((sendsz == 0) || (recvsz == 0)) { 21974462Salfred rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */ 22074462Salfred rpc_createerr.cf_error.re_errno = 0; 22174462Salfred return (NULL); 22274462Salfred } 22374462Salfred 22474462Salfred if ((cl = mem_alloc(sizeof (CLIENT))) == NULL) 22574462Salfred goto err1; 22674462Salfred /* 22774462Salfred * Should be multiple of 4 for XDR. 22874462Salfred */ 22974462Salfred sendsz = ((sendsz + 3) / 4) * 4; 23074462Salfred recvsz = ((recvsz + 3) / 4) * 4; 23174462Salfred cu = mem_alloc(sizeof (*cu) + sendsz + recvsz); 23274462Salfred if (cu == NULL) 23374462Salfred goto err1; 23474462Salfred (void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len); 23574462Salfred cu->cu_rlen = svcaddr->len; 23674462Salfred cu->cu_outbuf = &cu->cu_inbuf[recvsz]; 23774462Salfred /* Other values can also be set through clnt_control() */ 23874462Salfred cu->cu_wait.tv_sec = 15; /* heuristically chosen */ 23974462Salfred cu->cu_wait.tv_usec = 0; 24074462Salfred cu->cu_total.tv_sec = -1; 24174462Salfred cu->cu_total.tv_usec = -1; 24274462Salfred cu->cu_sendsz = sendsz; 24374462Salfred cu->cu_recvsz = recvsz; 24474879Swpaul cu->cu_async = FALSE; 24578678Siedowse cu->cu_connect = FALSE; 24678678Siedowse cu->cu_connected = FALSE; 24774462Salfred (void) gettimeofday(&now, NULL); 24874462Salfred call_msg.rm_xid = __RPC_GETXID(&now); 24974462Salfred call_msg.rm_call.cb_prog = program; 25074462Salfred call_msg.rm_call.cb_vers = version; 25174462Salfred xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE); 25274462Salfred if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { 25374462Salfred rpc_createerr.cf_stat = RPC_CANTENCODEARGS; /* XXX */ 25474462Salfred rpc_createerr.cf_error.re_errno = 0; 25574462Salfred goto err2; 25674462Salfred } 25774462Salfred cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); 25874462Salfred 25974462Salfred /* XXX fvdl - do we still want this? */ 26074462Salfred#if 0 26174462Salfred (void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf); 26274462Salfred#endif 26374462Salfred _ioctl(fd, FIONBIO, (char *)(void *)&one); 26474462Salfred 26574462Salfred /* 26674462Salfred * By default, closeit is always FALSE. It is users responsibility 26774462Salfred * to do a close on it, else the user may use clnt_control 26874462Salfred * to let clnt_destroy do it for him/her. 26974462Salfred */ 27074462Salfred cu->cu_closeit = FALSE; 27174462Salfred cu->cu_fd = fd; 27274462Salfred cl->cl_ops = clnt_dg_ops(); 27374462Salfred cl->cl_private = (caddr_t)(void *)cu; 27474462Salfred cl->cl_auth = authnone_create(); 27574462Salfred cl->cl_tp = NULL; 27674462Salfred cl->cl_netid = NULL; 27774462Salfred cu->pfdp.fd = cu->cu_fd; 27874462Salfred cu->pfdp.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; 27974462Salfred return (cl); 28074462Salfrederr1: 28174462Salfred warnx(mem_err_clnt_dg); 28274462Salfred rpc_createerr.cf_stat = RPC_SYSTEMERROR; 28374462Salfred rpc_createerr.cf_error.re_errno = errno; 28474462Salfrederr2: 28574462Salfred if (cl) { 28674462Salfred mem_free(cl, sizeof (CLIENT)); 28774462Salfred if (cu) 28874462Salfred mem_free(cu, sizeof (*cu) + sendsz + recvsz); 28974462Salfred } 29074462Salfred return (NULL); 29174462Salfred} 29274462Salfred 29374462Salfredstatic enum clnt_stat 29474462Salfredclnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) 29574462Salfred CLIENT *cl; /* client handle */ 29674462Salfred rpcproc_t proc; /* procedure number */ 29774462Salfred xdrproc_t xargs; /* xdr routine for args */ 29874462Salfred caddr_t argsp; /* pointer to args */ 29974462Salfred xdrproc_t xresults; /* xdr routine for results */ 30074462Salfred caddr_t resultsp; /* pointer to results */ 30174462Salfred struct timeval utimeout; /* seconds to wait before giving up */ 30274462Salfred{ 30374462Salfred struct cu_data *cu = (struct cu_data *)cl->cl_private; 30474462Salfred XDR *xdrs; 30574462Salfred size_t outlen; 30674462Salfred struct rpc_msg reply_msg; 30774462Salfred XDR reply_xdrs; 30874462Salfred struct timeval time_waited; 30974462Salfred bool_t ok; 31074462Salfred int nrefreshes = 2; /* number of times to refresh cred */ 31174462Salfred struct timeval timeout; 31274462Salfred struct timeval retransmit_time; 31374462Salfred struct timeval startime, curtime; 31474462Salfred int firsttimeout = 1; 31578678Siedowse struct sockaddr *sa; 31674462Salfred sigset_t mask; 31774462Salfred sigset_t newmask; 31878678Siedowse socklen_t inlen, salen; 31974462Salfred ssize_t recvlen = 0; 32074462Salfred int rpc_lock_value; 32174879Swpaul u_int32_t xid; 32274462Salfred 32390271Salfred outlen = 0; 32474462Salfred sigfillset(&newmask); 32574462Salfred thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 32674462Salfred mutex_lock(&clnt_fd_lock); 32774462Salfred while (dg_fd_locks[cu->cu_fd]) 32874462Salfred cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); 32974462Salfred if (__isthreaded) 33074462Salfred rpc_lock_value = 1; 33174462Salfred else 33274462Salfred rpc_lock_value = 0; 33374462Salfred dg_fd_locks[cu->cu_fd] = rpc_lock_value; 33474462Salfred mutex_unlock(&clnt_fd_lock); 33574462Salfred if (cu->cu_total.tv_usec == -1) { 33674462Salfred timeout = utimeout; /* use supplied timeout */ 33774462Salfred } else { 33874462Salfred timeout = cu->cu_total; /* use default timeout */ 33974462Salfred } 34074462Salfred 34178678Siedowse if (cu->cu_connect && !cu->cu_connected) { 34278678Siedowse if (_connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr, 34378678Siedowse cu->cu_rlen) < 0) { 34478678Siedowse release_fd_lock(cu->cu_fd, mask); 34578678Siedowse cu->cu_error.re_errno = errno; 34678678Siedowse return (cu->cu_error.re_status = RPC_CANTSEND); 34778678Siedowse } 34878678Siedowse cu->cu_connected = 1; 34978678Siedowse } 35078678Siedowse if (cu->cu_connected) { 35178678Siedowse sa = NULL; 35278678Siedowse salen = 0; 35378678Siedowse } else { 35478678Siedowse sa = (struct sockaddr *)&cu->cu_raddr; 35578678Siedowse salen = cu->cu_rlen; 35678678Siedowse } 35774462Salfred time_waited.tv_sec = 0; 35874462Salfred time_waited.tv_usec = 0; 35974462Salfred retransmit_time = cu->cu_wait; 36074462Salfred 36174462Salfredcall_again: 36274462Salfred xdrs = &(cu->cu_outxdrs); 36374879Swpaul if (cu->cu_async == TRUE && xargs == NULL) 36474879Swpaul goto get_reply; 36574462Salfred xdrs->x_op = XDR_ENCODE; 36674462Salfred XDR_SETPOS(xdrs, cu->cu_xdrpos); 36774462Salfred /* 36874462Salfred * the transaction is the first thing in the out buffer 36974879Swpaul * XXX Yes, and it's in network byte order, so we should to 37074879Swpaul * be careful when we increment it, shouldn't we. 37174462Salfred */ 37274879Swpaul xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf)); 37374879Swpaul xid++; 37474879Swpaul *(u_int32_t *)(void *)(cu->cu_outbuf) = htonl(xid); 37574879Swpaul 37674462Salfred if ((! XDR_PUTINT32(xdrs, &proc)) || 37774462Salfred (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || 37874462Salfred (! (*xargs)(xdrs, argsp))) { 37974462Salfred release_fd_lock(cu->cu_fd, mask); 38074462Salfred return (cu->cu_error.re_status = RPC_CANTENCODEARGS); 38174462Salfred } 38274462Salfred outlen = (size_t)XDR_GETPOS(xdrs); 38374462Salfred 38474462Salfredsend_again: 38578678Siedowse if (_sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) { 38674462Salfred cu->cu_error.re_errno = errno; 38774462Salfred release_fd_lock(cu->cu_fd, mask); 38874462Salfred return (cu->cu_error.re_status = RPC_CANTSEND); 38974462Salfred } 39074462Salfred 39174462Salfred /* 39274462Salfred * Hack to provide rpc-based message passing 39374462Salfred */ 39474462Salfred if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 39574462Salfred release_fd_lock(cu->cu_fd, mask); 39674462Salfred return (cu->cu_error.re_status = RPC_TIMEDOUT); 39774462Salfred } 39874879Swpaul 39974879Swpaulget_reply: 40074879Swpaul 40174462Salfred /* 40274462Salfred * sub-optimal code appears here because we have 40374462Salfred * some clock time to spare while the packets are in flight. 40474462Salfred * (We assume that this is actually only executed once.) 40574462Salfred */ 40674462Salfred reply_msg.acpted_rply.ar_verf = _null_auth; 40774462Salfred reply_msg.acpted_rply.ar_results.where = resultsp; 40874462Salfred reply_msg.acpted_rply.ar_results.proc = xresults; 40974462Salfred 41074462Salfred 41174462Salfred for (;;) { 41274462Salfred switch (_poll(&cu->pfdp, 1, 41374462Salfred __rpc_timeval_to_msec(&retransmit_time))) { 41474462Salfred case 0: 41574462Salfred time_waited.tv_sec += retransmit_time.tv_sec; 41674462Salfred time_waited.tv_usec += retransmit_time.tv_usec; 41774462Salfred while (time_waited.tv_usec >= 1000000) { 41874462Salfred time_waited.tv_sec++; 41974462Salfred time_waited.tv_usec -= 1000000; 42074462Salfred } 42174462Salfred /* update retransmit_time */ 42274462Salfred if (retransmit_time.tv_sec < RPC_MAX_BACKOFF) { 42374462Salfred retransmit_time.tv_usec *= 2; 42474462Salfred retransmit_time.tv_sec *= 2; 42574462Salfred while (retransmit_time.tv_usec >= 1000000) { 42674462Salfred retransmit_time.tv_sec++; 42774462Salfred retransmit_time.tv_usec -= 1000000; 42874462Salfred } 42974462Salfred } 43074462Salfred 43174462Salfred if ((time_waited.tv_sec < timeout.tv_sec) || 43274462Salfred ((time_waited.tv_sec == timeout.tv_sec) && 43374462Salfred (time_waited.tv_usec < timeout.tv_usec))) 43474462Salfred goto send_again; 43574462Salfred release_fd_lock(cu->cu_fd, mask); 43674462Salfred return (cu->cu_error.re_status = RPC_TIMEDOUT); 43774462Salfred 43874462Salfred case -1: 43974462Salfred if (errno == EBADF) { 44074462Salfred cu->cu_error.re_errno = errno; 44174462Salfred release_fd_lock(cu->cu_fd, mask); 44274462Salfred return (cu->cu_error.re_status = RPC_CANTRECV); 44374462Salfred } 44474462Salfred if (errno != EINTR) { 44574462Salfred errno = 0; /* reset it */ 44674462Salfred continue; 44774462Salfred } 44874462Salfred /* interrupted by another signal, update time_waited */ 44974462Salfred if (firsttimeout) { 45074462Salfred /* 45174462Salfred * Could have done gettimeofday before clnt_call 45274462Salfred * but that means 1 more system call per each 45374462Salfred * clnt_call, so do it after first time out 45474462Salfred */ 45574462Salfred if (gettimeofday(&startime, 45674462Salfred (struct timezone *) NULL) == -1) { 45774462Salfred errno = 0; 45874462Salfred continue; 45974462Salfred } 46074462Salfred firsttimeout = 0; 46174462Salfred errno = 0; 46274462Salfred continue; 46374462Salfred }; 46474462Salfred if (gettimeofday(&curtime, 46574462Salfred (struct timezone *) NULL) == -1) { 46674462Salfred errno = 0; 46774462Salfred continue; 46874462Salfred }; 46974462Salfred time_waited.tv_sec += curtime.tv_sec - startime.tv_sec; 47074462Salfred time_waited.tv_usec += curtime.tv_usec - 47174462Salfred startime.tv_usec; 47274462Salfred while (time_waited.tv_usec < 0) { 47374462Salfred time_waited.tv_sec--; 47474462Salfred time_waited.tv_usec += 1000000; 47574462Salfred }; 47674462Salfred while (time_waited.tv_usec >= 1000000) { 47774462Salfred time_waited.tv_sec++; 47874462Salfred time_waited.tv_usec -= 1000000; 47974462Salfred } 48074462Salfred startime.tv_sec = curtime.tv_sec; 48174462Salfred startime.tv_usec = curtime.tv_usec; 48274462Salfred if ((time_waited.tv_sec > timeout.tv_sec) || 48374462Salfred ((time_waited.tv_sec == timeout.tv_sec) && 48474462Salfred (time_waited.tv_usec > timeout.tv_usec))) { 48574462Salfred release_fd_lock(cu->cu_fd, mask); 48674462Salfred return (cu->cu_error.re_status = RPC_TIMEDOUT); 48774462Salfred } 48874462Salfred errno = 0; /* reset it */ 48974462Salfred continue; 49074462Salfred }; 49174462Salfred 49274462Salfred if (cu->pfdp.revents & POLLNVAL || (cu->pfdp.revents == 0)) { 49374462Salfred cu->cu_error.re_status = RPC_CANTRECV; 49474462Salfred /* 49574462Salfred * Note: we're faking errno here because we 49674462Salfred * previously would have expected _poll() to 49774462Salfred * return -1 with errno EBADF. Poll(BA_OS) 49874462Salfred * returns 0 and sets the POLLNVAL revents flag 49974462Salfred * instead. 50074462Salfred */ 50174462Salfred cu->cu_error.re_errno = errno = EBADF; 50274462Salfred release_fd_lock(cu->cu_fd, mask); 50374462Salfred return (-1); 50474462Salfred } 50574462Salfred 50674462Salfred /* We have some data now */ 50774462Salfred do { 50874462Salfred if (errno == EINTR) { 50974462Salfred /* 51074462Salfred * Must make sure errno was not already 51174462Salfred * EINTR in case _recvfrom() returns -1. 51274462Salfred */ 51374462Salfred errno = 0; 51474462Salfred } 51574462Salfred recvlen = _recvfrom(cu->cu_fd, cu->cu_inbuf, 51676824Siedowse cu->cu_recvsz, 0, NULL, NULL); 51774462Salfred } while (recvlen < 0 && errno == EINTR); 51874462Salfred if (recvlen < 0) { 51974462Salfred if (errno == EWOULDBLOCK) 52074462Salfred continue; 52174462Salfred cu->cu_error.re_errno = errno; 52274462Salfred release_fd_lock(cu->cu_fd, mask); 52374462Salfred return (cu->cu_error.re_status = RPC_CANTRECV); 52474462Salfred } 52574462Salfred if (recvlen < sizeof (u_int32_t)) 52674462Salfred continue; 52774462Salfred /* see if reply transaction id matches sent id */ 52874879Swpaul if (cu->cu_async == FALSE && 52974879Swpaul *((u_int32_t *)(void *)(cu->cu_inbuf)) != 53074462Salfred *((u_int32_t *)(void *)(cu->cu_outbuf))) 53174462Salfred continue; 53274462Salfred /* we now assume we have the proper reply */ 53374462Salfred break; 53474462Salfred } 53574462Salfred inlen = (socklen_t)recvlen; 53674462Salfred 53774462Salfred /* 53874462Salfred * now decode and validate the response 53974462Salfred */ 54074462Salfred 54174462Salfred xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE); 54274462Salfred ok = xdr_replymsg(&reply_xdrs, &reply_msg); 54374462Salfred /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ 54474462Salfred if (ok) { 54574462Salfred if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && 54674462Salfred (reply_msg.acpted_rply.ar_stat == SUCCESS)) 54774462Salfred cu->cu_error.re_status = RPC_SUCCESS; 54874462Salfred else 54974462Salfred _seterr_reply(&reply_msg, &(cu->cu_error)); 55074462Salfred 55174462Salfred if (cu->cu_error.re_status == RPC_SUCCESS) { 55274462Salfred if (! AUTH_VALIDATE(cl->cl_auth, 55374462Salfred &reply_msg.acpted_rply.ar_verf)) { 55474462Salfred cu->cu_error.re_status = RPC_AUTHERROR; 55574462Salfred cu->cu_error.re_why = AUTH_INVALIDRESP; 55674462Salfred } 55774462Salfred if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { 55874462Salfred xdrs->x_op = XDR_FREE; 55974462Salfred (void) xdr_opaque_auth(xdrs, 56074462Salfred &(reply_msg.acpted_rply.ar_verf)); 56174462Salfred } 56274462Salfred } /* end successful completion */ 56374462Salfred /* 56474462Salfred * If unsuccesful AND error is an authentication error 56574462Salfred * then refresh credentials and try again, else break 56674462Salfred */ 56774462Salfred else if (cu->cu_error.re_status == RPC_AUTHERROR) 56874462Salfred /* maybe our credentials need to be refreshed ... */ 56974462Salfred if (nrefreshes > 0 && 57074462Salfred AUTH_REFRESH(cl->cl_auth, &reply_msg)) { 57174462Salfred nrefreshes--; 57274462Salfred goto call_again; 57374462Salfred } 57474462Salfred /* end of unsuccessful completion */ 57574462Salfred } /* end of valid reply message */ 57674462Salfred else { 57774462Salfred cu->cu_error.re_status = RPC_CANTDECODERES; 57874462Salfred 57974462Salfred } 58074462Salfred release_fd_lock(cu->cu_fd, mask); 58174462Salfred return (cu->cu_error.re_status); 58274462Salfred} 58374462Salfred 58474462Salfredstatic void 58574462Salfredclnt_dg_geterr(cl, errp) 58674462Salfred CLIENT *cl; 58774462Salfred struct rpc_err *errp; 58874462Salfred{ 58974462Salfred struct cu_data *cu = (struct cu_data *)cl->cl_private; 59074462Salfred 59174462Salfred *errp = cu->cu_error; 59274462Salfred} 59374462Salfred 59474462Salfredstatic bool_t 59574462Salfredclnt_dg_freeres(cl, xdr_res, res_ptr) 59674462Salfred CLIENT *cl; 59774462Salfred xdrproc_t xdr_res; 59874462Salfred caddr_t res_ptr; 59974462Salfred{ 60074462Salfred struct cu_data *cu = (struct cu_data *)cl->cl_private; 60174462Salfred XDR *xdrs = &(cu->cu_outxdrs); 60274462Salfred bool_t dummy; 60374462Salfred sigset_t mask; 60474462Salfred sigset_t newmask; 60574462Salfred 60674462Salfred sigfillset(&newmask); 60774462Salfred thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 60874462Salfred mutex_lock(&clnt_fd_lock); 60974462Salfred while (dg_fd_locks[cu->cu_fd]) 61074462Salfred cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); 61174462Salfred xdrs->x_op = XDR_FREE; 61274462Salfred dummy = (*xdr_res)(xdrs, res_ptr); 61374462Salfred mutex_unlock(&clnt_fd_lock); 61474462Salfred thr_sigsetmask(SIG_SETMASK, &mask, NULL); 61574462Salfred cond_signal(&dg_cv[cu->cu_fd]); 61674462Salfred return (dummy); 61774462Salfred} 61874462Salfred 61974462Salfred/*ARGSUSED*/ 62074462Salfredstatic void 62174462Salfredclnt_dg_abort(h) 62274462Salfred CLIENT *h; 62374462Salfred{ 62474462Salfred} 62574462Salfred 62674462Salfredstatic bool_t 62774462Salfredclnt_dg_control(cl, request, info) 62874462Salfred CLIENT *cl; 62974462Salfred u_int request; 63074462Salfred char *info; 63174462Salfred{ 63274462Salfred struct cu_data *cu = (struct cu_data *)cl->cl_private; 63374462Salfred struct netbuf *addr; 63474462Salfred sigset_t mask; 63574462Salfred sigset_t newmask; 63674462Salfred int rpc_lock_value; 63774462Salfred 63874462Salfred sigfillset(&newmask); 63974462Salfred thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 64074462Salfred mutex_lock(&clnt_fd_lock); 64174462Salfred while (dg_fd_locks[cu->cu_fd]) 64274462Salfred cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock); 64374462Salfred if (__isthreaded) 64474462Salfred rpc_lock_value = 1; 64574462Salfred else 64674462Salfred rpc_lock_value = 0; 64774462Salfred dg_fd_locks[cu->cu_fd] = rpc_lock_value; 64874462Salfred mutex_unlock(&clnt_fd_lock); 64974462Salfred switch (request) { 65074462Salfred case CLSET_FD_CLOSE: 65174462Salfred cu->cu_closeit = TRUE; 65274462Salfred release_fd_lock(cu->cu_fd, mask); 65374462Salfred return (TRUE); 65474462Salfred case CLSET_FD_NCLOSE: 65574462Salfred cu->cu_closeit = FALSE; 65674462Salfred release_fd_lock(cu->cu_fd, mask); 65774462Salfred return (TRUE); 65874462Salfred } 65974462Salfred 66074462Salfred /* for other requests which use info */ 66174462Salfred if (info == NULL) { 66274462Salfred release_fd_lock(cu->cu_fd, mask); 66374462Salfred return (FALSE); 66474462Salfred } 66574462Salfred switch (request) { 66674462Salfred case CLSET_TIMEOUT: 66774462Salfred if (time_not_ok((struct timeval *)(void *)info)) { 66874462Salfred release_fd_lock(cu->cu_fd, mask); 66974462Salfred return (FALSE); 67074462Salfred } 67174462Salfred cu->cu_total = *(struct timeval *)(void *)info; 67274462Salfred break; 67374462Salfred case CLGET_TIMEOUT: 67474462Salfred *(struct timeval *)(void *)info = cu->cu_total; 67574462Salfred break; 67674462Salfred case CLGET_SERVER_ADDR: /* Give him the fd address */ 67774462Salfred /* Now obsolete. Only for backward compatibility */ 67874462Salfred (void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen); 67974462Salfred break; 68074462Salfred case CLSET_RETRY_TIMEOUT: 68174462Salfred if (time_not_ok((struct timeval *)(void *)info)) { 68274462Salfred release_fd_lock(cu->cu_fd, mask); 68374462Salfred return (FALSE); 68474462Salfred } 68574462Salfred cu->cu_wait = *(struct timeval *)(void *)info; 68674462Salfred break; 68774462Salfred case CLGET_RETRY_TIMEOUT: 68874462Salfred *(struct timeval *)(void *)info = cu->cu_wait; 68974462Salfred break; 69074462Salfred case CLGET_FD: 69174462Salfred *(int *)(void *)info = cu->cu_fd; 69274462Salfred break; 69374462Salfred case CLGET_SVC_ADDR: 69474462Salfred addr = (struct netbuf *)(void *)info; 69574462Salfred addr->buf = &cu->cu_raddr; 69674462Salfred addr->len = cu->cu_rlen; 69774462Salfred addr->maxlen = sizeof cu->cu_raddr; 69874462Salfred break; 69974462Salfred case CLSET_SVC_ADDR: /* set to new address */ 70074462Salfred addr = (struct netbuf *)(void *)info; 70175097Siedowse if (addr->len < sizeof cu->cu_raddr) { 70275097Siedowse release_fd_lock(cu->cu_fd, mask); 70374462Salfred return (FALSE); 70475097Siedowse } 70574462Salfred (void) memcpy(&cu->cu_raddr, addr->buf, addr->len); 70674462Salfred cu->cu_rlen = addr->len; 70774462Salfred break; 70874462Salfred case CLGET_XID: 70974462Salfred /* 71074462Salfred * use the knowledge that xid is the 71174462Salfred * first element in the call structure *. 71274462Salfred * This will get the xid of the PREVIOUS call 71374462Salfred */ 71474462Salfred *(u_int32_t *)(void *)info = 71574462Salfred ntohl(*(u_int32_t *)(void *)cu->cu_outbuf); 71674462Salfred break; 71774462Salfred 71874462Salfred case CLSET_XID: 71974462Salfred /* This will set the xid of the NEXT call */ 72074462Salfred *(u_int32_t *)(void *)cu->cu_outbuf = 72174462Salfred htonl(*(u_int32_t *)(void *)info - 1); 72274462Salfred /* decrement by 1 as clnt_dg_call() increments once */ 72374462Salfred break; 72474462Salfred 72574462Salfred case CLGET_VERS: 72674462Salfred /* 72774462Salfred * This RELIES on the information that, in the call body, 72874462Salfred * the version number field is the fifth field from the 72974462Salfred * begining of the RPC header. MUST be changed if the 73074462Salfred * call_struct is changed 73174462Salfred */ 73274462Salfred *(u_int32_t *)(void *)info = 73374462Salfred ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf + 73474462Salfred 4 * BYTES_PER_XDR_UNIT)); 73574462Salfred break; 73674462Salfred 73774462Salfred case CLSET_VERS: 73874462Salfred *(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT) 73974462Salfred = htonl(*(u_int32_t *)(void *)info); 74074462Salfred break; 74174462Salfred 74274462Salfred case CLGET_PROG: 74374462Salfred /* 74474462Salfred * This RELIES on the information that, in the call body, 74574462Salfred * the program number field is the fourth field from the 74674462Salfred * begining of the RPC header. MUST be changed if the 74774462Salfred * call_struct is changed 74874462Salfred */ 74974462Salfred *(u_int32_t *)(void *)info = 75074462Salfred ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf + 75174462Salfred 3 * BYTES_PER_XDR_UNIT)); 75274462Salfred break; 75374462Salfred 75474462Salfred case CLSET_PROG: 75574462Salfred *(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT) 75674462Salfred = htonl(*(u_int32_t *)(void *)info); 75774462Salfred break; 75874879Swpaul case CLSET_ASYNC: 75974879Swpaul cu->cu_async = *(int *)(void *)info; 76074879Swpaul break; 76178678Siedowse case CLSET_CONNECT: 76278678Siedowse cu->cu_connect = *(int *)(void *)info; 76378678Siedowse break; 76474462Salfred default: 76574462Salfred release_fd_lock(cu->cu_fd, mask); 76674462Salfred return (FALSE); 76774462Salfred } 76874462Salfred release_fd_lock(cu->cu_fd, mask); 76974462Salfred return (TRUE); 77074462Salfred} 77174462Salfred 77274462Salfredstatic void 77374462Salfredclnt_dg_destroy(cl) 77474462Salfred CLIENT *cl; 77574462Salfred{ 77674462Salfred struct cu_data *cu = (struct cu_data *)cl->cl_private; 77774462Salfred int cu_fd = cu->cu_fd; 77874462Salfred sigset_t mask; 77974462Salfred sigset_t newmask; 78074462Salfred 78174462Salfred sigfillset(&newmask); 78274462Salfred thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 78374462Salfred mutex_lock(&clnt_fd_lock); 78474462Salfred while (dg_fd_locks[cu_fd]) 78574462Salfred cond_wait(&dg_cv[cu_fd], &clnt_fd_lock); 78674462Salfred if (cu->cu_closeit) 78774462Salfred (void)_close(cu_fd); 78874462Salfred XDR_DESTROY(&(cu->cu_outxdrs)); 78974462Salfred mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz)); 79074462Salfred if (cl->cl_netid && cl->cl_netid[0]) 79174462Salfred mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); 79274462Salfred if (cl->cl_tp && cl->cl_tp[0]) 79374462Salfred mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); 79474462Salfred mem_free(cl, sizeof (CLIENT)); 79574462Salfred mutex_unlock(&clnt_fd_lock); 79674462Salfred thr_sigsetmask(SIG_SETMASK, &mask, NULL); 79774462Salfred cond_signal(&dg_cv[cu_fd]); 79874462Salfred} 79974462Salfred 80074462Salfredstatic struct clnt_ops * 80174462Salfredclnt_dg_ops() 80274462Salfred{ 80374462Salfred static struct clnt_ops ops; 80474462Salfred extern mutex_t ops_lock; 80574462Salfred sigset_t mask; 80674462Salfred sigset_t newmask; 80774462Salfred 80874462Salfred/* VARIABLES PROTECTED BY ops_lock: ops */ 80974462Salfred 81074462Salfred sigfillset(&newmask); 81174462Salfred thr_sigsetmask(SIG_SETMASK, &newmask, &mask); 81274462Salfred mutex_lock(&ops_lock); 81374462Salfred if (ops.cl_call == NULL) { 81474462Salfred ops.cl_call = clnt_dg_call; 81574462Salfred ops.cl_abort = clnt_dg_abort; 81674462Salfred ops.cl_geterr = clnt_dg_geterr; 81774462Salfred ops.cl_freeres = clnt_dg_freeres; 81874462Salfred ops.cl_destroy = clnt_dg_destroy; 81974462Salfred ops.cl_control = clnt_dg_control; 82074462Salfred } 82174462Salfred mutex_unlock(&ops_lock); 82274462Salfred thr_sigsetmask(SIG_SETMASK, &mask, NULL); 82374462Salfred return (&ops); 82474462Salfred} 82574462Salfred 82674462Salfred/* 82774462Salfred * Make sure that the time is not garbage. -1 value is allowed. 82874462Salfred */ 82974462Salfredstatic bool_t 83074462Salfredtime_not_ok(t) 83174462Salfred struct timeval *t; 83274462Salfred{ 83374462Salfred return (t->tv_sec < -1 || t->tv_sec > 100000000 || 83474462Salfred t->tv_usec < -1 || t->tv_usec > 1000000); 83574462Salfred} 83674462Salfred 83774462Salfred 83874462Salfred/* 83974462Salfred * Convert from timevals (used by select) to milliseconds (used by poll). 84074462Salfred */ 84174462Salfredstatic int 84274462Salfred__rpc_timeval_to_msec(t) 84374462Salfred struct timeval *t; 84474462Salfred{ 84574462Salfred int t1, tmp; 84674462Salfred 84774462Salfred /* 84874462Salfred * We're really returning t->tv_sec * 1000 + (t->tv_usec / 1000) 84974462Salfred * but try to do so efficiently. Note: 1000 = 1024 - 16 - 8. 85074462Salfred */ 85174462Salfred tmp = (int)t->tv_sec << 3; 85274462Salfred t1 = -tmp; 85374462Salfred t1 += t1 << 1; 85474462Salfred t1 += tmp << 7; 85574462Salfred if (t->tv_usec) 85674462Salfred t1 += (int)(t->tv_usec / 1000); 85774462Salfred 85874462Salfred return (t1); 85974462Salfred} 860