From 4b4777ab63522b2bdd1f98ad03558a5b1da53dc0 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 3 Jan 2016 12:48:55 +0200 Subject: [PATCH 01/11] Backported fix for ccache Fixed compiler warnings Added --big-test to tokudb change_column_char & change_column_bin --- BUILD/SETUP.sh | 15 +++++++++++++-- storage/connect/json.cpp | 2 +- storage/connect/jsonudf.cpp | 2 +- .../mysql-test/tokudb/t/change_column_bin.py | 1 + .../mysql-test/tokudb/t/change_column_bin.test | 1 + .../mysql-test/tokudb/t/change_column_char.py | 1 + .../mysql-test/tokudb/t/change_column_char.test | 1 + 7 files changed, 19 insertions(+), 4 deletions(-) diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index bde095b0aa8..858f42320b8 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -31,6 +31,7 @@ Usage: $0 [-h|-n] [configure-options] -h, --help Show this help message. -n, --just-print Don't actually run any commands; just print them. -c, --just-configure Stop after running configure. + Combined with --just-print shows configure options. --extra-configs=xxx Add this to configure options --extra-flags=xxx Add this C and CXX flags --extra-cflags=xxx Add this to C flags @@ -257,6 +258,10 @@ fi # (http://samba.org/ccache) is installed, use it. # We use 'grep' and hope 'grep' will work as expected # (returns 0 if finds lines) + +# As cmake doesn't like CC and CXX with a space, use symlinks from +# /usr/lib64/ccache if they exits. + if test "$USING_GCOV" != "1" then # Not using gcov; Safe to use ccache @@ -265,8 +270,14 @@ fi if ccache -V > /dev/null 2>&1 && test "$CCACHE_GCOV_VERSION_ENABLED" = "1" then - echo "$CC" | grep "ccache" > /dev/null || CC="ccache $CC" - echo "$CXX" | grep "ccache" > /dev/null || CXX="ccache $CXX" + if test -x /usr/lib64/ccache/gcc + then + CC=/usr/lib64/ccache/gcc + fi + if test -x /usr/lib64/ccache/g++ + then + CXX=/usr/lib64/ccache/g++ + fi fi # gcov diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp index fc7edb84c0f..aa677342fac 100644 --- a/storage/connect/json.cpp +++ b/storage/connect/json.cpp @@ -594,7 +594,7 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty) if (fs) { fputs(EL, fs); fclose(fs); - str = (err) ? NULL : "Ok"; + str = (err) ? NULL : (char*) "Ok"; } else if (!err) { str = ((JOUTSTR*)jp)->Strp; jp->WriteChr('\0'); diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index 40685ae0f0e..215562af46e 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -3365,7 +3365,7 @@ my_bool jsoncontains_init(UDF_INIT *initid, UDF_ARGS *args, char *message) long long jsoncontains(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *res_length, char *is_null, char *error) { - char *p, res[256]; + char *p __attribute__((unused)), res[256]; long long n; unsigned long reslen; diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py index 0ddee301d1b..b94a80ec7be 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py @@ -24,6 +24,7 @@ def gen_test(n): def main(): print "# this test is generated by change_bin.py" print "# test binary expansion is hot" + print "--source include/big_test.inc" print "--disable_warnings" print "DROP TABLE IF EXISTS t,ti;" print "--enable_warnings" diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test index 0365c155302..6053a263cf0 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test @@ -1,6 +1,7 @@ source include/have_tokudb.inc; # this test is generated by change_bin.py # test binary expansion is hot +--source include/big_test.inc --disable_warnings DROP TABLE IF EXISTS t,ti; --enable_warnings diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_char.py b/storage/tokudb/mysql-test/tokudb/t/change_column_char.py index 99d99d1c017..7481d9092f2 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_char.py +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_char.py @@ -24,6 +24,7 @@ def gen_test(n): def main(): print "# this test is generated by change_char.py" print "# test char expansion" + print "--source include/big_test.inc" print "--disable_warnings" print "DROP TABLE IF EXISTS t,ti;" print "--enable_warnings" diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_char.test b/storage/tokudb/mysql-test/tokudb/t/change_column_char.test index 8a0b0a9abd8..977420ad07e 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_char.test +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_char.test @@ -1,6 +1,7 @@ source include/have_tokudb.inc; # this test is generated by change_char.py # test char expansion +--source include/big_test.inc --disable_warnings DROP TABLE IF EXISTS t,ti; --enable_warnings From 661a6d89065390ca1e9b4be05219b75f850ed290 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 3 Jan 2016 13:20:07 +0200 Subject: [PATCH 02/11] Cleanup of slave code: - Added testing if connection is killed to shortcut reading of connection data This will allow us later in 10.2 to do a cleaner shutdown of slaves (less errors in the log) - Add new status variables: Slaves_connected, Slaves_running and Slave_connections. - Use MYSQL_SLAVE_NOT_RUN instead of 0 with slave_running. - Don't print obvious extra warnings to the error log when slave is shut down normally. --- sql/mysqld.cc | 35 +++++++++++++++++++++++++++++++++++ sql/net_serv.cc | 20 +++++++++++++++----- sql/rpl_mi.cc | 16 +++++++++------- sql/rpl_mi.h | 2 +- sql/rpl_rli.cc | 2 +- sql/slave.cc | 13 ++++++------- sql/sql_class.cc | 9 ++++++++- sql/sql_class.h | 1 + sql/sql_parse.cc | 1 + 9 files changed, 77 insertions(+), 22 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e3e331932d0..c6ac3f7a4a2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7299,6 +7299,38 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff) } +/* How many slaves are connected to this master */ + +static int show_slaves_connected(THD *thd, SHOW_VAR *var, char *buff) +{ + + var->type= SHOW_LONGLONG; + var->value= buff; + mysql_mutex_lock(&LOCK_slave_list); + + *((longlong *)buff)= slave_list.records; + + mysql_mutex_unlock(&LOCK_slave_list); + return 0; +} + + +/* How many masters this slave is connected to */ + + +static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) +{ + var->type= SHOW_LONGLONG; + var->value= buff; + mysql_mutex_lock(&LOCK_active_mi); + + *((longlong *)buff)= master_info_index->any_slave_sql_running(); + + mysql_mutex_unlock(&LOCK_active_mi); + return 0; +} + + static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff) { Master_info *mi= NULL; @@ -7950,6 +7982,9 @@ SHOW_VAR status_vars[]= { {"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count_), SHOW_LONG_STATUS}, {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_INT}, #ifdef HAVE_REPLICATION + {"Slaves_connected", (char*) &show_slaves_connected, SHOW_SIMPLE_FUNC }, + {"Slaves_running", (char*) &show_slaves_running, SHOW_SIMPLE_FUNC }, + {"Slave_connections", (char*) offsetof(STATUS_VAR, com_register_slave), SHOW_LONG_STATUS}, {"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_SIMPLE_FUNC}, {"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_SIMPLE_FUNC}, {"Slave_retried_transactions",(char*)&slave_retried_transactions, SHOW_LONG}, diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 0ce0fa93f99..91a17606d68 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -34,6 +34,7 @@ HFTODO this must be hidden if we don't want client capabilities in embedded library */ + #include #include #include @@ -107,13 +108,12 @@ extern void query_cache_insert(const char *packet, ulong length, unsigned pkt_nr); #endif // HAVE_QUERY_CACHE #define update_statistics(A) A -#else -#define update_statistics(A) -#endif - -#ifdef MYSQL_SERVER +extern my_bool thd_net_is_killed(); /* Additional instrumentation hooks for the server */ #include "mysql_com_server.h" +#else +#define update_statistics(A) +#define thd_net_is_killed() 0 #endif #define TEST_BLOCKING 8 @@ -875,6 +875,16 @@ my_real_read(NET *net, size_t *complen, DBUG_PRINT("info",("vio_read returned %ld errno: %d", (long) length, vio_errno(net->vio))); + + if (i== 0 && thd_net_is_killed()) + { + len= packet_error; + net->error= 0; + net->last_errno= ER_CONNECTION_KILLED; + MYSQL_SERVER_my_error(net->last_errno, MYF(0)); + goto end; + } + #if !defined(__WIN__) && defined(MYSQL_SERVER) /* We got an error that there was no data on the socket. We now set up diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index ae6cc1ac0a2..a7ee3f3f45e 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -35,7 +35,8 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, rli(is_slave_recovery), port(MYSQL_PORT), checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF), connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0), - slave_running(0), slave_run_id(0), clock_diff_with_master(0), + slave_running(MYSQL_SLAVE_NOT_RUN), slave_run_id(0), + clock_diff_with_master(0), sync_counter(0), heartbeat_period(0), received_heartbeats(0), master_id(0), prev_master_id(0), using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0), @@ -1273,23 +1274,24 @@ bool Master_info_index::give_error_if_slave_running() The LOCK_active_mi must be held while calling this function. @return - TRUE If some slave SQL thread is running. - FALSE No slave SQL thread is running + 0 No Slave SQL thread is running + # Number of slave SQL thread running */ -bool Master_info_index::any_slave_sql_running() +uint Master_info_index::any_slave_sql_running() { + uint count= 0; DBUG_ENTER("any_slave_sql_running"); if (!this) // master_info_index is set to NULL on server shutdown - DBUG_RETURN(TRUE); + DBUG_RETURN(count); for (uint i= 0; i< master_info_hash.records; ++i) { Master_info *mi= (Master_info *)my_hash_element(&master_info_hash, i); if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) - DBUG_RETURN(TRUE); + count++; } - DBUG_RETURN(FALSE); + DBUG_RETURN(count); } diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index 2b0b40feb3d..a27672e4c90 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -218,7 +218,7 @@ public: Master_info *get_master_info(LEX_STRING *connection_name, Sql_condition::enum_warning_level warning); bool give_error_if_slave_running(); - bool any_slave_sql_running(); + uint any_slave_sql_running(); bool start_all_slaves(THD *thd); bool stop_all_slaves(THD *thd); }; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 8c7724d88a3..422c38209b5 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -63,7 +63,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) last_master_timestamp(0), sql_thread_caught_up(true), slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), sql_driver_thd(), gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0), - slave_running(0), until_condition(UNTIL_NONE), + slave_running(MYSQL_SLAVE_NOT_RUN), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), executed_entries(0), m_flags(0) { diff --git a/sql/slave.cc b/sql/slave.cc index 3f8bc066378..6b2a88a2953 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -112,7 +112,7 @@ static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]= { { "Waiting to reconnect after a failed registration on master", - "Slave I/O thread killed while waitnig to reconnect after a failed \ + "Slave I/O thread killed while waiting to reconnect after a failed \ registration on master", "Reconnecting after a failed registration on master", "failed registering on master, reconnecting to try again, \ @@ -4040,10 +4040,9 @@ connected: if (request_dump(thd, mysql, mi, &suppress_warnings)) { sql_print_error("Failed on request_dump()"); - if (check_io_slave_killed(mi, "Slave I/O thread killed while \ -requesting master dump") || - try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, - reconnect_messages[SLAVE_RECON_ACT_DUMP])) + if (check_io_slave_killed(mi, NullS) || + try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_DUMP])) goto err; goto connected; } @@ -4059,6 +4058,7 @@ requesting master dump") || }); const char *event_buf; + mi->slave_running= MYSQL_SLAVE_RUN_READING; DBUG_ASSERT(mi->last_error().number == 0); while (!io_slave_killed(mi)) { @@ -4071,8 +4071,7 @@ requesting master dump") || */ THD_STAGE_INFO(thd, stage_waiting_for_master_to_send_event); event_len= read_event(mysql, mi, &suppress_warnings); - if (check_io_slave_killed(mi, "Slave I/O thread killed while \ -reading event")) + if (check_io_slave_killed(mi, NullS)) goto err; DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_EVENT", if (!retry_count_event) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7a9cdad84f6..3d0d99bcfa2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1743,7 +1743,8 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, void THD::awake(killed_state state_to_set) { DBUG_ENTER("THD::awake"); - DBUG_PRINT("enter", ("this: %p current_thd: %p", this, current_thd)); + DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d", + this, current_thd, (int) state_to_set)); THD_CHECK_SENTRY(this); mysql_mutex_assert_owner(&LOCK_thd_data); @@ -3799,6 +3800,12 @@ void thd_increment_bytes_sent(ulong length) } } +my_bool thd_net_is_killed() +{ + THD *thd= current_thd; + return thd && thd->killed ? 1 : 0; +} + void thd_increment_bytes_received(ulong length) { diff --git a/sql/sql_class.h b/sql/sql_class.h index a94236aec0b..8264921cabd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -656,6 +656,7 @@ typedef struct system_status_var { ulong com_other; ulong com_stat[(uint) SQLCOM_END]; + ulong com_register_slave; ulong created_tmp_disk_tables_; ulong created_tmp_tables_; ulong ha_commit_count; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 23629ff3ec4..8373ddabe3c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1177,6 +1177,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #ifdef HAVE_REPLICATION case COM_REGISTER_SLAVE: { + status_var_increment(thd->status_var.com_register_slave); if (!register_slave(thd, (uchar*)packet, packet_length)) my_ok(thd); break; From 8fcc0bfefadcb4e9f7acb13d11661daeea5097f9 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 3 Jan 2016 13:27:59 +0200 Subject: [PATCH 03/11] Fixed bug in semi_sync replication tests. The problem was that wait_for_slave_io_to_start reported that the io thread was ready, when it was still initializing. This caused test suite to continue too early, for example before the semi sync plugin was properly enabled. Fixed by introducing a new internal stage: "Preparing". Slave_IO_Running is now set to "Yes" only when all initializing is done and the IO thread is ready to read things from the master. The only test affected by this change is rpl_flsh_tbls, which got stuck in the preparing phase while trying to read the GTID position from a table. Fixed by having this test waiting for Preparing instead of Yes. --- mysql-test/extra/rpl_tests/rpl_flsh_tbls.test | 8 +++++++- mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result | 3 ++- .../suite/rpl/r/rpl_semi_sync_uninstall_plugin.result | 8 ++++++++ mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result | 3 ++- .../suite/rpl/t/rpl_semi_sync_uninstall_plugin.test | 6 ++++++ sql/mysqld.cc | 4 ++-- sql/rpl_mi.cc | 2 +- sql/slave.cc | 8 ++++---- sql/slave.h | 9 +++++---- 9 files changed, 37 insertions(+), 14 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test index 5cbda2d591f..a8cec3d2a1a 100644 --- a/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test +++ b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test @@ -45,7 +45,13 @@ drop table t1; connection slave; flush tables with read lock; start slave; -source include/wait_for_slave_to_start.inc; + +# The IO thread will not be able to read the GTID because of flush tables +let $slave_param= Slave_IO_Running; +let $slave_param_value= Preparing; +source include/wait_for_slave_param.inc; + +--source include/wait_for_slave_sql_to_start.inc --error 1192 stop slave; diff --git a/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result b/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result index 6aca4282ccd..27b23739831 100644 --- a/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result +++ b/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result @@ -22,7 +22,8 @@ include/wait_for_slave_to_stop.inc drop table t1; flush tables with read lock; start slave; -include/wait_for_slave_to_start.inc +include/wait_for_slave_param.inc [Slave_IO_Running] +include/wait_for_slave_sql_to_start.inc stop slave; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; diff --git a/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result b/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result index 0809af5f943..36d862dedce 100644 --- a/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result +++ b/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result @@ -14,6 +14,10 @@ DROP TABLE t1; [connection slave] include/install_semisync.inc [connection slave] +show global status like "Slave%_running"; +Variable_name Value +Slave_running ON +Slaves_running 1 UNINSTALL PLUGIN rpl_semi_sync_slave; Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown @@ -21,6 +25,10 @@ select plugin_name,plugin_status from information_schema.plugins where plugin_na plugin_name plugin_status rpl_semi_sync_slave DELETED [connection master] +show global status like "Slave%_connect%"; +Variable_name Value +Slave_connections 2 +Slaves_connected 1 UNINSTALL PLUGIN rpl_semi_sync_master; Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown diff --git a/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result b/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result index 6aca4282ccd..27b23739831 100644 --- a/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result +++ b/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result @@ -22,7 +22,8 @@ include/wait_for_slave_to_stop.inc drop table t1; flush tables with read lock; start slave; -include/wait_for_slave_to_start.inc +include/wait_for_slave_param.inc [Slave_IO_Running] +include/wait_for_slave_sql_to_start.inc stop slave; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; diff --git a/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test b/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test index 4ee345e54ba..0f86789cced 100644 --- a/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test +++ b/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test @@ -57,6 +57,8 @@ DROP TABLE t1; # possible at this state --connection slave --echo [connection slave] +show global status like "Slave%_running"; + UNINSTALL PLUGIN rpl_semi_sync_slave; select plugin_name,plugin_status from information_schema.plugins where plugin_name like 'rpl_%'; @@ -64,6 +66,10 @@ select plugin_name,plugin_status from information_schema.plugins where plugin_na # possible at this state --connection master --echo [connection master] + +# The following is to catch errors if the next uninstall plugin would succeed +show global status like "Slave%_connect%"; + UNINSTALL PLUGIN rpl_semi_sync_master; select plugin_name,plugin_status from information_schema.plugins where plugin_name like 'rpl_%'; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c6ac3f7a4a2..416193ff1e0 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7287,8 +7287,8 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff) get_master_info(&thd->variables.default_master_connection, Sql_condition::WARN_LEVEL_NOTE); if (mi) - tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT && - mi->rli.slave_running); + tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && + mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); } mysql_mutex_unlock(&LOCK_active_mi); if (mi) diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index a7ee3f3f45e..9c6f4639717 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -1321,7 +1321,7 @@ bool Master_info_index::start_all_slaves(THD *thd) Try to start all slaves that are configured (host is defined) and are not already running */ - if ((mi->slave_running != MYSQL_SLAVE_RUN_CONNECT || + if ((mi->slave_running == MYSQL_SLAVE_NOT_RUN || !mi->rli.slave_running) && *mi->host) { if ((error= start_slave(thd, mi, 1))) diff --git a/sql/slave.cc b/sql/slave.cc index 6b2a88a2953..7474aec38ca 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2615,6 +2615,8 @@ static bool send_show_master_info_header(THD *thd, bool full, DBUG_RETURN(FALSE); } +/* Text for Slave_IO_Running */ +static const char *slave_running[]= { "No", "Connecting", "Preparing", "Yes" }; static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, String *gtid_pos) @@ -2668,9 +2670,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, &my_charset_bin); protocol->store((ulonglong) mi->rli.group_relay_log_pos); protocol->store(mi->rli.group_master_log_name, &my_charset_bin); - protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ? - "Yes" : (mi->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT ? - "Connecting" : "No"), &my_charset_bin); + protocol->store(slave_running[mi->slave_running], &my_charset_bin); protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin); protocol->store(rpl_filter->get_do_db()); protocol->store(rpl_filter->get_ignore_db()); @@ -2713,7 +2713,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, Seconds_Behind_Master: if SQL thread is running and I/O thread is connected, we can compute it otherwise show NULL (i.e. unknown). */ - if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) && + if ((mi->slave_running == MYSQL_SLAVE_RUN_READING) && mi->rli.slave_running) { long time_diff; diff --git a/sql/slave.h b/sql/slave.h index e16f801b577..5cc02c8a10b 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -132,11 +132,11 @@ extern my_bool opt_replicate_annotate_row_events; extern ulonglong relay_log_space_limit; /* - 3 possible values for Master_info::slave_running and + 4 possible values for Master_info::slave_running and Relay_log_info::slave_running. - The values 0,1,2 are very important: to keep the diff small, I didn't - substitute places where we use 0/1 with the newly defined symbols. So don't change - these values. + The values 0,1,2,3 are very important: to keep the diff small, I didn't + substitute places where we use 0/1 with the newly defined symbols. + So don't change these values. The same way, code is assuming that in Relay_log_info we use only values 0/1. I started with using an enum, but @@ -145,6 +145,7 @@ extern ulonglong relay_log_space_limit; #define MYSQL_SLAVE_NOT_RUN 0 #define MYSQL_SLAVE_RUN_NOT_CONNECT 1 #define MYSQL_SLAVE_RUN_CONNECT 2 +#define MYSQL_SLAVE_RUN_READING 3 #define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\ "FIRST") From 5f48b61509e4bc0fcb4e679abe51a6fc2dbb6359 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 7 Jan 2016 14:45:40 +0100 Subject: [PATCH 04/11] MDEV-9298 : Build failure when linking libmysql. If GCC or CLang compile with link time optimization (-flto), they throw an error during link , when lto sees a function (e.g mysql_real_connect) is redeclared as "external void *" in libmysql_exports.cc The fix disables -flto for generated libmysql_exports.cc --- cmake/libutils.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index cca0032962d..41b7d9d1d86 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -87,6 +87,11 @@ MACRO(CREATE_EXPORT_FILE VAR TARGET API_FUNCTIONS) ENDFOREACH() SET(CONTENT "${CONTENT} (void *)0\n}\;") CONFIGURE_FILE_CONTENT(${CONTENT} ${EXPORTS}) + # Avoid "function redeclared as variable" error + # when using gcc/clang option -flto(link time optimization) + IF(" ${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} " MATCHES " -flto") + SET_SOURCE_FILES_PROPERTIES(${EXPORTS} PROPERTIES COMPILE_FLAGS "-fno-lto") + ENDIF() SET(${VAR} ${EXPORTS}) ENDIF() ENDMACRO() From 1236333333c351b93593b12c0e20b06313e4ac29 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 24 Dec 2015 21:46:38 +0100 Subject: [PATCH 05/11] Fix annoying repetitive tokudb build warning, if MariaDB is build on non-Linux x64 system --- storage/tokudb/CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/storage/tokudb/CMakeLists.txt b/storage/tokudb/CMakeLists.txt index d1f43d71f57..1b256ff100b 100644 --- a/storage/tokudb/CMakeLists.txt +++ b/storage/tokudb/CMakeLists.txt @@ -9,10 +9,9 @@ struct a d = { .b=1, .c=2 }; int main() { return 0; } " TOKUDB_OK) ELSE() - IF(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - MESSAGE(WARNING "Not Intel 64 bit CPU") - ELSE() - MESSAGE(WARNING "Too old CMAKE VERSION. 2.8.9 or higher is required by TokuDB") + IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND + CMAKE_VERSION VERSION_LESS "2.8.9") + MESSAGE(STATUS "CMake 2.8.9 or higher is required by TokuDB") ENDIF() ENDIF() From 3730d8a225fe33a0157e0aaa85121ef7cecf288d Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 5 Jan 2016 22:48:50 +0100 Subject: [PATCH 06/11] MDEV-9366 : do_shutdown_server fails to detect server shutdown on Windows. Fix test whether process is alive in mysqltest. Also fix SHUT_RD definition on Windows to be SD_RECEIVE. SD_BOTH was used instead prior to this patch, and this would occasionally make mysql_shutdown() fail - when the socket for the current connection is not able send the COM_SHUTDOWN response anymore. --- client/mysqltest.cc | 5 +++-- include/violite.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index c601fb57f2f..026934a2feb 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -5121,12 +5121,13 @@ static int my_kill(int pid, int sig) { #ifdef __WIN__ HANDLE proc; - if ((proc= OpenProcess(PROCESS_TERMINATE, FALSE, pid)) == NULL) + if ((proc= OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, pid)) == NULL) return -1; if (sig == 0) { + DWORD wait_result= WaitForSingleObject(proc, 0); CloseHandle(proc); - return 0; + return wait_result == WAIT_OBJECT_0?-1:0; } (void)TerminateProcess(proc, 201); CloseHandle(proc); diff --git a/include/violite.h b/include/violite.h index d5ce66c3061..a7165ca91a9 100644 --- a/include/violite.h +++ b/include/violite.h @@ -208,7 +208,7 @@ void vio_end(void); /* shutdown(2) flags */ #ifndef SHUT_RD -#define SHUT_RD SD_BOTH +#define SHUT_RD SD_RECEIVE #endif /* From c9552533c962af885796c3189d714305d8fbf7cb Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Jan 2016 16:29:02 +0400 Subject: [PATCH 07/11] MDEV-9106 Audit plugin compiled with MariaDB can't install on MySQL 5.7. The audit API was seriously changed in MySQL 5.7. so we had to adapt the plugin's code to that. --- .../suite/plugins/r/server_audit.result | 9 +- mysys/file_logger.c | 2 + plugin/server_audit/CMakeLists.txt | 5 +- plugin/server_audit/plugin_audit_v4.h | 561 +++++++++++++ plugin/server_audit/server_audit.c | 789 ++++++++++++------ plugin/server_audit/test_audit_v4.c | 162 ++++ 6 files changed, 1263 insertions(+), 265 deletions(-) create mode 100644 plugin/server_audit/plugin_audit_v4.h create mode 100644 plugin/server_audit/test_audit_v4.c diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 69c9bc3a4be..67338506001 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -8,6 +8,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users +server_audit_loc_info server_audit_logging OFF server_audit_mode 0 server_audit_output_type file @@ -71,6 +72,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 0 server_audit_output_type file @@ -216,6 +218,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 1 server_audit_output_type file @@ -229,9 +232,9 @@ Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_logging=on',0 TIME,HOSTNAME,root,localhost,ID,0,CONNECT,mysql,,0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,mysql,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID -TIME,HOSTNAME,no_such_user,localhost,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_incl_users=\'odin, root, dva, tri\'',0 TIME,HOSTNAME,root,localhost,ID,ID,CREATE,test,t2, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create table t2 (id int)',0 @@ -281,7 +284,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,READ,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,event, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop database sa_db',0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,sa_db,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create database sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'use sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, diff --git a/mysys/file_logger.c b/mysys/file_logger.c index 394b59f6378..b94cb705d3f 100644 --- a/mysys/file_logger.c +++ b/mysys/file_logger.c @@ -14,11 +14,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef FLOGGER_SKIP_INCLUDES #include "my_global.h" #include #include #include #include +#endif /*FLOGGER_SKIP_INCLUDES*/ #ifndef flogger_mutex_init #define flogger_mutex_init(A,B,C) mysql_mutex_init(A,B,C) diff --git a/plugin/server_audit/CMakeLists.txt b/plugin/server_audit/CMakeLists.txt index 527d0d702da..3de8c43c871 100644 --- a/plugin/server_audit/CMakeLists.txt +++ b/plugin/server_audit/CMakeLists.txt @@ -13,4 +13,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -MYSQL_ADD_PLUGIN(server_audit server_audit.c MODULE_ONLY) +SET(SERVER_AUDIT_SOURCES + server_audit.c test_audit_v4.c plugin_audit_v4.h) + + MYSQL_ADD_PLUGIN(server_audit ${SERVER_AUDIT_SOURCES} MODULE_ONLY) diff --git a/plugin/server_audit/plugin_audit_v4.h b/plugin/server_audit/plugin_audit_v4.h new file mode 100644 index 00000000000..5f8e43b3811 --- /dev/null +++ b/plugin/server_audit/plugin_audit_v4.h @@ -0,0 +1,561 @@ +/* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + 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 */ + +#ifndef _my_audit_h +#define _my_audit_h + +#ifndef PLUGIN_CONTEXT +#include "plugin.h" +#include "mysql/mysql_lex_string.h" +#ifndef MYSQL_ABI_CHECK +#include "m_string.h" +#endif +#include "my_command.h" +#include "my_sqlcommand.h" +#endif /*PLUGIN_CONTEXT*/ + +#define MYSQL_AUDIT_INTERFACE_VERSION 0x0401 + +/** + @enum mysql_event_class_t + + Audit event classes. +*/ +typedef enum +{ + MYSQL_AUDIT_GENERAL_CLASS = 0, + MYSQL_AUDIT_CONNECTION_CLASS = 1, + MYSQL_AUDIT_PARSE_CLASS = 2, + MYSQL_AUDIT_AUTHORIZATION_CLASS = 3, + MYSQL_AUDIT_TABLE_ACCESS_CLASS = 4, + MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS = 5, + MYSQL_AUDIT_SERVER_STARTUP_CLASS = 6, + MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS = 7, + MYSQL_AUDIT_COMMAND_CLASS = 8, + MYSQL_AUDIT_QUERY_CLASS = 9, + MYSQL_AUDIT_STORED_PROGRAM_CLASS = 10, + /* This item must be last in the list. */ + MYSQL_AUDIT_CLASS_MASK_SIZE +} mysql_event_class_t; + +/** + @struct st_mysql_audit + + The descriptor structure that is referred from st_mysql_plugin. +*/ +struct st_mysql_audit +{ + /** + Interface version. + */ + int interface_version; + + /** + 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. + */ + void (*release_thd)(MYSQL_THD); + + /** + Invoked whenever an event occurs which is of any + class for which the plugin has interest.The second argument + indicates the specific event class and the third argument is data + as required for that class. + */ + int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *); + + /** + An array of bits used to indicate what event classes + that this plugin wants to receive. + */ + unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; +}; + +/** + @typedef enum_sql_command_t + + SQL command type definition. +*/ +typedef enum enum_sql_command enum_sql_command_t; + +/** + @enum mysql_event_general_subclass_t + + Events for the MYSQL_AUDIT_GENERAL_CLASS event class. +*/ +typedef enum +{ + /** occurs before emitting to the general query log. */ + MYSQL_AUDIT_GENERAL_LOG = 1 << 0, + /** occurs before transmitting errors to the user. */ + MYSQL_AUDIT_GENERAL_ERROR = 1 << 1, + /** occurs after transmitting a resultset to the user. */ + MYSQL_AUDIT_GENERAL_RESULT = 1 << 2, + /** occurs after transmitting a resultset or errors */ + MYSQL_AUDIT_GENERAL_STATUS = 1 << 3 +} mysql_event_general_subclass_t; + +#define MYSQL_AUDIT_GENERAL_ALL (MYSQL_AUDIT_GENERAL_LOG | \ + MYSQL_AUDIT_GENERAL_ERROR | \ + MYSQL_AUDIT_GENERAL_RESULT | \ + MYSQL_AUDIT_GENERAL_STATUS) +/** + @struct mysql_event_general + + Structure for the MYSQL_AUDIT_GENERAL_CLASS event class. +*/ +struct mysql_event_general +{ + mysql_event_general_subclass_t event_subclass; + int general_error_code; + unsigned long general_thread_id; + MYSQL_LEX_CSTRING general_user; + MYSQL_LEX_CSTRING general_command; + MYSQL_LEX_CSTRING general_query; + struct charset_info_st *general_charset; + unsigned long long general_time; + unsigned long long general_rows; + MYSQL_LEX_CSTRING general_host; + MYSQL_LEX_CSTRING general_sql_command; + MYSQL_LEX_CSTRING general_external_user; + MYSQL_LEX_CSTRING general_ip; +}; + +/** + @enum mysql_event_connection_subclass_t + + Events for MYSQL_AUDIT_CONNECTION_CLASS event class. +*/ +typedef enum +{ + /** occurs after authentication phase is completed. */ + MYSQL_AUDIT_CONNECTION_CONNECT = 1 << 0, + /** occurs after connection is terminated. */ + MYSQL_AUDIT_CONNECTION_DISCONNECT = 1 << 1, + /** occurs after COM_CHANGE_USER RPC is completed. */ + MYSQL_AUDIT_CONNECTION_CHANGE_USER = 1 << 2, + /** occurs before authentication. */ + MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE = 1 << 3 +} mysql_event_connection_subclass_t; + +#define MYSQL_AUDIT_CONNECTION_ALL (MYSQL_AUDIT_CONNECTION_CONNECT | \ + MYSQL_AUDIT_CONNECTION_DISCONNECT | \ + MYSQL_AUDIT_CONNECTION_CHANGE_USER | \ + MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE) +/** + @struct mysql_event_connection + + Structure for the MYSQL_AUDIT_CONNECTION_CLASS event class. +*/ +struct mysql_event_connection +{ + /** Event subclass. */ + mysql_event_connection_subclass_t event_subclass; + /** Current status of the connection. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** User name of this connection. */ + MYSQL_LEX_CSTRING user; + /** Priv user name. */ + MYSQL_LEX_CSTRING priv_user; + /** External user name. */ + MYSQL_LEX_CSTRING external_user; + /** Proxy user used for this connection. */ + MYSQL_LEX_CSTRING proxy_user; + /** Connection host. */ + MYSQL_LEX_CSTRING host; + /** IP of the connection. */ + MYSQL_LEX_CSTRING ip; + /** Database name specified at connection time. */ + MYSQL_LEX_CSTRING database; + /** Connection type: + - 0 Undefined + - 1 TCP/IP + - 2 Socket + - 3 Named pipe + - 4 SSL + - 5 Shared memory + */ + int connection_type; +}; + +/** +@enum mysql_event_parse_subclass_t + +Events for MYSQL_AUDIT_PARSE_CLASS event class. +*/ +typedef enum +{ + /** occurs before the query parsing. */ + MYSQL_AUDIT_PARSE_PREPARSE = 1 << 0, + /** occurs after the query parsing. */ + MYSQL_AUDIT_PARSE_POSTPARSE = 1 << 1 +} mysql_event_parse_subclass_t; + +#define MYSQL_AUDIT_PARSE_ALL (MYSQL_AUDIT_PARSE_PREPARSE | \ + MYSQL_AUDIT_PARSE_POSTPARSE) + +typedef enum +{ + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_NONE = 0, + /// mysql_event_parse::flags Must be set by a plugin if the query is rewritten. + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_QUERY_REWRITTEN = 1 << 0, + /// mysql_event_parse::flags Is set by the server if the query is prepared statement. + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_IS_PREPARED_STATEMENT = 1 << 1 +} mysql_event_parse_rewrite_plugin_flag; + +/** Data for the MYSQL_AUDIT_PARSE events */ +struct mysql_event_parse +{ + /** MYSQL_AUDIT_[PRE|POST]_PARSE event id */ + mysql_event_parse_subclass_t event_subclass; + + /** one of FLAG_REWRITE_PLUGIN_* */ + mysql_event_parse_rewrite_plugin_flag *flags; + + /** input: the original query text */ + MYSQL_LEX_CSTRING query; + + /** output: returns the null-terminated rewriten query allocated by my_malloc() */ + MYSQL_LEX_CSTRING *rewritten_query; +}; + +/** + @enum mysql_event_authorization_subclass_t + + Events for MYSQL_AUDIT_AUTHORIZATION_CLASS event class. +*/ +typedef enum +{ + MYSQL_AUDIT_AUTHORIZATION_USER = 1 << 0, + /** Occurs when database privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_DB = 1 << 1, + /** Occurs when table privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_TABLE = 1 << 2, + /** Occurs when column privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_COLUMN = 1 << 3, + /** Occurs when procedure privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_PROCEDURE = 1 << 4, + /** Occurs when proxy privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_PROXY = 1 << 5 +} mysql_event_authorization_subclass_t; + +#define MYSQL_AUDIT_AUTHORIZATION_ALL (MYSQL_AUDIT_AUTHORIZATION_USER | \ + MYSQL_AUDIT_AUTHORIZATION_DB | \ + MYSQL_AUDIT_AUTHORIZATION_TABLE | \ + MYSQL_AUDIT_AUTHORIZATION_COLUMN | \ + MYSQL_AUDIT_AUTHORIZATION_PROCEDURE | \ + MYSQL_AUDIT_AUTHORIZATION_PROXY) +/** + @struct mysql_event_authorization + + Structure for MYSQL_AUDIT_AUTHORIZATION_CLASS event class. +*/ +struct mysql_event_authorization +{ + /** Event subclass. */ + mysql_event_authorization_subclass_t event_subclass; + /** Event status. */ + int status; + /** Connection id. */ + unsigned int connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query text. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** Database name. */ + MYSQL_LEX_CSTRING database; + /** Table name. */ + MYSQL_LEX_CSTRING table; + /** Other name associated with the event. */ + MYSQL_LEX_CSTRING object; + /** Requested authorization privileges. */ + unsigned long requested_privilege; + /** Currently granted authorization privileges. */ + unsigned long granted_privilege; +}; + +/** + @enum mysql_event_table_row_access_subclass_t + + Events for MYSQL_AUDIT_TABLE_ACCES_CLASS event class. +*/ +typedef enum +{ + /** Occurs when table data are read. */ + MYSQL_AUDIT_TABLE_ACCESS_READ = 1 << 0, + /** Occurs when table data are inserted. */ + MYSQL_AUDIT_TABLE_ACCESS_INSERT = 1 << 1, + /** Occurs when table data are updated. */ + MYSQL_AUDIT_TABLE_ACCESS_UPDATE = 1 << 2, + /** Occurs when table data are deleted. */ + MYSQL_AUDIT_TABLE_ACCESS_DELETE = 1 << 3 +} mysql_event_table_access_subclass_t; + +#define MYSQL_AUDIT_TABLE_ACCESS_ALL (MYSQL_AUDIT_TABLE_ACCESS_READ | \ + MYSQL_AUDIT_TABLE_ACCESS_INSERT | \ + MYSQL_AUDIT_TABLE_ACCESS_UPDATE | \ + MYSQL_AUDIT_TABLE_ACCESS_DELETE) + +/** + @struct mysql_event_table_row_access + + Structure for MYSQL_AUDIT_TABLE_ACCES_CLASS event class. +*/ +struct mysql_event_table_access +{ + /** Event subclass. */ + mysql_event_table_access_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** Database name. */ + MYSQL_LEX_CSTRING table_database; + /** Table name. */ + MYSQL_LEX_CSTRING table_name; +}; + +/** + @enum mysql_event_global_variable_subclass_t + + Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class. +*/ +typedef enum +{ + /** Occurs when global variable is retrieved. */ + MYSQL_AUDIT_GLOBAL_VARIABLE_GET = 1 << 0, + /** Occurs when global variable is set. */ + MYSQL_AUDIT_GLOBAL_VARIABLE_SET = 1 << 1 +} mysql_event_global_variable_subclass_t; + +#define MYSQL_AUDIT_GLOBAL_VARIABLE_ALL (MYSQL_AUDIT_GLOBAL_VARIABLE_GET | \ + MYSQL_AUDIT_GLOBAL_VARIABLE_SET) + +/** Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class. */ +struct mysql_event_global_variable +{ + /** Event subclass. */ + mysql_event_global_variable_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** Variable name. */ + MYSQL_LEX_CSTRING variable_name; + /** Variable value. */ + MYSQL_LEX_CSTRING variable_value; +}; + +/** + @enum mysql_event_server_startup_subclass_t + + Events for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class. +*/ +typedef enum +{ + /** Occurs after all subsystem are initialized during system start. */ + MYSQL_AUDIT_SERVER_STARTUP_STARTUP = 1 << 0 +} mysql_event_server_startup_subclass_t; + +#define MYSQL_AUDIT_SERVER_STARTUP_ALL (MYSQL_AUDIT_SERVER_STARTUP_STARTUP) + +/** + @struct mysql_event_server_startup + + Structure for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class. +*/ +struct mysql_event_server_startup +{ + /** Event subclass. */ + mysql_event_server_startup_subclass_t event_subclass; + /** Command line arguments. */ + const char **argv; + /** Command line arguments count. */ + unsigned int argc; +}; + +/** + @enum mysql_event_server_shutdown_subclass_t + + Events for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class. +*/ +typedef enum +{ + /** Occurs when global variable is set. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN = 1 << 0 +} mysql_event_server_shutdown_subclass_t; + +#define MYSQL_AUDIT_SERVER_SHUTDOWN_ALL (MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN) + +/** + @enum mysql_server_shutdown_reason_t + + Server shutdown reason. +*/ +typedef enum +{ + /** User requested shut down. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_SHUTDOWN, + /** The server aborts. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_ABORT +} mysql_server_shutdown_reason_t; + +/** + @struct mysql_event_server_shutdown + + Structure for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class. +*/ +struct mysql_event_server_shutdown +{ + /** Shutdown event. */ + mysql_event_server_shutdown_subclass_t event_subclass; + /** Exit code associated with the shutdown event. */ + int exit_code; + /** Shutdown reason. */ + mysql_server_shutdown_reason_t reason; +}; + +/** + @enum mysql_event_command_subclass_t + + Events for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +typedef enum +{ + /** Command start event. */ + MYSQL_AUDIT_COMMAND_START = 1 << 0, + /** Command end event. */ + MYSQL_AUDIT_COMMAND_END = 1 << 1 +} mysql_event_command_subclass_t; + +#define MYSQL_AUDIT_COMMAND_ALL (MYSQL_AUDIT_COMMAND_START | \ + MYSQL_AUDIT_COMMAND_END) +/** + @typedef enum_server_command_t + + Server command type definition. +*/ +typedef enum enum_server_command enum_server_command_t; + +/** + @struct mysql_event_command + + Event for MYSQL_AUDIT_COMMAND_CLASS event class. + Events generated as a result of RPC command requests. +*/ +struct mysql_event_command +{ + /** Command event subclass. */ + mysql_event_command_subclass_t event_subclass; + /** Command event status. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** Command id. */ + enum_server_command_t command_id; +}; + +/** + @enum mysql_event_query_subclass_t + + Events for MYSQL_AUDIT_QUERY_CLASS event class. +*/ +typedef enum +{ + /** Query start event. */ + MYSQL_AUDIT_QUERY_START = 1 << 0, + /** Nested query start event. */ + MYSQL_AUDIT_QUERY_NESTED_START = 1 << 1, + /** Query post parse event. */ + MYSQL_AUDIT_QUERY_STATUS_END = 1 << 2, + /** Nested query status end event. */ + MYSQL_AUDIT_QUERY_NESTED_STATUS_END = 1 << 3 +} mysql_event_query_subclass_t; + +#define MYSQL_AUDIT_QUERY_ALL (MYSQL_AUDIT_QUERY_START | \ + MYSQL_AUDIT_QUERY_NESTED_START | \ + MYSQL_AUDIT_QUERY_STATUS_END | \ + MYSQL_AUDIT_QUERY_NESTED_STATUS_END) +/** + @struct mysql_event_command + + Event for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +struct mysql_event_query +{ + /** Event subclass. */ + mysql_event_query_subclass_t event_subclass; + /** Event status. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; +}; + +/** + @enum mysql_event_stored_program_subclass_t + + Events for MYSQL_AUDIT_STORED_PROGRAM_CLASS event class. +*/ +typedef enum +{ + /** Stored program execution event. */ + MYSQL_AUDIT_STORED_PROGRAM_EXECUTE = 1 << 0 +} mysql_event_stored_program_subclass_t; + +#define MYSQL_AUDIT_STORED_PROGRAM_ALL (MYSQL_AUDIT_STORED_PROGRAM_EXECUTE) + +/** + @struct mysql_event_command + +Event for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +struct mysql_event_stored_program +{ + /** Event subclass. */ + mysql_event_stored_program_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query text. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** The Database the procedure is defined in. */ + MYSQL_LEX_CSTRING database; + /** Name of the stored program. */ + MYSQL_LEX_CSTRING name; + /** Stored program parameters. */ + void *parameters; +}; + +#endif diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index bede4c9545d..fc224db72ee 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Alexey Botchkov and SkySQL Ab +/* Copyright (C) 2013, 2015, Alexey Botchkov and SkySQL 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 @@ -14,13 +14,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define PLUGIN_VERSION 0x103 -#define PLUGIN_STR_VERSION "1.3.0" +#define PLUGIN_VERSION 0x104 +#define PLUGIN_STR_VERSION "1.4.0" + +#define _my_thread_var loc_thread_var #include #include #include #include +#include #ifndef _WIN32 #include @@ -80,8 +83,7 @@ static void closelog() {} #endif /*MARIADB_ONLY*/ #include -//#include -#include +//#include #include #include #include @@ -89,65 +91,135 @@ static void closelog() {} #define RTLD_DEFAULT NULL #endif -#undef my_init_dynamic_array_ci -#define init_dynamic_array2 loc_init_dynamic_array2 -#define my_init_dynamic_array_ci(A,B,C,D) loc_init_dynamic_array2(A,B,NULL,C,D) -#define _my_hash_init loc_my_hash_init -#define my_hash_search loc_my_hash_search -#define my_hash_insert loc_my_hash_insert -#define my_hash_delete loc_my_hash_delete -#define my_hash_update loc_my_hash_update -#define my_hash_free loc_my_hash_free -#define my_hash_first loc_my_hash_first -#define my_hash_reset loc_my_hash_reset -#define my_hash_search_using_hash_value loc_my_hash_search_using_hash_value -#define my_hash_first_from_hash_value loc_my_hash_first_from_hash_value -#define my_calc_hash loc_my_calc_hash -#undef my_hash_first_from_hash_value -#define my_hash_first_from_hash_value loc_my_my_hash_first_from_hash_value -#define my_hash_next loc_my_hash_next -#define my_hash_element loc_my_hash_element -#define my_hash_replace loc_my_hash_replace -#define my_hash_iterate loc_my_hash_iterate - -#define alloc_dynamic loc_alloc_dynamic -#define pop_dynamic loc_pop_dynamic -#define delete_dynamic loc_delete_dynamic -uchar *loc_alloc_dynamic(DYNAMIC_ARRAY *array); -#ifdef my_strnncoll -#undef my_strnncoll -#define my_strnncoll(s, a, b, c, d) (my_strnncoll_binary((s), (a), (b), (c), (d), 0)) -#endif - -static int my_strnncoll_binary(CHARSET_INFO * cs __attribute__((unused)), - const uchar *s, size_t slen, - const uchar *t, size_t tlen, - my_bool t_is_prefix) -{ - size_t len=min(slen,tlen); - int cmp= memcmp(s,t,len); - return cmp ? cmp : (int)((t_is_prefix ? len : slen) - tlen); -} - -#include "../../mysys/array.c" -#include "../../mysys/hash.c" - #ifndef MARIADB_ONLY #undef MYSQL_SERVICE_LOGGER_INCLUDED #undef MYSQL_DYNAMIC_PLUGIN #define FLOGGER_NO_PSI -#define flogger_mutex_init(A,B,C) pthread_mutex_init(&(B)->m_mutex, C) -#define flogger_mutex_destroy(A) pthread_mutex_destroy(&(A)->m_mutex) -#define flogger_mutex_lock(A) pthread_mutex_lock(&(A)->m_mutex) -#define flogger_mutex_unlock(A) pthread_mutex_unlock(&(A)->m_mutex) + +/* How to access the pthread_mutex in mysql_mutex_t */ +//#ifdef SAFE_MUTEX +//#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex +//#elif defined(MY_PTHREAD_FASTMUTEX) +//#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex +//#else +#define mysql_mutex_real_mutex(A) &(A)->m_mutex +//#endif + +#define flogger_mutex_init(A,B,C) do{}while(0) +#define flogger_mutex_destroy(A) do{}while(0) +#define flogger_mutex_lock(A) do{}while(0) +#define flogger_mutex_unlock(A) do{}while(0) static char **int_mysql_data_home; static char *default_home= (char *)"."; #define mysql_data_home (*int_mysql_data_home) +#define FLOGGER_SKIP_INCLUDES +#define my_open(A, B, C) loc_open(A, B) +#define my_close(A, B) close(A) +#define my_rename(A, B, C) loc_rename(A, B) +#define my_tell(A, B) loc_tell(A) +#define my_write(A, B, C, D) write(A, B, C) +#define my_malloc(A, B) malloc(A) +#define my_free(A) free(A) +#ifdef my_vsnprintf + #undef my_vsnprintf +#endif +#define my_vsnprintf vsnprintf + +File loc_open(const char *FileName, int Flags) + /* Path-name of file */ + /* Read | write .. */ + /* Special flags */ +{ + File fd; +#if defined(_WIN32) + fd= my_win_open(FileName, Flags); +#elif !defined(NO_OPEN_3) + fd = open(FileName, Flags, my_umask); /* Normal unix */ +#else + fd = open((char *) FileName, Flags); +#endif + return fd; +} + +static int loc_rename(const char *from, const char *to) +{ + int error = 0; + +#if defined(__WIN__) + if (!MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED | + MOVEFILE_REPLACE_EXISTING)) + { + my_osmaperr(GetLastError()); +#elif defined(HAVE_RENAME) + if (rename(from,to)) + { +#else + if (link(from, to) || unlink(from)) + { +#endif + my_errno=errno; + error = -1; + } + return error; +} + + +static my_off_t loc_seek(File fd, my_off_t pos, int whence) +{ + os_off_t newpos= -1; +#ifdef _WIN32 + newpos= my_win_lseek(fd, pos, whence); +#else + newpos= lseek(fd, pos, whence); +#endif + if (newpos == (os_off_t) -1) + { + my_errno= errno; + return MY_FILEPOS_ERROR; + } + + return (my_off_t) newpos; +} + + +static my_off_t loc_tell(File fd) +{ + os_off_t pos; +#if defined (HAVE_TELL) && !defined (_WIN32) + pos= tell(fd); +#else + pos= loc_seek(fd, 0L, MY_SEEK_CUR); +#endif + if (pos == (os_off_t) -1) + { + my_errno= errno; + } + return (my_off_t) pos; +} + +#ifdef HAVE_PSI_INTERFACE +#undef HAVE_PSI_INTERFACE +#include #include "../../mysys/file_logger.c" +#define HAVE_PSI_INTERFACE +#else +#include +#include "../../mysys/file_logger.c" +#endif #endif /*!MARIADB_ONLY*/ +#undef flogger_mutex_init +#undef flogger_mutex_destroy +#undef flogger_mutex_lock +#undef flogger_mutex_unlock + +#define flogger_mutex_init(A,B,C) pthread_mutex_init(mysql_mutex_real_mutex(B), C) +#define flogger_mutex_destroy(A) pthread_mutex_destroy(mysql_mutex_real_mutex(A)) +#define flogger_mutex_lock(A) pthread_mutex_lock(mysql_mutex_real_mutex(A)) +#define flogger_mutex_unlock(A) pthread_mutex_unlock(mysql_mutex_real_mutex(A)) + #ifndef DBUG_OFF #define PLUGIN_DEBUG_VERSION "-debug" #else @@ -168,7 +240,11 @@ static char *default_home= (char *)"."; extern char server_version[]; static const char *serv_ver= NULL; static int started_mysql= 0; +static int mysql_57_started= 0; +static int debug_server_started= 0; +static int use_event_data_for_disconnect= 0; static int started_mariadb= 0; +static int maria_55_started= 0; static int maria_above_5= 0; static char *incl_users, *excl_users, *file_path, *syslog_info; @@ -193,6 +269,27 @@ static char servhost[256]; static size_t servhost_len; static char *syslog_ident; static char syslog_ident_buffer[128]= "mysql-server_auditing"; + +struct connection_info +{ + int header; + unsigned long thread_id; + unsigned long long query_id; + char db[256]; + int db_length; + char user[64]; + int user_length; + char host[64]; + int host_length; + char ip[64]; + int ip_length; + const char *query; + int query_length; + char query_buffer[1024]; + time_t query_time; + int log_always; +}; + #define DEFAULT_FILENAME_LEN 16 static char default_file_name[DEFAULT_FILENAME_LEN+1]= "server_audit.log"; @@ -246,7 +343,8 @@ static TYPELIB events_typelib= array_elements(event_names) - 1, "", event_names, NULL }; static MYSQL_SYSVAR_SET(events, events, PLUGIN_VAR_RQCMDARG, - "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE, QUERY_DDL, QUERY_DML.", + "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE," + " QUERY_DDL, QUERY_DML, QUERY_DCL.", NULL, NULL, 0, &events_typelib); #define OUTPUT_SYSLOG 0 #define OUTPUT_FILE 1 @@ -287,6 +385,13 @@ static MYSQL_SYSVAR_UINT(query_log_limit, query_log_limit, PLUGIN_VAR_OPCMDARG, "Limit on the length of the query string in a record.", NULL, NULL, 1024, 0, 0x7FFFFFFF, 1); +char locinfo_ini_value[sizeof(struct connection_info)+4]; + +static MYSQL_THDVAR_STR(loc_info, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, + "Auxiliary info.", NULL, NULL, + locinfo_ini_value); + static const char *syslog_facility_names[]= { "LOG_USER", "LOG_MAIL", "LOG_DAEMON", "LOG_AUTH", @@ -366,6 +471,7 @@ static struct st_mysql_sys_var* vars[] = { MYSQL_SYSVAR(syslog_facility), MYSQL_SYSVAR(syslog_priority), MYSQL_SYSVAR(query_log_limit), + MYSQL_SYSVAR(loc_info), NULL }; @@ -376,6 +482,8 @@ static long log_write_failures= 0; static char current_log_buf[FN_REFLEN]= ""; static char last_error_buf[512]= ""; +extern void *mysql_v4_descriptor; + static struct st_mysql_show_var audit_status[]= { {"server_audit_active", (char *)&is_active, SHOW_BOOL}, @@ -415,7 +523,7 @@ static uchar *getkey_user(const char *entry, size_t *length, } -static void blank_user(uchar *user) +static void blank_user(char *user) { for (; *user && *user != ','; user++) *user= ' '; @@ -475,19 +583,98 @@ static void remove_blanks(char *user) } -static int user_hash_fill(HASH *h, char *users, - HASH *cmp_hash, int take_over_cmp) +struct user_name +{ + int name_len; + char *name; +}; + + +struct user_coll +{ + int n_users; + struct user_name *users; + int n_alloced; +}; + + +static void coll_init(struct user_coll *c) +{ + c->n_users= 0; + c->users= 0; + c->n_alloced= 0; +} + + +static void coll_free(struct user_coll *c) +{ + if (c->users) + { + free(c->users); + coll_init(c); + } +} + + +static int cmp_users(const void *ia, const void *ib) +{ + const struct user_name *a= (const struct user_name *) ia; + const struct user_name *b= (const struct user_name *) ib; + int dl= a->name_len - b->name_len; + if (dl != 0) + return dl; + + return strncmp(a->name, b->name, a->name_len); +} + + +static char *coll_search(struct user_coll *c, const char *n, int len) +{ + struct user_name un; + struct user_name *found; + un.name_len= len; + un.name= (char *) n; + found= (struct user_name*) bsearch(&un, c->users, c->n_users, + sizeof(c->users[0]), cmp_users); + return found ? found->name : 0; +} + + +static int coll_insert(struct user_coll *c, char *n, int len) +{ + if (c->n_users >= c->n_alloced) + { + c->n_alloced+= 128; + if (c->users == NULL) + c->users= malloc(c->n_alloced * sizeof(c->users[0])); + else + c->users= realloc(c->users, c->n_alloced * sizeof(c->users[0])); + + if (c->users == NULL) + return 1; + } + c->users[c->n_users].name= n; + c->users[c->n_users].name_len= len; + c->n_users++; + return 0; +} + + +static void coll_sort(struct user_coll *c) +{ + qsort(c->users, c->n_users, sizeof(c->users[0]), cmp_users); +} + + +static int user_coll_fill(struct user_coll *c, char *users, + struct user_coll *cmp_c, int take_over_cmp) { char *orig_users= users; - uchar *cmp_user= 0; + char *cmp_user= 0; size_t cmp_length; - int refill_cmp_hash= 0; + int refill_cmp_coll= 0; - if (my_hash_inited(h)) - my_hash_reset(h); - else - loc_my_hash_init(h, 0, &my_charset_bin, 0x100, 0, 0, - (my_hash_get_key) getkey_user, 0, 0); + c->n_users= 0; while (*users) { @@ -496,11 +683,10 @@ static int user_hash_fill(HASH *h, char *users, if (!*users) return 0; - - if (cmp_hash) + (void) getkey_user(users, &cmp_length, FALSE); + if (cmp_c) { - (void) getkey_user(users, &cmp_length, FALSE); - cmp_user= my_hash_search(cmp_hash, (const uchar *) users, cmp_length); + cmp_user= coll_search(cmp_c, users, cmp_length); if (cmp_user && take_over_cmp) { @@ -510,7 +696,7 @@ static int user_hash_fill(HASH *h, char *users, MYF(ME_JUST_WARNING), (int) cmp_length, users); internal_stop_logging= 0; blank_user(cmp_user); - refill_cmp_hash= 1; + refill_cmp_coll= 1; } else if (cmp_user) { @@ -522,7 +708,7 @@ static int user_hash_fill(HASH *h, char *users, continue; } } - if (my_hash_insert(h, (const uchar *) users)) + if (coll_insert(c, users, cmp_length)) return 1; while (*users && *users != ',') users++; @@ -531,15 +717,17 @@ static int user_hash_fill(HASH *h, char *users, users++; } - if (refill_cmp_hash) + if (refill_cmp_coll) { remove_blanks(excl_users); - return user_hash_fill(cmp_hash, excl_users, 0, 0); + return user_coll_fill(cmp_c, excl_users, 0, 0); } if (users > orig_users && users[-1] == ',') users[-1]= 0; + coll_sort(c); + return 0; } @@ -668,48 +856,19 @@ static void error_header() static LOGGER_HANDLE *logfile; -static HASH incl_user_hash, excl_user_hash; +static struct user_coll incl_user_coll, excl_user_coll; static unsigned long long query_counter= 1; -struct connection_info + +static struct connection_info *get_loc_info(MYSQL_THD thd) { - unsigned long thread_id; - unsigned long long query_id; - char db[256]; - int db_length; - char user[64]; - int user_length; - char host[64]; - int host_length; - char ip[64]; - int ip_length; - const char *query; - int query_length; - char query_buffer[1024]; - time_t query_time; - int log_always; -}; - - -static HASH connection_hash; - - -struct connection_info *alloc_connection() -{ - return malloc(ALIGN_SIZE(sizeof(struct connection_info))); + return (struct connection_info *) THDVAR(thd, loc_info); } -void free_connection(void* pconn) +static int ci_needs_setup(const struct connection_info *ci) { - (void) free(pconn); -} - - -static struct connection_info *find_connection(unsigned long id) -{ - return (struct connection_info *) - my_hash_search(&connection_hash, (const uchar *) &id, sizeof(id)); + return ci->header != 0; } @@ -873,15 +1032,24 @@ static int stop_logging() return 0; } -static struct connection_info * - add_connection(const struct mysql_event_connection *event) + +static void setup_connection_simple(struct connection_info *ci) +{ + ci->db_length= 0; + ci->user_length= 0; + ci->host_length= 0; + ci->ip_length= 0; + ci->query_length= 0; + ci->header= 0; +} + + +static void setup_connection_connect(struct connection_info *cn, + const struct mysql_event_connection *event) { - struct connection_info *cn= alloc_connection(); - if (!cn) - return 0; - cn->thread_id= event->thread_id; cn->query_id= 0; cn->log_always= 0; + cn->thread_id= event->thread_id; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), event->database, event->database_length); get_str_n(cn->user, &cn->user_length, sizeof(cn->db), @@ -890,11 +1058,7 @@ static struct connection_info * event->host, event->host_length); get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), event->ip, event->ip_length); - - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + cn->header= 0; } @@ -917,46 +1081,43 @@ do { \ -static struct connection_info * - add_connection_initdb(const struct mysql_event_general *event) +static void setup_connection_initdb(struct connection_info *cn, + const struct mysql_event_general *event) { - struct connection_info *cn; size_t user_len, host_len, ip_len; char uh_buffer[512]; - if (get_user_host(event->general_user, event->general_user_length, - uh_buffer, sizeof(uh_buffer), - &user_len, &host_len, &ip_len) || - (cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->general_thread_id; cn->query_id= 0; cn->log_always= 0; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), event->general_query, event->general_query_length); - get_str_n(cn->user, &cn->user_length, sizeof(cn->db), - uh_buffer, user_len); - get_str_n(cn->host, &cn->host_length, sizeof(cn->host), - uh_buffer+user_len+1, host_len); - get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), - uh_buffer+user_len+1+host_len+1, ip_len); - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + if (get_user_host(event->general_user, event->general_user_length, + uh_buffer, sizeof(uh_buffer), + &user_len, &host_len, &ip_len)) + { + /* The user@host line is incorrect. */ + cn->user_length= 0; + cn->host_length= 0; + cn->ip_length= 0; + } + else + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->db), + uh_buffer, user_len); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + uh_buffer+user_len+1, host_len); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + uh_buffer+user_len+1+host_len+1, ip_len); + } + cn->header= 0; } -static struct connection_info * - add_connection_table(const struct mysql_event_table *event) +static void setup_connection_table(struct connection_info *cn, + const struct mysql_event_table *event) { - struct connection_info *cn; - - if ((cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->thread_id; cn->query_id= query_counter++; cn->log_always= 0; @@ -968,42 +1129,40 @@ static struct connection_info * event->host, SAFE_STRLEN(event->host)); get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), event->ip, SAFE_STRLEN(event->ip)); - - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + cn->header= 0; } -static struct connection_info * - add_connection_query(const struct mysql_event_general *event) +static void setup_connection_query(struct connection_info *cn, + const struct mysql_event_general *event) { - struct connection_info *cn; size_t user_len, host_len, ip_len; char uh_buffer[512]; - if (get_user_host(event->general_user, event->general_user_length, - uh_buffer, sizeof(uh_buffer), - &user_len, &host_len, &ip_len) || - (cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->general_thread_id; cn->query_id= query_counter++; cn->log_always= 0; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), "", 0); - get_str_n(cn->user, &cn->user_length, sizeof(cn->db), - uh_buffer, user_len); - get_str_n(cn->host, &cn->host_length, sizeof(cn->host), - uh_buffer+user_len+1, host_len); - get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), - uh_buffer+user_len+1+host_len+1, ip_len); - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + if (get_user_host(event->general_user, event->general_user_length, + uh_buffer, sizeof(uh_buffer), + &user_len, &host_len, &ip_len)) + { + /* The user@host line is incorrect. */ + cn->user_length= 0; + cn->host_length= 0; + cn->ip_length= 0; + } + else + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->db), + uh_buffer, user_len); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + uh_buffer+user_len+1, host_len); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + uh_buffer+user_len+1+host_len+1, ip_len); + } + cn->header= 0; } @@ -1095,6 +1254,27 @@ static int log_connection(const struct connection_info *cn, } +static int log_connection_event(const struct mysql_event_connection *event, + const char *type) +{ + time_t ctime; + size_t csize; + char message[1024]; + + (void) time(&ctime); + csize= log_header(message, sizeof(message)-1, &ctime, + servhost, servhost_len, + event->user, event->user_length, + event->host, event->host_length, + event->ip, event->ip_length, + event->thread_id, 0, type); + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",%.*s,,%d", event->database_length, event->database, event->status); + message[csize]= '\n'; + return write_log(message, csize + 1); +} + + static size_t escape_string(const char *str, unsigned int len, char *result, size_t result_len) { @@ -1240,11 +1420,11 @@ static int do_log_user(const char *name) return 0; len= strlen(name); - if (incl_user_hash.records) - return my_hash_search(&incl_user_hash, (const uchar *) name, len) != 0; + if (incl_user_coll.n_users) + return coll_search(&incl_user_coll, name, len) != 0; - if (excl_user_hash.records) - return my_hash_search(&excl_user_hash, (const uchar *) name, len) == 0; + if (excl_user_coll.n_users) + return coll_search(&excl_user_coll, name, len) == 0; return 1; } @@ -1582,14 +1762,14 @@ static void update_general_user(struct connection_info *cn, } +static struct connection_info ci_disconnect_buffer; + #define AA_FREE_CONNECTION 1 #define AA_CHANGE_USER 2 -static struct connection_info *update_connection_hash(unsigned int event_class, - const void *ev, - int *after_action) +static void update_connection_info(struct connection_info *cn, + unsigned int event_class, const void *ev, int *after_action) { - struct connection_info *cn= NULL; *after_action= 0; switch (event_class) { @@ -1602,13 +1782,17 @@ static struct connection_info *update_connection_hash(unsigned int event_class, { int init_db_command= event->general_command_length == 7 && strncmp(event->general_command, "Init DB", 7) == 0; - if ((cn= find_connection(event->general_thread_id))) + if (!ci_needs_setup(cn)) { if (init_db_command) { /* Change DB */ - get_str_n(cn->db, &cn->db_length, sizeof(cn->db), - event->general_query, event->general_query_length); + if (mysql_57_started) + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->database, event->database_length); + else + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->general_query, event->general_query_length); } cn->query_id= mode ? query_counter++ : event->query_id; cn->query= event->general_query; @@ -1617,18 +1801,19 @@ static struct connection_info *update_connection_hash(unsigned int event_class, update_general_user(cn, event); } else if (init_db_command) - cn= add_connection_initdb(event); + setup_connection_initdb(cn, event); else if (event_query_command(event)) - cn= add_connection_query(event); + setup_connection_query(cn, event); + else + setup_connection_simple(cn); break; } case MYSQL_AUDIT_GENERAL_STATUS: if (event_query_command(event)) { - if (!(cn= find_connection(event->general_thread_id)) && - !(cn= add_connection_query(event))) - return 0; + if (ci_needs_setup(cn)) + setup_connection_query(cn, event); if (mode == 0 && cn->db_length == 0 && event->database_length > 0) get_str_n(cn->db, &cn->db_length, sizeof(cn->db), @@ -1654,13 +1839,13 @@ static struct connection_info *update_connection_hash(unsigned int event_class, } break; case MYSQL_AUDIT_GENERAL_ERROR: - /* We need this because of a bug in the MariaDB */ - /* that it returns NULL query field for the */ - /* MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare. */ - /* As a result we get empty QUERY field for errors. */ - if (!(cn= find_connection(event->general_thread_id)) && - !(cn= add_connection_query(event))) - return 0; + /* + We need this because the MariaDB returns NULL query field for the + MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare. + As a result we get empty QUERY field for errors. + */ + if (ci_needs_setup(cn)) + setup_connection_query(cn, event); cn->query_id= mode ? query_counter++ : event->query_id; get_str_n(cn->query_buffer, &cn->query_length, sizeof(cn->query_buffer), event->general_query, event->general_query_length); @@ -1675,9 +1860,9 @@ static struct connection_info *update_connection_hash(unsigned int event_class, { const struct mysql_event_table *event = (const struct mysql_event_table *) ev; - if (!(cn= find_connection(event->thread_id)) && - !(cn= add_connection_table(event))) - return 0; + if (ci_needs_setup(cn)) + setup_connection_table(cn, event); + if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0) { get_str_n(cn->user, &cn->user_length, sizeof(cn->user), @@ -1703,17 +1888,10 @@ static struct connection_info *update_connection_hash(unsigned int event_class, switch (event->event_subclass) { case MYSQL_AUDIT_CONNECTION_CONNECT: - cn= add_connection(ev); - break; - case MYSQL_AUDIT_CONNECTION_DISCONNECT: - cn= find_connection(event->thread_id); - if (cn) - *after_action= AA_FREE_CONNECTION; + setup_connection_connect(cn, event); break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: - cn= find_connection(event->thread_id); - if (cn) - *after_action= AA_CHANGE_USER; + *after_action= AA_CHANGE_USER; break; default:; } @@ -1722,17 +1900,17 @@ static struct connection_info *update_connection_hash(unsigned int event_class, default: break; } - return cn; } +struct connection_info cn_error_buffer; + + #define FILTER(MASK) (events == 0 || (events & MASK)) -static void auditing(MYSQL_THD thd __attribute__((unused)), - unsigned int event_class, - const void *ev) +void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) { - struct connection_info *cn; - int after_action; + struct connection_info *cn= 0; + int after_action= 0; /* That one is important as this function can be called with */ /* &lock_operations locked when the server logs an error reported */ @@ -1742,8 +1920,23 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), flogger_mutex_lock(&lock_operations); - if (!(cn= update_connection_hash(event_class, ev, &after_action))) - goto exit_func; + cn= get_loc_info(thd); + + if (ci_needs_setup(cn) && maria_55_started && + (event_class == MYSQL_AUDIT_GENERAL_CLASS && + *((int*)ev) == MYSQL_AUDIT_GENERAL_ERROR)) + { + /* + There's a bug in MariaDB 5.5 that prevents using thread local + variables in some cases. + The 'select * from notexisting_table;' query produces such case. + So just use the static buffer in this case. + */ + cn= &cn_error_buffer; + cn->header= 1; + } + + update_connection_info(cn, event_class, ev, &after_action); if (!logging) goto exit_func; @@ -1757,8 +1950,11 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), /* Only one subclass is logged. */ - if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS) + if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event_query_command(event)) + { log_statement(cn, event, "QUERY"); + } } else if (event_class == MYSQL_AUDIT_TABLE_CLASS && FILTER(EVENT_TABLE) && cn) { @@ -1799,7 +1995,10 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), log_connection(cn, event, event->status ? "FAILED_CONNECT": "CONNECT"); break; case MYSQL_AUDIT_CONNECTION_DISCONNECT: - log_connection(cn, event, "DISCONNECT"); + if (use_event_data_for_disconnect) + log_connection_event(event, "DISCONNECT"); + else + log_connection(&ci_disconnect_buffer, event, "DISCONNECT"); break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: log_connection(cn, event, "CHANGEUSER"); @@ -1814,10 +2013,6 @@ exit_func: if (after_action) { switch (after_action) { - case AA_FREE_CONNECTION: - my_hash_delete(&connection_hash, (uchar *) cn); - cn= 0; - break; case AA_CHANGE_USER: { const struct mysql_event_connection *event = @@ -1835,28 +2030,6 @@ exit_func: } -#ifdef DBUG_OFF - #ifdef __x86_64__ -static const int cmd_off= 4200; -static const int db_off= 120; -static const int db_len_off= 128; - #else -static const int cmd_off= 2668; -static const int db_off= 60; -static const int db_len_off= 64; - #endif /*x86_64*/ -#else - #ifdef __x86_64__ -static const int cmd_off= 4432; -static const int db_off= 120; -static const int db_len_off= 128; - #else -static const int cmd_off= 2808; -static const int db_off= 64; -static const int db_len_off= 68; - #endif /*x86_64*/ -#endif /*DBUG_OFF*/ - struct mysql_event_general_v8 { unsigned int event_class; @@ -1874,8 +2047,31 @@ struct mysql_event_general_v8 unsigned long long general_rows; }; + static void auditing_v8(MYSQL_THD thd, struct mysql_event_general_v8 *ev_v8) { +#ifdef DBUG_OFF + #ifdef __x86_64__ + static const int cmd_off= 4200; + static const int db_off= 120; + static const int db_len_off= 128; + #else + static const int cmd_off= 2668; + static const int db_off= 60; + static const int db_len_off= 64; + #endif /*x86_64*/ +#else + #ifdef __x86_64__ + static const int cmd_off= 4432; + static const int db_off= 120; + static const int db_len_off= 128; + #else + static const int cmd_off= 2808; + static const int db_off= 64; + static const int db_len_off= 68; + #endif /*x86_64*/ +#endif /*DBUG_OFF*/ + struct mysql_event_general event; if (ev_v8->event_class != MYSQL_AUDIT_GENERAL_CLASS) @@ -1934,6 +2130,41 @@ static void auditing_v13(MYSQL_THD thd, unsigned int *ev_v0) } +int get_db_mysql57(MYSQL_THD thd, char **name, int *len) +{ + int db_off; + int db_len_off; + if (debug_server_started) + { +#ifdef __x86_64__ + db_off= 608; + db_len_off= 616; +#else + db_off= 0; + db_len_off= 0; +#endif /*x86_64*/ + } + else + { +#ifdef __x86_64__ + db_off= 536; + db_len_off= 544; +#else + db_off= 0; + db_len_off= 0; +#endif /*x86_64*/ + } + +#ifdef __linux__ + *name= *(char **) (((char *) thd) + db_off); + *len= *((int *) (((char*) thd) + db_len_off)); + if (*name && (*name)[*len] != 0) + return 1; + return 0; +#else + return 1; +#endif +} /* As it's just too difficult to #include "sql_class.h", let's just copy the necessary part of the system_variables @@ -2060,8 +2291,8 @@ static int server_audit_init(void *p __attribute__((unused))) flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST); flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST); - my_hash_clear(&incl_user_hash); - my_hash_clear(&excl_user_hash); + coll_init(&incl_user_coll); + coll_init(&excl_user_coll); if (incl_users) { @@ -2079,9 +2310,6 @@ static int server_audit_init(void *p __attribute__((unused))) update_excl_users(NULL, NULL, NULL, &excl_users); } - loc_my_hash_init(&connection_hash, 0, &my_charset_bin, 0x100, 0, - sizeof(unsigned long), 0, free_connection, 0); - error_header(); fprintf(stderr, "MariaDB Audit Plugin version %s%s STARTED.\n", PLUGIN_STR_VERSION, PLUGIN_DEBUG_VERSION); @@ -2105,6 +2333,16 @@ static int server_audit_init(void *p __attribute__((unused))) } } + ci_disconnect_buffer.header= 10; + ci_disconnect_buffer.thread_id= 0; + ci_disconnect_buffer.query_id= 0; + ci_disconnect_buffer.db_length= 0; + ci_disconnect_buffer.user_length= 0; + ci_disconnect_buffer.host_length= 0; + ci_disconnect_buffer.ip_length= 0; + ci_disconnect_buffer.query= empty_str; + ci_disconnect_buffer.query_length= 0; + if (logging) start_logging(); @@ -2123,13 +2361,8 @@ static int server_audit_init_mysql(void *p) static int server_audit_deinit(void *p __attribute__((unused))) { - if (my_hash_inited(&incl_user_hash)) - my_hash_free(&incl_user_hash); - - if (my_hash_inited(&excl_user_hash)) - my_hash_free(&excl_user_hash); - - my_hash_free(&connection_hash); + coll_free(&incl_user_coll); + coll_free(&excl_user_coll); if (output_type == OUTPUT_FILE && logfile) logger_close(logfile); @@ -2164,6 +2397,7 @@ static struct st_mysql_audit mysql_descriptor = { MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK } }; + mysql_declare_plugin(server_audit) { MYSQL_AUDIT_PLUGIN, @@ -2206,7 +2440,7 @@ maria_declare_plugin(server_audit) audit_status, vars, PLUGIN_STR_VERSION, - MariaDB_PLUGIN_MATURITY_GAMMA + MariaDB_PLUGIN_MATURITY_STABLE } maria_declare_plugin_end; @@ -2214,22 +2448,21 @@ maria_declare_plugin_end; static void mark_always_logged(MYSQL_THD thd) { struct connection_info *cn; - if (thd && (cn= find_connection(thd_get_thread_id(thd)))) + if (thd && (cn= get_loc_info(thd))) cn->log_always= 1; } static void log_current_query(MYSQL_THD thd) { - unsigned long thd_id; struct connection_info *cn; - if (!thd || - !(cn= find_connection((thd_id= thd_get_thread_id(thd))))) + if (!thd) return; - if (FILTER(EVENT_QUERY) && do_log_user(cn->user)) + cn= get_loc_info(thd); + if (!ci_needs_setup(cn) && FILTER(EVENT_QUERY) && do_log_user(cn->user)) { - log_statement_ex(cn, cn->query_time, thd_id, cn->query, cn->query_length, - 0, "QUERY"); + log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd), + cn->query, cn->query_length, 0, "QUERY"); cn->log_always= 1; } } @@ -2325,7 +2558,7 @@ static void update_incl_users(MYSQL_THD thd, mark_always_logged(thd); strncpy(incl_user_buffer, new_users, sizeof(incl_user_buffer)); incl_users= incl_user_buffer; - user_hash_fill(&incl_user_hash, incl_users, &excl_user_hash, 1); + user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1); error_header(); fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users); flogger_mutex_unlock(&lock_operations); @@ -2341,7 +2574,7 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), mark_always_logged(thd); strncpy(excl_user_buffer, new_users, sizeof(excl_user_buffer)); excl_users= excl_user_buffer; - user_hash_fill(&excl_user_hash, excl_users, &incl_user_hash, 0); + user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0); error_header(); fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users); flogger_mutex_unlock(&lock_operations); @@ -2480,6 +2713,13 @@ static void update_syslog_ident(MYSQL_THD thd __attribute__((unused)), } +struct st_my_thread_var *loc_thread_var(void) +{ + return 0; +} + + + #ifdef _WIN32 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { @@ -2497,9 +2737,18 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) goto exit; started_mariadb= strstr(serv_ver, "MariaDB") != 0; + debug_server_started= strstr(serv_ver, "debug") != 0; - if (!started_mariadb) + if (started_mariadb) { + if (serv_ver[0] == '1') + use_event_data_for_disconnect= 1; + else + maria_55_started= 1; + } + else + { + /* Started MySQL. */ if (serv_ver[0] == '5' && serv_ver[2] == '5') { int sc= serv_ver[4] - '0'; @@ -2516,7 +2765,25 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) mysql_descriptor.event_notify= (void *) auditing_v13; } } + else if (serv_ver[0] == '5' && serv_ver[2] == '6') + { + int sc= serv_ver[4] - '0'; + if (serv_ver[5] >= '0' && serv_ver[5] <= '9') + sc= sc * 10 + serv_ver[5] - '0'; + if (sc >= 24) + use_event_data_for_disconnect= 1; + } + else if (serv_ver[0] == '5' && serv_ver[2] == '7') + { + mysql_57_started= 1; + _mysql_plugin_declarations_[0].info= mysql_v4_descriptor; + use_event_data_for_disconnect= 1; + } } + + memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1); + locinfo_ini_value[sizeof(locinfo_ini_value)]= 0; + exit: #ifdef _WIN32 return 1; diff --git a/plugin/server_audit/test_audit_v4.c b/plugin/server_audit/test_audit_v4.c new file mode 100644 index 00000000000..ae7527f8449 --- /dev/null +++ b/plugin/server_audit/test_audit_v4.c @@ -0,0 +1,162 @@ +#define PLUGIN_CONTEXT + +#include + +typedef void *MYSQL_THD; +struct st_mysql_const_lex_string +{ + const char *str; + size_t length; +}; +typedef struct st_mysql_const_lex_string MYSQL_LEX_CSTRING; +enum enum_sql_command{ SQLCOM_A, SQLCOM_B }; +enum enum_server_command{ SERVCOM_A, SERVCOM_B }; + +#include "plugin_audit_v4.h" + +extern void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev); +extern int get_db_mysql57(MYSQL_THD thd, char **name, int *len); + + +struct mysql_event_general_302 +{ + unsigned int event_subclass; + 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; + unsigned long long query_id; + char *database; + int database_length; +}; + + +static int auditing_v4(MYSQL_THD thd, mysql_event_class_t class, const void *ev) +{ + int *subclass= (int *)ev; + struct mysql_event_general_302 ev_302; + int subclass_v3, subclass_orig; + + if (class != MYSQL_AUDIT_GENERAL_CLASS && + class != MYSQL_AUDIT_CONNECTION_CLASS) + return 0; + + subclass_orig= *subclass; + + if (class == MYSQL_AUDIT_GENERAL_CLASS) + { + struct mysql_event_general *event= (struct mysql_event_general *) ev; + ev_302.general_error_code= event->general_error_code; + ev_302.general_thread_id= event->general_thread_id; + ev_302.general_user= event->general_user.str; + ev_302.general_user_length= event->general_user.length; + ev_302.general_command= event->general_command.str; + ev_302.general_command_length= event->general_command.length; + ev_302.general_query= event->general_query.str; + ev_302.general_query_length= event->general_query.length; + ev_302.general_charset= event->general_charset; + ev_302.general_time= event->general_time; + ev_302.general_rows= event->general_rows; + if (get_db_mysql57(thd, &ev_302.database, &ev_302.database_length)) + { + ev_302.database= 0; + ev_302.database_length= 0; + } + ev= &ev_302; + switch (subclass_orig) + { + case MYSQL_AUDIT_GENERAL_LOG: + subclass_v3= 0; + ev_302.event_subclass= 0; + break; + case MYSQL_AUDIT_GENERAL_ERROR: + subclass_v3= 1; + ev_302.event_subclass= 1; + break; + case MYSQL_AUDIT_GENERAL_RESULT: + subclass_v3= 2; + ev_302.event_subclass= 2; + break; + case MYSQL_AUDIT_GENERAL_STATUS: + { + subclass_v3= 3; + ev_302.event_subclass= 3; + break; + } + default: + return 0; + } + } + else /* if (class == MYSQL_AUDIT_CONNECTION_CLASS) */ + { + switch (subclass_orig) + { + case MYSQL_AUDIT_CONNECTION_CONNECT: + subclass_v3= 0; + break; + case MYSQL_AUDIT_CONNECTION_DISCONNECT: + subclass_v3= 1; + break; + default: + return 0; + } + } + + *subclass= subclass_v3; + + auditing(thd, (int) class, ev); + + *subclass= subclass_orig; + return 0; +} + + +static struct st_mysql_audit mysql_descriptor = +{ + MYSQL_AUDIT_INTERFACE_VERSION, + NULL, + auditing_v4, + { (unsigned long) MYSQL_AUDIT_GENERAL_ALL, + (unsigned long) MYSQL_AUDIT_CONNECTION_ALL, + (unsigned long) MYSQL_AUDIT_PARSE_ALL, + 0, /* This event class is currently not supported. */ + 0, /* This event class is currently not supported. */ + (unsigned long) MYSQL_AUDIT_GLOBAL_VARIABLE_ALL, + (unsigned long) MYSQL_AUDIT_SERVER_STARTUP_ALL, + (unsigned long) MYSQL_AUDIT_SERVER_SHUTDOWN_ALL, + (unsigned long) MYSQL_AUDIT_COMMAND_ALL, + (unsigned long) MYSQL_AUDIT_QUERY_ALL, + (unsigned long) MYSQL_AUDIT_STORED_PROGRAM_ALL } +#ifdef WHEN_MYSQL_BUG_FIXED + /* + By this moment MySQL just sends no notifications at all + when we request only those we actually need. + So we have to request everything and filter them inside the + handling function. + */ + { (unsigned long) MYSQL_AUDIT_GENERAL_ALL, + (unsigned long) (MYSQL_AUDIT_CONNECTION_CONNECT | + MYSQL_AUDIT_CONNECTION_DISCONNECT), + 0, + 0, /* This event class is currently not supported. */ + 0, /* This event class is currently not supported. */ + 0, + 0, + 0, + 0, + 0, + 0 + } +#endif /*WHEN_MYSQL_BUG_FIXED*/ +}; + + +void *mysql_v4_descriptor= &mysql_descriptor; + From cdc9aa5b5b83e33d5db3d32ef8b58d9de1dc8db9 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 13 Jan 2016 15:24:33 +0400 Subject: [PATCH 08/11] MDEV-9106 Audit Plugin doesn't run with MySQL 5.7. MariaDB 5.5 built in debug gets unhappy with mutexes. Although everything is correct, some DBUG_ASSERT can happen. So this patch keeps safe_mutex silent. --- plugin/server_audit/server_audit.c | 56 +++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index fc224db72ee..00958007ff6 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -1920,11 +1920,8 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) flogger_mutex_lock(&lock_operations); - cn= get_loc_info(thd); - - if (ci_needs_setup(cn) && maria_55_started && - (event_class == MYSQL_AUDIT_GENERAL_CLASS && - *((int*)ev) == MYSQL_AUDIT_GENERAL_ERROR)) + if (maria_55_started && debug_server_started && + event_class == MYSQL_AUDIT_GENERAL_CLASS) { /* There's a bug in MariaDB 5.5 that prevents using thread local @@ -1932,8 +1929,23 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) The 'select * from notexisting_table;' query produces such case. So just use the static buffer in this case. */ - cn= &cn_error_buffer; - cn->header= 1; + const struct mysql_event_general *event = + (const struct mysql_event_general *) ev; + + if (event->event_subclass == MYSQL_AUDIT_GENERAL_ERROR || + (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event->general_query_length == 0 && + cn_error_buffer.query_id == event->query_id)) + { + cn= &cn_error_buffer; + cn->header= 1; + } + else + cn= get_loc_info(thd); + } + else + { + cn= get_loc_info(thd); } update_connection_info(cn, event_class, ev, &after_action); @@ -2474,7 +2486,8 @@ static void update_file_path(MYSQL_THD thd, { char *new_name= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; error_header(); fprintf(stderr, "Log file name was changed to '%s'.\n", new_name); @@ -2510,7 +2523,8 @@ static void update_file_path(MYSQL_THD thd, file_path= path_buffer; exit_func: internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2554,14 +2568,16 @@ static void update_incl_users(MYSQL_THD thd, void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); mark_always_logged(thd); strncpy(incl_user_buffer, new_users, sizeof(incl_user_buffer)); incl_users= incl_user_buffer; user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1); error_header(); fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users); - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2570,14 +2586,16 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); mark_always_logged(thd); strncpy(excl_user_buffer, new_users, sizeof(excl_user_buffer)); excl_users= excl_user_buffer; user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0); error_header(); fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users); - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2653,7 +2671,8 @@ static void update_logging(MYSQL_THD thd, if (new_logging == logging) return; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; if ((logging= new_logging)) { @@ -2670,7 +2689,8 @@ static void update_logging(MYSQL_THD thd, } internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2682,14 +2702,16 @@ static void update_mode(MYSQL_THD thd __attribute__((unused)), if (mode_readonly || new_mode == mode) return; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; mark_always_logged(thd); error_header(); fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode); mode= new_mode; internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } From fe4823d28e1fdd5f76ddf9b6d63b3108d9763cef Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 13 Jan 2016 18:02:44 +0400 Subject: [PATCH 09/11] MDEV-9106 Audit plugin doesnt run with MySQL 5.7. updata thread_pool_server_audit test result. --- .../suite/plugins/r/thread_pool_server_audit.result | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/plugins/r/thread_pool_server_audit.result b/mysql-test/suite/plugins/r/thread_pool_server_audit.result index 69c9bc3a4be..67338506001 100644 --- a/mysql-test/suite/plugins/r/thread_pool_server_audit.result +++ b/mysql-test/suite/plugins/r/thread_pool_server_audit.result @@ -8,6 +8,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users +server_audit_loc_info server_audit_logging OFF server_audit_mode 0 server_audit_output_type file @@ -71,6 +72,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 0 server_audit_output_type file @@ -216,6 +218,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 1 server_audit_output_type file @@ -229,9 +232,9 @@ Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_logging=on',0 TIME,HOSTNAME,root,localhost,ID,0,CONNECT,mysql,,0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,mysql,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID -TIME,HOSTNAME,no_such_user,localhost,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_incl_users=\'odin, root, dva, tri\'',0 TIME,HOSTNAME,root,localhost,ID,ID,CREATE,test,t2, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create table t2 (id int)',0 @@ -281,7 +284,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,READ,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,event, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop database sa_db',0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,sa_db,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create database sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'use sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, From 9c9d10b441fe79b111509949ca40afe1a6284c0f Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Fri, 15 Jan 2016 09:50:27 +0400 Subject: [PATCH 10/11] MDEV-9106 Audit plugin not working with MySQL 5.7. fixing Windows crash. --- plugin/server_audit/server_audit.c | 47 ++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 00958007ff6..d988f7e8317 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -116,18 +116,41 @@ static char *default_home= (char *)"."; #define FLOGGER_SKIP_INCLUDES #define my_open(A, B, C) loc_open(A, B) -#define my_close(A, B) close(A) +#define my_close(A, B) loc_close(A) #define my_rename(A, B, C) loc_rename(A, B) #define my_tell(A, B) loc_tell(A) -#define my_write(A, B, C, D) write(A, B, C) +#define my_write(A, B, C, D) loc_write(A, B, C) #define my_malloc(A, B) malloc(A) #define my_free(A) free(A) +#ifdef my_errno + #undef my_errno +#endif +static int loc_file_errno; +#define my_errno loc_file_errno #ifdef my_vsnprintf #undef my_vsnprintf #endif #define my_vsnprintf vsnprintf +#define logger_open loc_logger_open +#define logger_close loc_logger_close +#define logger_write loc_logger_write +#define logger_rotate loc_logger_rotate +#define logger_init_mutexts loc_logger_init_mutexts -File loc_open(const char *FileName, int Flags) + +static size_t loc_write(File Filedes, const uchar *Buffer, size_t Count) +{ + size_t writtenbytes; +#ifdef _WIN32 + writtenbytes= my_win_write(Filedes, Buffer, Count); +#else + writtenbytes= write(Filedes, Buffer, Count); +#endif + return writtenbytes; +} + + +static File loc_open(const char *FileName, int Flags) /* Path-name of file */ /* Read | write .. */ /* Special flags */ @@ -140,9 +163,27 @@ File loc_open(const char *FileName, int Flags) #else fd = open((char *) FileName, Flags); #endif + my_errno= errno; return fd; } + +static int loc_close(File fd) +{ + int err; +#ifndef _WIN32 + do + { + err= close(fd); + } while (err == -1 && errno == EINTR); +#else + err= my_win_close(fd); +#endif + my_errno=errno; + return err; +} + + static int loc_rename(const char *from, const char *to) { int error = 0; From 666b9663f94c2a4f39d331b28fabd09f2dc43bf2 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 25 Jan 2016 19:03:33 +0100 Subject: [PATCH 11/11] update test results --- mysql-test/suite/plugins/r/thread_pool_server_audit.result | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/plugins/r/thread_pool_server_audit.result b/mysql-test/suite/plugins/r/thread_pool_server_audit.result index 68af53048c5..2577a36cad4 100644 --- a/mysql-test/suite/plugins/r/thread_pool_server_audit.result +++ b/mysql-test/suite/plugins/r/thread_pool_server_audit.result @@ -232,9 +232,9 @@ Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_logging=on',0 TIME,HOSTNAME,root,localhost,ID,0,CONNECT,mysql,,0 -TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,mysql,,0 TIME,HOSTNAME,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID -TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,no_such_user,localhost,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_incl_users=\'odin, root, dva, tri\'',0 TIME,HOSTNAME,root,localhost,ID,ID,CREATE,test,t2, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create table t2 (id int)',0 @@ -292,7 +292,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,READ,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,event, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop database sa_db',0 -TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create database sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'use sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user,