Commit | Line | Data |
---|---|---|
805e021f CE |
1 | =head1 NAME |
2 | ||
3 | rxgen - Stub generator for the Rx remote procedure call package | |
4 | ||
5 | =head1 SYNOPSIS | |
6 | ||
7 | =for html | |
8 | <div class="synopsis"> | |
9 | ||
10 | B<rxgen> [B<-h> | B<-c> | B<-C> | B<-S> | B<-r>] [B<-dkp>] | |
11 | [B<-I> I<dir>] [B<-P> I<prefix>] [B<-o> I<outfile>] [I<infile>] | |
12 | ||
13 | =for html | |
14 | </div> | |
15 | ||
16 | =head1 DESCRIPTION | |
17 | ||
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 based on Sun's | |
23 | B<rpcgen> (version 3.9) but does not maintain compatibility with rpcgen | |
24 | RPC descriptions. | |
25 | ||
26 | =head1 OPTIONS | |
27 | ||
28 | B<rxgen> operates in several different modes. The generated output files | |
29 | can be produced individually (using one of B<-h>, B<-c>, B<-C>, or B<-S>) | |
30 | or collectively. All output files are created when the default is used | |
31 | (i.e., no options), or the output is limited to the server stubs (B<-C> | |
32 | and B<-S>) when the B<-r> flag is used. The following describes the types | |
33 | of generated output files (for simplicity, I<filename> refers to the main | |
34 | output filename): | |
35 | ||
36 | =over 4 | |
37 | ||
38 | =item B<-h> | |
39 | ||
40 | Generate C data definitions (a header file) from standard RPCL definitions | |
41 | (default extension: I<filename>.h). | |
42 | ||
43 | =item B<-c> | |
44 | ||
45 | Compile the XDR routines required to serialize the protocol described by | |
46 | RPCL. Generate XDR routines for all declarations (default extension: | |
47 | I<filename>.xdr.c). | |
48 | ||
49 | =item B<-C> | |
50 | ||
51 | Generate all the client-side stub routines (default extension: | |
52 | I<filename>.cs.c). Calling a routine in this file will cause the | |
53 | arguments to be packed up and sent via Rx (or R). | |
54 | ||
55 | =item B<-S> | |
56 | ||
57 | Generate all the server-side stub routines (default extension: | |
58 | I<filename>.ss.c). Arguments are unpacked, and the corresponding server | |
59 | routine is called. | |
60 | ||
61 | =item B<-r> | |
62 | ||
63 | Generate the two default extension files produced by the B<-C> and B<-S> | |
64 | options. | |
65 | ||
66 | =back | |
67 | ||
68 | The following options can be used on any combination of B<rxgen> calls: | |
69 | ||
70 | =over 4 | |
71 | ||
72 | =item B<-k> | |
73 | ||
74 | Must be specified when the generated code is intended to be used by the | |
75 | kernel; special "includes" and other specifics are produced when the | |
76 | target output is for the kernel. | |
77 | ||
78 | =item B<-p> | |
79 | ||
80 | Package combination flag: when multiple packages are included within a | |
81 | single specification file, a single Execute Request routine will be used | |
82 | for all of them as a result of this flag. The default is to generate | |
83 | individual Execute Request stubs for each package. | |
84 | ||
85 | =item B<-I> I<dir> | |
86 | ||
87 | Similar to the B<-I> flag in the C compiler (B<cc>). This flag is passed | |
88 | to the pre-processor (B<cpp>) so that directory I<dir> is searched before | |
89 | the standard lookup list for #include files. As expected, multiple B<-I> | |
90 | flags can be used simultaneously. | |
91 | ||
92 | =item B<-P> I<prefix> | |
93 | ||
94 | The I<prefix> string following this switch is prepended to all generated | |
95 | output files; useful when multiple runs want to produce different versions | |
96 | of the same interface (say, kernel and non-kernel versions). | |
97 | ||
98 | =item B<-d> | |
99 | ||
100 | Debugging mode; only needed when B<rxgen> is to be debugged (say, via | |
101 | B<dbx>). | |
102 | ||
103 | =item B<-o> I<outfile> | |
104 | ||
105 | Specify the name of the output file. If none is specified, the standard | |
106 | output is used (B<-c>, B<-h>, B<-C>, and B<-S> modes only). Note that if | |
107 | an output file is specified in a multi-output file option (such as the | |
108 | default, or with option B<-r>), then the I<outfile> replaces the name | |
109 | generated by default (which is based on the configuration's main file | |
110 | name). | |
111 | ||
112 | =back | |
113 | ||
114 | =head1 B<rxgen> SYNTAX SUMMARY | |
115 | ||
116 | Specification file: | |
117 | ||
118 | <Package description option> | | |
119 | <Prefix description option> | | |
120 | <StartingOpcode description option> | | |
121 | <SplitPrefix description option> | | |
122 | <Procedure description option> | | |
123 | <RPCL language description option> | |
124 | ||
125 | <Package description option>: | |
126 | ||
127 | "package" <Package_ident> | |
128 | ||
129 | <Prefix description option>: | |
130 | ||
131 | "prefix" <Prefix_ident> | |
132 | ||
133 | <StartingOpcode description option>: | |
134 | ||
135 | "startingopcode" <constant> | |
136 | ||
137 | <SplitPrefix description option>: | |
138 | ||
139 | "splitprefix" <split options> ";" | |
140 | ||
141 | <Split options>: | |
142 | ||
143 | "IN =" <Start_prefix_ident> "|" | |
144 | "OUT =" <End_prefix_ident> "|" | |
145 | <Split options> | |
146 | ||
147 | <Procedure description option>: | |
148 | ||
149 | ["proc"] [<Procedure_ident>] [<ServerStub_ident>] | |
150 | <Argument list> ["split" | "multi"] | |
151 | ["=" <Opcode_ident>] ";" | |
152 | ||
153 | <Argument list>: | |
154 | ||
155 | "(" <Argument definition> <Comma_joined argument> ")" | |
156 | ||
157 | <Argument definition>: | |
158 | ||
159 | <Direction option> <Standard RPCL type decl> <Arg_ident> | |
160 | ["<" <Max_size> ">" | "[" <Max_size> "]"] | NULL | |
161 | ||
162 | <Comma_joined argument>: | |
163 | ||
164 | "," <Argument definition> | NULL | |
165 | ||
166 | <Direction option>: | |
167 | ||
168 | "IN" | "OUT" | "INOUT" | NULL | |
169 | ||
170 | <Max_size>: | |
171 | ||
172 | <constant> | NULL | |
173 | ||
174 | <Package_ident>: | |
175 | <Prefix_ident>: | |
176 | <String_ident>: | |
177 | <Start_prefix_ident>: | |
178 | <End_prefix_ident>: | |
179 | <Procedure_ident>: | |
180 | <ServerStub_ident>: | |
181 | <Arg_ident>: | |
182 | <Opcode_ident>: | |
183 | ||
184 | <identifier> | |
185 | ||
186 | <RPCL language description option>: | |
187 | <Standard RPCL type decl>: | |
188 | ||
189 | Sun's RPCL language syntax (see rpcgen(1)) | |
190 | ||
191 | =head1 B<rxgen> COMMANDS | |
192 | ||
193 | =head2 Comments and Preprocessing | |
194 | ||
195 | The input interface may contain preprocessor directives which are passed | |
196 | through the C preprocessor (i.e. C<cpp>). Since the preprocessor runs on | |
197 | all input files before they are actually interpreted by B<rxgen>, all | |
198 | B<cpp> directives (#include, #ifdefs, #defines, etc.) are legal and | |
199 | welcomed within an B<rxgen> input file. Of course, none of these | |
200 | preprocessor directives will be included in any of the generated files. | |
201 | To facilitate distinctions between the different types of output files, | |
202 | B<rxgen> defines certain special B<cpp> symbols for use by the B<rxgen> | |
203 | programmer. These are RPC_HDR (defined when compiling into header, | |
204 | I<filename>.h, files), RPC_XDR (defined when compiling into xdr, | |
205 | I<filename>.xdr.c, files), RPC_CLIENT (defined when compiling into client | |
206 | stubs, I<filename>.cs.c, files), and RPC_SERVER (defined when compiling | |
207 | into server stubs, I<filename>.ss.c, files). | |
208 | ||
209 | In addition, B<rxgen> does a little preprocessing of its own. Any line | |
210 | beginning with C<%> is passed directly into the output file, uninterpreted | |
211 | by B<rxgen>. For a more heavy en masse dumping of uninterpreted code, it | |
212 | would be advised to include all such code in an C<#include> file and pass | |
213 | it in preceded by C<%>. The input interface may also contain any C-style | |
214 | comments which are, of course, ignored. Interpretation is token-based, | |
215 | thus special line-orientation of separate statements is not necessary. | |
216 | B<rxgen> also provides a quite rich and helpful set of error reports, | |
217 | identifying them by exact line location and error type. Also, B<rxgen> | |
218 | will automatically generate #include lines for standard include files, | |
219 | such as F<rx/xdr.h> and F<rx/rx.h>, along with the generated header file | |
220 | from this interface. | |
221 | ||
222 | =head2 Prefixing stub procedures | |
223 | ||
224 | The I<package> statement tells B<rxgen> the name of the interface package. | |
225 | It is used for prefixing the naming of all generated stub routines and the | |
226 | execute request procedure. For example: | |
227 | ||
228 | package AFS_ | |
229 | ||
230 | causes the execute request procedure to be named AFS_ExecuteRequest | |
231 | (Warning: in the older version an additional C<_> was appended after the | |
232 | package name to the ExecuteRequest name; thus make sure you don't have an | |
233 | ExecuteRequest interface routine) and a given stub routine, say Fetch, to | |
234 | be actually named AFS_Fetch. Multiple package statements (current maximum | |
235 | size is 10) per configuration are permitted and are useful when multiple | |
236 | sets of interfaces are implemented (see the example at the end). Note | |
237 | that in such cases, use of the B<-p> flag results in the generation of | |
238 | just one ExecuteRequest procedure which recognizes the multiple interfaces | |
239 | and whose name is prefixed by the first package statement. In the default | |
240 | case, independent ExecuteRequest procedures will be created for each | |
241 | packaged group of remote procedure calls. | |
242 | ||
243 | The I<prefix> statement supplies a name to prepend to all calls to remote | |
244 | procedure names in the ExecuteRequest stub routine. It is useful when the | |
245 | server makes RPC calls to other servers (say, for debugging purposes). | |
246 | For example: | |
247 | ||
248 | prefix S | |
249 | ||
250 | causes the name C<S> to be prepended to the name of all routines called | |
251 | from the server stubs. The server can then call the original name and get | |
252 | the client stubs. | |
253 | ||
254 | =head2 B<rxgen> procedure declaration | |
255 | ||
256 | The I<proc> statement is the most common (and meaningful) in the B<rxgen> | |
257 | interface. Its syntax description is: | |
258 | ||
259 | [proc] [<proc_name>] [<server_stub>] (<arg>, ..., <arg>) | |
260 | [split | multi] [= <opcode>] ; | |
261 | ||
262 | where: | |
263 | ||
264 | =over 2 | |
265 | ||
266 | =item * | |
267 | ||
268 | C<proc> is an optional prefix of the procedure statement. This is just a | |
269 | stylistic item and not a required procedure delimiter. | |
270 | ||
271 | =item * | |
272 | ||
273 | <proc_name> is the name of the procedure. Note that even the name of the | |
274 | procedure is optional. This only makes sense when the name of the given | |
275 | procedure is identical to the name of the last I<package> statement (i.e., | |
276 | C<package RCallBack> and the declaration of the C<RCallBack> procedure). | |
277 | ||
278 | =item * | |
279 | ||
280 | <server_stub>, if present, causes the ExecuteRequest procedure to call | |
281 | that stub instead of the automatically generated stub when a call with | |
282 | that opcode is decoded. | |
283 | ||
284 | =item * | |
285 | ||
286 | <opcode> is a constant or symbol that is the opcode for that procedure. | |
287 | One might use the preprocessor features (i.e., #define), the I<const> | |
288 | RPC-language feature, or the old good constants as opcodes. Some further | |
289 | evaluation/processing of opcodes is done. Particularly, checks for | |
290 | duplicate and non-existent opcodes are performed, along with checks for | |
291 | "holes" (i.e., gaps in consecutive opcodes) in the opcode sequences. For | |
292 | example, we use the fact that when "holes" in opcodes exist, the | |
293 | ExecuteRequest procedure uses the I<case> statement rather than the faster | |
294 | (and smaller, codewise) indexed array method. | |
295 | ||
296 | Also, B<rxgen> defines (i.e., appends to the header file) three valuable | |
297 | macros for each package group: <package-name>LOWEST_OPCODE, | |
298 | <package-name>HIGHEST_OPCODE, and <package-name>NUMBER_OPCODES. These may | |
299 | be useful to the B<rxgen> programmer. Also, notice that the I<opcode> | |
300 | statement is an optional feature, and can be omitted. In such cases, | |
301 | automatic opcode numbers are generated sequentially, starting from 0. | |
302 | ||
303 | One can change the initial opcode number by using the I<startingopcode> | |
304 | (for lack of a better name) B<rxgen> command. Its syntax is: | |
305 | ||
306 | startingopcode <constant> | |
307 | ||
308 | where <constant> must be reasonable! Note that one can not mix | |
309 | procedures, some with opcodes and some without, nor allow opcodes after | |
310 | the specification of the I<startingopcode> statement. B<rxgen> will | |
311 | complain in all such cases. | |
312 | ||
313 | =item * | |
314 | ||
315 | The I<argument> entry represents a given parameter of the procedure. Its | |
316 | syntax is: | |
317 | ||
318 | [IN | INOUT | OUT | <null>] <type_decl> <arg_name> | |
319 | [<max>|<>|[max]|[]] | |
320 | ||
321 | If the type is an indirect type (i.e., is followed by *), it is assumed | |
322 | that the pointer should be followed one level and the data pointed to is | |
323 | to be transmitted. This should normally be used for all structures/arrays | |
324 | and out parameters. A noticeable exception is when explicit | |
325 | array/structure maximum size is given; since no array-of-pointer | |
326 | declarations are allowed one should use typedefs to achieve the similar | |
327 | effect. The parameters could be input parameters (preceded by IN), output | |
328 | parameters (preceded by OUT), or input/output parameters (preceded by | |
329 | INOUT). If not specified, then the direction of the previous parameter in | |
330 | the procedure is used. (Note: the first parameter must be preceded by the | |
331 | directional primitive!) | |
332 | ||
333 | =item * | |
334 | ||
335 | C<split> is a hack to handle stub routines that do things such as file | |
336 | transfers or any other operation that has to exchange information (e.g., | |
337 | length of a file) before the call returns its output parameters. Because | |
338 | of the particular handshake that is involved when doing remote file | |
339 | transfer, we currently break all such calls into two client-side stub | |
340 | routines. The first (with the default prefix of C<Begin>) is used to pass | |
341 | all IN and INOUT parameters to the server side. The second (with the | |
342 | default prefix of C<End>) is used to get back the INOUT and OUT parameters | |
343 | from the server. Between the two calls, the user is supposed to do the | |
344 | appropriate calls for the file transfer. For example, the following | |
345 | procedure declaration in package AFS_ | |
346 | ||
347 | Fetch (IN a, b,INOUT c, OUT d) split = FETCHOPCODE; | |
348 | ||
349 | will roughly generate the two independent client stub routines: | |
350 | ||
351 | BeginAFS_Fetch (IN a, b, c) | |
352 | ||
353 | and | |
354 | ||
355 | EndAFS_Fetch(OUT c, d) | |
356 | ||
357 | The I<splitprefix> statement is used to change the default prefix names | |
358 | used by the two client-side stub generated routines when dealing with file | |
359 | transfer-related procedure calls. For example: | |
360 | ||
361 | splitprefix IN=Before_ OUT=After_ | |
362 | ||
363 | will cause the naming of the two client stubs for a file transfer-related | |
364 | routine, say Fetch(), to be Before_AFS_Fetch() and After_AFS_Fetch(), | |
365 | respectively. | |
366 | ||
367 | =item * | |
368 | ||
369 | The C<multi> option is nearly identical to the C<split> feature described | |
370 | above. The only significant visible difference is that along with the two | |
371 | client stubs, the standard client stub is also generated. Since the | |
372 | intention is to handle the multi-Rx calls, we need the whole standard | |
373 | procedure stub in the cases where no multi-Rx call of the procedure is | |
374 | performed. A side effect of the C<multi> option is the generation of a | |
375 | special macro (i.e., C<< multi_<Procedure-name> >> which passes back as | |
376 | arguments the C<Begin> and C<End> stubs in the header output file. This | |
377 | macro is used directly by the Rx code when a multi-Rx call of this | |
378 | procedure is performed. | |
379 | ||
380 | =back | |
381 | ||
382 | =head2 OBSOLETE B<rxgen> FEATURES | |
383 | ||
384 | Although the following rxgen commands are still in effect, they will soon | |
385 | be removed since there are better alternatives. DO NOT USE THEM! | |
386 | ||
387 | The I<special> statement is a temporary hack used to handle certain | |
388 | inefficiencies of standard xdr routines to handle some user-customized | |
389 | declarations. In particular, this applies to a string pointer specified | |
390 | as part of a declaration. For example, | |
391 | ||
392 | special struct BBS SeqBody; | |
393 | ||
394 | tells B<rxgen> that the entry C<SeqBody> in the user-defined BBS xdr | |
395 | routine is a string (note that more than one string can be "special" per | |
396 | structure -- multiple ones are separated by commas); it will thus allocate | |
397 | and de-allocate space properly in the server-generated stubs that contain | |
398 | this structure as an IN or INOUT parameter. | |
399 | ||
400 | A better alternative to I<special> is the I<customized> statement, which | |
401 | is simply the C<customized> token followed by the regular declaration of a | |
402 | struct based on the RPCL rules. In this case, the declaration will be | |
403 | included in the generated header file (B<-h> option) but no xdr routine | |
404 | will be generated for this structure -- the user will supply this. All | |
405 | pointer entries in this structure will be remembered so when the structure | |
406 | is used as an IN or INOUT in the server stub, no core leaks will occur. | |
407 | For example, consider | |
408 | ||
409 | customized struct CBS { | |
410 | long Seqlen; | |
411 | char *SeqBody; | |
412 | } | |
413 | ||
414 | The C<xdr_CBS> routine would be provided by the user where during the | |
415 | DECODE xdr opcode, appropriate space for the C<SeqBody> string is | |
416 | allocated. Similarly, that space is freed during the FREE xdr opcode. | |
417 | ||
418 | Note: Old style "Array parameter specifications" are not supported any | |
419 | more. | |
420 | ||
421 | =head1 EXAMPLES | |
422 | ||
423 | In case there are some requirements not available by the current RPC | |
424 | language, one can customize some XDR routines by leaving those data types | |
425 | undefined. For every data type that is undefined, it will be assumed that | |
426 | a routine exists with the name C<xdr_> prepended to it. A selected set of | |
427 | B<rxgen> features is presented below, but for a more comprehensive one | |
428 | (unions, complex examples, etc) please refer to the I<rpcgen Programming | |
429 | Guide> and I<eXternal Data Representation: Sun Technical Notes>. | |
430 | ||
431 | =head2 Typedefs | |
432 | ||
433 | The RPC typedef statement is identical to the C typedef (i.e. C<< typedef | |
434 | <declaration> >>). By default, most user declarations (i.e. structs, | |
435 | unions, etc) are automatically typedef'ed by B<rxgen>. Since it makes | |
436 | parsing simpler, its usage is recommended by B<rxgen> scripts. | |
437 | ||
438 | =head2 Strings | |
439 | ||
440 | The C C<char *> string convention is kind of ambiguous, since it is | |
441 | usually intended to mean a null-terminated string of characters, but it | |
442 | could also represent a pointer to a single character, a pointer to an | |
443 | array of characters, etc. In the RPC language, a null-terminated string | |
444 | is unambiguously called a "string". Examples, | |
445 | ||
446 | string bigname<>; | |
447 | string name<MAXNAMELEN>; | |
448 | typedef string volname<MAXVOLNAME>; | |
449 | ||
450 | Notice that the maximum size of string can be arbitrary (like C<bigname> | |
451 | above) or, preferably, or specified in angle brackets (i.e. C<name> and | |
452 | C<volname> above). In practice, one should always use only bounded | |
453 | strings in interfaces. A sample calling proc using the declarations above | |
454 | would be: | |
455 | ||
456 | GetEntryByName (IN volname name, | |
457 | OUT struct vldbentry *entry) = VL_GETENTRYBYNAME; | |
458 | ||
459 | or, of course, | |
460 | ||
461 | GetEntryByName (IN string volname<MAXVOLNAME>, | |
462 | OUT struct vldbentry *entry) = VL_GETENTRYBYNAME; | |
463 | ||
464 | It is very important for the user to understand when the string parameters | |
465 | should be allocated and/or freed by the his/her client and/or server | |
466 | programs. A short analysis on string parameters handling follows (note | |
467 | that a similar method is used for the handling of variable length arrays | |
468 | as it will be shown later on): | |
469 | ||
470 | =over 2 | |
471 | ||
472 | =item * | |
473 | ||
474 | In the client side: IN and INOUT string parameters are the programmer's | |
475 | responsibility and should be allocated (static or via malloc) before | |
476 | calling the rpc and freed (if malloc was used) after the rpc's return in | |
477 | the user's client program; of course, for INOUT parameters, the returned | |
478 | string can't be bigger than the malloced input string. | |
479 | ||
480 | OUT string parameters are automatically malloced (based on the length of | |
481 | the returned string and not the maxsize) by the B<rxgen> client stubs (in | |
482 | I<filename>.cs.c) and must be freed by the client program; admittedly, | |
483 | this could be somewhat confusing since the user needs to free something | |
484 | that he/she didn't allocate.} | |
485 | ||
486 | =item * | |
487 | ||
488 | In the server side: IN and INOUT string parameters are automatically | |
489 | malloced (based on the size of incoming strings) by the rxgen server stubs | |
490 | (in I<filename>.ss.c) before they are passed to the user's server | |
491 | procedure; that space is automatically freed just before the rxgen server | |
492 | stub returns; therefore the user need not do anything special for IN and | |
493 | INOUT string parameters. | |
494 | ||
495 | OUT string parameters must be malloced by the user's server procedure | |
496 | (i.e. null pointer is passed to it by the rxgen server stub) and it is | |
497 | automatically freed at the end of the B<rxgen> server stub. Like in the | |
498 | client side, the OUT parameters are somewhat unorthodox (i.e. the server | |
499 | routine must malloc a string without ever freeing it itself; this is done | |
500 | by the B<rxgen> server stub). | |
501 | ||
502 | =back | |
503 | ||
504 | Note that for INOUT and OUT string parameters, in both the client and | |
505 | server sides their arguments must be char of pointers (i.e. char **). | |
506 | ||
507 | =head2 Pointers | |
508 | ||
509 | Pointer declarations in RPC are also exactly as they are in C | |
510 | (i.e. C<struct single_vldbentry *vldblist;>). Of course, one can't send | |
511 | pointers over the network, but one can use XDR pointers for sending | |
512 | recursive data types such as lists and trees (an example of a linked list | |
513 | will be demonstrated shortly). | |
514 | ||
515 | =head2 Arrays | |
516 | ||
517 | Fixed arrays are just like standard C array declarations (i.e. C<struct | |
518 | UpdateEntry entries[20]>) without any side effect problems in | |
519 | B<rxgen>. Since variable-length arrays have no explicit syntax in C, the | |
520 | angle-brackets are used for it and the array declarations are actually | |
521 | compiled into "struct"s. For example, declarations such as: | |
522 | ||
523 | const MAXBULKSIZE = 10000; | |
524 | const MAXENTRIES = 100; | |
525 | opaque bulk<MAXBULKSIZE>; /* At most 10000 items */ | |
526 | int hosts<>; /* any number of items */ | |
527 | typedef vldbentry blkentries<100>; /* Preferable array decl */ | |
528 | ||
529 | are compiled into the following structs: | |
530 | ||
531 | struct { | |
532 | u_int bulk_len; /* no of items */ | |
533 | char *bulk_val; /* pointer to array */ | |
534 | } bulk; | |
535 | ||
536 | for the C<bulk> array, and similarly for the C<< blkentries<100> >> array, | |
537 | ||
538 | struct { | |
539 | u_int blkentries_len; /* no of items in array */ | |
540 | vldbentry *blkentries_val; /* pointer to array */ | |
541 | } blkentries; | |
542 | ||
543 | Therefore the user should be aware of the "magically" generated structure | |
544 | entries such as the number of items in the array (<array_name>_len) and | |
545 | the pointer to the array (<array_name>_val) since some of the entries will | |
546 | have to be filled in from the client/server programs. A sample proc would | |
547 | be: | |
548 | ||
549 | typedef vldbentry blkentries<MAXENTRIES>; | |
550 | proc GetBlk (OUT blkentries *vlentries) = VL_GETBLK; | |
551 | ||
552 | or, more directly, | |
553 | ||
554 | GetBlk(OUT vldbentry vlentries<MAXENTRIES>) = VL_GETBLK; | |
555 | ||
556 | Note that although the latest method is preferable since one does not have | |
557 | to first use the typedef statement (and admittedly, programmers prefer | |
558 | avoiding typedefs), one should realize that B<rxgen> does the structure | |
559 | expansion and the xdr creation implicitly; therefore the user should be | |
560 | aware of the C<vldbentries_val> and C<vldbentries_len> fields as before | |
561 | (see following examples). | |
562 | ||
563 | =head3 Array example I (least desirable) | |
564 | ||
565 | Procedure declaration in the interface configuration: | |
566 | ||
567 | proc ListAttributes (IN vldblistbyattributes *attributes, | |
568 | INOUT blkentries *vldbentries) = VL_LISTATTRIBUTES; | |
569 | ||
570 | Sample CLIENT code: | |
571 | ||
572 | blkentries entries, *pnt; | |
573 | entries.blkentries_len = 10; /* max # returned entries */ | |
574 | entries.blkentries_val = (vldbentry *)malloc(LEN); | |
575 | /* It must be set */ | |
576 | ||
577 | code = VL_ListAttributes(&attributes, &entries); | |
578 | if (!code) { | |
579 | pnt = entries.blkentries_val; | |
580 | for (i=0; i < entries.blkentries_len; i++, pnt++) | |
581 | display_vldbentry(pnt); | |
582 | /* Make sure you free the allocated space */ | |
583 | free((char *)entries.blkentries_val); | |
584 | } | |
585 | ||
586 | Sample SERVER code: | |
587 | ||
588 | VL_ListAttributes(attributes, entries) | |
589 | { | |
590 | vldbentry *singleentry = entries->blkentries_val; | |
591 | entries->blkentries_len = 0; | |
592 | ||
593 | while (copy_to_vldbentry(&vlentry, singleentry)) | |
594 | singleentry++, vldbentries->entries_len++; | |
595 | } | |
596 | ||
597 | Although this method for variable-size arrays works fine, there are some | |
598 | major drawbacks. The array parameter (i.e. vldbentries above) must be | |
599 | declared as INOUT since we need to pass the max length of the expected | |
600 | returned array; more importantly, a big (depending on the value of | |
601 | C<_len>) chunk of junk code is going to be transferred to the server as | |
602 | result of the IN(out) side-effect of the array. It's an easy and | |
603 | convenient method if the returned array size can be predicted from the | |
604 | start and when the size is quite high. This method is included as an | |
605 | example of erroneous use (and abuse) of B<rxgen> and should not be used. | |
606 | ||
607 | =head3 Array example II (Desirable method) | |
608 | ||
609 | Procedure declaration in the interface configuration (using Example I | |
610 | above): | |
611 | ||
612 | proc ListAttributes (IN vldblistbyattributes *attributes, | |
613 | OUT blkentries *vldbentries) = VL_LISTATTRIBUTES; | |
614 | ||
615 | Sample CLIENT code: | |
616 | ||
617 | blkentries entries, *pnt; | |
618 | ||
619 | code = VL_ListAttributes(&attributes, &entries); | |
620 | if (!code) { | |
621 | pnt = entries.blkentries_val; | |
622 | for (i=0; i < entries.blkentries_len; i++, pnt++) | |
623 | display_vldbentry(pnt); | |
624 | /* Make sure you free the allocated space (by rxgen) */ | |
625 | free((char *)entries.blkentries_val); | |
626 | } | |
627 | ||
628 | Sample SERVER code: | |
629 | ||
630 | VL_ListAttributes(attributes, entries) | |
631 | { | |
632 | vldbentry *singleentry; | |
633 | entries->blkentries_len = 0; | |
634 | singleentry = entries->blkentries_val | |
635 | = (vldbentry *)malloc(MAXENTRIES * sizeof(vldbentry)); | |
636 | ||
637 | while (copy_to_vldbentry(&vlentry, singleentry)) | |
638 | singleentry++, vldbentries->entries_len++; | |
639 | } | |
640 | ||
641 | This is the best (and simplest) way of using variable-size arrays as an | |
642 | output parameter. It is the responsibility of the server-side stub to | |
643 | malloc() the adequate space which is automatically freed by the B<rxgen> | |
644 | stub; the client side should free the space allocated by the | |
645 | B<rxgen>-calling stub. | |
646 | ||
647 | =head3 Array example III (Linked Lists) | |
648 | ||
649 | Considering the following 3 declarations (could have applied some | |
650 | optimizations) in the configuration file: | |
651 | ||
652 | typedef struct single_vldbentry *vldblist; | |
653 | struct single_vldbentry { | |
654 | vldbentry vlentry; | |
655 | vldblist next_vldb; | |
656 | }; | |
657 | ||
658 | struct vldb_list { | |
659 | vldblist node; | |
660 | }; | |
661 | ||
662 | and the rxgen procedure declaration: | |
663 | ||
664 | LinkedList (IN vldblistbyattributes *attributes, | |
665 | OUT vldb_list *linkedentries) = VL_LINKEDLIST; | |
666 | ||
667 | Sample CLIENT code: | |
668 | ||
669 | vldb_list linkedvldbs; | |
670 | vldblist vllist, vllist1; | |
671 | ||
672 | bzero(&linkedvldbs, sizeof(vldb_list)); | |
673 | code = VL_LinkedList(&attributes, &nentries, &linkedvldbs); | |
674 | if (!code) { | |
675 | printf("We got %d vldb entries\n", nentries); | |
676 | for (vllist = linkedvldbs.node; vllist; vllist = vllist1) { | |
677 | vllist1 = vllist->next_vldb; | |
678 | display_entry(&vllist->vlentry); | |
679 | free((char *)vllist); | |
680 | } | |
681 | } | |
682 | ||
683 | Sample SERVER code: | |
684 | ||
685 | VL_LinkedList(rxcall, attributes, nentries, linkedvldbs); | |
686 | { | |
687 | vldblist vllist, *vllistptr = &linkedvldbs->node; | |
688 | while (...) { | |
689 | vllist = *vllistptr | |
690 | = (single_vldbentry *)malloc (sizeof (single_vldbentry)); | |
691 | copy_to_vldbentry(&tentry, &vllist->vlentry); | |
692 | nentries++; | |
693 | vllistptr = &vllist->next_vldb; | |
694 | }; | |
695 | *vllistptr = NULL; | |
696 | } | |
697 | ||
698 | Using a linked list offers many advantages: Nothing is passed to the | |
699 | server (the parameter is OUT), no additional overhead is involved, and the | |
700 | caller doesn't have to explicitly prepare for an arbitrary return size. A | |
701 | drawback is that the caller has the responsibility of malloc() (on the | |
702 | server) and free (on the client) of each entry (to avoid unwanted | |
703 | core-leaks). Another drawback is that since it's a recursive call, the C | |
704 | stack will grow linearly with respect to the number of nodes in the list | |
705 | (so it's wise to increase the Rx LWP stack if huge amounts of data are | |
706 | expected back -- default stack size is 4K). The advantages should | |
707 | outweigh the disadvantages here. | |
708 | ||
709 | It's important to pay attention to the comments of the three array | |
710 | examples above particularly when they're references to when the user | |
711 | should allocate/free space for the variable length arrays. The mechanism | |
712 | is very similar to the handling of strings thus you might need to review | |
713 | the strings section above; note that the linked lists are handled somewhat | |
714 | differently... | |
715 | ||
716 | =head2 Miscellaneous examples | |
717 | ||
718 | Below is an abbreviated version of a random interface file which shows | |
719 | some of the common cases. | |
720 | ||
721 | /* Declaration of all structures used by the R.xg script interface */ | |
722 | ||
723 | struct AFSFid { | |
724 | unsigned long Volume; | |
725 | unsigned long Vnode; | |
726 | unsigned long Unique; | |
727 | }; | |
728 | ||
729 | typedef long ViceDataType; | |
730 | ||
731 | /* Note that TEST would be equivalent to "HEADER" only during the | |
732 | processing of the header, *.h, file */ | |
733 | ||
734 | #ifdef RPC_HDR | |
735 | #define TEST "HEADER" | |
736 | #else | |
737 | #define TEST "REST" | |
738 | #endif | |
739 | ||
740 | /* This is the standard *.xg specification file */ | |
741 | ||
742 | package AFS_ | |
743 | splitprefix IN=BEFORE_ OUT=AFTER_; | |
744 | Prefix Test | |
745 | ||
746 | proc Remove(IN struct AFSFid *Did, IN string volname<64>, | |
747 | OUT struct AFSStatus *Status) = AFS_REMOVE; | |
748 | ||
749 | DisconnectFS AUX_disconnectFS() = AFS_DISCONNECTFS; | |
750 | ||
751 | proc GetVolumeInfo(IN string Vid, | |
752 | OUT struct VolumeInfo *Info) = AFS_GETVOLUMEINFO; | |
753 | ||
754 | /* You could have more than an interface per configuration */ | |
755 | ||
756 | package VOTE_ | |
757 | ||
758 | /* Using the "multi" feature; thus VOTE_Beacon can be called as an | |
759 | multi-Rx call or as a regular call */ | |
760 | ||
761 | Beacon (IN long state, long voteStart, | |
762 | net_version *version, net_tid *tid) | |
763 | multi = VOTE_BEACON; | |
764 | ||
765 | package DISK_ | |
766 | ||
767 | /* Using the "split" feature */ | |
768 | ||
769 | SendFile (IN long file, long offset, | |
770 | long length, net_version *version) | |
771 | split = DISK_SENDFILE; | |
772 | ||
773 | =head2 Output of an actual interface configuration | |
774 | ||
775 | We'll demonstrate some of the actual output generated by B<rxgen> by | |
776 | following an abbreviated actual interface configuration. | |
777 | ||
778 | =head3 Configuration file | |
779 | ||
780 | Contents of the interface configuration file (F<vldbint.xg>): | |
781 | ||
782 | package VL_ | |
783 | #include "vl_opcodes.h" /* The opcodes are included here */ | |
784 | %#include "vl_opcodes.h" /* directly to other places */ | |
785 | ||
786 | /* Current limitations on parameters that affect other packages | |
787 | (i.e. volume) */ | |
788 | ||
789 | const MAXNAMELEN = 65; | |
790 | const MAXNSERVERS = 8; | |
791 | const MAXTYPES = 3; | |
792 | ||
793 | /* External (visible) representation of an individual vldb entry */ | |
794 | ||
795 | struct vldbentry { | |
796 | char name[MAXNAMELEN]; | |
797 | long volumeType; | |
798 | long nServers; | |
799 | long serverNumber[MAXNSERVERS]; | |
800 | long serverPartition[MAXNSERVERS]; | |
801 | long serverFlags[MAXNSERVERS]; | |
802 | u_long volumeId[MAXTYPES]; | |
803 | long flags; | |
804 | }; | |
805 | ||
806 | typedef struct single_vldbentry *vldblist; | |
807 | struct single_vldbentry { | |
808 | vldbentry VldbEntry; | |
809 | vldblist next_vldb; | |
810 | }; | |
811 | ||
812 | struct vldb_list { | |
813 | vldblist node; | |
814 | }; | |
815 | ||
816 | /* vldb interface calls */ | |
817 | ||
818 | CreateEntry (IN long Volid, | |
819 | vldbentry *newentry) = VLCREATEENTRY; | |
820 | ||
821 | GetEntryByName (IN string volumename<MAXNAMELEN>, | |
822 | OUT vldbentry *entry) = VLGETENTRYBYNAME; | |
823 | ||
824 | GetNewVolumeId (IN long bumpcount, | |
825 | OUT long *newvolumid) = VLGETNEWVOLUMEID; | |
826 | ||
827 | ReplaceEntry (IN long Volid, | |
828 | long voltype, | |
829 | vldbentry *newentry, | |
830 | long ReleaseType) multi = VLREPLACEENTRY; | |
831 | ||
832 | ListAttributes (IN VldbListByAttributes *attributes, | |
833 | OUT long *nentries, | |
834 | OUT vldbentry bulkentries<MAXVLDBLEN>) | |
835 | = VLLISTATTRIBUTES; | |
836 | ||
837 | LinkedList (IN VldbListByAttributes *attributes, | |
838 | OUT long *nentries, | |
839 | OUT vldb_list *linkedentries) = VLLINKEDLIST; | |
840 | ||
841 | For a detailed description on the Rx-related calls inside the generated | |
842 | stubs (i.e., rx_NewCall(), rx_EndCall()), along with details on what happens | |
843 | inside certain calls (like xdrrx_create()) please refer to the Rx | |
844 | documentation. Typing C<rxgen vldbint.xg> will result in the creation of | |
845 | four files: F<vldbint.h>, F<vldbint.xdr.c>, F<vldbint.cs.c> and | |
846 | F<vldbint.ss.c>. A closer look at these files follows. | |
847 | ||
848 | =head3 Header file (F<vldbint.h>) | |
849 | ||
850 | /* Machine generated file -- Do NOT edit */ | |
851 | ||
852 | #include "vl_opcodes.h" /* directly to other places */ | |
853 | #define MAXNAMELEN 65 | |
854 | #define MAXNSERVERS 8 | |
855 | #define MAXTYPES 3 | |
856 | ||
857 | struct vldbentry { | |
858 | char name[MAXNAMELEN]; | |
859 | long volumeType; | |
860 | long nServers; | |
861 | long serverNumber[MAXNSERVERS]; | |
862 | long serverPartition[MAXNSERVERS]; | |
863 | long serverFlags[MAXNSERVERS]; | |
864 | u_long volumeId[MAXTYPES]; | |
865 | long flags; | |
866 | }; | |
867 | typedef struct vldbentry vldbentry; | |
868 | bool_t xdr_vldbentry(); | |
869 | ||
870 | typedef struct single_vldbentry *vldblist; | |
871 | bool_t xdr_vldblist(); | |
872 | ||
873 | struct single_vldbentry { | |
874 | vldbentry VldbEntry; | |
875 | vldblist next_vldb; | |
876 | }; | |
877 | typedef struct single_vldbentry single_vldbentry; | |
878 | bool_t xdr_single_vldbentry(); | |
879 | ||
880 | struct vldb_list { | |
881 | vldblist node; | |
882 | }; | |
883 | typedef struct vldb_list vldb_list; | |
884 | bool_t xdr_vldb_list(); | |
885 | ||
886 | #include <rx/rx_multi.h> | |
887 | #define multi_VL_ReplaceEntry(Volid, voltype, newentry, ReleaseType) \ | |
888 | multi_Body(StartVL_ReplaceEntry(multi_call, Volid, voltype, | |
889 | newentry, ReleaseType), EndVL_ReplaceEntry(multi_call)) | |
890 | ||
891 | typedef struct bulkentries { | |
892 | u_int bulkentries_len; | |
893 | vldbentry *bulkentries_val; | |
894 | } bulkentries; | |
895 | bool_t xdr_bulkentries(); | |
896 | ||
897 | /* Opcode-related useful stats for package: VL_ */ | |
898 | #define VL_LOWEST_OPCODE 501 | |
899 | #define VL_HIGHEST_OPCODE 506 | |
900 | #define VL_NUMBER_OPCODES 6 | |
901 | ||
902 | Notice that all structures are automatically typedef'ed and all C<const>s | |
903 | are converted to C<#define>s. Some data structures, such as bulkentries, | |
904 | are taken from procedure params (from ListAttributes proc). Thus, this | |
905 | should be kept in mind when creating stubs piecemeal with B<rxgen> (i.e., | |
906 | using the B<-c>, B<-h>, B<-C>, or B<-S> flags). Also, one of the side | |
907 | effects of the C<multi> option (in C<ReplaceEntry> proc) is the generation | |
908 | of the C<multi_VL_ReplaceEntry> above. | |
909 | ||
910 | =head3 XDR routines for structures (vldbint.xdr.c) | |
911 | ||
912 | /* Machine generated file -- Do NOT edit */ | |
913 | ||
914 | #include <rx/xdr.h> | |
915 | #include "vldbint.h" | |
916 | ||
917 | #include "vl_opcodes.h" /* directly to other places */ | |
918 | ||
919 | bool_t | |
920 | xdr_vldbentry(xdrs, objp) | |
921 | XDR *xdrs; | |
922 | vldbentry *objp; | |
923 | { | |
924 | if (!xdr_vector(xdrs, (char *)objp->name, MAXNAMELEN, | |
925 | sizeof(char), xdr_char)) | |
926 | return (FALSE); | |
927 | if (!xdr_long(xdrs, &objp->volumeType)) | |
928 | return (FALSE); | |
929 | if (!xdr_long(xdrs, &objp->nServers)) | |
930 | return (FALSE); | |
931 | if (!xdr_vector(xdrs, (char *)objp->serverNumber, MAXNSERVERS, | |
932 | sizeof(long), xdr_long)) | |
933 | return (FALSE); | |
934 | if (!xdr_vector(xdrs, (char *)objp->serverPartition, | |
935 | MAXNSERVERS, sizeof(long), xdr_long)) | |
936 | return (FALSE); | |
937 | if (!xdr_vector(xdrs, (char *)objp->serverFlags, MAXNSERVERS, | |
938 | sizeof(long), xdr_long)) | |
939 | return (FALSE); | |
940 | if (!xdr_vector(xdrs, (char *)objp->volumeId, MAXTYPES, | |
941 | sizeof(u_long), xdr_u_long)) | |
942 | return (FALSE); | |
943 | if (!xdr_long(xdrs, &objp->flags)) | |
944 | return (FALSE); | |
945 | return (TRUE); | |
946 | } | |
947 | ||
948 | bool_t | |
949 | xdr_vldblist(xdrs, objp) | |
950 | XDR *xdrs; | |
951 | vldblist *objp; | |
952 | { | |
953 | if (!xdr_pointer(xdrs, (char **)objp, | |
954 | sizeof(struct single_vldbentry), | |
955 | xdr_single_vldbentry)) | |
956 | return (FALSE); | |
957 | return (TRUE); | |
958 | } | |
959 | ||
960 | bool_t | |
961 | xdr_single_vldbentry(xdrs, objp) | |
962 | XDR *xdrs; | |
963 | single_vldbentry *objp; | |
964 | { | |
965 | if (!xdr_vldbentry(xdrs, &objp->VldbEntry)) | |
966 | return (FALSE); | |
967 | if (!xdr_vldblist(xdrs, &objp->next_vldb)) | |
968 | return (FALSE); | |
969 | return (TRUE); | |
970 | } | |
971 | ||
972 | bool_t | |
973 | xdr_vldb_list(xdrs, objp) | |
974 | XDR *xdrs; | |
975 | vldb_list *objp; | |
976 | { | |
977 | if (!xdr_vldblist(xdrs, &objp->node)) | |
978 | return (FALSE); | |
979 | return (TRUE); | |
980 | } | |
981 | ||
982 | bool_t | |
983 | xdr_bulkentries(xdrs, objp) | |
984 | XDR *xdrs; | |
985 | bulkentries *objp; | |
986 | { | |
987 | if (!xdr_array(xdrs, (char **)&objp->bulkentries_val, | |
988 | (u_int *)&objp->bulkentries_len, MAXVLDBLEN, | |
989 | sizeof(vldbentry), xdr_vldbentry)) | |
990 | return (FALSE); | |
991 | return (TRUE); | |
992 | } | |
993 | ||
994 | Note that the xdr_bulkentries() is automatically generated as a side | |
995 | effect of a procedure parameter declaration. Thus, if identical multiple | |
996 | type parameter declarations are used, then multiply-defined xdr_* stubs | |
997 | will be created! We felt this was a better alternative to having the | |
998 | B<rxgen> programmer deal with types such as bulkentries_1, | |
999 | bulkentries_2... | |
1000 | ||
1001 | =head3 Client-Side stub routines (vldbint.cs.c) | |
1002 | ||
1003 | /* Machine generated file -- Do NOT edit */ | |
1004 | ||
1005 | #include <rx/xdr.h> | |
1006 | #include <rx/rx.h> | |
1007 | #include <afs/rxgen_consts.h> | |
1008 | #include "vldbint.h" | |
1009 | ||
1010 | #include "vl_opcodes.h" /* directly to other places */ | |
1011 | ||
1012 | int VL_CreateEntry(z_conn, Volid, newentry) | |
1013 | register struct rx_connection *z_conn; | |
1014 | long Volid; | |
1015 | vldbentry * newentry; | |
1016 | { | |
1017 | struct rx_call *z_call = rx_NewCall(z_conn); | |
1018 | static int z_op = 501; | |
1019 | int z_result; | |
1020 | XDR z_xdrs; | |
1021 | ||
1022 | xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); | |
1023 | ||
1024 | /* Marshal the arguments */ | |
1025 | if ((!xdr_int(&z_xdrs, &z_op)) | |
1026 | || (!xdr_long(&z_xdrs, &Volid)) | |
1027 | || (!xdr_vldbentry(&z_xdrs, newentry))) { | |
1028 | z_result = RXGEN_CC_MARSHAL; | |
1029 | goto fail; | |
1030 | } | |
1031 | ||
1032 | z_result = RXGEN_SUCCESS; | |
1033 | fail: | |
1034 | return rx_EndCall(z_call, z_result); | |
1035 | } | |
1036 | ||
1037 | int VL_GetEntryByName(z_conn, volumename, entry) | |
1038 | register struct rx_connection *z_conn; | |
1039 | char * volumename; | |
1040 | vldbentry * entry; | |
1041 | { | |
1042 | struct rx_call *z_call = rx_NewCall(z_conn); | |
1043 | static int z_op = 504; | |
1044 | int z_result; | |
1045 | XDR z_xdrs; | |
1046 | ||
1047 | xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); | |
1048 | ||
1049 | /* Marshal the arguments */ | |
1050 | if ((!xdr_int(&z_xdrs, &z_op)) | |
1051 | || (!xdr_string(&z_xdrs, &volumename, 65))) { | |
1052 | z_result = RXGEN_CC_MARSHAL; | |
1053 | goto fail; | |
1054 | } | |
1055 | ||
1056 | /* Un-marshal the reply arguments */ | |
1057 | z_xdrs.x_op = XDR_DECODE; | |
1058 | if ((!xdr_vldbentry(&z_xdrs, entry))) { | |
1059 | z_result = RXGEN_CC_UNMARSHAL; | |
1060 | goto fail; | |
1061 | } | |
1062 | ||
1063 | z_result = RXGEN_SUCCESS; | |
1064 | fail: | |
1065 | return rx_EndCall(z_call, z_result); | |
1066 | } | |
1067 | ||
1068 | int VL_GetNewVolumeId(z_conn, bumpcount, newvolumid) | |
1069 | register struct rx_connection *z_conn; | |
1070 | long bumpcount; | |
1071 | long * newvolumid; | |
1072 | { | |
1073 | struct rx_call *z_call = rx_NewCall(z_conn); | |
1074 | static int z_op = 505; | |
1075 | int z_result; | |
1076 | XDR z_xdrs; | |
1077 | ||
1078 | xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); | |
1079 | ||
1080 | /* Marshal the arguments */ | |
1081 | if ((!xdr_int(&z_xdrs, &z_op)) | |
1082 | || (!xdr_long(&z_xdrs, &bumpcount))) { | |
1083 | z_result = RXGEN_CC_MARSHAL; | |
1084 | goto fail; | |
1085 | } | |
1086 | ||
1087 | /* Un-marshal the reply arguments */ | |
1088 | z_xdrs.x_op = XDR_DECODE; | |
1089 | if ((!xdr_long(&z_xdrs, newvolumid))) { | |
1090 | z_result = RXGEN_CC_UNMARSHAL; | |
1091 | goto fail; | |
1092 | } | |
1093 | ||
1094 | z_result = RXGEN_SUCCESS; | |
1095 | fail: | |
1096 | return rx_EndCall(z_call, z_result); | |
1097 | } | |
1098 | ||
1099 | int VL_ReplaceEntry(z_conn, Volid, voltype, newentry, ReleaseType) | |
1100 | register struct rx_connection *z_conn; | |
1101 | long Volid, voltype, ReleaseType; | |
1102 | vldbentry * newentry; | |
1103 | { | |
1104 | struct rx_call *z_call = rx_NewCall(z_conn); | |
1105 | static int z_op = 506; | |
1106 | int z_result; | |
1107 | XDR z_xdrs; | |
1108 | ||
1109 | xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); | |
1110 | ||
1111 | /* Marshal the arguments */ | |
1112 | if ((!xdr_int(&z_xdrs, &z_op)) | |
1113 | || (!xdr_long(&z_xdrs, &Volid)) | |
1114 | || (!xdr_long(&z_xdrs, &voltype)) | |
1115 | || (!xdr_vldbentry(&z_xdrs, newentry)) | |
1116 | || (!xdr_long(&z_xdrs, &ReleaseType))) { | |
1117 | z_result = RXGEN_CC_MARSHAL; | |
1118 | goto fail; | |
1119 | } | |
1120 | ||
1121 | z_result = RXGEN_SUCCESS; | |
1122 | fail: | |
1123 | return rx_EndCall(z_call, z_result); | |
1124 | } | |
1125 | ||
1126 | int StartVL_ReplaceEntry(z_call, Volid, voltype, newentry, ReleaseType) | |
1127 | register struct rx_call *z_call; | |
1128 | long Volid, voltype, ReleaseType; | |
1129 | vldbentry * newentry; | |
1130 | { | |
1131 | static int z_op = 506; | |
1132 | int z_result; | |
1133 | XDR z_xdrs; | |
1134 | ||
1135 | xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); | |
1136 | ||
1137 | /* Marshal the arguments */ | |
1138 | if ((!xdr_int(&z_xdrs, &z_op)) | |
1139 | || (!xdr_long(&z_xdrs, &Volid)) | |
1140 | || (!xdr_long(&z_xdrs, &voltype)) | |
1141 | || (!xdr_vldbentry(&z_xdrs, newentry)) | |
1142 | || (!xdr_long(&z_xdrs, &ReleaseType))) { | |
1143 | z_result = RXGEN_CC_MARSHAL; | |
1144 | goto fail; | |
1145 | } | |
1146 | ||
1147 | z_result = RXGEN_SUCCESS; | |
1148 | fail: | |
1149 | return z_result; | |
1150 | } | |
1151 | ||
1152 | int EndVL_ReplaceEntry(z_call) | |
1153 | register struct rx_call *z_call; | |
1154 | { | |
1155 | int z_result; | |
1156 | XDR z_xdrs; | |
1157 | ||
1158 | z_result = RXGEN_SUCCESS; | |
1159 | fail: | |
1160 | return z_result; | |
1161 | } | |
1162 | ||
1163 | int VL_ListAttributes(z_conn, attributes, nentries, bulkentries_1) | |
1164 | register struct rx_connection *z_conn; | |
1165 | VldbListByAttributes * attributes; | |
1166 | long * nentries; | |
1167 | bulkentries * bulkentries_1; | |
1168 | { | |
1169 | struct rx_call *z_call = rx_NewCall(z_conn); | |
1170 | static int z_op = 511; | |
1171 | int z_result; | |
1172 | XDR z_xdrs; | |
1173 | ||
1174 | xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); | |
1175 | ||
1176 | /* Marshal the arguments */ | |
1177 | if ((!xdr_int(&z_xdrs, &z_op)) | |
1178 | || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) { | |
1179 | z_result = RXGEN_CC_MARSHAL; | |
1180 | goto fail; | |
1181 | } | |
1182 | ||
1183 | /* Un-marshal the reply arguments */ | |
1184 | z_xdrs.x_op = XDR_DECODE; | |
1185 | if ((!xdr_long(&z_xdrs, nentries)) | |
1186 | || (!xdr_bulkentries(&z_xdrs, bulkentries_1))) { | |
1187 | z_result = RXGEN_CC_UNMARSHAL; | |
1188 | goto fail; | |
1189 | } | |
1190 | ||
1191 | z_result = RXGEN_SUCCESS; | |
1192 | fail: | |
1193 | return rx_EndCall(z_call, z_result); | |
1194 | } | |
1195 | ||
1196 | int VL_LinkedList(z_conn, attributes, nentries, linkedentries) | |
1197 | register struct rx_connection *z_conn; | |
1198 | VldbListByAttributes * attributes; | |
1199 | long * nentries; | |
1200 | vldb_list * linkedentries; | |
1201 | { | |
1202 | struct rx_call *z_call = rx_NewCall(z_conn); | |
1203 | static int z_op = 512; | |
1204 | int z_result; | |
1205 | XDR z_xdrs; | |
1206 | ||
1207 | xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); | |
1208 | ||
1209 | /* Marshal the arguments */ | |
1210 | if ((!xdr_int(&z_xdrs, &z_op)) | |
1211 | || (!xdr_VldbListByAttributes(&z_xdrs, attributes))) { | |
1212 | z_result = RXGEN_CC_MARSHAL; | |
1213 | goto fail; | |
1214 | } | |
1215 | ||
1216 | /* Un-marshal the reply arguments */ | |
1217 | z_xdrs.x_op = XDR_DECODE; | |
1218 | if ((!xdr_long(&z_xdrs, nentries)) | |
1219 | || (!xdr_vldb_list(&z_xdrs, linkedentries))) { | |
1220 | z_result = RXGEN_CC_UNMARSHAL; | |
1221 | goto fail; | |
1222 | } | |
1223 | ||
1224 | z_result = RXGEN_SUCCESS; | |
1225 | fail: | |
1226 | return rx_EndCall(z_call, z_result); | |
1227 | } | |
1228 | ||
1229 | Notice the side effect of the C<multi> feature (three different modules | |
1230 | for C<ReplaceEntry> proc). | |
1231 | ||
1232 | =head3 Server-Side stub routines (vldbint.ss.c) | |
1233 | ||
1234 | /* Machine generated file -- Do NOT edit */ | |
1235 | ||
1236 | #include <rx/xdr.h> | |
1237 | #include <rx/rx.h> | |
1238 | #include <afs/rxgen_consts.h> | |
1239 | #include "vldbint.h" | |
1240 | ||
1241 | #include "vl_opcodes.h" /* directly to other places */ | |
1242 | ||
1243 | long _VL_CreateEntry(z_call, z_xdrs) | |
1244 | struct rx_call *z_call; | |
1245 | XDR *z_xdrs; | |
1246 | { | |
1247 | long z_result; | |
1248 | long Volid; | |
1249 | vldbentry newentry; | |
1250 | ||
1251 | if ((!xdr_long(z_xdrs, &Volid)) | |
1252 | || (!xdr_vldbentry(z_xdrs, &newentry))) { | |
1253 | z_result = RXGEN_SS_UNMARSHAL; | |
1254 | goto fail; | |
1255 | } | |
1256 | ||
1257 | z_result = VL_CreateEntry(z_call, Volid, &newentry); | |
1258 | fail: | |
1259 | return z_result; | |
1260 | } | |
1261 | ||
1262 | long _VL_GetEntryByName(z_call, z_xdrs) | |
1263 | struct rx_call *z_call; | |
1264 | XDR *z_xdrs; | |
1265 | { | |
1266 | long z_result; | |
1267 | char *volumename = (char *)0; | |
1268 | vldbentry entry; | |
1269 | ||
1270 | if ((!xdr_string(z_xdrs, &volumename, 65))) { | |
1271 | z_result = RXGEN_SS_UNMARSHAL; | |
1272 | goto fail; | |
1273 | } | |
1274 | ||
1275 | z_result = VL_GetEntryByName(z_call, &volumename, &entry); | |
1276 | z_xdrs->x_op = XDR_ENCODE; | |
1277 | if ((!xdr_vldbentry(z_xdrs, &entry))) | |
1278 | z_result = RXGEN_SS_MARSHAL; | |
1279 | fail: | |
1280 | z_xdrs->x_op = XDR_FREE; | |
1281 | if (!xdr_string(z_xdrs, &volumename, 65)) goto fail1; | |
1282 | return z_result; | |
1283 | fail1: | |
1284 | return RXGEN_SS_XDRFREE; | |
1285 | } | |
1286 | ||
1287 | long _VL_GetNewVolumeId(z_call, z_xdrs) | |
1288 | struct rx_call *z_call; | |
1289 | XDR *z_xdrs; | |
1290 | { | |
1291 | long z_result; | |
1292 | long bumpcount; | |
1293 | long newvolumid; | |
1294 | ||
1295 | if ((!xdr_long(z_xdrs, &bumpcount))) { | |
1296 | z_result = RXGEN_SS_UNMARSHAL; | |
1297 | goto fail; | |
1298 | } | |
1299 | ||
1300 | z_result = VL_GetNewVolumeId(z_call, bumpcount, &newvolumid); | |
1301 | z_xdrs->x_op = XDR_ENCODE; | |
1302 | if ((!xdr_long(z_xdrs, &newvolumid))) | |
1303 | z_result = RXGEN_SS_MARSHAL; | |
1304 | fail: | |
1305 | return z_result; | |
1306 | } | |
1307 | ||
1308 | long _VL_ReplaceEntry(z_call, z_xdrs) | |
1309 | struct rx_call *z_call; | |
1310 | XDR *z_xdrs; | |
1311 | { | |
1312 | long z_result; | |
1313 | long Volid, voltype, ReleaseType; | |
1314 | vldbentry newentry; | |
1315 | ||
1316 | if ((!xdr_long(z_xdrs, &Volid)) | |
1317 | || (!xdr_long(z_xdrs, &voltype)) | |
1318 | || (!xdr_vldbentry(z_xdrs, &newentry)) | |
1319 | || (!xdr_long(z_xdrs, &ReleaseType))) { | |
1320 | z_result = RXGEN_SS_UNMARSHAL; | |
1321 | goto fail; | |
1322 | } | |
1323 | ||
1324 | z_result = VL_ReplaceEntry(z_call, Volid, voltype, &newentry, | |
1325 | ReleaseType); | |
1326 | fail: | |
1327 | return z_result; | |
1328 | } | |
1329 | ||
1330 | long _VL_ListAttributes(z_call, z_xdrs) | |
1331 | struct rx_call *z_call; | |
1332 | XDR *z_xdrs; | |
1333 | { | |
1334 | long z_result; | |
1335 | VldbListByAttributes attributes; | |
1336 | long nentries; | |
1337 | bulkentries bulkentries_1; | |
1338 | ||
1339 | if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) { | |
1340 | z_result = RXGEN_SS_UNMARSHAL; | |
1341 | goto fail; | |
1342 | } | |
1343 | ||
1344 | z_result = VL_ListAttributes(z_call, &attributes, &nentries, | |
1345 | &bulkentries_1); | |
1346 | z_xdrs->x_op = XDR_ENCODE; | |
1347 | if ((!xdr_long(z_xdrs, &nentries)) | |
1348 | || (!xdr_bulkentries(z_xdrs, &bulkentries_1))) | |
1349 | z_result = RXGEN_SS_MARSHAL; | |
1350 | fail: | |
1351 | z_xdrs->x_op = XDR_FREE; | |
1352 | if (!xdr_bulkentries(z_xdrs, &bulkentries_1)) goto fail1; | |
1353 | return z_result; | |
1354 | fail1: | |
1355 | return RXGEN_SS_XDRFREE; | |
1356 | } | |
1357 | ||
1358 | long _VL_LinkedList(z_call, z_xdrs) | |
1359 | struct rx_call *z_call; | |
1360 | XDR *z_xdrs; | |
1361 | { | |
1362 | long z_result; | |
1363 | VldbListByAttributes attributes; | |
1364 | long nentries; | |
1365 | vldb_list linkedentries; | |
1366 | ||
1367 | if ((!xdr_VldbListByAttributes(z_xdrs, &attributes))) { | |
1368 | z_result = RXGEN_SS_UNMARSHAL; | |
1369 | goto fail; | |
1370 | } | |
1371 | ||
1372 | z_result = VL_LinkedList(z_call, &attributes, &nentries, | |
1373 | &linkedentries); | |
1374 | z_xdrs->x_op = XDR_ENCODE; | |
1375 | if ((!xdr_long(z_xdrs, &nentries)) | |
1376 | || (!xdr_vldb_list(z_xdrs, &linkedentries))) | |
1377 | z_result = RXGEN_SS_MARSHAL; | |
1378 | fail: | |
1379 | return z_result; | |
1380 | } | |
1381 | ||
1382 | long _VL_CreateEntry(); | |
1383 | long _VL_GetEntryByName(); | |
1384 | long _VL_GetNewVolumeId(); | |
1385 | long _VL_ReplaceEntry(); | |
1386 | long _VL_ListAttributes(); | |
1387 | long _VL_LinkedList(); | |
1388 | ||
1389 | static long (*StubProcsArray0[])() = {_VL_CreateEntry, | |
1390 | _VL_GetEntryByName, _VL_GetNewVolumeId, _VL_ReplaceEntry, | |
1391 | _VL_ListAttributes, _VL_LinkedList}; | |
1392 | ||
1393 | VL_ExecuteRequest(z_call) | |
1394 | register struct rx_call *z_call; | |
1395 | { | |
1396 | int op; | |
1397 | XDR z_xdrs; | |
1398 | long z_result; | |
1399 | ||
1400 | xdrrx_create(&z_xdrs, z_call, XDR_DECODE); | |
1401 | if (!xdr_int(&z_xdrs, &op)) | |
1402 | z_result = RXGEN_DECODE; | |
1403 | else if (op < VL_LOWEST_OPCODE || op > VL_HIGHEST_OPCODE) | |
1404 | z_result = RXGEN_OPCODE; | |
1405 | else | |
1406 | z_result = (*StubProcsArray0[op - VL_LOWEST_OPCODE]) | |
1407 | (z_call, &z_xdrs); | |
1408 | return z_result; | |
1409 | } | |
1410 | ||
1411 | If there were gaps in the procedures' opcode sequence the code for | |
1412 | VL_ExecuteRequest() routine would be have been drastically different (it | |
1413 | would have been a case statement for each procedure). | |
1414 | ||
1415 | =head1 NOTES | |
1416 | ||
1417 | B<rxgen> is implemented from Sun's B<rpcgen> utility. | |
1418 | ||
1419 | When the C<%#include <include file>> feature is used make sure that you | |
1420 | don't have any B<rxgen> language features (i.e. %#defines) since you'll | |
1421 | get syntax errors during compilations.. | |
1422 | ||
1423 | Since this is an ongoing project many of the above may change/disappear | |
1424 | without a major warning. | |
1425 | ||
1426 | =head1 SEE ALSO | |
1427 | ||
1428 | I<Rxgen Syntax Summary>: Summary description of rxgen's grammar. | |
1429 | ||
1430 | I<Rpcgen Programming Guide>: Sun's RPC protocol compiler. B<rxgen> was | |
1431 | implemented as an extension to that compiler. | |
1432 | ||
1433 | I<External Data Representation: Sun Technical Notes>: Detailed examples in | |
1434 | using XDR. | |
1435 | ||
1436 | I<RPCL Syntax Summary>: Summary of Sun's Remote Procedure Call Language. | |
1437 | ||
1438 | I<Rx>: An extended Remote Procedure Call Protocol. | |
1439 | ||
1440 | I<rgen>: An earlier version of a similar stub generator used for the R RPC | |
1441 | protocol. | |
1442 | ||
1443 | =head1 COPYRIGHT | |
1444 | ||
1445 | IBM Corporation 2000. <http://www.ibm.com/> All Rights Reserved. | |
1446 | ||
1447 | This documentation is covered by the IBM Public License Version 1.0. It | |
1448 | was converted from the original TeX B<rxgen> manual to POD by Russ | |
1449 | Allbery. |