This chapter describes the XDR and RPC languages. Topics include:
XDR language structure, syntax, and examples
RPC language structure, syntax, and examples
![]() | Note: For an overview of the relationship between the RPC and XDR languages and the RPC interface, see Chapter 4, “Introduction to RPC Programming”. For information about rpcgen, see Chapter 5, “Programming with rpcgen”. For information about RPC programming, see Chapter 6, “RPC Programming Guide”. For technical details about XDR, see Chapter 8, “XDR Programming Notes”. For complete specifications of the RPC and XDR protocols, see Appendix A, “RPC Protocol Specification” and Appendix B, “XDR Protocol Specification”. |
This section describes the components of the XDR language.
This specification uses an extended Backus-Naur Form notation for describing the XDR language. This notation has the following characteristics:
These are the special characters:
| ( ) [ ] " *
Terminal symbols are strings of any characters surrounded by double quotes (" ").
Nonterminal symbols are strings of non-special characters.
Alternative items are separated by a vertical bar (|).
Optional items are enclosed in brackets ([]).
Items are grouped by enclosing them in parentheses (( )).
An asterisk (*) following an item means zero or more occurrences of that item.
For example, consider this pattern:
"a" "very" ("," "very")* ["cold" "and"] "rainy" ("day" | "night") |
An infinite number of strings match this pattern; for example:
"a very rainy day" "a very, very rainy day" "a very cold and rainy day" "a very, very, very cold and rainy night" |
This section discusses some lexical features of the XDR language:
Comments begin with /* and end with */. For example:
/* comment */
White space separates items and is otherwise ignored.
An identifier is a letter followed by an optional sequence of letters, digits, or an underscore (_). The case of identifiers is not ignored.
A constant is a sequence of one or more decimal digits, optionally preceded by a minus sign (–).
This section describes XDR language syntax:
declaration: type-specifier identifier | type-specifier identifier "[" value "]" | type-specifier identifier "<" [ value ] ">" | "opaque" identifier "[" value "]" | "opaque" identifier "<" [ value ] ">" | "string" identifier "<" [ value ] ">" | type-specifier "*" identifier | "void" value: constant | identifier type-specifier: [ "unsigned" ] "int" | [ "unsigned" ] "hyper" | "float" | "double" | "bool" | enum-type-spec | struct-type-spec | union-type-spec | identifier enum-type-spec: "enum" enum-body enum-body: "{" ( identifier "=" value ) ( "," identifier "=" value )* "}" struct-type-spec: "struct" struct-body struct-body: "{" ( declaration ";" ) ( declaration ";" )* "}" union-type-spec: "union" union-body union-body: "switch" "(" declaration ")" "{" ( "case" value ":" declaration ";" ) ( "case" value ":" declaration ";" )* [ "default" ":" declaration ";" ] "}" constant-def: "const" identifier "=" constant ";" type-def: "typedef" declaration ";" | "enum" identifier enum-body ";" | "struct" identifier struct-body ";" | "union" identifier union-body ";" definition: type-def | constant-def specification: definition * |
This section provides additional information about XDR language syntax:
The following keywords cannot be used as identifiers:
bool double opaque typedef case enum string union const float struct unsigned default hyper switch void |
Only unsigned constants may be used as size specifications for arrays. If an identifier is used, it must have been declared previously as an unsigned constant in a const definition.
Constant and type identifiers within the scope of a specification are in the same name space and must be declared uniquely within this scope.
Similarly, variable names must be unique within the scope of struct and union declarations. Nested struct and union declarations create new scopes.
The discriminant of a union must be of a type that evaluates to an integer. That is, int, unsigned int, bool, an enumerated type, or any typedef type that evaluates to one of these is legal. Also, the case values must be one of the legal values of the discriminant. Finally, a case value may not be specified more than once within the scope of a union declaration.
The following is a short XDR data description of a “file” that you might use to transfer files from one machine to another:
const MAXUSERNAME = 32; /* max length of a user name */ const MAXFILELEN = 65535; /* max length of a file */ const MAXNAMELEN = 255; /* max length of a filename */ /* Types of files: */ enum filekind { TEXT = 0, /* ascii data */ DATA = 1, /* raw data */ EXEC = 2 /* executable */ }; /* File information, per kind of file: */ union filetype switch (filekind kind) { case TEXT: void; /* no extra information */ case DATA: string creator<MAXNAMELEN>; /* data creator */ case EXEC: string interpreter<MAXNAMELEN>;/*program interpreter*/ }; /* A complete file: */ struct file { string filename<MAXNAMELEN>; /* name of file */ filetype type; /* info about file */ string owner<MAXUSERNAME>; /* owner of file */ opaque data<MAXFILELEN>; /* file data */ }; |
Suppose that a user named jean wants to store a LISP program sillyprog, which contains just the data “(quit).” The file would be encoded as shown in Table 7-1.
Table 7-1. DR Data Encoding Examples
Offset | Hex Bytes | ASCII | Description |
---|---|---|---|
0 | 00 00 00 09 | .... | Length of filename = 9 |
4 | 73 69 6c 6c | sill | Filename characters |
8 | 79 70 72 6 | ypro | More filename characters |
12 | 67 00 00 00 | g... | The last filename character plus 3 zero-bytes of fill |
16 | 00 00 00 02 | .... | File kind is EXEC = 2 |
20 | 00 00 00 04 | .... | Length of interpreter name = 4 |
24 | 6c 69 73 70 | lisp | Interpreter name |
28 | 00 00 00 04 | .... | Length of owner name = 4 |
32 | 6a 65 61 6e | jean | Owner name |
36 | 00 00 00 06 | .... | Length of data = 6 |
40 | 28 71 75 69 | (qui | File data bytes |
44 | 74 29 00 00 | t).. | More data plus 2 zero-bytes of fill |
RPC language is an extension of the XDR language; the sole extension is the addition of the program type.
An RPC language file consists of a series of definitions:
definition-list: definition ";" definition ";" definition-list |
It recognizes six types of definition:
definition: enum-definition struct-definition union-definition typedef-definition const-definition program-definition |
An XDR structure is declared almost exactly like its C counterpart:
struct-definition: "struct" struct-ident "{" declaration-list "}" declaration-list: declaration ";" declaration ";" declaration-list |
For example, the following code is an XDR structure for a two–dimensional coordinate and the C structure into which it is compiled in the output header file:
struct coord { struct coord { int x; --> int x; int y; int y; }; }; typedef struct coord coord; |
The output is identical to the input, except for the added typedef at the end of the output. Using typedef allows you to use coord instead of struct coord when declaring items.
XDR unions are discriminated unions, and they look different from C unions. They are more analogous to Pascal variant records than they are to C unions:
union-definition: "union" union-ident "switch" "(" declaration ")" "{" case-list "}" case-list: "case" value ":" declaration ";" "default" ":" declaration ";" "case" value ":" declaration ";" case-list |
The next example shows a type that might be returned as the result of a read data operation. If no error, return a block of data. Otherwise, return nothing.
union read_result switch (int errno) { case 0: opaque data[1024]; default: void; }; |
This code is compiled into:
struct read_result { int errno; union { char data[1024]; } read_result_u; }; typedef struct read_result read_result; |
Notice that the union component of the output struct has the name as the type name, except for the trailing _u.
XDR enumerations have the same syntax as C enumerations:
enum-definition: "enum" enum-ident "{" enum-value-list "}" enum-value-list: enum-value enum-value "," enum-value-list enum-value: enum-value-ident enum-value-ident "=" value |
The XDR enum and the C enum are compiled into:
enum colortype { enum colortype { RED = 0, RED = 0, GREEN = 1, --> GREEN = 1, BLUE = 2 BLUE = 2 }; }; typedef enum colortype colortype; |
An XDR typedef has the same syntax as a C typedef:
typedef-definition: "typedef" declaration |
The following example defines a fname_type used to declare filename strings that have a maximum length of 255 characters:
typedef string fname_type<255>; --> typedef char *fname_type; |
XDR constants are symbolic constants. They may be used wherever an integer constant is used; for example, in array-size specifications:
const-definition: "const" const-ident "=" integer |
For example, the following defines a constant DOZEN equal to 12:
const DOZEN = 12; --> #define DOZEN 12 |
RPC programs are declared using this syntax:
program-definition: "program" program-ident "{" version-list "}" "=" value version-list: version ";" version ";" version-list version: "version" version-ident "{" procedure-list "}" "=" value procedure-list: procedure ";" procedure ";" procedure-list procedure: type-ident procedure-ident "(" type-ident ")" "=" value |
This example shows the time protocol, revisited:
/* * time.x: Get or set the time. Time is represented as * number of seconds since 0:00, January 1, 1970. */ program TIMEPROG { version TIMEVERS { unsigned int TIMEGET(void) = 1; void TIMESET(unsigned) = 2; } = 1; } = 44; |
This file compiles into #defines in the output header file:
#define TIMEPROG 44 #define TIMEVERS 1 #define TIMEGET 1 #define TIMESET 2 |
There are four kinds of declaration in XDR:
Simple declarations are like simple C declarations:
simple-declaration: type-ident variable-ident |
For example:
colortype color; --> colortype color; |
Fixed-array declarations are like array declarations in C:
fixed-array-declaration: type-ident variable-ident "[" value "]" |
For example:
colortype palette[8]; --> colortype palette[8]; |
Variable-array declarations have no explicit syntax in C, so XDR invents its own using angle brackets:
variable-array-declaration: type-ident variable-ident "<" value ">" type-ident variable-ident "<" ">" |
The maximum size is specified between the angle brackets. The size may be omitted, indicating that the array may be of any size:
int heights<12>; /* at most 12 items */ int widths<>; /* any number of items */ |
Since variable-length arrays have no explicit syntax in C, these declarations are actually compiled into structs. For example, the heights declaration gets compiled into the following struct:
struct { u_int heights_len; /* # of items in array */ int *heights_val; /* pointer to array */ } heights; |
The number of items in the array is stored in the _len component, and the pointer to the array is stored in the _val component. The first part of each component's name is the same as the name of the declared XDR variable.
Pointer declarations are made in XDR exactly as they are in C. You can't really send pointers over the network, but you can use XDR pointers to send recursive data types such as lists and trees. The type is actually called optional data, not pointer, in XDR language.
pointer-declaration: type-ident "*" variable-ident |
For example:
listitem *next; --> listitem *next; |
There are a few exceptions to the rules described in the preceding sections:
C has no built-in boolean type. However, the RPC library does have a boolean type called bool_t that is either TRUE or FALSE. Things declared as type bool in XDR language are compiled into bool_t in the output header file. For example:
bool married; --> bool_t married; |
C has no built-in string type, but instead uses the null-terminated char * convention. In XDR language, strings are declared using the string keyword and are compiled into char *s in the output header file. The number contained in the angle brackets specifies the maximum number of characters allowed in the string (not counting the NULL character). The maximum size may be left off, indicating a string of arbitrary length. For example:
string name<32>; --> char *name; string longname<>; --> char *longname; |
Opaque data
Opaque data is used in RPC and XDR to describe untyped data; that is, just sequences of arbitrary bytes. Opaque data may be declared as either a fixed- or a variable-length array. For example:
opaque diskblock[512]; --> char diskblock[512]; opaque filedata<1024>; --> struct { u_int filedata_len; char *filedata_val; } filedata; |
In a void declaration, the variable is not named. The declaration is just void and nothing else. Void declarations can occur in only two places: union definitions and program definitions (as the argument or the result of a remote procedure).