diff options
-rw-r--r-- | include/Makefile.am | 3 | ||||
-rw-r--r-- | include/mysql/plugin.h | 220 | ||||
-rw-r--r-- | include/mysql/plugin.h.pp | 10 | ||||
-rw-r--r-- | include/mysql/plugin_audit.h | 97 | ||||
-rw-r--r-- | include/mysql/plugin_ftparser.h | 211 | ||||
-rw-r--r-- | libmysqld/CMakeLists.txt | 3 | ||||
-rw-r--r-- | libmysqld/Makefile.am | 4 | ||||
-rw-r--r-- | plugin/audit_null/Makefile.am | 32 | ||||
-rw-r--r-- | plugin/audit_null/audit_null.c | 130 | ||||
-rw-r--r-- | plugin/audit_null/plug.in | 4 | ||||
-rw-r--r-- | sql/CMakeLists.txt | 2 | ||||
-rw-r--r-- | sql/Makefile.am | 4 | ||||
-rw-r--r-- | sql/event_queue.cc | 8 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 12 | ||||
-rw-r--r-- | sql/log.cc | 17 | ||||
-rw-r--r-- | sql/mysqld.cc | 37 | ||||
-rw-r--r-- | sql/sql_audit.cc | 477 | ||||
-rw-r--r-- | sql/sql_audit.h | 131 | ||||
-rw-r--r-- | sql/sql_class.cc | 5 | ||||
-rw-r--r-- | sql/sql_class.h | 15 | ||||
-rw-r--r-- | sql/sql_connect.cc | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 4 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 58 | ||||
-rw-r--r-- | strings/longlong2str_asm.c | 3 |
25 files changed, 1220 insertions, 272 deletions
diff --git a/include/Makefile.am b/include/Makefile.am index 579eff1afad..5e30342c7eb 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -19,7 +19,8 @@ BUILT_SOURCES = $(HEADERS_GEN_MAKE) link_sources probes_mysql_nodtrace.h HEADERS_GEN_CONFIGURE = mysql_version.h HEADERS_GEN_MAKE = my_config.h HEADERS_ABI = mysql.h mysql_com.h mysql_time.h \ - my_list.h my_alloc.h typelib.h mysql/plugin.h + my_list.h my_alloc.h typelib.h mysql/plugin.h \ + mysql/plugin_audit.h mysql/plugin_ftparser.h pkginclude_HEADERS = $(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \ my_xml.h mysql_embed.h mysql/services.h \ mysql/service_my_snprintf.h mysql/service_thd_alloc.h \ diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index cd5b6f2fe2c..f6ba69bf8eb 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -66,8 +66,9 @@ typedef struct st_mysql_xid MYSQL_XID; #define MYSQL_FTPARSER_PLUGIN 2 /* Full-text parser plugin */ #define MYSQL_DAEMON_PLUGIN 3 /* The daemon/raw plugin type */ #define MYSQL_INFORMATION_SCHEMA_PLUGIN 4 /* The I_S plugin type */ -#define MYSQL_REPLICATION_PLUGIN 5 /* The replication plugin type */ -#define MYSQL_MAX_PLUGIN_TYPE_NUM 6 /* The number of plugin types */ +#define MYSQL_AUDIT_PLUGIN 5 /* The Audit plugin type */ +#define MYSQL_REPLICATION_PLUGIN 6 /* The replication plugin type */ +#define MYSQL_MAX_PLUGIN_TYPE_NUM 7 /* The number of plugin types */ /* We use the following strings to define licenses for plugins */ #define PLUGIN_LICENSE_PROPRIETARY 0 @@ -403,205 +404,43 @@ struct st_mysql_plugin /************************************************************************* API for Full-text parser plugin. (MYSQL_FTPARSER_PLUGIN) */ +#include "plugin_ftparser.h" -#define MYSQL_FTPARSER_INTERFACE_VERSION 0x0100 - -/* Parsing modes. Set in MYSQL_FTPARSER_PARAM::mode */ -enum enum_ftparser_mode -{ -/* - Fast and simple mode. This mode is used for indexing, and natural - language queries. - - The parser is expected to return only those words that go into the - index. Stopwords or too short/long words should not be returned. The - 'boolean_info' argument of mysql_add_word() does not have to be set. -*/ - MYSQL_FTPARSER_SIMPLE_MODE= 0, - -/* - Parse with stopwords mode. This mode is used in boolean searches for - "phrase matching." - - The parser is not allowed to ignore words in this mode. Every word - should be returned, including stopwords and words that are too short - or long. The 'boolean_info' argument of mysql_add_word() does not - have to be set. +/************************************************************************* + API for Storage Engine plugin. (MYSQL_DAEMON_PLUGIN) */ - MYSQL_FTPARSER_WITH_STOPWORDS= 1, -/* - Parse in boolean mode. This mode is used to parse a boolean query string. - - The parser should provide a valid MYSQL_FTPARSER_BOOLEAN_INFO - structure in the 'boolean_info' argument to mysql_add_word(). - Usually that means that the parser should recognize boolean operators - in the parsing stream and set appropriate fields in - MYSQL_FTPARSER_BOOLEAN_INFO structure accordingly. As for - MYSQL_FTPARSER_WITH_STOPWORDS mode, no word should be ignored. - Instead, use FT_TOKEN_STOPWORD for the token type of such a word. -*/ - MYSQL_FTPARSER_FULL_BOOLEAN_INFO= 2 -}; +/* handlertons of different MySQL releases are incompatible */ +#define MYSQL_DAEMON_INTERFACE_VERSION (MYSQL_VERSION_ID << 8) /* - Token types for boolean mode searching (used for the type member of - MYSQL_FTPARSER_BOOLEAN_INFO struct) - - FT_TOKEN_EOF: End of data. - FT_TOKEN_WORD: Regular word. - FT_TOKEN_LEFT_PAREN: Left parenthesis (start of group/sub-expression). - FT_TOKEN_RIGHT_PAREN: Right parenthesis (end of group/sub-expression). - FT_TOKEN_STOPWORD: Stopword. + Here we define only the descriptor structure, that is referred from + st_mysql_plugin. */ -enum enum_ft_token_type +struct st_mysql_daemon { - FT_TOKEN_EOF= 0, - FT_TOKEN_WORD= 1, - FT_TOKEN_LEFT_PAREN= 2, - FT_TOKEN_RIGHT_PAREN= 3, - FT_TOKEN_STOPWORD= 4 + int interface_version; }; -/* - This structure is used in boolean search mode only. It conveys - boolean-mode metadata to the MySQL search engine for every word in - the search query. A valid instance of this structure must be filled - in by the plugin parser and passed as an argument in the call to - mysql_add_word (the callback function in the MYSQL_FTPARSER_PARAM - structure) when a query is parsed in boolean mode. - - type: The token type. Should be one of the enum_ft_token_type values. - - yesno: Whether the word must be present for a match to occur: - >0 Must be present - <0 Must not be present - 0 Neither; the word is optional but its presence increases the relevance - With the default settings of the ft_boolean_syntax system variable, - >0 corresponds to the '+' operator, <0 corrresponds to the '-' operator, - and 0 means neither operator was used. - - weight_adjust: A weighting factor that determines how much a match - for the word counts. Positive values increase, negative - decrease the - relative word's importance in the query. - - wasign: The sign of the word's weight in the query. If it's non-negative - the match for the word will increase document relevance, if it's - negative - decrease (the word becomes a "noise word", the less of it the - better). - - trunc: Corresponds to the '*' operator in the default setting of the - ft_boolean_syntax system variable. -*/ - -typedef struct st_mysql_ftparser_boolean_info -{ - enum enum_ft_token_type type; - int yesno; - int weight_adjust; - char wasign; - char trunc; - /* These are parser state and must be removed. */ - char prev; - char *quot; -} MYSQL_FTPARSER_BOOLEAN_INFO; - -/* - The following flag means that buffer with a string (document, word) - may be overwritten by the caller before the end of the parsing (that is - before st_mysql_ftparser::deinit() call). If one needs the string - to survive between two successive calls of the parsing function, she - needs to save a copy of it. The flag may be set by MySQL before calling - st_mysql_ftparser::parse(), or it may be set by a plugin before calling - st_mysql_ftparser_param::mysql_parse() or - st_mysql_ftparser_param::mysql_add_word(). -*/ -#define MYSQL_FTFLAGS_NEED_COPY 1 - -/* - An argument of the full-text parser plugin. This structure is - filled in by MySQL server and passed to the parsing function of the - plugin as an in/out parameter. - - mysql_parse: A pointer to the built-in parser implementation of the - server. It's set by the server and can be used by the parser plugin - to invoke the MySQL default parser. If plugin's role is to extract - textual data from .doc, .pdf or .xml content, it might extract - plaintext from the content, and then pass the text to the default - MySQL parser to be parsed. - - mysql_add_word: A server callback to add a new word. When parsing - a document, the server sets this to point at a function that adds - the word to MySQL full-text index. When parsing a search query, - this function will add the new word to the list of words to search - for. The boolean_info argument can be NULL for all cases except - when mode is MYSQL_FTPARSER_FULL_BOOLEAN_INFO. - ftparser_state: A generic pointer. The plugin can set it to point - to information to be used internally for its own purposes. - - mysql_ftparam: This is set by the server. It is used by MySQL functions - called via mysql_parse() and mysql_add_word() callback. The plugin - should not modify it. - - cs: Information about the character set of the document or query string. - - doc: A pointer to the document or query string to be parsed. - - length: Length of the document or query string, in bytes. - - flags: See MYSQL_FTFLAGS_* constants above. - - mode: The parsing mode. With boolean operators, with stopwords, or - nothing. See enum_ftparser_mode above. +/************************************************************************* + API for I_S plugin. (MYSQL_INFORMATION_SCHEMA_PLUGIN) */ -typedef struct st_mysql_ftparser_param -{ - int (*mysql_parse)(struct st_mysql_ftparser_param *, - char *doc, int doc_len); - int (*mysql_add_word)(struct st_mysql_ftparser_param *, - char *word, int word_len, - MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info); - void *ftparser_state; - void *mysql_ftparam; - struct charset_info_st *cs; - char *doc; - int length; - int flags; - enum enum_ftparser_mode mode; -} MYSQL_FTPARSER_PARAM; +/* handlertons of different MySQL releases are incompatible */ +#define MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION (MYSQL_VERSION_ID << 8) /* - Full-text parser descriptor. - - interface_version is, e.g., MYSQL_FTPARSER_INTERFACE_VERSION. - The parsing, initialization, and deinitialization functions are - invoked per SQL statement for which the parser is used. + Here we define only the descriptor structure, that is referred from + st_mysql_plugin. */ -struct st_mysql_ftparser +struct st_mysql_information_schema { int interface_version; - int (*parse)(MYSQL_FTPARSER_PARAM *param); - int (*init)(MYSQL_FTPARSER_PARAM *param); - int (*deinit)(MYSQL_FTPARSER_PARAM *param); }; -/************************************************************************* - API for Storage Engine plugin. (MYSQL_DAEMON_PLUGIN) -*/ - -/* handlertons of different MySQL releases are incompatible */ -#define MYSQL_DAEMON_INTERFACE_VERSION (MYSQL_VERSION_ID << 8) - -/************************************************************************* - API for I_S plugin. (MYSQL_INFORMATION_SCHEMA_PLUGIN) -*/ - -/* handlertons of different MySQL releases are incompatible */ -#define MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION (MYSQL_VERSION_ID << 8) /************************************************************************* API for Storage Engine plugin. (MYSQL_STORAGE_ENGINE_PLUGIN) @@ -623,25 +462,6 @@ struct st_mysql_storage_engine struct handlerton; -/* - Here we define only the descriptor structure, that is referred from - st_mysql_plugin. -*/ - -struct st_mysql_daemon -{ - int interface_version; -}; - -/* - Here we define only the descriptor structure, that is referred from - st_mysql_plugin. -*/ - -struct st_mysql_information_schema -{ - int interface_version; -}; /* API for Replication plugin. (MYSQL_REPLICATION_PLUGIN) @@ -655,7 +475,7 @@ struct st_mysql_information_schema int interface_version; }; -/* +/************************************************************************* st_mysql_value struct for reading values from mysqld. Used by server variables framework to parse user-provided values. Will be used for arguments when implementing UDFs. diff --git a/include/mysql/plugin.h.pp b/include/mysql/plugin.h.pp index 709430db3fb..5dad31bd008 100644 --- a/include/mysql/plugin.h.pp +++ b/include/mysql/plugin.h.pp @@ -76,6 +76,8 @@ struct st_mysql_plugin struct st_mysql_sys_var **system_vars; void * __reserved1; }; +#include "plugin_ftparser.h" +#include "plugin.h" enum enum_ftparser_mode { MYSQL_FTPARSER_SIMPLE_MODE= 0, @@ -122,19 +124,19 @@ struct st_mysql_ftparser int (*init)(MYSQL_FTPARSER_PARAM *param); int (*deinit)(MYSQL_FTPARSER_PARAM *param); }; -struct st_mysql_storage_engine +struct st_mysql_daemon { int interface_version; }; -struct handlerton; -struct st_mysql_daemon +struct st_mysql_information_schema { int interface_version; }; -struct st_mysql_information_schema +struct st_mysql_storage_engine { int interface_version; }; +struct handlerton; struct Mysql_replication { int interface_version; }; diff --git a/include/mysql/plugin_audit.h b/include/mysql/plugin_audit.h new file mode 100644 index 00000000000..8932767075d --- /dev/null +++ b/include/mysql/plugin_audit.h @@ -0,0 +1,97 @@ +/* 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 */ + +#ifndef _my_audit_h +#define _my_audit_h + +/************************************************************************* + API for Audit plugin. (MYSQL_AUDIT_PLUGIN) +*/ + +#include "plugin.h" + +#define MYSQL_AUDIT_CLASS_MASK_SIZE 1 + +#define MYSQL_AUDIT_INTERFACE_VERSION 0x0100 + +/* + The first word in every event class struct indicates the specific + class of the event. +*/ +struct mysql_event +{ + int event_class; +}; + + +/************************************************************************* + AUDIT CLASS : GENERAL + + LOG events occurs before emitting to the general query log. + ERROR events occur before transmitting errors to the user. + RESULT events occur after transmitting a resultset to the user. +*/ + +#define MYSQL_AUDIT_GENERAL_CLASS 0 +#define MYSQL_AUDIT_GENERAL_CLASSMASK (1 << MYSQL_AUDIT_GENERAL_CLASS) +#define MYSQL_AUDIT_GENERAL_LOG 0 +#define MYSQL_AUDIT_GENERAL_ERROR 1 +#define MYSQL_AUDIT_GENERAL_RESULT 2 + +struct mysql_event_general +{ + int event_class; + int general_error_code; + unsigned long general_thread_id; + const char *general_user; + unsigned int general_user_length; + const char *general_command; + unsigned int general_command_length; + const char *general_query; + unsigned int general_query_length; + struct charset_info_st *general_charset; + unsigned long long general_time; + unsigned long long general_rows; +}; + + +/************************************************************************* + Here we define the descriptor structure, that is referred from + st_mysql_plugin. + + release_thd() event occurs when the event class consumer is to be + disassociated from the specified THD. This would typically occur + before some operation which may require sleeping - such as when + waiting for the next query from the client. + + event_notify() is invoked whenever an event occurs which is of any + class for which the plugin has interest. The first word of the + mysql_event argument indicates the specific event class and the + remainder of the structure is as required for that class. + + class_mask is an array of bits used to indicate what event classes + that this plugin wants to receive. +*/ + +struct st_mysql_audit +{ + int interface_version; + void (*release_thd)(MYSQL_THD); + void (*event_notify)(MYSQL_THD, const struct mysql_event *); + unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; +}; + + +#endif diff --git a/include/mysql/plugin_ftparser.h b/include/mysql/plugin_ftparser.h new file mode 100644 index 00000000000..7f9bde3a6a0 --- /dev/null +++ b/include/mysql/plugin_ftparser.h @@ -0,0 +1,211 @@ +/* Copyright (C) 2005 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 */ + +#ifndef _my_plugin_ftparser_h +#define _my_plugin_ftparser_h +#include "plugin.h" + +/************************************************************************* + API for Full-text parser plugin. (MYSQL_FTPARSER_PLUGIN) +*/ + +#define MYSQL_FTPARSER_INTERFACE_VERSION 0x0100 + +/* Parsing modes. Set in MYSQL_FTPARSER_PARAM::mode */ +enum enum_ftparser_mode +{ +/* + Fast and simple mode. This mode is used for indexing, and natural + language queries. + + The parser is expected to return only those words that go into the + index. Stopwords or too short/long words should not be returned. The + 'boolean_info' argument of mysql_add_word() does not have to be set. +*/ + MYSQL_FTPARSER_SIMPLE_MODE= 0, + +/* + Parse with stopwords mode. This mode is used in boolean searches for + "phrase matching." + + The parser is not allowed to ignore words in this mode. Every word + should be returned, including stopwords and words that are too short + or long. The 'boolean_info' argument of mysql_add_word() does not + have to be set. +*/ + MYSQL_FTPARSER_WITH_STOPWORDS= 1, + +/* + Parse in boolean mode. This mode is used to parse a boolean query string. + + The parser should provide a valid MYSQL_FTPARSER_BOOLEAN_INFO + structure in the 'boolean_info' argument to mysql_add_word(). + Usually that means that the parser should recognize boolean operators + in the parsing stream and set appropriate fields in + MYSQL_FTPARSER_BOOLEAN_INFO structure accordingly. As for + MYSQL_FTPARSER_WITH_STOPWORDS mode, no word should be ignored. + Instead, use FT_TOKEN_STOPWORD for the token type of such a word. +*/ + MYSQL_FTPARSER_FULL_BOOLEAN_INFO= 2 +}; + +/* + Token types for boolean mode searching (used for the type member of + MYSQL_FTPARSER_BOOLEAN_INFO struct) + + FT_TOKEN_EOF: End of data. + FT_TOKEN_WORD: Regular word. + FT_TOKEN_LEFT_PAREN: Left parenthesis (start of group/sub-expression). + FT_TOKEN_RIGHT_PAREN: Right parenthesis (end of group/sub-expression). + FT_TOKEN_STOPWORD: Stopword. +*/ + +enum enum_ft_token_type +{ + FT_TOKEN_EOF= 0, + FT_TOKEN_WORD= 1, + FT_TOKEN_LEFT_PAREN= 2, + FT_TOKEN_RIGHT_PAREN= 3, + FT_TOKEN_STOPWORD= 4 +}; + +/* + This structure is used in boolean search mode only. It conveys + boolean-mode metadata to the MySQL search engine for every word in + the search query. A valid instance of this structure must be filled + in by the plugin parser and passed as an argument in the call to + mysql_add_word (the callback function in the MYSQL_FTPARSER_PARAM + structure) when a query is parsed in boolean mode. + + type: The token type. Should be one of the enum_ft_token_type values. + + yesno: Whether the word must be present for a match to occur: + >0 Must be present + <0 Must not be present + 0 Neither; the word is optional but its presence increases the relevance + With the default settings of the ft_boolean_syntax system variable, + >0 corresponds to the '+' operator, <0 corrresponds to the '-' operator, + and 0 means neither operator was used. + + weight_adjust: A weighting factor that determines how much a match + for the word counts. Positive values increase, negative - decrease the + relative word's importance in the query. + + wasign: The sign of the word's weight in the query. If it's non-negative + the match for the word will increase document relevance, if it's + negative - decrease (the word becomes a "noise word", the less of it the + better). + + trunc: Corresponds to the '*' operator in the default setting of the + ft_boolean_syntax system variable. +*/ + +typedef struct st_mysql_ftparser_boolean_info +{ + enum enum_ft_token_type type; + int yesno; + int weight_adjust; + char wasign; + char trunc; + /* These are parser state and must be removed. */ + char prev; + char *quot; +} MYSQL_FTPARSER_BOOLEAN_INFO; + +/* + The following flag means that buffer with a string (document, word) + may be overwritten by the caller before the end of the parsing (that is + before st_mysql_ftparser::deinit() call). If one needs the string + to survive between two successive calls of the parsing function, she + needs to save a copy of it. The flag may be set by MySQL before calling + st_mysql_ftparser::parse(), or it may be set by a plugin before calling + st_mysql_ftparser_param::mysql_parse() or + st_mysql_ftparser_param::mysql_add_word(). +*/ +#define MYSQL_FTFLAGS_NEED_COPY 1 + +/* + An argument of the full-text parser plugin. This structure is + filled in by MySQL server and passed to the parsing function of the + plugin as an in/out parameter. + + mysql_parse: A pointer to the built-in parser implementation of the + server. It's set by the server and can be used by the parser plugin + to invoke the MySQL default parser. If plugin's role is to extract + textual data from .doc, .pdf or .xml content, it might extract + plaintext from the content, and then pass the text to the default + MySQL parser to be parsed. + + mysql_add_word: A server callback to add a new word. When parsing + a document, the server sets this to point at a function that adds + the word to MySQL full-text index. When parsing a search query, + this function will add the new word to the list of words to search + for. The boolean_info argument can be NULL for all cases except + when mode is MYSQL_FTPARSER_FULL_BOOLEAN_INFO. + + ftparser_state: A generic pointer. The plugin can set it to point + to information to be used internally for its own purposes. + + mysql_ftparam: This is set by the server. It is used by MySQL functions + called via mysql_parse() and mysql_add_word() callback. The plugin + should not modify it. + + cs: Information about the character set of the document or query string. + + doc: A pointer to the document or query string to be parsed. + + length: Length of the document or query string, in bytes. + + flags: See MYSQL_FTFLAGS_* constants above. + + mode: The parsing mode. With boolean operators, with stopwords, or + nothing. See enum_ftparser_mode above. +*/ + +typedef struct st_mysql_ftparser_param +{ + int (*mysql_parse)(struct st_mysql_ftparser_param *, + char *doc, int doc_len); + int (*mysql_add_word)(struct st_mysql_ftparser_param *, + char *word, int word_len, + MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info); + void *ftparser_state; + void *mysql_ftparam; + struct charset_info_st *cs; + char *doc; + int length; + int flags; + enum enum_ftparser_mode mode; +} MYSQL_FTPARSER_PARAM; + +/* + Full-text parser descriptor. + + interface_version is, e.g., MYSQL_FTPARSER_INTERFACE_VERSION. + The parsing, initialization, and deinitialization functions are + invoked per SQL statement for which the parser is used. +*/ + +struct st_mysql_ftparser +{ + int interface_version; + int (*parse)(MYSQL_FTPARSER_PARAM *param); + int (*init)(MYSQL_FTPARSER_PARAM *param); + int (*deinit)(MYSQL_FTPARSER_PARAM *param); +}; + + +#endif + diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 2eeb092b759..cf6c9d19e68 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -132,7 +132,8 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/strfunc.cc ../sql/table.cc ../sql/thr_malloc.cc ../sql/time.cc ../sql/tztime.cc ../sql/uniques.cc ../sql/unireg.cc ../sql/partition_info.cc ../sql/sql_connect.cc - ../sql/scheduler.cc ../sql/event_parse_data.cc + ../sql/scheduler.cc ../sql/sql_audit.cc + ../sql/event_parse_data.cc ../sql/sql_signal.cc ../sql/rpl_handler.cc ${GEN_SOURCES} ${LIB_SOURCES}) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 2dde00ad38a..a0af08f51d6 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -78,8 +78,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ debug_sync.cc \ sql_tablespace.cc \ rpl_injector.cc my_user.c partition_info.cc \ - sql_servers.cc event_parse_data.cc sql_signal.cc \ - rpl_handler.cc keycaches.cc + sql_servers.cc sql_audit.cc event_parse_data.cc \ + sql_signal.cc rpl_handler.cc keycaches.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources) diff --git a/plugin/audit_null/Makefile.am b/plugin/audit_null/Makefile.am new file mode 100644 index 00000000000..d57a72f8c18 --- /dev/null +++ b/plugin/audit_null/Makefile.am @@ -0,0 +1,32 @@ +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#Makefile.am example for a plugin + +pkgplugindir= $(pkglibdir)/plugin + +AM_CPPFLAGS = -I$(top_srcdir)/include + +EXTRA_LTLIBRARIES= adt_null.la +pkgplugin_LTLIBRARIES= @plugin_audit_null_shared_target@ +adt_null_la_LDFLAGS= -module -rpath $(pkgplugindir) +adt_null_la_CPPFLAGS= $(AM_CPPFLAGS) -DMYSQL_DYNAMIC_PLUGIN +adt_null_la_SOURCES= audit_null.c + +EXTRA_LIBRARIES= libadtnull.a +noinst_LIBRARIES= @plugin_audit_null_static_target@ +libadtnull_a_SOURCES= audit_null.c + +EXTRA_DIST= plug.in diff --git a/plugin/audit_null/audit_null.c b/plugin/audit_null/audit_null.c new file mode 100644 index 00000000000..52a9df08cf4 --- /dev/null +++ b/plugin/audit_null/audit_null.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2006-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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include <stdio.h> +#include <mysql/plugin.h> +#include <mysql/plugin_audit.h> + +#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) +#define __attribute__(A) +#endif + +static volatile int number_of_calls; /* for SHOW STATUS, see below */ + + +/* + Initialize the plugin at server start or plugin installation. + + SYNOPSIS + audit_null_plugin_init() + + DESCRIPTION + Does nothing. + + RETURN VALUE + 0 success + 1 failure (cannot happen) +*/ + +static int audit_null_plugin_init(void *arg __attribute__((unused))) +{ + number_of_calls= 0; + return(0); +} + + +/* + Terminate the plugin at server shutdown or plugin deinstallation. + + SYNOPSIS + audit_null_plugin_deinit() + Does nothing. + + RETURN VALUE + 0 success + 1 failure (cannot happen) + +*/ + +static int audit_null_plugin_deinit(void *arg __attribute__((unused))) +{ + printf("audit_null was invoked %u times\n", number_of_calls); + return(0); +} + + +/* + Foo + + SYNOPSIS + audit_null_notify() + thd connection context + + DESCRIPTION +*/ + +static void audit_null_notify(MYSQL_THD thd __attribute__((unused)), + const struct mysql_event *event + __attribute__((unused))) +{ + /* prone to races, oh well */ + number_of_calls++; +} + + +/* + Plugin type-specific descriptor +*/ + +static struct st_mysql_audit audit_null_descriptor= +{ + MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */ + NULL, /* release_thd function */ + audit_null_notify, /* notify function */ + { (unsigned long) -1 } /* class mask */ +}; + +/* + Plugin status variables for SHOW STATUS +*/ + +static struct st_mysql_show_var simple_status[]= +{ + {"audit_null_called", (char *)&number_of_calls, SHOW_INT}, + {0,0,0} +}; + + +/* + Plugin library descriptor +*/ + +mysql_declare_plugin(audit_null) +{ + MYSQL_AUDIT_PLUGIN, /* type */ + &audit_null_descriptor, /* descriptor */ + "NULL_AUDIT", /* name */ + "MySQL AB", /* author */ + "Simple NULL Audit", /* description */ + PLUGIN_LICENSE_GPL, + audit_null_plugin_init, /* init function (when loaded) */ + audit_null_plugin_deinit, /* deinit function (when unloaded) */ + 0x0001, /* version */ + simple_status, /* status variables */ + NULL, /* system variables */ + NULL +} +mysql_declare_plugin_end; + diff --git a/plugin/audit_null/plug.in b/plugin/audit_null/plug.in new file mode 100644 index 00000000000..15b1a48b408 --- /dev/null +++ b/plugin/audit_null/plug.in @@ -0,0 +1,4 @@ +MYSQL_PLUGIN(audit_null, [NULL Audit Plug-in], + [Simple black-hole Audit example plug-in]) +MYSQL_PLUGIN_DYNAMIC(audit_null, [adt_null.la]) +MYSQL_PLUGIN_STATIC(audit_null, [libadtnull.a]) 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; diff --git a/strings/longlong2str_asm.c b/strings/longlong2str_asm.c index 70fe5d7bd48..e5628043da6 100644 --- a/strings/longlong2str_asm.c +++ b/strings/longlong2str_asm.c @@ -28,5 +28,6 @@ extern char *longlong2str_with_dig_vector(longlong val,char *dst,int radix, char *ll2str(longlong val,char *dst,int radix, int upcase) { - return longlong2str_with_dig_vector(val, dst, radix, _dig_vec_upper); + return longlong2str_with_dig_vector(val, dst, radix, + upcase ? _dig_vec_upper : _dig_vec_lower); } |