1/* $NetBSD: svc_vc.c,v 1.24 2011/02/04 17:38:15 christos Exp $ */ 2 3/* 4 * Copyright (c) 2010, Oracle America, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following 14 * disclaimer in the documentation and/or other materials 15 * provided with the distribution. 16 * * Neither the name of the "Oracle America, Inc." nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#include <sys/cdefs.h> 35#if defined(LIBC_SCCS) && !defined(lint) 36#if 0 37static char *sccsid = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; 38static char *sccsid = "@(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC"; 39#else 40__RCSID("$NetBSD: svc_vc.c,v 1.24 2011/02/04 17:38:15 christos Exp $"); 41#endif 42#endif 43 44/* 45 * svc_vc.c, Server side for Connection Oriented based RPC. 46 * 47 * Actually implements two flavors of transporter - 48 * a tcp rendezvouser (a listner and connection establisher) 49 * and a record/tcp stream. 50 */ 51 52#include "namespace.h" 53#include "reentrant.h" 54#include <sys/types.h> 55#include <sys/param.h> 56#include <sys/poll.h> 57#include <sys/socket.h> 58#include <sys/un.h> 59#include <sys/time.h> 60#include <netinet/in.h> 61 62#include <assert.h> 63#include <err.h> 64#include <errno.h> 65#include <fcntl.h> 66#include <stdio.h> 67#include <stdlib.h> 68#include <string.h> 69#include <unistd.h> 70 71#include <rpc/rpc.h> 72 73#include "rpc_internal.h" 74 75#ifdef __weak_alias 76__weak_alias(svc_fd_create,_svc_fd_create) 77__weak_alias(svc_vc_create,_svc_vc_create) 78#endif 79 80#ifdef _REENTRANT 81extern rwlock_t svc_fd_lock; 82#endif 83 84static SVCXPRT *makefd_xprt(int, u_int, u_int); 85static bool_t rendezvous_request(SVCXPRT *, struct rpc_msg *); 86static enum xprt_stat rendezvous_stat(SVCXPRT *); 87static void svc_vc_destroy(SVCXPRT *); 88static void __svc_vc_dodestroy(SVCXPRT *); 89static int read_vc(caddr_t, caddr_t, int); 90static int write_vc(caddr_t, caddr_t, int); 91static enum xprt_stat svc_vc_stat(SVCXPRT *); 92static bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *); 93static bool_t svc_vc_getargs(SVCXPRT *, xdrproc_t, caddr_t); 94static bool_t svc_vc_freeargs(SVCXPRT *, xdrproc_t, caddr_t); 95static bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *); 96static void svc_vc_rendezvous_ops(SVCXPRT *); 97static void svc_vc_ops(SVCXPRT *); 98static bool_t svc_vc_control(SVCXPRT *, const u_int, void *); 99static bool_t svc_vc_rendezvous_control(SVCXPRT *, const u_int, void *); 100 101struct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */ 102 u_int sendsize; 103 u_int recvsize; 104 int maxrec; 105}; 106 107struct cf_conn { /* kept in xprt->xp_p1 for actual connection */ 108 enum xprt_stat strm_stat; 109 u_int32_t x_id; 110 XDR xdrs; 111 char verf_body[MAX_AUTH_BYTES]; 112 u_int sendsize; 113 u_int recvsize; 114 int maxrec; 115 bool_t nonblock; 116 struct timeval last_recv_time; 117}; 118 119/* 120 * Usage: 121 * xprt = svc_vc_create(sock, send_buf_size, recv_buf_size); 122 * 123 * Creates, registers, and returns a (rpc) tcp based transporter. 124 * Once *xprt is initialized, it is registered as a transporter 125 * see (svc.h, xprt_register). This routine returns 126 * a NULL if a problem occurred. 127 * 128 * The filedescriptor passed in is expected to refer to a bound, but 129 * not yet connected socket. 130 * 131 * Since streams do buffered io similar to stdio, the caller can specify 132 * how big the send and receive buffers are via the second and third parms; 133 * 0 => use the system default. 134 */ 135SVCXPRT * 136svc_vc_create(int fd, u_int sendsize, u_int recvsize) 137{ 138 SVCXPRT *xprt; 139 struct cf_rendezvous *r = NULL; 140 struct __rpc_sockinfo si; 141 struct sockaddr_storage sslocal; 142 socklen_t slen; 143 int one = 1; 144 145 if (!__rpc_fd2sockinfo(fd, &si)) 146 return NULL; 147 148 r = mem_alloc(sizeof(*r)); 149 if (r == NULL) { 150 warnx("svc_vc_create: out of memory"); 151 return NULL; 152 } 153 r->sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize); 154 r->recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize); 155 r->maxrec = __svc_maxrec; 156 xprt = mem_alloc(sizeof(SVCXPRT)); 157 if (xprt == NULL) { 158 warnx("svc_vc_create: out of memory"); 159 goto cleanup_svc_vc_create; 160 } 161 xprt->xp_tp = NULL; 162 xprt->xp_p1 = (caddr_t)(void *)r; 163 xprt->xp_p2 = NULL; 164 xprt->xp_p3 = NULL; 165 xprt->xp_verf = _null_auth; 166 svc_vc_rendezvous_ops(xprt); 167 xprt->xp_port = (u_short)-1; /* It is the rendezvouser */ 168 xprt->xp_fd = fd; 169 170 slen = sizeof (struct sockaddr_storage); 171 if (getsockname(fd, (struct sockaddr *)(void *)&sslocal, &slen) < 0) { 172 warnx("svc_vc_create: could not retrieve local addr"); 173 goto cleanup_svc_vc_create; 174 } 175 176 /* 177 * We want to be able to check credentials on local sockets. 178 */ 179 if (sslocal.ss_family == AF_LOCAL) 180 if (setsockopt(fd, 0, LOCAL_CREDS, &one, sizeof one) < 0) 181 goto cleanup_svc_vc_create; 182 183 xprt->xp_ltaddr.maxlen = xprt->xp_ltaddr.len = sslocal.ss_len; 184 xprt->xp_ltaddr.buf = mem_alloc((size_t)sslocal.ss_len); 185 if (xprt->xp_ltaddr.buf == NULL) { 186 warnx("svc_vc_create: no mem for local addr"); 187 goto cleanup_svc_vc_create; 188 } 189 memcpy(xprt->xp_ltaddr.buf, &sslocal, (size_t)sslocal.ss_len); 190 191 xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage); 192 xprt_register(xprt); 193 return xprt; 194cleanup_svc_vc_create: 195 if (xprt) 196 mem_free(xprt, sizeof(*xprt)); 197 if (r != NULL) 198 mem_free(r, sizeof(*r)); 199 return NULL; 200} 201 202/* 203 * Like svtcp_create(), except the routine takes any *open* UNIX file 204 * descriptor as its first input. 205 */ 206SVCXPRT * 207svc_fd_create(int fd, u_int sendsize, u_int recvsize) 208{ 209 struct sockaddr_storage ss; 210 socklen_t slen; 211 SVCXPRT *ret; 212 213 _DIAGASSERT(fd != -1); 214 215 ret = makefd_xprt(fd, sendsize, recvsize); 216 if (ret == NULL) 217 return NULL; 218 219 slen = sizeof (struct sockaddr_storage); 220 if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) { 221 warnx("svc_fd_create: could not retrieve local addr"); 222 goto freedata; 223 } 224 ret->xp_ltaddr.maxlen = ret->xp_ltaddr.len = ss.ss_len; 225 ret->xp_ltaddr.buf = mem_alloc((size_t)ss.ss_len); 226 if (ret->xp_ltaddr.buf == NULL) { 227 warnx("svc_fd_create: no mem for local addr"); 228 goto freedata; 229 } 230 memcpy(ret->xp_ltaddr.buf, &ss, (size_t)ss.ss_len); 231 232 slen = sizeof (struct sockaddr_storage); 233 if (getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) { 234 warnx("svc_fd_create: could not retrieve remote addr"); 235 goto freedata; 236 } 237 ret->xp_rtaddr.maxlen = ret->xp_rtaddr.len = ss.ss_len; 238 ret->xp_rtaddr.buf = mem_alloc((size_t)ss.ss_len); 239 if (ret->xp_rtaddr.buf == NULL) { 240 warnx("svc_fd_create: no mem for local addr"); 241 goto freedata; 242 } 243 memcpy(ret->xp_rtaddr.buf, &ss, (size_t)ss.ss_len); 244#ifdef PORTMAP 245 if (ss.ss_family == AF_INET) { 246 ret->xp_raddr = *(struct sockaddr_in *)ret->xp_rtaddr.buf; 247 ret->xp_addrlen = sizeof (struct sockaddr_in); 248 } 249#endif 250 251 return ret; 252 253freedata: 254 if (ret->xp_ltaddr.buf != NULL) 255 mem_free(ret->xp_ltaddr.buf, rep->xp_ltaddr.maxlen); 256 257 return NULL; 258} 259 260static SVCXPRT * 261makefd_xprt(int fd, u_int sendsize, u_int recvsize) 262{ 263 SVCXPRT *xprt; 264 struct cf_conn *cd; 265 const char *netid; 266 struct __rpc_sockinfo si; 267 268 _DIAGASSERT(fd != -1); 269 270 xprt = mem_alloc(sizeof(SVCXPRT)); 271 if (xprt == NULL) 272 goto out; 273 memset(xprt, 0, sizeof *xprt); 274 cd = mem_alloc(sizeof(struct cf_conn)); 275 if (cd == NULL) 276 goto out; 277 cd->strm_stat = XPRT_IDLE; 278 xdrrec_create(&(cd->xdrs), sendsize, recvsize, 279 (caddr_t)(void *)xprt, read_vc, write_vc); 280 xprt->xp_p1 = (caddr_t)(void *)cd; 281 xprt->xp_verf.oa_base = cd->verf_body; 282 svc_vc_ops(xprt); /* truely deals with calls */ 283 xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ 284 xprt->xp_fd = fd; 285 if (__rpc_fd2sockinfo(fd, &si) && __rpc_sockinfo2netid(&si, &netid)) 286 if ((xprt->xp_netid = strdup(netid)) == NULL) 287 goto out; 288 289 xprt_register(xprt); 290 return xprt; 291out: 292 warn("svc_tcp: makefd_xprt"); 293 if (xprt) 294 mem_free(xprt, sizeof(SVCXPRT)); 295 return NULL; 296} 297 298/*ARGSUSED*/ 299static bool_t 300rendezvous_request(SVCXPRT *xprt, struct rpc_msg *msg) 301{ 302 int sock, flags; 303 struct cf_rendezvous *r; 304 struct cf_conn *cd; 305 struct sockaddr_storage addr; 306 socklen_t len; 307 struct __rpc_sockinfo si; 308 SVCXPRT *newxprt; 309 fd_set cleanfds; 310 311 _DIAGASSERT(xprt != NULL); 312 _DIAGASSERT(msg != NULL); 313 314 r = (struct cf_rendezvous *)xprt->xp_p1; 315again: 316 len = sizeof addr; 317 if ((sock = accept(xprt->xp_fd, (struct sockaddr *)(void *)&addr, 318 &len)) < 0) { 319 if (errno == EINTR) 320 goto again; 321 /* 322 * Clean out the most idle file descriptor when we're 323 * running out. 324 */ 325 if (errno == EMFILE || errno == ENFILE) { 326 cleanfds = svc_fdset; 327 if (__svc_clean_idle(&cleanfds, 0, FALSE)) 328 goto again; 329 } 330 return FALSE; 331 } 332 /* 333 * make a new transporter (re-uses xprt) 334 */ 335 newxprt = makefd_xprt(sock, r->sendsize, r->recvsize); 336 if (newxprt == NULL) 337 goto out; 338 newxprt->xp_rtaddr.buf = mem_alloc(len); 339 if (newxprt->xp_rtaddr.buf == NULL) 340 goto out; 341 memcpy(newxprt->xp_rtaddr.buf, &addr, len); 342 newxprt->xp_rtaddr.len = len; 343#ifdef PORTMAP 344 if (addr.ss_family == AF_INET) { 345 newxprt->xp_raddr = *(struct sockaddr_in *)newxprt->xp_rtaddr.buf; 346 newxprt->xp_addrlen = sizeof (struct sockaddr_in); 347 } 348#endif 349 if (__rpc_fd2sockinfo(sock, &si)) 350 __rpc_setnodelay(sock, &si); 351 352 cd = (struct cf_conn *)newxprt->xp_p1; 353 354 cd->recvsize = r->recvsize; 355 cd->sendsize = r->sendsize; 356 cd->maxrec = r->maxrec; 357 358 if (cd->maxrec != 0) { 359 flags = fcntl(sock, F_GETFL, 0); 360 if (flags == -1) 361 goto out; 362 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) 363 goto out; 364 if (cd->recvsize > (u_int)cd->maxrec) 365 cd->recvsize = cd->maxrec; 366 cd->nonblock = TRUE; 367 __xdrrec_setnonblock(&cd->xdrs, cd->maxrec); 368 } else 369 cd->nonblock = FALSE; 370 371 (void)gettimeofday(&cd->last_recv_time, NULL); 372 373 return FALSE; /* there is never an rpc msg to be processed */ 374out: 375 (void)close(sock); 376 return FALSE; /* there was an error */ 377} 378 379/*ARGSUSED*/ 380static enum xprt_stat 381rendezvous_stat(SVCXPRT *xprt) 382{ 383 384 return XPRT_IDLE; 385} 386 387static void 388svc_vc_destroy(SVCXPRT *xprt) 389{ 390 _DIAGASSERT(xprt != NULL); 391 392 xprt_unregister(xprt); 393 __svc_vc_dodestroy(xprt); 394} 395 396static void 397__svc_vc_dodestroy(SVCXPRT *xprt) 398{ 399 struct cf_conn *cd; 400 struct cf_rendezvous *r; 401 402 cd = (struct cf_conn *)xprt->xp_p1; 403 404 if (xprt->xp_fd != RPC_ANYFD) 405 (void)close(xprt->xp_fd); 406 if (xprt->xp_port != 0) { 407 /* a rendezvouser socket */ 408 r = (struct cf_rendezvous *)xprt->xp_p1; 409 mem_free(r, sizeof (struct cf_rendezvous)); 410 xprt->xp_port = 0; 411 } else { 412 /* an actual connection socket */ 413 XDR_DESTROY(&(cd->xdrs)); 414 mem_free(cd, sizeof(struct cf_conn)); 415 } 416 if (xprt->xp_rtaddr.buf) 417 mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen); 418 if (xprt->xp_ltaddr.buf) 419 mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen); 420 if (xprt->xp_tp) 421 free(xprt->xp_tp); 422 if (xprt->xp_netid) 423 free(xprt->xp_netid); 424 mem_free(xprt, sizeof(SVCXPRT)); 425} 426 427/*ARGSUSED*/ 428static bool_t 429svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in) 430{ 431 return FALSE; 432} 433 434/*ARGSUSED*/ 435static bool_t 436svc_vc_rendezvous_control(SVCXPRT *xprt, const u_int rq, void *in) 437{ 438 struct cf_rendezvous *cfp; 439 440 cfp = (struct cf_rendezvous *)xprt->xp_p1; 441 if (cfp == NULL) 442 return FALSE; 443 switch (rq) { 444 case SVCGET_CONNMAXREC: 445 *(int *)in = cfp->maxrec; 446 break; 447 case SVCSET_CONNMAXREC: 448 cfp->maxrec = *(int *)in; 449 break; 450 default: 451 return FALSE; 452 } 453 return TRUE; 454} 455 456/* 457 * reads data from the tcp connection. 458 * any error is fatal and the connection is closed. 459 * (And a read of zero bytes is a half closed stream => error.) 460 * All read operations timeout after 35 seconds. A timeout is 461 * fatal for the connection. 462 */ 463static int 464read_vc(caddr_t xprtp, caddr_t buf, int len) 465{ 466 SVCXPRT *xprt; 467 int sock; 468 struct pollfd pollfd; 469 struct sockaddr *sa; 470 struct msghdr msg; 471 struct cmsghdr *cmp; 472 void *crmsg = NULL; 473 struct sockcred *sc; 474 socklen_t crmsgsize; 475 struct cf_conn *cfp; 476 static const struct timespec ts = { 35, 0 }; 477 478 xprt = (SVCXPRT *)(void *)xprtp; 479 _DIAGASSERT(xprt != NULL); 480 481 sock = xprt->xp_fd; 482 483 sa = (struct sockaddr *)xprt->xp_rtaddr.buf; 484 if (sa->sa_family == AF_LOCAL && xprt->xp_p2 == NULL) { 485 memset(&msg, 0, sizeof msg); 486 crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS)); 487 crmsg = malloc(crmsgsize); 488 if (crmsg == NULL) 489 goto fatal_err; 490 memset(crmsg, 0, crmsgsize); 491 492 msg.msg_control = crmsg; 493 msg.msg_controllen = crmsgsize; 494 495 if (recvmsg(sock, &msg, 0) < 0) 496 goto fatal_err; 497 498 if (msg.msg_controllen == 0 || 499 (msg.msg_flags & MSG_CTRUNC) != 0) 500 goto fatal_err; 501 502 cmp = CMSG_FIRSTHDR(&msg); 503 if (cmp->cmsg_level != SOL_SOCKET || 504 cmp->cmsg_type != SCM_CREDS) 505 goto fatal_err; 506 507 sc = (struct sockcred *)(void *)CMSG_DATA(cmp); 508 509 xprt->xp_p2 = mem_alloc(SOCKCREDSIZE(sc->sc_ngroups)); 510 if (xprt->xp_p2 == NULL) 511 goto fatal_err; 512 513 memcpy(xprt->xp_p2, sc, SOCKCREDSIZE(sc->sc_ngroups)); 514 free(crmsg); 515 crmsg = NULL; 516 } 517 518 cfp = (struct cf_conn *)xprt->xp_p1; 519 520 if (cfp->nonblock) { 521 len = read(sock, buf, (size_t)len); 522 if (len < 0) { 523 if (errno == EAGAIN) 524 len = 0; 525 else 526 goto fatal_err; 527 } 528 if (len != 0) 529 gettimeofday(&cfp->last_recv_time, NULL); 530 return len; 531 } 532 533 do { 534 pollfd.fd = sock; 535 pollfd.events = POLLIN; 536 switch (pollts(&pollfd, 1, &ts, NULL)) { 537 case -1: 538 if (errno == EINTR) { 539 continue; 540 } 541 /*FALLTHROUGH*/ 542 case 0: 543 goto fatal_err; 544 545 default: 546 break; 547 } 548 } while ((pollfd.revents & POLLIN) == 0); 549 550 if ((len = read(sock, buf, (size_t)len)) > 0) { 551 gettimeofday(&cfp->last_recv_time, NULL); 552 return len; 553 } 554 555fatal_err: 556 if (crmsg != NULL) 557 free(crmsg); 558 ((struct cf_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED; 559 return -1; 560} 561 562/* 563 * writes data to the tcp connection. 564 * Any error is fatal and the connection is closed. 565 */ 566static int 567write_vc(caddr_t xprtp, caddr_t buf, int len) 568{ 569 SVCXPRT *xprt; 570 int i, cnt; 571 struct cf_conn *cd; 572 struct timeval tv0, tv1; 573 574 xprt = (SVCXPRT *)(void *)xprtp; 575 _DIAGASSERT(xprt != NULL); 576 577 cd = (struct cf_conn *)xprt->xp_p1; 578 579 if (cd->nonblock) 580 gettimeofday(&tv0, NULL); 581 582 for (cnt = len; cnt > 0; cnt -= i, buf += i) { 583 if ((i = write(xprt->xp_fd, buf, (size_t)cnt)) < 0) { 584 if (errno != EAGAIN || !cd->nonblock) { 585 cd->strm_stat = XPRT_DIED; 586 return -1; 587 } 588 if (cd->nonblock) { 589 /* 590 * For non-blocking connections, do not 591 * take more than 2 seconds writing the 592 * data out. 593 * 594 * XXX 2 is an arbitrary amount. 595 */ 596 gettimeofday(&tv1, NULL); 597 if (tv1.tv_sec - tv0.tv_sec >= 2) { 598 cd->strm_stat = XPRT_DIED; 599 return -1; 600 } 601 } 602 i = 0; 603 } 604 } 605 return len; 606} 607 608static enum xprt_stat 609svc_vc_stat(SVCXPRT *xprt) 610{ 611 struct cf_conn *cd; 612 613 _DIAGASSERT(xprt != NULL); 614 615 cd = (struct cf_conn *)(xprt->xp_p1); 616 617 if (cd->strm_stat == XPRT_DIED) 618 return XPRT_DIED; 619 if (! xdrrec_eof(&(cd->xdrs))) 620 return XPRT_MOREREQS; 621 return XPRT_IDLE; 622} 623 624static bool_t 625svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg) 626{ 627 struct cf_conn *cd; 628 XDR *xdrs; 629 630 _DIAGASSERT(xprt != NULL); 631 _DIAGASSERT(msg != NULL); 632 633 cd = (struct cf_conn *)(xprt->xp_p1); 634 xdrs = &(cd->xdrs); 635 636 if (cd->nonblock) { 637 if (!__xdrrec_getrec(xdrs, &cd->strm_stat, TRUE)) 638 return FALSE; 639 } 640 641 xdrs->x_op = XDR_DECODE; 642 (void)xdrrec_skiprecord(xdrs); 643 644 if (xdr_callmsg(xdrs, msg)) { 645 cd->x_id = msg->rm_xid; 646 return TRUE; 647 } 648 cd->strm_stat = XPRT_DIED; 649 return FALSE; 650} 651 652static bool_t 653svc_vc_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 654{ 655 656 _DIAGASSERT(xprt != NULL); 657 /* args_ptr may be NULL */ 658 659 return (*xdr_args)(&(((struct cf_conn *)(xprt->xp_p1))->xdrs), 660 args_ptr); 661} 662 663static bool_t 664svc_vc_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) 665{ 666 XDR *xdrs; 667 668 _DIAGASSERT(xprt != NULL); 669 /* args_ptr may be NULL */ 670 671 xdrs = &(((struct cf_conn *)(xprt->xp_p1))->xdrs); 672 673 xdrs->x_op = XDR_FREE; 674 return (*xdr_args)(xdrs, args_ptr); 675} 676 677static bool_t 678svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg) 679{ 680 struct cf_conn *cd; 681 XDR *xdrs; 682 bool_t rstat; 683 684 _DIAGASSERT(xprt != NULL); 685 _DIAGASSERT(msg != NULL); 686 687 cd = (struct cf_conn *)(xprt->xp_p1); 688 xdrs = &(cd->xdrs); 689 690 xdrs->x_op = XDR_ENCODE; 691 msg->rm_xid = cd->x_id; 692 rstat = xdr_replymsg(xdrs, msg); 693 (void)xdrrec_endofrecord(xdrs, TRUE); 694 return rstat; 695} 696 697static void 698svc_vc_ops(SVCXPRT *xprt) 699{ 700 static struct xp_ops ops; 701 static struct xp_ops2 ops2; 702#ifdef _REENTRANT 703 extern mutex_t ops_lock; 704#endif 705 706/* VARIABLES PROTECTED BY ops_lock: ops, ops2 */ 707 708 mutex_lock(&ops_lock); 709 if (ops.xp_recv == NULL) { 710 ops.xp_recv = svc_vc_recv; 711 ops.xp_stat = svc_vc_stat; 712 ops.xp_getargs = svc_vc_getargs; 713 ops.xp_reply = svc_vc_reply; 714 ops.xp_freeargs = svc_vc_freeargs; 715 ops.xp_destroy = svc_vc_destroy; 716 ops2.xp_control = svc_vc_control; 717 } 718 xprt->xp_ops = &ops; 719 xprt->xp_ops2 = &ops2; 720 mutex_unlock(&ops_lock); 721} 722 723static void 724svc_vc_rendezvous_ops(xprt) 725 SVCXPRT *xprt; 726{ 727 static struct xp_ops ops; 728 static struct xp_ops2 ops2; 729#ifdef _REENTRANT 730 extern mutex_t ops_lock; 731#endif 732 mutex_lock(&ops_lock); 733 if (ops.xp_recv == NULL) { 734 ops.xp_recv = rendezvous_request; 735 ops.xp_stat = rendezvous_stat; 736 ops.xp_getargs = 737 (bool_t (*)(SVCXPRT *, xdrproc_t, caddr_t))abort; 738 ops.xp_reply = 739 (bool_t (*)(SVCXPRT *, struct rpc_msg *))abort; 740 ops.xp_freeargs = 741 (bool_t (*)(SVCXPRT *, xdrproc_t, caddr_t))abort; 742 ops.xp_destroy = svc_vc_destroy; 743 ops2.xp_control = svc_vc_rendezvous_control; 744 } 745 xprt->xp_ops = &ops; 746 xprt->xp_ops2 = &ops2; 747 mutex_unlock(&ops_lock); 748} 749 750/* 751 * Destroy xprts that have not have had any activity in 'timeout' seconds. 752 * If 'cleanblock' is true, blocking connections (the default) are also 753 * cleaned. If timeout is 0, the least active connection is picked. 754 */ 755bool_t 756__svc_clean_idle(fd_set *fds, int timeout, bool_t cleanblock) 757{ 758 int i, ncleaned; 759 SVCXPRT *xprt, *least_active; 760 struct timeval tv, tdiff, tmax; 761 struct cf_conn *cd; 762 763 gettimeofday(&tv, NULL); 764 tmax.tv_sec = tmax.tv_usec = 0; 765 least_active = NULL; 766 rwlock_wrlock(&svc_fd_lock); 767 for (i = ncleaned = 0; i <= svc_maxfd; i++) { 768 if (FD_ISSET(i, fds)) { 769 xprt = __svc_xports[i]; 770 if (xprt == NULL || xprt->xp_ops == NULL || 771 xprt->xp_ops->xp_recv != svc_vc_recv) 772 continue; 773 cd = (struct cf_conn *)xprt->xp_p1; 774 if (!cleanblock && !cd->nonblock) 775 continue; 776 if (timeout == 0) { 777 timersub(&tv, &cd->last_recv_time, &tdiff); 778 if (timercmp(&tdiff, &tmax, >)) { 779 tmax = tdiff; 780 least_active = xprt; 781 } 782 continue; 783 } 784 if (tv.tv_sec - cd->last_recv_time.tv_sec > timeout) { 785 __xprt_unregister_unlocked(xprt); 786 __svc_vc_dodestroy(xprt); 787 ncleaned++; 788 } 789 } 790 } 791 if (timeout == 0 && least_active != NULL) { 792 __xprt_unregister_unlocked(least_active); 793 __svc_vc_dodestroy(least_active); 794 ncleaned++; 795 } 796 rwlock_unlock(&svc_fd_lock); 797 return ncleaned > 0 ? TRUE : FALSE; 798} 799