import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / svc.c
blobc99b6a3d4f9e5ccb5775b1928e7514e1e797fee5
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
26 * Copyright 2017 Joyent Inc
29 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
31 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
34 * Portions of this source code were derived from Berkeley
35 * 4.3 BSD under license from the Regents of the University of
36 * California.
40 * svc.c, Server-side remote procedure call interface.
42 * There are two sets of procedures here. The xprt routines are
43 * for handling transport handles. The svc routines handle the
44 * list of service routines.
48 #include "mt.h"
49 #include "rpc_mt.h"
50 #include <assert.h>
51 #include <errno.h>
52 #include <sys/types.h>
53 #include <stropts.h>
54 #include <sys/conf.h>
55 #include <rpc/rpc.h>
56 #include <rpc/auth.h>
57 #include <rpc/pmap_clnt.h>
58 #include <sys/poll.h>
59 #include <netconfig.h>
60 #include <syslog.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <string.h>
64 #include <limits.h>
66 extern bool_t __svc_get_door_cred();
67 extern bool_t __rpc_get_local_cred();
69 SVCXPRT **svc_xports;
70 static int nsvc_xports; /* total number of svc_xports allocated */
72 XDR **svc_xdrs; /* common XDR receive area */
73 int nsvc_xdrs; /* total number of svc_xdrs allocated */
75 int __rpc_use_pollfd_done; /* to unlimit the number of connections */
78 * The services list
79 * Each entry represents a set of procedures (an rpc program).
80 * The dispatch routine takes request structs and runs the
81 * appropriate procedure.
83 static struct svc_callout {
84 struct svc_callout *sc_next;
85 rpcprog_t sc_prog;
86 rpcvers_t sc_vers;
87 char *sc_netid;
88 void (*sc_dispatch)();
89 } *svc_head;
90 extern rwlock_t svc_lock;
92 static struct svc_callout *svc_find();
93 int _svc_prog_dispatch();
94 void svc_getreq_common();
95 char *strdup();
97 extern mutex_t svc_door_mutex;
98 extern cond_t svc_door_waitcv;
99 extern int svc_ndoorfds;
100 extern SVCXPRT_LIST *_svc_xprtlist;
101 extern mutex_t xprtlist_lock;
102 extern void __svc_rm_from_xlist();
104 #if !defined(_LP64)
105 extern fd_set _new_svc_fdset;
106 #endif
109 * If the allocated array of reactor is too small, this value is used as a
110 * margin. This reduces the number of allocations.
112 #define USER_FD_INCREMENT 5
114 static void add_pollfd(int fd, short events);
115 static void remove_pollfd(int fd);
116 static void __svc_remove_input_of_fd(int fd);
119 * Data used to handle reactor:
120 * - one file descriptor we listen to,
121 * - one callback we call if the fd pops,
122 * - and a cookie passed as a parameter to the callback.
124 * The structure is an array indexed on the file descriptor. Each entry is
125 * pointing to the first element of a double-linked list of callback.
126 * only one callback may be associated to a couple (fd, event).
129 struct _svc_user_fd_head;
131 typedef struct {
132 struct _svc_user_fd_node *next;
133 struct _svc_user_fd_node *previous;
134 } _svc_user_link;
136 typedef struct _svc_user_fd_node {
137 _svc_user_link lnk;
138 svc_input_id_t id;
139 int fd;
140 unsigned int events;
141 svc_callback_t callback;
142 void* cookie;
143 } _svc_user_fd_node;
145 typedef struct _svc_user_fd_head {
146 struct _svc_user_fd_node *list;
147 unsigned int mask; /* logical OR of all sub-masks */
148 } _svc_user_fd_head;
151 /* Array of defined reactor - indexed on file descriptor */
152 static _svc_user_fd_head *svc_userfds = NULL;
154 /* current size of file descriptor */
155 static int svc_nuserfds = 0;
157 /* Mutex to ensure MT safe operations for user fds callbacks. */
158 static mutex_t svc_userfds_lock = DEFAULTMUTEX;
162 * This structure is used to have constant time alogrithms. There is an array
163 * of this structure as large as svc_nuserfds. When the user is registering a
164 * new callback, the address of the created structure is stored in a cell of
165 * this array. The address of this cell is the returned unique identifier.
167 * On removing, the id is given by the user, then we know if this cell is
168 * filled or not (with free). If it is free, we return an error. Otherwise,
169 * we can free the structure pointed by fd_node.
171 * On insertion, we use the linked list created by (first_free,
172 * next_free). In this way with a constant time computation, we can give a
173 * correct index to the user.
176 typedef struct _svc_management_user_fd {
177 bool_t free;
178 union {
179 svc_input_id_t next_free;
180 _svc_user_fd_node *fd_node;
181 } data;
182 } _svc_management_user_fd;
184 /* index to the first free elem */
185 static svc_input_id_t first_free = (svc_input_id_t)-1;
186 /* the size of this array is the same as svc_nuserfds */
187 static _svc_management_user_fd* user_fd_mgt_array = NULL;
189 /* current size of user_fd_mgt_array */
190 static int svc_nmgtuserfds = 0;
193 /* Define some macros to access data associated to registration ids. */
194 #define node_from_id(id) (user_fd_mgt_array[(int)id].data.fd_node)
195 #define is_free_id(id) (user_fd_mgt_array[(int)id].free)
197 #ifndef POLLSTANDARD
198 #define POLLSTANDARD \
199 (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| \
200 POLLWRBAND|POLLERR|POLLHUP|POLLNVAL)
201 #endif
204 * To free an Id, we set the cell as free and insert its address in the list
205 * of free cell.
208 static void
209 _svc_free_id(const svc_input_id_t id)
211 assert(((int)id >= 0) && ((int)id < svc_nmgtuserfds));
212 user_fd_mgt_array[(int)id].free = TRUE;
213 user_fd_mgt_array[(int)id].data.next_free = first_free;
214 first_free = id;
218 * To get a free cell, we just have to take it from the free linked list and
219 * set the flag to "not free". This function also allocates new memory if
220 * necessary
222 static svc_input_id_t
223 _svc_attribute_new_id(_svc_user_fd_node *node)
225 int selected_index = (int)first_free;
226 assert(node != NULL);
228 if (selected_index == -1) {
229 /* Allocate new entries */
230 int L_inOldSize = svc_nmgtuserfds;
231 int i;
232 _svc_management_user_fd *tmp;
234 svc_nmgtuserfds += USER_FD_INCREMENT;
236 tmp = reallocarray(user_fd_mgt_array, svc_nmgtuserfds,
237 sizeof (_svc_management_user_fd));
239 if (tmp == NULL) {
240 syslog(LOG_ERR, "_svc_attribute_new_id: out of memory");
241 svc_nmgtuserfds = L_inOldSize;
242 errno = ENOMEM;
243 return ((svc_input_id_t)-1);
246 user_fd_mgt_array = tmp;
248 for (i = svc_nmgtuserfds - 1; i >= L_inOldSize; i--)
249 _svc_free_id((svc_input_id_t)i);
250 selected_index = (int)first_free;
253 node->id = (svc_input_id_t)selected_index;
254 first_free = user_fd_mgt_array[selected_index].data.next_free;
256 user_fd_mgt_array[selected_index].data.fd_node = node;
257 user_fd_mgt_array[selected_index].free = FALSE;
259 return ((svc_input_id_t)selected_index);
263 * Access to a pollfd treatment. Scan all the associated callbacks that have
264 * at least one bit in their mask that masks a received event.
266 * If event POLLNVAL is received, we check that one callback processes it, if
267 * not, then remove the file descriptor from the poll. If there is one, let
268 * the user do the work.
270 void
271 __svc_getreq_user(struct pollfd *pfd)
273 int fd = pfd->fd;
274 short revents = pfd->revents;
275 bool_t invalHandled = FALSE;
276 _svc_user_fd_node *node;
278 (void) mutex_lock(&svc_userfds_lock);
280 if ((fd < 0) || (fd >= svc_nuserfds)) {
281 (void) mutex_unlock(&svc_userfds_lock);
282 return;
285 node = svc_userfds[fd].list;
287 /* check if at least one mask fits */
288 if (0 == (revents & svc_userfds[fd].mask)) {
289 (void) mutex_unlock(&svc_userfds_lock);
290 return;
293 while ((svc_userfds[fd].mask != 0) && (node != NULL)) {
295 * If one of the received events maps the ones the node listens
296 * to
298 _svc_user_fd_node *next = node->lnk.next;
300 if (node->callback != NULL) {
301 if (node->events & revents) {
302 if (revents & POLLNVAL) {
303 invalHandled = TRUE;
307 * The lock must be released before calling the
308 * user function, as this function can call
309 * svc_remove_input() for example.
311 (void) mutex_unlock(&svc_userfds_lock);
312 node->callback(node->id, node->fd,
313 node->events & revents, node->cookie);
315 * Do not use the node structure anymore, as it
316 * could have been deallocated by the previous
317 * callback.
319 (void) mutex_lock(&svc_userfds_lock);
322 node = next;
325 if ((revents & POLLNVAL) && !invalHandled)
326 __svc_remove_input_of_fd(fd);
327 (void) mutex_unlock(&svc_userfds_lock);
332 * Check if a file descriptor is associated with a user reactor.
333 * To do this, just check that the array indexed on fd has a non-void linked
334 * list (ie. first element is not NULL)
336 bool_t
337 __is_a_userfd(int fd)
339 /* Checks argument */
340 if ((fd < 0) || (fd >= svc_nuserfds))
341 return (FALSE);
342 return ((svc_userfds[fd].mask == 0x0000)? FALSE:TRUE);
345 /* free everything concerning user fd */
346 /* used in svc_run.c => no static */
348 void
349 __destroy_userfd(void)
351 int one_fd;
352 /* Clean user fd */
353 if (svc_userfds != NULL) {
354 for (one_fd = 0; one_fd < svc_nuserfds; one_fd++) {
355 _svc_user_fd_node *node;
357 node = svc_userfds[one_fd].list;
358 while (node != NULL) {
359 _svc_user_fd_node *tmp = node;
360 _svc_free_id(node->id);
361 node = node->lnk.next;
362 free(tmp);
366 free(user_fd_mgt_array);
367 user_fd_mgt_array = NULL;
368 first_free = (svc_input_id_t)-1;
370 free(svc_userfds);
371 svc_userfds = NULL;
372 svc_nuserfds = 0;
377 * Remove all the callback associated with a fd => useful when the fd is
378 * closed for instance
380 static void
381 __svc_remove_input_of_fd(int fd)
383 _svc_user_fd_node **pnode;
384 _svc_user_fd_node *tmp;
386 if ((fd < 0) || (fd >= svc_nuserfds))
387 return;
389 pnode = &svc_userfds[fd].list;
390 while ((tmp = *pnode) != NULL) {
391 *pnode = tmp->lnk.next;
393 _svc_free_id(tmp->id);
394 free(tmp);
397 svc_userfds[fd].mask = 0;
401 * Allow user to add an fd in the poll list. If it does not succeed, return
402 * -1. Otherwise, return a svc_id
405 svc_input_id_t
406 svc_add_input(int user_fd, unsigned int events,
407 svc_callback_t user_callback, void *cookie)
409 _svc_user_fd_node *new_node;
411 if (user_fd < 0) {
412 errno = EINVAL;
413 return ((svc_input_id_t)-1);
416 if ((events == 0x0000) ||
417 (events & ~(POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND|\
418 POLLWRBAND|POLLERR|POLLHUP|POLLNVAL))) {
419 errno = EINVAL;
420 return ((svc_input_id_t)-1);
423 (void) mutex_lock(&svc_userfds_lock);
425 if ((user_fd < svc_nuserfds) &&
426 (svc_userfds[user_fd].mask & events) != 0) {
427 /* Already registrated call-back */
428 errno = EEXIST;
429 (void) mutex_unlock(&svc_userfds_lock);
430 return ((svc_input_id_t)-1);
433 /* Handle memory allocation. */
434 if (user_fd >= svc_nuserfds) {
435 int oldSize = svc_nuserfds;
436 int i;
437 _svc_user_fd_head *tmp;
439 svc_nuserfds = (user_fd + 1) + USER_FD_INCREMENT;
441 tmp = reallocarray(svc_userfds, svc_nuserfds,
442 sizeof (_svc_user_fd_head));
444 if (tmp == NULL) {
445 syslog(LOG_ERR, "svc_add_input: out of memory");
446 svc_nuserfds = oldSize;
447 errno = ENOMEM;
448 (void) mutex_unlock(&svc_userfds_lock);
449 return ((svc_input_id_t)-1);
452 svc_userfds = tmp;
454 for (i = oldSize; i < svc_nuserfds; i++) {
455 svc_userfds[i].list = NULL;
456 svc_userfds[i].mask = 0;
460 new_node = malloc(sizeof (_svc_user_fd_node));
461 if (new_node == NULL) {
462 syslog(LOG_ERR, "svc_add_input: out of memory");
463 errno = ENOMEM;
464 (void) mutex_unlock(&svc_userfds_lock);
465 return ((svc_input_id_t)-1);
468 /* create a new node */
469 new_node->fd = user_fd;
470 new_node->events = events;
471 new_node->callback = user_callback;
472 new_node->cookie = cookie;
474 if (_svc_attribute_new_id(new_node) == -1) {
475 (void) mutex_unlock(&svc_userfds_lock);
476 free(new_node);
477 return ((svc_input_id_t)-1);
480 /* Add the new element at the beginning of the list. */
481 if (svc_userfds[user_fd].list != NULL)
482 svc_userfds[user_fd].list->lnk.previous = new_node;
483 new_node->lnk.next = svc_userfds[user_fd].list;
484 new_node->lnk.previous = NULL;
486 svc_userfds[user_fd].list = new_node;
488 /* refresh global mask for this file desciptor */
489 svc_userfds[user_fd].mask |= events;
491 /* refresh mask for the poll */
492 add_pollfd(user_fd, (svc_userfds[user_fd].mask));
494 (void) mutex_unlock(&svc_userfds_lock);
495 return (new_node->id);
499 svc_remove_input(svc_input_id_t id)
501 _svc_user_fd_node* node;
502 _svc_user_fd_node* next;
503 _svc_user_fd_node* previous;
504 int fd; /* caching optim */
506 (void) mutex_lock(&svc_userfds_lock);
508 /* Immediately update data for id management */
509 if (user_fd_mgt_array == NULL || id >= svc_nmgtuserfds ||
510 is_free_id(id)) {
511 errno = EINVAL;
512 (void) mutex_unlock(&svc_userfds_lock);
513 return (-1);
516 node = node_from_id(id);
517 assert(node != NULL);
519 _svc_free_id(id);
520 next = node->lnk.next;
521 previous = node->lnk.previous;
522 fd = node->fd; /* caching optim */
524 /* Remove this node from the list. */
525 if (previous != NULL) {
526 previous->lnk.next = next;
527 } else {
528 assert(svc_userfds[fd].list == node);
529 svc_userfds[fd].list = next;
531 if (next != NULL)
532 next->lnk.previous = previous;
534 /* Remove the node flags from the global mask */
535 svc_userfds[fd].mask ^= node->events;
537 free(node);
538 if (svc_userfds[fd].mask == 0) {
539 assert(svc_userfds[fd].list == NULL);
540 remove_pollfd(fd);
541 } else {
542 assert(svc_userfds[fd].list != NULL);
544 /* <=> CLEAN NEEDED TO SHRINK MEMORY USAGE */
546 (void) mutex_unlock(&svc_userfds_lock);
547 return (0);
551 * Provides default service-side functions for authentication flavors
552 * that do not use all the fields in struct svc_auth_ops.
555 /*ARGSUSED*/
556 static int
557 authany_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xfunc, caddr_t xwhere)
559 return (*xfunc)(xdrs, xwhere);
562 struct svc_auth_ops svc_auth_any_ops = {
563 authany_wrap,
564 authany_wrap,
568 * Return pointer to server authentication structure.
570 SVCAUTH *
571 __svc_get_svcauth(SVCXPRT *xprt)
573 /* LINTED pointer alignment */
574 return (&SVC_XP_AUTH(xprt));
578 * A callback routine to cleanup after a procedure is executed.
580 void (*__proc_cleanup_cb)() = NULL;
582 void *
583 __svc_set_proc_cleanup_cb(void *cb)
585 void *tmp = (void *)__proc_cleanup_cb;
587 __proc_cleanup_cb = (void (*)())cb;
588 return (tmp);
591 /* *************** SVCXPRT related stuff **************** */
594 static int pollfd_shrinking = 1;
598 * Add fd to svc_pollfd
600 static void
601 add_pollfd(int fd, short events)
603 if (fd < FD_SETSIZE) {
604 FD_SET(fd, &svc_fdset);
605 #if !defined(_LP64)
606 FD_SET(fd, &_new_svc_fdset);
607 #endif
608 svc_nfds++;
609 svc_nfds_set++;
610 if (fd >= svc_max_fd)
611 svc_max_fd = fd + 1;
613 if (fd >= svc_max_pollfd)
614 svc_max_pollfd = fd + 1;
615 if (svc_max_pollfd > svc_pollfd_allocd) {
616 int i = svc_pollfd_allocd;
617 pollfd_t *tmp;
618 do {
619 svc_pollfd_allocd += POLLFD_EXTEND;
620 } while (svc_max_pollfd > svc_pollfd_allocd);
621 tmp = reallocarray(svc_pollfd, svc_pollfd_allocd,
622 sizeof (pollfd_t));
623 if (tmp != NULL) {
624 svc_pollfd = tmp;
625 for (; i < svc_pollfd_allocd; i++)
626 POLLFD_CLR(i, tmp);
627 } else {
629 * give an error message; undo fdset setting
630 * above; reset the pollfd_shrinking flag.
631 * because of this poll will not be done
632 * on these fds.
634 if (fd < FD_SETSIZE) {
635 FD_CLR(fd, &svc_fdset);
636 #if !defined(_LP64)
637 FD_CLR(fd, &_new_svc_fdset);
638 #endif
639 svc_nfds--;
640 svc_nfds_set--;
641 if (fd == (svc_max_fd - 1))
642 svc_max_fd--;
644 if (fd == (svc_max_pollfd - 1))
645 svc_max_pollfd--;
646 pollfd_shrinking = 0;
647 syslog(LOG_ERR, "add_pollfd: out of memory");
648 _exit(1);
651 svc_pollfd[fd].fd = fd;
652 svc_pollfd[fd].events = events;
653 svc_npollfds++;
654 svc_npollfds_set++;
658 * the fd is still active but only the bit in fdset is cleared.
659 * do not subtract svc_nfds or svc_npollfds
661 void
662 clear_pollfd(int fd)
664 if (fd < FD_SETSIZE && FD_ISSET(fd, &svc_fdset)) {
665 FD_CLR(fd, &svc_fdset);
666 #if !defined(_LP64)
667 FD_CLR(fd, &_new_svc_fdset);
668 #endif
669 svc_nfds_set--;
671 if (fd < svc_pollfd_allocd && POLLFD_ISSET(fd, svc_pollfd)) {
672 POLLFD_CLR(fd, svc_pollfd);
673 svc_npollfds_set--;
678 * sets the bit in fdset for an active fd so that poll() is done for that
680 void
681 set_pollfd(int fd, short events)
683 if (fd < FD_SETSIZE) {
684 FD_SET(fd, &svc_fdset);
685 #if !defined(_LP64)
686 FD_SET(fd, &_new_svc_fdset);
687 #endif
688 svc_nfds_set++;
690 if (fd < svc_pollfd_allocd) {
691 svc_pollfd[fd].fd = fd;
692 svc_pollfd[fd].events = events;
693 svc_npollfds_set++;
698 * remove a svc_pollfd entry; it does not shrink the memory
700 static void
701 remove_pollfd(int fd)
703 clear_pollfd(fd);
704 if (fd == (svc_max_fd - 1))
705 svc_max_fd--;
706 svc_nfds--;
707 if (fd == (svc_max_pollfd - 1))
708 svc_max_pollfd--;
709 svc_npollfds--;
713 * delete a svc_pollfd entry; it shrinks the memory
714 * use remove_pollfd if you do not want to shrink
716 static void
717 delete_pollfd(int fd)
719 remove_pollfd(fd);
720 if (pollfd_shrinking && svc_max_pollfd <
721 (svc_pollfd_allocd - POLLFD_SHRINK)) {
722 do {
723 svc_pollfd_allocd -= POLLFD_SHRINK;
724 } while (svc_max_pollfd < (svc_pollfd_allocd - POLLFD_SHRINK));
725 svc_pollfd = reallocarray(svc_pollfd, svc_pollfd_allocd,
726 sizeof (pollfd_t));
727 if (svc_pollfd == NULL) {
728 syslog(LOG_ERR, "delete_pollfd: out of memory");
729 _exit(1);
736 * Activate a transport handle.
738 void
739 xprt_register(const SVCXPRT *xprt)
741 int fd = xprt->xp_fd;
742 #ifdef CALLBACK
743 extern void (*_svc_getreqset_proc)();
744 #endif
745 /* VARIABLES PROTECTED BY svc_fd_lock: svc_xports, svc_fdset */
747 (void) rw_wrlock(&svc_fd_lock);
748 if (svc_xports == NULL) {
749 /* allocate some small amount first */
750 svc_xports = calloc(FD_INCREMENT, sizeof (SVCXPRT *));
751 if (svc_xports == NULL) {
752 syslog(LOG_ERR, "xprt_register: out of memory");
753 _exit(1);
755 nsvc_xports = FD_INCREMENT;
757 #ifdef CALLBACK
759 * XXX: This code does not keep track of the server state.
761 * This provides for callback support. When a client
762 * recv's a call from another client on the server fd's,
763 * it calls _svc_getreqset_proc() which would return
764 * after serving all the server requests. Also look under
765 * clnt_dg.c and clnt_vc.c (clnt_call part of it)
767 _svc_getreqset_proc = svc_getreq_poll;
768 #endif
771 while (fd >= nsvc_xports) {
772 SVCXPRT **tmp_xprts = svc_xports;
774 /* time to expand svc_xprts */
775 tmp_xprts = reallocarray(svc_xports,
776 nsvc_xports + FD_INCREMENT, sizeof (SVCXPRT *));
777 if (tmp_xprts == NULL) {
778 syslog(LOG_ERR, "xprt_register : out of memory.");
779 _exit(1);
782 svc_xports = tmp_xprts;
783 (void) memset(&svc_xports[nsvc_xports], 0,
784 sizeof (SVCXPRT *) * FD_INCREMENT);
785 nsvc_xports += FD_INCREMENT;
788 svc_xports[fd] = (SVCXPRT *)xprt;
790 add_pollfd(fd, MASKVAL);
792 if (svc_polling) {
793 char dummy;
796 * This happens only in one of the MT modes.
797 * Wake up poller.
799 (void) write(svc_pipe[1], &dummy, sizeof (dummy));
802 * If already dispatching door based services, start
803 * dispatching TLI based services now.
805 (void) mutex_lock(&svc_door_mutex);
806 if (svc_ndoorfds > 0)
807 (void) cond_signal(&svc_door_waitcv);
808 (void) mutex_unlock(&svc_door_mutex);
810 if (svc_xdrs == NULL) {
811 /* allocate initial chunk */
812 svc_xdrs = calloc(FD_INCREMENT, sizeof (XDR *));
813 if (svc_xdrs != NULL)
814 nsvc_xdrs = FD_INCREMENT;
815 else {
816 syslog(LOG_ERR, "xprt_register : out of memory.");
817 _exit(1);
820 (void) rw_unlock(&svc_fd_lock);
824 * De-activate a transport handle.
826 void
827 __xprt_unregister_private(const SVCXPRT *xprt, bool_t lock_not_held)
829 int fd = xprt->xp_fd;
831 if (lock_not_held)
832 (void) rw_wrlock(&svc_fd_lock);
833 if ((fd < nsvc_xports) && (svc_xports[fd] == xprt)) {
834 svc_xports[fd] = NULL;
835 delete_pollfd(fd);
837 if (lock_not_held)
838 (void) rw_unlock(&svc_fd_lock);
839 __svc_rm_from_xlist(&_svc_xprtlist, xprt, &xprtlist_lock);
842 void
843 xprt_unregister(const SVCXPRT *xprt)
845 __xprt_unregister_private(xprt, TRUE);
848 /* ********************** CALLOUT list related stuff ************* */
851 * Add a service program to the callout list.
852 * The dispatch routine will be called when a rpc request for this
853 * program number comes in.
855 bool_t
856 svc_reg(const SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
857 void (*dispatch)(), const struct netconfig *nconf)
859 struct svc_callout *prev;
860 struct svc_callout *s, **s2;
861 struct netconfig *tnconf;
862 char *netid = NULL;
863 int flag = 0;
865 /* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
867 if (xprt->xp_netid) {
868 netid = strdup(xprt->xp_netid);
869 flag = 1;
870 } else if (nconf && nconf->nc_netid) {
871 netid = strdup(nconf->nc_netid);
872 flag = 1;
873 } else if ((tnconf = __rpcfd_to_nconf(xprt->xp_fd, xprt->xp_type))
874 != NULL) {
875 netid = strdup(tnconf->nc_netid);
876 flag = 1;
877 freenetconfigent(tnconf);
878 } /* must have been created with svc_raw_create */
879 if ((netid == NULL) && (flag == 1))
880 return (FALSE);
882 (void) rw_wrlock(&svc_lock);
883 if ((s = svc_find(prog, vers, &prev, netid)) != NULL) {
884 free(netid);
885 if (s->sc_dispatch == dispatch)
886 goto rpcb_it; /* it is registering another xptr */
887 (void) rw_unlock(&svc_lock);
888 return (FALSE);
890 s = malloc(sizeof (struct svc_callout));
891 if (s == NULL) {
892 free(netid);
893 (void) rw_unlock(&svc_lock);
894 return (FALSE);
897 s->sc_prog = prog;
898 s->sc_vers = vers;
899 s->sc_dispatch = dispatch;
900 s->sc_netid = netid;
901 s->sc_next = NULL;
904 * The ordering of transports is such that the most frequently used
905 * one appears first. So add the new entry to the end of the list.
907 for (s2 = &svc_head; *s2 != NULL; s2 = &(*s2)->sc_next)
909 *s2 = s;
911 if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
912 if ((((SVCXPRT *)xprt)->xp_netid = strdup(netid)) == NULL) {
913 syslog(LOG_ERR, "svc_reg : strdup failed.");
914 free(netid);
915 free(s);
916 *s2 = NULL;
917 (void) rw_unlock(&svc_lock);
918 return (FALSE);
921 rpcb_it:
922 (void) rw_unlock(&svc_lock);
924 /* now register the information with the local binder service */
925 if (nconf)
926 return (rpcb_set(prog, vers, nconf, &xprt->xp_ltaddr));
927 return (TRUE);
928 /*NOTREACHED*/
932 * Remove a service program from the callout list.
934 void
935 svc_unreg(const rpcprog_t prog, const rpcvers_t vers)
937 struct svc_callout *prev;
938 struct svc_callout *s;
940 /* unregister the information anyway */
941 (void) rpcb_unset(prog, vers, NULL);
943 (void) rw_wrlock(&svc_lock);
944 while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) {
945 if (prev == NULL) {
946 svc_head = s->sc_next;
947 } else {
948 prev->sc_next = s->sc_next;
950 s->sc_next = NULL;
951 free(s->sc_netid);
952 free(s);
954 (void) rw_unlock(&svc_lock);
958 * Add a service program to the callout list.
959 * The dispatch routine will be called when a rpc request for this
960 * program number comes in.
961 * For version 2 portmappers.
963 bool_t
964 svc_register(SVCXPRT *xprt, rpcprog_t prog, rpcvers_t vers,
965 void (*dispatch)(), int protocol)
967 struct svc_callout *prev;
968 struct svc_callout *s;
969 struct netconfig *nconf;
970 char *netid = NULL;
971 int flag = 0;
973 if (xprt->xp_netid) {
974 netid = strdup(xprt->xp_netid);
975 flag = 1;
976 } else if ((ioctl(xprt->xp_fd, I_FIND, "timod") > 0) && ((nconf =
977 __rpcfd_to_nconf(xprt->xp_fd, xprt->xp_type)) != NULL)) {
978 /* fill in missing netid field in SVCXPRT */
979 netid = strdup(nconf->nc_netid);
980 flag = 1;
981 freenetconfigent(nconf);
982 } /* must be svc_raw_create */
984 if ((netid == NULL) && (flag == 1))
985 return (FALSE);
987 (void) rw_wrlock(&svc_lock);
988 if ((s = svc_find(prog, vers, &prev, netid)) != NULL) {
989 free(netid);
990 if (s->sc_dispatch == dispatch)
991 goto pmap_it; /* it is registering another xptr */
992 (void) rw_unlock(&svc_lock);
993 return (FALSE);
995 s = malloc(sizeof (struct svc_callout));
996 if (s == NULL) {
997 free(netid);
998 (void) rw_unlock(&svc_lock);
999 return (FALSE);
1001 s->sc_prog = prog;
1002 s->sc_vers = vers;
1003 s->sc_dispatch = dispatch;
1004 s->sc_netid = netid;
1005 s->sc_next = svc_head;
1006 svc_head = s;
1008 if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
1009 if ((xprt->xp_netid = strdup(netid)) == NULL) {
1010 syslog(LOG_ERR, "svc_register : strdup failed.");
1011 free(netid);
1012 svc_head = s->sc_next;
1013 free(s);
1014 (void) rw_unlock(&svc_lock);
1015 return (FALSE);
1018 pmap_it:
1019 (void) rw_unlock(&svc_lock);
1020 /* now register the information with the local binder service */
1021 if (protocol)
1022 return (pmap_set(prog, vers, protocol, xprt->xp_port));
1023 return (TRUE);
1027 * Remove a service program from the callout list.
1028 * For version 2 portmappers.
1030 void
1031 svc_unregister(rpcprog_t prog, rpcvers_t vers)
1033 struct svc_callout *prev;
1034 struct svc_callout *s;
1036 (void) rw_wrlock(&svc_lock);
1037 while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) {
1038 if (prev == NULL) {
1039 svc_head = s->sc_next;
1040 } else {
1041 prev->sc_next = s->sc_next;
1043 s->sc_next = NULL;
1044 free(s->sc_netid);
1045 free(s);
1046 /* unregister the information with the local binder service */
1047 (void) pmap_unset(prog, vers);
1049 (void) rw_unlock(&svc_lock);
1053 * Search the callout list for a program number, return the callout
1054 * struct.
1055 * Also check for transport as well. Many routines such as svc_unreg
1056 * dont give any corresponding transport, so dont check for transport if
1057 * netid == NULL
1059 static struct svc_callout *
1060 svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev, char *netid)
1062 struct svc_callout *s, *p;
1064 /* WRITE LOCK HELD ON ENTRY: svc_lock */
1066 /* assert(RW_WRITE_HELD(&svc_lock)); */
1067 p = NULL;
1068 for (s = svc_head; s != NULL; s = s->sc_next) {
1069 if (((s->sc_prog == prog) && (s->sc_vers == vers)) &&
1070 ((netid == NULL) || (s->sc_netid == NULL) ||
1071 (strcmp(netid, s->sc_netid) == 0)))
1072 break;
1073 p = s;
1075 *prev = p;
1076 return (s);
1080 /* ******************* REPLY GENERATION ROUTINES ************ */
1083 * Send a reply to an rpc request
1085 bool_t
1086 svc_sendreply(const SVCXPRT *xprt, const xdrproc_t xdr_results,
1087 const caddr_t xdr_location)
1089 struct rpc_msg rply;
1091 rply.rm_direction = REPLY;
1092 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1093 rply.acpted_rply.ar_verf = xprt->xp_verf;
1094 rply.acpted_rply.ar_stat = SUCCESS;
1095 rply.acpted_rply.ar_results.where = xdr_location;
1096 rply.acpted_rply.ar_results.proc = xdr_results;
1097 return (SVC_REPLY((SVCXPRT *)xprt, &rply));
1101 * No procedure error reply
1103 void
1104 svcerr_noproc(const SVCXPRT *xprt)
1106 struct rpc_msg rply;
1108 rply.rm_direction = REPLY;
1109 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1110 rply.acpted_rply.ar_verf = xprt->xp_verf;
1111 rply.acpted_rply.ar_stat = PROC_UNAVAIL;
1112 SVC_REPLY((SVCXPRT *)xprt, &rply);
1116 * Can't decode args error reply
1118 void
1119 svcerr_decode(const SVCXPRT *xprt)
1121 struct rpc_msg rply;
1123 rply.rm_direction = REPLY;
1124 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1125 rply.acpted_rply.ar_verf = xprt->xp_verf;
1126 rply.acpted_rply.ar_stat = GARBAGE_ARGS;
1127 SVC_REPLY((SVCXPRT *)xprt, &rply);
1131 * Some system error
1133 void
1134 svcerr_systemerr(const SVCXPRT *xprt)
1136 struct rpc_msg rply;
1138 rply.rm_direction = REPLY;
1139 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1140 rply.acpted_rply.ar_verf = xprt->xp_verf;
1141 rply.acpted_rply.ar_stat = SYSTEM_ERR;
1142 SVC_REPLY((SVCXPRT *)xprt, &rply);
1146 * Tell RPC package to not complain about version errors to the client. This
1147 * is useful when revving broadcast protocols that sit on a fixed address.
1148 * There is really one (or should be only one) example of this kind of
1149 * protocol: the portmapper (or rpc binder).
1151 void
1152 __svc_versquiet_on(const SVCXPRT *xprt)
1154 /* LINTED pointer alignment */
1155 svc_flags(xprt) |= SVC_VERSQUIET;
1158 void
1159 __svc_versquiet_off(const SVCXPRT *xprt)
1161 /* LINTED pointer alignment */
1162 svc_flags(xprt) &= ~SVC_VERSQUIET;
1165 void
1166 svc_versquiet(const SVCXPRT *xprt)
1168 __svc_versquiet_on(xprt);
1172 __svc_versquiet_get(const SVCXPRT *xprt)
1174 /* LINTED pointer alignment */
1175 return (svc_flags(xprt) & SVC_VERSQUIET);
1179 * Authentication error reply
1181 void
1182 svcerr_auth(const SVCXPRT *xprt, const enum auth_stat why)
1184 struct rpc_msg rply;
1186 rply.rm_direction = REPLY;
1187 rply.rm_reply.rp_stat = MSG_DENIED;
1188 rply.rjcted_rply.rj_stat = AUTH_ERROR;
1189 rply.rjcted_rply.rj_why = why;
1190 SVC_REPLY((SVCXPRT *)xprt, &rply);
1194 * Auth too weak error reply
1196 void
1197 svcerr_weakauth(const SVCXPRT *xprt)
1199 svcerr_auth(xprt, AUTH_TOOWEAK);
1203 * Program unavailable error reply
1205 void
1206 svcerr_noprog(const SVCXPRT *xprt)
1208 struct rpc_msg rply;
1210 rply.rm_direction = REPLY;
1211 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1212 rply.acpted_rply.ar_verf = xprt->xp_verf;
1213 rply.acpted_rply.ar_stat = PROG_UNAVAIL;
1214 SVC_REPLY((SVCXPRT *)xprt, &rply);
1218 * Program version mismatch error reply
1220 void
1221 svcerr_progvers(const SVCXPRT *xprt, const rpcvers_t low_vers,
1222 const rpcvers_t high_vers)
1224 struct rpc_msg rply;
1226 rply.rm_direction = REPLY;
1227 rply.rm_reply.rp_stat = MSG_ACCEPTED;
1228 rply.acpted_rply.ar_verf = xprt->xp_verf;
1229 rply.acpted_rply.ar_stat = PROG_MISMATCH;
1230 rply.acpted_rply.ar_vers.low = low_vers;
1231 rply.acpted_rply.ar_vers.high = high_vers;
1232 SVC_REPLY((SVCXPRT *)xprt, &rply);
1235 /* ******************* SERVER INPUT STUFF ******************* */
1238 * Get server side input from some transport.
1240 * Statement of authentication parameters management:
1241 * This function owns and manages all authentication parameters, specifically
1242 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
1243 * the "cooked" credentials (rqst->rq_clntcred).
1244 * However, this function does not know the structure of the cooked
1245 * credentials, so it make the following assumptions:
1246 * a) the structure is contiguous (no pointers), and
1247 * b) the cred structure size does not exceed RQCRED_SIZE bytes.
1248 * In all events, all three parameters are freed upon exit from this routine.
1249 * The storage is trivially management on the call stack in user land, but
1250 * is mallocated in kernel land.
1253 void
1254 svc_getreq(int rdfds)
1256 fd_set readfds;
1258 FD_ZERO(&readfds);
1259 readfds.fds_bits[0] = rdfds;
1260 svc_getreqset(&readfds);
1263 void
1264 svc_getreqset(fd_set *readfds)
1266 int i;
1268 for (i = 0; i < svc_max_fd; i++) {
1269 /* fd has input waiting */
1270 if (FD_ISSET(i, readfds))
1271 svc_getreq_common(i);
1275 void
1276 svc_getreq_poll(struct pollfd *pfdp, const int pollretval)
1278 int i;
1279 int fds_found;
1281 for (i = fds_found = 0; fds_found < pollretval; i++) {
1282 struct pollfd *p = &pfdp[i];
1284 if (p->revents) {
1285 /* fd has input waiting */
1286 fds_found++;
1288 * We assume that this function is only called
1289 * via someone select()ing from svc_fdset or
1290 * poll()ing from svc_pollset[]. Thus it's safe
1291 * to handle the POLLNVAL event by simply turning
1292 * the corresponding bit off in svc_fdset. The
1293 * svc_pollset[] array is derived from svc_fdset
1294 * and so will also be updated eventually.
1296 * XXX Should we do an xprt_unregister() instead?
1298 /* Handle user callback */
1299 if (__is_a_userfd(p->fd) == TRUE) {
1300 (void) rw_rdlock(&svc_fd_lock);
1301 __svc_getreq_user(p);
1302 (void) rw_unlock(&svc_fd_lock);
1303 } else {
1304 if (p->revents & POLLNVAL) {
1305 (void) rw_wrlock(&svc_fd_lock);
1306 remove_pollfd(p->fd); /* XXX */
1307 (void) rw_unlock(&svc_fd_lock);
1308 } else {
1309 svc_getreq_common(p->fd);
1316 void
1317 svc_getreq_common(const int fd)
1319 SVCXPRT *xprt;
1320 enum xprt_stat stat;
1321 struct rpc_msg *msg;
1322 struct svc_req *r;
1323 char *cred_area;
1325 (void) rw_rdlock(&svc_fd_lock);
1327 /* HANDLE USER CALLBACK */
1328 if (__is_a_userfd(fd) == TRUE) {
1329 struct pollfd virtual_fd;
1331 virtual_fd.events = virtual_fd.revents = (short)0xFFFF;
1332 virtual_fd.fd = fd;
1333 __svc_getreq_user(&virtual_fd);
1334 (void) rw_unlock(&svc_fd_lock);
1335 return;
1339 * The transport associated with this fd could have been
1340 * removed from svc_timeout_nonblock_xprt_and_LRU, for instance.
1341 * This can happen if two or more fds get read events and are
1342 * passed to svc_getreq_poll/set, the first fd is seviced by
1343 * the dispatch routine and cleans up any dead transports. If
1344 * one of the dead transports removed is the other fd that
1345 * had a read event then svc_getreq_common() will be called with no
1346 * xprt associated with the fd that had the original read event.
1348 if ((fd >= nsvc_xports) || (xprt = svc_xports[fd]) == NULL) {
1349 (void) rw_unlock(&svc_fd_lock);
1350 return;
1352 (void) rw_unlock(&svc_fd_lock);
1353 /* LINTED pointer alignment */
1354 msg = SVCEXT(xprt)->msg;
1355 /* LINTED pointer alignment */
1356 r = SVCEXT(xprt)->req;
1357 /* LINTED pointer alignment */
1358 cred_area = SVCEXT(xprt)->cred_area;
1359 msg->rm_call.cb_cred.oa_base = cred_area;
1360 msg->rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
1361 r->rq_clntcred = &(cred_area[2 * MAX_AUTH_BYTES]);
1363 /* receive msgs from xprtprt (support batch calls) */
1364 do {
1365 bool_t dispatch;
1367 if (dispatch = SVC_RECV(xprt, msg))
1368 (void) _svc_prog_dispatch(xprt, msg, r);
1370 * Check if the xprt has been disconnected in a recursive call
1371 * in the service dispatch routine. If so, then break
1373 (void) rw_rdlock(&svc_fd_lock);
1374 if (xprt != svc_xports[fd]) {
1375 (void) rw_unlock(&svc_fd_lock);
1376 break;
1378 (void) rw_unlock(&svc_fd_lock);
1381 * Call cleanup procedure if set.
1383 if (__proc_cleanup_cb != NULL && dispatch)
1384 (*__proc_cleanup_cb)(xprt);
1386 if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
1387 SVC_DESTROY(xprt);
1388 break;
1390 } while (stat == XPRT_MOREREQS);
1394 _svc_prog_dispatch(SVCXPRT *xprt, struct rpc_msg *msg, struct svc_req *r)
1396 struct svc_callout *s;
1397 enum auth_stat why;
1398 int prog_found;
1399 rpcvers_t low_vers;
1400 rpcvers_t high_vers;
1401 void (*disp_fn)();
1403 r->rq_xprt = xprt;
1404 r->rq_prog = msg->rm_call.cb_prog;
1405 r->rq_vers = msg->rm_call.cb_vers;
1406 r->rq_proc = msg->rm_call.cb_proc;
1407 r->rq_cred = msg->rm_call.cb_cred;
1408 /* LINTED pointer alignment */
1409 SVC_XP_AUTH(r->rq_xprt).svc_ah_ops = svc_auth_any_ops;
1410 /* LINTED pointer alignment */
1411 SVC_XP_AUTH(r->rq_xprt).svc_ah_private = NULL;
1413 /* first authenticate the message */
1414 /* Check for null flavor and bypass these calls if possible */
1416 if (msg->rm_call.cb_cred.oa_flavor == AUTH_NULL) {
1417 r->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
1418 r->rq_xprt->xp_verf.oa_length = 0;
1419 } else {
1420 bool_t no_dispatch;
1422 if ((why = __gss_authenticate(r, msg,
1423 &no_dispatch)) != AUTH_OK) {
1424 svcerr_auth(xprt, why);
1425 return (0);
1427 if (no_dispatch)
1428 return (0);
1430 /* match message with a registered service */
1431 prog_found = FALSE;
1432 low_vers = (rpcvers_t)(0 - 1);
1433 high_vers = 0;
1434 (void) rw_rdlock(&svc_lock);
1435 for (s = svc_head; s != NULL; s = s->sc_next) {
1436 if (s->sc_prog == r->rq_prog) {
1437 prog_found = TRUE;
1438 if (s->sc_vers == r->rq_vers) {
1439 if ((xprt->xp_netid == NULL) ||
1440 (s->sc_netid == NULL) ||
1441 (strcmp(xprt->xp_netid,
1442 s->sc_netid) == 0)) {
1443 disp_fn = (*s->sc_dispatch);
1444 (void) rw_unlock(&svc_lock);
1445 disp_fn(r, xprt);
1446 return (1);
1448 prog_found = FALSE;
1450 if (s->sc_vers < low_vers)
1451 low_vers = s->sc_vers;
1452 if (s->sc_vers > high_vers)
1453 high_vers = s->sc_vers;
1454 } /* found correct program */
1456 (void) rw_unlock(&svc_lock);
1459 * if we got here, the program or version
1460 * is not served ...
1462 if (prog_found) {
1463 /* LINTED pointer alignment */
1464 if (!version_keepquiet(xprt))
1465 svcerr_progvers(xprt, low_vers, high_vers);
1466 } else {
1467 svcerr_noprog(xprt);
1469 return (0);
1472 /* ******************* SVCXPRT allocation and deallocation ***************** */
1475 * svc_xprt_alloc() - allocate a service transport handle
1477 SVCXPRT *
1478 svc_xprt_alloc(void)
1480 SVCXPRT *xprt = NULL;
1481 SVCXPRT_EXT *xt = NULL;
1482 SVCXPRT_LIST *xlist = NULL;
1483 struct rpc_msg *msg = NULL;
1484 struct svc_req *req = NULL;
1485 char *cred_area = NULL;
1487 if ((xprt = calloc(1, sizeof (SVCXPRT))) == NULL)
1488 goto err_exit;
1490 if ((xt = calloc(1, sizeof (SVCXPRT_EXT))) == NULL)
1491 goto err_exit;
1492 xprt->xp_p3 = (caddr_t)xt; /* SVCEXT(xprt) = xt */
1494 if ((xlist = calloc(1, sizeof (SVCXPRT_LIST))) == NULL)
1495 goto err_exit;
1496 xt->my_xlist = xlist;
1497 xlist->xprt = xprt;
1499 if ((msg = malloc(sizeof (struct rpc_msg))) == NULL)
1500 goto err_exit;
1501 xt->msg = msg;
1503 if ((req = malloc(sizeof (struct svc_req))) == NULL)
1504 goto err_exit;
1505 xt->req = req;
1507 if ((cred_area = malloc(2*MAX_AUTH_BYTES + RQCRED_SIZE)) == NULL)
1508 goto err_exit;
1509 xt->cred_area = cred_area;
1511 /* LINTED pointer alignment */
1512 (void) mutex_init(&svc_send_mutex(xprt), USYNC_THREAD, NULL);
1513 return (xprt);
1515 err_exit:
1516 svc_xprt_free(xprt);
1517 return (NULL);
1522 * svc_xprt_free() - free a service handle
1524 void
1525 svc_xprt_free(SVCXPRT *xprt)
1527 /* LINTED pointer alignment */
1528 SVCXPRT_EXT *xt = xprt ? SVCEXT(xprt) : NULL;
1529 SVCXPRT_LIST *my_xlist = xt ? xt->my_xlist: NULL;
1530 struct rpc_msg *msg = xt ? xt->msg : NULL;
1531 struct svc_req *req = xt ? xt->req : NULL;
1532 char *cred_area = xt ? xt->cred_area : NULL;
1534 free(xprt);
1535 free(xt);
1536 free(my_xlist);
1537 free(msg);
1538 free(req);
1539 free(cred_area);
1544 * svc_xprt_destroy() - free parent and child xprt list
1546 void
1547 svc_xprt_destroy(SVCXPRT *xprt)
1549 SVCXPRT_LIST *xlist, *xnext = NULL;
1550 int type;
1552 /* LINTED pointer alignment */
1553 if (SVCEXT(xprt)->parent)
1554 /* LINTED pointer alignment */
1555 xprt = SVCEXT(xprt)->parent;
1556 /* LINTED pointer alignment */
1557 type = svc_type(xprt);
1558 /* LINTED pointer alignment */
1559 for (xlist = SVCEXT(xprt)->my_xlist; xlist != NULL; xlist = xnext) {
1560 xnext = xlist->next;
1561 xprt = xlist->xprt;
1562 switch (type) {
1563 case SVC_DGRAM:
1564 svc_dg_xprtfree(xprt);
1565 break;
1566 case SVC_RENDEZVOUS:
1567 svc_vc_xprtfree(xprt);
1568 break;
1569 case SVC_CONNECTION:
1570 svc_fd_xprtfree(xprt);
1571 break;
1572 case SVC_DOOR:
1573 svc_door_xprtfree(xprt);
1574 break;
1581 * svc_copy() - make a copy of parent
1583 SVCXPRT *
1584 svc_copy(SVCXPRT *xprt)
1586 /* LINTED pointer alignment */
1587 switch (svc_type(xprt)) {
1588 case SVC_DGRAM:
1589 return (svc_dg_xprtcopy(xprt));
1590 case SVC_RENDEZVOUS:
1591 return (svc_vc_xprtcopy(xprt));
1592 case SVC_CONNECTION:
1593 return (svc_fd_xprtcopy(xprt));
1595 return (NULL);
1600 * _svc_destroy_private() - private SVC_DESTROY interface
1602 void
1603 _svc_destroy_private(SVCXPRT *xprt)
1605 /* LINTED pointer alignment */
1606 switch (svc_type(xprt)) {
1607 case SVC_DGRAM:
1608 _svc_dg_destroy_private(xprt);
1609 break;
1610 case SVC_RENDEZVOUS:
1611 case SVC_CONNECTION:
1612 _svc_vc_destroy_private(xprt, TRUE);
1613 break;
1618 * svc_get_local_cred() - fetch local user credentials. This always
1619 * works over doors based transports. For local transports, this
1620 * does not yield correct results unless the __rpc_negotiate_uid()
1621 * call has been invoked to enable this feature.
1623 bool_t
1624 svc_get_local_cred(SVCXPRT *xprt, svc_local_cred_t *lcred)
1626 /* LINTED pointer alignment */
1627 if (svc_type(xprt) == SVC_DOOR)
1628 return (__svc_get_door_cred(xprt, lcred));
1629 return (__rpc_get_local_cred(xprt, lcred));
1633 /* ******************* DUPLICATE ENTRY HANDLING ROUTINES ************** */
1636 * the dup cacheing routines below provide a cache of received
1637 * transactions. rpc service routines can use this to detect
1638 * retransmissions and re-send a non-failure response. Uses a
1639 * lru scheme to find entries to get rid of entries in the cache,
1640 * though only DUP_DONE entries are placed on the lru list.
1641 * the routines were written towards development of a generic
1642 * SVC_DUP() interface, which can be expanded to encompass the
1643 * svc_dg_enablecache() routines as well. the cache is currently
1644 * private to the automounter.
1648 /* dupcache header contains xprt specific information */
1649 struct dupcache {
1650 rwlock_t dc_lock;
1651 time_t dc_time;
1652 int dc_buckets;
1653 int dc_maxsz;
1654 int dc_basis;
1655 struct dupreq *dc_mru;
1656 struct dupreq **dc_hashtbl;
1660 * private duplicate cache request routines
1662 static int __svc_dupcache_check(struct svc_req *, caddr_t *, uint_t *,
1663 struct dupcache *, uint32_t, uint32_t);
1664 static struct dupreq *__svc_dupcache_victim(struct dupcache *, time_t);
1665 static int __svc_dupcache_enter(struct svc_req *, struct dupreq *,
1666 struct dupcache *, uint32_t, uint32_t, time_t);
1667 static int __svc_dupcache_update(struct svc_req *, caddr_t, uint_t, int,
1668 struct dupcache *, uint32_t, uint32_t);
1669 #ifdef DUP_DEBUG
1670 static void __svc_dupcache_debug(struct dupcache *);
1671 #endif /* DUP_DEBUG */
1673 /* default parameters for the dupcache */
1674 #define DUPCACHE_BUCKETS 257
1675 #define DUPCACHE_TIME 900
1676 #define DUPCACHE_MAXSZ INT_MAX
1679 * __svc_dupcache_init(void *condition, int basis, char *xprt_cache)
1680 * initialize the duprequest cache and assign it to the xprt_cache
1681 * Use default values depending on the cache condition and basis.
1682 * return TRUE on success and FALSE on failure
1684 bool_t
1685 __svc_dupcache_init(void *condition, int basis, char **xprt_cache)
1687 static mutex_t initdc_lock = DEFAULTMUTEX;
1688 int i;
1689 struct dupcache *dc;
1691 (void) mutex_lock(&initdc_lock);
1692 if (*xprt_cache != NULL) { /* do only once per xprt */
1693 (void) mutex_unlock(&initdc_lock);
1694 syslog(LOG_ERR,
1695 "__svc_dupcache_init: multiply defined dup cache");
1696 return (FALSE);
1699 switch (basis) {
1700 case DUPCACHE_FIXEDTIME:
1701 dc = malloc(sizeof (struct dupcache));
1702 if (dc == NULL) {
1703 (void) mutex_unlock(&initdc_lock);
1704 syslog(LOG_ERR,
1705 "__svc_dupcache_init: memory alloc failed");
1706 return (FALSE);
1708 (void) rwlock_init(&(dc->dc_lock), USYNC_THREAD, NULL);
1709 if (condition != NULL)
1710 dc->dc_time = *((time_t *)condition);
1711 else
1712 dc->dc_time = DUPCACHE_TIME;
1713 dc->dc_buckets = DUPCACHE_BUCKETS;
1714 dc->dc_maxsz = DUPCACHE_MAXSZ;
1715 dc->dc_basis = basis;
1716 dc->dc_mru = NULL;
1717 dc->dc_hashtbl = malloc(dc->dc_buckets *
1718 sizeof (struct dupreq *));
1719 if (dc->dc_hashtbl == NULL) {
1720 free(dc);
1721 (void) mutex_unlock(&initdc_lock);
1722 syslog(LOG_ERR,
1723 "__svc_dupcache_init: memory alloc failed");
1724 return (FALSE);
1726 for (i = 0; i < DUPCACHE_BUCKETS; i++)
1727 dc->dc_hashtbl[i] = NULL;
1728 *xprt_cache = (char *)dc;
1729 break;
1730 default:
1731 (void) mutex_unlock(&initdc_lock);
1732 syslog(LOG_ERR,
1733 "__svc_dupcache_init: undefined dup cache basis");
1734 return (FALSE);
1737 (void) mutex_unlock(&initdc_lock);
1739 return (TRUE);
1743 * __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1744 * char *xprt_cache)
1745 * searches the request cache. Creates an entry and returns DUP_NEW if
1746 * the request is not found in the cache. If it is found, then it
1747 * returns the state of the request (in progress, drop, or done) and
1748 * also allocates, and passes back results to the user (if any) in
1749 * resp_buf, and its length in resp_bufsz. DUP_ERROR is returned on error.
1752 __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1753 char *xprt_cache)
1755 uint32_t drxid, drhash;
1756 int rc;
1757 struct dupreq *dr = NULL;
1758 time_t timenow = time(NULL);
1760 /* LINTED pointer alignment */
1761 struct dupcache *dc = (struct dupcache *)xprt_cache;
1763 if (dc == NULL) {
1764 syslog(LOG_ERR, "__svc_dup: undefined cache");
1765 return (DUP_ERROR);
1768 /* get the xid of the request */
1769 if (SVC_CONTROL(req->rq_xprt, SVCGET_XID, (void*)&drxid) == FALSE) {
1770 syslog(LOG_ERR, "__svc_dup: xid error");
1771 return (DUP_ERROR);
1773 drhash = drxid % dc->dc_buckets;
1775 if ((rc = __svc_dupcache_check(req, resp_buf, resp_bufsz, dc, drxid,
1776 drhash)) != DUP_NEW)
1777 return (rc);
1779 if ((dr = __svc_dupcache_victim(dc, timenow)) == NULL)
1780 return (DUP_ERROR);
1782 if ((rc = __svc_dupcache_enter(req, dr, dc, drxid, drhash, timenow))
1783 == DUP_ERROR)
1784 return (rc);
1786 return (DUP_NEW);
1792 * __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf,
1793 * uint_t *resp_bufsz,truct dupcache *dc, uint32_t drxid,
1794 * uint32_t drhash)
1795 * Checks to see whether an entry already exists in the cache. If it does
1796 * copy back into the resp_buf, if appropriate. Return the status of
1797 * the request, or DUP_NEW if the entry is not in the cache
1799 static int
1800 __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1801 struct dupcache *dc, uint32_t drxid, uint32_t drhash)
1803 struct dupreq *dr = NULL;
1805 (void) rw_rdlock(&(dc->dc_lock));
1806 dr = dc->dc_hashtbl[drhash];
1807 while (dr != NULL) {
1808 if (dr->dr_xid == drxid &&
1809 dr->dr_proc == req->rq_proc &&
1810 dr->dr_prog == req->rq_prog &&
1811 dr->dr_vers == req->rq_vers &&
1812 dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
1813 memcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
1814 dr->dr_addr.len) == 0) { /* entry found */
1815 if (dr->dr_hash != drhash) {
1816 /* sanity check */
1817 (void) rw_unlock((&dc->dc_lock));
1818 syslog(LOG_ERR,
1819 "\n__svc_dupdone: hashing error");
1820 return (DUP_ERROR);
1824 * return results for requests on lru list, if
1825 * appropriate requests must be DUP_DROP or DUP_DONE
1826 * to have a result. A NULL buffer in the cache
1827 * implies no results were sent during dupdone.
1828 * A NULL buffer in the call implies not interested
1829 * in results.
1831 if (((dr->dr_status == DUP_DONE) ||
1832 (dr->dr_status == DUP_DROP)) &&
1833 resp_buf != NULL &&
1834 dr->dr_resp.buf != NULL) {
1835 *resp_buf = malloc(dr->dr_resp.len);
1836 if (*resp_buf == NULL) {
1837 syslog(LOG_ERR,
1838 "__svc_dupcache_check: malloc failed");
1839 (void) rw_unlock(&(dc->dc_lock));
1840 return (DUP_ERROR);
1842 (void) memset(*resp_buf, 0, dr->dr_resp.len);
1843 (void) memcpy(*resp_buf, dr->dr_resp.buf,
1844 dr->dr_resp.len);
1845 *resp_bufsz = dr->dr_resp.len;
1846 } else {
1847 /* no result */
1848 if (resp_buf)
1849 *resp_buf = NULL;
1850 if (resp_bufsz)
1851 *resp_bufsz = 0;
1853 (void) rw_unlock(&(dc->dc_lock));
1854 return (dr->dr_status);
1856 dr = dr->dr_chain;
1858 (void) rw_unlock(&(dc->dc_lock));
1859 return (DUP_NEW);
1863 * __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
1864 * Return a victim dupreq entry to the caller, depending on cache policy.
1866 static struct dupreq *
1867 __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
1869 struct dupreq *dr = NULL;
1871 switch (dc->dc_basis) {
1872 case DUPCACHE_FIXEDTIME:
1874 * The hash policy is to free up a bit of the hash
1875 * table before allocating a new entry as the victim.
1876 * Freeing up the hash table each time should split
1877 * the cost of keeping the hash table clean among threads.
1878 * Note that only DONE or DROPPED entries are on the lru
1879 * list but we do a sanity check anyway.
1881 (void) rw_wrlock(&(dc->dc_lock));
1882 while ((dc->dc_mru) && (dr = dc->dc_mru->dr_next) &&
1883 ((timenow - dr->dr_time) > dc->dc_time)) {
1884 /* clean and then free the entry */
1885 if (dr->dr_status != DUP_DONE &&
1886 dr->dr_status != DUP_DROP) {
1888 * The LRU list can't contain an
1889 * entry where the status is other than
1890 * DUP_DONE or DUP_DROP.
1892 syslog(LOG_ERR,
1893 "__svc_dupcache_victim: bad victim");
1894 #ifdef DUP_DEBUG
1896 * Need to hold the reader/writers lock to
1897 * print the cache info, since we already
1898 * hold the writers lock, we shall continue
1899 * calling __svc_dupcache_debug()
1901 __svc_dupcache_debug(dc);
1902 #endif /* DUP_DEBUG */
1903 (void) rw_unlock(&(dc->dc_lock));
1904 return (NULL);
1906 /* free buffers */
1907 if (dr->dr_resp.buf) {
1908 free(dr->dr_resp.buf);
1909 dr->dr_resp.buf = NULL;
1911 if (dr->dr_addr.buf) {
1912 free(dr->dr_addr.buf);
1913 dr->dr_addr.buf = NULL;
1916 /* unhash the entry */
1917 if (dr->dr_chain)
1918 dr->dr_chain->dr_prevchain = dr->dr_prevchain;
1919 if (dr->dr_prevchain)
1920 dr->dr_prevchain->dr_chain = dr->dr_chain;
1921 if (dc->dc_hashtbl[dr->dr_hash] == dr)
1922 dc->dc_hashtbl[dr->dr_hash] = dr->dr_chain;
1924 /* modify the lru pointers */
1925 if (dc->dc_mru == dr) {
1926 dc->dc_mru = NULL;
1927 } else {
1928 dc->dc_mru->dr_next = dr->dr_next;
1929 dr->dr_next->dr_prev = dc->dc_mru;
1931 free(dr);
1932 dr = NULL;
1934 (void) rw_unlock(&(dc->dc_lock));
1937 * Allocate and return new clean entry as victim
1939 if ((dr = malloc(sizeof (*dr))) == NULL) {
1940 syslog(LOG_ERR,
1941 "__svc_dupcache_victim: malloc failed");
1942 return (NULL);
1944 (void) memset(dr, 0, sizeof (*dr));
1945 return (dr);
1946 default:
1947 syslog(LOG_ERR,
1948 "__svc_dupcache_victim: undefined dup cache_basis");
1949 return (NULL);
1954 * __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
1955 * struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
1956 * build new duprequest entry and then insert into the cache
1958 static int
1959 __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
1960 struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
1962 dr->dr_xid = drxid;
1963 dr->dr_prog = req->rq_prog;
1964 dr->dr_vers = req->rq_vers;
1965 dr->dr_proc = req->rq_proc;
1966 dr->dr_addr.maxlen = req->rq_xprt->xp_rtaddr.len;
1967 dr->dr_addr.len = dr->dr_addr.maxlen;
1968 if ((dr->dr_addr.buf = malloc(dr->dr_addr.maxlen)) == NULL) {
1969 syslog(LOG_ERR, "__svc_dupcache_enter: malloc failed");
1970 free(dr);
1971 return (DUP_ERROR);
1973 (void) memset(dr->dr_addr.buf, 0, dr->dr_addr.len);
1974 (void) memcpy(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
1975 dr->dr_addr.len);
1976 dr->dr_resp.buf = NULL;
1977 dr->dr_resp.maxlen = 0;
1978 dr->dr_resp.len = 0;
1979 dr->dr_status = DUP_INPROGRESS;
1980 dr->dr_time = timenow;
1981 dr->dr_hash = drhash; /* needed for efficient victim cleanup */
1983 /* place entry at head of hash table */
1984 (void) rw_wrlock(&(dc->dc_lock));
1985 dr->dr_chain = dc->dc_hashtbl[drhash];
1986 dr->dr_prevchain = NULL;
1987 if (dc->dc_hashtbl[drhash] != NULL)
1988 dc->dc_hashtbl[drhash]->dr_prevchain = dr;
1989 dc->dc_hashtbl[drhash] = dr;
1990 (void) rw_unlock(&(dc->dc_lock));
1991 return (DUP_NEW);
1995 * __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
1996 * int status, char *xprt_cache)
1997 * Marks the request done (DUP_DONE or DUP_DROP) and stores the response.
1998 * Only DONE and DROP requests can be marked as done. Sets the lru pointers
1999 * to make the entry the most recently used. Returns DUP_ERROR or status.
2002 __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2003 int status, char *xprt_cache)
2005 uint32_t drxid, drhash;
2006 int rc;
2008 /* LINTED pointer alignment */
2009 struct dupcache *dc = (struct dupcache *)xprt_cache;
2011 if (dc == NULL) {
2012 syslog(LOG_ERR, "__svc_dupdone: undefined cache");
2013 return (DUP_ERROR);
2016 if (status != DUP_DONE && status != DUP_DROP) {
2017 syslog(LOG_ERR, "__svc_dupdone: invalid dupdone status");
2018 syslog(LOG_ERR, " must be DUP_DONE or DUP_DROP");
2019 return (DUP_ERROR);
2022 /* find the xid of the entry in the cache */
2023 if (SVC_CONTROL(req->rq_xprt, SVCGET_XID, (void*)&drxid) == FALSE) {
2024 syslog(LOG_ERR, "__svc_dup: xid error");
2025 return (DUP_ERROR);
2027 drhash = drxid % dc->dc_buckets;
2029 /* update the status of the entry and result buffers, if required */
2030 if ((rc = __svc_dupcache_update(req, resp_buf, resp_bufsz, status,
2031 dc, drxid, drhash)) == DUP_ERROR) {
2032 syslog(LOG_ERR, "__svc_dupdone: cache entry error");
2033 return (DUP_ERROR);
2036 return (rc);
2040 * __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf,
2041 * uint_t resp_bufsz, int status, struct dupcache *dc, uint32_t drxid,
2042 * uint32_t drhash)
2043 * Check if entry exists in the dupcacache. If it does, update its status
2044 * and time and also its buffer, if appropriate. Its possible, but unlikely
2045 * for DONE requests to not exist in the cache. Return DUP_ERROR or status.
2047 static int
2048 __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2049 int status, struct dupcache *dc, uint32_t drxid, uint32_t drhash)
2051 struct dupreq *dr = NULL;
2052 time_t timenow = time(NULL);
2054 (void) rw_wrlock(&(dc->dc_lock));
2055 dr = dc->dc_hashtbl[drhash];
2056 while (dr != NULL) {
2057 if (dr->dr_xid == drxid &&
2058 dr->dr_proc == req->rq_proc &&
2059 dr->dr_prog == req->rq_prog &&
2060 dr->dr_vers == req->rq_vers &&
2061 dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
2062 memcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
2063 dr->dr_addr.len) == 0) { /* entry found */
2064 if (dr->dr_hash != drhash) {
2065 /* sanity check */
2066 (void) rw_unlock(&(dc->dc_lock));
2067 syslog(LOG_ERR,
2068 "\n__svc_dupdone: hashing error");
2069 return (DUP_ERROR);
2072 /* store the results if bufer is not NULL */
2073 if (resp_buf != NULL) {
2074 if ((dr->dr_resp.buf =
2075 malloc(resp_bufsz)) == NULL) {
2076 (void) rw_unlock(&(dc->dc_lock));
2077 syslog(LOG_ERR,
2078 "__svc_dupdone: malloc failed");
2079 return (DUP_ERROR);
2081 (void) memset(dr->dr_resp.buf, 0, resp_bufsz);
2082 (void) memcpy(dr->dr_resp.buf, resp_buf,
2083 (uint_t)resp_bufsz);
2084 dr->dr_resp.len = resp_bufsz;
2087 /* update status and done time */
2088 dr->dr_status = status;
2089 dr->dr_time = timenow;
2091 /* move the entry to the mru position */
2092 if (dc->dc_mru == NULL) {
2093 dr->dr_next = dr;
2094 dr->dr_prev = dr;
2095 } else {
2096 dr->dr_next = dc->dc_mru->dr_next;
2097 dc->dc_mru->dr_next->dr_prev = dr;
2098 dr->dr_prev = dc->dc_mru;
2099 dc->dc_mru->dr_next = dr;
2101 dc->dc_mru = dr;
2103 (void) rw_unlock(&(dc->dc_lock));
2104 return (status);
2106 dr = dr->dr_chain;
2108 (void) rw_unlock(&(dc->dc_lock));
2109 syslog(LOG_ERR, "__svc_dupdone: entry not in dup cache");
2110 return (DUP_ERROR);
2113 #ifdef DUP_DEBUG
2115 * __svc_dupcache_debug(struct dupcache *dc)
2116 * print out the hash table stuff
2118 * This function requires the caller to hold the reader
2119 * or writer version of the duplicate request cache lock (dc_lock).
2121 static void
2122 __svc_dupcache_debug(struct dupcache *dc)
2124 struct dupreq *dr = NULL;
2125 int i;
2126 bool_t bval;
2128 fprintf(stderr, " HASHTABLE\n");
2129 for (i = 0; i < dc->dc_buckets; i++) {
2130 bval = FALSE;
2131 dr = dc->dc_hashtbl[i];
2132 while (dr != NULL) {
2133 if (!bval) { /* ensures bucket printed only once */
2134 fprintf(stderr, " bucket : %d\n", i);
2135 bval = TRUE;
2137 fprintf(stderr, "\txid: %u status: %d time: %ld",
2138 dr->dr_xid, dr->dr_status, dr->dr_time);
2139 fprintf(stderr, " dr: %x chain: %x prevchain: %x\n",
2140 dr, dr->dr_chain, dr->dr_prevchain);
2141 dr = dr->dr_chain;
2145 fprintf(stderr, " LRU\n");
2146 if (dc->dc_mru) {
2147 dr = dc->dc_mru->dr_next; /* lru */
2148 while (dr != dc->dc_mru) {
2149 fprintf(stderr, "\txid: %u status : %d time : %ld",
2150 dr->dr_xid, dr->dr_status, dr->dr_time);
2151 fprintf(stderr, " dr: %x next: %x prev: %x\n",
2152 dr, dr->dr_next, dr->dr_prev);
2153 dr = dr->dr_next;
2155 fprintf(stderr, "\txid: %u status: %d time: %ld",
2156 dr->dr_xid, dr->dr_status, dr->dr_time);
2157 fprintf(stderr, " dr: %x next: %x prev: %x\n",
2158 dr, dr->dr_next, dr->dr_prev);
2161 #endif /* DUP_DEBUG */