ONC+ 開発ガイド

付録 D RPC サンプルプログラム

この章では、このマニュアルの rpcgen と RPC の章で使用したサンプルプログラムの完全なリストを示します。特に但し書き (擬似プログラムであるというような但し書き) がない限り、このまま書かれているとおりにコンパイルして実行できます。これらのサンプルプログラムはマニュアルの説明を補うために示すものです。サン・ソフトは実行結果に対してどのような責任も負いません。

ディレクトリリストプログラムとその補助ルーチン (rpcgen)


例 D-1 rpcgen プログラム : dir.x

/*
 * 	dir.x: 遠隔ディレクトリリストの 
 *	プロトコル
 *
 *	このソースモジュールは rpcgen が生成したソースモジュールです。
 *	これを使用して rpcgen ツールの機能を説明します。
 *
 *	このプログラムは、rpcgen の -h -T のスイッチを使用して
 *	ヘッダーファイル (.h) と付随するデータ構造体も同時に生成して
 *	コンパイルします。
 *
 */
const MAXNAMELEN = 255;	/* ディレクトリエントリの最大長 */

typedef string nametype<MAXNAMELEN>; /* ディレクトリエントリ */
typedef struct namenode *namelist;   /* リスト内のリンク */
/*
 * ディレクトリリスト内のノード struct namenode
 */
struct namenode {
 	nametype name;       /* ディレクトリエントリ名 */
 	namelist next;       /* 次のエントリ */
 };

/*
 * READDIR 操作の結果:
 *	完全に移植可能なアプリケーションにするには、ここで行なっているように
 *	UNIX の errno を使用せずに、取り決めに従ったエラーコードリストを
 *	使用します。この例では、遠隔呼び出しが成功したか失敗したかを
 *	識別するのに共用体を使用しています。
 */
union readdir_res switch (int errno) {
	case 0:
		namelist list; /* エラーなし: ディレクトリリストを返す */
 default:
		void;      /* エラー発生: 戻り値なし */
};

/*
 * ディレクトリリストプログラムの定義
 */
program DIRPROG {
 	version DIRVERS {
 		readdir_res
 		READDIR(nametype) = 1;
 	} = 1;
 } = 0x20000076;


例 D-2 遠隔 dir_proc.c

/*
 * dir_proc.c: 遠隔手続き readdir
 */
#include <rpc/rpc.h>      /* 必ず必要 */
#include <dirent.h>
#include "dir.h"          /*  rpcgen が生成 */

extern int errno;
extern char *malloc();
extern char *strdup();

/* 使用する引数 */
readdir_res *
readdir_1(dirname,req)
 	nametype *dirname;
 	struct svc_req *req;
{
 	DIR *dirp;
 	struct dirent *d;
 	namelist nl;
 	namelist *nlp;
 	static readdir_res res; /* 必ず static で宣言 */

	/*
  * ディレクトリのオープン
	 */
	dirp = opendir(*dirname);
 if (dirp == (DIR *)NULL) {
 		res.errno = errno;
 		return (&res);
 	}
	/*
  * 以前の結果を解放
	 */
	xdr_free(xdr_readdir_res, &res);
	/*
	 * ディレクトリエントリの収集。ここで割り当てられたメモリーは、
  * 次に readdir_1 が呼び出されたときに xdr_free によって解放されます。
	 */

 nlp = &res.readdir_res_u.list;
 	while (d = readdir(dirp)) {
 		nl = *nlp = (namenode *) malloc(sizeof(namenode));
 		if (nl == (namenode *) NULL) {
 			res.errno = EAGAIN;
 			closedir(dirp);
 			return(&res);
 	 }
 		nl->name = strdup(d->d_name);
 		nlp = &nl->next;
 	}
 	*nlp = (namelist)NULL;
 	/* 結果を返す */
 	res.errno = 0;
 	closedir(dirp);
 	return (&res);
}


例 D-3 rls.c クライアント

/*
 * rls.c: 遠隔ディレクトリリスト (クライアント側)
 */

#include <stdio.h>
#include <rpc/rpc.h>	/* 必ず必要 */
#include "dir.h"	    /* rpcgen が生成 */

extern int errno;

main(argc, argv)
 	int argc;
 	char *argv[];
{
 	CLIENT *cl;
 	char *server;
 	char *dir;
 	readdir_res *result;
 	namelist nl;

	if (argc != 3) {
		fprintf(stderr, "usage: %s host directory¥n",
 	argv[0]);
		exit(1);
	}
	server = argv[1];
	 dir = argv[2];
/*
 * コマンド行で指定したサーバー上の MESSAGEPROG の呼び出しに使用する
 * クライアント「ハンドル」を作成します。
 */
	cl = clnt_create(server, DIRPROG, DIRVERS, "visible");
 	if (cl == (CLIENT *)NULL) {
 		clnt_pcreateerror(server);
 		exit(1);
 	}

	result = readdir_1(&dir, cl);
 	if (result == (readdir_res *)NULL) {
 		clnt_perror(cl, server);
 		exit(1);
 	}

/* 遠隔手続きの呼び出しに成功 */

	if (result->errno != 0) {
 /*
	 * 遠隔システムでエラーが発生。エラーメッセージを表示して終了。
	 */
 }
	if (result->errno < sys_nerr)
 		fprintf (stderr, "%s : %s¥n", dir,
 		sys_enlist[result->errno]);
 		errno = result->errno;
 		perror(dir);
 	 exit(1);
 	}

/* ディレクトリリストの取り出しに成功。ディレクトリリストを表示 */
	for(nl = result->readdir_res_u.list; nl != NULL; nl = nl-
>next) {
		printf("%s¥n", nl->name);
	}
exit(0); 

時刻サーバープログラム (rpcgen)


例 D-4 rpcgen プログラム : time.x

/*
 * time.x: 遠隔時刻プロトコル
 */
program TIMEPROG {
 	version TIMEVERS {
 			unsigned int TIMEGET(void) = 1;
 	} = 1;
} = 0x20000044;

#ifdef RPC_SVC
%int *
%timeget_1()
%{
% static int thetime;
%
%	thetime = time(0);
% return (&thetime);
%}
#endif

2 つの数値の合計を求めるプログラム (rpcgen)


例 D-5 rpcgen プログラム : 2 つの数値の合計を求める

/* 新たな rpcgen 機能を説明するために、このプログラムには
 * 2 つの数値を加える手続きが入っています。ここでは add() が 2 つの引数を取ること
 * に注意してください。
 */
program ADDPROG {			/* プログラム番号 */
	version ADDVER {		/* バージョン番号 */
 	int add ( int, int )  	/* 手続き */
			= 1;
	} = 1;
} = 199;

スプレイパケットプログラム (rpcgen)

このツールの使用方法については、spray(1M) のマニュアルページの注を参照してください。


例 D-6 rpcgen プログラム : spray.x

/*
 * 著作権所有 (c) 1987 年、1991 年 サンマイクロシステムズ社
 */

/* spray.x から */

#ifdef RPC_HDR
#pragma ident "@(#)spray.h  1.2  91/09/17 SMI"
#endif

/*
 * サーバーにパケットをスプレイします。
 * ネットワークインタフェースのもろさのテストに使用します。
 */

const SPRAYMAX = 8845;	/* スプレイ可能な最大量 */

/*
 * 1970 年 1 月 1 日 0:00 からの GMT
 */
struct spraytimeval {
 unsigned int sec;
	unsigned int usec;
};

/*
 * スプレイ統計情報
 */
struct spraycumul {
 unsigned int counter;
	spraytimeval clock;
};

/*
 * スプレイデータ
 */
typedef opaque sprayarr<SPRAYMAX>;

program SPRAYPROG {
	version SPRAYVERS {
		/*
 	 * データを捨てて、カウンタをインクリメントします。この呼び出しは
		 * リターンしないため、クライアントは必ずタイムアウトになります。
 	 */
		void
		SPRAYPROC_SPRAY(sprayarr) = 1;

	 /*
		 * カウンタ値と、最後にクリアしたときからの経過時間を取り出します。
		 */
		spraycumul	
		SPRAYPROC_GET(void) = 2;

	 /*
		 * カウンタをクリアし、経過時間をリセットします。
		 */
 	void
		SPRAYPROC_CLEAR(void) = 3;
	} = 1;
} = 100012;

メッセージ表示プログラムとその遠隔バージョン


例 D-7 printmesg.c

/* printmsg.c: コンソールにメッセージを表示 */
#include <stdio.h>

main(argc, argv)
 	int argc;
 	char *argv[];
{
 char *message;

	if (argc != 2) {
 	fprintf(stderr, "usage: %s <message>¥n", argv[0]);
		exit(1);
 }
	message = argv[1];
	if( !printmessage(message) ) {
		fprintf(stderr, "%s: couldn't print your message¥n",
 	        argv[0]);
		exit(1);
	}
 printf("Message Delivered!¥n");
	exit(0);
}

/* コンソールにメッセージを表示します。 */

/*
 * メッセージが実際に表示されたかどうかを示すブール値を返します。
 */
printmessage(msg)
	char *msg;
{
 FILE *f;

	if = fopen("/dev/console","w");
 	if (f == (FILE *)NULL)
			return (0);
	fprintf(f,"%sen", msg);
	fclose(f);
	return (1);
}


例 D-8 printmesg.c の遠隔バージョン

/*  * rprintmsg.c: printmsg.c の遠隔バージョン  */ 
#include <stdio.h> 
#include <rpc/rpc.h> /* 必ず必要 */ 
#include "msg.h"     /* msg.h は rpcgen が生成 */ 

main(argc, argv) 
	int argc;
	char *argv[];
{
 CLIENT *cl; 
	int *result;  	
	char *server;
 char *message;
	extern int sys_nerr;
	extern char *sys_errlist[]; 

	if (argc != 3) { 
 	fprintf(stderr,"usage: %s host messagen", argv[0]);
		exit(1);
 }
	/* 
	 * コマンド行で指定した引数の値を保存します。
  */
	server = argv[1];
	message = argv[2]; 
/*
 * コマンド行で指定したサーバー上の MESSAGEPROG の呼び出しに使用する
 * クライアント「ハンドル」を作成します。
 */ 
	cl = clnt_create(server, MESSAGEPROG, PRINTMESSAGEVERS,
                  "visible");
	if (cl == (CLIENT *)NULL) {
 	/* 
		 * サーバーとの接続の確立に失敗。
		 * エラーメッセージを表示して終了します。
 	 */
		clnt_pcreateerror(server);
		exit(1);
 }
	/* サーバー上の遠隔手続き printmessage を呼び出します。 */
 result = printmessage_1(&message, cl);
	if (result == (int *)NULL) {  	
	/*
 * サーバーの呼び出しでエラーが発生。
  * エラーメッセージを表示して終了します。
	 */ 
		clnt_perror(cl, server);
		exit(1);
 }
	/* 遠隔手続きの呼び出しに成功。 */
	if (*result == 0) { 
 	/*
 	 * サーバーはメッセージの表示に失敗。
		 * エラーメッセージを表示して終了します。
 	 */
		fprintf(stderr,"%s"
	}
/* サーバーのコンソールにメッセージが出力されました。 */
	printf("Message delivered to %s!¥n", server);
	exit(0);
}


例 D-9 rpcgen プログラム : msg.x

/* msg.x: 遠隔メッセージ印刷プロトコル */
program MESSAGEPROG {
 	version MESSAGEVERS {
 		int PRINTMESSAGE(string) = 1;
 	} = 1;
} = 0x20000001;


例 D-10 mesg_proc.c

/*
 *  msg_proc.c: 遠隔手続き printmessage
 */

#include <stdio.h>
#include <rpc/rpc.h> 	/* 必ず必要 */
#include "msg.h" 	/* msg.h は rpcgen が生成 */

/*
 * printmessage の遠隔バージョン
 */
/* 使用する引数 */
int printmessage_1(msg, req)
	char **msg;
	struct svc_req *req;
{
 static int result; /* 必ず static で宣言 */
	FILE *f;

	f = fopen("/dev/console", "w");
	if (f == (FILE *)NULL) {
 	result = 0;
 	return (&result);
	}
 fprintf(f, "%sen", *msg);
 	fclose(f);
 	result = 1;
 	return (&result);
}

バッチコードの例


例 D-11 バッチを使用するクライアントプログラムの例

#include <stdio.h>
#include <rpc/rpc.h>
#include "windows.h"

main(argc, argv)
 int             argc;
	char          **argv;
{
 struct timeval  total_timeout;
	register CLIENT *client;
 enum clnt_stat  clnt_stat;
	char            buf[1000], *s = buf;

	if ((client = clnt_create(argv[1], WINDOWPROG, WINDOWVERS,
	              "CIRCUIT_V")) == (CLIENT *) NULL) {
		clnt_pcreateerror("clnt_create");
		exit(1);
 }

	timerclear(&total_timeout);
 while (scanf("%s", s) != EOF) {
		clnt_call(client, RENDERSTRING_BATCHED, xdr_wrapstring,
		  &s,xdr_void, (caddr_t) NULL, total_timeout);
 }

	/* ここでパイプラインをフラッシュ */
	total_timeout.tv_sec = 20;
	clnt_stat = clnt_call(client, NULLPROC, xdr_void,
 		 (caddr_t) NULL, xdr_void, (caddr_t) NULL,
total_timeout);
 if (clnt_stat != RPC_SUCCESS) {
		clnt_perror(client, "rpc");
 	exit(1);
	}
	clnt_destroy(client);
 exit(0);
}


例 D-12 バッチを使用するサーバープログラムの例

#include <stdio.h>
#include <rpc/rpc.h>
#include "windows.h"

void            windowdispatch();
main()
{
 int num;

	num = svc_create(windowdispatch, WINDOWPROG, WINDOWVERS,
	      "CIRCUIT_V");
	if (num == 0) {
		fprintf(stderr, "can't create an RPC server¥n");
 	exit(1);
	}
	svc_run();                       /* 戻らない */
	fprintf(stderr, "should never reach this point¥n");
}

void
windowdispatch(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT        *transp;
{
	char           *s = NULL;

	switch (rqstp->rq_proc) {
		case NULLPROC:
			if (!svc_sendreply(transp, xdr_void, 0))
				fprintf(stderr, "can't reply to RPC call¥n");
 		return;
		case RENDERSTRING:
			if (!svc_getargs(transp, xdr_wrapstring, &s)) {
				fprintf(stderr, "can't decode arguments¥n");
				/* 呼び出し側にエラーを通知 */
				svcerr_decode(transp);
 			break;
			}
			/* 文字列 s を処理するコード */
 		if (!svc_sendreply(transp, xdr_void, (caddr_t) NULL))
		 	fprintf(stderr, "can't reply to RPC call¥n");
			break;
 	case RENDERSTRING_BATCHED:
 		if (!svc_getargs(transp, xdr_wrapstring, &s)) {
				fprintf(stderr, "can't decode arguments¥n");
 			/* プロトコルエラーのため何も返さない */
 			break;
			}
 		/* 文字列 s を処理するコード。ただし応答はしない。 */
			break;
 	default:
			svcerr_noproc(transp);
 		return;
 }
	/* 引数の復号化で割り当てた文字列を解放 */
 svc_freeargs(transp, xdr_wrapstring, &s);
}

バッチを使用しない例

次のプログラムは参考のためだけに示します。例 D-11 で示したバッチを使用するクライアントプログラムの例を、バッチを使用しないように書き直したものです。


例 D-13 バッチを使用しないクライアントプログラムの例

#include <stdio.h>
#include <rpc/rpc.h>
#include "windows.h"

main(argc, argv)
 int             argc;
	char          **argv;
{
 struct timeval  total_timeout;
	register CLIENT *client;
 enum clnt_stat  clnt_stat;
	char            buf[1000], *s = buf;

	if ((client = clnt_create(argv[1], WINDOWPROG, WINDOWVERS,
	            "CIRCUIT_V")) == (CLIENT *) NULL) {
		clnt_pcreateerror("clnt_create");
		exit(1);
 }
	total_timeout.tv_sec = 20;
	total_timeout.tv_usec = 0;
 while (scanf("%s", s) != EOF) {
		if(clnt_call(client, RENDERSTRING, xdr_wrapstring, &s,
		    xdr_void, (caddr_t) NULL, total_timeout) != RPC_SUCCESS) {
			clnt_perror(client, "rpc");
 		exit(1);
		}
	}
 clnt_destroy(client);
	exit(0);
}