RPCGEN(NC) UNIX System V
Name
rpcgen - An RPC protocol compiler
Syntax
rpcgen -h [-o outfile] [inputfile]
rpcgen -c [-o outfile] [infile]
rpcgen infile
rpcgen [-s transport]* [-o outfile] [infile]
Description
rpcgen is a tool that generates C code to implement an RPC
protocol. The input to rpcgen is a language with striking
similarity to C known as RPCL (Remote Procedure Call
Language). rpcgen operates in four modes:
⊕ The first mode is used to convert RPCL definitions into
C definitions for use as a header file.
⊕ The second mode compiles the XDR routines required to
serialize the protocol described by RPCL.
⊕ The third mode compiles both, leaving the header file
in a file named infile with an .h extension and the XDR
routines in infile with an xdr.c extension.
⊕ The fourth mode is used to compile an RPC server
skeleton, so that all you have to do is write local
procedures that know nothing about RPC in order to
implement an RPC server.
The input may contain C-style comments and preprocessor
directives. Comments are ignored, while the directives are
simply stuffed uninterpreted in the output header file.
You can customize some of your XDR routines by leaving those
data types undefined. For every data type that is
undefined, rpcgen will assume that there exists a routine
with the name xdr prepended to the name of the undefined
type.
It is highly recommended that you read the chapters on RPC
and XDR in the SCO NFS Programmer's Guide before using this
utility.
Options
-c Compile XDR routines.
-h Compile C data-definitions (a header file)
-o outfile
Specify the name of the output file. If none is
specified, standard output is used (-c, -h and -s modes
only).
-s transport
Compile a server, using the given transport. The
supported transports are udp and tcp. This option may
be invoked more than once so as to compile a server
that serves multiple transports.
Usage
RPCL Syntax Summary
This summary of RPCL syntax, which is used for rpcgen input,
is intended more for aiding comprehension than as an exact
statement of the language.
Primitive Data Types
[ unsigned ] char
[ unsigned ] short
[ unsigned ] int
[ unsigned ] long
unsigned
float
double
void
bool
Except for the added boolean data-type bool, RPCL is
identical to C. rpcgen converts bool declarations to int
declarations in the output header file (literally it is
converted to a boolt, which has been #define'd to be an
int). Also, void declarations may appear only inside of
union and program definitions. For those averse to typing
the prefix unsigned, the abbreviations uchar, ushort,
uint and ulong are available.
Declarations
RPCL allows only three kinds of declarations:
⊕ simple-declaration
type-name object-ident
For example,
long a;
⊕ pointer-declaration
type-name *object-ident
For example,
char *b;
⊕ vector-declaration
type-name object-ident[size]
(size can be either an integer or a symbolic constant)
For example,
opaque c[10];
RPCL declarations contain both limitations and extensions
with respect to C. The limitations are that you cannot
declare multidimensional arrays or pointers-to-pointers in-
line (You can still declare them though, using typedef).
There are two extensions:
⊕ Opaque data is declared as a vector as follows:
opaque object-ident [ size ]
In the protocol, this results in an object of size bytes.
Note that this is not the same as a declaration of size
characters, since XDR characters are 32-bits. Opaque
declarations are compiled in the output header file into
character array declarations of size bytes.
⊕ Strings are special and are declared as a vector
declaration:
string object-ident [ max-size ]
If max-size is unspecified, then there is essentially no
limit to the maximum length of the string. String
declarations get compiled into the following:
char *object-ident
Type Definitions
You need to use rpcgen to generate an XDR routine and/or
header file that defines a type in an input file. For every
zetype you define, rpcgen creates a corresponding XDR
routine named xdr_zetype that is mandatory for creating RPC
programs.
There are six ways to define a type:
type-definition:
typedef
enumeration-def
structure-def
variable-length-array-def
discriminated-union-def
program-def
The first three are very similar to their C namesakes. C
does not have a formal type mechanism to define variable-
length arrays and XDR unions are quite different from their
C counterparts. Program definitions are not really type
definitions, but they are useful nonetheless.
You may not nest XDR definitions. For example, the
following will cause rpcgen to choke:
struct dontdoit {
struct ididit {
int oops;
} sorry;
enum ididitagain { OOPS, WHOOPS } iapologize;
};
Typedefs
An XDR typedef looks as follows:
typedef:
typedef type-name object-ident ;
The object-ident is the name of the new type, whereas the
type-name part is the name of the type from which it is
derived. For example,
typedef longa;
Enumeration Types
The syntax is:
enumeration-def:
enum enum-ident {
enum-list
};
enum-list:
enum-symbol-ident [ = assignment ]
enum-symbol-ident [ = assignment ] , enum-list
(assignment may be either an integer or a symbolic constant)
If there is no explicit assignment, then the implicit
assignment is the value of the previous enumeration plus 1.
If not explicitly assigned, the first enumeration receives
the value 0.
Structures
structure-def:
struct struct-ident {
declaration-list
};
declaration-list:
declaration ;
declaration ; declaration-list
Variable-Length Arrays
variable-length-array-def:
array array-ident {
unsigned length-identifer ;
vector-declaration ;
};
A variable length array definition looks much like a
structure definition. Here's an example:
array mp_int {
unsigned len;
short val[MAX_MP_LENGTH];
};
This is compiled into:
struct mp_int {
unsigned len;
short *val;
};
typedef struct mp_int mp_int;
Discriminated Unions
discriminated-union-def:
union union-ident switch ( discriminant-declaration ) {
case-list
[ default : declaration ; ]
};
case-list:
case case-ident : declaration ;
case case-ident : declaration ; case-list
discriminant-declaration:
declaration
The union definition looks like a cross between a C-union
and a C-switch. For example:
union net_object switch (net_kind kind) {
case MACHINE:
struct sockaddr_in sin;
case USER:
int uid;
default:
string whatisit;
};
Compiles into:
struct net_object {
net_kind kind;
union {
struct sockaddr_in sin;
int uid;
char *whatisit;
} net_object;
};
typedef struct net_object net_object;
Note that the name of the union component of the output
struct is the same as the name of the type itself.
Program Definitions
program-def:
program program-ident {
version-list
} = program-number ;
version-list:
version
version version-list
version:
version version-ident {
procedure-list
} = version-number ;
procedure-list:
procedure-declaration
procedure-declaration procedure-list
procedure-declaration:
type-name procedure-ident ( type-name ) = procedure-number ;
Program definitions look like nothing you've ever seen
before, so we turn to an example to explain them. Suppose
you wanted to create server that could get or set the date.
Its declaration might look like this:
program DATE_PROG {
version DATE_VERS {
date DATE_GET(timezone) = 1;
void DATE_SET(date) = 2; /* Greenwich mean time */
} = 1;
} = 100;
In the header file, this compiles into the following:
#define DATE_PROG 100
#define DATE_VERS 1
#define DATE_GET 1
#define DATE_SET 2
These define's are intended for use by the client program to
reference the remote procedures.
If you are using rpcgen to compile your server, then there
are some important things for you to know. The server
interfaces to your local procedures by expecting a C
function with the same name as that in the program
definition, but in all lowercase letters and followed by the
version number. Here is the local procedure that implements
DATE_GET:
date * /* always returns a pointer to the results */
date_get_1(tz)
timezone *tz; /* always takes a pointer to the arguments */
{
static date d; /* must be static! */
/*
* figure out the date
* and store it in d
*/
return(&d);
}
The name of the routine is the same as the #define'd name,
but in all lowercase letters and followed by the version
number. XDR will recursively free the argument after
getting the results from your local procedure, so you should
copy from the argument any data that you will need between
calls. However, XDR neither allocates nor frees your
results. You must take care of their storage yourself.
Make Inference Rules
For Compiling XDR Headers
It is possible to set up suffix transformation rules in
make(C) for compiling XDR routines and header files. The
convention is that RPCL protocol files have the extension
.x. The make rules to do this are:
.SUFFIXES: .x
.x.c:
rpcgen -c $< -o $@
.x.h:
rpcgen -h $< -o $@
Example
Consider the following program, example , which defines
three data types:
const NFS_PORT = 2059;
enum nfsstat {
NFS_OK=0
};
struct gnumbers {
long g_assets;
long g_liabilities;
};
When you run rpcgen with no arguments, it generates a header
file, example.h and an XDR file, examplexdr.c.
example.h
#define NFS_PORT 2059
enum nfsstat {
NFS_OK = 0,
};
typedef enum nfsstat nfsstat;
bool_t xdr_nfsstat();
struct gnumbers {
long g_assets;
long g_liabilities;
};
typedef struct gnumbers gnumbers;
bool_t xdr_gnumbers();
examplexdr.c
#include <rpc/rpc.h>
#include "infile.h"
bool_t
xdr_nfsstat(xdrs, objp)
XDR *xdrs;
nfsstat *objp;
{
if (!xdr_enum(xdrs, (enum_t *)objp)) {
return (FALSE);
}
return (TRUE);
}
bool_t
xdr_gnumbers(xdrs, objp)
XDR *xdrs;
gnumbers *objp;
{
if (!xdr_long(xdrs, &objp->g_assets)) {
return (FALSE);
}
if (!xdr_long(xdrs, &objp->g_liabilities)) {
return (FALSE);
}
return (TRUE);
}
SEE ALSO
SCO NFS Programmer's Guide
Notes
Name clashes can occur when using program definitions, since
the apparent scoping does not really apply. Most of these
can be avoided by giving unique names for programs,
versions, procedures and types.
(printed 8/17/89) RPCGEN(NC)