ONC+ 開発ガイド

付録 D RPC コーディング例

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

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


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

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

typedef string nametype<MAXNAMELEN>; /* ディレクトリエントリ */
typedef struct namenode *namelist;   /*リスト内のリンク*/
/*
 * ディレクトリリスト内のノード
 */
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();

/* ARGSUSED1*/
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 Sun Microsystems, Inc.
 */
/* 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”     /* 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” 	/* rpcgen が 生成 */

/*
 * "printmessage" のリモートバージョン
 */
/*ARGSUSED1*/
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–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);}