Skip Navigation Links | |
Exit Print View | |
ONC+ Developer's Guide Oracle Solaris 11.1 Information Library |
1. Introduction to ONC+ Technologies
Converting Local Procedures to Remote Procedures
Passing Complex Data Structures
Compile-Time Client and Server Templates
Compile-Time TI-RPC or TS-RPC Library Selection
Network Types/Transport Selection
Command-Line Define Statements
Server Response to Broadcast Calls
64-Bit Considerations for rpcgen
IPv6 Considerations for rpcgen
4. Programmer's Interface to RPC
5. Advanced RPC Programming Techniques
6. Porting From TS-RPC to TI-RPC
7. Multithreaded RPC Programming
8. Extensions to the Oracle Solaris RPC Library
This section describes the rpcgen options available at compile time. The following table summarizes the options that are discussed in this section.
Table 3-2 rpcgen Compile-Time Flags
|
rpcgen generates sample code for the client and server sides. Use the options described in the following table to generate the desired templates.
Table 3-3 rpcgen Template Selection Flags
|
The files can be used as guides by filling in the missing parts. These files are in addition to the stubs generated.
A C-style mode server template is generated from the add.x source by the command rpcgen -N -Ss -o add_server_template.c add.x
The result is stored in the file add_server_template.c. A C-style mode, client template for the same add.x source is generated with the command rpcgen -N -Sc -o add_client_template.c add.x
The result is stored in the file add_client_template.c. A makefile template for the same add.x source is generated with the command rpcgen -N -Sm -o mkfile_template add.x
The result is stored in the file mkfile_template. It can be used to compile the client and the server. The -a flag, used in the command rpcgen -N -a add.x, generates all three template files. The client template is stored in add_client.c, the server template in add_server.c, and the makefile template inmakefile.a. If any of these files already exists, rpcgen displays an error message and exits.
Note - When you generate template files, give them new names to avoid the files being overwritten the next time rpcgen is executed.
Also called Newstyle mode, the -N flag causes rpcgen to produce code in which arguments are passed by value and multiple arguments are passed without a struct. These changes enable RPC code that is more like C and other high-level languages. For compatibility with existing programs and make files, the previous standard mode of argument passing is the default. The following examples demonstrate the new feature. The source modules for both modes, C-style and default, are shown in Example 3-8 and Example 3-9 respectively.
Example 3-8 C-style Mode Version of add.x
/* * This program contains a procedure * to add 2 numbers. It demonstrates * the C-style mode argument passing. * Note that add() has 2 arguments. */ program ADDPROG { /* program number */ version ADDVER { /* version number */ int add(int, int) = 1; /* procedure */ } = 1; } = 0x20000199;
Example 3-9 Default Mode Version of add.x
/* * This program contains a procedure * to add 2 numbers. It demonstrates * the "default" mode argument passing. * In this mode rpcgen can process * only one argument. */ struct add_arg { int first; int second; }; program ADDPROG { /* program number */ version ADDVER { /* version number */ int add (add_arg) = 1; /* procedure */ } = 1; } = 0x20000199;
The next four examples show the resulting client-side templates.
Example 3-10 C-style Mode Client Stub for add.x
/* * The C-style client side main * routine calls the add() function * on the remote rpc server */ #include <stdio.h> #include "add.h" main(argc, argv) int argc; char *argv[]; { CLIENT *clnt; int *result,x,y; if(argc != 4) { printf("usage: %s host num1 num2\n" argv[0]); exit(1); } /* create client handle - * bind to server */ clnt = clnt_create(argv[1], ADDPROG, ADDVER, "udp"); if (clnt == NULL) { clnt_pcreateerror(argv[1]); exit(1); } x = atoi(argv[2]); y = atoi(argv[3]); /* * invoke remote procedure: Note that * multiple arguments can be passed to * add_l() instead of a pointer */ result = add_1(x, y, clnt); if (result == (int *) NULL) { clnt_perror(clnt, "call failed:"); exit(1); } else { printf("Success: %d + %d = %d\n", x, y, *result); } exit(0); }
The following code example shows how the default mode code differs from C-style mode code.
Example 3-11 Default Mode Client
arg.first = atoi(argv[2]); arg.second = atoi(argv[3]); /* * invoke remote procedure -- note * that a pointer to the argument has * to be passed to the client stub */ result = add_1(&arg, clnt);
The server-side procedure in C-style mode is shown in the following example.
Example 3-12 C-style Mode Server
#include "add.h" int * add_1(arg1, arg2, rqstp) int arg1; int arg2; struct svc_req *rqstp; { static int result; result = arg1 + arg2; return(&result); }
The server-side procedure in default mode is shown in the following code example.
Example 3-13 Default Mode Server Stub
#include "add.h" int * add_1(argp, rqstp) add_arg *argp; struct svc_req *rqstp; { static int result; result = argp->first + argp->second; return(&result); }
By default, the code generated by rpcgen is not MT safe. It uses unprotected global variables and returns results in the form of static variables. The -M flag generates MT-safe code that can be used in a multithreaded environment. This code can be used with the C-style flag, the ANSI C flag, or both.
An example of an MT-safe program with this interface follows. The rpcgen protocol file is msg.x, shown in the following code example.
Example 3-14 MT-Safe Program: msg
program MESSAGEPROG { version PRINTMESSAGE { int PRINTMESSAGE(string) = 1; } = 1; } = 0x4001;
A string is passed to the remote procedure, which prints it and returns the length of the string to the client. The MT-safe stubs are generated with the rpcgen -M msg.x command.
Client-side code that could be used with this protocol file is shown in the following code example.
Example 3-15 MT-Safe Client Stub
#include "msg.h" void messageprog_1(host) char *host; { CLIENT *clnt; enum clnt_stat retval_1; int result_1; char * printmessage_1_arg; clnt = clnt_create(host, MESSAGEPROG, PRINTMESSAGE, "netpath"); if (clnt == (CLIENT *) NULL) { clnt_pcreateerror(host) exit(1); } printmessage_1_arg = (char *) malloc(256); strcpy(printmessage_1_arg, "Hello World"); retval_1 = printmessage_1(&printmessage_1_arg, &result_1,clnt); if (retval_1 != RPC_SUCCESS) { clnt_perror(clnt, "call failed"); } printf("result = %d\n", result_1); clnt_destroy(clnt); } main(argc, argv) int argc; char *argv[]; { char *host; if (argc < 2) { printf("usage: %s server_host\n", argv[0]); exit(1); } host = argv[1]; messageprog_1(host); }
A pointer to both the arguments and the results needs to be passed in to the rpcgen-generated code in order to preserve re-entrancy. The value returned by the stub function indicates whether this call is a success or a failure. The stub returns RPC_SUCCESS if the call is successful. Compare the MT-safe client stub, generated with the -M option, and the MT-unsafe client stub shown in Example 3-16. The client stub that is not MT-safe uses a static variable to store returned results and can use only one thread at a time.
Example 3-16 Client Stub (MT Unsafe)
int * printmessage_1(argp, clnt) char **argp; CLIENT *clnt; { static int clnt_res; memset((char *)&clnt_res, 0, sizeof (clnt_res)); if (clnt_call(clnt, PRINTMESSAGE, (xdrproc_t) xdr_wrapstring, (caddr_t) argp, (xdrproc_t) xdr_int, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); }
The server side code is shown in the following example.
Note - When compiling a server that uses MT-safe mode, you must link in the threads library. To do so, specify the -lthread option in the compile command.
Example 3-17 MT-Safe Server Stub
#include "msg.h" #include <syslog.h> bool_t printmessage_1_svc(argp, result, rqstp) char **argp; int *result; struct svc_req *rqstp; { int retval; if (*argp == NULL) { syslog(LOG_INFO, "argp is NULL\n"); *result = 0; } else { syslog("argp is %s\n", *argp); *result = strlen (*argp); } retval = 1; return (retval); } int messageprog_1_freeresult(transp, xdr_result, result) SVCXPRT *transp; xdrproc_t xdr_result; caddr_t result; { /* * Insert additional freeing code here, * if needed */ (void) xdr_free(xdr_result, result); }
The server-side code should not use statics to store returned results. A pointer to the result is passed in and this should be used to pass the result back to the calling routine. A return value of 1 indicates success to the calling routine, while 0 indicates a failure.
In addition, the code generated by rpcgen also generates a call to a routine to free any memory that might have been allocated when the procedure was called. To prevent memory leaks, any memory allocated in the service routine needs to be freed in this routine. messageprog_1_freeresult() frees the memory.
Normally, xdr_free() frees any allocated memory for you. In this example, no memory was allocated, so no freeing needs to take place.
The following add.x file shows the use of the -M flag with the C-style and ANSI C flag.
Example 3-18 MT-Safe Program: add.x
program ADDPROG { version ADDVER { int add(int, int) = 1; } = 1; }= 199;
This program adds two numbers and returns its result to the client. rpcgen is invoked on it, with the rpcgen -N -M -C add.x command. The following example shows the multithreaded client code to call this code.
Example 3-19 MT-Safe Client: add.x
/* * This client-side main routine starts up a number of threads, * each of which calls the server concurrently. */ #include "add.h" CLIENT *clnt; #define NUMCLIENTS 5 struct argrec { int arg1; int arg2; }; /* * Keeps count of number of threads running */ int numrunning; mutex_t numrun_lock; cond_t condnum; void addprog(struct argrec *args) { enum clnt_stat retval; int result; /* call server code */ retval = add_1(args->arg1, args->arg2, &result, clnt); if (retval != RPC_SUCCESS) { clnt_perror(clnt, "call failed"); } else printf("thread #%x call succeeded, result = %d\n", thr_getself(), result); /* decrement the number of running threads */ mutex_lock(&numrun_lock); numrunning--; cond_signal(&condnum); mutex_unlock(&numrun_lock); thr_exit(NULL); } main(int argc, char *argv[]) { char *host; struct argrec args[NUMCLIENTS]; int i; thread_t mt; int ret; if (argc < 2) { printf("usage: %s server_host\n", argv[0]); exit(1); } host = argv[1]; clnt = clnt_create(host, ADDPROG, ADDVER, "netpath"); if (clnt == (CLIENT *) NULL) { clnt_pcreateerror(host); exit(1); }; mutex_init(&numrun_lock, USYNC_THREAD, NULL); cond_init(&condnum, USYNC_THREAD, NULL); numrunning = 0; /* Start up separate threads */ for (i = 0; i < NUMCLIENTS; i++) { args[i].arg1 = i; args[i].arg2 = i + 1; ret = thr_create(NULL, NULL, addprog, (char *) &args[i], THR_NEW_LWP, &mt); if (ret == 0) numrunning++; } mutex_lock(&numrun_lock); /* are any threads still running ? */ while (numrunning != 0) cond_wait(&condnum, &numrun_lock); mutex_unlock(&numrun_lock); clnt_destroy(clnt);}
The server-side procedure is shown in the following example.
Note - When compiling a server that uses MT-safe mode, you must link in the threads library. To do so, specify the -lthread option in the compile command.
Example 3-20 MT-Safe Server: add.x
add_1_svc(int arg1, int arg2, int *result, struct svc_req *rqstp) { bool_t retval; /* Compute result */ *result = arg1 + arg2; retval = 1; return (retval); } /* Routine for freeing memory that may * be allocated in the server procedure */ int addprog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) { (void) xdr_free(xdr_result, result); }
MT Auto mode enables RPC servers to automatically use Oracle Solaris threads to process client requests concurrently. Use the -A option to generate RPC code in MT Auto mode. The -A option also has the effect of turning on the -M option, so -M does not need to be explicitly specified. The -M option is necessary because any code that is generated has to be multithread safe.
The section Chapter 7, Multithreaded RPC Programming contains further discussion on multithreaded RPC. See also MT Auto Mode.
An example of an Auto mode program generated by rpcgen follows in the rpcgen protocol file time.x. A string is passed to the remote procedure, which prints the string and returns its length to the client.
Example 3-21 MT Auto Mode: time.x
program TIMEPROG { version TIMEVERS { unsigned int TIMEGET(void) = 1; void TIMESET(unsigned) = 2; } = 1; } = 0x20000044;
The MT-safe stubs are generated with the rpcgen -A time.x command.
When the -A option is used, the generated server code contains instructions for enabling MT Auto mode for the server.
Note - When compiling a server that uses MT Auto mode, you must link in the threads library. To do so, specify the -lthread option in the compile command.
In older Oracle Solaris releases, rpcgen created stubs that used the socket functions. With the current Oracle Solaris release, you can use either the transport-independent RPC (TI-RPC) or the transport-specific socket (TS-RPC) routines. These routines provides backward compatibility with previous releases. The default uses the TI-RPC interfaces. The -b flag tells rpcgen to create TS-RPC variant source code as its output.
rpcgen can also produce output that is compatible with ANSI C. This feature is selected with the -C compile flag and is most often used with the -N flag, described in Compile-Time C-style Mode.
The add.x example of the server template is generated by the rpcgen -N -C -Ss -o add_server_template.c add.x command:
Note that on the C++ 3.0 server, remote procedure names require an _svc suffix. In the following example, the add.x template and the -C compile flag produce the client side add_1 and the server stub add_1_svc.
Example 3-22 rpcgen ANSI C Server Template
/* * This is a template. Use it to * develop your own functions. */ #include <c_varieties.h> #include "add.h" int * add_1_svc(int arg1, int arg2, struct svc_req *rqstp) { static int result; /* * insert server code here */ return(&result); }
This output conforms to the syntax requirements and structure of ANSI C. The header files that are generated when this option is invoked can be used with ANSI C or with C++.
rpcgen tries to generate more efficient code by using xdr_inline() when possible (see the xdr_admin(3NSL) man page). When a structure contains elements that xdr_inline() can be used on (for example integer(), long(), bool()), the relevant portion of the structure is packed with xdr_inline(). A default of five or more packed elements in sequence causes inline code to be generated. You can change this default with the -i flag. The rpcgen -i 3 test.x command causes rpcgen to start generating inline code after three qualifying elements are found in sequence. The rpcgen -i 0 test.x command prevents any inline code from being generated.
In most situations, you do not need to use the -i flag. The _xdr.c stub is the only file affected by this feature.