summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergey Vojtovich <svoj@sun.com>2010-01-19 17:30:55 +0400
committerSergey Vojtovich <svoj@sun.com>2010-01-19 17:30:55 +0400
commit667b5944a7a28dcefdd725d360b6631bd74aedc8 (patch)
tree667b5c726df9ea2c6435bb9a13fad521165d32ec /sql
parenta6165accb3279fe64b672fa0eeaa30ef12cf6557 (diff)
parent9130563708cbdbb488e5361af79eb9f4b9dab0ea (diff)
downloadmariadb-git-667b5944a7a28dcefdd725d360b6631bd74aedc8.tar.gz
Merge backport of WL#3771 with mysql-next-mr.
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt2
-rw-r--r--sql/Makefile.am4
-rw-r--r--sql/event_queue.cc8
-rw-r--r--sql/ha_ndbcluster.cc12
-rw-r--r--sql/log.cc17
-rw-r--r--sql/mysqld.cc37
-rw-r--r--sql/sql_audit.cc477
-rw-r--r--sql/sql_audit.h131
-rw-r--r--sql/sql_class.cc5
-rw-r--r--sql/sql_class.h15
-rw-r--r--sql/sql_connect.cc2
-rw-r--r--sql/sql_insert.cc3
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_plugin.cc58
14 files changed, 712 insertions, 63 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 82a94b64365..99b1a3dc206 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -73,7 +73,7 @@ SET (SQL_SOURCE
event_queue.cc event_db_repository.cc
sql_tablespace.cc events.cc ../sql-common/my_user.c
partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
- rpl_rli.cc rpl_mi.cc sql_servers.cc
+ rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc
sql_connect.cc scheduler.cc
sql_profile.cc event_parse_data.cc
sql_signal.cc rpl_handler.cc sys_vars.cc
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 5f8bc8ef112..e22de4931db 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -111,6 +111,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
sql_plugin.h authors.h event_parse_data.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
+ sql_audit.h \
contributors.h sql_servers.h sql_signal.h records.h \
sql_prepare.h rpl_handler.h replication.h sys_vars.h
@@ -157,7 +158,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
event_queue.cc event_db_repository.cc events.cc \
sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc \
- sql_servers.cc event_parse_data.cc sql_signal.cc \
+ sql_servers.cc sql_audit.cc \
+ event_parse_data.cc sql_signal.cc \
rpl_handler.cc
nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index cf63ba937e0..225029040b0 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -16,6 +16,7 @@
#include "mysql_priv.h"
#include "event_queue.h"
#include "event_data_objects.h"
+#include "sql_audit.h"
/**
@addtogroup Event_Scheduler
@@ -581,6 +582,9 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
/* There are no events in the queue */
next_activation_at= 0;
+ /* Release any held audit resources before waiting */
+ mysql_audit_release(thd);
+
/* Wait on condition until signaled. Release LOCK_queue while waiting. */
cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__);
@@ -600,6 +604,10 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
*/
struct timespec top_time;
set_timespec(top_time, next_activation_at - thd->query_start());
+
+ /* Release any held audit resources before waiting */
+ mysql_audit_release(thd);
+
cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__);
continue;
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index b254d5d387c..63716e8960e 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -7466,7 +7466,6 @@ static int connect_callback()
}
extern int ndb_dictionary_is_mysqld;
-extern mysql_mutex_t LOCK_plugin;
#ifdef HAVE_PSI_INTERFACE
@@ -7559,13 +7558,6 @@ static int ndbcluster_init(void *p)
init_ndbcluster_psi_keys();
#endif
- /*
- Below we create new THD's. They'll need LOCK_plugin, but it's taken now by
- plugin initialization code. Release it to avoid deadlocks. It's safe, as
- there're no threads that may concurrently access plugin control structures.
- */
- mysql_mutex_unlock(&LOCK_plugin);
-
mysql_mutex_init(key_ndbcluster_mutex,
&ndbcluster_mutex, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_ndb_util_thread,
@@ -7722,8 +7714,6 @@ static int ndbcluster_init(void *p)
goto ndbcluster_init_error;
}
- mysql_mutex_lock(&LOCK_plugin);
-
ndbcluster_inited= 1;
DBUG_RETURN(FALSE);
@@ -7736,8 +7726,6 @@ ndbcluster_init_error:
g_ndb_cluster_connection= NULL;
ndbcluster_hton->state= SHOW_OPTION_DISABLED; // If we couldn't use handler
- mysql_mutex_lock(&LOCK_plugin);
-
DBUG_RETURN(TRUE);
}
diff --git a/sql/log.cc b/sql/log.cc
index 7776b6bfbdc..81f8b01ba3c 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -28,6 +28,7 @@
#include "sql_repl.h"
#include "rpl_filter.h"
#include "rpl_rli.h"
+#include "sql_audit.h"
#include <my_dir.h>
#include <stdarg.h>
@@ -42,7 +43,6 @@
/* max size of the log message */
#define MAX_LOG_BUFFER_SIZE 1024
-#define MAX_USER_HOST_SIZE 512
#define MAX_TIME_SIZE 32
#define MY_OFF_T_UNDEF (~(my_off_t)0UL)
@@ -1069,7 +1069,6 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
bool error= FALSE;
Log_event_handler **current_handler= general_log_handler_list;
char user_host_buff[MAX_USER_HOST_SIZE + 1];
- Security_context *sctx= thd->security_ctx;
uint user_host_len= 0;
time_t current_time;
@@ -1081,14 +1080,16 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
unlock();
return 0;
}
- user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
- sctx->priv_user ? sctx->priv_user : "", "[",
- sctx->user ? sctx->user : "", "] @ ",
- sctx->host ? sctx->host : "", " [",
- sctx->ip ? sctx->ip : "", "]", NullS) -
- user_host_buff;
+ user_host_len= make_user_name(thd, user_host_buff);
current_time= my_time(0);
+
+ mysql_audit_general_log(thd, current_time,
+ user_host_buff, user_host_len,
+ command_name[(uint) command].str,
+ command_name[(uint) command].length,
+ query, query_length);
+
while (*current_handler)
error|= (*current_handler++)->
log_general(thd, current_time, user_host_buff,
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index e424735af1e..f7b86653908 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -26,6 +26,7 @@
#include "mysqld_suffix.h"
#include "mysys_err.h"
#include "events.h"
+#include "sql_audit.h"
#include "probes_mysql.h"
#include "debug_sync.h"
@@ -954,6 +955,7 @@ static void close_server_sock();
static void clean_up_mutexes(void);
static void wait_for_signal_thread_to_end(void);
static void create_pid_file();
+static void mysqld_exit(int exit_code) __attribute__((noreturn));
static void end_ssl();
#endif
@@ -1364,6 +1366,7 @@ void unireg_end(void)
#endif
}
+
extern "C" void unireg_abort(int exit_code)
{
DBUG_ENTER("unireg_abort");
@@ -1374,15 +1377,25 @@ extern "C" void unireg_abort(int exit_code)
sql_print_error("Aborting\n");
clean_up(!opt_help && (exit_code || !opt_bootstrap)); /* purecov: inspected */
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
+ mysqld_exit(exit_code);
+}
+
+static void mysqld_exit(int exit_code)
+{
+ /*
+ Important note: we wait for the signal thread to end,
+ but if a kill -15 signal was sent, the signal thread did
+ spawn the kill_server_thread thread, which is running concurrently.
+ */
wait_for_signal_thread_to_end();
+ mysql_audit_finalize();
clean_up_mutexes();
clean_up_error_log_mutex();
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
exit(exit_code); /* purecov: inspected */
}
-#endif /*EMBEDDED_LIBRARY*/
-
+#endif /* !EMBEDDED_LIBRARY */
void clean_up(bool print_message)
{
@@ -3023,6 +3036,8 @@ void my_message_sql(uint error, const char *str, myf MyFlags)
error= ER_UNKNOWN_ERROR;
}
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_ERROR, error, str);
+
if (thd)
{
if (MyFlags & ME_FATALERROR)
@@ -4568,6 +4583,9 @@ int main(int argc, char **argv)
thr_kill_signal= SIGINT;
#endif
+ /* Initialize audit interface globals. Audit plugins are inited later. */
+ mysql_audit_initialize();
+
/*
Perform basic logger initialization logger. Should be called after
MY_INIT, as it initializes mutexes. Log tables are inited later.
@@ -4860,14 +4878,6 @@ int main(int argc, char **argv)
}
#endif
clean_up(1);
- /*
- Important note: we wait for the signal thread to end,
- but if a kill -15 signal was sent, the signal thread did
- spawn the kill_server_thread thread, which is running concurrently.
- */
- wait_for_signal_thread_to_end();
- clean_up_mutexes();
- clean_up_error_log_mutex();
#ifdef HAVE_PSI_INTERFACE
/*
Disable the instrumentation, to avoid recording events
@@ -4880,13 +4890,10 @@ int main(int argc, char **argv)
}
shutdown_performance_schema();
#endif
- my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
-
- exit(0);
- return(0); /* purecov: deadcode */
+ mysqld_exit(0);
}
-#endif /* EMBEDDED_LIBRARY */
+#endif /* !EMBEDDED_LIBRARY */
/****************************************************************************
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
new file mode 100644
index 00000000000..5190cba64de
--- /dev/null
+++ b/sql/sql_audit.cc
@@ -0,0 +1,477 @@
+/* Copyright (C) 2007 MySQL AB, 2008-2009 Sun Microsystems, Inc
+
+ 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 mysql_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);
+}
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_audit_mask;
+
+static PSI_mutex_info all_audit_mutexes[]=
+{
+ { &key_LOCK_audit_mask, "LOCK_audit_mask", PSI_FLAG_GLOBAL}
+};
+
+static void init_audit_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_audit_mutexes);
+ PSI_server->register_mutex(category, all_audit_mutexes, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
+
+/**
+ Initialize Audit global variables
+*/
+
+void mysql_audit_initialize()
+{
+#ifdef HAVE_PSI_INTERFACE
+ init_audit_psi_keys();
+#endif
+
+ mysql_mutex_init(key_LOCK_audit_mask, &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()
+{
+ mysql_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 */
+ mysql_mutex_lock(&LOCK_audit_mask);
+ add_audit_mask(mysql_global_audit_mask, data->class_mask);
+ mysql_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 */
+
+ /*
+ LOCK_audit_mask/LOCK_plugin order is not fixed, but serialized with table
+ lock on mysql.plugin.
+ */
+ mysql_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));
+ mysql_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 */
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
new file mode 100644
index 00000000000..c25011d0d59
--- /dev/null
+++ b/sql/sql_audit.h
@@ -0,0 +1,131 @@
+#ifndef SQL_AUDIT_INCLUDED
+#define SQL_AUDIT_INCLUDED
+
+/* 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/plugin_audit.h>
+
+extern unsigned long mysql_global_audit_mask[];
+
+
+extern void mysql_audit_initialize();
+extern void mysql_audit_finalize();
+
+
+extern void mysql_audit_init_thd(THD *thd);
+extern void mysql_audit_free_thd(THD *thd);
+
+
+extern void mysql_audit_notify(THD *thd, uint event_class,
+ uint event_subtype, ...);
+extern void mysql_audit_release(THD *thd);
+
+#define MAX_USER_HOST_SIZE 512
+static inline uint make_user_name(THD *thd, char *buf)
+{
+ Security_context *sctx= thd->security_ctx;
+ return strxnmov(buf, MAX_USER_HOST_SIZE,
+ sctx->priv_user ? sctx->priv_user : "", "[",
+ sctx->user ? sctx->user : "", "] @ ",
+ sctx->host ? sctx->host : "", " [",
+ sctx->ip ? sctx->ip : "", "]", NullS) - buf;
+}
+
+/**
+ Call audit plugins of GENERAL audit class, MYSQL_AUDIT_GENERAL_LOG subtype.
+
+ @param[in] thd
+ @param[in] time time that event occurred
+ @param[in] user User name
+ @param[in] userlen User name length
+ @param[in] cmd Command name
+ @param[in] cmdlen Command name length
+ @param[in] query Query string
+ @param[in] querylen Query string length
+*/
+
+static inline
+void mysql_audit_general_log(THD *thd, time_t time,
+ const char *user, uint userlen,
+ const char *cmd, uint cmdlen,
+ const char *query, uint querylen)
+{
+#ifndef EMBEDDED_LIBRARY
+ if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK)
+ {
+ CHARSET_INFO *clientcs= thd ? thd->variables.character_set_client
+ : global_system_variables.character_set_client;
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, MYSQL_AUDIT_GENERAL_LOG,
+ 0, time, user, userlen, cmd, cmdlen,
+ query, querylen, clientcs, 0);
+ }
+#endif
+}
+
+/**
+ Call audit plugins of GENERAL audit class.
+ event_subtype should be set to one of:
+ MYSQL_AUDIT_GENERAL_ERROR
+ MYSQL_AUDIT_GENERAL_RESULT
+
+ @param[in] thd
+ @param[in] event_subtype Type of general audit event.
+ @param[in] error_code Error code
+ @param[in] msg Message
+*/
+static inline
+void mysql_audit_general(THD *thd, uint event_subtype,
+ int error_code, const char *msg)
+{
+#ifndef EMBEDDED_LIBRARY
+ if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK)
+ {
+ time_t time= my_time(0);
+ uint msglen= msg ? strlen(msg) : 0;
+ const char *query, *user;
+ uint querylen, userlen;
+ char user_buff[MAX_USER_HOST_SIZE];
+ CHARSET_INFO *clientcs;
+ ha_rows rows;
+
+ if (thd)
+ {
+ query= thd->query();
+ querylen= thd->query_length();
+ user= user_buff;
+ userlen= make_user_name(thd, user_buff);
+ clientcs= thd->variables.character_set_client;
+ rows= thd->warning_info->current_row_for_warning();
+ }
+ else
+ {
+ query= user= 0;
+ querylen= userlen= 0;
+ clientcs= global_system_variables.character_set_client;
+ rows= 0;
+ }
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype,
+ error_code, time, user, userlen, msg, msglen,
+ query, querylen, clientcs, rows);
+ }
+#endif
+}
+
+
+#endif /* SQL_AUDIT_INCLUDED */
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 2a82b0058da..60c1438b5cf 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -32,6 +32,7 @@
#include "slave.h"
#include <my_bitmap.h>
#include "log_event.h"
+#include "sql_audit.h"
#include <m_ctype.h>
#include <sys/stat.h>
#include <thr_alarm.h>
@@ -524,6 +525,7 @@ THD::THD()
dbug_sentry=THD_SENTRY_MAGIC;
#endif
#ifndef EMBEDDED_LIBRARY
+ mysql_audit_init_thd(this);
net.vio=0;
#endif
client_capabilities= 0; // minimalistic client
@@ -1041,6 +1043,7 @@ THD::~THD()
cleanup();
ha_close_connection(this);
+ mysql_audit_release(this);
plugin_thdvar_cleanup(this);
DBUG_PRINT("info", ("freeing security context"));
@@ -1058,6 +1061,8 @@ THD::~THD()
delete rli_fake;
rli_fake= NULL;
}
+
+ mysql_audit_free_thd(this);
#endif
free_root(&main_mem_root, MYF(0));
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b3cc1fc68c0..f1437f520f5 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -23,6 +23,7 @@
#pragma interface /* gcc class implementation */
#endif
+#include <mysql/plugin_audit.h>
#include "log.h"
#include "rpl_tblmap.h"
@@ -1784,6 +1785,20 @@ public:
partition_info *work_part_info;
#endif
+#ifndef EMBEDDED_LIBRARY
+ /**
+ Array of active audit plugins which have been used by this THD.
+ This list is later iterated to invoke release_thd() on those
+ plugins.
+ */
+ DYNAMIC_ARRAY audit_class_plugins;
+ /**
+ Array of bits indicating which audit classes have already been
+ added to the list of audit plugins which are currently in use.
+ */
+ unsigned long audit_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+#endif
+
#if defined(ENABLED_DEBUG_SYNC)
/* Debug Sync facility. See debug_sync.cc. */
struct st_debug_sync_control *debug_sync_control;
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index ccc176624ed..9758b2b4e4d 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -19,6 +19,7 @@
*/
#include "mysql_priv.h"
+#include "sql_audit.h"
#include "probes_mysql.h"
#ifdef HAVE_OPENSSL
@@ -1169,6 +1170,7 @@ void do_handle_one_connection(THD *thd_arg)
while (!net->error && net->vio != 0 &&
!(thd->killed == THD::KILL_CONNECTION))
{
+ mysql_audit_release(thd);
if (do_command(thd))
break;
}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 748052b2985..b2871e6ed74 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -61,6 +61,7 @@
#include "sql_show.h"
#include "slave.h"
#include "rpl_mi.h"
+#include "sql_audit.h"
#ifndef EMBEDDED_LIBRARY
static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
@@ -2459,6 +2460,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
while (!thd->killed)
{
int error;
+ mysql_audit_release(thd);
#if defined(HAVE_BROKEN_COND_TIMEDWAIT)
error= mysql_cond_wait(&di->cond, &di->mutex);
#else
@@ -2540,6 +2542,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
mysql_unlock_tables(thd, lock);
ha_autocommit_or_rollback(thd, 0);
di->group_count=0;
+ mysql_audit_release(thd);
mysql_mutex_lock(&di->mutex);
}
if (di->tables_in_use)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index d8e82c3e0d1..a544781d9ec 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -28,6 +28,7 @@
#include "sp_cache.h"
#include "events.h"
#include "sql_trigger.h"
+#include "sql_audit.h"
#include "sql_prepare.h"
#include "probes_mysql.h"
#include "set_var.h"
@@ -1493,6 +1494,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* Free tables */
close_thread_tables(thd);
+ if (!thd->is_error() && !thd->killed_errno())
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
+
log_slow_statement(thd);
thd_proc_info(thd, "cleaning up");
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 0a7864631ae..442c342fd31 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -16,6 +16,7 @@
#include "sys_vars_shared.h"
#include <my_pthread.h>
#include <my_getopt.h>
+#include <mysql/plugin_audit.h>
#define REPORT_TO_LOG 1
#define REPORT_TO_USER 2
@@ -48,12 +49,16 @@ const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{ C_STRING_WITH_LEN("FTPARSER") },
{ C_STRING_WITH_LEN("DAEMON") },
{ C_STRING_WITH_LEN("INFORMATION SCHEMA") },
+ { C_STRING_WITH_LEN("AUDIT") },
{ C_STRING_WITH_LEN("REPLICATION") },
};
extern int initialize_schema_table(st_plugin_int *plugin);
extern int finalize_schema_table(st_plugin_int *plugin);
+extern int initialize_audit_plugin(st_plugin_int *plugin);
+extern int finalize_audit_plugin(st_plugin_int *plugin);
+
/*
The number of elements in both plugin_type_initialize and
plugin_type_deinitialize should equal to the number of plugins
@@ -61,12 +66,14 @@ extern int finalize_schema_table(st_plugin_int *plugin);
*/
plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
- 0,ha_initialize_handlerton,0,0,initialize_schema_table
+ 0,ha_initialize_handlerton,0,0,initialize_schema_table,
+ initialize_audit_plugin
};
plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
- 0,ha_finalize_handlerton,0,0,finalize_schema_table
+ 0,ha_finalize_handlerton,0,0,finalize_schema_table,
+ finalize_audit_plugin
};
#ifdef HAVE_DLOPEN
@@ -88,7 +95,8 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_FTPARSER_INTERFACE_VERSION,
MYSQL_DAEMON_INTERFACE_VERSION,
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
- MYSQL_REPLICATION_INTERFACE_VERSION,
+ MYSQL_AUDIT_INTERFACE_VERSION,
+ MYSQL_REPLICATION_INTERFACE_VERSION
};
static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
@@ -97,7 +105,8 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_FTPARSER_INTERFACE_VERSION,
MYSQL_DAEMON_INTERFACE_VERSION,
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
- MYSQL_REPLICATION_INTERFACE_VERSION,
+ MYSQL_AUDIT_INTERFACE_VERSION,
+ MYSQL_REPLICATION_INTERFACE_VERSION
};
/* support for Services */
@@ -1025,6 +1034,10 @@ static int plugin_initialize(struct st_plugin_int *plugin)
DBUG_ENTER("plugin_initialize");
mysql_mutex_assert_owner(&LOCK_plugin);
+ uint state= plugin->state;
+ DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED);
+
+ mysql_mutex_unlock(&LOCK_plugin);
if (plugin_type_initialize[plugin->plugin->type])
{
if ((*plugin_type_initialize[plugin->plugin->type])(plugin))
@@ -1043,8 +1056,7 @@ static int plugin_initialize(struct st_plugin_int *plugin)
goto err;
}
}
-
- plugin->state= PLUGIN_IS_READY;
+ state= PLUGIN_IS_READY; // plugin->init() succeeded
if (plugin->plugin->status_vars)
{
@@ -1063,7 +1075,8 @@ static int plugin_initialize(struct st_plugin_int *plugin)
if (add_status_vars(array)) // add_status_vars makes a copy
goto err;
#else
- add_status_vars(plugin->plugin->status_vars); // add_status_vars makes a copy
+ if (add_status_vars(plugin->plugin->status_vars))
+ goto err;
#endif /* FIX_LATER */
}
@@ -1086,10 +1099,13 @@ static int plugin_initialize(struct st_plugin_int *plugin)
ret= 0;
err:
+ mysql_mutex_lock(&LOCK_plugin);
+ plugin->state= state;
+
/* maintain the obsolete @@have_innodb variable */
if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB"))
- have_innodb= plugin->state & PLUGIN_IS_READY ? SHOW_OPTION_YES
- : SHOW_OPTION_DISABLED;
+ have_innodb= state & PLUGIN_IS_READY ? SHOW_OPTION_YES
+ : SHOW_OPTION_DISABLED;
DBUG_RETURN(ret);
}
@@ -1422,26 +1438,22 @@ end:
*/
static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
{
+ THD thd;
TABLE_LIST tables;
TABLE *table;
READ_RECORD read_record_info;
int error;
- THD *new_thd;
+ THD *new_thd= &thd;
#ifdef EMBEDDED_LIBRARY
bool table_exists;
#endif /* EMBEDDED_LIBRARY */
DBUG_ENTER("plugin_load");
- if (!(new_thd= new THD))
- {
- sql_print_error("Can't allocate memory for plugin structures");
- delete new_thd;
- DBUG_VOID_RETURN;
- }
new_thd->thread_stack= (char*) &tables;
new_thd->store_globals();
new_thd->db= my_strdup("mysql", MYF(0));
new_thd->db_length= 5;
+ bzero((char*) &thd.net, sizeof(thd.net));
bzero((uchar*)&tables, sizeof(tables));
tables.alias= tables.table_name= (char*)"plugin";
tables.lock_type= TL_READ;
@@ -1499,7 +1511,6 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
new_thd->version--; // Force close to free memory
end:
close_thread_tables(new_thd);
- delete new_thd;
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_VOID_RETURN;
@@ -1763,7 +1774,6 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl
}
else
{
- DBUG_ASSERT(tmp->state == PLUGIN_IS_UNINITIALIZED);
if (plugin_initialize(tmp))
{
my_error(ER_CANT_INITIALIZE_UDF, MYF(0), name->str,
@@ -2176,7 +2186,7 @@ static int check_func_set(THD *thd, struct st_mysql_sys_var *var,
{
if (value->val_int(value, (long long *)&result))
goto err;
- if (unlikely((result >= (ULL(1) << typelib->count)) &&
+ if (unlikely((result >= (1ULL << typelib->count)) &&
(typelib->count < sizeof(long)*8)))
goto err;
}
@@ -2261,10 +2271,6 @@ sys_var *find_sys_var(THD *thd, const char *str, uint length)
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_mutex_unlock(&LOCK_plugin);
- /*
- If the variable exists but the plugin it is associated with is not ready
- then the intern_plugin_lock did not raise an error, so we do it here.
- */
if (!var)
my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
DBUG_RETURN(var);
@@ -2727,7 +2733,7 @@ TYPELIB* sys_var_pluginvar::plugin_var_typelib(void)
default:
return NULL;
}
- return NULL;
+ return NULL; /* Keep compiler happy */
}
@@ -2888,7 +2894,7 @@ static void plugin_opt_set_limits(struct my_option *options,
options->typelib= ((sysvar_set_t*) opt)->typelib;
options->def_value= ((sysvar_set_t*) opt)->def_val;
options->min_value= options->block_size= 0;
- options->max_value= (ULL(1) << options->typelib->count) - 1;
+ options->max_value= (1ULL << options->typelib->count) - 1;
break;
case PLUGIN_VAR_BOOL:
options->var_type= GET_BOOL;
@@ -2930,7 +2936,7 @@ static void plugin_opt_set_limits(struct my_option *options,
options->typelib= ((thdvar_set_t*) opt)->typelib;
options->def_value= ((thdvar_set_t*) opt)->def_val;
options->min_value= options->block_size= 0;
- options->max_value= (ULL(1) << options->typelib->count) - 1;
+ options->max_value= (1ULL << options->typelib->count) - 1;
break;
case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
options->var_type= GET_BOOL;