1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25 */
26 /*
27 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
28 */
29 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
31 /*
32 * Portions of this source code were derived from Berkeley
33 * 4.3 BSD under license from the Regents of the University of
34 * California.
35 */
36
37 /*
38 * svc.c, Server-side remote procedure call interface.
39 *
40 * There are two sets of procedures here. The xprt routines are
41 * for handling transport handles. The svc routines handle the
42 * list of service routines.
43 *
44 */
45
46 #include "mt.h"
47 #include "rpc_mt.h"
48 #include <assert.h>
49 #include <errno.h>
50 #include <sys/types.h>
51 #include <stropts.h>
52 #include <sys/conf.h>
53 #include <rpc/rpc.h>
54 #ifdef PORTMAP
55 #include <rpc/pmap_clnt.h>
56 #endif
57 #include <sys/poll.h>
58 #include <netconfig.h>
59 #include <syslog.h>
60 #include <stdlib.h>
61 #include <unistd.h>
62 #include <string.h>
63 #include <limits.h>
64
65 extern bool_t __svc_get_door_cred();
66 extern bool_t __rpc_get_local_cred();
67
68 SVCXPRT **svc_xports;
69 static int nsvc_xports; /* total number of svc_xports allocated */
70
71 XDR **svc_xdrs; /* common XDR receive area */
72 int nsvc_xdrs; /* total number of svc_xdrs allocated */
73
74 int __rpc_use_pollfd_done; /* to unlimit the number of connections */
75
76 #define NULL_SVC ((struct svc_callout *)0)
77 #define RQCRED_SIZE 400 /* this size is excessive */
78
79 /*
80 * The services list
81 * Each entry represents a set of procedures (an rpc program).
82 * The dispatch routine takes request structs and runs the
83 * appropriate procedure.
84 */
85 static struct svc_callout {
86 struct svc_callout *sc_next;
87 rpcprog_t sc_prog;
88 rpcvers_t sc_vers;
89 char *sc_netid;
90 void (*sc_dispatch)();
91 } *svc_head;
92 extern rwlock_t svc_lock;
93
94 static struct svc_callout *svc_find();
95 int _svc_prog_dispatch();
96 void svc_getreq_common();
97 char *strdup();
98
99 extern mutex_t svc_door_mutex;
100 extern cond_t svc_door_waitcv;
101 extern int svc_ndoorfds;
102 extern SVCXPRT_LIST *_svc_xprtlist;
103 extern mutex_t xprtlist_lock;
104 extern void __svc_rm_from_xlist();
105
106 #if !defined(_LP64)
107 extern fd_set _new_svc_fdset;
108 #endif
109
110 /*
111 * If the allocated array of reactor is too small, this value is used as a
112 * margin. This reduces the number of allocations.
113 */
114 #define USER_FD_INCREMENT 5
115
116 static void add_pollfd(int fd, short events);
117 static void remove_pollfd(int fd);
118 static void __svc_remove_input_of_fd(int fd);
119
120 /*
121 * Data used to handle reactor:
122 * - one file descriptor we listen to,
123 * - one callback we call if the fd pops,
124 * - and a cookie passed as a parameter to the callback.
125 *
126 * The structure is an array indexed on the file descriptor. Each entry is
127 * pointing to the first element of a double-linked list of callback.
128 * only one callback may be associated to a couple (fd, event).
129 */
130
131 struct _svc_user_fd_head;
132
133 typedef struct {
134 struct _svc_user_fd_node *next;
135 struct _svc_user_fd_node *previous;
136 } _svc_user_link;
137
138 typedef struct _svc_user_fd_node {
139 _svc_user_link lnk;
140 svc_input_id_t id;
141 int fd;
142 unsigned int events;
143 svc_callback_t callback;
144 void* cookie;
145 } _svc_user_fd_node;
146
147 typedef struct _svc_user_fd_head {
148 struct _svc_user_fd_node *list;
149 unsigned int mask; /* logical OR of all sub-masks */
150 } _svc_user_fd_head;
151
152
153 /* Array of defined reactor - indexed on file descriptor */
154 static _svc_user_fd_head *svc_userfds = NULL;
155
156 /* current size of file descriptor */
157 static int svc_nuserfds = 0;
158
159 /* Mutex to ensure MT safe operations for user fds callbacks. */
160 static mutex_t svc_userfds_lock = DEFAULTMUTEX;
161
162
163 /*
164 * This structure is used to have constant time alogrithms. There is an array
165 * of this structure as large as svc_nuserfds. When the user is registering a
166 * new callback, the address of the created structure is stored in a cell of
167 * this array. The address of this cell is the returned unique identifier.
168 *
169 * On removing, the id is given by the user, then we know if this cell is
170 * filled or not (with free). If it is free, we return an error. Otherwise,
171 * we can free the structure pointed by fd_node.
172 *
173 * On insertion, we use the linked list created by (first_free,
174 * next_free). In this way with a constant time computation, we can give a
175 * correct index to the user.
176 */
177
178 typedef struct _svc_management_user_fd {
179 bool_t free;
180 union {
181 svc_input_id_t next_free;
182 _svc_user_fd_node *fd_node;
183 } data;
184 } _svc_management_user_fd;
185
186 /* index to the first free elem */
187 static svc_input_id_t first_free = (svc_input_id_t)-1;
188 /* the size of this array is the same as svc_nuserfds */
189 static _svc_management_user_fd* user_fd_mgt_array = NULL;
190
191 /* current size of user_fd_mgt_array */
192 static int svc_nmgtuserfds = 0;
193
194
195 /* Define some macros to access data associated to registration ids. */
196 #define node_from_id(id) (user_fd_mgt_array[(int)id].data.fd_node)
197 #define is_free_id(id) (user_fd_mgt_array[(int)id].free)
198
199 #ifndef POLLSTANDARD
200 #define POLLSTANDARD \
201 (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| \
202 POLLWRBAND|POLLERR|POLLHUP|POLLNVAL)
203 #endif
204
205 /*
206 * To free an Id, we set the cell as free and insert its address in the list
207 * of free cell.
208 */
209
210 static void
211 _svc_free_id(const svc_input_id_t id)
212 {
213 assert(((int)id >= 0) && ((int)id < svc_nmgtuserfds));
214 user_fd_mgt_array[(int)id].free = TRUE;
215 user_fd_mgt_array[(int)id].data.next_free = first_free;
216 first_free = id;
217 }
218
219 /*
220 * To get a free cell, we just have to take it from the free linked list and
221 * set the flag to "not free". This function also allocates new memory if
222 * necessary
223 */
224 static svc_input_id_t
225 _svc_attribute_new_id(_svc_user_fd_node *node)
226 {
227 int selected_index = (int)first_free;
228 assert(node != NULL);
229
230 if (selected_index == -1) {
231 /* Allocate new entries */
232 int L_inOldSize = svc_nmgtuserfds;
233 int i;
234 _svc_management_user_fd *tmp;
235
236 svc_nmgtuserfds += USER_FD_INCREMENT;
237
238 tmp = realloc(user_fd_mgt_array,
239 svc_nmgtuserfds * sizeof (_svc_management_user_fd));
240
241 if (tmp == NULL) {
242 syslog(LOG_ERR, "_svc_attribute_new_id: out of memory");
243 svc_nmgtuserfds = L_inOldSize;
244 errno = ENOMEM;
245 return ((svc_input_id_t)-1);
246 }
247
248 user_fd_mgt_array = tmp;
249
250 for (i = svc_nmgtuserfds - 1; i >= L_inOldSize; i--)
251 _svc_free_id((svc_input_id_t)i);
252 selected_index = (int)first_free;
253 }
254
255 node->id = (svc_input_id_t)selected_index;
256 first_free = user_fd_mgt_array[selected_index].data.next_free;
257
258 user_fd_mgt_array[selected_index].data.fd_node = node;
259 user_fd_mgt_array[selected_index].free = FALSE;
260
261 return ((svc_input_id_t)selected_index);
262 }
263
264 /*
265 * Access to a pollfd treatment. Scan all the associated callbacks that have
266 * at least one bit in their mask that masks a received event.
267 *
268 * If event POLLNVAL is received, we check that one callback processes it, if
269 * not, then remove the file descriptor from the poll. If there is one, let
270 * the user do the work.
271 */
272 void
273 __svc_getreq_user(struct pollfd *pfd)
274 {
275 int fd = pfd->fd;
276 short revents = pfd->revents;
277 bool_t invalHandled = FALSE;
278 _svc_user_fd_node *node;
279
280 (void) mutex_lock(&svc_userfds_lock);
281
282 if ((fd < 0) || (fd >= svc_nuserfds)) {
283 (void) mutex_unlock(&svc_userfds_lock);
284 return;
285 }
286
287 node = svc_userfds[fd].list;
288
289 /* check if at least one mask fits */
290 if (0 == (revents & svc_userfds[fd].mask)) {
291 (void) mutex_unlock(&svc_userfds_lock);
292 return;
293 }
294
295 while ((svc_userfds[fd].mask != 0) && (node != NULL)) {
296 /*
297 * If one of the received events maps the ones the node listens
298 * to
299 */
300 _svc_user_fd_node *next = node->lnk.next;
301
302 if (node->callback != NULL) {
303 if (node->events & revents) {
304 if (revents & POLLNVAL) {
305 invalHandled = TRUE;
306 }
307
308 /*
309 * The lock must be released before calling the
310 * user function, as this function can call
311 * svc_remove_input() for example.
312 */
313 (void) mutex_unlock(&svc_userfds_lock);
314 node->callback(node->id, node->fd,
315 node->events & revents, node->cookie);
316 /*
317 * Do not use the node structure anymore, as it
318 * could have been deallocated by the previous
319 * callback.
320 */
321 (void) mutex_lock(&svc_userfds_lock);
322 }
323 }
324 node = next;
325 }
326
327 if ((revents & POLLNVAL) && !invalHandled)
328 __svc_remove_input_of_fd(fd);
329 (void) mutex_unlock(&svc_userfds_lock);
330 }
331
332
333 /*
334 * Check if a file descriptor is associated with a user reactor.
335 * To do this, just check that the array indexed on fd has a non-void linked
336 * list (ie. first element is not NULL)
337 */
338 bool_t
339 __is_a_userfd(int fd)
340 {
341 /* Checks argument */
342 if ((fd < 0) || (fd >= svc_nuserfds))
343 return (FALSE);
344 return ((svc_userfds[fd].mask == 0x0000)? FALSE:TRUE);
345 }
346
347 /* free everything concerning user fd */
348 /* used in svc_run.c => no static */
349
350 void
351 __destroy_userfd(void)
352 {
353 int one_fd;
354 /* Clean user fd */
355 if (svc_userfds != NULL) {
356 for (one_fd = 0; one_fd < svc_nuserfds; one_fd++) {
357 _svc_user_fd_node *node;
358
359 node = svc_userfds[one_fd].list;
360 while (node != NULL) {
361 _svc_user_fd_node *tmp = node;
362 _svc_free_id(node->id);
363 node = node->lnk.next;
364 free(tmp);
365 }
366 }
367
368 free(user_fd_mgt_array);
369 user_fd_mgt_array = NULL;
370 first_free = (svc_input_id_t)-1;
371
372 free(svc_userfds);
373 svc_userfds = NULL;
374 svc_nuserfds = 0;
375 }
376 }
377
378 /*
379 * Remove all the callback associated with a fd => useful when the fd is
380 * closed for instance
381 */
382 static void
383 __svc_remove_input_of_fd(int fd)
384 {
385 _svc_user_fd_node **pnode;
386 _svc_user_fd_node *tmp;
387
388 if ((fd < 0) || (fd >= svc_nuserfds))
389 return;
390
391 pnode = &svc_userfds[fd].list;
392 while ((tmp = *pnode) != NULL) {
393 *pnode = tmp->lnk.next;
394
395 _svc_free_id(tmp->id);
396 free(tmp);
397 }
398
399 svc_userfds[fd].mask = 0;
400 }
401
402 /*
403 * Allow user to add an fd in the poll list. If it does not succeed, return
404 * -1. Otherwise, return a svc_id
405 */
406
407 svc_input_id_t
408 svc_add_input(int user_fd, unsigned int events,
409 svc_callback_t user_callback, void *cookie)
410 {
411 _svc_user_fd_node *new_node;
412
413 if (user_fd < 0) {
414 errno = EINVAL;
415 return ((svc_input_id_t)-1);
416 }
417
418 if ((events == 0x0000) ||
419 (events & ~(POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND|\
420 POLLWRBAND|POLLERR|POLLHUP|POLLNVAL))) {
421 errno = EINVAL;
422 return ((svc_input_id_t)-1);
423 }
424
425 (void) mutex_lock(&svc_userfds_lock);
426
427 if ((user_fd < svc_nuserfds) &&
428 (svc_userfds[user_fd].mask & events) != 0) {
429 /* Already registrated call-back */
430 errno = EEXIST;
431 (void) mutex_unlock(&svc_userfds_lock);
432 return ((svc_input_id_t)-1);
433 }
434
435 /* Handle memory allocation. */
436 if (user_fd >= svc_nuserfds) {
437 int oldSize = svc_nuserfds;
438 int i;
439 _svc_user_fd_head *tmp;
440
441 svc_nuserfds = (user_fd + 1) + USER_FD_INCREMENT;
442
443 tmp = realloc(svc_userfds,
444 svc_nuserfds * sizeof (_svc_user_fd_head));
445
446 if (tmp == NULL) {
447 syslog(LOG_ERR, "svc_add_input: out of memory");
448 svc_nuserfds = oldSize;
449 errno = ENOMEM;
450 (void) mutex_unlock(&svc_userfds_lock);
451 return ((svc_input_id_t)-1);
452 }
453
454 svc_userfds = tmp;
455
456 for (i = oldSize; i < svc_nuserfds; i++) {
457 svc_userfds[i].list = NULL;
458 svc_userfds[i].mask = 0;
459 }
460 }
461
462 new_node = malloc(sizeof (_svc_user_fd_node));
463 if (new_node == NULL) {
464 syslog(LOG_ERR, "svc_add_input: out of memory");
465 errno = ENOMEM;
466 (void) mutex_unlock(&svc_userfds_lock);
467 return ((svc_input_id_t)-1);
468 }
469
470 /* create a new node */
471 new_node->fd = user_fd;
472 new_node->events = events;
473 new_node->callback = user_callback;
474 new_node->cookie = cookie;
475
476 if (_svc_attribute_new_id(new_node) == -1) {
477 (void) mutex_unlock(&svc_userfds_lock);
478 free(new_node);
479 return ((svc_input_id_t)-1);
480 }
481
482 /* Add the new element at the beginning of the list. */
483 if (svc_userfds[user_fd].list != NULL)
484 svc_userfds[user_fd].list->lnk.previous = new_node;
485 new_node->lnk.next = svc_userfds[user_fd].list;
486 new_node->lnk.previous = NULL;
487
488 svc_userfds[user_fd].list = new_node;
489
490 /* refresh global mask for this file desciptor */
491 svc_userfds[user_fd].mask |= events;
492
493 /* refresh mask for the poll */
494 add_pollfd(user_fd, (svc_userfds[user_fd].mask));
495
496 (void) mutex_unlock(&svc_userfds_lock);
497 return (new_node->id);
498 }
499
500 int
501 svc_remove_input(svc_input_id_t id)
502 {
503 _svc_user_fd_node* node;
504 _svc_user_fd_node* next;
505 _svc_user_fd_node* previous;
506 int fd; /* caching optim */
507
508 (void) mutex_lock(&svc_userfds_lock);
509
510 /* Immediately update data for id management */
511 if (user_fd_mgt_array == NULL || id >= svc_nmgtuserfds ||
512 is_free_id(id)) {
513 errno = EINVAL;
514 (void) mutex_unlock(&svc_userfds_lock);
515 return (-1);
516 }
517
518 node = node_from_id(id);
519 assert(node != NULL);
520
521 _svc_free_id(id);
522 next = node->lnk.next;
523 previous = node->lnk.previous;
524 fd = node->fd; /* caching optim */
525
526 /* Remove this node from the list. */
527 if (previous != NULL) {
528 previous->lnk.next = next;
529 } else {
530 assert(svc_userfds[fd].list == node);
531 svc_userfds[fd].list = next;
532 }
533 if (next != NULL)
534 next->lnk.previous = previous;
535
536 /* Remove the node flags from the global mask */
537 svc_userfds[fd].mask ^= node->events;
538
539 free(node);
540 if (svc_userfds[fd].mask == 0) {
541 assert(svc_userfds[fd].list == NULL);
542 remove_pollfd(fd);
543 } else {
544 assert(svc_userfds[fd].list != NULL);
545 }
546 /* <=> CLEAN NEEDED TO SHRINK MEMORY USAGE */
547
548 (void) mutex_unlock(&svc_userfds_lock);
549 return (0);
550 }
551
552 /*
553 * Provides default service-side functions for authentication flavors
554 * that do not use all the fields in struct svc_auth_ops.
555 */
556
557 /*ARGSUSED*/
558 static int
559 authany_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xfunc, caddr_t xwhere)
560 {
561 return (*xfunc)(xdrs, xwhere);
562 }
563
564 struct svc_auth_ops svc_auth_any_ops = {
565 authany_wrap,
566 authany_wrap,
567 };
568
569 /*
570 * Return pointer to server authentication structure.
571 */
572 SVCAUTH *
573 __svc_get_svcauth(SVCXPRT *xprt)
574 {
575 /* LINTED pointer alignment */
576 return (&SVC_XP_AUTH(xprt));
577 }
578
579 /*
580 * A callback routine to cleanup after a procedure is executed.
581 */
582 void (*__proc_cleanup_cb)() = NULL;
583
584 void *
585 __svc_set_proc_cleanup_cb(void *cb)
586 {
587 void *tmp = (void *)__proc_cleanup_cb;
588
589 __proc_cleanup_cb = (void (*)())cb;
590 return (tmp);
591 }
592
593 /* *************** SVCXPRT related stuff **************** */
594
595
596 static int pollfd_shrinking = 1;
597
598
599 /*
600 * Add fd to svc_pollfd
601 */
602 static void
603 add_pollfd(int fd, short events)
604 {
605 if (fd < FD_SETSIZE) {
606 FD_SET(fd, &svc_fdset);
607 #if !defined(_LP64)
608 FD_SET(fd, &_new_svc_fdset);
609 #endif
610 svc_nfds++;
611 svc_nfds_set++;
612 if (fd >= svc_max_fd)
613 svc_max_fd = fd + 1;
614 }
615 if (fd >= svc_max_pollfd)
616 svc_max_pollfd = fd + 1;
617 if (svc_max_pollfd > svc_pollfd_allocd) {
618 int i = svc_pollfd_allocd;
619 pollfd_t *tmp;
620 do {
621 svc_pollfd_allocd += POLLFD_EXTEND;
622 } while (svc_max_pollfd > svc_pollfd_allocd);
623 tmp = realloc(svc_pollfd,
624 sizeof (pollfd_t) * svc_pollfd_allocd);
625 if (tmp != NULL) {
626 svc_pollfd = tmp;
627 for (; i < svc_pollfd_allocd; i++)
628 POLLFD_CLR(i, tmp);
629 } else {
630 /*
631 * give an error message; undo fdset setting
632 * above; reset the pollfd_shrinking flag.
633 * because of this poll will not be done
634 * on these fds.
635 */
636 if (fd < FD_SETSIZE) {
637 FD_CLR(fd, &svc_fdset);
638 #if !defined(_LP64)
639 FD_CLR(fd, &_new_svc_fdset);
640 #endif
641 svc_nfds--;
642 svc_nfds_set--;
643 if (fd == (svc_max_fd - 1))
644 svc_max_fd--;
645 }
646 if (fd == (svc_max_pollfd - 1))
647 svc_max_pollfd--;
648 pollfd_shrinking = 0;
649 syslog(LOG_ERR, "add_pollfd: out of memory");
650 _exit(1);
651 }
652 }
653 svc_pollfd[fd].fd = fd;
654 svc_pollfd[fd].events = events;
655 svc_npollfds++;
656 svc_npollfds_set++;
657 }
658
659 /*
660 * the fd is still active but only the bit in fdset is cleared.
661 * do not subtract svc_nfds or svc_npollfds
662 */
663 void
664 clear_pollfd(int fd)
665 {
666 if (fd < FD_SETSIZE && FD_ISSET(fd, &svc_fdset)) {
667 FD_CLR(fd, &svc_fdset);
668 #if !defined(_LP64)
669 FD_CLR(fd, &_new_svc_fdset);
670 #endif
671 svc_nfds_set--;
672 }
673 if (fd < svc_pollfd_allocd && POLLFD_ISSET(fd, svc_pollfd)) {
674 POLLFD_CLR(fd, svc_pollfd);
675 svc_npollfds_set--;
676 }
677 }
678
679 /*
680 * sets the bit in fdset for an active fd so that poll() is done for that
681 */
682 void
683 set_pollfd(int fd, short events)
684 {
685 if (fd < FD_SETSIZE) {
686 FD_SET(fd, &svc_fdset);
687 #if !defined(_LP64)
688 FD_SET(fd, &_new_svc_fdset);
689 #endif
690 svc_nfds_set++;
691 }
692 if (fd < svc_pollfd_allocd) {
693 svc_pollfd[fd].fd = fd;
694 svc_pollfd[fd].events = events;
695 svc_npollfds_set++;
696 }
697 }
698
699 /*
700 * remove a svc_pollfd entry; it does not shrink the memory
701 */
702 static void
703 remove_pollfd(int fd)
704 {
705 clear_pollfd(fd);
706 if (fd == (svc_max_fd - 1))
707 svc_max_fd--;
708 svc_nfds--;
709 if (fd == (svc_max_pollfd - 1))
710 svc_max_pollfd--;
711 svc_npollfds--;
712 }
713
714 /*
715 * delete a svc_pollfd entry; it shrinks the memory
716 * use remove_pollfd if you do not want to shrink
717 */
718 static void
719 delete_pollfd(int fd)
720 {
721 remove_pollfd(fd);
722 if (pollfd_shrinking && svc_max_pollfd <
723 (svc_pollfd_allocd - POLLFD_SHRINK)) {
724 do {
725 svc_pollfd_allocd -= POLLFD_SHRINK;
726 } while (svc_max_pollfd < (svc_pollfd_allocd - POLLFD_SHRINK));
727 svc_pollfd = realloc(svc_pollfd,
728 sizeof (pollfd_t) * svc_pollfd_allocd);
729 if (svc_pollfd == NULL) {
730 syslog(LOG_ERR, "delete_pollfd: out of memory");
731 _exit(1);
732 }
733 }
734 }
735
736
737 /*
738 * Activate a transport handle.
739 */
740 void
741 xprt_register(const SVCXPRT *xprt)
742 {
743 int fd = xprt->xp_fd;
744 #ifdef CALLBACK
745 extern void (*_svc_getreqset_proc)();
746 #endif
747 /* VARIABLES PROTECTED BY svc_fd_lock: svc_xports, svc_fdset */
748
749 (void) rw_wrlock(&svc_fd_lock);
750 if (svc_xports == NULL) {
751 /* allocate some small amount first */
752 svc_xports = calloc(FD_INCREMENT, sizeof (SVCXPRT *));
753 if (svc_xports == NULL) {
754 syslog(LOG_ERR, "xprt_register: out of memory");
755 _exit(1);
756 }
757 nsvc_xports = FD_INCREMENT;
758
759 #ifdef CALLBACK
760 /*
761 * XXX: This code does not keep track of the server state.
762 *
763 * This provides for callback support. When a client
764 * recv's a call from another client on the server fd's,
765 * it calls _svc_getreqset_proc() which would return
766 * after serving all the server requests. Also look under
767 * clnt_dg.c and clnt_vc.c (clnt_call part of it)
768 */
769 _svc_getreqset_proc = svc_getreq_poll;
770 #endif
771 }
772
773 while (fd >= nsvc_xports) {
774 SVCXPRT **tmp_xprts = svc_xports;
775
776 /* time to expand svc_xprts */
777 tmp_xprts = realloc(svc_xports,
778 sizeof (SVCXPRT *) * (nsvc_xports + FD_INCREMENT));
779 if (tmp_xprts == NULL) {
780 syslog(LOG_ERR, "xprt_register : out of memory.");
781 _exit(1);
782 }
783
784 svc_xports = tmp_xprts;
785 (void) memset(&svc_xports[nsvc_xports], 0,
786 sizeof (SVCXPRT *) * FD_INCREMENT);
787 nsvc_xports += FD_INCREMENT;
788 }
789
790 svc_xports[fd] = (SVCXPRT *)xprt;
791
792 add_pollfd(fd, MASKVAL);
793
794 if (svc_polling) {
795 char dummy;
796
797 /*
798 * This happens only in one of the MT modes.
799 * Wake up poller.
800 */
801 (void) write(svc_pipe[1], &dummy, sizeof (dummy));
802 }
803 /*
804 * If already dispatching door based services, start
805 * dispatching TLI based services now.
806 */
807 (void) mutex_lock(&svc_door_mutex);
808 if (svc_ndoorfds > 0)
809 (void) cond_signal(&svc_door_waitcv);
810 (void) mutex_unlock(&svc_door_mutex);
811
812 if (svc_xdrs == NULL) {
813 /* allocate initial chunk */
814 svc_xdrs = calloc(FD_INCREMENT, sizeof (XDR *));
815 if (svc_xdrs != NULL)
816 nsvc_xdrs = FD_INCREMENT;
817 else {
818 syslog(LOG_ERR, "xprt_register : out of memory.");
819 _exit(1);
820 }
821 }
822 (void) rw_unlock(&svc_fd_lock);
823 }
824
825 /*
826 * De-activate a transport handle.
827 */
828 void
829 __xprt_unregister_private(const SVCXPRT *xprt, bool_t lock_not_held)
830 {
831 int fd = xprt->xp_fd;
832
833 if (lock_not_held)
834 (void) rw_wrlock(&svc_fd_lock);
835 if ((fd < nsvc_xports) && (svc_xports[fd] == xprt)) {
836 svc_xports[fd] = NULL;
837 delete_pollfd(fd);
838 }
839 if (lock_not_held)
840 (void) rw_unlock(&svc_fd_lock);
841 __svc_rm_from_xlist(&_svc_xprtlist, xprt, &xprtlist_lock);
842 }
843
844 void
845 xprt_unregister(const SVCXPRT *xprt)
846 {
847 __xprt_unregister_private(xprt, TRUE);
848 }
849
850 /* ********************** CALLOUT list related stuff ************* */
851
852 /*
853 * Add a service program to the callout list.
854 * The dispatch routine will be called when a rpc request for this
855 * program number comes in.
856 */
857 bool_t
858 svc_reg(const SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
859 void (*dispatch)(), const struct netconfig *nconf)
860 {
861 struct svc_callout *prev;
862 struct svc_callout *s, **s2;
863 struct netconfig *tnconf;
864 char *netid = NULL;
865 int flag = 0;
866
867 /* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
868
869 if (xprt->xp_netid) {
870 netid = strdup(xprt->xp_netid);
871 flag = 1;
872 } else if (nconf && nconf->nc_netid) {
873 netid = strdup(nconf->nc_netid);
874 flag = 1;
875 } else if ((tnconf = __rpcfd_to_nconf(xprt->xp_fd, xprt->xp_type))
876 != NULL) {
877 netid = strdup(tnconf->nc_netid);
878 flag = 1;
879 freenetconfigent(tnconf);
880 } /* must have been created with svc_raw_create */
881 if ((netid == NULL) && (flag == 1))
882 return (FALSE);
883
884 (void) rw_wrlock(&svc_lock);
885 if ((s = svc_find(prog, vers, &prev, netid)) != NULL_SVC) {
886 if (netid)
887 free(netid);
888 if (s->sc_dispatch == dispatch)
889 goto rpcb_it; /* he is registering another xptr */
890 (void) rw_unlock(&svc_lock);
891 return (FALSE);
892 }
893 s = malloc(sizeof (struct svc_callout));
894 if (s == NULL) {
895 if (netid)
896 free(netid);
897 (void) rw_unlock(&svc_lock);
898 return (FALSE);
899 }
900
901 s->sc_prog = prog;
902 s->sc_vers = vers;
903 s->sc_dispatch = dispatch;
904 s->sc_netid = netid;
905 s->sc_next = NULL;
906
907 /*
908 * The ordering of transports is such that the most frequently used
909 * one appears first. So add the new entry to the end of the list.
910 */
911 for (s2 = &svc_head; *s2 != NULL; s2 = &(*s2)->sc_next)
912 ;
913 *s2 = s;
914
915 if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
916 if ((((SVCXPRT *)xprt)->xp_netid = strdup(netid)) == NULL) {
917 syslog(LOG_ERR, "svc_reg : strdup failed.");
918 free(netid);
919 free(s);
920 *s2 = NULL;
921 (void) rw_unlock(&svc_lock);
922 return (FALSE);
923 }
924
925 rpcb_it:
926 (void) rw_unlock(&svc_lock);
927
928 /* now register the information with the local binder service */
929 if (nconf)
930 return (rpcb_set(prog, vers, nconf, &xprt->xp_ltaddr));
931 return (TRUE);
932 /*NOTREACHED*/
933 }
934
935 /*
936 * Remove a service program from the callout list.
937 */
938 void
939 svc_unreg(const rpcprog_t prog, const rpcvers_t vers)
940 {
941 struct svc_callout *prev;
942 struct svc_callout *s;
943
944 /* unregister the information anyway */
945 (void) rpcb_unset(prog, vers, NULL);
946
947 (void) rw_wrlock(&svc_lock);
948 while ((s = svc_find(prog, vers, &prev, NULL)) != NULL_SVC) {
949 if (prev == NULL_SVC) {
950 svc_head = s->sc_next;
951 } else {
952 prev->sc_next = s->sc_next;
953 }
954 s->sc_next = NULL_SVC;
955 if (s->sc_netid)
956 free(s->sc_netid);
957 free(s);
958 }
959 (void) rw_unlock(&svc_lock);
960 }
961
962 #ifdef PORTMAP
963 /*
964 * Add a service program to the callout list.
965 * The dispatch routine will be called when a rpc request for this
966 * program number comes in.
967 * For version 2 portmappers.
968 */
969 bool_t
970 svc_register(SVCXPRT *xprt, rpcprog_t prog, rpcvers_t vers,
971 void (*dispatch)(), int protocol)
972 {
973 struct svc_callout *prev;
974 struct svc_callout *s;
975 struct netconfig *nconf;
976 char *netid = NULL;
977 int flag = 0;
978
979 if (xprt->xp_netid) {
980 netid = strdup(xprt->xp_netid);
981 flag = 1;
982 } else if ((ioctl(xprt->xp_fd, I_FIND, "timod") > 0) && ((nconf =
983 __rpcfd_to_nconf(xprt->xp_fd, xprt->xp_type)) != NULL)) {
984 /* fill in missing netid field in SVCXPRT */
985 netid = strdup(nconf->nc_netid);
986 flag = 1;
987 freenetconfigent(nconf);
988 } /* must be svc_raw_create */
989
990 if ((netid == NULL) && (flag == 1))
991 return (FALSE);
992
993 (void) rw_wrlock(&svc_lock);
994 if ((s = svc_find(prog, vers, &prev, netid)) != NULL_SVC) {
995 if (netid)
996 free(netid);
997 if (s->sc_dispatch == dispatch)
998 goto pmap_it; /* he is registering another xptr */
999 (void) rw_unlock(&svc_lock);
1000 return (FALSE);
1001 }
1002 s = malloc(sizeof (struct svc_callout));
1003 if (s == (struct svc_callout *)0) {
1004 if (netid)
1005 free(netid);
1006 (void) rw_unlock(&svc_lock);
1007 return (FALSE);
1008 }
1009 s->sc_prog = prog;
1010 s->sc_vers = vers;
1011 s->sc_dispatch = dispatch;
1012 s->sc_netid = netid;
1013 s->sc_next = svc_head;
1014 svc_head = s;
1015
1016 if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
1017 if ((xprt->xp_netid = strdup(netid)) == NULL) {
1018 syslog(LOG_ERR, "svc_register : strdup failed.");
1019 free(netid);
1020 svc_head = s->sc_next;
1021 free(s);
1022 (void) rw_unlock(&svc_lock);
1023 return (FALSE);
1024 }
1025
1026 pmap_it:
1027 (void) rw_unlock(&svc_lock);
1028 /* now register the information with the local binder service */
1029 if (protocol)
1030 return (pmap_set(prog, vers, protocol, xprt->xp_port));
1031 return (TRUE);
1032 }
1033
1034 /*
1035 * Remove a service program from the callout list.
1036 * For version 2 portmappers.
1037 */
1038 void
1039 svc_unregister(rpcprog_t prog, rpcvers_t vers)
1040 {
1041 struct svc_callout *prev;
1042 struct svc_callout *s;
1043
1044 (void) rw_wrlock(&svc_lock);
1045 while ((s = svc_find(prog, vers, &prev, NULL)) != NULL_SVC) {
1046 if (prev == NULL_SVC) {
1047 svc_head = s->sc_next;
1048 } else {
1049 prev->sc_next = s->sc_next;
1050 }
1051 s->sc_next = NULL_SVC;
1052 if (s->sc_netid)
1053 free(s->sc_netid);
1054 free(s);
1055 /* unregister the information with the local binder service */
1056 (void) pmap_unset(prog, vers);
1057 }
1058 (void) rw_unlock(&svc_lock);
1059 }
1060 #endif /* PORTMAP */
1061
1062 /*
1063 * Search the callout list for a program number, return the callout
1064 * struct.
1065 * Also check for transport as well. Many routines such as svc_unreg
1066 * dont give any corresponding transport, so dont check for transport if
1067 * netid == NULL
1068 */
1069 static struct svc_callout *
1070 svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev, char *netid)
1071 {
1072 struct svc_callout *s, *p;
1073
1074 /* WRITE LOCK HELD ON ENTRY: svc_lock */
1075
1076 /* assert(RW_WRITE_HELD(&svc_lock)); */
1077 p = NULL_SVC;
1078 for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
1079 if (((s->sc_prog == prog) && (s->sc_vers == vers)) &&
1080 ((netid == NULL) || (s->sc_netid == NULL) ||
1081 (strcmp(netid, s->sc_netid) == 0)))
1082 break;
1083 p = s;
1084 }
1085 *prev = p;
1086 return (s);
1087 }
1088
1089
1090 /* ******************* REPLY GENERATION ROUTINES ************ */
1091
1092 /*
1093 * Send a reply to an rpc request
1094 */
1095 bool_t
1096 svc_sendreply(const SVCXPRT *xprt, const xdrproc_t xdr_results,
1097 const caddr_t xdr_location)
1098 {
1099 struct rpc_msg rply;
1100
1101 rply.rm_direction = REPLY;
1102 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1103 rply.acpted_rply.ar_verf = xprt->xp_verf;
1104 rply.acpted_rply.ar_stat = SUCCESS;
1105 rply.acpted_rply.ar_results.where = xdr_location;
1106 rply.acpted_rply.ar_results.proc = xdr_results;
1107 return (SVC_REPLY((SVCXPRT *)xprt, &rply));
1108 }
1109
1110 /*
1111 * No procedure error reply
1112 */
1113 void
1114 svcerr_noproc(const SVCXPRT *xprt)
1115 {
1116 struct rpc_msg rply;
1117
1118 rply.rm_direction = REPLY;
1119 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1120 rply.acpted_rply.ar_verf = xprt->xp_verf;
1121 rply.acpted_rply.ar_stat = PROC_UNAVAIL;
1122 SVC_REPLY((SVCXPRT *)xprt, &rply);
1123 }
1124
1125 /*
1126 * Can't decode args error reply
1127 */
1128 void
1129 svcerr_decode(const SVCXPRT *xprt)
1130 {
1131 struct rpc_msg rply;
1132
1133 rply.rm_direction = REPLY;
1134 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1135 rply.acpted_rply.ar_verf = xprt->xp_verf;
1136 rply.acpted_rply.ar_stat = GARBAGE_ARGS;
1137 SVC_REPLY((SVCXPRT *)xprt, &rply);
1138 }
1139
1140 /*
1141 * Some system error
1142 */
1143 void
1144 svcerr_systemerr(const SVCXPRT *xprt)
1145 {
1146 struct rpc_msg rply;
1147
1148 rply.rm_direction = REPLY;
1149 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1150 rply.acpted_rply.ar_verf = xprt->xp_verf;
1151 rply.acpted_rply.ar_stat = SYSTEM_ERR;
1152 SVC_REPLY((SVCXPRT *)xprt, &rply);
1153 }
1154
1155 /*
1156 * Tell RPC package to not complain about version errors to the client. This
1157 * is useful when revving broadcast protocols that sit on a fixed address.
1158 * There is really one (or should be only one) example of this kind of
1159 * protocol: the portmapper (or rpc binder).
1160 */
1161 void
1162 __svc_versquiet_on(const SVCXPRT *xprt)
1163 {
1164 /* LINTED pointer alignment */
1165 svc_flags(xprt) |= SVC_VERSQUIET;
1166 }
1167
1168 void
1169 __svc_versquiet_off(const SVCXPRT *xprt)
1170 {
1171 /* LINTED pointer alignment */
1172 svc_flags(xprt) &= ~SVC_VERSQUIET;
1173 }
1174
1175 void
1176 svc_versquiet(const SVCXPRT *xprt)
1177 {
1178 __svc_versquiet_on(xprt);
1179 }
1180
1181 int
1182 __svc_versquiet_get(const SVCXPRT *xprt)
1183 {
1184 /* LINTED pointer alignment */
1185 return (svc_flags(xprt) & SVC_VERSQUIET);
1186 }
1187
1188 /*
1189 * Authentication error reply
1190 */
1191 void
1192 svcerr_auth(const SVCXPRT *xprt, const enum auth_stat why)
1193 {
1194 struct rpc_msg rply;
1195
1196 rply.rm_direction = REPLY;
1197 rply.rm_reply.rp_stat = MSG_DENIED;
1198 rply.rjcted_rply.rj_stat = AUTH_ERROR;
1199 rply.rjcted_rply.rj_why = why;
1200 SVC_REPLY((SVCXPRT *)xprt, &rply);
1201 }
1202
1203 /*
1204 * Auth too weak error reply
1205 */
1206 void
1207 svcerr_weakauth(const SVCXPRT *xprt)
1208 {
1209 svcerr_auth(xprt, AUTH_TOOWEAK);
1210 }
1211
1212 /*
1213 * Program unavailable error reply
1214 */
1215 void
1216 svcerr_noprog(const SVCXPRT *xprt)
1217 {
1218 struct rpc_msg rply;
1219
1220 rply.rm_direction = REPLY;
1221 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1222 rply.acpted_rply.ar_verf = xprt->xp_verf;
1223 rply.acpted_rply.ar_stat = PROG_UNAVAIL;
1224 SVC_REPLY((SVCXPRT *)xprt, &rply);
1225 }
1226
1227 /*
1228 * Program version mismatch error reply
1229 */
1230 void
1231 svcerr_progvers(const SVCXPRT *xprt, const rpcvers_t low_vers,
1232 const rpcvers_t high_vers)
1233 {
1234 struct rpc_msg rply;
1235
1236 rply.rm_direction = REPLY;
1237 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1238 rply.acpted_rply.ar_verf = xprt->xp_verf;
1239 rply.acpted_rply.ar_stat = PROG_MISMATCH;
1240 rply.acpted_rply.ar_vers.low = low_vers;
1241 rply.acpted_rply.ar_vers.high = high_vers;
1242 SVC_REPLY((SVCXPRT *)xprt, &rply);
1243 }
1244
1245 /* ******************* SERVER INPUT STUFF ******************* */
1246
1247 /*
1248 * Get server side input from some transport.
1249 *
1250 * Statement of authentication parameters management:
1251 * This function owns and manages all authentication parameters, specifically
1252 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
1253 * the "cooked" credentials (rqst->rq_clntcred).
1254 * However, this function does not know the structure of the cooked
1255 * credentials, so it make the following assumptions:
1256 * a) the structure is contiguous (no pointers), and
1257 * b) the cred structure size does not exceed RQCRED_SIZE bytes.
1258 * In all events, all three parameters are freed upon exit from this routine.
1259 * The storage is trivially management on the call stack in user land, but
1260 * is mallocated in kernel land.
1261 */
1262
1263 void
1264 svc_getreq(int rdfds)
1265 {
1266 fd_set readfds;
1267
1268 FD_ZERO(&readfds);
1269 readfds.fds_bits[0] = rdfds;
1270 svc_getreqset(&readfds);
1271 }
1272
1273 void
1274 svc_getreqset(fd_set *readfds)
1275 {
1276 int i;
1277
1278 for (i = 0; i < svc_max_fd; i++) {
1279 /* fd has input waiting */
1280 if (FD_ISSET(i, readfds))
1281 svc_getreq_common(i);
1282 }
1283 }
1284
1285 void
1286 svc_getreq_poll(struct pollfd *pfdp, const int pollretval)
1287 {
1288 int i;
1289 int fds_found;
1290
1291 for (i = fds_found = 0; fds_found < pollretval; i++) {
1292 struct pollfd *p = &pfdp[i];
1293
1294 if (p->revents) {
1295 /* fd has input waiting */
1296 fds_found++;
1297 /*
1298 * We assume that this function is only called
1299 * via someone select()ing from svc_fdset or
1300 * poll()ing from svc_pollset[]. Thus it's safe
1301 * to handle the POLLNVAL event by simply turning
1302 * the corresponding bit off in svc_fdset. The
1303 * svc_pollset[] array is derived from svc_fdset
1304 * and so will also be updated eventually.
1305 *
1306 * XXX Should we do an xprt_unregister() instead?
1307 */
1308 /* Handle user callback */
1309 if (__is_a_userfd(p->fd) == TRUE) {
1310 (void) rw_rdlock(&svc_fd_lock);
1311 __svc_getreq_user(p);
1312 (void) rw_unlock(&svc_fd_lock);
1313 } else {
1314 if (p->revents & POLLNVAL) {
1315 (void) rw_wrlock(&svc_fd_lock);
1316 remove_pollfd(p->fd); /* XXX */
1317 (void) rw_unlock(&svc_fd_lock);
1318 } else {
1319 svc_getreq_common(p->fd);
1320 }
1321 }
1322 }
1323 }
1324 }
1325
1326 void
1327 svc_getreq_common(const int fd)
1328 {
1329 SVCXPRT *xprt;
1330 enum xprt_stat stat;
1331 struct rpc_msg *msg;
1332 struct svc_req *r;
1333 char *cred_area;
1334
1335 (void) rw_rdlock(&svc_fd_lock);
1336
1337 /* HANDLE USER CALLBACK */
1338 if (__is_a_userfd(fd) == TRUE) {
1339 struct pollfd virtual_fd;
1340
1341 virtual_fd.events = virtual_fd.revents = (short)0xFFFF;
1342 virtual_fd.fd = fd;
1343 __svc_getreq_user(&virtual_fd);
1344 (void) rw_unlock(&svc_fd_lock);
1345 return;
1346 }
1347
1348 /*
1349 * The transport associated with this fd could have been
1350 * removed from svc_timeout_nonblock_xprt_and_LRU, for instance.
1351 * This can happen if two or more fds get read events and are
1352 * passed to svc_getreq_poll/set, the first fd is seviced by
1353 * the dispatch routine and cleans up any dead transports. If
1354 * one of the dead transports removed is the other fd that
1355 * had a read event then svc_getreq_common() will be called with no
1356 * xprt associated with the fd that had the original read event.
1357 */
1358 if ((fd >= nsvc_xports) || (xprt = svc_xports[fd]) == NULL) {
1359 (void) rw_unlock(&svc_fd_lock);
1360 return;
1361 }
1362 (void) rw_unlock(&svc_fd_lock);
1363 /* LINTED pointer alignment */
1364 msg = SVCEXT(xprt)->msg;
1365 /* LINTED pointer alignment */
1366 r = SVCEXT(xprt)->req;
1367 /* LINTED pointer alignment */
1368 cred_area = SVCEXT(xprt)->cred_area;
1369 msg->rm_call.cb_cred.oa_base = cred_area;
1370 msg->rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
1371 r->rq_clntcred = &(cred_area[2 * MAX_AUTH_BYTES]);
1372
1373 /* receive msgs from xprtprt (support batch calls) */
1374 do {
1375 bool_t dispatch;
1376
1377 if (dispatch = SVC_RECV(xprt, msg))
1378 (void) _svc_prog_dispatch(xprt, msg, r);
1379 /*
1380 * Check if the xprt has been disconnected in a recursive call
1381 * in the service dispatch routine. If so, then break
1382 */
1383 (void) rw_rdlock(&svc_fd_lock);
1384 if (xprt != svc_xports[fd]) {
1385 (void) rw_unlock(&svc_fd_lock);
1386 break;
1387 }
1388 (void) rw_unlock(&svc_fd_lock);
1389
1390 /*
1391 * Call cleanup procedure if set.
1392 */
1393 if (__proc_cleanup_cb != NULL && dispatch)
1394 (*__proc_cleanup_cb)(xprt);
1395
1396 if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
1397 SVC_DESTROY(xprt);
1398 break;
1399 }
1400 } while (stat == XPRT_MOREREQS);
1401 }
1402
1403 int
1404 _svc_prog_dispatch(SVCXPRT *xprt, struct rpc_msg *msg, struct svc_req *r)
1405 {
1406 struct svc_callout *s;
1407 enum auth_stat why;
1408 int prog_found;
1409 rpcvers_t low_vers;
1410 rpcvers_t high_vers;
1411 void (*disp_fn)();
1412
1413 r->rq_xprt = xprt;
1414 r->rq_prog = msg->rm_call.cb_prog;
1415 r->rq_vers = msg->rm_call.cb_vers;
1416 r->rq_proc = msg->rm_call.cb_proc;
1417 r->rq_cred = msg->rm_call.cb_cred;
1418 /* LINTED pointer alignment */
1419 SVC_XP_AUTH(r->rq_xprt).svc_ah_ops = svc_auth_any_ops;
1420 /* LINTED pointer alignment */
1421 SVC_XP_AUTH(r->rq_xprt).svc_ah_private = NULL;
1422
1423 /* first authenticate the message */
1424 /* Check for null flavor and bypass these calls if possible */
1425
1426 if (msg->rm_call.cb_cred.oa_flavor == AUTH_NULL) {
1427 r->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
1428 r->rq_xprt->xp_verf.oa_length = 0;
1429 } else {
1430 bool_t no_dispatch;
1431
1432 if ((why = __gss_authenticate(r, msg,
1433 &no_dispatch)) != AUTH_OK) {
1434 svcerr_auth(xprt, why);
1435 return (0);
1436 }
1437 if (no_dispatch)
1438 return (0);
1439 }
1440 /* match message with a registered service */
1441 prog_found = FALSE;
1442 low_vers = (rpcvers_t)(0 - 1);
1443 high_vers = 0;
1444 (void) rw_rdlock(&svc_lock);
1445 for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
1446 if (s->sc_prog == r->rq_prog) {
1447 prog_found = TRUE;
1448 if (s->sc_vers == r->rq_vers) {
1449 if ((xprt->xp_netid == NULL) ||
1450 (s->sc_netid == NULL) ||
1451 (strcmp(xprt->xp_netid,
1452 s->sc_netid) == 0)) {
1453 disp_fn = (*s->sc_dispatch);
1454 (void) rw_unlock(&svc_lock);
1455 disp_fn(r, xprt);
1456 return (1);
1457 }
1458 prog_found = FALSE;
1459 }
1460 if (s->sc_vers < low_vers)
1461 low_vers = s->sc_vers;
1462 if (s->sc_vers > high_vers)
1463 high_vers = s->sc_vers;
1464 } /* found correct program */
1465 }
1466 (void) rw_unlock(&svc_lock);
1467
1468 /*
1469 * if we got here, the program or version
1470 * is not served ...
1471 */
1472 if (prog_found) {
1473 /* LINTED pointer alignment */
1474 if (!version_keepquiet(xprt))
1475 svcerr_progvers(xprt, low_vers, high_vers);
1476 } else {
1477 svcerr_noprog(xprt);
1478 }
1479 return (0);
1480 }
1481
1482 /* ******************* SVCXPRT allocation and deallocation ***************** */
1483
1484 /*
1485 * svc_xprt_alloc() - allocate a service transport handle
1486 */
1487 SVCXPRT *
1488 svc_xprt_alloc(void)
1489 {
1490 SVCXPRT *xprt = NULL;
1491 SVCXPRT_EXT *xt = NULL;
1492 SVCXPRT_LIST *xlist = NULL;
1493 struct rpc_msg *msg = NULL;
1494 struct svc_req *req = NULL;
1495 char *cred_area = NULL;
1496
1497 if ((xprt = calloc(1, sizeof (SVCXPRT))) == NULL)
1498 goto err_exit;
1499
1500 if ((xt = calloc(1, sizeof (SVCXPRT_EXT))) == NULL)
1501 goto err_exit;
1502 xprt->xp_p3 = (caddr_t)xt; /* SVCEXT(xprt) = xt */
1503
1504 if ((xlist = calloc(1, sizeof (SVCXPRT_LIST))) == NULL)
1505 goto err_exit;
1506 xt->my_xlist = xlist;
1507 xlist->xprt = xprt;
1508
1509 if ((msg = malloc(sizeof (struct rpc_msg))) == NULL)
1510 goto err_exit;
1511 xt->msg = msg;
1512
1513 if ((req = malloc(sizeof (struct svc_req))) == NULL)
1514 goto err_exit;
1515 xt->req = req;
1516
1517 if ((cred_area = malloc(2*MAX_AUTH_BYTES + RQCRED_SIZE)) == NULL)
1518 goto err_exit;
1519 xt->cred_area = cred_area;
1520
1521 /* LINTED pointer alignment */
1522 (void) mutex_init(&svc_send_mutex(xprt), USYNC_THREAD, (void *)0);
1523 return (xprt);
1524
1525 err_exit:
1526 svc_xprt_free(xprt);
1527 return (NULL);
1528 }
1529
1530
1531 /*
1532 * svc_xprt_free() - free a service handle
1533 */
1534 void
1535 svc_xprt_free(SVCXPRT *xprt)
1536 {
1537 /* LINTED pointer alignment */
1538 SVCXPRT_EXT *xt = xprt ? SVCEXT(xprt) : NULL;
1539 SVCXPRT_LIST *my_xlist = xt ? xt->my_xlist: NULL;
1540 struct rpc_msg *msg = xt ? xt->msg : NULL;
1541 struct svc_req *req = xt ? xt->req : NULL;
1542 char *cred_area = xt ? xt->cred_area : NULL;
1543
1544 if (xprt)
1545 free(xprt);
1546 if (xt)
1547 free(xt);
1548 if (my_xlist)
1549 free(my_xlist);
1550 if (msg)
1551 free(msg);
1552 if (req)
1553 free(req);
1554 if (cred_area)
1555 free(cred_area);
1556 }
1557
1558
1559 /*
1560 * svc_xprt_destroy() - free parent and child xprt list
1561 */
1562 void
1563 svc_xprt_destroy(SVCXPRT *xprt)
1564 {
1565 SVCXPRT_LIST *xlist, *xnext = NULL;
1566 int type;
1567
1568 /* LINTED pointer alignment */
1569 if (SVCEXT(xprt)->parent)
1570 /* LINTED pointer alignment */
1571 xprt = SVCEXT(xprt)->parent;
1572 /* LINTED pointer alignment */
1573 type = svc_type(xprt);
1574 /* LINTED pointer alignment */
1575 for (xlist = SVCEXT(xprt)->my_xlist; xlist != NULL; xlist = xnext) {
1576 xnext = xlist->next;
1577 xprt = xlist->xprt;
1578 switch (type) {
1579 case SVC_DGRAM:
1580 svc_dg_xprtfree(xprt);
1581 break;
1582 case SVC_RENDEZVOUS:
1583 svc_vc_xprtfree(xprt);
1584 break;
1585 case SVC_CONNECTION:
1586 svc_fd_xprtfree(xprt);
1587 break;
1588 case SVC_DOOR:
1589 svc_door_xprtfree(xprt);
1590 break;
1591 }
1592 }
1593 }
1594
1595
1596 /*
1597 * svc_copy() - make a copy of parent
1598 */
1599 SVCXPRT *
1600 svc_copy(SVCXPRT *xprt)
1601 {
1602 /* LINTED pointer alignment */
1603 switch (svc_type(xprt)) {
1604 case SVC_DGRAM:
1605 return (svc_dg_xprtcopy(xprt));
1606 case SVC_RENDEZVOUS:
1607 return (svc_vc_xprtcopy(xprt));
1608 case SVC_CONNECTION:
1609 return (svc_fd_xprtcopy(xprt));
1610 }
1611 return (NULL);
1612 }
1613
1614
1615 /*
1616 * _svc_destroy_private() - private SVC_DESTROY interface
1617 */
1618 void
1619 _svc_destroy_private(SVCXPRT *xprt)
1620 {
1621 /* LINTED pointer alignment */
1622 switch (svc_type(xprt)) {
1623 case SVC_DGRAM:
1624 _svc_dg_destroy_private(xprt);
1625 break;
1626 case SVC_RENDEZVOUS:
1627 case SVC_CONNECTION:
1628 _svc_vc_destroy_private(xprt, TRUE);
1629 break;
1630 }
1631 }
1632
1633 /*
1634 * svc_get_local_cred() - fetch local user credentials. This always
1635 * works over doors based transports. For local transports, this
1636 * does not yield correct results unless the __rpc_negotiate_uid()
1637 * call has been invoked to enable this feature.
1638 */
1639 bool_t
1640 svc_get_local_cred(SVCXPRT *xprt, svc_local_cred_t *lcred)
1641 {
1642 /* LINTED pointer alignment */
1643 if (svc_type(xprt) == SVC_DOOR)
1644 return (__svc_get_door_cred(xprt, lcred));
1645 return (__rpc_get_local_cred(xprt, lcred));
1646 }
1647
1648
1649 /* ******************* DUPLICATE ENTRY HANDLING ROUTINES ************** */
1650
1651 /*
1652 * the dup cacheing routines below provide a cache of received
1653 * transactions. rpc service routines can use this to detect
1654 * retransmissions and re-send a non-failure response. Uses a
1655 * lru scheme to find entries to get rid of entries in the cache,
1656 * though only DUP_DONE entries are placed on the lru list.
1657 * the routines were written towards development of a generic
1658 * SVC_DUP() interface, which can be expanded to encompass the
1659 * svc_dg_enablecache() routines as well. the cache is currently
1660 * private to the automounter.
1661 */
1662
1663
1664 /* dupcache header contains xprt specific information */
1665 struct dupcache {
1666 rwlock_t dc_lock;
1667 time_t dc_time;
1668 int dc_buckets;
1669 int dc_maxsz;
1670 int dc_basis;
1671 struct dupreq *dc_mru;
1672 struct dupreq **dc_hashtbl;
1673 };
1674
1675 /*
1676 * private duplicate cache request routines
1677 */
1678 static int __svc_dupcache_check(struct svc_req *, caddr_t *, uint_t *,
1679 struct dupcache *, uint32_t, uint32_t);
1680 static struct dupreq *__svc_dupcache_victim(struct dupcache *, time_t);
1681 static int __svc_dupcache_enter(struct svc_req *, struct dupreq *,
1682 struct dupcache *, uint32_t, uint32_t, time_t);
1683 static int __svc_dupcache_update(struct svc_req *, caddr_t, uint_t, int,
1684 struct dupcache *, uint32_t, uint32_t);
1685 #ifdef DUP_DEBUG
1686 static void __svc_dupcache_debug(struct dupcache *);
1687 #endif /* DUP_DEBUG */
1688
1689 /* default parameters for the dupcache */
1690 #define DUPCACHE_BUCKETS 257
1691 #define DUPCACHE_TIME 900
1692 #define DUPCACHE_MAXSZ INT_MAX
1693
1694 /*
1695 * __svc_dupcache_init(void *condition, int basis, char *xprt_cache)
1696 * initialize the duprequest cache and assign it to the xprt_cache
1697 * Use default values depending on the cache condition and basis.
1698 * return TRUE on success and FALSE on failure
1699 */
1700 bool_t
1701 __svc_dupcache_init(void *condition, int basis, char **xprt_cache)
1702 {
1703 static mutex_t initdc_lock = DEFAULTMUTEX;
1704 int i;
1705 struct dupcache *dc;
1706
1707 (void) mutex_lock(&initdc_lock);
1708 if (*xprt_cache != NULL) { /* do only once per xprt */
1709 (void) mutex_unlock(&initdc_lock);
1710 syslog(LOG_ERR,
1711 "__svc_dupcache_init: multiply defined dup cache");
1712 return (FALSE);
1713 }
1714
1715 switch (basis) {
1716 case DUPCACHE_FIXEDTIME:
1717 dc = malloc(sizeof (struct dupcache));
1718 if (dc == NULL) {
1719 (void) mutex_unlock(&initdc_lock);
1720 syslog(LOG_ERR,
1721 "__svc_dupcache_init: memory alloc failed");
1722 return (FALSE);
1723 }
1724 (void) rwlock_init(&(dc->dc_lock), USYNC_THREAD, NULL);
1725 if (condition != NULL)
1726 dc->dc_time = *((time_t *)condition);
1727 else
1728 dc->dc_time = DUPCACHE_TIME;
1729 dc->dc_buckets = DUPCACHE_BUCKETS;
1730 dc->dc_maxsz = DUPCACHE_MAXSZ;
1731 dc->dc_basis = basis;
1732 dc->dc_mru = NULL;
1733 dc->dc_hashtbl = malloc(dc->dc_buckets *
1734 sizeof (struct dupreq *));
1735 if (dc->dc_hashtbl == NULL) {
1736 free(dc);
1737 (void) mutex_unlock(&initdc_lock);
1738 syslog(LOG_ERR,
1739 "__svc_dupcache_init: memory alloc failed");
1740 return (FALSE);
1741 }
1742 for (i = 0; i < DUPCACHE_BUCKETS; i++)
1743 dc->dc_hashtbl[i] = NULL;
1744 *xprt_cache = (char *)dc;
1745 break;
1746 default:
1747 (void) mutex_unlock(&initdc_lock);
1748 syslog(LOG_ERR,
1749 "__svc_dupcache_init: undefined dup cache basis");
1750 return (FALSE);
1751 }
1752
1753 (void) mutex_unlock(&initdc_lock);
1754
1755 return (TRUE);
1756 }
1757
1758 /*
1759 * __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1760 * char *xprt_cache)
1761 * searches the request cache. Creates an entry and returns DUP_NEW if
1762 * the request is not found in the cache. If it is found, then it
1763 * returns the state of the request (in progress, drop, or done) and
1764 * also allocates, and passes back results to the user (if any) in
1765 * resp_buf, and its length in resp_bufsz. DUP_ERROR is returned on error.
1766 */
1767 int
1768 __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1769 char *xprt_cache)
1770 {
1771 uint32_t drxid, drhash;
1772 int rc;
1773 struct dupreq *dr = NULL;
1774 time_t timenow = time(NULL);
1775
1776 /* LINTED pointer alignment */
1777 struct dupcache *dc = (struct dupcache *)xprt_cache;
1778
1779 if (dc == NULL) {
1780 syslog(LOG_ERR, "__svc_dup: undefined cache");
1781 return (DUP_ERROR);
1782 }
1783
1784 /* get the xid of the request */
1785 if (SVC_CONTROL(req->rq_xprt, SVCGET_XID, (void*)&drxid) == FALSE) {
1786 syslog(LOG_ERR, "__svc_dup: xid error");
1787 return (DUP_ERROR);
1788 }
1789 drhash = drxid % dc->dc_buckets;
1790
1791 if ((rc = __svc_dupcache_check(req, resp_buf, resp_bufsz, dc, drxid,
1792 drhash)) != DUP_NEW)
1793 return (rc);
1794
1795 if ((dr = __svc_dupcache_victim(dc, timenow)) == NULL)
1796 return (DUP_ERROR);
1797
1798 if ((rc = __svc_dupcache_enter(req, dr, dc, drxid, drhash, timenow))
1799 == DUP_ERROR)
1800 return (rc);
1801
1802 return (DUP_NEW);
1803 }
1804
1805
1806
1807 /*
1808 * __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf,
1809 * uint_t *resp_bufsz,truct dupcache *dc, uint32_t drxid,
1810 * uint32_t drhash)
1811 * Checks to see whether an entry already exists in the cache. If it does
1812 * copy back into the resp_buf, if appropriate. Return the status of
1813 * the request, or DUP_NEW if the entry is not in the cache
1814 */
1815 static int
1816 __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1817 struct dupcache *dc, uint32_t drxid, uint32_t drhash)
1818 {
1819 struct dupreq *dr = NULL;
1820
1821 (void) rw_rdlock(&(dc->dc_lock));
1822 dr = dc->dc_hashtbl[drhash];
1823 while (dr != NULL) {
1824 if (dr->dr_xid == drxid &&
1825 dr->dr_proc == req->rq_proc &&
1826 dr->dr_prog == req->rq_prog &&
1827 dr->dr_vers == req->rq_vers &&
1828 dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
1829 memcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
1830 dr->dr_addr.len) == 0) { /* entry found */
1831 if (dr->dr_hash != drhash) {
1832 /* sanity check */
1833 (void) rw_unlock((&dc->dc_lock));
1834 syslog(LOG_ERR,
1835 "\n__svc_dupdone: hashing error");
1836 return (DUP_ERROR);
1837 }
1838
1839 /*
1840 * return results for requests on lru list, if
1841 * appropriate requests must be DUP_DROP or DUP_DONE
1842 * to have a result. A NULL buffer in the cache
1843 * implies no results were sent during dupdone.
1844 * A NULL buffer in the call implies not interested
1845 * in results.
1846 */
1847 if (((dr->dr_status == DUP_DONE) ||
1848 (dr->dr_status == DUP_DROP)) &&
1849 resp_buf != NULL &&
1850 dr->dr_resp.buf != NULL) {
1851 *resp_buf = malloc(dr->dr_resp.len);
1852 if (*resp_buf == NULL) {
1853 syslog(LOG_ERR,
1854 "__svc_dupcache_check: malloc failed");
1855 (void) rw_unlock(&(dc->dc_lock));
1856 return (DUP_ERROR);
1857 }
1858 (void) memset(*resp_buf, 0, dr->dr_resp.len);
1859 (void) memcpy(*resp_buf, dr->dr_resp.buf,
1860 dr->dr_resp.len);
1861 *resp_bufsz = dr->dr_resp.len;
1862 } else {
1863 /* no result */
1864 if (resp_buf)
1865 *resp_buf = NULL;
1866 if (resp_bufsz)
1867 *resp_bufsz = 0;
1868 }
1869 (void) rw_unlock(&(dc->dc_lock));
1870 return (dr->dr_status);
1871 }
1872 dr = dr->dr_chain;
1873 }
1874 (void) rw_unlock(&(dc->dc_lock));
1875 return (DUP_NEW);
1876 }
1877
1878 /*
1879 * __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
1880 * Return a victim dupreq entry to the caller, depending on cache policy.
1881 */
1882 static struct dupreq *
1883 __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
1884 {
1885 struct dupreq *dr = NULL;
1886
1887 switch (dc->dc_basis) {
1888 case DUPCACHE_FIXEDTIME:
1889 /*
1890 * The hash policy is to free up a bit of the hash
1891 * table before allocating a new entry as the victim.
1892 * Freeing up the hash table each time should split
1893 * the cost of keeping the hash table clean among threads.
1894 * Note that only DONE or DROPPED entries are on the lru
1895 * list but we do a sanity check anyway.
1896 */
1897 (void) rw_wrlock(&(dc->dc_lock));
1898 while ((dc->dc_mru) && (dr = dc->dc_mru->dr_next) &&
1899 ((timenow - dr->dr_time) > dc->dc_time)) {
1900 /* clean and then free the entry */
1901 if (dr->dr_status != DUP_DONE &&
1902 dr->dr_status != DUP_DROP) {
1903 /*
1904 * The LRU list can't contain an
1905 * entry where the status is other than
1906 * DUP_DONE or DUP_DROP.
1907 */
1908 syslog(LOG_ERR,
1909 "__svc_dupcache_victim: bad victim");
1910 #ifdef DUP_DEBUG
1911 /*
1912 * Need to hold the reader/writers lock to
1913 * print the cache info, since we already
1914 * hold the writers lock, we shall continue
1915 * calling __svc_dupcache_debug()
1916 */
1917 __svc_dupcache_debug(dc);
1918 #endif /* DUP_DEBUG */
1919 (void) rw_unlock(&(dc->dc_lock));
1920 return (NULL);
1921 }
1922 /* free buffers */
1923 if (dr->dr_resp.buf) {
1924 free(dr->dr_resp.buf);
1925 dr->dr_resp.buf = NULL;
1926 }
1927 if (dr->dr_addr.buf) {
1928 free(dr->dr_addr.buf);
1929 dr->dr_addr.buf = NULL;
1930 }
1931
1932 /* unhash the entry */
1933 if (dr->dr_chain)
1934 dr->dr_chain->dr_prevchain = dr->dr_prevchain;
1935 if (dr->dr_prevchain)
1936 dr->dr_prevchain->dr_chain = dr->dr_chain;
1937 if (dc->dc_hashtbl[dr->dr_hash] == dr)
1938 dc->dc_hashtbl[dr->dr_hash] = dr->dr_chain;
1939
1940 /* modify the lru pointers */
1941 if (dc->dc_mru == dr) {
1942 dc->dc_mru = NULL;
1943 } else {
1944 dc->dc_mru->dr_next = dr->dr_next;
1945 dr->dr_next->dr_prev = dc->dc_mru;
1946 }
1947 free(dr);
1948 dr = NULL;
1949 }
1950 (void) rw_unlock(&(dc->dc_lock));
1951
1952 /*
1953 * Allocate and return new clean entry as victim
1954 */
1955 if ((dr = malloc(sizeof (*dr))) == NULL) {
1956 syslog(LOG_ERR,
1957 "__svc_dupcache_victim: malloc failed");
1958 return (NULL);
1959 }
1960 (void) memset(dr, 0, sizeof (*dr));
1961 return (dr);
1962 default:
1963 syslog(LOG_ERR,
1964 "__svc_dupcache_victim: undefined dup cache_basis");
1965 return (NULL);
1966 }
1967 }
1968
1969 /*
1970 * __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
1971 * struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
1972 * build new duprequest entry and then insert into the cache
1973 */
1974 static int
1975 __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
1976 struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
1977 {
1978 dr->dr_xid = drxid;
1979 dr->dr_prog = req->rq_prog;
1980 dr->dr_vers = req->rq_vers;
1981 dr->dr_proc = req->rq_proc;
1982 dr->dr_addr.maxlen = req->rq_xprt->xp_rtaddr.len;
1983 dr->dr_addr.len = dr->dr_addr.maxlen;
1984 if ((dr->dr_addr.buf = malloc(dr->dr_addr.maxlen)) == NULL) {
1985 syslog(LOG_ERR, "__svc_dupcache_enter: malloc failed");
1986 free(dr);
1987 return (DUP_ERROR);
1988 }
1989 (void) memset(dr->dr_addr.buf, 0, dr->dr_addr.len);
1990 (void) memcpy(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
1991 dr->dr_addr.len);
1992 dr->dr_resp.buf = NULL;
1993 dr->dr_resp.maxlen = 0;
1994 dr->dr_resp.len = 0;
1995 dr->dr_status = DUP_INPROGRESS;
1996 dr->dr_time = timenow;
1997 dr->dr_hash = drhash; /* needed for efficient victim cleanup */
1998
1999 /* place entry at head of hash table */
2000 (void) rw_wrlock(&(dc->dc_lock));
2001 dr->dr_chain = dc->dc_hashtbl[drhash];
2002 dr->dr_prevchain = NULL;
2003 if (dc->dc_hashtbl[drhash] != NULL)
2004 dc->dc_hashtbl[drhash]->dr_prevchain = dr;
2005 dc->dc_hashtbl[drhash] = dr;
2006 (void) rw_unlock(&(dc->dc_lock));
2007 return (DUP_NEW);
2008 }
2009
2010 /*
2011 * __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2012 * int status, char *xprt_cache)
2013 * Marks the request done (DUP_DONE or DUP_DROP) and stores the response.
2014 * Only DONE and DROP requests can be marked as done. Sets the lru pointers
2015 * to make the entry the most recently used. Returns DUP_ERROR or status.
2016 */
2017 int
2018 __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2019 int status, char *xprt_cache)
2020 {
2021 uint32_t drxid, drhash;
2022 int rc;
2023
2024 /* LINTED pointer alignment */
2025 struct dupcache *dc = (struct dupcache *)xprt_cache;
2026
2027 if (dc == NULL) {
2028 syslog(LOG_ERR, "__svc_dupdone: undefined cache");
2029 return (DUP_ERROR);
2030 }
2031
2032 if (status != DUP_DONE && status != DUP_DROP) {
2033 syslog(LOG_ERR, "__svc_dupdone: invalid dupdone status");
2034 syslog(LOG_ERR, " must be DUP_DONE or DUP_DROP");
2035 return (DUP_ERROR);
2036 }
2037
2038 /* find the xid of the entry in the cache */
2039 if (SVC_CONTROL(req->rq_xprt, SVCGET_XID, (void*)&drxid) == FALSE) {
2040 syslog(LOG_ERR, "__svc_dup: xid error");
2041 return (DUP_ERROR);
2042 }
2043 drhash = drxid % dc->dc_buckets;
2044
2045 /* update the status of the entry and result buffers, if required */
2046 if ((rc = __svc_dupcache_update(req, resp_buf, resp_bufsz, status,
2047 dc, drxid, drhash)) == DUP_ERROR) {
2048 syslog(LOG_ERR, "__svc_dupdone: cache entry error");
2049 return (DUP_ERROR);
2050 }
2051
2052 return (rc);
2053 }
2054
2055 /*
2056 * __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf,
2057 * uint_t resp_bufsz, int status, struct dupcache *dc, uint32_t drxid,
2058 * uint32_t drhash)
2059 * Check if entry exists in the dupcacache. If it does, update its status
2060 * and time and also its buffer, if appropriate. Its possible, but unlikely
2061 * for DONE requests to not exist in the cache. Return DUP_ERROR or status.
2062 */
2063 static int
2064 __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2065 int status, struct dupcache *dc, uint32_t drxid, uint32_t drhash)
2066 {
2067 struct dupreq *dr = NULL;
2068 time_t timenow = time(NULL);
2069
2070 (void) rw_wrlock(&(dc->dc_lock));
2071 dr = dc->dc_hashtbl[drhash];
2072 while (dr != NULL) {
2073 if (dr->dr_xid == drxid &&
2074 dr->dr_proc == req->rq_proc &&
2075 dr->dr_prog == req->rq_prog &&
2076 dr->dr_vers == req->rq_vers &&
2077 dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
2078 memcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
2079 dr->dr_addr.len) == 0) { /* entry found */
2080 if (dr->dr_hash != drhash) {
2081 /* sanity check */
2082 (void) rw_unlock(&(dc->dc_lock));
2083 syslog(LOG_ERR,
2084 "\n__svc_dupdone: hashing error");
2085 return (DUP_ERROR);
2086 }
2087
2088 /* store the results if bufer is not NULL */
2089 if (resp_buf != NULL) {
2090 if ((dr->dr_resp.buf =
2091 malloc(resp_bufsz)) == NULL) {
2092 (void) rw_unlock(&(dc->dc_lock));
2093 syslog(LOG_ERR,
2094 "__svc_dupdone: malloc failed");
2095 return (DUP_ERROR);
2096 }
2097 (void) memset(dr->dr_resp.buf, 0, resp_bufsz);
2098 (void) memcpy(dr->dr_resp.buf, resp_buf,
2099 (uint_t)resp_bufsz);
2100 dr->dr_resp.len = resp_bufsz;
2101 }
2102
2103 /* update status and done time */
2104 dr->dr_status = status;
2105 dr->dr_time = timenow;
2106
2107 /* move the entry to the mru position */
2108 if (dc->dc_mru == NULL) {
2109 dr->dr_next = dr;
2110 dr->dr_prev = dr;
2111 } else {
2112 dr->dr_next = dc->dc_mru->dr_next;
2113 dc->dc_mru->dr_next->dr_prev = dr;
2114 dr->dr_prev = dc->dc_mru;
2115 dc->dc_mru->dr_next = dr;
2116 }
2117 dc->dc_mru = dr;
2118
2119 (void) rw_unlock(&(dc->dc_lock));
2120 return (status);
2121 }
2122 dr = dr->dr_chain;
2123 }
2124 (void) rw_unlock(&(dc->dc_lock));
2125 syslog(LOG_ERR, "__svc_dupdone: entry not in dup cache");
2126 return (DUP_ERROR);
2127 }
2128
2129 #ifdef DUP_DEBUG
2130 /*
2131 * __svc_dupcache_debug(struct dupcache *dc)
2132 * print out the hash table stuff
2133 *
2134 * This function requires the caller to hold the reader
2135 * or writer version of the duplicate request cache lock (dc_lock).
2136 */
2137 static void
2138 __svc_dupcache_debug(struct dupcache *dc)
2139 {
2140 struct dupreq *dr = NULL;
2141 int i;
2142 bool_t bval;
2143
2144 fprintf(stderr, " HASHTABLE\n");
2145 for (i = 0; i < dc->dc_buckets; i++) {
2146 bval = FALSE;
2147 dr = dc->dc_hashtbl[i];
2148 while (dr != NULL) {
2149 if (!bval) { /* ensures bucket printed only once */
2150 fprintf(stderr, " bucket : %d\n", i);
2151 bval = TRUE;
2152 }
2153 fprintf(stderr, "\txid: %u status: %d time: %ld",
2154 dr->dr_xid, dr->dr_status, dr->dr_time);
2155 fprintf(stderr, " dr: %x chain: %x prevchain: %x\n",
2156 dr, dr->dr_chain, dr->dr_prevchain);
2157 dr = dr->dr_chain;
2158 }
2159 }
2160
2161 fprintf(stderr, " LRU\n");
2162 if (dc->dc_mru) {
2163 dr = dc->dc_mru->dr_next; /* lru */
2164 while (dr != dc->dc_mru) {
2165 fprintf(stderr, "\txid: %u status : %d time : %ld",
2166 dr->dr_xid, dr->dr_status, dr->dr_time);
2167 fprintf(stderr, " dr: %x next: %x prev: %x\n",
2168 dr, dr->dr_next, dr->dr_prev);
2169 dr = dr->dr_next;
2170 }
2171 fprintf(stderr, "\txid: %u status: %d time: %ld",
2172 dr->dr_xid, dr->dr_status, dr->dr_time);
2173 fprintf(stderr, " dr: %x next: %x prev: %x\n",
2174 dr, dr->dr_next, dr->dr_prev);
2175 }
2176 }
2177 #endif /* DUP_DEBUG */