Example: TI-RPC top-level service API

This code example illustrates a top-level service API used in developing TI-RPC services.

The development of a service is more complicated at the top level, because it requires the developer to write a dispatch routine. At this level, when a service request comes in, a dispatch routine is called. The dispatch routine must collect the arguments and call the correct local procedure, catch all errors and results, and return that information to the client. After dispatch function is written, it can be readily copied and used in other services with only slight modifications.

The top, intermediate, and expert layers can use the same dispatch function without modification. In the following example, the dispatch function is bundled with the other local functions in this file. Both files need to be compiled and linked together before the service runs. The advantage of the top level over the other layers is the ability to specify the NETTYPE as a string, instead of using the network selection APIs. After calling the top-level API, the service is created, bound to the dispatch function, and registered with the rpcbind service.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.
#include <stdio.h>
#include <netconfig.h>
#include <rpc/rpc.h>
#include <errno.h>
#include "myapp.h"
int main(int argc, char *argv[]) {

	int num_svc; /* return value for the svc_create() API */

	/* unregister any existing copy of this service */
	/* (void)svc_unreg(program, version) */
	svc_unreg(PROGNUM, VERSNUM);

	/* (int)svc_create(dispatch, prognum, versnum, nettype); */
	num_svc = svc_create(myapp_dispatch, PROGNUM, VERSNUM, NETTYPE);

	/* check for errors calling svc_create() */
	if (num_svc == 0) {
		/* print error messages and exit */
		fprintf(stderr, "Error calling %s.\n", "svc_create");
		fprintf(stderr, "PROG: %lu\nVERS: %lu\tNET: %s\n", 
				PROGNUM, VERSNUM, NETTYPE);
		fprintf(stderr, "errno: %d\n", errno);
		return 1;
		}

	/* this should loop indefinitely waiting for client connections */
	svc_run();

	/* if we get here, svc_run() returned */
	fprintf(stderr, "svc_run() returned.  ERROR has occurred.\n");
	fprintf(stderr, "errno: %d\n", errno);

	/* clean up by unregistering.  then, exit */
	svc_unreg(PROGNUM, VERSNUM);

	return 1;

} /* end of main() */
/* This is an example of the dispatch function */
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <pwd.h>
#include <rpc/rpc.h>
#include <time.h>
#include "myapp.h"

char * myapp_get_uid(char *in) {

	u_int retval;              /* return value for this procedure() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	/* (int)stat(filename, struct stat *) */
	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		retval = (u_int)-1;
		}
	else {
		retval = (u_int)(sbuf.st_uid);
		}

	return (char *)&retval;
}

char *myapp_get_uid_string(char *in) {

	char *retval;              /* return value for this procedure() */
	struct passwd *pbuf;       /* return value for getpwuid() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	/* (int)stat(filename, struct stat *) */
	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		retval = (char *)NULL;
		}
	else {

		pbuf = (struct passwd *)getpwuid((uid_t)(sbuf.st_uid));

		if (pbuf == NULL) {
			retval = (char *)NULL;
			}
		else {
			retval = (char *)(pbuf->pw_name);
			}
		}

	return (char *)&retval;
}

char * myapp_get_size(char *in) {

	int retval;                /* return value for this procedure() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	/* (int)stat(filename, struct stat *) */
	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		retval = (int)-1;
		}
	else {
		retval = (int)(sbuf.st_size);
		}

	return (char *)&retval;
}

char * myapp_get_mtime(char *in) {

	long retval;               /* return value for this procedure() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	/* (int)stat(filename, struct stat *) */
	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		retval = (long)-1;
		}
	else {
		retval = (long)(sbuf.st_mtime);
		}

	return (char *)&retval;

}
		
char *myapp_get_mtime_string(char *in) {

	char *retval;              /* return value for this procedure() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	/* (int)stat(filename, struct stat *) */
	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		retval = (char *)NULL;
		}

	else {
		retval = (char *)ctime((time_t *)&(sbuf.st_mtime));
		}

	return (char *)&retval;
}

char * myapp_get_codepage(char *in) {

	u_short retval;            /* return value for this procedure() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		retval = (u_short)-1;
	}
	
	else {
		retval = (u_short)(sbuf.st_codepage);
	}

	return (char *)&retval;
}


char *myapp_get_objtype(char *in) {

	char *retval;              /* return value for this procedure() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	/* (int)stat(filename, struct stat *) */
	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		retval = (char *)NULL;
	}
	
	else {
		retval = (char *)(sbuf.st_objtype);
	}

	return (char *)&retval;

}

char *myapp_get_filetype(char *in) {

	char *result = NULL;       /* return value for this procedure() */
	struct stat sbuf;          /* data storage area for stat() */
	int stat_ret;              /* return value for stat() */
	char *file = *(char **)in; /* input value for stat() */

	/* (int)stat(filename, struct stat *) */
	stat_ret = stat(file, &sbuf);

	if (stat_ret == -1) {
		return (char *)NULL;
		}
	
	if (S_ISDIR(sbuf.st_mode)) {
		result = "Directory";
		}

	if (S_ISREG(sbuf.st_mode)) {
		result = "Regulare File";
		}

	if (S_ISLNK(sbuf.st_mode)) {
		result = "Symbolic Link";
		}

	if (S_ISSOCK(sbuf.st_mode)) {
		result = "Socket";
		}

	if (S_ISNATIVE(sbuf.st_mode)) {
		result = "AS/400 Native Object";
		}

	if (S_ISFIFO(sbuf.st_mode)) {
		result = "FIFO";
		}

	if (S_ISCHR(sbuf.st_mode)) {
		result = "Character Special";
		}

	if (S_ISBLK(sbuf.st_mode)) {
		result = "Block Special";
		}

	return (char *)&result;

}

char * myapp_end_server(char *empty) {

	/* char *empty is not used      */
	/* function always returns NULL */

	svc_unreg(PROGNUM, VERSNUM);
	
	return (char *)NULL;

}
	
void myapp_dispatch(struct svc_req *request, SVCXPRT *svc) {

	union {
		/* all of the procedures take a string */
		/* if there were other procedures, it */
		/* might look like this:              */
		/* int set_codepage_arg               */
		char *  filename_arg;
	} argument;

	char *result;            /* pointer to returned data from proc */
	xdrproc_t xdr_argument;  /* decodes data from client call */
	xdrproc_t xdr_result;    /* encodes data to return to client */
	char *(*proc)(char *);   /* pointer to local procedure to call */

	switch (request->rq_proc) {
	
		case NULLPROC:
			/* a special case.  always return void */
                        (void)svc_sendreply((SVCXPRT *)svc, 
                                            (xdrproc_t)xdr_void, 
                                            (char *)NULL);
			return;

		case GET_UID:
			/* takes a string argument (filename) */
			/* returns an u_int (uid of file ownder) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_u_int;
			proc         = (char *(*)(char *))myapp_get_uid;
			break;

		case GET_UID_STRING:
			/* takes a string argument (filename) */
			/* returns a string (owner's name in string format) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_wrapstring;
			proc         = (char *(*)(char *))myapp_get_uid_string;
			break;

		case GET_SIZE:
			/* takes a string argument (filename) */
			/* returns an int (size of file in bytes) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_int;
			proc         = (char *(*)(char *))myapp_get_size;
			break;

		case GET_MTIME:
			/* takes a string argument (filename) */
			/* returns a long (time last modified) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_long;
			proc         = (char *(*)(char *))myapp_get_mtime;
			break;

		case GET_MTIME_STRING:
			/* takes a string argument (filename) */
			/* returns a string (time last modified, string format) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_wrapstring;
			proc         = (char *(*)(char *))myapp_get_mtime_string;
			break;

		case GET_CODEPAGE:
			/* takes a string argument (filename) */
			/* returns an u_short (codepage of file) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_u_short;
			proc         = (char *(*)(char *))myapp_get_codepage;
			break;

		case GET_OBJTYPE:
			/* takes a string argument (filename) */
			/* returns a string (object type) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_wrapstring;
			proc         = (char *(*)(char *))myapp_get_objtype;
			break;

		case GET_FILETYPE:
			/* takes a string argument (filename) */
			/* returns a string (file type) */
			xdr_argument = xdr_wrapstring;
			xdr_result   = xdr_wrapstring;
			proc         = (char *(*)(char *))myapp_get_filetype;
			break;

		case END_SERVER:
			/* takes no arguments */
			/* returns no data */
			/* unregisters service with local rpcbind daemon */
			xdr_argument = (xdrproc_t)xdr_void;
			xdr_result   = (xdrproc_t)xdr_void;
			proc         = (char *(*)(char *))myapp_end_server;
			break;
		
		default:
			/* fall through case.  return error to client */
			svcerr_noproc(svc);
			return;

	} /* end switch(request->rq_proc) */


	/* clear the argument */
	memset((char *)&argument, (int)0, sizeof(argument));

	/* decode argument from client using xdr_argument() */
	if (svc_getargs(svc, xdr_argument, (char *)&argument) == FALSE) {
		/* if svc_getargs() fails, return RPC_CANTDECODEARGS to client */
		svcerr_decode(svc);
		return;
	}

	/* call local procedure, passing in pointer to argument */
	result = (char *)(*proc)((char *)&argument);

	/* check first that result isn't NULL */
	/* try to send results back to client.  check for failure */
	if ((result != NULL) && (svc_sendreply(svc, xdr_result, result) == FALSE))
{
		/* send error message back to client */
		svcerr_systemerr(svc);
	}

	/* free the decoded argument's space */
	if (svc_freeargs(svc, xdr_argument, (char *)&argument) == FALSE) {
		/* if unable to free, print error and exit */
		(void)fprintf(stderr, "unable to free arguments\n");
		exit(1);
	}

} /* end of myapp_dispatch() */