ONC+ 開発ガイド

第 6 章 TS-RPC から TI-RPC への移行について

トランスポート独立の RPC ルーチン (TI-RPC ルーチン) を使用すると、アプリケーション開発者はトランスポート層へのアクセスレベルを自由に選択できます。最上位レベルのルーチンは、トランスポートが完全に抽象化されて、本当の意味でトランスポート独立になっています。下位レベルのルーチンを使用すると、旧バージョンと同じように個々のトランスポートに依存したアクセスレベルになります。

この節は、トランスポート特定 RPC (TS-RPC) アプリケーションを TI-RPC へ移行するための非公式ガイドになっています。 表 6–1では、いくつかのルーチンを選んで相違点を示します。ソケットとトランスポート層インタフェース(TLI) の移行の問題点についての詳細は、『プログラミングインタフェース』を参照してください。

アプリケーションの移行

TCP または UDP に基づくアプリケーションはバイナリ互換モードで実行できます。すべてのソースファイルをコンパイルし直したり、リンクし直したりできるのは、一部のアプリケーションだけです。RPC 呼び出しだけを使用し、ソケット、TCP、UDP に固有の機能を使用していないアプリケーションがこれに当たります。

ソケットセマンティクスに依存していたり、TCP や UDP の固有の機能を使用しているアプリケーションでは、コードの変更や追加が必要な場合があります。ホストアドレス形式を使用したり、バークレイ UNIX の特権ポートを使用するアプリケーションがこれに当たります。

ライブラリ内部や個々のソケット仕様に依存していたり、特定のトランスポートアドレスに依存するアプリケーションは、移行の手間も大きく、本質的な変更が必要な場合もあります。

移行の必要性

TI-RPC へ移行することの利点を次に示します。

RPC の場合の IPv6 の考慮事項

IPv6 は、IPv4 の後継バージョンで、今日のインターネットテクノロジーにおいて、最も一般的に使用される階層 2 のプロトコルです。また、IPv6 は IP の次世代 (IPng) とも呼ばれています。詳細については、『Solaris のシステム管理 (IP サービス)』を参照してください。

ユーザーは、IPv4 と IPv6 の両方を使用できます。COTS (コネクション型のトランスポートサービス) を使用する場合、アプリケーションにより、使用する「スタック」が選択されます。この場合、TCP または CLTS (コネクションレス型のトランスポートサービス) を選択できます。

次の図では、IPv4 または IPv6 プロトコルスタックを経由して実行される、典型的な RPC アプリケーションを示しています。

図 6–1 RCP アプリケーション

Graphic

IPv6 がサポートされるのは、TI-RPC アプリケーションだけです。現在、TS-RPC では、IPv6 をサポートしていません。TI-RPC におけるトランスポートの選択は、NETPATH 環境変数、または /etc/netconfig のいずれかによって制御されます。

IPv4 または IPv6 の代わりに、TCP または UDP を選択するかどうかは、/etc/netconfig 内の該当エントリの順序によって決まります。/etc/netconfig には、IPv6 に関連する新しいエントリが 2 つあります。また、デフォルトでは、これらのエントリは、ファイルの最初の 2 つのエントリです。まず、TI-RPC が IPv6 を試行します。失敗すると、IPv4 を試行します。この場合、RPC アプリケーション自体に変更が無いこと (つまり、トランスポートに関する情報がまったく無く、トップレベルのインタフェースを使用して書かれたものであること) が必要条件です。

特殊事項

TI-RPC と TS-RPC の相違点

トランスポート独立型の RPC とトランスポート特定の RPC との主な相違点を、次の表に示します。TI-RPC と TS-RPC の比較については、旧バージョンとの比較 のサンプルプログラムを参照してください。

表 6–1 TI-RPC と TS-RPC の相違点

項目 

TI-RPC 

TS-RPC 

デフォルトのトランスポート選択 

TI-RPC では TLI インタフェースを使用する。 

TS-RPC ではソケットインタフェースを使用する。 

RPC アドレス結合 

TI-RPC ではサービスの結合に rpcbind() を使用する。 rpcbind() はアドレスを汎用アドレス形式で扱う。

TS-RPC ではサービスの結合に portmap を使用する。

トランスポート情報  

トランスポート情報はローカルファイル /etc/netconfig に保存する。netconfig で指定したトランスポートはすべてアクセス可能になる。

トランスポートは TCP と UDP だけをサポートする。 

ループバックトランスポート  

rpcbind サービスではサーバー登録に安全なループバックトランスポートが必要。

TS-RPC サービスではループバックトランスポートは不要。 

ホスト名の解決 

TI-RPC のホスト名の解決順序は、/etc/netconfig で指定した動的ライブラリのエントリ順で決定される。

ホスト名の解決はネームサービスが実行する。順序は hosts データベースの状態で設定される。

ファイル記述子 

ファイル記述子は TLI 端点とみなす。 

ファイル記述子はソケットとみなす。 

rpcgen

TI-RPC の rpcgen では、複数引数、値渡し、サンプルクライアントとサンプルサーバーファイルのサポートを追加。

SunOS 4.1 と直前のリリースは TI-RPC rpcgen に対して一覧表示された特徴はサポートしない。

ライブラリ 

TI-RPC では、アプリケーションが libnsl ライブラリにリンクしていることを必要とする。

TS-RPC の機能はすべて libc で提供される。

マルチスレッドのサポート 

マルチスレッド RPC クライアントとサーバーがサポートされる。 

マルチスレッド RPC はサポートされない。 

関数の互換性のリスト

この節では、RPC ライブラリ関数を機能別にグループ分けして示します。各グループ内では、旧バージョンと同じ関数、機能が追加された関数、旧バージョンにはなかった新規関数に分けて示します。


注 –

アスタリスクの付いた関数は、新バージョンへの移行のために残されているものです。将来のバージョンではなくなる可能性もあります。


サービスの作成と廃棄

次の関数は、旧バージョンと同じで、現在の SunOS で使用できます。

svc_destroy
svcfd_create
*svc_raw_create
*svc_tp_create
*svcudp_create
*svc_udp_bufcreate
svc_create
svc_dg_create
svc_fd_create
svc_raw_create
svc_tli_create
svc_tp_create
svc_vc_create

サービスの登録と登録削除

次の関数は、旧バージョンと同じで、現在の SunOS で使用できます。

*registerrpc
*svc_register
*svc_unregister
xprt_register
xprt_unregister
rpc_reg
svc_reg
svc_unreg

SunOS 互換性呼び出し

次の関数は、旧バージョンと同じで、現在の SunOS リリースで使用できます。

*callrpc
clnt_call
*svc_getcaller - IP ベースのトランスポートでのみ使用可
rpc_call
svc_getrpccaller 

ブロードキャスト

clnt_broadcast 関数の機能は、旧バージョンと同じです。旧バージョンとの互換性を保つためにだけサポートされています。

clnt_broadcast()portmap サービスにだけブロードキャストできます。 rpcbind サービスは、サポートしていません。

rpc_broadcast 関数は、 portmaprpcbind の両方にブロードキャストでき、現在の SunOS リリースで使用できます。

アドレス管理

TI-RPC ライブラリ関数は、portmaprpcbind の両方で使用できます。ただし、それぞれサービスが異なるため、次のように 2 組の関数が提供されています。

次の関数は portmap と共に使用します。

pmap_set
pmap_unset
pmap_getport
pmap_getmaps
pmap_rmtcall 

次の関数は rpcbind と共に使用します。

rpcb_set
rpcb_unset
rpcb_getaddr
rpcb_getmaps
rpcb_rmtcall

認証

次の関数の機能は旧バージョンと同じです。旧バージョンとの互換性を保つためにだけサポートされています。

authdes_create
authunix_create
authunix_create_default
authdes_seccreate
authsys_create
authsys_create_default 

その他の関数

現バージョンの rpcbind ではタイムサービス (主として、安全な RPC のためにクライアント側とサーバー側の時間を同期させるときに使用) が提供されており、rpcb_gettime() 関数で利用できます。pmap_getport()rpcb_getaddr() は、登録サービスのポート番号を取り出すときに使用します。サーバーでバージョンが 2、3、4 の rcpbind が実行されている場合には、rpcb_getaddr() を使用します。pmap_getport() はバージョン 2 が実行されている場合しか使用できません。

旧バージョンとの比較

例 6–1例 6–2では、クライアント作成部分が TS-RPC と TI-RPC とでどう違うかを示します。どちらのプログラムも、次のことを実行します。


例 6–1 TS-RPC におけるクライアント作成

	struct hostent *h;
	struct sockaddr_in sin;
 int sock = RPC_ANYSOCK;
	u_short port;
	struct timeval wait;
 
	if ((h = gethostbyname( "host" )) == (struct hostent *) NULL)
{
		syslog(LOG_ERR, "gethostbyname failed");
		exit(1);
	}
 sin.sin_addr.s_addr = *(u_int *) hp->h_addr;
	if ((port = pmap_getport(&sin, PROGRAM, VERSION, "udp")) == 0) {
	 syslog (LOG_ERR, "pmap_getport failed");
		exit(1);
 } else
		sin.sin_port = htons(port);
	wait.tv_sec = 25;
	wait.tv_usec = 0;
	clntudp_create(&sin, PROGRAM, VERSION, wait, &sock);

次の例で示すように、TI-RPC では、UDP トランスポートは netid udp を持つものとみなします。netid はよく知られた名前でなくてもかまいません。


例 6–2 TI-RPC でのクライアント作成

	struct netconfig *nconf;
	struct netconfig *getnetconfigent();
	struct t_bind *tbind;
	struct timeval wait;

	nconf = getnetconfigent("udp");
	if (nconf == (struct netconfig *) NULL) {
		syslog(LOG_ERR, "getnetconfigent for udp failed");
		exit(1);
	}
	fd = t_open(nconf->nc_device, O_RDWR, (struct t_info *)NULL);
	if (fd == -1) {
		syslog(LOG_ERR, "t_open failed");
		exit(1);
	}
	tbind = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
	if (tbind == (struct t_bind *) NULL) {
		syslog(LOG_ERR, "t_bind failed");
		exit(1);
	}
	if (rpcb_getaddr( PROGRAM, VERSION, nconf, &tbind->addr, "host")
								== FALSE) {
		syslog(LOG_ERR, "rpcb_getaddr failed");
		exit(1);
	}
	cl = clnt_tli_create(fd, nconf, &tbind->addr, PROGRAM, VERSION,
	                      0, 0);
	(void) t_free((char *) tbind, T_BIND);
	if (cl == (CLIENT *) NULL) {
		syslog(LOG_ERR, "clnt_tli_create failed");
		exit(1);
	}
	wait.tv_sec = 25;
	wait.tv_usec = 0;
	clnt_control(cl, CLSET_TIMEOUT, (char *) &wait);

例 6–3例 6–4 では、ブロードキャスト部分が旧バージョンと SunOS 5.x とでどう違うかを示します。SunOS 4.xclnt_broadcast() は SunOS 5.xrpc_broadcast() とほぼ同じです。大きく異なるのは、collectnames() 関数です。 collectnames() 関数は、重複アドレスを削除ブロードキャストに応答したホスト名を表示します。


例 6–3 TS-RPC におけるブロードキャスト

statstime sw;
extern int collectnames();
 
clnt_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,         
    	xdr_void, NULL, xdr_statstime, &sw, collectnames);
	...
collectnames(resultsp, raddrp)
	char *resultsp;
	struct sockaddr_in *raddrp;
{
	u_int addr;
	struct entry *entryp, *lim;
	struct hostent *hp;
	extern int curentry;
 
	/* 重複アドレスはカット */
 addr = raddrp->sin_addr.s_addr;
	lim = entry + curentry;
 for (entryp = entry; entryp < lim; entryp++)
		if (addr == entryp->addr)
			return (0);
	...
 /* ホスト名がわかればホスト名、わからなければアドレスを表示 */
	hp = gethostbyaddr(&raddrp->sin_addr.s_addr, sizeof(u_int),
	    AF_INET);
	if( hp == (struct hostent *) NULL)
		printf("0x%x", addr);
	else
 	printf("%s", hp->h_name);
}
 

次に、TI-RPC におけるブロードキャストを示します。


例 6–4 TI-RPC におけるブロードキャスト

statstime sw;
extern int collectnames();
 
rpc_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,
     xdr_void, NULL, xdr_statstime, &sw, collectnames, (char *)
0);
	...
collectnames(resultsp, taddr, nconf)
	char *resultsp;
	struct t_bind *taddr;
	struct netconfig *nconf;
{
 struct entry *entryp, *lim;
	struct nd_hostservlist *hs;
 extern int curentry;
	extern int netbufeq();
 
	/* 重複アドレスはカット */
	lim = entry + curentry;
	for (entryp = entry; entryp < lim; entryp++)
		if (netbufeq( &taddr->addr, entryp->addr))
			return (0);
 ...
	/* ホスト名がわかればホスト名、わからなければアドレスを表示 */
	if (netdir_getbyaddr( nconf, &hs, &taddr->addr ) == ND_OK)
 	printf("%s", hs->h_hostservs->h_host);
	else {
 	char *uaddr = taddr2uaddr(nconf, &taddr->addr);
		if (uaddr) {
			printf("%s\n", uaddr);
			(void) free(uaddr);
 	} else
			printf("unknown");
	}
}
netbufeq(a, b)
	struct netbuf *a, *b;
{
	return(a->len == b->len && !memcmp( a->buf, b->buf, a->len));
}