rrppccggeenn PPrrooggrraammmmiinngg GGuuiiddee 11.. TThhee rrppccggeenn PPrroottooccooll CCoommppiilleerr The details of programming applications to use Remote Proce- dure Calls can be overwhelming. Perhaps most daunting is the writing of the XDR routines necessary to convert proce- dure arguments and results into their network format and vice-versa. Fortunately, _r_p_c_g_e_n_(_1_) exists to help programmers write RPC applications simply and directly. _r_p_c_g_e_n does most of the dirty work, allowing programmers to debug the main fea- tures of their application, instead of requiring them to spend most of their time debugging their network interface code. _r_p_c_g_e_n is a compiler. It accepts a remote program inter- face definition written in a language, called RPC Language, which is similar to C. It produces a C language output which includes stub versions of the client routines, a server skeleton, XDR filter routines for both parameters and results, and a header file that contains common definitions. The client stubs interface with the RPC library and effec- tively hide the network from their callers. The server stub similarly hides the network from the server procedures that are to be invoked by remote clients. _r_p_c_g_e_n's output files can be compiled and linked in the usual way. The developer writes server procedures--in any language that observes Sun calling conventions--and links them with the server skeleton produced by _r_p_c_g_e_n to get an executable server program. To use a remote program, a programmer writes an ordinary main program that makes local procedure calls to the client stubs produced by _r_p_c_g_e_n. Linking this program with _r_p_c_g_e_n's stubs creates an executable program. (At present the main program must be written in C). _r_p_c_g_e_n options can be used to suppress stub generation and to specify the transport to be used by the server stub. Like all compilers, _r_p_c_g_e_n reduces development time that would otherwise be spent coding and debugging low-level rou- tines. All compilers, including _r_p_c_g_e_n, do this at a small cost in efficiency and flexibility. However, many compil- ers allow escape hatches for programmers to mix low-level code with high-level code. _r_p_c_g_e_n is no exception. In speed-critical applications, hand-written routines can be linked with the _r_p_c_g_e_n output without any difficulty. Also, one may proceed by using _r_p_c_g_e_n output as a starting point, and then rewriting it as necessary. (If you need a discus- sion of RPC programming without _r_p_c_g_e_n, see the _R_e_m_o_t_e _P_r_o_- _c_e_d_u_r_e _C_a_l_l _P_r_o_g_r_a_m_m_i_n_g _G_u_i_d_e_)_. - 1 - Page 2 rrppccggeenn Programming Guide 22.. CCoonnvveerrttiinngg LLooccaall PPrroocceedduurreess iinnttoo RReemmoottee PPrroocceedduurreess Assume an application that runs on a single machine, one which we want to convert to run over the network. Here we will demonstrate such a conversion by way of a simple exam- ple--a program that prints a message to the console: _/_* _* _p_r_i_n_t_m_s_g_._c_: _p_r_i_n_t _a _m_e_s_s_a_g_e _o_n _t_h_e _c_o_n_s_o_l_e _*_/ ##iinncclluuddee <> mmaaiinn((aarrggcc,, aarrggvv)) iinntt aarrggcc;; cchhaarr **aarrggvv[[]];; {{ cchhaarr **mmeessssaaggee;; iiff ((aarrggcc << 22)) {{ ffpprriinnttff((ssttddeerrrr,, ""uussaaggee:: %%ss <>\\nn"",, aarrggvv[[00]]));; eexxiitt((11));; }} mmeessssaaggee == aarrggvv[[11]];; iiff ((!!pprriinnttmmeessssaaggee((mmeessssaaggee)))) {{ ffpprriinnttff((ssttddeerrrr,, ""%%ss:: ccoouullddnn''tt pprriinntt yyoouurr mmeessssaaggee\\nn"",, aarrggvv[[00]]));; eexxiitt((11));; }} pprriinnttff((""MMeessssaaggee DDeelliivveerreedd!!\\nn""));; eexxiitt((00));; }} _/_* _* _P_r_i_n_t _a _m_e_s_s_a_g_e _t_o _t_h_e _c_o_n_s_o_l_e_. _* _R_e_t_u_r_n _a _b_o_o_l_e_a_n _i_n_d_i_c_a_t_i_n_g _w_h_e_t_h_e_r _t_h_e _m_e_s_s_a_g_e _w_a_s _a_c_t_u_a_l_l_y _p_r_i_n_t_e_d_. _*_/ pprriinnttmmeessssaaggee((mmssgg)) cchhaarr **mmssgg;; {{ FFIILLEE **ff;; ff == ffooppeenn((""//ddeevv//ccoonnssoollee"",, ""ww""));; iiff ((ff ==== NNUULLLL)) {{ rreettuurrnn ((00));; }} ffpprriinnttff((ff,, ""%%ss\\nn"",, mmssgg));; ffcclloossee((ff));; rreettuurrnn((11));; }} And then, of course: rrppccggeenn Programming Guide Page 3 eexxaammppllee%% cccc pprriinnttmmssgg..cc --oo pprriinnttmmssgg eexxaammppllee%% pprriinnttmmssgg ""HHeelllloo,, tthheerree.."" MMeessssaaggee ddeelliivveerreedd!! eexxaammppllee%% If _p_r_i_n_t_m_e_s_s_a_g_e_(_) was turned into a remote procedure, then it could be called from anywhere in the network. Ide- ally, one would just like to stick a keyword like _r_e_m_o_t_e in front of a procedure to turn it into a remote procedure. Unfortunately, we have to live within the constraints of the C language, since it existed long before RPC did. But even without language support, it's not very difficult to make a procedure remote. In general, it's necessary to figure out what the types are for all procedure inputs and outputs. In this case, we have a procedure _p_r_i_n_t_m_e_s_s_a_g_e_(_) which takes a string as input, and returns an integer as output. Knowing this, we can write a protocol specification in RPC language that describes the remote version of _p_r_i_n_t_m_e_s_s_a_g_e(). Here it is: _/_* _* _m_s_g_._x_: _R_e_m_o_t_e _m_e_s_s_a_g_e _p_r_i_n_t_i_n_g _p_r_o_t_o_c_o_l _*_/ pprrooggrraamm MMEESSSSAAGGEEPPRROOGG {{ vveerrssiioonn MMEESSSSAAGGEEVVEERRSS {{ iinntt PPRRIINNTTMMEESSSSAAGGEE((ssttrriinngg)) == 11;; }} == 11;; }} == 9999;; Remote procedures are part of remote programs, so we actu- ally declared an entire remote program here which con- tains the single procedure _P_R_I_N_T_M_E_S_S_A_G_E. This procedure was declared to be in version 1 of the remote program. No null procedure (procedure 0) is necessary because _r_p_c_g_e_n generates it automatically. Notice that everything is declared with all capital let- ters. This is not required, but is a good convention to follow. Notice also that the argument type is "string" and not "char *". This is because a "char *" in C is ambiguous. Program- mers usually intend it to mean a null-terminated string of characters, but it could also represent a pointer to a single character or a pointer to an array of characters. In RPC language, a null-terminated string is unambigu- ously called a "string". Page 4 rrppccggeenn Programming Guide There are just two more things to write. First, there is the remote procedure itself. Here's the definition of a remote procedure to implement the _P_R_I_N_T_M_E_S_S_A_G_E procedure we declared above: _/_* _* _m_s_g___p_r_o_c_._c_: _i_m_p_l_e_m_e_n_t_a_t_i_o_n _o_f _t_h_e _r_e_m_o_t_e _p_r_o_c_e_d_u_r_e _"_p_r_i_n_t_m_e_s_s_a_g_e_" _*_/ ##iinncclluuddee <> ##iinncclluuddee <> //** _a_l_w_a_y_s _n_e_e_d_e_d **// ##iinncclluuddee ""mmssgg..hh"" //** _n_e_e_d _t_h_i_s _t_o_o_: _m_s_g_._h _w_i_l_l _b_e _g_e_n_e_r_a_t_e_d _b_y _r_p_c_g_e_n **// _/_* _* _R_e_m_o_t_e _v_e_r_s_o_n _o_f _"_p_r_i_n_t_m_e_s_s_a_g_e_" _*_/ iinntt ** pprriinnttmmeessssaaggee__11((mmssgg)) cchhaarr ****mmssgg;; {{ ssttaattiicc iinntt rreessuulltt;; //** _m_u_s_t _b_e _s_t_a_t_i_c_! **// FFIILLEE **ff;; ff == ffooppeenn((""//ddeevv//ccoonnssoollee"",, ""ww""));; iiff ((ff ==== NNUULLLL)) {{ rreessuulltt == 00;; rreettuurrnn ((&&rreessuulltt));; }} ffpprriinnttff((ff,, ""%%ss\\nn"",, **mmssgg));; ffcclloossee((ff));; rreessuulltt == 11;; rreettuurrnn ((&&rreessuulltt));; }} Notice here that the declaration of the remote procedure _p_r_i_n_t_m_e_s_s_a_g_e___1_(_) differs from that of the local procedure _p_r_i_n_t_m_e_s_s_a_g_e_(_) in three ways: 1. It takes a pointer to a string instead of a string itself. This is true of all remote procedures: they always take pointers to their arguments rather than the arguments themselves. 2. It returns a pointer to an integer instead of an integer itself. This is also generally true of remote procedures: they always return a pointer to their results. 3. It has an "_1" appended to its name. In general, all remote procedures called by _r_p_c_g_e_n are named by the following rule: the name in the program definition (here _P_R_I_N_T_M_E_S_S_A_G_E) is converted to all lower-case letters, an underbar ("_") is appended to it, and rrppccggeenn Programming Guide Page 5 finally the version number (here 1) is appended. The last thing to do is declare the main client program that will call the remote procedure. Here it is: Page 6 rrppccggeenn Programming Guide _/_* _* _r_p_r_i_n_t_m_s_g_._c_: _r_e_m_o_t_e _v_e_r_s_i_o_n _o_f _"_p_r_i_n_t_m_s_g_._c_" _*_/ ##iinncclluuddee <> ##iinncclluuddee <> //** _a_l_w_a_y_s _n_e_e_d_e_d **// ##iinncclluuddee ""mmssgg..hh"" //** _n_e_e_d _t_h_i_s _t_o_o_: _m_s_g_._h _w_i_l_l _b_e _g_e_n_e_r_a_t_e_d _b_y _r_p_c_g_e_n **// mmaaiinn((aarrggcc,, aarrggvv)) iinntt aarrggcc;; cchhaarr **aarrggvv[[]];; {{ CCLLIIEENNTT **ccll;; iinntt **rreessuulltt;; cchhaarr **sseerrvveerr;; cchhaarr **mmeessssaaggee;; iiff ((aarrggcc << 33)) {{ ffpprriinnttff((ssttddeerrrr,, ""uussaaggee:: %%ss hhoosstt mmeessssaaggee\\nn"",, aarrggvv[[00]]));; eexxiitt((11));; }} _/_* _* _S_a_v_e _v_a_l_u_e_s _o_f _c_o_m_m_a_n_d _l_i_n_e _a_r_g_u_m_e_n_t_s _*_/ sseerrvveerr == aarrggvv[[11]];; mmeessssaaggee == aarrggvv[[22]];; _/_* _* _C_r_e_a_t_e _c_l_i_e_n_t _"_h_a_n_d_l_e_" _u_s_e_d _f_o_r _c_a_l_l_i_n_g _M_E_S_S_A_G_E_P_R_O_G _o_n _t_h_e _* _s_e_r_v_e_r _d_e_s_i_g_n_a_t_e_d _o_n _t_h_e _c_o_m_m_a_n_d _l_i_n_e_. _W_e _t_e_l_l _t_h_e _R_P_C _p_a_c_k_a_g_e _* _t_o _u_s_e _t_h_e _"_t_c_p_" _p_r_o_t_o_c_o_l _w_h_e_n _c_o_n_t_a_c_t_i_n_g _t_h_e _s_e_r_v_e_r_. _*_/ ccll == ccllnntt__ccrreeaattee((sseerrvveerr,, MMEESSSSAAGGEEPPRROOGG,, MMEESSSSAAGGEEVVEERRSS,, ""ttccpp""));; iiff ((ccll ==== NNUULLLL)) {{ _/_* _* _C_o_u_l_d_n_'_t _e_s_t_a_b_l_i_s_h _c_o_n_n_e_c_t_i_o_n _w_i_t_h _s_e_r_v_e_r_. _* _P_r_i_n_t _e_r_r_o_r _m_e_s_s_a_g_e _a_n_d _d_i_e_. _*_/ ccllnntt__ppccrreeaatteeeerrrroorr((sseerrvveerr));; eexxiitt((11));; }} _/_* _* _C_a_l_l _t_h_e _r_e_m_o_t_e _p_r_o_c_e_d_u_r_e _"_p_r_i_n_t_m_e_s_s_a_g_e_" _o_n _t_h_e _s_e_r_v_e_r _*_/ rreessuulltt == pprriinnttmmeessssaaggee__11((&&mmeessssaaggee,, ccll));; iiff ((rreessuulltt ==== NNUULLLL)) {{ _/_* _* _A_n _e_r_r_o_r _o_c_c_u_r_r_e_d _w_h_i_l_e _c_a_l_l_i_n_g _t_h_e _s_e_r_v_e_r_. _* _P_r_i_n_t _e_r_r_o_r _m_e_s_s_a_g_e _a_n_d _d_i_e_. _*_/ ccllnntt__ppeerrrroorr((ccll,, sseerrvveerr));; eexxiitt((11));; }} rrppccggeenn Programming Guide Page 7 _/_* _* _O_k_a_y_, _w_e _s_u_c_c_e_s_s_f_u_l_l_y _c_a_l_l_e_d _t_h_e _r_e_m_o_t_e _p_r_o_c_e_d_u_r_e_. _*_/ iiff ((**rreessuulltt ==== 00)) {{ _/_* _* _S_e_r_v_e_r _w_a_s _u_n_a_b_l_e _t_o _p_r_i_n_t _o_u_r _m_e_s_s_a_g_e_. _* _P_r_i_n_t _e_r_r_o_r _m_e_s_s_a_g_e _a_n_d _d_i_e_. _*_/ ffpprriinnttff((ssttddeerrrr,, ""%%ss:: %%ss ccoouullddnn''tt pprriinntt yyoouurr mmeessssaaggee\\nn"",, aarrggvv[[00]],, sseerrvveerr));; eexxiitt((11));; }} _/_* _* _T_h_e _m_e_s_s_a_g_e _g_o_t _p_r_i_n_t_e_d _o_n _t_h_e _s_e_r_v_e_r_'_s _c_o_n_s_o_l_e _*_/ pprriinnttff((""MMeessssaaggee ddeelliivveerreedd ttoo %%ss!!\\nn"",, sseerrvveerr));; }} There are two things to note here: 1. First a client "handle" is created using the RPC library routine _c_l_n_t___c_r_e_a_t_e(). This client handle will be passed to the stub routines which call the remote procedure. 2. The remote procedure _p_r_i_n_t_m_e_s_s_a_g_e___1_(_) is called exactly the same way as it is declared in _m_s_g___p_r_o_c_._c except for the inserted client handle as the first argument. Here's how to put all of the pieces together: eexxaammppllee%% rrppccggeenn mmssgg..xx eexxaammppllee%% cccc rrpprriinnttmmssgg..cc mmssgg__ccllnntt..cc --oo rrpprriinnttmmssgg eexxaammppllee%% cccc mmssgg__pprroocc..cc mmssgg__ssvvcc..cc --oo mmssgg__sseerrvveerr Two programs were compiled here: the client program _r_p_r_i_n_t_m_s_g and the server program _m_s_g___s_e_r_v_e_r. Before doing this though, _r_p_c_g_e_n was used to fill in the missing pieces. Here is what _r_p_c_g_e_n did with the input file _m_s_g_._x: 1. It created a header file called _m_s_g_._h that contained _#_d_e_f_i_n_e's for _M_E_S_S_A_G_E_P_R_O_G, _M_E_S_S_A_G_E_V_E_R_S and _P_R_I_N_T_M_E_S_S_A_G_E for use in the other modules. 2. It created client "stub" routines in the _m_s_g___c_l_n_t_._c file. In this case there is only one, the _p_r_i_n_t_m_e_s_- _s_a_g_e___1_(_) that was referred to from the _p_r_i_n_t_m_s_g client program. The name of the output file for client stub routines is always formed in this way: if the name of the input file is _F_O_O_._x, the client stubs output file is called _F_O_O___c_l_n_t_._c. Page 8 rrppccggeenn Programming Guide 3. It created the server program which calls _p_r_i_n_t_m_e_s_- _s_a_g_e___1_(_) in _m_s_g___p_r_o_c_._c. This server program is named _m_s_g___s_v_c_._c. The rule for naming the server output file is similar to the previous one: for an input file called _F_O_O_._x, the output server file is named _F_O_O___s_v_c_._c. Now we're ready to have some fun. First, copy the server to a remote machine and run it. For this example, the machine is called "moon". Server processes are run in the background, because they never exit. mmoooonn%% mmssgg__sseerrvveerr && Then on our local machine ("sun") we can print a message on "moon"s console. ssuunn%% pprriinnttmmssgg mmoooonn ""HHeelllloo,, mmoooonn.."" The message will get printed to "moon"s console. You can print a message on anybody's console (including your own) with this program if you are able to copy the server to their machine and run it. 33.. GGeenneerraattiinngg XXDDRR RRoouuttiinneess The previous example only demonstrated the automatic gen- eration of client and server RPC code. _r_p_c_g_e_n may also be used to generate XDR routines, that is, the routines necessary to convert local data structures into network format and vice-versa. This example presents a complete RPC service--a remote directory listing service, which uses _r_p_c_- _g_e_n not only to generate stub routines, but also to gen- erate the XDR routines. Here is the protocol description file: rrppccggeenn Programming Guide Page 9 _/_* _* _d_i_r_._x_: _R_e_m_o_t_e _d_i_r_e_c_t_o_r_y _l_i_s_t_i_n_g _p_r_o_t_o_c_o_l _*_/ ccoonnsstt MMAAXXNNAAMMEELLEENN == 225555;; //** _m_a_x_i_m_u_m _l_e_n_g_t_h _o_f _a _d_i_r_e_c_t_o_r_y _e_n_t_r_y **// ttyyppeeddeeff ssttrriinngg nnaammeettyyppee<>;; //** _a _d_i_r_e_c_t_o_r_y _e_n_t_r_y **// ttyyppeeddeeff ssttrruucctt nnaammeennooddee **nnaammeelliisstt;; //** _a _l_i_n_k _i_n _t_h_e _l_i_s_t_i_n_g **// _/_* _* _A _n_o_d_e _i_n _t_h_e _d_i_r_e_c_t_o_r_y _l_i_s_t_i_n_g _*_/ ssttrruucctt nnaammeennooddee {{ nnaammeettyyppee nnaammee;; //** _n_a_m_e _o_f _d_i_r_e_c_t_o_r_y _e_n_t_r_y **// nnaammeelliisstt nneexxtt;; //** _n_e_x_t _e_n_t_r_y **// }};; _/_* _* _T_h_e _r_e_s_u_l_t _o_f _a _R_E_A_D_D_I_R _o_p_e_r_a_t_i_o_n_. _*_/ uunniioonn rreeaaddddiirr__rreess sswwiittcchh ((iinntt eerrrrnnoo)) {{ ccaassee 00:: nnaammeelliisstt lliisstt;; //** _n_o _e_r_r_o_r_: _r_e_t_u_r_n _d_i_r_e_c_t_o_r_y _l_i_s_t_i_n_g **// ddeeffaauulltt:: vvooiidd;; //** _e_r_r_o_r _o_c_c_u_r_r_e_d_: _n_o_t_h_i_n_g _e_l_s_e _t_o _r_e_t_u_r_n **// }};; _/_* _* _T_h_e _d_i_r_e_c_t_o_r_y _p_r_o_g_r_a_m _d_e_f_i_n_i_t_i_o_n _*_/ pprrooggrraamm DDIIRRPPRROOGG {{ vveerrssiioonn DDIIRRVVEERRSS {{ rreeaaddddiirr__rreess RREEAADDDDIIRR((nnaammeettyyppee)) == 11;; }} == 11;; }} == 7766;; NNoottee:: _T_y_p_e_s _(_l_i_k_e _r_e_a_d_d_i_r___r_e_s _i_n _t_h_e _e_x_a_m_p_l_e _a_b_o_v_e_) _c_a_n _b_e _d_e_f_i_n_e_d _u_s_i_n_g _t_h_e _"_s_t_r_u_c_t_"_, _"_u_n_i_o_n_" _a_n_d _"_e_n_u_m_" _k_e_y_w_o_r_d_s_, _b_u_t _t_h_o_s_e _k_e_y_w_o_r_d_s _s_h_o_u_l_d _n_o_t _b_e _u_s_e_d _i_n _s_u_b_s_e_q_u_e_n_t _d_e_c_l_a_r_a_t_i_o_n_s _o_f _v_a_r_i_a_b_l_e_s _o_f _t_h_o_s_e _t_y_p_e_s_. _F_o_r _e_x_a_m_p_l_e_, _i_f _y_o_u _d_e_f_i_n_e _a _u_n_i_o_n _"_f_o_o_"_, _y_o_u _s_h_o_u_l_d _d_e_c_l_a_r_e _u_s_i_n_g _o_n_l_y _"_f_o_o_" _a_n_d _n_o_t _"_u_n_i_o_n _f_o_o_"_. _I_n _f_a_c_t_, _r_p_c_g_e_n _c_o_m_p_i_l_e_s _R_P_C _u_n_i_o_n_s _i_n_t_o _C _s_t_r_u_c_t_u_r_e_s _a_n_d _i_t _i_s _a_n _e_r_r_o_r _t_o _d_e_c_l_a_r_e _t_h_e_m _u_s_i_n_g _t_h_e _"_u_n_i_o_n_" _k_e_y_w_o_r_d_. Running _r_p_c_g_e_n on _d_i_r_._x creates four output files. Three are the same as before: header file, client stub routines and server skeleton. The fourth are the XDR routines neces- sary for converting the data types we declared into XDR for- mat and vice-versa. These are output in the file _d_i_r___x_d_r_._c. Page 10 rrppccggeenn Programming Guide Here is the implementation of the _R_E_A_D_D_I_R procedure. rrppccggeenn Programming Guide Page 11 _/_* _* _d_i_r___p_r_o_c_._c_: _r_e_m_o_t_e _r_e_a_d_d_i_r _i_m_p_l_e_m_e_n_t_a_t_i_o_n _*_/ ##iinncclluuddee <> ##iinncclluuddee <> ##iinncclluuddee ""ddiirr..hh"" eexxtteerrnn iinntt eerrrrnnoo;; eexxtteerrnn cchhaarr **mmaalllloocc(());; eexxtteerrnn cchhaarr **ssttrrdduupp(());; rreeaaddddiirr__rreess ** rreeaaddddiirr__11((ddiirrnnaammee)) nnaammeettyyppee **ddiirrnnaammee;; {{ DDIIRR **ddiirrpp;; ssttrruucctt ddiirreecctt **dd;; nnaammeelliisstt nnll;; nnaammeelliisstt **nnllpp;; ssttaattiicc rreeaaddddiirr__rreess rreess;; //** _m_u_s_t _b_e _s_t_a_t_i_c!! **// _/_* _* _O_p_e_n _d_i_r_e_c_t_o_r_y _*_/ ddiirrpp == ooppeennddiirr((**ddiirrnnaammee));; iiff ((ddiirrpp ==== NNUULLLL)) {{ rreess..eerrrrnnoo == eerrrrnnoo;; rreettuurrnn ((&&rreess));; }} _/_* _* _F_r_e_e _p_r_e_v_i_o_u_s _r_e_s_u_l_t _*_/ xxddrr__ffrreeee((xxddrr__rreeaaddddiirr__rreess,, &&rreess));; _/_* _* _C_o_l_l_e_c_t _d_i_r_e_c_t_o_r_y _e_n_t_r_i_e_s_. _* _M_e_m_o_r_y _a_l_l_o_c_a_t_e_d _h_e_r_e _w_i_l_l _b_e _f_r_e_e_d _b_y _x_d_r___f_r_e_e _* _n_e_x_t _t_i_m_e _r_e_a_d_d_i_r___1 _i_s _c_a_l_l_e_d _*_/ nnllpp == &&rreess..rreeaaddddiirr__rreess__uu..lliisstt;; wwhhiillee ((dd == rreeaaddddiirr((ddiirrpp)))) {{ nnll == **nnllpp == ((nnaammeennooddee **)) mmaalllloocc((ssiizzeeooff((nnaammeennooddee))));; nnll-->>nnaammee == ssttrrdduupp((dd-->>dd__nnaammee));; nnllpp == &&nnll-->>nneexxtt;; }} **nnllpp == NNUULLLL;; _/_* _* _R_e_t_u_r_n _t_h_e _r_e_s_u_l_t _*_/ rreess..eerrrrnnoo == 00;; cclloosseeddiirr((ddiirrpp));; rreettuurrnn ((&&rreess));; Page 12 rrppccggeenn Programming Guide }} Finally, there is the client side program to call the server: rrppccggeenn Programming Guide Page 13 _/_* _* _r_l_s_._c_: _R_e_m_o_t_e _d_i_r_e_c_t_o_r_y _l_i_s_t_i_n_g _c_l_i_e_n_t _*_/ ##iinncclluuddee <> ##iinncclluuddee <> //** _a_l_w_a_y_s _n_e_e_d _t_h_i_s **// ##iinncclluuddee ""ddiirr..hh"" //** _w_i_l_l _b_e _g_e_n_e_r_a_t_e_d _b_y _r_p_c_g_e_n _*_/ _e_x_t_e_r_n _i_n_t _e_r_r_n_o_; _m_a_i_n_(_a_r_g_c_, _a_r_g_v_) _i_n_t _a_r_g_c_; _c_h_a_r _*_a_r_g_v_[_]_; _{ _C_L_I_E_N_T _*_c_l_; _c_h_a_r _*_s_e_r_v_e_r_; _c_h_a_r _*_d_i_r_; _r_e_a_d_d_i_r___r_e_s _*_r_e_s_u_l_t_; _n_a_m_e_l_i_s_t _n_l_; _i_f _(_a_r_g_c _!_= _3_) _{ _f_p_r_i_n_t_f_(_s_t_d_e_r_r_, _"_u_s_a_g_e_: _%_s _h_o_s_t _d_i_r_e_c_t_o_r_y_\_n_"_, _a_r_g_v_[_0_]_)_; _e_x_i_t_(_1_)_; _} _/_* _* _R_e_m_e_m_b_e_r _w_h_a_t _o_u_r _c_o_m_m_a_n_d _l_i_n_e _a_r_g_u_m_e_n_t_s _r_e_f_e_r _t_o _*_/ sseerrvveerr == aarrggvv[[11]];; ddiirr == aarrggvv[[22]];; _/_* _* _C_r_e_a_t_e _c_l_i_e_n_t _"_h_a_n_d_l_e_" _u_s_e_d _f_o_r _c_a_l_l_i_n_g _M_E_S_S_A_G_E_P_R_O_G _o_n _t_h_e _* _s_e_r_v_e_r _d_e_s_i_g_n_a_t_e_d _o_n _t_h_e _c_o_m_m_a_n_d _l_i_n_e_. _W_e _t_e_l_l _t_h_e _R_P_C _p_a_c_k_a_g_e _* _t_o _u_s_e _t_h_e _"_t_c_p_" _p_r_o_t_o_c_o_l _w_h_e_n _c_o_n_t_a_c_t_i_n_g _t_h_e _s_e_r_v_e_r_. _*_/ ccll == ccllnntt__ccrreeaattee((sseerrvveerr,, DDIIRRPPRROOGG,, DDIIRRVVEERRSS,, ""ttccpp""));; iiff ((ccll ==== NNUULLLL)) {{ _/_* _* _C_o_u_l_d_n_'_t _e_s_t_a_b_l_i_s_h _c_o_n_n_e_c_t_i_o_n _w_i_t_h _s_e_r_v_e_r_. _* _P_r_i_n_t _e_r_r_o_r _m_e_s_s_a_g_e _a_n_d _d_i_e_. _*_/ ccllnntt__ppccrreeaatteeeerrrroorr((sseerrvveerr));; eexxiitt((11));; }} _/_* _* _C_a_l_l _t_h_e _r_e_m_o_t_e _p_r_o_c_e_d_u_r_e _r_e_a_d_d_i_r _o_n _t_h_e _s_e_r_v_e_r _*_/ rreessuulltt == rreeaaddddiirr__11((&&ddiirr,, ccll));; iiff ((rreessuulltt ==== NNUULLLL)) {{ _/_* _* _A_n _e_r_r_o_r _o_c_c_u_r_r_e_d _w_h_i_l_e _c_a_l_l_i_n_g _t_h_e _s_e_r_v_e_r_. Page 14 rrppccggeenn Programming Guide _* _P_r_i_n_t _e_r_r_o_r _m_e_s_s_a_g_e _a_n_d _d_i_e_. _*_/ ccllnntt__ppeerrrroorr((ccll,, sseerrvveerr));; eexxiitt((11));; }} _/_* _* _O_k_a_y_, _w_e _s_u_c_c_e_s_s_f_u_l_l_y _c_a_l_l_e_d _t_h_e _r_e_m_o_t_e _p_r_o_c_e_d_u_r_e_. _*_/ iiff ((rreessuulltt-->>eerrrrnnoo !!== 00)) {{ _/_* _* _A _r_e_m_o_t_e _s_y_s_t_e_m _e_r_r_o_r _o_c_c_u_r_r_e_d_. _* _P_r_i_n_t _e_r_r_o_r _m_e_s_s_a_g_e _a_n_d _d_i_e_. _*_/ eerrrrnnoo == rreessuulltt-->>eerrrrnnoo;; ppeerrrroorr((ddiirr));; eexxiitt((11));; }} _/_* _* _S_u_c_c_e_s_s_f_u_l_l_y _g_o_t _a _d_i_r_e_c_t_o_r_y _l_i_s_t_i_n_g_. _* _P_r_i_n_t _i_t _o_u_t_. _*_/ ffoorr ((nnll == rreessuulltt-->>rreeaaddddiirr__rreess__uu..lliisstt;; nnll !!== NNUULLLL;; nnll == nnll-->>nneexxtt)) {{ pprriinnttff((""%%ss\\nn"",, nnll-->>nnaammee));; }} eexxiitt((00));; }} Compile everything, and run. ssuunn%% rrppccggeenn ddiirr..xx ssuunn%% cccc rrllss..cc ddiirr__ccllnntt..cc ddiirr__xxddrr..cc --oo rrllss ssuunn%% cccc ddiirr__ssvvcc..cc ddiirr__pprroocc..cc ddiirr__xxddrr..cc --oo ddiirr__ssvvcc ssuunn%% ddiirr__ssvvcc && mmoooonn%% rrllss ssuunn //uussrr//ppuubb .. .... aasscciiii eeqqnncchhaarr ggrreeeekk kkbbdd mmaarrgg88 ttaabbccllrr ttaabbss ttaabbss44 mmoooonn%% A final note about _r_p_c_g_e_n: The client program and the server procedure can be tested together as a single program by rrppccggeenn Programming Guide Page 15 simply linking them with each other rather than with the client and server stubs. The procedure calls will be exe- cuted as ordinary local procedure calls and the program can be debugged with a local debugger such as _d_b_x. When the program is working, the client program can be linked to the client stub produced by _r_p_c_g_e_n and the server procedures can be linked to the server stub produced by _r_p_c_g_e_n. _N_O_T_E:: _I_f _y_o_u _d_o _t_h_i_s_, _y_o_u _m_a_y _w_a_n_t _t_o _c_o_m_m_e_n_t _o_u_t _c_a_l_l_s _t_o _R_P_C _l_i_b_r_a_r_y _r_o_u_t_i_n_e_s_, _a_n_d _h_a_v_e _c_l_i_e_n_t_-_s_i_d_e _r_o_u_t_i_n_e_s _c_a_l_l _s_e_r_v_e_r _r_o_u_t_i_n_e_s _d_i_r_e_c_t_l_y_. 44.. TThhee CC--PPrreepprroocceessssoorr The C-preprocessor is run on all input files before they are compiled, so all the preprocessor directives are legal within a ".x" file. Four symbols may be defined, depending upon which output file is getting generated. The symbols are: +--------------------------------------+ |_S_y_m_b_o_l _U_s_a_g_e | +--------------------------------------+ |RPC_HDR for header-file output | |RPC_XDR for XDR routine output | |RPC_SVC for server-skeleton output | |RPC_CLNT for client stub output | +--------------------------------------+ Also, _r_p_c_g_e_n does a little preprocessing of its own. Any line that begins with a percent sign is passed directly into the output file, without any interpretation of the line. Here is a simple example that demonstrates the pre- processing features. Page 16 rrppccggeenn Programming Guide _/_* _* _t_i_m_e_._x_: _R_e_m_o_t_e _t_i_m_e _p_r_o_t_o_c_o_l _*_/ pprrooggrraamm TTIIMMEEPPRROOGG {{ vveerrssiioonn TTIIMMEEVVEERRSS {{ uunnssiiggnneedd iinntt TTIIMMEEGGEETT((vvooiidd)) == 11;; }} == 11;; }} == 4444;; ##iiffddeeff RRPPCC__SSVVCC %%iinntt ** %%ttiimmeeggeett__11(()) %%{{ %% ssttaattiicc iinntt tthheettiimmee;; %% %% tthheettiimmee == ttiimmee((00));; %% rreettuurrnn ((&&tthheettiimmee));; %%}} ##eennddiiff The '%' feature is not generally recommended, as there is no guarantee that the compiler will stick the output where you intended. 55.. rrppccggeenn PPrrooggrraammmmiinngg NNootteess 55..11.. TTiimmeeoouutt CChhaannggeess RPC sets a default timeout of 25 seconds for RPC calls when _c_l_n_t___c_r_e_a_t_e_(_) is used. This timeout may be changed using _c_l_n_t___c_o_n_t_r_o_l_(_) Here is a small code fragment to demonstrate use of _c_l_n_t___c_o_n_t_r_o_l(): struct timeval tv; CLIENT *cl; cl = clnt_create("somehost", SOMEPROG, SOMEVERS, "tcp"); if (cl == NULL) { exit(1); } tv.tv_sec = 60; /* _c_h_a_n_g_e _t_i_m_e_o_u_t _t_o _1 _m_i_n_u_t_e */ tv.tv_usec = 0; clnt_control(cl, CLSET_TIMEOUT, &tv); 55..22.. HHaannddlliinngg BBrrooaaddccaasstt oonn tthhee SSeerrvveerr SSiiddee When a procedure is known to be called via broadcast RPC, it is usually wise for the server to not reply unless it can provide some useful information to the client. This pre- vents the network from getting flooded by useless replies. To prevent the server from replying, a remote procedure can return NULL as its result, and the server code generated by rrppccggeenn Programming Guide Page 17 _r_p_c_g_e_n will detect this and not send out a reply. Here is an example of a procedure that replies only if it thinks it is an NFS server: void * reply_if_nfsserver() { char notnull; /* _j_u_s_t _h_e_r_e _s_o _w_e _c_a_n _u_s_e _i_t_s _a_d_d_r_e_s_s */ if (access("/etc/exports", F_OK) < 0) { return (NULL); /* _p_r_e_v_e_n_t _R_P_C _f_r_o_m _r_e_p_l_y_i_n_g */ } _/_* _* _r_e_t_u_r_n _n_o_n_-_n_u_l_l _p_o_i_n_t_e_r _s_o _R_P_C _w_i_l_l _s_e_n_d _o_u_t _a _r_e_p_l_y _*_/ return ((void *)¬null); } Note that if procedure returns type "void *", they must return a non-NULL pointer if they want RPC to reply for them. 55..33.. OOtthheerr IInnffoorrmmaattiioonn PPaasssseedd ttoo SSeerrvveerr PPrroocceedduurreess Server procedures will often want to know more about an RPC call than just its arguments. For example, getting authen- tication information is important to procedures that want to implement some level of security. This extra information is actually supplied to the server procedure as a second argu- ment. Here is an example to demonstrate its use. What we've done here is rewrite the previous _p_r_i_n_t_m_e_s_s_a_g_e___1_(_) procedure to only allow root users to print a message to the console. int * printmessage_1(msg, rq) char **msg; struct svc_req *rq; { static in result; /* _M_u_s_t _b_e _s_t_a_t_i_c */ FILE *f; struct suthunix_parms *aup; aup = (struct authunix_parms *)rq->rq_clntcred; if (aup->aup_uid != 0) { result = 0; return (&result); } _/_* _* _S_a_m_e _c_o_d_e _a_s _b_e_f_o_r_e_. _*_/ } Page 18 rrppccggeenn Programming Guide 66.. RRPPCC LLaanngguuaaggee RPC language is an extension of XDR language. The sole extension is the addition of the _p_r_o_g_r_a_m type. For a com- plete description of the XDR language syntax, see the _E_x_t_e_r_- _n_a_l _D_a_t_a _R_e_p_r_e_s_e_n_t_a_t_i_o_n _S_t_a_n_d_a_r_d_: _P_r_o_t_o_c_o_l _S_p_e_c_i_f_i_c_a_t_i_o_n chapter. For a description of the RPC extensions to the XDR language, see the _R_e_m_o_t_e _P_r_o_c_e_d_u_r_e _C_a_l_l_s_: _P_r_o_t_o_c_o_l _S_p_e_c_i_f_i_- _c_a_t_i_o_n chapter. However, XDR language is so close to C that if you know C, you know most of it already. We describe here the syntax of the RPC language, showing a few examples along the way. We also show how the various RPC and XDR type definitions get compiled into C type definitions in the output header file. 66..11.. DDeeffiinniittiioonnss An RPC language file consists of a series of definitions. ddeeffiinniittiioonn--lliisstt:: ddeeffiinniittiioonn "";;"" ddeeffiinniittiioonn "";;"" ddeeffiinniittiioonn--lliisstt It recognizes five types of definitions. ddeeffiinniittiioonn:: eennuumm--ddeeffiinniittiioonn ssttrruucctt--ddeeffiinniittiioonn uunniioonn--ddeeffiinniittiioonn ttyyppeeddeeff--ddeeffiinniittiioonn ccoonnsstt--ddeeffiinniittiioonn pprrooggrraamm--ddeeffiinniittiioonn 66..22.. SSttrruuccttuurreess An XDR struct is declared almost exactly like its C coun- terpart. It looks like the following: ssttrruucctt--ddeeffiinniittiioonn:: ""ssttrruucctt"" ssttrruucctt--iiddeenntt ""{{"" ddeeccllaarraattiioonn--lliisstt ""}}"" ddeeccllaarraattiioonn--lliisstt:: ddeeccllaarraattiioonn "";;"" ddeeccllaarraattiioonn "";;"" ddeeccllaarraattiioonn--lliisstt As an example, here is an XDR structure to a two-dimensional coordinate, and the C structure that it gets compiled into in the output header file. rrppccggeenn Programming Guide Page 19 ssttrruucctt ccoooorrdd {{ ssttrruucctt ccoooorrdd {{ iinntt xx;; ---->> iinntt xx;; iinntt yy;; iinntt yy;; }};; }};; ttyyppeeddeeff ssttrruucctt ccoooorrdd ccoooorrdd;; The output is identical to the input, except for the added _t_y_p_e_d_e_f at the end of the output. This allows one to use "coord" instead of "struct coord" when declaring items. 66..33.. UUnniioonnss XDR unions are discriminated unions, and look quite differ- ent from C unions. They are more analogous to Pascal vari- ant records than they are to C unions. uunniioonn--ddeeffiinniittiioonn:: ""uunniioonn"" uunniioonn--iiddeenntt ""sswwiittcchh"" ""(("" ddeeccllaarraattiioonn ""))"" ""{{"" ccaassee--lliisstt ""}}"" ccaassee--lliisstt:: ""ccaassee"" vvaalluuee ""::"" ddeeccllaarraattiioonn "";;"" ""ddeeffaauulltt"" ""::"" ddeeccllaarraattiioonn "";;"" ""ccaassee"" vvaalluuee ""::"" ddeeccllaarraattiioonn "";;"" ccaassee--lliisstt Here is an example of a type that might be returned as the result of a "read data" operation. If there is no error, return a block of data. Otherwise, don't return anything. uunniioonn rreeaadd__rreessuulltt sswwiittcchh ((iinntt eerrrrnnoo)) {{ ccaassee 00:: ooppaaqquuee ddaattaa[[11002244]];; ddeeffaauulltt:: vvooiidd;; }};; It gets compiled into the following: ssttrruucctt rreeaadd__rreessuulltt {{ iinntt eerrrrnnoo;; uunniioonn {{ cchhaarr ddaattaa[[11002244]];; }} rreeaadd__rreessuulltt__uu;; }};; ttyyppeeddeeff ssttrruucctt rreeaadd__rreessuulltt rreeaadd__rreessuulltt;; Notice that the union component of the output struct has the name as the type name, except for the trailing "_u". 66..44.. EEnnuummeerraattiioonnss XDR enumerations have the same syntax as C enumerations. Page 20 rrppccggeenn Programming Guide eennuumm--ddeeffiinniittiioonn:: ""eennuumm"" eennuumm--iiddeenntt ""{{"" eennuumm--vvaalluuee--lliisstt ""}}"" eennuumm--vvaalluuee--lliisstt:: eennuumm--vvaalluuee eennuumm--vvaalluuee "",,"" eennuumm--vvaalluuee--lliisstt eennuumm--vvaalluuee:: eennuumm--vvaalluuee--iiddeenntt eennuumm--vvaalluuee--iiddeenntt ""=="" vvaalluuee Here is a short example of an XDR enum, and the C enum that it gets compiled into. eennuumm ccoolloorrttyyppee {{ eennuumm ccoolloorrttyyppee {{ RREEDD == 00,, RREEDD == 00,, GGRREEEENN == 11,, ---->> GGRREEEENN == 11,, BBLLUUEE == 22 BBLLUUEE == 22,, }};; }};; ttyyppeeddeeff eennuumm ccoolloorrttyyppee ccoolloorrttyyppee;; 66..55.. TTyyppeeddeeff XDR typedefs have the same syntax as C typedefs. ttyyppeeddeeff--ddeeffiinniittiioonn:: ""ttyyppeeddeeff"" ddeeccllaarraattiioonn Here is an example that defines a _f_n_a_m_e___t_y_p_e used for declaring file name strings that have a maximum length of 255 characters. ttyyppeeddeeff ssttrriinngg ffnnaammee__ttyyppee<<225555>>;; ---->> ttyyppeeddeeff cchhaarr **ffnnaammee__ttyyppee;; 66..66.. CCoonnssttaannttss XDR constants symbolic constants that may be used wher- ever a integer constant is used, for example, in array size specifications. ccoonnsstt--ddeeffiinniittiioonn:: ""ccoonnsstt"" ccoonnsstt--iiddeenntt ""=="" iinntteeggeerr For example, the following defines a constant _D_O_Z_E_N equal to 12. ccoonnsstt DDOOZZEENN == 1122;; ---->> ##ddeeffiinnee DDOOZZEENN 1122 rrppccggeenn Programming Guide Page 21 66..77.. PPrrooggrraammss RPC programs are declared using the following syntax: pprrooggrraamm--ddeeffiinniittiioonn:: ""pprrooggrraamm"" pprrooggrraamm--iiddeenntt ""{{"" vveerrssiioonn--lliisstt ""}}"" ""=="" vvaalluuee vveerrssiioonn--lliisstt:: vveerrssiioonn "";;"" vveerrssiioonn "";;"" vveerrssiioonn--lliisstt vveerrssiioonn:: ""vveerrssiioonn"" vveerrssiioonn--iiddeenntt ""{{"" pprroocceedduurree--lliisstt ""}}"" ""=="" vvaalluuee pprroocceedduurree--lliisstt:: pprroocceedduurree "";;"" pprroocceedduurree "";;"" pprroocceedduurree--lliisstt pprroocceedduurree:: ttyyppee--iiddeenntt pprroocceedduurree--iiddeenntt ""(("" ttyyppee--iiddeenntt ""))"" ""=="" vvaalluuee For example, here is the time protocol, revisited: _/_* _* _t_i_m_e_._x_: _G_e_t _o_r _s_e_t _t_h_e _t_i_m_e_. _T_i_m_e _i_s _r_e_p_r_e_s_e_n_t_e_d _a_s _n_u_m_b_e_r _o_f _s_e_c_o_n_d_s _* _s_i_n_c_e _0_:_0_0_, _J_a_n_u_a_r_y _1_, _1_9_7_0_. _*_/ pprrooggrraamm TTIIMMEEPPRROOGG {{ vveerrssiioonn TTIIMMEEVVEERRSS {{ uunnssiiggnneedd iinntt TTIIMMEEGGEETT((vvooiidd)) == 11;; vvooiidd TTIIMMEESSEETT((uunnssiiggnneedd)) == 22;; }} == 11;; }} == 4444;; This file compiles into #defines in the output header file: ##ddeeffiinnee TTIIMMEEPPRROOGG 4444 ##ddeeffiinnee TTIIMMEEVVEERRSS 11 ##ddeeffiinnee TTIIMMEEGGEETT 11 ##ddeeffiinnee TTIIMMEESSEETT 22 66..88.. DDeeccllaarraattiioonnss In XDR, there are only four kinds of declarations. Page 22 rrppccggeenn Programming Guide ddeeccllaarraattiioonn:: ssiimmppllee--ddeeccllaarraattiioonn ffiixxeedd--aarrrraayy--ddeeccllaarraattiioonn vvaarriiaabbllee--aarrrraayy--ddeeccllaarraattiioonn ppooiinntteerr--ddeeccllaarraattiioonn 11)) SSiimmppllee ddeeccllaarraattiioonnss are just like simple C declarations. ssiimmppllee--ddeeccllaarraattiioonn:: ttyyppee--iiddeenntt vvaarriiaabbllee--iiddeenntt Example: ccoolloorrttyyppee ccoolloorr;; ---->> ccoolloorrttyyppee ccoolloorr;; 22)) FFiixxeedd--lleennggtthh AArrrraayy DDeeccllaarraattiioonnss are just like C array declarations: ffiixxeedd--aarrrraayy--ddeeccllaarraattiioonn:: ttyyppee--iiddeenntt vvaarriiaabbllee--iiddeenntt ""[["" vvaalluuee ""]]"" Example: ccoolloorrttyyppee ppaalleettttee[[88]];; ---->> ccoolloorrttyyppee ppaalleettttee[[88]];; 33)) VVaarriiaabbllee--LLeennggtthh AArrrraayy DDeeccllaarraattiioonnss have no explicit syn- tax in C, so XDR invents its own using angle-brackets. vvaarriiaabbllee--aarrrraayy--ddeeccllaarraattiioonn:: ttyyppee--iiddeenntt vvaarriiaabbllee--iiddeenntt ""<<"" vvaalluuee "">>"" ttyyppee--iiddeenntt vvaarriiaabbllee--iiddeenntt ""<<"" "">>"" The maximum size is specified between the angle brackets. The size may be omitted, indicating that the array may be of any size. iinntt hheeiigghhttss<<1122>>;; //** _a_t _m_o_s_t _1_2 _i_t_e_m_s **// iinntt wwiiddtthhss<<>>;; //** _a_n_y _n_u_m_b_e_r _o_f _i_t_e_m_s **// Since variable-length arrays have no explicit syntax in C, these declarations are actually compiled into "struct"s. For example, the "heights" declaration gets compiled into the following struct: ssttrruucctt {{ uu__iinntt hheeiigghhttss__lleenn;; //** _# _o_f _i_t_e_m_s _i_n _a_r_r_a_y **// iinntt **hheeiigghhttss__vvaall;; //** _p_o_i_n_t_e_r _t_o _a_r_r_a_y **// }} hheeiigghhttss;; Note that 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 of these compo- nent's names is the same as the name of the declared XDR variable. rrppccggeenn Programming Guide Page 23 44)) PPooiinntteerr DDeeccllaarraattiioonnss are made in XDR exactly as they are in C. You can't really send pointers over the net- work, but you can use XDR pointers for sending recursive data types such as lists and trees. The type is actually called "optional-data", not "pointer", in XDR language. ppooiinntteerr--ddeeccllaarraattiioonn:: ttyyppee--iiddeenntt ""**"" vvaarriiaabbllee--iiddeenntt Example: lliissttiitteemm **nneexxtt;; ---->> lliissttiitteemm **nneexxtt;; 66..99.. SSppeecciiaall CCaasseess There are a few exceptions to the rules described above. BBoooolleeaannss:: C has no built-in boolean type. However, the RPC library does a boolean type called _b_o_o_l___t that is either _T_R_U_E or _F_A_L_S_E. Things declared as type _b_o_o_l in XDR language are compiled into _b_o_o_l___t in the output header file. Example: bbooooll mmaarrrriieedd;; ---->> bbooooll__tt mmaarrrriieedd;; SSttrriinnggss:: 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 com- piled into "char *"s in the output header file. The maximum size contained in the angle brackets specifies the maximum number of characters allowed in the strings (not counting the _N_U_L_L character). The maximum size may be left off, indi- cating a string of arbitrary length. Examples: ssttrriinngg nnaammee<<3322>>;; ---->> cchhaarr **nnaammee;; ssttrriinngg lloonnggnnaammee<<>>;; ---->> cchhaarr **lloonnggnnaammee;; OOppaaqquuee DDaattaa:: Opaque data is used in RPC and XDR to describe untyped data, that is, just sequences of arbitrary bytes. It may be declared either as a fixed or variable length array. Page 24 rrppccggeenn Programming Guide Examples: ooppaaqquuee ddiisskkbblloocckk[[551122]];; ---->> cchhaarr ddiisskkbblloocckk[[551122]];; ooppaaqquuee ffiilleeddaattaa<<11002244>>;; ---->> ssttrruucctt {{ uu__iinntt ffiilleeddaattaa__lleenn;; cchhaarr **ffiilleeddaattaa__vvaall;; }} ffiilleeddaattaa;; VVooiiddss:: In a void declaration, the variable is not named. The declaration is just "void" and nothing else. Void dec- larations can only occur in two places: union definitions and program definitions (as the argument or result of a remote procedure).