ONC+ 開発ガイド

付録 F SAF を使用したポートモニタプログラムの作成

この付録は、ポートモニタがサービスアクセス機能 (SAF : Service Access Facility) とサービスアクセスコントローラ (SAC) の下で走る時に実行する必要のある各機能について簡単に説明します。

SAF の概要

SAF を使用すると、サービスアクセスの手続きが汎用化され、ローカルシステムへのログインアクセスも、ローカルサービスへのネットワークアクセスも同じ方法で管理できます。SAF を使用すると、システムがサービスにアクセスするときに、ttymon を始めとする各種のポートモニタ、リスナ、ユーザーアプリケーションのために特別に作成したポートモニタを使用できます。

ポートモニタがどのようにアクセスポートの監視と管理を行うかは、SAF から決まるのではなく個々のポートモニタで決まります。したがって、ユーザーは独自のポートモニタを開発してインストールすれば、システムを希望に応じて拡張できます。このようにユーザーが自由に拡張できるという点は、SAF の大きな特徴の一つです。

SAF については、サービスは要求に応じて起動するプロセスにすぎず、サービスの提供する機能に制限はありません。

SAF は、制御プロセスであるサービスアクセスコントローラ (SAC) と、サポートディレクトリ構造の 2 つのレベルに対応する 2 つの管理レベルとで構成されます。上位の管理レベルはポートモニタを管理し、下位レベルはサービスを管理します。

管理レベルでみると、SAF は次の要素で構成されています

SAC の概要

SACSAF の制御プロセスです。SAC/etc/inittab のエントリに入れて init() で起動します。SAC の機能は、ポートモニタを、システム管理者が指定した状態に保持することです。

管理コマンド sacadm を使用して、SAC にポートモニタの状態を変更させることができます。また、sacadm を使用して、ポートモニタを SAC の管理下に入れたり、管理から外したり、SAC が管理しているポートモニタの情報をリストしたりできます。

SAC の管理ファイルには、SAC の管理下の各ポートモニタを一意的に識別できるタグと、各ポートモニタを起動するコマンドのパス名が入っています。

SAC が実行する主な機能を簡単に示します。

ポートモニタの基本機能

ポートモニタは、マシン上の同タイプの入力ポートセットを監視するプロセスです。ポートモニタの主目的は、外部からのサービス要求を検出し、それを適切にディスパッチすることです。

ポートとは、外部に開かれたシステム上のアクセスポイントです。ポートには具体的には、ネットワーク上のアドレス (TSAPPSAP)、コードで接続された端末ライン、電話線入力などがあります。何をポートとみなすかは、ポートモニタ自体が定義します。

ポートモニタはいくつかの基本機能を実行します。機能によっては SAF のために必要なものもありますし、ポートモニタ自身が必要に応じて設計した機能もあります。

ポートモニタの主な機能を 2 つ示します。

ポート管理

ポートモニタの第 1 の機能はポートの管理です。ポートを実際にどう管理するかの詳細は、ポートモニタの開発者が定義します。1 つのポートモニタで 1 つのポートを管理するとは限りません。同時に複数のポートを管理することもできます。


注 -

ポート管理の例として、電話回線の接続における回線速度の設定、適切なネットワークアドレスとの結合、サービス終了後のポートの再初期化、プロンプトの出力などがあります。


アクティビティの監視

ポートモニタの第 2 の機能は、アクティビティ指示に対応する責任があるポートを監視することです。検出されるアクティビティには、次の 2 つのタイプがあります。

  1. アクティビティの 1 つは、ポートモニタがモニタ固有のアクションを取るための指示です。ポートモニタ固有アクティビティの例として、ブレークキーが押されると回線速度を循環させるという指示があります。すべてのポートモニタが、同じ指示を認識して同じように反応するわけではありません。ポートモニタがどのような指示を検出するかは、ポートモニタの開発者が定義します。

  2. もう 1 つは外部からのサービス要求です。ポートモニタはサービス要求を受信すると、受信したポートからどのサービスが要求されているかを判定できなければなりません。同じサービスを複数のポートで提供できることに注意してください。

ポートモニタのその他の機能

この項では、ポートモニタのその他の機能について簡単に説明します。

システムへのアクセス制限

ポートモニタは現在実行中のサービスに影響することなく、システムへのアクセスを制限できなければなりません。そのために、ポートモニタは有効と無効の 2 つの内部状態を保持しています。ポートモニタの起動時の状態は、sac で設定した環境変数 ISTATE の値で決まります。SAC とポートモニタのインタフェース」 の項を参照してください。

ポートモニタの有効と無効を切り換えると、その管理下にあるすべてのポートが影響を受けます。ポートモニタが 1 つのポートだけを管理している場合は、そのポートだけが影響を受けます。複数ポートがそのポートモニタの管理下にあるときは、それらのポートすべてが影響を受けます。

ポートモニタの有効と無効の切り換えは動的な操作です。この操作で、ポートモニタの内部状態が変わります。ただし、次にポートモニタを起動するときには、この状態は失われます。

これに対して、個々のポートの有効と無効の切り換えは静的な操作です。この操作によって管理ファイルが変更されます。次にポートモニタを起動しても、ポートの状態は残ります。これに対して、個々のポートの有効と無効の切り換えは静的な操作です。この操作によって管理ファイルが変更されます。次にポートモニタを起動しても、ポートの状態は残ります。

utmp エントリの作成

ポートモニタは、起動するサービスの utmp エントリを作成する責任があります。そのとき、エントリのタイプフィールドは USER_PROCESS に設定します。ただし、pmadm でサービスを追加したときに -fu を指定した場合だけです。utmp エントリは、次にサービスによって変更されます。サービスが終了したときは、utmp エントリは DEAD_PROCESS に設定されなければなりません。

ポートモニタのプロセス ID とファイルのロック

ポートモニタは、起動したときに自分のプロセス ID をカレントディレクトリの _pid というファイルに書き込み、そのファイルにアドバイザリロックを設定します。

サービス環境の変更: doconfig() の実行

ポートモニタは、ポートモニタの管理ファイル _pmtab に指定されているサービスを起動する前に、サービスごとの構成スクリプトがあればライブラリ関数 doconfig() を呼び出してそれを実行します。セキュリティ上の理由だけではなく、サービスごとの構成スクリプトで制限コマンドの実行が指示される場合があるので、ポートモニタはルートアクセス権で起動されます。サービスの呼び出し方法の詳細は、ポートモニタの開発者が定義します。

ポートモニタの終了

ポートモニタはシグナル SIGTERM を受け取ると、手続きを守って自分自身を終了させます。ポートモニタの終了シーケンスを次に示します。

  1. ポートモニタは停止状態に入ります。これ以降はサービス要求を受け取りません。

  2. ポートモニタの状態を有効に切り換える指示はすべて無視します。

  3. ポートモニタは、管理下の全ポートの制御を明け渡します。ポートモニタの以前のインスタンスが停止状態のときは、新たなインスタンスが正常に起動できなければなりません。

  4. プロセス ID ファイルのアドバイザリロックを解除します。アドバイザリロックが解除されると、プロセス ID ファイルの内容は未定となり、ポートモニタを新たに起動できるようになります。

SAF ファイル

この項では、SAF によって使用されるファイルについて簡単に説明します。

ポートモニタの管理ファイル

ポートモニタのカレントディレクトリには、_pmtab という管理ファイルがあります。_pmtab は、pmadm コマンドとポートモニタ固有の管理コマンドとを使用して管理します。ポートモニタ固有の管理コマンドについては次に説明します。


注 -

ポートモニタ listen に固有の管理コマンドは nlsadmin() です。ポートモニタ ttymon に固有の管理コマンドは ttyadm() です。ユーザーがポートモニタを作成するときは、これらのコマンドと同様の機能を持つ、ポートモニタ固有の管理コマンドを提供する必要があります。


サービスごとの構成ファイル

ポートモニタのカレントディレクトリには、サービスごとの構成スクリプトも含まれています (サービスごとの構成スクリプトはオプションのためでない場合もあります)。サービスごとの構成スクリプトの名前は、_pmtab ファイルで設定したサービスタグと同じです。

ポートモニタのプライベートファイル

ポートモニタは、ディレクトリ /var/saf/tag (tag はポートモニタの名前) にプライベートファイルを作成できます。プライベートファイルの例として、ログファイルや一時ファイルがあります。

SAC とポートモニタのインタフェース

sac は、起動するポートモニタごとに次の 2 つの環境変数を作成します。

  1. PMTAG

  2. ISTATE

    sac は、この変数を一意的に識別できるポートモニタタグに設定します。ポートモニタは sac メッセージに応答するときに、このタグを使用して自分自身を示します。ISTATE は、ポートモニタの起動時の内部状態を指定するのに使用します。ISTATE は、ポートモニタが許可状態でスタートするときに enable に、禁止状態でスタートするときに disable に設定されます。sac はポートモニタの正常なポーリングを定期的に実行します。

sac とポートモニタは FIFO を使用して通信します。ポートモニタは、sac からのメッセージを受信するためにカレントディレクトリに _pmpipe をオープンし、sac に応答メッセージを返すために ../_sacpipe をオープンします。

メッセージ形式

この節では、sac からポートモニタに送るメッセージ (sac メッセージ) と、ポートモニタから sac に送るメッセージ (ポートモニタメッセージ) について説明します。メッセージは、FIFO を通して C の構造体形式で送信されます。例 F-2 を参照してください。

sac メッセージ

sac から送られるメッセージの形式は、次の構造体 sacmsg で定義されています

struct sacmsg {
 	int sc_size; /* オプションデータ部分のサイズ */
 	char sc_type; /* メッセージのタイプ */
};

sac からポートモニタに送られるメッセージには 4 つのタイプがあります。メッセージがどのタイプなのかは、sacmsg 構造体の sc_type フィールドに次のどれかの値を設定して示します。

SC_STATUS

ステータス要求

SC_ENABLE

有効設定メッセージ

SC_DISABLE

無効設定メッセージ

SC_READDB

ポートモニタの _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_STATUS

状態情報

PM_UNKNOWN

否定応答

どちらのタイプのメッセージの場合も、pm_tag フィールドにはポートモニタのタグが、pm_state フィールドにはポートモニタの現在の状態が設定されます。pm_state フィールドに設定できる状態は次の 4 つです。

PM_STARTING

起動

PM_ENABLED

有効

PM_DISABLED

無効

PM_STOPPING

停止

現在の状態は、sac からの最後のメッセージによる状態変更を反映しています。

通常の応答メッセージは状態メッセージです。否定応答メッセージを返すのは、sac から受信したメッセージが理解できなかったときだけです。

pm_size はメッセージのオプションデータ部分のサイズを示します。pm_maxclass はメッセージクラスを指定するのに使用します。この 2 つのフィールドは、「メッセージクラス」 の項で説明します。Solaris では常に pm_maxclass1sc_size0 です。

ポートモニタから先にメッセージを送ることはありません。ポートモニタは、受信したメッセージに応答するだけです。

メッセージクラス

メッセージクラスの概念は、SAF の拡張性に含まれています。これまでに説明したメッセージはすべてクラス 1 のメッセージです。どのメッセージにも可変長データは含まれておらず、必要な情報はすべてメッセージヘッダーに入っていました。

新たなメッセージをプロトコルに加えると、それによって新たなメッセージクラス (たとえば、クラス 2) が定義されます。sac からポートモニタに送られる最初のメッセージは常にクラス 1 のメッセージです。どのポートモニタもクラス 1 のメッセージは理解できますから、sac が送る最初のメッセージは必ず理解されます。ポートモニタは、それに対する sac への応答の中で、ポートモニタが理解できる最大メッセージクラス番号を pm_maxclass フィールドに設定します。sac は、ポートモニタに対して、pm_maxclass の値より大きいクラスのメッセージは送りません。ポートモニタが理解できるクラスより上のクラスのメッセージを必要とする要求は、失敗に終わります。Solaris では、pm_maxclass は常に 1 です。


注 -

どのポートモニタも、pm_maxclass の値と等しいクラスかそれより小さいクラスのメッセージを受信できます。したがって、pm_maxclass フィールドが 3 の場合、このポートモニタはクラス 123 のメッセージを理解できます。ポートモニタはメッセージを生成することはありません。受信したメッセージに応答するだけです。ポートモニタからの応答は、元のメッセージと同じクラスでなければなりません。


sac の側からだけメッセージが生成されるので、sac が生成できるクラスより上のクラスのメッセージをポートモニタが処理できる場合も、このプロトコルは正しく機能します。

pm_size (pmmsg 構造体の要素) と sc_size (sacmsg 構造体の要素) は、メッセージの中のオプションデータ部分のサイズを示します。オプションデータ部分の形式は未定義です。オプションデータ部分の形式定義は、メッセージのタイプによって決まっています。Solaris では、sc_sizepm_size は常に 0 です。

ポートモニタの管理インタフェース

この項では、SAC で使用できる管理ファイルについて説明します。

SAC の管理ファイル _sactab

サービスアクセスコントローラの管理ファイルには、SAC の管理下にある全ポートモニタの情報が入っています。このファイルは配布システムに入っています。最初、このファイルには SAC のバージョン番号の入ったコメント行が 1 行入っているだけです。システムにポートモニタを追加するときは、SAC の管理ファイルにエントリを作成します。エントリを追加するには、管理コマンド sacadm-a オプションを付けて実行します。SAC の管理ファイルからエントリを削除するときも、sacadm を使用します。

SAC の管理ファイルの各エントリには、表 F-1 で示されている次の情報が含まれています。

表 F-1 SAC_sactab ファイル

フィールド 

説明 

PMTAG

個々のポートモニタを一意的に識別できるタグ。ポートモニタの命名はシステム管理者が行う。SAC は、管理目的でポートモニタを識別するときはこのタグを使用する。PMTAG には、14 文字までの英数字が入る。

PMTYPE

ポートモニタのタイプ。各ポートモニタには、それを一意に識別できるタグのほかにタイプ指示子がある。タイプ指示子は、同一エンティティを別々に起動したことによるポートモニタグループを示す。有効なポートモニタタイプの例として ttymonlisten が挙げられる。タイプ指示子を使用すると、関連ポートモニタからなるグループの管理が楽になる。タイプ指示子がないと、システム管理者は各ポートモニタタグがどのタイプのポートモニタなのか判断できない。PMTYPE には、14 文字までの英数字が入る。

FLGS

現在定義されているフラグは次の 2 つがある。 -d 起動時にポートモニタを有効にしない。 -x ポートモニタを起動しない。 フラグを指定しないと、デフォルトのアクションが実行される。デフォルトでは、ポートモニタは起動され有効に設定される。

RCNT

ポートモニタが何回エラーを起こしたらエラー停止状態になるかを示す。SAC は、エラー停止状態になったポートモニタは再起動しない。エントリ作成時にこの回数を指定しないと、このフィールドは 0 になる。再起動回数が 0 ということは、ポートモニタが一度でもエラーストップすると、以後再起動されないことを意味する。

COMMAND

ポートモニタを起動するコマンドの文字列。文字列の最初の要素 (コマンド自身) は、フルパスで指定しなければならない。 

ポートモニタの管理ファイル _pmtab

各ポートモニタは、それぞれ 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 このサービスの utmp エントリを作成する。デフォルトではサービスの utmp エントリは作成されない。

ID

サービスを起動するときの ID。ID は、/etc/passwd に入っているログイン名と同じ形式を持つ。

PMSPECIFIC

ポートモニタ固有情報の例として、アドレス、実行するプロセスの名前、接続を渡す STREAMS パイプの名前が挙げられる。この情報はポートモニタのタイプごとに異なる。

COMMENT

サービスエントリに関するコメントを書く。


注 -

サービス呼び出し方法からみて、utmp を作成するのが適当でない場合は、ポートモニタが -u フラグを無視することに注意してください。サービスによっては、utmp エントリが作成されていないと正しく起動できないものもあります (たとえば、login サービス)。


各ポートモニタの管理ファイルには、次の形式の特殊なコメントが入っていなければなりません。

# VERSION=value 

ここで、value はポートモニタのバージョン番号を表す整数です。バージョン番号により、ポートモニタの管理ファイルの形式がわかります。このコメント行は、ポートモニタをシステムに追加したときに自動的に作成されます。これだけが 1 行となってサービスエントリの前に入ります。

SAC 管理コマンド sacadm

sacadm は、SAF 階層の上位レベル (すなわち、ポートモニタ管理) に対する管理コマンドです (sacadm(1M) のマニュアルページを参照)。SAF では、sacadm コマンドを使用して SAC の管理ファイルを変更することによりポートモニタを管理します。sacadm は、次にのような機能があります。

ポートモニタの管理コマンド pmadm

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 つのインタフェースがあります。このインタフェースを使用するサービスは、ストリームパイプの一端をオープンにしておき、そこを通して接続を受信できるようにしておかなければなりません。

ポートモニタに必要な条件

ポートモニタを開発するには、いくつかの一般的な条件が満たされていなければなりません。この節では、そのような条件を簡単に説明します。ポートモニタだけでなく、管理コマンドも同時に作成する必要があります。

起動時の環境

ポートモニタが起動されるときは、次のような初期実行環境が整っていなければなりません。

重要なファイル

カレントディレクトリには、次に示すポートモニタの主要ファイルが存在します。

表 F-3 ポートモニタの主要ファイル

ファイル 

説明 

_config

ポートモニタの構成スクリプト。ポートモニタの構成スクリプトは SAC が実行する。SACinit() により起動される。そのためには、/etc/inittabsac を呼び出すためのエントリを入れておく

_pid

ポートモニタが自分のプロセス ID を書き込むファイル

_pmtab

ポートモニタの管理ファイル。このファイルには、ポートモニタの管理下にあるポートとサービスの情報が入っている 

_pmpipe

ポートモニタが sac からのメッセージを受信するための FIFO

svctag

svctag というタグを持つサービスの構成スクリプト

../_sacpipe

ポートモニタが sac へのメッセージを送信するための FIFO

ポートモニタの実行すべきタスク

ポートモニタは、ポートモニタ固有機能のほかに次のタスクを実行する責任があります。

ポートモニタはサービスを起動するときに次のタスクを実行しなければなりません。


注 -

サービスの起動方法から見て utmp エントリを作成する意味がなければ、ポートモニタは「-u」フラグを無視します。これとは反対に、utmp エントリが作成されていないと正しく起動できないサービスもあります。


構成ファイルとスクリプト

構成スクリプトのインタプリタ: doconfig()

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 に指定できる値は、NORUNNOASSIGN、またはこの 2 つの OR を取った値です。rflag がゼロの場合は、構成スクリプトのすべてのコマンドが解釈されます。rflagNOASSIGN ビットがオンになっていると、assign コマンドは解釈できず doconfig() はエラー終了します。rflagNORUN ビットがオンになっていると、runrunwait のコマンドは解釈できず 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(3N) のマニュアルページで説明するインタプリタ言語で書かれたコマンドスクリプトが入っています。SAC は、起動されると doconfig() 関数を呼び出してシステムごとの構成スクリプトを翻訳します。SAC が起動されるのは、システムがマルチユーザーモードに入ったときです。

ポートモニタごとの構成ファイル

ポートモニタごとの構成ファイル (/etc/saf/pmtag/_config) はオプションの構成ファイルです。ユーザーはこのファイルを使用して、ポートモニタの環境と、ポートモニタが管理している特定のポートで提供されるサービスの環境とをカスタマイズできます。ポートモニタごとの構成スクリプトも、システムごとの構成スクリプトと同じ言語で書かれます。

ポートモニタごとの構成スクリプトは、ポートモニタが起動されたときに翻訳されて実行されます。ポートモニタが SAC に起動されるのは、SAC が起動されて自分自身の構成スクリプト (/etc/saf/_sysconfig) を実行した後です。

ポートモニタごとの構成スクリプトがあると、システムごとの構成スクリプトで提供されるデフォルトの構成スクリプトではなく、ポートモニタごとのスクリプトが実行されます。

サービスごとの構成ファイル

ユーザーは、サービスごとの構成ファイルを使用して特定のサービスの環境をカスタマイズできます。たとえば、一般ユーザーには許されていない特殊な特権を必要とするサービスがあるとします。doconfig(3N) のマニュアルページに記述されている言語を使用すると、特定のポートモニタで提供される特定のサービスにこのような特殊な特権を与えたり奪ったりするスクリプトを書くことができます。

サービスごとの構成スクリプトを作成しておくと、上位レベルの構成スクリプトで提供されるデフォルトのスクリプトの代わりに、そのスクリプトが実行されます。たとえば、サービスごとの構成スクリプトで、デフォルトとは異なる STREAMS モジュールセットを指定することができます。

構成スクリプト言語

構成スクリプトを記述する言語は、一連のコマンドで構成されており、各コマンドはそれぞれ個別に翻訳されます。5 つのキーワード assignpushpoprunwaitrun が定義されています。コメント文字は # です。空白行は無視されます。スクリプトの各行は 1024 文字を超えてはいけません。

assign variable=value

assign コマンドは環境変数を定義するのに使用します。variable は環境変数名で、value は環境変数に割り当てられる値です。value は文字列定数でなければなりません。パラメータ置換は使用できません。value は引用符で囲むことができます。引用符で囲むときの規則は、シェルで環境変数を定義するときの規則と同じです。新たな環境変数を割り当てるための空間が足りない場合、指定構文に誤りがあるときは assign コマンドでエラーが起こります。

push module1[, 
 module2, module3, ...] 

push コマンドは、STREAMS モジュールを fd で指定されたストリームにプッシュします。doconfig(3N) マニュアルページを参照してください。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 を実行する子プロセスを作成できなかったときだけです。

構文上は区別がつきませんが、runrunwait で実行されるコマンドのいくつかはインタプリタの組み込みコマンドです。インタプリタの組み込みコマンドが使用されるのは、プロセスのコンテキストの中でプロセスの状態を変える必要があるときです。doconfig() の組み込みコマンドは、シェルの特殊コマンドと同様、実行するための別のプロセスを生成しません。sh(1) マニュアルページを参照してください。組み込みコマンドの初期セットを次に示します。

cd ulimit umask

構成スクリプトの印刷、インストール、置き換え

この節では、SAC とポートモニタの管理コマンドを使用して、3 種類の構成スクリプトをインストールする方法を説明します。システムごとの構成スクリプトとポートモニタごとの構成スクリプトは sacadm コマンドで管理します。サービスごとの構成スクリプトは pmadm コマンドで管理します。

システムごとの構成スクリプト

sacadm -G  [ -z  script ]

システムごとの構成スクリプトを印刷するか置き換えるときは、-G オプションを使用します。-G オプションだけを指定すると、システムごとの構成スクリプトが印刷されます。-G オプションと -z オプションを組み合わせると、/etc/saf/_sysconfigscript に指定されたファイルの内容で置き換えられます。-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 つは、プロセスの ulimit4096 に設定することにより、プロセスが作成するファイルサイズの上限を設定しています。もう 1 つは、umask077 に設定することにより、プロセスが作成するファイルに適用される保護マスクを指定しています。

runwait ulimit 4096
runwait umask 077

ポートモニタのサンプルプログラム

例 F-1 に示すのは、SAC からのメッセージに応答する以外は何もしないポートモニタのサンプルプログラムです。


例 F-1 ポートモニタのサンプル

# 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 を示しています。


例 F-2 ヘッダーファイル sac.h

/* utmp id のバイト数 */
# define IDLEN 4
/* utmp 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 /* status response */
# define PM_UNKNOWN 2 /* unknown message was received */
/*
 * 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 が、システムごとに複数のポートモニタをどのように生成するのかを示しています。これは、いくつかのモニタが並行して作動し、各種の異なるプロトコルを同時に起動させていることを意味します。

図 F-1 SAF の論理フレームワーク

Graphic

/etc/saf/_sysconfig は、論理ダイアグラムに対応するディレクトリ構造を示します。/etc/saf/_sysconfig の後に、ディレクトリ構造内のファイルおよびディレクトリの説明があります。

図 F-2 SAF のディレクトリ構造

Graphic

/etc/saf/_sysconfig

システムごとの構成スクリプト。

/etc/saf/_sactab

SAC の管理ファイル。ここには、SAC の管理下にあるポートモニタの情報が入っています。

/etc/saf/pmtag

ポートモニタ pmtag のホームディレクトリ。

/etc/saf/pmtag/_config

ポートモニタ pmtag の、ポートモニタごとの構成スクリプト。

/etc/saf/pmtag/_pmtab

ポートモニタ pmtag の管理ファイル。ここには、pmtag の管理下にあるサービス情報が入っています。

/etc/saf/pmtag/svctag

ポートモニタ pmtag の管理下にあるサービス svctag の、サービスごとの構成スクリプトが入っているファイル。

/etc/saf/pmtag/_pid

ポートモニタがカレントディレクトリで プロセス ID を書き込んだり、アドバイザリロックを設定したりするファイル。

/etc/saf/pmtag/_pmpipe

ポートモニタが、sac および ../_sacpipe からのメッセージを受信したり、sac に応答メッセージを返したりするファイル。

/var/saf/_log

SAC のログファイル。

/var/saf/pmtag

ポートモニタ pmtag が作成するファイル (たとえば、ログファイル) を入れるディレクトリ。