2005-12-27 23:30:22 +00:00
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
rxgen - Stub generator for the Rx remote procedure call package
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
2006-03-01 05:02:29 +00:00
|
|
|
=for html
|
|
|
|
<div class="synopsis">
|
|
|
|
|
2013-09-20 15:42:20 +01:00
|
|
|
B<rxgen> [B<-h> | B<-c> | B<-C> | B<-S> | B<-r>] [B<-dkp>]
|
2005-12-27 23:30:22 +00:00
|
|
|
[B<-I> I<dir>] [B<-P> I<prefix>] [B<-o> I<outfile>] [I<infile>]
|
|
|
|
|
2006-03-01 05:02:29 +00:00
|
|
|
=for html
|
|
|
|
</div>
|
|
|
|
|
2005-12-27 23:30:22 +00:00
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
|
|
B<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
|
2013-09-20 15:42:20 +01:00
|
|
|
procedures through local procedure calls. B<rxgen> is based on Sun's
|
|
|
|
B<rpcgen> (version 3.9) but does not maintain compatibility with rpcgen
|
|
|
|
RPC descriptions.
|
2005-12-27 23:30:22 +00:00
|
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
|
|
|
|
B<rxgen> operates in several different modes. The generated output files
|
|
|
|
can be produced individually (using one of B<-h>, B<-c>, B<-C>, or B<-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 (B<-C>
|
|
|
|
and B<-S>) when the B<-r> flag is used. The following describes the types
|
|
|
|
of generated output files (for simplicity, I<filename> refers to the main
|
|
|
|
output filename):
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
|
|
=item B<-h>
|
|
|
|
|
|
|
|
Generate C data definitions (a header file) from standard RPCL definitions
|
|
|
|
(default extension: I<filename>.h).
|
|
|
|
|
|
|
|
=item B<-c>
|
|
|
|
|
|
|
|
Compile the XDR routines required to serialize the protocol described by
|
|
|
|
RPCL. Generate XDR routines for all declarations (default extension:
|
|
|
|
I<filename>.xdr.c).
|
|
|
|
|
|
|
|
=item B<-C>
|
|
|
|
|
|
|
|
Generate all the client-side stub routines (default extension:
|
|
|
|
I<filename>.cs.c). Calling a routine in this file will cause the
|
|
|
|
arguments to be packed up and sent via Rx (or R).
|
|
|
|
|
|
|
|
=item B<-S>
|
|
|
|
|
|
|
|
Generate all the server-side stub routines (default extension:
|
|
|
|
I<filename>.ss.c). Arguments are unpacked, and the corresponding server
|
|
|
|
routine is called.
|
|
|
|
|
|
|
|
=item B<-r>
|
|
|
|
|
|
|
|
Generate the two default extension files produced by the B<-C> and B<-S>
|
|
|
|
options.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
The following options can be used on any combination of B<rxgen> calls:
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
|
|
=item B<-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.
|
|
|
|
|
|
|
|
=item B<-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.
|
|
|
|
|
|
|
|
=item B<-I> I<dir>
|
|
|
|
|
|
|
|
Similar to the B<-I> flag in the C compiler (B<cc>). This flag is passed
|
|
|
|
to the pre-processor (B<cpp>) so that directory I<dir> is searched before
|
|
|
|
the standard lookup list for #include files. As expected, multiple B<-I>
|
|
|
|
flags can be used simultaneously.
|
|
|
|
|
|
|
|
=item B<-P> I<prefix>
|
|
|
|
|
|
|
|
The I<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).
|
|
|
|
|
|
|
|
=item B<-d>
|
|
|
|
|
|
|
|
Debugging mode; only needed when B<rxgen> is to be debugged (say, via
|
|
|
|
B<dbx>).
|
|
|
|
|
|
|
|
=item B<-o> I<outfile>
|
|
|
|
|
|
|
|
Specify the name of the output file. If none is specified, the standard
|
|
|
|
output is used (B<-c>, B<-h>, B<-C>, and B<-S> modes only). Note that if
|
|
|
|
an output file is specified in a multi-output file option (such as the
|
|
|
|
default, or with option B<-r>), then the I<outfile> replaces the name
|
|
|
|
generated by default (which is based on the configuration's main file
|
|
|
|
name).
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
=head1 B<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))
|
|
|
|
|
|
|
|
=head1 B<rxgen> COMMANDS
|
|
|
|
|
|
|
|
=head2 Comments and Preprocessing
|
|
|
|
|
|
|
|
The input interface may contain preprocessor directives which are passed
|
|
|
|
through the C preprocessor (i.e. C<cpp>). Since the preprocessor runs on
|
|
|
|
all input files before they are actually interpreted by B<rxgen>, all
|
|
|
|
B<cpp> directives (#include, #ifdefs, #defines, etc.) are legal and
|
|
|
|
welcomed within an B<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,
|
|
|
|
B<rxgen> defines certain special B<cpp> symbols for use by the B<rxgen>
|
|
|
|
programmer. These are RPC_HDR (defined when compiling into header,
|
|
|
|
I<filename>.h, files), RPC_XDR (defined when compiling into xdr,
|
|
|
|
I<filename>.xdr.c, files), RPC_CLIENT (defined when compiling into client
|
|
|
|
stubs, I<filename>.cs.c, files), and RPC_SERVER (defined when compiling
|
|
|
|
into server stubs, I<filename>.ss.c, files).
|
|
|
|
|
|
|
|
In addition, B<rxgen> does a little preprocessing of its own. Any line
|
|
|
|
beginning with C<%> is passed directly into the output file, uninterpreted
|
|
|
|
by B<rxgen>. For a more heavy en masse dumping of uninterpreted code, it
|
2009-12-31 04:30:27 +00:00
|
|
|
would be advised to include all such code in an C<#include> file and pass
|
2005-12-27 23:30:22 +00:00
|
|
|
it in preceded by C<%>. 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.
|
|
|
|
B<rxgen> also provides a quite rich and helpful set of error reports,
|
|
|
|
identifying them by exact line location and error type. Also, B<rxgen>
|
|
|
|
will automatically generate #include lines for standard include files,
|
|
|
|
such as F<rx/xdr.h> and F<rx/rx.h>, along with the generated header file
|
|
|
|
from this interface.
|
|
|
|
|
|
|
|
=head2 Prefixing stub procedures
|
|
|
|
|
|
|
|
The I<package> statement tells B<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 C<_> 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 B<-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 I<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 C<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.
|
|
|
|
|
|
|
|
=head2 B<rxgen> procedure declaration
|
|
|
|
|
|
|
|
The I<proc> statement is the most common (and meaningful) in the B<rxgen>
|
|
|
|
interface. Its syntax description is:
|
|
|
|
|
|
|
|
[proc] [<proc_name>] [<server_stub>] (<arg>, ..., <arg>)
|
|
|
|
[split | multi] [= <opcode>] ;
|
|
|
|
|
|
|
|
where:
|
|
|
|
|
|
|
|
=over 2
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
C<proc> is an optional prefix of the procedure statement. This is just a
|
|
|
|
stylistic item and not a required procedure delimiter.
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
<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 I<package> statement (i.e.,
|
|
|
|
C<package RCallBack> and the declaration of the C<RCallBack> procedure).
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
<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.
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
<opcode> is a constant or symbol that is the opcode for that procedure.
|
|
|
|
One might use the preprocessor features (i.e., #define), the I<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 I<case> statement rather than the faster
|
|
|
|
(and smaller, codewise) indexed array method.
|
|
|
|
|
|
|
|
Also, B<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 B<rxgen> programmer. Also, notice that the I<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 I<startingopcode>
|
|
|
|
(for lack of a better name) B<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 I<startingopcode> statement. B<rxgen> will
|
|
|
|
complain in all such cases.
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
The I<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!)
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
C<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 C<Begin>) is used to pass
|
|
|
|
all IN and INOUT parameters to the server side. The second (with the
|
|
|
|
default prefix of C<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 I<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.
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
The C<multi> option is nearly identical to the C<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 C<multi> option is the generation of a
|
|
|
|
special macro (i.e., C<< multi_<Procedure-name> >> which passes back as
|
|
|
|
arguments the C<Begin> and C<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.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
=head2 OBSOLETE B<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 I<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 B<rxgen> that the entry C<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 I<special> is the I<customized> statement, which
|
|
|
|
is simply the C<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 (B<-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 C<xdr_CBS> routine would be provided by the user where during the
|
|
|
|
DECODE xdr opcode, appropriate space for the C<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.
|
|
|
|
|
|
|
|
=head1 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 C<xdr_> prepended to it. A selected set of
|
|
|
|
B<rxgen> features is presented below, but for a more comprehensive one
|
|
|
|
(unions, complex examples, etc) please refer to the I<rpcgen Programming
|
|
|
|
Guide> and I<eXternal Data Representation: Sun Technical Notes>.
|
|
|
|
|
|
|
|
=head2 Typedefs
|
|
|
|
|
|
|
|
The RPC typedef statement is identical to the C typedef (i.e. C<< typedef
|
|
|
|
<declaration> >>). By default, most user declarations (i.e. structs,
|
|
|
|
unions, etc) are automatically typedef'ed by B<rxgen>. Since it makes
|
|
|
|
parsing simpler, its usage is recommended by B<rxgen> scripts.
|
|
|
|
|
|
|
|
=head2 Strings
|
|
|
|
|
|
|
|
The C 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 C<bigname>
|
|
|
|
above) or, preferably, or specified in angle brackets (i.e. C<name> and
|
|
|
|
C<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):
|
|
|
|
|
|
|
|
=over 2
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
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 B<rxgen> client stubs (in
|
|
|
|
I<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.}
|
|
|
|
|
|
|
|
=item *
|
|
|
|
|
|
|
|
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 I<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 B<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 B<rxgen> server stub).
|
|
|
|
|
2006-02-27 20:46:25 +00:00
|
|
|
=back
|
|
|
|
|
2005-12-27 23:30:22 +00:00
|
|
|
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 **).
|
|
|
|
|
|
|
|
=head2 Pointers
|
|
|
|
|
|
|
|
Pointer declarations in RPC are also exactly as they are in C
|
|
|
|
(i.e. C<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).
|
|
|
|
|
|
|
|
=head2 Arrays
|
|
|
|
|
|
|
|
Fixed arrays are just like standard C array declarations (i.e. C<struct
|
|
|
|
UpdateEntry entries[20]>) without any side effect problems in
|
|
|
|
B<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 C<bulk> array, and similarly for the C<< 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 B<rxgen> does the structure
|
|
|
|
expansion and the xdr creation implicitly; therefore the user should be
|
|
|
|
aware of the C<vldbentries_val> and C<vldbentries_len> fields as before
|
|
|
|
(see following examples).
|
|
|
|
|
|
|
|
=head3 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
|
|
|
|
C<_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 B<rxgen> and should not be used.
|
|
|
|
|
|
|
|
=head3 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 B<rxgen>
|
|
|
|
stub; the client side should free the space allocated by the
|
|
|
|
B<rxgen>-calling stub.
|
|
|
|
|
|
|
|
=head3 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;
|
|
|
|
|
2023-05-08 22:04:58 +01:00
|
|
|
memset(&linkedvldbs, 0, sizeof(vldb_list));
|
2005-12-27 23:30:22 +00:00
|
|
|
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
|
2011-02-03 20:22:02 +00:00
|
|
|
outweigh the disadvantages here.
|
2005-12-27 23:30:22 +00:00
|
|
|
|
|
|
|
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...
|
|
|
|
|
|
|
|
=head2 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;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
=head2 Output of an actual interface configuration
|
|
|
|
|
|
|
|
We'll demonstrate some of the actual output generated by B<rxgen> by
|
|
|
|
following an abbreviated actual interface configuration.
|
|
|
|
|
|
|
|
=head3 Configuration file
|
|
|
|
|
|
|
|
Contents of the interface configuration file (F<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;
|
|
|
|
|
2013-09-20 15:42:20 +01:00
|
|
|
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 C<rxgen vldbint.xg> will result in the creation of
|
|
|
|
four files: F<vldbint.h>, F<vldbint.xdr.c>, F<vldbint.cs.c> and
|
|
|
|
F<vldbint.ss.c>. A closer look at these files follows.
|
2005-12-27 23:30:22 +00:00
|
|
|
|
|
|
|
=head3 Header file (F<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 C<const>s
|
|
|
|
are converted to C<#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 B<rxgen> (i.e.,
|
|
|
|
using the B<-c>, B<-h>, B<-C>, or B<-S> flags). Also, one of the side
|
|
|
|
effects of the C<multi> option (in C<ReplaceEntry> proc) is the generation
|
|
|
|
of the C<multi_VL_ReplaceEntry> above.
|
|
|
|
|
|
|
|
=head3 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
|
|
|
|
B<rxgen> programmer deal with types such as bulkentries_1,
|
|
|
|
bulkentries_2...
|
|
|
|
|
|
|
|
=head3 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 C<multi> feature (three different modules
|
|
|
|
for C<ReplaceEntry> proc).
|
|
|
|
|
|
|
|
=head3 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).
|
|
|
|
|
|
|
|
=head1 NOTES
|
|
|
|
|
2013-09-20 15:42:20 +01:00
|
|
|
B<rxgen> is implemented from Sun's B<rpcgen> utility.
|
2005-12-27 23:30:22 +00:00
|
|
|
|
|
|
|
When the C<%#include <include file>> feature is used make sure that you
|
|
|
|
don't have any B<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.
|
|
|
|
|
|
|
|
=head1 SEE ALSO
|
|
|
|
|
|
|
|
I<Rxgen Syntax Summary>: Summary description of rxgen's grammar.
|
|
|
|
|
|
|
|
I<Rpcgen Programming Guide>: Sun's RPC protocol compiler. B<rxgen> was
|
|
|
|
implemented as an extension to that compiler.
|
|
|
|
|
|
|
|
I<External Data Representation: Sun Technical Notes>: Detailed examples in
|
|
|
|
using XDR.
|
|
|
|
|
|
|
|
I<RPCL Syntax Summary>: Summary of Sun's Remote Procedure Call Language.
|
|
|
|
|
|
|
|
I<Rx>: An extended Remote Procedure Call Protocol.
|
|
|
|
|
|
|
|
I<rgen>: An earlier version of a similar stub generator used for the R RPC
|
|
|
|
protocol.
|
|
|
|
|
|
|
|
=head1 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 B<rxgen> manual to POD by Russ
|
|
|
|
Allbery.
|