このセクションでは、MySQL ソース配布の plugin/audit_null
ディレクトリにあるプラグインの例を使用して、監査サーバープラグインを作成する方法について説明します。このディレクトリにある audit_null.c
ソースファイルは、NULL_AUDIT
という名前の簡単な監査プラグインの例を実装します。
サーバー内では、プラガブルな監査インタフェースは MySQL ソース配布の sql
ディレクトリの sql_audit.h
ファイルおよび sql_audit.cc
ファイル内に実装されています。また、サーバー内のいくつかの場所は、監査可能なイベントが発生したときに監査インタフェースを呼び出すように変更されているため、登録された監査プラグインは必要に応じてイベントに関する通知を受けることができます。そのような呼び出しが行われる場所を確認するには、mysql_audit_
という形式の名前を持つ関数の呼び出しを探してください。監査通知は、次のようなサーバーの動作に対して発生します。
xxx
()
一般クエリーログへのメッセージの書き込み (ログが有効にされている場合)
エラーログへのメッセージの書き込み
クライアントへのクエリー結果の送信
クライアントの接続イベントおよび接続解除イベント
監査プラグインを作成するには、プラグインのソースファイルに次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。
#include <mysql/plugin_audit.h>
plugin_audit.h
は plugin.h
をインクルードするため、後者のファイルを明示的にインクルードする必要はありません。plugin.h
は MYSQL_AUDIT_PLUGIN
サーバープラグインタイプおよびプラグインを宣言するために必要なデータ構造体を定義します。plugin_audit.h
は、監査プラグインに固有のデータ構造体を定義します。
監査プラグインには、ほかの MySQL サーバープラグインと同様に、一般プラグインディスクリプタがあります (セクション24.2.4.2.1「サーバープラグインライブラリおよびプラグインディスクリプタ」を参照してください)。audit_null.c
では、一般ディスクリプタは次のようになります。
mysql_declare_plugin(audit_null)
{
MYSQL_AUDIT_PLUGIN, /* type */
&audit_null_descriptor, /* descriptor */
"NULL_AUDIT", /* name */
"Oracle Corp", /* author */
"Simple NULL Audit", /* description */
PLUGIN_LICENSE_GPL,
audit_null_plugin_init, /* init function (when loaded) */
audit_null_plugin_deinit, /* deinit function (when unloaded) */
0x0003, /* version */
simple_status, /* status variables */
NULL, /* system variables */
NULL,
0,
}
mysql_declare_plugin_end;
name
メンバー (NULL_AUDIT
) は、INSTALL PLUGIN
、UNINSTALL PLUGIN
などのステートメントでプラグインを参照するために使用する名前を指定します。これは INFORMATION_SCHEMA.PLUGINS
または SHOW PLUGINS
によって表示される名前でもあります。
一般ディスクリプタは、SHOW STATUS
ステートメントに対して各種のステータス変数を公開する構造体である simple_status
も参照しています。
static struct st_mysql_show_var simple_status[]=
{
{ "Audit_null_called",
(char *) &number_of_calls,
SHOW_INT },
{ "Audit_null_general_log",
(char *) &number_of_calls_general_log,
SHOW_INT },
{ "Audit_null_general_error",
(char *) &number_of_calls_general_error,
SHOW_INT },
{ "Audit_null_general_result",
(char *) &number_of_calls_general_result,
SHOW_INT },
{ "Audit_null_general_status",
(char *) &number_of_calls_general_status,
SHOW_INT },
{ "Audit_null_connection_connect",
(char *) &number_of_calls_connection_connect,
SHOW_INT },
{ "Audit_null_connection_disconnect",
(char *) &number_of_calls_connection_disconnect,
SHOW_INT },
{ "Audit_null_connection_change_user",
(char *) &number_of_calls_connection_change_user,
SHOW_INT },
{ 0, 0, 0}
};
audit_null_plugin_init
初期化関数は、プラグインがロードされたときにステータス変数をゼロに設定します。audit_null_plugin_deinit
関数は、プラグインがアンロードされるときにクリーンアップ処理を実行します。プラグインは動作中に、通知を受け取るたびに最初のステータス変数を増分します。また、イベントクラスおよびサブクラスに応じてほかのものも増分します。つまり、最初の変数はイベントサブクラスのカウントの総計となります。
一般ディスクリプタの audit_null_descriptor
値は、タイプ固有のディスクリプタを指しています。監査プラグインの場合、このディスクリプタの構造体は次のようになります。
struct st_mysql_audit
{
int interface_version;
void (*release_thd)(MYSQL_THD);
void (*event_notify)(MYSQL_THD, unsigned int, const void *);
unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
};
タイプ固有のディスクリプタには次のメンバーがあります。
interface_version
: 規則により、タイプ固有のプラグインディスクリプタは、特定のプラグインタイプのインタフェースバージョンで始まります。サーバーはプラグインをロードするときにinterface_version
を検査し、プラグインと互換性があるかどうかを確認します。監査プラグインの場合、interface_version
メンバーの値はMYSQL_AUDIT_INTERFACE_VERSION
(plugin_audit.h
に定義されています) です。release_thd
: スレッドコンテキストとの関連付けが解除されていることをプラグインに通知するためにサーバーが呼び出す関数。そのような関数がない場合は、NULL
を設定します。event_notify
: 監査可能イベントが発生したことをプラグインに通知するためにサーバーが呼び出す関数。この関数はNULL
にはできません。監査が行われなくては意味がないためです。class_mask
: プラグインが通知を受け取るイベントクラスを示すビットマスク。この値が 0 の場合、サーバーはプラグインにイベントを渡しません。
サーバーは event_notify
関数および release_thd
関数を一緒に使用します。これらは特定のスレッドのコンテキストで呼び出され、スレッドはいくつかのイベント通知が生成されるアクティビティーを実行する場合があります。サーバーは、スレッドに対して event_notify
を最初に呼び出しすときに、プラグインからスレッドへのバインドを作成します。このバインドが存在する間はプラグインをアンインストールできません。スレッドに対してイベントが発生しなくなると、サーバーは release_thd
関数を呼び出すことによってプラグインにこれを通知してからバインドを破棄します。たとえば、クライアントがステートメントを発行したとき、ステートメントを処理するスレッドは、ステートメントによって生成された結果セットとログに記録されたステートメントについて、監査プラグインに通知することがあります。これらの通知が実行されたあと、クライアントが別のステートメントを発行するまでの間、サーバーはスレッドをスリープ状態にする前にプラグインを解放します。
この設計により、プラグインは event_notify
関数への最初の呼び出しで対象となるスレッドに必要なリソースを割り当て、release_thd
関数でリソースを解放します。
event_notify function:
if memory is needed to service the thread
allocate memory
... rest of notification processing ...
release_thd function:
if memory was allocated
release memory
... rest of release processing ...
これは、通知関数でメモリーの割り当てと解放を繰り返すよりも効率的です。
NULL_AUDIT
監査プラグインの例では、タイプ固有のディスクリプタは次のようになります。
static struct st_mysql_audit audit_null_descriptor=
{
MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */
NULL, /* release_thd function */
audit_null_notify, /* notify function */
{ (unsigned long) MYSQL_AUDIT_GENERAL_CLASSMASK |
MYSQL_AUDIT_CONNECTION_CLASSMASK } /* class mask */
};
サーバーは audit_null_notify
を呼び出して監査イベント情報をプラグインに渡します。release_thd
関数はありません。
イベントクラスマスクは、「general」クラスおよび「connection」クラスのすべてのイベントを対象とすることを示しています。plugin_audit.h
は、これらのクラスとそれらに対応するクラスマスクのシンボルを定義しています。
#define MYSQL_AUDIT_GENERAL_CLASS 0
#define MYSQL_AUDIT_GENERAL_CLASSMASK (1 << MYSQL_AUDIT_GENERAL_CLASS)
#define MYSQL_AUDIT_CONNECTION_CLASS 1
#define MYSQL_AUDIT_CONNECTION_CLASSMASK (1 << MYSQL_AUDIT_CONNECTION_CLASS)
タイプ固有のディスクリプタでは、event_notify
関数プロトタイプの 2 番目と 3 番目のパラメータは、イベントクラス、およびイベント構造体への一般的なポインタを表しています。
void (*event_notify)(MYSQL_THD, unsigned int, const void *);
異なるクラスのイベントは異なる構造体を持つ場合があるため、通知関数はイベントクラス値を使用して、イベント構造へのポインタを解釈する方法を判断します。
イベントクラスが MYSQL_AUDIT_GENERAL_CLASS
である通知関数をサーバーが呼び出す場合、サーバーは mysql_event_general
構造へのポインタとしてイベント構造体を渡します。
struct mysql_event_general
{
unsigned int event_subclass;
int general_error_code;
unsigned long general_thread_id;
const char *general_user;
unsigned int general_user_length;
const char *general_command;
unsigned int general_command_length;
const char *general_query;
unsigned int general_query_length;
struct charset_info_st *general_charset;
unsigned long long general_time;
unsigned long long general_rows;
};
監査プラグインは mysql_event_general
メンバーを次のように解釈できます。
-
event_subclass
: 次のいずれかの値を持つイベントサブクラス。#define MYSQL_AUDIT_GENERAL_LOG 0 #define MYSQL_AUDIT_GENERAL_ERROR 1 #define MYSQL_AUDIT_GENERAL_RESULT 2 #define MYSQL_AUDIT_GENERAL_STATUS 3
general_error_code
: エラーコード。これはmysql_errno()
C API 関数によって返されるものと似た値であり、0 は「エラーなし」を意味します。general_thread_id
: イベントが発生したスレッドの ID。general_user
: イベントの現在のユーザー。general_user_length
:general_user
のバイト単位の長さ。general_command
: 一般クエリーログイベントの場合、操作の種類。たとえば、Connect
、Query
、Shutdown
です。エラーログイベントの場合、エラーメッセージ。これはmysql_error()
C API 関数によって返されるものと似た値であり、空の文字列は「エラーなし」を意味します。結果イベントの場合、これは空です。general_command_length
:general_command
のバイト単位の長さ。general_query
: ログに記録されたか結果を生成した SQL ステートメント。general_query_length
:general_query
のバイト単位の長さ。general_charset
: イベントの文字セット情報。general_time
: 通知関数が呼び出された直前の時間を示すTIMESTAMP
値。general_rows
: 一般クエリーログイベントの場合、ゼロ。エラーログイベントの場合、エラーが発生した行番号。結果イベントの場合、結果の行数に 1 を加えた数値。結果セットを生成しないステートメントの場合、値は 0 です。このエンコードによって、結果セットを生成しないステートメントを、空の結果セットを生成するステートメントと区別できます。たとえば、DELETE
ステートメントの場合、この値は 0 です。SELECT
の場合、結果は常に 1 以上であり、1 は空の結果セットを表します。general_host
: 一般クエリーログイベントの場合、クライアントのホスト名を表す文字列。general_sql_command
: 一般クエリーログイベントの場合、connect
、drop_table
などの実行されるアクションの種類を示す文字列。general_external_user
: 一般クエリーログイベントの場合、外部ユーザーを表す文字列 (ない場合は空)。general_ip
: 一般クエリーログイベントの場合、クライアントの IP アドレスを表す文字列。
general_host
、general_sql_command
、general_external_user
、および general_ip
メンバーは、MySQL 5.6.14 で新しく導入されました。これらは、文字列とその長さがペアになった MYSQL_LEX_STRING
構造体です。たとえば、event_general
が一般イベントへのポインタである場合、次のようにして general_host
値のメンバーにアクセスできます。
event_general->general_host.length
event_general->general_host.str
イベントクラスが MYSQL_AUDIT_CONNECTION_CLASS
である通知関数をサーバーが呼び出す場合、サーバーは mysql_event_connection
構造へのポインタとしてイベント構造体を渡します。これは mysql_event_general
構造と似ており、ほぼ同じように解釈されます。
NULL_AUDIT
プラグインの通知関数はきわめて単純です。これは、グローバルイベントカウンタを増分して、イベントクラスを判別し、イベントサブクラスを参照して増分するサブクラスカウンタを判別します。
static void audit_null_notify(MYSQL_THD thd __attribute__((unused)),
unsigned int event_class,
const void *event)
{
/* prone to races, oh well */
number_of_calls++;
if (event_class == MYSQL_AUDIT_GENERAL_CLASS)
{
const struct mysql_event_general *event_general=
(const struct mysql_event_general *) event;
switch (event_general->event_subclass)
{
case MYSQL_AUDIT_GENERAL_LOG:
number_of_calls_general_log++;
break;
case MYSQL_AUDIT_GENERAL_ERROR:
number_of_calls_general_error++;
break;
case MYSQL_AUDIT_GENERAL_RESULT:
number_of_calls_general_result++;
break;
case MYSQL_AUDIT_GENERAL_STATUS:
number_of_calls_general_status++;
break;
default:
break;
}
}
else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
{
const struct mysql_event_connection *event_connection=
(const struct mysql_event_connection *) event;
switch (event_connection->event_subclass)
{
case MYSQL_AUDIT_CONNECTION_CONNECT:
number_of_calls_connection_connect++;
break;
case MYSQL_AUDIT_CONNECTION_DISCONNECT:
number_of_calls_connection_disconnect++;
break;
case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
number_of_calls_connection_change_user++;
break;
default:
break;
}
}
}
プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を使用します。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir
システム変数で指定されているディレクトリ) にインストールされている必要があります。AUDIT_NULL
プラグインの場合、ソースから MySQL をビルドするときにコンパイルおよびインストールされます。これはバイナリ配布にも含められます。ビルド処理では、adt_null.so
という名前の共有オブジェクトライブラリが生成されます (サフィクスはプラットフォームによって異なる場合があります)。
プラグインを実行時に登録するには、次のステートメントを使用します (必要に応じてサフィクスを変更します)。
mysql> INSTALL PLUGIN NULL_AUDIT SONAME 'adt_null.so';
プラグインのロードについての追加情報は、セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。
プラグインのインストールを検証するには、INFORMATION_SCHEMA.PLUGINS
テーブルを調査するか、SHOW PLUGINS
ステートメントを使用します。
監査プラグインがインストールされている間、監査プラグインはプラグインが呼び出されたイベントを示すステータス変数を公開します。
mysql> SHOW STATUS LIKE 'Audit_null%';
+-----------------------------------+-------+
| Variable_name | Value |
+-----------------------------------+-------+
| Audit_null_called | 1388 |
| Audit_null_connection_change_user | 0 |
| Audit_null_connection_connect | 22 |
| Audit_null_connection_disconnect | 21 |
| Audit_null_general_error | 1 |
| Audit_null_general_log | 513 |
| Audit_null_general_result | 415 |
| Audit_null_general_status | 416 |
+-----------------------------------+-------+
Audit_null_called
はすべてのイベントをカウントし、ほかの変数はイベントサブクラスのインスタンスをカウントします。たとえば、上記の SHOW STATUS
ステートメントによって、サーバーは結果をクライアントに送信し、ログが有効にされている場合は一般クエリーログにメッセージを書き込みます。このため、クライアントがステートメントを繰り返し発行すると、Audit_null_called
および Audit_null_general_result
が毎回増分され、ログが有効にされている場合は Audit_null_general_log
が増分されます。
プラグインをテスト後に無効にするには、次のステートメントを使用してプラグインをアンロードします。
mysql> UNINSTALL PLUGIN NULL_AUDIT;