XDR データ型は形式言語で記述する必要があるのと同じように、XDR データ型に対して動作する手続きも形式言語で記述する必要があります。XDR 言語の拡張版である RPC 言語は、XDR 言語を形式言語で記述するための言語です。次に、RPC 言語についての例を示します。
次のコーディング例は、シンプルな ping プログラムの仕様を示します。
/*
* シンプルな ping プログラム
*/
program PING_PROG {
version PING_VERS_PINGBACK {
void
PINGPROC_NULL(void) = 0;
/*
* 呼び出し側 ping は往復時間をミリ秒単位で返します。
* 処理が時間切れとなった場合は -1 を返します。
*/
int
PINGPROC_PINGBACK(void) = 1;
/* void - 上記はこの呼び出しへの引数 */
} = 2;
/*
* オリジナルのバージョン
*/
version PING_VERS_ORIG {
void
PINGPROC_NULL(void) = 0;
} = 1;
} = 200000;
const PING_VERS = 2; /* 最新バージョン */
記述された最初のバージョンは、2 つの手続き、PINGPROC_NULL、および PINGPROC_PINGBACK が組み込まれた PING_VERS_PINGBACKです。
PINGPROC_NULL は引数を必要とせず、結果も返しませんが、クライアントとサーバー間の往復時間を計算するときになどに便利です。規則によると、RPC プログラムの手続き 0 はすべて同じセマンティクスを持つことになっているので、認証は必要ありません。
2 番目の手続きは、処理にかかった合計時間をマイクロ秒で返します。
次のバージョンである PING_VERS_ORIG は、プロトコルのオリジナルのバージョンで、PINGPROC_PINGBACK 手続きは含まれません。これは古いクライアントプログラムと互換性を持たせるために便利です。
RPC 言語 (RPCL) は C に似ています。この節では、例を含め RPC 言語の構文を説明します。また、出力ヘッダーファイルで、RPC 型定義および XDR 型定義を C 型定義にコンパイルする方法についても説明します。
RPC 言語ファイルは次の一連の定義から構成されています。
definition-list:
definition;
definition; definition-list
このファイルは 6 つの型の定義を認識します。
definition:
enum-definition
const-definition
typedef-definition
struct-definition
union-definition
program-definition
定義は宣言と同じではありません。1 つまたは一連のデータ要素の型定義以外の定義によっては領域を割り当てることはできません。これは、変数は定義するだけでは十分でなく、宣言もする必要があることを意味しています。
RPC 言語は、次のテーブルを記述する定義が追加されている点を除けば XDR 言語と同一です。
表 B–2 RPC 言語の定義|
用語 |
定義 |
|---|---|
|
program program-ident {version-list} = value |
|
|
version; version; version-list |
|
|
version version-ident {procedure-list} = value |
|
|
procedure; procedure; procedure-list |
|
|
type-ident procedure-ident (type-ident) = value |
RPC 言語では、
program、version のキーワードが追加されますが、識別子としては使用できません。
バージョン名およびバージョン番号は、プログラム定義範囲内で一度しか指定できません。
手続き名および手続き番号は、バージョン定義内で一度しか指定できません。
プログラム識別子は、定数および型識別子と同じ名前空間にあります。
enum-definition:
"enum" enum-ident "{"
enum-value-list
"}"
enum-value-list:
enum-value
enum-value "," enum-value-list
enum-value:
enum-value-ident
enum-value-ident "=" value
次に、コンパイルされる XDR enum および C enum の例を示します。
enum colortype { enum colortype {
RED = 0, RED = 0,
GREEN = 1, --> GREEN = 1,
BLUE = 2 BLUE = 2,
}; };
typedef enum colortype colortype;
整数の定数を使用する場合は、XDR シンボリック定数を使用できます。たとえば、配列サイズの指定に使用します。
const-definition: const const-ident = integer
次の例では定数 DOZEN を 12 に定義します。
const DOZEN = 12; --> #define DOZEN 12
XDR typedef の構文は、C typedef と同じです。
typedef-definition:
typedef declaration
この例では、最大 255 文字のファイル名の文字列を宣言するために使用する fname_type を定義します。
typedef string fname_type<255>; --> typedef char *fname_type;
XDR には 4 種類の宣言があります。これらの宣言は、struct または typedef の一部でなければならず、単独では使用できません。
declaration:
simple-declaration
fixed-array-declaration
variable-array-declaration
pointer-declaration
simple-declaration:
type-ident variable-ident
次に例を示します。
colortype color; --> colortype color;
fixed-array-declaration:
type-ident variable-ident [value]
次に例を示します。
colortype palette[8]; --> colortype palette[8];
変数宣言を型宣言と混同するプログラマがよくいます。rpcgen は変数宣言をサポートしない点に注意してください。次はコンパイルできないプログラムの例です。
int data[10];
program P {
version V {
int PROC(data) = 1;
} = 1;
} = 0x200000;
上記の例は、変数宣言なのでコンパイルされません。
int data[10]
代わりに以下を使用します。
typedef int data[10];
または以下を使用します。
struct data {int dummy [10]};
可変長配列宣言は、C 構文とはまったく異なります。 XDR 言語は、構文を使用しないで山括弧で囲みます。
variable-array-declaration:
type-ident variable-ident <value>
type-ident variable-ident <>
最大サイズは山括弧内で指定します。サイズ指定は省略できます。この場合、配列は任意の長さとなります。
int heights<12>; /* 最高 12 項目 */ int widths<>; /* 項目数に制限なし */
可変長配列が C 構文とまったく異なるので、これらの宣言はコンパイルされてstruct 宣言になります。たとえば、 heights 宣言は struct へコンパイルされます。
struct {
u_int heights_len; /* # 配列の項目番号 */
int *heights_val; /* 配列へのポインタ */
} heights;
配列の項目の番号は、_len 構成要素に、配列へのポインタは _val 構成要素に格納されます。各構成要素名のはじめの部分は、宣言された XDR 変数名 heights と同じです。
XDR でポインタ宣言は、C で行われる場合とまったく同様に行われます。アドレスポインタは、実際にはネットワーク上に送信されないのに対して、XDR ポインタは、リストおよびツリーなどの再帰的なデータ型を送信するのに有効です。この型は、XDR 言語ではポインタではなく、オプションデータと呼ばれます。
ポインタ宣言: type-ident *variable-ident
次に例を示します。
listitem *next; --> listitem *next;
RPC/XDR struct は C struct とほぼ同様に宣言されます。RPC/XDR struct の宣言は次のようになります。
struct-definition:
struct struct-ident "{"
declaration-list
"}"
declaration-list: declaration ";" declaration ";" declaration-list
次の左の部分は二次元の座標の XDR 構造体の例で、右の部分はそれを C 言語にコンパイルした構造体です。
struct coord { struct coord {
int x; --> int x;
int y; int y;
}; };
typedef struct coord coord;
出力は、出力の末端部で追加された typedef 以外は入力と同じです。この typedef では、アイテムを宣言する際に、 struct coord の代わりに coord を使用できます。
XDR 共用体は識別型の共用体で、C 共用体とは異なります。これらは Pascal の可変レコードに似ています。
union-definition:
"union" union-ident "switch" "("simple declaration")" "{"
case-list
"}"
case-list:
"case" value ":" declaration ";"
"case" value ":" declaration ";" case-list
"default" ":" declaration ";"
以下は、「読み取りデータ」操作の結果として返された型の例です。エラーが発生しなければ、データのブロックを返します。 エラーがある場合は、何も返されません。
union read_result switch (int errno) {
case 0:
opaque data[1024];
default:
void;
};
この共用体は次のようにコンパイルされます。
struct read_result {
int errno;
union {
char data[1024];
} read_result_u;
};
typedef struct read_result read_result;
出力 struct の共用体構成要素の名前が、接尾辞 _u を除いて型の名前と同じ名称であることに注意してください。
program-definition:
"program" program-ident "{"
version-list
"}" "=" value;
version-list:
version ";"
version ";" version-list
version:
"version" version-ident "{"
procedure-list
"}" "=" value;
procedure-list:
procedure ";"
procedure ";" procedure-list
procedure:
type-ident procedure-ident "(" type-ident ")" "=" value;
-N オプションが指定されると、rpcgen は次の構文も認識できます。
手続き:
type-ident procedure-ident "(" type-ident-list ")" "=" value;
type-ident-list:
type-ident
type-ident "," type-ident-list
次に例を示します。
/*
* time.x: 時間を取得、または設定します。
* 時間は、1970 年 1 月 1 日 0:00 から経過した秒数で表されます。
*/
program TIMEPROG {
version TIMEVERS {
unsigned int TIMEGET(void) = 1;
void TIMESET(unsigned) = 2;
} = 1;
} = 0x20000044;
|
void という引数の型は、引数が渡されないことを意味しています。
次のファイルはコンパイルされると、出力ヘッダーファイル内でこれらの#define 文になります。
#define TIMEPROG 0x20000044 #define TIMEVERS 1 #define TIMEGET 1 #define TIMESET 2
C 形式モードでは、rpcgen への void 引数の渡し方が異なります。値が void の場合、引数が渡される必要はありません。
C には組み込み型のブール型はありません。ただし、RPC ライブラリは、TRUE または FALSE のうちいずれかの bool_t と呼ばれるブール値を使用します。XDR 言語で型 bool として宣言されたパラメータは、コンパイルされると、出力ヘッダーファイルで bool_t になります。
次に例を示します。
bool married; --> bool_t married;
C 言語は組み込み型の文字列型ではありませんが、代わりに null で終了する char * 規則を使用します。C では、文字列は通常 null で終了する単一配列であるとみなされます。
XDR 言語では、string キーワードを使用して文字列が宣言されて、出力ヘッダーファイルで char * 型にコンパイルされます。山括弧でくくられた最大サイズは、文字列で使用できる最大文字数を指定します。NULL 文字をカウントしません。最大サイズは省略できます。この場合、文字列は任意の長さとなります。
次に例を示します。
string name<32>; --> char *name; string longname<>; --> char *longname;
NULL 文字列は渡されません。ただし、0 長の文字列 (つまりターミネータだけ、または NULL バイト) は渡されます。
隠されたデータは、未入力のデータ、つまり任意のバイトのシーケンスを記述するために、XDR で使用されます。隠されたデータは固定長配列または可変長配列として宣言できます。
opaque diskblock[512]; --> char diskblock[512];
opaque filedata<1024>; --> struct {
u_int filedata_len;
char *filedata_val;
} filedata;
void 宣言では、変数を指定できません。void 宣言には、void 以外何も記述しません。 void 宣言は次の 2 箇所でのみ行います。 リモートプロシージャの引数または戻り値として、共用体の定義とプログラムの定義 (引数が渡されないなどで使用される) です。たとえば、引数は渡されません。
rpcbind は RPC のプログラム番号とバージョン番号を汎用アドレスにマップし、リモートプログラムの動的結合を可能にします。
rpcbind はそれをサポートしているトランスポートのよく知られたアドレスに結合しています。他のプログラムは、動的に割り当てられたアドレスを rpcbind で登録します。rpcbind は、それらのアドレスを一般に使用できるようにします。汎用アドレスとは、トランスポートに依存したアドレスで、文字列で表現されています。汎用アドレスは、各トランスポートのアドレス管理者が定義します。
rpcbind はブロードキャスト RPC にも利用できます。RPC プログラムでは、マシンが異なる場合、アドレスも異なるため、これらのプログラムすべてに直接ブロードキャスト通信を行うことは不可能です。ところが、rpcbind のアドレスはわかっています。 そのため、特定のプログラムへブロードキャスト通信を行うには、クライアントは送信先マシン上にある rpcbind プロセスへメッセージを送信します。 rpcbind はブロードキャストメッセージを取り出し、クライアントが指定したローカルサービスを呼び出します。rpcbind はローカルサービスからの応答を取り出すと、それをクライアントに送信します。
次のコーディング例は、RPC 言語の rpcbind プロトコル仕様を示します。
/*
* rpcb_prot.x
* rpc 言語で記述した RPCBIND プロトコル
*/
/*
* (プログラム、バージョン、ネットワーク ID) の汎用アドレスへの割り当て
*/
struct rpcb {
rpcproc_t r_prog; /* プログラム番号 */
rpcvers_t r_vers; /* バージョン番号 */
string r_netid<>; /* ネットワーク ID */
string r_addr<>; /* 汎用アドレス */
string r_owner<>; /* このサービスの所有者 */ };
/* 割り当てのリスト */
struct rpcblist {
rpcb rpcb_map;
struct rpcblist *rpcb_next;
};
/* リモート呼び出しの引数 */
struct rpcb_rmtcallargs {
rpcprog_t prog; /* プログラム番号 */
rpcvers_t vers; /* バージョン番号 */
rpcproc_t proc; /* 手続き番号 */
opaque args<>; /* 引数 */
};
/* リモート呼び出しの戻り値 */
struct rpcb_rmtcallres {
string addr<>; /* リモート汎用アドレス */
opaque results<>; /* 戻り値 */
};
/*
* rpcb_entry には、特定のトランスポート上のサービスの
* マージされたアドレスと関連付けられた netconfig 情報を含みます。
* RPCBPROC_GETADDRLIST は rpcb_entry のリストを返します。
* r_nc_* フィールドで使用できる値については、netconfig.h を
* 参照してください。
*/
struct rpcb_entry {
string r_maddr<>; /* サービスのマージされたアドレス */
string r_nc_netid<>; /* netid フィールド */
unsigned int r_nc_semantics; /* トランスポートのセマンティクス */
string r_nc_protofmly<>; /* プロトコルファミリ */
string r_nc_proto<>; /* プロトコル名 */
};
/* サービスがサポートするアドレスのリスト */
struct rpcb_entry_list {
rpcb_entry rpcb_entry_map;
struct rpcb_entry_list *rpcb_entry_next;
};
typedef rpcb_entry_list *rpcb_entry_list_ptr;
/* rpcbind 統計情報 */
const rpcb_highproc_2 = RPCBPROC_CALLIT;
const rpcb_highproc_3 = RPCBPROC_TADDR2UADDR;
const rpcb_highproc_4 = RPCBPROC_GETSTAT;
const RPCBSTAT_HIGHPROC = 13; /* rpcbind V4 内の手続きに 1 を足した数 */
const RPCBVERS_STAT = 3; /* rpcbind V2、V3、V4 だけのために提供 */
const RPCBVERS_4_STAT = 2;
const RPCBVERS_3_STAT = 1;
const RPCBVERS_2_STAT = 0;
/* getport と getaddr に関するすべての状態のリンクリスト */
struct rpcbs_addrlist {
rpcprog_t prog;
rpcvers_t vers;
int success;
int failure;
string netid<>;
struct rpcbs_addrlist *next;
};
/* rmtcall に関するすべての状態のリンクリスト*/
struct rpcbs_rmtcalllist {
rpcprog_t prog;
rpcvers_t vers;
rpcproc_t proc;
int success;
int failure;
int indirect; /* 直接的に呼び出すか、間接的に呼び出すか */
string netid<>;
struct rpcbs_rmtcalllist *next;
};
typedef int rpcbs_proc[RPCBSTAT_HIGHPROC];
typedef rpcbs_addrlist *rpcbs_addrlist_ptr;
typedef rpcbs_rmtcalllist *rpcbs_rmtcalllist_ptr;
struct rpcb_stat {
rpcbs_proc info;
int setinfo;
int unsetinfo;
rpcbs_addrlist_ptr addrinfo;
rpcbs_rmtcalllist_ptr rmtinfo;
};
/*
* 監視する rpcbind のバージョン 1 つに対して
* 1 つの rpcb_stat 構造体が返される。
*/
typedef rpcb_stat rpcb_stat_byvers[RPCBVERS_STAT];
/* rpcbind 手続き */
program RPCBPROG {
version RPCBVERS {
void
RPCBPROC_NULL(void) = 0;
/*
* [r_prog, r_vers, r_addr, r_owner,r_netid] の組み合わせを登録。
* セキュリティー上の理由から、rpcbind サーバーはこの手続きの要求を
* ループバックトランスポートのみで受け付ける。成功の場合は真を、
* 失敗の場合は偽を返す。
*/
bool
RPCBPROC_SET(rpcb) = 1;
/*
* [r_prog, r_vers, r_owner, r_netid] の組み合わせを登録解除。
* vers がゼロの場合、すべてのバージョンが登録解除される。
* セキュリティー上の理由から、rpcbind サーバーは
* この手続きの要求をループバックトランスポート
* のみで受け付ける。成功の場合は真を、失敗の場合は偽を返す。
*/
bool
RPCBPROC_UNSET(rpcb) = 2;
/*
* [r_prog, r_vers, r_netid] の組み合わせが登録されている
* 汎用アドレスを返す。r_addr を指定すると、
* r_addr へマージされた汎用アドレスを返す。
* r_owner は無視する。失敗の場合は偽を返す。
*/
string
RPCBPROC_GETADDR(rpcb) = 3;
/* すべての割り当てのリストを返す。 */
rpcblist
RPCBPROC_DUMP(void) = 4;
/*
* リモートマシン上の手続きを呼び出す。
* 登録されていない場合はこの手続きは
* 何も出力しない。つまり、エラー情報を返さない。
*/
rpcb_rmtcallres
RPCBPROC_CALLIT(rpcb_rmtcallargs) = 5;
/*
* rpcbind サーバーシステム上の時刻を返す。
*/
unsigned int
RPCBPROC_GETTIME(void) = 6;
struct netbuf
RPCBPROC_UADDR2TADDR(string) = 7;
string
RPCBPROC_TADDR2UADDR(struct netbuf) = 8;
} = 3;
version RPCBVERS4 {
bool
RPCBPROC_SET(rpcb) = 1;
bool
RPCBPROC_UNSET(rpcb) = 2;
string
RPCBPROC_GETADDR(rpcb) = 3;
rpcblist_ptr
RPCBPROC_DUMP(void) = 4;
/*
* 注: RPCBPROC_BCAST は CALLIT と同じ機能を持つ。
* 新しい名前の目的は RPCBPROC_BCAST はブロードキャスト RPC に
* 使用し、RPCBPROC_INDIRECT は間接呼び出しに使用する
* ことを示すため。
*/
rpcb_rmtcallres
RPCBPROC_BCAST(rpcb_rmtcallargs) = RPCBPROC_CALLIT;
unsigned int
RPCBPROC_GETTIME(void) = 6;
struct netbuf
RPCBPROC_UADDR2TADDR(string) = 7;
string
RPCBPROC_TADDR2UADDR(struct netbuf) = 8;
/*
* RPCBPROC_GETADDR と同じ機能を持つが、
* バージョン番号がわからなければ、
* アドレスが返されない点が異なる。
*/
string
RPCBPROC_GETVERSADDR(rpcb) = 9;
/*
* リモートマシン上の手続きを呼び出す。登録されていない場合は、
* この手続きは出力を行う。つまり、エラー情報を返す。
*/
rpcb_rmtcallres
RPCBPROC_INDIRECT(rpcb_rmtcallargs) = 10;
/*
* RPCBPROC_GETADDR と同じ機能を持つが、
* この組み合わせ (prog, vers) へ
* 登録されたアドレスのリストを返す点が異なる。
*/
rpcb_entry_list_ptr
RPCBPROC_GETADDRLIST(rpcb) = 11;
/*
* rpcbind サーバーの動作に関する統計情報を返す。
*/
rpcb_stat_byvers
RPCBPROC_GETSTAT(void) = 12;
} = 4;
} = 100000;
rpcbind にアクセスするには、使用するトランスポートごとに割り当てられているアドレスを使用します。たとえば TCP/IP と UDP/IP の場合は、ポート番号 111 が割り当てられています。各トランスポートには、このようによく知られているアドレスがあります。この節では、rpcbind がサポートしている各手続きを説明します。
この手続きは何もしない手続きです。習慣的にどのプログラムでも、手続き 0 は引数も戻り値もない手続きとします。
マシン上でプログラムが初めて使用可能になるときは、そのマシンで実行されている rpcbind に自分自身を登録します。プログラムは次を渡します。 プログラム番号 prog、バージョン番号 vers、ネットワーク ID netid、および、プログラムがサービス要求を待機する汎用アドレス uaddr。
この手続きは、プログラムのマッピングに成功すれば TRUE、失敗すれば FALSE のブール値を返します。指定された (prog、 vers、 netid) の組み合わせで既にマップされたものがあれば、新たなマップは行いません。
netid と uaddr はどちらも NULL にはできません。また、netid には、呼び出しを行うマシン上のネットワーク ID が正しく指定されていなければなりません。
プログラムが使用できなくなった場合は、同一マシン上の rpcbind で自分自身を登録解除する必要があります。
この手続きの引数と戻り値は、RPCBPROC_SET と同じです。(prog、vers、netid) の組み合わせと uaddr のマッピングが削除されます。
netid が NULL の場合は、(prog、vers、*) 組み合わせとそれに対応する汎用アドレスのマッピングがすべて削除されます。サービスの登録解除は、サービスの所有者かスーパーユーザーだけが実行できます。
プログラム番号 prog、バージョン番号 vers、ネットワークID netid を指定してこの手続きを呼び出すと、そのプログラムが呼び出し要求を待っている汎用アドレスが返されます。
引数の netid フィールドは無視され、要求が到着するトランスポートの netid から取り出します。
この手続きは、rpcbind データベースの全エントリのリストを返します。
この手続きには引数がなく、戻り値は、プログラム、バージョン、ネットワーク ID、汎用アドレスのリストです。この手続きを呼び出すときは、データグラムトランスポートではなくストリームトランスポートを使用します。これは、大量のデータが返されるのを回避するためです。
この手続きを使用すると、汎用アドレスがわからなくても同一マシン上にあるリモートプロシージャを呼び出すことができます。 RPCBPROC_CALLIT は、rpcbind の汎用アドレス経由での任意のリモートプログラムへのブロードキャスト通信をサポートします。
パラメータ prog、vers、proc、args_ptr にはそれぞれプログラム番号、バージョン番号、手続き番号、リモートプロシージャへの引数を指定します
この手続きは正常終了の場合は応答しますが、異常終了の場合は一切応答しません。
この手続きからは、リモートプログラムの汎用アドレスと、リモートプロシージャからの戻り値が返されます。
この手続きは、自分のマシンのローカル時刻を、1970 年 1 月 1 日午前 0 時からの秒数で返します。
この手続きは、汎用アドレスをトランスポート (netbuf) アドレスに変換します。RPCBPROC_UADDR2TADDR は uaddr2taddr() と同じ機能を持ちます。 マニュアルページ netdir(3NSL) を参照してください。名前 - アドレス変換のライブラリモジュールとリンクできないプロセスだけが、この手続きを使用します。
この手続きは、トランスポート (netbuf) アドレスを汎用アドレスに変換します。RPCBPROC_TADDR2UADDR は taddr2uaddr() と同じ機能を持ちます。マニュアルページ netdir(3NSL) を参照してください。名前 - アドレス変換のライブラリモジュールとリンクできないプロセスだけが、この手続きを使用します。
rpcbind のバージョン 4 は、以前の手続きのほかに、次に示す手続きが追加されています。
この手続きは、バージョン 3 の RPCBPROC_CALLIT 手続きと同じです。新たな名前を付けたのは、この手続きはブロードキャスト RPC だけに使用することを示すためです。これに対して、次のテキストで定義する RPCBPROC_INDIRECT は、間接 RPC 呼び出しだけに使用します。
この手続きは、RPCBPROC_GETADDR に似ています。異なる点は、rpcb 構造体の r_vers フィールドで目的のバージョンを指定できることです。そのバージョンが登録されていない場合、アドレスは返されません。
この手続きは、RPCBPROC_CALLIT に似ています。しかし、たとえば呼び出すプログラムがシステムに登録されていないなどのエラーが起こった場合、エラー情報を返す点が異なります。ブロードキャスト RPC でこの手続きを使用しないでください。 この手続きは間接 RPC 呼び出しのみで使用します。
この手続きは、指定された rpcb エントリのアドレスリストを返します。クライアントはそのリストを使用して、サーバーと通信するための代替トランスポートを調べることができます。
この手続きは、rpcbind サーバーの動作に関する統計情報を返します。統計情報には、サーバーが受信した要求の種類と回数が示されます。
RPCBPROC_SET と RPCBPROC_UNSET 以外の手続きはすべて、rpcbind が実行されているマシンとは別のマシン上のクライアントから呼び出すことができます。rpcbind は、RPCPROC_SET と RPCBPROC_UNSET の要求だけはループバックトランスポートからでないと受け入れません。