We have seen a simple example of the interface declaration in section 0.1.1. Now that we have learned how to declare types, what remains is simple: to declare RPC functions, using the declared types in function signatures or as return types.
A powerRPC IDL files may contain the following components,
Interface declaration...
An interface declaration consists property definitions and functions declarations.
The following is an example,
1 interface test { 2 property TRANSPORT_PROTOCOL = tcp; 3 int my_read( 4 out char [maxsize=maxlen, size=return>0?return:0] buf, 5 int maxlen 6 ) 7 { 8 property FORK_ON_CALL = true; 10 }; 12 int my_write(in char [size = len] buf, int len); 13 } = 12221;At line 1, the keyword interface followed by the identifier test announces our RPC. At line 2, we set the TRANSPORT PROTOCOL of the RPC to be TCP. Properties are optional settings that customize RPCes on the client or server side. If the TRANSPORT_PROTOCOL property is not set, the default is tcp_and_udp, meaning the client can connect to the server via both protocols. From line 3 to line 10, we declared an RPC function my_read(char* buf, int maxlen), the out keyword on line 4 says that the buf is used for output only, and its maximum size is determined by the second argument maxlen, its size is determined by the expression (return > 0? return:0), where the return keyword is the return value of the RPC function. Obviously, return can ONLY be used in size expressions for out arguments. The FORK_ON_CALL property says that the RPC server will fork a child upon receiving the my_read() call, so the parent can handle other requests. Line 12 declares another RPC function int my_write(char* buf, int len), in this case, the buf is used for input only, the size of buf is determined by the len argument. my_write() does not have additional properties, unlike my_read(), the server will not fork a child to handle it. Line 13 specifies that the test RPC is identified by the program number 12221. The program number is a long integer used to identify the RPC program, and it should not be in conflict with other RPC programs.
PowerRPC IDL allows you to specify the direction of the argument as one of in, out and inout, the default direction is in. You can use an argument in the size expression for an array argument.
The property definitions have scopes. When a property is defined in the scope of the interface, it is shared by all RPC functions. However, a function can redefines a particular property, and overrides the common value.
RPC properties are very useful in customizing how RPC works. They can be a boolean that toggle between options, or a value to set a parameter, or a function to be called at a particular point.
RPC function declarations should be enclosed in the body of the interface declaration. A function can use any of the types declared previously in the IDL file. An array argument can use an argument of the same function in its size expressions. Thus, we can have the following,
interface hello { void printmsg(char [size=strlen(msg)+1, 1024] msg); } 0x12345;
A function argument can be of one of the three directions,
The return type of an RPC function can also be of any type. However, you must note that for a functions that return a pointer, powerRPC allocates the memory to which the pointer points, and it is the programmer responsibility to free that memory using the library function pw_free_reference().
As shown in the previous examples, property definitions come in two places: immediately enclosed in the ``body" of an interface declaration, or inside the ``body" of a function declaration.
The following properties can be defined.
VERSION
An integer value to specified version number for the interface.
The default value is 1.
TRANSPORT_PROTOCOL
The protocol used by client/server for communications. Choices are
The default is tcp_and_udp.
SERVER_PORT
An integer value used to specify to the port number of the RPC server. When this property is not set (the usual case), the server choose an arbitrary port that is available and the client consults the portmapper on the server host to obtain the port number. When this set, the client will bypass the portmapper and uses the port number specified.
NO_PMAP_REGISTER
When this property is set to true, the server will skip registration with the portmapper. The rpcinfo command won't find your server. You would normally set this property when you also specified a fixed port.
The default is false.
INIT_BEFORE_REGISTER
A user defined function to be called before server register itself. This function must be of the type of void (*) (int, char**), it is passed the argc and argv arguments from the main(int argc, char**argv) function.
No default value for this property.
INIT_AFTER_REGISTER
The user defined function to be called after server register itself. This function will be passed the argc and argv arguments from the main(int argc, char**argv) function.
For example, you could use this initialization function to set up signal handlers, a useful one is to unregister your RPC server with the portmapper when a SIGINT is received. Using the quote RPC server as an example, we can write the following functions,
void handler(int sig) { quote_1_unmap(0,0); exit(0); } void set_sigint_handler(int argc, char**argv) { signal(SIGINT, handler); }where quote_1_unmap() is a function generated by powerRPC for the purpose of unsetting the portmapper entry of the quote server. By setting
INIT_AFTER_REGISTER = set_sigint_handler;you make sure that the quote server will remove its entry from the portmapper's database.
No default value for this property.
GEN_MAIN_FUNC
When set to false, the powerRPC compiler will not generate the main() function for the server.
The default is true.
FORK_ON_CONNECTION
When set to true, the RPC server will fork a dedicated server for each client, all RPCes from the client will be handled by the child. For TCP, the child is created when a request for connection is received, when the client detaches from the server, the forked child will exit. The semantics with UDP transport is different, since UDP is not connection oriented. For UDP, the server would fork to handle each incoming RPC call. It is recommended not to use this option with UDP transport, use FORK_ON_CALL instead.
The default is false.
FORK_ON_CALL
When set to true for an RPC function, the server will fork a child to handle the call, so itself can still handle other requests.
The forked child will exit at the completion of the call.
The default is false.
NON_BLOCKING
When this is set to true for an RPC function, the server will reply to the server immediately, before it actually calls the user implementation of the function.
Obviously, the return type of such an RPC function should be void and it should have no out or inout arguments.
TIMEOUT_VALUE
This is an integral value for the RPC timeout in seconds on the client side. When specified for an RPC function, a timeout error will occur when a reply for this function is not received within the timeout.
For example, if you set this to be 0 for function foo(), then every call of foo() will timeout.
The default value is 60.
SERV_CALL_PREFIX
For an RPC function foo() , the powerRPC compiler will generate the client definition of foo() for you, and you must write the server implementation of foo() yourself. However, sometimes, you want to make a program both a server and a client of the same RPC interface. To make this possible, you must set this property.
For example, when you set
property SERV_CALL_PREFIX = serv_ ;powerRPC expects your sever implementation of foo() to be serv_foo(). This solve the name conflict and you can write a program that is both a server and also a client (of another server) of the same RPC.
Various functions are generated from your IDL file, occasionally, you may want to call them in your code directly.
For any data type T used in an RPC function, an XDR function bool_t xdr_T(XDR* xdrs, T*ptr) is generated when T is not one of the primitive types. This function takes a pointer to an object of type T as the second argument, and performs encode, decode or free operation based on the value of xdrs->x_op.
One possible use of the XDR functions is for serializing data to and from files.
CLIENT* <interface>_bind(char*host, u_long pno, u_long vno, char*protocol)
Return value: On success, it returns a pointer to CLIENT struct. On failure, it returns NULL.
This function binds the client to the RPC server specified by the parameters. All subsequent RPC calls go to the specified server. The client application may save the returned client handle, so it can talk to multiple servers.
void <interface>_bind_handle(CLIENT*pclnt)
This function binds the client to a previously bound server. All subsequent RPC calls go to this server.
void <interface>_unbind(CLIENT*pclnt)
This function unbinds the client to a server.
enum clnt_stat <interface>_errno(CLIENT*pclnt)
This function returns the RPC call status after an RPC has been made. If this is not RPC_SUCCESS, an error condition has occured.
SVCXPRT* <interface>_<version>_reg(int sock, u_long pno, u_long vno, int protocol)
Return value:
A pointer to the server transport. NULL on failure.
This function takes register the <interface> RPC server with the specified parameters.
void <interface>_<version>_main(int argc, char**argv)
This function starts the RPC server. It should never return.
void <interface>_unmap (u_long pno, u_long vno)
This function undo the RPC server registration with the portmapper.
PowerRPC library...