3 rxgen - Stub generator for the Rx remote procedure call package
7 B<rxgen> [B<-h> | B<-c> | B<-C> | B<-S> | B<-r>] [B<-dkpR>]
8 [B<-I> I<dir>] [B<-P> I<prefix>] [B<-o> I<outfile>] [I<infile>]
10 B<rxgen> B<-s> I<transport> [B<-o> I<outfile>] [I<infile>]
12 B<rxgen> B<-l> [B<-o> I<outfile>] [I<infile>]
14 B<rxgen> B<-m> [B<-o> I<outfile>] [I<infile>]
18 B<rxgen> is a tool that generates C code to implement the Rx RPC protocol;
19 it takes as input a description of an application interface similar to C
20 and produces a number of server and/or client stub routines to be linked
21 with RPC-based programs. These stubs allow programs to invoke remote
22 procedures through local procedure calls. B<rxgen> is an extension of
23 Sun's B<rpcgen> (version 3.9) and retains full B<rpcgen> functionality (at
24 least as of that version). Please refer to rpcgen(1) for more details on
25 the Sun's RPC specific flags, and to the RPC programming guide regarding
26 the RPC language along with useful examples.
30 B<rxgen> operates in several different modes. The generated output files
31 can be produced individually (using one of B<-h>, B<-c>, B<-C>, or B<-S>)
32 or collectively. All output files are created when the default is used
33 (i.e., no options), or the output is limited to the server stubs (B<-C>
34 and B<-S>) when the B<-r> flag is used. The following describes the types
35 of generated output files (for simplicity, I<filename> refers to the main
42 Generate C data definitions (a header file) from standard RPCL definitions
43 (default extension: I<filename>.h).
47 Compile the XDR routines required to serialize the protocol described by
48 RPCL. Generate XDR routines for all declarations (default extension:
53 Generate all the client-side stub routines (default extension:
54 I<filename>.cs.c). Calling a routine in this file will cause the
55 arguments to be packed up and sent via Rx (or R).
59 Generate all the server-side stub routines (default extension:
60 I<filename>.ss.c). Arguments are unpacked, and the corresponding server
65 Generate the two default extension files produced by the B<-C> and B<-S>
70 The following options can be used on any combination of B<rxgen> calls:
76 Generate code for the older \R protocol, as opposed to Rx, which is the
81 Must be specified when the generated code is intended to be used by the
82 kernel; special "includes" and other specifics are produced when the
83 target output is for the kernel.
87 Package combination flag: when multiple packages are included within a
88 single specification file, a single Execute Request routine will be used
89 for all of them as a result of this flag. The default is to generate
90 individual Execute Request stubs for each package.
94 Similar to the B<-I> flag in the C compiler (B<cc>). This flag is passed
95 to the pre-processor (B<cpp>) so that directory I<dir> is searched before
96 the standard lookup list for #include files. As expected, multiple B<-I>
97 flags can be used simultaneously.
101 The I<prefix> string following this switch is prepended to all generated
102 output files; useful when multiple runs want to produce different versions
103 of the same interface (say, kernel and non-kernel versions).
107 Debugging mode; only needed when B<rxgen> is to be debugged (say, via
110 =item B<-o> I<outfile>
112 Specify the name of the output file. If none is specified, the standard
113 output is used (B<-c>, B<-h>, B<-C>, and B<-S> modes only). Note that if
114 an output file is specified in a multi-output file option (such as the
115 default, or with option B<-r>), then the I<outfile> replaces the name
116 generated by default (which is based on the configuration's main file
121 The B<-s>, B<-l>, and B<-m> options are present only for B<rpcgen>
122 support. See rpcgen(1) for information on their use.
124 =head1 B<rxgen> SYNTAX SUMMARY
128 <Package description option> |
129 <Prefix description option> |
130 <StartingOpcode description option> |
131 <SplitPrefix description option> |
132 <Procedure description option> |
133 <RPCL language description option>
135 <Package description option>:
137 "package" <Package_ident>
139 <Prefix description option>:
141 "prefix" <Prefix_ident>
143 <StartingOpcode description option>:
145 "startingopcode" <constant>
147 <SplitPrefix description option>:
149 "splitprefix" <split options> ";"
153 "IN =" <Start_prefix_ident> "|"
154 "OUT =" <End_prefix_ident> "|"
157 <Procedure description option>:
159 ["proc"] [<Procedure_ident>] [<ServerStub_ident>]
160 <Argument list> ["split" | "multi"]
161 ["=" <Opcode_ident>] ";"
165 "(" <Argument definition> <Comma_joined argument> ")"
167 <Argument definition>:
169 <Direction option> <Standard RPCL type decl> <Arg_ident>
170 ["<" <Max_size> ">" | "[" <Max_size> "]"] | NULL
172 <Comma_joined argument>:
174 "," <Argument definition> | NULL
178 "IN" | "OUT" | "INOUT" | NULL
187 <Start_prefix_ident>:
196 <RPCL language description option>:
197 <Standard RPCL type decl>:
199 Sun's RPCL language syntax (see rpcgen(1))
201 =head1 B<rxgen> COMMANDS
203 =head2 Comments and Preprocessing
205 The input interface may contain preprocessor directives which are passed
206 through the C preprocessor (i.e. C<cpp>). Since the preprocessor runs on
207 all input files before they are actually interpreted by B<rxgen>, all
208 B<cpp> directives (#include, #ifdefs, #defines, etc.) are legal and
209 welcomed within an B<rxgen> input file. Of course, none of these
210 preprocessor directives will be included in any of the generated files.
211 To facilitate distinctions between the different types of output files,
212 B<rxgen> defines certain special B<cpp> symbols for use by the B<rxgen>
213 programmer. These are RPC_HDR (defined when compiling into header,
214 I<filename>.h, files), RPC_XDR (defined when compiling into xdr,
215 I<filename>.xdr.c, files), RPC_CLIENT (defined when compiling into client
216 stubs, I<filename>.cs.c, files), and RPC_SERVER (defined when compiling
217 into server stubs, I<filename>.ss.c, files).
219 In addition, B<rxgen> does a little preprocessing of its own. Any line
220 beginning with C<%> is passed directly into the output file, uninterpreted
221 by B<rxgen>. For a more heavy en masse dumping of uninterpreted code, it
222 would be adviced to include all such code in an C<#include> file and pass
223 it in preceded by C<%>. The input interface may also contain any C-style
224 comments which are, of course, ignored. Interpretation is token-based,
225 thus special line-orientation of separate statements is not necessary.
226 B<rxgen> also provides a quite rich and helpful set of error reports,
227 identifying them by exact line location and error type. Also, B<rxgen>
228 will automatically generate #include lines for standard include files,
229 such as F<rx/xdr.h> and F<rx/rx.h>, along with the generated header file
232 =head2 Prefixing stub procedures
234 The I<package> statement tells B<rxgen> the name of the interface package.
235 It is used for prefixing the naming of all generated stub routines and the
236 execute request procedure. For example:
240 causes the execute request procedure to be named AFS_ExecuteRequest
241 (Warning: in the older version an additional C<_> was appended after the
242 package name to the ExecuteRequest name; thus make sure you don't have an
243 ExecuteRequest interface routine) and a given stub routine, say Fetch, to
244 be actually named AFS_Fetch. Multiple package statements (current maximum
245 size is 10) per configuration are permitted and are useful when multiple
246 sets of interfaces are implemented (see the example at the end). Note
247 that in such cases, use of the B<-p> flag results in the generation of
248 just one ExecuteRequest procedure which recognizes the multiple interfaces
249 and whose name is prefixed by the first package statement. In the default
250 case, independent ExecuteRequest procedures will be created for each
251 packaged group of remote procedure calls.
253 The I<prefix> statement supplies a name to prepend to all calls to remote
254 procedure names in the ExecuteRequest stub routine. It is useful when the
255 server makes RPC calls to other servers (say, for debugging purposes).
260 causes the name C<S> to be prepended to the name of all routines called
261 from the server stubs. The server can then call the original name and get
264 =head2 B<rxgen> procedure declaration
266 The I<proc> statement is the most common (and meaningful) in the B<rxgen>
267 interface. Its syntax description is:
269 [proc] [<proc_name>] [<server_stub>] (<arg>, ..., <arg>)
270 [split | multi] [= <opcode>] ;
278 C<proc> is an optional prefix of the procedure statement. This is just a
279 stylistic item and not a required procedure delimiter.
283 <proc_name> is the name of the procedure. Note that even the name of the
284 procedure is optional. This only makes sense when the name of the given
285 procedure is identical to the name of the last I<package> statement (i.e.,
286 C<package RCallBack> and the declaration of the C<RCallBack> procedure).
290 <server_stub>, if present, causes the ExecuteRequest procedure to call
291 that stub instead of the automatically generated stub when a call with
292 that opcode is decoded.
296 <opcode> is a constant or symbol that is the opcode for that procedure.
297 One might use the preprocessor features (i.e., #define), the I<const>
298 RPC-language feature, or the old good constants as opcodes. Some further
299 evaluation/processing of opcodes is done. Particularly, checks for
300 duplicate and non-existent opcodes are performed, along with checks for
301 "holes" (i.e., gaps in consecutive opcodes) in the opcode sequences. For
302 example, we use the fact that when "holes" in opcodes exist, the
303 ExecuteRequest procedure uses the I<case> statement rather than the faster
304 (and smaller, codewise) indexed array method.
306 Also, B<rxgen> defines (i.e., appends to the header file) three valuable
307 macros for each package group: <package-name>LOWEST_OPCODE,
308 <package-name>HIGHEST_OPCODE, and <package-name>NUMBER_OPCODES. These may
309 be useful to the B<rxgen> programmer. Also, notice that the I<opcode>
310 statement is an optional feature, and can be omitted. In such cases,
311 automatic opcode numbers are generated sequentially, starting from 0.
313 One can change the initial opcode number by using the I<startingopcode>
314 (for lack of a better name) B<rxgen> command. Its syntax is:
316 startingopcode <constant>
318 where <constant> must be reasonable! Note that one can not mix
319 procedures, some with opcodes and some without, nor allow opcodes after
320 the specification of the I<startingopcode> statement. B<rxgen> will
321 complain in all such cases.
325 The I<argument> entry represents a given parameter of the procedure. Its
328 [IN | INOUT | OUT | <null>] <type_decl> <arg_name>
331 If the type is an indirect type (i.e., is followed by *), it is assumed
332 that the pointer should be followed one level and the data pointed to is
333 to be transmitted. This should normally be used for all structures/arrays
334 and out parameters. A noticeable exception is when explicit
335 array/structure maximum size is given; since no array-of-pointer
336 declarations are allowed one should use typedefs to achieve the similar
337 effect. The parameters could be input parameters (preceded by IN), output
338 parameters (preceded by OUT), or input/output parameters (preceded by
339 INOUT). If not specified, then the direction of the previous parameter in
340 the procedure is used. (Note: the first parameter must be preceded by the
341 directional primitive!)
345 C<split> is a hack to handle stub routines that do things such as file
346 transfers or any other operation that has to exchange information (e.g.,
347 length of a file) before the call returns its output parameters. Because
348 of the particular handshake that is involved when doing remote file
349 transfer, we currently break all such calls into two client-side stub
350 routines. The first (with the default prefix of C<Begin>) is used to pass
351 all IN and INOUT parameters to the server side. The second (with the
352 default prefix of C<End>) is used to get back the INOUT and OUT parameters
353 from the server. Between the two calls, the user is supposed to do the
354 appropriate calls for the file transfer. For example, the following
355 procedure declaration in package AFS_
357 Fetch (IN a, b,INOUT c, OUT d) split = FETCHOPCODE;
359 will roughly generate the two independent client stub routines:
361 BeginAFS_Fetch (IN a, b, c)
365 EndAFS_Fetch(OUT c, d)
367 The I<splitprefix> statement is used to change the default prefix names
368 used by the two client-side stub generated routines when dealing with file
369 transfer-related procedure calls. For example:
371 splitprefix IN=Before_ OUT=After_
373 will cause the naming of the two client stubs for a file transfer-related
374 routine, say Fetch(), to be Before_AFS_Fetch() and After_AFS_Fetch(),
379 The C<multi> option is nearly identical to the C<split> feature described
380 above. The only significant visible difference is that along with the two
381 client stubs, the standard client stub is also generated. Since the
382 intention is to handle the multi-Rx calls, we need the whole standard
383 procedure stub in the cases where no multi-Rx call of the procedure is
384 performed. A side effect of the C<multi> option is the generation of a
385 special macro (i.e., C<< multi_<Procedure-name> >> which passes back as
386 arguments the C<Begin> and C<End> stubs in the header output file. This
387 macro is used directly by the Rx code when a multi-Rx call of this
388 procedure is performed.
392 =head2 OBSOLETE B<rxgen> FEATURES
394 Although the following rxgen commands are still in effect, they will soon
395 be removed since there are better alternatives. DO NOT USE THEM!
397 The I<special> statement is a temporary hack used to handle certain
398 inefficiencies of standard xdr routines to handle some user-customized
399 declarations. In particular, this applies to a string pointer specified
400 as part of a declaration. For example,
402 special struct BBS SeqBody;
404 tells B<rxgen> that the entry C<SeqBody> in the user-defined BBS xdr
405 routine is a string (note that more than one string can be "special" per
406 structure -- multiple ones are separated by commas); it will thus allocate
407 and de-allocate space properly in the server-generated stubs that contain
408 this structure as an IN or INOUT parameter.
410 A better alternative to I<special> is the I<customized> statement, which
411 is simply the C<customized> token followed by the regular declaration of a
412 struct based on the RPCL rules. In this case, the declaration will be
413 included in the generated header file (B<-h> option) but no xdr routine
414 will be generated for this structure -- the user will supply this. All
415 pointer entries in this structure will be remembered so when the structure
416 is used as an IN or INOUT in the server stub, no core leaks will occur.
417 For example, consider
419 customized struct CBS {
424 The C<xdr_CBS> routine would be provided by the user where during the
425 DECODE xdr opcode, appropriate space for the C<SeqBody> string is
426 allocated. Similarly, that space is freed during the FREE xdr opcode.
428 Note: Old style "Array parameter specifications" are not supported any
433 In case there are some requirements not available by the current RPC
434 language, one can customize some XDR routines by leaving those data types
435 undefined. For every data type that is undefined, it will be assumed that
436 a routine exists with the name C<xdr_> prepended to it. A selected set of
437 B<rxgen> features is presented below, but for a more comprehensive one
438 (unions, complex examples, etc) please refer to the I<rpcgen Programming
439 Guide> and I<eXternal Data Representation: Sun Technical Notes>.
443 The RPC typedef statement is identical to the C typedef (i.e. C<< typedef
444 <declaration> >>). By default, most user declarations (i.e. structs,
445 unions, etc) are automatically typedef'ed by B<rxgen>. Since it makes
446 parsing simpler, its usage is recommended by B<rxgen> scripts.
450 The C C<char *> string convention is kind of ambiguous, since it is
451 usually intended to mean a null-terminated string of characters, but it
452 could also represent a pointer to a single character, a pointer to an
453 array of characters, etc. In the RPC language, a null-terminated string
454 is unambiguously called a "string". Examples,
457 string name<MAXNAMELEN>;
458 typedef string volname<MAXVOLNAME>;
460 Notice that the maximum size of string can be arbitrary (like C<bigname>
461 above) or, preferably, or specified in angle brackets (i.e. C<name> and
462 C<volname> above). In practice, one should always use only bounded
463 strings in interfaces. A sample calling proc using the declarations above
466 GetEntryByName (IN volname name,
467 OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;
471 GetEntryByName (IN string volname<MAXVOLNAME>,
472 OUT struct vldbentry *entry) = VL_GETENTRYBYNAME;
474 It is very important for the user to understand when the string parameters
475 should be allocated and/or freed by the his/her client and/or server
476 programs. A short analysis on string parameters handling follows (note
477 that a similar method is used for the handling of variable length arrays
478 as it will be shown later on):
484 In the client side: IN and INOUT string parameters are the programmer's
485 responsibility and should be allocated (static or via malloc) before
486 calling the rpc and freed (if malloc was used) after the rpc's return in
487 the user's client program; of course, for INOUT parameters, the returned
488 string can't be bigger than the malloced input string.
490 OUT string parameters are automatically malloced (based on the length of
491 the returned string and not the maxsize) by the B<rxgen> client stubs (in
492 I<filename>.cs.c) and must be freed by the client program; admittedly,
493 this could be somewhat confusing since the user needs to free something
494 that he/she didn't allocate.}
498 In the server side: IN and INOUT string parameters are automatically
499 malloced (based on the size of incoming strings) by the rxgen server stubs
500 (in I<filename>.ss.c) before they are passed to the user's server
501 procedure; that space is automatically freed just before the rxgen server
502 stub returns; therefore the user need not do anything special for IN and
503 INOUT string parameters.
505 OUT string parameters must be malloced by the user's server procedure
506 (i.e. null pointer is passed to it by the rxgen server stub) and it is
507 automatically freed at the end of the B<rxgen> server stub. Like in the
508 client side, the OUT parameters are somewhat unorthodox (i.e. the server
509 routine must malloc a string without ever freeing it itself; this is done
510 by the B<rxgen> server stub).
512 Note that for INOUT and OUT string parameters, in both the client and
513 server sides their arguments must be char of pointers (i.e. char **).
517 Pointer declarations in RPC are also exactly as they are in C
518 (i.e. C<struct single_vldbentry *vldblist;>). Of course, one can't send
519 pointers over the network, but one can use XDR pointers for sending
520 recursive data types such as lists and trees (an example of a linked list
521 will be demonstrated shortly).
525 Fixed arrays are just like standard C array declarations (i.e. C<struct
526 UpdateEntry entries[20]>) without any side effect problems in
527 B<rxgen>. Since variable-length arrays have no explicit syntax in C, the
528 angle-brackets are used for it and the array declarations are actually
529 compiled into "struct"s. For example, declarations such as:
531 const MAXBULKSIZE = 10000;
532 const MAXENTRIES = 100;
533 opaque bulk<MAXBULKSIZE>; /* At most 10000 items */
534 int hosts<>; /* any number of items */
535 typedef vldbentry blkentries<100>; /* Preferable array decl */
537 are compiled into the following structs:
540 u_int bulk_len; /* no of items */
541 char *bulk_val; /* pointer to array */
544 for the C<bulk> array, and similarly for the C<< blkentries<100> >> array,
547 u_int blkentries_len; /* no of items in array */
548 vldbentry *blkentries_val; /* pointer to array */
551 Therefore the user should be aware of the "magically" generated structure
552 entries such as the number of items in the array (<array_name>_len) and
553 the pointer to the array (<array_name>_val) since some of the entries will
554 have to be filled in from the client/server programs. A sample proc would
557 typedef vldbentry blkentries<MAXENTRIES>;
558 proc GetBlk (OUT blkentries *vlentries) = VL_GETBLK;
562 GetBlk(OUT vldbentry vlentries<MAXENTRIES>) = VL_GETBLK;
564 Note that although the latest method is preferable since one does not have
565 to first use the typedef statement (and admittedly, programmers prefer
566 avoiding typedefs), one should realize that B<rxgen> does the structure
567 expansion and the xdr creation implicitly; therefore the user should be
568 aware of the C<vldbentries_val> and C<vldbentries_len> fields as before
569 (see following examples).
571 =head3 Array example I (least desirable)
573 Procedure declaration in the interface configuration:
575 proc ListAttributes (IN vldblistbyattributes *attributes,
576 INOUT blkentries *vldbentries) = VL_LISTATTRIBUTES;
580 blkentries entries, *pnt;
581 entries.blkentries_len = 10; /* max # returned entries */
582 entries.blkentries_val = (vldbentry *)malloc(LEN);
585 code = VL_ListAttributes(&attributes, &entries);
587 pnt = entries.blkentries_val;
588 for (i=0; i < entries.blkentries_len; i++, pnt++)
589 display_vldbentry(pnt);
590 /* Make sure you free the allocated space */
591 free((char *)entries.blkentries_val);
596 VL_ListAttributes(attributes, entries)
598 vldbentry *singleentry = entries->blkentries_val;
599 entries->blkentries_len = 0;
601 while (copy_to_vldbentry(&vlentry, singleentry))
602 singleentry++, vldbentries->entries_len++;
605 Although this method for variable-size arrays works fine, there are some
606 major drawbacks. The array parameter (i.e. vldbentries above) must be
607 declared as INOUT since we need to pass the max length of the expected
608 returned array; more importantly, a big (depending on the value of
609 C<_len>) chunk of junk code is going to be transferred to the server as
610 result of the IN(out) side-effect of the array. It's an easy and
611 convenient method if the returned array size can be predicted from the
612 start and when the size is quite high. This method is included as an
613 example of erroneous use (and abuse) of B<rxgen> and should not be used.
615 =head3 Array example II (Desirable method)
617 Procedure declaration in the interface configuration (using Example I
620 proc ListAttributes (IN vldblistbyattributes *attributes,
621 OUT blkentries *vldbentries) = VL_LISTATTRIBUTES;
625 blkentries entries, *pnt;
627 code = VL_ListAttributes(&attributes, &entries);
629 pnt = entries.blkentries_val;
630 for (i=0; i < entries.blkentries_len; i++, pnt++)
631 display_vldbentry(pnt);
632 /* Make sure you free the allocated space (by rxgen) */
633 free((char *)entries.blkentries_val);
638 VL_ListAttributes(attributes, entries)
640 vldbentry *singleentry;
641 entries->blkentries_len = 0;
642 singleentry = entries->blkentries_val
643 = (vldbentry *)malloc(MAXENTRIES * sizeof(vldbentry));
645 while (copy_to_vldbentry(&vlentry, singleentry))
646 singleentry++, vldbentries->entries_len++;
649 This is the best (and simplest) way of using variable-size arrays as an
650 output parameter. It is the responsibility of the server-side stub to
651 malloc() the adequate space which is automatically freed by the B<rxgen>
652 stub; the client side should free the space allocated by the
653 B<rxgen>-calling stub.
655 =head3 Array example III (Linked Lists)
657 Considering the following 3 declarations (could have applied some
658 optimizations) in the configuration file:
660 typedef struct single_vldbentry *vldblist;
661 struct single_vldbentry {
670 and the rxgen procedure declaration:
672 LinkedList (IN vldblistbyattributes *attributes,
673 OUT vldb_list *linkedentries) = VL_LINKEDLIST;
677 vldb_list linkedvldbs;
678 vldblist vllist, vllist1;
680 bzero(&linkedvldbs, sizeof(vldb_list));
681 code = VL_LinkedList(&attributes, &nentries, &linkedvldbs);
683 printf("We got %d vldb entries\n", nentries);
684 for (vllist = linkedvldbs.node; vllist; vllist = vllist1) {
685 vllist1 = vllist->next_vldb;
686 display_entry(&vllist->vlentry);
687 free((char *)vllist);
693 VL_LinkedList(rxcall, attributes, nentries, linkedvldbs);
695 vldblist vllist, *vllistptr = &linkedvldbs->node;
698 = (single_vldbentry *)malloc (sizeof (single_vldbentry));
699 copy_to_vldbentry(&tentry, &vllist->vlentry);
701 vllistptr = &vllist->next_vldb;
706 Using a linked list offers many advantages: Nothing is passed to the
707 server (the parameter is OUT), no additional overhead is involved, and the
708 caller doesn't have to explicitly prepare for an arbitrary return size. A
709 drawback is that the caller has the responsibility of malloc() (on the
710 server) and free (on the client) of each entry (to avoid unwanted
711 core-leaks). Another drawback is that since it's a recursive call, the C
712 stack will grow linearly with respect to the number of nodes in the list
713 (so it's wise to increase the Rx LWP stack if huge amounts of data are
714 expected back -- default stack size is 4K). The advantages should
715 outweight the disadvantages here.
717 It's important to pay attention to the comments of the three array
718 examples above particularly when they're references to when the user
719 should allocate/free space for the variable length arrays. The mechanism
720 is very similar to the handling of strings thus you might need to review
721 the strings section above; note that the linked lists are handled somewhat
724 =head2 Miscellaneous examples
726 Below is an abbreviated version of a random interface file which shows
727 some of the common cases.
729 /* Declaration of all structures used by the R.xg script interface */
732 unsigned long Volume;
734 unsigned long Unique;
737 typedef long ViceDataType;
739 /* Note that TEST would be equivalent to "HEADER" only during the
740 processing of the header, *.h, file */
743 #define TEST "HEADER"
748 /* This is the standard *.xg specification file */
751 splitprefix IN=BEFORE_ OUT=AFTER_;
754 proc Remove(IN struct AFSFid *Did, IN string volname<64>,
755 OUT struct AFSStatus *Status) = AFS_REMOVE;
757 DisconnectFS AUX_disconnectFS() = AFS_DISCONNECTFS;
759 proc GetVolumeInfo(IN string Vid,
760 OUT struct VolumeInfo *Info) = AFS_GETVOLUMEINFO;
762 /* You could have more than an interface per configuration */
766 /* Using the "multi" feature; thus VOTE_Beacon can be called as an
767 multi-Rx call or as a regular call */
769 Beacon (IN long state, long voteStart,
770 net_version *version, net_tid *tid)
775 /* Using the "split" feature */
777 SendFile (IN long file, long offset,
778 long length, net_version *version)
779 split = DISK_SENDFILE;
781 =head2 Output of an actual interface configuration
783 We'll demonstrate some of the actual output generated by B<rxgen> by
784 following an abbreviated actual interface configuration.
786 =head3 Configuration file
788 Contents of the interface configuration file (F<vldbint.xg>):
791 #include "vl_opcodes.h" /* The opcodes are included here */
792 %#include "vl_opcodes.h" /* directly to other places */
794 /* Current limitations on parameters that affect other packages
797 const MAXNAMELEN = 65;
798 const MAXNSERVERS = 8;
801 /* External (visible) representation of an individual vldb entry */
804 char name[MAXNAMELEN];
807 long serverNumber[MAXNSERVERS];
808 long serverPartition[MAXNSERVERS];
809 long serverFlags[MAXNSERVERS];
810 u_long volumeId[MAXTYPES];
814 typedef struct single_vldbentry *vldblist;
815 struct single_vldbentry {
824 /* vldb interface calls */
826 CreateEntry (IN long Volid,
827 vldbentry *newentry) = VLCREATEENTRY;
829 GetEntryByName (IN string volumename<MAXNAMELEN>,
830 OUT vldbentry *entry) = VLGETENTRYBYNAME;
832 GetNewVolumeId (IN long bumpcount,
833 OUT long *newvolumid) = VLGETNEWVOLUMEID;
835 ReplaceEntry (IN long Volid,
838 long ReleaseType) multi = VLREPLACEENTRY;
840 ListAttributes (IN VldbListByAttributes *attributes,
842 OUT vldbentry bulkentries<MAXVLDBLEN>)
845 LinkedList (IN VldbListByAttributes *attributes,
847 OUT vldb_list *linkedentries) = VLLINKEDLIST;
849 We'll concentrate only on the Rx generated code since the R generated code
850 (B<-R> option) will soon be obsolete. For a detailed description on the
851 Rx-related calls inside the generated stubs (i.e., rx_NewCall(),
852 rx_EndCall()), along with details on what happens inside certain calls
853 (like xdrrx_create()) please refer to the Rx documentation. Typing C<rxgen
854 vldbint.xg> will result in the creation of four files: F<vldbint.h>,
855 F<vldbint.xdr.c>, F<vldbint.cs.c> and F<vldbint.ss.c>. A closer look at
858 =head3 Header file (F<vldbint.h>)
860 /* Machine generated file -- Do NOT edit */
862 #include "vl_opcodes.h" /* directly to other places */
863 #define MAXNAMELEN 65
864 #define MAXNSERVERS 8
868 char name[MAXNAMELEN];
871 long serverNumber[MAXNSERVERS];
872 long serverPartition[MAXNSERVERS];
873 long serverFlags[MAXNSERVERS];
874 u_long volumeId[MAXTYPES];
877 typedef struct vldbentry vldbentry;
878 bool_t xdr_vldbentry();
880 typedef struct single_vldbentry *vldblist;
881 bool_t xdr_vldblist();
883 struct single_vldbentry {
887 typedef struct single_vldbentry single_vldbentry;
888 bool_t xdr_single_vldbentry();
893 typedef struct vldb_list vldb_list;
894 bool_t xdr_vldb_list();
896 #include <rx/rx_multi.h>
897 #define multi_VL_ReplaceEntry(Volid, voltype, newentry, ReleaseType) \
898 multi_Body(StartVL_ReplaceEntry(multi_call, Volid, voltype,
899 newentry, ReleaseType), EndVL_ReplaceEntry(multi_call))
901 typedef struct bulkentries {
902 u_int bulkentries_len;
903 vldbentry *bulkentries_val;
905 bool_t xdr_bulkentries();
907 /* Opcode-related useful stats for package: VL_ */
908 #define VL_LOWEST_OPCODE 501
909 #define VL_HIGHEST_OPCODE 506
910 #define VL_NUMBER_OPCODES 6
912 Notice that all structures are automatically typedef'ed and all C<const>s
913 are converted to C<#define>s. Some data structures, such as bulkentries,
914 are taken from procedure params (from ListAttributes proc). Thus, this
915 should be kept in mind when creating stubs piecemeal with B<rxgen> (i.e.,
916 using the B<-c>, B<-h>, B<-C>, or B<-S> flags). Also, one of the side
917 effects of the C<multi> option (in C<ReplaceEntry> proc) is the generation
918 of the C<multi_VL_ReplaceEntry> above.
920 =head3 XDR routines for structures (vldbint.xdr.c)
922 /* Machine generated file -- Do NOT edit */
927 #include "vl_opcodes.h" /* directly to other places */
930 xdr_vldbentry(xdrs, objp)
934 if (!xdr_vector(xdrs, (char *)objp->name, MAXNAMELEN,
935 sizeof(char), xdr_char))
937 if (!xdr_long(xdrs, &objp->volumeType))
939 if (!xdr_long(xdrs, &objp->nServers))
941 if (!xdr_vector(xdrs, (char *)objp->serverNumber, MAXNSERVERS,
942 sizeof(long), xdr_long))
944 if (!xdr_vector(xdrs, (char *)objp->serverPartition,
945 MAXNSERVERS, sizeof(long), xdr_long))
947 if (!xdr_vector(xdrs, (char *)objp->serverFlags, MAXNSERVERS,
948 sizeof(long), xdr_long))
950 if (!xdr_vector(xdrs, (char *)objp->volumeId, MAXTYPES,
951 sizeof(u_long), xdr_u_long))
953 if (!xdr_long(xdrs, &objp->flags))
959 xdr_vldblist(xdrs, objp)
963 if (!xdr_pointer(xdrs, (char **)objp,
964 sizeof(struct single_vldbentry),
965 xdr_single_vldbentry))
971 xdr_single_vldbentry(xdrs, objp)
973 single_vldbentry *objp;
975 if (!xdr_vldbentry(xdrs, &objp->VldbEntry))
977 if (!xdr_vldblist(xdrs, &objp->next_vldb))
983 xdr_vldb_list(xdrs, objp)
987 if (!xdr_vldblist(xdrs, &objp->node))
993 xdr_bulkentries(xdrs, objp)
997 if (!xdr_array(xdrs, (char **)&objp->bulkentries_val,
998 (u_int *)&objp->bulkentries_len, MAXVLDBLEN,
999 sizeof(vldbentry), xdr_vldbentry))
1004 Note that the xdr_bulkentries() is automatically generated as a side
1005 effect of a procedure parameter declaration. Thus, if identical multiple
1006 type parameter declarations are used, then multiply-defined xdr_* stubs
1007 will be created! We felt this was a better alternative to having the
1008 B<rxgen> programmer deal with types such as bulkentries_1,
1011 =head3 Client-Side stub routines (vldbint.cs.c)
1013 /* Machine generated file -- Do NOT edit */
1017 #include <afs/rxgen_consts.h>
1018 #include "vldbint.h"
1020 #include "vl_opcodes.h" /* directly to other places */
1022 int VL_CreateEntry(z_conn, Volid, newentry)
1023 register struct rx_connection *z_conn;
1025 vldbentry * newentry;
1027 struct rx_call *z_call = rx_NewCall(z_conn);
1028 static int z_op = 501;
1032 xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1034 /* Marshal the arguments */
1035 if ((!xdr_int(&z_xdrs, &z_op))
1036 || (!xdr_long(&z_xdrs, &Volid))
1037 || (!xdr_vldbentry(&z_xdrs, newentry))) {
1038 z_result = RXGEN_CC_MARSHAL;
1042 z_result = RXGEN_SUCCESS;
1044 return rx_EndCall(z_call, z_result);
1047 int VL_GetEntryByName(z_conn, volumename, entry)
1048 register struct rx_connection *z_conn;
1052 struct rx_call *z_call = rx_NewCall(z_conn);
1053 static int z_op = 504;
1057 xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1059 /* Marshal the arguments */
1060 if ((!xdr_int(&z_xdrs, &z_op))
1061 || (!xdr_string(&z_xdrs, &volumename, 65))) {
1062 z_result = RXGEN_CC_MARSHAL;
1066 /* Un-marshal the reply arguments */
1067 z_xdrs.x_op = XDR_DECODE;
1068 if ((!xdr_vldbentry(&z_xdrs, entry))) {
1069 z_result = RXGEN_CC_UNMARSHAL;
1073 z_result = RXGEN_SUCCESS;
1075 return rx_EndCall(z_call, z_result);
1078 int VL_GetNewVolumeId(z_conn, bumpcount, newvolumid)
1079 register struct rx_connection *z_conn;
1083 struct rx_call *z_call = rx_NewCall(z_conn);
1084 static int z_op = 505;
1088 xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1090 /* Marshal the arguments */
1091 if ((!xdr_int(&z_xdrs, &z_op))
1092 || (!xdr_long(&z_xdrs, &bumpcount))) {
1093 z_result = RXGEN_CC_MARSHAL;
1097 /* Un-marshal the reply arguments */
1098 z_xdrs.x_op = XDR_DECODE;
1099 if ((!xdr_long(&z_xdrs, newvolumid))) {
1100 z_result = RXGEN_CC_UNMARSHAL;
1104 z_result = RXGEN_SUCCESS;
1106 return rx_EndCall(z_call, z_result);
1109 int VL_ReplaceEntry(z_conn, Volid, voltype, newentry, ReleaseType)
1110 register struct rx_connection *z_conn;
1111 long Volid, voltype, ReleaseType;
1112 vldbentry * newentry;
1114 struct rx_call *z_call = rx_NewCall(z_conn);
1115 static int z_op = 506;
1119 xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1121 /* Marshal the arguments */
1122 if ((!xdr_int(&z_xdrs, &z_op))
1123 || (!xdr_long(&z_xdrs, &Volid))
1124 || (!xdr_long(&z_xdrs, &voltype))
1125 || (!xdr_vldbentry(&z_xdrs, newentry))
1126 || (!xdr_long(&z_xdrs, &ReleaseType))) {
1127 z_result = RXGEN_CC_MARSHAL;
1131 z_result = RXGEN_SUCCESS;
1133 return rx_EndCall(z_call, z_result);
1136 int StartVL_ReplaceEntry(z_call, Volid, voltype, newentry, ReleaseType)
1137 register struct rx_call *z_call;
1138 long Volid, voltype, ReleaseType;
1139 vldbentry * newentry;
1141 static int z_op = 506;
1145 xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1147 /* Marshal the arguments */
1148 if ((!xdr_int(&z_xdrs, &z_op))
1149 || (!xdr_long(&z_xdrs, &Volid))
1150 || (!xdr_long(&z_xdrs, &voltype))
1151 || (!xdr_vldbentry(&z_xdrs, newentry))
1152 || (!xdr_long(&z_xdrs, &ReleaseType))) {
1153 z_result = RXGEN_CC_MARSHAL;
1157 z_result = RXGEN_SUCCESS;
1162 int EndVL_ReplaceEntry(z_call)
1163 register struct rx_call *z_call;
1168 z_result = RXGEN_SUCCESS;
1173 int VL_ListAttributes(z_conn, attributes, nentries, bulkentries_1)
1174 register struct rx_connection *z_conn;
1175 VldbListByAttributes * attributes;
1177 bulkentries * bulkentries_1;
1179 struct rx_call *z_call = rx_NewCall(z_conn);
1180 static int z_op = 511;
1184 xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1186 /* Marshal the arguments */
1187 if ((!xdr_int(&z_xdrs, &z_op))
1188 || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
1189 z_result = RXGEN_CC_MARSHAL;
1193 /* Un-marshal the reply arguments */
1194 z_xdrs.x_op = XDR_DECODE;
1195 if ((!xdr_long(&z_xdrs, nentries))
1196 || (!xdr_bulkentries(&z_xdrs, bulkentries_1))) {
1197 z_result = RXGEN_CC_UNMARSHAL;
1201 z_result = RXGEN_SUCCESS;
1203 return rx_EndCall(z_call, z_result);
1206 int VL_LinkedList(z_conn, attributes, nentries, linkedentries)
1207 register struct rx_connection *z_conn;
1208 VldbListByAttributes * attributes;
1210 vldb_list * linkedentries;
1212 struct rx_call *z_call = rx_NewCall(z_conn);
1213 static int z_op = 512;
1217 xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
1219 /* Marshal the arguments */
1220 if ((!xdr_int(&z_xdrs, &z_op))
1221 || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) {
1222 z_result = RXGEN_CC_MARSHAL;
1226 /* Un-marshal the reply arguments */
1227 z_xdrs.x_op = XDR_DECODE;
1228 if ((!xdr_long(&z_xdrs, nentries))
1229 || (!xdr_vldb_list(&z_xdrs, linkedentries))) {
1230 z_result = RXGEN_CC_UNMARSHAL;
1234 z_result = RXGEN_SUCCESS;
1236 return rx_EndCall(z_call, z_result);
1239 Notice the side effect of the C<multi> feature (three different modules
1240 for C<ReplaceEntry> proc).
1242 =head3 Server-Side stub routines (vldbint.ss.c)
1244 /* Machine generated file -- Do NOT edit */
1248 #include <afs/rxgen_consts.h>
1249 #include "vldbint.h"
1251 #include "vl_opcodes.h" /* directly to other places */
1253 long _VL_CreateEntry(z_call, z_xdrs)
1254 struct rx_call *z_call;
1261 if ((!xdr_long(z_xdrs, &Volid))
1262 || (!xdr_vldbentry(z_xdrs, &newentry))) {
1263 z_result = RXGEN_SS_UNMARSHAL;
1267 z_result = VL_CreateEntry(z_call, Volid, &newentry);
1272 long _VL_GetEntryByName(z_call, z_xdrs)
1273 struct rx_call *z_call;
1277 char *volumename = (char *)0;
1280 if ((!xdr_string(z_xdrs, &volumename, 65))) {
1281 z_result = RXGEN_SS_UNMARSHAL;
1285 z_result = VL_GetEntryByName(z_call, &volumename, &entry);
1286 z_xdrs->x_op = XDR_ENCODE;
1287 if ((!xdr_vldbentry(z_xdrs, &entry)))
1288 z_result = RXGEN_SS_MARSHAL;
1290 z_xdrs->x_op = XDR_FREE;
1291 if (!xdr_string(z_xdrs, &volumename, 65)) goto fail1;
1294 return RXGEN_SS_XDRFREE;
1297 long _VL_GetNewVolumeId(z_call, z_xdrs)
1298 struct rx_call *z_call;
1305 if ((!xdr_long(z_xdrs, &bumpcount))) {
1306 z_result = RXGEN_SS_UNMARSHAL;
1310 z_result = VL_GetNewVolumeId(z_call, bumpcount, &newvolumid);
1311 z_xdrs->x_op = XDR_ENCODE;
1312 if ((!xdr_long(z_xdrs, &newvolumid)))
1313 z_result = RXGEN_SS_MARSHAL;
1318 long _VL_ReplaceEntry(z_call, z_xdrs)
1319 struct rx_call *z_call;
1323 long Volid, voltype, ReleaseType;
1326 if ((!xdr_long(z_xdrs, &Volid))
1327 || (!xdr_long(z_xdrs, &voltype))
1328 || (!xdr_vldbentry(z_xdrs, &newentry))
1329 || (!xdr_long(z_xdrs, &ReleaseType))) {
1330 z_result = RXGEN_SS_UNMARSHAL;
1334 z_result = VL_ReplaceEntry(z_call, Volid, voltype, &newentry,
1340 long _VL_ListAttributes(z_call, z_xdrs)
1341 struct rx_call *z_call;
1345 VldbListByAttributes attributes;
1347 bulkentries bulkentries_1;
1349 if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
1350 z_result = RXGEN_SS_UNMARSHAL;
1354 z_result = VL_ListAttributes(z_call, &attributes, &nentries,
1356 z_xdrs->x_op = XDR_ENCODE;
1357 if ((!xdr_long(z_xdrs, &nentries))
1358 || (!xdr_bulkentries(z_xdrs, &bulkentries_1)))
1359 z_result = RXGEN_SS_MARSHAL;
1361 z_xdrs->x_op = XDR_FREE;
1362 if (!xdr_bulkentries(z_xdrs, &bulkentries_1)) goto fail1;
1365 return RXGEN_SS_XDRFREE;
1368 long _VL_LinkedList(z_call, z_xdrs)
1369 struct rx_call *z_call;
1373 VldbListByAttributes attributes;
1375 vldb_list linkedentries;
1377 if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) {
1378 z_result = RXGEN_SS_UNMARSHAL;
1382 z_result = VL_LinkedList(z_call, &attributes, &nentries,
1384 z_xdrs->x_op = XDR_ENCODE;
1385 if ((!xdr_long(z_xdrs, &nentries))
1386 || (!xdr_vldb_list(z_xdrs, &linkedentries)))
1387 z_result = RXGEN_SS_MARSHAL;
1392 long _VL_CreateEntry();
1393 long _VL_GetEntryByName();
1394 long _VL_GetNewVolumeId();
1395 long _VL_ReplaceEntry();
1396 long _VL_ListAttributes();
1397 long _VL_LinkedList();
1399 static long (*StubProcsArray0[])() = {_VL_CreateEntry,
1400 _VL_GetEntryByName, _VL_GetNewVolumeId, _VL_ReplaceEntry,
1401 _VL_ListAttributes, _VL_LinkedList};
1403 VL_ExecuteRequest(z_call)
1404 register struct rx_call *z_call;
1410 xdrrx_create(&z_xdrs, z_call, XDR_DECODE);
1411 if (!xdr_int(&z_xdrs, &op))
1412 z_result = RXGEN_DECODE;
1413 else if (op < VL_LOWEST_OPCODE || op > VL_HIGHEST_OPCODE)
1414 z_result = RXGEN_OPCODE;
1416 z_result = (*StubProcsArray0[op - VL_LOWEST_OPCODE])
1421 If there were gaps in the procedures' opcode sequence the code for
1422 VL_ExecuteRequest() routine would be have been drastically different (it
1423 would have been a case statement for each procedure).
1427 B<rxgen> is implemented from Sun's B<rpcgen> utility. All of the standard
1428 B<rpcgen>'s functionality is fully maintained. Note that some active
1429 B<rpcgen> options that don't apply to B<rxgen>'s purpose aren't referenced
1430 here (i.e., B<-s>, B<-l>, B<-m> options) and the interested reader should
1431 refer to rpcgen(1) for details.
1433 When the C<%#include <include file>> feature is used make sure that you
1434 don't have any B<rxgen> language features (i.e. %#defines) since you'll
1435 get syntax errors during compilations..
1437 Since this is an ongoing project many of the above may change/disappear
1438 without a major warning.
1442 I<Rxgen Syntax Summary>: Summary description of rxgen's grammar.
1444 I<Rpcgen Programming Guide>: Sun's RPC protocol compiler. B<rxgen> was
1445 implemented as an extension to that compiler.
1447 I<External Data Representation: Sun Technical Notes>: Detailed examples in
1450 I<RPCL Syntax Summary>: Summary of Sun's Remote Procedure Call Language.
1452 I<Rx>: An extended Remote Procedure Call Protocol.
1454 I<rgen>: An earlier version of a similar stub generator used for the R RPC
1459 IBM Corporation 2000. <http://www.ibm.com/> All Rights Reserved.
1461 This documentation is covered by the IBM Public License Version 1.0. It
1462 was converted from the original TeX B<rxgen> manual to POD by Russ