Extending MySQL 8.0  /  ...  /  Using Your Own Protocol Trace Plugins

4.4.11.2 Using Your Own Protocol Trace Plugins

Note

To use your own protocol trace plugins, you must configure MySQL with the WITH_TEST_TRACE_PLUGIN CMake option disabled because only one protocol trace plugin can be loaded at a time and an error occurs for attempts to load a second one. If you have already built MySQL with the test protocol trace plugin enabled to see how it works, you must rebuild MySQL without it before you can use your own plugins.

This section discusses how to write a basic protocol trace plugin named simple_trace. This plugin provides a framework showing how to set up the client plugin descriptor and create the trace-related callback functions. In simple_trace, these functions are rudimentary and do little other than illustrate the arguments required. To see in detail how a trace plugin can make use of trace event information, check the source file for the test protocol trace plugin (test_trace_plugin.cc in the libmysql directory of a MySQL source distribution). However, note that the st_mysql_client_plugin_TRACE structure used there differs from the structures used with the usual client plugin declaration macros. In particular, the first two members are defined explicitly, not implicitly by declaration macros.

Several header files contain information relevant to protocol trace plugins:

  • client_plugin.h: Defines the API for client plugins. This includes the client plugin descriptor and function prototypes for client plugin C API calls (see C API Client Plugin Interface).

  • plugin_trace.h: Contains declarations for client-side plugins of type MYSQL_CLIENT_TRACE_PLUGIN. It also contains descriptions of the permitted protocol stages, transitions between stages, and the types of events permitted at each stage.

To write a protocol trace plugin, include the following header files in the plugin source file. Other MySQL or general header files might also be needed, depending on the plugin capabilities and requirements.

Press CTRL+C to copy
#include <mysql/plugin_trace.h> #include <mysql.h>

plugin_trace.h includes client_plugin.h, so you need not include the latter file explicitly.

Declare the client-side plugin descriptor with the mysql_declare_client_plugin() and mysql_end_client_plugin macros (see Section 4.4.2.3, “Client Plugin Descriptors”). For the simple_trace plugin, the descriptor looks like this:

Press CTRL+C to copy
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;

The descriptor members from the plugin name through the option-handling function are common to all client plugin types. The members following the common members implement trace event handling.

Function members for which the plugin needs no processing can be declared as NULL in the descriptor, in which case you need not write any corresponding function. For illustration purposes and to show the argument syntax, the following discussion implements all functions listed in the descriptor, even though some of them do nothing,

The initialization, deinitialization, and options functions common to all client plugins are declared as follows. For a description of the arguments and return values, see Section 4.4.2.3, “Client Plugin Descriptors”.

Press CTRL+C to copy
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; }

The trace-specific members of the client plugin descriptor are callback functions. The following descriptions provide more detail on how they are used. Each has a first argument that is a pointer to the plugin instance in case your implementation needs to access it.

trace_start(): This function is called at the start of each traced connection (each connection that starts after the plugin is loaded). It is passed the connection handler and the protocol stage at which tracing starts. trace_start() allocates memory needed by the trace_event() function, if any, and returns a pointer to it. If no memory is needed, this function returns NULL.

Press CTRL+C to copy
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(): This function is called when tracing of the connection ends. That usually happens when the connection is closed, but can happen earlier. For example, trace_event() can return a nonzero value at any time and that causes tracing of the connection to terminate. trace_stop() is then called even though the connection has not ended.

trace_stop() is passed the connection handler and a pointer to the memory allocated by trace_start() (NULL if none). If the pointer is non-NULL, trace_stop() should deallocate the memory. This function returns no value.

Press CTRL+C to copy
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(): This function is called for each event occurrence. It is passed a pointer to the memory allocated by trace_start() (NULL if none), the connection handler, the current protocol stage and event codes, and event data. This function returns 0 to continue tracing, nonzero if tracing should stop.

Press CTRL+C to copy
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; }

The tracing framework shuts down tracing of the connection when the connection ends, so trace_event() should return nonzero only if you want to terminate tracing of the connection early. Suppose that you want to trace only connections for a certain MySQL account. After authentication, you can check the user name for the connection and stop tracing if it is not the user in whom you are interested.

For each call to trace_event(), the st_trace_event_args structure contains the event data. It has this definition:

Press CTRL+C to copy
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; };

For different event types, the st_trace_event_args structure contains the information described following. All lengths are in bytes. Unused members are set to 0/NULL.

AUTH_PLUGIN event:

Press CTRL+C to copy
plugin_name The name of the plugin

SEND_COMMAND event:

Press CTRL+C to copy
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

Other SEND_xxx and xxx_RECEIVED events:

Press CTRL+C to copy
pkt Pointer to the data sent or received pkt_len Length of the data

PACKET_SENT event:

Press CTRL+C to copy
pkt_len Number of bytes sent

To compile and install a plugin library file, use the instructions in Section 4.4.3, “Compiling and Installing Plugin Libraries”. To make the library file available for use, install it in the plugin directory (the directory named by the plugin_dir system variable).

After the plugin library file is compiled and installed in the plugin directory, you can test it easily by setting the LIBMYSQL_PLUGINS environment variable to the plugin name, which affects any client program that uses that variable. mysql is one such program:

Press CTRL+C to copy
$> 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

To stop the trace plugin from being loaded, do this:

Press CTRL+C to copy
$> LIBMYSQL_PLUGINS=

It is also possible to write client programs that directly load the plugin. You can tell the client where the plugin directory is located by calling mysql_options() to set the MYSQL_PLUGIN_DIR option:

Press CTRL+C to copy
char *plugin_dir = "path_to_plugin_dir"; /* ... process command-line options ... */ mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);

Typically, the program will also accept a --plugin-dir option that enables users to override the default value.

Should a client program require lower-level plugin management, the client library contains functions that take an st_mysql_client_plugin argument. See C API Client Plugin Interface.