Tuesday, April 28, 2009

Passing multiple parameters with rpcgen

RPC handles arbitrary data structures and converts these structures to a standard transfer format called external data representation (XDR) before sending them over the transport.

The conversion from a machine representation to XDR is called serializing, and the reverse process is called deserializing.

For example, to send 2 string parameters, the routine is packaged in a structure containing these 2 different string parameters.

Client sends two different string parameters which are names of programs that are going to be executed at server side and the result is going to be returned back to the client.

Names of programs are set in client main() function. First program to be executed is "netstat -s" and the result of that program is going to be piped into another program "grep TCP".

Result of "grep TCP" is returned to the client program. At the same time this an example of using pipes in linux environment.

Define an extra struct in your prog.x file.
/* prog.x file                                                                
 * run_progs_1(program_names) returns the output to the client(struct param)
*/   

const MAXPARAMNAME = 255;
typedef string prog<MAXPARAMNAME>;
struct prognames
{
 prog program1;
 prog program2;
};
                                                            
program RPC_PROG {                                               
    version RPC_VERS {                                         
 string RUN_PROGS(prognames) = 1; /* procedure numer=1 */
    } = 1; /* version number = 1 */
                                                                  
} = 0x12345678; /* program number = 0x12345678 */


Running rpcgen on prog.x generates four output files:

1-)Header file (prog.h),
2-)Client stub (prog_clnt.c),
3-)Server skeleton (prog_svc.c),
4-)XDR routines in the file (prog_xdr.c).

Additionally, you need to implement that defined function (string RUN_PROGS(prognames)) in your prog.x file.

Create a new file "server_source.c" and add your implementation for
(string RUN_PROGS(prognames)) function into that file.

/* server_source.c file
 * run_progs_1(program_names) returns the output to the client(struct param)
*/   
#include <rpc/rpc.h> /* standard RPC include file */
#include "prog.h" /* this file is generated by rpcgen */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define READ_MAX_SIZE 512

/* Return the result of executed commands function1*/    
char** run_progs_1_svc(prognames *argparams, struct svc_req *arg2)
{
 FILE* fpipe;
 char* operator = " | ";
 char* program1AndProgram2;
 char* prog1 = argparams->program1;
 char* prog2 = argparams->program2;
 static char* readPipeInto;
 readPipeInto = (char*)malloc((READ_MAX_SIZE + 1)*sizeof(char));
 memset(readPipeInto, 0, READ_MAX_SIZE + 1);
 program1AndProgram2=(char*)malloc((strlen(prog1)+strlen(operator)+strlen(prog2)+1)*sizeof(char));
 strcpy(program1AndProgram2, argparams->program1);
 strcat(program1AndProgram2, operator);
 strcat(program1AndProgram2, argparams->program2);
 //execute commands
 if ( !(fpipe = (FILE*)popen(program1AndProgram2,"r")) )
 { 
   perror("Cant open pipe!");
   exit(1);
 }
 //store result in readPipeInto
 fread((char *)readPipeInto, READ_MAX_SIZE, 1, fpipe); 
 pclose(fpipe); 
 free(program1AndProgram2);
 //return output to the client
 return (char **)&readPipeInto;
}


Lastly, prepare your client and call the routine from server. Create a new file client_source.c .


/* client_source.c file */   
#include <stdio.h>
#include <stdlib.h>                                 
#include <rpc/rpc.h> /* standard RPC include file */     
#include "prog.h" /* this file is generated by rpcgen */ 

main(int argc, char *argv[])                               
{ 
   CLIENT *cl; /* RPC handle */

   char *server;

   char** resultStringFromServerExecutePrograms;
   if (argc != 2) {
       fprintf(stderr, "usage: %s hostname\n", argv[0]);
       exit(1);
   }

   server = argv[1];
   /* Create client handle */
   if ((cl = clnt_create(server, RPC_PROG, RPC_VERS, "udp")) == NULL) {
       /* can't establish connection with server */
       clnt_pcreateerror(server);
       exit(2);
   }
  
   prognames args;
   args.program1 = "netstat -s";
   args.program2 = "grep TCP";
   /*remote procedure run_progs */
   resultStringFromServerExecutePrograms = run_progs_1(&args, cl);
   if (resultStringFromServerExecutePrograms == NULL) {
      clnt_perror(cl, server);
      exit(3);
   }
   printf("Function1 Result of Run Programs is: \n\n");
   printf("%s",*resultStringFromServerExecutePrograms);
   printf("\n\n"); 
 
   clnt_destroy(cl); 
   exit(0);
}


In order to create server and client executables by using these 3 different files, issue the following commands from the terminal in the same order. 


Put your (prog.x, client_source.c, server_source.c) files into the same folder.

user@machine:~/Params$ rpcgen prog.x
user@machine:~/Params$ cc -o client client_source.c prog_clnt.c prog_xdr.c 
user@machine:~/Params$ cc -o server server_source.c prog_svc.c prog_xdr.c 
user@machine:~/Params$ ./server localhost

Open a new terminal tab and run the client.
user@machine:~/Params$ ./client localhost

Output of running client is as follows:
Function1 Result of Run Programs is: 

    35 TCP sockets finished time wait in fast timer
    15 other TCP timeouts

Run on ubuntu 9.04.

No comments:

Post a Comment