名称
prcmd ― 並列リモートコマンドへのストリームのリターン
構文
#include <prcmd.h>
void prcmd_init (
struct prc_host *hostp,
int num_hosts,
int caller_status,
char *command,
time_t timeout
);
int prcmd (
struct prc_host *hostp,
int num_hosts
);
特記事項
これらの関数は、libdc に常駐するもので、 ld または cc のコマンドに -ldc オプションを使用してリンクします。
説明
prcmd() は、クライアント側の呼び出しプログラムがサーバ側の既存デーモン rcmd() を使用できるようになる点で、 rcmd() に似ています。
ただし、 prcmd() では、コマンドをリモートで実行
(接続、コマンド呼び出し、および コマンドの実行/相互動作などのコマンド)
することに伴い、時間のかかるステップが複数のシステムの間で 並列的に実行されます。 rcmd() では、コマンドの実行と相互動作だけが
呼び出し側プログラムの制御に置かれるので、 呼び出し側プログラムでは独自の select() 呼び出しを実行することにより、複数の並列接続を管理しなければなりません。
サーバ側の既存リモートシェル デーモン (remshd)
の機能には、既知のサービス、アクセス制御、およびコマンド行のシェル処理が含まれます。
呼び出し側プログラムでは、 インターネットソケット接続を介して
リモートコマンド処理を開始したり、続行したりするホストのリストで、
最初にあるノードへの ポインタ、 hostp) を prcmd() に渡します。 また、リスト中のホスト番号 num_hosts) も送られます。
呼び出し側プログラムでは、 prcmd() を繰り返し呼び出して、
ホスト接続のステータスを進行させます。 prcmd() を呼び出すたびに、 select() の各コールで、チェックの必要な接続ステータス
(存在する場合) が チェックされるようになります。 読み取り用としての接続
(リモートコマンド) が完了すると、 呼び出し側プログラムでは読み込みが可能になります。また、
オプションとしてここにデータを書き込み、さらにデータを待機して、
再度読み取ることもできます。
ヘッダファイル
<prcmd.h> ヘッダファイルにより、
次のフィールドを含む prc_host 構造が定義されます。
| char | *prc_hostid; | /* host name or IP address | */ |
| int | prc_prev_status; | /* previous connection status | */ |
| int | prc_conn_status; | /* connection status | */ |
| time_t | prc_conn_time; | /* time of connection | */ |
| int | prc_errno; | /* for failed connections | */ |
| char | *prc_errmsg; | /* string from remshd | */ |
| FILE | *prc_fp; | /* file ptr to stdin/stdout | */ |
| FILE | *prc_fp2; | /* file pointer to stderr | */ |
| int | prc_conn_close; | /* flag: close connection | */ |
| int | prc_caller_status; | /* caller's info about conn | */ |
呼び出し側プログラムで変更できるフィールドは、 以下の説明のとおり prc_hostid, prc_conn_close, prc_caller_status,
および prc_conn_time (該当する場合) だけです。
これ以外のフィールドは読み取り専用でなければなりません。
またヘッダファイルでは、次のマクロも定義されます。
| PRC_OK | /* function succeeded, check entries | */ |
| PRC_ERR_NETWORKING | /* no networking on calling program's system | */ |
| PRC_ERR_NOFILE | /* cannot get even one file descriptor | */ |
| PRC_ERR_RCMD | /* cannot get ``shell'' service num/etc. | */ |
| PRC_ERR_SELECT | /* select() failed | */ |
| PRC_CSBIT_ERR | /* connection has errored out | */ |
| PRC_CONN_NONE | /* needs connection | */ |
| PRC_CONN1_WAIT | /* waiting for stdio connect() | */ |
| PRC_CONN2_WAIT | /* waiting for stderr connect() | */ |
| PRC_CONN3_WAIT | /* waiting for remshd reply | */ |
| PRC_READ_WAIT | /* waiting for data | */ |
| PRC_READ_READY | /* data is ready to read | */ |
| PRC_CONN_DONE | /* connection closed | */ |
| PRC_CONN_NO_IPS | /* can't get IP addresses | */ |
| PRC_CONN_FAILED | /* various causes | */ |
| PRC_CONN_REFUSED | /* by remote system | */ |
| PRC_CONN_TIMEOUT | /* during connection attempt | */ |
typedef struct prc_host *prcp_t;
| #define PRC_NULLP | ((prcp_t) 0) |
| #define PRC_SIZE | (sizeof (struct prc_host)) |
データの初期化
prcmd() を呼び出すプログラムでは、各ターゲットシステムごとに 1 つのノードを持つ一連の prc_host 構造を作成する必要があります。
次に、このアレイ hostp) と各サイズ num_hosts) 、各ホストの prc_caller_status フィールドに必要な初期値 caller_status) 、ホストへのリモート接続を開始するときに実行する command および接続のセットアップ時に各ホストに適用する timeout 値を使用して、 prcmd_init() を呼び出します。
この呼び出しにより、各ホストに対する大部分のフィールドの 値がセットされます。
呼び出し側プログラムでは、 prc_caller_status に対する各ホストの値を変更できますが、通常はすべて同じ値に
初期化されます。
最後に、各配列要素にある次のフィールドを初期化してから、最初の prcmd() を呼び出します。
| prc_hostid | | システム名 (ドメインサフィックスの有無を問わず)、
またはドット表記で指定したインターネットアドレスのいずれか。 この値は各ホストごとに異なるので、呼び出し側プログラムは、 prcmd_init() にリストを直接渡すよりも、簡単に値をセットすることができます。 この値は、 呼び出し側プログラムが割り当てるメモリのポインタになります。 prcmd() がホストへの接続を確立するまで、下層のデータを破壊しないようにしてください。
接続が確立された後は、 prc_hostid は必要なくなります。 rcmd() と同様に prcmd() は、ホスト名の参照を指定してインターネットアドレスを
確保しますが、 rcmd() とは異なり、呼び出し側プログラムの prc_hostid 値は変更しません。 |
共通エラーの処理
prcmd() が呼び出されるごとに、ホストリストをスキャンすることにより、 prc_conn_close フィールドと prc_conn_status フィールドに基づいて取るべき動作を探します
(下記参照)。 エラーは通常、次のように処理されます。
| 異常終了 | | あるホストの接続を行っている間にシステムコールが異常終了すると、
下記の場合を除き、 prcmd() は、ホストの prc_conn_status フィールドを PRC_CONN_FAILED にセットし、
異常終了した呼び出しから戻される値 errno に prc_errno をセットした後、
ファイル (ソケット) 記述子と、 ホストに対応するストリームポインタ
(存在する場合) をすべて クローズします。 |
| 拒否 | | 接続のリモート側の (remshd) で異常終了が起きた場合
(下記参照)、 あるいはホストの標準エラーポートに不当な接続を試みた場合、 prcmd() は、 prc_conn_status を PRC_CONN_REFUSED にセットして、
ホストの prc_fp と prc_fp2 の各フィールドで指定されたファイルをクローズします。
リモート remshd からのメッセージ (最大 BUFSIZ-1
文字) があり、 malloc() が正常終了の場合、 prcmd() は prc_errmsg が指す位置にメッセージテキストを保存します。
これ以外の場合、そのフィールドにはヌルポインタが残ります。 また、「ローカル」な理由で拒否が起き、 errno がある場合、 prcmd() は、その値を prc_errno にセットします。これ以外の場合は
0 にセットします。 |
| タイムアウト | | 接続が PRC_CONN1_WAIT, PRC_CONN2_WAIT,
または PRC_CONN3_WAIT のいずれかの状態にあり
(下記参照)、 select() によりホストの準備できていないことが報告され、
かつホストが該当する状態に入ってから timeout 秒が経過すると、接続がタイムアウトになります。 テストは、 time() を使用して秒単位の精度で行われます。
この場合、 prcmd() は、ホストに対する prc_conn_status フィールドを PRC_CONN_TIMEOUT にセットし、ホストの prc_fp と prc_fp2 の各フィールドで指定されたファイルをクローズします。 名前で指定されたホストは、 ホストデータベースにある複数の
IP アドレスでリストすることができます。 この場合、ホストの IP アドレスの 1 つに対する接続が失敗すると、 PRC_READ_WAIT 状態に入って prcmd() がホストの次の
IP アドレス (ホストあたりの最大 IP は 5 個) への接続オープンを試みる前に、接続が拒否、あるいは
タイムアウトになります。 すべての IP アドレスが試行されるまで、異常終了にはなりません。 |
ホスト/接続ステータス
呼び出し側プログラムで、次の接続ステータスを処理する形式については、
``使用上の注意'' を参照してください。
| PRC_CONN_NONE | | prcmd() は、名前の prc_hostid 値を 1 つ以上の
IP アドレスにマップします (アドレスとみなされない場合)。 さらに、
IP アドレスをバイナリにマップし、 ホストとの通信用として予約されたポート番号に対応する
インターネットソケットを受け取り、 fdopen() を使用して、このソケット記述子をストリームポインタにマップした後、リモートホストの「シェル」サービスに対して
ソケット記述子のノンブロッキング connect() を実行します。
ソケットの送受信バッファサイズはデフォルトとなります。 ただし、呼び出し側プログラムでは必要に応じ setsockopt
(fileno (hostp → prc_fp)) を使用して、サイズを変更できます。なお、このサイズは setsockopt(2) で説明された制限に従います。 gethostbyname() ( gethostbyname(3N) 参照)、または inet_addr() ( inet_addr(3N) 参照) が異常終了すると、 prcmd() は、 prc_conn_status フィールドを PRC_CONN_NO_IPS にセットします。 rcmd() 接続に必要な情報には、 「シェル」サービスのポート番号、 およびローカル/リモートユーザー名としての
実効ユーザー ID のユーザー名があります。 prcmd() は、最初に接続を行う時点でこの情報を探します。 prcmd() がこの情報を得られない場合は、 PRC_ERR_RCMD を返します
(呼び出しのたびに試行します)。 socket() が、 EHOSTDOWN, EAFNOSUPPORT, ESOCKTNOSUPPORT, EPROTONOSUPPORT, EPROTOTYPE,
または EINVAL のいずれかのローカルホスト エラーとともに異常終了すると、 prcmd() は PRC_ERR_NETWORKING を返し、
ホストリストのステータスは未定義になります。 socket() が EMFILE または ENFILE とともに異常終了
(すなわちプロセス、またはシステムにファイル記述子がないとき ) した場合、あるいは
予約されたポートがない場合は、 prc_conn_status フィールドは、後で使用できるように PRC_CONN_NONE として残されます。
ホストエントリーをすべてチェックした後、 prcmd() がいずれのホストについてもオープンファイル
(ソケット) を持っておらず、 かつ、この呼び出しの間にいずれのファイルもクローズしていない場合は、 PRC_ERR_NOFILE が返されます。 ― すなわちこのプロセスに全ファイル記述子が使用され、 prcmd() にはファイル記述子がないことになります。 connect() が正常終了した場合、あるいは EINPROGRESS とともに異常終了した場合
(ノンブロッキング接続要求の場合は正常です)、 prcmd() は、 prc_conn_status を PRC_CONN1_WAIT に、また prc_conn_time を現在のシステムクロック
(秒単位) に、さらに prc_fp をソケットのストリームポインタにそれぞれセットします。 次いで、このホストは、 PRC_CONN1_WAIT ごとに select() リストに入れられます
(下記参照)。 |
| PRC_CONN1_WAIT | | prcmd() により、このホストが書き込みレディとして select() リストに入れられます。
接続が書き込み用として準備できていない場合、 prc_conn_status フィールドは変更されません。 接続が書き込み用として準備できている場合、 prcmd() は、「ブロッキング」にソケットを返し、
リモートコマンドから戻される標準エラーの接続を開始します。 また、この接続に対するファイルポインタに prc_fp2 をセットし、 sigvector() を使用して一時的に無視された SIGPIPE とともにリモートプロセスに対し標準 remsh 接続情報
( remshd(1M) および上記の説明を参照)
を書き込みます。 さらに、 prc_conn_status を PRC_CONN2_WAIT,
に変更して、 prc_conn_time を現在のシステムクロック
(秒単位) にセットします。 接続が拒否されると、 prcmd() は、remsh 接続情報を書き込もうとした時点で ECONNREFUSED または EPIPE を受け取り、
``共通エラーの処理'' の項で先述した形式でエラーを処理します。 |
| PRC_CONN2_WAIT | | prcmd() により、 prc_fp (エラーの場合)、と prc_fp2 (標準エラーポートに接続成功が戻され場合)
の両方に 読み取りレディとして select() リストにこのホストが入れられます。
いずれの接続も読み取りレディでない場合は、 prc_conn_status フィールドは変更されません。 標準エラー接続が読み取りレディの場合、 prcmd() は、 accept() を実行して、もとの prc_fp2 をクローズし、新しい socket/fd に対する値を更新します。
さらに prc_conn_status を PRC_CONN3_WAIT を変更して、 prc_conn_time を現在のシステムクロック
(秒単位) にセットします。 読み取りレディとなるのが stdout 接続だけの場合は、 remshd が異常終了したことになります。 prcmd() は、この状態を
``共通エラーの処理'' で説明したとおり、接続の拒否として処理します。 ファイル記述子、または予約ポートが必要なときに、 これが存在しない場合は、
ホストが PRC_CONN1_WAIT または PRC_CONN2_WAIT のステータスに置かれます。 |
| PRC_CONN3_WAIT | | prcmd() により、このホストが読み取りレディとして select() リストに入れられます。
接続が読み取りレディでない場合、 prc_conn_status フィールドは変更されません。 接続が読み取りレディでない場合、 prcmd() は remshd からの stdout 中でヌルバイトを占め、 prc_conn_status を PRC_READ_WAIT にセットします。また、
呼び出し側のプログラムが対話できるように、 prc_conn_time を現在のシステムクロック
(秒単位) にリセットします。 ヌル以外のバイトが返された場合は、 remshd が異常終了したことになります。 prcmd() は、先述のように、この状態を接続の拒否として処理します。 |
| PRC_READ_WAIT | | prcmd() により、このホストが
読み取りレディとして select() リストに入れられます。
接続が読み取りレディでない場合、 prc_conn_status フィールドは変更されません。
リモートコマンドが、呼び出し側プログラムから データが書き込まれるまで待っていることがあります。 接続が読み取りレディの場合、 prcmd() は prc_conn_status を PRC_READ_READY にセットします。 |
| PRC_READ_READY | | prcmd() は、この状態のホストを PRC_READ_WAIT と同様に処理します。
接続が読み取りレディでない場合、 prcmd() は、 prc_conn_status を PRC_READ_WAIT に戻します。 |
| PRC_CONN_DONE | | prcmd() は、このホストのエントリーを無視します。 prc_conn_close フィールド (呼び出し側フィールドでセットされる)
が非ゼロの場合、 prcmd() は、別の状態からこの状態に入ります。 また、 prc_fp と prc_fp2 で指定されたファイル
(ヌルでない場合) をクローズし、 各フィールドをヌルによりセットし、 prc_conn_close フィールドを
0 にリセットします。 |
| PRC_CONN_NO_IPS | | |
| PRC_CONN_FAILED | | |
| PRC_CONN_REFUSED |
| | | |
| PRC_CONN_TIMEOUT |
| | | これらのいずれの故障ステータス値の場合、 prcmd() はホストエントリーを無視します。ただし prc_conn_close がセットされているときは、エントリーの prc_conn_status フィールドが PRC_CONN_DONE にセットされます。 |
ホストステータスの変化
prcmd() の呼び出しがあるごとに、 各ホストエントリーの prc_prev_status フィールドが prc_conn_status フィールドの前の値にセットされます。
呼び出し側プログラムでは、 (prc_prev_status != prc_conn_status) を使用して、任意のホストの新しいステータスを高速でチェック
することができます。 これは、 PRC_READ_WAIT および PRC_READ_READY を除き、ステータス値を 1 回限りログ、あるいは表示する場合に便利です ― 各ステータスには、対話中に何度入ってもかまいません)。
ホストに、複数の IP アドレスがあり、いずれかの IP に対する接続が失敗して、
ホストの次の IP アドレスを試すためのファイル、またはポートがない場合、
ホストは、 PRC_CONN_NONE をいったん抜けて、再度このステータスに入ることができます。
呼び出し側プログラムでは、自由に prc_caller_status フィールドを使用して、各接続のステータスに関する追加情報を
記録することができます。
使用上の注意
接続が確立して、ホストがいずれかの PRC_READ_*
ステータスに入った後は、 呼び出し側プログラムとリモートホストの間の「対話」は、
呼び出し側プログラムが制御します。対話は次のいずれかの 形式で行われます。
1. 最初に、呼び出し側プログラムが通信する。
2. 最初にリモートコマンドが通信する。
呼び出し側プログラムが最初に通信する場合は、 prc_caller_status フィールドを使用する、あるいはその他の手段によって
コマンドに初期データがすでに送られているかどうかを認識し、 ホストが初めて PRC_READ_WAIT 状態に入ったと同時にデータを転送しなければなりません。
コマンドが最初に通信する場合、 または呼び出し側プログラムが初期データを転送した後の対話では、
呼び出し側プログラムが対話を「追跡」して、 追加データを転送する時期
(データがある場合)、および prc_conn_close により対話をクローズする時期を認識しなければなりません。
呼び出し側プログラムが次の入力を転送する前、あるいは 接続をクローズする前に、 PRC_READ_WAIT および PRC_READ_READY の各ステータスを待機
(コマンドから 1 回の応答の全バイトが受信されるまで) しなければならないことがあります。
各 prcmd() 呼び出しの後、呼び出し側プログラムでは
戻り値をチェックして、重大なエラーがないかどうかを確認する 必要があります。
戻り値が PRC_OK の場合、 呼び出し側プログラムでは、次の hostp アレイをスキャンして、 各ホストに対する prc_prev_status (必要な場合)
と prc_conn_status のフィールドをチェックします。
呼び出し側プログラムでは、単に 特定のステータス値 (以下の ``•''
のマークの付いたもの). に対応する動作を実行します。
| PRC_CONN_NONE | | 最初の prcmd() 呼び出しの後。
1 つ以上の接続を行った後 (あるいは今回の呼び出し、 または前回の呼び出しでまだオープンされているときに)、
プロセスのソケット (ファイル) 記述子、 または予約済みポートが不足していることを示します。
このホストに対する接続が、次回の呼び出しで再度試みられます。 呼び出し側プログラムは、ファイルをクローズする、あるいは rlimit() を呼び出すことにより、可用なファイル記述子の数が増やすか、
あるいは既存の接続が通信を終了するまで待機します。 |
| PRC_CONN1_WAIT | | |
| PRC_CONN2_WAIT | | |
| PRC_CONN3_WAIT | | これらは、呼び出し側プログラムにはほとんど無関係の内部状態です。
後で prcmd() を再度呼び出す場合を除き、 必要な動作はありません。 (prc_prev_status
!= prc_conn_status) の場合、呼び出し側プログラムではホストの新しいステータス
をログ/表示することができます。 |
| PRC_READ_WAIT • | | コマンドが実行中ですが、まだ出力が生成されていません。
呼び出し側プログラムが最初に通信する場合は、 コマンドで何らかの初期入力が必要になるので、 prc_fp で指定されるファイルに書き込みを行い、 prc_caller_status フィールドを使用して書き込みが行われたことを記憶しておきます。
これ以外の場合は、接続ステータスのログ/表示を除き、 必要な動作はありません。 |
| PRC_READ_READY • |
| | | コマンドにより、読み取りレディの出力が生成されました。
出力を読み取った後、コマンドがさらに入力を必要とする場合は、 prc_fp で指定されたファイルに書き込みを行います。
代わりに、対話を行う場合は、 prc_conn_close フィールドを非ゼロにセットします。 |
| PRC_CONN_DONE | | コマンドが終了しました。 呼び出し側プログラムが prc_conn_close フィールドをセットした後、
接続がクローズされました。 必要な動作はありません。 呼び出し側プログラムでは、 (prc_conn_status & PRC_CSBIT_ERR) を使用して、エラーステータスを効率的に検出
できます。 1 回限りの処理の場合は (prc_prev_status != prc_conn_status) を使用して
新しいエラー状態を検出することができます。 また、 prc_caller_status を使用して、 prcmd() から最初に戻された時点で処理されたことを記録することもできます。
いずれの場合も、呼び出し側プログラムでは、必要に応じてエラーを 処理、または報告できなければなりません。 |
| PRC_CONN_NO_IPS • |
| | | prc_hostid フィールドがアドレスではなく名前とみなされ、
対応する gethostbyname() が異常終了、あるいは inet_addr() が prc_hostid の数値で異常終了します。 その他の条件は、ホストの IP アドレスの最後 (複数存在する場合) で起きた異常終了の原因
だけを参照します。 |
| PRC_CONN_FAILED • |
| | | ローカルシステムが故障したことにより、何らかの理由で
(ホストの IP アドレスのいずれか) 接続が開始できません。 prc_errno が異常終了したシステムコールの値 errno にセットされます。 |
| PRC_CONN_REFUSED • |
| | | 接続の拒否。 errno(2)ごとに、
`` 外部ホストで非アクティブになっているサービスに接続しようとすると起きます。"
また、その他のリモート (remshd) の故障 (アクセスの拒否など)
や標準エラーポートへの 不当な接続なども考られます。 呼び出し側プログラムでは、ホストの prc_errmsg フィールドをチェックしなければなりません。
ヌルポインタでない場合は、 remshd リモートコマンドからメッセージテキストが送られ、
呼び出し側プログラムでは値の使用が終わった時点で ポインタを free() にする必要があります。 |
| PRC_CONN_TIMEOUT • |
| | | 接続のタイムアウト。 接続は、 I/O の準備ができない PRC_CONN1_WAIT、 PRC_CONN2_WAIT、
または PRC_CONN3_WAIT のいずれかの状態に timeout 秒以上の間、置かれます。 呼び出し側プログラムが、ストリームではなく、 ソケット (ファイル)
記述子を使用してバッファリングされていない I/O を行う場合、 fileno(prc_fp) ( fileno(3S) 参照) を参照することができます。 呼び出し側プログラムがリモートコマンドに 信号を送信する場合、 prc_fp2 で指定されるファイルに信号番号を書き込むことができます
( rcmd(3N) 参照)。 prcmd() にすべての接続をクローズさせる場合は、
全ホストに prc_conn_close をセットし、 prcmd() を再度呼び出す以外に、方法はありません。
これにより、 prc_conn_status が PRC_CONN_DONE にセットされます。 |
タイムアウトについて
prcmd() は、最大限の並列接続数を確保し、
呼び出し側プログラムから極力制御できるように 設計されています。
したがって、 prcmd() は必ず、タイムアウト値ゼロ
(即時ポーリング) で select() を呼び出します。 prcmd() を必要以上に呼び出して、不要な CPU
時間を消費することがないように、 呼び出し側プログラムの方で制御する必要があります。
例えば、 prcmd() の呼び出しの間に、 sleep(1) を呼び出すことがあります
( sleep(3C) 参照)。 注記: これは、呼び出し側プログラム自体がタイマ割り込みにより定期的に呼び出される場合、あるいは prcmd() 呼び出しの間に、その他の時間を要するタスクを実行する場合は、
必要ありません。
ホストの接続が次のような待機状態の 1 つになると、タイムアウトが発生します。
I/O の準備ができない PRC_CONN1_WAIT、 PRC_CONN2_WAIT、
または PRC_CONN3_WAIT のいずれかの状態に timeout 秒以上、置かれ、 このホストで未使用の IP が残っていない場合。
ホスト接続が初めて、 PRC_READ_WAIT 状態になると、 prcmd() は、ホストの prc_conn_time フィールドをセットしますが、
ホストのタイムアウトについては再チェックしません。 呼び出し側プログラムでは、これを必要に応じて行うことができます。
また、呼び出し側プログラムでは、 通信が進行するに従って (例えば、リモートコマンドにデータを
書き込むごとに)、 prc_conn_time フィールドをリセット
(更新) することができます。
戻り値
| PRC_OK | | 呼び出しの正常終了。 ホストリストをチェックして、 prc_prev_status と prc_conn_status フィールドの値を調べます。 select() が EINTR とともに異常終了
(信号の着信など) すると、 prcmd() は、 PRC_OK とともに呼び出し側プログラムに制御を返します。
この場合、一部の接続が呼び出し側プログラムの制御のもとで、 開始
( PRC_CONN1_WAIT 状態) したり、終了 ( PRC_CONN_DONE 状態) したりすることがありますが、
I/O ステータスによる変化はありません。通常どおり、再度 prcmd() を呼び出してください。 |
| PRC_ERR_NETWORKING |
| | | いくつかの重大なネットワークの問題により、 socket() 呼び出しが異常終了しました
(先述のリストを参照してください)。 ローカルホストのネットワークが使用不可能で、 prcmd() が無効になっていることを示します。 prcmd() からの復帰時に errno がセットされます。 |
| PRC_ERR_NOFILE | | socket() 呼び出しが EMFILE または ENFILE とともに異常終了しました。または prcmd() にオープンな接続
(ソケット) がなくなった時点で、 使用可能な予約ポートがなくなりました。
これは、呼び出し側プログラム、またはシステムが ファイル記述子、または予約ポートの一部を解放しない限り、
これ以上、接続をオープンしようとしても意味がないことになります。 |
| PRC_ERR_RCMD | | 実効ユーザー ID に対する「シェル」サービスポート番号、または ユーザー名が得られません。
この状態を修正しない限り、 prcmd() を呼び出しても意味がありません。 |
| PRC_ERR_SELECT | | select() 呼び出しが、 EINTR 以外のコードとともに異常終了しました。 prcmd() からの復帰時に errno がセットされます。
ホストリストのデータは有効ですが、 前回の正常な呼び出しで PRC_READ_READY とマークされていても、
読み取りレディ、または書き込みレディのホストはありません。 |
診断
rcmd() と異なり、 remshd が異常終了した時点で、 remshd のメッセージをローカル標準エラーにはコピーしません。
単に、ホストが PRC_CONN_REFUSED の状態に置かれるだけです。
例
次のコードフラグメントは、 一連のホストを割り当て、初期化する方法、および
ホストリスト全体に対して prcmd() を呼び出す方法を示したものです。
int index;
struct prc_host prc_host [MAXHOSTS];
prcmd_init (prc_host, argc, 0, REMCMD, TIMEOUT);
for (index = 0; index < argc; ++index)
(prc_host[index].prc_hostid) = argv [index];
if ((rc = prcmd (prc_host, argc)) != PRC_OK)
...
警告
prcmd() は、マルチスレッドアプリケーションについては安全に動作しません。
rcmd() と異なり prcmd() は、特権プロセスからのみ呼び出す必要があります。
これ以外の場合は、 予約ポートに bind() できなくなるので、
ホストがすべて、 errno = 13 (EACCES)
とともに PRC_CONN_FAILED の状態に置かれます。
このとき、 select() 呼び出しは、 prc_fp2 ではなく、 prc_fp で指定されたファイルを対象に行われます。 prcmd() では、リモートコマンドが標準エラーには書き込みを行わずに
ブロックし、代わりに stdout を終了、あるいはクローズすることを 前提とします。
stdout 読み取りレディの場合、呼び出し側プログラムでは prc_fp にブロッキング read() を行い、
EOF 、またはエラーと同時に、 ブロッキング read() で prc_fp2 をチェックします。
(例外: PRC_CONN2_WAIT ステータスの場合、
先述のとおり、成否に応じて一方のファイルがレディ状態になってから
両方が選択されます。 ただし、この例外は prcmd() で内部的に処理されるので、
呼び出し側プログラムには影響はありません。
prcmd_init() により、 値自体ではなく、 command の値に対するポインタが保存されます。 したがって、呼び出し側プログラムでは prcmd() のすべての呼び出しを介して、値を保持しなければなりません。
connect() は、ノンブロッキング I/O の場合でも、約 2 分間経過すると、タイムアウトになります。
これがホストの接続に起きた場合は、 結果が PRC_CONN_REFUSED とは区別できなくなります。いずれの場合も、 write() が EPIPE を返すからです。
呼び出し側プログラムで許可される時間が 2 分以内であれば (すなわち 120 以下の timeout 値を送り、頻繁に prcmd() を呼び出すことによって、 connect() の前に、 PRC_CONN_REFUSED がタイムアウトをキャッチできるようにすれば、問題にはなりません。
バッファリング I/O を使用するときは注意が必要です。 要求した量よりデータが少ないときに、
無限にブロックする可能性のある状況では fgets(), fread(),
その他類似した関数の呼び出しは避けてください。 また、接続が読み取りレディになるのを待つ前に、 prc_fp で指定されるファイルを介してホストから可用なデータをすべて
読み取るようにしてください。 バッファリングした保留データがすでに stdio ライブラリで
読み取られ、可用になっている場合でも、接続の準備が できていないように見える場合があります。
こうした問題は、 ノンブロッキング I/O を使用すれば、避けられる場合があります。 fcntl(2) を参照してください。
リモートコマンドが終了した場合、あるいは 接続が失われた場合にクローズされる可能性のあるソケットに
書き込みを行う場合は注意が必要です。 これにより、呼び出し側プロセスに SIGPIPE が送られることがあります。
呼び出し側プログラムでは、必要に応じてこの信号を処理できなければなりません。
接続が PRC_CONN_REFUSED とともに異常終了したホストを処理する場合は、
必ずホストの prc_errmsg ポインタ (非ヌルの場合)
を free() するようにしてください。 これは、malloc
を行ったメモリを取り戻す必要がなければ、スキップすることができます。
パフォーマンスについて
prcmd() は、 名前より、インターネットアドレスとして指定された
ホスト IDs を使用して 呼び出す方が高速です。これは、 prcmd() が、ホストデータベースの各ホストを逐一調べる必要がない
からです。 ただし、複数のインターネットアドレスを持つホストに この方法を使用する場合は、
指定されたインターネットアドレスが試されます。
各システムごとに予約ポートは約 512 個に制限されます。また、
各プロセスについて使用できるファイル記述子にもソフト上の制限が あります。
指定するホストごとに、各ステータスをチェックする 接続に対する 2 つの予約ポートと、2 つのファイル記述子が必要に
なります。 いったんポート、またはファイル記述子がすべて使用されると、 prcmd() は、以降のホストが無視されないように、この後の接続を遅らせ
(順番に並べ) ます。 ただし、すべての接続が完了するまでの時間は、これ応じて長くなります。
パフォーマンスを改善するには、 ホストの数が多い場合にはオープンファイルの限界を
上げなければならないことがあります。 setrlimit(2) を参照してください。
参照
remshd(1M), accept(2), bind(2), connect(2), errno(2), fcntl(2), read(2), select(2), setsockopt(2), sigvector(2), socket(2), time(2), fdopen(3S), fileno(3S), gethostbyname(3N), inet_addr(3N), malloc(3C), rcmd(3N), sleep(3C)