Merge lp:~sergei.glushchenko/percona-server/5.6-ps-blueprint-audit-log-plugin into lp:percona-server/5.6

Proposed by Sergei Glushchenko
Status: Merged
Approved by: Alexey Kopytov
Approved revision: 541
Merged at revision: 582
Proposed branch: lp:~sergei.glushchenko/percona-server/5.6-ps-blueprint-audit-log-plugin
Merge into: lp:percona-server/5.6
Diff against target: 1801 lines (+1726/-0) (has conflicts)
14 files modified
mysql-test/include/audit_log_events.inc (+53/-0)
mysql-test/include/plugin.defs (+4/-0)
mysql-test/r/audit_log.result (+140/-0)
mysql-test/r/audit_log_install.result (+4/-0)
mysql-test/t/audit_log-master.opt (+4/-0)
mysql-test/t/audit_log.test (+23/-0)
mysql-test/t/audit_log_install-master.opt (+1/-0)
mysql-test/t/audit_log_install.test (+14/-0)
plugin/audit_log/CMakeLists.txt (+17/-0)
plugin/audit_log/audit_log.c (+815/-0)
plugin/audit_log/buffer.c (+196/-0)
plugin/audit_log/buffer.h (+32/-0)
plugin/audit_log/file_logger.c (+338/-0)
plugin/audit_log/logger.h (+85/-0)
Text conflict in mysql-test/include/plugin.defs
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-server/5.6-ps-blueprint-audit-log-plugin
Reviewer Review Type Date Requested Status
Alexey Kopytov (community) Approve
Review via email: mp+205595@code.launchpad.net

Description of the change

To post a comment you must log in.
Revision history for this message
Alexey Kopytov (akopytov) wrote :

Same questions as in 5.5 MP.

review: Needs Information
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Alexey Kopytov (akopytov) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'mysql-test/include/audit_log_events.inc'
2--- mysql-test/include/audit_log_events.inc 1970-01-01 00:00:00 +0000
3+++ mysql-test/include/audit_log_events.inc 2014-04-21 12:12:26 +0000
4@@ -0,0 +1,53 @@
5+# produce some events for audit log
6+
7+CREATE TABLE t1 (c1 INT, c2 CHAR(20));
8+--error ER_TABLE_EXISTS_ERROR
9+CREATE TABLE t1 (c1 INT, c2 CHAR(20));
10+INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c');
11+SELECT * FROM t1;
12+--error ER_NO_SUCH_TABLE
13+SELECT * FROM t2;
14+DROP TABLE t1;
15+
16+PREPARE stmt1 FROM 'SELECT 1';
17+EXECUTE stmt1;
18+SHOW STATUS LIKE 'audit_log%';
19+
20+DEALLOCATE PREPARE stmt1;
21+
22+show variables like 'audit_log%';
23+connect (con1,localhost,root,,mysql);
24+connection default;
25+disconnect con1;
26+--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
27+--error ER_ACCESS_DENIED_ERROR
28+connect (con1,localhost,no_such_user,,mysql);
29+connection default;
30+create table t1 (id int);
31+create table t2 (id int);
32+insert into t1 values (1), (2);
33+insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2);
34+select * from t1;
35+alter table t1 rename renamed_t1;
36+--error ER_NO_SUCH_TABLE
37+select * from t_doesnt_exist;
38+--error 1064
39+syntax_error_query;
40+drop table renamed_t1, t2;
41+show variables like 'audit_log%';
42+create database sa_db;
43+connect (con1,localhost,root,,test);
44+connection con1;
45+create table t1 (id2 int);
46+insert into t1 values (1), (2);
47+select * from t1;
48+drop table t1;
49+use sa_db;
50+create table sa_t1(id int);
51+insert into sa_t1 values (1), (2);
52+drop table sa_t1;
53+drop database sa_db;
54+connection default;
55+create user 'jeffrey'@'localhost' IDENTIFIED BY 'mypass';
56+drop user 'jeffrey'@'localhost';
57+disconnect con1;
58
59=== modified file 'mysql-test/include/plugin.defs'
60--- mysql-test/include/plugin.defs 2014-03-17 07:07:54 +0000
61+++ mysql-test/include/plugin.defs 2014-04-21 12:12:26 +0000
62@@ -45,5 +45,9 @@
63 libmemcached plugin/innodb_memcached/daemon_memcached DAEMON_MEMCACHED daemon_memcached
64 innodb_engine plugin/innodb_memcached/innodb_memcache INNODB_ENGINE
65 validate_password plugin/password_validation VALIDATE_PASSWORD validate_password
66+<<<<<<< TREE
67 auth_socket plugin/auth SOCKET_AUTH
68 scalability_metrics plugin/scalability_metrics SCALABILITY_METRICS
69+=======
70+audit_log plugin/audit_log AUDIT_LOG audit_log
71+>>>>>>> MERGE-SOURCE
72
73=== added file 'mysql-test/r/audit_log.result'
74--- mysql-test/r/audit_log.result 1970-01-01 00:00:00 +0000
75+++ mysql-test/r/audit_log.result 2014-04-21 12:12:26 +0000
76@@ -0,0 +1,140 @@
77+CREATE TABLE t1 (c1 INT, c2 CHAR(20));
78+CREATE TABLE t1 (c1 INT, c2 CHAR(20));
79+ERROR 42S01: Table 't1' already exists
80+INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c');
81+SELECT * FROM t1;
82+c1 c2
83+1 a
84+2 b
85+3 c
86+SELECT * FROM t2;
87+ERROR 42S02: Table 'test.t2' doesn't exist
88+DROP TABLE t1;
89+PREPARE stmt1 FROM 'SELECT 1';
90+EXECUTE stmt1;
91+1
92+1
93+SHOW STATUS LIKE 'audit_log%';
94+Variable_name Value
95+DEALLOCATE PREPARE stmt1;
96+show variables like 'audit_log%';
97+Variable_name Value
98+audit_log_buffer_size 4096
99+audit_log_file test_audit.log
100+audit_log_flush OFF
101+audit_log_format OLD
102+audit_log_policy ALL
103+audit_log_rotate_on_size 0
104+audit_log_rotations 0
105+audit_log_strategy ASYNCHRONOUS
106+connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET);
107+ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO)
108+create table t1 (id int);
109+create table t2 (id int);
110+insert into t1 values (1), (2);
111+insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2);
112+select * from t1;
113+id
114+1
115+2
116+alter table t1 rename renamed_t1;
117+select * from t_doesnt_exist;
118+ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist
119+syntax_error_query;
120+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1
121+drop table renamed_t1, t2;
122+show variables like 'audit_log%';
123+Variable_name Value
124+audit_log_buffer_size 4096
125+audit_log_file test_audit.log
126+audit_log_flush OFF
127+audit_log_format OLD
128+audit_log_policy ALL
129+audit_log_rotate_on_size 0
130+audit_log_rotations 0
131+audit_log_strategy ASYNCHRONOUS
132+create database sa_db;
133+create table t1 (id2 int);
134+insert into t1 values (1), (2);
135+select * from t1;
136+id2
137+1
138+2
139+drop table t1;
140+use sa_db;
141+create table sa_t1(id int);
142+insert into sa_t1 values (1), (2);
143+drop table sa_t1;
144+drop database sa_db;
145+create user 'jeffrey'@'localhost' IDENTIFIED BY 'mypass';
146+drop user 'jeffrey'@'localhost';
147+CREATE TABLE t1 (c1 INT, c2 CHAR(20));
148+CREATE TABLE t1 (c1 INT, c2 CHAR(20));
149+ERROR 42S01: Table 't1' already exists
150+INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c');
151+SELECT * FROM t1;
152+c1 c2
153+1 a
154+2 b
155+3 c
156+SELECT * FROM t2;
157+ERROR 42S02: Table 'test.t2' doesn't exist
158+DROP TABLE t1;
159+PREPARE stmt1 FROM 'SELECT 1';
160+EXECUTE stmt1;
161+1
162+1
163+SHOW STATUS LIKE 'audit_log%';
164+Variable_name Value
165+DEALLOCATE PREPARE stmt1;
166+show variables like 'audit_log%';
167+Variable_name Value
168+audit_log_buffer_size 4096
169+audit_log_file test_audit.log
170+audit_log_flush OFF
171+audit_log_format NEW
172+audit_log_policy LOGINS
173+audit_log_rotate_on_size 0
174+audit_log_rotations 0
175+audit_log_strategy SYNCHRONOUS
176+connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET);
177+ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO)
178+create table t1 (id int);
179+create table t2 (id int);
180+insert into t1 values (1), (2);
181+insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2);
182+select * from t1;
183+id
184+1
185+2
186+alter table t1 rename renamed_t1;
187+select * from t_doesnt_exist;
188+ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist
189+syntax_error_query;
190+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1
191+drop table renamed_t1, t2;
192+show variables like 'audit_log%';
193+Variable_name Value
194+audit_log_buffer_size 4096
195+audit_log_file test_audit.log
196+audit_log_flush OFF
197+audit_log_format NEW
198+audit_log_policy LOGINS
199+audit_log_rotate_on_size 0
200+audit_log_rotations 0
201+audit_log_strategy SYNCHRONOUS
202+create database sa_db;
203+create table t1 (id2 int);
204+insert into t1 values (1), (2);
205+select * from t1;
206+id2
207+1
208+2
209+drop table t1;
210+use sa_db;
211+create table sa_t1(id int);
212+insert into sa_t1 values (1), (2);
213+drop table sa_t1;
214+drop database sa_db;
215+create user 'jeffrey'@'localhost' IDENTIFIED BY 'mypass';
216+drop user 'jeffrey'@'localhost';
217
218=== added file 'mysql-test/r/audit_log_install.result'
219--- mysql-test/r/audit_log_install.result 1970-01-01 00:00:00 +0000
220+++ mysql-test/r/audit_log_install.result 2014-04-21 12:12:26 +0000
221@@ -0,0 +1,4 @@
222+INSTALL PLUGIN audit_log SONAME 'audit_log.<expected_extension>';
223+UNINSTALL PLUGIN audit_log;
224+Warnings:
225+Warning 1620 Plugin is busy and will be uninstalled on shutdown
226
227=== added file 'mysql-test/t/audit_log-master.opt'
228--- mysql-test/t/audit_log-master.opt 1970-01-01 00:00:00 +0000
229+++ mysql-test/t/audit_log-master.opt 2014-04-21 12:12:26 +0000
230@@ -0,0 +1,4 @@
231+$AUDIT_LOG_OPT
232+$AUDIT_LOG_LOAD
233+--audit_log_file=test_audit.log
234+--audit_log_buffer_size=4096
235
236=== added file 'mysql-test/t/audit_log.test'
237--- mysql-test/t/audit_log.test 1970-01-01 00:00:00 +0000
238+++ mysql-test/t/audit_log.test 2014-04-21 12:12:26 +0000
239@@ -0,0 +1,23 @@
240+--source include/not_embedded.inc
241+
242+# Adjustment to the OS dependent extension of shared libraries.
243+let $expected_extension= so;
244+if(`SELECT CONVERT(@@version_compile_os USING latin1)
245+ IN ("Win32","Win64","Windows")`)
246+{
247+ let $expected_extension= dll;
248+}
249+
250+let $MYSQL_DATA_DIR= `select @@datadir`;
251+
252+--source include/audit_log_events.inc
253+
254+--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
255+--shutdown_server
256+--source include/wait_until_disconnected.inc
257+--enable_reconnect
258+--exec echo "restart: --audit_log_policy=LOGINS --audit-log-format=NEW --audit_log_strategy=SYNCHRONOUS" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
259+--source include/wait_until_connected_again.inc
260+
261+--source include/audit_log_events.inc
262+
263
264=== added file 'mysql-test/t/audit_log_install-master.opt'
265--- mysql-test/t/audit_log_install-master.opt 1970-01-01 00:00:00 +0000
266+++ mysql-test/t/audit_log_install-master.opt 2014-04-21 12:12:26 +0000
267@@ -0,0 +1,1 @@
268+$AUDIT_LOG_OPT
269
270=== added file 'mysql-test/t/audit_log_install.test'
271--- mysql-test/t/audit_log_install.test 1970-01-01 00:00:00 +0000
272+++ mysql-test/t/audit_log_install.test 2014-04-21 12:12:26 +0000
273@@ -0,0 +1,14 @@
274+--source include/not_embedded.inc
275+
276+# Adjustment to the OS dependent extension of shared libraries.
277+let $expected_extension= so;
278+if(`SELECT CONVERT(@@version_compile_os USING latin1)
279+ IN ("Win32","Win64","Windows")`)
280+{
281+ let $expected_extension= dll;
282+}
283+
284+--replace_result $expected_extension <expected_extension>
285+eval INSTALL PLUGIN audit_log SONAME 'audit_log.$expected_extension';
286+
287+UNINSTALL PLUGIN audit_log;
288
289=== added directory 'plugin/audit_log'
290=== added file 'plugin/audit_log/CMakeLists.txt'
291--- plugin/audit_log/CMakeLists.txt 1970-01-01 00:00:00 +0000
292+++ plugin/audit_log/CMakeLists.txt 2014-04-21 12:12:26 +0000
293@@ -0,0 +1,17 @@
294+# Copyright (c) 2014 Percona LLC and/or its affiliates. All rights reserved.
295+#
296+# This program is free software; you can redistribute it and/or modify
297+# it under the terms of the GNU General Public License as published by
298+# the Free Software Foundation; version 2 of the License.
299+#
300+# This program is distributed in the hope that it will be useful,
301+# but WITHOUT ANY WARRANTY; without even the implied warranty of
302+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303+# GNU General Public License for more details.
304+#
305+# You should have received a copy of the GNU General Public License
306+# along with this program; if not, write to the Free Software
307+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
308+
309+MYSQL_ADD_PLUGIN(audit_log audit_log.c file_logger.c buffer.c
310+ MODULE_ONLY MODULE_OUTPUT_NAME "audit_log")
311
312=== added file 'plugin/audit_log/audit_log.c'
313--- plugin/audit_log/audit_log.c 1970-01-01 00:00:00 +0000
314+++ plugin/audit_log/audit_log.c 2014-04-21 12:12:26 +0000
315@@ -0,0 +1,815 @@
316+/* Copyright (c) 2014 Percona LLC and/or its affiliates. All rights reserved.
317+
318+ This program is free software; you can redistribute it and/or
319+ modify it under the terms of the GNU General Public License
320+ as published by the Free Software Foundation; version 2 of
321+ the License.
322+
323+ This program is distributed in the hope that it will be useful,
324+ but WITHOUT ANY WARRANTY; without even the implied warranty of
325+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
326+ GNU General Public License for more details.
327+
328+ You should have received a copy of the GNU General Public License
329+ along with this program; if not, write to the Free Software
330+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
331+
332+#include <time.h>
333+#include <string.h>
334+#include <stdio.h>
335+
336+#include <my_global.h>
337+#include <mysql/plugin.h>
338+#include <mysql/plugin_audit.h>
339+#include <typelib.h>
340+#include <mysql_version.h>
341+#include <mysql_com.h>
342+#include <my_pthread.h>
343+
344+#include "logger.h"
345+#include "buffer.h"
346+
347+#define PLUGIN_VERSION 0x0001
348+
349+
350+enum audit_log_policy_t { ALL, NONE, LOGINS, QUERIES };
351+enum audit_log_strategy_t
352+ { ASYNCHRONOUS, PERFORMANCE, SEMISYNCHRONOUS, SYNCHRONOUS };
353+enum audit_log_format_t { OLD, NEW };
354+
355+static LOGGER_HANDLE *audit_file_logger= NULL;
356+static audit_log_buffer_t *audit_log_buffer= NULL;
357+static ulonglong record_id= 0;
358+static time_t log_file_time= 0;
359+char *audit_log_file;
360+char default_audit_log_file[]= "audit.log";
361+ulong audit_log_policy= ALL;
362+ulong audit_log_strategy= ASYNCHRONOUS;
363+ulonglong audit_log_buffer_size= 1048576;
364+ulonglong audit_log_rotate_on_size= 0;
365+ulonglong audit_log_rotations= 0;
366+char audit_log_flush= FALSE;
367+ulong audit_log_format= OLD;
368+
369+
370+static
371+void init_record_id(off_t size)
372+{
373+ record_id= size;
374+}
375+
376+
377+static
378+ulonglong next_record_id()
379+{
380+ return __sync_add_and_fetch(&record_id, 1);
381+}
382+
383+
384+#define MAX_RECORD_ID_SIZE 50
385+#define MAX_TIMESTAMP_SIZE 25
386+
387+
388+static
389+void fprintf_timestamp(FILE *file)
390+{
391+ char timebuf[50];
392+ struct tm tm;
393+ time_t curtime;
394+
395+ memset(&tm, 0, sizeof(tm));
396+ time(&curtime);
397+ localtime_r(&curtime, &tm);
398+
399+ strftime(timebuf, sizeof(timebuf), "%FT%T", gmtime_r(&curtime, &tm));
400+
401+ fprintf(file, "%s audit_log: ", timebuf);
402+}
403+
404+
405+static
406+char *make_timestamp(char *buf, size_t buf_len, time_t t)
407+{
408+ struct tm tm;
409+
410+ memset(&tm, 0, sizeof(tm));
411+ strftime(buf, buf_len, "%FT%T UTC", gmtime_r(&t, &tm));
412+
413+ return buf;
414+}
415+
416+static
417+char *make_record_id(char *buf, size_t buf_len)
418+{
419+ struct tm tm;
420+ size_t len;
421+
422+ memset(&tm, 0, sizeof(tm));
423+ len= snprintf(buf, buf_len, "%llu_", next_record_id());
424+
425+ strftime(buf + len, buf_len - len,
426+ "%FT%T", gmtime_r(&log_file_time, &tm));
427+
428+ return buf;
429+}
430+
431+static
432+void xml_escape(const char *in, size_t *inlen, char* out, size_t *outlen)
433+{
434+ char* outstart = out;
435+ const char* base = in;
436+ char* outend = out + *outlen;
437+ const char* inend;
438+
439+ inend = in + (*inlen);
440+
441+ while ((in < inend) && (out < outend))
442+ {
443+ if (*in == '<')
444+ {
445+ if (outend - out < 4)
446+ break;
447+ *out++ = '&';
448+ *out++ = 'l';
449+ *out++ = 't';
450+ *out++ = ';';
451+ }
452+ else if (*in == '>')
453+ {
454+ if (outend - out < 4)
455+ break;
456+ *out++ = '&';
457+ *out++ = 'g';
458+ *out++ = 't';
459+ *out++ = ';';
460+ }
461+ else if (*in == '&')
462+ {
463+ if (outend - out < 5)
464+ break;
465+ *out++ = '&';
466+ *out++ = 'a';
467+ *out++ = 'm';
468+ *out++ = 'p';
469+ *out++ = ';';
470+ }
471+ else if (*in == '\r')
472+ {
473+ if (outend - out < 5)
474+ break;
475+ *out++ = '&';
476+ *out++ = '#';
477+ *out++ = '1';
478+ *out++ = '3';
479+ *out++ = ';';
480+ }
481+ else
482+ {
483+ *out++ = *in;
484+ }
485+ ++in;
486+ }
487+ *outlen = out - outstart;
488+ *inlen = in - base;
489+}
490+
491+
492+static
493+void attr_escape(const char *in, size_t *inlen, char* out, size_t *outlen)
494+{
495+ char* outstart = out;
496+ const char* base = in;
497+ char* outend = out + *outlen;
498+ const char* inend;
499+
500+ inend = in + (*inlen);
501+
502+ while ((in < inend) && (out < outend))
503+ {
504+ if (*in == '"')
505+ {
506+ if (outend - out < 2)
507+ break;
508+ *out++ = '\\';
509+ *out++ = '"';
510+ }
511+ else
512+ {
513+ *out++ = *in;
514+ }
515+ ++in;
516+ }
517+ *outlen = out - outstart;
518+ *inlen = in - base;
519+}
520+
521+
522+static
523+char *xml_escape_string(const char *in, size_t inlen,
524+ char *out, size_t outlen)
525+{
526+ if (in != NULL)
527+ {
528+ --outlen;
529+ xml_escape(in, &inlen, out, &outlen);
530+ out[outlen]= 0;
531+ }
532+ else
533+ {
534+ out= 0;
535+ }
536+ return out;
537+}
538+
539+static
540+char *attr_escape_string(const char *in, size_t inlen,
541+ char *out, size_t outlen)
542+{
543+ if (in != NULL)
544+ {
545+ --outlen;
546+ attr_escape(in, &inlen, out, &outlen);
547+ out[outlen]= 0;
548+ }
549+ else
550+ {
551+ out= 0;
552+ }
553+ return out;
554+}
555+
556+static
557+char *escape_string(const char *in, size_t inlen,
558+ char *out, size_t outlen)
559+{
560+ typedef char *(*escape_func_t)(const char *, size_t, char *, size_t);
561+ const escape_func_t escape_func[] = { attr_escape_string, xml_escape_string };
562+
563+ return escape_func[audit_log_format](in, inlen, out, outlen);
564+}
565+
566+static
567+void logger_write_safe(LOGGER_HANDLE *log, const char *buffer, size_t size)
568+{
569+ static int write_error= 0;
570+
571+ if (log != NULL)
572+ {
573+ if (logger_write(log, buffer, size) < 0)
574+ {
575+ if (!write_error)
576+ {
577+ write_error= 1;
578+ fprintf_timestamp(stderr);
579+ fprintf(stderr, "Error writing to file %s. ", audit_log_file);
580+ perror("Error: ");
581+ }
582+ }
583+ else
584+ {
585+ write_error= 0;
586+ }
587+ }
588+}
589+
590+
591+static
592+void logger_write_safe_void(void *log, const char *buffer, size_t size)
593+{
594+ logger_write_safe((LOGGER_HANDLE *)log, buffer, size);
595+}
596+
597+
598+static
599+void audit_log_write_without_buffer(const char *buf, size_t len)
600+{
601+ logger_write_safe(audit_file_logger, buf, len);
602+ if (audit_log_strategy == SYNCHRONOUS && audit_file_logger != NULL)
603+ {
604+ logger_sync(audit_file_logger);
605+ }
606+}
607+
608+
609+static
610+void audit_log_write(const char *buf, size_t len)
611+{
612+ switch (audit_log_strategy)
613+ {
614+ case ASYNCHRONOUS:
615+ case PERFORMANCE:
616+ if (audit_log_buffer != NULL)
617+ audit_log_buffer_write(audit_log_buffer, buf, len);
618+ break;
619+ case SEMISYNCHRONOUS:
620+ case SYNCHRONOUS:
621+ audit_log_write_without_buffer(buf, len);
622+ break;
623+ default:
624+ DBUG_ASSERT(0);
625+ }
626+}
627+
628+
629+
630+/* Defined in MySQL server */
631+extern int orig_argc;
632+extern char **orig_argv;
633+extern char server_version[SERVER_VERSION_LENGTH];
634+
635+static
636+char *make_argv(char *buf, size_t len, int argc, char **argv)
637+{
638+ size_t left= len;
639+
640+ buf[0]= 0;
641+ while (argc > 0 && left > 0)
642+ {
643+ left-= my_snprintf(buf + len - left, left,
644+ "%s%c", *argv, argc > 1 ? ' ' : 0);
645+ argc--; argv++;
646+ }
647+
648+ return buf;
649+}
650+
651+static
652+size_t audit_log_audit_record(char *buf, size_t buflen,
653+ const char *name, time_t t)
654+{
655+ char id_str[MAX_RECORD_ID_SIZE];
656+ char timestamp[MAX_TIMESTAMP_SIZE];
657+ char arg_buf[512];
658+ const char *format_string[] = {
659+ "<AUDIT_RECORD\n"
660+ " \"NAME\"=\"%s\"\n"
661+ " \"RECORD\"=\"%s\"\n"
662+ " \"TIMESTAMP\"=\"%s\"\n"
663+ " \"MYSQL_VERSION\"=\"%s\"\n"
664+ " \"STARTUP_OPTIONS\"=\"%s\"\n"
665+ " \"OS_VERSION\"=\""MACHINE_TYPE"-"SYSTEM_TYPE"\",\n"
666+ "/>\n",
667+ "<AUDIT_RECORD>\n"
668+ " <NAME>%s</NAME>\n"
669+ " <RECORD>%s</RECORD>\n"
670+ " <TIMESTAMP>%s</TIMESTAMP>\n"
671+ " <MYSQL_VERSION>%s</MYSQL_VERSION>\n"
672+ " <STARTUP_OPTIONS>%s</STARTUP_OPTIONS>\n"
673+ " <OS_VERSION>"MACHINE_TYPE"-"SYSTEM_TYPE"</OS_VERSION>\n"
674+ "</AUDIT_RECORD>\n" };
675+
676+ return my_snprintf(buf, buflen,
677+ format_string[audit_log_format],
678+ name,
679+ make_record_id(id_str, sizeof(id_str)),
680+ make_timestamp(timestamp, sizeof(timestamp), t),
681+ server_version,
682+ make_argv(arg_buf, sizeof(arg_buf),
683+ orig_argc - 1, orig_argv + 1));
684+}
685+
686+
687+static
688+size_t audit_log_general_record(char *buf, size_t buflen,
689+ const char *name, time_t t, int status,
690+ const struct mysql_event_general *event)
691+{
692+ char id_str[MAX_RECORD_ID_SIZE];
693+ char timestamp[MAX_TIMESTAMP_SIZE];
694+ char query[512];
695+ const char *format_string[] = {
696+ "<AUDIT_RECORD\n"
697+ " \"NAME\"=\"%s\"\n"
698+ " \"RECORD\"=\"%s\"\n"
699+ " \"TIMESTAMP\"=\"%s\"\n"
700+ " \"COMMAND_CLASS\"=\"%s\"\n"
701+ " \"CONNECTION_ID\"=\"%lu\"\n"
702+ " \"STATUS\"=\"%d\"\n"
703+ " \"SQLTEXT\"=\"%s\"\n"
704+ " \"USER\"=\"%s\"\n"
705+ " \"HOST\"=\"%s\"\n"
706+ " \"OS_USER\"=\"%s\"\n"
707+ " \"IP\"=\"%s\"\n"
708+ "/>\n",
709+ "<AUDIT_RECORD>\n"
710+ " <NAME>%s</NAME>\n"
711+ " <RECORD>%s</RECORD>\n"
712+ " <TIMESTAMP>%s</TIMESTAMP>\n"
713+ " <COMMAND_CLASS>%s</COMMAND_CLASS>\n"
714+ " <CONNECTION_ID>%lu</CONNECTION_ID>\n"
715+ " <STATUS>%d</STATUS>\n"
716+ " <SQLTEXT>%s</SQLTEXT>\n"
717+ " <USER>%s</USER>\n"
718+ " <HOST>%s</HOST>\n"
719+ " <OS_USER>%s</OS_USER>\n"
720+ " <IP>%s</IP>\n"
721+ "</AUDIT_RECORD>\n" };
722+
723+ return my_snprintf(buf, buflen,
724+ format_string[audit_log_format],
725+ name,
726+ make_record_id(id_str, sizeof(id_str)),
727+ make_timestamp(timestamp, sizeof(timestamp), t),
728+ event->general_sql_command.str,
729+ event->general_thread_id, status,
730+ escape_string(event->general_query,
731+ event->general_query_length,
732+ query, sizeof(query)),
733+ event->general_user,
734+ event->general_host.str,
735+ event->general_external_user.str,
736+ event->general_ip.str);
737+}
738+
739+static
740+size_t audit_log_connection_record(char *buf, size_t buflen,
741+ const char *name, time_t t,
742+ const struct mysql_event_connection *event)
743+{
744+ char id_str[MAX_RECORD_ID_SIZE];
745+ char timestamp[MAX_TIMESTAMP_SIZE];
746+ const char *format_string[] = {
747+ "<AUDIT_RECORD\n"
748+ " \"NAME\"=\"%s\"\n"
749+ " \"RECORD\"=\"%s\"\n"
750+ " \"TIMESTAMP\"=\"%s\"\n"
751+ " \"CONNECTION_ID\"=\"%lu\"\n"
752+ " \"STATUS\"=\"%d\"\n"
753+ " \"USER\"=\"%s\"\n"
754+ " \"PRIV_USER\"=\"%s\"\n"
755+ " \"OS_LOGIN\"=\"%s\"\n"
756+ " \"PROXY_USER\"=\"%s\"\n"
757+ " \"HOST\"=\"%s\"\n"
758+ " \"IP\"=\"%s\"\n"
759+ " \"DB\"=\"%s\"\n"
760+ "/>\n",
761+ "<AUDIT_RECORD>\n"
762+ " <NAME>%s</NAME>\n"
763+ " <RECORD>%s</RECORD>\n"
764+ " <TIMESTAMP>%s</TIMESTAMP>\n"
765+ " <CONNECTION_ID>%lu</CONNECTION_ID>\n"
766+ " <STATUS>%d</STATUS>\n"
767+ " <USER>%s</USER>\n"
768+ " <PRIV_USER>%s</PRIV_USER>\n"
769+ " <OS_LOGIN>%s</OS_LOGIN>\n"
770+ " <PROXY_USER>%s</PROXY_USER>\n"
771+ " <HOST>%s</HOST>\n"
772+ " <IP>%s</IP>\n"
773+ " <DB>%s</DB>\n"
774+ "</AUDIT_RECORD>\n" };
775+
776+ return my_snprintf(buf, buflen,
777+ format_string[audit_log_format],
778+ name,
779+ make_record_id(id_str, sizeof(id_str)),
780+ make_timestamp(timestamp, sizeof(timestamp), t),
781+ event->thread_id,
782+ event->status,
783+ event->user ? event->user : "",
784+ event->priv_user ? event->priv_user : "",
785+ event->external_user ? event->external_user : "",
786+ event->proxy_user ? event->proxy_user : "",
787+ event->host ? event->host : "",
788+ event->ip ? event->ip : "",
789+ event->database ? event->database : "");
790+}
791+
792+
793+static
794+int init_new_log_file()
795+{
796+ MY_STAT stat_arg;
797+
798+ audit_file_logger= logger_open(audit_log_file, audit_log_rotate_on_size,
799+ audit_log_rotate_on_size ? audit_log_rotations : 0,
800+ audit_log_strategy >= SEMISYNCHRONOUS,
801+ &stat_arg);
802+ if (audit_file_logger == NULL)
803+ {
804+ fprintf_timestamp(stderr);
805+ fprintf(stderr, "Cannot open file %s. ", audit_log_file);
806+ perror("Error: ");
807+ return(1);
808+ }
809+
810+ log_file_time= stat_arg.st_mtime;
811+
812+ init_record_id(stat_arg.st_size);
813+
814+ return(0);
815+}
816+
817+
818+static
819+int reopen_log_file()
820+{
821+ MY_STAT stat_arg;
822+
823+ if (logger_reopen(audit_file_logger, &stat_arg))
824+ {
825+ fprintf_timestamp(stderr);
826+ fprintf(stderr, "Cannot open file %s. ", audit_log_file);
827+ perror("Error: ");
828+ return(1);
829+ }
830+
831+ log_file_time= stat_arg.st_mtime;
832+
833+ init_record_id(stat_arg.st_size);
834+
835+ return(0);
836+}
837+
838+
839+static
840+void close_log_file()
841+{
842+ if (audit_file_logger != NULL)
843+ logger_close(audit_file_logger);
844+}
845+
846+
847+static
848+int audit_log_plugin_init(void *arg __attribute__((unused)))
849+{
850+ char buf[1024];
851+ size_t len;
852+
853+ if (init_new_log_file())
854+ return(1);
855+
856+ if (audit_log_strategy < SEMISYNCHRONOUS)
857+ {
858+ audit_log_buffer= audit_log_buffer_init(audit_log_buffer_size,
859+ audit_log_strategy == PERFORMANCE, logger_write_safe_void,
860+ audit_file_logger);
861+ }
862+
863+ len= audit_log_audit_record(buf, sizeof(buf), "Audit", time(NULL));
864+ audit_log_write(buf, len);
865+
866+ return(0);
867+}
868+
869+
870+static
871+int audit_log_plugin_deinit(void *arg __attribute__((unused)))
872+{
873+ char buf[1024];
874+ size_t len;
875+
876+ len= audit_log_audit_record(buf, sizeof(buf), "NoAudit", time(NULL));
877+ audit_log_write(buf, len);
878+
879+ if (audit_log_buffer != NULL)
880+ audit_log_buffer_shutdown(audit_log_buffer);
881+
882+ close_log_file();
883+
884+ return(0);
885+}
886+
887+
888+static
889+int is_event_class_allowed_by_policy(unsigned int class,
890+ enum audit_log_policy_t policy)
891+{
892+ static unsigned int class_mask[]=
893+ {
894+ MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK, /* ALL */
895+ 0, /* NONE */
896+ MYSQL_AUDIT_CONNECTION_CLASSMASK, /* LOGINS */
897+ MYSQL_AUDIT_GENERAL_CLASSMASK, /* QUERIES */
898+ };
899+
900+ return (class_mask[policy] & (1 << class)) != 0;
901+}
902+
903+
904+static
905+void audit_log_notify(MYSQL_THD thd __attribute__((unused)),
906+ unsigned int event_class,
907+ const void *event)
908+{
909+ char buf[1024];
910+ size_t len;
911+
912+ if (!is_event_class_allowed_by_policy(event_class, audit_log_policy))
913+ return;
914+
915+ if (event_class == MYSQL_AUDIT_GENERAL_CLASS)
916+ {
917+ const struct mysql_event_general *event_general=
918+ (const struct mysql_event_general *) event;
919+ switch (event_general->event_subclass)
920+ {
921+ case MYSQL_AUDIT_GENERAL_STATUS:
922+ if (event_general->general_command_length == 4 &&
923+ strncmp(event_general->general_command, "Quit", 4) == 0)
924+ break;
925+ len= audit_log_general_record(buf, sizeof(buf),
926+ event_general->general_command,
927+ event_general->general_time,
928+ event_general->general_error_code,
929+ event_general);
930+ audit_log_write(buf, len);
931+ break;
932+ }
933+ }
934+ else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
935+ {
936+ const struct mysql_event_connection *event_connection=
937+ (const struct mysql_event_connection *) event;
938+ switch (event_connection->event_subclass)
939+ {
940+ case MYSQL_AUDIT_CONNECTION_CONNECT:
941+ len= audit_log_connection_record(buf, sizeof(buf),
942+ "Connect", time(NULL), event_connection);
943+ audit_log_write(buf, len);
944+ break;
945+ case MYSQL_AUDIT_CONNECTION_DISCONNECT:
946+ len= audit_log_connection_record(buf, sizeof(buf),
947+ "Quit", time(NULL), event_connection);
948+ audit_log_write(buf, len);
949+ break;
950+ case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
951+ len= audit_log_connection_record(buf, sizeof(buf),
952+ "Change user", time(NULL),
953+ event_connection);
954+ audit_log_write(buf, len);
955+ break;
956+ default:
957+ break;
958+ }
959+ }
960+}
961+
962+
963+/*
964+ * Plugin system vars
965+ */
966+
967+static MYSQL_SYSVAR_STR(file, audit_log_file,
968+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
969+ "The name of the log file.", NULL, NULL, default_audit_log_file);
970+
971+static const char *audit_log_policy_names[]=
972+ { "ALL", "NONE", "LOGINS", "QUERIES", 0 };
973+
974+static TYPELIB audit_log_policy_typelib=
975+{
976+ array_elements(audit_log_policy_names) - 1, "audit_log_policy_typelib",
977+ audit_log_policy_names, NULL
978+};
979+
980+static MYSQL_SYSVAR_ENUM(policy, audit_log_policy, PLUGIN_VAR_RQCMDARG,
981+ "The policy controlling the information written by the audit log "
982+ "plugin to its log file.", NULL, NULL, ALL,
983+ &audit_log_policy_typelib);
984+
985+static const char *audit_log_strategy_names[]=
986+ { "ASYNCHRONOUS", "PERFORMANCE", "SEMISYNCHRONOUS", "SYNCHRONOUS", 0 };
987+static TYPELIB audit_log_strategy_typelib=
988+{
989+ array_elements(audit_log_strategy_names) - 1, "audit_log_strategy_typelib",
990+ audit_log_strategy_names, NULL
991+};
992+
993+static MYSQL_SYSVAR_ENUM(strategy, audit_log_strategy,
994+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
995+ "The logging method used by the audit log plugin.", NULL, NULL,
996+ ASYNCHRONOUS, &audit_log_strategy_typelib);
997+
998+static const char *audit_log_format_names[]=
999+ { "OLD", "NEW", 0 };
1000+static TYPELIB audit_log_format_typelib=
1001+{
1002+ array_elements(audit_log_format_names) - 1, "audit_log_format_typelib",
1003+ audit_log_format_names, NULL
1004+};
1005+
1006+static MYSQL_SYSVAR_ENUM(format, audit_log_format,
1007+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
1008+ "The audit log file format.", NULL, NULL,
1009+ ASYNCHRONOUS, &audit_log_format_typelib);
1010+
1011+static MYSQL_SYSVAR_ULONGLONG(buffer_size, audit_log_buffer_size,
1012+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
1013+ "The size of the buffer for asynchronous logging.",
1014+ NULL, NULL, 1048576UL, 4096UL, ULONGLONG_MAX, 4096UL);
1015+
1016+static
1017+void audit_log_rotate_on_size_update(
1018+ MYSQL_THD thd __attribute__((unused)),
1019+ struct st_mysql_sys_var *var __attribute__((unused)),
1020+ void *var_ptr __attribute__((unused)),
1021+ const void *save)
1022+{
1023+ ulonglong new_val= *(ulonglong *)(save);
1024+
1025+ if (audit_file_logger)
1026+ logger_set_size_limit(audit_file_logger, new_val);
1027+}
1028+
1029+static MYSQL_SYSVAR_ULONGLONG(rotate_on_size, audit_log_rotate_on_size,
1030+ PLUGIN_VAR_RQCMDARG,
1031+ "Maximum size of the log to start the rotation.",
1032+ NULL, audit_log_rotate_on_size_update, 0UL, 0UL, ULONGLONG_MAX, 4096UL);
1033+
1034+static
1035+void audit_log_rotations_update(
1036+ MYSQL_THD thd __attribute__((unused)),
1037+ struct st_mysql_sys_var *var __attribute__((unused)),
1038+ void *var_ptr __attribute__((unused)),
1039+ const void *save)
1040+{
1041+ ulonglong new_val= *(ulonglong *)(save);
1042+
1043+ if (audit_file_logger)
1044+ logger_set_rotations(audit_file_logger, new_val);
1045+}
1046+
1047+static MYSQL_SYSVAR_ULONGLONG(rotations, audit_log_rotations,
1048+ PLUGIN_VAR_RQCMDARG,
1049+ "Maximum number of rotations to keep.",
1050+ NULL, audit_log_rotations_update, 0UL, 0UL, 999UL, 1UL);
1051+
1052+static
1053+void audit_log_flush_update(
1054+ MYSQL_THD thd __attribute__((unused)),
1055+ struct st_mysql_sys_var *var __attribute__((unused)),
1056+ void *var_ptr __attribute__((unused)),
1057+ const void *save)
1058+{
1059+ char new_val= *(const char *)(save);
1060+
1061+ if (new_val != audit_log_flush && new_val == TRUE)
1062+ {
1063+ audit_log_flush= TRUE;
1064+ reopen_log_file();
1065+ audit_log_flush= FALSE;
1066+ }
1067+}
1068+
1069+static MYSQL_SYSVAR_BOOL(flush, audit_log_flush,
1070+ PLUGIN_VAR_OPCMDARG, "Flush the log file.", NULL,
1071+ audit_log_flush_update, 0);
1072+
1073+static struct st_mysql_sys_var* audit_log_system_variables[] =
1074+{
1075+ MYSQL_SYSVAR(file),
1076+ MYSQL_SYSVAR(policy),
1077+ MYSQL_SYSVAR(strategy),
1078+ MYSQL_SYSVAR(format),
1079+ MYSQL_SYSVAR(buffer_size),
1080+ MYSQL_SYSVAR(rotate_on_size),
1081+ MYSQL_SYSVAR(rotations),
1082+ MYSQL_SYSVAR(flush),
1083+ NULL
1084+};
1085+
1086+
1087+/*
1088+ Plugin type-specific descriptor
1089+*/
1090+static struct st_mysql_audit audit_log_descriptor=
1091+{
1092+ MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */
1093+ NULL, /* release_thd function */
1094+ audit_log_notify, /* notify function */
1095+ { MYSQL_AUDIT_GENERAL_CLASSMASK |
1096+ MYSQL_AUDIT_CONNECTION_CLASSMASK } /* class mask */
1097+};
1098+
1099+/*
1100+ Plugin status variables for SHOW STATUS
1101+*/
1102+
1103+static struct st_mysql_show_var audit_log_status_variables[]=
1104+{
1105+ { 0, 0, 0}
1106+};
1107+
1108+
1109+/*
1110+ Plugin library descriptor
1111+*/
1112+
1113+mysql_declare_plugin(audit_log)
1114+{
1115+ MYSQL_AUDIT_PLUGIN, /* type */
1116+ &audit_log_descriptor, /* descriptor */
1117+ "audit_log", /* name */
1118+ "Percona LLC and/or its affiliates.", /* author */
1119+ "Audit log", /* description */
1120+ PLUGIN_LICENSE_GPL,
1121+ audit_log_plugin_init, /* init function (when loaded) */
1122+ audit_log_plugin_deinit, /* deinit function (when unloaded) */
1123+ PLUGIN_VERSION, /* version */
1124+ audit_log_status_variables, /* status variables */
1125+ audit_log_system_variables, /* system variables */
1126+ NULL,
1127+ 0,
1128+}
1129+mysql_declare_plugin_end;
1130+
1131
1132=== added file 'plugin/audit_log/buffer.c'
1133--- plugin/audit_log/buffer.c 1970-01-01 00:00:00 +0000
1134+++ plugin/audit_log/buffer.c 2014-04-21 12:12:26 +0000
1135@@ -0,0 +1,196 @@
1136+/* Copyright (c) 2014 Percona LLC and/or its affiliates. All rights reserved.
1137+
1138+ This program is free software; you can redistribute it and/or
1139+ modify it under the terms of the GNU General Public License
1140+ as published by the Free Software Foundation; version 2 of
1141+ the License.
1142+
1143+ This program is distributed in the hope that it will be useful,
1144+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1145+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1146+ GNU General Public License for more details.
1147+
1148+ You should have received a copy of the GNU General Public License
1149+ along with this program; if not, write to the Free Software
1150+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
1151+
1152+#include "buffer.h"
1153+
1154+#include <my_pthread.h>
1155+#include <my_sys.h>
1156+
1157+struct audit_log_buffer {
1158+ char *buf;
1159+ size_t size;
1160+ size_t write_pos;
1161+ size_t flush_pos;
1162+ pthread_t flush_worker_thread;
1163+ int stop;
1164+ int drop_if_full;
1165+ void *write_func_data;
1166+ audit_log_write_func write_func;
1167+ mysql_mutex_t mutex;
1168+ mysql_cond_t flushed_cond;
1169+ mysql_cond_t written_cond;
1170+};
1171+
1172+#if defined(HAVE_PSI_INTERFACE)
1173+/* These belong to the service initialization */
1174+static PSI_mutex_key key_log_mutex;
1175+static PSI_mutex_info mutex_key_list[]=
1176+{{ &key_log_mutex, "audit_log_buffer::mutex", PSI_FLAG_GLOBAL}};
1177+
1178+static PSI_cond_key key_log_written_cond, key_log_flushed_cond;
1179+static PSI_cond_info cond_key_list[]=
1180+{{ &key_log_written_cond, "audit_log_buffer::written_cond", PSI_FLAG_GLOBAL },
1181+ { &key_log_flushed_cond, "audit_log_buffer::flushed_cond", PSI_FLAG_GLOBAL }};
1182+
1183+#endif
1184+
1185+#ifndef min
1186+#define min(a,b) (((a)<(b))?(a):(b))
1187+#endif
1188+
1189+
1190+static
1191+void audit_log_flush(audit_log_buffer_t *log)
1192+{
1193+ mysql_mutex_lock(&log->mutex);
1194+ while (log->flush_pos == log->write_pos)
1195+ {
1196+ struct timespec abstime;
1197+ if (log->stop)
1198+ {
1199+ mysql_mutex_unlock(&log->mutex);
1200+ return;
1201+ }
1202+ set_timespec(abstime, 1);
1203+ mysql_cond_timedwait(&log->written_cond, &log->mutex, &abstime);
1204+ }
1205+
1206+ if (log->flush_pos > log->write_pos % log->size)
1207+ {
1208+ mysql_mutex_unlock(&log->mutex);
1209+ log->write_func(log->write_func_data,
1210+ log->buf + log->flush_pos,
1211+ log->size - log->flush_pos);
1212+ mysql_mutex_lock(&log->mutex);
1213+ log->flush_pos= 0;
1214+ log->write_pos%= log->size;
1215+ DBUG_ASSERT(log->write_pos >= log->flush_pos);
1216+ mysql_cond_broadcast(&log->flushed_cond);
1217+ mysql_mutex_unlock(&log->mutex);
1218+ }
1219+ else
1220+ {
1221+ size_t flushlen= log->write_pos - log->flush_pos;
1222+ mysql_mutex_unlock(&log->mutex);
1223+ log->write_func(log->write_func_data,
1224+ log->buf + log->flush_pos, flushlen);
1225+ mysql_mutex_lock(&log->mutex);
1226+ log->flush_pos+= flushlen;
1227+ DBUG_ASSERT(log->write_pos >= log->flush_pos);
1228+ mysql_cond_broadcast(&log->flushed_cond);
1229+ mysql_mutex_unlock(&log->mutex);
1230+ }
1231+}
1232+
1233+
1234+static
1235+void *audit_log_flush_worker(void *arg)
1236+{
1237+ audit_log_buffer_t *log= (audit_log_buffer_t*) arg;
1238+
1239+ my_thread_init();
1240+ while (!(log->stop && log->flush_pos == log->write_pos))
1241+ {
1242+ audit_log_flush(log);
1243+ }
1244+ my_thread_end();
1245+
1246+ return NULL;
1247+}
1248+
1249+
1250+audit_log_buffer_t *audit_log_buffer_init(size_t size, int drop_if_full,
1251+ audit_log_write_func write_func, void *data)
1252+{
1253+ audit_log_buffer_t *log= (audit_log_buffer_t*)
1254+ calloc(sizeof(audit_log_buffer_t) + size, 1);
1255+
1256+#ifdef HAVE_PSI_INTERFACE
1257+ if(PSI_server)
1258+ {
1259+ PSI_server->register_mutex("server_audit",
1260+ mutex_key_list, array_elements(mutex_key_list));
1261+ PSI_server->register_cond("server_audit",
1262+ cond_key_list, array_elements(cond_key_list));
1263+ }
1264+#endif /* HAVE_PSI_INTERFACE */
1265+
1266+ if (log != NULL)
1267+ {
1268+ log->buf= ((char*) log + sizeof(audit_log_buffer_t));
1269+ log->drop_if_full= drop_if_full;
1270+ log->write_func= write_func;
1271+ log->write_func_data= data;
1272+ log->size= size;
1273+
1274+ mysql_mutex_init(key_log_mutex, &log->mutex, MY_MUTEX_INIT_FAST);
1275+ mysql_cond_init(key_log_flushed_cond, &log->flushed_cond, NULL);
1276+ mysql_cond_init(key_log_written_cond, &log->written_cond, NULL);
1277+ pthread_create(&log->flush_worker_thread, NULL,
1278+ audit_log_flush_worker, log);
1279+
1280+ }
1281+
1282+ return log;
1283+}
1284+
1285+
1286+void audit_log_buffer_shutdown(audit_log_buffer_t *log)
1287+{
1288+ log->stop= TRUE;
1289+
1290+ pthread_join(log->flush_worker_thread, NULL);
1291+ mysql_cond_destroy(&log->flushed_cond);
1292+ mysql_cond_destroy(&log->written_cond);
1293+ mysql_mutex_destroy(&log->mutex);
1294+
1295+ free(log);
1296+}
1297+
1298+
1299+int audit_log_buffer_write(audit_log_buffer_t *log, const char *buf, size_t len)
1300+{
1301+ if (len > log->size)
1302+ return(1);
1303+
1304+ mysql_mutex_lock(&log->mutex);
1305+loop:
1306+ if (log->write_pos + len < log->flush_pos + log->size)
1307+ {
1308+ size_t wrlen= min(len, log->size -
1309+ (log->write_pos % log->size));
1310+ memcpy(log->buf + (log->write_pos % log->size), buf, wrlen);
1311+ if (wrlen < len)
1312+ memcpy(log->buf, buf + wrlen, len - wrlen);
1313+ log->write_pos= log->write_pos + len;
1314+ DBUG_ASSERT(log->write_pos >= log->flush_pos);
1315+ }
1316+ else
1317+ {
1318+ if (!log->drop_if_full)
1319+ {
1320+ mysql_cond_wait(&log->flushed_cond, &log->mutex);
1321+ goto loop;
1322+ }
1323+ }
1324+ if (log->write_pos > log->flush_pos + log->size / 2)
1325+ {
1326+ mysql_cond_signal(&log->written_cond);
1327+ }
1328+ mysql_mutex_unlock(&log->mutex);
1329+
1330+ return(0);
1331+}
1332
1333=== added file 'plugin/audit_log/buffer.h'
1334--- plugin/audit_log/buffer.h 1970-01-01 00:00:00 +0000
1335+++ plugin/audit_log/buffer.h 2014-04-21 12:12:26 +0000
1336@@ -0,0 +1,32 @@
1337+/* Copyright (c) 2014 Percona LLC and/or its affiliates. All rights reserved.
1338+
1339+ This program is free software; you can redistribute it and/or
1340+ modify it under the terms of the GNU General Public License
1341+ as published by the Free Software Foundation; version 2 of
1342+ the License.
1343+
1344+ This program is distributed in the hope that it will be useful,
1345+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1346+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1347+ GNU General Public License for more details.
1348+
1349+ You should have received a copy of the GNU General Public License
1350+ along with this program; if not, write to the Free Software
1351+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
1352+
1353+
1354+#ifndef AUDIT_LOG_BUFFER_INCLUDED
1355+#define AUDIT_LOG_BUFFER_INCLUDED
1356+
1357+#include <string.h> // for size_t
1358+
1359+typedef struct audit_log_buffer audit_log_buffer_t;
1360+
1361+typedef void (*audit_log_write_func)(void *data, const char *buf, size_t len);
1362+
1363+audit_log_buffer_t *audit_log_buffer_init(size_t size, int drop_if_full,
1364+ audit_log_write_func write_func, void *data);
1365+void audit_log_buffer_shutdown(audit_log_buffer_t *log);
1366+int audit_log_buffer_write(audit_log_buffer_t *log, const char *buf, size_t len);
1367+
1368+#endif
1369
1370=== added file 'plugin/audit_log/file_logger.c'
1371--- plugin/audit_log/file_logger.c 1970-01-01 00:00:00 +0000
1372+++ plugin/audit_log/file_logger.c 2014-04-21 12:12:26 +0000
1373@@ -0,0 +1,338 @@
1374+/* Copyright (C) 2012 Monty Program Ab
1375+
1376+ This program is free software; you can redistribute it and/or modify
1377+ it under the terms of the GNU General Public License as published by
1378+ the Free Software Foundation; version 2 of the License.
1379+
1380+ This program is distributed in the hope that it will be useful,
1381+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1382+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1383+ GNU General Public License for more details.
1384+
1385+ You should have received a copy of the GNU General Public License
1386+ along with this program; if not, write to the Free Software
1387+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
1388+
1389+#include <string.h>
1390+
1391+#include <mysql/plugin.h>
1392+#include <my_global.h>
1393+#include <my_sys.h>
1394+#include <my_pthread.h>
1395+
1396+#include "logger.h"
1397+
1398+extern char *mysql_data_home;
1399+
1400+#ifndef FLOGGER_NO_PSI
1401+ #define flogger_mutex_init(A,B,C) \
1402+ if ((B)->thread_safe) \
1403+ mysql_mutex_init(A,&((B)->lock),C)
1404+
1405+ #define flogger_mutex_destroy(A) \
1406+ if ((A)->thread_safe) \
1407+ mysql_mutex_destroy(&((A)->lock))
1408+
1409+ #define flogger_mutex_lock(A) \
1410+ if ((A)->thread_safe) \
1411+ mysql_mutex_lock(&((A)->lock))
1412+
1413+ #define flogger_mutex_unlock(A) \
1414+ if ((A)->thread_safe) \
1415+ mysql_mutex_unlock(&((A)->lock))
1416+#else
1417+ #define flogger_mutex_init(A,B,C) \
1418+ if ((B)->thread_safe) \
1419+ pthread_mutex_init(&((B)->lock.m_mutex), C)
1420+
1421+ #define flogger_mutex_destroy(A) \
1422+ if ((A)->thread_safe) \
1423+ pthread_mutex_destroy(&((A)->lock.m_mutex))
1424+
1425+ #define flogger_mutex_lock(A) \
1426+ if ((A)->thread_safe) \
1427+ pthread_mutex_lock(&((A)->lock.m_mutex))
1428+
1429+ #define flogger_mutex_unlock(A) \
1430+ if ((A)->thread_safe) \
1431+ pthread_mutex_unlock(&((A)->lock.m_mutex))
1432+#endif /*!FLOGGER_NO_PSI*/
1433+
1434+#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI)
1435+/* These belong to the service initialization */
1436+static PSI_mutex_key key_LOCK_logger_service;
1437+static PSI_mutex_info mutex_list[]=
1438+{{ &key_LOCK_logger_service, "file_logger::lock", PSI_FLAG_GLOBAL}};
1439+#else
1440+#define key_LOCK_logger_service 0
1441+#endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/
1442+
1443+typedef struct logger_handle_st {
1444+ File file;
1445+ char path[FN_REFLEN];
1446+ unsigned long long size_limit;
1447+ unsigned int rotations;
1448+ size_t path_len;
1449+ mysql_mutex_t lock;
1450+ int thread_safe;
1451+} LSFS;
1452+
1453+
1454+#define LOG_FLAGS (O_APPEND | O_CREAT | O_WRONLY)
1455+
1456+static unsigned int n_dig(unsigned int i)
1457+{
1458+ return (i == 0) ? 0 : ((i < 10) ? 1 : ((i < 100) ? 2 : 3));
1459+}
1460+
1461+
1462+LOGGER_HANDLE *logger_open(const char *path,
1463+ unsigned long long size_limit,
1464+ unsigned int rotations,
1465+ int thread_safe,
1466+ MY_STAT *stat)
1467+{
1468+ LOGGER_HANDLE new_log, *l_perm;
1469+ /*
1470+ I don't think we ever need more rotations,
1471+ but if it's so, the rotation procedure should be adapted to it.
1472+ */
1473+ if (rotations > 999)
1474+ return 0;
1475+
1476+ new_log.rotations= rotations;
1477+ new_log.size_limit= size_limit;
1478+ new_log.path_len= strlen(fn_format(new_log.path, path,
1479+ mysql_data_home, "", MY_UNPACK_FILENAME));
1480+ new_log.thread_safe= thread_safe;
1481+
1482+ if (new_log.path_len+n_dig(rotations)+1 > FN_REFLEN)
1483+ {
1484+ errno= ENAMETOOLONG;
1485+ /* File path too long */
1486+ return 0;
1487+ }
1488+
1489+ if ((new_log.file= open(new_log.path, LOG_FLAGS, 0666)) < 0)
1490+ {
1491+ errno= my_errno;
1492+ /* Check errno for the cause */
1493+ return 0;
1494+ }
1495+
1496+ if (stat != NULL)
1497+ {
1498+ if (my_fstat(new_log.file, stat, MYF(0)))
1499+ {
1500+ errno= my_errno;
1501+ my_close(new_log.file, MYF(0));
1502+ new_log.file= -1;
1503+ return 0;
1504+ }
1505+ }
1506+
1507+ if (!(l_perm= (LOGGER_HANDLE *) my_malloc(sizeof(LOGGER_HANDLE), MYF(0))))
1508+ {
1509+ my_close(new_log.file, MYF(0));
1510+ new_log.file= -1;
1511+ return 0; /* End of memory */
1512+ }
1513+ *l_perm= new_log;
1514+
1515+#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) && !defined(FLOGGER_NO_THREADSAFE)
1516+ if (PSI_server)
1517+ PSI_server->register_mutex("file_logger",
1518+ mutex_list, array_elements(mutex_list));
1519+#endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/
1520+
1521+ flogger_mutex_init(key_LOCK_logger_service, l_perm,
1522+ MY_MUTEX_INIT_FAST);
1523+
1524+ return l_perm;
1525+}
1526+
1527+int logger_close(LOGGER_HANDLE *log)
1528+{
1529+ int result;
1530+ File file= log->file;
1531+ flogger_mutex_destroy(log);
1532+ my_free(log);
1533+ if ((result= my_close(file, MYF(0))))
1534+ errno= my_errno;
1535+ return result;
1536+}
1537+
1538+
1539+int logger_reopen(LOGGER_HANDLE *log, MY_STAT *stat)
1540+{
1541+ int result= 0;
1542+
1543+ flogger_mutex_lock(log);
1544+
1545+ if ((result= my_close(log->file, MYF(0))))
1546+ {
1547+ errno= my_errno;
1548+ goto error;
1549+ }
1550+
1551+ if ((log->file= my_open(log->path, LOG_FLAGS, MYF(0))) < 0)
1552+ {
1553+ errno= my_errno;
1554+ result= 1;
1555+ goto error;
1556+ }
1557+
1558+ if (stat != NULL)
1559+ {
1560+ if ((result= my_fstat(log->file, stat, MYF(0))))
1561+ {
1562+ errno= my_errno;
1563+ goto error;
1564+ }
1565+ }
1566+
1567+error:
1568+ flogger_mutex_unlock(log);
1569+
1570+ return 0;
1571+}
1572+
1573+
1574+static char *logname(LOGGER_HANDLE *log, char *buf, unsigned int n_log)
1575+{
1576+ sprintf(buf+log->path_len, ".%0*u", n_dig(log->rotations), n_log);
1577+ return buf;
1578+}
1579+
1580+
1581+static int do_rotate(LOGGER_HANDLE *log)
1582+{
1583+ char namebuf[FN_REFLEN];
1584+ int result;
1585+ unsigned int i;
1586+ char *buf_old, *buf_new, *tmp;
1587+
1588+ if (log->rotations == 0)
1589+ return 0;
1590+
1591+ memcpy(namebuf, log->path, log->path_len);
1592+
1593+ buf_new= logname(log, namebuf, log->rotations);
1594+ buf_old= log->path;
1595+ for (i=log->rotations-1; i>0; i--)
1596+ {
1597+ logname(log, buf_old, i);
1598+ if (!access(buf_old, F_OK) &&
1599+ (result= my_rename(buf_old, buf_new, MYF(0))))
1600+ goto exit;
1601+ tmp= buf_old;
1602+ buf_old= buf_new;
1603+ buf_new= tmp;
1604+ }
1605+ if ((result= my_close(log->file, MYF(0))))
1606+ goto exit;
1607+ namebuf[log->path_len]= 0;
1608+ result= my_rename(namebuf, logname(log, log->path, 1), MYF(0));
1609+ log->file= my_open(namebuf, LOG_FLAGS, MYF(0));
1610+exit:
1611+ errno= my_errno;
1612+ return log->file < 0 || result;
1613+}
1614+
1615+
1616+int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap)
1617+{
1618+ int result;
1619+ my_off_t filesize;
1620+ char cvtbuf[1024];
1621+ size_t n_bytes;
1622+
1623+ flogger_mutex_lock(log);
1624+ if (log->rotations > 0)
1625+ if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 ||
1626+ ((unsigned long long)filesize >= log->size_limit &&
1627+ do_rotate(log)))
1628+ {
1629+ result= -1;
1630+ errno= my_errno;
1631+ goto exit; /* Log rotation needed but failed */
1632+ }
1633+
1634+ n_bytes= my_vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap);
1635+ if (n_bytes >= sizeof(cvtbuf))
1636+ n_bytes= sizeof(cvtbuf) - 1;
1637+
1638+ result= my_write(log->file, (uchar *) cvtbuf, n_bytes, MYF(0));
1639+
1640+exit:
1641+ flogger_mutex_unlock(log);
1642+ return result;
1643+}
1644+
1645+
1646+int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size)
1647+{
1648+ int result;
1649+ my_off_t filesize;
1650+
1651+ flogger_mutex_lock(log);
1652+ if (log->rotations > 0)
1653+ if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 ||
1654+ ((unsigned long long)filesize >= log->size_limit &&
1655+ do_rotate(log)))
1656+ {
1657+ result= -1;
1658+ errno= my_errno;
1659+ goto exit; /* Log rotation needed but failed */
1660+ }
1661+
1662+ result= my_write(log->file, (uchar *) buffer, size, MYF(0));
1663+
1664+exit:
1665+ flogger_mutex_unlock(log);
1666+ return result;
1667+}
1668+
1669+
1670+int logger_rotate(LOGGER_HANDLE *log)
1671+{
1672+ int result;
1673+ flogger_mutex_lock(log);
1674+ result= do_rotate(log);
1675+ flogger_mutex_unlock(log);
1676+ return result;
1677+}
1678+
1679+
1680+int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...)
1681+{
1682+ int result;
1683+ va_list args;
1684+ va_start(args,fmt);
1685+ result= logger_vprintf(log, fmt, args);
1686+ va_end(args);
1687+ return result;
1688+}
1689+
1690+void logger_init_mutexes()
1691+{
1692+#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) && !defined(FLOGGER_NO_THREADSAFE)
1693+ if (PSI_server)
1694+ PSI_server->register_mutex("audit_logger", mutex_list, 1);
1695+#endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/
1696+}
1697+
1698+int logger_sync(LOGGER_HANDLE *log)
1699+{
1700+ return my_sync(log->file, MYF(0));
1701+}
1702+
1703+void logger_set_size_limit(LOGGER_HANDLE *log, unsigned long long size_limit)
1704+{
1705+ log->size_limit= size_limit;
1706+}
1707+
1708+void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations)
1709+{
1710+ log->rotations= rotations;
1711+}
1712
1713=== added file 'plugin/audit_log/logger.h'
1714--- plugin/audit_log/logger.h 1970-01-01 00:00:00 +0000
1715+++ plugin/audit_log/logger.h 2014-04-21 12:12:26 +0000
1716@@ -0,0 +1,85 @@
1717+/* Copyright (C) 2012 Monty Program Ab
1718+
1719+ This program is free software; you can redistribute it and/or modify
1720+ it under the terms of the GNU General Public License as published by
1721+ the Free Software Foundation; version 2 of the License.
1722+
1723+ This program is distributed in the hope that it will be useful,
1724+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1725+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1726+ GNU General Public License for more details.
1727+
1728+ You should have received a copy of the GNU General Public License
1729+ along with this program; if not, write to the Free Software
1730+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
1731+
1732+#ifndef MYSQL_SERVICE_LOGGER_INCLUDED
1733+#define MYSQL_SERVICE_LOGGER_INCLUDED
1734+
1735+#ifndef MYSQL_ABI_CHECK
1736+#include <stdarg.h>
1737+#endif
1738+
1739+/**
1740+ @file
1741+ logger service
1742+
1743+ Log file with rotation implementation.
1744+
1745+ This service implements logging with possible rotation
1746+ of the log files. Interface intentionally tries to be similar to FILE*
1747+ related functions.
1748+
1749+ So that one can open the log with logger_open(), specifying
1750+ the limit on the logfile size and the rotations number.
1751+
1752+ Then it's possible to write messages to the log with
1753+ logger_printf or logger_vprintf functions.
1754+
1755+ As the size of the logfile grows over the specified limit,
1756+ it is renamed to 'logfile.1'. The former 'logfile.1' becomes
1757+ 'logfile.2', etc. The file 'logfile.rotations' is removed.
1758+ That's how the rotation works.
1759+
1760+ The rotation can be forced with the logger_rotate() call.
1761+
1762+ Finally the log should be closed with logger_close().
1763+
1764+@notes:
1765+ Implementation checks the size of the log file before it starts new
1766+ printf into it. So the size of the file gets over the limit when it rotates.
1767+
1768+ The access is secured with the mutex, so the log is threadsafe.
1769+*/
1770+
1771+#include <sys/types.h>
1772+#include <my_dir.h>
1773+
1774+#ifdef __cplusplus
1775+extern "C" {
1776+#endif
1777+
1778+typedef struct logger_handle_st LOGGER_HANDLE;
1779+
1780+void logger_init_mutexes();
1781+LOGGER_HANDLE *logger_open(const char *path,
1782+ unsigned long long size_limit,
1783+ unsigned int rotations,
1784+ int thread_safe,
1785+ MY_STAT *stat);
1786+int logger_close(LOGGER_HANDLE *log);
1787+int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
1788+int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...);
1789+int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size);
1790+int logger_rotate(LOGGER_HANDLE *log);
1791+int logger_sync(LOGGER_HANDLE *log);
1792+int logger_reopen(LOGGER_HANDLE *log, MY_STAT *stat);
1793+void logger_set_size_limit(LOGGER_HANDLE *log, unsigned long long size_limit);
1794+void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations);
1795+
1796+#ifdef __cplusplus
1797+}
1798+#endif
1799+
1800+#endif /*MYSQL_SERVICE_LOGGER_INCLUDED*/
1801+

Subscribers

People subscribed via source and target branches