1373 lines
52 KiB
C++
1373 lines
52 KiB
C++
/* Copyright (c) 2022, 2024, Oracle and/or its affiliates.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is designed to work with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have either included with
|
|
the program or referenced in the documentation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include "mysql_server_event_tracking_bridge_imp.h"
|
|
|
|
#include "sql/command_mapping.h"
|
|
#include "sql/current_thd.h"
|
|
#include "sql/mysqld.h" /* sql_statement_names */
|
|
#include "sql/sql_audit.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/sql_lex.h"
|
|
#include "sql/sql_plugin.h"
|
|
#include "sql/sql_rewrite.h"
|
|
|
|
#include "my_sys.h"
|
|
#include "mysql/components/services/log_builtins.h" /* LogErr */
|
|
#include "mysql/plugin_audit.h"
|
|
|
|
#include <memory>
|
|
|
|
/**
|
|
@file sql/server_component/mysql_server_event_tracking_bridge_imp.cc
|
|
A bridge implementation that translates event tracking APIs to
|
|
audit plugin APIs.
|
|
*/
|
|
|
|
// clang-format off
|
|
|
|
/**
|
|
@page page_event_tracking_services Event Tracking Services
|
|
|
|
@section section_introduction Introduction
|
|
|
|
Event tracking services are a group of services that allow implementor
|
|
to monitor various database activities. Two main actors involved are:
|
|
|
|
- Producers: Those who emit one or more events
|
|
- Consumers: Those who are interesting in processing events
|
|
generated by consumers
|
|
|
|
Following services are part of this groups of services.
|
|
- Events related to user authentication (@ref s_mysql_event_tracking_authentication)
|
|
- Events related to COM command execution (@ref s_mysql_event_tracking_command)
|
|
- Events related to a session connection (@ref s_mysql_event_tracking_connection)
|
|
- Events related to execution status (@ref s_mysql_event_tracking_general)
|
|
- Events related to global variable access (@ref s_mysql_event_tracking_global_variable)
|
|
- Events related to program lifecycle (@ref s_mysql_event_tracking_lifecycle)
|
|
- Events to emit special message to log (@ref s_mysql_event_tracking_message)
|
|
- Events to perform query rewrite (@ref s_mysql_event_tracking_parse)
|
|
- Events to track query execution (@ref s_mysql_event_tracking_query)
|
|
- Events to track stored program execution (@ref s_mysql_event_tracking_stored_program)
|
|
- Events to track table access (@ref s_mysql_event_tracking_table_access)
|
|
|
|
When a consumer component is installed, it registeres its interest in receiving
|
|
notification for any subset of events by implementing corresponding services.
|
|
|
|
A producer, as a part of generating an event will inform all consumers who
|
|
have registered interest of a given event.
|
|
|
|
Through some of the above mentioned events, a consumer can indicate producer to
|
|
either generate an error/warning or stop the execution or both.
|
|
|
|
server component, which is part of MySQL server is one example of producer component.
|
|
At different point of execution, it emits events of all classes of events mentioned above.
|
|
|
|
Please take a look at following pages for more details:
|
|
|
|
- @subpage page_event_tracking_event_details
|
|
- @subpage page_event_tracking_new_consumer
|
|
- @subpage page_event_tracking_new_producer
|
|
*/
|
|
|
|
/**
|
|
@page page_event_tracking_event_details Details of existing event tracking services
|
|
|
|
@section section_event_tracking_user_management Events related to user management
|
|
|
|
User management events are produced by server component as part of various
|
|
user management DDL.
|
|
@sa @ref s_mysql_event_tracking_authentication
|
|
|
|
- EVENT_TRACKING_AUTHENTICATION_FLUSH: Emitted as a part of FLUSH PRIVILEGES
|
|
- EVENT_TRACKING_AUTHENTICATION_AUTHID_CREATE: Emitted as a part of user creation through CREATE USER
|
|
- EVENT_TRACKING_AUTHENTICATION_CREDENTIAL_CHANGE: Emitted as a part of credential change through
|
|
ALTER USER or SET PASSWORD
|
|
- EVENT_TRACKING_AUTHENTICATION_AUTHID_RENAME: Emitted as a part of RENAME USER
|
|
- EVENT_TRACKING_AUTHENTICATION_AUTHID_DROP: Emitted as a part of DROP USER
|
|
|
|
Refer to @ref mysql_event_tracking_authentication_data to know more about data passed to consumer.
|
|
|
|
In order to provide additional information for above events, server component implements
|
|
event_tracking_authentication_information (@ref s_mysql_event_tracking_authentication_information) service that a consumer
|
|
component can use.
|
|
|
|
|
|
@section section_event_tracking_command Events related to COM command execution
|
|
|
|
Commands events are executed whenever COM_* commands are executed.
|
|
@sa @ref s_mysql_event_tracking_command
|
|
|
|
- EVENT_TRACKING_COMMAND_START: Emitted whenever server component starts processing of COM_* command
|
|
- EVENT_TRACKING_COMMAND_END: Emitted whenever server component is done processing a given COM_* command
|
|
|
|
Refer to @ref mysql_event_tracking_command_data to know more about data passed to consumer.
|
|
|
|
@section section_event_tracking_connection Events related to a session connection
|
|
|
|
Connection events are generated at various stages of connection lifecycle.
|
|
@sa @ref s_mysql_event_tracking_connection
|
|
|
|
- EVENT_TRACKING_CONNECTION_PRE_AUTHENTICATE: Emitted whenever a new connection request from a client is received.
|
|
- EVENT_TRACKING_CONNECTION_CONNECT: Emitted once authentication is completed for a new incoming connection
|
|
- EVENT_TRACKING_CONNECTION_CHANGE_USER: Emitted when server receives CHANGE USER request on an existing connection
|
|
- EVENT_TRACKING_CONNECTION_DISCONNECT: Emitted when an establish session is diconnecting.
|
|
|
|
Refer to @ref mysql_event_tracking_connection_data to know more about data passed to consumer.
|
|
|
|
|
|
@section section_event_tracking_general Events related to execution status
|
|
|
|
Execution status events occur at various stages of SQL execution.
|
|
@sa @ref s_mysql_event_tracking_general
|
|
|
|
- EVENT_TRACKING_GENERAL_LOG: Emitted whenever an SQL statement is logged into server's general log
|
|
- EVENT_TRACKING_GENERAL_ERROR: Emitted whenever an error is raised
|
|
- EVENT_TRACKING_GENERAL_RESULT: Emitted once result is transmitted to user
|
|
- EVENT_TRACKING_GENERAL_STATUS: Emitted once query execution is complete
|
|
|
|
Refer to @ref mysql_event_tracking_general_data to know more about data passed to consumer.
|
|
|
|
In order to provide additional information for above events, server component implements
|
|
event_tracking_general_information (@ref s_mysql_event_tracking_general_information) service that a consumer
|
|
component can use.
|
|
|
|
|
|
@section section_event_tracking_global_variable Events related to global variable access
|
|
|
|
Global variable events occur whenever a global system variable is queried or is set.
|
|
@sa @ref s_mysql_event_tracking_global_variable
|
|
|
|
- EVENT_TRACKING_GLOBAL_VARIABLE_GET: Emitted whenever a global system variable value is queried.
|
|
- EVENT_TRACKING_GLOBAL_VARIABLE_SET: Emitted whenever a global system variable value is set.
|
|
|
|
Refer to @ref mysql_event_tracking_global_variable_data to know more about data passed to consumer.
|
|
|
|
|
|
@section section_event_tracking_lifecycle Events related to program lifecycle
|
|
|
|
Lifecycle events occur at program start-up/shutdown.
|
|
@sa @ref s_mysql_event_tracking_lifecycle
|
|
|
|
- EVENT_TRACKING_STARTUP_STARTUP: Emitted after program startup
|
|
- EVENT_TRACKING_SHUTDOWN_SHUTDOWN: Emitted before program shutdown
|
|
|
|
Refer to @ref mysql_event_tracking_startup_data and @ref mysql_event_tracking_shutdown_data to know
|
|
more about data passed to cunsumer.
|
|
|
|
|
|
@section section_event_tracking_message Events to emit special message to log
|
|
|
|
Message events are special events to inform a consumer about certain messages that producer want
|
|
to deliver. A usecase of such a message is to add special markers in e.g. audit log files.
|
|
@sa @ref s_mysql_event_tracking_message
|
|
|
|
- EVENT_TRACKING_MESSAGE_INTERNAL: Denotes system generated messages
|
|
- EVENT_TRACKING_MESSAGE_USER: Denotes user generated messages
|
|
|
|
Refer to @ref mysql_event_tracking_message_data to know more about data passed to consumer.
|
|
|
|
|
|
@section section_event_tracking_parse Events to perform query rewrite or intercept queries
|
|
|
|
These events allow consumer to either abort an operation or rewrite a query to suite the need.
|
|
@sa @ref s_mysql_event_tracking_parse
|
|
|
|
- EVENT_TRACKING_PARSE_PREPARSE: Emitted right after query is received but before execution begins
|
|
- EVENT_TRACKING_PARSE_POSTPARSE: Emitted after parsing phase of the query
|
|
|
|
Refer to @ref mysql_event_tracking_message_data to know more about data passed to consumer.
|
|
|
|
|
|
@section section_event_tracking_query Events to track query execution
|
|
|
|
These events occur at the beginning and end of query execution.
|
|
@sa @ref s_mysql_event_tracking_query
|
|
|
|
- EVENT_TRACKING_QUERY_START: Emitted at the start of top level query execution
|
|
- EVENT_TRACKING_QUERY_STATUS_END: Emitted at the end of top level query execution
|
|
- EVENT_TRACKING_QUERY_NESTED_START: Emitted at the start of subquery execution
|
|
- EVENT_TRACKING_QUERY_NESTED_STATUS_END: Emitted at the end of subquery execution
|
|
|
|
Refer to @ref mysql_event_tracking_query_data to know more about data passed to consumer.
|
|
|
|
|
|
@section section_event_tracking_stored_program Events to track stored program execution
|
|
|
|
These events occur whenever a stored program is executed.
|
|
@sa @ref s_mysql_event_tracking_stored_program
|
|
|
|
- EVENT_TRACKING_STORED_PROGRAM_EXECUTE: Emitted whenever a stored program is executed.
|
|
|
|
Refer to @ref mysql_event_tracking_stored_program_data to know more about data passed to consumer.
|
|
|
|
|
|
@section section_event_tracking_table_access Events to track table access
|
|
|
|
These events track different types of table access.
|
|
@sa @ref s_mysql_event_tracking_table_access
|
|
|
|
- EVENT_TRACKING_TABLE_ACCESS_READ: Emitted whenever a table is read
|
|
- EVENT_TRACKING_TABLE_ACCESS_INSERT: Emitted whenever data is inserted in a table
|
|
- EVENT_TRACKING_TABLE_ACCESS_UPDATE: Emitted whenever data is updated in a table
|
|
- EVENT_TRACKING_TABLE_ACCESS_DELETE: Emitted whenever data is deleted from a table
|
|
|
|
Refer to @ref mysql_event_tracking_table_access_data to know more about data passed to consumer.
|
|
*/
|
|
|
|
/**
|
|
@page page_event_tracking_new_consumer How to create a new consumer component
|
|
|
|
A consumer of events essentially need to implement a subset of event tracking services.
|
|
Once such a consumer is installed, producer will use service APIs to notify it about
|
|
given set of events that the consumer is interested in.
|
|
|
|
In order to simplify writing the core part of the functionality, we have helper headers
|
|
with skeleton implementation of each class of events.
|
|
|
|
@sa @ref EVENT_TRACKING_AUTHENTICATION_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_COMMAND_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_CONNECTION_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_GENERAL_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_GLOBAL_VARIABLE_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_LIFECYCLE_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_MESSAGE_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_PARSE_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_QUERY_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_STORED_PROGRAM_CONSUMER_EXAMPLE
|
|
@sa @ref EVENT_TRACKING_TABLE_ACCESS_CONSUMER_EXAMPLE
|
|
|
|
Following is an example of a consumer that implements various
|
|
event tracking services, increments simple counters for each
|
|
event and then displays them through status variables.
|
|
|
|
@code
|
|
#include "mysql/components/services/component_status_var_service.h"
|
|
#include "mysql/components/util/event_tracking_authentication_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_command_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_connection_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_general_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_global_variable_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_lifecycle_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_message_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_parse_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_query_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_stored_program_consumer_helper.h"
|
|
#include "mysql/components/util/event_tracking_table_access_consumer_helper.h"
|
|
|
|
REQUIRES_SERVICE_PLACEHOLDER_AS(status_variable_registration,
|
|
mysql_status_var_service);
|
|
|
|
namespace Event_tracking_implementation {
|
|
|
|
enum class Event_types {
|
|
AUTHENTICATION = 0,
|
|
COMMAND,
|
|
CONNECTION,
|
|
GENERAL,
|
|
GLOBAL_VARIABLE,
|
|
MESSAGE,
|
|
PARSE,
|
|
QUERY,
|
|
SHUTDOWN,
|
|
STARTUP,
|
|
STORED_PROGRAM,
|
|
TABLE_ACCESS,
|
|
LAST
|
|
};
|
|
|
|
// For the sake of simplicity, locking is ignored
|
|
// Ideally one should use atomic variables and use
|
|
// status variable of type SHOW_FUNC to fetch and
|
|
// display the value
|
|
|
|
unsigned long g_counters[static_cast<unsigned int>(Event_types::LAST)] = {0};
|
|
|
|
static void increment_counter(Event_types event_type) {
|
|
if (event_type < Event_types::LAST) {
|
|
++g_counters[static_cast<unsigned int>(event_type)];
|
|
}
|
|
}
|
|
|
|
// Status variables
|
|
static SHOW_VAR status_vars[] = {
|
|
{"component_event_tracking_example.counter_event_tracking_authentication",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::AUTHENTICATION)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_command",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::COMMAND)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_connection",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::CONNECTION)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_general",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::GENERAL)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_global_"
|
|
"variable",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::GLOBAL_VARIABLES)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_message",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::MESSAGE)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_parse",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::PARSE)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_query",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::QUERY)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_"
|
|
"shutdown",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::SHUTDOWN)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_"
|
|
"startup",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::STARTUP)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_tracking_stored_"
|
|
"program",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::STORED_PROGRAM)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
{"component_event_tracking_example.counter_event_track_table_"
|
|
"access",
|
|
(char
|
|
*)&g_counters[static_cast<unsigned int>(Event_types::TABLE_ACCESS)],
|
|
SHOW_LONG, SHOW_SCOPE_GLOBAL},
|
|
// null terminator required
|
|
{nullptr, nullptr, SHOW_UNDEF, SHOW_SCOPE_UNDEF}};
|
|
|
|
// START: Event tracking implementation
|
|
|
|
mysql_event_tracking_authentication_subclass_t
|
|
Event_tracking_authentication_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_authentication_implementation::callback(
|
|
const mysql_event_tracking_authentication_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::AUTHENTICATION);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_command_subclass_t
|
|
Event_tracking_command_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_command_implementation::callback(
|
|
const mysql_event_tracking_command_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::COMMAND);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_connection_subclass_t
|
|
Event_tracking_connection_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_connection_implementation::callback(
|
|
const mysql_event_tracking_connection_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::CONNECTION);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_general_subclass_t
|
|
Event_tracking_general_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_general_implementation::callback(
|
|
const mysql_event_tracking_general_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::GENERAL);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_global_variable_subclass_t
|
|
Event_tracking_global_variable_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_global_variable_implementation::callback(
|
|
const mysql_event_tracking_global_variable_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::GLOBAL_VARIABLE);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_startup_subclass_t
|
|
Event_tracking_lifecycle_implementation::startup_filtered_sub_events = 0;
|
|
bool Event_tracking_lifecycle_implementation::callback(
|
|
const mysql_event_tracking_startup_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::STARTUP);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_shutdown_subclass_t
|
|
Event_tracking_lifecycle_implementation::shutdown_filtered_sub_events = 0;
|
|
bool Event_tracking_lifecycle_implementation::callback(
|
|
const mysql_event_tracking_shutdown_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::SHUTDOWN);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_message_subclass_t
|
|
Event_tracking_message_implementation::filtered_sub_events = 0;
|
|
|
|
bool Event_tracking_message_implementation::callback(
|
|
const mysql_event_tracking_message_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::MESSAGE);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_parse_subclass_t
|
|
Event_tracking_parse_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_parse_implementation::callback(
|
|
mysql_event_tracking_parse_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::PARSE);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_query_subclass_t
|
|
Event_tracking_query_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_query_implementation::callback(
|
|
const mysql_event_tracking_query_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::QUERY);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_stored_program_subclass_t
|
|
Event_tracking_stored_program_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_stored_program_implementation::callback(
|
|
const mysql_event_tracking_stored_program_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::STORED_PROGRAM);
|
|
return false;
|
|
}
|
|
|
|
mysql_event_tracking_table_access_subclass_t
|
|
Event_tracking_table_access_implementation::filtered_sub_events = 0;
|
|
bool Event_tracking_table_access_implementation::callback(
|
|
const mysql_event_tracking_table_access_data *data [[maybe_unused]]) {
|
|
increment_counter(Event_types::TABLE_ACCESS);
|
|
return false;
|
|
}
|
|
|
|
// STOP: Event tracking implementation
|
|
|
|
static mysql_service_status_t init() {
|
|
|
|
if ((variables_registered =
|
|
mysql_status_var_service->register_variable(status_vars))) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static mysql_service_status_t deinit() {
|
|
|
|
if (mysql_status_var_service->unregister_variable(status_vars)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
} // namespace Event_tracking_implementation
|
|
|
|
// =======================================================================
|
|
|
|
// Component declaration related stuff
|
|
|
|
// This component provides implementation of following component services
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_AUTHENTICATION(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_COMMAND(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_CONNECTION(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_GENERAL(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_GLOBAL_VARIABLE(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_LIFECYCLE(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_MESSAGE(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_PARSE(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_QUERY(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_STORED_PROGRAM(
|
|
component_event_tracking_example);
|
|
IMPLEMENTS_SERVICE_EVENT_TRACKING_TABLE_ACCESS(
|
|
component_event_tracking_example);
|
|
|
|
// This component provides following services
|
|
BEGIN_COMPONENT_PROVIDES(component_event_tracking_example)
|
|
PROVIDES_SERVICE_EVENT_TRACKING_AUTHENTICATION(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_COMMAND(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_CONNECTION(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_GENERAL(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_GLOBAL_VARIABLE(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_LIFECYCLE(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_MESSAGE(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_PARSE(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_QUERY(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_STORED_PROGRAM(
|
|
component_event_tracking_example),
|
|
PROVIDES_SERVICE_EVENT_TRACKING_TABLE_ACCESS(
|
|
component_event_tracking_example),
|
|
END_COMPONENT_PROVIDES();
|
|
|
|
// List of dependencies
|
|
BEGIN_COMPONENT_REQUIRES(component_event_tracking_example)
|
|
REQUIRES_SERVICE_AS(status_variable_registration, mysql_status_var_service),
|
|
END_COMPONENT_REQUIRES();
|
|
|
|
// Component description
|
|
BEGIN_COMPONENT_METADATA(component_event_tracking_example)
|
|
METADATA("mysql.author", "Oracle Corporation"),
|
|
METADATA("mysql.license", "GPL"),
|
|
METADATA("component_event_tracking_example", "1"),
|
|
END_COMPONENT_METADATA();
|
|
|
|
// Component declaration
|
|
DECLARE_COMPONENT(component_event_tracking_example,
|
|
"component_event_tracking_example")
|
|
Event_tracking_consumer_a::init,
|
|
Event_tracking_consumer_a::deinit END_DECLARE_COMPONENT();
|
|
|
|
// Component contained in this library
|
|
DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(
|
|
component_event_tracking_example) END_DECLARE_LIBRARY_COMPONENTS
|
|
|
|
@endcode
|
|
*/
|
|
|
|
/**
|
|
@page page_event_tracking_new_producer How to create a new producer component
|
|
|
|
|
|
A new producer component would typically mean new set of events being introduced.
|
|
In such a case, new services should be introduced - just like existing set of
|
|
event tracking services. E.g. something like following:
|
|
|
|
@code
|
|
BEGIN_SERVICE_DEFINITION(event_tracking_<new_service_name>)
|
|
|
|
DECLARE_BOOL_METHOD(notify,
|
|
(const mysql_<new_service_name>_data *data));
|
|
|
|
END_SERVICE_DEFINITION(event_tracking_<new_service_name>)
|
|
@endcode
|
|
|
|
Once introduced, producer should be able to broadcast the event to all interested
|
|
consumer. To achieve that, a consumer should do following:
|
|
|
|
- Define a new reference caching change using @ref s_mysql_reference_caching_channel
|
|
- Create a reference caching cache to obtain references to all interested component
|
|
- Iterate over the reference cache and notify each consumer component
|
|
|
|
Further, a consumer component may be uninstalled at any time. To facilitate that, the
|
|
reference cache created by the producer should release corresponding reference.
|
|
This can be achieved by implementing @ref s_mysql_dynamic_loader_services_unload_notification
|
|
and refreshing reference caches as a part of it.
|
|
Reference caching component will take care of informing the producer whenever a component
|
|
is being uinstalled.
|
|
|
|
In addition, developer may also think about writing wrappers to facilitate development
|
|
of consumer component. For example see @ref EVENT_TRACKING_GENERAL_CONSUMER_EXAMPLE.
|
|
*/
|
|
|
|
// clang-format on
|
|
|
|
/** Macro to perform LEX_CSTRING transformation */
|
|
#define TO_LEXCSTRING(x) \
|
|
{ x.str, x.length }
|
|
|
|
using event_tracking_authentication_t =
|
|
SERVICE_TYPE_NO_CONST(event_tracking_authentication);
|
|
using event_tracking_command_t = SERVICE_TYPE_NO_CONST(event_tracking_command);
|
|
using event_tracking_connection_t =
|
|
SERVICE_TYPE_NO_CONST(event_tracking_connection);
|
|
using event_tracking_general_t = SERVICE_TYPE_NO_CONST(event_tracking_general);
|
|
using event_tracking_global_variable_t =
|
|
SERVICE_TYPE_NO_CONST(event_tracking_global_variable);
|
|
using event_tracking_lifecycle_t =
|
|
SERVICE_TYPE_NO_CONST(event_tracking_lifecycle);
|
|
using event_tracking_message_t = SERVICE_TYPE_NO_CONST(event_tracking_message);
|
|
using event_tracking_parse_t = SERVICE_TYPE_NO_CONST(event_tracking_parse);
|
|
using event_tracking_query_t = SERVICE_TYPE_NO_CONST(event_tracking_query);
|
|
using event_tracking_stored_program_t =
|
|
SERVICE_TYPE_NO_CONST(event_tracking_stored_program);
|
|
using event_tracking_table_access_t =
|
|
SERVICE_TYPE_NO_CONST(event_tracking_table_access);
|
|
|
|
SERVICE_TYPE(event_tracking_authentication) *srv_event_tracking_authentication =
|
|
nullptr;
|
|
SERVICE_TYPE(event_tracking_command) *srv_event_tracking_command = nullptr;
|
|
SERVICE_TYPE(event_tracking_connection) *srv_event_tracking_connection =
|
|
nullptr;
|
|
SERVICE_TYPE(event_tracking_general) *srv_event_tracking_general = nullptr;
|
|
SERVICE_TYPE(event_tracking_global_variable)
|
|
*srv_event_tracking_global_variable = nullptr;
|
|
SERVICE_TYPE(event_tracking_lifecycle) *srv_event_tracking_lifecycle = nullptr;
|
|
SERVICE_TYPE(event_tracking_message) *srv_event_tracking_message = nullptr;
|
|
SERVICE_TYPE(event_tracking_parse) *srv_event_tracking_parse = nullptr;
|
|
SERVICE_TYPE(event_tracking_query) *srv_event_tracking_query = nullptr;
|
|
SERVICE_TYPE(event_tracking_stored_program) *srv_event_tracking_stored_program =
|
|
nullptr;
|
|
SERVICE_TYPE(event_tracking_table_access) *srv_event_tracking_table_access =
|
|
nullptr;
|
|
|
|
static bool inited = false;
|
|
void init_srv_event_tracking_handles() {
|
|
srv_registry->acquire("event_tracking_authentication.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_authentication_t **>(
|
|
&srv_event_tracking_authentication)));
|
|
srv_registry->acquire(
|
|
"event_tracking_command.mysql_server",
|
|
reinterpret_cast<my_h_service *>(const_cast<event_tracking_command_t **>(
|
|
&srv_event_tracking_command)));
|
|
srv_registry->acquire("event_tracking_connection.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_connection_t **>(
|
|
&srv_event_tracking_connection)));
|
|
srv_registry->acquire(
|
|
"event_tracking_general.mysql_server",
|
|
reinterpret_cast<my_h_service *>(const_cast<event_tracking_general_t **>(
|
|
&srv_event_tracking_general)));
|
|
srv_registry->acquire("event_tracking_global_variable.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_global_variable_t **>(
|
|
&srv_event_tracking_global_variable)));
|
|
srv_registry->acquire("event_tracking_lifecycle.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_lifecycle_t **>(
|
|
&srv_event_tracking_lifecycle)));
|
|
srv_registry->acquire(
|
|
"event_tracking_message.mysql_server",
|
|
reinterpret_cast<my_h_service *>(const_cast<event_tracking_message_t **>(
|
|
&srv_event_tracking_message)));
|
|
srv_registry->acquire(
|
|
"event_tracking_parse.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_parse_t **>(&srv_event_tracking_parse)));
|
|
srv_registry->acquire(
|
|
"event_tracking_query.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_query_t **>(&srv_event_tracking_query)));
|
|
srv_registry->acquire("event_tracking_stored_program.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_stored_program_t **>(
|
|
&srv_event_tracking_stored_program)));
|
|
srv_registry->acquire("event_tracking_table_access.mysql_server",
|
|
reinterpret_cast<my_h_service *>(
|
|
const_cast<event_tracking_table_access_t **>(
|
|
&srv_event_tracking_table_access)));
|
|
|
|
assert(srv_event_tracking_authentication != nullptr &&
|
|
srv_event_tracking_command != nullptr &&
|
|
srv_event_tracking_connection != nullptr &&
|
|
srv_event_tracking_general != nullptr &&
|
|
srv_event_tracking_global_variable != nullptr &&
|
|
srv_event_tracking_lifecycle != nullptr &&
|
|
srv_event_tracking_message != nullptr &&
|
|
srv_event_tracking_parse != nullptr &&
|
|
srv_event_tracking_query != nullptr &&
|
|
srv_event_tracking_stored_program != nullptr &&
|
|
srv_event_tracking_table_access != nullptr);
|
|
|
|
inited = true;
|
|
}
|
|
void deinit_srv_event_tracking_handles() {
|
|
if (inited) {
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_authentication_t *>(
|
|
srv_event_tracking_authentication)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_command_t *>(srv_event_tracking_command)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_connection_t *>(
|
|
srv_event_tracking_connection)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_general_t *>(srv_event_tracking_general)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_global_variable_t *>(
|
|
srv_event_tracking_global_variable)));
|
|
srv_registry->release(
|
|
reinterpret_cast<my_h_service>(const_cast<event_tracking_lifecycle_t *>(
|
|
srv_event_tracking_lifecycle)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_message_t *>(srv_event_tracking_message)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_parse_t *>(srv_event_tracking_parse)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_query_t *>(srv_event_tracking_query)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_stored_program_t *>(
|
|
srv_event_tracking_stored_program)));
|
|
srv_registry->release(reinterpret_cast<my_h_service>(
|
|
const_cast<event_tracking_table_access_t *>(
|
|
srv_event_tracking_table_access)));
|
|
}
|
|
inited = false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/**
|
|
Check, whether masks specified by lhs parameter and rhs parameters overlap.
|
|
|
|
@param lhs First mask to check.
|
|
@param rhs Second mask to check.
|
|
|
|
@return false, when masks overlap, otherwise true.
|
|
*/
|
|
static inline bool check_audit_mask(const unsigned long lhs,
|
|
const unsigned long rhs) {
|
|
return !(lhs & rhs);
|
|
}
|
|
|
|
/**
|
|
Dispatches an event by invoking the plugin's event_notify method.
|
|
|
|
@param[in] thd Session THD containing references to the audit plugins.
|
|
@param[in] plugin Plugin used for dispatching the event.
|
|
@param[in] arg Opaque event data structure.
|
|
|
|
@retval false always
|
|
*/
|
|
|
|
static int plugins_dispatch(THD *thd, plugin_ref plugin, void *arg) {
|
|
const struct st_mysql_event_plugin_generic *event_generic =
|
|
(const struct st_mysql_event_plugin_generic *)arg;
|
|
unsigned long subclass = static_cast<unsigned long>(
|
|
*static_cast<const int *>(event_generic->event));
|
|
st_mysql_audit *data = plugin_data<st_mysql_audit *>(plugin);
|
|
|
|
/* Check to see if the plugin is interested in this event */
|
|
if (check_audit_mask(data->class_mask[event_generic->event_class], subclass))
|
|
return 0;
|
|
|
|
/* Actually notify the plugin */
|
|
return data->event_notify(thd, event_generic->event_class,
|
|
event_generic->event);
|
|
}
|
|
|
|
static bool plugins_dispatch_bool(THD *thd, plugin_ref plugin, void *arg) {
|
|
return plugins_dispatch(thd, plugin, arg) ? true : false;
|
|
}
|
|
|
|
/**
|
|
Distributes an audit event to plug-ins
|
|
|
|
@param[in] thd THD that generated the event.
|
|
@param event_class Audit event class.
|
|
@param[in] event Opaque pointer to the event data.
|
|
*/
|
|
|
|
int event_class_dispatch(THD *thd, mysql_event_class_t event_class,
|
|
const void *event) {
|
|
int result = 0;
|
|
struct st_mysql_event_plugin_generic event_generic;
|
|
event_generic.event_class = event_class;
|
|
event_generic.event = event;
|
|
/*
|
|
Check if we are doing a slow global dispatch. This event occurs when
|
|
thd == NULL as it is not associated with any particular thread.
|
|
*/
|
|
if (unlikely(!thd)) {
|
|
return plugin_foreach(thd, plugins_dispatch_bool, MYSQL_AUDIT_PLUGIN,
|
|
&event_generic)
|
|
? 1
|
|
: 0;
|
|
} else {
|
|
plugin_ref *plugins, *plugins_last;
|
|
|
|
/* Use the cached set of audit plugins */
|
|
plugins = thd->audit_class_plugins.begin();
|
|
plugins_last = thd->audit_class_plugins.end();
|
|
|
|
for (; plugins != plugins_last; plugins++)
|
|
result |= plugins_dispatch(thd, *plugins, &event_generic);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static const CHARSET_INFO *get_charset_from_thd(THD *thd) {
|
|
if (thd->rewritten_query().length() > 0)
|
|
return thd->rewritten_query().charset();
|
|
return thd->charset();
|
|
}
|
|
|
|
/**
|
|
Fill query info extracted from the thread object and return
|
|
the thread object charset info.
|
|
|
|
@param[in] thd Thread data.
|
|
@param[out] query SQL query text.
|
|
|
|
@return SQL query charset.
|
|
*/
|
|
inline const CHARSET_INFO *thd_get_audit_query(THD *thd, LEX_CSTRING *query) {
|
|
/*
|
|
If we haven't tried to rewrite the query to obfuscate passwords
|
|
etc. yet, do so now.
|
|
*/
|
|
if (thd->rewritten_query().length() == 0) mysql_rewrite_query(thd);
|
|
|
|
/*
|
|
If there was something to rewrite, use the rewritten query;
|
|
otherwise, just use the original as submitted by the client.
|
|
*/
|
|
if (thd->rewritten_query().length() > 0) {
|
|
query->str = thd->rewritten_query().ptr();
|
|
query->length = thd->rewritten_query().length();
|
|
return thd->rewritten_query().charset();
|
|
} else {
|
|
query->str = thd->query().str;
|
|
query->length = thd->query().length;
|
|
return thd->charset();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
DEFINE_BOOL_METHOD(Event_authentication_bridge_implementation::notify,
|
|
(const mysql_event_tracking_authentication_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
auto event_information =
|
|
static_cast<Event_tracking_authentication_information *>(
|
|
thd->get_event_tracking_data().second);
|
|
|
|
mysql_event_authentication plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_AUTHENTICATION_FLUSH:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_AUTHENTICATION_FLUSH;
|
|
break;
|
|
case EVENT_TRACKING_AUTHENTICATION_AUTHID_CREATE:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_AUTHENTICATION_AUTHID_CREATE;
|
|
break;
|
|
case EVENT_TRACKING_AUTHENTICATION_CREDENTIAL_CHANGE:
|
|
plugin_data.event_subclass =
|
|
MYSQL_AUDIT_AUTHENTICATION_CREDENTIAL_CHANGE;
|
|
break;
|
|
case EVENT_TRACKING_AUTHENTICATION_AUTHID_RENAME:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_AUTHENTICATION_AUTHID_RENAME;
|
|
break;
|
|
case EVENT_TRACKING_AUTHENTICATION_AUTHID_DROP:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_AUTHENTICATION_AUTHID_DROP;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
const char *auth_method =
|
|
event_information->authentication_methods_.size()
|
|
? event_information->authentication_methods_[0]
|
|
: nullptr;
|
|
plugin_data.authentication_plugin = {auth_method,
|
|
auth_method ? strlen(auth_method) : 0};
|
|
plugin_data.connection_id = data->connection_id;
|
|
plugin_data.host = TO_LEXCSTRING(data->host);
|
|
plugin_data.is_role = event_information->is_role_;
|
|
plugin_data.new_host = TO_LEXCSTRING(event_information->new_host_);
|
|
plugin_data.new_user = TO_LEXCSTRING(event_information->new_user_);
|
|
plugin_data.query_charset = thd_get_audit_query(thd, &plugin_data.query);
|
|
plugin_data.sql_command_id = thd->lex->sql_command;
|
|
plugin_data.status = data->status;
|
|
plugin_data.user = TO_LEXCSTRING(data->user);
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_AUTHENTICATION_CLASS,
|
|
&plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_command_bridge_implementation::notify,
|
|
(const mysql_event_tracking_command_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_command plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_COMMAND_START:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_COMMAND_START;
|
|
break;
|
|
case EVENT_TRACKING_COMMAND_END:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_COMMAND_END;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.command_id = get_server_command(data->command.str);
|
|
plugin_data.connection_id = data->connection_id;
|
|
plugin_data.status = data->status;
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_COMMAND_CLASS, &plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_connection_bridge_implementation::notify,
|
|
(const mysql_event_tracking_connection_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_connection plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_CONNECTION_CONNECT:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_CONNECTION_CONNECT;
|
|
break;
|
|
case EVENT_TRACKING_CONNECTION_DISCONNECT:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_CONNECTION_DISCONNECT;
|
|
break;
|
|
case EVENT_TRACKING_CONNECTION_CHANGE_USER:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_CONNECTION_CHANGE_USER;
|
|
break;
|
|
case EVENT_TRACKING_CONNECTION_PRE_AUTHENTICATE:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.status = data->status;
|
|
plugin_data.connection_id = data->connection_id;
|
|
plugin_data.user = TO_LEXCSTRING(data->user);
|
|
plugin_data.priv_user = TO_LEXCSTRING(data->priv_user);
|
|
plugin_data.external_user = TO_LEXCSTRING(data->external_user);
|
|
plugin_data.proxy_user = TO_LEXCSTRING(data->proxy_user);
|
|
plugin_data.host = TO_LEXCSTRING(data->host);
|
|
plugin_data.ip = TO_LEXCSTRING(data->ip);
|
|
plugin_data.database = TO_LEXCSTRING(data->database);
|
|
plugin_data.connection_type = data->connection_type;
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_CONNECTION_CLASS,
|
|
&plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_general_bridge_implementation::notify,
|
|
(const mysql_event_tracking_general_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
auto event_information = static_cast<Event_tracking_general_information *>(
|
|
thd->get_event_tracking_data().second);
|
|
|
|
mysql_event_general plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_GENERAL_LOG:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_GENERAL_LOG;
|
|
break;
|
|
case EVENT_TRACKING_GENERAL_ERROR:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_GENERAL_ERROR;
|
|
break;
|
|
case EVENT_TRACKING_GENERAL_STATUS:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_GENERAL_STATUS;
|
|
break;
|
|
case EVENT_TRACKING_GENERAL_RESULT:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_GENERAL_RESULT;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
};
|
|
|
|
plugin_data.general_error_code = data->error_code;
|
|
plugin_data.general_thread_id = data->connection_id;
|
|
plugin_data.general_user = TO_LEXCSTRING(data->user);
|
|
plugin_data.general_ip = TO_LEXCSTRING(data->ip);
|
|
plugin_data.general_host = TO_LEXCSTRING(data->host);
|
|
plugin_data.general_external_user =
|
|
TO_LEXCSTRING(event_information->external_user_);
|
|
plugin_data.general_rows =
|
|
static_cast<unsigned long long>(event_information->rows_);
|
|
|
|
if (event_information->command_.str != nullptr &&
|
|
thd->lex->sql_command == SQLCOM_END &&
|
|
thd->get_command() != COM_QUERY) {
|
|
plugin_data.general_sql_command = {STRING_WITH_LEN("")};
|
|
} else {
|
|
plugin_data.general_sql_command =
|
|
sql_statement_names[thd->lex->sql_command];
|
|
}
|
|
|
|
plugin_data.general_charset = const_cast<CHARSET_INFO *>(
|
|
thd_get_audit_query(thd, &plugin_data.general_query));
|
|
plugin_data.general_time =
|
|
static_cast<unsigned long long>(event_information->time_);
|
|
plugin_data.general_command = {event_information->command_.str,
|
|
event_information->command_.length};
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &plugin_data);
|
|
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_global_variable_bridge_implementation::notify,
|
|
(const mysql_event_tracking_global_variable_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_global_variable plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_GLOBAL_VARIABLE_GET:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_GLOBAL_VARIABLE_GET;
|
|
break;
|
|
case EVENT_TRACKING_GLOBAL_VARIABLE_SET:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_GLOBAL_VARIABLE_SET;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.connection_id = data->connection_id;
|
|
plugin_data.sql_command_id = thd->lex->sql_command;
|
|
plugin_data.variable_name = TO_LEXCSTRING(data->variable_name);
|
|
plugin_data.variable_value = TO_LEXCSTRING(data->variable_value);
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS,
|
|
&plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_lifecycle_bridge_implementation::notify_shutdown,
|
|
(const mysql_event_tracking_shutdown_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_server_shutdown plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_SHUTDOWN_SHUTDOWN:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.exit_code = data->exit_code;
|
|
|
|
switch (data->reason) {
|
|
case EVENT_TRACKING_SHUTDOWN_REASON_SHUTDOWN:
|
|
plugin_data.exit_code = MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_SHUTDOWN;
|
|
break;
|
|
case EVENT_TRACKING_SHUTDOWN_REASON_ABORT:
|
|
plugin_data.reason = MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_ABORT;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS,
|
|
&plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_lifecycle_bridge_implementation::notify_startup,
|
|
(const mysql_event_tracking_startup_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_server_startup plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_STARTUP_STARTUP:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_SERVER_STARTUP_STARTUP;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.argc = data->argc;
|
|
plugin_data.argv = data->argv;
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_SERVER_STARTUP_CLASS,
|
|
&plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_message_bridge_implementation::notify,
|
|
(const mysql_event_tracking_message_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_message plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_MESSAGE_INTERNAL:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_MESSAGE_INTERNAL;
|
|
break;
|
|
case EVENT_TRACKING_MESSAGE_USER:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_MESSAGE_USER;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
std::unique_ptr<mysql_event_message_key_value_t[]> local_key_value_map(
|
|
data->key_value_map_length > 0
|
|
? new mysql_event_message_key_value_t[data->key_value_map_length]
|
|
: nullptr);
|
|
|
|
mysql_event_message_key_value_t *local_kv = local_key_value_map.get();
|
|
mysql_event_tracking_message_key_value_t *kv = data->key_value_map;
|
|
|
|
for (size_t i = 0; i < data->key_value_map_length; ++i, ++local_kv, ++kv) {
|
|
local_kv->key = {kv->key.str, kv->key.length};
|
|
switch (kv->value_type) {
|
|
case EVENT_TRACKING_MESSAGE_VALUE_TYPE_STR:
|
|
local_kv->value_type = MYSQL_AUDIT_MESSAGE_VALUE_TYPE_STR;
|
|
local_kv->value.str = {kv->value.str.str, kv->value.str.length};
|
|
break;
|
|
case EVENT_TRACKING_MESSAGE_VALUE_TYPE_NUM:
|
|
local_kv->value_type = MYSQL_AUDIT_MESSAGE_VALUE_TYPE_NUM;
|
|
local_kv->value.num = kv->value.num;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
plugin_data.component = TO_LEXCSTRING(data->component);
|
|
plugin_data.key_value_map = local_key_value_map.get();
|
|
plugin_data.key_value_map_length = data->key_value_map_length;
|
|
plugin_data.message = TO_LEXCSTRING(data->message);
|
|
plugin_data.producer = TO_LEXCSTRING(data->producer);
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_MESSAGE_CLASS, &plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_parse_bridge_implementation::notify,
|
|
(mysql_event_tracking_parse_data * data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_parse plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_PARSE_PREPARSE:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_PARSE_PREPARSE;
|
|
break;
|
|
case EVENT_TRACKING_PARSE_POSTPARSE:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_PARSE_POSTPARSE;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
mysql_event_parse_rewrite_plugin_flag plugin_flag;
|
|
switch (*(data->flags)) {
|
|
case EVENT_TRACKING_PARSE_REWRITE_NONE:
|
|
plugin_flag = MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_NONE;
|
|
break;
|
|
case EVENT_TRACKING_PARSE_REWRITE_QUERY_REWRITTEN:
|
|
plugin_flag = MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_QUERY_REWRITTEN;
|
|
break;
|
|
case EVENT_TRACKING_PARSE_REWRITE_IS_PREPARED_STATEMENT:
|
|
plugin_flag = MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_IS_PREPARED_STATEMENT;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
LEX_CSTRING rewritten_query{nullptr, 0};
|
|
plugin_data.flags = &plugin_flag;
|
|
plugin_data.query = TO_LEXCSTRING(data->query);
|
|
plugin_data.rewritten_query = nullptr;
|
|
if (data->rewritten_query) {
|
|
plugin_data.rewritten_query = &rewritten_query;
|
|
}
|
|
|
|
bool retval =
|
|
event_class_dispatch(thd, MYSQL_AUDIT_PARSE_CLASS, &plugin_data);
|
|
|
|
if ((int)plugin_flag & EVENT_TRACKING_PARSE_REWRITE_QUERY_REWRITTEN) {
|
|
*(data->flags) |= EVENT_TRACKING_PARSE_REWRITE_QUERY_REWRITTEN;
|
|
if (data->rewritten_query) {
|
|
data->rewritten_query->str = rewritten_query.str;
|
|
data->rewritten_query->length = rewritten_query.length;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_query_bridge_implementation::notify,
|
|
(const mysql_event_tracking_query_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_query plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_QUERY_START:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_QUERY_START;
|
|
break;
|
|
case EVENT_TRACKING_QUERY_NESTED_START:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_QUERY_NESTED_START;
|
|
break;
|
|
case EVENT_TRACKING_QUERY_STATUS_END:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_QUERY_STATUS_END;
|
|
break;
|
|
case EVENT_TRACKING_QUERY_NESTED_STATUS_END:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_QUERY_NESTED_STATUS_END;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.connection_id = data->connection_id;
|
|
plugin_data.query = TO_LEXCSTRING(data->query);
|
|
plugin_data.query_charset = get_charset_from_thd(thd);
|
|
plugin_data.sql_command_id = thd->lex->sql_command;
|
|
plugin_data.status = data->status;
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_QUERY_CLASS, &plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_stored_program_bridge_implementation::notify,
|
|
(const mysql_event_tracking_stored_program_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_stored_program plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_STORED_PROGRAM_EXECUTE:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_STORED_PROGRAM_EXECUTE;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.connection_id = data->connection_id;
|
|
plugin_data.database = TO_LEXCSTRING(data->database);
|
|
plugin_data.name = TO_LEXCSTRING(data->name);
|
|
plugin_data.parameters = data->parameters;
|
|
plugin_data.query_charset = thd_get_audit_query(thd, &plugin_data.query);
|
|
plugin_data.sql_command_id = thd->lex->sql_command;
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_STORED_PROGRAM_CLASS,
|
|
&plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEFINE_BOOL_METHOD(Event_table_access_bridge_implementation::notify,
|
|
(const mysql_event_tracking_table_access_data *data)) {
|
|
try {
|
|
if (data == nullptr) return false;
|
|
THD *thd = current_thd;
|
|
|
|
mysql_event_table_access plugin_data;
|
|
|
|
switch (data->event_subclass) {
|
|
case EVENT_TRACKING_TABLE_ACCESS_READ:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_TABLE_ACCESS_READ;
|
|
break;
|
|
case EVENT_TRACKING_TABLE_ACCESS_INSERT:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_TABLE_ACCESS_INSERT;
|
|
break;
|
|
case EVENT_TRACKING_TABLE_ACCESS_UPDATE:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_TABLE_ACCESS_UPDATE;
|
|
break;
|
|
case EVENT_TRACKING_TABLE_ACCESS_DELETE:
|
|
plugin_data.event_subclass = MYSQL_AUDIT_TABLE_ACCESS_DELETE;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
plugin_data.connection_id = data->connection_id;
|
|
plugin_data.sql_command_id = thd->lex->sql_command;
|
|
plugin_data.query_charset = thd_get_audit_query(thd, &plugin_data.query);
|
|
plugin_data.table_database = TO_LEXCSTRING(data->table_database);
|
|
plugin_data.table_name = TO_LEXCSTRING(data->table_name);
|
|
|
|
return event_class_dispatch(thd, MYSQL_AUDIT_TABLE_ACCESS_CLASS,
|
|
&plugin_data);
|
|
} catch (...) {
|
|
return true;
|
|
}
|
|
}
|