1/*	$NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2009, Sun Microsystems, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice,
10 *   this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright notice,
12 *   this list of conditions and the following disclaimer in the documentation
13 *   and/or other materials provided with the distribution.
14 * - Neither the name of Sun Microsystems, Inc. nor the names of its
15 *   contributors may be used to endorse or promote products derived
16 *   from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#if defined(LIBC_SCCS) && !defined(lint)
32static char *sccsid2 = "@(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro";
33static char *sccsid = "@(#)svc.c	2.4 88/08/11 4.0 RPCSRC";
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: stable/10/lib/libc/rpc/svc.c 318327 2017-05-15 23:13:49Z brooks $");
37
38/*
39 * svc.c, Server-side remote procedure call interface.
40 *
41 * There are two sets of procedures here.  The xprt routines are
42 * for handling transport handles.  The svc routines handle the
43 * list of service routines.
44 *
45 * Copyright (C) 1984, Sun Microsystems, Inc.
46 */
47
48#include "namespace.h"
49#include "reentrant.h"
50#include <sys/types.h>
51#include <sys/poll.h>
52#include <assert.h>
53#include <errno.h>
54#include <stdlib.h>
55#include <string.h>
56
57#include <rpc/rpc.h>
58#ifdef PORTMAP
59#include <rpc/pmap_clnt.h>
60#endif				/* PORTMAP */
61#include "un-namespace.h"
62
63#include "rpc_com.h"
64#include "mt_misc.h"
65
66#define	RQCRED_SIZE	400		/* this size is excessive */
67
68#define SVC_VERSQUIET 0x0001		/* keep quiet about vers mismatch */
69#define version_keepquiet(xp) (SVC_EXT(xp)->xp_flags & SVC_VERSQUIET)
70
71#define max(a, b) (a > b ? a : b)
72
73/*
74 * The services list
75 * Each entry represents a set of procedures (an rpc program).
76 * The dispatch routine takes request structs and runs the
77 * apropriate procedure.
78 */
79static struct svc_callout {
80	struct svc_callout *sc_next;
81	rpcprog_t	    sc_prog;
82	rpcvers_t	    sc_vers;
83	char		   *sc_netid;
84	void		    (*sc_dispatch)(struct svc_req *, SVCXPRT *);
85} *svc_head;
86
87SVCXPRT **__svc_xports;
88int __svc_maxrec;
89
90static struct svc_callout *svc_find(rpcprog_t, rpcvers_t,
91    struct svc_callout **, char *);
92static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock);
93
94/* ***************  SVCXPRT related stuff **************** */
95
96/*
97 * Activate a transport handle.
98 */
99void
100xprt_register(SVCXPRT *xprt)
101{
102	int sock;
103
104	assert(xprt != NULL);
105
106	sock = xprt->xp_fd;
107
108	rwlock_wrlock(&svc_fd_lock);
109	if (__svc_xports == NULL) {
110		__svc_xports = (SVCXPRT **)
111			mem_alloc((FD_SETSIZE + 1) * sizeof(SVCXPRT *));
112		if (__svc_xports == NULL) {
113			rwlock_unlock(&svc_fd_lock);
114			return;
115		}
116		memset(__svc_xports, '\0', (FD_SETSIZE + 1) * sizeof(SVCXPRT *));
117	}
118	if (sock < FD_SETSIZE) {
119		__svc_xports[sock] = xprt;
120		FD_SET(sock, &svc_fdset);
121		svc_maxfd = max(svc_maxfd, sock);
122	} else if (sock == FD_SETSIZE)
123		__svc_xports[sock] = xprt;
124	rwlock_unlock(&svc_fd_lock);
125}
126
127void
128xprt_unregister(SVCXPRT *xprt)
129{
130	__xprt_do_unregister(xprt, TRUE);
131}
132
133void
134__xprt_unregister_unlocked(SVCXPRT *xprt)
135{
136	__xprt_do_unregister(xprt, FALSE);
137}
138
139/*
140 * De-activate a transport handle.
141 */
142static void
143__xprt_do_unregister(SVCXPRT *xprt, bool_t dolock)
144{
145	int sock;
146
147	assert(xprt != NULL);
148
149	sock = xprt->xp_fd;
150
151	if (dolock)
152		rwlock_wrlock(&svc_fd_lock);
153	if ((sock < FD_SETSIZE) && (__svc_xports[sock] == xprt)) {
154		__svc_xports[sock] = NULL;
155		FD_CLR(sock, &svc_fdset);
156		if (sock >= svc_maxfd) {
157			for (svc_maxfd--; svc_maxfd>=0; svc_maxfd--)
158				if (__svc_xports[svc_maxfd])
159					break;
160		}
161	} else if ((sock == FD_SETSIZE) && (__svc_xports[sock] == xprt))
162		__svc_xports[sock] = NULL;
163	if (dolock)
164		rwlock_unlock(&svc_fd_lock);
165}
166
167/*
168 * Add a service program to the callout list.
169 * The dispatch routine will be called when a rpc request for this
170 * program number comes in.
171 */
172bool_t
173svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
174    void (*dispatch)(struct svc_req *, SVCXPRT *),
175    const struct netconfig *nconf)
176{
177	bool_t dummy;
178	struct svc_callout *prev;
179	struct svc_callout *s;
180	struct netconfig *tnconf;
181	char *netid = NULL;
182	int flag = 0;
183
184/* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
185
186	if (xprt->xp_netid) {
187		netid = strdup(xprt->xp_netid);
188		flag = 1;
189	} else if (nconf && nconf->nc_netid) {
190		netid = strdup(nconf->nc_netid);
191		flag = 1;
192	} else if ((tnconf = __rpcgettp(xprt->xp_fd)) != NULL) {
193		netid = strdup(tnconf->nc_netid);
194		flag = 1;
195		freenetconfigent(tnconf);
196	} /* must have been created with svc_raw_create */
197	if ((netid == NULL) && (flag == 1)) {
198		return (FALSE);
199	}
200
201	rwlock_wrlock(&svc_lock);
202	if ((s = svc_find(prog, vers, &prev, netid)) != NULL) {
203		free(netid);
204		if (s->sc_dispatch == dispatch)
205			goto rpcb_it; /* he is registering another xptr */
206		rwlock_unlock(&svc_lock);
207		return (FALSE);
208	}
209	s = mem_alloc(sizeof (struct svc_callout));
210	if (s == NULL) {
211		free(netid);
212		rwlock_unlock(&svc_lock);
213		return (FALSE);
214	}
215
216	s->sc_prog = prog;
217	s->sc_vers = vers;
218	s->sc_dispatch = dispatch;
219	s->sc_netid = netid;
220	s->sc_next = svc_head;
221	svc_head = s;
222
223	if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
224		((SVCXPRT *) xprt)->xp_netid = strdup(netid);
225
226rpcb_it:
227	rwlock_unlock(&svc_lock);
228	/* now register the information with the local binder service */
229	if (nconf) {
230		/*LINTED const castaway*/
231		dummy = rpcb_set(prog, vers, (struct netconfig *) nconf,
232		&((SVCXPRT *) xprt)->xp_ltaddr);
233		return (dummy);
234	}
235	return (TRUE);
236}
237
238/*
239 * Remove a service program from the callout list.
240 */
241void
242svc_unreg(const rpcprog_t prog, const rpcvers_t vers)
243{
244	struct svc_callout *prev;
245	struct svc_callout *s;
246
247	/* unregister the information anyway */
248	(void) rpcb_unset(prog, vers, NULL);
249	rwlock_wrlock(&svc_lock);
250	while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) {
251		if (prev == NULL) {
252			svc_head = s->sc_next;
253		} else {
254			prev->sc_next = s->sc_next;
255		}
256		s->sc_next = NULL;
257		if (s->sc_netid)
258			mem_free(s->sc_netid, sizeof (s->sc_netid) + 1);
259		mem_free(s, sizeof (struct svc_callout));
260	}
261	rwlock_unlock(&svc_lock);
262}
263
264/* ********************** CALLOUT list related stuff ************* */
265
266#ifdef PORTMAP
267/*
268 * Add a service program to the callout list.
269 * The dispatch routine will be called when a rpc request for this
270 * program number comes in.
271 */
272bool_t
273svc_register(SVCXPRT *xprt, u_long prog, u_long vers,
274    void (*dispatch)(struct svc_req *, SVCXPRT *),
275    int protocol)
276{
277	struct svc_callout *prev;
278	struct svc_callout *s;
279
280	assert(xprt != NULL);
281	assert(dispatch != NULL);
282
283	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) !=
284	    NULL) {
285		if (s->sc_dispatch == dispatch)
286			goto pmap_it;  /* he is registering another xptr */
287		return (FALSE);
288	}
289	s = mem_alloc(sizeof(struct svc_callout));
290	if (s == NULL) {
291		return (FALSE);
292	}
293	s->sc_prog = (rpcprog_t)prog;
294	s->sc_vers = (rpcvers_t)vers;
295	s->sc_dispatch = dispatch;
296	s->sc_next = svc_head;
297	svc_head = s;
298pmap_it:
299	/* now register the information with the local binder service */
300	if (protocol) {
301		return (pmap_set(prog, vers, protocol, xprt->xp_port));
302	}
303	return (TRUE);
304}
305
306/*
307 * Remove a service program from the callout list.
308 */
309void
310svc_unregister(u_long prog, u_long vers)
311{
312	struct svc_callout *prev;
313	struct svc_callout *s;
314
315	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) ==
316	    NULL)
317		return;
318	if (prev == NULL) {
319		svc_head = s->sc_next;
320	} else {
321		prev->sc_next = s->sc_next;
322	}
323	s->sc_next = NULL;
324	mem_free(s, sizeof(struct svc_callout));
325	/* now unregister the information with the local binder service */
326	(void)pmap_unset(prog, vers);
327}
328#endif				/* PORTMAP */
329
330/*
331 * Search the callout list for a program number, return the callout
332 * struct.
333 */
334static struct svc_callout *
335svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev,
336    char *netid)
337{
338	struct svc_callout *s, *p;
339
340	assert(prev != NULL);
341
342	p = NULL;
343	for (s = svc_head; s != NULL; s = s->sc_next) {
344		if (((s->sc_prog == prog) && (s->sc_vers == vers)) &&
345		    ((netid == NULL) || (s->sc_netid == NULL) ||
346		    (strcmp(netid, s->sc_netid) == 0)))
347			break;
348		p = s;
349	}
350	*prev = p;
351	return (s);
352}
353
354/* ******************* REPLY GENERATION ROUTINES  ************ */
355
356/*
357 * Send a reply to an rpc request
358 */
359bool_t
360svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results,
361    void * xdr_location)
362{
363	struct rpc_msg rply;
364
365	assert(xprt != NULL);
366
367	rply.rm_direction = REPLY;
368	rply.rm_reply.rp_stat = MSG_ACCEPTED;
369	rply.acpted_rply.ar_verf = xprt->xp_verf;
370	rply.acpted_rply.ar_stat = SUCCESS;
371	rply.acpted_rply.ar_results.where = xdr_location;
372	rply.acpted_rply.ar_results.proc = xdr_results;
373	return (SVC_REPLY(xprt, &rply));
374}
375
376/*
377 * No procedure error reply
378 */
379void
380svcerr_noproc(SVCXPRT *xprt)
381{
382	struct rpc_msg rply;
383
384	assert(xprt != NULL);
385
386	rply.rm_direction = REPLY;
387	rply.rm_reply.rp_stat = MSG_ACCEPTED;
388	rply.acpted_rply.ar_verf = xprt->xp_verf;
389	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
390	SVC_REPLY(xprt, &rply);
391}
392
393/*
394 * Can't decode args error reply
395 */
396void
397svcerr_decode(SVCXPRT *xprt)
398{
399	struct rpc_msg rply;
400
401	assert(xprt != NULL);
402
403	rply.rm_direction = REPLY;
404	rply.rm_reply.rp_stat = MSG_ACCEPTED;
405	rply.acpted_rply.ar_verf = xprt->xp_verf;
406	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
407	SVC_REPLY(xprt, &rply);
408}
409
410/*
411 * Some system error
412 */
413void
414svcerr_systemerr(SVCXPRT *xprt)
415{
416	struct rpc_msg rply;
417
418	assert(xprt != NULL);
419
420	rply.rm_direction = REPLY;
421	rply.rm_reply.rp_stat = MSG_ACCEPTED;
422	rply.acpted_rply.ar_verf = xprt->xp_verf;
423	rply.acpted_rply.ar_stat = SYSTEM_ERR;
424	SVC_REPLY(xprt, &rply);
425}
426
427#if 0
428/*
429 * Tell RPC package to not complain about version errors to the client.	 This
430 * is useful when revving broadcast protocols that sit on a fixed address.
431 * There is really one (or should be only one) example of this kind of
432 * protocol: the portmapper (or rpc binder).
433 */
434void
435__svc_versquiet_on(SVCXPRT *xprt)
436{
437
438	SVC_EXT(xprt)->xp_flags |= SVC_VERSQUIET;
439}
440
441void
442__svc_versquiet_off(SVCXPRT *xprt)
443{
444
445	SVC_EXT(xprt)->xp_flags &= ~SVC_VERSQUIET;
446}
447
448void
449svc_versquiet(SVCXPRT *xprt)
450{
451	__svc_versquiet_on(xprt);
452}
453
454int
455__svc_versquiet_get(SVCXPRT *xprt)
456{
457
458	return (SVC_EXT(xprt)->xp_flags & SVC_VERSQUIET);
459}
460#endif
461
462/*
463 * Authentication error reply
464 */
465void
466svcerr_auth(SVCXPRT *xprt, enum auth_stat why)
467{
468	struct rpc_msg rply;
469
470	assert(xprt != NULL);
471
472	rply.rm_direction = REPLY;
473	rply.rm_reply.rp_stat = MSG_DENIED;
474	rply.rjcted_rply.rj_stat = AUTH_ERROR;
475	rply.rjcted_rply.rj_why = why;
476	SVC_REPLY(xprt, &rply);
477}
478
479/*
480 * Auth too weak error reply
481 */
482void
483svcerr_weakauth(SVCXPRT *xprt)
484{
485
486	assert(xprt != NULL);
487
488	svcerr_auth(xprt, AUTH_TOOWEAK);
489}
490
491/*
492 * Program unavailable error reply
493 */
494void
495svcerr_noprog(SVCXPRT *xprt)
496{
497	struct rpc_msg rply;
498
499	assert(xprt != NULL);
500
501	rply.rm_direction = REPLY;
502	rply.rm_reply.rp_stat = MSG_ACCEPTED;
503	rply.acpted_rply.ar_verf = xprt->xp_verf;
504	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
505	SVC_REPLY(xprt, &rply);
506}
507
508/*
509 * Program version mismatch error reply
510 */
511void
512svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers)
513{
514	struct rpc_msg rply;
515
516	assert(xprt != NULL);
517
518	rply.rm_direction = REPLY;
519	rply.rm_reply.rp_stat = MSG_ACCEPTED;
520	rply.acpted_rply.ar_verf = xprt->xp_verf;
521	rply.acpted_rply.ar_stat = PROG_MISMATCH;
522	rply.acpted_rply.ar_vers.low = (u_int32_t)low_vers;
523	rply.acpted_rply.ar_vers.high = (u_int32_t)high_vers;
524	SVC_REPLY(xprt, &rply);
525}
526
527/*
528 * Allocate a new server transport structure. All fields are
529 * initialized to zero and xp_p3 is initialized to point at an
530 * extension structure to hold various flags and authentication
531 * parameters.
532 */
533SVCXPRT *
534svc_xprt_alloc(void)
535{
536	SVCXPRT *xprt;
537	SVCXPRT_EXT *ext;
538
539	xprt = mem_alloc(sizeof(SVCXPRT));
540	if (xprt == NULL)
541		return (NULL);
542	memset(xprt, 0, sizeof(SVCXPRT));
543	ext = mem_alloc(sizeof(SVCXPRT_EXT));
544	if (ext == NULL) {
545		mem_free(xprt, sizeof(SVCXPRT));
546		return (NULL);
547	}
548	memset(ext, 0, sizeof(SVCXPRT_EXT));
549	xprt->xp_p3 = ext;
550	ext->xp_auth.svc_ah_ops = &svc_auth_null_ops;
551
552	return (xprt);
553}
554
555/*
556 * Free a server transport structure.
557 */
558void
559svc_xprt_free(SVCXPRT *xprt)
560{
561
562	mem_free(xprt->xp_p3, sizeof(SVCXPRT_EXT));
563	mem_free(xprt, sizeof(SVCXPRT));
564}
565
566/* ******************* SERVER INPUT STUFF ******************* */
567
568/*
569 * Get server side input from some transport.
570 *
571 * Statement of authentication parameters management:
572 * This function owns and manages all authentication parameters, specifically
573 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
574 * the "cooked" credentials (rqst->rq_clntcred).
575 * However, this function does not know the structure of the cooked
576 * credentials, so it make the following assumptions:
577 *   a) the structure is contiguous (no pointers), and
578 *   b) the cred structure size does not exceed RQCRED_SIZE bytes.
579 * In all events, all three parameters are freed upon exit from this routine.
580 * The storage is trivially management on the call stack in user land, but
581 * is mallocated in kernel land.
582 */
583
584void
585svc_getreq(int rdfds)
586{
587	fd_set readfds;
588
589	FD_ZERO(&readfds);
590	readfds.fds_bits[0] = rdfds;
591	svc_getreqset(&readfds);
592}
593
594void
595svc_getreqset(fd_set *readfds)
596{
597	int bit, fd;
598	fd_mask mask, *maskp;
599	int sock;
600
601	assert(readfds != NULL);
602
603	maskp = readfds->fds_bits;
604	for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) {
605	    for (mask = *maskp++; (bit = ffsl(mask)) != 0;
606		mask ^= (1ul << (bit - 1))) {
607		/* sock has input waiting */
608		fd = sock + bit - 1;
609		svc_getreq_common(fd);
610	    }
611	}
612}
613
614void
615svc_getreq_common(int fd)
616{
617	SVCXPRT *xprt;
618	struct svc_req r;
619	struct rpc_msg msg;
620	int prog_found;
621	rpcvers_t low_vers;
622	rpcvers_t high_vers;
623	enum xprt_stat stat;
624	char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE];
625
626	msg.rm_call.cb_cred.oa_base = cred_area;
627	msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
628	r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]);
629
630	rwlock_rdlock(&svc_fd_lock);
631	xprt = __svc_xports[fd];
632	rwlock_unlock(&svc_fd_lock);
633	if (xprt == NULL)
634		/* But do we control sock? */
635		return;
636	/* now receive msgs from xprtprt (support batch calls) */
637	do {
638		if (SVC_RECV(xprt, &msg)) {
639
640			/* now find the exported program and call it */
641			struct svc_callout *s;
642			enum auth_stat why;
643
644			r.rq_xprt = xprt;
645			r.rq_prog = msg.rm_call.cb_prog;
646			r.rq_vers = msg.rm_call.cb_vers;
647			r.rq_proc = msg.rm_call.cb_proc;
648			r.rq_cred = msg.rm_call.cb_cred;
649			/* first authenticate the message */
650			if ((why = _authenticate(&r, &msg)) != AUTH_OK) {
651				/*
652				 * RPCSEC_GSS uses this return code
653				 * for requests that form part of its
654				 * context establishment protocol and
655				 * should not be dispatched to the
656				 * application.
657				 */
658				if (why != RPCSEC_GSS_NODISPATCH)
659					svcerr_auth(xprt, why);
660				goto call_done;
661			}
662			/* now match message with a registered service*/
663			prog_found = FALSE;
664			low_vers = (rpcvers_t) -1L;
665			high_vers = (rpcvers_t) 0L;
666			for (s = svc_head; s != NULL; s = s->sc_next) {
667				if (s->sc_prog == r.rq_prog) {
668					if (s->sc_vers == r.rq_vers) {
669						(*s->sc_dispatch)(&r, xprt);
670						goto call_done;
671					}  /* found correct version */
672					prog_found = TRUE;
673					if (s->sc_vers < low_vers)
674						low_vers = s->sc_vers;
675					if (s->sc_vers > high_vers)
676						high_vers = s->sc_vers;
677				}   /* found correct program */
678			}
679			/*
680			 * if we got here, the program or version
681			 * is not served ...
682			 */
683			if (prog_found)
684				svcerr_progvers(xprt, low_vers, high_vers);
685			else
686				svcerr_noprog(xprt);
687			/* Fall through to ... */
688		}
689		/*
690		 * Check if the xprt has been disconnected in a
691		 * recursive call in the service dispatch routine.
692		 * If so, then break.
693		 */
694		rwlock_rdlock(&svc_fd_lock);
695		if (xprt != __svc_xports[fd]) {
696			rwlock_unlock(&svc_fd_lock);
697			break;
698		}
699		rwlock_unlock(&svc_fd_lock);
700call_done:
701		if ((stat = SVC_STAT(xprt)) == XPRT_DIED){
702			SVC_DESTROY(xprt);
703			break;
704		}
705	} while (stat == XPRT_MOREREQS);
706}
707
708
709void
710svc_getreq_poll(struct pollfd *pfdp, int pollretval)
711{
712	int i;
713	int fds_found;
714
715	for (i = fds_found = 0; fds_found < pollretval; i++) {
716		struct pollfd *p = &pfdp[i];
717
718		if (p->revents) {
719			/* fd has input waiting */
720			fds_found++;
721			/*
722			 *	We assume that this function is only called
723			 *	via someone _select()ing from svc_fdset or
724			 *	_poll()ing from svc_pollset[].  Thus it's safe
725			 *	to handle the POLLNVAL event by simply turning
726			 *	the corresponding bit off in svc_fdset.  The
727			 *	svc_pollset[] array is derived from svc_fdset
728			 *	and so will also be updated eventually.
729			 *
730			 *	XXX Should we do an xprt_unregister() instead?
731			 */
732			if (p->revents & POLLNVAL) {
733				rwlock_wrlock(&svc_fd_lock);
734				FD_CLR(p->fd, &svc_fdset);
735				rwlock_unlock(&svc_fd_lock);
736			} else
737				svc_getreq_common(p->fd);
738		}
739	}
740}
741
742bool_t
743rpc_control(int what, void *arg)
744{
745	int val;
746
747	switch (what) {
748	case RPC_SVC_CONNMAXREC_SET:
749		val = *(int *)arg;
750		if (val <= 0)
751			return FALSE;
752		__svc_maxrec = val;
753		return TRUE;
754	case RPC_SVC_CONNMAXREC_GET:
755		*(int *)arg = __svc_maxrec;
756		return TRUE;
757	default:
758		break;
759	}
760	return FALSE;
761}
762