79fed5d8caebecc3c2ebc26e33d1434564ff612f
[platform/upstream/libtirpc.git] / src / clnt_dg.c
1 /*
2  * Copyright (c) 2009, Sun Microsystems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of Sun Microsystems, Inc. nor the names of its
13  *   contributors may be used to endorse or promote products derived
14  *   from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 /*
29  * Copyright (c) 1986-1991 by Sun Microsystems Inc. 
30  */
31
32 /*
33  * Implements a connectionless client side RPC.
34  */
35 #include <pthread.h>
36 #include <reentrant.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <stdint.h>
40 #include <sys/poll.h>
41
42 #include <sys/time.h>
43
44 #include <sys/ioctl.h>
45 #include <rpc/clnt.h>
46 #include <arpa/inet.h>
47 #include <rpc/rpc.h>
48 #include <rpc/xdr.h>
49 #include <errno.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <signal.h>
53 #include <unistd.h>
54 #include <err.h>
55 #include "rpc_com.h"
56
57 #ifdef IP_RECVERR
58 #include <asm/types.h>
59 #include <linux/errqueue.h>
60 #include <sys/uio.h>
61 #endif
62
63
64 #define MAX_DEFAULT_FDS                 20000
65
66 static struct clnt_ops *clnt_dg_ops(void);
67 static bool_t time_not_ok(struct timeval *);
68 static enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
69             xdrproc_t, void *, struct timeval);
70 static void clnt_dg_geterr(CLIENT *, struct rpc_err *);
71 static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
72 static void clnt_dg_abort(CLIENT *);
73 static bool_t clnt_dg_control(CLIENT *, u_int, void *);
74 static void clnt_dg_destroy(CLIENT *);
75
76
77 /*
78  *      This machinery implements per-fd locks for MT-safety.  It is not
79  *      sufficient to do per-CLIENT handle locks for MT-safety because a
80  *      user may create more than one CLIENT handle with the same fd behind
81  *      it.  Therfore, we allocate an array of flags (dg_fd_locks), protected
82  *      by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables
83  *      similarly protected.  Dg_fd_lock[fd] == 1 => a call is activte on some
84  *      CLIENT handle created for that fd.
85  *      The current implementation holds locks across the entire RPC and reply,
86  *      including retransmissions.  Yes, this is silly, and as soon as this
87  *      code is proven to work, this should be the first thing fixed.  One step
88  *      at a time.
89  */
90 static int      *dg_fd_locks;
91 extern mutex_t clnt_fd_lock;
92 static cond_t   *dg_cv;
93 #define release_fd_lock(fd, mask) {             \
94         mutex_lock(&clnt_fd_lock);      \
95         dg_fd_locks[fd] = 0;            \
96         mutex_unlock(&clnt_fd_lock);    \
97         thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
98         cond_signal(&dg_cv[fd]);        \
99 }
100
101 static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
102
103 /* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */
104
105 /*
106  * Private data kept per client handle
107  */
108 struct cu_data {
109         int                     cu_fd;          /* connections fd */
110         bool_t                  cu_closeit;     /* opened by library */
111         struct sockaddr_storage cu_raddr;       /* remote address */
112         int                     cu_rlen;
113         struct timeval          cu_wait;        /* retransmit interval */
114         struct timeval          cu_total;       /* total time for the call */
115         struct rpc_err          cu_error;
116         XDR                     cu_outxdrs;
117         u_int                   cu_xdrpos;
118         u_int                   cu_sendsz;      /* send size */
119         char                    *cu_outbuf;
120         u_int                   cu_recvsz;      /* recv size */
121         int                     cu_async;
122         int                     cu_connect;     /* Use connect(). */
123         int                     cu_connected;   /* Have done connect(). */
124         char                    cu_inbuf[1];
125 };
126
127 /*
128  * Connection less client creation returns with client handle parameters.
129  * Default options are set, which the user can change using clnt_control().
130  * fd should be open and bound.
131  * NB: The rpch->cl_auth is initialized to null authentication.
132  *      Caller may wish to set this something more useful.
133  *
134  * sendsz and recvsz are the maximum allowable packet sizes that can be
135  * sent and received. Normally they are the same, but they can be
136  * changed to improve the program efficiency and buffer allocation.
137  * If they are 0, use the transport default.
138  *
139  * If svcaddr is NULL, returns NULL.
140  */
141 CLIENT *
142 clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
143         int fd;                         /* open file descriptor */
144         const struct netbuf *svcaddr;   /* servers address */
145         rpcprog_t program;              /* program number */
146         rpcvers_t version;              /* version number */
147         u_int sendsz;                   /* buffer recv size */
148         u_int recvsz;                   /* buffer send size */
149 {
150         CLIENT *cl = NULL;              /* client handle */
151         struct cu_data *cu = NULL;      /* private data */
152         struct timeval now;
153         struct rpc_msg call_msg;
154         sigset_t mask;
155         sigset_t newmask;
156         struct __rpc_sockinfo si;
157         int one = 1;
158
159         sigfillset(&newmask);
160         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
161         mutex_lock(&clnt_fd_lock);
162         if (dg_fd_locks == (int *) NULL) {
163                 int cv_allocsz;
164                 size_t fd_allocsz;
165                 int dtbsize = __rpc_dtbsize();
166
167                 fd_allocsz = dtbsize * sizeof (int);
168                 dg_fd_locks = (int *) mem_alloc(fd_allocsz);
169                 if (dg_fd_locks == (int *) NULL) {
170                         mutex_unlock(&clnt_fd_lock);
171                         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
172                         goto err1;
173                 } else
174                         memset(dg_fd_locks, '\0', fd_allocsz);
175
176                 cv_allocsz = dtbsize * sizeof (cond_t);
177                 dg_cv = (cond_t *) mem_alloc(cv_allocsz);
178                 if (dg_cv == (cond_t *) NULL) {
179                         mem_free(dg_fd_locks, fd_allocsz);
180                         dg_fd_locks = (int *) NULL;
181                         mutex_unlock(&clnt_fd_lock);
182                         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
183                         goto err1;
184                 } else {
185                         int i;
186
187                         for (i = 0; i < dtbsize; i++)
188                                 cond_init(&dg_cv[i], 0, (void *) 0);
189                 }
190         }
191
192         mutex_unlock(&clnt_fd_lock);
193         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
194
195         if (svcaddr == NULL) {
196                 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
197                 return (NULL);
198         }
199
200         if (!__rpc_fd2sockinfo(fd, &si)) {
201                 rpc_createerr.cf_stat = RPC_TLIERROR;
202                 rpc_createerr.cf_error.re_errno = 0;
203                 return (NULL);
204         }
205         /*
206          * Find the receive and the send size
207          */
208         sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
209         recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
210         if ((sendsz == 0) || (recvsz == 0)) {
211                 rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
212                 rpc_createerr.cf_error.re_errno = 0;
213                 return (NULL);
214         }
215
216         if ((cl = mem_alloc(sizeof (CLIENT))) == NULL)
217                 goto err1;
218         /*
219          * Should be multiple of 4 for XDR.
220          */
221         sendsz = ((sendsz + 3) / 4) * 4;
222         recvsz = ((recvsz + 3) / 4) * 4;
223         cu = mem_alloc(sizeof (*cu) + sendsz + recvsz);
224         if (cu == NULL)
225                 goto err1;
226         (void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len);
227         cu->cu_rlen = svcaddr->len;
228         cu->cu_outbuf = &cu->cu_inbuf[recvsz];
229         /* Other values can also be set through clnt_control() */
230         cu->cu_wait.tv_sec = 15;        /* heuristically chosen */
231         cu->cu_wait.tv_usec = 0;
232         cu->cu_total.tv_sec = -1;
233         cu->cu_total.tv_usec = -1;
234         cu->cu_sendsz = sendsz;
235         cu->cu_recvsz = recvsz;
236         cu->cu_async = FALSE;
237         cu->cu_connect = FALSE;
238         cu->cu_connected = FALSE;
239         (void) gettimeofday(&now, NULL);
240         call_msg.rm_xid = __RPC_GETXID(&now);
241         call_msg.rm_call.cb_prog = program;
242         call_msg.rm_call.cb_vers = version;
243         xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
244         if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
245                 rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
246                 rpc_createerr.cf_error.re_errno = 0;
247                 goto err2;
248         }
249         cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
250
251         /* XXX fvdl - do we still want this? */
252 #if 0
253         (void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf);
254 #endif
255 #ifdef IP_RECVERR
256         {
257         int on = 1;
258         setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on));
259         }
260 #endif
261         ioctl(fd, FIONBIO, (char *)(void *)&one);
262         /*
263          * By default, closeit is always FALSE. It is users responsibility
264          * to do a close on it, else the user may use clnt_control
265          * to let clnt_destroy do it for him/her.
266          */
267         cu->cu_closeit = FALSE;
268         cu->cu_fd = fd;
269         cl->cl_ops = clnt_dg_ops();
270         cl->cl_private = (caddr_t)(void *)cu;
271         cl->cl_auth = authnone_create();
272         cl->cl_tp = NULL;
273         cl->cl_netid = NULL;
274         
275         return (cl);
276 err1:
277         warnx(mem_err_clnt_dg);
278         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
279         rpc_createerr.cf_error.re_errno = errno;
280 err2:
281         if (cl) {
282                 mem_free(cl, sizeof (CLIENT));
283                 if (cu)
284                         mem_free(cu, sizeof (*cu) + sendsz + recvsz);
285         }
286         return (NULL);
287 }
288
289 static enum clnt_stat
290 clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
291         CLIENT  *cl;                    /* client handle */
292         rpcproc_t       proc;           /* procedure number */
293         xdrproc_t       xargs;          /* xdr routine for args */
294         void            *argsp;         /* pointer to args */
295         xdrproc_t       xresults;       /* xdr routine for results */
296         void            *resultsp;      /* pointer to results */
297         struct timeval  utimeout;       /* seconds to wait before giving up */
298 {
299         struct cu_data *cu = (struct cu_data *)cl->cl_private;
300         XDR *xdrs;
301         size_t outlen = 0;
302         struct rpc_msg reply_msg;
303         XDR reply_xdrs;
304         bool_t ok;
305         int nrefreshes = 2;             /* number of times to refresh cred */
306         struct timeval timeout;
307         struct pollfd fd;
308         int total_time, nextsend_time, tv=0;
309         struct sockaddr *sa;
310         sigset_t mask;
311         sigset_t newmask;
312         socklen_t inlen, salen;
313         ssize_t recvlen = 0;
314         int rpc_lock_value;
315         u_int32_t xid, inval, outval;
316
317         outlen = 0;
318         sigfillset(&newmask);
319         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
320         mutex_lock(&clnt_fd_lock);
321         while (dg_fd_locks[cu->cu_fd])
322                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
323         rpc_lock_value = 1;
324         dg_fd_locks[cu->cu_fd] = rpc_lock_value;
325         mutex_unlock(&clnt_fd_lock);
326         if (cu->cu_total.tv_usec == -1) {
327                 timeout = utimeout;     /* use supplied timeout */
328         } else {
329                 timeout = cu->cu_total; /* use default timeout */
330         }
331         total_time = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
332         nextsend_time = cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000;
333
334         if (cu->cu_connect && !cu->cu_connected) {
335                 if (connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr,
336                     cu->cu_rlen) < 0) {
337                         cu->cu_error.re_errno = errno;
338                         cu->cu_error.re_status = RPC_CANTSEND;
339                         goto out;
340                 }
341                 cu->cu_connected = 1;
342         }
343         if (cu->cu_connected) {
344                 sa = NULL;
345                 salen = 0;
346         } else {
347                 sa = (struct sockaddr *)&cu->cu_raddr;
348                 salen = cu->cu_rlen;
349         }
350
351         /* Clean up in case the last call ended in a longjmp(3) call. */
352 call_again:
353         xdrs = &(cu->cu_outxdrs);
354         if (cu->cu_async == TRUE && xargs == NULL)
355                 goto get_reply;
356         xdrs->x_op = XDR_ENCODE;
357         XDR_SETPOS(xdrs, cu->cu_xdrpos);
358         /*
359          * the transaction is the first thing in the out buffer
360          * XXX Yes, and it's in network byte order, so we should to
361          * be careful when we increment it, shouldn't we.
362          */
363         xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf));
364         xid++;
365         *(u_int32_t *)(void *)(cu->cu_outbuf) = htonl(xid);
366
367         if ((! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
368             (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
369             (! (*xargs)(xdrs, argsp))) {
370                 cu->cu_error.re_status = RPC_CANTENCODEARGS;
371                 goto out;
372         }
373         outlen = (size_t)XDR_GETPOS(xdrs);
374
375         /*
376          * Hack to provide rpc-based message passing
377          */
378         if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
379                 cu->cu_error.re_status = RPC_TIMEDOUT;
380                 goto out;
381         }
382
383 send_again:
384         if (total_time <= 0) {
385                 cu->cu_error.re_status = RPC_TIMEDOUT;
386                 goto out;
387         }
388         nextsend_time = cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000;
389         if (sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) {
390                 cu->cu_error.re_errno = errno;
391                 cu->cu_error.re_status = RPC_CANTSEND;
392                 goto out;
393         }
394
395 get_reply:
396
397         /*
398          * sub-optimal code appears here because we have
399          * some clock time to spare while the packets are in flight.
400          * (We assume that this is actually only executed once.)
401          */
402         reply_msg.acpted_rply.ar_verf = _null_auth;
403         reply_msg.acpted_rply.ar_results.where = resultsp;
404         reply_msg.acpted_rply.ar_results.proc = xresults;
405
406         fd.fd = cu->cu_fd;
407         fd.events = POLLIN;
408         fd.revents = 0;
409         while (total_time > 0) {
410                 tv = total_time < nextsend_time ? total_time : nextsend_time;
411                 switch (poll(&fd, 1, tv)) {
412                 case 0:
413                         total_time -= tv;
414                         goto send_again;
415                 case -1:
416                         if (errno == EINTR)
417                                 continue;
418                         cu->cu_error.re_status = RPC_CANTRECV;
419                         cu->cu_error.re_errno = errno;
420                         goto out;
421                 }
422                 break;
423         }
424 #ifdef IP_RECVERR
425       if (fd.revents & POLLERR)
426         {
427           struct msghdr msg;
428           struct cmsghdr *cmsg;
429           struct sock_extended_err *e;
430           struct sockaddr_in err_addr;
431           struct sockaddr_in *sin = (struct sockaddr_in *)&cu->cu_raddr;
432           struct iovec iov;
433           char *cbuf = (char *) alloca (outlen + 256);
434           int ret;
435
436           iov.iov_base = cbuf + 256;
437           iov.iov_len = outlen;
438           msg.msg_name = (void *) &err_addr;
439           msg.msg_namelen = sizeof (err_addr);
440           msg.msg_iov = &iov;
441           msg.msg_iovlen = 1;
442           msg.msg_flags = 0;
443           msg.msg_control = cbuf;
444           msg.msg_controllen = 256;
445           ret = recvmsg (cu->cu_fd, &msg, MSG_ERRQUEUE);
446           if (ret >= 0
447               && memcmp (cbuf + 256, cu->cu_outbuf, ret) == 0
448               && (msg.msg_flags & MSG_ERRQUEUE)
449               && ((msg.msg_namelen == 0
450                    && ret >= 12)
451                   || (msg.msg_namelen == sizeof (err_addr)
452                       && err_addr.sin_family == AF_INET
453                       && memcmp (&err_addr.sin_addr, &sin->sin_addr,
454                                  sizeof (err_addr.sin_addr)) == 0
455                       && err_addr.sin_port == sin->sin_port)))
456             for (cmsg = CMSG_FIRSTHDR (&msg); cmsg;
457                  cmsg = CMSG_NXTHDR (&msg, cmsg))
458               if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
459                 {
460                   e = (struct sock_extended_err *) CMSG_DATA(cmsg);
461                   cu->cu_error.re_errno = e->ee_errno;
462                   release_fd_lock(cu->cu_fd, mask);
463                   return (cu->cu_error.re_status = RPC_CANTRECV);
464                 }
465         }
466 #endif
467
468         /* We have some data now */
469         do {
470                 recvlen = recvfrom(cu->cu_fd, cu->cu_inbuf,
471                     cu->cu_recvsz, 0, NULL, NULL);
472         } while (recvlen < 0 && errno == EINTR);
473         if (recvlen < 0 && errno != EWOULDBLOCK) {
474                 cu->cu_error.re_errno = errno;
475                 cu->cu_error.re_status = RPC_CANTRECV;
476                 goto out;
477         }
478
479         if (recvlen < sizeof(u_int32_t)) {
480                 total_time -= tv;
481                 goto send_again;
482         }
483
484         if (cu->cu_async == TRUE)
485                 inlen = (socklen_t)recvlen;
486         else {
487                 memcpy(&inval, cu->cu_inbuf, sizeof(u_int32_t));
488                 memcpy(&outval, cu->cu_outbuf, sizeof(u_int32_t));
489                 if (inval != outval) {
490                         total_time -= tv;
491                         goto send_again;
492                 }
493                 inlen = (socklen_t)recvlen;
494         }
495
496         /*
497          * now decode and validate the response
498          */
499
500         xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE);
501         ok = xdr_replymsg(&reply_xdrs, &reply_msg);
502         /* XDR_DESTROY(&reply_xdrs);    save a few cycles on noop destroy */
503         if (ok) {
504                 if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
505                         (reply_msg.acpted_rply.ar_stat == SUCCESS))
506                         cu->cu_error.re_status = RPC_SUCCESS;
507                 else
508                         _seterr_reply(&reply_msg, &(cu->cu_error));
509
510                 if (cu->cu_error.re_status == RPC_SUCCESS) {
511                         if (! AUTH_VALIDATE(cl->cl_auth,
512                                             &reply_msg.acpted_rply.ar_verf)) {
513                                 cu->cu_error.re_status = RPC_AUTHERROR;
514                                 cu->cu_error.re_why = AUTH_INVALIDRESP;
515                         }
516                         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
517                                 xdrs->x_op = XDR_FREE;
518                                 (void) xdr_opaque_auth(xdrs,
519                                         &(reply_msg.acpted_rply.ar_verf));
520                         }
521                 }               /* end successful completion */
522                 /*
523                  * If unsuccesful AND error is an authentication error
524                  * then refresh credentials and try again, else break
525                  */
526                 else if (cu->cu_error.re_status == RPC_AUTHERROR)
527                         /* maybe our credentials need to be refreshed ... */
528                         if (nrefreshes > 0 &&
529                             AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
530                                 nrefreshes--;
531                                 goto call_again;
532                         }
533                 /* end of unsuccessful completion */
534         }       /* end of valid reply message */
535         else {
536                 cu->cu_error.re_status = RPC_CANTDECODERES;
537
538         }
539 out:
540         release_fd_lock(cu->cu_fd, mask);
541         return (cu->cu_error.re_status);
542 }
543
544 static void
545 clnt_dg_geterr(cl, errp)
546         CLIENT *cl;
547         struct rpc_err *errp;
548 {
549         struct cu_data *cu = (struct cu_data *)cl->cl_private;
550
551         *errp = cu->cu_error;
552 }
553
554 static bool_t
555 clnt_dg_freeres(cl, xdr_res, res_ptr)
556         CLIENT *cl;
557         xdrproc_t xdr_res;
558         void *res_ptr;
559 {
560         struct cu_data *cu = (struct cu_data *)cl->cl_private;
561         XDR *xdrs = &(cu->cu_outxdrs);
562         bool_t dummy;
563         sigset_t mask;
564         sigset_t newmask;
565
566         sigfillset(&newmask);
567         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
568         mutex_lock(&clnt_fd_lock);
569         while (dg_fd_locks[cu->cu_fd])
570                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
571         xdrs->x_op = XDR_FREE;
572         dummy = (*xdr_res)(xdrs, res_ptr);
573         mutex_unlock(&clnt_fd_lock);
574         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
575         cond_signal(&dg_cv[cu->cu_fd]);
576         return (dummy);
577 }
578
579 /*ARGSUSED*/
580 static void
581 clnt_dg_abort(h)
582         CLIENT *h;
583 {
584 }
585
586 static bool_t
587 clnt_dg_control(cl, request, info)
588         CLIENT *cl;
589         u_int request;
590         void *info;
591 {
592         struct cu_data *cu = (struct cu_data *)cl->cl_private;
593         struct netbuf *addr;
594         sigset_t mask;
595         sigset_t newmask;
596         int rpc_lock_value;
597
598         sigfillset(&newmask);
599         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
600         mutex_lock(&clnt_fd_lock);
601         while (dg_fd_locks[cu->cu_fd])
602                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
603         rpc_lock_value = 1;
604         dg_fd_locks[cu->cu_fd] = rpc_lock_value;
605         mutex_unlock(&clnt_fd_lock);
606         switch (request) {
607         case CLSET_FD_CLOSE:
608                 cu->cu_closeit = TRUE;
609                 release_fd_lock(cu->cu_fd, mask);
610                 return (TRUE);
611         case CLSET_FD_NCLOSE:
612                 cu->cu_closeit = FALSE;
613                 release_fd_lock(cu->cu_fd, mask);
614                 return (TRUE);
615         }
616
617         /* for other requests which use info */
618         if (info == NULL) {
619                 release_fd_lock(cu->cu_fd, mask);
620                 return (FALSE);
621         }
622         switch (request) {
623         case CLSET_TIMEOUT:
624                 if (time_not_ok((struct timeval *)info)) {
625                         release_fd_lock(cu->cu_fd, mask);
626                         return (FALSE);
627                 }
628                 cu->cu_total = *(struct timeval *)info;
629                 break;
630         case CLGET_TIMEOUT:
631                 *(struct timeval *)info = cu->cu_total;
632                 break;
633         case CLGET_SERVER_ADDR:         /* Give him the fd address */
634                 /* Now obsolete. Only for backward compatibility */
635                 (void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen);
636                 break;
637         case CLSET_RETRY_TIMEOUT:
638                 if (time_not_ok((struct timeval *)info)) {
639                         release_fd_lock(cu->cu_fd, mask);
640                         return (FALSE);
641                 }
642                 cu->cu_wait = *(struct timeval *)info;
643                 break;
644         case CLGET_RETRY_TIMEOUT:
645                 *(struct timeval *)info = cu->cu_wait;
646                 break;
647         case CLGET_FD:
648                 *(int *)info = cu->cu_fd;
649                 break;
650         case CLGET_SVC_ADDR:
651                 addr = (struct netbuf *)info;
652                 addr->buf = &cu->cu_raddr;
653                 addr->len = cu->cu_rlen;
654                 addr->maxlen = sizeof cu->cu_raddr;
655                 break;
656         case CLSET_SVC_ADDR:            /* set to new address */
657                 addr = (struct netbuf *)info;
658                 if (addr->len < sizeof cu->cu_raddr) {
659                         release_fd_lock(cu->cu_fd, mask);
660                         return (FALSE);
661                 }
662                 (void) memcpy(&cu->cu_raddr, addr->buf, addr->len);
663                 cu->cu_rlen = addr->len;
664                 break;
665         case CLGET_XID:
666                 /*
667                  * use the knowledge that xid is the
668                  * first element in the call structure *.
669                  * This will get the xid of the PREVIOUS call
670                  */
671                 *(u_int32_t *)info =
672                     ntohl(*(u_int32_t *)(void *)cu->cu_outbuf);
673                 break;
674
675         case CLSET_XID:
676                 /* This will set the xid of the NEXT call */
677                 *(u_int32_t *)(void *)cu->cu_outbuf =
678                     htonl(*(u_int32_t *)info - 1);
679                 /* decrement by 1 as clnt_dg_call() increments once */
680                 break;
681
682         case CLGET_VERS:
683                 /*
684                  * This RELIES on the information that, in the call body,
685                  * the version number field is the fifth field from the
686                  * begining of the RPC header. MUST be changed if the
687                  * call_struct is changed
688                  */
689                 *(u_int32_t *)info =
690                     ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
691                     4 * BYTES_PER_XDR_UNIT));
692                 break;
693
694         case CLSET_VERS:
695                 *(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
696                         = htonl(*(u_int32_t *)info);
697                 break;
698
699         case CLGET_PROG:
700                 /*
701                  * This RELIES on the information that, in the call body,
702                  * the program number field is the fourth field from the
703                  * begining of the RPC header. MUST be changed if the
704                  * call_struct is changed
705                  */
706                 *(u_int32_t *)info =
707                     ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
708                     3 * BYTES_PER_XDR_UNIT));
709                 break;
710
711         case CLSET_PROG:
712                 *(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
713                         = htonl(*(u_int32_t *)info);
714                 break;
715         case CLSET_ASYNC:
716                 cu->cu_async = *(int *)info;
717                 break;
718         case CLSET_CONNECT:
719                 cu->cu_connect = *(int *)info;
720                 break;
721         default:
722                 release_fd_lock(cu->cu_fd, mask);
723                 return (FALSE);
724         }
725         release_fd_lock(cu->cu_fd, mask);
726         return (TRUE);
727 }
728
729 static void
730 clnt_dg_destroy(cl)
731         CLIENT *cl;
732 {
733         struct cu_data *cu = (struct cu_data *)cl->cl_private;
734         int cu_fd = cu->cu_fd;
735         sigset_t mask;
736         sigset_t newmask;
737
738         sigfillset(&newmask);
739         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
740         mutex_lock(&clnt_fd_lock);
741         while (dg_fd_locks[cu_fd])
742                 cond_wait(&dg_cv[cu_fd], &clnt_fd_lock);
743         if (cu->cu_closeit)
744                 (void)close(cu_fd);
745         XDR_DESTROY(&(cu->cu_outxdrs));
746         mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
747         if (cl->cl_netid && cl->cl_netid[0])
748                 mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
749         if (cl->cl_tp && cl->cl_tp[0])
750                 mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
751         mem_free(cl, sizeof (CLIENT));
752         mutex_unlock(&clnt_fd_lock);
753         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
754         cond_signal(&dg_cv[cu_fd]);
755 }
756
757 static struct clnt_ops *
758 clnt_dg_ops()
759 {
760         static struct clnt_ops ops;
761         extern mutex_t  ops_lock;
762         sigset_t mask;
763         sigset_t newmask;
764
765 /* VARIABLES PROTECTED BY ops_lock: ops */
766
767         sigfillset(&newmask);
768         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
769         mutex_lock(&ops_lock);
770         if (ops.cl_call == NULL) {
771                 ops.cl_call = clnt_dg_call;
772                 ops.cl_abort = clnt_dg_abort;
773                 ops.cl_geterr = clnt_dg_geterr;
774                 ops.cl_freeres = clnt_dg_freeres;
775                 ops.cl_destroy = clnt_dg_destroy;
776                 ops.cl_control = clnt_dg_control;
777         }
778         mutex_unlock(&ops_lock);
779         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
780         return (&ops);
781 }
782
783 /*
784  * Make sure that the time is not garbage.  -1 value is allowed.
785  */
786 static bool_t
787 time_not_ok(t)
788         struct timeval *t;
789 {
790         return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
791                 t->tv_usec < -1 || t->tv_usec > 1000000);
792 }
793