Digital TCP/IP Services for OpenVMS
ONC RPC Programming


Previous | Contents

3.8.2 Program Versions on the Client Side

The network can have different versions of an RPC server. For example, one server might run RUSERSVERS_ORIG, and another might run RUSERSVERS_SHORT.

If the version of the server running does not match the version number in the client creation routines, then clnt_call fails with a RPC_PROGVERSMISMATCH error. You can determine the version numbers supported by the server and then create a client handle with an appropriate version number. To do this, use clnt_create_vers (refer to Chapter 5 for more information) or the routine shown in

Examples

3-11.
  1. (1) The program begins by creating the client handle with the clnt_create routine.
  2. (2) Next, the clnt_call routine attempts to call the remote program. Because of the previous clnt_create call, the program version requested is RUSERVERS_SHORT. If the clnt_call routine is successful, the version was correct.
  3. (3) If the clnt_call attempt failed, then the program checks the failure reason. If it is RPC_PROGVERSMISMATCH, the program goes on to find the versions supported.
  4. (4) In this step the program parses the error status and retrieves the highest and lowest versions supported by the server. The program then checks to see if the version RUSERSVERS_SHORT is in the supported range.
  5. (5) If the RUSERSVERS_SHORT version is supported, the program destroys the old client handle using the clnt_destroy routine. It then creates a new handle using the RUSERSVERS_SHORT version.
  6. (6) Finally, the program uses the new client handle to make a call to the server using the RUSERSVERS_SHORT version.

Examples

3-11 Determining Server-Supported Versions and Creating Associated Client Handles
 
/* 
 * A sample client to sense server versions 
 */ 
#include <rpc/rpc.h> 
#include <stdio.h> 
#include "rusers.h" 
 
main(argc,argv) 
     int argc; 
     char **argv; 
{ 
     struct rpc_err rpcerr; 
     struct timeval to; 
     CLIENT *clnt; 
     enum clnt_stat status; 
     int maxvers, minvers; 
     int exit(); 
     u_short num_s; 
     u_int num_l; 
     char *host; 
 
     host = argv[1]; 
 
     clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_SHORT, "udp"); (1)
 
     if (clnt == NULL) { 
          clnt_pcreateerror("clnt"); 
          exit(-1); 
     } 
 
     to.tv_sec = 10; /* set the time outs */ 
     to.tv_usec = 0; 
     status = clnt_call(clnt, RUSERSPROC_NUM, (2)
       xdr_void, NULL, xdr_u_short, &num_s, to); 
 
     if (status == RPC_SUCCESS) { 
          /* We found the latest version number */ 
          clnt_destroy(clnt); 
          printf("num = %d\n",num_s); 
          exit(0); 
     } 
 
     if (status != RPC_PROGVERSMISMATCH) { (3)
          /* Some other error */ 
          clnt_perror(clnt, "rusers"); 
          exit(-1); 
     } 
 
     clnt_geterr(clnt, &rpcerr); (4)
     maxvers = rpcerr.re_vers.high; /*highest version supported */ 
     minvers = rpcerr.re_vers.low;  /*lowest version supported */ 
 
     if (RUSERSVERS_ORIG < minvers || 
          RUSERS_ORIG > maxvers) { 
          /* doesn't meet minimum standards */ 
          clnt_perror(clnt, "version mismatch"); 
          exit(-1); 
     } 
 
     /* This version not supported */ 
     clnt_destroy(clnt);     /* destroy the earlier handle */ (5)
     clnt = clnt_create(host, RUSERSPROG, 
       RUSERSVERS_ORIG, "udp"); /* try different version */ 
 
     if (clnt == NULL) { 
          clnt_pcreateerror("clnt"); 
          exit(-1); 
     } 
 
     status = clnt_call(clnt, RUSERSPROCNUM, (6)
       xdr_void, NULL, xdr_u_long, &num_l, to); 
 
     if (status == RPC_SUCCESS) { 
          /* We found the latest version number */ 
          printf("num = %d\n", num_l); 
     } else { 
          clnt_perror(clnt, "rusers"); 
          exit(-1); 
     } 
} 
 

3.8.3 Using the TCP Transport

Examples 3_12 , 3_13 , and 3-14 work like the remote file copy command RCP. The initiator of the RPC call, snd, takes its standard input and sends it to the server rcv, which prints it on standard output. The RPC call uses TCP. The example also shows how an XDR procedure behaves differently on serialization than on deserialization.

Examples

3-12 RPC Example that Uses TCP Protocol---XDR Routine
/* 
 * The XDR routine: 
 *              on decode, read from wire, write onto fp 
 *              on encode, read from fp, write onto wire 
 */ 
 
#include <stdio.h> 
#include <rpc/rpc.h> 
 
xdr_rcp(xdrs, fp) 
     XDR *xdrs; 
     FILE *fp; 
{ 
     unsigned long size; 
     char buf[BUFSIZ], *p; 
 
     if (xdrs->x_op == XDR_FREE)/* nothing to free */ 
          return 1; 
     while (1) { 
          if (xdrs->x_op == XDR_ENCODE) { 
               if ((size = fread(buf, sizeof(char), BUFSIZ, 
                    fp)) == 0 && ferror(fp)) { 
                    fprintf(stderr, "can't fread\n"); 
                    return (1); 
               } 
          } 
          p = buf; 
          if (!xdr_bytes(xdrs, &p, &size, BUFSIZ)) 
               return (0); 
          if (size == 0) 
               return (1); 
          if (xdrs->x_op == XDR_DECODE) { 
               if (fwrite(buf, sizeof(char), size, 
                    fp) != size) { 
                    fprintf(stderr, "can't fwrite\n"); 
                    return (1); 
               } 
          } 
     } 
} 

Examples

3-13 RPC Example that Uses TCP Protocol--- Client
/* 
 * snd.c - the sender routines 
 */ 
#include <stdio.h> 
#include <netdb.h> 
#include <rpc/rpc.h> 
#include <sys/socket.h> 
#include "rcp.h"        /* for prog, vers definitions */ 
 
main(argc, argv) 
     int argc; 
     char **argv; 
{ 
     int xdr_rcp(); 
     int err; 
     int exit(); 
     int callrpctcp(); 
 
     if (argc < 2) { 
          fprintf(stderr, "usage: %s servername\n", argv[0]); 
          exit(-1); 
     } 
     if ((err = callrpctcp(argv[1], RCPPROG, RCPPROC, 
       RCPVERS, xdr_rcp, stdin, xdr_void, 0) > 0)) { 
          clnt_perrno(err); 
          fprintf(stderr, "can't make RPC call\n"); 
          exit(1); 
     } 
     exit(0); 
} 
 
int 
callrpctcp(host, prognum, procnum, versnum, 
  inproc, in, outproc, out) 
     char *host, *in, *out; 
     xdrproc_t inproc, outproc; 
{ 
     struct sockaddr_in server_addr; 
     int socket = RPC_ANYSOCK; 
     enum clnt_stat clnt_stat; 
     struct hostent *hp; 
     register CLIENT *client; 
     struct timeval total_timeout; 
     void bcopy(); 
 
     if ((hp = gethostbyname(host)) == NULL) { 
          fprintf(stderr, "can't get addr for '%s'\n", host); 
          return (-1); 
     } 
     bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, 
       hp->h_length); 
     server_addr.sin_family = AF_INET; 
     server_addr.sin_port =  0; 
     if ((client = clnttcp_create(&server_addr, prognum, 
       versnum, &socket, BUFSIZ, BUFSIZ)) == NULL) { 
          clnt_pcreateerror("rpctcp_create"); 
          return (-1); 
     } 
     total_timeout.tv_sec = 20; 
     total_timeout.tv_usec = 0; 
     clnt_stat = clnt_call(client, procnum, 
       inproc, in, outproc, out, total_timeout); 
     clnt_destroy(client); 
     return ((int)clnt_stat); 
} 

Examples

3-14 RPC Example that Uses TCP Protocol--- Server
/* 
 * rcv.c - the receiving routines 
 */ 
#include <stdio.h> 
#include <rpc/rpc.h> 
#include <rpc/pmap_clnt.h> 
#include "rcp.h"        /* for prog, vers definitions */ 
 
main() 
{ 
     register SVCXPRT *transp; 
     int rcp_service(), exit(); 
 
     if ((transp = svctcp_create(RPC_ANYSOCK, 
       BUFSIZ, BUFSIZ)) == NULL) { 
          fprintf(stderr,"svctcp_create: error\n"); 
          exit(1); 
     } 
     pmap_unset(RCPPROG, RCPVERS); 
     if (!svc_register(transp, RCPPROG, 
       RCPVERS, rcp_service, IPPROTO_TCP)) { 
          fprintf(stderr, "svc_register: error\n"); 
          exit(1); 
     } 
     svc_run();  /* never returns */ 
     fprintf(stderr, "svc_run should never return\n"); 
} 
 
int 
rcp_service(rqstp, transp) 
     register struct svc_req *rqstp; 
     register SVCXPRT *transp; 
{ 
     int xdr_rcp(); 
 
     switch (rqstp->rq_proc) { 
     case NULLPROC: 
          if (svc_sendreply(transp, xdr_void, 0) == 0) 
               fprintf(stderr, "err: rcp_service"); 
          return; 
     case RCPPROC: 
          if (!svc_getargs(transp, xdr_rcp, stdout)) { 
               svcerr_decode(transp); 
               return; 
          } 
          if (!svc_sendreply(transp, xdr_void, 0)) 
               fprintf(stderr, "can't reply\n"); 
          return; 
     default: 
          svcerr_noproc(transp); 
          return; 
     } 
} 

3.8.4 Callback Procedures

It is sometimes useful to have a server become a client, and make an RPC call back to the process that is its client. An example of this is remote debugging, where the client is a window-system program and the server is a debugger running on the remote system. Mostly, the user clicks a mouse button at the debugging window (converting this to a debugger command), and then makes an RPC call to the server (where the debugger is actually running), telling it to execute that command. However, when the debugger reaches a breakpoint, the roles are reversed, and the debugger wants to make an RPC call to the window program, so it can tell the user that a breakpoint has been reached.

Callbacks are also useful when the client cannot block (that is, wait) to hear back from the server (possibly because of excessive processing in serving the request). In such cases, the server could acknowledge the request and use a callback to reply.

To do an RPC callback, you need a program number on which to make the RPC call. The program number is generated dynamically, so it must be in the transient range 0x40000000 to 0c5fffffff. The sample routine gettransient returns a valid program number in the transient range, and registers it with the Portmapper. It only communicates with the Portmapper running on the same system as the gettransient routine itself.

The call to pmap_set is a test-and-set operation, because it indivisibly tests whether a program number has been registered; if not, it is reserved. The following example shows the sample gettransient routine:

#include <stdio.h> 
#include <rpc/rpc.h> 
 
gettransient(proto, vers, portnum) 
     int proto; 
     u_long vers; 
     u_short portnum; 
{ 
     static u_long prognum = 0x40000000; 
 
     while (!pmap_set(prognum++, vers, proto, portnum)) 
          continue; 
     return (prognum - 1); 
} 

Note that the call to ntohs for portnum is unnecessary because it was already passed in host byte order (as pmap_set expects).

The following list describes how the client/server programs in

Examples

3_15 and

Examples

3-16
use the gettransient routine:

In

Examples

3_15 and

Examples

3-16
, both the client and the server are on the same system; otherwise, host name handling would be different.

Examples

3-15 Client Usage of the gettransient Routine
/* 
* client 
*/ 
#include <stdio.h> 
#include <rpc/rpc.h> 
#include "example.h" 
 
int callback(); 
 
main() 
{ 
     int tmp_prog; 
     char hostname[256]; 
     SVCXPRT *xprt; 
     int stat; 
     int callback(), gettransient(); 
     int exit(); 
 
     gethostname(hostname, sizeof(hostname)); 
     if ((xprt = svcudp_create(RPC_ANYSOCK)) == NULL) { 
          fprintf(stderr, "rpc_server: svcudp_create\n"); 
          exit(1); 
     } 
     if ((tmp_prog = gettransient(IPPROTO_UDP, 1, 
          xprt->xp_port)) == 0) { 
          fprintf(stderr,"Client: failed to get transient number\n"); 
          exit(1); 
     } 
     fprintf(stderr, "Client: got program number %08x\n", tmp_prog); 
 
     /* protocol is 0 - gettransient does registering */ 
 
     (void)svc_register(xprt, tmp_prog, 1, callback, 0); 
     stat = callrpc(hostname, EXAMPLEPROG, EXAMPLEVERS, 
       EXAMPLEPROC_CALLBACK,xdr_int,&tmp_prog,xdr_void,0); 
     if (stat != RPC_SUCCESS) { 
          clnt_perrno(stat); 
          exit(1); 
     } 
     svc_run(); 
     fprintf(stderr, "Error: svc_run shouldn't return\n"); 
} 
int 
callback(rqstp, transp) 
     register struct svc_req *rqstp; 
     register SVCXPRT *transp; 
{ 
     int exit(); 
 
     switch (rqstp->rq_proc) { 
 
     case 0: 
          if (!svc_sendreply(transp, xdr_void, 0)) { 
               fprintf(stderr, "err: exampleprog\n"); 
               return (1); 
          } 
          return (0); 
 
     case 1: 
          fprintf(stderr, "Client: got callback\n"); 
          if (!svc_sendreply(transp, xdr_void, 0)) { 
               fprintf(stderr, "Client: error replyingto exampleprog\n"); 
               return (1); 
          } 
          exit(0); 
     } 
     return (0); 
} 

Examples

3-16 Server Usage of the gettransient Routine
/* 
* server 
*/ 
#include <stdio.h> 
#include <rpc/rpc.h> 
#include <sys/signal.h> 
#include "example.h" 
 
char hostname[256]; 
void docallback(int); 
int pnum = -1;          /* program number for callback routine */ 
 
main() 
{ 
     char *getnewprog(); 
 
     gethostname(hostname, sizeof(hostname)); 
     registerrpc(EXAMPLEPROG, EXAMPLEVERS, 
       EXAMPLEPROC_CALLBACK, getnewprog, xdr_int, xdr_void); 
     signal(SIGALRM, docallback); 
     alarm(10); 
     svc_run(); 
     fprintf(stderr, "Server: error, svc_run shouldn't return\n"); 
} 
 
char * 
getnewprog(pnump) 
     int *pnump; 
{ 
     pnum = *(int *)pnump; 
     return NULL; 
} 
void 
docallback(int signum) 
{ 
     int ans; 
 
     if (pnum == -1) { 
          fprintf(stderr, "Server: program number not received yet"); 
          signal(SIGALRM, docallback); 
          alarm(10); 
          return;     
     } 
     ans = callrpc(hostname, pnum, 1, 1, xdr_void, 0, 
       xdr_void, 0); 
     if (ans != RPC_SUCCESS) { 
          fprintf(stderr, "Server: %s\n",clnt_sperrno(ans)); 
          exit(1); 
     } 
     if (ans == RPC_SUCCESS) 
          exit(0); 
 
} 


Chapter 4
External Data Representation

This chapter describes the external data representation (XDR) standard, a set of routines that enable C programmers to describe arbitrary data structures in a system-independent way. For a formal specification of the XDR standard, see RFC 1014: XDR: External Data Representation Standard.

XDR is the backbone of ONC RPC, because data for remote procedure calls is transmitted using the XDR standard. ONC RPC uses the XDR routines to transmit data that is read or written from several types of systems. For a complete specification of the XDR routines, see Chapter 8.

This chapter also contains a short tutorial overview of the XDR routines, a guide to accessing currently available XDR streams, and information on defining new streams and data types.

XDR was designed to work across different languages, operating systems, and computer architectures. Most users (particularly RPC users) only need the information on number filters (Section 4.2.1) floating-point filters (Section 4.2.2) and enumeration filters (Section 4.2.3). Programmers who want to implement RPC and XDR on new systems should read the rest of the chapter.


Note

You can use RPCGEN to write XDR routines regardless of whether RPC calls are being made.

C programs that need XDR routines must include the file <rpc/rpc.h>, which contains all necessary interfaces to the XDR system. The object library UCX$RPC:UCX$RPCXDR.OLB contains all the XDR routines, so you can link as you usually would when using a library. If you wish to use a shareable version of the library, reference the library SYS$SHARE:UCX$RPCXDR_SHR in your LINK options file.

4.1 Usefulness of XDR

Consider the following two programs, writer.c and reader.c:

#include <stdio.h> 
 
main()                  /* writer.c */ 
{ 
     long i; 
 
     for (i = 0; i < 8; i++) { 
          if (fwrite((char *)&i, sizeof(i), 1, stdout) != 1) { 
               fprintf(stderr, "failed!\n"); 
               exit(1); 
          } 
     } 
     exit(0); 
} 
 
#include <stdio.h> 
 
main()                  /* reader.c */ 
{ 
     long i, j; 
 
     for (j = 0; j < 8; j++) { 
          if (fread((char *)&i, sizeof (i), 1, stdin) != 1) { 
               fprintf(stderr, "failed!\n"); 
               exit(1); 
          } 
          printf("%ld ", i); 
     } 
     printf("\n"); 
     exit(0); 
} 

The two programs appear to be portable because:

Piping the output of the writer.c program to the reader.c program gives identical results on an Alpha computer or on a Sun computer, as shown:

sun% writer | reader 
0 1 2 3 4 5 6 7 
sun% 
 
$ writer | reader 
0 1 2 3 4 5 6 7 
$ 

With local area networks and Berkeley UNIX 4.2 BSD came the concept of network pipes, in which a process produces data on one system, and a second process on another system uses this data. You can construct a network pipe with writer.c and reader.c. Here, the first process (on a Sun Microsystem's computer) produces data used by a second process (on a Digital Alpha computer):

sun% writer | rsh alpha reader 
0 16777216 33554432 50331648 67108864 83886080 100663296 
117440512 
sun% 

You get identical results by executing writer.c on the Digital Alpha computer and reader.c on the Sun computer. These results occur because the byte ordering of long integers differs between the Alpha computer and the Sun computer, although the word size is the same. Note that 16777216 is equal to 224. When 4 bytes are reversed, the 1 is in the 24th bit.

Whenever data is shared by two or more system types, there is a need for portable data. You can make programs data-portable by replacing the read and write calls with calls to an XDR library routine xdr_long, which is a filter that recognizes the standard representation of a long integer in its external form. Here are the revised versions of writer.c and reader.c:

/*        Revised Version of writer.c       */ 
 
 
#include <stdio.h> 
#include <rpc/rpc.h>    /* xdr is a sub-library of rpc */ 
 
main()          /* writer.c */ 
{ 
     XDR xdrs; 
     long i; 
     xdrstdio_create(&xdrs, stdout, XDR_ENCODE); 
     for (i = 0; i < 8; i++) { 
          if (!xdr_long(&xdrs, &i)) { 
               fprintf(stderr, "failed!\n"); 
               exit(1); 
          } 
     } 
     exit(0); 
} 
 
/*        Revised Version of reader.c      */ 
 
#include <stdio.h> 
#include <rpc/rpc.h>    /* XDR is a sub-library of RPC */ 
 
main()          /* reader.c */ 
{ 
     XDR xdrs; 
     long i, j; 
     xdrstdio_create(&xdrs, stdin, XDR_DECODE); 
     for (j = 0; j < 8; j++) { 
          if (!xdr_long(&xdrs, &i)) { 
               fprintf(stderr, "failed!\n"); 
               exit(1); 
          } 
          printf("%ld ", i); 
     } 
     printf("\n"); 
     exit(0); 
} 

The new programs were executed on an Alpha computer, a Sun computer, and from a Sun computer to an Alpha computer; the results are as follows:

sun% writer | reader 
0 1 2 3 4 5 6 7 
sun% 
 
$ writer | reader 
0 1 2 3 4 5 6 7 
$ 
 
sun% writer | rsh alpha reader 
0 1 2 3 4 5 6 7 
sun% 

Note

Arbitrary data structures create portability problems, particularly with alignment and pointers:

4.1.1 A Canonical Standard

The XDR approach to standardizing data representations is canonical, because XDR defines a single byte order (big-endian), a single floating-point representation (IEEE), and so on. A program running on any system can use XDR to create portable data by translating its local representation to the XDR standard. Similarly, any such program can read portable data by translating the XDR standard representation to the local equivalent.

The single standard treats separately those programs that create or send portable data and those that use or receive the data. A new system or language has no effect on existing portable data creators and users. Any new system simply uses the canonical standards of XDR; the local representations of other system are irrelevant. To existing programs on other systems, the local representations of the new system are also irrelevant. There are strong precedents for the canonical approach of XDR. For example, TCP/IP, UDP/IP, XNS, Ethernet, and all protocols below layer 5 of the ISO model, are canonical protocols. The advantage of any canonical approach is simplicity; in the case of XDR, a single set of conversion routines is written once.

The canonical approach does have one disadvantage of little practical importance. Suppose two little-endian systems transfer integers according to the XDR standard. The sending system converts the integers from little-endian byte order to XDR (big-endian) byte order, and the receiving system does the reverse. Because both systems observe the same byte order, the conversions were really unnecessary. Fortunately, the time spent converting to and from a canonical representation is insignificant, especially in networking applications. Most of the time required to prepare a data structure for transfer is not spent in conversion but in traversing the elements of the data structure.

4.1.2 The XDR Library

The XDR library enables you to write and read arbitrary C constructs consistently. This makes it useful even when the data is not shared among systems on a network. The XDR library can do this because it has filter routines for strings (null-terminated arrays of bytes), structures, unions, and arrays. Using more primitive routines, you can write your own specific XDR routines to describe arbitrary data structures, including elements of arrays, arms of unions, or objects pointed at from other structures. The structures themselves may contain arrays of arbitrary elements, or pointers to other structures.

The previous writer.c and reader.c routines manipulate data by using standard I/O routines, so xdrstdio_create was used. The parameters to XDR stream creation routines vary according to their function. For example, xdrstdio_create takes the following parameters:

It is not necessary for RPC users to create XDR streams; the RPC system itself can create these streams and pass them to the users. There is a family of XDR stream creation routines in which each member treats the stream of bits differently.

The xdr_long primitive is characteristic of most XDR library primitives and all client XDR routines for two reasons:

In this case, xxx is long, and the corresponding XDR routine is a primitive, xdr_long. The client could also define an arbitrary structure xxx in which case the client would also supply the routine xdr_xxx, describing each field by calling XDR routines of the appropriate type. In all cases, the first parameter, xdrs, is treated as an opaque handle and passed to the primitive routines.

XDR routines are direction-independent; that is, the same routines are called to serialize or deserialize data. This feature is important for portable data. Calling the same routine for either operation practically guarantees that serialized data can also be deserialized. Thus, one routine is used by both producer and consumer of networked data.

You implement direction independence by passing a pointer to an object rather than the object itself (only with deserialization is the object modified). If needed, the user can obtain the direction of the XDR operation. See Section 4.3 for details.

For a more complicated example, assume that a person's gross assets and liabilities are to be exchanged among processes, and each is a separate data type:

struct gnumbers { 
     long g_assets; 
     long g_liabilities; 
}; 

The corresponding XDR routine describing this structure would be as follows:

bool_t                  /* TRUE is success, FALSE is failure */ 
xdr_gnumbers(xdrs, gp) 
     XDR *xdrs; 
     struct gnumbers *gp; 
{ 
     if (xdr_long(xdrs, &gp->g_assets) && 
       xdr_long(xdrs, &gp->g_liabilities)) 
          return(TRUE); 
     return(FALSE); 
} 

In the preceding example, the parameter, xdrs, is never inspected or modified; it is only passed to subcomponent routines. The program must inspect the return value of each XDR routine call and stop immediately and return FALSE upon subroutine failure.

The preceding example also shows that the type bool_t is declared as an integer whose only value is TRUE (1) or FALSE (0). The following definitions apply:

#define bool_t  int 
#define TRUE    1 
#define FALSE   0 

With these conventions, you can rewrite xdr_gnumbers as follows:

bool_t 
xdr_gnumbers(xdrs, gp) 
     XDR *xdrs; 
     struct gnumbers *gp; 
{ 
     return(xdr_long(xdrs, &gp->g_assets) && 
       xdr_long(xdrs, &gp->g_liabilities)); 
} 

Either coding style can be used.

4.2 XDR Library Primitives

The following sections describe the XDR primitives--- basic and constructed data types---and XDR utilities. The include file <rpc/xdr.h>, (automatically included by <rpc/rpc.h>), defines the interface to these primitives and utilities.

4.2.1 Number and Single-Character Filters

The XDR library provides primitives that translate between numbers and single characters and their corresponding external representations. Primitives include the set of numbers in:

[signed, unsigned] * [char, short, int, long, hyper] 

Specifically, the ten primitives are:

bool_t xdr_char(xdrs, cp) 
     XDR *xdrs; 
     char *cp; 
 
bool_t xdr_u_char(xdrs, ucp) 
     XDR *xdrs; 
     unsigned char *ucp; 
     
bool_t xdr_short(xdrs, sip) 
     XDR *xdrs; 
     short *sip; 
 
bool_t xdr_u_short(xdrs, sup) 
     XDR *xdrs; 
     u_short *sup; 
 
bool_t xdr_int(xdrs, ip) 
     XDR *xdrs; 
     int *ip; 
 
bool_t xdr_u_int(xdrs, up) 
     XDR *xdrs; 
     unsigned *up; 
 
bool_t xdr_long(xdrs, lip) 
     XDR *xdrs; 
     long *lip; 
 
bool_t xdr_u_long(xdrs, lup) 
     XDR *xdrs; 
     u_long *lup; 
 
bool_t xdr_hyper(xdrs, hp) 
     XDR *xdrs; 
     longlong_t *hp; 
 
bool_t xdr_u_hyper(xdrs, uhp) 
     XDR *xdrs; 
     u_longlong_t *uhp; 
 


Previous | Next | Contents