この付録は、ポートモニタがサービスアクセス機能 (SAF : Service Access Facility) とサービスアクセスコントローラ (SAC) の下で動作する時に実行する必要のある各機能について簡単に説明します。
SAF を使用すると、サービスアクセスの手続きが汎用化され、ローカルシステムへのログインアクセスも、ローカルサービスへのネットワークアクセスも同じ方法で管理できます。SAF を使用すると、システムがサービスにアクセスするときに、ttymon を始めとする各種のポートモニタ、リスナ、ユーザーアプリケーションのために特別に作成したポートモニタを使用できます。
ポートモニタがどのようにアクセスポートの監視と管理を行うかは、SAF から決まるのではなく個々のポートモニタで決まります。したがって、ユーザーは独自のポートモニタを開発してインストールすれば、システムを必要に応じて拡張できます。このようにユーザーが自由に拡張できるという点は、SAF の大きな特徴の一つです。
SAF については、サービスは要求に応じて起動するプロセスにすぎず、サービスの提供する機能に制限はありません。
SAF は、制御プロセスであるサービスアクセスコントローラ (SAC) と、サポートディレクトリ構造の 2 つのレベルに対応する 2 つの管理レベルとで構成されます。上位の管理レベルはポートモニタを管理し、下位レベルはサービスを管理します。
管理レベルでみると、SAF は次の要素で構成されています
SAC
システムごとの構成スクリプト
SAC 管理ファイル
SAC 管理コマンド sacadm
ポートモニタ
ポートモニタごとの構成スクリプト (オプション)
ポートモニタごとの管理ファイル
管理コマンド pmadm
サービスごとの構成スクリプト (オプション)
SAC は SAF の制御プロセスです。SAC は /etc/inittab のエントリに入れて init() で起動します。SAC の機能は、ポートモニタを、システム管理者が指定した状態に保持することです。
管理コマンド sacadm を使用して、SAC にポートモニタの状態を変更させることができます。また、sacadm を使用して、ポートモニタを SAC の管理下に入れたり、管理から外したり、SAC が管理しているポートモニタの情報をリストしたりできます。
SAC の管理ファイルには、SAC の管理下の各ポートモニタを一意的に識別できるタグと、各ポートモニタを起動するコマンドのパス名が入っています。
SAC が実行する主な機能を簡単に示します。
環境のカスタマイズ
適切なポートモニタの起動
管理下のポートモニタをポーリングして、必要に応じて回復手続きを実行
ポートモニタは、マシン上の同タイプの入力ポートセットを監視するプロセスです。ポートモニタの主目的は、外部からのサービス要求を検出し、それを適切にディスパッチすることです。
ポートとは、外部に開かれたシステム上のアクセスポイントです。ポートには具体的には、ネットワーク上のアドレス (TSAP や PSAP)、コードで接続された端末ライン、電話線入力などがあります。何をポートとみなすかは、ポートモニタ自体が定義します。
ポートモニタはいくつかの基本機能を実行します。機能によっては SAF のために必要なものもありますし、ポートモニタ自身が必要に応じて設計した機能もあります。
ポートモニタの主な機能を 2 つ示します。
ポートの管理
ポートに発生するアクティビティの監視
ポートモニタの第 1 の機能はポートの管理です。ポートを実際にどう管理するかの詳細は、ポートモニタの開発者が定義します。1 つのポートモニタで 1 つのポートを管理するとは限りません。同時に複数のポートを管理することもできます。
ポート管理の例として、電話回線の接続における回線速度の設定、適切なネットワークアドレスとの結合、サービス終了後のポートの再初期化、プロンプトの出力などがあります。
ポートモニタの第 2 の機能は、アクティビティ指示に対応するポートを監視することです。検出されるアクティビティには、次の 2 つのタイプがあります。
アクティビティの 1 つは、ポートモニタがモニタ固有のアクションを取るための指示です。ポートモニタ固有アクティビティの例として、ブレークキーが押されると回線速度を循環させるという指示があります。すべてのポートモニタが、同じ指示を認識して同じように反応するわけではありません。ポートモニタがどのような指示を検出するかは、ポートモニタの開発者が定義します。
もう 1 つは外部からのサービス要求です。ポートモニタはサービス要求を受信すると、受信したポートからどのサービスが要求されているかを判定できなければなりません。同じサービスを複数のポートで提供できることに注意してください。
この項では、ポートモニタのその他の機能について簡単に説明します。
ポートモニタは現在実行中のサービスに影響することなく、システムへのアクセスを制限できなければなりません。そのために、ポートモニタは有効と無効の 2 つの内部状態を保持しています。ポートモニタの起動時の状態は、sac で設定した環境変数 ISTATE
の値で決まります。「SAC とポートモニタのインタフェース」 の項を参照してください。
ポートモニタの有効と無効を切り換えると、その管理下にあるすべてのポートが影響を受けます。ポートモニタが 1 つのポートだけを管理している場合は、そのポートだけが影響を受けます。複数ポートがそのポートモニタの管理下にあるときは、それらのポートすべてが影響を受けます。
ポートモニタの有効と無効の切り換えは動的な操作です。この操作で、ポートモニタの内部状態が変わります。ただし、次にポートモニタを起動するときには、この状態は失われます。
これに対して、個々のポートの有効と無効の切り換えは静的な操作です。この操作によって管理ファイルが変更されます。次にポートモニタを起動しても、ポートの状態は残ります。
ポートモニタは、起動するサービスの utmpx エントリを作成します。そのとき、エントリのタイプフィールドは USER_PROCESS に設定されます。ただし、pmadm でサービスを追加したときに -fu を指定した場合だけです。utmpx エントリは、次にサービスによって変更されます。サービスが終了したときは、utmpx エントリは DEAD_PROCESS に設定されなければなりません。
ポートモニタは、起動したときに自分のプロセス ID をカレントディレクトリの _pid というファイルに書き込み、そのファイルにアドバイザリロックを設定します。
ポートモニタは、ポートモニタの管理ファイル _pmtab に指定されているサービスを起動する前に、サービスごとの構成スクリプトがあればライブラリ関数 doconfig() を呼び出してそれを実行します。セキュリティ上の理由だけではなく、サービスごとの構成スクリプトで制限コマンドの実行が指示される場合があるので、ポートモニタはルートアクセス権で起動されます。サービスの呼び出し方法の詳細は、ポートモニタの開発者が定義します。
ポートモニタはシグナル SIGTERM を受け取ると、手続きに従って自分自身を終了させます。ポートモニタの終了シーケンスを次に示します。
ポートモニタは停止状態に入ります。これ以降はサービス要求を受け取りません。
ポートモニタの状態を有効に切り換える指示はすべて無視します。
ポートモニタは、管理下の全ポートの制御を停止します。ポートモニタの以前のインスタンスが停止状態のときは、新たなインスタンスが正常に起動されなければなりません。
プロセス ID ファイルのアドバイザリロックを解除します。アドバイザリロックが解除されると、プロセス ID ファイルの内容は未定となり、ポートモニタを新たに起動できるようになります。
この項では、SAF によって使用されるファイルについて簡単に説明します。
ポートモニタのカレントディレクトリには、_pmtab という管理ファイルがあります。_pmtab は、pmadm コマンドとポートモニタ固有の管理コマンドとを使用して管理します。ポートモニタ固有の管理コマンドについては次に説明します。
ポートモニタ listen に固有の管理コマンドは nlsadmin() です。ポートモニタ ttymon に固有の管理コマンドは ttyadm() です。ユーザーがポートモニタを作成するときは、これらのコマンドと同様の機能を持つ、ポートモニタ固有の管理コマンドを提供する必要があります。
ポートモニタのカレントディレクトリには、サービスごとの構成スクリプトも含まれています (サービスごとの構成スクリプトはオプションのため存在しない場合もあります)。サービスごとの構成スクリプトの名前は、_pmtab ファイルで設定したサービスタグと同じです。
ポートモニタは、ディレクトリ /var/saf/tag (tag はポートモニタの名前) にプライベートファイルを作成できます。プライベートファイルの例として、ログファイルや一時ファイルがあります。
sac は、起動するポートモニタごとに次の 2 つの環境変数を作成します。
sac は、この変数を一意的に識別できるポートモニタタグに設定します。ポートモニタは sac メッセージに応答するときに、このタグを使用して自分自身を示します。ISTATE は、ポートモニタの起動時の内部状態を指定するのに使用します。ISTATE は、ポートモニタが許可状態でスタートするときに enable に、禁止状態でスタートするときに disable に設定されます。sac はポートモニタの正常なポーリングを定期的に実行します。
sac とポートモニタは FIFO を使用して通信します。ポートモニタは、sac からのメッセージを受信するためにカレントディレクトリに _pmpipe をオープンし、sac に応答メッセージを返すために ../_sacpipe をオープンします。
この節では、sac からポートモニタに送るメッセージ (sac メッセージ) と、ポートモニタから sac に送るメッセージ (ポートモニタメッセージ) について説明します。メッセージは、FIFO を通して C の構造体形式で送信されます。例 F-2 を参照してください。
sac から送られるメッセージの形式は、次の構造体 sacmsg で定義されています。
struct sacmsg { int sc_size; /* オプションデータ部分のサイズ */ char sc_type; /* メッセージのタイプ */ };
sac からポートモニタに送られるメッセージには 4 つのタイプがあります。メッセージがどのタイプなのかは、sacmsg 構造体の sc_type フィールドに次のどれかの値を設定して示します。
ステータス要求
有効設定メッセージ
無効設定メッセージ
ポートモニタの _pmtab ファイルを読み込むように指示するメッセージ
sc_size はメッセージのオプションデータ部分のサイズを示します。「メッセージクラス」 を参照してください。sc_size では、sc_size は常に 0 です。
ポートモニタは、sac からのメッセージには必ず応答しなければなりません。
ポートモニタから sac に送られるメッセージの形式は、次の構造体 pmmsg で定義されています。
struct pmmsg { char pm_type; /* メッセージのタイプ */ unchar pm_state; /* ポートモニタの現在の状態 */ char pm_maxclass; /* このポートモニタが解釈できる 最大メッセージクラス */ char pm_tag[PMTAGSIZE + 1]; /* ポートモニタのタグ */ int pm_size; /* オプションデータ部分のサイズ */ };
ポートモニタから sac に送られるメッセージには、2 つのタイプがあります。メッセージがどのタイプなのかは、pmmsg 構造体の pm_type フィールドに次のどちらかの値を設定して示します。
状態情報
否定応答
どちらのタイプのメッセージの場合も、pm_tag フィールドにはポートモニタのタグが、pm_state フィールドにはポートモニタの現在の状態が設定されます。pm_state フィールドに設定できる状態は次の 4 つです。
起動
有効
無効
停止
現在の状態は、sac からの最後のメッセージによる状態変更を反映しています。
通常の応答メッセージは状態メッセージです。否定応答メッセージを返すのは、sac から受信したメッセージが理解できなかったときだけです。
pm_size はメッセージのオプションデータ部分のサイズを示します。pm_maxclass はメッセージクラスを指定するのに使用します。この 2 つのフィールドは、「メッセージクラス」 の項で説明します。Solaris では常に pm_maxclass は 1、sc_size は 0 です。
ポートモニタから先にメッセージを送ることはありません。ポートモニタは、受信したメッセージに応答するだけです。
メッセージクラスの概念は、SAF の拡張性に含まれています。これまでに説明したメッセージはすべてクラス 1 のメッセージです。どのメッセージにも可変長データは含まれておらず、必要な情報はすべてメッセージヘッダーに入っていました。
新たなメッセージをプロトコルに加えると、それによって新たなメッセージクラス (たとえば、クラス 2) が定義されます。sac からポートモニタに送られる最初のメッセージは常にクラス 1 のメッセージです。どのポートモニタもクラス 1 のメッセージは理解できますから、sac が送る最初のメッセージは必ず理解されます。ポートモニタは、それに対する sac への応答の中で、ポートモニタが理解できる最大メッセージクラス番号を pm_maxclass フィールドに設定します。sac は、ポートモニタに対して、pm_maxclass の値より大きいクラスのメッセージは送りません。ポートモニタが理解できるクラスより上のクラスのメッセージを必要とする要求は、失敗に終わります。Solaris では、pm_maxclass は常に 1 です。
どのポートモニタも、pm_maxclass の値と等しいクラスかそれより小さいクラスのメッセージを受信できます。したがって、pm_maxclass フィールドが 3 の場合、このポートモニタはクラス 1、2、3 のメッセージを理解できます。ポートモニタはメッセージを生成することはありません。受信したメッセージに応答するだけです。ポートモニタからの応答は、元のメッセージと同じクラスでなければなりません。
sac の側からだけメッセージが生成されるので、sac が生成できるクラスより上のクラスのメッセージをポートモニタが処理できる場合も、このプロトコルは正しく機能します。
pm_size (pmmsg 構造体の要素) と sc_size (sacmsg 構造体の要素) は、メッセージの中のオプションデータ部分のサイズを示します。オプションデータ部分の形式は未定義です。オプションデータ部分の形式定義は、メッセージのタイプによって決まっています。Solaris では、sc_size と pm_size は常に 0 です。
この項では、SAC で使用できる管理ファイルについて説明します。
サービスアクセスコントローラの管理ファイルには、SAC の管理下にある全ポートモニタの情報が入っています。このファイルは配布システムに入っています。最初、このファイルには SAC のバージョン番号の入ったコメント行が 1 行入っているだけです。システムにポートモニタを追加するときは、SAC の管理ファイルにエントリを作成します。エントリを追加するには、管理コマンド sacadm に -a オプションを付けて実行します。SAC の管理ファイルからエントリを削除するときも、sacadm を使用します。
SAC の管理ファイルの各エントリには、表 F-1 で示されている次の情報が含まれています。
表 F-1 SAC_sactab ファイル
フィールド |
説明 |
---|---|
PMTAG |
個々のポートモニタを一意的に識別できるタグ。ポートモニタの命名はシステム管理者が行う。SAC は、管理目的でポートモニタを識別するときはこのタグを使用する。PMTAG には、14 文字までの英数字が入る。 |
ポートモニタのタイプ。各ポートモニタには、それを一意に識別できるタグのほかにタイプ指示子がある。タイプ指示子は、同一エンティティを別々に起動したことによるポートモニタグループを示す。有効なポートモニタタイプの例として ttymon と listen が挙げられる。タイプ指示子を使用すると、関連ポートモニタからなるグループの管理が楽になる。タイプ指示子がないと、システム管理者は各ポートモニタタグがどのタイプのポートモニタなのか判断できない。PMTYPE には、14 文字までの英数字が入る。 |
|
FLGS |
現在定義されているフラグは次の 2 つがある。 -d は、起動時にポートモニタを有効にしない。 -x は、ポートモニタを起動しない。 フラグを指定しないと、デフォルトのアクションが実行される。デフォルトでは、ポートモニタは起動され有効に設定される。 |
RCNT |
ポートモニタが何回エラーを起こしたらエラー停止状態になるかを示す。SAC は、エラー停止状態になったポートモニタは再起動しない。エントリ作成時にこの回数を指定しないと、このフィールドは 0 になる。再起動回数が 0 ということは、ポートモニタが一度でもエラーストップすると、以後再起動されないことを意味する。 |
COMMAND |
ポートモニタを起動するコマンドの文字列。文字列の最初の要素 (コマンド自体) は、フルパスで指定しなければならない。 |
各ポートモニタは、それぞれ 2 つの専用ディレクトリを持ちます。カレントディレクトリには、SAF で決められたファイル (_pmtab、_pid) と、サービスごとの構成スクリプト (作成した場合のみ) とがあります。ディレクトリ /var/saf/pmtag (pmtag はポートモニタのタグ) には、ポートモニタのプライベートファイルを入れることができます。
各ポートモニタは自分の管理ファイルを持っています。管理ファイルのサービスエントリの追加、削除、変更を行うときは、pmadm コマンドを使用します。pmadm で管理ファイルを変更するたびに、対応するポートモニタはそのファイルを読み直します。ポートモニタの管理ファイルの各エントリでは、個々のポートの扱いと、そのポートで呼び出されるサービスとを定義します。
どのタイプのポートモニタでも省略できないフィールドがあります。エントリには、サービスを一意的に識別するためのサービスタグと、サービスが起動されたときに割り当てられる ID (たとえば、root) が必ず入っていなければなりません。
サービスタグとポートモニタタグを組み合わせると、サービスインスタンスが一意的に定義されます。別のポートモニタの下のサービスを識別するのに、同じサービスタグを使用することができます。レコードにはポートモニタに固有のデータも入っています。たとえば、ポートモニタ ttymon の場合、ttymon にだけ必要なプロンプト文字列が入っています。ポートモニタのタイプごとに、ポートモニタの固有データを引数とし、そのデータを適当な形式で管理ファイルの中に書き出すコマンドが提供されていなければなりません。ttymon に対しては ttyadm コマンドが、listen に対しては nlsadmin コマンドがこの処理を行います。ユーザーがポートモニタを作成するときは、このような管理コマンドも同時に作成する必要があります。
ポートモニタ管理ファイルの各サービスエントリには、以下に示す情報が次の形式で入っていなければなりません。
svctag:flgs:id:reserved:reserved:reserved: pmspecific# comment
SVCTAG はサービスを一意的に識別するタグです。タグは、サービスが提供されるポートモニタの中でのみ一意になるようにします。別のポートモニタのサービス (同一サービスまたは別のサービス) には、同じタグが付けられていてもかまいません。サービスを一意的に識別するには、ポートモニタタグとサービスタグの両方が必要です。
SVCTAG には、14 文字までの英数字が入ります。サービスエントリは、表 F-2 で定義されています。
表 F-2 SVCTAG サービスエントリ
サービスエントリ |
説明 |
---|---|
FLGS |
このフィールドには、現在次のフラグを入れることができる -x を指定すると、このポートは有効にしない。デフォルトでは有効になる -u を指定すると、このサービスの utmpx エントリを作成する。デフォルトではサービスの utmpx エントリは作成されない |
ID |
サービスを起動するときの ID。ID は、/etc/passwd に入っているログイン名と同じ形式を持つ |
PMSPECIFIC |
ポートモニタ固有情報の例として、アドレス、実行するプロセスの名前、接続を渡す STREAMS パイプの名前が挙げられる。この情報はポートモニタのタイプごとに異なる |
COMMENT |
サービス呼び出し方法からみて、utmpx を作成するのが適当でない場合は、ポートモニタが -u フラグを無視します。サービスによっては、utmpx エントリが作成されていないと正しく起動できないものもあります (たとえば、login サービス)。
各ポートモニタの管理ファイルには、次の形式の特殊なコメントが入っていなければなりません。
# VERSION=value
ここで、value はポートモニタのバージョン番号を表す整数です。バージョン番号により、ポートモニタの管理ファイルの形式がわかります。このコメント行は、ポートモニタをシステムに追加したときに自動的に作成されます。これだけが 1 行となってサービスエントリの前に入ります。
sacadm は、SAF 階層の上位レベル (すなわち、ポートモニタ管理) に対する管理コマンドです (sacadm(1M) のマニュアルページを参照)。SAF では、sacadm コマンドを使用して SAC の管理ファイルを変更することによりポートモニタを管理します。sacadm は、次にのような機能があります。
指定されたポートモニタ情報を SAC 管理ファイルから取り出して印刷する
ポートモニタの追加と削除を行う
ポートモニタの有効と無効を切り換える
ポートモニタの起動と停止を行う
システムごとの構成スクリプトのインストールまたは置き換えを行う
ポートモニタごとの構成スクリプトのインストールまたは置き換えを行う
pmadm は、SAF 階層の下位レベル (すなわち、サービス管理) に対する管理コマンドです (pmadm(1M) のマニュアルページを参照)。複数のポートで同じサービスを提供することは可能ですが、1 つのポートには 1 つのサービスしか結合できません。pmadm の機能を次に示します。
指定されたサービスステータス情報をポートモニタの管理ファイルから取り出して印刷する
サービスの追加と削除を行う
サービスの有効と無効を切り換える
サービスごとの構成スクリプトのインストールまたは置き換えを行う
サービスインスタンスを一意的に識別するには、pmadm コマンドで、サービス (-s) と、サービスが提供されるポートモニタ (-p または -t) の両方を指定する必要があります。
前の節では、_pmtab ファイル内の 2 つの情報、すなわち、ポートモニタのバージョン番号と、_pmtab ファイルのサービスエントリに入っているポートモニタ情報とを説明しました。新たなポートモニタを追加するときは、_pmtab ファイルを正しく初期化するためにバージョン番号がわかっていなければなりません。新たなサービスを追加するときは、_pmtab エントリのポートモニタ情報が正しくフォーマットされなければなりません。 各ポートモニタには、この 2 つの処理を行うための管理コマンドが必要です。ポートモニタの設計者は、そのような管理コマンドとその入力オプションも定義しなければなりません。
管理コマンドは、そのような入力オプションを指定して呼び出されると、サービスエントリのポートモニタ固有部分に必要な情報を、ポートモニタの _pmtab ファイルに入るようにフォーマットし、それを標準出力に書き出さなければなりません。バージョン番号を調べるときは、コマンドに -V オプションを付けて実行します。その場合管理コマンドは、ポートモニタの現在のバージョン番号を標準出力に書き出さなければなりません。
どちらの処理を実行している場合も、管理コマンドに何らかのエラーが起これば、標準出力には何も表示されません。
ポートモニタとサービスのインタフェースは、サービスの側から決まります。サービスを呼び出す方法の例として、2 つの方法を説明します。
インタフェースの 1 つに、要求ごとに新たにサービスを起動する方法があります。この場合、最初にポートモニタに子プロセスの fork() を要求します。子プロセスは、exec() を実行することで、指定されたサービスになります。ポートモニタは、exec() が起こる前に、ポートモニタ固有のアクションを実行します。ただし、サービスごとの構成スクリプトがあれば、必ずそれを読み込んで実行しなければなりません。そのためには、ライブラリルーチン doconfig() を呼び出します。
現在実行中のサービスを呼び出すためのもう 1 つのインタフェースがあります。このインタフェースを使用するサービスは、ストリームパイプの一端をオープンにしておき、そこを通して接続を受信できるようにしておかなければなりません。
ポートモニタを開発するには、いくつかの一般的な条件が満たされていなければなりません。この節では、そのような条件を簡単に説明します。ポートモニタだけでなく、管理コマンドも同時に作成する必要があります。
ポートモニタが起動されるときは、次のような初期実行環境が整っていなければなりません。
オープンしているファイル記述子を持たない
プロセスグループリーダにはならない
/var/adm/utmpx に、タイプが LOGIN_PROCESS のエントリを持つ
環境変数 ISTATE
が「enabled」か「disabled」のどちらかに設定されており、ポートモニタの正しい初期状態を示す
ポートモニタの管理ファイルの入ったディレクトリがカレントディレクトリになっている
ポートモニタは、/var/saf/tag ディレクトリにプライベートファイルを作成できる (ここで、tag はポートモニタのタグ)
カレントディレクトリには、次に示すポートモニタの主要ファイルが存在します。
表 F-3 ポートモニタの主要ファイル
ファイル |
説明 |
---|---|
ポートモニタの構成スクリプト。ポートモニタの構成スクリプトは SAC が実行する。SAC は init() により起動される。そのためには、/etc/inittab に sac を呼び出すためのエントリを入れておく |
|
ポートモニタの管理ファイル。このファイルには、ポートモニタの管理下にあるポートとサービスの情報が入っている |
|
ポートモニタが sac からのメッセージを受信するための FIFO |
|
svctag というタグを持つサービスの構成スクリプト |
|
ポートモニタが sac へのメッセージを送信するための FIFO |
ポートモニタは、ポートモニタ固有機能のほかに次のタスクを実行します。
sac とのメッセージ交換プロトコルに従う
ポートモニタはサービスを起動するときに次のタスクを実行しなければなりません。
要求されたサービスに対する _pmtab エントリに -u フラグがあれば、utmp エントリを作成する
サービスの起動方法から見て utmp エントリを作成する意味がなければ、ポートモニタは -u フラグを無視します。これとは反対に、utmp エントリが作成されていないと正しく起動できないサービスもあります。
libnsl.so に定義されているライブラリルーチン doconfig() は、ファイル /etc/saf/pmtag/_sysconfig (システムごとの構成スクリプト)、/etc/saf/_sysconfig (ポートモニタごとの構成スクリプト)、/etc/saf/pmtag/svctag (サービスごとの構成スクリプト) に入っている構成スクリプトを解釈します。doconfig() の構文を次に示します。
# include <sac.h> int doconfig (int fd, char *script, long rflag);
script は構成スクリプト名です。fd は、ストリーム操作オペレーションが適用されるストリームのファイル記述子です。rflag は、script を解釈するモードを指定するビットマスクです。rflag に指定できる値は、NORUN か NOASSIGN、またはこの 2 つの OR を取った値です。rflag がゼロの場合は、構成スクリプトのすべてのコマンドが解釈されます。rflag の NOASSIGN ビットがオンになっていると、assign コマンドは解釈できず doconfig() はエラー終了します。rflag の NORUN ビットがオンになっていると、run と runwait のコマンドは解釈できず doconfig() はエラー終了します。
スクリプトのどれかのコマンドでエラーが起こった場合、doconfig() はそこでスクリプトの解釈を終了し正の整数を返します。この値は、エラーが起こった行番号を表します。システムエラーが起こった場合は、-1 を返します。
スクリプトでエラーが起こった場合は、スクリプトが実行環境を設定していたプロセスは起動されません。
次の例では、サービスごとの構成スクリプトを解釈するのに doconfig() を使用しています。
. . . if ((i = doconfig (fd, svctag, 0)) != 0){ error ("doconfig failed online %d of script %s",i,svctag); }
システムごとの構成ファイル /etc/saf/_sysconfig は空の状態で配布されます。このファイルはシステム上の全サービスの環境をカスタマイズするもので、この章と doconfig(3NSL) のマニュアルページで説明するインタプリタ言語で書かれたコマンドスクリプトが入っています。SAC は、起動されると doconfig() 関数を呼び出してシステムごとの構成スクリプトを翻訳します。SAC が起動されるのは、システムがマルチユーザーモードに入ったときです。
ポートモニタごとの構成ファイル (/etc/saf/pmtag/_config) はオプションの構成ファイルです。ユーザーはこのファイルを使用して、ポートモニタの環境と、ポートモニタが管理している特定のポートで提供されるサービスの環境とをカスタマイズできます。ポートモニタごとの構成スクリプトも、システムごとの構成スクリプトと同じ言語で書かれます。
ポートモニタごとの構成スクリプトは、ポートモニタが起動されたときに翻訳されて実行されます。ポートモニタが SAC に起動されるのは、SAC が起動されて自分自身の構成スクリプト (/etc/saf/_sysconfig) を実行した後です。
ポートモニタごとの構成スクリプトがあると、システムごとの構成スクリプトで提供されるデフォルトの構成スクリプトではなく、ポートモニタごとのスクリプトが実行されます。
ユーザーは、サービスごとの構成ファイルを使用して特定のサービスの環境をカスタマイズできます。たとえば、一般ユーザーには許されていない特殊な特権を必要とするサービスがあるとします。doconfig(3NSL) のマニュアルページに記述されている言語を使用すると、特定のポートモニタで提供される特定のサービスにこのような特殊な特権を与えたり奪ったりするスクリプトを書くことができます。
サービスごとの構成スクリプトを作成しておくと、上位レベルの構成スクリプトで提供されるデフォルトのスクリプトの代わりに、そのスクリプトが実行されます。たとえば、サービスごとの構成スクリプトで、デフォルトとは異なる STREAMS モジュールセットを指定することができます。
構成スクリプトを記述する言語は、一連のコマンドで構成されており、各コマンドはそれぞれ個別に翻訳されます。5 つのキーワード assign、push、pop、runwait、run が定義されています。コメント文字は # です。空白行は無視されます。スクリプトの各行は 1024 文字を超えることはできません。
assign variable=value
assign コマンドは環境変数を定義するのに使用します。variable は環境変数名で、value は環境変数に割り当てられる値です。value は文字列定数でなければなりません。パラメータ置換は使用できません。value は引用符で囲むことができます。引用符で囲むときの規則は、シェルで環境変数を定義するときの規則と同じです。新たな環境変数を割り当てるための空間が足りない場合、指定構文に誤りがあるときは assign コマンドでエラーが起こります。
push module1[, module2, module3, ...]
push コマンドは、STREAMS モジュールを fd で指定されたストリームにプッシュします。doconfig(3NSL) のマニュアルページを参照してください。module1 は最初にプッシュされるモジュール名、module2 は 次にプッシュされるモジュール名です。(module3 以下も同様)。指定したどれかのモジュールがプッシュできなかったとき、このコマンドはエラーとなります。その場合、同じ行で指定している残りのモジュールは無視され、既にプッシュされたモジュールはポップされます。
pop [module]
pop コマンドは、指定したストリームから STREAMS モジュールをポップするのに使用します。引数なしで pop コマンドを実行すると、ストリームの一番上のモジュールがポップされます。引数を指定して pop コマンドを実行すると、指定したモジュールがストリームの一番上に来るまで、モジュールが 1 つずつポップされます。指定したモジュールがストリームにない場合は、ストリームはもとの状態のままで、コマンドはエラー終了します。モジュール名の代わりに特殊キーワード ALL を指定すると、ストリームから全モジュールがポップされます。ただし、一番上のドライバより上のモジュールだけが対象になることに注意してください。
runwait command
runwait コマンドは、コマンドを実行してその終了を待ちます。command には、実行するコマンドのパス名を指定します。command は /bin/sh -c を付けて実行されます。シェルスクリプトもこのようにして構成スクリプトから実行できます。指定した command が見つからないか実行できなかったとき、または、command は存在していてもステータスが 0 以外のときは、runwait コマンドがエラー終了します。
run command
run コマンドは runwait コマンドと同じですが、run コマンドでは command の終了を待たない点が違います。command には、実行するコマンドのパス名を指定します。run コマンドがエラー終了するのは、command を実行する子プロセスを作成できなかったときだけです。
構文上は区別がつきませんが、run と runwait で実行されるコマンドのいくつかはインタプリタの組み込みコマンドです。インタプリタの組み込みコマンドが使用されるのは、プロセスのコンテキストの中でプロセスの状態を変える必要があるときです。doconfig() の組み込みコマンドは、シェルの特殊コマンドと同様、実行するための別のプロセスを生成しません。sh(1) のマニュアルページを参照してください。組み込みコマンドの初期セットを次に示します。
cd ulimit umask
この節では、SAC とポートモニタの管理コマンドを使用して、3 種類の構成スクリプトをインストールする方法を説明します。システムごとの構成スクリプトとポートモニタごとの構成スクリプトは sacadm コマンドで管理します。サービスごとの構成スクリプトは pmadm コマンドで管理します。
sacadm -G [ -z script ]
システムごとの構成スクリプトを印刷するか置き換えるときは、-G オプションを使用します。-G オプションだけを指定すると、システムごとの構成スクリプトが印刷されます。-G オプションと -z オプションを組み合わせると、/etc/saf/_sysconfig が script に指定されたファイルの内容で置き換えられます。-G オプションは -z オプション以外のオプションと組み合わせて使用することはできません。
次に示すシステムごとの構成ファイル (_sysconfig) では、時間帯を示す変数 TZ を設定しています。
assign TZ=EST5EDT # set TZ runwait echo SAC is starting > /dev/console
sacadm -g -p pmtag [ -z script ]
-g オプションは、ポートモニタごとの構成スクリプトの印刷、インストール、置き換えを行うときに使用します。-g オプションには -p オプションが必要です。-g オプションに -p オプションだけを組み合わせて指定すると、pmtag に指定したポートモニタのポートモニタごとの構成スクリプトが印刷されます。-g オプションに -p オプションと -z オプションを組み合わせると、script に指定したファイルがポートモニタ (pmtag) に対するポートモニタごとの構成スクリプトとしてインストールされます。あるいは、/etc/saf/pmtag/_config が既に存在している場合は、そのファイルの内容が script に指定したファイルで置き換わります。-g オプションをこれ以外のオプションと組み合わせて使用することはできません。
次に示す _config ファイルの例では、/usr/bin/daemon を、STREAMS マルチプレクサを構築するデーモンプロセスを起動するためのコマンドとします。この構成スクリプトをインストールすると、ポートモニタが必要とするコマンドを、ポートモニタの起動の直前に実行できます。
# build a STREAMS multiplexor run /usr/bin/daemon runwait echo $PMTAG is starting > /dev/console
pmadm -g -p pmtag -s svctag [ -z script ] pmadm -g -s svctag -t type -z script
サービスごとの構成スクリプトは、サービスが呼び出される前に、ポートモニタによって解釈されて実行されます。
SAC は自分自身の構成ファイル (_sysconfig) と、ポートモニタの構成ファイルの両方を解釈して実行します。サービスごとの構成ファイルだけは、ポートモニタが実行します。
-g オプションは、サービスごとの構成スクリプトの印刷、インストール、置き換えを行うときに使用します。-g オプションに -p オプションと -s オプションを組み合わせると、ポートモニタ (pmtag) で提供されるサービス (svctag) のサービスごとの構成スクリプトが印刷されます。-g オプションに -p オプション、-s オプション、-z オプションを組み合わせると、指定したファイル (script) に入っているスクリプトが、ポートモニタ (pmtag) で提供されるサービス (svctag) のサービスごとの構成スクリプトとしてインストールされます。-g オプションに -s オプション、-t オプション、-z オプションを組み合わせると、指定したファイル (script) が、ポートモニタタイプ (type) で提供されるサービス (svctag) のサービスごとの構成スクリプトとしてインストールされます。-g オプションを、上で述べた以外のオプションと組み合わせて使用することはできません。
次に示すサービスごとの構成スクリプトでは 2 つのことを実行します。1 つは、プロセスの ulimit を 4096 に設定することにより、プロセスが作成するファイルサイズの上限を設定しています。もう 1 つは、umask を 077 に設定することにより、プロセスが作成するファイルに適用される保護マスクを指定しています。
runwait ulimit 4096 runwait umask 077
例 F-1 に示すのは、SAC からのメッセージに応答する以外は何もしないポートモニタのサンプルプログラムです。
# include <stdlib.h> # include <stdio.h> # include <unistd.h> # include <fcntl.h> # include <signal.h> # include <sac.h> char Scratch[BUFSIZ]; /* スクラッチバッファ */ char Tag[PMTAGSIZE + 1]; /* ポートモニタのタグ */ FILE *Fp; /* ログファイルへのファイルポインタ */ FILE *Tfp; /* pid ファイルへのファイルポインタ */ char State; /* ポートモニタの現在の状態 */ main(argc, argv) int argc; char *argv[]; { char *istate; strcpy(Tag, getenv("PMTAG")); /* * ポートモニタのプライベートディレクトリにログファイルをオープンします。 */ sprintf(Scratch, "/var/saf/%s/log", Tag); Fp = fopen(Scratch, "a+"); if (Fp == (FILE *)NULL) exit(1); log(Fp, "starting"); /* * 初期状態 (enabled または disabled) を取り出して、 * State をそれに合わせて設定します。 */ istate = getenv("ISTATE"); sprintf(Scratch, "ISTATE is %s", istate); log(Fp, Scratch); if (!strcmp(istate, "enabled")) State = PM_ENABLED; else if (!strcmp(istate, "disabled")) State = PM_DISABLED; else { log(Fp, "invalid initial state"); exit(1); } sprintf(Scratch, "PMTAG is %s", Tag); log(Fp, Scratch); /* * pid ファイルを設定してロックし、ポートモニタがアクティブなことを示します。 */ Tfp = fopen("_pid", "w"); if (Tfp == (FILE *)NULL) { log(Fp, "couldn't open pid file"); exit(1); } if (lockf(fileno(Tfp), F_TEST, 0) < 0) { log(Fp, "pid file already locked"); exit(1); } fprintf(Tfp, "%d", getpid()); fflush(Tfp); log(Fp, "locking file"); if (lockf(fileno(Tfp), F_LOCK, 0) < 0) { log(Fp, "lock failed"); exit(1); } /* * sac からのポーリングメッセージを処理します。...この関数は終了しません。 */ handlepoll(); pause(); fclose(Tfp); fclose(Fp); } handlepoll() { int pfd; /* 入力パイプのファイル記述子 */ int sfd; /* 出力パイプのファイル記述子 */ struct sacmsg sacmsg; /* 入力メッセージ */ struct pmmsg pmmsg; /* 出力メッセージ */ /* * sac からのメッセージを受信するためのパイプをオープンします。 */ pfd = open("_pmpipe", O_RDONLY|O_NONBLOCK); if (pfd < 0) { log(Fp, "_pmpipe open failed"); exit(1); } /* * sac へメッセージを送信するためのパイプをオープンします。 */ sfd = open("../_sacpipe", O_WRONLY); if (sfd < 0) { log(Fp, "_sacpipe open failed"); exit(1); } /* * 応答メッセージの構築を開始します。ここでは、クラス 1 のメッセージだけを * サポートします。 */ strcpy(pmmsg.pm_tag, Tag); pmmsg.pm_size = 0; pmmsg.pm_maxclass = 1; /* * sac からのメッセージへの応答を続けます。 */ for (;;) { if (read(pfd, &sacmsg, sizeof(sacmsg)) != sizeof(sacmsg)) { log(Fp, "_pmpipe read failed"); exit(1); } /* * メッセージのタイプを判定して、正しい応答を返します。 */ switch (sacmsg.sc_type) { case SC_STATUS: log(Fp, "Got SC_STATUS message"); pmmsg.pm_type = PM_STATUS; pmmsg.pm_state = State; break; case SC_ENABLE: /* 内部状態が変わっていることに注意 */ log(Fp, "Got SC_ENABLE message"); pmmsg.pm_type = PM_STATUS; State = PM_ENABLED; pmmsg.pm_state = State; break; case SC_DISABLE: /*内部状態が変わっていることに注意 */ log(Fp, "Got SC_DISABLE message"); pmmsg.pm_type = PM_STATUS; State = PM_DISABLED; pmmsg.pm_state = State; break; case SC_READDB: /* * 完全なポートモニタの場合は、ここで _pmtab を読み込んで * 必要なアクションを起こします。 */ log(Fp, "Got SC_READDB message"); pmmsg.pm_type = PM_STATUS; pmmsg.pm_state = State; break; default: sprintf(Scratch, "Got unknown message <%d>", sacmsg.sc_type); log(Fp, Scratch); pmmsg.pm_type = PM_UNKNOWN; pmmsg.pm_state = State; break; } /* * ポーリングに対する応答を返し、 * 現在の状態を知らせます。 */ if (write(sfd, &pmmsg, sizeof(pmmsg)) != sizeof(pmmsg)) log(Fp, "sanity response failed"); } } /* * 一般的なログ機能 */ log(fp, msg) FILE *fp; char *msg; { fprintf(fp, "%d; %s¥n", getpid(), msg); fflush(fp); } |
例 F-2 はヘッダーファイル sac.h を示しています。
/* utmpx id のバイト数 */ # define IDLEN 4 /* utmpx ids のワイルド文字 */ # define SC_WILDC 0xff /* ポートモニタタグの最大長 (バイト数) */ # define PMTAGSIZE 14 /* * doconfig() のための rflag 値 */ /* assign コマンドは許可しない */ # define NOASSIGN 0x1 /* run と runwait のコマンドは許可しない */ # define NORUN 0x2 /* * sac へのメッセージ (ヘッダーのみ)。このヘッダー形式はずっと固定されます。 * サイズフィールド (pm_size) には、ヘッダーに続くオプションデータ部のサイズを * 指定します。オプションデータ部の形式は、メッセージタイプ (pm_type) によって * 厳密に定義されます。 */ struct pmmsg { char pm_type; /* メッセージタイプ */ unchar pm_state; /* ポートモニタの現在の状態 */ char pm_maxclass; /* ポートモニタが理解できる最大の メッセージクラス */ char pm_tag[PMTAGSIZE + 1]; /* ポートモニタのタグ */ int pm_size; /* オプションデータ部のサイズ */ }; /* * pm_type の値 */ # define PM_STATUS 1 /* 応答ステータス */ # define PM_UNKNOWN 2 /* 未知のメッセージを受信しました */ /* * pm_state の値 */ /* * クラス 1 の応答 */ # define PM_STARTING 1 /* ポートモニタは起動状態 */ # define PM_ENABLED 2 /* ポートモニタは有効状態 */ # define PM_DISABLED 3 /* ポートモニタは無効状態 */ # define PM_STOPPING 4 /* ポートモニタは停止状態 */ /* * ポートモニタへのメッセージ */ struct sacmsg { int sc_size; /* オプションデータ部のサイズ */ char sc_type; /* メッセージタイプ */ }; /* * sc_type の値 * 次に定義するコマンドは SAC がポートモニタに送信するコマンドです。 * 拡張性を持たせるため、コマンドは各クラスに分類されます。 * 後に定義されるクラスは、それまでに定義されたクラスのコマンドに、 * そのクラスの新たなコマンドが追加されたスーパーセットです。 * どのクラスもヘッダーは同じです。新たなコマンドは、 * ヘッダーにオプションデータ部が追加される形で定義されます。 * オプションデータ部の形式は、コマンドで自動的に決まります。 * 重要な注: SAC から最初に送信されるメッセージは常にクラス 1 の * メッセージです。これに対して、ポートモニタは応答メッセージで * 自分が理解できる最大のクラスを知らせます。 * もう 1 つ注意しなければならないのは、ポートモニタは必ず * 受信したメッセージと同じクラスで応答しなければならないことです。 * (すなわち、クラス 1 のコマンドには必ずクラス 1 で応答します。) */ /* * クラス 1 のコマンド (現在は、クラス 1 のコマンドしかありません) */ # define SC_STATUS 1 /* ステータス要求 */ # define SC_ENABLE 2 /* 有効に設定 */ # define SC_DISABLE 3 /* 無効に設定 */ # define SC_READDB 4 /* pmtab の読み込みを要求 */ /* * Saferrno のエラー番号。Saferrno は pmadm と sacadm の両方で共有する * ことに注意してください。 */ # define E_BADARGS 1 /* 引数またはコマンド行が不正 */ # define E_NOPRIV 2 /* ユーザーは操作特権を持っていない */ # define E_SAFERR 3 /* 一般的な SAF エラー */ # define E_SYSERR 4 /* システムエラー */ # define E_NOEXIST 5 /* 指定が無効 */ # define E_DUP 6 /* エントリが既に存在している */ # define E_PMRUN 7 /* ポートモニタが実行中 */ # define E_PMNOTRUN 8 /* ポートモニタが実行されていない */ # define E_RECOVER 9 /* 修復中 */ |
図 F-2 は、SAF の論理ダイアグラムを示します。ひとつの SAF が、システムごとに複数のポートモニタをどのように生成するのかを示しています。これは、いくつかのモニタが並行して動作し、各種の異なるプロトコルを同時に起動させていることを意味します。
/etc/saf/_sysconfig は、論理ダイアグラムに対応するディレクトリ構造を示します。「/etc/saf/_sysconfig」 の後に、ディレクトリ構造内のファイルおよびディレクトリの説明があります。
システムごとの構成スクリプト。
SAC の管理ファイル。ここには、SAC の管理下にあるポートモニタの情報が入っています。
ポートモニタ pmtag のホームディレクトリ。
ポートモニタ pmtag の、ポートモニタごとの構成スクリプト。
ポートモニタ pmtag の管理ファイル。ここには、pmtag の管理下にあるサービス情報が入っています。
ポートモニタ pmtag の管理下にあるサービス svctag の、サービスごとの構成スクリプトが入っているファイル。
ポートモニタがカレントディレクトリで プロセス ID を書き込んだり、アドバイザリロックを設定したりするファイル。
ポートモニタが、sac および ../_sacpipe からのメッセージを受信したり、sac に応答メッセージを返したりするファイル。
SAC のログファイル。
ポートモニタ pmtag が作成するファイル (たとえば、ログファイル) を入れるディレクトリ。