ONC+ 開発ガイド

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

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

この付録の内容は、次のとおりです。

SAF の概要

サービスアクセス機能 (SAF) はサービスアクセス手順を一般化するため、ローカルシステム上へのログインアクセスとローカルサービスへのネットワークアクセスを同じように管理できます。 SAF を使用すると、システムがサービスにアクセスするときに、ttymon を始めとする各種のポートモニター、リスナー、ユーザーアプリケーションのために特別に作成したポートモニターを使用できます。

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

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

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

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

SAC の概要

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

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

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

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

ポートモニターの基本機能

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

ポートとは、外部に開かれたシステム上のアクセスポイントです。ネットワーク上のアドレス (TSAP または PSAP)、固定された端末回線、着信電話回線などがポートとなります。何をポートとみなすかは、ポートモニター自体が定義します。

ポートモニターはいくつかの基本機能を実行します。これらの機能の一部は SAF への適合が必要となります。 また、ポートモニター自体の条件や設計により指定される機能もあります。

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

ポート管理

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

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

アクティビティの監視

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

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

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

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

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

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

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

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

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

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

utmpx エントリの作成

ポートモニターは、起動するサービスの utmpx エントリを作成します。そのとき、エントリのタイプフィールドは USER_PROCESS に設定されます。ただし、pmadm でサービスを追加したときに -fu を指定した場合だけです。utmpx エントリは、次にサービスによって変更されます。サービスが終了したときは、utmpx エントリは 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 つの環境変数を作成します。

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 フィールドに次のどれかの値を設定して示します。

ポートモニターは、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_size0 へ設定してください。

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

メッセージクラス

メッセージクラスの概念は、SAF の拡張性に含まれています。前述のメッセージはすべて class 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_sizesacmsg 構造体の要素です。 これらの要素はメッセージのオプションデータ部分のサイズを示します。オプションデータ部分の形式は未定義です。オプションデータ部分の形式定義は、メッセージのタイプによって決まっています。Solaris 環境では、常に sc_sizepm_size0 へ設定してください。

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

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

SAC の管理ファイル _sactab

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

SAC の管理ファイル内の各エントリには次の表に示す情報が含まれます。

表 F–1 サービスアクセスコントローラ _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 にだけ必要なプロンプト文字列が入っています。ポートモニターのタイプごとに、ポートモニターの固有データを引数とし、そのデータを適当な形式で管理ファイルの中に書き出すコマンドが提供されていなければなりません。コマンド ttyadmttymon の形式を提供し、nlsadmin listen の形式を提供します。 ユーザーが定義するポートモニターでも、同じような管理コマンドを提供する必要があります。

ポートモニター管理ファイルの各サービスエントリには、次の情報が次の形式で入っていなければなりません。

svctag:flgs:id:reserved:reserved:reserved:pmspecific# comment 

SVCTAG はサービスを一意的に識別するタグです。タグは、サービスが提供されるポートモニターの中でのみ一意になるようにします。別のポートモニターのサービス (同一サービスまたは別のサービス) には、同じタグが付けられていてもかまいません。サービスを一意的に識別するには、ポートモニタータグとサービスタグの両方が必要です。

SVCTAG には、14 文字までの英数字が入ります。次の表にサービスエントリを定義します。

表 F–2 SVCTAG サービスエントリ

サービスエントリ 

説明 

FLGS

このフィールドには、現在次のフラグを入れることができる  

-x を指定すると、このポートは有効にしない。デフォルトでは有効になる

-u を指定すると、このサービスの utmpx エントリを作成する。デフォルトではサービスの utmpx エントリは作成されない

ID

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

PMSPECIFIC

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

COMMENT

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


注 –

サービス呼び出し方法からみて、utmpx を作成するのが適当でない場合は、ポートモニターが -u フラグを無視します。サービスによっては、utmpx エントリが作成されていないと正しく起動できないものもあります。たとえば、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 が実行する。SAC は init() により起動される。そのためには、/etc/inittab に sac を呼び出すためのエントリを入れておく

_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);

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(3NSL) マニュアルページにも説明があります。SAC は、起動されると doconfig() 関数を呼び出してシステムごとの構成スクリプトを翻訳します。SAC が起動されるのは、システムがマルチユーザーモードに入ったときです。

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

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

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

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

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

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

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

構成スクリプト言語

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

assign キーワード

assign キーワードは環境変数の定義に使用します。

assign variable=value

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

push キーワード

push キーワードは fd が指定するストリームへ STREAMS モジュールをプッシュするために使用します。 doconfig(3NSL) マニュアルページを参照してください。

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

module1 は最初にプッシュされるモジュール名、module2 は 次にプッシュされるモジュール名です (module3 以降も同様)。指定したどれかのモジュールがプッシュできなかったとき、このコマンドはエラーとなります。その場合、同じ行で指定している残りのモジュールは無視され、既にプッシュされたモジュールはポップされます。

pop キーワード

pop キーワードは指定したストリームから STREAMS モジュールをポップするために使用します。

pop [module]

引数なしで pop コマンドを実行すると、ストリームの一番上のモジュールがポップされます。引数を指定すると、モジュールは、指定したモジュールがストリームの一番上にくるまで、モジュールが 1 つずつポップされます。指定したモジュールがストリームにない場合は、ストリームはもとの状態のままで、コマンドはエラー終了します。モジュール名の代わりに特殊キーワード ALL を指定すると、ストリームから全モジュールがポップされます。ただし、一番上のドライバより上のモジュールだけが対象になることに注意してください。

runwait キーワード

runwait キーワードは、コマンドを実行してその終了を待ちます。

runwait command

command には、実行するコマンドのパス名を指定します。command/bin/sh -c を付けて実行されます。シェルスクリプトもこのようにして構成スクリプトから実行できます。command が見つからないか、実行できなかったとき、または command は存在してもステータスが 0 以外のときは、runwait コマンドは実行エラーとなります。

runキーワード

run キーワードは、runwait と同じですが、command の終了を待たない点が異なります。

run command

command には、実行するコマンドのパス名を指定します。run コマンドがエラー終了するのは、command を実行する子プロセスを作成できなかったときだけです。

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

cd ulimit umask

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

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

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

システムごとの構成スクリプトは sacadm コマンドで管理します。

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 コマンドで管理します。

sacadm -g -p pmtag [ -z script ]

-g オプションは、ポートモニターごとの構成スクリプトの印刷、インストール、置き換えを行うときに使用します。-g オプションには -p オプションが必要です。-g オプションに -p オプションだけを組み合わせて指定すると、script に指定したファイルがポートモニター pmtag に対するポートモニターごとの構成スクリプトとしてインストールされます。-g オプションに -p オプションと -z オプションを組み合わせて指定すると、pmtag に指定したポートモニターのポートモニターごとの構成スクリプトと同じファイル script がインストールされます。あるいは、/etc/saf/pmtag/_config が存在する場合、script の内容に _config が置き換わります。-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

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

次のコーディング例は、SAC からのメッセージに応答するだけの NULL ポートモニターです。


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

次のコーディング例は sac.h ヘッダーファイルを示します。


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

/* utmpx ID のバイト長 */
# define IDLEN 4
/* utmpx ID で使用できるワイルド文字 */
# define SC_WILDC 0xff
/* ポートモニターのタグの最大バイト長 */
# define PMTAGSIZE 14
/*
 * doconfig() の rflag 値
 */
/* assign を許可しない */
# define NOASSIGN 0x1
/* run または runwait を許可しない */
# define NORUN 0x2
/*
 * SAC へのメッセージ (ヘッダーのみ) このヘッダーは常に固定。
 * size フィールド (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 の errno 値。pmadm と sacadm の両方が Saferrno を使用し、
 * これらの値はこの両者で共有される点に注意してください。
 */
# 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-1 に SAF の論理ダイアグラムを示します。ひとつの SAF が、システムごとに複数のポートモニターをどのように生成するのかを示しています。これは、複数のモニターが並行して動作できるようにするため、異なるプロトコルを同時に提供することを意味します。

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

Graphic

次の図は 論理ダイアグラムに対応するディレクトリ構造を示します。 図の後に、ディレクトリ構造内のファイルおよびディレクトリについて説明します。

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

Graphic

SAF ディレクトリ構造内のスクリプトとファイルは次のとおりです。