The Lowest Layer of RPC from the Server Side
This section describes the lowest layer of RPC.
#include <stdio.h>
#include <rpc/rpc.h>
#include <utmp.h>
#include <rpcsvc/rusers.h>
main()
{
SVCXPRT *transp;
int nuser();
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL){
fprintf(stderr, "can't create an RPC server\n");
exit(1);
}
pmap_unset(RUSERSPROG, RUSERSVERS);
if (!svc_register(transp, RUSERSPROG, RUSERSVERS,
nuser, IPPROTO_UDP)) {
fprintf(stderr, "can't register RUSER service\n");
exit(1);
}
svc_run(); /* Never returns */
fprintf(stderr, "should never reach this point\n");
}
switch (rqstp->rq_proc) {
case NULLPROC:
if (!svc_sendreply(transp, xdr_void, 0))
fprintf(stderr, "can't reply to RPC call\n");
return;
case RUSERSPROC_NUM:
/*
* Code here to compute the number of users
* and assign it to the nusers variable
*/
if (!svc_sendreply(transp, xdr_u_long, &nusers))
fprintf(stderr, "can't reply to RPC call\n");
return;
default:
svcerr_noproc(transp);
return;
}
}
First, the server gets a transport handle, which is used for receiving and replying to RPC messages. The registerrpc routine calls the svcudp_create routine to get a User Datagram Protocol (UDP) handle. If a more reliable protocol is required, the svctcp_create routine can be called instead. If the argument to the svcudp_create routine is RPC_ANYSOCK, the RPC library creates a socket on which to receive and reply to remote procedure calls. Otherwise, the svcudp_create routine expects its argument to be a valid socket number. If a programmer specifies a socket, it can be bound or unbound. If it is bound to a port by the programmer, the port numbers of the svcudp_create routine and the clnttcp_create routine (the low-level client routine) must match.
If the programmer specifies the RPC_ANYSOCK argument, the RPC library routines open sockets. The svcudp_create and clntudp_create routines cause the RPC library routines to bind the appropriate socket, if not already bound.
A service may register its port number with the local port mapper service. This is done by specifying a nonzero protocol number in the svc_register routine. A programmer at the client machine can discover the server port number by consulting the port mapper at the server workstation. This is done automatically by specifying a zero port number in the clntudp_create or clnttcp_create routines.
After creating a service transport (SVCXPRT) handle, the next step is to call the pmap_unset routine. If the nusers server crashed earlier, this routine erases any trace of the crash before restarting. Specifically, the pmap_unset routine erases the entry for RUSERSPROG from the port mapper's tables.
Finally, the program number for nusers is associated with the nuser procedure. The final argument to the svc_register routine is normally the protocol being used, in this case IPPROTO_UDP. Registration is performed at the program level, rather than the procedure level.
The nuser user service routine must call and dispatch the appropriate eXternal Data Representation (XDR) routines based on the procedure number. The nuser routine has two requirements, unlike the registerrpc routine which performs them automatically. The first is that the NULLPROC procedure (currently 0) return with no results. This is a simple test for detecting whether a remote program is running. Second, the subroutine checks for invalid procedure numbers. If one is detected, the svcerr_noproc routine is called to handle the error.
The user service routine serializes the results and returns them to the RPC caller through the svc_sendreply routine. The first parameter of this routine is the SVCXPRT handle, the second is the XDR routine that indicates return data type, and the third is a pointer to the data to be returned.
case RUSERSPROC_BOOL: {
int bool;
unsigned nuserquery;
if (!svc_getargs(transp, xdr_u_int, &nuserquery) {
svcerr_decode(transp);
return;
}
/*
* Code to set nusers = number of users
*/
if (nuserquery == nusers)
bool = TRUE;
else
bool = FALSE;
if (!svc_sendreply(transp, xdr_bool, &bool)) {
fprintf(stderr, "can't reply to RPC call\n");
return (1);
}
return;
}
The svc_getargs routine takes the following arguments: an SVCXPRT handle, the XDR routine, and a pointer that indicates where to place the input.