diff options
author | Sergey Vojtovich <svoj@sun.com> | 2009-12-15 23:52:47 +0400 |
---|---|---|
committer | Sergey Vojtovich <svoj@sun.com> | 2009-12-15 23:52:47 +0400 |
commit | 45f2e0a7aad762af94eedd70b6fc0b91ae8e3376 (patch) | |
tree | 124275c3f1ec273b11d8f4b9c3952eef78477a02 /sql/sql_audit.cc | |
parent | 376cf4275f28f6b8167eaf19b2c66dee41fbc5c5 (diff) | |
download | mariadb-git-45f2e0a7aad762af94eedd70b6fc0b91ae8e3376.tar.gz |
Backport from 6.0-codebase.
WL#3771
"Audit Plugin Interface"
Implement new plug-in type - AUDIT
New plug-in: audit_null
simply increments counter for how many times it was called.
include/Makefile.am:
wl3771
add new headers to distribution
include/mysql/plugin.h:
wl3771
define new AUDIT plugin type
Split out fulltext plugin type into its own header
include/mysql/plugin.h.pp:
wl3771
no real API change, just re-arranged some code
include/mysql/plugin_audit.h:
wl3771
pluggable audit interface
include/mysql/plugin_ftparser.h:
wl3771
Split out fulltext plugin type into its own header
libmysqld/CMakeLists.txt:
wl3771
add sql_audit.cc to build
libmysqld/Makefile.am:
wl3771
add sql_audit.cc to build
plugin/audit_null:
wl3771
an example plugin for testing pluggable audit interface
plugin/audit_null/Makefile.am:
wl3771
an example plugin for testing pluggable audit interface
plugin/audit_null/audit_null.c:
wl3771
an example plugin for testing pluggable audit interface
plugin/audit_null/plug.in:
wl3771
an example plugin for testing pluggable audit interface
sql/CMakeLists.txt:
wl3771
add sql_audit.cc to build
sql/Makefile.am:
wl3771
add sql_audit.cc to build
sql/event_queue.cc:
wl3771
release audit resources before waiting
sql/log.cc:
wl3771
add general audit call for log
sql/mysqld.cc:
wl3771
add audit initialize/finalize
add general audit call for error
sql/sql_audit.cc:
wl3771
pluggable audit interface implementation
sql/sql_audit.h:
wl3771
pluggable audit interface implementation
sql/sql_class.cc:
wl3771
add thd audit init/deinit calls
sql/sql_class.h:
wl3771
add required data structures for audit to THD
sql/sql_connect.cc:
wl3771
release audit resources before waiting
sql/sql_insert.cc:
wl3771
release audit plugins before waiting
sql/sql_parse.cc:
wl3771
add general audit call for results
sql/sql_plugin.cc:
wl3771
add declarations for audit plugin type
Diffstat (limited to 'sql/sql_audit.cc')
-rw-r--r-- | sql/sql_audit.cc | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc new file mode 100644 index 00000000000..0e9b5302728 --- /dev/null +++ b/sql/sql_audit.cc @@ -0,0 +1,448 @@ +/* Copyright (C) 2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" +#include "sql_audit.h" + +extern int initialize_audit_plugin(st_plugin_int *plugin); +extern int finalize_audit_plugin(st_plugin_int *plugin); + +#ifndef EMBEDDED_LIBRARY + +unsigned long mysql_global_audit_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + +static pthread_mutex_t LOCK_audit_mask; + +static void event_class_dispatch(THD *thd, const struct mysql_event *event); + + +static inline +void set_audit_mask(unsigned long *mask, uint event_class) +{ + mask[0]= 1; + mask[0]<<= event_class; +} + +static inline +void add_audit_mask(unsigned long *mask, const unsigned long *rhs) +{ + mask[0]|= rhs[0]; +} + +static inline +bool check_audit_mask(const unsigned long *lhs, + const unsigned long *rhs) +{ + return !(lhs[0] & rhs[0]); +} + + +typedef void (*audit_handler_t)(THD *thd, uint event_subtype, va_list ap); + +/** + MYSQL_AUDIT_GENERAL_CLASS handler + + @param[in] thd + @param[in] event_subtype + @param[in] error_code + @param[in] ap + +*/ + +static void general_class_handler(THD *thd, uint event_subtype, va_list ap) +{ + mysql_event_general event; + event.event_class= MYSQL_AUDIT_GENERAL_CLASS; + event.general_error_code= va_arg(ap, int); + event.general_thread_id= thd ? thd->thread_id : 0; + event.general_time= va_arg(ap, time_t); + event.general_user= va_arg(ap, const char *); + event.general_user_length= va_arg(ap, unsigned int); + event.general_command= va_arg(ap, const char *); + event.general_command_length= va_arg(ap, unsigned int); + event.general_query= va_arg(ap, const char *); + event.general_query_length= va_arg(ap, unsigned int); + event.general_charset= va_arg(ap, struct charset_info_st *); + event.general_rows= (unsigned long long) va_arg(ap, ha_rows); + event_class_dispatch(thd, (const mysql_event*) &event); +} + + +static audit_handler_t audit_handlers[] = +{ + general_class_handler +}; + +static const uint audit_handlers_count= + (sizeof(audit_handlers) / sizeof(audit_handler_t)); + + +/** + Acquire and lock any additional audit plugins as required + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE Always +*/ + +static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg) +{ + uint event_class= *(uint*) arg; + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *); + + set_audit_mask(event_class_mask, event_class); + + /* Check if this plugin is interested in the event */ + if (check_audit_mask(data->class_mask, event_class_mask)) + return 0; + + /* + Check if this plugin may already be registered. This will fail to + acquire a newly installed plugin on a specific corner case where + one or more event classes already in use by the calling thread + are an event class of which the audit plugin has interest. + */ + if (!check_audit_mask(data->class_mask, thd->audit_class_mask)) + return 0; + + /* Check if we need to initialize the array of acquired plugins */ + if (unlikely(!thd->audit_class_plugins.buffer)) + { + /* specify some reasonable initialization defaults */ + my_init_dynamic_array(&thd->audit_class_plugins, + sizeof(plugin_ref), 16, 16); + } + + /* lock the plugin and add it to the list */ + plugin= my_plugin_lock(NULL, &plugin); + insert_dynamic(&thd->audit_class_plugins, (uchar*) &plugin); + + return 0; +} + + +/** + Notify the audit system of an event + + @param[in] thd + @param[in] event_class + @param[in] event_subtype + @param[in] error_code + +*/ + +void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...) +{ + va_list ap; + audit_handler_t *handlers= audit_handlers + event_class; + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + + DBUG_ASSERT(event_class < audit_handlers_count); + + set_audit_mask(event_class_mask, event_class); + /* + Check to see if we have acquired the audit plugins for the + required audit event classes. + */ + if (thd && check_audit_mask(thd->audit_class_mask, event_class_mask)) + { + plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, &event_class); + add_audit_mask(thd->audit_class_mask, event_class_mask); + } + + va_start(ap, event_subtype); + (*handlers)(thd, event_subtype, ap); + va_end(ap); +} + + +/** + Release any resources associated with the current thd. + + @param[in] thd + +*/ + +void mysql_audit_release(THD *thd) +{ + plugin_ref *plugins, *plugins_last; + + if (!thd || !(thd->audit_class_plugins.elements)) + return; + + plugins= (plugin_ref*) thd->audit_class_plugins.buffer; + plugins_last= plugins + thd->audit_class_plugins.elements; + for (; plugins < plugins_last; plugins++) + { + st_mysql_audit *data= plugin_data(*plugins, struct st_mysql_audit *); + + /* Check to see if the plugin has a release method */ + if (!(data->release_thd)) + continue; + + /* Tell the plugin to release its resources */ + data->release_thd(thd); + } + + /* Now we actually unlock the plugins */ + plugin_unlock_list(NULL, (plugin_ref*) thd->audit_class_plugins.buffer, + thd->audit_class_plugins.elements); + + /* Reset the state of thread values */ + reset_dynamic(&thd->audit_class_plugins); + bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask)); +} + + +/** + Initialize thd variables used by Audit + + @param[in] thd + +*/ + +void mysql_audit_init_thd(THD *thd) +{ + bzero(&thd->audit_class_plugins, sizeof(thd->audit_class_plugins)); + bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask)); +} + + +/** + Free thd variables used by Audit + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE Always +*/ + +void mysql_audit_free_thd(THD *thd) +{ + mysql_audit_release(thd); + DBUG_ASSERT(thd->audit_class_plugins.elements == 0); + delete_dynamic(&thd->audit_class_plugins); +} + + +/** + Initialize Audit global variables +*/ + +void mysql_audit_initialize() +{ + pthread_mutex_init(&LOCK_audit_mask, MY_MUTEX_INIT_FAST); + bzero(mysql_global_audit_mask, sizeof(mysql_global_audit_mask)); +} + + +/** + Finalize Audit global variables +*/ + +void mysql_audit_finalize() +{ + pthread_mutex_destroy(&LOCK_audit_mask); +} + + +/** + Initialize an Audit plug-in + + @param[in] plugin + + @retval FALSE OK + @retval TRUE There was an error. +*/ + +int initialize_audit_plugin(st_plugin_int *plugin) +{ + st_mysql_audit *data= (st_mysql_audit*) plugin->plugin->info; + + if (!data->class_mask || !data->event_notify || + !data->class_mask[0]) + { + sql_print_error("Plugin '%s' has invalid data.", + plugin->name.str); + return 1; + } + + if (plugin->plugin->init && plugin->plugin->init(NULL)) + { + sql_print_error("Plugin '%s' init function returned error.", + plugin->name.str); + return 1; + } + + /* Make the interface info more easily accessible */ + plugin->data= plugin->plugin->info; + + /* Add the bits the plugin is interested in to the global mask */ + pthread_mutex_lock(&LOCK_audit_mask); + add_audit_mask(mysql_global_audit_mask, data->class_mask); + pthread_mutex_unlock(&LOCK_audit_mask); + + return 0; +} + + +/** + Performs a bitwise OR of the installed plugins event class masks + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE always +*/ +static my_bool calc_class_mask(THD *thd, plugin_ref plugin, void *arg) +{ + st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *); + if ((data= plugin_data(plugin, struct st_mysql_audit *))) + add_audit_mask((unsigned long *) arg, data->class_mask); + return 0; +} + + +/** + Finalize an Audit plug-in + + @param[in] plugin + + @retval FALSE OK + @retval TRUE There was an error. +*/ +int finalize_audit_plugin(st_plugin_int *plugin) +{ + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + + if (plugin->plugin->deinit && plugin->plugin->deinit(NULL)) + { + DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", + plugin->name.str)); + DBUG_EXECUTE("finalize_audit_plugin", return 1; ); + } + + plugin->data= NULL; + bzero(&event_class_mask, sizeof(event_class_mask)); + + /* Iterate through all the installed plugins to create new mask */ + pthread_mutex_lock(&LOCK_audit_mask); + plugin_foreach(current_thd, calc_class_mask, MYSQL_AUDIT_PLUGIN, + &event_class_mask); + + /* Set the global audit mask */ + bmove(mysql_global_audit_mask, event_class_mask, sizeof(event_class_mask)); + pthread_mutex_unlock(&LOCK_audit_mask); + + return 0; +} + + +/** + Dispatches an event by invoking the plugin's event_notify method. + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE always +*/ + +static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg) +{ + const struct mysql_event *event= (const struct mysql_event *) arg; + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *); + + set_audit_mask(event_class_mask, event->event_class); + + /* Check to see if the plugin is interested in this event */ + if (check_audit_mask(data->class_mask, event_class_mask)) + return 0; + + /* Actually notify the plugin */ + data->event_notify(thd, event); + + return 0; +} + + +/** + Distributes an audit event to plug-ins + + @param[in] thd + @param[in] event +*/ + +static void event_class_dispatch(THD *thd, const struct mysql_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)) + { + plugin_foreach(thd, plugins_dispatch, MYSQL_AUDIT_PLUGIN, (void*) event); + } + else + { + plugin_ref *plugins, *plugins_last; + + /* Use the cached set of audit plugins */ + plugins= (plugin_ref*) thd->audit_class_plugins.buffer; + plugins_last= plugins + thd->audit_class_plugins.elements; + + for (; plugins < plugins_last; plugins++) + plugins_dispatch(thd, *plugins, (void*) event); + } +} + + +#else /* EMBEDDED_LIBRARY */ + + +void mysql_audit_initialize() +{ +} + + +void mysql_audit_finalize() +{ +} + + +int initialize_audit_plugin(st_plugin_int *plugin) +{ + return 1; +} + + +int finalize_audit_plugin(st_plugin_int *plugin) +{ + return 0; +} + + +void mysql_audit_release(THD *thd) +{ +} + + +#endif /* EMBEDDED_LIBRARY */ |