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