The rpcgen tool generates remote program interface modules. It compiles source code
written in the RPC Language. RPC Language is similar in syntax and structure to C. rpcgen
produces one or more C language source modules, which are then compiled by a C compiler.

The default output of rpcgen is:

A header file of definitions common to the server and the client

A set of XDR routines that translate each data type defined in the header file

A stub program for the server

A stub program for the client

rpcgen can optionally generate (although we do not consider these issues here -- see
man pages or receommended reading):

Various transports

A time-out for servers

Server stubs that are MT safe

Server stubs that are not main programs

C-style arguments passing ANSI C-compliant code

An RPC dispatch table that checks authorizations and invokes service routines

rpcgen significantly reduces the development time that would otherwise be spent developing
low-level routines. Handwritten routines link easily with the rpcgen output.

rpcgen provides programmers a simple and direct way to write distributed applications. Server
procedures may be written in any language that observes procedure-calling conventions. They are linked
with the server stub produced by rpcgen to form an executable server program. Client procedures are
written and linked in the same way.
This section presents some basic rpcgen programming examples. Refer also to the man rpcgen online
manual page.

Assume that an application runs on a single computer and you want to convert it to run in a "distributed"
manner on a network. This example shows the stepwise conversion of this program that writes a message
to the system console.

If the printmessage() function is turned into a remote procedure, it can be called from anywhere
in the network. rpcgen makes it easy to do this:

First, determine the data types of all procedure-calling arguments and the result argument. The
calling argument of printmessage() is a string, and the result is an integer. We can write a protocol
specification in RPC language that describes the remote version of printmessage. The RPC language source
code for such a specification is:

Remote procedures are always declared as part of remote programs.
The code above declares an entire
remote program that contains the single procedure PRINTMESSAGE.

In this example,

PRINTMESSAGE procedure is declared to be:

the
procedure 1,

in version 1 of the remote program

MESSAGEPROG, with the program number 0x20000001.

Version numbers are incremented
when functionality is changed in the remote program. Existing procedures can be changed or new ones can
be added. More than one version of a remote program can be defined and a version can have more than
one procedure defined.

Note: that the program and procedure names are declared with all capital letters. This is not
required, but is a good convention to follow.
Note also that the argument type is string and not char * as it would be in C. This is because a char * in C
is ambiguous. char usually means an array of characters, but it could also represent a pointer to a single
character. In RPC language, a null-terminated array of char is called a string.

Note that the declaration of the remote procedure printmessage_1 differs from that of the local
procedure printmessage in four ways:

It takes a pointer to the character array instead of the pointer itself. This is true of all remote
procedures when the '-' N option is not used: They always take pointers to their arguments rather
than the arguments themselves. Without the '-' N option, remote procedures are always called with a
single argument. If more than one argument is required the arguments must be passed in a struct.

It is called with two arguments. The second argument contains information on the context of an
invocation: the program, version, and procedure numbers, raw and canonical credentials, and an
SVCXPRT structure pointer (the SVCXPRT structure contains transport information). This
information is made available in case the invoked procedure requires it to perform the request.

It returns a pointer to an integer instead of the integer itself. This is also true of remote
procedures when the '-' N option is not used: They return pointers to the result. The result should be
declared static unless the '-' M (multithread) or '-' A (Auto mode) options are used.
Ordinarily, if the result is declared local to the remote procedure, references to it by the server stub
are invalid after the
remote procedure returns. In the case of '-' M and '-' A options, a pointer to the result is
passed as a third argument to the procedure, so the result is not declared in the procedure.

An _1 is appended to its name. In general, all remote procedures calls generated by rpcgen are
named as follows: the procedure name in the program definition (here PRINTMESSAGE) is
converted to all lowercase letters, an underbar (_) is appended to it, and the version number (here
1) is appended. This naming scheme allows multiple versions of the same procedure.

First, a client handle is created by the RPC library routine clnt_create(). This client handle
is passed to the stub routine that calls the remote procedure. If no more calls are to be
made using the client handle, destroy it with a call to clnt_destroy() to conserve system resources.

The last parameter to clnt_create() is visible, which specifies that any transport noted as
visible in /etc/netconfig can be used.

The remote procedure printmessage_1 is called exactly the same way as it is declared in
msg_proc.c, except for the inserted client handle as the second argument. It also returns a pointer
to the result instead of the result.

The remote procedure call can fail in two ways. The RPC mechanism can fail or there can be an error
in the execution of the remote procedure. In the former case, the remote procedure printmessage_1
returns a NULL. In the latter case, the error reporting is application dependent. Here, the error is
returned through *result.

The C object files must be linked with the library libnsl, which contains all of the
networking functions, including those for RPC and XDR.

In this example, no XDR routines were generated because the application uses only the basic types that are
included in libnsl
.
Let us consider further what rpcgen did with the input file msg.x:

It created a header file called msg.h that contained #define statements for
MESSAGEPROG, MESSAGEVERS, and PRINTMESSAGE for use in the other modules. This filemust be
included by both the client and server modules.

It created the client stub routines in the msg_clnt.c file. Here there is only one, the
printmessage_1 routine, that was called from the rprintmsg client program. If the name of an
rpcgen input file is prog.x, the client stub's output file is called prog_clnt.c.

It created the server program in msg_svc.c that calls printmessage_1 from
msg_proc.c. The rule for naming the server output file is similar to that of the client: for an input
file called prog.x, the output server file is named prog_svc.c.

Once created, the server program is installed on a remote machine and run. (If the machines are
homogeneous, the server binary can just be copied. If they are not, the server source files must be copied
to and compiled on the remote machine.)

You can redefine types (like
readdir_res in the example above) using the struct, union, and enum RPC
language keywords. These keywords are not used in later declarations of variables of those types. For
example, if you define a union, my_un, you declare using only my_un, and not union
my_un. rpcgen compiles RPC unions into C structures. Do not declare C unions using the union
keyword.

Running rpcgen on dir.x generates four output files:

the header file, dir.h,

the client stub, dir_clnt.c,

the server skeleton, dir_svc.c ,and

the XDR routines in the file dir_xdr.c.

This last file
contains the XDR routines to convert declared data types from the host platform representation into XDR
format, and vice versa. For each RPCL data type used in the .x file, rpcgen assumes that
libnsl contains a routine whose name is the name of the data type, prepended by the XDR routine header
xdr_ (for example, xdr_int). If a data type is defined in the .x file, rpcgen
generates the required xdr_ routine. If there is no data type definition in the .x source file
(for example, msg.x, above), then no _xdr.c file is generated. You can write a .x source
file that uses a data type not supported by libnsl, and deliberately omit defining the type (in the
.x file). In doing so, you must provide the xdr_ routine. This is a way to provide your own
customized xdr_ routines.

rpcgen generated client code does not release the memory allocated for the results of the RPC call.
Call xdr_free() to release the memory when you are finished with it. It is similar to calling the
free() routine, except that you pass the XDR routine for the result. In this example, after printing
the list, xdr_free(xdr_readdir_res, result); was called.

rpcgen supports C and other preprocessing features. C preprocessing is performed on rpcgen input
files before they are compiled. All standard C preprocessing directives are allowed in the .x source
files. Depending on the type of output file being generated, five symbols are defined by rpcgen.
rpcgen provides an additional preprocessing feature: any line that begins with a percent sign (%) is
passed directly to the output file, with no action on the line's content. Caution is required because
rpcgen does not always place the lines where you intend. Check the output source file and, if needed,
edit it.

The following symbols may be used to process file specific output:

RPC_HDR

-- Header file output

RPC_XDR

-- XDR routine output

RPC_SVC

-- Server stub output

RPC_CLNT

-- Client stub output

RPC_TB

-- Index table output

The following example illustrates tthe use of rpcgens
pre-processing features.

rpcgen supports C preprocessing features. rpcgen defaults to use /usr/ccs/lib/cpp as the C
preprocessor. If that fails, rpcgen tries to use
/lib/cpp. You may specify a library containing a
different cpp to rpcgen with the '-' Y flag.

For example, if /usr/local/bin/cpp exists, you can specify it to rpcgen as follows:

A C-style mode server template is generated from the add.x source by the command:

rpcgen -N -Ss -o add_server_template.c add.x

The result is stored in the file add_server_template.c.

A C-style mode, client template for the same
add.x source is generated with the command line:

rpcgen -N -Sc -o add_client_template.c add.x

The result is stored in the file add_client_template.c.

A make file template for the same add.x
source is generated with the command line:

rpcgen -N -Sm -o mkfile_template add.x

The result is stored in the file mkfile_template. It can be used to compile the client and the
server. If the '-' a flag is used as follows:

rpcgen -N -a add.x

rpcgen generates all three template files. The client template goes into add_client.c, the
server template to add_server.c, and the makefile template to makefile.a. If any of these files
already exists, rpcgen displays an error message and exits.

Note - When you generate template files, give them new names to avoid the files being overwritten the
next time rpcgen is executed.

Use rpcgen the generate and compile the rprintmsg listing example given in this chapter.

Exercise 12835

Use rpcgen the generate and compile the dir listing example given in this chapter.

Exercise 12836

Develop a Remote Procedure Call suite of programs that enables a user to search for specific files or
filtererd files in a remote directory. That is to say you can search for a named file e.g. file.c or
all files named *.c or even *.x.

Exercise 12837

Develop a Remote Procedure Call suite of programs that enables a user to grep files remotely. You may
use code developed previously or unix system calls to implement grep.

Exercise 12838

Develop a Remote Procedure Call suite of programs that enables a user to list the contents of a
named remote files.