14    EVM イベントの発信と受信

この章では,Tru64 UNIX Event Manager (EVM) のユーザ・レベル・プログラミング・インタフェースについて説明します。 この章に記載した主な問題は次のとおりです。

この章では,イベントとは,次のいずれかが関心を持つ可能性のある現象が起きたことを示します。

これらは,ローカル・システムまたはリモート・システムに存在します。

この章では,ユーザ・レベルのイベント処理についてのみ説明します。 カーネル内部でのイベントの発信と登録については, kevm(7) を参照してください。

この章では,以下の項目について説明します。

EVM プログラミング・インタフェースがサポートする関数の詳細については,オンライン・リファレンス・ページに記載しています。 使用頻度の高い EVM 関数の概要と例については 14.7 節 を参照してください。

イベント・ビューアとコマンド行インタフェースについては,この章では説明していません。 ビューアについては,オンライン・ヘルプを参照してください。 コマンド行での操作については,『システム管理ガイド』 マニュアルを参照してください。

14.1    イベントとイベント管理

EVM では,イベント情報の発信,配信,保存,および表示を集中管理することができます。 このとき,各イベント発信者が使用するイベント・チャネルには依存せず,発信者と現在のチャネルの関係を変更する必要もありません。 EVM を使用すると,従来のバージョンの Tru64 UNIX システムと比較して,システム管理者は簡単にイベント情報にアクセスできます。 また,柔軟なインフラストラクチャが提供されているので,次の配信元でイベント配信チャネルとして使用することができます。

イベント情報を渡すためのメカニズムは,イベント (またはイベント通知) と呼ばれ,イベントを生成するコンポーネントはイベント発信者 (event poster) と呼ばれます。 EVM のイベント発信メカニズムは,単方向の通信チャネルです。 これは,発信者が,イベントのアクセスを希望する任意エンティティに対して情報を通信できるようにしたものです。 発信者は,どのエンティティが発信イベントのアクセスを希望しているかを把握している必要はありません。

イベント情報を受信するエンティティは,イベント受信者 (event subscriber) と呼ばれます。 イベントによって,受信者の種類は異なり,システム管理者や,他のソフトウェア・コンポーネント,または一般ユーザなどが含まれることがあります。 また,イベントによっては受信者が存在しないこともあります。

イベントは,任意のプロセスで発信および受信を行うことができます。 また,同一のプロセスで発信と受信の両方を行うこともできます。 ただし,特定のイベントの発信および受信は,セキュリティ権限によって管理されます (14.4 節)。

Tru64 UNIX システムの以前のバージョンでは,イベント処理のためにさまざまな種類のチャネルがサポートされており,標準のものもあれば専用のものもありました。 最も単純なイベント・チャネルは,静的な ASCII ログ・ファイルです。 このログ・ファイルには,単一のソースからのイベント情報が格納され,ユーザは標準の UNIX ツール (more など) を使用して表示できます。 動的なチャネルとしては,システム・ロガー (syslog) やバイナリ・イベント・ロガー (binlog) などがあります。 どちらのチャネルでも,デーモン・プロセスを使用して,複数のソースに関するイベント情報の受信,記録,および転送を行います。 syslog と binlog についての詳細は, syslogd(8) および binlogd(8) を参照してください。

EVM では,複数のイベント・チャネルを集中管理するために,すべてのソースのイベントが 1 つのイベント・ストリームに結合されます。 イベントに関心のあるユーザは,結合されたストリームをリアル・タイムで監視したり,イベントの履歴をストレージから取得して表示したりできます。 EVM の表示機能には,グラフィカルなイベント・ビューアと,完全なセットのコマンド行ユーティリティがあります。 イベント・ビューアは,SysMan アプリケーションに統合されています。 コマンド行ユーティリティでは,イベントのフィルタ,ソート,およびフォーマットをさまざまな方法で行うことができます。 選択した状態が自動的に通知されるように EVM を構成することもできます。

注意

EVM は,メッセージをブロードキャストするための機能です。 2 つのプロセス間に二地点間専用通信チャネルをインプリメントする場合には使用しないでください。 このような目的で EVM を使用すると,システムの性能が低下する可能性があります。 別のプロセスと通信を確立する必要があるときに,送信する情報がシステム管理者やその他のプロセスに関係がない場合は,ソケットまたはパイプなどの通信チャネルを使用して直接接続してください。

図 14-1 は,発信,受信,および取得操作の概要です。 イベント取得操作については, evmget(1) を参照してください。

図 14-1:  EVM の概要

14.2    EVM イベント処理の概要

EVM イベントはデータのパッケージであり,さまざまなソフトウェア・コンポーネント間で受渡したり,後で表示するためにデータを保存できます。

イベントを発信するためには,EVM データベースに保存されているイベント・テンプレートと一致している必要があります。 イベントのデータ項目は,発信するイベントまたは一致するテンプレートに設定できます。 発信したイベントのデータ項目とイベント・テンプレートのデータ項目をマージした結果が,イベント受信者が受信するイベントの内容になります。 イベントの一致およびマージについての詳細は,14.6.3.2 項 および 14.6.3.3 項を参照してください。

標準的なイベントのライフ・サイクルには,次の操作が含まれます。

  1. プロセスから発信される可能性のあるすべてのイベントに対して,テンプレートが作成されます。 テンプレートは,製品またはサブシステムのインストール時に EVM データベースに格納されます。

  2. イベントの受信に関係するプロセスでは,EVM デーモンに対する接続が確立されて,受信要求が発行されます。 受信要求には,関心のあるイベントの識別に使用するフィルタが指定されています。

  3. プロセスまたはカーネル構成要素でイベントに値する状態変更が検出されると,EVM への接続が行われて,状態変更に対応するイベントが発信されます。

    また,EVM 以外のイベント・チャネルでイベントが発信された場合,そのイベントはチャネルによって独自の方法で処理されます (ログの記録など)。 次に,EVM フォーマットに変換されて,EVM に渡されます。

  4. EVM では,イベントの発信要求の有効性が検査されます。 特に,EVM テンプレート・データベースに対応するテンプレートが存在するかどうかと,発信者にイベントを発信する権限があるかどうかが検査されます。 有効である場合は,発信されたイベントとテンプレートのデータ項目がマージされたイベントが作成されます。

  5. マージされたイベントが,EVM からイベントを受信するすべてのプロセスに渡されます。

  6. 受信者によって,イベントが適切に処理されます。 たとえば,保存や,システム管理者へのメール送信,アプリケーションのフェールオーバーの開始などが行われます。

14.3    EVM の起動と停止

EVM は,システムのスタートアップ時に自動的に起動し,システムのシャットダウン時に自動的に停止します。

EVM デーモンの起動と停止については,『システム管理ガイド』を参照してください。

起動時に EVM がイベント・テンプレート・データベースを設定する手順,および起動後にイベント・テンプレート・データベースを修正する方法については,14.6.3.4 項を参照してください。

14.4    イベントの発信とアクセスの権限

イベントの発信とアクセスを行う場合は,セキュリティに関して考慮することが重要です。

システム管理者は,EVM 権限ファイル /etc/evm.auth を変更することによって,どのユーザがイベントのアクセスや発信を行えるかということを制御できます。

アクセス制御についての詳細は,『システム管理ガイド』 マニュアルを参照してください。

14.5    EVM イベントの内容

イベントによって送信されるデータのフォーマット,タイプ,および目的には,さまざまな種類があります。 単純なイベントの場合は,単純なテキスト・メッセージです。 複雑なイベントの場合は,複雑なバイナリ・データの集合で構成されます。 この場合,データの変換時にフォーマット情報が必要になります。 2 つのアプリケーションが協調して動作する場合は,簡単なバイナリ・フラグまたはカウントの場合もあります。 ただし,イベントにとってデータは必須ではありません。 たとえば,特定のイベント・タイプを送信するだけで,特定の状態変更が発生したことを受信者が認識できることもあります。

イベント・データは,変換方法に応じて複雑になります。 イベントをわかりやすくするために,数値データと説明テキストを組み合わせることもできます。 また,受信プロセスによっては,バイナリ・データとして表示する必要があります。 さらに,複数の言語で表示しなければならないこともあります。

EVM のデータ・メカニズムでは,イベントに適した任意の方法でデータを送信できます。 この機能を使用できる構造体には,EVM フォーマット・データ項目 (EVM の標準データ項目の 1 つ) および EVM 変数データ項目があります。

イベント・データの構造体には,次の 2 種類のデータ項目があります。

イベントを作成する場合は,任意の数のデータ項目を含めることができます。 イベントを発信すると,ホスト名やタイムスタンプなど,一定の環境標準データ項目が EVM によって自動的に追加されます。

14.5.1    標準データ項目

標準データ項目は,イベントで必須の共通データ項目で,EVM によって認識または処理されます。 標準データ項目の名前は,列挙型定数として指定されています。 したがって,名前自体の領域は,イベントの主な領域ではありません。

一部の標準データ項目は,イベントを発信するアプリケーションまたはイベントのテンプレートによってイベントに挿入されます。 その他のデータ項目は,必要に応じて,EVM コンポーネントにより,自動的に挿入されます。 任意のデータ項目をイベントから抽出することができます。

標準データ項目は, EvmEvent(5) で説明しています。 特に注意が必要な項目は,表 14-1 にリストし,以下の節で説明しています。

表 14-1:  標準データ項目

データ項目 説明
イベント名 (14.5.1.1 項) イベントの名前を指定する。
フォーマット (14.5.1.2 項) イベントのメッセージ文字列を指定する。
優先度 (14.5.1.3 項) 受信者にとってのイベントの重要度。 イベント配信の順序には影響しない。
I18N カタログ (14.5.1.4 項) 多国語対応イベント用の I18N カタログ・ファイルの名前。
I18N メッセージ・セット ID (14.5.1.4 項) I18N メッセージ・カタログ内のメッセージ・セットを識別する。
I18N メッセージ ID (14.5.1.4 項) イベント・フォーマットの I18N メッセージ ID。
クラスタ・イベント (14.5.1.5 項) TruCluster 環境では,この項目によりイベントはクラスタ内の全ノードに配信される。
参照 (14.5.1.6 項) イベントの説明テキストに対する参照。

以降の各項で,表 14-1 の標準データ項目について詳しく説明します。

14.5.1.1    イベント名データ項目

イベント名データ項目は,イベントを識別する文字列で,通常はイベントの発信元と何が起こったかを知らせます。 イベント名は,イベントの型を一意に識別するので,同じイベントの異なるインスタンスに同じ名前を付けることができます。

EVM は,特定のイベントを発信またはアクセスする権限がユーザにあるかどうかを判断する場合や,イベントに対応するテンプレート情報を検索する場合に,イベント名を使用します。 クライアント・アプリケーションは,イベント名をイベント・フィルタと組み合わせて使用し (14.7.11 項),受信するイベントを選択したり,イベントの受信時に必要な動作を判断したりします。 システム管理者は,イベント名を使用して,特に関心のあるイベントのイベント・ログを検索できます。

イベント名には次のような特徴があります。

コンポーネントを 3 つ以上含んだ名前を持たないイベントは,発信できません。

イベント名のコンポーネントは,最初 (左端) のコンポーネントで始まるイベントの階層構造を表します。 最初のコンポーネントは最も一般的で,イベントの発信を行うエンティティをグローバルなレベルで区別します。 その他のコンポーネントは,レベルの詳細さが増していく順番に並べます。 まずイベントを発信すべきと判断したコンポーネント,何が起こったかという情報,最後に最も詳細な情報を置きます。 たとえば,sys.unix.fs.filesystem_full というイベント名があるとします。

何が起こったかを示す基本イベント名は,イベントが発信されるときにコンポーネントを追加して拡張し,より詳しい情報を示すようにすることができます。 前述したイベント名の例では,追加のコンポーネントとしてファイル・システム名を追加して (sys.unix.fs.filesystem_full.usr など),どのファイル・システムが満杯になったかという情報による拡張が行えます。 名前を拡張しても,短い方の名前の各コンポーネントはすべて拡張した名前に一致するので,このイベントが sys.unix.fs.filesystem_full イベントであることに変わりはありません。 sys.unix.fs.filesystem_full という名前のイベントを検索すると,拡張した名前のイベントも見つかります。

この命名規則では,オープンエンドな方法でイベントを識別できます。 つまり,どのレベルでも詳細な情報を記述できるようになっています。 注意深く命名すると,受信者が,特定のイベントやイベント・クラスを選んで表示したり監視したりするのが容易になります。 イベント名を詳しく指定すると,指定する基準も正確になります。 たとえば,イベント名を myco.myprod.env.temp.ok および myco.myprod.env.temp.high とすると,システム管理者は myco.myprod.env.temp と指定して温度に関するイベントをすべて監視したり,myco.myprod.env.temp.high と指定して,監視するイベントを高温に関するイベントのみに制限したりすることができます。

システム管理者やモニタリング・ソフトウェアの混乱や誤動作 (誤操作) を避けるため,新しいイベントに一意の名前を付けるようにしてください。 次の規則に従って命名すれば,すでに使用されている名前との重複を避けることができます。

次の表に,特殊な意味を持つ名前をリストします。 名前の衝突を防ぐために,これらの名前は指定以外のイベントの最初のコンポーネントには使用しないようにしてください。

名前 用途
sys オペレーティング・システムのイベント sys.unix.evm.logger.config_err
local ローカルなユーザ組織用

local.app.payroll.started
local.app.payroll.started

test ローカルなテスト・イベントの発信用  

イベント名は,選択して公開した後は変更しないでください。 アプリケーションが発信するイベントは,外部インタフェースの一部であり,他のアプリケーションや製品コンポーネントがそのイベントの受信に依存している可能性があるためです。

14.5.1.1.1    予約コンポーネント名

前項では,イベント名が詳細になれば,管理者は EVM フィルタを用いて特定のタイプのイベントを探す操作がやりやすくなると説明しました。 たとえば,sys.unix.hw.registered.cpu というイベントは,オペレーティング・システムのハードウェア管理サブシステムが,プロセッサを検出して登録したことを示します。 このイベントでは,変数データ項目 (14.5.2 項) にプロセッサのハードウェア ID が含まれているので,イベント・ビューアまたは evmshow でイベントを表示させれば,ハードウェア ID が分かります。 そのプロセッサに関連する問題をトレースしている場合は,イベント・フィルタを使用して,そのハードウェア ID を含むイベントのみを選択することができます。 イベント自体に含まれる変数の値でイベントをフィルタリングすることはできないので,この選択は,選択に使用する情報がイベント名にも含まれている場合にのみ使用できます。 この場合,ハードウェア管理サブシステムは,登録した各デバイスに割り当てたハードウェア ID によって名前を拡張することで,選択を可能にします。 ハードウェア ID が 2 の場合,次のコマンドで関連するイベントを検索できます。

evmget -A -f "[name sys.unix.hw.*.2]"

この例では,アスタリスク (*) がワイルドカード文字になり,ID 2 を含むハードウェア・サブシステムが発信したイベントがすべて,報告内容にかかわらず見つけられるようになります。 ただし,ハードウェア・サブシステム以外のサブシステムも,プロセッサに関する情報を通知する可能性があるので,このフィルタでは,関心のあるデバイスのイベントをすべて選択できないことがあります。 このようなイベントを検索に含めたい場合もあります。 [name *.2] と指定すると検索範囲を広げることはできますが,ハードウェア ID に関係のない 2 という数値を含む他のイベントがログに入っていると,それも結果に含まれてしまいます。

予約コンポーネント名の規約により,発信したサブシステムやアプリケーションにかかわらず,関連イベントを選択できるようになります。 規約では,予約コンポーネント名は下線文字 (_) で始まり,常に特定タイプのエンティティを識別します。 発信サブシステムやアプリケーションは,イベントを発信する前に予約コンポーネント名を基本イベント名に付加し,識別するエンティティに応じて,それに続くコンポーネントが特定のインスタンスを識別します。

予約コンポーネント名は,一般に特定のサブシステムやアプリケーションの代わりとして定義されますが,名前が定義された後の使用は制限されません。 そのエンティティに関して報告する情報を持つものはすべて,発信するイベント内に,予約名とエンティティ ID を含めることができます。 たとえば,予約コンポーネント名 _hwid は,ハードウェア管理サブシステムで定義されているとおり,ハードウェア・デバイス ID を指定します。 その後にはデバイス ID が必要です。 したがって,前述の例を続けると,プロセッサの登録を報告するイベントの名前は,sys.unix.hw.registered.cpu._hwid.2 になります。 次のコマンドで,すべてのハードウェア・デバイスに関連するすべてのイベントが検索できます。

evmget -A -f "[name *._hwid]"
 

次のコマンドを使用すると,最も関心のあるプロセッサまで検索の幅を狭めることができます。

evmget -A -f "[name *._hwid.2]"
 

発信者が規約に従っているという前提で,ハードウェア ID を除くすべてのコンポーネントに対するフィルタにワイルドカードを使用すると,発信したサブシステムやアプリケーションにかかわらず,関連するイベントがすべて確実に検索されます。

予約コンポーネント名の規約では,予約コンポーネント名を使用する際は,発信されるイベントに同じ名前と値の変数 (先頭の下線を含む) が含まれていなければなりません。 この規約により,受信クライアントは,イベント名を解析して値を検索することなく,EVM の通常の API 関数を通じてイベントから値を取得できます。 それ以外の場合,この規約では同じ情報をイベントに 2 回追加しなければならないので,EvmEventPost() 関数は名前が下線で始まる変数をイベント内で自動的に検索し,呼び出し側のプログラムが対応するコンポーネントをイベント名にまだ含めていない場合,イベントを発信する前にそれを付加します。 名前の自動拡張については, EvmEventPost(1) を参照してください。

イベントには,任意の数の予約コンポーネント名を,イベント基本名の後の任意のコンポーネント位置に順不同で付加できます。 各予約コンポーネントのすぐ後には,対応する値コンポーネントが必要です。

EVM の名前照合方式では,発信イベントと一致する部分の最も多い名前が検索されるため,イベントを登録するときに,イベントのテンプレート名に予約コンポーネントを指定する必要はありません。 この節で説明した例では,テンプレート (およびイベントの基本名) は sys.unix.hw.registered になります。 予約コンポーネントを名前に追加すると,イベントの発信時に,より詳細なレベルを指定できます。

先頭に下線が 1 つある予約コンポーネント名を定義する規約は,システム用に予約されています。 ローカル・サイトとサードパーティ製品のベンダは,_ _prod_code のように,予約コンポーネント名の先頭に下線を 2 つ使用して,独自の規約を規定しなければなりません。

Tru64 UNIX が使用する予約済みのコンポーネント名のリストは, EvmEvent(5) を参照してください。

14.5.1.1.2    イベント名の比較

イベント名を検索対象の名前と比較するには,EVM の API 関数を使用します。 ただし,strcmp() は使用しないでください。 着信イベントの名前には,予期しない数のコンポーネントが含まれることがあります。 イベント名の照合についての詳細は,14.7.12.9 項を参照してください。

14.5.1.2    イベントのフォーマット・データ項目

フォーマット・データ項目には,分かりやすい文字列でイベントの概要が説明されています。 フォーマット行は,次のような場合に展開されて表示されます。

フォーマット・データ項目には,イベント内の他のデータ項目への参照が含まれることもあります。 次の例について説明します。

Application close-down has started
 
Close-down of application $app has started
 

2 つ目の例では,$app はアプリケーションの名前に置き換わります。 このとき,イベントにはこの名前の変数の値が含まれているとします。 したがって,この行は次のように表示されます。

Close-down of application payroll has started

変数については,14.5.2 項 で説明しています。

フォーマット行は,発信コンポーネントに関する簡潔な一貫性のある識別情報から開始して,発生した現象が簡単にわかるように記述します。 次のガイドラインを考慮してください。

代入を行う変数名を指定するには,$app のように,前に $ を付けます。 必要に応じて,@host_name のように,標準データ項目の前に @ を付けることもできます。 標準データ項目名では大文字および小文字は区別されませんが,変数名では大文字と小文字が区別されます。

フォーマット・テキストで $@ の特別な意味を無効にするには,前にバックスラッシュ (\) を付けます。 テキストに定数のバックスラッシュを含めるには,バックスラッシュを 2 つ (\\) 使用します。 隣接するテキストからデータ項目または変数を区別するには,${app} のように,名前を中カッコで囲みます。

変数やデータ項目の表示方法は,名前の後にパーセント記号 (%) とフォーマット指定子を続けることで制御できます。 フォーマット指定子では,表示フィールドの最小幅と表示フォーマットを指定します。 たとえば,次のフォーマット文字列は,device_id という変数が 16 進数形式で表示され,temperature が浮動小数点,小数点以下 2 桁で表示されることを示します。

The cabinet temperature for device 0x$device_id%x is $temperature%.2 degrees
 

これに対応するイベントは次のように表示されます。

The cabinet temperature for device 0x7a is 62.4 degrees

有効なフォーマット指定のリストは, evmshow(1) を参照してください。

表 14-2 は,フォーマットされたイベントを作成するときに,イベントのフォーマット・テキストに変数とデータ項目が代入される場合の例です。

表 14-2:  イベント・テキストへの変数代入

フォーマット・データ項目 変数データ項目 生成されたテキスト
Application started なし Application started
Debug message: $msg msg (string) = "Checkpoint reached" Debug message: Checkpoint reached
Temperature too high: $temp temp (float) = 87.2 Temperature too high: 87.2
This event was posted by @user_name なし This event was posted by jem

14.5.1.3    イベントの優先度データ項目

イベントの優先度は,イベントのログ記録,ソート,表示,および対応を手動または自動で行うときの,イベント選択基準として使用されます。 この優先度は,受信者の EVM クライアントにイベントを配信する順序の決定には使用されません。 イベントは,EVM デーモンが受信した順に配信されます。 優先度は 0 〜 700 の整数値で,0 が最も低い重要度になります。 優先度レベルについての詳細は, EvmEvent(5) を参照してください。

システム管理者は,優先度に応じて次のような対応を行います。

優先度は,発信コード内にハードコードしないでください。 優先度はテンプレート・ファイルに設定します。

イベントの優先度を選択する場合は,同じアプリケーション内,およびその他の類似したアプリケーション内のほかのイベントと一貫性を保つようにします。 アプリケーションに適用できない優先度がある場合は,使用する優先度を制限してください。 EvmEvent(5) の優先度情報をガイドラインとして使用し,既存のテンプレートを参照して,同様のイベントで使用されている優先度を確認します。

イベントの優先度は慎重かつ客観的に選択してください。 発生する結果ではなく,通知する内容を考慮します。 たとえば,アプリケーションの障害には重要なものもありますが,多くはそうではありません。 アプリケーションの重要度は,上位のコンポーネントで判断します。 上位のコンポーネントでは,エラー・イベントを受信して,重要度が認識されているアプリケーション・エラーが通知されたときに,クリティカル・レベルのイベントを発行します。

これらの考慮事項を基準にして,エラーは発生しているがクリティカルの基準に達していないイベントの優先度は,500 (クリティカル・レベル) ではなく 400 (エラー・レベル) に指定します。 一方,システムの温度が高すぎることを通知するイベントは,ほとんどの場合クリティカルです。

相互に関連するイベントの優先度は,イベントごとに設定します。 たとえば,優先度が 500 の障害イベントを発信してアプリケーションに障害が発生したことを通知してから,関連するイベントを発信してアプリケーションがリストアされたことを通知する場合,2 つ目のイベントの優先度は,たとえば 200 など,最初のイベントより低く設定します。 リストア・イベントに優先度 500 を設定した場合は,イベントに対して不適切なアクションがとられることがあります。

14.5.1.4    I18N カタログ名,メッセージ・セット ID,およびメッセージ ID データ項目

イベントに含まれているテキストが,さまざまな国の管理者に通知される可能性がある場合は,イベントの多国語対応を検討してください。 イベントの多国語対応を行うには,すべてのイベントに対するフォーマット文字列が格納された I18N カタログ・ファイルが必要です。 ただし,イベント・テキストを表示するときにカタログ・ファイルが使用できない場合は,イベントにテキストを記述してください。

カタログ・ファイルは,通常の I18N の規則に従って配置し,イベントの仕様にそのファイル名を含める必要があります。 言語ロケールごとに,独自のカタログ・ファイルを指定することもできます。 詳細は,『国際化ソフトウェア・プログラミング・ガイド』を参照してください。

カタログ・ファイルを複数のメッセージ・セットに分割し,イベントにメッセージ・セット ID を指定することもできます。 1 つのイベントに関連するメッセージは,すべて同じメッセージ・セットに属していなければなりません。

データ項目 I18N_catalog および I18N_msgset_id はカタログ名を定義します。 また,該当する場合は,フォーマット・テキストのメッセージ・セットと,イベントに含まれる文字列変数のうち関連するメッセージ ID が指定されているものをすべて定義します。 データ項目 I18N_format_msg_id には,フォーマット・テキストのメッセージ ID を定義します。 カタログまたはメッセージ ID が指定されていない場合は,イベント概要を表示するときに,フォーマット・データ項目に指定されたフォーマット・テキストが使用されます。

イベント・テキストの翻訳の設定方法については,14.6.4 項を参照してください。

14.5.1.5    クラスタ・イベント・データ項目

クラスタ・イベント・データ項目が true に設定され,TruCluster システムのメンバであるシステムにイベントが発信されると,EVM はクラスタのアクティブなノードすべての受信者にイベントを配信します。 項目が設定されていないか,false に設定されている場合,イベントはローカル・ノードの受信者にのみ配信されます。 イベントがスタンドアロンのシステムに発信される場合,この項目に効果はありません。 クラスタ・イベント・フラグを true に設定しているイベントは,クラスタワイド・イベントと呼ばれます。

テンプレートまたは evmpost へのイベント指定の中で使用すると,クラスタ・イベント・データ項目の値は true または false になります。 API 関数のデータ項目として使用されると,値は EvmTRUE または EvmFALSE のいずれかになります。

クラスタワイド・イベントは,イベントがユーザ・レベルとカーネルのどちらから発信されたかによって,異なります。

EVM は,クラスタ別名を使用してクラスタに接続されている受信者が,クラスタワイド・イベントのコピーを 1 つだけ受信するようにします。 ただし EVM logger は,どのクラスタ・メンバが発信したかに関係なく,フィルタに一致するイベントをすべて受信して記録するため,各ノードでコピーが別々に記録されます。 その結果,格納されたイベントを,クラスタ別名を使用した接続により evmget コマンドを使用して取得する場合,イベントを記録した各ノードからイベントのコピーが 1 つづつ返されることになります。

クラスタベース・アプリケーションを開発する場合,どのイベント (存在する場合) をクラスタワイド・イベントに指定する必要があるかを,注意して判断してください。 目的の受信者が,イベントの各インスタンスをどのノードがポストしたかを認識して適切な動作を行うように設計することが特に重要です。 member _id データ項目を標準ライブラリ関数 (clu_get_info() など) と組み合わせて使用すると,発信とローカル・ノードについての詳細が得られます。 詳細は, EvmEvent(5) および clu_get_info(3) を参照してください。

14.5.1.6    参照データ項目

参照データ項目は,イベントの詳細表示のために,イベントの説明テキストを検索するときに使用されます。 このデータ項目の値は,イベント名とともにイベント・チャネルの説明スクリプトに渡されて,イベントに関連付けられている説明テキストが識別できるようにします。 説明スクリプトはチャネルごとに異なるため,フィールドのフォーマットはチャネルごとに異なります。 ただし,EVM ログ (evmlog チャネル) に対して格納/取得されるイベントの場合は,参照データ項目には次のフォーマットの文字列を指定します。

cat:catalog_name[:set_number]
 

catalog_name は,イベントの説明テキストが格納されている I18N カタログの名前です。 説明スクリプトで適切なメッセージを検索できるようにするには,カタログ内の各説明メッセージの先頭に,中カッコで囲まれたイベントの名前がなければなりません。

オプションの set_number は,説明テキストが格納されたカタログのメッセージ・セットの番号です。 セット番号を指定しない場合は,カタログ全体が検索されます。

サードパーティ製品のベンダおよびローカル・アプリケーションの場合は,説明テキストを Tru64 UNIX の説明テキスト・カタログ evmexp.cat には追加しないで,別のカタログを提供します。 この項目の値は,通常,テンプレート・ファイルにグローバル・データ項目として設定します。

イベントの説明テキストの作成方法についての詳細は,14.6.2 項を参照してください。

14.5.2    変数データ項目

イベントの変数データ項目は,イベントのインスタンスごとに異なる情報を指定するときに使用します。 たとえば,高温が検出されたときにイベントを発信する場合は,実際の温度を浮動小数点値として変数データ項目に指定することができます。

変数データ項目には,次のプロパティがあります。

変数名は,大文字または小文字の英数字および下線 (_) を任意に組み合わせて指定できます。 名前はわかりやすく指定する必要があります。 ただし,変数名はイベントに含めて送信されるため,名前が長くなるほどイベントの物理的な長さも大きくなります。

変数データ項目は,受信者が直接抽出および使用したり,表示用のフォーマット済みイベントを作成するために,イベント・フォーマットのテキスト文字列と結合することができます。

表 14-3 に,EVM でサポートされる変数の型を示します。

表 14-3:  EVM の変数データの型

型識別子 サイズと型
EvmTYPE_BOOLEAN 8 ビットの整数
EvmTYPE_CHAR 8 ビットの文字
EvmTYPE_INT16 16 ビットの符号付き整数
EvmTYPE_INT32 32 ビットの符号付き整数
EvmTYPE_INT64 64 ビットの符号付き整数
EvmTYPE_UINT8 8 ビットの符号なし整数
EvmTYPE_UINT16 16 ビットの符号なし整数
EvmTYPE_UINT32 32 ビットの符号なし整数
EvmTYPE_UINT64 64 ビットの符号なし整数
EvmTYPE_FLOAT 32 ビットの浮動小数点値
EvmTYPE_DOUBLE 64 ビットの浮動小数点値
EvmTYPE_STRING ヌルで終了する文字列
EvmTYPE_OPAQUE サイズを明示的に指定しなければならないバイナリ・データ

変数にはイベントのインスタンス固有の情報が含まれるため,通常は発信者が指定します。 ただし,文書化の目的では,変数の名前と型,およびダミーの値をイベントのテンプレートに指定しておくと便利です。 テンプレートについては,14.6.3 項を参照してください。

14.6    イベント・セットの設計

アプリケーションまたはサブシステムを設計する場合は,関連するイベント・セットも設計する必要があります。

EVM イベントは,慎重に設計する必要があります。 イベントは,ヒューマン・スタイル (読みやすいテキスト) とプログラム・スタイル (バイナリ・データ) の 2 種類のインタフェース要件を満たす必要があります。 イベントが発信された後は,テキスト形式またはバイナリ・データ形式のいずれかで表示および処理することができます。

イベントを設計するときは,次の事項を考慮します。

  1. 関連するイベント・セットに対して,ファミリ名を決定します (詳細は,14.5.1.1 項を参照)。

  2. 監視しているエンティティが関心のある状態変更の一覧を作成し,各イベントの名前を選択します (詳細は,14.6.1 項を参照)。

  3. 各イベントの内容を決定します。 すべてのイベントには固有の名前が必要です。 ほとんどの場合,フォーマット文字列と優先度を指定する必要があります。 また,多くの場合,変数も必要です。 各変数について,型および指定できる値を決定します (詳細は,14.5 節を参照)。

  4. 文書化のために,各イベントの詳細な説明を記述します。 イベントの意味,発生するタイミング,ユーザまたは責任のある受信者の対応方法,およびイベントの内容 (特に,すべての変数データ項目) の詳細を記述します。 説明テキストは,通常,カタログ・ファイル内に保存され,アクセスして表示することができます (詳細は,14.6.2 項を参照)。

  5. イベントごとに,テンプレートに指定する項目,および発信者が指定する項目を決定します。 イベント名を除いて,発信コードおよびテンプレートの項目はすべてオプションです。 オプション項目が発信コードおよびテンプレートの両方で宣言されている場合は,発信者の宣言が優先されます (テンプレートについての詳細は 14.6.3 項を,一般に発信されるイベント項目についての詳細は 14.5 節を,テンプレートと発信イベントのデータ項目のマージ方法についての詳細は 14.6.3.3 項をそれぞれ参照)。

  6. イベントを多国語対応にするかどうかを決定します。 多国語対応にする場合は,I18N カタログ・ファイルに対して名前を選択し,必要なメッセージ・セットをカタログに設定します (詳細は,14.6.4 項を参照)。

設計者は,EVM イベントは,ほかのプログラムまたはサブシステムが依存するインタフェースであることを認識する必要があります。 このため,設定後は,通常変更しないようにします。

14.6.1    イベントに値する状態変更の決定

イベントの重要性は,アプリケーションごとに異なります。 場合によっては,コードで認識された状態変更が,ほかのコードに通知が必要な重要性を持つかどうかを決定するのが困難なこともあります。

次の現象が発生した場合は,イベントを発信することをお勧めします。

次の現象が発生した場合は,イベントを発信しないでください。

イベントを発信するときは,特定の状態が繰り返し発生する場合などに,短期間に同じイベントを繰り返し発信しないようにします。 場合によっては,指定の期間内に同じ現象が発生した回数を通知するなど,要約イベントを一定間隔で発信することも有効な方法です。

イベント処理が頻繁に発生すると,アプリケーションがメッセージの負荷に対応できなくなる可能性があり,イベントが失われる原因になります。 失われたイベントの処理方法については,14.7.12.10 項を参照してください。

14.6.2    イベントの説明テキストの作成

説明テキストは,すべての EVM イベントに必要です。 説明テキストは,発信時にはイベントに含まれていませんが,カタログ・ファイルに保存されており,イベントの参照データ項目と名前データ項目の内容によって参照されます (14.5.1.6 項を参照)。 sys.unix イベントの説明テキストは,evmexp.cat というカタログ・ファイルに物理的に格納されています。 イベントの説明テキストを表示するには,evmshow-x または -d オプションを使用するか,または SysMan イベント・ビューアを使用してイベントの詳細な表示を要求します。

説明には,イベントの名前および意味を記述します。 コンテキスト (一定時間内の発生回数やほかのイベントの存在など) によって意味が異なってくるイベントの場合は,この内容および例をいくつか記述します。 イベントに対してアクションが必要な場合は,そのアクションを記述します。 コンテキストによってアクションが異なってくる場合は,その内容と例を記述します。 イベントに対するユーザのアクションが必要ない場合は,その点を明示的に記述することをお勧めします。

例 14-1 は,システム・イベントの説明テキストの例です。

例 14-1:  イベントの説明テキストの例

Example 1:
 
EVENT sys.unix.evm.daemon.event_activity
 
Explanation:
 
This high-priority event is posted by the EVM daemon when it
detects a high number of events occurring over several minutes.
 
Action: Use the EVM event viewer or the evmget(1) command to
review the event log for the source of the activity. If the
log does not show high activity around the time at which this
event was posted, it is likely that the events were low priority,
and hence were not logged. You can monitor low-priority events by
running the evmwatch(1) command with an appropriate filter, or
by temporarily reconfiguring the EVM logger to log low-priority
events.
 
Note: You can change the parameters that control the posting
of this event by modifying the daemon configuration file,
/etc/evmdaemon.conf.
 
 

14.6.3    イベント・テンプレートの設計

各発信イベントには,テンプレートが必要です。 1 つのテンプレートに複数のイベントを割り当てることもできます。 テンプレートには,イベント名およびイベントの定数のデータ項目を定義します。

イベント・テンプレートは,次の 2 つの目的で使用されます。

テンプレートは,集中管理されたデータベース内のファイルに保存されます (14.6.3.4 項を参照)。 テンプレート・ファイルには,任意の数のイベント・テンプレートを含めることができます。

14.6.3.1    イベント・テンプレートに設定する項目の決定

イベント発信者が設定する項目およびテンプレートで設定する項目は,設計時に決定します。 定数データ項目は,通常,発信プログラムでイベントにハードコードするのではなく,イベント・テンプレートに設定します。

イベント・テンプレート方式の主な利点は,アプリケーションのすべてのイベントの定数属性を集中管理できるため,開発サイクル時に容易に変更ができることと,アプリケーションの開発後に属性情報の検索先が一箇所になることです。

原則として,発信アプリケーション・プログラムにハードコードするイベント情報は最小限にし,できるだけ多くの情報をイベント・テンプレートに設定します。 通常,アプリケーションでは次の情報だけを設定します。

通常,テンプレートには次の情報を設定します。

変数データ項目の値は,通常,イベント発信時に発信アプリケーションで設定しますが,テンプレートにも変数を設定しておくと,文書化の点から,テンプレートの重要性が高まります。 テンプレートの変数には,通常,ゼロ (文字列変数の場合は空文字列) を設定します。 テンプレートに,実際の省略時の値 (発信者が変更できる) を設定しておくと便利なこともあります。 この場合は,テンプレート・ファイルにコメントとして記述します。

次のソース・ファイルは,1 つのイベントを含むイベントのテンプレート・ファイルの例です。

# Example event file
priority 200            # Default priority
ref  cat:myapp_exp.cat  # Global reference
event {
        name            myco.myapp.env.temperature
        format          "Temperature is $temperature"
        var { name temperature type FLOAT value 0,0 }
}

イベントには,必要に応じて任意の数の変数を指定できます。 ただし,不透明な変数 (バイナリ構造) は,テンプレートではサポートされません。

14.6.3.2    イベント・テンプレート名と発信イベントの名前の照合

EVM は,イベントが発信されると,テンプレート・データベースで,発信イベントの名前とイベント名が一致するテンプレートを検索します。 一致する名前が存在しない場合は,イベントを発信したプログラムにエラー・コードを返します。 一致する名前が見つかった場合は,EVM はテンプレートに設定されているデータ項目を,イベントを発信したプログラムで設定された項目と結合します。 この操作によりマージされたイベントが生成されて,受信者に配信されます。 マージ操作についての詳細は,14.6.3.3 項を参照してください。

テンプレート照合プロセスでは,発信イベント名の左端のコンポーネントと,テンプレートのイベント名のすべてのコンポーネントが一致していることのみが検査されます。 EVM では,次の規則に基づいて,データベース内で最も近い一致を検索します。

テンプレートは,アプリケーションが発信するイベントごとに用意してください。 こうしておくと,イベント固有の情報を,テンプレートに格納することによって集中管理することができます。 しかし,最適一致方式の利点は,発信するときにイベント名にさまざまなインスタンス情報を追加して拡張できることです。 たとえば,デバイス名や温度の値を追加コンポーネントとして追加できます。 インスタンス・コンポーネントを追加した場合は,イベントのフィルタおよびソートが簡単になります。 イベント名の拡張方法の例については,14.5.1.1.1 項を参照してください。

表 14-4 は,イベント・テンプレートと発信イベント間のイベント名の照合の例です。

表 14-4:  名前照合の例

発信イベント名 テンプレートのイベント名 一致の状態
myco.myprod.env myco.myprod.env 一致
myco.myprod.env.temp.high.70 myco.myprod.env.temp 一致
myco.myotherprod myco.myotherprod.start 不一致。 発信イベントのコンポーネント数が足りない。

14.6.3.3    テンプレートと発信イベントのデータ項目のマージ

EVM デーモンによって発信イベントの有効性が確認されると,発信イベントのデータ項目とテンプレートのデータ項目がマージされ,イベントを受信しているクライアントにマージされたイベントが配信されます。 マージ・プロセスでは,テンプレートで設定するテキストおよびデータ項目と,発信者で設定するテキストとデータ項目の選択は,イベント設計者が大部分を決定することができます。

図 14-2 は,イベントのマージの概念図です。

図 14-2:  発信イベントとテンプレートのマージ

テンプレートおよび発信イベントの両方に同じデータ項目が設定されている場合は,マージされたイベントでは発信イベントの値が使用されます。

マージ・プロセスでは,発信イベントおよびテンプレートのデータ項目が結合された,バイナリ形式のイベント構造が生成されます。 マージされたイベントは,フォーマットされたメッセージとしてではなく,バイナリ形式で受信者に配信されるため,受信者は,EVM API 関数を使用して,イベントを表示できるようにフォーマットしたり,イベントから情報を抽出する必要があります。 API 関数については,14.7 節を参照してください。

14.6.3.4    テンプレート・ファイルのインストール -- 位置,命名,所有権,および許可の要件

通常,イベントのテンプレート・ファイルは,製品またはアプリケーションをインストールするときにインストールされます。 システム・テンプレートは,/usr/share/evm/templates の下のサブディレクトリに格納されます。 サードパーティ製品およびローカル・アプリケーションのテンプレートは,ローカル・テンプレート・ディレクトリ /var/evm/adm/templates に格納されます。 EVM デーモンが別のテンプレートの位置を指定できるように,システム・テンプレート・ディレクトリおよびローカル・ディレクトリ間には,リンクが作成されます。

製品またはローカル・アプリケーションにテンプレートを追加するには,アプリケーションをインストールするときに,ローカル・テンプレート・ディレクトリの下に適切な名前のサブディレクトリを作成し,そのディレクトリにテンプレートをインストールする必要があります。 EVM のテンプレート検索ポリシでは,シンボリック・リンクを介して検索されるため,アプリケーションと密接に関連するディレクトリにもテンプレートをインストールし,そのディレクトリとローカル・ディレクトリの間にリンクを作成して接続することもできます。

テンプレート・ファイルの接尾語は,.evt でなければなりません。 また,所有者は root または bin にし,0600,0400,0640,または 0440 のいずれかのアクセス許可が必要です。 新しいディレクトリにも,適切な許可を割り当てます。

ファイルをインストールしたら,root として evmreload -d コマンドを実行し,EVM で新しいテンプレートを認識できるようにします。 次に,エラーがないかどうかを確認します。 詳細は, evmreload(8) を参照してください。

14.6.3.5    イベント・テンプレートの登録の確認

テンプレートが登録されたかどうかを判断するには,-i オプションを指定して evmwatch を使用します。 たとえば,次のコマンドを実行すると,myapp アプリケーションに登録されているすべてのイベント・テンプレートの名前の一覧が表示されます。

evmwatch -i -f "[name myco.myprod.myapp]" | evmshow -t "@name"

テンプレートの詳細を表示するには,evmshow-d オプションを使用します。 evmwatch では,アクセス権限のないイベントのテンプレートは返されません。 このため,テンプレートを表示する場合は,root としてログインしなければならないことがあります。

14.6.4    イベント・テキストの翻訳の設定 (I18N)

イベントのフォーマット・データ項目およびイベントに含まれる文字列変数の値を表示するときに,別の言語に変換できるようにするには,イベントを多国語対応 (I18N) にする必要があります。 さまざまな国で使用される製品を開発している場合は,任意のまたはすべての項目を変換できるようにします。

格納されている同一のイベントを表示する場合,ユーザごとに異なる言語が使用される可能性があります。 このため,言語の翻訳は,イベントの発信時ではなく,イベントが表示用のフォーマットに変換されるときに同時に行う必要があります。 使用されるすべての言語のテキストをイベントに設定することは実際的ではないため,イベントがフォーマットされるときに,関連するメッセージ・カタログが使用できるようにしなければなりません。 製品の開発者は,メッセージ・カタログを提供し,製品と一緒にインストールされるようにメッセージ・カタログを組み込む必要があります。 カタログが使用できない場合のために,多国語対応のイベントに対して省略時の母国語文字列を含めることができます。

次のデータ項目は,イベントの多国語対応化をサポートしています。

イベントのメッセージ識別子は,すべて同じメッセージ・カタログに関連付けられているため,すべて同じメッセージ・セット (省略時は 1) に属していなければなりません。

イベントのフォーマット文字列のカタログ ID,セット ID,およびメッセージ ID は,通常は変更されないため,すべてイベント・テンプレートに設定してください。 イベントに文字列型の変数が含まれる場合,その変数はデバイス名やアプリケーション名などの項目を参照していることが多いため,表示する言語にかかわらず,通常は翻訳する必要はありません。 このため,ほとんどの場合,メッセージ ID を設定する必要はありません。 ただし,文字列変数の値を翻訳しなければならない稀な場合は,発信者はメッセージ ID を設定する必要があります。

表 14-5 は,イベント例に対する多国語対応の値の例です。

表 14-5:  多国語対応のイベントに対するデータ項目値の例

イベントのデータ項目 メッセージ ID
名前 acme.prod.env.temp n/a
メッセージ・カタログ acme_prod.cat n/a
フォーマット文字列 Temperature of sensor $sensor is $temperature 541
文字列変数 "sensor" S27 n/a
文字列変数 "temperature" high 542

temperature.cat の英語版とフランス語版は,次のようになります。

ビューアでイベントを表示する場合は,ビューアによってフォーマット関数が呼び出され,ユーザのロケール設定に応じて,フォーマット関数から次のいずれかの文字列が返されます。 ただし,適切なカタログ・ファイルを見つけることができ,指定されたメッセージがファイルに格納されている必要があります。

"Temperature at sensor S27 is high"
"La temperature du senseur S27 est haute"

フォーマット関数でカタログからイベントが解釈できない場合は,イベントに設定されている値が使用され,ユーザのロケールにかかわらず,次のメッセージが返されます。

"Temperature at sensor S27 is high"

イベント・ファイルが別のシステムに渡されて分析される場合は,関連付けられているカタログ・ファイルが使用できないことがあります。 上の例のように,イベントに省略時の値が含まれている場合は,母国語で表示することができます。 イベントに省略時の値が含まれていない場合は,フォーマットされた文字列を使用して,イベント名およびすべての変数のダンプを表示することができます。 たとえば,次のように表示されます。

Event "myco.myprod.env.temp": $sensor = "S27" $temperature = "high"

I18N についての詳細は,『国際化ソフトウェア・プログラミング・ガイド』を参照してください。

14.7    EVM プログラミング・インタフェース

EVM イベントは不透明なデータ構造であり,EVM のアプリケーション・プログラミング・インタフェース (API) 関数によるアクセスと操作が可能です。

Tru64 UNIX システム上で実行する Java プログラムをサポートするために,JNI (Java Native Interface) パッケージがあります。 Java インタフェースの詳細は,Web ブラウザを使用して次のファイルを参照してください。

file:/usr/share/doclib/evm/java_api/help-doc.html

以降の各項では,EVM イベントに対して通常実行されるいくつかの操作に関するプログラミング情報について説明するとともに例を示します。

14.7.1    EVM ヘッダ・ファイル

EVM 関数を使用するプログラムには,次のヘッダ・ファイルをインクルードします。

#include <evm/evm.h>

14.7.2    EVM API ライブラリ

EVM API 関数を使用するプログラムでは,シェアード・ライブラリ libevm.so またはスタティック・ライブラリ libevm.a に対してリンクを作成する必要があります。 シェアード・ライブラリはルート・パーティション・ディレクトリ /shlib に格納されています。 このため,システムがシングルユーザ・モードで稼働しているときに,動作している必要があるプログラムから使用できます。 ただし,EVM デーモンは,実行レベルが 2 になったときに起動されるため,シングルユーザ・モードのときにデーモンに接続しようとすると失敗します。 /usr/shlib から /shlib 内のシェアード・ライブラリに対するシンボリック・リンクが作成されているため,省略時のライブラリ検索パスを使用して,リンク先のプログラムがライブラリにアクセスすることができます。

14.7.3    戻り状態コード

EVM 関数から返された状態コードは,ヘッダ・ファイル evm/evm.h に列挙されます。 EVM 関数から共通に返される値は,次のとおりです。

状態コードの詳細なリストは,evm/evm.h ファイルを参照してください。

14.7.4    シグナルの処理

EVM API は,通常の処理ではシグナルは使用せず,アプリケーション・プログラムでシグナルが使用されるときにも通常は干渉しません。 ただし,Tru64 UNIX システムの省略時のアクションでは,データを読み込むプロセスが存在しないときに,ローカル接続に対して書き込もうとした場合,サイレントに終了します。 このため,EVM デーモンが接続アクティビティの前または途中で終了した場合は,クライアント・プロセスが何も記録しないで終了する恐れがあります。

この状態が発生するのを回避するために,EvmConnCreate() 関数は,SIGPIPE のハンドラが呼び出し側によって設定されているかどうかをチェックし,設定されていない場合は省略時のハンドラをインストールします。 シグナルが発生してもハンドラは何のアクションもとりませんが,ハンドラが存在するためにクライアントは終了しません。 EVM ハンドラは,EvmConnCreate() 呼び出しの前または後にプログラムで独自のハンドラを設定することにより,無効にできます。 プログラムで省略時のアクションが必要な場合は,EvmConnCreate() を呼び出してから SIG_DFL にアクションを設定します。

詳細は, signal(2) を参照してください。

14.7.5    マルチスレッド・プログラムでの EVM

EVM API 関数は,すべてスレッド・セーフです。 このため,内部静的記憶域を使用しなければならない場合は,ロックを使用して,記憶域に対して各スレッドから同時にアクセスされないようにします。 それでも,マルチスレッド・プログラムで EVM API 呼び出しを使用する場合は,同期エラーを回避するために一定の予防策が必要です。

  1. 可能であれば,API 関数から返されたエンティティの使用を,エンティティが設定されているスレッド内に制限します。 次のエンティティで制限することができます。

    エンティティ エンティティを返す関数
    接続コンテキスト EvmConnection_t EvmConnCreate()
    イベント EvmEvent_t

    EvmEventCreate()
    EvmEventCreateVa()
    EvmEventRead()
    EvmEventDup()

    データ項目 EvmItemValue_t EvmItemGet()
    データ項目リスト EvmItemList_t EvmItemListGet()
    変数 EvmVarValue_t EvmVarGet()
    変数リスト EvmVarList_t EvmVarListGet()
    イベント・フィルタ EvmFilter_t EvmFilterCreate()
    接続 fd EvmFd_t EvmConnFdGet()

  2. これらのエンティティを複数のスレッドから参照する場合は,ロックを使用して,同時アクセスまたは同時更新が行われないようにする必要があります。

これらの規則に従わない場合は,予期しないエラーが発生する可能性が高くなります。

14.7.6    EVM イベントの再割り当てと複製

EVM イベントの作成,受信,または読み取りが行われた後で,そのイベントを再度割り当てる必要がある場合は,イベントがメモリに保存される方法を理解しておくと便利です。

EvmEvent_t 型には,制御情報を保持する短いハンドル構造体へのポインタと,イベント本体へのポインタが定義されています。 EvmEventCreate() または関連する関数を使用して新しいイベントを作成すると,その関数によってハンドルおよびイベント本体に対してヒープ・メモリがそれぞれ割り当てられます。 次に,本体のアドレスがハンドルに格納され,ハンドルのアドレスが関数の EvmEvent_t 出力引数として返されます。 イベントを参照する場合は,返されたポインタを使用しなければなりません。

変数を追加するなど,イベントを変更すると,多くの場合,イベント本体に対して使用されていた領域が解放されて,別の位置に再度割り当てられます。 このとき,ハンドルに格納されたアドレスが自動的に更新され,新しい位置が反映されます。 つまり,再割り当ては,プログラムに対して完全に透過的に行われます。 ハンドルの位置は,イベントの存在期間内は変更されません。

イベントを変数間で転送する場合は,簡単な C 言語の代入文を EvmEvent_t 型の変数間で使用します。 保持されている値は,一定の位置 (イベントのハンドル) へのポインタだけなので,必要に応じて両方の変数からイベントを参照できます。 ただし,後で EvmEventDestroy() を使用してイベントを破壊する場合は,両方の参照を破棄しなければなりません。

イベントの再割り当てでは,イベント本体はコピーされず,イベントのハンドルへの参照だけがコピーされることに注意してください。 完全に独立したイベントのコピーを作成する必要がある場合には,EvmEventDup() 関数呼び出しを使用します。

14.7.7    コールバック関数

EVM の発信および受信クライアントは,EvmConnCreate() 関数呼び出しを使用して EVM デーモンに接続します。 このとき,EvmRESPONSE_IGNOREEvmRESPONSE_WAITEvmRESPONSE_CALLBACK の 3 つの応答モードから 1 つを指定しなければなりません。 これらのモードについては, EvmConnCreate(3) を参照してください。

受信クライアントの場合は,応答モードとして EvmRESPONSE_CALLBACK を指定する必要があります。 着信イベントは,EvmConnCreate() の 4 番目の引数に指定したコールバック関数によって受信クライアントに渡されます。 受信クライアントからコールバック関数を使用する場合の例については,14.7.12.6 項を参照してください。

コールバック関数を使用する場合は,シグナル・ハンドラと異なり,非同期的に呼び出されないことに注意してください。 プログラムで EvmConnWait()select(),または関連する関数を使用して入力アクティビティの接続を確認してから,EvmConnDispatch() を呼び出してアクティビティを処理する必要があります。 EvmConnDispatch() によって,接続からの着信メッセージが読み込まれ,必要に応じてコールバック関数が呼び出されます。 コールバック関数が呼び出されるタイミングについては, EvmCallback(5) を参照してください。

ほかの関数と同様に,コールバック関数に渡される引数は,プログラム・スタックに渡され,関数の有効範囲内でのみ使用できます。 このため,コールバックから戻った後で使用するために値を保存しておきたい場合は,戻る前に値をグローバル・メモリ空間にコピーする必要があります。

コールバックによって着信イベントの存在が通知されたときに,イベントをコールバック内で処理および破壊するのではなく,保存したい場合は,グローバル・アクセスできる EvmEvent_t 型の変数を宣言して,着信イベントを割り当てます。 次に例を示します。

      /* In global declarations */
      EvmEvent_t SavedEvent;
      ...
 
      /* In your callback function */
      SavedEvent = cbdata->event;
 

イベントを割り当てたときは,イベント本体ではなくイベントへの参照だけがコピーされるため,コールバック関数でイベントを破壊してはなりません。 イベントの処理が終了したら,後でイベントを破壊する必要があります。 破壊しない場合,メモリ・リークが発生することがあります。

イベントの割り当てについては,14.7.6 項を参照してください。

14.7.8    接続ポリシの選択

プログラムが発信クライアントの場合,EVM デーモンに接続するポリシを選択する必要があります。 次のいずれかを選択してください。

処理中に EVM 接続を確立する場合の CPU 負荷は無視できません。 セットアップと認証のプロトコルで,ファイル入出力の他にメッセージのトランザクションが複数必要になるからです。 原則的には,通常のプログラム動作中に何度もイベントの発信があると予測される場合には,永続的な接続を行ってください。 このオプションを選択した場合,プログラム・コードでは,EVM デーモンが終了した場合の予期しない切断を考慮しなければなりません。 切断の処理については,14.7.9 項 を参照してください。

イベントをあまり発信しない場合 (たとえば,予期しないエラーが起きたときに限られる場合) は,一時的な接続を行ってください。 一時的な接続では,複数のイベントが発信されるとセットアップ時間の負荷が大きくなりますが,永続的な接続に必要なシステム資源の消費と,予期しない切断に対するプログラム・コードでの対処が不要になります。 一時的な接続でローカル・デーモンにイベントを発信するための最も簡単な方法は,EvmEventPost() または EvmEventPostVa() の接続引数として NULL を渡すことです。

永続的な接続は必要としないが,特定の状況で短期間に何度もイベントを発信することが予測される場合は,個別に接続してイベントを連続的に発信するよりも,一時的な接続を作成して,動作が完了した時点でそれを破棄すると良いでしょう。

受信クライアントは,イベントを確実に受け取るために永続的な接続を維持しなければなりません。

14.7.9    切断の処理

プログラムが 受信クライアント の場合は,EVM デーモンからの切断を適切に処理することが特に重要です。 通常の操作では切断は発生しませんが,システムをテストしている場合や障害が検出された場合は,切断が発生することがあります。 EVM デーモンが中断されると,通常数秒のうちに Essential Services Monitor デーモンがこれを自動的に再開させます。 詳細は, esmd(8) を参照してください。

接続を処理する関数からの戻りコードは,必ず検査する必要がありますが,受信クライアントではこれが特に重要です。 受信クライアントは,接続上でのアクティビティを待機していることが多いためです。

切断が発生した場合,プログラムは select() または EvmConnWait() 呼び出しを中断し,後続の EvmConnCheck()EvmConnDispatch() への呼び出しで障害状態のコードが返されます。 このとき,select() または EvmConnWait() 呼び出しにすぐに戻らないでください。 CPU にバインドされたループが発生します。 代わりに,状態コードが接続エラーを示しているかどうかを判断し,接続エラーの場合は EvmConnDestroy() を使用して接続を破壊してから,再接続を試みます。 再接続の最初の試行に失敗した場合は,一定の間隔が経過してから接続が再試行されるように設定して,接続が再度確立されるまで定期的に再試行を繰り返します。 どのような障害でも,通常デーモンはその後自動的に再開されるため,再試行の間隔は,最初の 60 秒は 1 秒に 1 回,その後は接続が回復するまで 5 秒おきにすることをお勧めします。

接続関数から返されるエラー・コードは,切断以外の状態を示していることもあります。 特に,シグナルの結果として,プログラムが EvmConnWait() を中断することがあります。 この場合は,EvmConnWait() 呼び出しにすぐに戻ってください。

プログラムが発信クライアントの場合,切断を処理しなければならないのは,通常は永続的な接続を行っている場合だけです (14.7.8 項 を参照)。 EvmEventPost()EvmEventPostVa() からのリターン状態をチェックし,必要に応じて再度接続を確立し,発信動作をやり直します。

14.7.10    失われたイベント

イベントの動きが活発なシステムでは,発信されたイベントを多く受信するクライアントが,イベントの一部を受信できなくなることがあります。 クライアントが長時間かけてイベントを処理する場合 (たとえば,各イベントをディスクに書き込まなければならないなど) では,起こりやすくなります。 このようなクライアントに EVM logger があります。 これは,イベントを失ったときにそれを認識し,特別な "missed events" を記録することで,その失敗を通知します。 アプリケーションがイベントを失うおそれがある場合は,その危険が最小になるようにシステムを構成し,プログラムが適切に対処するようにしてください。

EVM デーモンから,受信クライアントにイベントを送るときに,固定サイズの通信 (送信および受信) バッファによる接続で行います。 クライアントが適切な時間内に受信イベントを処理することができず,イベントの負荷が高い場合,バッファが満杯になり,デーモンはその後イベントを送信できなくなります。 省略時の受信バッファ・サイズは,sysconfig パラメータ sb_max で定義されたシステムワイド・ソケット・バッファの最大値に設定されています。 送信バッファのサイズは,ソケット・バッファに対するシステムの省略時の値,32,767 に設定されています。

EVM デーモンは多くのシステム・コンポーネントとアプリケーションに対して重要なリソースなので,クライアントがバッファのクリアを待っている間にブロックすることはできません。 その結果,バッファが満杯のために接続バッファに書き込めない場合,デーモンはその接続にブロックされたというマークを付け,他の動作を続けます。 その後にクライアントが入力を読み,バッファに空き領域がある場合,EVM デーモンは失敗した書き込みを完了し,クライアントはイベントを受信します。 ただし,その間に他のイベントが到着し,ブロックされた受信者に送信する必要があっても,デーモンはそれを送信しません。 その代わりに,失った回数をカウントし,接続のブロックが解除された時点でその数を受信者に通知します。 受信者は適切な対処が必要ですが,どのイベントを失ったかを知る方法はありません。

省略時の受信バッファ・サイズを大きくした結果,イベントが失われる可能性は旧バージョンよりも低くなりました。 しかし極端な状況では受信イベントを失う危険性はまだあります。 このようなことが起こる可能性は,いくつかの要因によって決まります。

着信イベントを失うリスクを最小限に抑えるには,着信イベントをできるだけ素早く,かつ効率的に処理できるように,また,イベントを失ったことが通知されたときには適切な対処を行うように,アプリケーションを設計します。 失ったイベントを処理するプログラムの例については,14.7.12.10 項 を参照してください。

アプリケーションがイベントを続けて失う場合は,システム・パラメータを変更して受信バッファ・サイズを大きくすることができます。 受信バッファ・サイズは,省略時のシステム・ソケット・バッファの最大サイズに設定されています。 このパラメータの現在のサイズを調べるには,次のコマンドを入力します。

sysconfig -q socket sb_max

このパラメータの実行時の値を変更して,その値が次のリブートまで有効になるようにするには,次のコマンドを入力します。

sysconfig -r socket sb_max=new_value

この変更は,新しい EVM 接続にのみ影響します。

変更を持続的にするには,sysconfigdb または dxkerneltuner を使用して変更します。 詳細については, sysconfigdb(8) または dxkerneltuner(8) を参照してください。

14.7.11    イベント・フィルタの使用

イベント・フィルタは,ユーザが関心を持つイベント・セットを識別するときに使用します。 イベント・フィルタを設定すると,フィルタ・エバリュエータに対して,対象となるイベントを定義する文字列が渡されます。 次に,一連のイベントが渡されて,フィルタを通過できるかどうかを示す論理値が各イベントに返されます。

EVM の受信クライアント・プログラムでは,フィルタは受信対象のイベントを指定するときに使用されます。 また,受信したイベントに対するアクションを決定するときにも使用されます。 返されるイベントを制限するために,コマンド行ユーティリティで起動オプションとして使用することもできます。 コマンド行ユーティリティでフィルタを使用する方法については,『システム管理ガイド』を参照してください。 フィルタの構文についての詳細は, EvmFilter(5) を参照してください。

フィルタの使用方法についての詳細は,14.7.12.8 項を参照してください。

14.7.12    EVM プログラミング操作の例

以降の各項では,次の操作の例を示します。

14.7.12.1    簡単なイベント操作の実行

すべての EVM クライアントは,標準のデータ項目および変数で構成される不透明なバイナリ構造である EVM イベントを処理できる必要があります。 例 14-2 は,イベントを作成して,そのイベントに項目を追加し,次にそのイベントから項目を取得する場合の例です。

この例では,次の関数について説明します。

例 14-2:  簡単なイベント操作の実行

 #include <stdio.h>
 #include <evm/evm.h>
 
 main()
 {
     EvmEvent_t          event;
     EvmItemValue_t      itemval;
     EvmStatus_t         status;
 
     EvmEventCreate(&event);                                      [1]
 
     EvmItemSet(event,EvmITEM_NAME,"myco.examples.app.started");  [2]
     EvmItemSet(event,EvmITEM_PRIORITY,200);
 
     status = EvmItemGet(event,EvmITEM_NAME,&itemval);            [3]
     if (status == EvmERROR_NONE)
     {   fprintf(stdout,"Event name: %s\n",itemval.NAME); 
         EvmItemRelease(EvmITEM_NAME,itemval);
     }
 
     EvmEventDestroy(event);                                      [4]
 }

  1. EvmEventCreate() を使用して空のイベントを作成します。 この関数を使用する場合は,イベント・ハンドルへのポインタを指定して,標準データ項目が設定されていないイベントを受信します。 イベントは空ですが,メモリは使用するので,後で EvmEventDestroy() を使用して領域を解放する必要があります。 [例に戻る]

  2. 標準データ項目をイベントに追加するには,EvmItemSet() を使用します。 ただし,ほとんどの場合,プログラムで追加する項目はイベントの名前だけです。 その他の標準データ項目は,イベントが発信されたときに自動的に追加されます。 または,イベント・テンプレートに設定してください。 設定可能な項目の一覧については, EvmItemSet(3) を参照してください。 [例に戻る]

  3. イベントの項目を取得するには,EvmItemGet() を使用します。 項目の値は,イベントから EvmItemValue_t 構造体を介して参照される記憶域にコピーされるため,使い終わったら EvmItemRelease() を使用してその記憶域を解放する必要があります。 項目を取得しても,その項目はイベントからは削除されません。 また,コピーを受け取るため,必要なコピー数を取得できます。

    このコード例では,イベントの名前 (既に追加したもの) を取得し,その値をプリントしてから,記憶域を解放します。 イベント内に存在しない項目を要求していることもあるので,取得操作からの戻り状態は必ず確認します。 [例に戻る]

  4. イベントの処理が終了したら,イベントによって使用されていた記憶域を解放します。 [例に戻る]

14.7.12.2    可変長の引数リストの使用

varargs (可変長引数リスト) 版の作成関数を使用してイベントの作成および項目の追加を 1 つのステップで行うと,コードのサイズを削減して,効率を向上させることができます。 また,varargs 版の項目設定関数を使用すると,既存のイベントに対して効率的に項目を追加することができます。

例 14-3 では,次の関数について説明します。

例 14-3:  可変長の引数リストの使用

 #include <stdio.h>
 #include <evm/evm.h>
 
 main()
 {
     EvmEvent_t          event;
 
     EvmEventCreateVa(&event,                                  [1]
                     EvmITEM_NAME,"myco.examples.app.started",
                     EvmITEM_PRIORITY,200,
                     EvmITEM_NONE);
 
     EvmItemSetVa(event,
                 EvmITEM_NAME,"myco.examples.app.finished",    [2]
                 EvmITEM_PRIORITY,100,
                 EvmITEM_NONE);
 
     EvmItemSetVa(event,
                 EvmITEM_VAR_UINT16,"exit_code",17,            [3]
                 EvmITEM_VAR_STRING,"progname","my_app",
                 EvmITEM_NONE);
 
     EvmEventDump(event,stdout);                               [4]
 
     EvmEventDestroy(event);                                   [5]
 }

  1. EvmEventCreateVa() に指定する各項目には,識別子と値が必要です。 引数リストの最後には,EvmITEM_NONE 識別子を付けます。 [例に戻る]

  2. varags 版の EvmItemSet() には,EvmEventCreateVa() と同じ形式の引数リストを使用します。 この例では,イベントに既に設定されている項目を追加しているため,実際には前の値が新しい値に置き換わるだけです。 [例に戻る]

  3. 変数データ項目を varargs リストに入れることは,項目識別子 EvmITEM_VAR_Xxx を用いて簡単に実行できます。 ここで Xxx は変数の型です。 このようにして変数を含める場合,変数を記述するために,識別子の後に正しい数の引数を続けて指定することが重要です。 2 つの引数を必ず付加しなければなりません。 これは,変数名を含む文字列と値引数です。 値引数の型は変数の型に応じて変わります。 この例では,最初の変数には文字列が必要で,2 番目の変数には整数が必要です。 変数の型によっては,さらに変数が必要になることがあります。 詳細は EvmItemSet(3) を参照してください。 [例に戻る]

  4. EvmEventDump() を呼び出すと,イベントがフォーマットされて標準出力に表示されるため,データ項目と変数が期待どおりに追加されたことを確認できます。 [例に戻る]

  5. イベントの処理が終了したら,これが使用していた記憶域を解放します。 [例に戻る]

14.7.12.3    変数の追加と取得

上記の例では,変数項目は EvmItemSetVa() を用いて varargs リストに入れることでイベントに追加されています。 変数の値は,引数として項目識別子を取る varargs 関数のいずれかを用いて追加または変更できます。

例 14-4 では,既存のイベントに変数データ項目を追加するもうひとつの方法を示し,また,変数の値を取得する方法も示します。 EVM ライブラリには,変数について記述するデータ構造体を使用する基本的な get および set 関数と,構造体の処理をカプセル化してプログラミングを簡単にする便利な関数のセットが含まれています。 この例では,両方の型の関数の使い方を示します。

この例では,次の関数について説明します。

例 14-4:  変数の追加と取得

#include <stdio.h>
#include <evm/evm.h>
void  main()
{
   EvmEvent_t          event;
   EvmStatus_t         status;
   EvmVarValue_t       varval_1, varval_2;                   [1]
   EvmVarStruct_t      varinfo;
   EvmString_t         progname;
   EvmUint16_t         exit_code;
 
   EvmEventCreateVa(&event,
           EvmITEM_NAME,"myco.examples.app.finished",
           EvmITEM_NONE);
   /*
    * Set and retrieve some values using basic set/get functions:
    */
   varval_1.STRING = "my_app";
   varval_2.UINT16 = 17;
 
   EvmVarSet(event,"progname",EvmTYPE_STRING,varval_1,0,0);   [2]
   EvmVarSet(event,"exit_code",EvmTYPE_UINT16,varval_2,0,0);
 
   status = EvmVarGet(event,"progname",&varinfo);             [3]
   if (status == EvmERROR_NONE)
   {   fprintf(stdout,"Program name: %s\n",varinfo.value.STRING);
       EvmVarRelease(&varinfo);
   }
 
   status = EvmVarGet(event,"exit_code",&varinfo);
   if (status == EvmERROR_NONE)
   {   fprintf(stdout,"Exit code: %d\n",varinfo.value.UINT16);
       EvmVarRelease(&varinfo);
   }
 
   /*
    * Set and retrieve the same values using convenience functions:
    */
   EvmVarSetString(event,"progname","my_app");                 [4]
   EvmVarSetUint16(event,"exit_code",17);
 
   status = EvmVarGetString(event,"progname",&progname,NULL);  [5]
   if (status == EvmERROR_NONE)
       fprintf(stdout,"Program name: %s\n",progname);
   free(progname);                                             [6]
 
   status = EvmVarGetUint16(event,"exit_code",&exit_code);
   if (status == EvmERROR_NONE)
       fprintf(stdout,"Exit code: %d\n",exit_code);
 
   EvmEventDestroy(event);                                     [7]
}
 

  1. プリミティブ関数を使用してイベントに変数を追加するには,まず EvmVarValue_t 型の共用体に値を設定します。 共用体のメンバの名前は,EVM 変数型の名前と同じです。 [例に戻る]

  2. EvmVarSet() を使用して,イベントに変数を追加します。 変数にはわかりやすい名前を付けます。 最後の 2 つの引数は,不透明な変数を追加するか,または文字列変数に I18N メッセージを設定したとき以外は,0 に設定されます。 [例に戻る]

  3. 変数の値を取得するには,変数の名前を EvmVarGet() に渡します。 EvmVarGet() は,値を EvmVarStruct_t 構造体にコピーします。 この構造体には,変数の名前,型,およびサイズも格納されているので,汎用コードを作成して,任意の型の変数を処理することができます。 変数を取得しても変数はイベントから削除されないので,何回でも変数を取得できます。 返された情報によってヒープ・メモリの空間が使用されるので,値を使い終わったら,EvmVarRelease() を使用してクリーンアップする必要があります。 [例に戻る]

  4. EvmVarSetString() および EvmVarSetUint16() 関数は,EvmVarSetXxx() ファミリの他の関数とともに,EvmVarSet() 関数のシンプルな代用品として使用できます。 これらは必要な引数が少なく,値の構造体を設定する必要もありません。

    文字列変数に I18N メッセージ ID を指定する場合は,EvmVarSetStringI18N() 関数を使用します。 [例に戻る]

  5. EvmVarGetString() および EvmVarGetUint16() 関数は,EvmVarGetXxx() ファミリの他の関数とともに,EvmVarGet() 関数のシンプルな代用品として使用できます。 これらは必要な引数が少なく,変数情報の構造体をセットアップして EvmVarRelease() を呼び出す取得操作に従う必要もありません。 [例に戻る]

  6. 簡易関数を用いて文字列または不透明な変数を取得する場合,この関数は値をヒープ領域に割り当て,そのヒープへのポインタを返します。 この値を使い終えたときは,free() を用いてヒープ領域を解放しなければなりません。 [例に戻る]

  7. イベントの処理が終了したら,イベントによって使用されていた記憶域を解放します。 [例に戻る]

14.7.12.4    イベントの発信

イベントを作成したら,多くの場合,発信します。 イベントを発信すると,EVM デーモンによって受信者に配信されます。 イベントを発信するには,デーモンに対して発信接続を作成しておく必要があります。

例 14-5 は,接続を作成し,イベントを発信して,接続を切断する方法の例です。

この例では,次の関数について説明します。

EvmConnCreate() の代わりに,シンプルな EvmConnCreatePoster() マクロを用いて発信接続を行うことができます。 このマクロは必要な引数が EvmConnCreate() よりも少なく,指定できる接続オプションが少ないものの,ローカル・システムにイベントを発信する必要のあるアプリケーションのほとんどで使用できます。

プログラムで一時的な EVM 接続を使用する場合は,EvmConnCreate()EvmConnDestroy() の呼び出しを省略して,EvmEventPost() への接続引数として NULL を渡すこともできます。 接続の確立には処理時間がかかるので,プログラムに適していれば,一時的な接続のみを使用してください。 詳細は,14.7.8 項 および EvmEventPost(3) を参照してください。

EvmEventPostVa() 関数を使用すると,イベントの作成,発信,廃棄を 1 回の呼び出しで行えます。 詳細は EvmEventPost(3) を参照してください。

例 14-5:  イベントの発信

 #include <stdio.h>
 #include <evm/evm.h>
 
 void main()
 {   EvmEvent_t          event;
     EvmStatus_t         status;
     EvmConnection_t     conn;
 
     status = EvmConnCreate(EvmCONNECTION_POST, EvmRESPONSE_WAIT, [1]
                         NULL,NULL,NULL,&conn);
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to create EVM posting connection\n");
         exit(1);
     }
 
     EvmEventCreateVa(&event,                                     [2]
             EvmITEM_NAME,"myco.examples.app.error_detected",
             EvmITEM_NONE);
 
     status = EvmEventPost(conn,event);
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to post event\n");
         exit(1);
     }
 
     EvmEventDestroy(event);                                      [3]
     EvmConnDestroy(conn);
 }

  1. EVM デーモンへの接続を作成するには,EvmConnCreate() を使用します。 接続は,プログラムを終了するか,または EvmConnDestroy() を使用して明示的に破壊するまで切断されません。 EvmConnCreate() の最初の 2 つの引数では,接続がイベントの発信に使用されること,および EVM デーモンがイベントの受諾を肯定応答してから発信関数が戻ることを指定しています。 このため,発信に失敗した場合は,対応する処置を実行できます (その他の応答オプションについては, EvmConnCreate(3) を参照)。 3 番目の引数に対する NULL 値は,ローカル・システム上で動作する EVM デーモンに接続することを示します。 ほとんどの場合,ここには NULL を指定します。 リモート接続についての詳細は, EvmConnCreate(3) を参照してください。 4 番目と 5番目の値は,ほかの応答オプション用なので,待機モード応答の場合は NULL を指定します。 最後の引数を使用して,接続のハンドルを取得します。 この接続で今後呼び出しを実行するときは,この値を指定しなければなりません。 [例に戻る]

  2. イベントを作成して発信します。 [例に戻る]

  3. イベントおよび接続を破壊して,クリーンアップします。 定期的にイベントを発信する場合は接続を破壊しないで,今後のすべてのイベントに対して再使用することをお勧めします。 こうしておくと,発信するたびに接続を再確立するオーバヘッドを削減できます。 [例に戻る]

14.7.12.5    イベントの読み取りと書き込み

次のいずれかの操作を実行するプログラムを作成する場合は,EVM の読み取りおよび書き込み関数を使用する必要があります。

標準の UNIX 書き込み関数を使用して,イベントを直接書き込むことはできません。 これは,イベント・ハンドルには,イベント本体へのポインタ以外は設定されていないうえ,イベントが変更されるたびにイベント本体の位置が変更される可能性があるためです。 逆に,イベントを読み込むときは,本体を読み込むだけでは十分ではありません。 ハンドルを作成し,API 関数を介してイベントを参照できるようにする必要があります。

例 14-6 は,ファイルへのイベントの書き込み,ファイルからプログラムへのイベントの読み取り,およびイベントの有効性の確認を行う方法の例です。

この例では,次の関数について説明します。

例 14-6:  イベントの読み取りと書き込み

#include <stdio.h>
 #include <fcntl.h>
 #include <evm/evm.h>
 
 void
 main()
 {   EvmEvent_t          event_in,event_out;
     EvmStatus_t         status;
     EvmItemValue_t      itemval;
     int                 fd;
 
     EvmEventCreateVa(&event_out,                               [1]
             EvmITEM_NAME,"myco.examples.app.saved_event",
             EvmITEM_NONE);
 
     fd = open("eventlog",O_RDWR | O_CREAT | O_TRUNC,           [2]
                         S_IRUSR | S_IWUSR);
     if (fd < 0)
     {   fprintf(stderr,"Failed to open output log file\n");
         exit(1);
     }
 
     status = EvmEventWrite(fd,event_out);
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to write event to log file\n");
         exit(1);
     }
 
     lseek(fd,0,SEEK_SET);                                      [3]
     status = EvmEventRead(fd,&event_in);
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to read event from log file\n");
         exit(1);
     } 
 
     status = EvmEventValidate(event_in);                       [4]
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Event read from logfile is invalid");
         exit(1);
     }
 
     status = EvmItemGet(event_in,EvmITEM_NAME,&*itemval);      [5]
     if(status == EvmERROR_NONE)
     {   fprintf(stdout,"Event name: %s\n",itemval.NAME);
         EvmItemRelease(EvmITEM_NAME,itemval);
     }
 
     EvmEventDestroy(event_in);                                 [6]
     EvmEventDestroy(event_out);
 }

  1. 名前が設定されたイベントを作成します。 [例に戻る]

  2. 出力ログ・ファイルを作成し,EvmEventWrite() を使用してイベントを書き込みます。 イベントは,別のプロセスへのパイプを含め,任意のファイル記述子に書き込むことができます。 ただし,イベントはバイナリ・データ・パッケージであるため,端末やプリンタには直接書き込まないでください。 [例に戻る]

  3. EvmEventRead() を使用してイベントを読み戻します。 このとき,別のイベントが作成され,イベント・ハンドル自体ではなく,イベント・ハンドルへのポインタを設定する必要があることに注意してください。 [例に戻る]

  4. 着信イベントは,このプロセスによって制御されていないため,一貫性を確認することが重要です。 ファイルからイベントを読み込むたびに,または EVM デーモン以外のプロセスからイベントを受信するたびに,EvmEventValidate() 関数を使用して確認します。 [例に戻る]

  5. 読み込んだイベントが,既に書き込んだイベントと同じものであることを確認するには,名前を取得して表示します。 [例に戻る]

  6. イベントによって使用された領域を解放します。 [例に戻る]

14.7.12.6    イベント通知の受信

イベント通知を受信するプログラムでは,次の操作を実行する必要があります。

例 14-7 は,イベントの受信を待機し,イベントが着信するたびに stdout に表示される例です。

この例では,次の関数について説明します。

注意

EvmConnCreate() の代わりに,シンプルな EvmConnCreateSubscriber() マクロを用いて受信接続を行うことができます。 このマクロは必要な引数が EvmConnCreate() よりも少なく,指定できる接続オプションは少ないものの,受信アプリケーションのほとんどで使用できます。 詳細は EvmConnCreate(3) を参照してください。

例 14-7:  イベント通知の受信

 #include <stdio.h>
 #include <evm/evm.h>
 
 void
 EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,
            EvmCallbackData_t *cbdata);
 
 /*====================================================
  * Function: main()
  *====================================================*/
 main()
 {   EvmConnection_t     conn;
     EvmStatus_t         status;
 
     status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK,
                         NULL,EventCB,NULL,&conn);              [1]
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to create EVM listening connection\n");
         exit(1);
     }
 
     status = EvmConnSubscribe(conn,NULL,"[name *.evm.msg.user]");  [2]
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to subscribe for event notification\n");
         exit(1);
     }
 
     for (;;)                                                   [3]
     {   status = EvmConnWait(conn,NULL);
         if (status == EvmERROR_NONE)
         {   fprintf(stderr,"Connection error\n");
             exit(1);
         }
 
         if (EvmConnDispatch(conn) != EvmERROR_NONE)
         {   fprintf(stderr,"Connection dispatch error\n");
             exit(1);
         }
     }
 }
 
 /*====================================================
  * Function: EventCB()
  *====================================================*/
 void
 EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,          [4]
                 EvmCallbackData_t *cbdata)
 {   char buff[256];
 
     switch (cbdata->reason) {                                  [5]
     case    EvmREASON_EVENT_DELIVERED:
             EvmEventFormat(buff, sizeof(buff), cbdata->event);
             fprintf(stdout,"Event: %s\n",buff);
 
             EvmEventDestroy(cbdata->event);                    [6]
             break;
     default:                                                   [7]
             break;
     }
 }

  1. EvmConnCreate() を使用して EVM デーモンへの接続を確立します。 ここではリッスン接続を指定します。 このコード例では,次の引数が指定されています。

    [例に戻る]

  2. 次のステップでは,EvmConnSubscribe() 関数を使用して EVM デーモンに受信したいイベントを通知しています。 ここでは,evmpost で発信できるユーザ・メッセージを待機しています。 これらのイベントには,sys.unix.evm.msg.user という名前が付いています。 EvmConnSubscribe() を呼び出すと,EvmConnDispatch() を呼び出したときに,コールバック関数が EvmREASON_SUBSCRIBE_COMPLETE という理由コードで呼び出されます。 [例に戻る]

  3. この例では,イベントの着信を待機する以外に処理が記述されていません。 このため,EvmConnWait() を使用して接続上のアクティビティを監視しながら,永久にループします。 EvmConnWait() の 2 番目の引数として渡された NULL は,アクティビティが存在しない場合でもタイムアウトにならないことを示します。 EvmConnWait() は,アクティビティが発生するたびに戻りますが,イベントが着信しているとは限りません。 たとえば,EVM デーモンからほかのメッセージが送信された場合,またはプロセスがシグナルによって割り込まれた場合にも戻ります。 この関数が正常に戻った場合は,EvmConnDispatch() を呼び出してアクティビティを処理する必要があります。 通常は,コールバック関数 EventCB() が呼び出されますが,いつもとは限りません。 [例に戻る]

  4. EvmConnDispatch() では,アプリケーション・コードによる処理を要求するメッセージがデーモンから読み込まれたときに,コールバック関数が呼び出されます。 このメッセージには,着信イベントも含まれます。 ただし,アプリケーションでは,ほかの種類のメッセージも考慮する必要があります。 このコード例では,次の引数が指定されています。

    この例では,理由コード EvmREASON_EVENT_DELIVERED に記述されているように,着信イベント・メッセージを待機しています。 イベントが着信すると,フォーマット後に表示します。 イベントによって使用される領域を解放するため,処理が終了したらイベントを破壊します。 [例に戻る]

  5. 実行されるアクションは,コールバックの理由コードに依存しています。 この場合,理由はイベントが配信されたことなので,EvmEventFormat() 関数を使用してイベントを表示用にフォーマットし,stdout に出力します。

    EvmEventFormat() では,イベントのフォーマット・データ項目 (設定されている場合) を基にしてテキスト行が生成され,その結果が指定したバッファに格納されます。 フォーマットによって,イベント本体が変更されることはありません。 [例に戻る]

  6. 配信されたイベントによってヒープ領域が使用されています。 イベントの処理が終了したら,アプリケーションでこの領域を解放する必要があります。 [例に戻る]

  7. 既に受信要求を発行しているため,EvmREASON_SUBSCRIBE_COMPLETE というコールバック理由によってその関数も起動されます。 この例の場合は,この理由コードおよびその他の理由コードは無視して構いません。 [例に戻る]

14.7.12.7    複数の入出力ソースの処理

イベントのリッスン以外に処理を行うプログラムを作成する場合は,イベントの着信を待機するときに EvmEventWait() 以外の関数を使用することもできます。 たとえば,select システム・コールを使用して,EVM 接続など,複数のファイル記述子に対する入出力アクティビティを監視する方法があります。

例 14-8 は,EVM 接続を stdin からの入力とともに処理する方法の例です。

この例では,次の関数について説明します。

例 14-8:  複数の入出力ソースの処理

 #include <stdio.h>
 #include <sys/time.h>
 #include <evm/evm.h>
 
 void HandleInput();
 void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,
                 EvmCallbackData_t *cbdata);
 
 /*===============================================
  * Function: main()
  *===============================================*/
  main()
  {   EvmConnection_t     conn;
      EvmStatus_t         status;
      fd_set              read_fds;
      int                 conn_fd;
      EvmBoolean_t        io_waiting;
 
      status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK,
                          NULL,EventCB,NULL,&conn);
      if (status != EvmERROR_NONE)
      {   fprintf(stderr,"Failed to create EVM listening connection\n");
          exit(1);
      }
 
      status = EvmConnSubscribe(conn,NULL,"[name sys.unix.evm.msg.user]");
      if (status != EvmERROR_NONE)
      {   fprintf(stderr,"Failed to subscribe for event notification\n");
          exit(1);
      }
 
      EvmConnFdGet(conn,&conn_fd);                              [1]
 
      for (;;)                                                  [2]
      {   FD_ZERO(&read_fds);
          FD_SET(fileno(stdin),&read_fds);
          FD_SET(conn_fd,&read_fds);
 
          select(FD_SETSIZE,&read_fds,NULL,NULL,NULL);
 
          if (FD_ISSET(fileno(stdin),&read_fds))
              HandleInput();
 
          status = EvmConnCheck(conn,&io_waiting);                  [3]
          if (status != EvmERROR_NONE)
          {   fprintf(stderr,"Connection error\n");
              exit(1);
          }
          if (io_waiting)
          {   status = EvmConnDispatch(conn);
              if (status != EvmERROR_NONE)
              {   fprintf(stderr,"Connection dispatch error\n");
                  exit(1); 
              }
          }
      }
 }
 
 /*===============================================
  * Function: HandleInput()
  *===============================================*/
 void
 HandleInput()                                                  [4]
 {   char buff[256];
     if (feof(stdin))
         exit(0);
 
     if (fgets(buff,sizeof(buff),stdin) == NULL)
         exit(0);
 
     if (buff[0] == '\n')
         exit(0);
 
     fprintf(stdout,buff);
 }
 
 /*===============================================
  * Function: EventCB()
  *===============================================*/
  void
  EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,         [5]
                  EvmCallbackData_t *cbdata)
  {   char buff[256];
 
      switch (cbdata->reason) {
      case    EvmREASON_EVENT_DELIVERED:
 
              EvmEventFormat(buff, sizeof(buff), cbdata->event);
              fprintf(stdout,"Event: %s\n",buff);
 
              EvmEventDestroy(cbdata->event);
              break;
      default:
              break;
      }
  }

  1. EvmConnFdGet() を使用して,接続に割り当てられたファイル記述子を検索します。 [例に戻る]

  2. この例では,入出力アクティビティを監視するときに select を使用しています。 select を使用すると,複数のファイル記述子を監視できます。 select 呼び出しが戻ったときに,アクティビティが発生したファイル記述子を確認し,適切に処理します。 [例に戻る]

  3. EvmConnCheck() を使用して,EVM 接続上で未処理のアクティビティが存在するかどうかを確認します。 接続のファイル記述子がわかっているので,FD_ISSET() を使用することもできます。 [例に戻る]

  4. HandleInput() 関数は,stdin から入力行を読み取り,stdout にエコーします。 エラーが発生するか空の行を読み込んだ場合は,プログラムが終了します。 [例に戻る]

  5. イベントのコールバック関数は,前の例で使用したものと同じです。 [例に戻る]

14.7.12.8    フィルタ・エバリュエータの使用

イベント受信者によっては,さまざまな基準を使用してイベントを監視し,着信イベントの属性に応じて異なる対応が必要になることがあります。 たとえば,EVM ロガーでは,構成ファイルからフィルタ文字列を読み込んで,その文字列に記述されているすべてのイベントを受信します。 このため,イベントを適切なログに書き込むには,イベントが着信したときに,イベントと一致するフィルタ文字列を識別する必要があります。

たとえば,EVM デーモンに対して複数の接続を作成し,接続ごとに別個のフィルタ文字列を使用して受信する方法があります。 しかし,この方法の場合は,接続のオーバヘッドが大きくなり,同じイベントが 2 つ以上の接続を介して送信される可能性が大きくなります。

もっと効率的な方法として,論理演算子 OR を使用して,フィルタ文字列をすべて取り込み,それらを結合して 1 つの論理文字列にする方法があります。 結合された文字列は,単一の接続上で一致するイベントをすべて受信するときに使用することができます。 結合された文字列は後で破棄できますが,元の文字列は保持しておく必要があります。 接続の再確立またはフィルタの変更が必要になった場合は,元の文字列を使用して再受信することができます。

ただし,イベントが着信したときには,イベントの処理方法を決定するために,イベントと一致する元のフィルタ文字列を識別する必要があります。 このとき,EVM フィルタ・エバリュエータを使用することができます。 フィルタ・エバリュエータは,ユーザが作成できるオブジェクトで,フィルタ文字列とともにロードされ,フィルタと一致するイベントを識別する (一致が存在する場合) ために複数のイベントが渡されます。 元の各フィルタ文字列に対して別個のエバリュエータを割り当てておくと,着信イベントごとにそれぞれのエバリュエータを適用して,イベントと一致するエバリュエータを識別することができます。

例 14-9 はこの方法の例です。 簡単な 3 つのフィルタ文字列を使用して,一致が存在する場合は,着信イベントと一致するフィルタに応じて異なるメッセージを出力しています。

この例では,次の関数について説明します。

例 14-9:  フィルタ・エバリュエータの使用

 #include <stdio.h>
 #include <sys/time.h>
 #include <evm/evm.h>
 
 void
 EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,
            EvmCallbackData_t *cbdata);
 
 #define FILTER_1 "[name *.class_1]"                            [1]
 #define FILTER_2 "[name *.class_2]"
 #define FILTER_3 "([name *.class_2] | [name *.class_3]) & [priority >= 300]"
 
 /*===============================================
  * Function: main()
  *===============================================*/
  main()
  {   EvmConnection_t     conn;
      Evmstatus_t         status;
      int                 conn_fd;
      char                *filter_string;
 
      status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK,
                          NULL,EventCB,NULL,&conn);
      if (status != EvmERROR_NONE)
      {   fprintf(stderr,"Failed to create EVM listening connection\n");
          exit(1);
      }
 
      filter_string = (char *)malloc(strlen(FILTER_1) + strlen(FILTER_2) +
                       strlen(FILTER_3) + 30);                  [2]
      sprintf(filter_string,"(%s) | (%s) | (%s)",FILTER_1,FILTER_2,FILTER_3);
 
      status = EvmConnSubscribe(conn,NULL,filter_string);       [3]
      if (status != EvmERROR_NONE)
      {   fprintf(stderr,"Failed to subscribe for event notification\n");
          exit(1);
      }
      free(filter_string);
 
      for (;;)                                                  [4]
      {   status = EvmConnWait(conn,NULL);
          if (status != EvmERROR_NONE)
          {   fprintf(stderr,"Connection error\n");
              exit(1);
          }
          if (EvmConnDispatch(conn) != EvmERROR_NONE)
          {   fprintf(stderr,"Connection dispatch error\n");
              exit(1);
          }
       }
    }
 
 /*===============================================
  * Function: EventCB()
  *===============================================*/
 void
 EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,
                 EvmCallbackData_t *cbdata)
 {   EvmBoolean_t        match;
 
     static EvmFilter_t  f1,f2,f3;                             [5]
     static EvmBoolean_t filters_initialized = EvmFALSE;
 
     if (! filters_initialized)
     {   if (EvmFilterCreate(&f1) != EvmERROR_NONE)
         {   fprintf(stderr,"Failed to create filter evaluator\n");
             exit(1);
         }
         if (EvmFilterSet(f1,FILTER_1) != EvmERROR_NONE)
         {   fprintf(stderr,"Failed to set filter evaluator\n");
             exit(1);
         }
 
         EvmFilterCreate(&f2);
         EvmFilterSet(f2,FILTER_2);
         EvmFilterCreate(&f3);
         EvmFilterSet(f3,FILTER_3);
 
         filters_initialized = EvmTRUE;
     }
 
     switch (cbdata->reason) {                                  [6]
     case    EvmREASON_EVENT_DELIVERED:
 
             EvmFilterTest(f1,cbdata->event,&match);
             if (match)
                 fprintf(stdout,"Filter 1 event received\n");
 
             EvmFilterTest(f2,cbdata->event,&match);
             if (match)
                 fprintf(stdout,"Filter 2 event received\n");
 
             EvmFilterTest(f3,cbdata->event,&match);
             if (match)
                 fprintf(stdout,"Filter 3 event received\n");
 
             EvmEventDestroy(cbdata->event);
             break;
 
     default:
             break;
     }
 }

  1. 3 つの簡単なフィルタ文字列を定義します。 最初の 2 つは,名前だけを基にしてフィルタを実行します。 3 番目の文字列は,300 以上の優先度が設定されたイベントのうち,2 つの名前のいずれかと一致するイベントを選択します。 [例に戻る]

  2. 3 つのフィルタ文字列を,1 つの論理式に結合します。 各部分文字列はカッコで囲まれ,論理演算子 OR で区切られています。 この結果,イベントが 1 つ以上の部分文字列と一致したときに,EVM デーモンから通知されます。 [例に戻る]

  3. 結合された文字列を使用してイベントを受信します。 その後,文字列の使用が終了したため,その領域を解放します。 何らかの理由で再受信が必要になった場合は,いつでも部分文字列を再度結合できます。 [例に戻る]

  4. エバリュエータを設定したら,受信するすべてのイベントに対して使用できるように保存します (つまり,静的要素にします)。 もう 1 つの方法として,イベントを受信するたびに新しいエバリュエータを作成し,関数を終了するときに破壊することもできます。 ただし,保存しておくと,セットアップおよび破棄の繰り返しによるオーバヘッドを回避できます。 [例に戻る]

  5. EvmFilterCreate() を使用して 3 つのフィルタ・エバリュエータを作成し,3 つのフィルタ文字列をロードします。 わかりやすくするために,この例では最初のフィルタの戻り状態だけを確認していますが,製品コードでは各フィルタの戻り状態を確認してください。 このコードは,イベントを最初に受信した場合にだけ実行されます。 [例に戻る]

  6. 前述のセクションで設定した 3 つのフィルタ・エバリュエータに対して,各着信イベントを検査し,イベントと一致したエバリュエータごとに異なるメッセージを出力しています。 イベントが複数のエバリュエータと一致することもあり,この場合は,複数のメッセージを出力します。 [例に戻る]

14.7.12.9    イベント名の照合

EVM の命名ポリシでは,基本イベント名の後に任意の数のコンポーネントを追加して拡張することができます。 つまり,コンポーネントが追加されている可能性があるため,イベント名が元の名前と全く同じである保証はありません。

このため,イベント名を既知のイベント名と比較する場合は,通常の文字列比較関数を使用しないでください。 コンポーネントが追加されている場合は,適切に名前を照合することができません。 代わりに,EVM の名前照合関数を使用します。 名前照合関数を使用すると,指定したパターンを基にしてイベント名が照合され,照合するイベント名にコンポーネントが追加されていても無視されます。 また,名前パターンにワイルドカード文字を含めることもできます。

例 14-10 では,次の関数について説明します。

次の関数は,EvmEventNameMatch に関連しています。

例 14-10:  イベント名の照合

 #include <stdio.h>
 #include <evm/evm.h>
 
 /*===============================================
  * Function: main()
  *===============================================*/
 main() 
 {   EvmStatus_t     status;
     EvmEvent_t      event;
     EvmBoolean_t    match;
     char            buff[80];
 
     while (EvmERROR_NONE == EvmEventRead(fileno(stdin),&event))  [1]
     {   EvmEventNameMatch("*.msg",event,&match);
         if (match)
         {   EvmEventFormat(buff,sizeof(buff),event);
             fprintf(stdout,"%s\n",buff);
         }
     }
 
 }

  1. stdin からイベントを読み取り,ワイルドカード文字列 *.msg と一致するイベントだけを表示します。 この種類のイベントの名前は通常 sys.unix.evm.msg.user または sys.unix.evm.msg.admin ですが,照合は機能します。 [例に戻る]

14.7.12.10    失われたイベントの処理

開発中のアプリケーションが,短期間に大量に発信されるイベントのセットを受信する場合,プログラムが EVM デーモンにより,1 つまたは複数のイベントを失ったと通知することがあります。 これは,プログラムの処理量がひじょうに多く,新しくイベントが到着してもすぐに対処できず,未処理のイベントで通信バッファが満杯になってしまうような場合に起こりやすくなります。 失われたイベントについての詳細は,14.7.10 項 を参照してください。

例 14-11 では,イベントをすべて受信し,各イベントを到着時に stdout に表示します。 これは,コールバック関数に EvmREASON_EVENTS_MISSED という理由を入れることで失われたイベントの通知を監視し,失ったイベントの数を含むメッセージを表示します。

例 14-11:  失われたイベントの処理

 #include <stdio.h>
 #include <evm/evm.h>
 
 void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,
                       EvmCallbackData_t *cbdata);
 
 /*===============================================
  * Function: main()
  *===============================================*/
 main() 
 {   EvmConnection_t     conn;
     EvmStatus_t         status;
     int                 conn_fd;
 
     status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK,
                         NULL,EventCB,NULL,&conn);
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to create EVM listening connection\n");
         exit(1);
     }
 
     status = EvmConnSubscribe(conn,NULL,"[name *]");
     if (status != EvmERROR_NONE)
     {   fprintf(stderr,"Failed to subscribe for event notification\n");  [1]
         exit(1);
     }
 
     for (;;)
     {   status = EvmConnWait(conn,NULL);
         if (status != EvmERROR_NONE)
         {   fprintf(stderr,"Connection error\n"); 
             exit(1);
         }
         if (EvmConnDispatch(conn) != EvmERROR_NONE)
         {   fprintf(stderr,"Connection dispatch error\n");
             exit(1);
         }
     }
 }
 
 /*===============================================
  * Function: EventCB()
  *===============================================*/
  void
  EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg,
                  EvmCallbackData_t *cbdata)
  {   char                buff[256];
 
      switch (cbdata->reason) {
      case    EvmREASON_EVENT_DELIVERED:
              EvmEventFormat(buff, sizeof(buff), cbdata->event);
              fprintf(stdout,"Event: %s\n",buff);
 
              EvmEventDestroy(cbdata->event);
 
              sleep(1);  [2]
              break;
 
      case    EvmREASON_EVENTS_MISSED:  [3]
              fprintf(stdout,"*** Missed %d incoming events\n",
                  cbdata->extension.eventMissedData.missedCount);
              break;
 
      default:
              break;
      }
 }

  1. この例では,すべてのイベントの通知を受信しています。 [例に戻る]

  2. イベントが失われた状態を示すために,各イベントを受信した後に 1 秒間スリープしています。 これは,負荷の超過が発生した状況をシミュレートしており,イベントの負荷が大きい場合は,入力接続バッファがいっぱいになることが確認されます。 [例に戻る]

  3. イベントが失われたため,EVM デーモンがイベントを送信できない場合は,コールバック関数が理由コード EvmREASON_EVENTS_MISSED で呼び出されます。 コールバック・データ・メンバ extension.eventMissedData.missedCount には,失われたイベント数が格納されます。 [例に戻る]

14.8    EVM へのイベント・チャネルの追加

イベント・チャネルという言葉は,イベント情報を公開または取得するために使用する機能を表します。 また,次のいずれかを表す場合もあります。

イベント・チャネルには,アクティブ・チャネルパッシブ・チャネルがあります。 アクティブ・チャネルでは,イベントが発生すると直ちに固有のイベント情報が EVM に発信されます。 パッシブ・チャネルでは,イベント情報はチャネル内に蓄積され,検索するには EVM からのアクションが必要です。

イベント・チャネルは,正式なイベント通知メカニズムである必要はありません。 情報の格納,あるいは状態または状態変更の表示を行うことができるものであれば,何でも構いません。 次に例を示します。

既存のイベント・チャネルを EVM を介してアクセスできるようにする処理は,カプセル化と呼ばれます。

EVM イベント・チャネルは,チャネル構成ファイルを使用して構成します。 このファイルは,EVM の起動時に EVM チャネル・マネージャによって読み込まれ,また,チャネル情報が必要な場合は,コマンド行ユーティリティでも使用されます。 このファイルを変更する場合は,次のコマンドを入力して,チャネル・マネージャに変更を通知する必要があります。

evmreload -c

チャネル構成ファイルの構文については, evmchannel.conf(4) を参照してください。 イベント・チャネルをカプセル化するには,さまざまなチャネル関数を処理する実行可能プログラムが必要です。 次のようなチャネル関数があります。

これらの関数はすべてオプションです。 チャネル構成ファイルのチャネル定義に適切なエントリを追加することにより,チャネルに対して定義されます。 チャネル関数には任意の種類の実行可能ファイルを使用できますが,この後の各項で説明するとおりに動作する必要があります。

一時ファイルを使用するチャネル関数では,終了前に必ずクリーンアップを実行する必要があります。 また,割り込みが発生した場合でも,クリーンアップを実行できなければなりません。

また,次の操作が必要です。

14.8.1    取得関数

チャネルの取得関数は,EVM の get_server によって実行されます。 get_server は,EVM デーモンによって実行され,evmget を介して行われたイベント取得要求を処理します。 この関数は常に root として実行されるので,適切なセキュリティ対策が必要です。

この関数では,次の呼び出し構文を使用します。

function-name [-f filter-string]

必要に応じて,その他の引数も関数に渡すことができます。 チャネル構成ファイルの該当する関数の行に,引数を指定してください。

取得関数を実行すると,イベントがチャネルのログ・ファイルから取得されて,EVM のイベント・フォーマットに変換され,変換後の EVM イベントが stdout ストリームに書き込まれます。 filter-string を指定すると,フィルタと一致するイベントだけが stdout に書き込まれます。 エラー・メッセージは,stderr に書き込まれ,evmget に渡されてから,その stderr ストリームに出力されます。 このため,エラー・メッセージがこの関数で発生したことを明示的に示す必要があります。 stdout には,EVM イベント以外は書き込まないでください。

取得スクリプトの形式は,元のイベントの格納形式に大きく依存します。 標準では,次のステップが実行されます。

  1. grepawk および sed などの UNIX の標準ツールか,または perl などのプログラミング言語を使用して,イベント行の選択およびブランク行とコメントの削除を行い,次のステップに必要な形式にフォーマットします。 イベントが単一行テキストで構成され,各行のフォーマットが一定しており,タイムスタンプ,ホスト名,およびメッセージなどの項目がすべての行で同じ位置に配置されている場合,この作業は簡単に行うことができます。

  2. 各行を EVM イベントに変換します。 これを行うには,UNIX ツールを使用して,各行を evmpost への入力に適した形式にフォーマットし,発信するのではなく,-r オプションを使用して,EVM イベントを stdout に出力します。 迅速に変換する方法として,EVM チャネル・ユーティリティの /usr/share/evm/channels/bin/text2evm を使用することもできます。 このツールを使用する場合は,現在,次の形式で入力する必要があります。

    evm-event-name date time host user message

    各項目の意味は以下のとおりです。

  3. filter-string を指定した場合は,-f および -r オプションを指定した evmshow を介してその出力を渡すと,フィルタで要求されている文字列だけに出力が制限されます。

  4. 最後に,取得したイベントに対してイベント・テンプレートに含まれるデータ項目を設定したい場合は,EVM チャネル・ユーティリティの /usr/share/evm/channels/bin/merge_template を介して出力をパイプすることもできます。 このプログラムは stdin の EVM イベントを読み取り,各イベントに対応するテンプレートをEVM デーモンから取得します。 次にテンプレート情報をイベントにマージし,処理結果の展開済みイベントを stdout に書き込みます。

チャネルのログ・ファイルを EVM フォーマットに変換するのが困難な場合,たとえば,各エントリが構造化されていない複数のテキスト行で構成されているため,簡単に解析できない場合は,取得関数を使用しないで,イベントが発信されたときに EVM ロガーでログが記録されるようにすることをお勧めします。 この場合,イベントが 2 つの場所に格納されるため,記憶域の使用量が多くなりますが,取得時間およびプログラミング工数が大幅に削減されます。

チャネルに対して独自の取得関数を使用する場合は,EVM ロガーの構成ファイルのフィルタ文字列を変更して,イベントが EVM ログ内で重複しないようにする必要があります。 EVM ロガーの構成ファイルを変更する方法については,『システム管理ガイド』を参照してください。

14.8.2    詳細関数

詳細関数は,evmshow-d オプションを指定して呼び出したときに実行されます。 現在は evmshow を実行するユーザの権限で実行されますが,今後のリリースでは変更される可能性があるため,適切なセキュリティ対策が重要になります。

この関数では,次の呼び出し構文を使用します。

function-name [arguments]

チャネル構成ファイルで,詳細関数の行に引数が指定されると,これらは実行時に直接関数に渡されます。 詳細関数は,stdin を介して EVM のイベント・ストリームを受信し,各イベントの内容を説明するテキスト・ストリームを stdout に表示する必要があります。 出力を作成するときは,さまざまな形式の evmshow (特に -x-D) が役に立ちますが,-d オプションは使用しないように注意してください。 再帰的なループが発生する可能性があります。

stderr に書き込まれたメッセージは,リダイレクトされない限り,evmshowstderr ストリームに出力されます。 必要な場合は,この関数によって書き込まれたことを明示的に記述してください。

新しいチャネルに対する詳細スクリプトを開発するためのモデルとして,evmlog チャネル関数,/usr/share/evm/channels/evmlog/evmlog_details が使用できます。 詳細関数を指定していないときには,evmshow とイベント・ビューアは,詳細表示が要求されたときに,チャネルに属するイベントのダンプをフォーマットして表示します。

14.8.3    説明関数

説明関数は,evmshow-x オプションを指定して呼び出したときに実行されます。 現在は evmshow を実行するユーザの権限で実行されますが,今後のリリースでは変更される可能性があるため,適切なセキュリティ対策が重要になります。 この関数では,次の呼び出し構文を使用します。

function-name event-name [reference]

説明関数は,説明が必要なイベントの名前およびオプションの参照値を指定して呼び出します。 参照値を指定した場合は,イベントの参照データ項目の内容が参照されます。 参照値が利用できない場合は,evmshow はこの引数としてハイフン (-) を渡しますが,この引数を省略することもできます。

説明関数は,引数を使用して,指定されたイベント名に対応するイベントの説明をフォーマットし,テキスト行として stdout に書き込みます。 説明が見つからない場合は,代わりに適切なメッセージを stdout に書き込みます。 stderr に書き込まれたメッセージは,リダイレクトされない限り,evmshowstderr ストリームに出力されます。 このため,必要な場合は,この関数によって書き込まれたことを明示的に記述してください。

説明関数では,次の基準を満たす場合にだけ,evmlog 説明関数の /usr/share/evm/channels/evmlog/evmlog_explain を呼び出すことができます。

メッセージ・カタログは,通常の I18N 規則に従って配置されていなければなりません。 検索時間を最小限に抑えるには,説明をいくつかのセットにグループ化し,イベントの参照データ項目にセット番号を指定します。 カタログ・ファイルを生成する手順については, mkcatdefs(1) および gencat(1) を参照してください。

14.8.4    監視関数

監視関数は,EVM チャネル・マネージャによって実行されます。 チャネル・マネージャの起動時,およびチャネル・マネージャが evmreload によって再構成されたときは,引数 -init が必要ですが,その後は引数 -init を指定しないで定期的に実行されます。 実行期間は,チャネル値 mon_period で制御されます。 この関数は,常に root として実行されるので,適切なセキュリティ対策が必要です。

この関数では,次の呼び出し構文を使用します。

function-name [init]

引数 init が指定されているかどうかによって,この関数が管理している作業ファイルの初期化が必要かどうかが決定されます。 必要な場合には,チャネル構成ファイルの該当するコマンド行に引数を設定することにより,追加の引数を渡すことができます。 ただし,引数 init は常に最後の引数として渡されることに注意してください。

監視関数によって実行されるアクションについて制限はありませんが,通常,このジョブでは,状態をチェックして,状態の変化を検出した場合はイベントを発信します。

この関数は,stdout を指定せず,チャネル・マネージャのログ・ファイルに stderr を指定して呼び出します。 関数が stderr の再割り当てを行わない場合,ここに書き込まれるメッセージはすべて,チャネル・マネージャのログ・エントリと同じフォーマットになるので,そのチャネルから送られたこと明確に分かるようにする必要があります。 または,エラー・メッセージを EVM イベントとして送信し,関数が起動されるたびに同じ状態を不必要に通知することのないようにします。

次の監視スクリプトの例では,ログ・ファイルの行数をカウントし,そのカウントを状態ファイルに保存することにより,初期化を行っています。 後続の呼び出しでは,ファイルの行数を古い行数と比較し,新しい行を UNIX の tail コマンドで抽出してから,evmpost を使用して EVM イベントとして発信します。

#! /bin/sh
INIT=$1
STATE=/tmp/mylog.state
LOG=/tmp/mylog
 
EVENT_NAME=myco.admin.mylog.line_added
 
# No log?  Create one!
if [ ! -f $LOG ]
then
    touch $LOG
fi
 
# If we're initializing then save the current logfile
# state and exit:
if [ "$INIT" != "" ]
then
    # Count the lines in the demolog, and save the count
    # in the state file:
    wc -l $LOG | awk '{print $1}' > $STATE
    exit
fi
 
# Find out how many lines there were in the file last time
# we checked:
OLDCOUNT=`cat $STATE`
 
# How many now?
NEWCOUNT=`wc -l $LOG | awk '{print $1}'`
if [ $NEWCOUNT > $OLDCOUNT ]
then
    # Save the new line count to the state file:
    echo $NEWCOUNT > $STATE
 
    # What's the difference between the old and new counts?
    diff=`expr $NEWCOUNT - $OLDCOUNT | awk '{print $1}'`
 
    # Post an event for each new line:
    tail -$diff $LOG | while read LINE
    do
        echo 'event { name '${EVENT_NAME} \
            ' var {name msg type STRING value "'$LINE'"} }' | evmpost
    done
fi

14.8.5    クリーンアップ関数

クリーンアップ関数は,EVM チャネル・マネージャによって毎日実行されます。 チャネル構成ファイルに指定されている時間に,チャネルのログ・ファイルの保管や削除などのハウスキーピング処理が行われます。 この関数は常に root として実行されるので,適切なセキュリティ対策が必要です。

クリーンアップ関数は,コマンド行として指定し,そのまま実行されます。 このため,必要に応じて,コマンド行から引数を渡すことができます。 この関数では,任意の必要なアクションを実行できます。 この関数は,stdout を指定せず,チャネル・マネージャのログ・ファイルに stderr を指定して実行します。 このため,状態メッセージが必要な場合は,stderr には書き込まずに,通常は evmpost を使用して EVM イベントの形式で発信します。 関数が stderr の再割り当てを行わない場合,ここに書き込まれるメッセージはすべて,チャネル・マネージャのログ・エントリと同じフォーマットになるので,そのチャネルから送られたことが明確に分かるようにする必要があります。 stdout には何も書き込まないでください。

この関数は,実行する時間にかかわらず同じ結果が得られるように作成します。 たとえば,find コマンドの -mtime オプションを使用して,保管するログ・ファイルを特定することがあります。

14.8.6    チャネルのセキュリティ

ほとんどの場合,チャネル関数は EVM デーモンの子プロセスによって実行されるため,完全な root 権限で実行されることになります。 このため,システムの一貫性を保持するために,次の点に従ってください。