FreeBSD/Linux Kernel Cross Reference
sys/rpc/svc.c
1 /* $NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $ */
2
3 /*
4 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5 * unrestricted use provided that this legend is included on all tape
6 * media and as a part of the software program in whole or part. Users
7 * may copy or modify Sun RPC without charge, but are not authorized
8 * to license or distribute it to anyone else except as part of a product or
9 * program developed by the user.
10 *
11 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14 *
15 * Sun RPC is provided with no support and without any obligation on the
16 * part of Sun Microsystems, Inc. to assist in its use, correction,
17 * modification or enhancement.
18 *
19 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21 * OR ANY PART THEREOF.
22 *
23 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24 * or profits or other special, indirect and consequential damages, even if
25 * Sun has been advised of the possibility of such damages.
26 *
27 * Sun Microsystems, Inc.
28 * 2550 Garcia Avenue
29 * Mountain View, California 94043
30 */
31
32 #if defined(LIBC_SCCS) && !defined(lint)
33 static char *sccsid2 = "@(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro";
34 static char *sccsid = "@(#)svc.c 2.4 88/08/11 4.0 RPCSRC";
35 #endif
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
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 * Copyright (C) 1984, Sun Microsystems, Inc.
47 */
48
49 #include <sys/param.h>
50 #include <sys/lock.h>
51 #include <sys/kernel.h>
52 #include <sys/malloc.h>
53 #include <sys/mutex.h>
54 #include <sys/queue.h>
55 #include <sys/systm.h>
56 #include <sys/ucred.h>
57
58 #include <rpc/rpc.h>
59 #include <rpc/rpcb_clnt.h>
60
61 #include <rpc/rpc_com.h>
62
63 #define SVC_VERSQUIET 0x0001 /* keep quiet about vers mismatch */
64 #define version_keepquiet(xp) ((u_long)(xp)->xp_p3 & SVC_VERSQUIET)
65
66 static struct svc_callout *svc_find(SVCPOOL *pool, rpcprog_t, rpcvers_t,
67 char *);
68 static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock);
69
70 /* *************** SVCXPRT related stuff **************** */
71
72 SVCPOOL*
73 svcpool_create(void)
74 {
75 SVCPOOL *pool;
76
77 pool = malloc(sizeof(SVCPOOL), M_RPC, M_WAITOK|M_ZERO);
78
79 mtx_init(&pool->sp_lock, "sp_lock", NULL, MTX_DEF);
80 TAILQ_INIT(&pool->sp_xlist);
81 TAILQ_INIT(&pool->sp_active);
82 TAILQ_INIT(&pool->sp_callouts);
83
84 return pool;
85 }
86
87 void
88 svcpool_destroy(SVCPOOL *pool)
89 {
90 SVCXPRT *xprt;
91 struct svc_callout *s;
92
93 mtx_lock(&pool->sp_lock);
94
95 while (TAILQ_FIRST(&pool->sp_xlist)) {
96 xprt = TAILQ_FIRST(&pool->sp_xlist);
97 mtx_unlock(&pool->sp_lock);
98 SVC_DESTROY(xprt);
99 mtx_lock(&pool->sp_lock);
100 }
101
102 while (TAILQ_FIRST(&pool->sp_callouts)) {
103 s = TAILQ_FIRST(&pool->sp_callouts);
104 mtx_unlock(&pool->sp_lock);
105 svc_unreg(pool, s->sc_prog, s->sc_vers);
106 mtx_lock(&pool->sp_lock);
107 }
108
109 mtx_destroy(&pool->sp_lock);
110 free(pool, M_RPC);
111 }
112
113 /*
114 * Activate a transport handle.
115 */
116 void
117 xprt_register(SVCXPRT *xprt)
118 {
119 SVCPOOL *pool = xprt->xp_pool;
120
121 mtx_lock(&pool->sp_lock);
122 xprt->xp_registered = TRUE;
123 xprt->xp_active = FALSE;
124 TAILQ_INSERT_TAIL(&pool->sp_xlist, xprt, xp_link);
125 mtx_unlock(&pool->sp_lock);
126 }
127
128 void
129 xprt_unregister(SVCXPRT *xprt)
130 {
131 __xprt_do_unregister(xprt, TRUE);
132 }
133
134 void
135 __xprt_unregister_unlocked(SVCXPRT *xprt)
136 {
137 __xprt_do_unregister(xprt, FALSE);
138 }
139
140 /*
141 * De-activate a transport handle.
142 */
143 static void
144 __xprt_do_unregister(SVCXPRT *xprt, bool_t dolock)
145 {
146 SVCPOOL *pool = xprt->xp_pool;
147
148 //__svc_generic_cleanup(xprt);
149
150 if (dolock)
151 mtx_lock(&pool->sp_lock);
152
153 if (xprt->xp_active) {
154 TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
155 xprt->xp_active = FALSE;
156 }
157 TAILQ_REMOVE(&pool->sp_xlist, xprt, xp_link);
158 xprt->xp_registered = FALSE;
159
160 if (dolock)
161 mtx_unlock(&pool->sp_lock);
162 }
163
164 void
165 xprt_active(SVCXPRT *xprt)
166 {
167 SVCPOOL *pool = xprt->xp_pool;
168
169 mtx_lock(&pool->sp_lock);
170
171 if (!xprt->xp_active) {
172 TAILQ_INSERT_TAIL(&pool->sp_active, xprt, xp_alink);
173 xprt->xp_active = TRUE;
174 }
175 wakeup(&pool->sp_active);
176
177 mtx_unlock(&pool->sp_lock);
178 }
179
180 void
181 xprt_inactive_locked(SVCXPRT *xprt)
182 {
183 SVCPOOL *pool = xprt->xp_pool;
184
185 if (xprt->xp_active) {
186 TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
187 xprt->xp_active = FALSE;
188 }
189 }
190
191 void
192 xprt_inactive(SVCXPRT *xprt)
193 {
194 SVCPOOL *pool = xprt->xp_pool;
195
196 mtx_lock(&pool->sp_lock);
197 xprt_inactive_locked(xprt);
198 mtx_unlock(&pool->sp_lock);
199 }
200
201 /*
202 * Add a service program to the callout list.
203 * The dispatch routine will be called when a rpc request for this
204 * program number comes in.
205 */
206 bool_t
207 svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
208 void (*dispatch)(struct svc_req *, SVCXPRT *),
209 const struct netconfig *nconf)
210 {
211 SVCPOOL *pool = xprt->xp_pool;
212 struct svc_callout *s;
213 char *netid = NULL;
214 int flag = 0;
215
216 /* VARIABLES PROTECTED BY svc_lock: s, svc_head */
217
218 if (xprt->xp_netid) {
219 netid = strdup(xprt->xp_netid, M_RPC);
220 flag = 1;
221 } else if (nconf && nconf->nc_netid) {
222 netid = strdup(nconf->nc_netid, M_RPC);
223 flag = 1;
224 } /* must have been created with svc_raw_create */
225 if ((netid == NULL) && (flag == 1)) {
226 return (FALSE);
227 }
228
229 mtx_lock(&pool->sp_lock);
230 if ((s = svc_find(pool, prog, vers, netid)) != NULL) {
231 if (netid)
232 free(netid, M_RPC);
233 if (s->sc_dispatch == dispatch)
234 goto rpcb_it; /* he is registering another xptr */
235 mtx_unlock(&pool->sp_lock);
236 return (FALSE);
237 }
238 s = malloc(sizeof (struct svc_callout), M_RPC, M_NOWAIT);
239 if (s == NULL) {
240 if (netid)
241 free(netid, M_RPC);
242 mtx_unlock(&pool->sp_lock);
243 return (FALSE);
244 }
245
246 s->sc_prog = prog;
247 s->sc_vers = vers;
248 s->sc_dispatch = dispatch;
249 s->sc_netid = netid;
250 TAILQ_INSERT_TAIL(&pool->sp_callouts, s, sc_link);
251
252 if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
253 ((SVCXPRT *) xprt)->xp_netid = strdup(netid, M_RPC);
254
255 rpcb_it:
256 mtx_unlock(&pool->sp_lock);
257 /* now register the information with the local binder service */
258 if (nconf) {
259 bool_t dummy;
260 struct netconfig tnc;
261 tnc = *nconf;
262 dummy = rpcb_set(prog, vers, &tnc,
263 &((SVCXPRT *) xprt)->xp_ltaddr);
264 return (dummy);
265 }
266 return (TRUE);
267 }
268
269 /*
270 * Remove a service program from the callout list.
271 */
272 void
273 svc_unreg(SVCPOOL *pool, const rpcprog_t prog, const rpcvers_t vers)
274 {
275 struct svc_callout *s;
276
277 /* unregister the information anyway */
278 (void) rpcb_unset(prog, vers, NULL);
279 mtx_lock(&pool->sp_lock);
280 while ((s = svc_find(pool, prog, vers, NULL)) != NULL) {
281 TAILQ_REMOVE(&pool->sp_callouts, s, sc_link);
282 if (s->sc_netid)
283 mem_free(s->sc_netid, sizeof (s->sc_netid) + 1);
284 mem_free(s, sizeof (struct svc_callout));
285 }
286 mtx_unlock(&pool->sp_lock);
287 }
288
289 /* ********************** CALLOUT list related stuff ************* */
290
291 /*
292 * Search the callout list for a program number, return the callout
293 * struct.
294 */
295 static struct svc_callout *
296 svc_find(SVCPOOL *pool, rpcprog_t prog, rpcvers_t vers, char *netid)
297 {
298 struct svc_callout *s;
299
300 mtx_assert(&pool->sp_lock, MA_OWNED);
301 TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
302 if (s->sc_prog == prog && s->sc_vers == vers
303 && (netid == NULL || s->sc_netid == NULL ||
304 strcmp(netid, s->sc_netid) == 0))
305 break;
306 }
307
308 return (s);
309 }
310
311 /* ******************* REPLY GENERATION ROUTINES ************ */
312
313 /*
314 * Send a reply to an rpc request
315 */
316 bool_t
317 svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results, void * xdr_location)
318 {
319 struct rpc_msg rply;
320
321 rply.rm_direction = REPLY;
322 rply.rm_reply.rp_stat = MSG_ACCEPTED;
323 rply.acpted_rply.ar_verf = xprt->xp_verf;
324 rply.acpted_rply.ar_stat = SUCCESS;
325 rply.acpted_rply.ar_results.where = xdr_location;
326 rply.acpted_rply.ar_results.proc = xdr_results;
327
328 return (SVC_REPLY(xprt, &rply));
329 }
330
331 /*
332 * No procedure error reply
333 */
334 void
335 svcerr_noproc(SVCXPRT *xprt)
336 {
337 struct rpc_msg rply;
338
339 rply.rm_direction = REPLY;
340 rply.rm_reply.rp_stat = MSG_ACCEPTED;
341 rply.acpted_rply.ar_verf = xprt->xp_verf;
342 rply.acpted_rply.ar_stat = PROC_UNAVAIL;
343
344 SVC_REPLY(xprt, &rply);
345 }
346
347 /*
348 * Can't decode args error reply
349 */
350 void
351 svcerr_decode(SVCXPRT *xprt)
352 {
353 struct rpc_msg rply;
354
355 rply.rm_direction = REPLY;
356 rply.rm_reply.rp_stat = MSG_ACCEPTED;
357 rply.acpted_rply.ar_verf = xprt->xp_verf;
358 rply.acpted_rply.ar_stat = GARBAGE_ARGS;
359
360 SVC_REPLY(xprt, &rply);
361 }
362
363 /*
364 * Some system error
365 */
366 void
367 svcerr_systemerr(SVCXPRT *xprt)
368 {
369 struct rpc_msg rply;
370
371 rply.rm_direction = REPLY;
372 rply.rm_reply.rp_stat = MSG_ACCEPTED;
373 rply.acpted_rply.ar_verf = xprt->xp_verf;
374 rply.acpted_rply.ar_stat = SYSTEM_ERR;
375
376 SVC_REPLY(xprt, &rply);
377 }
378
379 /*
380 * Authentication error reply
381 */
382 void
383 svcerr_auth(SVCXPRT *xprt, enum auth_stat why)
384 {
385 struct rpc_msg rply;
386
387 rply.rm_direction = REPLY;
388 rply.rm_reply.rp_stat = MSG_DENIED;
389 rply.rjcted_rply.rj_stat = AUTH_ERROR;
390 rply.rjcted_rply.rj_why = why;
391
392 SVC_REPLY(xprt, &rply);
393 }
394
395 /*
396 * Auth too weak error reply
397 */
398 void
399 svcerr_weakauth(SVCXPRT *xprt)
400 {
401
402 svcerr_auth(xprt, AUTH_TOOWEAK);
403 }
404
405 /*
406 * Program unavailable error reply
407 */
408 void
409 svcerr_noprog(SVCXPRT *xprt)
410 {
411 struct rpc_msg rply;
412
413 rply.rm_direction = REPLY;
414 rply.rm_reply.rp_stat = MSG_ACCEPTED;
415 rply.acpted_rply.ar_verf = xprt->xp_verf;
416 rply.acpted_rply.ar_stat = PROG_UNAVAIL;
417
418 SVC_REPLY(xprt, &rply);
419 }
420
421 /*
422 * Program version mismatch error reply
423 */
424 void
425 svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers)
426 {
427 struct rpc_msg rply;
428
429 rply.rm_direction = REPLY;
430 rply.rm_reply.rp_stat = MSG_ACCEPTED;
431 rply.acpted_rply.ar_verf = xprt->xp_verf;
432 rply.acpted_rply.ar_stat = PROG_MISMATCH;
433 rply.acpted_rply.ar_vers.low = (uint32_t)low_vers;
434 rply.acpted_rply.ar_vers.high = (uint32_t)high_vers;
435
436 SVC_REPLY(xprt, &rply);
437 }
438
439 /* ******************* SERVER INPUT STUFF ******************* */
440
441 /*
442 * Get server side input from some transport.
443 *
444 * Statement of authentication parameters management:
445 * This function owns and manages all authentication parameters, specifically
446 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
447 * the "cooked" credentials (rqst->rq_clntcred).
448 * In-kernel, we represent non-trivial cooked creds with struct ucred.
449 * In all events, all three parameters are freed upon exit from this routine.
450 * The storage is trivially management on the call stack in user land, but
451 * is mallocated in kernel land.
452 */
453
454 static void
455 svc_getreq(SVCXPRT *xprt)
456 {
457 SVCPOOL *pool = xprt->xp_pool;
458 struct svc_req r;
459 struct rpc_msg msg;
460 int prog_found;
461 rpcvers_t low_vers;
462 rpcvers_t high_vers;
463 enum xprt_stat stat;
464 char cred_area[2*MAX_AUTH_BYTES + sizeof(struct xucred)];
465
466 msg.rm_call.cb_cred.oa_base = cred_area;
467 msg.rm_call.cb_verf.oa_base = &cred_area[MAX_AUTH_BYTES];
468 r.rq_clntcred = &cred_area[2*MAX_AUTH_BYTES];
469
470 /* now receive msgs from xprtprt (support batch calls) */
471 do {
472 if (SVC_RECV(xprt, &msg)) {
473
474 /* now find the exported program and call it */
475 struct svc_callout *s;
476 enum auth_stat why;
477
478 r.rq_xprt = xprt;
479 r.rq_prog = msg.rm_call.cb_prog;
480 r.rq_vers = msg.rm_call.cb_vers;
481 r.rq_proc = msg.rm_call.cb_proc;
482 r.rq_cred = msg.rm_call.cb_cred;
483 /* first authenticate the message */
484 if ((why = _authenticate(&r, &msg)) != AUTH_OK) {
485 svcerr_auth(xprt, why);
486 goto call_done;
487 }
488 /* now match message with a registered service*/
489 prog_found = FALSE;
490 low_vers = (rpcvers_t) -1L;
491 high_vers = (rpcvers_t) 0L;
492 TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
493 if (s->sc_prog == r.rq_prog) {
494 if (s->sc_vers == r.rq_vers) {
495 (*s->sc_dispatch)(&r, xprt);
496 goto call_done;
497 } /* found correct version */
498 prog_found = TRUE;
499 if (s->sc_vers < low_vers)
500 low_vers = s->sc_vers;
501 if (s->sc_vers > high_vers)
502 high_vers = s->sc_vers;
503 } /* found correct program */
504 }
505 /*
506 * if we got here, the program or version
507 * is not served ...
508 */
509 if (prog_found)
510 svcerr_progvers(xprt, low_vers, high_vers);
511 else
512 svcerr_noprog(xprt);
513 /* Fall through to ... */
514 }
515 /*
516 * Check if the xprt has been disconnected in a
517 * recursive call in the service dispatch routine.
518 * If so, then break.
519 */
520 mtx_lock(&pool->sp_lock);
521 if (!xprt->xp_registered) {
522 mtx_unlock(&pool->sp_lock);
523 break;
524 }
525 mtx_unlock(&pool->sp_lock);
526 call_done:
527 if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
528 SVC_DESTROY(xprt);
529 break;
530 }
531 } while (stat == XPRT_MOREREQS);
532 }
533
534 void
535 svc_run(SVCPOOL *pool)
536 {
537 SVCXPRT *xprt;
538 int error;
539
540 mtx_lock(&pool->sp_lock);
541
542 pool->sp_exited = FALSE;
543
544 while (!pool->sp_exited) {
545 xprt = TAILQ_FIRST(&pool->sp_active);
546 if (!xprt) {
547 error = msleep(&pool->sp_active, &pool->sp_lock, PCATCH,
548 "rpcsvc", 0);
549 if (error)
550 break;
551 continue;
552 }
553
554 /*
555 * Move this transport to the end to ensure fairness
556 * when multiple transports are active. If this was
557 * the last queued request, svc_getreq will end up
558 * calling xprt_inactive to remove from the active
559 * list.
560 */
561 TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
562 TAILQ_INSERT_TAIL(&pool->sp_active, xprt, xp_alink);
563
564 mtx_unlock(&pool->sp_lock);
565 svc_getreq(xprt);
566 mtx_lock(&pool->sp_lock);
567 }
568
569 mtx_unlock(&pool->sp_lock);
570 }
571
572 void
573 svc_exit(SVCPOOL *pool)
574 {
575 mtx_lock(&pool->sp_lock);
576 pool->sp_exited = TRUE;
577 wakeup(&pool->sp_active);
578 mtx_unlock(&pool->sp_lock);
579 }
Cache object: f718d391beabc324afa3e5d3906a638f
|