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