1 /* $NetBSD: clnt_vc.c,v 1.26 2015/01/20 18:31:25 christos Exp $ */
4 * Copyright (c) 2010, Oracle America, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 * * Neither the name of the "Oracle America, Inc." nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/cdefs.h>
35 #if defined(LIBC_SCCS) && !defined(lint)
37 static char *sccsid
= "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
38 static char *sccsid
= "@(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC";
39 static char sccsid
[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro";
41 __RCSID("$NetBSD: clnt_vc.c,v 1.26 2015/01/20 18:31:25 christos Exp $");
46 * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
48 * Copyright (C) 1984, Sun Microsystems, Inc.
50 * TCP based RPC supports 'batched calls'.
51 * A sequence of calls may be batched-up in a send buffer. The rpc call
52 * return immediately to the client even though the call was not necessarily
53 * sent. The batching occurs if the results' xdr routine is NULL (0) AND
54 * the rpc timeout value is zero (see clnt.h, rpc).
56 * Clients should NOT casually batch calls that in fact return results; that is,
57 * the server side should be aware that a call is batched and not produce any
58 * return message. Batched calls that produce many result messages can
59 * deadlock (netlock) the client and the server....
61 * Now go hang yourself.
64 #include "namespace.h"
65 #include "reentrant.h"
66 #include <sys/types.h>
68 #include <sys/socket.h>
82 #include "svc_fdset.h"
83 #include "rpc_internal.h"
86 __weak_alias(clnt_vc_create
,_clnt_vc_create
)
89 #define MCALL_MSG_SIZE 24
91 static enum clnt_stat
clnt_vc_call(CLIENT
*, rpcproc_t
, xdrproc_t
,
92 const char *, xdrproc_t
, caddr_t
, struct timeval
);
93 static void clnt_vc_geterr(CLIENT
*, struct rpc_err
*);
94 static bool_t
clnt_vc_freeres(CLIENT
*, xdrproc_t
, caddr_t
);
95 static void clnt_vc_abort(CLIENT
*);
96 static bool_t
clnt_vc_control(CLIENT
*, u_int
, char *);
97 static void clnt_vc_destroy(CLIENT
*);
98 static struct clnt_ops
*clnt_vc_ops(void);
99 static bool_t
time_not_ok(struct timeval
*);
100 static int read_vc(caddr_t
, caddr_t
, int);
101 static int write_vc(caddr_t
, caddr_t
, int);
106 struct timeval ct_wait
;
107 bool_t ct_waitset
; /* wait set by clnt_control? */
108 struct netbuf ct_addr
;
109 struct rpc_err ct_error
;
111 char ct_mcallc
[MCALL_MSG_SIZE
]; /* marshalled callmsg */
114 u_int ct_mpos
; /* pos after marshal */
119 * This machinery implements per-fd locks for MT-safety. It is not
120 * sufficient to do per-CLIENT handle locks for MT-safety because a
121 * user may create more than one CLIENT handle with the same fd behind
122 * it. Therfore, we allocate an array of flags (vc_fd_locks), protected
123 * by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables
124 * similarly protected. Vc_fd_lock[fd] == 1 => a call is activte on some
125 * CLIENT handle created for that fd.
126 * The current implementation holds locks across the entire RPC and reply.
127 * Yes, this is silly, and as soon as this code is proven to work, this
128 * should be the first thing fixed. One step at a time.
131 static int *vc_fd_locks
;
132 #define __rpc_lock_value __isthreaded;
133 extern mutex_t clnt_fd_lock
;
134 static cond_t
*vc_cv
;
135 #define release_fd_lock(fd, mask) { \
136 mutex_lock(&clnt_fd_lock); \
137 vc_fd_locks[fd] = 0; \
138 mutex_unlock(&clnt_fd_lock); \
139 thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
140 cond_signal(&vc_cv[fd]); \
143 #define release_fd_lock(fd,mask)
144 #define __rpc_lock_value 0
148 htonlp(void *dst
, const void *src
, uint32_t incr
)
152 memcpy(&tmp
, src
, sizeof(tmp
));
153 tmp
= htonl(tmp
+ incr
);
154 memcpy(dst
, &tmp
, sizeof(tmp
));
156 /* We are aligned, so we think */
157 *(uint32_t *)dst
= htonl(*(const uint32_t *)src
+ incr
);
162 ntohlp(void *dst
, const void *src
)
166 memcpy(&tmp
, src
, sizeof(tmp
));
168 memcpy(dst
, &tmp
, sizeof(tmp
));
170 /* We are aligned, so we think */
171 *(uint32_t *)dst
= htonl(*(const uint32_t *)src
);
176 * Create a client handle for a connection.
177 * Default options are set, which the user can change using clnt_control()'s.
178 * The rpc/vc package does buffering similar to stdio, so the client
179 * must pick send and receive buffer sizes, 0 => use the default.
180 * NB: fd is copied into a private area.
181 * NB: The rpch->cl_auth is set null authentication. Caller may wish to
182 * set this something more useful.
184 * fd should be an open socket
189 const struct netbuf
*raddr
,
197 struct ct_data
*ct
= NULL
;
198 struct rpc_msg call_msg
;
203 struct sockaddr_storage ss
;
205 struct __rpc_sockinfo si
;
207 _DIAGASSERT(raddr
!= NULL
);
209 h
= mem_alloc(sizeof(*h
));
211 warnx("clnt_vc_create: out of memory");
212 rpc_createerr
.cf_stat
= RPC_SYSTEMERROR
;
213 rpc_createerr
.cf_error
.re_errno
= errno
;
216 ct
= mem_alloc(sizeof(*ct
));
218 warnx("clnt_vc_create: out of memory");
219 rpc_createerr
.cf_stat
= RPC_SYSTEMERROR
;
220 rpc_createerr
.cf_error
.re_errno
= errno
;
224 __clnt_sigfillset(&newmask
);
225 thr_sigsetmask(SIG_SETMASK
, &newmask
, &mask
);
227 mutex_lock(&clnt_fd_lock
);
228 if (vc_fd_locks
== NULL
) {
229 size_t cv_allocsz
, fd_allocsz
;
230 int dtbsize
= __rpc_dtbsize();
232 fd_allocsz
= dtbsize
* sizeof (int);
233 vc_fd_locks
= mem_alloc(fd_allocsz
);
234 if (vc_fd_locks
== NULL
) {
237 memset(vc_fd_locks
, '\0', fd_allocsz
);
239 _DIAGASSERT(vc_cv
== NULL
);
240 cv_allocsz
= dtbsize
* sizeof (cond_t
);
241 vc_cv
= mem_alloc(cv_allocsz
);
243 mem_free(vc_fd_locks
, fd_allocsz
);
249 for (i
= 0; i
< dtbsize
; i
++)
250 cond_init(&vc_cv
[i
], 0, (void *) 0);
253 _DIAGASSERT(vc_cv
!= NULL
);
257 * XXX - fvdl connecting while holding a mutex?
260 if (getpeername(fd
, (struct sockaddr
*)(void *)&ss
, &slen
) < 0) {
261 if (errno
!= ENOTCONN
) {
262 rpc_createerr
.cf_stat
= RPC_SYSTEMERROR
;
263 rpc_createerr
.cf_error
.re_errno
= errno
;
266 if (connect(fd
, (struct sockaddr
*)raddr
->buf
, raddr
->len
) < 0){
267 rpc_createerr
.cf_stat
= RPC_SYSTEMERROR
;
268 rpc_createerr
.cf_error
.re_errno
= errno
;
272 mutex_unlock(&clnt_fd_lock
);
273 thr_sigsetmask(SIG_SETMASK
, &(mask
), NULL
);
274 if (!__rpc_fd2sockinfo(fd
, &si
))
277 ct
->ct_closeit
= FALSE
;
280 * Set up private data struct
283 ct
->ct_wait
.tv_usec
= 0;
284 ct
->ct_waitset
= FALSE
;
285 ct
->ct_addr
.buf
= malloc((size_t)raddr
->maxlen
);
286 if (ct
->ct_addr
.buf
== NULL
)
288 memcpy(ct
->ct_addr
.buf
, raddr
->buf
, (size_t)raddr
->len
);
289 ct
->ct_addr
.len
= raddr
->len
;
290 ct
->ct_addr
.maxlen
= raddr
->maxlen
;
293 * Initialize call message
295 call_msg
.rm_xid
= __RPC_GETXID();
296 call_msg
.rm_direction
= CALL
;
297 call_msg
.rm_call
.cb_rpcvers
= RPC_MSG_VERSION
;
298 call_msg
.rm_call
.cb_prog
= (u_int32_t
)prog
;
299 call_msg
.rm_call
.cb_vers
= (u_int32_t
)vers
;
302 * pre-serialize the static part of the call msg and stash it away
304 xdrmem_create(&(ct
->ct_xdrs
), ct
->ct_u
.ct_mcallc
, MCALL_MSG_SIZE
,
306 if (! xdr_callhdr(&(ct
->ct_xdrs
), &call_msg
)) {
307 if (ct
->ct_closeit
) {
312 ct
->ct_mpos
= XDR_GETPOS(&(ct
->ct_xdrs
));
313 XDR_DESTROY(&(ct
->ct_xdrs
));
316 * Create a client handle which uses xdrrec for serialization
317 * and authnone for authentication.
319 h
->cl_ops
= clnt_vc_ops();
321 h
->cl_auth
= authnone_create();
322 sendsz
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)sendsz
);
323 recvsz
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)recvsz
);
324 xdrrec_create(&(ct
->ct_xdrs
), sendsz
, recvsz
,
325 h
->cl_private
, read_vc
, write_vc
);
329 mutex_unlock(&clnt_fd_lock
);
330 thr_sigsetmask(SIG_SETMASK
, &(mask
), NULL
);
333 * Something goofed, free stuff and barf
336 mem_free(ct
, sizeof(struct ct_data
));
338 mem_free(h
, sizeof(CLIENT
));
342 static enum clnt_stat
347 const char *args_ptr
,
348 xdrproc_t xdr_results
,
350 struct timeval timeout
355 struct rpc_msg reply_msg
;
361 sigset_t mask
, newmask
;
364 _DIAGASSERT(h
!= NULL
);
366 ct
= (struct ct_data
*) h
->cl_private
;
369 __clnt_sigfillset(&newmask
);
370 thr_sigsetmask(SIG_SETMASK
, &newmask
, &mask
);
371 mutex_lock(&clnt_fd_lock
);
372 while (vc_fd_locks
[ct
->ct_fd
])
373 cond_wait(&vc_cv
[ct
->ct_fd
], &clnt_fd_lock
);
374 vc_fd_locks
[ct
->ct_fd
] = __rpc_lock_value
;
375 mutex_unlock(&clnt_fd_lock
);
378 xdrs
= &(ct
->ct_xdrs
);
379 msg_x_id
= &ct
->ct_u
.ct_mcalli
;
381 if (!ct
->ct_waitset
) {
382 if (time_not_ok(&timeout
) == FALSE
)
383 ct
->ct_wait
= timeout
;
387 (xdr_results
== NULL
&& timeout
.tv_sec
== 0
388 && timeout
.tv_usec
== 0) ? FALSE
: TRUE
;
391 xdrs
->x_op
= XDR_ENCODE
;
392 ct
->ct_error
.re_status
= RPC_SUCCESS
;
393 x_id
= ntohl(--(*msg_x_id
));
394 if ((! XDR_PUTBYTES(xdrs
, ct
->ct_u
.ct_mcallc
, ct
->ct_mpos
)) ||
395 (! XDR_PUTINT32(xdrs
, (int32_t *)&proc
)) ||
396 (! AUTH_MARSHALL(h
->cl_auth
, xdrs
)) ||
397 (! (*xdr_args
)(xdrs
, __UNCONST(args_ptr
)))) {
398 if (ct
->ct_error
.re_status
== RPC_SUCCESS
)
399 ct
->ct_error
.re_status
= RPC_CANTENCODEARGS
;
400 (void)xdrrec_endofrecord(xdrs
, TRUE
);
401 release_fd_lock(ct
->ct_fd
, mask
);
402 return (ct
->ct_error
.re_status
);
404 if (! xdrrec_endofrecord(xdrs
, shipnow
)) {
405 release_fd_lock(ct
->ct_fd
, mask
);
406 return (ct
->ct_error
.re_status
= RPC_CANTSEND
);
409 release_fd_lock(ct
->ct_fd
, mask
);
410 return (RPC_SUCCESS
);
413 * Hack to provide rpc-based message passing
415 if (timeout
.tv_sec
== 0 && timeout
.tv_usec
== 0) {
416 release_fd_lock(ct
->ct_fd
, mask
);
417 return(ct
->ct_error
.re_status
= RPC_TIMEDOUT
);
422 * Keep receiving until we get a valid transaction id
424 xdrs
->x_op
= XDR_DECODE
;
426 reply_msg
.acpted_rply
.ar_verf
= _null_auth
;
427 reply_msg
.acpted_rply
.ar_results
.where
= NULL
;
428 reply_msg
.acpted_rply
.ar_results
.proc
= (xdrproc_t
)xdr_void
;
429 if (! xdrrec_skiprecord(xdrs
)) {
430 release_fd_lock(ct
->ct_fd
, mask
);
431 return (ct
->ct_error
.re_status
);
433 /* now decode and validate the response header */
434 if (! xdr_replymsg(xdrs
, &reply_msg
)) {
435 if (ct
->ct_error
.re_status
== RPC_SUCCESS
)
437 release_fd_lock(ct
->ct_fd
, mask
);
438 return (ct
->ct_error
.re_status
);
440 if (reply_msg
.rm_xid
== x_id
)
447 _seterr_reply(&reply_msg
, &(ct
->ct_error
));
448 if (ct
->ct_error
.re_status
== RPC_SUCCESS
) {
449 if (! AUTH_VALIDATE(h
->cl_auth
,
450 &reply_msg
.acpted_rply
.ar_verf
)) {
451 ct
->ct_error
.re_status
= RPC_AUTHERROR
;
452 ct
->ct_error
.re_why
= AUTH_INVALIDRESP
;
453 } else if (! (*xdr_results
)(xdrs
, results_ptr
)) {
454 if (ct
->ct_error
.re_status
== RPC_SUCCESS
)
455 ct
->ct_error
.re_status
= RPC_CANTDECODERES
;
457 /* free verifier ... */
458 if (reply_msg
.acpted_rply
.ar_verf
.oa_base
!= NULL
) {
459 xdrs
->x_op
= XDR_FREE
;
460 (void)xdr_opaque_auth(xdrs
,
461 &(reply_msg
.acpted_rply
.ar_verf
));
463 } /* end successful completion */
465 /* maybe our credentials need to be refreshed ... */
466 if (refreshes
-- && AUTH_REFRESH(h
->cl_auth
))
468 } /* end of unsuccessful completion */
469 release_fd_lock(ct
->ct_fd
, mask
);
470 return (ct
->ct_error
.re_status
);
481 _DIAGASSERT(h
!= NULL
);
482 _DIAGASSERT(errp
!= NULL
);
484 ct
= (struct ct_data
*) h
->cl_private
;
485 *errp
= ct
->ct_error
;
503 _DIAGASSERT(cl
!= NULL
);
505 ct
= (struct ct_data
*)cl
->cl_private
;
506 xdrs
= &(ct
->ct_xdrs
);
508 __clnt_sigfillset(&newmask
);
509 thr_sigsetmask(SIG_SETMASK
, &newmask
, &mask
);
510 mutex_lock(&clnt_fd_lock
);
512 while (vc_fd_locks
[ct
->ct_fd
])
513 cond_wait(&vc_cv
[ct
->ct_fd
], &clnt_fd_lock
);
516 xdrs
->x_op
= XDR_FREE
;
517 dummy
= (*xdr_res
)(xdrs
, res_ptr
);
518 mutex_unlock(&clnt_fd_lock
);
519 thr_sigsetmask(SIG_SETMASK
, &(mask
), NULL
);
520 cond_signal(&vc_cv
[ct
->ct_fd
]);
527 clnt_vc_abort(CLIENT
*cl
)
545 _DIAGASSERT(cl
!= NULL
);
547 ct
= (struct ct_data
*)cl
->cl_private
;
549 __clnt_sigfillset(&newmask
);
550 thr_sigsetmask(SIG_SETMASK
, &newmask
, &mask
);
551 mutex_lock(&clnt_fd_lock
);
553 while (vc_fd_locks
[ct
->ct_fd
])
554 cond_wait(&vc_cv
[ct
->ct_fd
], &clnt_fd_lock
);
555 vc_fd_locks
[ct
->ct_fd
] = __rpc_lock_value
;
557 mutex_unlock(&clnt_fd_lock
);
561 ct
->ct_closeit
= TRUE
;
562 release_fd_lock(ct
->ct_fd
, mask
);
564 case CLSET_FD_NCLOSE
:
565 ct
->ct_closeit
= FALSE
;
566 release_fd_lock(ct
->ct_fd
, mask
);
572 /* for other requests which use info */
574 release_fd_lock(ct
->ct_fd
, mask
);
579 if (time_not_ok((struct timeval
*)(void *)info
)) {
580 release_fd_lock(ct
->ct_fd
, mask
);
583 ct
->ct_wait
= *(struct timeval
*)infop
;
584 ct
->ct_waitset
= TRUE
;
587 *(struct timeval
*)infop
= ct
->ct_wait
;
589 case CLGET_SERVER_ADDR
:
590 (void) memcpy(info
, ct
->ct_addr
.buf
, (size_t)ct
->ct_addr
.len
);
593 *(int *)(void *)info
= ct
->ct_fd
;
596 /* The caller should not free this memory area */
597 *(struct netbuf
*)(void *)info
= ct
->ct_addr
;
599 case CLSET_SVC_ADDR
: /* set to new address */
600 release_fd_lock(ct
->ct_fd
, mask
);
604 * use the knowledge that xid is the
605 * first element in the call structure
606 * This will get the xid of the PREVIOUS call
608 ntohlp(info
, &ct
->ct_u
.ct_mcalli
);
611 /* This will set the xid of the NEXT call */
612 /* increment by 1 as clnt_vc_call() decrements once */
613 htonlp(&ct
->ct_u
.ct_mcalli
, info
, 1);
617 * This RELIES on the information that, in the call body,
618 * the version number field is the fifth field from the
619 * begining of the RPC header. MUST be changed if the
620 * call_struct is changed
622 ntohlp(info
, ct
->ct_u
.ct_mcallc
+ 4 * BYTES_PER_XDR_UNIT
);
626 htonlp(ct
->ct_u
.ct_mcallc
+ 4 * BYTES_PER_XDR_UNIT
, info
, 0);
631 * This RELIES on the information that, in the call body,
632 * the program number field is the fourth field from the
633 * begining of the RPC header. MUST be changed if the
634 * call_struct is changed
636 ntohlp(info
, ct
->ct_u
.ct_mcallc
+ 3 * BYTES_PER_XDR_UNIT
);
640 htonlp(ct
->ct_u
.ct_mcallc
+ 3 * BYTES_PER_XDR_UNIT
, info
, 0);
644 release_fd_lock(ct
->ct_fd
, mask
);
647 release_fd_lock(ct
->ct_fd
, mask
);
653 clnt_vc_destroy(CLIENT
*cl
)
662 _DIAGASSERT(cl
!= NULL
);
664 ct
= (struct ct_data
*) cl
->cl_private
;
666 __clnt_sigfillset(&newmask
);
667 thr_sigsetmask(SIG_SETMASK
, &newmask
, &mask
);
668 mutex_lock(&clnt_fd_lock
);
671 while (vc_fd_locks
[ct_fd
])
672 cond_wait(&vc_cv
[ct_fd
], &clnt_fd_lock
);
674 if (ct
->ct_closeit
&& ct
->ct_fd
!= -1) {
675 (void)close(ct
->ct_fd
);
677 XDR_DESTROY(&(ct
->ct_xdrs
));
679 free(ct
->ct_addr
.buf
);
680 mem_free(ct
, sizeof(struct ct_data
));
681 mem_free(cl
, sizeof(CLIENT
));
682 mutex_unlock(&clnt_fd_lock
);
683 thr_sigsetmask(SIG_SETMASK
, &(mask
), NULL
);
685 cond_signal(&vc_cv
[ct_fd
]);
689 * Interface between xdr serializer and tcp connection.
690 * Behaves like the system calls, read & write, but keeps some error state
691 * around for the rpc level.
694 read_vc(char *ctp
, char *buf
, int len
)
696 struct ct_data
*ct
= (struct ct_data
*)(void *)ctp
;
704 TIMEVAL_TO_TIMESPEC(&ct
->ct_wait
, &ts
);
708 switch (pollts(&fd
, 1, &ts
, NULL
)) {
710 ct
->ct_error
.re_status
= RPC_TIMEDOUT
;
716 ct
->ct_error
.re_status
= RPC_CANTRECV
;
717 ct
->ct_error
.re_errno
= errno
;
722 switch (nread
= read(ct
->ct_fd
, buf
, (size_t)len
)) {
726 ct
->ct_error
.re_errno
= ECONNRESET
;
727 ct
->ct_error
.re_status
= RPC_CANTRECV
;
728 nread
= -1; /* it's really an error */
732 ct
->ct_error
.re_errno
= errno
;
733 ct
->ct_error
.re_status
= RPC_CANTRECV
;
740 write_vc(char *ctp
, char *buf
, int len
)
742 struct ct_data
*ct
= (struct ct_data
*)(void *)ctp
;
746 for (cnt
= len
; cnt
> 0; cnt
-= i
, buf
+= i
) {
747 if ((i
= write(ct
->ct_fd
, buf
, cnt
)) == -1) {
748 ct
->ct_error
.re_errno
= errno
;
749 ct
->ct_error
.re_status
= RPC_CANTSEND
;
756 static struct clnt_ops
*
759 static struct clnt_ops ops
;
761 extern mutex_t ops_lock
;
766 /* VARIABLES PROTECTED BY ops_lock: ops */
768 __clnt_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_vc_call
;
773 ops
.cl_abort
= clnt_vc_abort
;
774 ops
.cl_geterr
= clnt_vc_geterr
;
775 ops
.cl_freeres
= clnt_vc_freeres
;
776 ops
.cl_destroy
= clnt_vc_destroy
;
777 ops
.cl_control
= clnt_vc_control
;
779 mutex_unlock(&ops_lock
);
780 thr_sigsetmask(SIG_SETMASK
, &(mask
), NULL
);
785 * Make sure that the time is not garbage. -1 value is disallowed.
786 * Note this is different from time_not_ok in clnt_dg.c
789 time_not_ok(struct timeval
*t
)
792 _DIAGASSERT(t
!= NULL
);
794 return (t
->tv_sec
<= -1 || t
->tv_sec
> 100000000 ||
795 t
->tv_usec
<= -1 || t
->tv_usec
> 1000000);