rxgen(1)


NAME

   rxgen - Stub generator for the Rx remote procedure call package

SYNOPSIS

   rxgen [-h | -c | -C | -S | -r] [-dkpR]
       [-I dir] [-P prefix] [-o outfile] [infile]

   rxgen -s transport [-o outfile] [infile]

   rxgen -l [-o outfile] [infile]

   rxgen -m [-o outfile] [infile]

DESCRIPTION

   rxgen is a tool that generates C code to implement the Rx RPC protocol;
   it takes as input a description of an application interface similar to
   C and produces a number of server and/or client stub routines to be
   linked with RPC-based programs.  These stubs allow programs to invoke
   remote procedures through local procedure calls.  rxgen is an extension
   of Sun's rpcgen (version 3.9) and retains full rpcgen functionality (at
   least as of that version).  Please refer to rpcgen(1) for more details
   on the Sun's RPC specific flags, and to the RPC programming guide
   regarding the RPC language along with useful examples.

OPTIONS

   rxgen operates in several different modes.  The generated output files
   can be produced individually (using one of -h, -c, -C, or -S) or
   collectively.  All output files are created when the default is used
   (i.e., no options), or the output is limited to the server stubs (-C
   and -S) when the -r flag is used.  The following describes the types of
   generated output files (for simplicity, filename refers to the main
   output filename):

   -h  Generate C data definitions (a header file) from standard RPCL
       definitions (default extension: filename.h).

   -c  Compile the XDR routines required to serialize the protocol
       described by RPCL.  Generate XDR routines for all declarations
       (default extension: filename.xdr.c).

   -C  Generate all the client-side stub routines (default extension:
       filename.cs.c).  Calling a routine in this file will cause the
       arguments to be packed up and sent via Rx (or R).

   -S  Generate all the server-side stub routines (default extension:
       filename.ss.c).  Arguments are unpacked, and the corresponding
       server routine is called.

   -r  Generate the two default extension files produced by the -C and -S
       options.

   The following options can be used on any combination of rxgen calls:

   -R  Generate code for the older \R protocol, as opposed to Rx, which is
       the default.

   -k  Must be specified when the generated code is intended to be used by
       the kernel; special "includes" and other specifics are produced
       when the target output is for the kernel.

   -p  Package combination flag: when multiple packages are included
       within a single specification file, a single Execute Request
       routine will be used for all of them as a result of this flag.  The
       default is to generate individual Execute Request stubs for each
       package.

   -I dir
       Similar to the -I flag in the C compiler (cc). This flag is passed
       to the pre-processor (cpp) so that directory dir is searched before
       the standard lookup list for #include files.  As expected, multiple
       -I flags can be used simultaneously.

   -P prefix
       The prefix string following this switch is prepended to all
       generated output files; useful when multiple runs want to produce
       different versions of the same interface (say, kernel and non-
       kernel versions).

   -d  Debugging mode; only needed when rxgen is to be debugged (say, via
       dbx).

   -o outfile
       Specify the name of the output file.  If none is specified, the
       standard output is used (-c, -h, -C, and -S modes only).  Note that
       if an output file is specified in a multi-output file option (such
       as the default, or with option -r), then the outfile replaces the
       name generated by default (which is based on the configuration's
       main file name).

   The -s, -l, and -m options are present only for rpcgen support.  See
   rpcgen(1) for information on their use.

rxgen SYNTAX SUMMARY

       Specification file:

           <Package description option> |
           <Prefix description option> |
           <StartingOpcode description option> |
           <SplitPrefix description option> |
           <Procedure description option> |
           <RPCL language description option>

       <Package description option>:

           "package" <Package_ident>

       <Prefix description option>:

           "prefix" <Prefix_ident>

       <StartingOpcode description option>:

           "startingopcode" <constant>

       <SplitPrefix description option>:

           "splitprefix" <split options> ";"

       <Split options>:

           "IN =" <Start_prefix_ident> "|"
           "OUT =" <End_prefix_ident> "|"
           <Split options>

       <Procedure description option>:

           ["proc"] [<Procedure_ident>] [<ServerStub_ident>]
               <Argument list> ["split" | "multi"]
               ["=" <Opcode_ident>] ";"

       <Argument list>:

           "(" <Argument definition> <Comma_joined argument> ")"

       <Argument definition>:

           <Direction option> <Standard RPCL type decl> <Arg_ident>
               ["<" <Max_size> ">" | "[" <Max_size> "]"] | NULL

       <Comma_joined argument>:

           "," <Argument definition> | NULL

       <Direction option>:

           "IN" | "OUT" | "INOUT" | NULL

       <Max_size>:

           <constant> | NULL

       <Package_ident>:
       <Prefix_ident>:
       <String_ident>:
       <Start_prefix_ident>:
       <End_prefix_ident>:
       <Procedure_ident>:
       <ServerStub_ident>:
       <Arg_ident>:
       <Opcode_ident>:

           <identifier>

       <RPCL language description option>:
       <Standard RPCL type decl>:

           Sun's RPCL language syntax (see rpcgen(1))

rxgen COMMANDS

   Comments and Preprocessing
   The input interface may contain preprocessor directives which are
   passed through the C preprocessor (i.e. "cpp").  Since the preprocessor
   runs on all input files before they are actually interpreted by rxgen,
   all cpp directives (#include, #ifdefs, #defines, etc.) are legal and
   welcomed within an rxgen input file.  Of course, none of these
   preprocessor directives will be included in any of the generated files.
   To facilitate distinctions between the different types of output files,
   rxgen defines certain special cpp symbols for use by the rxgen
   programmer.  These are RPC_HDR (defined when compiling into header,
   filename.h, files), RPC_XDR (defined when compiling into xdr,
   filename.xdr.c, files), RPC_CLIENT (defined when compiling into client
   stubs, filename.cs.c, files), and RPC_SERVER (defined when compiling
   into server stubs, filename.ss.c, files).

   In addition, rxgen does a little preprocessing of its own.  Any line
   beginning with "%" is passed directly into the output file,
   uninterpreted by rxgen.  For a more heavy en masse dumping of
   uninterpreted code, it would be advised to include all such code in an
   "#include" file and pass it in preceded by "%".  The input interface
   may also contain any C-style comments which are, of course, ignored.
   Interpretation is token-based, thus special line-orientation of
   separate statements is not necessary.  rxgen also provides a quite rich
   and helpful set of error reports, identifying them by exact line
   location and error type.  Also, rxgen will automatically generate
   #include lines for standard include files, such as rx/xdr.h and
   rx/rx.h, along with the generated header file from this interface.

   Prefixing stub procedures
   The package statement tells rxgen the name of the interface package.
   It is used for prefixing the naming of all generated stub routines and
   the execute request procedure.  For example:

       package AFS_

   causes the execute request procedure to be named AFS_ExecuteRequest
   (Warning: in the older version an additional "_" was appended after the
   package name to the ExecuteRequest name; thus make sure you don't have
   an ExecuteRequest interface routine) and a given stub routine, say
   Fetch, to be actually named AFS_Fetch.  Multiple package statements
   (current maximum size is 10) per configuration are permitted and are
   useful when multiple sets of interfaces are implemented (see the
   example at the end).  Note that in such cases, use of the -p flag
   results in the generation of just one ExecuteRequest procedure which
   recognizes the multiple interfaces and whose name is prefixed by the
   first package statement.  In the default case, independent
   ExecuteRequest procedures will be created for each packaged group of
   remote procedure calls.

   The prefix statement supplies a name to prepend to all calls to remote
   procedure names in the ExecuteRequest stub routine.  It is useful when
   the server makes RPC calls to other servers (say, for debugging
   purposes).  For example:

       prefix S

   causes the name "S" to be prepended to the name of all routines called
   from the server stubs.  The server can then call the original name and
   get the client stubs.

   rxgen procedure declaration
   The proc statement is the most common (and meaningful) in the rxgen
   interface.  Its syntax description is:

           [proc] [<proc_name>] [<server_stub>] (<arg>, ..., <arg>)
               [split | multi] [= <opcode>] ;

   where:

   * "proc" is an optional prefix of the procedure statement. This is just
     a stylistic item and not a required procedure delimiter.

   * <proc_name> is the name of the procedure.  Note that even the name of
     the procedure is optional.  This only makes sense when the name of
     the given procedure is identical to the name of the last package
     statement (i.e., "package RCallBack" and the declaration of the
     "RCallBack" procedure).

   * <server_stub>, if present, causes the ExecuteRequest procedure to
     call that stub instead of the automatically generated stub when a
     call with that opcode is decoded.

   * <opcode> is a constant or symbol that is the opcode for that
     procedure.  One might use the preprocessor features (i.e., #define),
     the const RPC-language feature, or the old good constants as opcodes.
     Some further evaluation/processing of opcodes is done.  Particularly,
     checks for duplicate and non-existent opcodes are performed, along
     with checks for "holes" (i.e., gaps in consecutive opcodes) in the
     opcode sequences.  For example, we use the fact that when "holes" in
     opcodes exist, the ExecuteRequest procedure uses the case statement
     rather than the faster (and smaller, codewise) indexed array method.

     Also, rxgen defines (i.e., appends to the header file) three valuable
     macros for each package group: <package-name>LOWEST_OPCODE,
     <package-name>HIGHEST_OPCODE, and <package-name>NUMBER_OPCODES.
     These may be useful to the rxgen programmer.  Also, notice that the
     opcode statement is an optional feature, and can be omitted.  In such
     cases, automatic opcode numbers are generated sequentially, starting
     from 0.

     One can change the initial opcode number by using the startingopcode
     (for lack of a better name) rxgen command.  Its syntax is:

         startingopcode <constant>

     where <constant> must be reasonable!  Note that one can not mix
     procedures, some with opcodes and some without, nor allow opcodes
     after the specification of the startingopcode statement.  rxgen will
     complain in all such cases.

   * The argument entry represents a given parameter of the procedure.
     Its syntax is:

         [IN | INOUT | OUT | <null>] <type_decl> <arg_name>
             [<max>|<>|[max]|[]]

     If the type is an indirect type (i.e., is followed by *), it is
     assumed that the pointer should be followed one level and the data
     pointed to is to be transmitted. This should normally be used for all
     structures/arrays and out parameters.  A noticeable exception is when
     explicit array/structure maximum size is given; since no array-of-
     pointer declarations are allowed one should use typedefs to achieve
     the similar effect.  The parameters could be input parameters
     (preceded by IN), output parameters (preceded by OUT), or
     input/output parameters (preceded by INOUT).  If not specified, then
     the direction of the previous parameter in the procedure is used.
     (Note: the first parameter must be preceded by the directional
     primitive!)

   * "split" is a hack to handle stub routines that do things such as file
     transfers or any other operation that has to exchange information
     (e.g., length of a file) before the call returns its output
     parameters.  Because of the particular handshake that is involved
     when doing remote file transfer, we currently break all such calls
     into two client-side stub routines.  The first (with the default
     prefix of "Begin") is used to pass all IN and INOUT parameters to the
     server side.  The second (with the default prefix of "End") is used
     to get back the INOUT and OUT parameters from the server.  Between
     the two calls, the user is supposed to do the appropriate calls for
     the file transfer. For example, the following procedure declaration
     in package AFS_

         Fetch (IN a, b,INOUT c, OUT d) split = FETCHOPCODE;

     will roughly generate the two independent client stub routines:

         BeginAFS_Fetch (IN a, b, c)

     and

         EndAFS_Fetch(OUT c, d)

     The splitprefix statement is used to change the default prefix names
     used by the two client-side stub generated routines when dealing with
     file transfer-related procedure calls.  For example:

         splitprefix IN=Before_ OUT=After_

     will cause the naming of the two client stubs for a file transfer-
     related routine, say Fetch(), to be Before_AFS_Fetch() and
     After_AFS_Fetch(), respectively.

   * The "multi" option is nearly identical to the "split" feature
     described above.  The only significant visible difference is that
     along with the two client stubs, the standard client stub is also
     generated.  Since the intention is to handle the multi-Rx calls, we
     need the whole standard procedure stub in the cases where no multi-Rx
     call of the procedure is performed.  A side effect of the "multi"
     option is the generation of a special macro (i.e.,
     "multi_<Procedure-name>" which passes back as arguments the "Begin"
     and "End" stubs in the header output file. This macro is used
     directly by the Rx code when a multi-Rx call of this procedure is
     performed.

   OBSOLETE rxgen FEATURES
   Although the following rxgen commands are still in effect, they will
   soon be removed since there are better alternatives. DO NOT USE THEM!

   The special statement is a temporary hack used to handle certain
   inefficiencies of standard xdr routines to handle some user-customized
   declarations.  In particular, this applies to a string pointer
   specified as part of a declaration.  For example,

       special struct BBS SeqBody;

   tells rxgen that the entry "SeqBody" in the user-defined BBS xdr
   routine is a string (note that more than one string can be "special"
   per structure -- multiple ones are separated by commas); it will thus
   allocate and de-allocate space properly in the server-generated stubs
   that contain this structure as an IN or INOUT parameter.

   A better alternative to special is the customized statement, which is
   simply the "customized" token followed by the regular declaration of a
   struct based on the RPCL rules. In this case, the declaration will be
   included in the generated header file (-h option) but no xdr routine
   will be generated for this structure -- the user will supply this.  All
   pointer entries in this structure will be remembered so when the
   structure is used as an IN or INOUT in the server stub, no core leaks
   will occur.  For example, consider

       customized struct CBS {
           long Seqlen;
           char *SeqBody;
       }

   The "xdr_CBS" routine would be provided by the user where during the
   DECODE xdr opcode, appropriate space for the "SeqBody" string is
   allocated.  Similarly, that space is freed during the FREE xdr opcode.

   Note: Old style "Array parameter specifications" are not supported any
   more.

EXAMPLES

   In case there are some requirements not available by the current RPC
   language, one can customize some XDR routines by leaving those data
   types undefined. For every data type that is undefined, it will be
   assumed that a routine exists with the name "xdr_" prepended to it.  A
   selected set of rxgen features is presented below, but for a more
   comprehensive one (unions, complex examples, etc) please refer to the
   rpcgen Programming Guide and eXternal Data Representation: Sun
   Technical Notes.

   Typedefs
   The RPC typedef statement is identical to the C typedef (i.e. "typedef
   <declaration>").  By default, most user declarations (i.e. structs,
   unions, etc) are automatically typedef'ed by rxgen.  Since it makes
   parsing simpler, its usage is recommended by rxgen scripts.

   Strings
   The C "char *" string convention is kind of ambiguous, since it is
   usually intended to mean a null-terminated string of characters, but it
   could also represent a pointer to a single character, a pointer to an
   array of characters, etc.  In the RPC language, a null-terminated
   string is unambiguously called a "string".  Examples,

       string bigname<>;
       string name<MAXNAMELEN>;
       typedef string volname<MAXVOLNAME>;

   Notice that the maximum size of string can be arbitrary (like "bigname"
   above) or, preferably, or specified in angle brackets (i.e. "name" and
   "volname" above).  In practice, one should always use only bounded
   strings in interfaces.  A sample calling proc using the declarations
   above would be:

       GetEntryByName (IN volname name,
           OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;

   or, of course,

       GetEntryByName (IN string volname<MAXVOLNAME>,
           OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;

   It is very important for the user to understand when the string
   parameters should be allocated and/or freed by the his/her client
   and/or server programs. A short analysis on string parameters handling
   follows (note that a similar method is used for the handling of
   variable length arrays as it will be shown later on):

   * In the client side: IN and INOUT string parameters are the
     programmer's responsibility and should be allocated (static or via
     malloc) before calling the rpc and freed (if malloc was used) after
     the rpc's return in the user's client program; of course, for INOUT
     parameters, the returned string can't be bigger than the malloced
     input string.

     OUT string parameters are automatically malloced (based on the length
     of the returned string and not the maxsize) by the rxgen client stubs
     (in filename.cs.c) and must be freed by the client program;
     admittedly, this could be somewhat confusing since the user needs to
     free something that he/she didn't allocate.}

   * In the server side: IN and INOUT string parameters are automatically
     malloced (based on the size of incoming strings) by the rxgen server
     stubs (in filename.ss.c) before they are passed to the user's server
     procedure; that space is automatically freed just before the rxgen
     server stub returns; therefore the user need not do anything special
     for IN and INOUT string parameters.

     OUT string parameters must be malloced by the user's server procedure
     (i.e. null pointer is passed to it by the rxgen server stub) and it
     is automatically freed at the end of the rxgen server stub.  Like in
     the client side, the OUT parameters are somewhat unorthodox (i.e. the
     server routine must malloc a string without ever freeing it itself;
     this is done by the rxgen server stub).

   Note that for INOUT and OUT string parameters, in both the client and
   server sides their arguments must be char of pointers (i.e. char **).

   Pointers
   Pointer declarations in RPC are also exactly as they are in C (i.e.
   "struct single_vldbentry *vldblist;").  Of course, one can't send
   pointers over the network, but one can use XDR pointers for sending
   recursive data types such as lists and trees (an example of a linked
   list will be demonstrated shortly).

   Arrays
   Fixed arrays are just like standard C array declarations (i.e. "struct
   UpdateEntry entries[20]") without any side effect problems in rxgen.
   Since variable-length arrays have no explicit syntax in C, the angle-
   brackets are used for it and the array declarations are actually
   compiled into "struct"s. For example, declarations such as:

       const   MAXBULKSIZE     = 10000;
       const   MAXENTRIES      = 100;
       opaque  bulk<MAXBULKSIZE>;           /* At most 10000 items */
       int     hosts<>;                     /* any number of items */
       typedef vldbentry blkentries<100>;   /* Preferable array decl */

   are compiled into the following structs:

       struct {
           u_int   bulk_len;       /* no of items */
           char    *bulk_val;      /* pointer to array */
       } bulk;

   for the "bulk" array, and similarly for the "blkentries<100>" array,

       struct {
           u_int      blkentries_len;   /* no of items in array */
           vldbentry  *blkentries_val;  /* pointer to array */
       } blkentries;

   Therefore the user should be aware of the "magically" generated
   structure entries such as the number of items in the array
   (<array_name>_len) and the pointer to the array (<array_name>_val)
   since some of the entries will have to be filled in from the
   client/server programs.  A sample proc would be:

       typedef vldbentry blkentries<MAXENTRIES>;
       proc GetBlk (OUT blkentries *vlentries) = VL_GETBLK;

   or, more directly,

       GetBlk(OUT vldbentry vlentries<MAXENTRIES>) = VL_GETBLK;

   Note that although the latest method is preferable since one does not
   have to first use the typedef statement (and admittedly, programmers
   prefer avoiding typedefs), one should realize that rxgen does the
   structure expansion and the xdr creation implicitly; therefore the user
   should be aware of the "vldbentries_val" and "vldbentries_len" fields
   as before (see following examples).

   Array example I (least desirable)

   Procedure declaration in the interface configuration:

       proc ListAttributes (IN vldblistbyattributes *attributes,
                    INOUT blkentries *vldbentries) = VL_LISTATTRIBUTES;

   Sample CLIENT code:

       blkentries entries, *pnt;
       entries.blkentries_len = 10;   /* max # returned entries */
       entries.blkentries_val = (vldbentry *)malloc(LEN);
                                      /* It must be set */

       code = VL_ListAttributes(&attributes, &entries);
       if (!code) {
           pnt = entries.blkentries_val;
           for (i=0; i < entries.blkentries_len; i++, pnt++)
                   display_vldbentry(pnt);
           /* Make sure you free the allocated space */
           free((char *)entries.blkentries_val);
       }

   Sample SERVER code:

       VL_ListAttributes(attributes, entries)
       {
           vldbentry *singleentry = entries->blkentries_val;
           entries->blkentries_len = 0;

           while (copy_to_vldbentry(&vlentry, singleentry))
               singleentry++, vldbentries->entries_len++;
       }

   Although this method for variable-size arrays works fine, there are
   some major drawbacks.  The array parameter (i.e. vldbentries above)
   must be declared as INOUT since we need to pass the max length of the
   expected returned array; more importantly, a big (depending on the
   value of "_len") chunk of junk code is going to be transferred to the
   server as result of the IN(out) side-effect of the array.  It's an easy
   and convenient method if the returned array size can be predicted from
   the start and when the size is quite high.  This method is included as
   an example of erroneous use (and abuse) of rxgen and should not be
   used.

   Array example II (Desirable method)

   Procedure declaration in the interface configuration (using Example I
   above):

       proc ListAttributes (IN vldblistbyattributes *attributes,
           OUT blkentries *vldbentries) = VL_LISTATTRIBUTES;

   Sample CLIENT code:

       blkentries entries, *pnt;

       code = VL_ListAttributes(&attributes, &entries);
       if (!code) {
           pnt = entries.blkentries_val;
           for (i=0; i < entries.blkentries_len; i++, pnt++)
                   display_vldbentry(pnt);
           /* Make sure you free the allocated space (by rxgen) */
           free((char *)entries.blkentries_val);
       }

   Sample SERVER code:

       VL_ListAttributes(attributes, entries)
       {
           vldbentry *singleentry;
           entries->blkentries_len = 0;
           singleentry = entries->blkentries_val
               = (vldbentry *)malloc(MAXENTRIES * sizeof(vldbentry));

           while (copy_to_vldbentry(&vlentry, singleentry))
                   singleentry++, vldbentries->entries_len++;
       }

   This is the best (and simplest) way of using variable-size arrays as an
   output parameter.  It is the responsibility of the server-side stub to
   malloc() the adequate space which is automatically freed by the rxgen
   stub; the client side should free the space allocated by the
   rxgen-calling stub.

   Array example III (Linked Lists)

   Considering the following 3 declarations (could have applied some
   optimizations) in the configuration file:

       typedef struct single_vldbentry *vldblist;
       struct single_vldbentry {
           vldbentry vlentry;
           vldblist  next_vldb;
       };

       struct vldb_list {
           vldblist node;
       };

   and the rxgen procedure declaration:

       LinkedList (IN vldblistbyattributes *attributes,
           OUT vldb_list *linkedentries) = VL_LINKEDLIST;

   Sample CLIENT code:

       vldb_list       linkedvldbs;
       vldblist        vllist, vllist1;

       bzero(&linkedvldbs, sizeof(vldb_list));
       code = VL_LinkedList(&attributes, &nentries, &linkedvldbs);
       if (!code) {
           printf("We got %d vldb entries\n", nentries);
           for (vllist = linkedvldbs.node; vllist; vllist = vllist1) {
               vllist1 = vllist->next_vldb;
               display_entry(&vllist->vlentry);
               free((char *)vllist);
           }
       }

   Sample SERVER code:

       VL_LinkedList(rxcall, attributes, nentries, linkedvldbs);
       {
           vldblist vllist, *vllistptr = &linkedvldbs->node;
           while (...) {
               vllist = *vllistptr
                   = (single_vldbentry *)malloc (sizeof (single_vldbentry));
               copy_to_vldbentry(&tentry, &vllist->vlentry);
               nentries++;
               vllistptr = &vllist->next_vldb;
           };
           *vllistptr = NULL;
       }

   Using a linked list offers many advantages: Nothing is passed to the
   server (the parameter is OUT), no additional overhead is involved, and
   the caller doesn't have to explicitly prepare for an arbitrary return
   size.  A drawback is that the caller has the responsibility of malloc()
   (on the server) and free (on the client) of each entry (to avoid
   unwanted core-leaks).  Another drawback is that since it's a recursive
   call, the C stack will grow linearly with respect to the number of
   nodes in the list (so it's wise to increase the Rx LWP stack if huge
   amounts of data are expected back -- default stack size is 4K).  The
   advantages should outweigh the disadvantages here.

   It's important to pay attention to the comments of the three array
   examples above particularly when they're references to when the user
   should allocate/free space for the variable length arrays.  The
   mechanism is very similar to the handling of strings thus you might
   need to review the strings section above; note that the linked lists
   are handled somewhat differently...

   Miscellaneous examples
   Below is an abbreviated version of a random interface file which shows
   some of the common cases.

       /* Declaration of all structures used by the R.xg script interface */

       struct AFSFid {
           unsigned long Volume;
           unsigned long Vnode;
           unsigned long Unique;
       };

       typedef long ViceDataType;

       /* Note that TEST would be equivalent to "HEADER" only during the
          processing of the header, *.h, file */

       #ifdef RPC_HDR
       #define TEST "HEADER"
       #else
       #define TEST "REST"
       #endif

       /* This is the standard *.xg specification file */

       package AFS_
       splitprefix IN=BEFORE_ OUT=AFTER_;
       Prefix Test

       proc Remove(IN struct AFSFid *Did, IN string volname<64>,
           OUT struct AFSStatus *Status) = AFS_REMOVE;

       DisconnectFS AUX_disconnectFS() = AFS_DISCONNECTFS;

       proc GetVolumeInfo(IN string Vid,
           OUT struct VolumeInfo *Info) = AFS_GETVOLUMEINFO;

       /* You could have more than an interface per configuration */

       package VOTE_

       /* Using the "multi" feature; thus VOTE_Beacon can be called as an
          multi-Rx call or as a regular call */

       Beacon (IN long state, long voteStart,
           net_version *version, net_tid *tid)
           multi = VOTE_BEACON;

       package DISK_

       /* Using the "split" feature */

       SendFile (IN long file, long offset,
           long length, net_version *version)
           split = DISK_SENDFILE;

   Output of an actual interface configuration
   We'll demonstrate some of the actual output generated by rxgen by
   following an abbreviated actual interface configuration.

   Configuration file

   Contents of the interface configuration file (vldbint.xg):

       package VL_
       #include "vl_opcodes.h"   /* The opcodes are included here */
       %#include "vl_opcodes.h"  /* directly to other places */

       /* Current limitations on parameters that affect other packages
          (i.e. volume) */

       const   MAXNAMELEN      =       65;
       const   MAXNSERVERS     =       8;
       const   MAXTYPES        =       3;

       /* External (visible) representation of an individual vldb entry */

       struct vldbentry {
           char    name[MAXNAMELEN];
           long    volumeType;
           long    nServers;
           long    serverNumber[MAXNSERVERS];
           long    serverPartition[MAXNSERVERS];
           long    serverFlags[MAXNSERVERS];
           u_long  volumeId[MAXTYPES];
           long    flags;
       };

       typedef struct single_vldbentry  *vldblist;
       struct single_vldbentry {
           vldbentry VldbEntry;
           vldblist next_vldb;
       };

       struct vldb_list {
           vldblist node;
       };

       /* vldb interface calls */

       CreateEntry     (IN long Volid,
                       vldbentry *newentry) = VLCREATEENTRY;

       GetEntryByName  (IN string volumename<MAXNAMELEN>,
                       OUT vldbentry *entry) = VLGETENTRYBYNAME;

       GetNewVolumeId  (IN long bumpcount,
                       OUT long *newvolumid) = VLGETNEWVOLUMEID;

       ReplaceEntry    (IN long Volid,
                       long voltype,
                       vldbentry *newentry,
                       long ReleaseType) multi = VLREPLACEENTRY;

       ListAttributes  (IN VldbListByAttributes *attributes,
                       OUT long *nentries,
                       OUT vldbentry bulkentries<MAXVLDBLEN>)
                       = VLLISTATTRIBUTES;

       LinkedList      (IN VldbListByAttributes *attributes,
                       OUT long *nentries,
                       OUT vldb_list *linkedentries) = VLLINKEDLIST;

   We'll concentrate only on the Rx generated code since the R generated
   code (-R option) will soon be obsolete.  For a detailed description on
   the Rx-related calls inside the generated stubs (i.e., rx_NewCall(),
   rx_EndCall()), along with details on what happens inside certain calls
   (like xdrrx_create()) please refer to the Rx documentation. Typing
   "rxgen vldbint.xg" will result in the creation of four files:
   vldbint.h, vldbint.xdr.c, vldbint.cs.c and vldbint.ss.c.  A closer look
   at these files follows.

   Header file (vldbint.h)

       /* Machine generated file -- Do NOT edit */

       #include "vl_opcodes.h"  /* directly to other places */
       #define MAXNAMELEN 65
       #define MAXNSERVERS 8
       #define MAXTYPES 3

       struct vldbentry {
           char name[MAXNAMELEN];
           long volumeType;
           long nServers;
           long serverNumber[MAXNSERVERS];
           long serverPartition[MAXNSERVERS];
           long serverFlags[MAXNSERVERS];
           u_long volumeId[MAXTYPES];
           long flags;
       };
       typedef struct vldbentry vldbentry;
       bool_t xdr_vldbentry();

       typedef struct single_vldbentry *vldblist;
       bool_t xdr_vldblist();

       struct single_vldbentry {
           vldbentry VldbEntry;
           vldblist next_vldb;
       };
       typedef struct single_vldbentry single_vldbentry;
       bool_t xdr_single_vldbentry();

       struct vldb_list {
           vldblist node;
       };
       typedef struct vldb_list vldb_list;
       bool_t xdr_vldb_list();

       #include <rx/rx_multi.h>
       #define multi_VL_ReplaceEntry(Volid, voltype, newentry, ReleaseType) \
           multi_Body(StartVL_ReplaceEntry(multi_call, Volid, voltype,
                      newentry, ReleaseType), EndVL_ReplaceEntry(multi_call))

       typedef struct bulkentries {
           u_int bulkentries_len;
           vldbentry *bulkentries_val;
       } bulkentries;
       bool_t xdr_bulkentries();

       /* Opcode-related useful stats for package: VL_ */
       #define VL_LOWEST_OPCODE        501
       #define VL_HIGHEST_OPCODE       506
       #define VL_NUMBER_OPCODES       6

   Notice that all structures are automatically typedef'ed and all
   "const"s are converted to "#define"s. Some data structures, such as
   bulkentries, are taken from procedure params (from ListAttributes
   proc). Thus, this should be kept in mind when creating stubs piecemeal
   with rxgen (i.e., using the -c, -h, -C, or -S flags).  Also, one of the
   side effects of the "multi" option (in "ReplaceEntry" proc) is the
   generation of the "multi_VL_ReplaceEntry" above.

   XDR routines for structures (vldbint.xdr.c)

       /* Machine generated file -- Do NOT edit */

       #include <rx/xdr.h>
       #include "vldbint.h"

       #include "vl_opcodes.h"  /* directly to other places */

       bool_t
       xdr_vldbentry(xdrs, objp)
           XDR *xdrs;
           vldbentry *objp;
       {
           if (!xdr_vector(xdrs, (char *)objp->name, MAXNAMELEN,
                           sizeof(char), xdr_char))
               return (FALSE);
           if (!xdr_long(xdrs, &objp->volumeType))
               return (FALSE);
           if (!xdr_long(xdrs, &objp->nServers))
               return (FALSE);
           if (!xdr_vector(xdrs, (char *)objp->serverNumber, MAXNSERVERS,
                           sizeof(long), xdr_long))
               return (FALSE);
           if (!xdr_vector(xdrs, (char *)objp->serverPartition,
                           MAXNSERVERS, sizeof(long), xdr_long))
               return (FALSE);
           if (!xdr_vector(xdrs, (char *)objp->serverFlags, MAXNSERVERS,
                           sizeof(long), xdr_long))
               return (FALSE);
           if (!xdr_vector(xdrs, (char *)objp->volumeId, MAXTYPES,
                           sizeof(u_long), xdr_u_long))
               return (FALSE);
           if (!xdr_long(xdrs, &objp->flags))
               return (FALSE);
           return (TRUE);
       }

       bool_t
       xdr_vldblist(xdrs, objp)
           XDR *xdrs;
           vldblist *objp;
       {
           if (!xdr_pointer(xdrs, (char **)objp,
                            sizeof(struct single_vldbentry),
                            xdr_single_vldbentry))
               return (FALSE);
           return (TRUE);
       }

       bool_t
       xdr_single_vldbentry(xdrs, objp)
           XDR *xdrs;
           single_vldbentry *objp;
       {
           if (!xdr_vldbentry(xdrs, &objp->VldbEntry))
               return (FALSE);
           if (!xdr_vldblist(xdrs, &objp->next_vldb))
               return (FALSE);
           return (TRUE);
       }

       bool_t
       xdr_vldb_list(xdrs, objp)
           XDR *xdrs;
           vldb_list *objp;
       {
           if (!xdr_vldblist(xdrs, &objp->node))
               return (FALSE);
           return (TRUE);
       }

       bool_t
       xdr_bulkentries(xdrs, objp)
           XDR *xdrs;
           bulkentries *objp;
       {
           if (!xdr_array(xdrs, (char **)&objp->bulkentries_val,
                          (u_int *)&objp->bulkentries_len, MAXVLDBLEN,
                          sizeof(vldbentry), xdr_vldbentry))
               return (FALSE);
           return (TRUE);
       }

   Note that the xdr_bulkentries() is automatically generated as a side
   effect of a procedure parameter declaration.  Thus, if identical
   multiple type parameter declarations are used, then multiply-defined
   xdr_* stubs will be created!  We felt this was a better alternative to
   having the rxgen programmer deal with types such as bulkentries_1,
   bulkentries_2...

   Client-Side stub routines (vldbint.cs.c)

       /* Machine generated file -- Do NOT edit */

       #include <rx/xdr.h>
       #include <rx/rx.h>
       #include <afs/rxgen_consts.h>
       #include "vldbint.h"

       #include "vl_opcodes.h"  /* directly to other places */

       int VL_CreateEntry(z_conn, Volid, newentry)
           register struct rx_connection *z_conn;
           long Volid;
           vldbentry * newentry;
       {
           struct rx_call *z_call = rx_NewCall(z_conn);
           static int z_op = 501;
           int z_result;
           XDR z_xdrs;

           xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

           /* Marshal the arguments */
           if ((!xdr_int(&z_xdrs, &z_op))
                || (!xdr_long(&z_xdrs, &Volid))
                || (!xdr_vldbentry(&z_xdrs, newentry))) {
                   z_result = RXGEN_CC_MARSHAL;
                   goto fail;
           }

           z_result = RXGEN_SUCCESS;
       fail:
           return rx_EndCall(z_call, z_result);
       }

       int VL_GetEntryByName(z_conn, volumename, entry)
           register struct rx_connection *z_conn;
           char * volumename;
           vldbentry * entry;
       {
           struct rx_call *z_call = rx_NewCall(z_conn);
           static int z_op = 504;
           int z_result;
           XDR z_xdrs;

           xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

           /* Marshal the arguments */
           if ((!xdr_int(&z_xdrs, &z_op))
                || (!xdr_string(&z_xdrs, &volumename, 65))) {
                   z_result = RXGEN_CC_MARSHAL;
                   goto fail;
           }

           /* Un-marshal the reply arguments */
           z_xdrs.x_op = XDR_DECODE;
           if ((!xdr_vldbentry(&z_xdrs, entry))) {
                   z_result = RXGEN_CC_UNMARSHAL;
                   goto fail;
           }

           z_result = RXGEN_SUCCESS;
       fail:
           return rx_EndCall(z_call, z_result);
       }

       int VL_GetNewVolumeId(z_conn, bumpcount, newvolumid)
           register struct rx_connection *z_conn;
           long bumpcount;
           long * newvolumid;
       {
           struct rx_call *z_call = rx_NewCall(z_conn);
           static int z_op = 505;
           int z_result;
           XDR z_xdrs;

           xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

           /* Marshal the arguments */
           if ((!xdr_int(&z_xdrs, &z_op))
                || (!xdr_long(&z_xdrs, &bumpcount))) {
                   z_result = RXGEN_CC_MARSHAL;
                   goto fail;
           }

           /* Un-marshal the reply arguments */
           z_xdrs.x_op = XDR_DECODE;
           if ((!xdr_long(&z_xdrs, newvolumid))) {
                   z_result = RXGEN_CC_UNMARSHAL;
                   goto fail;
           }

           z_result = RXGEN_SUCCESS;
       fail:
           return rx_EndCall(z_call, z_result);
       }

       int VL_ReplaceEntry(z_conn, Volid, voltype, newentry, ReleaseType)
           register struct rx_connection *z_conn;
           long Volid, voltype, ReleaseType;
           vldbentry * newentry;
       {
           struct rx_call *z_call = rx_NewCall(z_conn);
           static int z_op = 506;
           int z_result;
           XDR z_xdrs;

           xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

           /* Marshal the arguments */
           if ((!xdr_int(&z_xdrs, &z_op))
                || (!xdr_long(&z_xdrs, &Volid))
                || (!xdr_long(&z_xdrs, &voltype))
                || (!xdr_vldbentry(&z_xdrs, newentry))
                || (!xdr_long(&z_xdrs, &ReleaseType))) {
                   z_result = RXGEN_CC_MARSHAL;
                   goto fail;
           }

           z_result = RXGEN_SUCCESS;
       fail:
           return rx_EndCall(z_call, z_result);
       }

       int StartVL_ReplaceEntry(z_call, Volid, voltype, newentry, ReleaseType)
           register struct rx_call *z_call;
           long Volid, voltype, ReleaseType;
           vldbentry * newentry;
       {
           static int z_op = 506;
           int z_result;
           XDR z_xdrs;

           xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

           /* Marshal the arguments */
           if ((!xdr_int(&z_xdrs, &z_op))
                || (!xdr_long(&z_xdrs, &Volid))
                || (!xdr_long(&z_xdrs, &voltype))
                || (!xdr_vldbentry(&z_xdrs, newentry))
                || (!xdr_long(&z_xdrs, &ReleaseType))) {
                   z_result = RXGEN_CC_MARSHAL;
                   goto fail;
           }

           z_result = RXGEN_SUCCESS;
       fail:
           return z_result;
       }

       int EndVL_ReplaceEntry(z_call)
           register struct rx_call *z_call;
       {
           int z_result;
           XDR z_xdrs;

           z_result = RXGEN_SUCCESS;
       fail:
           return z_result;
       }

       int VL_ListAttributes(z_conn, attributes, nentries, bulkentries_1)
           register struct rx_connection *z_conn;
           VldbListByAttributes * attributes;
           long * nentries;
           bulkentries * bulkentries_1;
       {
           struct rx_call *z_call = rx_NewCall(z_conn);
           static int z_op = 511;
           int z_result;
           XDR z_xdrs;

           xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

           /* Marshal the arguments */
           if ((!xdr_int(&z_xdrs, &z_op))
                || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
                   z_result = RXGEN_CC_MARSHAL;
                   goto fail;
           }

           /* Un-marshal the reply arguments */
           z_xdrs.x_op = XDR_DECODE;
           if ((!xdr_long(&z_xdrs, nentries))
                || (!xdr_bulkentries(&z_xdrs, bulkentries_1))) {
                   z_result = RXGEN_CC_UNMARSHAL;
                   goto fail;
           }

           z_result = RXGEN_SUCCESS;
       fail:
           return rx_EndCall(z_call, z_result);
       }

       int VL_LinkedList(z_conn, attributes, nentries, linkedentries)
           register struct rx_connection *z_conn;
           VldbListByAttributes * attributes;
           long * nentries;
           vldb_list * linkedentries;
       {
           struct rx_call *z_call = rx_NewCall(z_conn);
           static int z_op = 512;
           int z_result;
           XDR z_xdrs;

           xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);

           /* Marshal the arguments */
           if ((!xdr_int(&z_xdrs, &z_op))
                || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
                   z_result = RXGEN_CC_MARSHAL;
                   goto fail;
           }

           /* Un-marshal the reply arguments */
           z_xdrs.x_op = XDR_DECODE;
           if ((!xdr_long(&z_xdrs, nentries))
                || (!xdr_vldb_list(&z_xdrs, linkedentries))) {
                   z_result = RXGEN_CC_UNMARSHAL;
                   goto fail;
           }

           z_result = RXGEN_SUCCESS;
       fail:
           return rx_EndCall(z_call, z_result);
       }

   Notice the side effect of the "multi" feature (three different modules
   for "ReplaceEntry" proc).

   Server-Side stub routines (vldbint.ss.c)

       /* Machine generated file -- Do NOT edit */

       #include <rx/xdr.h>
       #include <rx/rx.h>
       #include <afs/rxgen_consts.h>
       #include "vldbint.h"

       #include "vl_opcodes.h"  /* directly to other places */

       long _VL_CreateEntry(z_call, z_xdrs)
           struct rx_call *z_call;
           XDR *z_xdrs;
       {
           long z_result;
           long Volid;
           vldbentry newentry;

           if ((!xdr_long(z_xdrs, &Volid))
                || (!xdr_vldbentry(z_xdrs, &newentry))) {
                   z_result = RXGEN_SS_UNMARSHAL;
                   goto fail;
           }

           z_result = VL_CreateEntry(z_call, Volid, &newentry);
       fail:
           return z_result;
       }

       long _VL_GetEntryByName(z_call, z_xdrs)
           struct rx_call *z_call;
           XDR *z_xdrs;
       {
           long z_result;
           char *volumename = (char *)0;
           vldbentry entry;

           if ((!xdr_string(z_xdrs, &volumename, 65))) {
                   z_result = RXGEN_SS_UNMARSHAL;
                   goto fail;
           }

           z_result = VL_GetEntryByName(z_call, &volumename, &entry);
           z_xdrs->x_op = XDR_ENCODE;
           if ((!xdr_vldbentry(z_xdrs, &entry)))
                   z_result = RXGEN_SS_MARSHAL;
       fail:
           z_xdrs->x_op = XDR_FREE;
           if (!xdr_string(z_xdrs, &volumename, 65)) goto fail1;
           return z_result;
       fail1:
           return RXGEN_SS_XDRFREE;
       }

       long _VL_GetNewVolumeId(z_call, z_xdrs)
           struct rx_call *z_call;
           XDR *z_xdrs;
       {
           long z_result;
           long bumpcount;
           long newvolumid;

           if ((!xdr_long(z_xdrs, &bumpcount))) {
                   z_result = RXGEN_SS_UNMARSHAL;
                   goto fail;
           }

           z_result = VL_GetNewVolumeId(z_call, bumpcount, &newvolumid);
           z_xdrs->x_op = XDR_ENCODE;
           if ((!xdr_long(z_xdrs, &newvolumid)))
                   z_result = RXGEN_SS_MARSHAL;
       fail:
           return z_result;
       }

       long _VL_ReplaceEntry(z_call, z_xdrs)
           struct rx_call *z_call;
           XDR *z_xdrs;
       {
           long z_result;
           long Volid, voltype, ReleaseType;
           vldbentry newentry;

           if ((!xdr_long(z_xdrs, &Volid))
                || (!xdr_long(z_xdrs, &voltype))
                || (!xdr_vldbentry(z_xdrs, &newentry))
                || (!xdr_long(z_xdrs, &ReleaseType))) {
                   z_result = RXGEN_SS_UNMARSHAL;
                   goto fail;
           }

           z_result = VL_ReplaceEntry(z_call, Volid, voltype, &newentry,
                                      ReleaseType);
       fail:
           return z_result;
       }

       long _VL_ListAttributes(z_call, z_xdrs)
           struct rx_call *z_call;
           XDR *z_xdrs;
       {
           long z_result;
           VldbListByAttributes attributes;
           long nentries;
           bulkentries bulkentries_1;

           if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
                   z_result = RXGEN_SS_UNMARSHAL;
                   goto fail;
           }

           z_result = VL_ListAttributes(z_call, &attributes, &nentries,
                                        &bulkentries_1);
           z_xdrs->x_op = XDR_ENCODE;
           if ((!xdr_long(z_xdrs, &nentries))
                || (!xdr_bulkentries(z_xdrs, &bulkentries_1)))
                   z_result = RXGEN_SS_MARSHAL;
       fail:
           z_xdrs->x_op = XDR_FREE;
           if (!xdr_bulkentries(z_xdrs, &bulkentries_1)) goto fail1;
           return z_result;
       fail1:
           return RXGEN_SS_XDRFREE;
       }

       long _VL_LinkedList(z_call, z_xdrs)
           struct rx_call *z_call;
           XDR *z_xdrs;
       {
           long z_result;
           VldbListByAttributes attributes;
           long nentries;
           vldb_list linkedentries;

           if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
                   z_result = RXGEN_SS_UNMARSHAL;
                   goto fail;
           }

           z_result = VL_LinkedList(z_call, &attributes, &nentries,
                                    &linkedentries);
           z_xdrs->x_op = XDR_ENCODE;
           if ((!xdr_long(z_xdrs, &nentries))
                || (!xdr_vldb_list(z_xdrs, &linkedentries)))
                   z_result = RXGEN_SS_MARSHAL;
       fail:
           return z_result;
       }

       long _VL_CreateEntry();
       long _VL_GetEntryByName();
       long _VL_GetNewVolumeId();
       long _VL_ReplaceEntry();
       long _VL_ListAttributes();
       long _VL_LinkedList();

       static long (*StubProcsArray0[])() = {_VL_CreateEntry,
           _VL_GetEntryByName, _VL_GetNewVolumeId, _VL_ReplaceEntry,
           _VL_ListAttributes, _VL_LinkedList};

       VL_ExecuteRequest(z_call)
           register struct rx_call *z_call;
       {
           int op;
           XDR z_xdrs;
           long z_result;

           xdrrx_create(&z_xdrs, z_call, XDR_DECODE);
           if (!xdr_int(&z_xdrs, &op))
               z_result = RXGEN_DECODE;
           else if (op < VL_LOWEST_OPCODE || op > VL_HIGHEST_OPCODE)
               z_result = RXGEN_OPCODE;
           else
               z_result = (*StubProcsArray0[op - VL_LOWEST_OPCODE])
                   (z_call, &z_xdrs);
           return z_result;
       }

   If there were gaps in the procedures' opcode sequence the code for
   VL_ExecuteRequest() routine would be have been drastically different
   (it would have been a case statement for each procedure).

NOTES

   rxgen is implemented from Sun's rpcgen utility.  All of the standard
   rpcgen's functionality is fully maintained.  Note that some active
   rpcgen options that don't apply to rxgen's purpose aren't referenced
   here (i.e., -s, -l, -m options) and the interested reader should refer
   to rpcgen(1) for details.

   When the "%#include <include file"> feature is used make sure that you
   don't have any rxgen language features (i.e. %#defines) since you'll
   get syntax errors during compilations..

   Since this is an ongoing project many of the above may change/disappear
   without a major warning.

SEE ALSO

   Rxgen Syntax Summary: Summary description of rxgen's grammar.

   Rpcgen Programming Guide: Sun's RPC protocol compiler.  rxgen was
   implemented as an extension to that compiler.

   External Data Representation: Sun Technical Notes: Detailed examples in
   using XDR.

   RPCL Syntax Summary: Summary of Sun's Remote Procedure Call Language.

   Rx: An extended Remote Procedure Call Protocol.

   rgen: An earlier version of a similar stub generator used for the R RPC
   protocol.

COPYRIGHT

   IBM Corporation 2000. <http://www.ibm.com/> All Rights Reserved.

   This documentation is covered by the IBM Public License Version 1.0.
   It was converted from the original TeX rxgen manual to POD by Russ
   Allbery.





Opportunity


Personal Opportunity - Free software gives you access to billions of dollars of software at no cost. Use this software for your business, personal use or to develop a profitable skill. Access to source code provides access to a level of capabilities/information that companies protect though copyrights. Open source is a core component of the Internet and it is available to you. Leverage the billions of dollars in resources and capabilities to build a career, establish a business or change the world. The potential is endless for those who understand the opportunity.

Business Opportunity - Goldman Sachs, IBM and countless large corporations are leveraging open source to reduce costs, develop products and increase their bottom lines. Learn what these companies know about open source and how open source can give you the advantage.





Free Software


Free Software provides computer programs and capabilities at no cost but more importantly, it provides the freedom to run, edit, contribute to, and share the software. The importance of free software is a matter of access, not price. Software at no cost is a benefit but ownership rights to the software and source code is far more significant.


Free Office Software - The Libre Office suite provides top desktop productivity tools for free. This includes, a word processor, spreadsheet, presentation engine, drawing and flowcharting, database and math applications. Libre Office is available for Linux or Windows.





Free Books


The Free Books Library is a collection of thousands of the most popular public domain books in an online readable format. The collection includes great classical literature and more recent works where the U.S. copyright has expired. These books are yours to read and use without restrictions.


Source Code - Want to change a program or know how it works? Open Source provides the source code for its programs so that anyone can use, modify or learn how to write those programs themselves. Visit the GNU source code repositories to download the source.





Education


Study at Harvard, Stanford or MIT - Open edX provides free online courses from Harvard, MIT, Columbia, UC Berkeley and other top Universities. Hundreds of courses for almost all major subjects and course levels. Open edx also offers some paid courses and selected certifications.


Linux Manual Pages - A man or manual page is a form of software documentation found on Linux/Unix operating systems. Topics covered include computer programs (including library and system calls), formal standards and conventions, and even abstract concepts.