28.2.4.11 编写协议跟踪插件

MySQL 支持协议跟踪插件的使用:Client 端插件,用于实现使用 Client 机/服务器协议对 Client 机与服务器之间的通信进行跟踪。

28.2.4.11.1 使用测试协议跟踪插件

MySQL 包括一个测试协议跟踪插件,该插件用于说明可从此类插件获得的信息,并作为编写其他协议跟踪插件的指南。要查看测试插件的工作方式,请使用 MySQL 源代码分发。二进制发行版是在禁用测试插件的情况下构建的。

通过配置启用了WITH_TEST_TRACE_PLUGIN CMake 选项的 MySQL 来启用测试协议跟踪插件。这将导致构建测试跟踪插件并由 MySQLClient 端程序加载它,但是默认情况下该插件无效。使用以下环境变量控制插件:

  • MYSQL_TEST_TRACE_DEBUG:将此变量设置为 0 以外的值,以使测试插件在stderr上生成诊断输出。

  • MYSQL_TEST_TRACE_CRASH:将此变量设置为非 0 的值,如果检测到无效的跟踪事件,则导致测试插件中止 Client 端程序。

Caution

测试协议跟踪插件的诊断输出可以泄露密码和其他敏感信息。

给定启用了源代码的 MySQL 安装并启用了测试插件,您可以看到mysqlClient 端与 MySQL 服务器之间的通信轨迹,如下所示:

shell> export MYSQL_TEST_TRACE_DEBUG=1
shqll> mysql
test_trace: Test trace plugin initialized
test_trace: Starting tracing in stage CONNECTING
test_trace: stage: CONNECTING, event: CONNECTING
test_trace: stage: CONNECTING, event: CONNECTED
test_trace: stage: WAIT_FOR_INIT_PACKET, event: READ_PACKET
test_trace: stage: WAIT_FOR_INIT_PACKET, event: PACKET_RECEIVED
test_trace: packet received: 87 bytes
  0A 35 2E 37 2E 33 2D 6D  31 33 2D 64 65 62 75 67   .5.7.3-m13-debug
  2D 6C 6F 67 00 04 00 00  00 2B 7C 4F 55 3F 79 67   -log.....+|OU?yg
test_trace: 004: stage: WAIT_FOR_INIT_PACKET, event: INIT_PACKET_RECEIVED
test_trace: 004: stage: AUTHENTICATE, event: AUTH_PLUGIN
test_trace: 004: Using authentication plugin: mysql_native_password
test_trace: 004: stage: AUTHENTICATE, event: SEND_AUTH_RESPONSE
test_trace: 004: sending packet: 188 bytes
  85 A6 7F 00 00 00 00 01  21 00 00 00 00 00 00 00   .?......!.......
  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
...
mysql> quit
test_trace: 008: stage: READY_FOR_COMMAND, event: SEND_COMMAND
test_trace: 008: QUIT
test_trace: 008: stage: READY_FOR_COMMAND, event: PACKET_SENT
test_trace: 008: packet sent: 0 bytes
test_trace: 008: stage: READY_FOR_COMMAND, event: DISCONNECTED
test_trace: 008: Connection  closed
test_trace: 008: Tracing connection has ended
Bye
test_trace: Test trace plugin de-initialized

要禁用跟踪输出,请执行以下操作:

shell> MYSQL_TEST_TRACE_DEBUG=
28.2.4.11.2 使用自己的协议跟踪插件

Note

要使用自己的协议跟踪插件,必须使用WITH_TEST_TRACE_PLUGIN CMake 选项* disabled *配置 MySQL,因为一次只能加载一个协议跟踪插件,并且尝试加载第二个协议跟踪插件时会出错。如果已经构建了启用了测试协议跟踪插件的 MySQL 以查看其工作原理,则必须在没有 MySQL 的情况下重建 MySQL,然后才能使用自己的插件。

本节讨论如何编写名为simple_trace的基本协议跟踪插件。该插件提供了一个框架,该框架显示了如何设置 Client 端插件 Descriptors 并创建与跟踪相关的回调函数。在simple_trace中,这些函数是基本函数,除了说明所需的参数外没有其他作用。要详细查看跟踪插件如何利用跟踪事件信息,请检查测试协议跟踪插件的源文件(MySQL 源分发的libmysql目录中的test_trace_plugin.cc)。但是,请注意,此处使用的st_mysql_client_plugin_TRACE结构与通常的 Client 端插件声明宏所使用的结构不同。特别是,前两个成员是由声明宏显式定义的,而不是隐式定义的。

几个头文件包含与协议跟踪插件有关的信息:

  • client_plugin.h:定义 Client 端插件的 API。这包括 Client 端插件 Descriptors 和 Client 端插件 C API 调用的函数原型(请参见第 27.7.13 节“ C APIClient 端插件功能”)。

  • plugin_trace.h:包含类型MYSQL_CLIENT_TRACE_PLUGIN的 Client 端插件的声明。它还包含对允许的协议阶段,阶段之间的过渡以及每个阶段允许的事件类型的描述。

要编写协议跟踪插件,请在插件源文件中包含以下头文件。根据插件的功能和要求,可能还需要其他 MySQL 或常规头文件。

#include <mysql/plugin_trace.h>
#include <mysql.h>

plugin_trace.h包含client_plugin.h,因此您无需显式包括后者。

使用mysql_declare_client_plugin()mysql_end_client_plugin宏声明 Client 端插件 Descriptors(请参见第 28.2.4.2.3 节“Client 端插件 Descriptors”)。对于simple_trace插件,Descriptors 如下所示:

mysql_declare_client_plugin(TRACE)
  "simple_trace",                 /* plugin name */
  "Author Name",                  /* author */
  "Simple protocol trace plugin", /* description */
  {1,0,0},                        /* version = 1.0.0 */
  "GPL",                          /* license type */
  NULL,                           /* for internal use */
  plugin_init,                    /* initialization function */
  plugin_deinit,                  /* deinitialization function */
  plugin_options,                 /* option-handling function */
  trace_start,                    /* start-trace function */
  trace_stop,                     /* stop-trace function */
  trace_event                     /* event-handling function */
mysql_end_client_plugin;

从插件名称到选项处理功能的 Descriptors 成员对于所有 Client 端插件类型都是通用的。普通成员之后的成员实现跟踪事件处理。

可以在 Descriptors 中将不需要插件处理的函数成员声明为NULL,在这种情况下,您无需编写任何相应的函数。为了说明目的并显示参数语法,以下讨论实现了 Descriptors 中列出的所有功能,即使其中一些功能什么也不做,

所有 Client 端插件共有的初始化,取消初始化和选项功能声明如下。有关参数和返回值的描述,请参见第 28.2.4.2.3 节“Client 端插件 Descriptors”

static int
plugin_init(char *errbuf, size_t errbuf_len, int argc, va_list args)
{
  return 0;
}

static int
plugin_deinit()
{
  return 0;
}

static int
plugin_options(const char *option, const void *value)
{
  return 0;
}

Client 端插件 Descriptors 的特定于跟踪的成员是回调函数。以下描述提供了有关如何使用它们的更多详细信息。每个都有一个第一个参数,该参数是指向插件实例的指针,以防您的实现需要访问它。

trace_start():在每个跟踪的连接的开始处调用此函数(每个连接在加载插件后开始)。传递给连接处理程序和协议阶段,在此阶段开始跟踪。 trace_start()分配trace_event()函数所需的内存(如果有),并返回指向它的指针。如果不需要内存,则此函数返回NULL

static void*
trace_start(struct st_mysql_client_plugin_TRACE *self,
            MYSQL *conn,
            enum protocol_stage stage)
{
  struct st_trace_data *plugin_data= malloc(sizeof(struct st_trace_data));

  fprintf(stderr, "Initializing trace: stage %d\n", stage);
  if (plugin_data)
  {
    memset(plugin_data, 0, sizeof(struct st_trace_data));
    fprintf(stderr, "Trace initialized\n");
    return plugin_data;
  }
  fprintf(stderr, "Could not initialize trace\n");
  exit(1);
}

trace_stop():连接跟踪结束时调用此函数。通常在关闭连接时发生,但可以更早发生。例如,trace_event()可以随时返回非零值,这将导致连接跟踪终止。即使连接尚未结束,也会调用trace_stop()

trace_stop()传递给连接处理程序和指向trace_start()分配的内存的指针(如果没有则为NULL)。如果指针不是NULL,则trace_stop()应该释放内存。此函数不返回任何值。

static void
trace_stop(struct st_mysql_client_plugin_TRACE *self,
           MYSQL *conn,
           void *plugin_data)
{
  fprintf(stderr, "Terminating trace\n");
  if (plugin_data)
    free(plugin_data);
}

trace_event():每次事件发生时都会调用此函数。它传递给指向由trace_start()(如果没有则为NULL)分配的内存,连接处理程序,当前协议阶段和事件代码以及事件数据的指针。此函数返回 0 以 continue 跟踪,如果跟踪应停止,则返回非零。

static int
trace_event(struct st_mysql_client_plugin_TRACE *self,
            void *plugin_data,
            MYSQL *conn,
            enum protocol_stage stage,
            enum trace_event event,
            struct st_trace_event_args args)
{
  fprintf(stderr, "Trace event received: stage %d, event %d\n", stage, event);
  if (event == TRACE_EVENT_DISCONNECTED)
    fprintf(stderr, "Connection closed\n");
  return 0;
}

跟踪框架会在连接结束时关闭连接的跟踪,因此trace_event()仅在您想尽早终止连接的跟踪时才应返回非零。假设您只想跟踪某个 MySQL 帐户的连接。验证后,您可以检查连接的用户名,如果不是您感兴趣的用户,则停止跟踪。

对于每次对trace_event()的调用,st_trace_event_args结构均包含事件数据。它具有以下定义:

struct st_trace_event_args
{
  const char           *plugin_name;
  int                   cmd;
  const unsigned char  *hdr;
  size_t                hdr_len;
  const unsigned char  *pkt;
  size_t                pkt_len;
};

对于不同的事件类型,st_trace_event_args结构包含以下信息。所有长度以字节为单位。未使用的成员设置为0/NULL

AUTH_PLUGIN事件:

plugin_name  The name of the plugin

SEND_COMMAND事件:

cmd          The command code
hdr          Pointer to the command packet header
hdr_len      Length of the header
pkt          Pointer to the command arguments
pkt_len      Length of the arguments

其他SEND_xxxxxx_RECEIVED事件:

pkt          Pointer to the data sent or received
pkt_len      Length of the data

PACKET_SENT事件:

pkt_len      Number of bytes sent

要编译和安装插件库文件,请使用第 28.2.4.3 节“编译和安装插件库”中的说明。要使该库文件可供使用,请将其安装在插件目录(由plugin_dir系统变量命名的目录)中。

将插件库文件编译并安装到插件目录中之后,您可以通过将LIBMYSQL_PLUGINS环境变量设置为插件名称来轻松对其进行测试,这会影响使用该变量的所有 Client 端程序。 mysql是这样一个程序:

shell> export LIBMYSQL_PLUGINS=simple_trace
shqll> mysql
Initializing trace: stage 0
Trace initialized
Trace event received: stage 0, event 1
Trace event received: stage 0, event 2
...
Welcome to the MySQL monitor.  Commands end with ; or \g.
Trace event received
Trace event received
...
mysql> SELECT 1;
Trace event received: stage 4, event 12
Trace event received: stage 4, event 16
...
Trace event received: stage 8, event 14
Trace event received: stage 8, event 15
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

mysql> quit
Trace event received: stage 4, event 12
Trace event received: stage 4, event 16
Trace event received: stage 4, event 3
Connection closed
Terminating trace
Bye

要停止跟踪插件的加载,请执行以下操作:

shell> LIBMYSQL_PLUGINS=

也可以编写直接加载插件的 Client 端程序。您可以通过调用mysql_options()来设置MYSQL_PLUGIN_DIR选项,以告知 Client 端插件目录的位置:

char *plugin_dir = "path_to_plugin_dir";

/* ... process command-line options ... */

mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);

通常,该程序还将接受--plugin-dir选项,该选项使用户可以覆盖默认值。

如果 Client 端程序需要较低级别的插件 Management,则 Client 端库应包含带有st_mysql_client_plugin参数的函数。参见第 27.7.13 节“ C APIClient 端插件功能”