Imported Upstream version 0.2.5
[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 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             (! AUTH_WRAP(cl->cl_auth, xdrs, xargs, 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 = NULL;
404         reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
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 == FALSE) {
485                 memcpy(&inval, cu->cu_inbuf, sizeof(u_int32_t));
486                 memcpy(&outval, cu->cu_outbuf, sizeof(u_int32_t));
487                 if (inval != outval) {
488                         total_time -= tv;
489                         goto send_again;
490                 }
491         }
492
493         /*
494          * now decode and validate the response
495          */
496
497         xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE);
498         ok = xdr_replymsg(&reply_xdrs, &reply_msg);
499         /* XDR_DESTROY(&reply_xdrs);    save a few cycles on noop destroy */
500         if (ok) {
501                 if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
502                         (reply_msg.acpted_rply.ar_stat == SUCCESS))
503                         cu->cu_error.re_status = RPC_SUCCESS;
504                 else
505                         _seterr_reply(&reply_msg, &(cu->cu_error));
506
507                 if (cu->cu_error.re_status == RPC_SUCCESS) {
508                         if (! AUTH_VALIDATE(cl->cl_auth,
509                                             &reply_msg.acpted_rply.ar_verf)) {
510                                 cu->cu_error.re_status = RPC_AUTHERROR;
511                                 cu->cu_error.re_why = AUTH_INVALIDRESP;
512                         } else if (! AUTH_UNWRAP(cl->cl_auth, &reply_xdrs,
513                                                  xresults, resultsp)) {
514                                 if (cu->cu_error.re_status == RPC_SUCCESS)
515                                      cu->cu_error.re_status = RPC_CANTDECODERES;
516                         }
517                         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
518                                 xdrs->x_op = XDR_FREE;
519                                 (void) xdr_opaque_auth(xdrs,
520                                         &(reply_msg.acpted_rply.ar_verf));
521                         }
522                 }               /* end successful completion */
523                 /*
524                  * If unsuccesful AND error is an authentication error
525                  * then refresh credentials and try again, else break
526                  */
527                 else if (cu->cu_error.re_status == RPC_AUTHERROR)
528                         /* maybe our credentials need to be refreshed ... */
529                         if (nrefreshes > 0 &&
530                             AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
531                                 nrefreshes--;
532                                 goto call_again;
533                         }
534                 /* end of unsuccessful completion */
535         }       /* end of valid reply message */
536         else {
537                 cu->cu_error.re_status = RPC_CANTDECODERES;
538
539         }
540 out:
541         release_fd_lock(cu->cu_fd, mask);
542         return (cu->cu_error.re_status);
543 }
544
545 static void
546 clnt_dg_geterr(cl, errp)
547         CLIENT *cl;
548         struct rpc_err *errp;
549 {
550         struct cu_data *cu = (struct cu_data *)cl->cl_private;
551
552         *errp = cu->cu_error;
553 }
554
555 static bool_t
556 clnt_dg_freeres(cl, xdr_res, res_ptr)
557         CLIENT *cl;
558         xdrproc_t xdr_res;
559         void *res_ptr;
560 {
561         struct cu_data *cu = (struct cu_data *)cl->cl_private;
562         XDR *xdrs = &(cu->cu_outxdrs);
563         bool_t dummy;
564         sigset_t mask;
565         sigset_t newmask;
566
567         sigfillset(&newmask);
568         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
569         mutex_lock(&clnt_fd_lock);
570         while (dg_fd_locks[cu->cu_fd])
571                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
572         xdrs->x_op = XDR_FREE;
573         dummy = (*xdr_res)(xdrs, res_ptr);
574         mutex_unlock(&clnt_fd_lock);
575         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
576         cond_signal(&dg_cv[cu->cu_fd]);
577         return (dummy);
578 }
579
580 /*ARGSUSED*/
581 static void
582 clnt_dg_abort(h)
583         CLIENT *h;
584 {
585 }
586
587 static bool_t
588 clnt_dg_control(cl, request, info)
589         CLIENT *cl;
590         u_int request;
591         void *info;
592 {
593         struct cu_data *cu = (struct cu_data *)cl->cl_private;
594         struct netbuf *addr;
595         sigset_t mask;
596         sigset_t newmask;
597         int rpc_lock_value;
598
599         sigfillset(&newmask);
600         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
601         mutex_lock(&clnt_fd_lock);
602         while (dg_fd_locks[cu->cu_fd])
603                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
604         rpc_lock_value = 1;
605         dg_fd_locks[cu->cu_fd] = rpc_lock_value;
606         mutex_unlock(&clnt_fd_lock);
607         switch (request) {
608         case CLSET_FD_CLOSE:
609                 cu->cu_closeit = TRUE;
610                 release_fd_lock(cu->cu_fd, mask);
611                 return (TRUE);
612         case CLSET_FD_NCLOSE:
613                 cu->cu_closeit = FALSE;
614                 release_fd_lock(cu->cu_fd, mask);
615                 return (TRUE);
616         }
617
618         /* for other requests which use info */
619         if (info == NULL) {
620                 release_fd_lock(cu->cu_fd, mask);
621                 return (FALSE);
622         }
623         switch (request) {
624         case CLSET_TIMEOUT:
625                 if (time_not_ok((struct timeval *)info)) {
626                         release_fd_lock(cu->cu_fd, mask);
627                         return (FALSE);
628                 }
629                 cu->cu_total = *(struct timeval *)info;
630                 break;
631         case CLGET_TIMEOUT:
632                 *(struct timeval *)info = cu->cu_total;
633                 break;
634         case CLGET_SERVER_ADDR:         /* Give him the fd address */
635                 /* Now obsolete. Only for backward compatibility */
636                 (void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen);
637                 break;
638         case CLSET_RETRY_TIMEOUT:
639                 if (time_not_ok((struct timeval *)info)) {
640                         release_fd_lock(cu->cu_fd, mask);
641                         return (FALSE);
642                 }
643                 cu->cu_wait = *(struct timeval *)info;
644                 break;
645         case CLGET_RETRY_TIMEOUT:
646                 *(struct timeval *)info = cu->cu_wait;
647                 break;
648         case CLGET_FD:
649                 *(int *)info = cu->cu_fd;
650                 break;
651         case CLGET_SVC_ADDR:
652                 addr = (struct netbuf *)info;
653                 addr->buf = &cu->cu_raddr;
654                 addr->len = cu->cu_rlen;
655                 addr->maxlen = sizeof cu->cu_raddr;
656                 break;
657         case CLSET_SVC_ADDR:            /* set to new address */
658                 addr = (struct netbuf *)info;
659                 if (addr->len < sizeof cu->cu_raddr) {
660                         release_fd_lock(cu->cu_fd, mask);
661                         return (FALSE);
662                 }
663                 (void) memcpy(&cu->cu_raddr, addr->buf, addr->len);
664                 cu->cu_rlen = addr->len;
665                 break;
666         case CLGET_XID:
667                 /*
668                  * use the knowledge that xid is the
669                  * first element in the call structure *.
670                  * This will get the xid of the PREVIOUS call
671                  */
672                 *(u_int32_t *)info =
673                     ntohl(*(u_int32_t *)(void *)cu->cu_outbuf);
674                 break;
675
676         case CLSET_XID:
677                 /* This will set the xid of the NEXT call */
678                 *(u_int32_t *)(void *)cu->cu_outbuf =
679                     htonl(*(u_int32_t *)info - 1);
680                 /* decrement by 1 as clnt_dg_call() increments once */
681                 break;
682
683         case CLGET_VERS:
684                 /*
685                  * This RELIES on the information that, in the call body,
686                  * the version number field is the fifth field from the
687                  * begining of the RPC header. MUST be changed if the
688                  * call_struct is changed
689                  */
690                 *(u_int32_t *)info =
691                     ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
692                     4 * BYTES_PER_XDR_UNIT));
693                 break;
694
695         case CLSET_VERS:
696                 *(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
697                         = htonl(*(u_int32_t *)info);
698                 break;
699
700         case CLGET_PROG:
701                 /*
702                  * This RELIES on the information that, in the call body,
703                  * the program number field is the fourth field from the
704                  * begining of the RPC header. MUST be changed if the
705                  * call_struct is changed
706                  */
707                 *(u_int32_t *)info =
708                     ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
709                     3 * BYTES_PER_XDR_UNIT));
710                 break;
711
712         case CLSET_PROG:
713                 *(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
714                         = htonl(*(u_int32_t *)info);
715                 break;
716         case CLSET_ASYNC:
717                 cu->cu_async = *(int *)info;
718                 break;
719         case CLSET_CONNECT:
720                 cu->cu_connect = *(int *)info;
721                 break;
722         default:
723                 release_fd_lock(cu->cu_fd, mask);
724                 return (FALSE);
725         }
726         release_fd_lock(cu->cu_fd, mask);
727         return (TRUE);
728 }
729
730 static void
731 clnt_dg_destroy(cl)
732         CLIENT *cl;
733 {
734         struct cu_data *cu = (struct cu_data *)cl->cl_private;
735         int cu_fd = cu->cu_fd;
736         sigset_t mask;
737         sigset_t newmask;
738
739         sigfillset(&newmask);
740         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
741         mutex_lock(&clnt_fd_lock);
742         while (dg_fd_locks[cu_fd])
743                 cond_wait(&dg_cv[cu_fd], &clnt_fd_lock);
744         if (cu->cu_closeit)
745                 (void)close(cu_fd);
746         XDR_DESTROY(&(cu->cu_outxdrs));
747         mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
748         if (cl->cl_netid && cl->cl_netid[0])
749                 mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
750         if (cl->cl_tp && cl->cl_tp[0])
751                 mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
752         mem_free(cl, sizeof (CLIENT));
753         mutex_unlock(&clnt_fd_lock);
754         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
755         cond_signal(&dg_cv[cu_fd]);
756 }
757
758 static struct clnt_ops *
759 clnt_dg_ops()
760 {
761         static struct clnt_ops ops;
762         extern mutex_t  ops_lock;
763         sigset_t mask;
764         sigset_t newmask;
765
766 /* VARIABLES PROTECTED BY ops_lock: ops */
767
768         sigfillset(&newmask);
769         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
770         mutex_lock(&ops_lock);
771         if (ops.cl_call == NULL) {
772                 ops.cl_call = clnt_dg_call;
773                 ops.cl_abort = clnt_dg_abort;
774                 ops.cl_geterr = clnt_dg_geterr;
775                 ops.cl_freeres = clnt_dg_freeres;
776                 ops.cl_destroy = clnt_dg_destroy;
777                 ops.cl_control = clnt_dg_control;
778         }
779         mutex_unlock(&ops_lock);
780         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
781         return (&ops);
782 }
783
784 /*
785  * Make sure that the time is not garbage.  -1 value is allowed.
786  */
787 static bool_t
788 time_not_ok(t)
789         struct timeval *t;
790 {
791         return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
792                 t->tv_usec < -1 || t->tv_usec > 1000000);
793 }
794