1/* $NetBSD: svc_vc.c,v 1.37 2024/01/23 17:24:38 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.37 2024/01/23 17:24:38 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 listener 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 "svc_fdset.h" 74#include "rpc_internal.h" 75 76#ifdef __weak_alias 77__weak_alias(svc_fd_create,_svc_fd_create) 78__weak_alias(svc_vc_create,_svc_vc_create) 79#endif 80 81static SVCXPRT *makefd_xprt(int, u_int, u_int); 82static bool_t rendezvous_request(SVCXPRT *, struct rpc_msg *); 83static enum xprt_stat rendezvous_stat(SVCXPRT *); 84static void svc_vc_destroy(SVCXPRT *); 85static void __svc_vc_dodestroy(SVCXPRT *); 86static int read_vc(caddr_t, caddr_t, int); 87static int write_vc(caddr_t, caddr_t, int); 88static enum xprt_stat svc_vc_stat(SVCXPRT *); 89static bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *); 90static bool_t svc_vc_getargs(SVCXPRT *, xdrproc_t, caddr_t); 91static bool_t svc_vc_freeargs(SVCXPRT *, xdrproc_t, caddr_t); 92static bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *); 93static void svc_vc_rendezvous_ops(SVCXPRT *); 94static void svc_vc_ops(SVCXPRT *); 95static bool_t svc_vc_control(SVCXPRT *, const u_int, void *); 96static bool_t svc_vc_rendezvous_control(SVCXPRT *, const u_int, void *); 97 98struct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */ 99 u_int sendsize; 100 u_int recvsize; 101 int maxrec; 102}; 103 104struct cf_conn { /* kept in xprt->xp_p1 for actual connection */ 105 enum xprt_stat strm_stat; 106 u_int32_t x_id; 107 XDR xdrs; 108 char verf_body[MAX_AUTH_BYTES]; 109 u_int sendsize; 110 u_int recvsize; 111 int maxrec; 112 bool_t nonblock; 113 struct timeval last_recv_time; 114}; 115 116/* 117 * Usage: 118 * xprt = svc_vc_create(sock, send_buf_size, recv_buf_size); 119 * 120 * Creates, registers, and returns a (rpc) tcp based transporter. 121 * Once *xprt is initialized, it is registered as a transporter 122 * see (svc.h, xprt_register). This routine returns 123 * a NULL if a problem occurred. 124 * 125 * The filedescriptor passed in is expected to refer to a bound, but 126 * not yet connected socket. 127 * 128 * Since streams do buffered io similar to stdio, the caller can specify 129 * how big the send and receive buffers are via the second and third parms; 130 * 0 => use the system default. 131 */ 132SVCXPRT * 133svc_vc_create(int fd, u_int sendsize, u_int recvsize) 134{ 135 SVCXPRT *xprt; 136 struct cf_rendezvous *r = NULL; 137 struct __rpc_sockinfo si; 138 struct sockaddr_storage sslocal; 139 socklen_t slen; 140 int one = 1; 141 142 if (!__rpc_fd2sockinfo(fd, &si)) 143 return NULL; 144 145 r = mem_alloc(sizeof(*r)); 146 if (r == NULL) { 147 warn("%s: out of memory", __func__); 148 return NULL; 149 } 150 r->sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize); 151 r->recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize); 152 r->maxrec = __svc_maxrec; 153 xprt = mem_alloc(sizeof(SVCXPRT)); 154 if (xprt == NULL) { 155 warn("%s: out of memory", __func__); 156 goto cleanup_svc_vc_create; 157 } 158 xprt->xp_tp = NULL; 159 xprt->xp_p1 = (caddr_t)(void *)r; 160 xprt->xp_p2 = NULL; 161 xprt->xp_p3 = NULL; 162 xprt->xp_verf = _null_auth; 163 svc_vc_rendezvous_ops(xprt); 164 xprt->xp_port = (u_short)-1; /* It is the rendezvouser */ 165 xprt->xp_fd = fd; 166 167 slen = sizeof (struct sockaddr_storage); 168 if (getsockname(fd, (struct sockaddr *)(void *)&sslocal, &slen) < 0) { 169 warn("%s: could not retrieve local addr", __func__); 170 goto cleanup_svc_vc_create; 171 } 172 173 /* 174 * We want to be able to check credentials on local sockets. 175 */ 176 if (sslocal.ss_family == AF_LOCAL) 177 if (setsockopt(fd, SOL_LOCAL, LOCAL_CREDS, &one, 178 (socklen_t)sizeof one) == -1) 179 goto cleanup_svc_vc_create; 180 181 xprt->xp_ltaddr.maxlen = xprt->xp_ltaddr.len = sslocal.ss_len; 182 xprt->xp_ltaddr.buf = mem_alloc((size_t)sslocal.ss_len); 183 if (xprt->xp_ltaddr.buf == NULL) { 184 warn("%s: out of memory", __func__); 185 goto cleanup_svc_vc_create; 186 } 187 memcpy(xprt->xp_ltaddr.buf, &sslocal, (size_t)sslocal.ss_len); 188 189 xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage); 190 if (!xprt_register(xprt)) 191 goto cleanup_svc_vc_create; 192 return xprt; 193cleanup_svc_vc_create: 194 if (xprt) 195 mem_free(xprt, sizeof(*xprt)); 196 if (r != NULL) 197 mem_free(r, sizeof(*r)); 198 return NULL; 199} 200 201/* 202 * Like svtcp_create(), except the routine takes any *open* UNIX file 203 * descriptor as its first input. 204 */ 205SVCXPRT * 206svc_fd_create(int fd, u_int sendsize, u_int recvsize) 207{ 208 struct sockaddr_storage ss; 209 socklen_t slen; 210 SVCXPRT *ret; 211 212 _DIAGASSERT(fd != -1); 213 214 ret = makefd_xprt(fd, sendsize, recvsize); 215 if (ret == NULL) 216 return NULL; 217 218 slen = sizeof (struct sockaddr_storage); 219 if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) { 220 warn("%s: could not retrieve local addr", __func__); 221 goto freedata; 222 } 223 ret->xp_ltaddr.maxlen = ret->xp_ltaddr.len = ss.ss_len; 224 ret->xp_ltaddr.buf = mem_alloc((size_t)ss.ss_len); 225 if (ret->xp_ltaddr.buf == NULL) { 226 warn("%s: out of memory", __func__); 227 goto freedata; 228 } 229 memcpy(ret->xp_ltaddr.buf, &ss, (size_t)ss.ss_len); 230 231 slen = sizeof (struct sockaddr_storage); 232 if (getpeername(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) { 233 warn("%s: could not retrieve remote addr", __func__); 234 goto freedata; 235 } 236 ret->xp_rtaddr.maxlen = ret->xp_rtaddr.len = ss.ss_len; 237 ret->xp_rtaddr.buf = mem_alloc((size_t)ss.ss_len); 238 if (ret->xp_rtaddr.buf == NULL) { 239 warn("%s: out of memory", __func__); 240 goto freedata; 241 } 242 memcpy(ret->xp_rtaddr.buf, &ss, (size_t)ss.ss_len); 243#ifdef PORTMAP 244 if (ss.ss_family == AF_INET) { 245 ret->xp_raddr = *(struct sockaddr_in *)ret->xp_rtaddr.buf; 246 ret->xp_addrlen = sizeof (struct sockaddr_in); 247 } 248#endif 249 250 return ret; 251 252freedata: 253 if (ret->xp_ltaddr.buf != NULL) 254 mem_free(ret->xp_ltaddr.buf, rep->xp_ltaddr.maxlen); 255 256 return NULL; 257} 258 259static SVCXPRT * 260makefd_xprt(int fd, u_int sendsize, u_int recvsize) 261{ 262 SVCXPRT *xprt; 263 struct cf_conn *cd; 264 const char *netid; 265 struct __rpc_sockinfo si; 266 267 _DIAGASSERT(fd != -1); 268 269 xprt = mem_alloc(sizeof(SVCXPRT)); 270 if (xprt == NULL) 271 goto outofmem; 272 memset(xprt, 0, sizeof *xprt); 273 cd = mem_alloc(sizeof(struct cf_conn)); 274 if (cd == NULL) 275 goto outofmem; 276 cd->strm_stat = XPRT_IDLE; 277 xdrrec_create(&(cd->xdrs), sendsize, recvsize, 278 (caddr_t)(void *)xprt, read_vc, write_vc); 279 xprt->xp_p1 = (caddr_t)(void *)cd; 280 xprt->xp_verf.oa_base = cd->verf_body; 281 svc_vc_ops(xprt); /* truely deals with calls */ 282 xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ 283 xprt->xp_fd = fd; 284 if (__rpc_fd2sockinfo(fd, &si) && __rpc_sockinfo2netid(&si, &netid)) 285 if ((xprt->xp_netid = strdup(netid)) == NULL) 286 goto outofmem; 287 288 if (!xprt_register(xprt)) 289 goto out; 290 return xprt; 291 292outofmem: 293 warn("svc_tcp: makefd_xprt"); 294out: 295 if (xprt) 296 mem_free(xprt, sizeof(SVCXPRT)); 297 return NULL; 298} 299 300/*ARGSUSED*/ 301static bool_t 302rendezvous_request(SVCXPRT *xprt, struct rpc_msg *msg) 303{ 304 int sock, flags; 305 struct cf_rendezvous *r; 306 struct cf_conn *cd; 307 struct sockaddr_storage addr; 308 socklen_t len; 309 struct __rpc_sockinfo si; 310 SVCXPRT *newxprt; 311 312 _DIAGASSERT(xprt != NULL); 313 _DIAGASSERT(msg != NULL); 314 315 r = (struct cf_rendezvous *)xprt->xp_p1; 316again: 317 len = sizeof addr; 318 if ((sock = accept(xprt->xp_fd, (struct sockaddr *)(void *)&addr, 319 &len)) < 0) { 320 if (errno == EINTR) 321 goto again; 322 /* 323 * Clean out the most idle file descriptor when we're 324 * running out. 325 */ 326 if (errno == EMFILE || errno == ENFILE) { 327 if (__svc_clean_idle(NULL, 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 = (int)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 = (int)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 = (int)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 703/* VARIABLES PROTECTED BY ops_lock: ops, ops2 */ 704 705 mutex_lock(&ops_lock); 706 if (ops.xp_recv == NULL) { 707 ops.xp_recv = svc_vc_recv; 708 ops.xp_stat = svc_vc_stat; 709 ops.xp_getargs = svc_vc_getargs; 710 ops.xp_reply = svc_vc_reply; 711 ops.xp_freeargs = svc_vc_freeargs; 712 ops.xp_destroy = svc_vc_destroy; 713 ops2.xp_control = svc_vc_control; 714 } 715 xprt->xp_ops = &ops; 716 xprt->xp_ops2 = &ops2; 717 mutex_unlock(&ops_lock); 718} 719 720static void 721svc_vc_rendezvous_ops(SVCXPRT *xprt) 722{ 723 static struct xp_ops ops; 724 static struct xp_ops2 ops2; 725 726 mutex_lock(&ops_lock); 727 if (ops.xp_recv == NULL) { 728 ops.xp_recv = rendezvous_request; 729 ops.xp_stat = rendezvous_stat; 730 ops.xp_getargs = 731 (bool_t (*)(SVCXPRT *, xdrproc_t, caddr_t))abort; 732 ops.xp_reply = 733 (bool_t (*)(SVCXPRT *, struct rpc_msg *))abort; 734 ops.xp_freeargs = 735 (bool_t (*)(SVCXPRT *, xdrproc_t, caddr_t))abort; 736 ops.xp_destroy = svc_vc_destroy; 737 ops2.xp_control = svc_vc_rendezvous_control; 738 } 739 xprt->xp_ops = &ops; 740 xprt->xp_ops2 = &ops2; 741 mutex_unlock(&ops_lock); 742} 743 744/* 745 * Destroy xprts that have not have had any activity in 'timeout' seconds. 746 * If 'cleanblock' is true, blocking connections (the default) are also 747 * cleaned. If timeout is 0, the least active connection is picked. 748 */ 749bool_t 750/*ARGSUSED1*/ 751__svc_clean_idle(fd_set *fds __unused, int timeout, bool_t cleanblock) 752{ 753 int i, ncleaned, *fdmax; 754 SVCXPRT *xprt, *least_active; 755 struct timeval tv, tdiff, tmax; 756 struct cf_conn *cd; 757 758 gettimeofday(&tv, NULL); 759 tmax.tv_sec = tmax.tv_usec = 0; 760 least_active = NULL; 761 rwlock_wrlock(&svc_fd_lock); 762 fdmax = svc_fdset_getmax(); 763 if (fdmax == NULL) 764 return FALSE; 765 for (i = ncleaned = 0; i <= *fdmax; i++) { 766 switch (svc_fdset_isset(i)) { 767 case 0: 768 case -1: 769 continue; 770 default: 771 break; 772 } 773 774 xprt = __svc_xports[i]; 775 if (xprt == NULL || xprt->xp_ops == NULL || 776 xprt->xp_ops->xp_recv != svc_vc_recv) 777 continue; 778 779 cd = (struct cf_conn *)xprt->xp_p1; 780 if (!cleanblock && !cd->nonblock) 781 continue; 782 783 if (timeout == 0) { 784 timersub(&tv, &cd->last_recv_time, &tdiff); 785 if (timercmp(&tdiff, &tmax, >)) { 786 tmax = tdiff; 787 least_active = xprt; 788 } 789 continue; 790 } 791 792 if (tv.tv_sec - cd->last_recv_time.tv_sec > timeout) { 793 __xprt_unregister_unlocked(xprt); 794 __svc_vc_dodestroy(xprt); 795 ncleaned++; 796 } 797 } 798 if (timeout == 0 && least_active != NULL) { 799 __xprt_unregister_unlocked(least_active); 800 __svc_vc_dodestroy(least_active); 801 ncleaned++; 802 } 803 rwlock_unlock(&svc_fd_lock); 804 return ncleaned > 0 ? TRUE : FALSE; 805} 806