Merge lp:~sergei.glushchenko/percona-server/5.6-ps-blueprint-audit-log-plugin into lp:percona-server/5.6
- 5.6-ps-blueprint-audit-log-plugin
- Merge into 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 |
Related bugs: | |
Related blueprints: |
Audit Log Plugin 5.6
(High)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexey Kopytov (community) | Approve | ||
Review via email: mp+205595@code.launchpad.net |
Commit message
Description of the change
Merge audit log plugin from 5.5
http://
To post a comment you must log in.
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 | + |
Same questions as in 5.5 MP.