/* Parts of this file are derived from the original Sun (ONC) RPC
*/
/*
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
* media and as a part of the software program in whole or part. Users
* may copy or modify Sun RPC without charge, but are not authorized
* to license or distribute it to anyone else except as part of a product or
* program developed by the user.
*
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun RPC is provided with no support and without any obligation on the
* part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* 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 <rpc/rpc.h>
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <rpc/rpc_router_ioctl.h>
#include <debug.h>
#include <pthread.h>
#include <stdlib.h>
extern XDR *xdr_init_common(const char *name, int is_client);
extern void xdr_destroy_common(XDR *xdr);
extern int r_control(int handle, const uint32 cmd, void *arg);
extern void grabPartialWakeLock();
extern void releaseWakeLock();
#include <stdio.h>
#include <errno.h>
#include <string.h>
typedef struct registered_server_struct {
/* MUST BE AT OFFSET ZERO! The client code assumes this when it overwrites
the XDR for server entries which represent a callback client. Those
server entries do not have their own XDRs.
*/
XDR *xdr;
/* Because the xdr is NULL for callback clients (as opposed to true
servers), we keep track of the program number and version number in this
structure as well.
*/
rpcprog_t x_prog; /* program number */
rpcvers_t x_vers; /* program version */
int active;
struct registered_server_struct *next;
SVCXPRT *xprt;
__dispatch_fn_t dispatch;
} registered_server;
struct SVCXPRT {
fd_set fdset;
int max_fd;
pthread_attr_t thread_attr;
pthread_t svc_thread;
pthread_mutexattr_t lock_attr;
pthread_mutex_t lock;
registered_server *servers;
volatile int num_servers;
};
static pthread_mutex_t xprt_lock = PTHREAD_MUTEX_INITIALIZER;
int xprt_refcount;
SVCXPRT *the_xprt; /* FIXME: have a list or something */
/*
This routine is only of interest if a service implementor does not
call svc_run(), but instead implements custom asynchronous event
processing. It is called when the select() system call has
determined that an RPC request has arrived on some RPC socket(s);
rdfds is the resultant read file descriptor bit mask. The routine
returns when all sockets associated with the value of rdfds have
been serviced.
*/
void svc_dispatch(registered_server *svc, SVCXPRT *xprt);
static void* svc_context(void *__u)
{
SVCXPRT *xprt = (SVCXPRT *)__u;
int n;
struct timeval tv;
volatile fd_set rfds;
while(xprt->num_servers) {
rfds = xprt->fdset;
tv.tv_sec = 1; tv.tv_usec = 0;
n = select(xprt->max_fd + 1, (fd_set *)&rfds, NULL, NULL, &tv);
if (n < 0) {
E("select() error %s (%d)\n", strerror(errno), errno);
continue;
}
if (n) {
grabPartialWakeLock();
for (n = 0; n <= xprt->max_fd; n++) {
if (FD_ISSET(n, &rfds)) {
/* the file descriptor points to the service instance; we
simply look that service by its file descriptor, and
call its service function. */
registered_server *trav = xprt->servers;
for (; trav; trav = trav->next)
if (trav->xdr->fd == n) {
/* read the entire RPC */
if (trav->xdr->xops->read(trav->xdr) == 0) {
E("%08x:%08x ONCRPC read error: aborting!\n",
trav->xdr->x_prog, trav->xdr->x_vers);
abort();
}
svc_dispatch(trav, xprt);
break;
}
} /* if fd is set */
} /* for each fd */
releaseWakeLock();
}
}
D("RPC-server thread exiting!\n");
return NULL;
}
SVCXPRT *svcrtr_create (void)
{
SVCXPRT *xprt;
pthread_mutex_lock(&xprt_lock);
if (the_xprt) {
D("The RPC transport has already been created.\n");
xprt = the_xprt;
} else {
xprt = calloc(1, sizeof(SVCXPRT));
if (xprt) {
FD_ZERO(&xprt->fdset);
xprt->max_fd = 0;
pthread_attr_init(&xprt->thread_attr);
pthread_attr_setdetachstate(&xprt->thread_attr,
PTHREAD_CREATE_DETACHED);
pthread_mutexattr_init(&xprt->lock_attr);
// pthread_mutexattr_settype(&xprt->lock_attr,
// PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&xprt->lock, &xprt->lock_attr);
}
}
pthread_mutex_unlock(&xprt_lock);
return xprt;
}
void svc_destroy(SVCXPRT *xprt)
{
/* the last call to xprt_unregister() does the job */
}
/* NOTE: this function must always be called with the xprt->lock held! */
static registered_server* svc_find_nosync(SVCXPRT *xprt,
rpcprog_t prog, rpcvers_t vers,
registered_server **prev)
{
registered_server *trav;
trav = xprt->servers;
if (prev) *prev = NULL;
for (; trav; trav = trav->next) {
if (trav->x_prog == prog && trav->x_vers == vers)
break;
if (prev) *prev = trav;
}
return trav;
}
registered_server* svc_find(SVCXPRT *xprt,
rpcprog_t prog, rpcvers_t vers)
{
pthread_mutex_lock(&xprt->lock);
registered_server *svc = svc_find_nosync(xprt, prog, vers, NULL);
pthread_mutex_unlock(&xprt->lock);
return svc;
}
bool_t svc_register (SVCXPRT *xprt, rpcprog_t prog, rpcvers_t vers,
__dispatch_fn_t dispatch,
rpcprot_t protocol)
{
struct rpcrouter_ioctl_server_args args;
registered_server* svc;
pthread_mutex_lock(&xprt->lock);
D("registering for service %08x:%d\n", (uint32_t)prog, (int)vers);
svc = svc_find_nosync(xprt, prog, vers, NULL);
if (svc) {
E("service is already registered!\n");
pthread_mutex_unlock(&xprt->lock);
return svc->dispatch == dispatch;
}
svc = malloc(sizeof(registered_server));
/* If the program number of the RPC server ANDs with 0x01000000, then it is
not a true RPC server, but a callback client for an existing RPC client.
For example, if you have an RPC client with the program number
0x30000000, then its callback client will have a program number
0x31000000. RPC calls on program number 0x31000000 will arrive on the
RPC client 0x30000000.
*/
if (prog & 0x01000000) {
D("