diff options
author | Michael Widenius <monty@mariadb.org> | 2014-03-11 17:49:09 +0200 |
---|---|---|
committer | Michael Widenius <monty@mariadb.org> | 2014-03-11 17:49:09 +0200 |
commit | 172784965981c7d06ddb50457f0178da25104b9e (patch) | |
tree | b0759d12a5abd3acf49ac11d4f601d46bc6c5e0a | |
parent | 23af77d26e3ff336e415a5d8bf3f4c4a7bc84a18 (diff) | |
parent | 800a278fd09c353a02f02c9c32074d3807861967 (diff) | |
download | mariadb-git-172784965981c7d06ddb50457f0178da25104b9e.tar.gz |
Merge with 5.5
-rw-r--r-- | debian/dist/Debian/mariadb-server-5.5.files.in | 1 | ||||
-rw-r--r-- | debian/dist/Ubuntu/mariadb-server-5.5.files.in | 1 | ||||
-rw-r--r-- | extra/replace.c | 4 | ||||
-rw-r--r-- | mysql-test/r/stack-crash.result | 93 | ||||
-rw-r--r-- | mysql-test/suite/plugins/r/server_audit.result | 203 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/server_audit.opt | 2 | ||||
-rw-r--r-- | mysql-test/suite/plugins/t/server_audit.test | 77 | ||||
-rw-r--r-- | mysql-test/t/stack-crash.test | 96 | ||||
-rw-r--r-- | plugin/server_audit/CMakeLists.txt | 16 | ||||
-rw-r--r-- | plugin/server_audit/COPYING | 339 | ||||
-rw-r--r-- | plugin/server_audit/server_audit.c | 1836 | ||||
-rw-r--r-- | sql/sql_audit.cc | 4 | ||||
-rw-r--r-- | sql/sql_audit.h | 12 | ||||
-rw-r--r-- | storage/innobase/fil/fil0fil.c | 27 | ||||
-rw-r--r-- | storage/maria/ma_blockrec.c | 22 | ||||
-rw-r--r-- | storage/maria/ma_dynrec.c | 32 | ||||
-rw-r--r-- | storage/maria/ma_unique.c | 6 | ||||
-rw-r--r-- | storage/maria/maria_def.h | 1 | ||||
-rw-r--r-- | storage/maria/maria_pack.c | 10 | ||||
-rw-r--r-- | storage/myisam/mi_check.c | 2 | ||||
-rw-r--r-- | storage/myisam/mi_checksum.c | 2 | ||||
-rw-r--r-- | storage/xtradb/fil/fil0fil.c | 38 |
22 files changed, 2759 insertions, 65 deletions
diff --git a/debian/dist/Debian/mariadb-server-5.5.files.in b/debian/dist/Debian/mariadb-server-5.5.files.in index eb9ba7a9a2c..5682adbdce0 100644 --- a/debian/dist/Debian/mariadb-server-5.5.files.in +++ b/debian/dist/Debian/mariadb-server-5.5.files.in @@ -7,6 +7,7 @@ usr/lib/mysql/plugin/semisync_master.so usr/lib/mysql/plugin/semisync_slave.so usr/lib/mysql/plugin/handlersocket.so usr/lib/mysql/plugin/sql_errlog.so +usr/lib/mysql/plugin/server_audit.so usr/lib/libhsclient.so.* etc/mysql/debian-start etc/mysql/conf.d/mysqld_safe_syslog.cnf diff --git a/debian/dist/Ubuntu/mariadb-server-5.5.files.in b/debian/dist/Ubuntu/mariadb-server-5.5.files.in index 4921d3fa267..06a6643d650 100644 --- a/debian/dist/Ubuntu/mariadb-server-5.5.files.in +++ b/debian/dist/Ubuntu/mariadb-server-5.5.files.in @@ -7,6 +7,7 @@ usr/lib/mysql/plugin/semisync_master.so usr/lib/mysql/plugin/semisync_slave.so usr/lib/mysql/plugin/handlersocket.so usr/lib/mysql/plugin/sql_errlog.so +usr/lib/mysql/plugin/server_audit.so usr/lib/libhsclient.so.* etc/apparmor.d/usr.sbin.mysqld usr/share/apport/package-hooks/source_mariadb-5.5.py diff --git a/extra/replace.c b/extra/replace.c index c2dcc9f50b5..c4105e45973 100644 --- a/extra/replace.c +++ b/extra/replace.c @@ -265,7 +265,7 @@ static int insert_pointer_name(reg1 POINTER_ARRAY *pa,char * name) if (!(pa->str= (uchar*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD), MYF(MY_WME)))) { - my_free((char*) pa->typelib.type_names); + my_free((void*) pa->typelib.type_names); DBUG_RETURN (-1); } pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(uchar*)+ @@ -327,7 +327,7 @@ static void free_pointer_array(reg1 POINTER_ARRAY *pa) if (pa->typelib.count) { pa->typelib.count=0; - my_free((char*) pa->typelib.type_names); + my_free((void*) pa->typelib.type_names); pa->typelib.type_names=0; my_free(pa->str); } diff --git a/mysql-test/r/stack-crash.result b/mysql-test/r/stack-crash.result new file mode 100644 index 00000000000..d0a25837675 --- /dev/null +++ b/mysql-test/r/stack-crash.result @@ -0,0 +1,93 @@ +drop table if exists t1,t2; +Warnings: +Note 1051 Unknown table 't1' +Note 1051 Unknown table 't2' +CREATE TABLE t1 ( +`sspo_id` int(11) NOT NULL AUTO_INCREMENT, +`sspo_uid` int(11) NOT NULL DEFAULT '0', +`sspo_type` varchar(1) NOT NULL DEFAULT 'P', +`sspo_text` longtext NOT NULL, +`sspo_image` varchar(255) NOT NULL, +`sspo_source` int(11) NOT NULL DEFAULT '0', +`sspo_event_name` varchar(255) NOT NULL DEFAULT '', +`sspo_event_location` varchar(255) NOT NULL DEFAULT '', +`sspo_event_date` datetime DEFAULT NULL, +`sspo_remote_title` varchar(255) NOT NULL, +`sspo_remote_url` varchar(255) NOT NULL, +`sspo_remote_desc` text NOT NULL, +`sspo_remote_image` varchar(255) NOT NULL, +`sspo_obj_status` varchar(1) NOT NULL DEFAULT 'A', +`sspo_cr_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', +`sspo_cr_uid` int(11) NOT NULL DEFAULT '0', +`sspo_lu_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', +`sspo_lu_uid` int(11) NOT NULL DEFAULT '0', +PRIMARY KEY (`sspo_id`), +KEY `post_uid` (`sspo_uid`,`sspo_cr_date`) +) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8; +Warnings: +Warning 1286 Unknown storage engine 'InnoDB' +Warning 1266 Using storage engine MyISAM for table 't1' +INSERT INTO t1 VALUES (1,2,'P','test1','',0,'','',NULL,'','','','','A','2013-09-30 00:19:32',2,'2013-09-30 00:19:32',2),(2,2,'P','bbb','',0,'','',NULL,'','','','','A','2013-10-02 15:06:35',2,'2013-10-02 15:06:35',2); +CREATE TABLE `t2` ( +`spoo_id` int(11) NOT NULL AUTO_INCREMENT, +`spoo_user_type_id` int(11) NOT NULL DEFAULT '0', +`spoo_uid` int(11) NOT NULL DEFAULT '0', +`spoo_option_id` int(11) NOT NULL DEFAULT '0', +`spoo_value` varchar(10000) NOT NULL, +`spoo_obj_status` varchar(1) NOT NULL DEFAULT 'A', +`spoo_cr_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', +`spoo_cr_uid` int(11) NOT NULL DEFAULT '0', +`spoo_lu_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', +`spoo_lu_uid` int(11) NOT NULL DEFAULT '0', +PRIMARY KEY (`spoo_id`), +KEY `object_option_main_idx` (`spoo_user_type_id`,`spoo_uid`,`spoo_option_id`,`spoo_value`(255)) +) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8; +Warnings: +Warning 1286 Unknown storage engine 'InnoDB' +Warning 1266 Using storage engine MyISAM for table 't2' +INSERT INTO `t2` VALUES (19,1,2,6,'Dortmund','A','2013-09-26 01:36:51',2,'2013-09-26 01:36:51',2),(20,1,2,8,'49','A','2013-09-26 01:36:51',2,'2013-09-26 01:36:51',2); +SELECT Count(*) +FROM t1 AS tbl +LEFT JOIN t2 a +ON a.spoo_uid = sspo_uid +AND a.spoo_option_id = 1 +LEFT JOIN t2 b +ON b.spoo_uid = sspo_uid +AND b.spoo_option_id = 2 +LEFT JOIN t2 c +ON c.spoo_uid = sspo_uid +AND c.spoo_option_id = 3 +LEFT JOIN t2 d +ON d.spoo_uid = sspo_uid +AND d.spoo_option_id = 5 +LEFT JOIN t2 e +ON e.spoo_uid = sspo_uid +AND e.spoo_option_id = 4 +LEFT JOIN t2 f +ON f.spoo_uid = sspo_uid +AND f.spoo_option_id = 11 +LEFT JOIN t2 g +ON g.spoo_uid = sspo_uid +AND g.spoo_option_id = 7 +LEFT JOIN t2 h +ON h.spoo_uid = sspo_uid +AND h.spoo_option_id = 10 +LEFT JOIN t2 i +ON i.spoo_uid = sspo_uid +AND i.spoo_option_id = 18 +LEFT JOIN t2 j +ON j.spoo_uid = sspo_uid +AND j.spoo_option_id = 6 +GROUP BY a.spoo_value, +b.spoo_value, +c.spoo_value, +d.spoo_value, +e.spoo_value, +f.spoo_value, +g.spoo_value, +h.spoo_value, +i.spoo_value, +j.spoo_value; +Count(*) +2 +drop table t1,t2; diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result new file mode 100644 index 00000000000..f5c925a13dd --- /dev/null +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -0,0 +1,203 @@ +install plugin server_audit soname 'server_audit'; +show variables like 'server_audit%'; +Variable_name Value +server_audit_events +server_audit_excl_users +server_audit_file_path server_audit.log +server_audit_file_rotate_now OFF +server_audit_file_rotate_size 1000000 +server_audit_file_rotations 9 +server_audit_incl_users +server_audit_logging OFF +server_audit_mode 0 +server_audit_output_type file +server_audit_syslog_facility LOG_USER +server_audit_syslog_ident mysql-server_auditing +server_audit_syslog_info +server_audit_syslog_priority LOG_INFO +set global server_audit_file_path='server_audit.log'; +set global server_audit_output_type=file; +set global server_audit_logging=on; +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +set global server_audit_incl_users='odin, dva, tri'; +create table t1 (id int); +set global server_audit_incl_users='odin, root, dva, tri'; +create table t2 (id int); +set global server_audit_excl_users='odin, dva, tri'; +Warnings: +Warning 1 User 'odin' is in the server_audit_incl_users, so wasn't added. +Warning 1 User 'dva' is in the server_audit_incl_users, so wasn't added. +Warning 1 User 'tri' is in the server_audit_incl_users, so wasn't added. +insert into t1 values (1), (2); +select * from t1; +id +1 +2 +set global server_audit_incl_users='odin, root, dva, tri'; +insert into t2 values (1), (2); +select * from t2; +id +1 +2 +alter table t1 rename renamed_t1; +set global server_audit_events='connect,query'; +insert into t2 values (1), (2); +select * from t2; +id +1 +2 +1 +2 +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'server_audit%'; +Variable_name Value +server_audit_events CONNECT,QUERY +server_audit_excl_users +server_audit_file_path server_audit.log +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_logging ON +server_audit_mode 0 +server_audit_output_type file +server_audit_syslog_facility LOG_USER +server_audit_syslog_ident mysql-server_auditing +server_audit_syslog_info +server_audit_syslog_priority LOG_INFO +set global server_audit_mode=1; +set global server_audit_events=''; +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +set global server_audit_file_path='.'; +show status like 'server_audit_current_log'; +Variable_name Value +Server_audit_current_log HOME_DIR/server_audit.log +set global server_audit_file_path=''; +show status like 'server_audit_current_log'; +Variable_name Value +Server_audit_current_log server_audit.log +set global server_audit_file_path=' '; +show status like 'server_audit_current_log'; +Variable_name Value +Server_audit_current_log server_audit.log +set global server_audit_file_path='nonexisting_dir/'; +Warnings: +Warning 1 SERVER AUDIT plugin can't create file 'nonexisting_dir/'. +show status like 'server_audit_current_log'; +Variable_name Value +Server_audit_current_log server_audit.log +show variables like 'server_audit%'; +Variable_name Value +server_audit_events +server_audit_excl_users +server_audit_file_path +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_logging ON +server_audit_mode 1 +server_audit_output_type file +server_audit_syslog_facility LOG_USER +server_audit_syslog_ident mysql-server_auditing +server_audit_syslog_info +server_audit_syslog_priority LOG_INFO +uninstall plugin server_audit; +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,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID +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 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_excl_users=\'odin, dva, tri\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'SHOW WARNINGS',0 +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'insert into t1 values (1), (2)',0 +TIME,HOSTNAME,root,localhost,ID,ID,READ,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'select * from t1',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,WRITE,test,t2, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'insert into t2 values (1), (2)',0 +TIME,HOSTNAME,root,localhost,ID,ID,READ,test,t2, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'select * from t2',0 +TIME,HOSTNAME,root,localhost,ID,ID,READ,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,ALTER,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,table_stats, +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,column_stats, +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,index_stats, +TIME,HOSTNAME,root,localhost,ID,ID,RENAME,test,t1|test.renamed_t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'alter table t1 rename renamed_t1',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_events=\'connect,query\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'insert into t2 values (1), (2)',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'select * from t2',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'select * from t_doesnt_exist',ID +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'syntax_error_query',ID +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'drop table renamed_t1, t2',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show variables like \'server_audit%\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_mode=1',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_events=\'\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create database sa_db',0 +TIME,HOSTNAME,root,localhost,ID,0,CONNECT,test,,0 +TIME,HOSTNAME,root,localhost,ID,ID,CREATE,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create table t1 (id2 int)',0 +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'insert into t1 values (1), (2)',0 +TIME,HOSTNAME,root,localhost,ID,ID,READ,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'select * from t1',0 +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,table_stats, +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,column_stats, +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,index_stats, +TIME,HOSTNAME,root,localhost,ID,ID,DROP,test,t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'drop table t1',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'use sa_db',0 +TIME,HOSTNAME,root,localhost,ID,ID,CREATE,sa_db,sa_t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'create table sa_t1(id int)',0 +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,sa_db,sa_t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'insert into sa_t1 values (1), (2)',0 +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,table_stats, +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,column_stats, +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,index_stats, +TIME,HOSTNAME,root,localhost,ID,ID,DROP,sa_db,sa_t1, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop table sa_t1',0 +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,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\'.\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\'.\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show status like \'server_audit_current_log\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\'\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\'\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show status like \'server_audit_current_log\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\' \'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\' \'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show status like \'server_audit_current_log\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\'nonexisting_dir/\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_file_path=\'nonexisting_dir/\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'SHOW WARNINGS',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show status like \'server_audit_current_log\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show variables like \'server_audit%\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,plugin, +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'uninstall plugin server_audit',0 diff --git a/mysql-test/suite/plugins/t/server_audit.opt b/mysql-test/suite/plugins/t/server_audit.opt new file mode 100644 index 00000000000..089ed94b688 --- /dev/null +++ b/mysql-test/suite/plugins/t/server_audit.opt @@ -0,0 +1,2 @@ +--thread_handling='one-thread-per-connection' + diff --git a/mysql-test/suite/plugins/t/server_audit.test b/mysql-test/suite/plugins/t/server_audit.test new file mode 100644 index 00000000000..f62980259c0 --- /dev/null +++ b/mysql-test/suite/plugins/t/server_audit.test @@ -0,0 +1,77 @@ + +--source include/not_embedded.inc + +if (!$SERVER_AUDIT_SO) { + skip No SERVER_AUDIT plugin; +} + +install plugin server_audit soname 'server_audit'; + +show variables like 'server_audit%'; +set global server_audit_file_path='server_audit.log'; +set global server_audit_output_type=file; +set global server_audit_logging=on; +connect (con1,localhost,root,,mysql); +connection default; +disconnect con1; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_ACCESS_DENIED_ERROR +connect (con1,localhost,no_such_user,,mysql); +connection default; +--sleep 2 +set global server_audit_incl_users='odin, dva, tri'; +create table t1 (id int); +set global server_audit_incl_users='odin, root, dva, tri'; +create table t2 (id int); +set global server_audit_excl_users='odin, dva, tri'; +insert into t1 values (1), (2); +select * from t1; +set global server_audit_incl_users='odin, root, dva, tri'; +insert into t2 values (1), (2); +select * from t2; +alter table t1 rename renamed_t1; +set global server_audit_events='connect,query'; +insert into t2 values (1), (2); +select * from t2; +--error ER_NO_SUCH_TABLE +select * from t_doesnt_exist; +--error 1064 +syntax_error_query; +drop table renamed_t1, t2; +show variables like 'server_audit%'; +set global server_audit_mode=1; +set global server_audit_events=''; +create database sa_db; +connect (con1,localhost,root,,test); +connection con1; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +connection default; +disconnect con1; + +--sleep 2 +set global server_audit_file_path='.'; +--replace_regex /\.[\\\/]/HOME_DIR\// +show status like 'server_audit_current_log'; +set global server_audit_file_path=''; +show status like 'server_audit_current_log'; +set global server_audit_file_path=' '; +show status like 'server_audit_current_log'; +set global server_audit_file_path='nonexisting_dir/'; +show status like 'server_audit_current_log'; +show variables like 'server_audit%'; +uninstall plugin server_audit; + +let $MYSQLD_DATADIR= `SELECT @@datadir`; +# replace the timestamp and the hostname with constant values +--replace_regex /[0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9]\,[^,]*\,/TIME,HOSTNAME,/ /\,[1-9][0-9]*\,/,1,/ /\,[1-9][0-9]*/,ID/ +cat_file $MYSQLD_DATADIR/server_audit.log; +remove_file $MYSQLD_DATADIR/server_audit.log; + diff --git a/mysql-test/t/stack-crash.test b/mysql-test/t/stack-crash.test new file mode 100644 index 00000000000..d5bdccc132d --- /dev/null +++ b/mysql-test/t/stack-crash.test @@ -0,0 +1,96 @@ +# +# Test to ensure that we don't get stack overflows +# + +drop table if exists t1,t2; + +# +# MDEV-5724 +# Server crashes on SQL select containing more group by and left join +# statements +# This was because record_buffer was 300,000 bytes and caused stack overflow +# + +CREATE TABLE t1 ( + `sspo_id` int(11) NOT NULL AUTO_INCREMENT, + `sspo_uid` int(11) NOT NULL DEFAULT '0', + `sspo_type` varchar(1) NOT NULL DEFAULT 'P', + `sspo_text` longtext NOT NULL, + `sspo_image` varchar(255) NOT NULL, + `sspo_source` int(11) NOT NULL DEFAULT '0', + `sspo_event_name` varchar(255) NOT NULL DEFAULT '', + `sspo_event_location` varchar(255) NOT NULL DEFAULT '', + `sspo_event_date` datetime DEFAULT NULL, + `sspo_remote_title` varchar(255) NOT NULL, + `sspo_remote_url` varchar(255) NOT NULL, + `sspo_remote_desc` text NOT NULL, + `sspo_remote_image` varchar(255) NOT NULL, + `sspo_obj_status` varchar(1) NOT NULL DEFAULT 'A', + `sspo_cr_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', + `sspo_cr_uid` int(11) NOT NULL DEFAULT '0', + `sspo_lu_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', + `sspo_lu_uid` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`sspo_id`), + KEY `post_uid` (`sspo_uid`,`sspo_cr_date`) +) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8; +INSERT INTO t1 VALUES (1,2,'P','test1','',0,'','',NULL,'','','','','A','2013-09-30 00:19:32',2,'2013-09-30 00:19:32',2),(2,2,'P','bbb','',0,'','',NULL,'','','','','A','2013-10-02 15:06:35',2,'2013-10-02 15:06:35',2); + +CREATE TABLE `t2` ( + `spoo_id` int(11) NOT NULL AUTO_INCREMENT, + `spoo_user_type_id` int(11) NOT NULL DEFAULT '0', + `spoo_uid` int(11) NOT NULL DEFAULT '0', + `spoo_option_id` int(11) NOT NULL DEFAULT '0', + `spoo_value` varchar(10000) NOT NULL, + `spoo_obj_status` varchar(1) NOT NULL DEFAULT 'A', + `spoo_cr_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', + `spoo_cr_uid` int(11) NOT NULL DEFAULT '0', + `spoo_lu_date` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', + `spoo_lu_uid` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`spoo_id`), + KEY `object_option_main_idx` (`spoo_user_type_id`,`spoo_uid`,`spoo_option_id`,`spoo_value`(255)) +) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8; +INSERT INTO `t2` VALUES (19,1,2,6,'Dortmund','A','2013-09-26 01:36:51',2,'2013-09-26 01:36:51',2),(20,1,2,8,'49','A','2013-09-26 01:36:51',2,'2013-09-26 01:36:51',2); + +SELECT Count(*) +FROM t1 AS tbl + LEFT JOIN t2 a + ON a.spoo_uid = sspo_uid + AND a.spoo_option_id = 1 + LEFT JOIN t2 b + ON b.spoo_uid = sspo_uid + AND b.spoo_option_id = 2 + LEFT JOIN t2 c + ON c.spoo_uid = sspo_uid + AND c.spoo_option_id = 3 + LEFT JOIN t2 d + ON d.spoo_uid = sspo_uid + AND d.spoo_option_id = 5 + LEFT JOIN t2 e + ON e.spoo_uid = sspo_uid + AND e.spoo_option_id = 4 + LEFT JOIN t2 f + ON f.spoo_uid = sspo_uid + AND f.spoo_option_id = 11 + LEFT JOIN t2 g + ON g.spoo_uid = sspo_uid + AND g.spoo_option_id = 7 + LEFT JOIN t2 h + ON h.spoo_uid = sspo_uid + AND h.spoo_option_id = 10 + LEFT JOIN t2 i + ON i.spoo_uid = sspo_uid + AND i.spoo_option_id = 18 + LEFT JOIN t2 j + ON j.spoo_uid = sspo_uid + AND j.spoo_option_id = 6 +GROUP BY a.spoo_value, + b.spoo_value, + c.spoo_value, + d.spoo_value, + e.spoo_value, + f.spoo_value, + g.spoo_value, + h.spoo_value, + i.spoo_value, + j.spoo_value; +drop table t1,t2; diff --git a/plugin/server_audit/CMakeLists.txt b/plugin/server_audit/CMakeLists.txt new file mode 100644 index 00000000000..527d0d702da --- /dev/null +++ b/plugin/server_audit/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (C) 2013 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 +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +MYSQL_ADD_PLUGIN(server_audit server_audit.c MODULE_ONLY) diff --git a/plugin/server_audit/COPYING b/plugin/server_audit/COPYING new file mode 100644 index 00000000000..d511905c164 --- /dev/null +++ b/plugin/server_audit/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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; either version 2 of the License, or + (at your option) any later version. + + 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c new file mode 100644 index 00000000000..f45701db8a9 --- /dev/null +++ b/plugin/server_audit/server_audit.c @@ -0,0 +1,1836 @@ +/* Copyright (C) 2013 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#define PLUGIN_VERSION 0x101 +#define PLUGIN_STR_VERSION "1.1.5" + +#include <stdio.h> +#include <time.h> +#include <string.h> + +#ifndef _WIN32 +#include <syslog.h> +#else +#define syslog(PRIORITY, FORMAT, INFO, MESSAGE_LEN, MESSAGE) do {}while(0) +static void closelog() {} +#define openlog(IDENT, LOG_NOWAIT, LOG_USER) do {}while(0) + +/* priorities */ +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) + +/* facility codes */ +#define LOG_KERN (0<<3) /* kernel messages */ +#define LOG_USER (1<<3) /* random user-level messages */ +#define LOG_MAIL (2<<3) /* mail system */ +#define LOG_DAEMON (3<<3) /* system daemons */ +#define LOG_AUTH (4<<3) /* security/authorization messages */ +#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ +#define LOG_LPR (6<<3) /* line printer subsystem */ +#define LOG_NEWS (7<<3) /* network news subsystem */ +#define LOG_UUCP (8<<3) /* UUCP subsystem */ +#define LOG_CRON (9<<3) /* clock daemon */ +#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */ +#define LOG_FTP (11<<3) /* ftp daemon */ +#define LOG_LOCAL0 (16<<3) /* reserved for local use */ +#define LOG_LOCAL1 (17<<3) /* reserved for local use */ +#define LOG_LOCAL2 (18<<3) /* reserved for local use */ +#define LOG_LOCAL3 (19<<3) /* reserved for local use */ +#define LOG_LOCAL4 (20<<3) /* reserved for local use */ +#define LOG_LOCAL5 (21<<3) /* reserved for local use */ +#define LOG_LOCAL6 (22<<3) /* reserved for local use */ +#define LOG_LOCAL7 (23<<3) /* reserved for local use */ + +#endif /*!_WIN32*/ + +/* + Defines that can be used to reshape the pluging: + #define MARIADB_ONLY + #define USE_MARIA_PLUGIN_INTERFACE +*/ + +#if !defined(MYSQL_DYNAMIC_PLUGIN) && !defined(MARIADB_ONLY) +#define MARIADB_ONLY +#endif /*MYSQL_PLUGIN_DYNAMIC*/ + +#ifndef MARIADB_ONLY +#define MYSQL_SERVICE_LOGGER_INCLUDED +#endif /*MARIADB_ONLY*/ + +#include <my_base.h> +//#include <hash.h> +#include <my_dir.h> +#include <typelib.h> +#include <mysql/plugin.h> +#include <mysql/plugin_audit.h> + +#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_init2 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); + +#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) + +#include "../../mysys/file_logger.c" +#endif /*!MARIADB_ONLY*/ + +#ifndef DBUG_OFF +#define PLUGIN_DEBUG_VERSION "-debug" +#else +#define PLUGIN_DEBUG_VERSION "" +#endif /*DBUG_OFF*/ +/* + Disable __attribute__() on non-gcc compilers. +*/ +#if !defined(__attribute__) && !defined(__GNUC__) +#define __attribute__(A) +#endif + +#ifdef _WIN32 +#define localtime_r(a, b) localtime_s(b, a) +#endif /*WIN32*/ + + +extern char server_version[]; +static const char *serv_ver= NULL; +static int started_mysql= 0; +static int maria_above_5= 0; +static char *incl_users, *excl_users, + *file_path, *syslog_info; +static char path_buffer[FN_REFLEN]; +static unsigned int mode, mode_readonly= 0; +static ulong output_type; +static ulong syslog_facility, syslog_priority; + +static ulonglong events; /* mask for events to log */ +static unsigned long long file_rotate_size; +static unsigned int rotations; +static my_bool rotate= TRUE; +static char logging; +static int internal_stop_logging= 0; +static char incl_user_buffer[1024]; +static char excl_user_buffer[1024]; + +static char servhost[256]; +static size_t servhost_len; +static char *syslog_ident; +static char syslog_ident_buffer[128]= "mysql-server_auditing"; +#define DEFAULT_FILENAME_LEN 16 +static char default_file_name[DEFAULT_FILENAME_LEN+1]= "server_audit.log"; + +static void update_file_path(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_excl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_output_type(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_syslog_facility(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_syslog_priority(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_mode(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_logging(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void update_syslog_ident(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +static void rotate_log(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); + +static MYSQL_SYSVAR_STR(incl_users, incl_users, PLUGIN_VAR_RQCMDARG, + "Comma separated list of users to monitor.", + NULL, update_incl_users, NULL); +static MYSQL_SYSVAR_STR(excl_users, excl_users, PLUGIN_VAR_RQCMDARG, + "Comma separated list of users to exclude from auditing.", + NULL, update_excl_users, NULL); +/* bits in the event filter. */ +#define EVENT_CONNECT 1 +#define EVENT_QUERY 2 +#define EVENT_TABLE 4 +static const char *event_names[]= +{ + "CONNECT", "QUERY", "TABLE", + NULL +}; +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.", + NULL, NULL, 0, &events_typelib); +#define OUTPUT_SYSLOG 0 +#define OUTPUT_FILE 1 +#define OUTPUT_NO 0xFFFF +static const char *output_type_names[]= { "syslog", "file", 0 }; +static TYPELIB output_typelib= +{ + array_elements(output_type_names) - 1, "output_typelib", + output_type_names, NULL +}; +static MYSQL_SYSVAR_ENUM(output_type, output_type, PLUGIN_VAR_RQCMDARG, + "Desired output type. Possible values - 'syslog', 'file'" + " or 'null' as no output.", 0, update_output_type, OUTPUT_FILE, + &output_typelib); +static MYSQL_SYSVAR_STR(file_path, file_path, PLUGIN_VAR_RQCMDARG, + "Path to the log file.", NULL, update_file_path, default_file_name); +static MYSQL_SYSVAR_ULONGLONG(file_rotate_size, file_rotate_size, + PLUGIN_VAR_RQCMDARG, "Maximum size of the log to start the rotation.", + NULL, NULL, + 1000000, 100, ((long long) 0x7FFFFFFFFFFFFFFFLL), 1); +static MYSQL_SYSVAR_UINT(file_rotations, rotations, + PLUGIN_VAR_RQCMDARG, "Number of rotations before log is removed.", + NULL, NULL, 9, 0, 999, 1); +static MYSQL_SYSVAR_BOOL(file_rotate_now, rotate, PLUGIN_VAR_OPCMDARG, + "Force log rotation now.", NULL, rotate_log, FALSE); +static MYSQL_SYSVAR_BOOL(logging, logging, + PLUGIN_VAR_OPCMDARG, "Turn on/off the logging.", NULL, + update_logging, 0); +static MYSQL_SYSVAR_UINT(mode, mode, + PLUGIN_VAR_OPCMDARG, "Auditing mode.", NULL, update_mode, 0, 0, 1, 1); +static MYSQL_SYSVAR_STR(syslog_ident, syslog_ident, PLUGIN_VAR_RQCMDARG, + "The SYSLOG identifier - the beginning of each SYSLOG record.", + NULL, update_syslog_ident, syslog_ident_buffer); +static MYSQL_SYSVAR_STR(syslog_info, syslog_info, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, + "The <info> string to be added to the SYSLOG record.", NULL, NULL, ""); + +static const char *syslog_facility_names[]= +{ + "LOG_USER", "LOG_MAIL", "LOG_DAEMON", "LOG_AUTH", + "LOG_SYSLOG", "LOG_LPR", "LOG_NEWS", "LOG_UUCP", + "LOG_CRON", "LOG_AUTHPRIV", "LOG_FTP", + "LOG_LOCAL0", "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", + "LOG_LOCAL4", "LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", + 0 +}; +static unsigned int syslog_facility_codes[]= +{ + LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, + LOG_SYSLOG, LOG_LPR, LOG_NEWS, LOG_UUCP, + LOG_CRON, LOG_AUTHPRIV, LOG_FTP, + LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, + LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, +}; +static TYPELIB syslog_facility_typelib= +{ + array_elements(syslog_facility_names) - 1, "syslog_facility_typelib", + syslog_facility_names, NULL +}; +static MYSQL_SYSVAR_ENUM(syslog_facility, syslog_facility, PLUGIN_VAR_RQCMDARG, + "The 'facility' parameter of the SYSLOG record." + " The default is LOG_USER.", 0, update_syslog_facility, 0/*LOG_USER*/, + &syslog_facility_typelib); + +static const char *syslog_priority_names[]= +{ + "LOG_EMERG", "LOG_ALERT", "LOG_CRIT", "LOG_ERR", + "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG", + 0 +}; + +static unsigned int syslog_priority_codes[]= +{ + LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, + LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG, +}; + +static TYPELIB syslog_priority_typelib= +{ + array_elements(syslog_priority_names) - 1, "syslog_priority_typelib", + syslog_priority_names, NULL +}; +static MYSQL_SYSVAR_ENUM(syslog_priority, syslog_priority, PLUGIN_VAR_RQCMDARG, + "The 'priority' parameter of the SYSLOG record." + " The default is LOG_INFO.", 0, update_syslog_priority, 6/*LOG_INFO*/, + &syslog_priority_typelib); + + +static struct st_mysql_sys_var* vars[] = { + MYSQL_SYSVAR(incl_users), + MYSQL_SYSVAR(excl_users), + MYSQL_SYSVAR(events), + MYSQL_SYSVAR(output_type), + MYSQL_SYSVAR(file_path), + MYSQL_SYSVAR(file_rotate_size), + MYSQL_SYSVAR(file_rotations), + MYSQL_SYSVAR(file_rotate_now), + MYSQL_SYSVAR(logging), + MYSQL_SYSVAR(mode), + MYSQL_SYSVAR(syslog_info), + MYSQL_SYSVAR(syslog_ident), + MYSQL_SYSVAR(syslog_facility), + MYSQL_SYSVAR(syslog_priority), + NULL +}; + + +/* Status variables for SHOW STATUS */ +static int is_active= 0; +static long log_write_failures= 0; +static char current_log_buf[FN_REFLEN]= ""; +static char last_error_buf[512]= ""; + +static struct st_mysql_show_var audit_status[]= +{ + {"server_audit_active", (char *)&is_active, SHOW_BOOL}, + {"server_audit_current_log", current_log_buf, SHOW_CHAR}, + {"server_audit_writes_failed", (char *)&log_write_failures, SHOW_LONG}, + {"server_audit_last_error", last_error_buf, SHOW_CHAR}, + {0,0,0} +}; + +#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) +/* These belong to the service initialization */ +static PSI_mutex_key key_LOCK_operations; +static PSI_mutex_info mutex_key_list[]= +{{ &key_LOCK_operations, "SERVER_AUDIT_plugin::lock_operations", + PSI_FLAG_GLOBAL}}; +#endif +static mysql_mutex_t lock_operations; + +/* The Percona server and partly MySQL don't support */ +/* launching client errors in the 'update_variable' methods. */ +/* So the client errors just disabled for them. */ +/* The possible solution is to implement the 'check_variable'*/ +/* methods there properly, but at the moment i'm not sure it */ +/* worths doing. */ +#define CLIENT_ERROR if (!started_mysql) my_printf_error + +static uchar *getkey_user(const char *entry, size_t *length, + my_bool nu __attribute__((unused)) ) +{ + const char *e= entry; + while (*e && *e != ' ' && *e != ',') + ++e; + *length= e - entry; + return (uchar *) entry; +} + + +static void blank_user(uchar *user) +{ + for (; *user && *user != ','; user++) + *user= ' '; +} + + +static void remove_user(char *user) +{ + char *start_user= user; + while (*user != ',') + { + if (*user == 0) + { + *start_user= 0; + return; + } + user++; + } + user++; + while (*user == ' ') + user++; + + do { + *(start_user++)= *user; + } while (*(user++)); +} + + +static void remove_blanks(char *user) +{ + char *user_orig= user; + char *user_to= user; + char *start_tok; + int blank_name; + + while (*user != 0) + { + start_tok= user; + blank_name= 1; + while (*user !=0 && *user != ',') + { + if (*user != ' ') + blank_name= 0; + user++; + } + if (!blank_name) + { + while (start_tok <= user) + *(user_to++)= *(start_tok++); + } + if (*user == ',') + user++; + } + if (user_to > user_orig && user_to[-1] == ',') + user_to--; + *user_to= 0; +} + + +static int user_hash_fill(HASH *h, char *users, + HASH *cmp_hash, int take_over_cmp) +{ + char *orig_users= users; + uchar *cmp_user= 0; + size_t cmp_length; + int refill_cmp_hash= 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); + + while (*users) + { + while (*users == ' ') + users++; + if (!*users) + return 0; + + + if (cmp_hash) + { + (void) getkey_user(users, &cmp_length, FALSE); + cmp_user= my_hash_search(cmp_hash, (const uchar *) users, cmp_length); + + if (cmp_user && take_over_cmp) + { + internal_stop_logging= 1; + CLIENT_ERROR(1, "User '%.*s' was removed from the" + " server_audit_excl_users.", + MYF(ME_JUST_WARNING), (int) cmp_length, users); + internal_stop_logging= 0; + blank_user(cmp_user); + refill_cmp_hash= 1; + } + else if (cmp_user) + { + internal_stop_logging= 1; + CLIENT_ERROR(1, "User '%.*s' is in the server_audit_incl_users, " + "so wasn't added.", MYF(ME_JUST_WARNING), (int) cmp_length, users); + internal_stop_logging= 0; + remove_user(users); + continue; + } + } + if (my_hash_insert(h, (const uchar *) users)) + return 1; + while (*users && *users != ',') + users++; + if (!*users) + break; + users++; + } + + if (refill_cmp_hash) + { + remove_blanks(excl_users); + return user_hash_fill(cmp_hash, excl_users, 0, 0); + } + + if (users > orig_users && users[-1] == ',') + users[-1]= 0; + + return 0; +} + + +static void error_header() +{ + struct tm tm_time; + time_t curtime; + + (void) time(&curtime); + (void) localtime_r(&curtime, &tm_time); + + (void) fprintf(stderr,"%02d%02d%02d %2d:%02d:%02d server_audit: ", + tm_time.tm_year % 100, tm_time.tm_mon + 1, + tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec); +} + + +static LOGGER_HANDLE *logfile; +static HASH incl_user_hash, excl_user_hash; +static unsigned long long query_counter= 1; + +struct connection_info +{ + 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))); +} + + +void free_connection(void* pconn) +{ + (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)); +} + + +static void get_str_n(char *dest, int *dest_len, size_t dest_size, + const char *src, size_t src_len) +{ + if (src_len >= dest_size) + src_len= dest_size - 1; + + memcpy(dest, src, src_len); + dest[src_len]= 0; + *dest_len= src_len; +} + + +static int get_user_host(const char *uh_line, unsigned int uh_len, + char *buffer, size_t buf_len, + size_t *user_len, size_t *host_len, size_t *ip_len) +{ + const char *buf_end= buffer + buf_len - 1; + const char *buf_start; + const char *uh_end= uh_line + uh_len; + + while (uh_line < uh_end && *uh_line != '[') + ++uh_line; + + if (uh_line == uh_end) + return 1; + ++uh_line; + + buf_start= buffer; + while (uh_line < uh_end && *uh_line != ']') + { + if (buffer == buf_end) + return 1; + *(buffer++)= *(uh_line++); + } + if (uh_line == uh_end) + return 1; + *user_len= buffer - buf_start; + *(buffer++)= 0; + + while (uh_line < uh_end && *uh_line != '@') + ++uh_line; + if (uh_line == uh_end || *(++uh_line) == 0) + return 1; + ++uh_line; + + buf_start= buffer; + while (uh_line < uh_end && *uh_line != ' ' && *uh_line != '[') + { + if (buffer == buf_end) + break; + *(buffer++)= *(uh_line++); + } + *host_len= buffer - buf_start; + *(buffer++)= 0; + + while (uh_line < uh_end && *uh_line != '[') + ++uh_line; + + buf_start= buffer; + if (*uh_line == '[') + { + ++uh_line; + while (uh_line < uh_end && *uh_line != ']') + *(buffer++)= *(uh_line++); + } + *ip_len= buffer - buf_start; + return 0; +} + +#if defined(__WIN__) && !defined(S_ISDIR) +#define S_ISDIR(x) ((x) & _S_IFDIR) +#endif /*__WIN__ && !S_ISDIR*/ + +static int start_logging() +{ + last_error_buf[0]= 0; + log_write_failures= 0; + if (output_type == OUTPUT_FILE) + { + char alt_path_buffer[FN_REFLEN+1+DEFAULT_FILENAME_LEN]; + MY_STAT *f_stat; + const char *alt_fname= file_path; + + while (*alt_fname == ' ') + alt_fname++; + + if (*alt_fname == 0) + { + /* Empty string means the default file name. */ + alt_fname= default_file_name; + } + else + { + /* See if the directory exists with the name of file_path. */ + /* Log file name should be [file_path]/server_audit.log then. */ + if ((f_stat= my_stat(file_path, (MY_STAT *)alt_path_buffer, MYF(0))) && + S_ISDIR(f_stat->st_mode)) + { + size_t p_len= strlen(file_path); + memcpy(alt_path_buffer, file_path, p_len); + if (alt_path_buffer[p_len-1] != FN_LIBCHAR) + { + alt_path_buffer[p_len]= FN_LIBCHAR; + p_len++; + } + memcpy(alt_path_buffer+p_len, default_file_name, DEFAULT_FILENAME_LEN); + alt_path_buffer[p_len+DEFAULT_FILENAME_LEN]= 0; + alt_fname= alt_path_buffer; + } + } + + logfile= logger_open(alt_fname, file_rotate_size, rotations); + + if (logfile == NULL) + { + error_header(); + fprintf(stderr, "Could not create file '%s'.\n", + alt_fname); + logging= 0; + my_snprintf(last_error_buf, sizeof(last_error_buf), + "Could not create file '%s'.", alt_fname); + is_active= 0; + CLIENT_ERROR(1, "SERVER AUDIT plugin can't create file '%s'.", + MYF(ME_JUST_WARNING), alt_fname); + return 1; + } + error_header(); + fprintf(stderr, "logging started to the file %s.\n", alt_fname); + strncpy(current_log_buf, alt_fname, sizeof(current_log_buf)); + } + else if (output_type == OUTPUT_SYSLOG) + { + openlog(syslog_ident, LOG_NOWAIT, syslog_facility_codes[syslog_facility]); + error_header(); + fprintf(stderr, "logging started to the syslog.\n"); + strncpy(current_log_buf, "[SYSLOG]", sizeof(current_log_buf)); + } + is_active= 1; + return 0; +} + + +static int stop_logging() +{ + last_error_buf[0]= 0; + if (output_type == OUTPUT_FILE && logfile) + { + logger_close(logfile); + logfile= NULL; + } + else if (output_type == OUTPUT_SYSLOG) + { + closelog(); + } + error_header(); + fprintf(stderr, "logging was stopped.\n"); + is_active= 0; + return 0; +} + +static struct connection_info * + add_connection(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; + 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), + event->user, event->user_length); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + 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; +} + + +#define SAFE_STRLEN(s) (s ? strlen(s) : 0) + + +static struct connection_info * + add_connection_initdb(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; +} + + +static struct connection_info * + add_connection_table(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; + 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), + event->user, SAFE_STRLEN(event->user)); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + 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; +} + + +static struct connection_info * + add_connection_query(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; +} + + +static void change_connection(struct connection_info *cn, + const struct mysql_event_connection *event) +{ + get_str_n(cn->user, &cn->user_length, sizeof(cn->user), + event->user, event->user_length); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + event->ip, event->ip_length); +} + +static int write_log(const char *message, int len) +{ + if (output_type == OUTPUT_FILE) + { + if (logfile && + (is_active= (logger_write(logfile, message, len) == len))) + return 0; + ++log_write_failures; + return 1; + } + else if (output_type == OUTPUT_SYSLOG) + { + syslog(syslog_facility_codes[syslog_facility] | + syslog_priority_codes[syslog_priority], + "%s %.*s", syslog_info, len, message); + } + return 0; +} + + +static size_t log_header(char *message, size_t message_len, + time_t *ts, + const char *serverhost, unsigned int serverhost_len, + const char *username, unsigned int username_len, + const char *host, unsigned int host_len, + const char *userip, unsigned int userip_len, + unsigned int connection_id, long long query_id, + const char *operation) +{ + struct tm tm_time; + + if (host_len == 0 && userip_len != 0) + { + host_len= userip_len; + host= userip; + } + + if (output_type == OUTPUT_SYSLOG) + return my_snprintf(message, message_len, + "%.*s,%.*s,%.*s,%d,%lld,%s", + serverhost_len, serverhost, + username_len, username, + host_len, host, + connection_id, query_id, operation); + + (void) localtime_r(ts, &tm_time); + return my_snprintf(message, message_len, + "%04d%02d%02d %02d:%02d:%02d,%.*s,%.*s,%.*s,%d,%lld,%s", + tm_time.tm_year+1900, tm_time.tm_mon+1, tm_time.tm_mday, + tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, + serverhost_len, serverhost, + username_len, username, + host_len, host, + connection_id, query_id, operation); +} + + +static int log_connection(const struct connection_info *cn, + 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, + cn->user, cn->user_length, + cn->host, cn->host_length, + cn->ip, cn->ip_length, + event->thread_id, 0, type); + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",%.*s,,%d", cn->db_length, cn->db, 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) +{ + const char *res_start= result; + const char *res_end= result + result_len - 2; + while (len) + { + if (result >= res_end) + break; + if (*str == '\'') + { + *(result++)= '\\'; + *(result++)= '\''; + } + else if (*str == '\\') + { + *(result++)= '\\'; + *(result++)= '\\'; + } + else + *(result++)= *str; + str++; + len--; + } + *result= 0; + return result - res_start; +} + + +static int do_log_user(const char *name) +{ + size_t len; + + if (!name) + return 0; + len= strlen(name); + + if (incl_user_hash.records) + return my_hash_search(&incl_user_hash, (const uchar *) name, len) != 0; + + if (excl_user_hash.records) + return my_hash_search(&excl_user_hash, (const uchar *) name, len) == 0; + + return 1; +} + + +static int log_statement_ex(const struct connection_info *cn, + time_t ev_time, unsigned long thd_id, + const char *query, unsigned int query_len, + int error_code, const char *type) +{ + size_t csize, esc_q_len; + char message[1024]; + char uh_buffer[768]; + const char *db; + unsigned int db_length; + long long query_id; + + if ((db= cn->db)) + db_length= cn->db_length; + else + { + db= ""; + db_length= 0; + } + + if (!(query_id= cn->query_id)) + query_id= query_counter++; + + csize= log_header(message, sizeof(message)-1, &ev_time, + servhost, servhost_len, + cn->user, cn->user_length,cn->host, cn->host_length, + cn->ip, cn->ip_length, thd_id, query_id, type); + + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",%.*s", db_length, db); + + if (query == 0) + { + /* Can happen after the error in mysqld_prepare_stmt() */ + query= cn->query; + query_len= cn->query_length; + } + + esc_q_len= escape_string(query, query_len, + uh_buffer, sizeof(uh_buffer)); + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",\'%.*s\',%d", esc_q_len, uh_buffer, error_code); + message[csize]= '\n'; + return write_log(message, csize + 1); +} + + +static int log_statement(const struct connection_info *cn, + const struct mysql_event_general *event, + const char *type) +{ + return log_statement_ex(cn, event->general_time, event->general_thread_id, + event->general_query, event->general_query_length, + event->general_error_code, type); +} + + +static int log_table(const struct connection_info *cn, + const struct mysql_event_table *event, const char *type) +{ + size_t csize; + char message[1024]; + time_t ctime; + + (void) time(&ctime); + csize= log_header(message, sizeof(message)-1, &ctime, + servhost, servhost_len, + event->user, SAFE_STRLEN(event->user), + event->host, SAFE_STRLEN(event->host), + event->ip, SAFE_STRLEN(event->ip), + event->thread_id, cn->query_id, type); + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",%.*s,%.*s,",event->database_length, event->database, + event->table_length, event->table); + message[csize]= '\n'; + return write_log(message, csize + 1); +} + + +static int log_rename(const struct connection_info *cn, + const struct mysql_event_table *event) +{ + size_t csize; + char message[1024]; + time_t ctime; + + (void) time(&ctime); + csize= log_header(message, sizeof(message)-1, &ctime, + servhost, servhost_len, + event->user, SAFE_STRLEN(event->user), + event->host, SAFE_STRLEN(event->host), + event->ip, SAFE_STRLEN(event->ip), + event->thread_id, cn->query_id, "RENAME"); + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",%.*s,%.*s|%.*s.%.*s,",event->database_length, event->database, + event->table_length, event->table, + event->new_database_length, event->new_database, + event->new_table_length, event->new_table); + message[csize]= '\n'; + return write_log(message, csize + 1); +} + + +static int event_query_command(const struct mysql_event_general *event) +{ + return (event->general_command_length == 5 && + strncmp(event->general_command, "Query", 5) == 0) || + (event->general_command_length == 7 && + (strncmp(event->general_command, "Execute", 7) == 0 || + (event->general_error_code != 0 && + strncmp(event->general_command, "Prepare", 7) == 0))); +} + + +static void update_general_user(struct connection_info *cn, + const struct mysql_event_general *event) +{ + char uh_buffer[768]; + size_t user_len, host_len, ip_len; + if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0 && + get_user_host(event->general_user, event->general_user_length, + uh_buffer, sizeof(uh_buffer), + &user_len, &host_len, &ip_len) == 0) + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->user), + 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); + } + +} + + +#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) +{ + struct connection_info *cn= NULL; + *after_action= 0; + + switch (event_class) { + case MYSQL_AUDIT_GENERAL_CLASS: + { + const struct mysql_event_general *event = + (const struct mysql_event_general *) ev; + switch (event->event_subclass) { + case MYSQL_AUDIT_GENERAL_LOG: + { + 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 (init_db_command) + { + /* Change DB */ + 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; + cn->query_length= event->general_query_length; + cn->query_time= (time_t) event->general_time; + update_general_user(cn, event); + } + else if (init_db_command) + cn= add_connection_initdb(event); + else if (event_query_command(event)) + cn= add_connection_query(event); + 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 (mode == 0 && cn->db_length == 0 && event->database_length > 0) + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->database, event->database_length); + + if (event->general_error_code == 0) + { + /* We need to check if it's the USE command to change the DB */ + int use_command= event->general_query_length > 4 && + strncasecmp(event->general_query, "use ", 4) == 0; + if (use_command) + { + /* Change DB */ + if (mode) + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->general_query + 4, event->general_query_length - 4); + else + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->database, event->database_length); + } + } + update_general_user(cn, event); + } + 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; + 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); + cn->query= cn->query_buffer; + cn->query_time= (time_t) event->general_time; + break; + default:; + } + break; + } + case MYSQL_AUDIT_TABLE_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 (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0) + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->user), + event->user, SAFE_STRLEN(event->user)); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + event->host, SAFE_STRLEN(event->host)); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + event->ip, SAFE_STRLEN(event->ip)); + } + + if (cn->db_length == 0 && event->database_length != 0) + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->database, event->database_length); + + if (mode == 0) + cn->query_id= event->query_id; + break; + } + case MYSQL_AUDIT_CONNECTION_CLASS: + { + const struct mysql_event_connection *event = + (const struct mysql_event_connection *) ev; + 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; + break; + case MYSQL_AUDIT_CONNECTION_CHANGE_USER: + cn= find_connection(event->thread_id); + if (cn) + *after_action= AA_CHANGE_USER; + break; + default:; + } + break; + } + default: + break; + } + return cn; +} + + +#define FILTER(MASK) (events == 0 || (events & MASK)) +static void auditing(MYSQL_THD thd __attribute__((unused)), + unsigned int event_class, + const void *ev) +{ + struct connection_info *cn; + int after_action; + + /* That one is important as this function can be called with */ + /* &lock_operations locked when the server logs an error reported */ + /* by this plugin. */ + if (internal_stop_logging) + return; + + flogger_mutex_lock(&lock_operations); + + if (!(cn= update_connection_hash(event_class, ev, &after_action))) + goto exit_func; + + if (!logging) + goto exit_func; + + if (event_class == MYSQL_AUDIT_GENERAL_CLASS && FILTER(EVENT_QUERY) && + cn && do_log_user(cn->user)) + { + const struct mysql_event_general *event = + (const struct mysql_event_general *) ev; + + /* + Only one subclass is logged. + */ + if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS) + log_statement(cn, event, "QUERY"); + } + else if (event_class == MYSQL_AUDIT_TABLE_CLASS && FILTER(EVENT_TABLE) && cn) + { + const struct mysql_event_table *event = + (const struct mysql_event_table *) ev; + if (do_log_user(event->user)) + { + switch (event->event_subclass) + { + case MYSQL_AUDIT_TABLE_LOCK: + log_table(cn, event, event->read_only ? "READ" : "WRITE"); + break; + case MYSQL_AUDIT_TABLE_CREATE: + log_table(cn, event, "CREATE"); + break; + case MYSQL_AUDIT_TABLE_DROP: + log_table(cn, event, "DROP"); + break; + case MYSQL_AUDIT_TABLE_RENAME: + log_rename(cn, event); + break; + case MYSQL_AUDIT_TABLE_ALTER: + log_table(cn, event, "ALTER"); + break; + default: + break; + } + } + } + else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS && + FILTER(EVENT_CONNECT) && cn) + { + const struct mysql_event_connection *event = + (const struct mysql_event_connection *) ev; + switch (event->event_subclass) + { + case MYSQL_AUDIT_CONNECTION_CONNECT: + log_connection(cn, event, event->status ? "FAILED_CONNECT": "CONNECT"); + break; + case MYSQL_AUDIT_CONNECTION_DISCONNECT: + log_connection(cn, event, "DISCONNECT"); + break; + case MYSQL_AUDIT_CONNECTION_CHANGE_USER: + log_connection(cn, event, "CHANGEUSER"); + break; + default:; + } + } +exit_func: + /* + This must work always, whether logging is ON or not. + */ + if (after_action) + { + switch (after_action) { + case AA_FREE_CONNECTION: + my_hash_delete(&connection_hash, (uchar *) cn); + break; + case AA_CHANGE_USER: + { + const struct mysql_event_connection *event = + (const struct mysql_event_connection *) ev; + change_connection(cn, event); + break; + } + default: + break; + } + } + if (cn) + cn->log_always= 0; + flogger_mutex_unlock(&lock_operations); +} + + +/* + As it's just too difficult to #include "sql_class.h", + let's just copy the necessary part of the system_variables + structure here. +*/ +typedef struct loc_system_variables +{ + ulong dynamic_variables_version; + char* dynamic_variables_ptr; + uint dynamic_variables_head; /* largest valid variable offset */ + uint dynamic_variables_size; /* how many bytes are in use */ + + ulonglong max_heap_table_size; + ulonglong tmp_table_size; + ulonglong long_query_time; + ulonglong optimizer_switch; + ulonglong sql_mode; ///< which non-standard SQL behaviour should be enabled + ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING + ulonglong join_buff_space_limit; + ulonglong log_slow_filter; + ulonglong log_slow_verbosity; + ulonglong bulk_insert_buff_size; + ulonglong join_buff_size; + ulonglong sortbuff_size; + ulonglong group_concat_max_len; + ha_rows select_limit; + ha_rows max_join_size; + ha_rows expensive_subquery_limit; + ulong auto_increment_increment, auto_increment_offset; + ulong lock_wait_timeout; + ulong join_cache_level; + ulong max_allowed_packet; + ulong max_error_count; + ulong max_length_for_sort_data; + ulong max_sort_length; + ulong max_tmp_tables; + ulong max_insert_delayed_threads; + ulong min_examined_row_limit; + ulong multi_range_count; + ulong net_buffer_length; + ulong net_interactive_timeout; + ulong net_read_timeout; + ulong net_retry_count; + ulong net_wait_timeout; + ulong net_write_timeout; + ulong optimizer_prune_level; + ulong optimizer_search_depth; + ulong preload_buff_size; + ulong profiling_history_size; + ulong read_buff_size; + ulong read_rnd_buff_size; + ulong mrr_buff_size; + ulong div_precincrement; + /* Total size of all buffers used by the subselect_rowid_merge_engine. */ + ulong rowid_merge_buff_size; + ulong max_sp_recursion_depth; + ulong default_week_format; + ulong max_seeks_for_key; + ulong range_alloc_block_size; + ulong query_alloc_block_size; + ulong query_prealloc_size; + ulong trans_alloc_block_size; + ulong trans_prealloc_size; + ulong log_warnings; + /* Flags for slow log filtering */ + ulong log_slow_rate_limit; + ulong binlog_format; ///< binlog format for this thd (see enum_binlog_format) + ulong progress_report_time; + my_bool binlog_annotate_row_events; + my_bool binlog_direct_non_trans_update; + my_bool sql_log_bin; + ulong completion_type; + ulong query_cache_type; +} LOC_SV; + +static int server_audit_init(void *p __attribute__((unused))) +{ + const void *my_hash_init_ptr; +#ifdef _WIN32 + serv_ver= (const char *) GetProcAddress(0, "server_version"); +#else + serv_ver= server_version; +#endif /*_WIN32*/ + + my_hash_init_ptr= dlsym(NULL, "_my_hash_init"); + if (!my_hash_init_ptr) + { + maria_above_5= 1; + my_hash_init_ptr= dlsym(NULL, "my_hash_init2"); + } + + if (!serv_ver || !my_hash_init_ptr) + return 0; + + if (!started_mysql) + { + if (!maria_above_5 && serv_ver[4]=='3' && serv_ver[5]<'3') + { + mode= 1; + mode_readonly= 1; + } + } + + + if (gethostname(servhost, sizeof(servhost))) + strcpy(servhost, "unknown"); + + servhost_len= strlen(servhost); + + logger_init_mutexes(); +#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) + if (PSI_server) + PSI_server->register_mutex("server_audit", mutex_key_list, 1); +#endif + flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST); + + my_hash_clear(&incl_user_hash); + my_hash_clear(&excl_user_hash); + + if (incl_users) + { + if (excl_users) + { + incl_users= excl_users= NULL; + error_header(); + fprintf(stderr, "INCL_DML_USERS and EXCL_DML_USERS specified" + " simultaneously - both set to empty\n"); + } + update_incl_users(NULL, NULL, NULL, &incl_users); + } + else if (excl_users) + { + 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); + + /* The Query Cache shadows TABLE events if the result is taken from it */ + /* so we warn users if both Query Cashe and TABLE events enabled. */ + if (!started_mysql && FILTER(EVENT_TABLE)) + { + ulonglong *qc_size= (ulonglong *) dlsym(NULL, "query_cache_size"); + if (qc_size == NULL || *qc_size != 0) + { + struct loc_system_variables *g_sys_var= + (struct loc_system_variables *) dlsym(NULL, "global_system_variables"); + if (g_sys_var && g_sys_var->query_cache_type != 0) + { + error_header(); + fprintf(stderr, "Query cache is enabled with the TABLE events. Some table reads can be veiled."); + } + } + } + + if (logging) + start_logging(); + + return 0; +} + + +static int server_audit_init_mysql(void *p) +{ + started_mysql= 1; + mode= 1; + mode_readonly= 1; + return server_audit_init(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); + + if (output_type == OUTPUT_FILE && logfile) + logger_close(logfile); + else if (output_type == OUTPUT_SYSLOG) + closelog(); + flogger_mutex_destroy(&lock_operations); + + error_header(); + fprintf(stderr, "STOPPED\n"); + return 0; +} + + +static void rotate_log(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), + const void *save __attribute__((unused))) +{ + if (output_type == OUTPUT_FILE && logfile && *(my_bool*) save) + (void) logger_rotate(logfile); +} + + +static struct st_mysql_audit mysql_descriptor = +{ + MYSQL_AUDIT_INTERFACE_VERSION, + NULL, + auditing, + { MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK } +}; + +mysql_declare_plugin(server_audit) +{ + MYSQL_AUDIT_PLUGIN, + &mysql_descriptor, + "SERVER_AUDIT", + " Alexey Botchkov (MariaDB)", + "Audit the server activity.", + PLUGIN_LICENSE_GPL, + server_audit_init_mysql, + server_audit_deinit, + PLUGIN_VERSION, + audit_status, + vars, + NULL, + 0 +} +mysql_declare_plugin_end; + + +static struct st_mysql_audit maria_descriptor = +{ + MYSQL_AUDIT_INTERFACE_VERSION, + NULL, + auditing, + { MYSQL_AUDIT_GENERAL_CLASSMASK | + MYSQL_AUDIT_TABLE_CLASSMASK | + MYSQL_AUDIT_CONNECTION_CLASSMASK } +}; +maria_declare_plugin(server_audit) +{ + MYSQL_AUDIT_PLUGIN, + &maria_descriptor, + "SERVER_AUDIT", + "Alexey Botchkov (MariaDB)", + "Audit the server activity.", + PLUGIN_LICENSE_GPL, + server_audit_init, + server_audit_deinit, + PLUGIN_VERSION, + audit_status, + vars, + PLUGIN_STR_VERSION, + MariaDB_PLUGIN_MATURITY_BETA +} +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)))) + 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))))) + return; + if (FILTER(EVENT_QUERY) && do_log_user(cn->user)) + { + log_statement_ex(cn, cn->query_time, thd_id, cn->query, cn->query_length, + 0, "QUERY"); + cn->log_always= 1; + } +} + + +static void update_file_path(MYSQL_THD thd, + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + flogger_mutex_lock(&lock_operations); + internal_stop_logging= 1; + error_header(); + fprintf(stderr, "Log file name was changed to '%s'.\n", *(const char **) save); + + if (logging) + log_current_query(thd); + + if (logging && output_type == OUTPUT_FILE) + { + char *sav_path= file_path; + + file_path= *(char **) save; + internal_stop_logging= 1; + stop_logging(); + if (start_logging()) + { + file_path= sav_path; + error_header(); + fprintf(stderr, "Reverting log filename back to '%s'.\n", file_path); + logging= (start_logging() == 0); + if (!logging) + { + error_header(); + fprintf(stderr, "Logging was disabled..\n"); + CLIENT_ERROR(1, "Logging was disabled.", MYF(ME_JUST_WARNING)); + } + goto exit_func; + } + internal_stop_logging= 0; + } + + strncpy(path_buffer, *(const char **) save, sizeof(path_buffer)); + file_path= path_buffer; +exit_func: + internal_stop_logging= 0; + flogger_mutex_unlock(&lock_operations); +} + + +static void update_incl_users(MYSQL_THD thd, + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + flogger_mutex_lock(&lock_operations); + mark_always_logged(thd); + strncpy(incl_user_buffer, *(const char **) save, sizeof(incl_user_buffer)); + incl_users= incl_user_buffer; + user_hash_fill(&incl_user_hash, incl_users, &excl_user_hash, 1); + error_header(); + fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users); + flogger_mutex_unlock(&lock_operations); +} + + +static void update_excl_users(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + flogger_mutex_lock(&lock_operations); + mark_always_logged(thd); + strncpy(excl_user_buffer, *(const char **) save, sizeof(excl_user_buffer)); + excl_users= excl_user_buffer; + user_hash_fill(&excl_user_hash, excl_users, &incl_user_hash, 0); + error_header(); + fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users); + flogger_mutex_unlock(&lock_operations); +} + + +static void update_output_type(MYSQL_THD thd, + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + ulong new_output_type= *((ulong *) save); + if (output_type == new_output_type) + return; + + flogger_mutex_lock(&lock_operations); + internal_stop_logging= 1; + if (logging) + { + log_current_query(thd); + stop_logging(); + } + + output_type= new_output_type; + error_header(); + fprintf(stderr, "Output was redirected to '%s'\n", + output_type_names[output_type]); + + if (logging) + start_logging(); + internal_stop_logging= 0; + flogger_mutex_unlock(&lock_operations); +} + + +static void update_syslog_facility(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + ulong new_facility= *((ulong *) save); + if (syslog_facility == new_facility) + return; + + mark_always_logged(thd); + error_header(); + fprintf(stderr, "SysLog facility was changed from '%s' to '%s'.\n", + syslog_facility_names[syslog_facility], + syslog_facility_names[new_facility]); + syslog_facility= new_facility; +} + + +static void update_syslog_priority(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + ulong new_priority= *((ulong *) save); + if (syslog_priority == new_priority) + return; + + flogger_mutex_lock(&lock_operations); + mark_always_logged(thd); + flogger_mutex_unlock(&lock_operations); + error_header(); + fprintf(stderr, "SysLog priority was changed from '%s' to '%s'.\n", + syslog_priority_names[syslog_priority], + syslog_priority_names[new_priority]); + syslog_priority= new_priority; +} + + +static void update_logging(MYSQL_THD thd, + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + char new_logging= *(char *) save; + if (new_logging == logging) + return; + + flogger_mutex_lock(&lock_operations); + internal_stop_logging= 1; + if ((logging= new_logging)) + { + start_logging(); + if (!logging) + { + CLIENT_ERROR(1, "Logging was disabled.", MYF(ME_JUST_WARNING)); + } + } + else + { + log_current_query(thd); + stop_logging(); + } + + internal_stop_logging= 0; + flogger_mutex_unlock(&lock_operations); +} + + +static void update_mode(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + unsigned int new_mode= *(unsigned int *) save; + if (mode_readonly || new_mode == mode) + return; + + 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); +} + +static void update_syslog_ident(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *var_ptr __attribute__((unused)), const void *save) +{ + strncpy(syslog_ident_buffer, *(const char **) save, + sizeof(syslog_ident_buffer)); + syslog_ident= syslog_ident_buffer; + flogger_mutex_lock(&lock_operations); + mark_always_logged(thd); + flogger_mutex_unlock(&lock_operations); +} + + diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index a92f69f3da4..84f2d95c5da 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -84,7 +84,7 @@ static void general_class_handler(THD *thd, uint event_subtype, va_list ap) event.general_rows= (unsigned long long) va_arg(ap, ha_rows); event.database= va_arg(ap, const char *); event.database_length= va_arg(ap, unsigned int); - event.query_id= (unsigned long long) thd->query_id; + event.query_id= (unsigned long long) (thd ? thd->query_id : 0); event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &event); } @@ -134,7 +134,7 @@ static void table_class_handler(THD *thd, uint event_subclass, va_list ap) event.new_database_length= va_arg(ap, unsigned int); event.new_table= va_arg(ap, const char *); event.new_table_length= va_arg(ap, unsigned int); - event.query_id= (unsigned long long) thd->query_id; + event.query_id= (unsigned long long) (thd ? thd->query_id : 0); event_class_dispatch(thd, MYSQL_AUDIT_TABLE_CLASS, &event); } diff --git a/sql/sql_audit.h b/sql/sql_audit.h index 8172610607a..86e911c686d 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -96,11 +96,13 @@ void mysql_audit_general_log(THD *thd, time_t time, { CHARSET_INFO *clientcs= thd ? thd->variables.character_set_client : global_system_variables.character_set_client; + const char *db= thd ? thd->db : ""; + size_t db_length= thd ? thd->db_length : 0; mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, MYSQL_AUDIT_GENERAL_LOG, 0, time, user, userlen, cmd, cmdlen, query, querylen, clientcs, (ha_rows) 0, - thd->db, thd->db_length); + db, db_length); } } @@ -129,6 +131,8 @@ void mysql_audit_general(THD *thd, uint event_subtype, char user_buff[MAX_USER_HOST_SIZE]; CSET_STRING query; ha_rows rows; + const char *db; + size_t db_length; if (thd) { @@ -136,18 +140,22 @@ void mysql_audit_general(THD *thd, uint event_subtype, user= user_buff; userlen= make_user_name(thd, user_buff); rows= thd->warning_info->current_row_for_warning(); + db= thd->db; + db_length= thd->db_length; } else { user= 0; userlen= 0; rows= 0; + db= ""; + db_length= 0; } mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype, error_code, time, user, userlen, msg, msglen, query.str(), query.length(), query.charset(), rows, - thd->db, thd->db_length); + db, db_length); } } diff --git a/storage/innobase/fil/fil0fil.c b/storage/innobase/fil/fil0fil.c index 700d1404193..0367484ebf0 100644 --- a/storage/innobase/fil/fil0fil.c +++ b/storage/innobase/fil/fil0fil.c @@ -4110,18 +4110,31 @@ fil_extend_space_to_desired_size( #ifdef HAVE_POSIX_FALLOCATE if (srv_use_posix_fallocate) { - offset_high = size_after_extend * page_size / (4ULL*1024*1024*1024); - offset_low = size_after_extend * page_size % (4ULL*1024*1024*1024); + ib_int64_t start_offset = start_page_no * page_size; + ib_int64_t end_offset = (size_after_extend - start_page_no) * page_size; + ib_int64_t desired_size = size_after_extend*page_size; mutex_exit(&fil_system->mutex); - success = os_file_set_size(node->name, node->handle, - offset_low, offset_high); + + if (posix_fallocate(node->handle, start_offset, end_offset) == -1) { + fprintf(stderr, "InnoDB: Error: preallocating file " + "space for file \'%s\' failed. Current size " + " %lld, len %lld, desired size %lld\n", + node->name, start_offset, end_offset, desired_size); + success = FALSE; + } else { + success = TRUE; + } + mutex_enter(&fil_system->mutex); + if (success) { node->size += (size_after_extend - start_page_no); space->size += (size_after_extend - start_page_no); os_has_said_disk_full = FALSE; } + + fil_node_complete_io(node, fil_system, OS_FILE_READ); goto complete_io; } #endif @@ -4178,12 +4191,10 @@ fil_extend_space_to_desired_size( mem_free(buf2); -#ifdef HAVE_POSIX_FALLOCATE -complete_io: -#endif - fil_node_complete_io(node, fil_system, OS_FILE_WRITE); +complete_io: + *actual_size = space->size; #ifndef UNIV_HOTBACKUP diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c index 03fd0200d18..253f5b69623 100644 --- a/storage/maria/ma_blockrec.c +++ b/storage/maria/ma_blockrec.c @@ -3248,8 +3248,9 @@ static my_bool write_block_record(MARIA_HA *info, blob_length-= (blob_length % FULL_PAGE_SIZE(block_size)); if (blob_length) { - memcpy(&log_array_pos->str, record + tmp_column->offset + length, - sizeof(uchar*)); + memcpy((void*) &log_array_pos->str, + record + tmp_column->offset + length, + sizeof(uchar*)); log_array_pos->length= blob_length; log_entry_length+= blob_length; log_array_pos++; @@ -5144,7 +5145,12 @@ my_bool _ma_cmp_block_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, int error; DBUG_ENTER("_ma_cmp_block_unique"); - if (!(old_record= my_alloca(info->s->base.reclength))) + /* + Don't allocate more than 16K on the stack to ensure we don't get + stack overflow. + */ + if (!(old_record= my_safe_alloca(info->s->base.reclength, + MARIA_MAX_RECORD_ON_STACK))) DBUG_RETURN(1); /* Don't let the compare destroy blobs that may be in use */ @@ -5166,7 +5172,8 @@ my_bool _ma_cmp_block_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, info->rec_buff_size= org_rec_buff_size; } DBUG_PRINT("exit", ("result: %d", error)); - my_afree(old_record); + my_safe_afree(old_record, info->s->base.reclength, + MARIA_MAX_RECORD_ON_STACK); DBUG_RETURN(error != 0); } @@ -5338,6 +5345,7 @@ int _ma_scan_restore_block_record(MARIA_HA *info, info Maria handler record Store found here record_pos Value stored in info->cur_row.next_pos after last call + This is offset inside the current pagebuff skip_deleted NOTES @@ -5375,7 +5383,7 @@ restart_record_read: /* Ensure that scan.dir and record_pos are in sync */ DBUG_ASSERT(info->scan.dir == dir_entry_pos(info->scan.page_buff, share->block_size, - record_pos)); + (uint) record_pos)); /* Search for a valid directory entry (not 0) */ while (!(offset= uint2korr(info->scan.dir))) @@ -5971,12 +5979,12 @@ static size_t fill_update_undo_parts(MARIA_HA *info, const uchar *oldrec, { uint size_length= column->length - portable_sizeof_char_ptr; old_column_length= _ma_calc_blob_length(size_length, old_column_pos); - memcpy(&old_column_pos, oldrec + column->offset + size_length, + memcpy((void*) &old_column_pos, oldrec + column->offset + size_length, sizeof(old_column_pos)); if (!new_column_is_empty) { new_column_length= _ma_calc_blob_length(size_length, new_column_pos); - memcpy(&new_column_pos, newrec + column->offset + size_length, + memcpy((void*) &new_column_pos, newrec + column->offset + size_length, sizeof(old_column_pos)); } break; diff --git a/storage/maria/ma_dynrec.c b/storage/maria/ma_dynrec.c index c1c0a8e9729..c47da42b555 100644 --- a/storage/maria/ma_dynrec.c +++ b/storage/maria/ma_dynrec.c @@ -36,12 +36,6 @@ static my_bool delete_dynamic_record(MARIA_HA *info,MARIA_RECORD_POS filepos, static my_bool _ma_cmp_buffer(File file, const uchar *buff, my_off_t filepos, uint length); -/* Play it safe; We have a small stack when using threads */ -#undef my_alloca -#undef my_afree -#define my_alloca(A) my_malloc((A),MYF(0)) -#define my_afree(A) my_free((A)) - /* Interface function from MARIA_HA */ #ifdef HAVE_MMAP @@ -256,7 +250,8 @@ my_bool _ma_write_blob_record(MARIA_HA *info, const uchar *record) MARIA_DYN_DELETE_BLOCK_HEADER+1); reclength= (info->s->base.pack_reclength + _ma_calc_total_blob_length(info,record)+ extra); - if (!(rec_buff=(uchar*) my_alloca(reclength))) + if (!(rec_buff=(uchar*) my_safe_alloca(reclength, + MARIA_MAX_RECORD_ON_STACK))) { my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(1); @@ -270,7 +265,7 @@ my_bool _ma_write_blob_record(MARIA_HA *info, const uchar *record) error= write_dynamic_record(info, rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER), reclength2); - my_afree(rec_buff); + my_safe_afree(rec_buff, reclength, MARIA_MAX_RECORD_ON_STACK); return(error != 0); } @@ -294,7 +289,8 @@ my_bool _ma_update_blob_record(MARIA_HA *info, MARIA_RECORD_POS pos, return 1; } #endif - if (!(rec_buff=(uchar*) my_alloca(reclength))) + if (!(rec_buff=(uchar*) my_safe_alloca(reclength, + MARIA_MAX_RECORD_ON_STACK))) { my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */ return(1); @@ -304,7 +300,7 @@ my_bool _ma_update_blob_record(MARIA_HA *info, MARIA_RECORD_POS pos, error=update_dynamic_record(info,pos, rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER), reclength); - my_afree(rec_buff); + my_safe_afree(rec_buff, reclength, MARIA_MAX_RECORD_ON_STACK); return(error != 0); } @@ -1559,7 +1555,8 @@ my_bool _ma_cmp_dynamic_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, my_bool error; DBUG_ENTER("_ma_cmp_dynamic_unique"); - if (!(old_record=my_alloca(info->s->base.reclength))) + if (!(old_record= my_safe_alloca(info->s->base.reclength, + MARIA_MAX_RECORD_ON_STACK))) DBUG_RETURN(1); /* Don't let the compare destroy blobs that may be in use */ @@ -1580,7 +1577,8 @@ my_bool _ma_cmp_dynamic_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def, info->rec_buff= old_rec_buff; info->rec_buff_size= old_rec_buff_size; } - my_afree(old_record); + my_safe_afree(old_record, info->s->base.reclength, + MARIA_MAX_RECORD_ON_STACK); DBUG_RETURN(error); } @@ -1595,7 +1593,9 @@ my_bool _ma_cmp_dynamic_record(register MARIA_HA *info, uchar *buffer; MARIA_BLOCK_INFO block_info; my_bool error= 1; + size_t buffer_length; DBUG_ENTER("_ma_cmp_dynamic_record"); + LINT_INIT(buffer_length); if (info->opt_flag & WRITE_CACHE_USED) { @@ -1612,8 +1612,10 @@ my_bool _ma_cmp_dynamic_record(register MARIA_HA *info, { /* If check isn't disabled */ if (info->s->base.blobs) { - if (!(buffer=(uchar*) my_alloca(info->s->base.pack_reclength+ - _ma_calc_total_blob_length(info,record)))) + buffer_length= (info->s->base.pack_reclength + + _ma_calc_total_blob_length(info,record)); + if (!(buffer=(uchar*) my_safe_alloca(buffer_length, + MARIA_MAX_RECORD_ON_STACK))) DBUG_RETURN(1); } reclength= _ma_rec_pack(info,buffer,record); @@ -1665,7 +1667,7 @@ my_bool _ma_cmp_dynamic_record(register MARIA_HA *info, error= 0; err: if (buffer != info->rec_buff) - my_afree(buffer); + my_safe_afree(buffer, buffer_length, MARIA_MAX_RECORD_ON_STACK); DBUG_PRINT("exit", ("result: %d", error)); DBUG_RETURN(error); } diff --git a/storage/maria/ma_unique.c b/storage/maria/ma_unique.c index ef7aec86834..fd823f7b52d 100644 --- a/storage/maria/ma_unique.c +++ b/storage/maria/ma_unique.c @@ -135,7 +135,7 @@ ha_checksum _ma_unique_hash(MARIA_UNIQUEDEF *def, const uchar *record) else if (keyseg->flag & HA_BLOB_PART) { uint tmp_length= _ma_calc_blob_length(keyseg->bit_start,pos); - memcpy(&pos,pos+keyseg->bit_start,sizeof(char*)); + memcpy((void*) &pos,pos+keyseg->bit_start,sizeof(char*)); if (!length || length > tmp_length) length=tmp_length; /* The whole blob */ } @@ -231,8 +231,8 @@ my_bool _ma_unique_comp(MARIA_UNIQUEDEF *def, const uchar *a, const uchar *b, set_if_smaller(a_length, keyseg->length); set_if_smaller(b_length, keyseg->length); } - memcpy(&pos_a, pos_a+keyseg->bit_start, sizeof(char*)); - memcpy(&pos_b, pos_b+keyseg->bit_start, sizeof(char*)); + memcpy((void*) &pos_a, pos_a+keyseg->bit_start, sizeof(char*)); + memcpy((void*) &pos_b, pos_b+keyseg->bit_start, sizeof(char*)); } if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 || type == HA_KEYTYPE_VARTEXT2) diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index 94592a87d86..538ee461da1 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -42,6 +42,7 @@ #define MAX_NONMAPPED_INSERTS 1000 #define MARIA_MAX_TREE_LEVELS 32 +#define MARIA_MAX_RECORD_ON_STACK 16384 /* maria_open() flag, specific for maria_pack */ #define HA_OPEN_IGNORE_MOVED_STATE (1U << 30) diff --git a/storage/maria/maria_pack.c b/storage/maria/maria_pack.c index 788bc5c2ad3..ef3adfd9138 100644 --- a/storage/maria/maria_pack.c +++ b/storage/maria/maria_pack.c @@ -861,7 +861,7 @@ static int get_statistic(PACK_MRG_INFO *mrg,HUFF_COUNTS *huff_counts) reclength= mrg->file[0]->s->base.reclength; null_bytes= mrg->file[0]->s->base.null_bytes; - record=(uchar*) my_alloca(reclength); + record=(uchar*) my_safe_alloca(reclength, MARIA_MAX_RECORD_ON_STACK); end_count=huff_counts+mrg->file[0]->s->base.fields; record_count=0; glob_crc=0; max_blob_length=0; @@ -1145,7 +1145,7 @@ static int get_statistic(PACK_MRG_INFO *mrg,HUFF_COUNTS *huff_counts) mrg->records=record_count; mrg->max_blob_length=max_blob_length; - my_afree(record); + my_safe_afree(record, reclength, MARIA_MAX_RECORD_ON_STACK); DBUG_RETURN(error != HA_ERR_END_OF_FILE); } @@ -2415,7 +2415,8 @@ static int compress_maria_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) DBUG_ENTER("compress_maria_file"); /* Allocate a buffer for the records (excluding blobs). */ - if (!(record=(uchar*) my_alloca(isam_file->s->base.reclength))) + if (!(record=(uchar*) my_safe_alloca(isam_file->s->base.reclength, + MARIA_MAX_RECORD_ON_STACK))) return -1; end_count=huff_counts+isam_file->s->base.fields; @@ -2778,7 +2779,8 @@ static int compress_maria_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) if (verbose >= 2) printf("wrote %s records.\n", llstr((longlong) record_count, llbuf)); - my_afree(record); + my_safe_afree(record, isam_file->s->base.reclength, + MARIA_MAX_RECORD_ON_STACK); mrg->ref_length=max_pack_length; mrg->min_pack_length=max_record_length ? min_record_length : 0; mrg->max_pack_length=max_record_length; diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index 74b81b3822d..fbf9199db76 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -1536,7 +1536,7 @@ int mi_repair(HA_CHECK *param, register MI_INFO *info, if (!param->using_global_keycache) (void) init_key_cache(dflt_key_cache, param->key_cache_block_size, - param->use_buffers, 0, 0, 0); + (size_t) param->use_buffers, 0, 0, 0); if (init_io_cache(¶m->read_cache,info->dfile, (uint) param->read_buffer_length, diff --git a/storage/myisam/mi_checksum.c b/storage/myisam/mi_checksum.c index f0a4d4b0785..ec8b51fce7b 100644 --- a/storage/myisam/mi_checksum.c +++ b/storage/myisam/mi_checksum.c @@ -40,7 +40,7 @@ ha_checksum mi_checksum(MI_INFO *info, const uchar *buf) length=_mi_calc_blob_length(column->length- portable_sizeof_char_ptr, buf); - memcpy(&pos, buf+column->length - portable_sizeof_char_ptr, + memcpy((void*) &pos, buf+column->length - portable_sizeof_char_ptr, sizeof(char*)); break; } diff --git a/storage/xtradb/fil/fil0fil.c b/storage/xtradb/fil/fil0fil.c index f598c717e00..e966510b654 100644 --- a/storage/xtradb/fil/fil0fil.c +++ b/storage/xtradb/fil/fil0fil.c @@ -4953,20 +4953,30 @@ fil_extend_space_to_desired_size( #ifdef HAVE_POSIX_FALLOCATE if (srv_use_posix_fallocate) { - offset_high = (size_after_extend - file_start_page_no) - * page_size / (4ULL * 1024 * 1024 * 1024); - offset_low = (size_after_extend - file_start_page_no) - * page_size % (4ULL * 1024 * 1024 * 1024); + ib_int64_t start_offset = start_page_no * page_size; + ib_int64_t end_offset = (size_after_extend - start_page_no) * page_size; + ib_int64_t desired_size = size_after_extend*page_size; mutex_exit(&fil_system->mutex); - success = os_file_set_size(node->name, node->handle, - offset_low, offset_high); + + if (posix_fallocate(node->handle, start_offset, end_offset) == -1) { + fprintf(stderr, "InnoDB: Error: preallocating file " + "space for file \'%s\' failed. Current size " + " %lld, len %lld, desired size %lld\n", + node->name, start_offset, end_offset, desired_size); + success = FALSE; + } else { + success = TRUE; + } + mutex_enter(&fil_system->mutex); + if (success) { node->size += (size_after_extend - start_page_no); space->size += (size_after_extend - start_page_no); os_has_said_disk_full = FALSE; } + fil_node_complete_io(node, fil_system, OS_FILE_READ); goto complete_io; } @@ -5028,21 +5038,9 @@ fil_extend_space_to_desired_size( mem_free(buf2); -#ifdef HAVE_POSIX_FALLOCATE -complete_io: - /* If posix_fallocate was used to extent the file space - we need to complete the io. Because no actual writes were - dispatched read operation is enough here. Without this - there will be assertion at shutdown indicating that - all IO is not completed. */ - if (srv_use_posix_fallocate) { - fil_node_complete_io(node, fil_system, OS_FILE_READ); - } else { - fil_node_complete_io(node, fil_system, OS_FILE_WRITE); - } -#else fil_node_complete_io(node, fil_system, OS_FILE_WRITE); -#endif + +complete_io: *actual_size = space->size; |