# Digital TCP/IP Services for OpenVMS
ONC RPC Programming

Previous | Contents

Using the percent sign feature does not guarantee that RPCGEN will place the output where you intend. If you have problems of this type, do not use this feature.

# 2.6 RPCGEN Programming

The following sections contain additional RPCGEN programming information about network types, defining symbols, INETd support, and dispatch tables.

## 2.6.1 Network Types

By default, RPCGEN generates server code for both UDP and TCP transports. The /TRANSPORT option creates a server that responds to requests on the specified transport. The following example creates a UDP server from a file called PROTO.X:

    $ RPCGEN /TRANSPORT=UDP PROTO.X

## 2.6.2 User-Provided Define Statements

The RPCGEN protocol compiler provides a way to define symbols and assign values to them. These defined symbols are passed on to the C preprocessor when it is invoked. This facility is useful when, for example, invoking debugging code that is enabled only when you define the DEBUG symbol. For example, to enable the DEBUG symbol in the code generated from the PROTO.X file, use the following command:

    $ RPCGEN /DEFINE=DEBUG PROTO.X

## 2.6.3 INETd Support

The RPCGEN protocol compiler can create RPC servers that INETd can invoke when it receives a request for that service. For example, to generate INETd support for the code generated for the PROTO.X file, use the following command:

    RPCGEN /INET_SERVICE PROTO.X

The server code in proto_svc.c supports INETd. For more information on setting up UCX entries for RPC services, see Section 3.7.

In many applications, it is useful for services to wait after responding to a request, on the chance that another will soon follow. However, if there is no call within a certain time (by default, 120 seconds), the server exits and the port monitor continues to monitor requests for its services. You can use the /TIMEOUT_SECONDS option to change the default waiting time. In the following example, the server waits only 20 seconds before exiting:

    $ RPCGEN /INET_SERVICE /TIMEOUT_SECONDS=20 PROTO.X

If you want the server to exit immediately, use /TIMEOUT_SECONDS = 0; if you want the server to wait forever (a normal server situation), use /TIMEOUT_SECONDS = -1.

## 2.6.4 Dispatch Tables

Dispatch tables are often useful. For example, the server dispatch routine may need to check authorization and then invoke the service routine, or a client library may need to control all details of storage management and XDR data conversion. The following RPCGEN command generates RPC dispatch tables for each program defined in the protocol description file, PROTO.X, and places them in the file PROTO_TBL.I (the suffix .I indicates index):

    $ RPCGEN /TABLE PROTO.X

Each entry in the table is a struct rpcgen_table defined in the header file, PROTO.H, as follows:

                   struct rpcgen_table {
                       char        *(*proc)();
                       xdrproc_t   inproc;
                       unsigned    len_in;
                       xdrproc_t   outproc;
                       unsigned    len_out;
                   };

In this definition:

- proc is a pointer to the service routine
- inproc is a pointer to the input (arguments) XDR routine
- len_in is the length in bytes of the input argument
- outproc is a pointer to the output (results) XDR routine
- len_out is the length in bytes of the output result

The table dirprog_1_table is indexed by procedure number. The variable dirprog_1_nproc contains the number of entries in the table. The find_proc routine in the following example shows how to locate a procedure in the dispatch tables.

    struct rpcgen_table *
    find_proc(proc)
         long    proc;
    {
         if (proc >= dirprog_1_nproc)

              /* error */
         else
              return (&dirprog_1_table[proc]);
    }

Each entry in the dispatch table (in the file input_file_TBL.I) contains a pointer to the corresponding service routine. However, the service routine is not defined in the client code. To avoid generating unresolved external references, and to require only one source file for the dispatch table, the actual service routine initializer is RPCGEN_ACTION(proc_ver). The following example shows the dispatch table entry for the procedure printmessage with a procedure number of 1:

         .....
         (char *(*)())RPCGEN_ACTION(printmessage_1),
         xdr_wrapstring,       0,
         xdr_int,              0,
         .....

With this feature, you can include the same dispatch table in both the client and the server. Use the following define statement when compiling the client:

    #define RPCGEN_ACTION(routine)  0

Use the following define statement when compiling the server:

    #define RPCGEN_ACTION(routine)  routine

# 2.7 Client Programming

The following sections contain client programming information about default timeouts and client authentication.

## 2.7.1 Timeout Changes

A call to clnt_create sets a default timeout of 25 seconds for RPC calls. RPC waits for 25 seconds to get the results from the server. If it does not get any results, then this usually means that one of the following conditions exists:

- The server is not running.
- The remote system has crashed.
- The network is unreachable.

In such cases, the function returns NULL; you can print the error with clnt_perrno.

Sometimes you may need to change the timeout value to accommodate the application or because the server is slow or far away. Change the timeout by using clnt_control. The code segment in the following example demonstrates the use of clnt_control.

    struct timeval tv;
    CLIENT *cl;

    cl = clnt_create("somehost", SOMEPROG, SOMEVERS, "tcp");
    if (cl == NULL) {
         exit(1);
    }
    tv.tv_sec = 60; /* change timeout to 1 minute */
    tv.tv_usec = 0; /* this should always be set  */
    clnt_control(cl, CLSET_TIMEOUT, &tv);

## 2.7.2 Client Authentication

By default, client creation routines do not handle client authentication. Sometimes, you may want the client to authenticate itself to the server. This is easy to do, as shown in the following code segment:

    CLIENT *cl;

    cl = clnt_create("somehost", SOMEPROG, SOMEVERS, "udp");
    if (cl != NULL) {
         /* To set UNIX style authentication */
         cl->cl_auth = authunix_create_default();
    }

For more information on authentication, see Section 3.6.

# 2.8 Server Programming

The following sections contain server programming information about system broadcasts and passing data to server procedures.

## 2.8.1 Handling Broadcasts

Sometimes, clients broadcast to determine whether a particular server exists on the network, or to determine all the servers for a particular program and version number. You make these calls with clnt_broadcast (for which there is no RPCGEN support). Refer to Section 3.5.2.

When a procedure is known to be called via broadcast RPC, it is best for the server not to reply unless it can provide useful information to the client. Otherwise, servers could overload the network with useless replies. To prevent the server from replying, a remote procedure can return NULL as its result; the server code generated by RPCGEN can detect this and prevent a reply.

In the following example, the procedure replies only if it acts as an NFS server:

    void *
    reply_if_nfsserver()
    {
         char notnull;   /* just here so we can use its address */
         if (access("/etc/exports", F_OK) < 0) {
              return (NULL);  /* prevent RPC from replying */
         }
         /*
          * return non-null pointer so RPC will send out a reply
          */
         return ((void *)&notnull);
    }

If a procedure returns type void*, it must return a non-null pointer if it wants RPC to reply for it.

## 2.8.2 Passing Data to Server Procedures

Server procedures often need to know more about an RPC call than just its arguments. For example, getting authentication information is useful to procedures that want to implement some level of security. This information is supplied to the server procedure as a second argument. (For details, see the structure of svc_req in Section 3.6.2.) The following code segment shows the use of svc_req, where the first part of the previous printmessage_1 procedure is modified to allow only root users to print a message to the console:

    int *
    printmessage_1(msg, rqstp)
         char **msg;
         struct svc_req  *rqstp;
    {
         static int result;      /* Must be static */
         FILE *f;
         struct authunix_parms *aup;

         aup = (struct authunix_parms *)rqstp->rq_clntcred;
         if (aup->aup_uid != 0) {
              result = 0;
              return (&result);
         }
    /* Same code as before */

# 2.9 RPC and XDR Languages

The RPC language is an extension of the XDR language through the addition of the program and version types. The XDR language is similar to C. For a complete description of the XDR language syntax, see RFC 1014: XDR: External Data Representation Standard. For a description of the RPC extensions to the XDR language, see RFC 1057: RPC: Remote Procedure Calls Protocol Specification Version 2.

The following sections describe the syntax of the RPC and XDR languages, with examples and descriptions of how RPCGEN compiles the various RPC and XDR type definitions into C type definitions in the output header file.

## 2.9.1 Definitions

An RPC language file consists of a series of definitions:

         definition-list:
              definition ";"
              definition ";" definition-list

RPC recognizes the following definition types:

         definition:
              enum-definition
              typedef-definition
              const-definition
              declaration-definition
              struct-definition
              union-definition
              program-definition

## 2.9.2 Enumerations

XDR enumerations have the same syntax as C enumerations:

         enum-definition:
              "enum" enum-ident "{"
                   enum-value-list
              "}"
         enum-value-list:
              enum-value
              enum-value "," enum-value-list
         enum-value:
              enum-value-ident
              enum-value-ident "=" value

The following example defines a enum type with three values:

         enum colortype {
              RED = 0,
              GREEN = 1,
              BLUE = 2
         };

This coding compiles into the following:

         enum colortype {
              RED = 0,
              GREEN = 1,
              BLUE = 2,
         };
         typedef enum colortype colortype;

## 2.9.3 Typedefs

XDR typedefs have the same syntax as C typedefs:

         typedef-definition:
              "typedef" declaration

The following example in XDR defines a fname_type that declares file name strings with a maximum length of 255 characters:

         typedef string fname_type<255>;

The following example shows the corresponding C definition for this:

         typedef char *fname_type;

## 2.9.4 Constants

XDR constants are used wherever an integer constant is used (for example, in array size specifications), as shown by the following syntax:

         const-definition:
              "const" const-ident "=" integer

The following XDR example defines a constant DOZEN equal to 12:

         const DOZEN = 12;

The following example shows the corresponding C definition for this:

         #define DOZEN 12

## 2.9.5 Declarations

XDR provides only four kinds of declarations, shown by the following syntax:

         declaration:
              simple-declaration
              fixed-array-declaration
              variable-array-declaration
              pointer-declaration

The following lists the syntax for each, followed by examples:

- Simple declarations

           simple-declaration:
                type-ident variable-ident

  For example, colortype color in XDR, is the same in C: colortype color.
- Fixed-length array declarations

           fixed-array-declaration:
                type-ident variable-ident "[" value "]"

  For example, colortype palette[8] in XDR, is the same in C: colortype palette[8].
- Variable-length array declarations
  These have no explicit syntax in C, so XDR creates its own by using angle brackets, as in the following syntax:

           variable-array-declaration:
                type-ident variable-ident "<" value ">"
                type-ident variable-ident "<" ">"

  Specify the maximum size between the angle brackets. You can omit the value, indicating that the array can be of any size, as shown in the following example:

           int heights<12>;/* at most 12 items */
           int widths<>;/* any number of items */

  Variable-length arrays have no explicit syntax in C, so RPCGEN compiles each of their declarations into a struct. For example, RPCGEN compiles the heights declaration into the following struct:

           struct {
                u_int heights_len;/* number of items in array */
                int *heights_val;/* pointer to array */
           } heights;

  Here, the _len component stores the number of items in the array and the _val component stores the pointer to the array. The first part of each of these component names is the same as the name of the declared XDR variable.
- Pointer declarations
  These are the same in XDR as in C. You cannot send pointers over the network, but you can use XDR pointers to send recursive data types, such as lists and trees. In XDR language, this type is called optional-data, not pointer, as in the following syntax:

           optional-data:
                type-ident "*"variable-ident

  An example of this (the same in both XDR and C) follows:

           listitem *next;

## 2.9.6 Structures

XDR declares a struct almost exactly like its C counterpart. The XDR syntax is the following:

         struct-definition:
              "struct" struct-ident "{"
                   declaration-list
              "}"

         declaration-list:
              declaration ";"
              declaration ";" declaration-list

The following example shows an XDR structure for a two-dimensional coordinate, followed by the C structure into which RPCGEN compiles it in the output header file:

         struct coord {
              int x;
              int y;
         };

The following example shows the C structure that results from compiling the previous XDR structure:

         struct coord {
              int x;
              int y;
         };
         typedef struct coord coord;

Here, the output is identical to the input, except for the added typedef at the end of the output. This enables the use of coord instead of struct coord in declarations.

## 2.9.7 Unions

XDR unions are discriminated unions, and are different from C unions. They are more analogous to Pascal variant records than to C unions. The syntax is shown here:

         union-definition:
              "union" union-ident "switch" ("simple declaration") "{"
              case-list
         "}"

         case-list:
              "case" value ":" declaration ";"
              "case" value ":" declaration ";" case-list
              "default" ":" declaration ";"

The following is an example of a type that might be returned as the result of a read data. If there is no error, it returns a block of data; otherwise, it returns nothing:

         union read_result switch (int errno) {
              case 0:
                   opaque data[1024];
              default:
                   void;
         };

RPCGEN compiles this coding into the following:

         struct read_result {
              int errno;
              union {
                   char data[1024];
              } read_result_u;
         };
         typedef struct read_result read_result;

Notice that the union component of the output structure has the same name as the structure type name, except for the suffix, _u.

## 2.9.8 Programs

You declare RPC programs using the following syntax:

         program-definition:
              "program" program-ident "{"
                   version-list
              "}" "=" value

         version-list:
              version ";"
              version ";" version-list

         version:
              "version" version-ident "{"
                   procedure-list
              "}" "=" value

         procedure-list:
              procedure ";"
              procedure ";" procedure-list

         procedure:
              type-ident procedure-ident "("type-ident")" "=" value

The following example shows a program specification for a time protocol program:

    /*
    * time.x: Get or set the time.  Time is represented as number
    * of seconds since 0:00, January 1, 1970.
    */
    program TIMEPROG {
         version TIMEVERS {
              unsigned int TIMEGET(void) = 1;
              void TIMESET(unsigned) = 2;
         } = 1;
    } = 44;

This coding compiles into the following #define statements in the output header file:

    #define TIMEPROG 44
    #define TIMEVERS 1
    #define TIMEGET 1
    #define TIMESET 2

## 2.9.9 Special Cases

The following list describes exceptions to the syntax rules described in the previous sections:

- Booleans
  C has no built-in boolean type. However, the RPC library has a boolean type called bool_t that is either TRUE or FALSE. RPCGEN compiles items declared as type bool in XDR language into bool_t in the output header file. For example, RPCGEN compiles bool married into bool_t married.
- Strings
  C has no built-in string type, but instead uses the null-terminated char* convention. In XDR language, you declare strings by using the string keyword. RPCGEN compiles each string into a char* in the output header file. The maximum size contained in the angle brackets specifies the maximum number of characters allowed in the strings (excluding the NULL character). For example, RPCGEN compiles string name<32> into char*name. You can omit a maximum size to indicate a string of arbitrary length. For example, RPCGEN compiles string longname<> into char*longname.
- Opaque data
  RPC and XDR use opaque data to describe untyped data, which consists simply of sequences of arbitrary bytes. You declare opaque data as an array of either fixed or variable length. An opaque declaration of a fixed-length array is opaque diskblock[512], whose C counterpart is char diskblock[512]. An opaque declaration of a variable-length array is opaque filedata<1024>, whose C counterpart could be the following:

           struct {
                u_int filedata_len;
                char *filedata_val;
           } filedata;

- Voids
  In a void declaration, the variable is not named. The declaration is just a void. Declarations of void occur only in union and program definitions (as the argument or result of a remote procedure).

# RPCGEN

> A code-generating tool for creating programming skeletons that implement the RPC mechanism.

> > Note

> > RPCGEN runs the C preprocessor, CC/DECC/PREPROCESSOR, on all input files before actually interpreted the files. Therefore, all the preprocessor directives are legal within an RPCGEN input file. For each type of output file, RPCGEN defines a special preprocessor symbol for use by the RPCGEN programmer:

> > RPC_HDR | Defined when compiling into header files
> > RPC_XDR | Defined when compiling into XDR routines
> > RPC_SVC | Defined when compiling into server-side skeletons
> > RPC_CLNT | Defined when compiling into client-side skeletons
> > RPC_TBL | Defined when compiling into RPC dispatch table

> > In addition, RPCGEN does a little preprocessing of its own. RPCGEN passes any line beginning with "%" directly into the output file, without interpreting the line.

## Format

> ### RPCGEN infile[[/HEADER_FILE ]
> [/CLIENT_STUBS_FILE | /DISPATCH_TABLE | /XDR_FILE]
> [/SERVER_STUBS_FILE | /TRANSPORT [=(TCP,UDP)]]]
> [[/TABLE]
> [/DEFINE = (name=[value][,....]) | /OUTPUT = file]
> [/DEFINE = (name=[value][,....]) | /ERRLOG | /INET_SERVICE | /OUTPUT = file | /TIMEOUT_SECONDS=seconds]]]

## PARAMETERS

> ### infile

> The input file to RPCGEN. The input file contains ONC RPC programming language. This language is very similar to the C language. By default, RPCGEN uses the name of the input file to create the four default output files as follows:
> - infile.H---the header file
> - infile_CLNT.C---the client skeleton
> - infile_SVC.C---the server skeleton with support for both UDP and TCP transports
> - infile_XDR.C---the XDR routines

> If you specify the /DISPATCH_TABLE qualifier, RPCGEN uses the default name infile_TBL.I for the dispatch table.

## QUALIFIERS

> ### /CLIENT_STUBS_FILE

> Optional.
> Digital UNIX equivalent: -l
> Default: Create a client skeleton file.

> Creates the client skeleton file.

> Mutually exclusive with the /DISPATCH_TABLE, /HEADER_FILE, /SERVER_STUBS_FILE, /TRANSPORT, and XDR_FILE qualifiers.

> ### /DEFINE = (name[=value][,....])

> Optional.
> Digital UNIX equivalent: -D
> Default: No definitions.

> Defines one or more symbol names. Equivalent to one or more #define directives. Names are defined as they appear in the argument to the qualifier. For example, /DEFINE=TEST=1 creates the line #define TEST=1 in the output files. If you omit the value, RPCGEN defines the name with the value 1.

> ### /DISPATCH_TABLE

> Optional.
> Digital UNIX equivalent: -t
> Default: No dispatch file created.

> Creates the server dispatch table file. An RPCGEN dispatch table contains:

> - Pointers to the service routines corresponding to a procedure
> - A pointer to the input and output arguments
> - The size of these routines

> A server can use the dispatch table to check authorization and then to execute the service routine; a client may use it to deal with the details of storage management and XDR data conversion.

> Mutually exclusive with the /CLIENT_STUBS_FILE, /HEADER_FILE, /SERVER_STUBS_FILE, /TRANSPORT, and XDR_FILE qualifiers.

> ### /ERRLOG

> Optional.
> Digital UNIX equivalent: -L
> Default: Logging to stderr.

> Specifies that servers should log errors to the operator console instead of using fprintf with stderr. You must install servers with OPER privileges in order to use this feature.

> ### /HEADER_FILE

> Optional.
> Digital UNIX equivalent: -h
> Default: Create a header file.

> Creates the C data definitions header file. Use the /TABLE qualifier in conjunction with this qualifier to generate a header file that supports dispatch tables.

> Mutually exclusive with the /CLIENT_STUBS_FILE, /DISPATCH_TABLE, /SERVER_STUBS_FILE, /TRANSPORT, and XDR_FILE qualifiers.

> ### /INET_SERVICE

> Optional.
> Digital UNIX equivalent: -I
> Default: no INETd support.

> Compiles support for INETd in the server side stubs. You can start servers yourself or you can have INETd start them. Servers started by INETd log all error messages to the operator console.

> If there are no pending client requests, the INETd servers exit after 120 seconds (default). You can change this default with the /TIMEOUT_SECONDS qualifier.

> When RPCGEN creates servers with INETd support, it defines two global variables: _rpcpmstart and rpcfdtype. The runtime value of _rpcpmstart is 1 or 0 depending on whether INDEd started the server program. The value of rpcfdtype should be SOCK_STREAM or SOCK_DGRAM depending on the type of the connection.

> ### /OUTPUT = file

> Optional.
> Digital UNIX equivalent: -o
> Default: Direct output to one of the standard default files.

> Use this qualifier to direct the output of the /CLIENT_STUBS_FILE, /DISPATCH_TABLE, /HEADER_FILE, /SERVER_STUBS_FILE, /TRANSPORT, and /XDR_FILE qualifiers.

> ### /SERVER_STUBS_FILE

> Optional.
> Digital UNIX equivalent: -m
> Default: Create a server skeleton file.

> Creates a server skeleton file without the main routine. Use this qualifier to generate a server skeleton when you wish to create your own main routine. This option is useful for programs that have callback routines and for programs that have customized initialization requirements.

> Mutually exclusive with the /CLIENT_STUBS_FILE, /DISPATCH_TABLE, /HEADER_FILE, /TRANSPORT, and XDR_FILE qualifiers.

> ### /TABLE

> Optional.
> Digital UNIX equivalent: -T
> Default: No dispatch table code created.

> Creates the code in the header file to support an RPCGEN dispatch table. You can use this qualifier only when you are generating all files (the default) or when you are using the /HEADER_FILE qualifier to generate the header file. This /TABLE qualifier includes a definition of the dispatch table structure in the header file; it does not modify the server routine to use the table.

> ### /TRANSPORT [= (TCP, UDP)]

> Optional.
> Digital UNIX equivalent: -s
> Default: Create a server-side skeleton that supports both protocols.

> Creates a server-side skeleton that includes a main routine that uses the given transport. The supported transports are UDP and TCP. To compile a server that supports multiple transports, specify both.

> ### /TIMEOUT_SECONDS=seconds

> Optional.
> Digital UNIX equivalent: -K
> Default: 120 seconds.

> If INETd starts the server, this option specifies the time (in seconds) after which the server should exit if there is no further activity. By default, if there are no pending client requests, INETd servers exit after 120 seconds. This option is useful for customization. If seconds is 0, the server exits after serving a request. If seconds is -1, the server never exits after being started by INETd.

> ### /XDR_FILE

> Optional.
> Digital UNIX equivalent: -c
> Default: Create an XDR file.

> You can customize some of your XDR routines by leaving those data types undefined. For every data type that is undefined, RPCGEN assumes that there exists a routine with the name xdr_ prepended to the name of the undefined type.

> Mutually exclusive with the /CLIENT_STUBS_FILE, /DISPATCH_TABLE, /HEADER_FILE, /TRANSPORT, and /SERVER_STUBS_FILE qualifiers.

##

### Examples

#1

    RPCGEN /ERRLOG /TABLE PROTO.X

> This example generates all of the five possible files using the default file names: PROTO.H, PROTO_CLNT.C, PROTO_SVC.C, PROTO_XDR.C, and PROTO_TBL.I. The PROTO_SVC.C code supports the use of the dispatch table found in PROTO_TBL.I. The server error messages are logged to the operator console, instead of being sent to the standard error.

#2

    RPCGEN /INET_SERVICE /TIMEOUT_SECONDS=20 PROTO.X

> Previous | Next | Contents