blob: 666152f51a3b3072404a14e6bee107ed5b1b830a [file] [log] [blame]
/*
* Copyright (c) 2009, Sun Microsystems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of Sun Microsystems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* svc.c, Server-side remote procedure call interface.
*
* There are two sets of procedures here. The xprt routines are
* for handling transport handles. The svc routines handle the
* list of service routines.
*
* Copyright (C) 1984, Sun Microsystems, Inc.
*/
#include <config.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#if !defined (_WIN32)
#include <err.h>
#include <sys/poll.h>
#endif
#include <sys/socket.h>
#ifdef RPC_VSOCK
#include <linux/vm_sockets.h>
#endif /* VSOCK */
#include <rpc/types.h>
#include <misc/portable.h>
#include <rpc/rpc.h>
#ifdef PORTMAP
#include <rpc/pmap_clnt.h>
#endif /* PORTMAP */
#include "rpc_com.h"
#include <rpc/svc.h>
#include <rpc/svc_auth.h>
#include <arpa/inet.h>
#include "clnt_internal.h"
#include "svc_internal.h"
#include "svc_xprt.h"
#include "rpc_dplx_internal.h"
#include <rpc/svc_rqst.h>
#ifdef USE_RPC_RDMA
#include "rpc_rdma.h"
#endif
#include "svc_ioq.h"
#define SVC_VERSQUIET 0x0001 /* keep quiet about vers mismatch */
#define version_keepquiet(xp) ((u_long)(xp)->xp_p3 & SVC_VERSQUIET)
/* svc_internal.h */
#ifdef IOV_MAX
int __svc_maxiov = IOV_MAX;
#else
int __svc_maxiov = 1024; /* UIO_MAXIOV value from sys/uio.h */
#endif
int __svc_maxrec = 0;
struct svc_params __svc_params[1] = {
{
false /* !initialized */ ,
MUTEX_INITIALIZER}
};
/*
* The services list
* Each entry represents a set of procedures (an rpc program).
* The dispatch routine takes request structs and runs the
* apropriate procedure.
*
* The service record is factored out to permit exporting the find
* routines without exposing the db implementation.
*/
static struct svc_callout {
struct svc_callout *sc_next;
struct svc_record rec;
} *svc_head;
extern rwlock_t svc_lock;
extern rwlock_t svc_fd_lock;
static struct svc_callout *svc_find(rpcprog_t, rpcvers_t, struct svc_callout **,
char *);
struct work_pool svc_work_pool;
static int
svc_work_pool_init()
{
struct work_pool_params params = {
.thrd_max = __svc_params->ioq.thrd_max,
.thrd_min = 2
};
return work_pool_init(&svc_work_pool, "svc_work_pool", &params);
}
/* Package init function.
* It is intended that applications which must make use of global state
* will call svc_init() before accessing such state and before executing
* any svc exported functions. Traditional TI-RPC programs need not
* call the function as presently integrated. */
bool
svc_init(svc_init_params *params)
{
mutex_lock(&__svc_params->mtx);
if (__svc_params->initialized) {
__warnx(TIRPC_DEBUG_FLAG_SVC,
"svc_init: multiple initialization attempt (nothing happens)");
mutex_unlock(&__svc_params->mtx);
return true;
}
__svc_params->max_connections =
(params->max_connections) ? params->max_connections : FD_SETSIZE;
/* svc_vc */
__svc_params->xprt_u.vc.nconns = 0;
mutex_init(&__svc_params->xprt_u.vc.mtx, NULL);
#if defined(HAVE_BLKIN)
if (params->flags & SVC_INIT_BLKIN) {
int r = blkin_init();
if (r < 0) {
__warnx(TIRPC_DEBUG_FLAG_SVC,
"%s: blkn_init failed\n",
__func__);
}
}
#endif
#if defined(TIRPC_EPOLL)
if (params->flags & SVC_INIT_EPOLL) {
__svc_params->ev_type = SVC_EVENT_EPOLL;
__svc_params->ev_u.evchan.max_events = params->max_events;
}
#else
/* XXX formerly select/fd_set case, now placeholder for new
* event systems, reworked select, etc. */
#endif
__svc_params->idle_timeout = params->idle_timeout;
/* XXX defaults to the legacy max sendsz */
__svc_params->svc_ioq_maxbuf =
(params->svc_ioq_maxbuf) ? (params->svc_ioq_maxbuf) : 262144;
/* allow consumers to manage all xprt registration */
if (params->flags & SVC_INIT_NOREG_XPRTS)
__svc_params->flags |= SVC_FLAG_NOREG_XPRTS;
if (params->ioq_thrd_max)
__svc_params->ioq.thrd_max = params->ioq_thrd_max;
else
__svc_params->ioq.thrd_max = 200;
/* uses ioq.thrd_max */
if (svc_work_pool_init()) {
mutex_unlock(&__svc_params->mtx);
return false;
}
if (params->gss_ctx_hash_partitions)
__svc_params->gss.ctx_hash_partitions =
params->gss_ctx_hash_partitions;
else
__svc_params->gss.ctx_hash_partitions = 13;
if (params->gss_max_ctx)
__svc_params->gss.max_ctx = params->gss_max_ctx;
else
__svc_params->gss.max_ctx = 16384; /* 16K clients */
/* XXX deprecating */
if (params->gss_max_idle_gen)
__svc_params->gss.max_idle_gen = params->gss_max_idle_gen;
else
__svc_params->gss.max_idle_gen = 1024;
if (params->gss_max_gc)
__svc_params->gss.max_gc = params->gss_max_gc;
else
__svc_params->gss.max_gc = 200;
#ifdef USE_RPC_RDMA
rpc_rdma_internals_init();
#endif
__svc_params->initialized = true;
mutex_unlock(&__svc_params->mtx);
#if defined(_SC_IOV_MAX) /* IRIX, MacOS X, FreeBSD, Solaris, ... */
__svc_maxiov = sysconf(_SC_IOV_MAX);
#endif
return true;
}
struct rpc_msg *
alloc_rpc_msg(void)
{
struct rpc_msg *msg = mem_alloc(sizeof(struct rpc_msg));
TAILQ_INIT_ENTRY(msg, msg_q);
/* avoid separate alloc/free */
msg->rm_call.cb_cred.oa_base = msg->cb_cred_body;
msg->rm_call.cb_verf.oa_base = msg->cb_verf_body;
/* required for REPLY decodes */
msg->acpted_rply.ar_verf = _null_auth;
msg->acpted_rply.ar_results.where = NULL;
msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
return (msg);
}
void
free_rpc_msg(struct rpc_msg *msg)
{
mem_free(msg, sizeof(struct rpc_msg));
}
static inline void
free_req_rpc_msg(struct svc_req *req)
{
if (req->rq_msg) {
struct rpc_msg *msg = req->rq_msg;
mem_free(msg, sizeof(struct rpc_msg));
req->rq_msg = NULL;
}
}
/* *************** SVCXPRT related stuff **************** */
/*
* This is used to set local and remote addresses in a way legacy
* apps can deal with, at the same time setting up a corresponding
* netbuf -- with no alloc/free needed.
*/
void
__rpc_set_address(struct rpc_address *rpca, const struct sockaddr_storage *ss,
socklen_t sl)
{
socklen_t l;
switch (ss->ss_family) {
case AF_INET6:
l = sl ? sl : sizeof(struct sockaddr_in6);
memcpy(&rpca->ss, ss, l);
break;
case AF_INET:
l = sl ? sl : sizeof(struct sockaddr_in);
memcpy(&rpca->ss, ss, l);
break;
case AF_LOCAL:
l = sl ? sl : sizeof(struct sockaddr);
memcpy(&rpca->ss, ss, l);
break;
#ifdef RPC_VSOCK
case AF_VSOCK:
l = sl ? sl : sizeof(struct sockaddr_vm);
memcpy(&rpca->ss, ss, l);
break;
#endif /* VSOCK */
default:
memset(&rpca->ss, 0xfe, sizeof(struct sockaddr_storage));
l = sl ? sl : sizeof(struct sockaddr);
rpca->ss.ss_family = AF_UNSPEC;
break;
}
rpca->nb.buf = &rpca->ss;
rpca->nb.len = l;
rpca->nb.maxlen = sizeof(struct sockaddr_storage);
}
/*
* Add a service program to the callout list.
* The dispatch routine will be called when a rpc request for this
* program number comes in.
*/
bool
svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
void (*dispatch) (struct svc_req *req, SVCXPRT *xprt),
const struct netconfig *nconf)
{
bool dummy;
struct svc_callout *prev;
struct svc_callout *s;
struct netconfig *tnconf;
char *netid = NULL;
int flag = 0;
/* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
if (xprt->xp_netid) {
netid = mem_strdup(xprt->xp_netid);
flag = 1;
} else if (nconf) {
netid = mem_strdup(nconf->nc_netid);
flag = 1;
} else {
tnconf = __rpcgettp(xprt->xp_fd);
if (tnconf) {
netid = mem_strdup(tnconf->nc_netid);
flag = 1;
freenetconfigent(tnconf);
}
} /* must have been created with svc_raw_create */
if ((netid == NULL) && (flag == 1))
return (false);
rwlock_wrlock(&svc_lock);
s = svc_find(prog, vers, &prev, netid);
if (s) {
if (netid)
mem_free(netid, 0);
if (s->rec.sc_dispatch == dispatch)
goto rpcb_it; /* he is registering another xptr */
rwlock_unlock(&svc_lock);
return (false);
}
s = mem_alloc(sizeof(struct svc_callout));
s->rec.sc_prog = prog;
s->rec.sc_vers = vers;
s->rec.sc_dispatch = dispatch;
s->rec.sc_netid = netid;
s->sc_next = svc_head;
svc_head = s;
if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
((SVCXPRT *) xprt)->xp_netid = mem_strdup(netid);
rpcb_it:
rwlock_unlock(&svc_lock);
/* now register the information with the local binder service */
if (nconf) {
/*LINTED const castaway */
dummy =
rpcb_set(prog, vers, (struct netconfig *)nconf,
&((SVCXPRT *) xprt)->xp_local.nb);
return (dummy);
}
return (true);
}
/*
* Remove a service program from the callout list.
*/
void
svc_unreg(const rpcprog_t prog, const rpcvers_t vers)
{
struct svc_callout *prev;
struct svc_callout *s;
/* unregister the information anyway */
(void)rpcb_unset(prog, vers, NULL);
rwlock_wrlock(&svc_lock);
while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) {
if (prev == NULL)
svc_head = s->sc_next;
else
prev->sc_next = s->sc_next;
s->sc_next = NULL;
if (s->rec.sc_netid)
mem_free(s->rec.sc_netid, sizeof(s->rec.sc_netid) + 1);
mem_free(s, sizeof(struct svc_callout));
}
rwlock_unlock(&svc_lock);
}
/* ********************** CALLOUT list related stuff ************* */
#ifdef PORTMAP
/*
* Add a service program to the callout list.
* The dispatch routine will be called when a rpc request for this
* program number comes in.
*/
bool
svc_register(SVCXPRT *xprt, u_long prog, u_long vers,
void (*dispatch) (struct svc_req *req, SVCXPRT *xprt),
int protocol)
{
struct svc_callout *prev;
struct svc_callout *s;
assert(xprt != NULL);
assert(dispatch != NULL);
s = svc_find((rpcprog_t) prog, (rpcvers_t) vers, &prev, NULL);
if (s) {
if (s->rec.sc_dispatch == dispatch)
goto pmap_it; /* he is registering another xprt */
return (false);
}
s = mem_alloc(sizeof(struct svc_callout));
s->rec.sc_prog = (rpcprog_t) prog;
s->rec.sc_vers = (rpcvers_t) vers;
s->rec.sc_dispatch = dispatch;
s->sc_next = svc_head;
svc_head = s;
pmap_it:
/* now register the information with the local binder service */
if (protocol)
return (pmap_set(prog, vers, protocol, xprt->xp_port));
return (true);
}
/*
* Remove a service program from the callout list.
*/
void
svc_unregister(u_long prog, u_long vers)
{
struct svc_callout *prev;
struct svc_callout *s;
s = svc_find((rpcprog_t) prog, (rpcvers_t) vers, &prev, NULL);
if (!s)
return;
if (prev == NULL)
svc_head = s->sc_next;
else
prev->sc_next = s->sc_next;
s->sc_next = NULL;
mem_free(s, sizeof(struct svc_callout));
/* now unregister the information with the local binder service */
(void)pmap_unset(prog, vers);
}
#endif /* PORTMAP */
/*
* Search the callout list for a program number, return the callout
* struct.
*/
static struct svc_callout *
svc_find(rpcprog_t prog, rpcvers_t vers,
struct svc_callout **prev, char *netid)
{
struct svc_callout *s, *p;
assert(prev != NULL);
p = NULL;
for (s = svc_head; s != NULL; s = s->sc_next) {
if (((s->rec.sc_prog == prog) && (s->rec.sc_vers == vers))
&& ((netid == NULL) || (s->rec.sc_netid == NULL)
|| (strcmp(netid, s->rec.sc_netid) == 0)))
break;
p = s;
}
*prev = p;
return (s);
}
/* An exported search routing similar to svc_find, but with error reporting
* needed by svc_getreq routines. */
svc_lookup_result_t
svc_lookup(svc_rec_t **rec, svc_vers_range_t *vrange,
rpcprog_t prog, rpcvers_t vers, char *netid,
u_int flags)
{
struct svc_callout *s, *p;
svc_lookup_result_t code = SVC_LKP_ERR;
bool prog_found, vers_found, netid_found;
p = NULL;
prog_found = vers_found = netid_found = false;
vrange->lowvers = vrange->highvers = 0;
for (s = svc_head; s != NULL; s = s->sc_next) {
if (s->rec.sc_prog == prog) {
prog_found = true;
/* track supported versions for SVC_LKP_VERS_NOTFOUND */
if (s->rec.sc_vers > vrange->highvers)
vrange->highvers = s->rec.sc_vers;
if (vrange->lowvers < s->rec.sc_vers)
vrange->lowvers = s->rec.sc_vers;
/* vers match */
if (s->rec.sc_vers == vers) {
vers_found = true;
/* the following semantics are unchanged */
if ((netid == NULL) || (s->rec.sc_netid == NULL)
|| (strcmp(netid, s->rec.sc_netid) == 0)) {
netid_found = true;
p = s;
} /* netid */
} /* vers */
} /* prog */
} /* for */
if (p != NULL) {
*rec = &(p->rec);
code = SVC_LKP_SUCCESS;
goto out;
}
if (!prog_found) {
code = SVC_LKP_PROG_NOTFOUND;
goto out;
}
if (!vers_found) {
code = SVC_LKP_VERS_NOTFOUND;
goto out;
}
if ((netid != NULL) && (!netid_found)) {
code = SVC_LKP_NETID_NOTFOUND;
goto out;
}
out:
return (code);
}
/* ******************* REPLY GENERATION ROUTINES ************ */
/*
* Send a reply to an rpc request (MT-SAFE).
*
* XXX Rather than marshal an rpc_msg on the stack, we may
* want another variant which takes a message, perhaps one from
* a reply cache (e.g., CITI Windows NFS client).
*
*/
bool
svc_sendreply(SVCXPRT *xprt, struct svc_req *req, xdrproc_t xdr_results,
void *xdr_location)
{
struct rpc_msg rply;
assert(xprt != NULL);
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
rply.rm_xid = req->rq_xid;
rply.acpted_rply.ar_verf = req->rq_verf;
rply.acpted_rply.ar_stat = SUCCESS;
rply.acpted_rply.ar_results.where = xdr_location;
rply.acpted_rply.ar_results.proc = xdr_results;
return (SVC_REPLY(xprt, req, &rply));
}
/*
* No procedure error reply (MT-SAFE)
*/
void
svcerr_noproc(SVCXPRT *xprt, struct svc_req *req)
{
struct rpc_msg rply;
assert(xprt != NULL);
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
rply.rm_xid = req->rq_xid;
rply.acpted_rply.ar_verf = req->rq_verf;
rply.acpted_rply.ar_stat = PROC_UNAVAIL;
SVC_REPLY(xprt, req, &rply);
}
/*
* Can't decode args error reply (MT-SAFE)
*/
void
svcerr_decode(SVCXPRT *xprt, struct svc_req *req)
{
struct rpc_msg rply;
assert(xprt != NULL);
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
rply.rm_xid = req->rq_xid;
rply.acpted_rply.ar_verf = req->rq_verf;
rply.acpted_rply.ar_stat = GARBAGE_ARGS;
SVC_REPLY(xprt, req, &rply);
}
/*
* Some system error (MT-SAFE)
*/
void
svcerr_systemerr(SVCXPRT *xprt, struct svc_req *req)
{
struct rpc_msg rply;
assert(xprt != NULL);
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
rply.rm_xid = req->rq_xid;
rply.acpted_rply.ar_verf = req->rq_verf;
rply.acpted_rply.ar_stat = SYSTEM_ERR;
SVC_REPLY(xprt, req, &rply);
}
#if 0
/*
* Tell RPC package to not complain about version errors to the client. This
* is useful when revving broadcast protocols that sit on a fixed address.
* There is really one (or should be only one) example of this kind of
* protocol: the portmapper (or rpc binder).
*/
void
__svc_versquiet_on(SVCXPRT *xprt)
{
u_long tmp;
tmp = ((u_long) xprt->xp_p3) | SVC_VERSQUIET;
xprt->xp_p3 = tmp;
}
void
__svc_versquiet_off(SVCXPRT *xprt)
{
u_long tmp;
tmp = ((u_long) xprt->xp_p3) & ~SVC_VERSQUIET;
xprt->xp_p3 = tmp;
}
void svc_versquiet(xprt)
SVCXPRT *xprt;
{
__svc_versquiet_on(xprt);
}
int
__svc_versquiet_get(SVCXPRT *xprt)
{
return ((int)xprt->xp_p3) & SVC_VERSQUIET;
}
#endif
/*
* Authentication error reply (MT-SAFE)
*/
void
svcerr_auth(SVCXPRT *xprt, struct svc_req *req, enum auth_stat why)
{
struct rpc_msg rply;
assert(xprt != NULL);
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_DENIED;
rply.rm_xid = req->rq_xid;
rply.rjcted_rply.rj_stat = AUTH_ERROR;
rply.rjcted_rply.rj_why = why;
SVC_REPLY(xprt, req, &rply);
}
/*
* Auth too weak error reply (MT-SAFE)
*/
void
svcerr_weakauth(SVCXPRT *xprt, struct svc_req *req)
{
assert(xprt != NULL);
svcerr_auth(xprt, req, AUTH_TOOWEAK);
}
/*
* Program unavailable error reply (MT-SAFE)
*/
void
svcerr_noprog(SVCXPRT *xprt, struct svc_req *req)
{
struct rpc_msg rply;
assert(xprt != NULL);
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
rply.rm_xid = req->rq_xid;
rply.acpted_rply.ar_verf = req->rq_verf;
rply.acpted_rply.ar_stat = PROG_UNAVAIL;
SVC_REPLY(xprt, req, &rply);
}
/*
* Program version mismatch error reply
*/
void
svcerr_progvers(SVCXPRT *xprt, struct svc_req *req, rpcvers_t low_vers,
rpcvers_t high_vers)
{
struct rpc_msg rply;
assert(xprt != NULL);
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
rply.rm_xid = req->rq_xid;
rply.acpted_rply.ar_verf = req->rq_verf;
rply.acpted_rply.ar_stat = PROG_MISMATCH;
rply.acpted_rply.ar_vers.low = (u_int32_t) low_vers;
rply.acpted_rply.ar_vers.high = (u_int32_t) high_vers;
SVC_REPLY(xprt, req, &rply);
}
/* ******************* SERVER INPUT STUFF ******************* */
/*
* Get server side input from some transport.
*/
#if !defined(_WIN32) /* XXX */
void
svc_getreq(int rdfds)
{
fd_set readfds;
FD_ZERO(&readfds);
readfds.fds_bits[0] = rdfds;
svc_getreqset(&readfds);
}
void
svc_getreqset(fd_set *readfds)
{
int bit, fd;
fd_mask mask, *maskp;
int sock;
assert(readfds != NULL);
maskp = readfds->fds_bits;
for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) {
for (mask = *maskp++; (bit = ffsl(mask)) != 0;
mask ^= (1L << (bit - 1))) {
/* sock has input waiting */
fd = sock + bit - 1;
svc_getreq_common(fd);
}
}
}
void
svc_getreq_poll(struct pollfd *pfdp, int pollretval)
{
int i;
int fds_found;
for (i = fds_found = 0; fds_found < pollretval; i++) {
struct pollfd *p = &pfdp[i];
if (p->revents) {
/* fd has input waiting */
fds_found++;
/*
* We assume that this function is only called
* via someone _select()ing from svc_fdset or
* _poll()ing from svc_pollset[]. Thus it's safe
* to handle the POLLNVAL event by simply turning
* the corresponding bit off in svc_fdset. The
* svc_pollset[] array is derived from svc_fdset
* and so will also be updated eventually.
*
* XXX Should we do an xprt_unregister() instead?
*/
if (!(p->revents & POLLNVAL))
svc_getreq_common(p->fd);
}
}
}
#endif /* _WIN32 */
#if defined(TIRPC_EPOLL)
void
svc_getreqset_epoll(struct epoll_event *events, int nfds)
{
int ix, code __attribute__ ((unused)) = 0;
SVCXPRT *xprt;
assert(events != NULL);
for (ix = 0; ix < nfds; ++ix) {
/* XXX should do constant-time validity check */
xprt = (SVCXPRT *) events[ix].data.ptr;
code = xprt->xp_ops->xp_getreq(xprt);
}
}
#endif /* TIRPC_EPOLL */
void
svc_getreq_common(int fd)
{
SVCXPRT *xprt;
bool code __attribute__ ((unused));
xprt = svc_xprt_get(fd);
if (xprt == NULL)
return;
code = xprt->xp_ops->xp_getreq(xprt);
return;
}
/* Allow internal or external getreq routines to validate xprt
* has not been recursively disconnected. (I don't completely buy the
* logic, but it should be unchanged (Matt)) */
bool
svc_validate_xprt_list(SVCXPRT *xprt)
{
bool code;
code = (xprt == svc_xprt_get(xprt->xp_fd));
return (code);
}
extern enum auth_stat
svc_auth_authenticate(struct svc_req *, struct rpc_msg *, bool *);
void
svc_dispatch_default(SVCXPRT *xprt, struct rpc_msg **ind_msg)
{
struct svc_req r;
struct rpc_msg *msg = *ind_msg;
svc_vers_range_t vrange;
svc_lookup_result_t lkp_res;
svc_rec_t *svc_rec;
enum auth_stat why;
bool no_dispatch = false;
r.rq_xprt = xprt;
r.rq_prog = msg->rm_call.cb_prog;
r.rq_vers = msg->rm_call.cb_vers;
r.rq_proc = msg->rm_call.cb_proc;
r.rq_cred = msg->rm_call.cb_cred;
r.rq_xid = msg->rm_xid;
/* first authenticate the message */
why = svc_auth_authenticate(&r, msg, &no_dispatch);
if ((why != AUTH_OK) || no_dispatch) {
svcerr_auth(xprt, &r, why);
return;
}
lkp_res = svc_lookup(&svc_rec, &vrange, r.rq_prog, r.rq_vers, NULL, 0);
switch (lkp_res) {
case SVC_LKP_SUCCESS:
/* call it */
(*svc_rec->sc_dispatch) (&r, xprt);
return;
case SVC_LKP_VERS_NOTFOUND:
svcerr_progvers(xprt, &r, vrange.lowvers, vrange.highvers);
break;
default:
svcerr_noprog(xprt, &r);
break;
}
}
bool
svc_getreq_default(SVCXPRT *xprt)
{
enum xprt_stat stat;
struct svc_req req = {.rq_xprt = xprt };
bool no_dispatch = false;
/* XXX !MT-SAFE */
/* now receive msgs from xprt (support batch calls) */
do {
if (SVC_RECV(xprt, &req)) {
/* now find the exported program and call it */
svc_vers_range_t vrange;
svc_lookup_result_t lkp_res;
svc_rec_t *svc_rec;
enum auth_stat why;
/* first authenticate the message */
why =
svc_auth_authenticate(&req, req.rq_msg,
&no_dispatch);
if ((why != AUTH_OK) || no_dispatch) {
svcerr_auth(xprt, &req, why);
goto call_done;
}
lkp_res =
svc_lookup(&svc_rec, &vrange, req.rq_prog,
req.rq_vers, NULL, 0);
switch (lkp_res) {
case SVC_LKP_SUCCESS:
(*svc_rec->sc_dispatch) (&req, xprt);
goto call_done;
break;
case SVC_LKP_VERS_NOTFOUND:
__warnx(TIRPC_DEBUG_FLAG_SVC,
"%s: dispatch prog vers notfound\n",
__func__);
svcerr_progvers(xprt, &req, vrange.lowvers,
vrange.highvers);
break;
default:
__warnx(TIRPC_DEBUG_FLAG_SVC,
"%s: dispatch prog notfound\n",
__func__);
svcerr_noprog(xprt, &req);
break;
}
/* dispose RPC header */
free_req_rpc_msg(&req);
}
/* SVC_RECV again? */
call_done:
/* dispose RPC header */
free_req_rpc_msg(&req); /* SAFE */
stat = SVC_STAT(xprt);
if (stat == XPRT_DIED) {
__warnx(TIRPC_DEBUG_FLAG_SVC,
"%s: stat == XPRT_DIED (%p)\n", __func__,
xprt);
SVC_DESTROY(xprt);
SVC_RELEASE(xprt, SVC_RELEASE_FLAG_NONE);
xprt = 0;
break;
} else {
/*
* Check if the xprt has been disconnected in a
* recursive call in the service dispatch routine.
* If so, then break.
*/
if (!svc_validate_xprt_list(xprt)) {
SVC_RELEASE(xprt, SVC_RELEASE_FLAG_NONE);
xprt = 0;
break;
}
}
} while (stat == XPRT_MOREREQS);
if (xprt) {
svc_rqst_rearm_events(xprt, SVC_RQST_FLAG_NONE);
SVC_RELEASE(xprt, SVC_RELEASE_FLAG_NONE);
}
return (stat);
}
bool
rpc_control(int what, void *arg)
{
int val;
switch (what) {
case RPC_SVC_CONNMAXREC_SET:
val = *(int *)arg;
if (val <= 0)
return false;
__svc_maxrec = val;
break;
case RPC_SVC_CONNMAXREC_GET:
*(int *)arg = __svc_maxrec;
break;
default:
return (false);
}
return (true);
}
#if defined(HAVE_BLKIN)
void __rpc_set_blkin_endpoint(SVCXPRT *xprt, const char *tag)
{
struct sockaddr_in *salocal_in;
struct sockaddr_in6 *salocal_in6;
struct sockaddr *salocal =
(struct sockaddr *)&xprt->xp_local.ss;
char saddr[INET6_ADDRSTRLEN];
int xp_port = 0;
switch (salocal->sa_family) {
case AF_INET:
salocal_in = (struct sockaddr_in *)salocal;
(void) inet_ntop(salocal->sa_family,
&salocal_in->sin_addr,
saddr, INET6_ADDRSTRLEN);
xp_port = ntohs(salocal_in->sin_port);
break;
case AF_INET6:
salocal_in6 = (struct sockaddr_in6 *)salocal;
(void) inet_ntop(salocal->sa_family,
&salocal_in6->sin6_addr,
saddr, INET6_ADDRSTRLEN);
xp_port = ntohs(salocal_in6->sin6_port);
break;
}
/* in Zipkin, traces are localized by a service name at an endpoint,
* plus an "address" which
* a) is represented by an integer
* b) is treated by libraries as if it is an ipv4 address
* c) is mostly ignored if it converts as 0 (see below)
*
* the blkin c interface doesn't copy the endpoint string, so we
* allocate one;
*/
xprt->blkin.svc_name = mem_zalloc(2*INET6_ADDRSTRLEN);
snprintf(xprt->blkin.svc_name, 2*INET6_ADDRSTRLEN,
"ntirpc-%s:%s:%d", tag, saddr, xp_port);
blkin_init_endpoint(&xprt->blkin.endp, "0.0.0.0", xp_port,
xprt->blkin.svc_name);
}
#endif
int
svc_shutdown(u_long flags)
{
int code = 0;
#ifdef USE_RPC_RDMA
/* wait until RDMA control threads have finished */
rpc_rdma_internals_fini();
#endif
/* finalize ioq */
work_pool_shutdown(&svc_work_pool);
/* dispose all xprts and support */
svc_xprt_shutdown();
/* release fd lock records */
rpc_dplx_shutdown();
/* release request event channels */
svc_rqst_shutdown();
/* XXX assert quiescent */
return (code);
}