ONC+ 開発ガイド

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

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

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

アプリケーションの移行

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

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

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

移行の必要性

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

RPC の場合の IPv6 の考慮事項

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

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

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

図 4-4 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 アプリケーション自体に変更が無いこと (つまり、トランスポートに関する情報がまったく無く、トップレベルのインタフェースを使用して書かれたものであること) が必要条件です。

clnt_create()  

svc_create() 

clnt_call() 

clnt_create_timed() 

このインタフェースでは、/etc/netconfig 内の最初のエントリが IPv6 である場合、IPv6 が自動的に選択されます。

IPv6 を使用すると、アプリケーションは、RPCBIND プロトコルの V3 と V4 だけを使用して、サービスバーと番号を配置できます。

clnt_tli_create()  

svc_tli_create() 

clnt_dg_create() 

svc_dg_create() 

clnt_vc_create() 

svc_vc_create() 

上記のいずれかのインタフェースを使用する場合は、コードを移植する必要があります。

特殊事項

libnsl ライブラリ

ネットワーク関数は libc から外されました。コンパイル時には libnsl を明示的に指定して、ネットワーク・サービス・ルーチンをリンクする必要があります。

旧インタフェース

旧インタフェースの多くは libnsl ライブラリでもサポートされていますが、TCP と UDP でしか使用できません。それ以外の新たなトランスポートを利用するには、新インタフェースを使用する必要があります。

名前 - アドレス変換機能

トランスポート独立にするには、アドレスを直接使用できません。すなわち、アプリケーションでアドレス変換を行う必要があります。

TI-RPC と TS-RPC の相違点

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

表 4-11 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 ライブラリ関数を機能別にグループ化して示します。各グループ内では、旧バージョンと同じ関数、機能が追加された関数、旧バージョンにはなかった新規関数に分けて示します。


注 -

アスタリスクの付いた関数は、新バージョンへの移行期間はサポートされていますが、Solaris の将来のバージョンではなくなる可能性があります。


クライアントハンドルの作成

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

clnt_destroy
clnt_pcreateerror
*clntraw_create
clnt_spcreateerror
*clnttcp_create
*clntudp_bufcreate
*clntudp_create
clnt_control
clnt_create
clnt_create_timed
clnt_create_vers
clnt_dg_create
clnt_raw_create
clnt_tli_create
clnt_tp_create
clnt_tp_create_timed
clnt_vc_create
 

サービスの作成と廃棄

次の関数は、旧バージョンと同じで、現在の 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 4.x との互換性呼び出し

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

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

ブロードキャスト

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

*clnt_broadcast 

clnt_broadcast()portmap サービスにだけブロードキャストできます。

portmaprpcbind の両方にブロードキャストできる次の関数が現在の SunOS で使用できます。

rpc_broadcast 

アドレス管理

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 が実行されている場合しか使用できません。

旧バージョンとの比較

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


例 4-47 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 はよく知られた名前でなくてもかまいません。


例 4-48 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);
 

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


例 4-49 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);
}
 

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


例 4-50 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));
}