This code example illustrates an expert-level client API that is used in developing TI-RPC applications.
The expert level for the development of a client API is the most complicated. It also offers the most customization. This is the only level where the buffer size can be tuned for the client API. This level requires the programmer to set up the universal address for the client to connect to, either by using the name-to-address translation APIs or one of the other expert-level APIs. Either way, this level requires more work, but it allows the programmer to tailor the client application to the environment it runs in.
#include <stdio.h> #include <netconfig.h> #include <netdir.h> #include <errno.h> #include "myapp.h" #define EXIT 100 int main(void) { enum clnt_stat rslt; /* return value of clnt_call() */ char hostname[256]; /* buffer for remote service's hostname */ unsigned long procnum; /* procedure to call */ char filename[512]; /* buffer for filename */ xdrproc_t xdr_argument; /* xdr procedure to encode arguments */ xdrproc_t xdr_result; /* xdr procedure to decode results */ CLIENT *clnt; /* pointer to client handle */ struct timeval tout; /* timeout for clnt_call() */ struct netconfig *nconf;/* transport information */ struct netbuf svcaddr; /* universal address of remote service */ bool_t rpcb_rslt; /* return value for rpcb_getaddr() */ char *arg = filename; /* pointer to filename buffer */ union { u_int myapp_get_uid_result; char * myapp_get_uid_string_result; int myapp_get_size_result; long myapp_get_mtime_result; char * myapp_get_mtime_string_result; u_short myapp_get_codepage_result; char * myapp_get_objtype_result; char * myapp_get_filetype_result; } result; /* a union of all the possible results */ /* initialize the struct netbuf space */ svcaddr.maxlen = 16; svcaddr.buf = (char *)malloc(svcaddr.maxlen); if (svcaddr.buf == (char *)NULL) { /* if malloc() failed, print error messages and exit */ fprintf(stderr, "Error calling malloc() for struct netbuf\n"); fprintf(stderr, "errno: %d\n", errno); return 1; } tout.tv_sec = 30; /* set default timeout to 30.00 seconds */ tout.tv_usec = 0; /* get the hostname from the user */ printf("Enter the hostname where the remote service is running: \n"); scanf("%s", (char *)&hostname); myapp_print_menu(); /* print out the menu choices */ /* get the procedure number to call from the user */ printf("\nEnter a procedure number to call: \n"); scanf("%lu", &procnum); /* get the filename from the user */ printf("\nEnter a filename to stat: \n"); scanf("%s", (char *)&filename); /* getnetconfigent(nettype) */ nconf = getnetconfigent(NETTYPE); /* check to make sure getnetconfigent() didn't fail */ if (nconf == NULL) { /* if getnetconfigent() failed, print error messages and exit */ fprintf(stderr, "Error calling getnetconfigent(%s)\n", NETTYPE); fprintf(stderr, "errno: %d\n", errno); return 1; } /* rpcb_getaddr(prognum, versnum, nconf, output netbuf, hostname) */ /* this sets the universal address svcaddr */ rpcb_rslt = rpcb_getaddr(PROGNUM, VERSNUM, nconf, &svcaddr, hostname); /* check to make sure rpcb_getaddr() didn't fail */ if (rpcb_rslt == FALSE) { /* if rpcb_getaddr() failed, print error messages and exit */ fprintf(stderr, "Error calling rpcb_getaddr()\n"); fprintf(stderr, "PROG: %lu\tVERS: %lu\tNET: %s\n", PROGNUM, VERSNUM, NETTYPE); fprintf(stderr, "clnt_stat: %d\n", rpc_createerr.cf_stat); fprintf(stderr, "errno: %d\n", errno); fprintf(stderr, "re_errno: %d\n", rpc_createerr.cf_error.re_errno); return 1; } /* clnt_tli_create(filedes, netconfig, netbuf, */ /* prognum, versnum, sendsz, recvsz); */ clnt = clnt_tli_create(RPC_ANYFD, nconf, &svcaddr, PROGNUM, VERSNUM, 0, 0); /* check to make sure clnt_tli_create() didn't fail */ if (clnt == (CLIENT *)NULL) { /* if we failed, print out all appropriate error messages and exit */ fprintf(stderr, "Error calling clnt_tli_create()\n"); fprintf(stderr, "PROG: %lu\tVERS: %lu\tNET: %s\n", PROGNUM, VERSNUM, NETTYPE); fprintf(stderr, "clnt_stat: %d\n", rpc_createerr.cf_stat); fprintf(stderr, "errno: %d\n", errno); fprintf(stderr, "re_errno: %d\n", rpc_createerr.cf_error.re_errno); return 1; } /* switch on the input */ switch (procnum) { case NULLPROC: /* set the encode procedure */ xdr_argument = (xdrproc_t)xdr_void; /* set the decode procedure */ xdr_result = (xdrproc_t)xdr_void; break; case GET_UID: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_u_int; break; case GET_UID_STRING: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_wrapstring; break; case GET_SIZE: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_int; break; case GET_MTIME: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_long; break; case GET_MTIME_STRING: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_wrapstring; break; case GET_CODEPAGE: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_u_short; break; case GET_OBJTYPE: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_wrapstring; break; case GET_FILETYPE: /* set the encode procedure */ xdr_argument = xdr_wrapstring; /* set the decode procedure */ xdr_result = xdr_wrapstring; break; case END_SERVER: /* set the encode procedure */ xdr_argument = (xdrproc_t)xdr_void; /* set the decode procedure */ xdr_result = (xdrproc_t)xdr_void; break; case EXIT: /* we're done. clean up and exit */ clnt_destroy(clnt); return 1; break; default: /* invalid procedure number entered. defaulting to NULLPROC */ printf("Invalid choice. Issuing NULLRPOC instead.\n"); procnum = NULLPROC; /* set the encode procedure */ xdr_argument = (xdrproc_t)xdr_void; /* set the decode procedure */ xdr_result = (xdrproc_t)xdr_void; break; } /* end of switch(procnum) */ /* clnt_call(client, procnum, xdr_inproc, in, xdr_outproc, out, timeout) */ rslt = clnt_call(clnt, procnum, xdr_argument, (char *)&arg, xdr_result, (char *)&result, tout); /* check to make sure clnt_call() succeeded */ if (rslt != RPC_SUCCESS) { /* if clnt_call() failed, print errors and exit */ printf("An error occurred calling %lu procedure\n", procnum); printf("clnt_stat: %d\terrno: %d\n", rslt, errno); clnt_destroy(clnt); return 1; } /* clnt_call() succeeded. switch on procedure and print results */ switch (procnum) { case NULLPROC: /* print results and exit */ printf("NULLRPOC call succeeded\n"); break; case GET_UID: /* print results and exit */ printf("uid of %s: %u\n", filename, result.myapp_get_uid_result); break; case GET_UID_STRING: /* print results and exit */ printf("owner of %s: %s\n", filename, result.myapp_get_uid_string_result); break; case GET_SIZE: /* print results and exit */ printf("size of %s: %d\n", filename, result.myapp_get_size_result); break; case GET_MTIME: /* print results and exit */ printf("last modified time of %s: %ld\n", filename, result.myapp_get_mtime_result); break; case GET_MTIME_STRING: /* print results and exit */ printf("last modified time of %s: %s\n", filename, result.myapp_get_mtime_string_result); break; case GET_CODEPAGE: /* print results and exit */ printf("codepage of %s: %d\n", filename, result.myapp_get_codepage_result); break; case GET_OBJTYPE: /* print results and exit */ printf("object type of %s: %s\n", filename, result.myapp_get_objtype_result); break; case GET_FILETYPE: /* print results and exit */ printf("file type of %s: %s\n", filename, result.myapp_get_filetype_result); break; case END_SERVER: /* print results and exit */ printf("Service has been unregistered.\n"); printf("You must still kill the job in QBATCH\n"); break; default: /* we should never get the default case. */ /* the previous switch should catch it. */ break; } /* end of switch(procnum) */ /* clean up and exit */ /* free the netconfig struct */ freenetconfigent(nconf); /* free the universal address buffer */ free(svcaddr.buf); /* destroy the client handle */ clnt_destroy(clnt); return 0; } void myapp_print_menu(void) { /* print out the procedure choices */ printf("%.2ld - GET_UID %.2ld - GET_UID_STRING\n", GET_UID, GET_UID_STRING); printf("%.2ld - GET_SIZE %.2ld - GET_MTIME\n", GET_SIZE, GET_MTIME); printf("%.2ld - GET_MTIME_STRING %.2ld - GET_CODEPAGE\n", GET_MTIME_STRING, GET_CODEPAGE); printf("%.2ld - GET_OBJTYPE %.2ld - GET_FILETYPE\n", GET_OBJTYPE, GET_FILETYPE); printf("%.2ld - END_SERVER %.2d - EXIT\n", END_SERVER, EXIT); }