summaryrefslogtreecommitdiffstats
path: root/plugin/server_audit
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plugin/server_audit/CMakeLists.txt18
-rw-r--r--plugin/server_audit/COPYING339
-rw-r--r--plugin/server_audit/plugin_audit_v4.h561
-rw-r--r--plugin/server_audit/server_audit.c3104
-rw-r--r--plugin/server_audit/test_audit_v4.c163
5 files changed, 4185 insertions, 0 deletions
diff --git a/plugin/server_audit/CMakeLists.txt b/plugin/server_audit/CMakeLists.txt
new file mode 100644
index 00000000..8a5333cf
--- /dev/null
+++ b/plugin/server_audit/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2013 Alexey Botchkov and SkySQL Ab
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+SET(SOURCES server_audit.c test_audit_v4.c plugin_audit_v4.h)
+
+MYSQL_ADD_PLUGIN(server_audit ${SOURCES} MODULE_ONLY RECOMPILE_FOR_EMBEDDED)
diff --git a/plugin/server_audit/COPYING b/plugin/server_audit/COPYING
new file mode 100644
index 00000000..6e475df5
--- /dev/null
+++ b/plugin/server_audit/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugin/server_audit/plugin_audit_v4.h b/plugin/server_audit/plugin_audit_v4.h
new file mode 100644
index 00000000..a2a38806
--- /dev/null
+++ b/plugin/server_audit/plugin_audit_v4.h
@@ -0,0 +1,561 @@
+/* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; version 2 of
+ the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#ifndef _my_audit_h
+#define _my_audit_h
+
+#ifndef PLUGIN_CONTEXT
+#include "plugin.h"
+#include "mysql/mysql_lex_string.h"
+#ifndef MYSQL_ABI_CHECK
+#include "m_string.h"
+#endif
+#include "my_command.h"
+#include "my_sqlcommand.h"
+#endif /*PLUGIN_CONTEXT*/
+
+#define MYSQL_AUDIT_INTERFACE_VERSION 0x0401
+
+/**
+ @enum mysql_event_class_t
+
+ Audit event classes.
+*/
+typedef enum
+{
+ MYSQL_AUDIT_GENERAL_CLASS = 0,
+ MYSQL_AUDIT_CONNECTION_CLASS = 1,
+ MYSQL_AUDIT_PARSE_CLASS = 2,
+ MYSQL_AUDIT_AUTHORIZATION_CLASS = 3,
+ MYSQL_AUDIT_TABLE_ACCESS_CLASS = 4,
+ MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS = 5,
+ MYSQL_AUDIT_SERVER_STARTUP_CLASS = 6,
+ MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS = 7,
+ MYSQL_AUDIT_COMMAND_CLASS = 8,
+ MYSQL_AUDIT_QUERY_CLASS = 9,
+ MYSQL_AUDIT_STORED_PROGRAM_CLASS = 10,
+ /* This item must be last in the list. */
+ MYSQL_AUDIT_CLASS_MASK_SIZE
+} mysql_event_class_t;
+
+/**
+ @struct st_mysql_audit
+
+ The descriptor structure that is referred from st_mysql_plugin.
+*/
+struct st_mysql_audit
+{
+ /**
+ Interface version.
+ */
+ int interface_version;
+
+ /**
+ Event occurs when the event class consumer is to be
+ disassociated from the specified THD.This would typically occur
+ before some operation which may require sleeping - such as when
+ waiting for the next query from the client.
+ */
+ void (*release_thd)(MYSQL_THD);
+
+ /**
+ Invoked whenever an event occurs which is of any
+ class for which the plugin has interest.The second argument
+ indicates the specific event class and the third argument is data
+ as required for that class.
+ */
+ int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *);
+
+ /**
+ An array of bits used to indicate what event classes
+ that this plugin wants to receive.
+ */
+ unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+};
+
+/**
+ @typedef enum_sql_command_t
+
+ SQL command type definition.
+*/
+typedef enum enum_sql_command enum_sql_command_t;
+
+/**
+ @enum mysql_event_general_subclass_t
+
+ Events for the MYSQL_AUDIT_GENERAL_CLASS event class.
+*/
+typedef enum
+{
+ /** occurs before emitting to the general query log. */
+ MYSQL_AUDIT_GENERAL_LOG = 1 << 0,
+ /** occurs before transmitting errors to the user. */
+ MYSQL_AUDIT_GENERAL_ERROR = 1 << 1,
+ /** occurs after transmitting a resultset to the user. */
+ MYSQL_AUDIT_GENERAL_RESULT = 1 << 2,
+ /** occurs after transmitting a resultset or errors */
+ MYSQL_AUDIT_GENERAL_STATUS = 1 << 3
+} mysql_event_general_subclass_t;
+
+#define MYSQL_AUDIT_GENERAL_ALL (MYSQL_AUDIT_GENERAL_LOG | \
+ MYSQL_AUDIT_GENERAL_ERROR | \
+ MYSQL_AUDIT_GENERAL_RESULT | \
+ MYSQL_AUDIT_GENERAL_STATUS)
+/**
+ @struct mysql_event_general
+
+ Structure for the MYSQL_AUDIT_GENERAL_CLASS event class.
+*/
+struct mysql_event_general
+{
+ mysql_event_general_subclass_t event_subclass;
+ int general_error_code;
+ unsigned long general_thread_id;
+ MYSQL_LEX_CSTRING general_user;
+ MYSQL_LEX_CSTRING general_command;
+ MYSQL_LEX_CSTRING general_query;
+ struct charset_info_st *general_charset;
+ unsigned long long general_time;
+ unsigned long long general_rows;
+ MYSQL_LEX_CSTRING general_host;
+ MYSQL_LEX_CSTRING general_sql_command;
+ MYSQL_LEX_CSTRING general_external_user;
+ MYSQL_LEX_CSTRING general_ip;
+};
+
+/**
+ @enum mysql_event_connection_subclass_t
+
+ Events for MYSQL_AUDIT_CONNECTION_CLASS event class.
+*/
+typedef enum
+{
+ /** occurs after authentication phase is completed. */
+ MYSQL_AUDIT_CONNECTION_CONNECT = 1 << 0,
+ /** occurs after connection is terminated. */
+ MYSQL_AUDIT_CONNECTION_DISCONNECT = 1 << 1,
+ /** occurs after COM_CHANGE_USER RPC is completed. */
+ MYSQL_AUDIT_CONNECTION_CHANGE_USER = 1 << 2,
+ /** occurs before authentication. */
+ MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE = 1 << 3
+} mysql_event_connection_subclass_t;
+
+#define MYSQL_AUDIT_CONNECTION_ALL (MYSQL_AUDIT_CONNECTION_CONNECT | \
+ MYSQL_AUDIT_CONNECTION_DISCONNECT | \
+ MYSQL_AUDIT_CONNECTION_CHANGE_USER | \
+ MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE)
+/**
+ @struct mysql_event_connection
+
+ Structure for the MYSQL_AUDIT_CONNECTION_CLASS event class.
+*/
+struct mysql_event_connection
+{
+ /** Event subclass. */
+ mysql_event_connection_subclass_t event_subclass;
+ /** Current status of the connection. */
+ int status;
+ /** Connection id. */
+ unsigned long connection_id;
+ /** User name of this connection. */
+ MYSQL_LEX_CSTRING user;
+ /** Priv user name. */
+ MYSQL_LEX_CSTRING priv_user;
+ /** External user name. */
+ MYSQL_LEX_CSTRING external_user;
+ /** Proxy user used for this connection. */
+ MYSQL_LEX_CSTRING proxy_user;
+ /** Connection host. */
+ MYSQL_LEX_CSTRING host;
+ /** IP of the connection. */
+ MYSQL_LEX_CSTRING ip;
+ /** Database name specified at connection time. */
+ MYSQL_LEX_CSTRING database;
+ /** Connection type:
+ - 0 Undefined
+ - 1 TCP/IP
+ - 2 Socket
+ - 3 Named pipe
+ - 4 SSL
+ - 5 Shared memory
+ */
+ int connection_type;
+};
+
+/**
+@enum mysql_event_parse_subclass_t
+
+Events for MYSQL_AUDIT_PARSE_CLASS event class.
+*/
+typedef enum
+{
+ /** occurs before the query parsing. */
+ MYSQL_AUDIT_PARSE_PREPARSE = 1 << 0,
+ /** occurs after the query parsing. */
+ MYSQL_AUDIT_PARSE_POSTPARSE = 1 << 1
+} mysql_event_parse_subclass_t;
+
+#define MYSQL_AUDIT_PARSE_ALL (MYSQL_AUDIT_PARSE_PREPARSE | \
+ MYSQL_AUDIT_PARSE_POSTPARSE)
+
+typedef enum
+{
+ MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_NONE = 0,
+ /// mysql_event_parse::flags Must be set by a plugin if the query is rewritten.
+ MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_QUERY_REWRITTEN = 1 << 0,
+ /// mysql_event_parse::flags Is set by the server if the query is prepared statement.
+ MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_IS_PREPARED_STATEMENT = 1 << 1
+} mysql_event_parse_rewrite_plugin_flag;
+
+/** Data for the MYSQL_AUDIT_PARSE events */
+struct mysql_event_parse
+{
+ /** MYSQL_AUDIT_[PRE|POST]_PARSE event id */
+ mysql_event_parse_subclass_t event_subclass;
+
+ /** one of FLAG_REWRITE_PLUGIN_* */
+ mysql_event_parse_rewrite_plugin_flag *flags;
+
+ /** input: the original query text */
+ MYSQL_LEX_CSTRING query;
+
+ /** output: returns the null-terminated rewritten query allocated by my_malloc() */
+ MYSQL_LEX_CSTRING *rewritten_query;
+};
+
+/**
+ @enum mysql_event_authorization_subclass_t
+
+ Events for MYSQL_AUDIT_AUTHORIZATION_CLASS event class.
+*/
+typedef enum
+{
+ MYSQL_AUDIT_AUTHORIZATION_USER = 1 << 0,
+ /** Occurs when database privilege is checked. */
+ MYSQL_AUDIT_AUTHORIZATION_DB = 1 << 1,
+ /** Occurs when table privilege is checked. */
+ MYSQL_AUDIT_AUTHORIZATION_TABLE = 1 << 2,
+ /** Occurs when column privilege is checked. */
+ MYSQL_AUDIT_AUTHORIZATION_COLUMN = 1 << 3,
+ /** Occurs when procedure privilege is checked. */
+ MYSQL_AUDIT_AUTHORIZATION_PROCEDURE = 1 << 4,
+ /** Occurs when proxy privilege is checked. */
+ MYSQL_AUDIT_AUTHORIZATION_PROXY = 1 << 5
+} mysql_event_authorization_subclass_t;
+
+#define MYSQL_AUDIT_AUTHORIZATION_ALL (MYSQL_AUDIT_AUTHORIZATION_USER | \
+ MYSQL_AUDIT_AUTHORIZATION_DB | \
+ MYSQL_AUDIT_AUTHORIZATION_TABLE | \
+ MYSQL_AUDIT_AUTHORIZATION_COLUMN | \
+ MYSQL_AUDIT_AUTHORIZATION_PROCEDURE | \
+ MYSQL_AUDIT_AUTHORIZATION_PROXY)
+/**
+ @struct mysql_event_authorization
+
+ Structure for MYSQL_AUDIT_AUTHORIZATION_CLASS event class.
+*/
+struct mysql_event_authorization
+{
+ /** Event subclass. */
+ mysql_event_authorization_subclass_t event_subclass;
+ /** Event status. */
+ int status;
+ /** Connection id. */
+ unsigned int connection_id;
+ /** SQL command id. */
+ enum_sql_command_t sql_command_id;
+ /** SQL query text. */
+ MYSQL_LEX_CSTRING query;
+ /** SQL query charset. */
+ const struct charset_info_st *query_charset;
+ /** Database name. */
+ MYSQL_LEX_CSTRING database;
+ /** Table name. */
+ MYSQL_LEX_CSTRING table;
+ /** Other name associated with the event. */
+ MYSQL_LEX_CSTRING object;
+ /** Requested authorization privileges. */
+ unsigned long requested_privilege;
+ /** Currently granted authorization privileges. */
+ unsigned long granted_privilege;
+};
+
+/**
+ @enum mysql_event_table_row_access_subclass_t
+
+ Events for MYSQL_AUDIT_TABLE_ACCES_CLASS event class.
+*/
+typedef enum
+{
+ /** Occurs when table data are read. */
+ MYSQL_AUDIT_TABLE_ACCESS_READ = 1 << 0,
+ /** Occurs when table data are inserted. */
+ MYSQL_AUDIT_TABLE_ACCESS_INSERT = 1 << 1,
+ /** Occurs when table data are updated. */
+ MYSQL_AUDIT_TABLE_ACCESS_UPDATE = 1 << 2,
+ /** Occurs when table data are deleted. */
+ MYSQL_AUDIT_TABLE_ACCESS_DELETE = 1 << 3
+} mysql_event_table_access_subclass_t;
+
+#define MYSQL_AUDIT_TABLE_ACCESS_ALL (MYSQL_AUDIT_TABLE_ACCESS_READ | \
+ MYSQL_AUDIT_TABLE_ACCESS_INSERT | \
+ MYSQL_AUDIT_TABLE_ACCESS_UPDATE | \
+ MYSQL_AUDIT_TABLE_ACCESS_DELETE)
+
+/**
+ @struct mysql_event_table_row_access
+
+ Structure for MYSQL_AUDIT_TABLE_ACCES_CLASS event class.
+*/
+struct mysql_event_table_access
+{
+ /** Event subclass. */
+ mysql_event_table_access_subclass_t event_subclass;
+ /** Connection id. */
+ unsigned long connection_id;
+ /** SQL command id. */
+ enum_sql_command_t sql_command_id;
+ /** SQL query. */
+ MYSQL_LEX_CSTRING query;
+ /** SQL query charset. */
+ const struct charset_info_st *query_charset;
+ /** Database name. */
+ MYSQL_LEX_CSTRING table_database;
+ /** Table name. */
+ MYSQL_LEX_CSTRING table_name;
+};
+
+/**
+ @enum mysql_event_global_variable_subclass_t
+
+ Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class.
+*/
+typedef enum
+{
+ /** Occurs when global variable is retrieved. */
+ MYSQL_AUDIT_GLOBAL_VARIABLE_GET = 1 << 0,
+ /** Occurs when global variable is set. */
+ MYSQL_AUDIT_GLOBAL_VARIABLE_SET = 1 << 1
+} mysql_event_global_variable_subclass_t;
+
+#define MYSQL_AUDIT_GLOBAL_VARIABLE_ALL (MYSQL_AUDIT_GLOBAL_VARIABLE_GET | \
+ MYSQL_AUDIT_GLOBAL_VARIABLE_SET)
+
+/** Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class. */
+struct mysql_event_global_variable
+{
+ /** Event subclass. */
+ mysql_event_global_variable_subclass_t event_subclass;
+ /** Connection id. */
+ unsigned long connection_id;
+ /** SQL command id. */
+ enum_sql_command_t sql_command_id;
+ /** Variable name. */
+ MYSQL_LEX_CSTRING variable_name;
+ /** Variable value. */
+ MYSQL_LEX_CSTRING variable_value;
+};
+
+/**
+ @enum mysql_event_server_startup_subclass_t
+
+ Events for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class.
+*/
+typedef enum
+{
+ /** Occurs after all subsystem are initialized during system start. */
+ MYSQL_AUDIT_SERVER_STARTUP_STARTUP = 1 << 0
+} mysql_event_server_startup_subclass_t;
+
+#define MYSQL_AUDIT_SERVER_STARTUP_ALL (MYSQL_AUDIT_SERVER_STARTUP_STARTUP)
+
+/**
+ @struct mysql_event_server_startup
+
+ Structure for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class.
+*/
+struct mysql_event_server_startup
+{
+ /** Event subclass. */
+ mysql_event_server_startup_subclass_t event_subclass;
+ /** Command line arguments. */
+ const char **argv;
+ /** Command line arguments count. */
+ unsigned int argc;
+};
+
+/**
+ @enum mysql_event_server_shutdown_subclass_t
+
+ Events for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class.
+*/
+typedef enum
+{
+ /** Occurs when global variable is set. */
+ MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN = 1 << 0
+} mysql_event_server_shutdown_subclass_t;
+
+#define MYSQL_AUDIT_SERVER_SHUTDOWN_ALL (MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN)
+
+/**
+ @enum mysql_server_shutdown_reason_t
+
+ Server shutdown reason.
+*/
+typedef enum
+{
+ /** User requested shut down. */
+ MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_SHUTDOWN,
+ /** The server aborts. */
+ MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_ABORT
+} mysql_server_shutdown_reason_t;
+
+/**
+ @struct mysql_event_server_shutdown
+
+ Structure for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class.
+*/
+struct mysql_event_server_shutdown
+{
+ /** Shutdown event. */
+ mysql_event_server_shutdown_subclass_t event_subclass;
+ /** Exit code associated with the shutdown event. */
+ int exit_code;
+ /** Shutdown reason. */
+ mysql_server_shutdown_reason_t reason;
+};
+
+/**
+ @enum mysql_event_command_subclass_t
+
+ Events for MYSQL_AUDIT_COMMAND_CLASS event class.
+*/
+typedef enum
+{
+ /** Command start event. */
+ MYSQL_AUDIT_COMMAND_START = 1 << 0,
+ /** Command end event. */
+ MYSQL_AUDIT_COMMAND_END = 1 << 1
+} mysql_event_command_subclass_t;
+
+#define MYSQL_AUDIT_COMMAND_ALL (MYSQL_AUDIT_COMMAND_START | \
+ MYSQL_AUDIT_COMMAND_END)
+/**
+ @typedef enum_server_command_t
+
+ Server command type definition.
+*/
+typedef enum enum_server_command enum_server_command_t;
+
+/**
+ @struct mysql_event_command
+
+ Event for MYSQL_AUDIT_COMMAND_CLASS event class.
+ Events generated as a result of RPC command requests.
+*/
+struct mysql_event_command
+{
+ /** Command event subclass. */
+ mysql_event_command_subclass_t event_subclass;
+ /** Command event status. */
+ int status;
+ /** Connection id. */
+ unsigned long connection_id;
+ /** Command id. */
+ enum_server_command_t command_id;
+};
+
+/**
+ @enum mysql_event_query_subclass_t
+
+ Events for MYSQL_AUDIT_QUERY_CLASS event class.
+*/
+typedef enum
+{
+ /** Query start event. */
+ MYSQL_AUDIT_QUERY_START = 1 << 0,
+ /** Nested query start event. */
+ MYSQL_AUDIT_QUERY_NESTED_START = 1 << 1,
+ /** Query post parse event. */
+ MYSQL_AUDIT_QUERY_STATUS_END = 1 << 2,
+ /** Nested query status end event. */
+ MYSQL_AUDIT_QUERY_NESTED_STATUS_END = 1 << 3
+} mysql_event_query_subclass_t;
+
+#define MYSQL_AUDIT_QUERY_ALL (MYSQL_AUDIT_QUERY_START | \
+ MYSQL_AUDIT_QUERY_NESTED_START | \
+ MYSQL_AUDIT_QUERY_STATUS_END | \
+ MYSQL_AUDIT_QUERY_NESTED_STATUS_END)
+/**
+ @struct mysql_event_command
+
+ Event for MYSQL_AUDIT_COMMAND_CLASS event class.
+*/
+struct mysql_event_query
+{
+ /** Event subclass. */
+ mysql_event_query_subclass_t event_subclass;
+ /** Event status. */
+ int status;
+ /** Connection id. */
+ unsigned long connection_id;
+ /** SQL command id. */
+ enum_sql_command_t sql_command_id;
+ /** SQL query. */
+ MYSQL_LEX_CSTRING query;
+ /** SQL query charset. */
+ const struct charset_info_st *query_charset;
+};
+
+/**
+ @enum mysql_event_stored_program_subclass_t
+
+ Events for MYSQL_AUDIT_STORED_PROGRAM_CLASS event class.
+*/
+typedef enum
+{
+ /** Stored program execution event. */
+ MYSQL_AUDIT_STORED_PROGRAM_EXECUTE = 1 << 0
+} mysql_event_stored_program_subclass_t;
+
+#define MYSQL_AUDIT_STORED_PROGRAM_ALL (MYSQL_AUDIT_STORED_PROGRAM_EXECUTE)
+
+/**
+ @struct mysql_event_command
+
+Event for MYSQL_AUDIT_COMMAND_CLASS event class.
+*/
+struct mysql_event_stored_program
+{
+ /** Event subclass. */
+ mysql_event_stored_program_subclass_t event_subclass;
+ /** Connection id. */
+ unsigned long connection_id;
+ /** SQL command id. */
+ enum_sql_command_t sql_command_id;
+ /** SQL query text. */
+ MYSQL_LEX_CSTRING query;
+ /** SQL query charset. */
+ const struct charset_info_st *query_charset;
+ /** The Database the procedure is defined in. */
+ MYSQL_LEX_CSTRING database;
+ /** Name of the stored program. */
+ MYSQL_LEX_CSTRING name;
+ /** Stored program parameters. */
+ void *parameters;
+};
+
+#endif
diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c
new file mode 100644
index 00000000..e94361de
--- /dev/null
+++ b/plugin/server_audit/server_audit.c
@@ -0,0 +1,3104 @@
+/* Copyright (C) 2013, 2015, Alexey Botchkov and SkySQL Ab
+ Copyright (c) 2019, 2020, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+
+#define PLUGIN_VERSION 0x104
+#define PLUGIN_STR_VERSION "1.4.13"
+
+#define _my_thread_var loc_thread_var
+
+#include <my_config.h>
+#include <assert.h>
+
+#ifndef _WIN32
+#define DO_SYSLOG
+#include <syslog.h>
+static const char out_type_desc[]= "Desired output type. Possible values - 'syslog', 'file'"
+ " or 'null' as no output.";
+#else
+static const char out_type_desc[]= "Desired output type. Possible values - 'file'"
+ " or 'null' as no output.";
+#define syslog(PRIORITY, FORMAT, INFO, MESSAGE_LEN, MESSAGE) do {}while(0)
+static void closelog() {}
+#define openlog(IDENT, LOG_NOWAIT, LOG_USER) do {}while(0)
+
+/* priorities */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but significant condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+
+#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
+
+/* facility codes */
+#define LOG_KERN (0<<3) /* kernel messages */
+#define LOG_USER (1<<3) /* random user-level messages */
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
+#define LOG_LPR (6<<3) /* line printer subsystem */
+#define LOG_NEWS (7<<3) /* network news subsystem */
+#define LOG_UUCP (8<<3) /* UUCP subsystem */
+#define LOG_CRON (9<<3) /* clock daemon */
+#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
+#define LOG_FTP (11<<3) /* ftp daemon */
+#define LOG_LOCAL0 (16<<3) /* reserved for local use */
+#define LOG_LOCAL1 (17<<3) /* reserved for local use */
+#define LOG_LOCAL2 (18<<3) /* reserved for local use */
+#define LOG_LOCAL3 (19<<3) /* reserved for local use */
+#define LOG_LOCAL4 (20<<3) /* reserved for local use */
+#define LOG_LOCAL5 (21<<3) /* reserved for local use */
+#define LOG_LOCAL6 (22<<3) /* reserved for local use */
+#define LOG_LOCAL7 (23<<3) /* reserved for local use */
+
+#endif /*!_WIN32*/
+
+/*
+ Defines that can be used to reshape the pluging:
+ #define MARIADB_ONLY
+ #define USE_MARIA_PLUGIN_INTERFACE
+*/
+
+#if !defined(MYSQL_DYNAMIC_PLUGIN) && !defined(MARIADB_ONLY)
+#include <typelib.h>
+#define MARIADB_ONLY
+#endif /*MYSQL_PLUGIN_DYNAMIC*/
+
+#ifndef MARIADB_ONLY
+#define MYSQL_SERVICE_LOGGER_INCLUDED
+#endif /*MARIADB_ONLY*/
+
+#include <my_global.h>
+#include <my_base.h>
+#include <typelib.h>
+#include <mysql/plugin.h>
+#include <mysql/plugin_audit.h>
+#include <string.h>
+#include "../../mysys/mysys_priv.h"
+#ifndef RTLD_DEFAULT
+#define RTLD_DEFAULT NULL
+#endif
+
+#ifndef MARIADB_ONLY
+#undef MYSQL_SERVICE_LOGGER_INCLUDED
+#undef MYSQL_DYNAMIC_PLUGIN
+#define FLOGGER_NO_PSI
+
+/* How to access the pthread_mutex in mysql_mutex_t */
+#ifdef SAFE_MUTEX
+#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex
+#else
+#define mysql_mutex_real_mutex(A) &(A)->m_mutex
+#endif
+
+#define flogger_mutex_init(A,B,C) do{}while(0)
+#define flogger_mutex_destroy(A) do{}while(0)
+#define flogger_mutex_lock(A) do{}while(0)
+#define flogger_mutex_unlock(A) do{}while(0)
+
+static char **int_mysql_data_home;
+static char *default_home= (char *)".";
+#define mysql_data_home (*int_mysql_data_home)
+
+#define FLOGGER_SKIP_INCLUDES
+#define my_open(A, B, C) loc_open(A, B)
+#define my_close(A, B) loc_close(A)
+#define my_rename(A, B, C) loc_rename(A, B)
+#define my_tell(A, B) loc_tell(A)
+#define my_write(A, B, C, D) loc_write(A, B, C)
+#define my_malloc(A, B, C) malloc(B)
+#define my_free(A) free(A)
+#ifdef my_errno
+ #undef my_errno
+#endif
+static int loc_file_errno;
+#define my_errno loc_file_errno
+#ifdef my_vsnprintf
+ #undef my_vsnprintf
+#endif
+#define my_vsnprintf vsnprintf
+#define logger_open loc_logger_open
+#define logger_close loc_logger_close
+#define logger_write loc_logger_write
+#define logger_rotate loc_logger_rotate
+#define logger_init_mutexts loc_logger_init_mutexts
+#define logger_time_to_rotate loc_logger_time_to_rotate
+
+
+static size_t loc_write(File Filedes, const uchar *Buffer, size_t Count)
+{
+ size_t writtenbytes;
+#ifdef _WIN32
+ writtenbytes= (size_t)_write(Filedes, Buffer, (unsigned int)Count);
+#else
+ writtenbytes= write(Filedes, Buffer, Count);
+#endif
+ return writtenbytes;
+}
+
+
+static File loc_open(const char *FileName, int Flags)
+ /* Path-name of file */
+ /* Read | write .. */
+ /* Special flags */
+{
+ File fd;
+#ifdef _WIN32
+ HANDLE h;
+ /*
+ We could just use _open() here. but prefer to open in unix-similar way
+ just like my_open() does it on Windows.
+ This gives atomic multiprocess-safe appends, and possibility to rename
+ or even delete file while it is open, and CRT lacks this features.
+ */
+ assert(Flags == (O_APPEND | O_CREAT | O_WRONLY));
+ h= CreateFile(FileName, FILE_APPEND_DATA,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ fd= -1;
+ my_osmaperr(GetLastError());
+ }
+ else
+ {
+ fd= _open_osfhandle((intptr)h, O_WRONLY|O_BINARY);
+ }
+#else
+ fd= open(FileName, Flags, my_umask);
+#endif
+ my_errno= errno;
+ return fd;
+}
+
+
+static int loc_close(File fd)
+{
+ int err;
+#ifndef _WIN32
+ do
+ {
+ err= close(fd);
+ } while (err == -1 && errno == EINTR);
+#else
+ err= close(fd);
+#endif
+ my_errno=errno;
+ return err;
+}
+
+
+static int loc_rename(const char *from, const char *to)
+{
+ int error = 0;
+
+#if defined(__WIN__)
+ if (!MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED |
+ MOVEFILE_REPLACE_EXISTING))
+ {
+ my_osmaperr(GetLastError());
+#elif defined(HAVE_RENAME)
+ if (rename(from,to))
+ {
+#else
+ if (link(from, to) || unlink(from))
+ {
+#endif
+ my_errno=errno;
+ error = -1;
+ }
+ return error;
+}
+
+
+static my_off_t loc_tell(File fd)
+{
+ os_off_t pos= IF_WIN(_telli64(fd),lseek(fd, 0, SEEK_CUR));
+ if (pos == (os_off_t) -1)
+ {
+ my_errno= errno;
+ }
+ return (my_off_t) pos;
+}
+
+#ifdef HAVE_PSI_INTERFACE
+#undef HAVE_PSI_INTERFACE
+#include <mysql/service_logger.h>
+#include "../../mysys/file_logger.c"
+#define HAVE_PSI_INTERFACE
+#else
+#include <mysql/service_logger.h>
+#include "../../mysys/file_logger.c"
+#endif
+#endif /*!MARIADB_ONLY*/
+
+#undef flogger_mutex_init
+#undef flogger_mutex_destroy
+#undef flogger_mutex_lock
+#undef flogger_mutex_unlock
+
+#define flogger_mutex_init(A,B,C) pthread_mutex_init(mysql_mutex_real_mutex(B), C)
+#define flogger_mutex_destroy(A) pthread_mutex_destroy(mysql_mutex_real_mutex(A))
+#define flogger_mutex_lock(A) pthread_mutex_lock(mysql_mutex_real_mutex(A))
+#define flogger_mutex_unlock(A) pthread_mutex_unlock(mysql_mutex_real_mutex(A))
+
+#ifndef DBUG_OFF
+#define PLUGIN_DEBUG_VERSION "-debug"
+#else
+#define PLUGIN_DEBUG_VERSION ""
+#endif /*DBUG_OFF*/
+/*
+ Disable __attribute__() on non-gcc compilers.
+*/
+#if !defined(__attribute__) && !defined(__GNUC__)
+#define __attribute__(A)
+#endif
+
+#ifdef _WIN32
+#define localtime_r(a, b) localtime_s(b, a)
+#endif /*WIN32*/
+
+
+extern MYSQL_PLUGIN_IMPORT char server_version[];
+static const char *serv_ver= NULL;
+static int started_mysql= 0;
+static int mysql_57_started= 0;
+static int debug_server_started= 0;
+static int use_event_data_for_disconnect= 0;
+static int started_mariadb= 0;
+static int maria_55_started= 0;
+static int maria_above_5= 0;
+static char *incl_users, *excl_users,
+ *file_path, *syslog_info;
+static char path_buffer[FN_REFLEN];
+static unsigned int mode, mode_readonly= 0;
+static ulong output_type;
+static ulong syslog_facility, syslog_priority;
+
+static ulonglong events; /* mask for events to log */
+static unsigned long long file_rotate_size;
+static unsigned int rotations;
+static my_bool rotate= TRUE;
+static char logging;
+static volatile int internal_stop_logging= 0;
+static char incl_user_buffer[1024];
+static char excl_user_buffer[1024];
+static char *big_buffer= NULL;
+static size_t big_buffer_alloced= 0;
+static unsigned int query_log_limit= 0;
+
+static char servhost[256];
+static uint servhost_len;
+static char *syslog_ident;
+static char syslog_ident_buffer[128]= "mysql-server_auditing";
+
+struct connection_info
+{
+ int header;
+ unsigned long thread_id;
+ unsigned long long query_id;
+ char db[256];
+ int db_length;
+ char user[64];
+ int user_length;
+ char host[64];
+ int host_length;
+ char ip[64];
+ int ip_length;
+ const char *query;
+ int query_length;
+ char query_buffer[1024];
+ time_t query_time;
+ int log_always;
+ char proxy[64];
+ int proxy_length;
+ char proxy_host[64];
+ int proxy_host_length;
+};
+
+#define DEFAULT_FILENAME_LEN 16
+static char default_file_name[DEFAULT_FILENAME_LEN+1]= "server_audit.log";
+
+static void update_file_path(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_file_rotate_size(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_file_rotations(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static int check_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save,
+ struct st_mysql_value *value);
+static int check_excl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save,
+ struct st_mysql_value *value);
+static void update_excl_users(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_output_type(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_syslog_facility(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_syslog_priority(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_mode(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_logging(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void update_syslog_ident(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+static void rotate_log(MYSQL_THD thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+
+static MYSQL_SYSVAR_STR(incl_users, incl_users, PLUGIN_VAR_RQCMDARG,
+ "Comma separated list of users to monitor.",
+ check_incl_users, update_incl_users, NULL);
+static MYSQL_SYSVAR_STR(excl_users, excl_users, PLUGIN_VAR_RQCMDARG,
+ "Comma separated list of users to exclude from auditing.",
+ check_excl_users, update_excl_users, NULL);
+/* bits in the event filter. */
+#define EVENT_CONNECT 1
+#define EVENT_QUERY_ALL 2
+#define EVENT_QUERY 122
+#define EVENT_TABLE 4
+#define EVENT_QUERY_DDL 8
+#define EVENT_QUERY_DML 16
+#define EVENT_QUERY_DCL 32
+#define EVENT_QUERY_DML_NO_SELECT 64
+
+static const char *event_names[]=
+{
+ "CONNECT", "QUERY", "TABLE", "QUERY_DDL", "QUERY_DML", "QUERY_DCL",
+ "QUERY_DML_NO_SELECT", NULL
+};
+static TYPELIB events_typelib=
+{
+ array_elements(event_names) - 1, "", event_names, NULL
+};
+static MYSQL_SYSVAR_SET(events, events, PLUGIN_VAR_RQCMDARG,
+ "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE,"
+ " QUERY_DDL, QUERY_DML, QUERY_DML_NO_SELECT, QUERY_DCL.",
+ NULL, NULL, 0, &events_typelib);
+#ifdef DO_SYSLOG
+#define OUTPUT_SYSLOG 0
+#define OUTPUT_FILE 1
+#else
+#define OUTPUT_SYSLOG 0xFFFF
+#define OUTPUT_FILE 0
+#endif /*DO_SYSLOG*/
+
+#define OUTPUT_NO 0xFFFF
+static const char *output_type_names[]= {
+#ifdef DO_SYSLOG
+ "syslog",
+#endif
+ "file", 0 };
+static TYPELIB output_typelib=
+{
+ array_elements(output_type_names) - 1, "output_typelib",
+ output_type_names, NULL
+};
+static MYSQL_SYSVAR_ENUM(output_type, output_type, PLUGIN_VAR_RQCMDARG,
+ out_type_desc,
+ 0, update_output_type, OUTPUT_FILE,
+ &output_typelib);
+static MYSQL_SYSVAR_STR(file_path, file_path, PLUGIN_VAR_RQCMDARG,
+ "Path to the log file.", NULL, update_file_path, default_file_name);
+static MYSQL_SYSVAR_ULONGLONG(file_rotate_size, file_rotate_size,
+ PLUGIN_VAR_RQCMDARG, "Maximum size of the log to start the rotation.",
+ NULL, update_file_rotate_size,
+ 1000000, 100, ((long long) 0x7FFFFFFFFFFFFFFFLL), 1);
+static MYSQL_SYSVAR_UINT(file_rotations, rotations,
+ PLUGIN_VAR_RQCMDARG, "Number of rotations before log is removed.",
+ NULL, update_file_rotations, 9, 0, 999, 1);
+static MYSQL_SYSVAR_BOOL(file_rotate_now, rotate, PLUGIN_VAR_OPCMDARG,
+ "Force log rotation now.", NULL, rotate_log, FALSE);
+static MYSQL_SYSVAR_BOOL(logging, logging,
+ PLUGIN_VAR_OPCMDARG, "Turn on/off the logging.", NULL,
+ update_logging, 0);
+static MYSQL_SYSVAR_UINT(mode, mode,
+ PLUGIN_VAR_OPCMDARG, "Auditing mode.", NULL, update_mode, 0, 0, 1, 1);
+static MYSQL_SYSVAR_STR(syslog_ident, syslog_ident, PLUGIN_VAR_RQCMDARG,
+ "The SYSLOG identifier - the beginning of each SYSLOG record.",
+ NULL, update_syslog_ident, syslog_ident_buffer);
+static MYSQL_SYSVAR_STR(syslog_info, syslog_info,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "The <info> string to be added to the SYSLOG record.", NULL, NULL, "");
+static MYSQL_SYSVAR_UINT(query_log_limit, query_log_limit,
+ PLUGIN_VAR_OPCMDARG, "Limit on the length of the query string in a record.",
+ NULL, NULL, 1024, 0, 0x7FFFFFFF, 1);
+
+char locinfo_ini_value[sizeof(struct connection_info)+4];
+
+static MYSQL_THDVAR_STR(loc_info,
+ PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_MEMALLOC,
+ "Internal info", NULL, NULL, locinfo_ini_value);
+
+static const char *syslog_facility_names[]=
+{
+ "LOG_USER", "LOG_MAIL", "LOG_DAEMON", "LOG_AUTH",
+ "LOG_SYSLOG", "LOG_LPR", "LOG_NEWS", "LOG_UUCP",
+ "LOG_CRON",
+#ifdef LOG_AUTHPRIV
+ "LOG_AUTHPRIV",
+#endif
+#ifdef LOG_FTP
+ "LOG_FTP",
+#endif
+ "LOG_LOCAL0", "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3",
+ "LOG_LOCAL4", "LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7",
+ 0
+};
+#ifndef _WIN32
+static unsigned int syslog_facility_codes[]=
+{
+ LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH,
+ LOG_SYSLOG, LOG_LPR, LOG_NEWS, LOG_UUCP,
+ LOG_CRON,
+#ifdef LOG_AUTHPRIV
+ LOG_AUTHPRIV,
+#endif
+#ifdef LOG_FTP
+ LOG_FTP,
+#endif
+ LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3,
+ LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7,
+};
+#endif
+static TYPELIB syslog_facility_typelib=
+{
+ array_elements(syslog_facility_names) - 1, "syslog_facility_typelib",
+ syslog_facility_names, NULL
+};
+static MYSQL_SYSVAR_ENUM(syslog_facility, syslog_facility, PLUGIN_VAR_RQCMDARG,
+ "The 'facility' parameter of the SYSLOG record."
+ " The default is LOG_USER.", 0, update_syslog_facility, 0/*LOG_USER*/,
+ &syslog_facility_typelib);
+
+static const char *syslog_priority_names[]=
+{
+ "LOG_EMERG", "LOG_ALERT", "LOG_CRIT", "LOG_ERR",
+ "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG",
+ 0
+};
+
+#ifndef _WIN32
+static unsigned int syslog_priority_codes[]=
+{
+ LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
+ LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG,
+};
+#endif
+
+static TYPELIB syslog_priority_typelib=
+{
+ array_elements(syslog_priority_names) - 1, "syslog_priority_typelib",
+ syslog_priority_names, NULL
+};
+static MYSQL_SYSVAR_ENUM(syslog_priority, syslog_priority, PLUGIN_VAR_RQCMDARG,
+ "The 'priority' parameter of the SYSLOG record."
+ " The default is LOG_INFO.", 0, update_syslog_priority, 6/*LOG_INFO*/,
+ &syslog_priority_typelib);
+
+
+static struct st_mysql_sys_var* vars[] = {
+ MYSQL_SYSVAR(incl_users),
+ MYSQL_SYSVAR(excl_users),
+ MYSQL_SYSVAR(events),
+ MYSQL_SYSVAR(output_type),
+ MYSQL_SYSVAR(file_path),
+ MYSQL_SYSVAR(file_rotate_size),
+ MYSQL_SYSVAR(file_rotations),
+ MYSQL_SYSVAR(file_rotate_now),
+ MYSQL_SYSVAR(logging),
+ MYSQL_SYSVAR(mode),
+ MYSQL_SYSVAR(syslog_info),
+ MYSQL_SYSVAR(syslog_ident),
+ MYSQL_SYSVAR(syslog_facility),
+ MYSQL_SYSVAR(syslog_priority),
+ MYSQL_SYSVAR(query_log_limit),
+ MYSQL_SYSVAR(loc_info),
+ NULL
+};
+
+
+/* Status variables for SHOW STATUS */
+static int is_active= 0;
+static long log_write_failures= 0;
+static char current_log_buf[FN_REFLEN]= "";
+static char last_error_buf[512]= "";
+
+extern void *mysql_v4_descriptor;
+
+static struct st_mysql_show_var audit_status[]=
+{
+ {"server_audit_active", (char *)&is_active, SHOW_BOOL},
+ {"server_audit_current_log", current_log_buf, SHOW_CHAR},
+ {"server_audit_writes_failed", (char *)&log_write_failures, SHOW_LONG},
+ {"server_audit_last_error", last_error_buf, SHOW_CHAR},
+ {0,0,0}
+};
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_operations;
+static PSI_mutex_info mutex_key_list[]=
+{
+ { &key_LOCK_operations, "SERVER_AUDIT_plugin::lock_operations",
+ PSI_FLAG_GLOBAL}
+#ifndef FLOGGER_NO_PSI
+ ,
+ { &key_LOCK_atomic, "SERVER_AUDIT_plugin::lock_atomic",
+ PSI_FLAG_GLOBAL},
+ { &key_LOCK_bigbuffer, "SERVER_AUDIT_plugin::lock_bigbuffer",
+ PSI_FLAG_GLOBAL}
+#endif /*FLOGGER_NO_PSI*/
+};
+#endif /*HAVE_PSI_INTERFACE*/
+static mysql_prlock_t lock_operations;
+static mysql_mutex_t lock_atomic;
+static mysql_mutex_t lock_bigbuffer;
+
+/* The Percona server and partly MySQL don't support */
+/* launching client errors in the 'update_variable' methods. */
+/* So the client errors just disabled for them. */
+/* The possible solution is to implement the 'check_variable'*/
+/* methods there properly, but at the moment i'm not sure it */
+/* worths doing. */
+#define CLIENT_ERROR if (!started_mysql) my_printf_error
+
+#define ADD_ATOMIC(x, a) \
+ do { \
+ flogger_mutex_lock(&lock_atomic); \
+ x+= a; \
+ flogger_mutex_unlock(&lock_atomic); \
+ } while (0)
+
+
+static uchar *getkey_user(const char *entry, size_t *length,
+ my_bool nu __attribute__((unused)) )
+{
+ const char *e= entry;
+ while (*e && *e != ' ' && *e != ',')
+ ++e;
+ *length= e - entry;
+ return (uchar *) entry;
+}
+
+
+static void blank_user(char *user)
+{
+ for (; *user && *user != ','; user++)
+ *user= ' ';
+}
+
+
+static void remove_user(char *user)
+{
+ char *start_user= user;
+ while (*user != ',')
+ {
+ if (*user == 0)
+ {
+ *start_user= 0;
+ return;
+ }
+ user++;
+ }
+ user++;
+ while (*user == ' ')
+ user++;
+
+ do {
+ *(start_user++)= *user;
+ } while (*(user++));
+}
+
+
+static void remove_blanks(char *user)
+{
+ char *user_orig= user;
+ char *user_to= user;
+ char *start_tok;
+ int blank_name;
+
+ while (*user != 0)
+ {
+ start_tok= user;
+ blank_name= 1;
+ while (*user !=0 && *user != ',')
+ {
+ if (*user != ' ')
+ blank_name= 0;
+ user++;
+ }
+ if (!blank_name)
+ {
+ while (start_tok <= user)
+ *(user_to++)= *(start_tok++);
+ }
+ if (*user == ',')
+ user++;
+ }
+ if (user_to > user_orig && user_to[-1] == ',')
+ user_to--;
+ *user_to= 0;
+}
+
+
+struct user_name
+{
+ size_t name_len;
+ char *name;
+};
+
+
+struct user_coll
+{
+ int n_users;
+ struct user_name *users;
+ int n_alloced;
+};
+
+
+static void coll_init(struct user_coll *c)
+{
+ c->n_users= 0;
+ c->users= 0;
+ c->n_alloced= 0;
+}
+
+
+static void coll_free(struct user_coll *c)
+{
+ if (c->users)
+ {
+ free(c->users);
+ coll_init(c);
+ }
+}
+
+
+static int cmp_users(const void *ia, const void *ib)
+{
+ const struct user_name *a= (const struct user_name *) ia;
+ const struct user_name *b= (const struct user_name *) ib;
+ int dl= (int)(a->name_len - b->name_len);
+ if (dl != 0)
+ return dl;
+
+ return strncmp(a->name, b->name, a->name_len);
+}
+
+
+static char *coll_search(struct user_coll *c, const char *n, size_t len)
+{
+ struct user_name un;
+ struct user_name *found;
+ if (!c->n_users)
+ return 0;
+ un.name_len= len;
+ un.name= (char *) n;
+ found= (struct user_name*) bsearch(&un, c->users, c->n_users,
+ sizeof(c->users[0]), cmp_users);
+ return found ? found->name : 0;
+}
+
+
+static int coll_insert(struct user_coll *c, char *n, size_t len)
+{
+ if (c->n_users >= c->n_alloced)
+ {
+ c->n_alloced+= 128;
+ if (c->users == NULL)
+ c->users= malloc(c->n_alloced * sizeof(c->users[0]));
+ else
+ c->users= realloc(c->users, c->n_alloced * sizeof(c->users[0]));
+
+ if (c->users == NULL)
+ return 1;
+ }
+ c->users[c->n_users].name= n;
+ c->users[c->n_users].name_len= len;
+ c->n_users++;
+ return 0;
+}
+
+
+static void coll_sort(struct user_coll *c)
+{
+ if (c->n_users)
+ qsort(c->users, c->n_users, sizeof(c->users[0]), cmp_users);
+}
+
+
+static int user_coll_fill(struct user_coll *c, char *users,
+ struct user_coll *cmp_c, int take_over_cmp)
+{
+ char *orig_users= users;
+ char *cmp_user= 0;
+ size_t cmp_length;
+ int refill_cmp_coll= 0;
+
+ c->n_users= 0;
+
+ while (*users)
+ {
+ while (*users == ' ')
+ users++;
+ if (!*users)
+ return 0;
+
+ (void) getkey_user(users, &cmp_length, FALSE);
+ if (cmp_c)
+ {
+ cmp_user= coll_search(cmp_c, users, cmp_length);
+
+ if (cmp_user && take_over_cmp)
+ {
+ ADD_ATOMIC(internal_stop_logging, 1);
+ CLIENT_ERROR(1, "User '%.*b' was removed from the"
+ " server_audit_excl_users.",
+ MYF(ME_WARNING), (int) cmp_length, users);
+ ADD_ATOMIC(internal_stop_logging, -1);
+ blank_user(cmp_user);
+ refill_cmp_coll= 1;
+ }
+ else if (cmp_user)
+ {
+ ADD_ATOMIC(internal_stop_logging, 1);
+ CLIENT_ERROR(1, "User '%.*b' is in the server_audit_incl_users, "
+ "so wasn't added.", MYF(ME_WARNING), (int) cmp_length, users);
+ ADD_ATOMIC(internal_stop_logging, -1);
+ remove_user(users);
+ continue;
+ }
+ }
+ if (coll_insert(c, users, cmp_length))
+ return 1;
+ while (*users && *users != ',')
+ users++;
+ if (!*users)
+ break;
+ users++;
+ }
+
+ if (refill_cmp_coll)
+ {
+ remove_blanks(excl_users);
+ return user_coll_fill(cmp_c, excl_users, 0, 0);
+ }
+
+ if (users > orig_users && users[-1] == ',')
+ users[-1]= 0;
+
+ coll_sort(c);
+
+ return 0;
+}
+
+
+enum sa_keywords
+{
+ SQLCOM_NOTHING=0,
+ SQLCOM_DDL,
+ SQLCOM_DML,
+ SQLCOM_GRANT,
+ SQLCOM_CREATE_USER,
+ SQLCOM_ALTER_USER,
+ SQLCOM_CHANGE_MASTER,
+ SQLCOM_CREATE_SERVER,
+ SQLCOM_SET_OPTION,
+ SQLCOM_ALTER_SERVER,
+ SQLCOM_TRUNCATE,
+ SQLCOM_QUERY_ADMIN,
+ SQLCOM_DCL,
+};
+
+struct sa_keyword
+{
+ int length;
+ const char *wd;
+ struct sa_keyword *next;
+ enum sa_keywords type;
+};
+
+
+struct sa_keyword xml_word= {3, "XML", 0, SQLCOM_NOTHING};
+struct sa_keyword user_word= {4, "USER", 0, SQLCOM_NOTHING};
+struct sa_keyword data_word= {4, "DATA", 0, SQLCOM_NOTHING};
+struct sa_keyword server_word= {6, "SERVER", 0, SQLCOM_NOTHING};
+struct sa_keyword master_word= {6, "MASTER", 0, SQLCOM_NOTHING};
+struct sa_keyword password_word= {8, "PASSWORD", 0, SQLCOM_NOTHING};
+struct sa_keyword function_word= {8, "FUNCTION", 0, SQLCOM_NOTHING};
+struct sa_keyword statement_word= {9, "STATEMENT", 0, SQLCOM_NOTHING};
+struct sa_keyword procedure_word= {9, "PROCEDURE", 0, SQLCOM_NOTHING};
+
+
+struct sa_keyword keywords_to_skip[]=
+{
+ {3, "SET", &statement_word, SQLCOM_QUERY_ADMIN},
+ {0, NULL, 0, SQLCOM_DDL}
+};
+
+
+struct sa_keyword not_ddl_keywords[]=
+{
+ {4, "DROP", &user_word, SQLCOM_DCL},
+ {6, "CREATE", &user_word, SQLCOM_DCL},
+ {6, "RENAME", &user_word, SQLCOM_DCL},
+ {0, NULL, 0, SQLCOM_DDL}
+};
+
+
+struct sa_keyword ddl_keywords[]=
+{
+ {4, "DROP", 0, SQLCOM_DDL},
+ {5, "ALTER", 0, SQLCOM_DDL},
+ {6, "CREATE", 0, SQLCOM_DDL},
+ {6, "RENAME", 0, SQLCOM_DDL},
+ {8, "TRUNCATE", 0, SQLCOM_DDL},
+ {0, NULL, 0, SQLCOM_DDL}
+};
+
+
+struct sa_keyword dml_keywords[]=
+{
+ {2, "DO", 0, SQLCOM_DML},
+ {4, "CALL", 0, SQLCOM_DML},
+ {4, "LOAD", &data_word, SQLCOM_DML},
+ {4, "LOAD", &xml_word, SQLCOM_DML},
+ {6, "DELETE", 0, SQLCOM_DML},
+ {6, "INSERT", 0, SQLCOM_DML},
+ {6, "SELECT", 0, SQLCOM_DML},
+ {6, "UPDATE", 0, SQLCOM_DML},
+ {7, "HANDLER", 0, SQLCOM_DML},
+ {7, "REPLACE", 0, SQLCOM_DML},
+ {0, NULL, 0, SQLCOM_DML}
+};
+
+
+struct sa_keyword dml_no_select_keywords[]=
+{
+ {2, "DO", 0, SQLCOM_DML},
+ {4, "CALL", 0, SQLCOM_DML},
+ {4, "LOAD", &data_word, SQLCOM_DML},
+ {4, "LOAD", &xml_word, SQLCOM_DML},
+ {6, "DELETE", 0, SQLCOM_DML},
+ {6, "INSERT", 0, SQLCOM_DML},
+ {6, "UPDATE", 0, SQLCOM_DML},
+ {7, "HANDLER", 0, SQLCOM_DML},
+ {7, "REPLACE", 0, SQLCOM_DML},
+ {0, NULL, 0, SQLCOM_DML}
+};
+
+
+struct sa_keyword dcl_keywords[]=
+{
+ {6, "CREATE", &user_word, SQLCOM_DCL},
+ {4, "DROP", &user_word, SQLCOM_DCL},
+ {6, "RENAME", &user_word, SQLCOM_DCL},
+ {5, "GRANT", 0, SQLCOM_DCL},
+ {6, "REVOKE", 0, SQLCOM_DCL},
+ {3, "SET", &password_word, SQLCOM_DCL},
+ {0, NULL, 0, SQLCOM_DDL}
+};
+
+
+struct sa_keyword passwd_keywords[]=
+{
+ {3, "SET", &password_word, SQLCOM_SET_OPTION},
+ {5, "ALTER", &server_word, SQLCOM_ALTER_SERVER},
+ {5, "ALTER", &user_word, SQLCOM_ALTER_USER},
+ {5, "GRANT", 0, SQLCOM_GRANT},
+ {6, "CREATE", &user_word, SQLCOM_CREATE_USER},
+ {6, "CREATE", &server_word, SQLCOM_CREATE_SERVER},
+ {6, "CHANGE", &master_word, SQLCOM_CHANGE_MASTER},
+ {0, NULL, 0, SQLCOM_NOTHING}
+};
+
+#define MAX_KEYWORD 9
+
+
+static void error_header()
+{
+ struct tm tm_time;
+ time_t curtime;
+
+ (void) time(&curtime);
+ (void) localtime_r(&curtime, &tm_time);
+
+ (void) fprintf(stderr,"%02d%02d%02d %2d:%02d:%02d server_audit: ",
+ tm_time.tm_year % 100, tm_time.tm_mon + 1,
+ tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
+}
+
+
+static LOGGER_HANDLE *logfile;
+static struct user_coll incl_user_coll, excl_user_coll;
+static unsigned long long query_counter= 1;
+
+
+static struct connection_info *get_loc_info(MYSQL_THD thd)
+{
+ return (struct connection_info *) THDVAR(thd, loc_info);
+}
+
+
+static int ci_needs_setup(const struct connection_info *ci)
+{
+ return ci->header != 0;
+}
+
+
+static void get_str_n(char *dest, int *dest_len, size_t dest_size,
+ const char *src, size_t src_len)
+{
+ if (src_len >= dest_size)
+ src_len= dest_size - 1;
+
+ if (src_len)
+ memcpy(dest, src, src_len);
+ dest[src_len]= 0;
+ *dest_len= (int)src_len;
+}
+
+
+static int get_user_host(const char *uh_line, unsigned int uh_len,
+ char *buffer, size_t buf_len,
+ size_t *user_len, size_t *host_len, size_t *ip_len)
+{
+ const char *buf_end= buffer + buf_len - 1;
+ const char *buf_start;
+ const char *uh_end= uh_line + uh_len;
+
+ while (uh_line < uh_end && *uh_line != '[')
+ ++uh_line;
+
+ if (uh_line == uh_end)
+ return 1;
+ ++uh_line;
+
+ buf_start= buffer;
+ while (uh_line < uh_end && *uh_line != ']')
+ {
+ if (buffer == buf_end)
+ return 1;
+ *(buffer++)= *(uh_line++);
+ }
+ if (uh_line == uh_end)
+ return 1;
+ *user_len= buffer - buf_start;
+ *(buffer++)= 0;
+
+ while (uh_line < uh_end && *uh_line != '@')
+ ++uh_line;
+ if (uh_line == uh_end || *(++uh_line) == 0)
+ return 1;
+ ++uh_line;
+
+ buf_start= buffer;
+ while (uh_line < uh_end && *uh_line != ' ' && *uh_line != '[')
+ {
+ if (buffer == buf_end)
+ break;
+ *(buffer++)= *(uh_line++);
+ }
+ *host_len= buffer - buf_start;
+ *(buffer++)= 0;
+
+ while (uh_line < uh_end && *uh_line != '[')
+ ++uh_line;
+
+ buf_start= buffer;
+ if (*uh_line == '[')
+ {
+ ++uh_line;
+ while (uh_line < uh_end && *uh_line != ']')
+ *(buffer++)= *(uh_line++);
+ }
+ *ip_len= buffer - buf_start;
+ return 0;
+}
+
+#if defined(__WIN__) && !defined(S_ISDIR)
+#define S_ISDIR(x) ((x) & _S_IFDIR)
+#endif /*__WIN__ && !S_ISDIR*/
+
+static int start_logging()
+{
+ last_error_buf[0]= 0;
+ log_write_failures= 0;
+ if (output_type == OUTPUT_FILE)
+ {
+ char alt_path_buffer[FN_REFLEN+1+DEFAULT_FILENAME_LEN];
+ struct stat *f_stat= (struct stat *)alt_path_buffer;
+ const char *alt_fname= file_path;
+
+ while (*alt_fname == ' ')
+ alt_fname++;
+
+ if (*alt_fname == 0)
+ {
+ /* Empty string means the default file name. */
+ alt_fname= default_file_name;
+ }
+ else
+ {
+ /* See if the directory exists with the name of file_path. */
+ /* Log file name should be [file_path]/server_audit.log then. */
+ if (stat(file_path, (struct stat *)alt_path_buffer) == 0 &&
+ S_ISDIR(f_stat->st_mode))
+ {
+ size_t p_len= strlen(file_path);
+ memcpy(alt_path_buffer, file_path, p_len);
+ if (alt_path_buffer[p_len-1] != FN_LIBCHAR)
+ {
+ alt_path_buffer[p_len]= FN_LIBCHAR;
+ p_len++;
+ }
+ memcpy(alt_path_buffer+p_len, default_file_name, DEFAULT_FILENAME_LEN);
+ alt_path_buffer[p_len+DEFAULT_FILENAME_LEN]= 0;
+ alt_fname= alt_path_buffer;
+ }
+ }
+
+ logfile= logger_open(alt_fname, file_rotate_size, rotations);
+
+ if (logfile == NULL)
+ {
+ error_header();
+ fprintf(stderr, "Could not create file '%s'.\n",
+ alt_fname);
+ logging= 0;
+ my_snprintf(last_error_buf, sizeof(last_error_buf),
+ "Could not create file '%s'.", alt_fname);
+ is_active= 0;
+ CLIENT_ERROR(1, "SERVER AUDIT plugin can't create file '%s'.",
+ MYF(ME_WARNING), alt_fname);
+ return 1;
+ }
+ error_header();
+ fprintf(stderr, "logging started to the file %s.\n", alt_fname);
+ strncpy(current_log_buf, alt_fname, sizeof(current_log_buf)-1);
+ current_log_buf[sizeof(current_log_buf)-1]= 0;
+ }
+ else if (output_type == OUTPUT_SYSLOG)
+ {
+ openlog(syslog_ident, LOG_NOWAIT, syslog_facility_codes[syslog_facility]);
+ error_header();
+ fprintf(stderr, "logging started to the syslog.\n");
+ strncpy(current_log_buf, "[SYSLOG]", sizeof(current_log_buf)-1);
+ compile_time_assert(sizeof current_log_buf > sizeof "[SYSLOG]");
+ }
+ is_active= 1;
+ return 0;
+}
+
+
+static int stop_logging()
+{
+ last_error_buf[0]= 0;
+ if (output_type == OUTPUT_FILE && logfile)
+ {
+ logger_close(logfile);
+ logfile= NULL;
+ }
+ else if (output_type == OUTPUT_SYSLOG)
+ {
+ closelog();
+ }
+ error_header();
+ fprintf(stderr, "logging was stopped.\n");
+ is_active= 0;
+ return 0;
+}
+
+
+static void setup_connection_simple(struct connection_info *ci)
+{
+ ci->db_length= 0;
+ ci->user_length= 0;
+ ci->host_length= 0;
+ ci->ip_length= 0;
+ ci->query_length= 0;
+ ci->header= 0;
+ ci->proxy_length= 0;
+}
+
+
+#define MAX_HOSTNAME 61
+#define USERNAME_LENGTH 384
+
+static void setup_connection_connect(struct connection_info *cn,
+ const struct mysql_event_connection *event)
+{
+ cn->query_id= 0;
+ cn->query_length= 0;
+ cn->log_always= 0;
+ cn->thread_id= event->thread_id;
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->database.str, event->database.length);
+ get_str_n(cn->user, &cn->user_length, sizeof(cn->db),
+ event->user, event->user_length);
+ get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
+ event->host, event->host_length);
+ get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
+ event->ip, event->ip_length);
+ cn->header= 0;
+ if (event->proxy_user && event->proxy_user[0])
+ {
+ const char *priv_host= event->proxy_user +
+ sizeof(char[MAX_HOSTNAME+USERNAME_LENGTH+5]);
+ size_t priv_host_length;
+
+ if (mysql_57_started)
+ {
+ priv_host+= sizeof(size_t);
+ priv_host_length= *(size_t *) (priv_host + MAX_HOSTNAME);
+ }
+ else
+ priv_host_length= strlen(priv_host);
+
+
+ get_str_n(cn->proxy, &cn->proxy_length, sizeof(cn->proxy),
+ event->priv_user, event->priv_user_length);
+ get_str_n(cn->proxy_host, &cn->proxy_host_length,
+ sizeof(cn->proxy_host),
+ priv_host, priv_host_length);
+ }
+ else
+ cn->proxy_length= 0;
+}
+
+
+#define SAFE_STRLEN(s) (s ? strlen(s) : 0)
+#define SAFE_STRLEN_UI(s) ((unsigned int) (s ? strlen(s) : 0))
+static char empty_str[1]= { 0 };
+
+
+static int is_space(char c)
+{
+ return c == ' ' || c == '\r' || c == '\n' || c == '\t';
+}
+
+
+#define SKIP_SPACES(str) \
+do { \
+ while (is_space(*str)) \
+ ++str; \
+} while(0)
+
+
+#define ESC_MAP_SIZE 0x60
+static const char esc_map[ESC_MAP_SIZE]=
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, '\'', 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0
+};
+
+static char escaped_char(char c)
+{
+ return ((unsigned char ) c) >= ESC_MAP_SIZE ? 0 : esc_map[(unsigned char) c];
+}
+
+
+static void setup_connection_initdb(struct connection_info *cn,
+ const struct mysql_event_general *event)
+{
+ size_t user_len, host_len, ip_len;
+ char uh_buffer[512];
+
+ cn->thread_id= event->general_thread_id;
+ cn->query_id= 0;
+ cn->query_length= 0;
+ cn->log_always= 0;
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->general_query, event->general_query_length);
+
+ if (get_user_host(event->general_user, event->general_user_length,
+ uh_buffer, sizeof(uh_buffer),
+ &user_len, &host_len, &ip_len))
+ {
+ /* The user@host line is incorrect. */
+ cn->user_length= 0;
+ cn->host_length= 0;
+ cn->ip_length= 0;
+ }
+ else
+ {
+ get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
+ uh_buffer, user_len);
+ get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
+ uh_buffer+user_len+1, host_len);
+ get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
+ uh_buffer+user_len+1+host_len+1, ip_len);
+ }
+ cn->header= 0;
+}
+
+
+static void setup_connection_table(struct connection_info *cn,
+ const struct mysql_event_table *event)
+{
+ cn->thread_id= event->thread_id;
+ cn->query_id= query_counter++;
+ cn->log_always= 0;
+ cn->query_length= 0;
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->database.str, event->database.length);
+ get_str_n(cn->user, &cn->user_length, sizeof(cn->db),
+ event->user, SAFE_STRLEN(event->user));
+ get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
+ event->host, SAFE_STRLEN(event->host));
+ get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
+ event->ip, SAFE_STRLEN(event->ip));
+ cn->header= 0;
+}
+
+
+static void setup_connection_query(struct connection_info *cn,
+ const struct mysql_event_general *event)
+{
+ size_t user_len, host_len, ip_len;
+ char uh_buffer[512];
+
+ cn->thread_id= event->general_thread_id;
+ cn->query_id= query_counter++;
+ cn->log_always= 0;
+ cn->query_length= 0;
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db), "", 0);
+
+ if (get_user_host(event->general_user, event->general_user_length,
+ uh_buffer, sizeof(uh_buffer),
+ &user_len, &host_len, &ip_len))
+ {
+ /* The user@host line is incorrect. */
+ cn->user_length= 0;
+ cn->host_length= 0;
+ cn->ip_length= 0;
+ }
+ else
+ {
+ get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
+ uh_buffer, user_len);
+ get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
+ uh_buffer+user_len+1, host_len);
+ get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
+ uh_buffer+user_len+1+host_len+1, ip_len);
+ }
+ cn->header= 0;
+}
+
+
+static void change_connection(struct connection_info *cn,
+ const struct mysql_event_connection *event)
+{
+ get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
+ event->user, event->user_length);
+ get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
+ event->ip, event->ip_length);
+}
+
+/*
+ Write to the log
+
+ @param take_lock If set, take a read lock (or write lock on rotate).
+ If not set, the caller has a already taken a write lock
+*/
+
+static int write_log(const char *message, size_t len, int take_lock)
+{
+ int result= 0;
+ if (take_lock)
+ {
+ /* Start by taking a read lock */
+ mysql_prlock_rdlock(&lock_operations);
+ }
+
+ if (output_type == OUTPUT_FILE)
+ {
+ if (logfile)
+ {
+ my_bool allow_rotate= !take_lock; /* Allow rotate if caller write lock */
+ if (take_lock && logger_time_to_rotate(logfile))
+ {
+ /* We have to rotate the log, change above read lock to write lock */
+ mysql_prlock_unlock(&lock_operations);
+ mysql_prlock_wrlock(&lock_operations);
+ allow_rotate= 1;
+ }
+ if (!(is_active= (logger_write_r(logfile, allow_rotate, message, len) ==
+ (int) len)))
+ {
+ ++log_write_failures;
+ result= 1;
+ }
+ }
+ }
+ else if (output_type == OUTPUT_SYSLOG)
+ {
+ syslog(syslog_facility_codes[syslog_facility] |
+ syslog_priority_codes[syslog_priority],
+ "%s %.*s", syslog_info, (int) len, message);
+ }
+ if (take_lock)
+ mysql_prlock_unlock(&lock_operations);
+ return result;
+}
+
+
+static size_t log_header(char *message, size_t message_len,
+ time_t *ts,
+ const char *serverhost, size_t serverhost_len,
+ const char *username, unsigned int username_len,
+ const char *host, unsigned int host_len,
+ const char *userip, unsigned int userip_len,
+ unsigned int connection_id, long long query_id,
+ const char *operation)
+{
+ struct tm tm_time;
+
+ if (host_len == 0 && userip_len != 0)
+ {
+ host_len= userip_len;
+ host= userip;
+ }
+
+ if (output_type == OUTPUT_SYSLOG)
+ return my_snprintf(message, message_len,
+ "%.*s,%.*s,%.*s,%d,%lld,%s",
+ (unsigned int) serverhost_len, serverhost,
+ username_len, username,
+ host_len, host,
+ connection_id, query_id, operation);
+
+ (void) localtime_r(ts, &tm_time);
+ return my_snprintf(message, message_len,
+ "%04d%02d%02d %02d:%02d:%02d,%.*s,%.*s,%.*s,%d,%lld,%s",
+ tm_time.tm_year+1900, tm_time.tm_mon+1, tm_time.tm_mday,
+ tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
+ serverhost_len, serverhost,
+ username_len, username,
+ host_len, host,
+ connection_id, query_id, operation);
+}
+
+
+static int log_proxy(const struct connection_info *cn,
+ const struct mysql_event_connection *event)
+
+{
+ time_t ctime;
+ size_t csize;
+ char message[1024];
+
+ (void) time(&ctime);
+ csize= log_header(message, sizeof(message)-1, &ctime,
+ servhost, servhost_len,
+ cn->user, cn->user_length,
+ cn->host, cn->host_length,
+ cn->ip, cn->ip_length,
+ event->thread_id, 0, "PROXY_CONNECT");
+ csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
+ ",%.*s,`%.*s`@`%.*s`,%d", cn->db_length, cn->db,
+ cn->proxy_length, cn->proxy,
+ cn->proxy_host_length, cn->proxy_host,
+ event->status);
+ message[csize]= '\n';
+ return write_log(message, csize + 1, 1);
+}
+
+
+static int log_connection(const struct connection_info *cn,
+ const struct mysql_event_connection *event,
+ const char *type)
+{
+ time_t ctime;
+ size_t csize;
+ char message[1024];
+
+ (void) time(&ctime);
+ csize= log_header(message, sizeof(message)-1, &ctime,
+ servhost, servhost_len,
+ cn->user, cn->user_length,
+ cn->host, cn->host_length,
+ cn->ip, cn->ip_length,
+ event->thread_id, 0, type);
+ csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
+ ",%.*s,,%d", cn->db_length, cn->db, event->status);
+ message[csize]= '\n';
+ return write_log(message, csize + 1, 1);
+}
+
+
+static int log_connection_event(const struct mysql_event_connection *event,
+ const char *type)
+{
+ time_t ctime;
+ size_t csize;
+ char message[1024];
+
+ (void) time(&ctime);
+ csize= log_header(message, sizeof(message)-1, &ctime,
+ servhost, servhost_len,
+ event->user, event->user_length,
+ event->host, event->host_length,
+ event->ip, event->ip_length,
+ event->thread_id, 0, type);
+ csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
+ ",%.*s,,%d", event->database.length, event->database.str, event->status);
+ message[csize]= '\n';
+ return write_log(message, csize + 1, 1);
+}
+
+
+static size_t escape_string(const char *str, unsigned int len,
+ char *result, size_t result_len)
+{
+ const char *res_start= result;
+ const char *res_end= result + result_len - 2;
+ while (len)
+ {
+ char esc_c;
+
+ if (result >= res_end)
+ break;
+ if ((esc_c= escaped_char(*str)))
+ {
+ if (result+1 >= res_end)
+ break;
+ *(result++)= '\\';
+ *(result++)= esc_c;
+ }
+ else if (is_space(*str))
+ *(result++)= ' ';
+ else
+ *(result++)= *str;
+ str++;
+ len--;
+ }
+ *result= 0;
+ return result - res_start;
+}
+
+
+static size_t escape_string_hide_passwords(const char *str, unsigned int len,
+ char *result, size_t result_len,
+ const char *word1, size_t word1_len,
+ const char *word2, size_t word2_len,
+ int next_text_string)
+{
+ const char *res_start= result;
+ const char *res_end= result + result_len - 2;
+ size_t d_len;
+
+ while (len)
+ {
+ if (len > word1_len + 1 && strncasecmp(str, word1, word1_len) == 0)
+ {
+ const char *next_s= str + word1_len;
+ size_t c;
+
+ if (next_text_string)
+ {
+ while (*next_s && *next_s != '\'' && *next_s != '"')
+ ++next_s;
+ }
+ else
+ {
+ if (word2)
+ {
+ SKIP_SPACES(next_s);
+ if (len < (next_s - str) + word2_len + 1 ||
+ strncasecmp(next_s, word2, word2_len) != 0)
+ goto no_password;
+ next_s+= word2_len;
+ }
+
+ while (*next_s && *next_s != '\'' && *next_s != '"')
+ ++next_s;
+ }
+
+ d_len= next_s - str;
+ if (result + d_len + 5 > res_end)
+ break;
+
+ for (c=0; c<d_len; c++)
+ result[c]= is_space(str[c]) ? ' ' : str[c];
+
+ if (*next_s)
+ {
+ const char b_char= *next_s++;
+ memset(result + d_len, '*', 5);
+ result+= d_len + 5;
+
+ while (*next_s)
+ {
+ if (*next_s == b_char)
+ {
+ ++next_s;
+ break;
+ }
+ if (*next_s == '\\')
+ {
+ if (next_s[1])
+ next_s++;
+ }
+ next_s++;
+ }
+ }
+ else
+ result+= d_len;
+
+ len-= (uint)(next_s - str);
+ str= next_s;
+ continue;
+ }
+no_password:
+ if (result >= res_end)
+ break;
+ else
+ {
+ const char b_char= escaped_char(*str);
+ if (b_char)
+ {
+ if (result+1 >= res_end)
+ break;
+ *(result++)= '\\';
+ *(result++)= b_char;
+ }
+ else if (is_space(*str))
+ *(result++)= ' ';
+ else
+ *(result++)= *str;
+ str++;
+ len--;
+ }
+ }
+ *result= 0;
+ return result - res_start;
+}
+
+
+
+static int do_log_user(const char *name, int len,
+ const char *proxy, int proxy_len, int take_lock)
+{
+ int result;
+
+ if (!name)
+ return 0;
+
+ if (take_lock)
+ mysql_prlock_rdlock(&lock_operations);
+
+ if (incl_user_coll.n_users)
+ {
+ result= coll_search(&incl_user_coll, name, len) != 0 ||
+ (proxy && coll_search(&incl_user_coll, proxy, proxy_len) != 0);
+ }
+ else if (excl_user_coll.n_users)
+ {
+ result= coll_search(&excl_user_coll, name, len) == 0 &&
+ (proxy && coll_search(&excl_user_coll, proxy, proxy_len) == 0);
+ }
+ else
+ result= 1;
+
+ if (take_lock)
+ mysql_prlock_unlock(&lock_operations);
+ return result;
+}
+
+
+static int get_next_word(const char *query, char *word)
+{
+ int len= 0;
+ char c;
+ while ((c= query[len]))
+ {
+ if (c >= 'a' && c <= 'z')
+ word[len]= 'A' + (c-'a');
+ else if (c >= 'A' && c <= 'Z')
+ word[len]= c;
+ else
+ break;
+
+ if (len++ == MAX_KEYWORD)
+ return 0;
+ }
+ word[len]= 0;
+ return len;
+}
+
+
+static int filter_query_type(const char *query, struct sa_keyword *kwd)
+{
+ int qwe_in_list;
+ char fword[MAX_KEYWORD + 1], nword[MAX_KEYWORD + 1];
+ int len, nlen= 0;
+ const struct sa_keyword *l_keywords;
+
+ while (*query && (is_space(*query) || *query == '(' || *query == '/'))
+ {
+ /* comment handling */
+ if (*query == '/' && query[1] == '*')
+ {
+ if (query[2] == '!')
+ {
+ query+= 3;
+ while (*query >= '0' && *query <= '9')
+ query++;
+ continue;
+ }
+ query+= 2;
+ while (*query)
+ {
+ if (*query=='*' && query[1] == '/')
+ {
+ query+= 2;
+ break;
+ }
+ query++;
+ }
+ continue;
+ }
+ query++;
+ }
+
+ qwe_in_list= 0;
+ if (!(len= get_next_word(query, fword)))
+ goto not_in_list;
+ query+= len+1;
+
+ l_keywords= kwd;
+ while (l_keywords->length)
+ {
+ if (l_keywords->length == len && strncmp(l_keywords->wd, fword, len) == 0)
+ {
+ if (l_keywords->next)
+ {
+ if (nlen == 0)
+ {
+ while (*query && is_space(*query))
+ query++;
+ nlen= get_next_word(query, nword);
+ }
+ if (l_keywords->next->length != nlen ||
+ strncmp(l_keywords->next->wd, nword, nlen) != 0)
+ goto do_loop;
+ }
+
+ qwe_in_list= l_keywords->type;
+ break;
+ };
+do_loop:
+ l_keywords++;
+ }
+
+not_in_list:
+ return qwe_in_list;
+}
+
+
+static int log_statement_ex(const struct connection_info *cn,
+ time_t ev_time, unsigned long thd_id,
+ const char *query, unsigned int query_len,
+ int error_code, const char *type, int take_lock)
+{
+ size_t csize;
+ char message_loc[1024];
+ char *message= message_loc;
+ size_t message_size= sizeof(message_loc);
+ char *uh_buffer;
+ size_t uh_buffer_size;
+ const char *db;
+ unsigned int db_length;
+ long long query_id;
+ int result;
+
+ if ((db= cn->db))
+ db_length= cn->db_length;
+ else
+ {
+ db= "";
+ db_length= 0;
+ }
+
+ if (!(query_id= cn->query_id))
+ query_id= query_counter++;
+
+ if (query == 0)
+ {
+ /* Can happen after the error in mysqld_prepare_stmt() */
+ query= cn->query;
+ query_len= cn->query_length;
+ if (query == 0 || query_len == 0)
+ return 0;
+ }
+
+ if (query && !(events & EVENT_QUERY_ALL) &&
+ (events & EVENT_QUERY && !cn->log_always))
+ {
+ const char *orig_query= query;
+
+ if (filter_query_type(query, keywords_to_skip))
+ {
+ char fword[MAX_KEYWORD + 1];
+ int len;
+ do
+ {
+ len= get_next_word(query, fword);
+ query+= len ? len : 1;
+ if (len == 3 && strncmp(fword, "FOR", 3) == 0)
+ break;
+ } while (*query);
+
+ if (*query == 0)
+ return 0;
+ }
+
+ if (events & EVENT_QUERY_DDL)
+ {
+ if (!filter_query_type(query, not_ddl_keywords) &&
+ filter_query_type(query, ddl_keywords))
+ goto do_log_query;
+ }
+ if (events & EVENT_QUERY_DML)
+ {
+ if (filter_query_type(query, dml_keywords))
+ goto do_log_query;
+ }
+ if (events & EVENT_QUERY_DML_NO_SELECT)
+ {
+ if (filter_query_type(query, dml_no_select_keywords))
+ goto do_log_query;
+ }
+ if (events & EVENT_QUERY_DCL)
+ {
+ if (filter_query_type(query, dcl_keywords))
+ goto do_log_query;
+ }
+
+ return 0;
+do_log_query:
+ query= orig_query;
+ }
+
+ csize= log_header(message, message_size-1, &ev_time,
+ servhost, servhost_len,
+ cn->user, cn->user_length,cn->host, cn->host_length,
+ cn->ip, cn->ip_length, thd_id, query_id, type);
+
+ csize+= my_snprintf(message+csize, message_size - 1 - csize,
+ ",%.*s,\'", db_length, db);
+
+ if (query_log_limit > 0 && query_len > query_log_limit)
+ query_len= query_log_limit;
+
+ if (query_len > (message_size - csize)/2)
+ {
+ flogger_mutex_lock(&lock_bigbuffer);
+ if (big_buffer_alloced < (query_len * 2 + csize))
+ {
+ big_buffer_alloced= (query_len * 2 + csize + 4095) & ~4095L;
+ big_buffer= realloc(big_buffer, big_buffer_alloced);
+ if (big_buffer == NULL)
+ {
+ big_buffer_alloced= 0;
+ return 0;
+ }
+ }
+
+ memcpy(big_buffer, message, csize);
+ message= big_buffer;
+ message_size= big_buffer_alloced;
+ }
+
+ uh_buffer= message + csize;
+ uh_buffer_size= message_size - csize;
+ if (query_log_limit > 0 && uh_buffer_size > query_log_limit+2)
+ uh_buffer_size= query_log_limit+2;
+
+ switch (filter_query_type(query, passwd_keywords))
+ {
+ case SQLCOM_GRANT:
+ case SQLCOM_CREATE_USER:
+ case SQLCOM_ALTER_USER:
+ csize+= escape_string_hide_passwords(query, query_len,
+ uh_buffer, uh_buffer_size,
+ "IDENTIFIED", 10, "BY", 2, 0);
+ break;
+ case SQLCOM_CHANGE_MASTER:
+ csize+= escape_string_hide_passwords(query, query_len,
+ uh_buffer, uh_buffer_size,
+ "MASTER_PASSWORD", 15, "=", 1, 0);
+ break;
+ case SQLCOM_CREATE_SERVER:
+ case SQLCOM_ALTER_SERVER:
+ csize+= escape_string_hide_passwords(query, query_len,
+ uh_buffer, uh_buffer_size,
+ "PASSWORD", 8, NULL, 0, 0);
+ break;
+ case SQLCOM_SET_OPTION:
+ csize+= escape_string_hide_passwords(query, query_len,
+ uh_buffer, uh_buffer_size,
+ "=", 1, NULL, 0, 1);
+ break;
+ default:
+ csize+= escape_string(query, query_len,
+ uh_buffer, uh_buffer_size);
+ break;
+ }
+ csize+= my_snprintf(message+csize, message_size - 1 - csize,
+ "\',%d", error_code);
+ message[csize]= '\n';
+ result= write_log(message, csize + 1, take_lock);
+ if (message == big_buffer)
+ flogger_mutex_unlock(&lock_bigbuffer);
+
+ return result;
+}
+
+
+static int log_statement(const struct connection_info *cn,
+ const struct mysql_event_general *event,
+ const char *type)
+{
+ return log_statement_ex(cn, event->general_time, event->general_thread_id,
+ event->general_query, event->general_query_length,
+ event->general_error_code, type, 1);
+}
+
+
+static int log_table(const struct connection_info *cn,
+ const struct mysql_event_table *event, const char *type)
+{
+ size_t csize;
+ char message[1024];
+ time_t ctime;
+
+ (void) time(&ctime);
+ csize= log_header(message, sizeof(message)-1, &ctime,
+ servhost, servhost_len,
+ event->user, SAFE_STRLEN_UI(event->user),
+ event->host, SAFE_STRLEN_UI(event->host),
+ event->ip, SAFE_STRLEN_UI(event->ip),
+ event->thread_id, cn->query_id, type);
+ csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
+ ",%.*s,%.*s,",event->database.length, event->database.str,
+ event->table.length, event->table.str);
+ message[csize]= '\n';
+ return write_log(message, csize + 1, 1);
+}
+
+
+static int log_rename(const struct connection_info *cn,
+ const struct mysql_event_table *event)
+{
+ size_t csize;
+ char message[1024];
+ time_t ctime;
+
+ (void) time(&ctime);
+ csize= log_header(message, sizeof(message)-1, &ctime,
+ servhost, servhost_len,
+ event->user, SAFE_STRLEN_UI(event->user),
+ event->host, SAFE_STRLEN_UI(event->host),
+ event->ip, SAFE_STRLEN_UI(event->ip),
+ event->thread_id, cn->query_id, "RENAME");
+ csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize,
+ ",%.*s,%.*s|%.*s.%.*s,",event->database.length, event->database.str,
+ event->table.length, event->table.str,
+ event->new_database.length, event->new_database.str,
+ event->new_table.length, event->new_table.str);
+ message[csize]= '\n';
+ return write_log(message, csize + 1, 1);
+}
+
+
+static int event_query_command(const struct mysql_event_general *event)
+{
+ return (event->general_command_length == 5 &&
+ strncmp(event->general_command, "Query", 5) == 0) ||
+ (event->general_command_length == 7 &&
+ (strncmp(event->general_command, "Execute", 7) == 0 ||
+ (event->general_error_code != 0 &&
+ strncmp(event->general_command, "Prepare", 7) == 0)));
+}
+
+
+static void update_general_user(struct connection_info *cn,
+ const struct mysql_event_general *event)
+{
+ char uh_buffer[768];
+ size_t user_len, host_len, ip_len;
+ if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0 &&
+ get_user_host(event->general_user, event->general_user_length,
+ uh_buffer, sizeof(uh_buffer),
+ &user_len, &host_len, &ip_len) == 0)
+ {
+ get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
+ uh_buffer, user_len);
+ get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
+ uh_buffer+user_len+1, host_len);
+ get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
+ uh_buffer+user_len+1+host_len+1, ip_len);
+ }
+
+}
+
+
+static struct connection_info ci_disconnect_buffer;
+
+#define AA_FREE_CONNECTION 1
+#define AA_CHANGE_USER 2
+
+static void update_connection_info(struct connection_info *cn,
+ unsigned int event_class, const void *ev, int *after_action)
+{
+ *after_action= 0;
+
+ switch (event_class) {
+ case MYSQL_AUDIT_GENERAL_CLASS:
+ {
+ const struct mysql_event_general *event =
+ (const struct mysql_event_general *) ev;
+ switch (event->event_subclass) {
+ case MYSQL_AUDIT_GENERAL_LOG:
+ {
+ int init_db_command= event->general_command_length == 7 &&
+ strncmp(event->general_command, "Init DB", 7) == 0;
+ if (!ci_needs_setup(cn))
+ {
+ if (init_db_command)
+ {
+ /* Change DB */
+ if (mysql_57_started)
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->database.str, event->database.length);
+ else
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->general_query, event->general_query_length);
+ }
+ cn->query_id= mode ? query_counter++ : event->query_id;
+ cn->query= event->general_query;
+ cn->query_length= event->general_query_length;
+ cn->query_time= (time_t) event->general_time;
+ update_general_user(cn, event);
+ }
+ else if (init_db_command)
+ setup_connection_initdb(cn, event);
+ else if (event_query_command(event))
+ setup_connection_query(cn, event);
+ else
+ setup_connection_simple(cn);
+ break;
+ }
+
+ case MYSQL_AUDIT_GENERAL_STATUS:
+ if (event_query_command(event))
+ {
+ if (ci_needs_setup(cn))
+ setup_connection_query(cn, event);
+
+ if (mode == 0 && cn->db_length == 0 && event->database.length > 0)
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->database.str, event->database.length);
+
+ if (event->general_error_code == 0)
+ {
+ /* We need to check if it's the USE command to change the DB */
+ int use_command= event->general_query_length > 4 &&
+ strncasecmp(event->general_query, "use ", 4) == 0;
+ if (use_command)
+ {
+ /* Change DB */
+ if (mode)
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->general_query + 4, event->general_query_length - 4);
+ else
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->database.str, event->database.length);
+ }
+ }
+ update_general_user(cn, event);
+ }
+ break;
+ case MYSQL_AUDIT_GENERAL_ERROR:
+ /*
+ We need this because the MariaDB returns NULL query field for the
+ MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare.
+ As a result we get empty QUERY field for errors.
+ */
+ if (ci_needs_setup(cn))
+ setup_connection_query(cn, event);
+ cn->query_id= mode ? query_counter++ : event->query_id;
+ get_str_n(cn->query_buffer, &cn->query_length, sizeof(cn->query_buffer),
+ event->general_query, event->general_query_length);
+ cn->query= cn->query_buffer;
+ cn->query_time= (time_t) event->general_time;
+ break;
+ default:;
+ }
+ break;
+ }
+ case MYSQL_AUDIT_TABLE_CLASS:
+ {
+ const struct mysql_event_table *event =
+ (const struct mysql_event_table *) ev;
+ if (ci_needs_setup(cn))
+ setup_connection_table(cn, event);
+
+ if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0)
+ {
+ get_str_n(cn->user, &cn->user_length, sizeof(cn->user),
+ event->user, SAFE_STRLEN(event->user));
+ get_str_n(cn->host, &cn->host_length, sizeof(cn->host),
+ event->host, SAFE_STRLEN(event->host));
+ get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip),
+ event->ip, SAFE_STRLEN(event->ip));
+ }
+
+ if (cn->db_length == 0 && event->database.length != 0)
+ get_str_n(cn->db, &cn->db_length, sizeof(cn->db),
+ event->database.str, event->database.length);
+
+ if (mode == 0)
+ cn->query_id= event->query_id;
+ break;
+ }
+ case MYSQL_AUDIT_CONNECTION_CLASS:
+ {
+ const struct mysql_event_connection *event =
+ (const struct mysql_event_connection *) ev;
+ switch (event->event_subclass)
+ {
+ case MYSQL_AUDIT_CONNECTION_CONNECT:
+ setup_connection_connect(cn, event);
+ break;
+ case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
+ *after_action= AA_CHANGE_USER;
+ break;
+ default:;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+struct connection_info cn_error_buffer;
+
+
+#define FILTER(MASK) (events == 0 || (events & MASK))
+void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
+{
+ struct connection_info *cn= 0;
+ int after_action= 0;
+
+ /* That one is important as this function can be called with */
+ /* &lock_operations locked when the server logs an error reported */
+ /* by this plugin. */
+ if (!thd || internal_stop_logging)
+ return;
+
+ if (maria_55_started && debug_server_started &&
+ event_class == MYSQL_AUDIT_GENERAL_CLASS)
+ {
+ /*
+ There's a bug in MariaDB 5.5 that prevents using thread local
+ variables in some cases.
+ The 'select * from notexisting_table;' query produces such case.
+ So just use the static buffer in this case.
+ */
+ const struct mysql_event_general *event =
+ (const struct mysql_event_general *) ev;
+
+ if (event->event_subclass == MYSQL_AUDIT_GENERAL_ERROR ||
+ (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS &&
+ event->general_query_length == 0 &&
+ cn_error_buffer.query_id == event->query_id))
+ {
+ cn= &cn_error_buffer;
+ cn->header= 1;
+ }
+ else
+ cn= get_loc_info(thd);
+ }
+ else
+ {
+ cn= get_loc_info(thd);
+ }
+
+ update_connection_info(cn, event_class, ev, &after_action);
+
+ if (!logging)
+ {
+ if (cn)
+ cn->log_always= 0;
+ goto exit_func;
+ }
+
+ if (event_class == MYSQL_AUDIT_GENERAL_CLASS && FILTER(EVENT_QUERY) &&
+ cn && (cn->log_always || do_log_user(cn->user, cn->user_length,
+ cn->proxy, cn->proxy_length,
+ 1)))
+ {
+ const struct mysql_event_general *event =
+ (const struct mysql_event_general *) ev;
+
+ /*
+ Only one subclass is logged.
+ */
+ if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS &&
+ event_query_command(event))
+ {
+ log_statement(cn, event, "QUERY");
+ cn->query_length= 0; /* So the log_current_query() won't log this again. */
+ cn->log_always= 0;
+ }
+ }
+ else if (event_class == MYSQL_AUDIT_TABLE_CLASS && FILTER(EVENT_TABLE) && cn)
+ {
+ const struct mysql_event_table *event =
+ (const struct mysql_event_table *) ev;
+ if (do_log_user(event->user, (int) SAFE_STRLEN(event->user),
+ cn->proxy, cn->proxy_length, 1))
+ {
+ switch (event->event_subclass)
+ {
+ case MYSQL_AUDIT_TABLE_LOCK:
+ log_table(cn, event, event->read_only ? "READ" : "WRITE");
+ break;
+ case MYSQL_AUDIT_TABLE_CREATE:
+ log_table(cn, event, "CREATE");
+ break;
+ case MYSQL_AUDIT_TABLE_DROP:
+ log_table(cn, event, "DROP");
+ break;
+ case MYSQL_AUDIT_TABLE_RENAME:
+ log_rename(cn, event);
+ break;
+ case MYSQL_AUDIT_TABLE_ALTER:
+ log_table(cn, event, "ALTER");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS &&
+ FILTER(EVENT_CONNECT) && cn)
+ {
+ const struct mysql_event_connection *event =
+ (const struct mysql_event_connection *) ev;
+ switch (event->event_subclass)
+ {
+ case MYSQL_AUDIT_CONNECTION_CONNECT:
+ log_connection(cn, event, event->status ? "FAILED_CONNECT": "CONNECT");
+ if (event->status == 0 && event->proxy_user && event->proxy_user[0])
+ log_proxy(cn, event);
+ break;
+ case MYSQL_AUDIT_CONNECTION_DISCONNECT:
+ if (use_event_data_for_disconnect)
+ log_connection_event(event, "DISCONNECT");
+ else
+ log_connection(&ci_disconnect_buffer, event, "DISCONNECT");
+ break;
+ case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
+ log_connection(cn, event, "CHANGEUSER");
+ if (event->proxy_user && event->proxy_user[0])
+ log_proxy(cn, event);
+ break;
+ default:;
+ }
+ }
+exit_func:
+ /*
+ This must work always, whether logging is ON or not.
+ */
+ if (after_action)
+ {
+ switch (after_action) {
+ case AA_CHANGE_USER:
+ {
+ const struct mysql_event_connection *event =
+ (const struct mysql_event_connection *) ev;
+ change_connection(cn, event);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+
+struct mysql_event_general_v8
+{
+ unsigned int event_class;
+ unsigned int event_subclass;
+ int general_error_code;
+ unsigned long general_thread_id;
+ const char *general_user;
+ unsigned int general_user_length;
+ const char *general_command;
+ unsigned int general_command_length;
+ const char *general_query;
+ unsigned int general_query_length;
+ struct charset_info_st *general_charset;
+ unsigned long long general_time;
+ unsigned long long general_rows;
+};
+
+
+static void auditing_v8(MYSQL_THD thd, struct mysql_event_general_v8 *ev_v8)
+{
+#ifdef __linux__
+#ifdef DBUG_OFF
+ #ifdef __x86_64__
+ static const int cmd_off= 4200;
+ static const int db_off= 120;
+ static const int db_len_off= 128;
+ #else
+ static const int cmd_off= 2668;
+ static const int db_off= 60;
+ static const int db_len_off= 64;
+ #endif /*x86_64*/
+#else
+ #ifdef __x86_64__
+ static const int cmd_off= 4432;
+ static const int db_off= 120;
+ static const int db_len_off= 128;
+ #else
+ static const int cmd_off= 2808;
+ static const int db_off= 64;
+ static const int db_len_off= 68;
+ #endif /*x86_64*/
+#endif /*DBUG_OFF*/
+#endif /* __linux__ */
+
+ struct mysql_event_general event;
+
+ if (ev_v8->event_class != MYSQL_AUDIT_GENERAL_CLASS)
+ return;
+
+ event.event_subclass= ev_v8->event_subclass;
+ event.general_error_code= ev_v8->general_error_code;
+ event.general_thread_id= ev_v8->general_thread_id;
+ event.general_user= ev_v8->general_user;
+ event.general_user_length= ev_v8->general_user_length;
+ event.general_command= ev_v8->general_command;
+ event.general_command_length= ev_v8->general_command_length;
+ event.general_query= ev_v8->general_query;
+ event.general_query_length= ev_v8->general_query_length;
+ event.general_charset= ev_v8->general_charset;
+ event.general_time= ev_v8->general_time;
+ event.general_rows= ev_v8->general_rows;
+ event.database.str= 0;
+ event.database.length= 0;
+
+ if (event.general_query_length > 0)
+ {
+ event.event_subclass= MYSQL_AUDIT_GENERAL_STATUS;
+ event.general_command= "Query";
+ event.general_command_length= 5;
+#ifdef __linux__
+ event.database.str= *(char **) (((char *) thd) + db_off);
+ event.database.length= *(size_t *) (((char *) thd) + db_len_off);
+#endif /*__linux*/
+ }
+#ifdef __linux__
+ else if (*((int *) (((char *)thd) + cmd_off)) == 2)
+ {
+ event.event_subclass= MYSQL_AUDIT_GENERAL_LOG;
+ event.general_command= "Init DB";
+ event.general_command_length= 7;
+ event.general_query= *(char **) (((char *) thd) + db_off);
+ event.general_query_length= *(size_t *) (((char *) thd) + db_len_off);
+ }
+#endif /*__linux*/
+ auditing(thd, ev_v8->event_class, &event);
+}
+
+
+static void auditing_v13(MYSQL_THD thd, unsigned int *ev_v0)
+{
+ struct mysql_event_general event= *(const struct mysql_event_general *) (ev_v0+1);
+
+ if (event.general_query_length > 0)
+ {
+ event.event_subclass= MYSQL_AUDIT_GENERAL_STATUS;
+ event.general_command= "Query";
+ event.general_command_length= 5;
+ }
+ auditing(thd, ev_v0[0], &event);
+}
+
+
+int get_db_mysql57(MYSQL_THD thd, char **name, size_t *len)
+{
+#ifdef __linux__
+ int db_off;
+ int db_len_off;
+ if (debug_server_started)
+ {
+#ifdef __x86_64__
+ db_off= 608;
+ db_len_off= 616;
+#else
+ db_off= 0;
+ db_len_off= 0;
+#endif /*x86_64*/
+ }
+ else
+ {
+#ifdef __x86_64__
+ db_off= 536;
+ db_len_off= 544;
+#else
+ db_off= 0;
+ db_len_off= 0;
+#endif /*x86_64*/
+ }
+
+ *name= *(char **) (((char *) thd) + db_off);
+ *len= *((size_t *) (((char*) thd) + db_len_off));
+ if (*name && (*name)[*len] != 0)
+ return 1;
+ return 0;
+#else
+ return 1;
+#endif
+}
+/*
+ As it's just too difficult to #include "sql_class.h",
+ let's just copy the necessary part of the system_variables
+ structure here.
+*/
+typedef struct loc_system_variables
+{
+ ulong dynamic_variables_version;
+ char* dynamic_variables_ptr;
+ uint dynamic_variables_head; /* largest valid variable offset */
+ uint dynamic_variables_size; /* how many bytes are in use */
+
+ ulonglong max_heap_table_size;
+ ulonglong tmp_table_size;
+ ulonglong long_query_time;
+ ulonglong optimizer_switch;
+ ulonglong sql_mode; ///< which non-standard SQL behaviour should be enabled
+ ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING
+ ulonglong join_buff_space_limit;
+ ulonglong log_slow_filter;
+ ulonglong log_slow_verbosity;
+ ulonglong bulk_insert_buff_size;
+ ulonglong join_buff_size;
+ ulonglong sortbuff_size;
+ ulonglong group_concat_max_len;
+ ha_rows select_limit;
+ ha_rows max_join_size;
+ ha_rows expensive_subquery_limit;
+ ulong auto_increment_increment, auto_increment_offset;
+ ulong lock_wait_timeout;
+ ulong join_cache_level;
+ ulong max_allowed_packet;
+ ulong max_error_count;
+ ulong max_length_for_sort_data;
+ ulong max_sort_length;
+ ulong max_tmp_tables;
+ ulong max_insert_delayed_threads;
+ ulong min_examined_row_limit;
+ ulong net_buffer_length;
+ ulong net_interactive_timeout;
+ ulong net_read_timeout;
+ ulong net_retry_count;
+ ulong net_wait_timeout;
+ ulong net_write_timeout;
+ ulong optimizer_prune_level;
+ ulong optimizer_search_depth;
+ ulong preload_buff_size;
+ ulong profiling_history_size;
+ ulong read_buff_size;
+ ulong read_rnd_buff_size;
+ ulong mrr_buff_size;
+ ulong div_precincrement;
+ /* Total size of all buffers used by the subselect_rowid_merge_engine. */
+ ulong rowid_merge_buff_size;
+ ulong max_sp_recursion_depth;
+ ulong default_week_format;
+ ulong max_seeks_for_key;
+ ulong range_alloc_block_size;
+ ulong query_alloc_block_size;
+ ulong query_prealloc_size;
+ ulong trans_alloc_block_size;
+ ulong trans_prealloc_size;
+ ulong log_warnings;
+ /* Flags for slow log filtering */
+ ulong log_slow_rate_limit;
+ ulong binlog_format; ///< binlog format for this thd (see enum_binlog_format)
+ ulong progress_report_time;
+ my_bool binlog_annotate_row_events;
+ my_bool binlog_direct_non_trans_update;
+ my_bool sql_log_bin;
+ ulong completion_type;
+ ulong query_cache_type;
+} LOC_SV;
+
+
+static int init_done= 0;
+
+static void* find_sym(const char *sym)
+{
+#ifdef _WIN32
+ return GetProcAddress(GetModuleHandle("server.dll"),sym);
+#else
+ return dlsym(RTLD_DEFAULT, sym);
+#endif
+}
+
+static int server_audit_init(void *p __attribute__((unused)))
+{
+ if (!serv_ver)
+ {
+ serv_ver= find_sym("server_version");
+ }
+
+ if (!mysql_57_started)
+ {
+ const void *my_hash_init_ptr= find_sym("_my_hash_init");
+ if (!my_hash_init_ptr)
+ {
+ maria_above_5= 1;
+ my_hash_init_ptr= find_sym("my_hash_init2");
+ }
+ if (!my_hash_init_ptr)
+ return 1;
+ }
+
+ if(!(int_mysql_data_home= find_sym("mysql_data_home")))
+ {
+ if(!(int_mysql_data_home= find_sym("?mysql_data_home@@3PADA")))
+ int_mysql_data_home= &default_home;
+ }
+
+ if (!serv_ver)
+ return 1;
+
+ if (!started_mysql)
+ {
+ if (!maria_above_5 && serv_ver[4]=='3' && serv_ver[5]<'3')
+ {
+ mode= 1;
+ mode_readonly= 1;
+ }
+ }
+
+ if (gethostname(servhost, sizeof(servhost)))
+ strcpy(servhost, "unknown");
+
+ servhost_len= (uint)strlen(servhost);
+
+ logger_init_mutexes();
+#ifdef HAVE_PSI_INTERFACE
+ if (PSI_server)
+ PSI_server->register_mutex("server_audit", mutex_key_list, 1);
+#endif
+ mysql_prlock_init(key_LOCK_operations, &lock_operations);
+ flogger_mutex_init(key_LOCK_operations, &lock_atomic, MY_MUTEX_INIT_FAST);
+ flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST);
+
+ coll_init(&incl_user_coll);
+ coll_init(&excl_user_coll);
+
+ if (incl_users)
+ {
+ if (excl_users)
+ {
+ incl_users= excl_users= NULL;
+ error_header();
+ fprintf(stderr, "INCL_DML_USERS and EXCL_DML_USERS specified"
+ " simultaneously - both set to empty\n");
+ }
+ update_incl_users(NULL, NULL, NULL, &incl_users);
+ }
+ else if (excl_users)
+ {
+ update_excl_users(NULL, NULL, NULL, &excl_users);
+ }
+
+ error_header();
+ fprintf(stderr, "MariaDB Audit Plugin version %s%s STARTED.\n",
+ PLUGIN_STR_VERSION, PLUGIN_DEBUG_VERSION);
+
+ /* The Query Cache shadows TABLE events if the result is taken from it */
+ /* so we warn users if both Query Cashe and TABLE events enabled. */
+ if (!started_mysql && FILTER(EVENT_TABLE))
+ {
+ ulonglong *qc_size= (ulonglong *) dlsym(RTLD_DEFAULT, "query_cache_size");
+ if (qc_size == NULL || *qc_size != 0)
+ {
+ struct loc_system_variables *g_sys_var=
+ (struct loc_system_variables *) dlsym(RTLD_DEFAULT,
+ "global_system_variables");
+ if (g_sys_var && g_sys_var->query_cache_type != 0)
+ {
+ error_header();
+ fprintf(stderr, "Query cache is enabled with the TABLE events."
+ " Some table reads can be veiled.");
+ }
+ }
+ }
+
+ ci_disconnect_buffer.header= 10;
+ ci_disconnect_buffer.thread_id= 0;
+ ci_disconnect_buffer.query_id= 0;
+ ci_disconnect_buffer.db_length= 0;
+ ci_disconnect_buffer.user_length= 0;
+ ci_disconnect_buffer.host_length= 0;
+ ci_disconnect_buffer.ip_length= 0;
+ ci_disconnect_buffer.query= empty_str;
+ ci_disconnect_buffer.query_length= 0;
+
+ if (logging)
+ start_logging();
+
+ init_done= 1;
+ return 0;
+}
+
+
+static int server_audit_init_mysql(void *p)
+{
+ started_mysql= 1;
+ mode= 1;
+ mode_readonly= 1;
+ return server_audit_init(p);
+}
+
+
+static int server_audit_deinit(void *p __attribute__((unused)))
+{
+ if (!init_done)
+ return 0;
+
+ init_done= 0;
+ coll_free(&incl_user_coll);
+ coll_free(&excl_user_coll);
+
+ if (output_type == OUTPUT_FILE && logfile)
+ logger_close(logfile);
+ else if (output_type == OUTPUT_SYSLOG)
+ closelog();
+
+ (void) free(big_buffer);
+ mysql_prlock_destroy(&lock_operations);
+ flogger_mutex_destroy(&lock_atomic);
+ flogger_mutex_destroy(&lock_bigbuffer);
+
+ error_header();
+ fprintf(stderr, "STOPPED\n");
+ return 0;
+}
+
+
+static void rotate_log(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)),
+ const void *save __attribute__((unused)))
+{
+ if (output_type == OUTPUT_FILE && logfile && *(my_bool*) save)
+ (void) logger_rotate(logfile);
+}
+
+
+static struct st_mysql_audit mysql_descriptor =
+{
+ MYSQL_AUDIT_INTERFACE_VERSION,
+ NULL,
+ auditing,
+ { MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK }
+};
+
+
+mysql_declare_plugin(server_audit)
+{
+ MYSQL_AUDIT_PLUGIN,
+ &mysql_descriptor,
+ "SERVER_AUDIT",
+ " Alexey Botchkov (MariaDB Corporation)",
+ "Audit the server activity",
+ PLUGIN_LICENSE_GPL,
+ server_audit_init_mysql,
+ server_audit_deinit,
+ PLUGIN_VERSION,
+ audit_status,
+ vars,
+ NULL,
+ 0
+}
+mysql_declare_plugin_end;
+
+
+static struct st_mysql_audit maria_descriptor =
+{
+ MYSQL_AUDIT_INTERFACE_VERSION,
+ NULL,
+ auditing,
+ { MYSQL_AUDIT_GENERAL_CLASSMASK |
+ MYSQL_AUDIT_TABLE_CLASSMASK |
+ MYSQL_AUDIT_CONNECTION_CLASSMASK }
+};
+maria_declare_plugin(server_audit)
+{
+ MYSQL_AUDIT_PLUGIN,
+ &maria_descriptor,
+ "SERVER_AUDIT",
+ "Alexey Botchkov (MariaDB Corporation)",
+ "Audit the server activity",
+ PLUGIN_LICENSE_GPL,
+ server_audit_init,
+ server_audit_deinit,
+ PLUGIN_VERSION,
+ audit_status,
+ vars,
+ PLUGIN_STR_VERSION,
+ MariaDB_PLUGIN_MATURITY_STABLE
+}
+maria_declare_plugin_end;
+
+
+static void mark_always_logged(MYSQL_THD thd)
+{
+ struct connection_info *cn;
+ if (thd && (cn= get_loc_info(thd)))
+ cn->log_always= 1;
+}
+
+
+static void log_current_query(MYSQL_THD thd)
+{
+ struct connection_info *cn;
+ if (!thd)
+ return;
+ cn= get_loc_info(thd);
+ if (!ci_needs_setup(cn) && cn->query_length)
+ {
+ cn->log_always= 1;
+ log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd),
+ cn->query, cn->query_length, 0, "QUERY", 0);
+ cn->log_always= 0;
+ }
+}
+
+
+static void update_file_path(MYSQL_THD thd,
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ char *new_name= (*(char **) save) ? *(char **) save : empty_str;
+
+ ADD_ATOMIC(internal_stop_logging, 1);
+ error_header();
+ fprintf(stderr, "Log file name was changed to '%s'.\n", new_name);
+
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_wrlock(&lock_operations);
+
+ if (logging)
+ log_current_query(thd);
+
+ if (logging && output_type == OUTPUT_FILE)
+ {
+ char *sav_path= file_path;
+
+ file_path= new_name;
+ stop_logging();
+ if (start_logging())
+ {
+ file_path= sav_path;
+ error_header();
+ fprintf(stderr, "Reverting log filename back to '%s'.\n", file_path);
+ logging= (start_logging() == 0);
+ if (!logging)
+ {
+ error_header();
+ fprintf(stderr, "Logging was disabled..\n");
+ CLIENT_ERROR(1, "Logging was disabled.", MYF(ME_WARNING));
+ }
+ goto exit_func;
+ }
+ }
+
+ strncpy(path_buffer, new_name, sizeof(path_buffer)-1);
+ path_buffer[sizeof(path_buffer)-1]= 0;
+ file_path= path_buffer;
+exit_func:
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
+}
+
+
+static void update_file_rotations(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ rotations= *(unsigned int *) save;
+ error_header();
+ fprintf(stderr, "Log file rotations was changed to '%d'.\n", rotations);
+
+ if (!logging || output_type != OUTPUT_FILE)
+ return;
+
+ mysql_prlock_wrlock(&lock_operations);
+ logfile->rotations= rotations;
+ mysql_prlock_unlock(&lock_operations);
+}
+
+
+static void update_file_rotate_size(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ file_rotate_size= *(unsigned long long *) save;
+ error_header();
+ fprintf(stderr, "Log file rotate size was changed to '%lld'.\n",
+ file_rotate_size);
+
+ if (!logging || output_type != OUTPUT_FILE)
+ return;
+
+ mysql_prlock_wrlock(&lock_operations);
+ logfile->size_limit= file_rotate_size;
+ mysql_prlock_unlock(&lock_operations);
+}
+
+
+static int check_users(void *save, struct st_mysql_value *value,
+ size_t s, const char *name)
+{
+ const char *users;
+ int len= 0;
+
+ users= value->val_str(value, NULL, &len);
+ if ((size_t) len > s)
+ {
+ error_header();
+ fprintf(stderr,
+ "server_audit_%s_users value can't be longer than %zu characters.\n",
+ name, s);
+ return 1;
+ }
+ *((const char**)save)= users;
+ return 0;
+}
+
+static int check_incl_users(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *save, struct st_mysql_value *value)
+{
+ return check_users(save, value, sizeof(incl_user_buffer), "incl");
+}
+
+static int check_excl_users(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *save, struct st_mysql_value *value)
+{
+ return check_users(save, value, sizeof(excl_user_buffer), "excl");
+}
+
+
+static void update_incl_users(MYSQL_THD thd,
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ char *new_users= (*(char **) save) ? *(char **) save : empty_str;
+ size_t new_len= strlen(new_users) + 1;
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_wrlock(&lock_operations);
+ mark_always_logged(thd);
+
+ if (new_len > sizeof(incl_user_buffer))
+ new_len= sizeof(incl_user_buffer);
+
+ memcpy(incl_user_buffer, new_users, new_len - 1);
+ incl_user_buffer[new_len - 1]= 0;
+
+ incl_users= incl_user_buffer;
+ user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1);
+ error_header();
+ fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users);
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_unlock(&lock_operations);
+}
+
+
+static void update_excl_users(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ char *new_users= (*(char **) save) ? *(char **) save : empty_str;
+ size_t new_len= strlen(new_users) + 1;
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_wrlock(&lock_operations);
+ mark_always_logged(thd);
+
+ if (new_len > sizeof(excl_user_buffer))
+ new_len= sizeof(excl_user_buffer);
+
+ memcpy(excl_user_buffer, new_users, new_len - 1);
+ excl_user_buffer[new_len - 1]= 0;
+
+ excl_users= excl_user_buffer;
+ user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0);
+ error_header();
+ fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users);
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_unlock(&lock_operations);
+}
+
+
+static void update_output_type(MYSQL_THD thd,
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ ulong new_output_type= *((ulong *) save);
+ if (output_type == new_output_type)
+ return;
+
+ ADD_ATOMIC(internal_stop_logging, 1);
+ mysql_prlock_wrlock(&lock_operations);
+ if (logging)
+ {
+ log_current_query(thd);
+ stop_logging();
+ }
+
+ output_type= new_output_type;
+ error_header();
+ fprintf(stderr, "Output was redirected to '%s'\n",
+ output_type_names[output_type]);
+
+ if (logging)
+ start_logging();
+ mysql_prlock_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
+}
+
+
+static void update_syslog_facility(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ ulong new_facility= *((ulong *) save);
+ if (syslog_facility == new_facility)
+ return;
+
+ mark_always_logged(thd);
+ error_header();
+ fprintf(stderr, "SysLog facility was changed from '%s' to '%s'.\n",
+ syslog_facility_names[syslog_facility],
+ syslog_facility_names[new_facility]);
+ syslog_facility= new_facility;
+}
+
+
+static void update_syslog_priority(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ ulong new_priority= *((ulong *) save);
+ if (syslog_priority == new_priority)
+ return;
+
+ mysql_prlock_wrlock(&lock_operations);
+ mark_always_logged(thd);
+ mysql_prlock_unlock(&lock_operations);
+ error_header();
+ fprintf(stderr, "SysLog priority was changed from '%s' to '%s'.\n",
+ syslog_priority_names[syslog_priority],
+ syslog_priority_names[new_priority]);
+ syslog_priority= new_priority;
+}
+
+
+static void update_logging(MYSQL_THD thd,
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ char new_logging= *(char *) save;
+ if (new_logging == logging)
+ return;
+
+ ADD_ATOMIC(internal_stop_logging, 1);
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_wrlock(&lock_operations);
+ if ((logging= new_logging))
+ {
+ start_logging();
+ if (!logging)
+ {
+ CLIENT_ERROR(1, "Logging was disabled.", MYF(ME_WARNING));
+ }
+ mark_always_logged(thd);
+ }
+ else
+ {
+ log_current_query(thd);
+ stop_logging();
+ }
+
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
+}
+
+
+static void update_mode(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ unsigned int new_mode= *(unsigned int *) save;
+ if (mode_readonly || new_mode == mode)
+ return;
+
+ ADD_ATOMIC(internal_stop_logging, 1);
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_wrlock(&lock_operations);
+ mark_always_logged(thd);
+ error_header();
+ fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode);
+ mode= new_mode;
+ if (!maria_55_started || !debug_server_started)
+ mysql_prlock_unlock(&lock_operations);
+ ADD_ATOMIC(internal_stop_logging, -1);
+}
+
+
+static void update_syslog_ident(MYSQL_THD thd __attribute__((unused)),
+ struct st_mysql_sys_var *var __attribute__((unused)),
+ void *var_ptr __attribute__((unused)), const void *save)
+{
+ char *new_ident= (*(char **) save) ? *(char **) save : empty_str;
+ strncpy(syslog_ident_buffer, new_ident, sizeof(syslog_ident_buffer)-1);
+ syslog_ident_buffer[sizeof(syslog_ident_buffer)-1]= 0;
+ syslog_ident= syslog_ident_buffer;
+ error_header();
+ fprintf(stderr, "SYSYLOG ident was changed to '%s'\n", syslog_ident);
+ mysql_prlock_wrlock(&lock_operations);
+ mark_always_logged(thd);
+ if (logging && output_type == OUTPUT_SYSLOG)
+ {
+ stop_logging();
+ start_logging();
+ }
+ mysql_prlock_unlock(&lock_operations);
+}
+
+
+struct st_my_thread_var *loc_thread_var(void)
+{
+ return 0;
+}
+
+
+
+#ifdef _WIN32
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ if (fdwReason != DLL_PROCESS_ATTACH)
+ return 1;
+
+ serv_ver= server_version;
+#else
+void __attribute__ ((constructor)) audit_plugin_so_init(void)
+{
+ serv_ver= server_version;
+#endif /*_WIN32*/
+
+ if (!serv_ver)
+ goto exit;
+
+ started_mariadb= strstr(serv_ver, "MariaDB") != 0;
+ debug_server_started= strstr(serv_ver, "debug") != 0;
+
+ if (started_mariadb)
+ {
+ if (serv_ver[0] == '1')
+ use_event_data_for_disconnect= 1;
+ else
+ maria_55_started= 1;
+ }
+ else
+ {
+ /* Started MySQL. */
+ if (serv_ver[0] == '5' && serv_ver[2] == '5')
+ {
+ int sc= serv_ver[4] - '0';
+ if (serv_ver[5] >= '0' && serv_ver[5] <= '9')
+ sc= sc * 10 + serv_ver[5] - '0';
+ if (sc <= 10)
+ {
+ mysql_descriptor.interface_version= 0x0200;
+ mysql_descriptor.event_notify= (void *) auditing_v8;
+ }
+ else if (sc < 14)
+ {
+ mysql_descriptor.interface_version= 0x0200;
+ mysql_descriptor.event_notify= (void *) auditing_v13;
+ }
+ }
+ else if (serv_ver[0] == '5' && serv_ver[2] == '6')
+ {
+ int sc= serv_ver[4] - '0';
+ if (serv_ver[5] >= '0' && serv_ver[5] <= '9')
+ sc= sc * 10 + serv_ver[5] - '0';
+ if (sc >= 24)
+ use_event_data_for_disconnect= 1;
+ }
+ else if ((serv_ver[0] == '5' && serv_ver[2] == '7') ||
+ (serv_ver[0] == '8' && serv_ver[2] == '0'))
+ {
+ mysql_57_started= 1;
+ _mysql_plugin_declarations_[0].info= mysql_v4_descriptor;
+ use_event_data_for_disconnect= 1;
+ }
+ MYSQL_SYSVAR_NAME(loc_info).flags= PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL |
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC;
+ }
+
+ memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1);
+ locinfo_ini_value[sizeof(locinfo_ini_value)-1]= 0;
+
+exit:
+#ifdef _WIN32
+ return 1;
+#else
+ return;
+#endif
+}
+
diff --git a/plugin/server_audit/test_audit_v4.c b/plugin/server_audit/test_audit_v4.c
new file mode 100644
index 00000000..f37d8c7c
--- /dev/null
+++ b/plugin/server_audit/test_audit_v4.c
@@ -0,0 +1,163 @@
+#define PLUGIN_CONTEXT
+
+/* Can't use <my_global.h> as this includes plugin.h */
+#include <stdio.h>
+
+typedef void *MYSQL_THD;
+struct st_mysql_const_lex_string
+{
+ const char *str;
+ size_t length;
+};
+typedef struct st_mysql_const_lex_string MYSQL_LEX_CSTRING;
+enum enum_sql_command{ SQLCOM_A, SQLCOM_B };
+enum enum_server_command{ SERVCOM_A, SERVCOM_B };
+
+#include "plugin_audit_v4.h"
+
+extern void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev);
+extern int get_db_mysql57(MYSQL_THD thd, char **name, size_t *len);
+
+
+struct mysql_event_general_302
+{
+ unsigned int event_subclass;
+ int general_error_code;
+ unsigned long general_thread_id;
+ const char *general_user;
+ unsigned int general_user_length;
+ const char *general_command;
+ unsigned int general_command_length;
+ const char *general_query;
+ unsigned int general_query_length;
+ struct charset_info_st *general_charset;
+ unsigned long long general_time;
+ unsigned long long general_rows;
+ unsigned long long query_id;
+ char *database;
+ size_t database_length;
+};
+
+
+static int auditing_v4(MYSQL_THD thd, mysql_event_class_t class, const void *ev)
+{
+ int *subclass= (int *)ev;
+ struct mysql_event_general_302 ev_302;
+ int subclass_v3, subclass_orig;
+
+ if (class != MYSQL_AUDIT_GENERAL_CLASS &&
+ class != MYSQL_AUDIT_CONNECTION_CLASS)
+ return 0;
+
+ subclass_orig= *subclass;
+
+ if (class == MYSQL_AUDIT_GENERAL_CLASS)
+ {
+ struct mysql_event_general *event= (struct mysql_event_general *) ev;
+ ev_302.general_error_code= event->general_error_code;
+ ev_302.general_thread_id= event->general_thread_id;
+ ev_302.general_user= event->general_user.str;
+ ev_302.general_user_length= (unsigned int)event->general_user.length;
+ ev_302.general_command= event->general_command.str;
+ ev_302.general_command_length= (unsigned int)event->general_command.length;
+ ev_302.general_query= event->general_query.str;
+ ev_302.general_query_length= (unsigned int)event->general_query.length;
+ ev_302.general_charset= event->general_charset;
+ ev_302.general_time= event->general_time;
+ ev_302.general_rows= event->general_rows;
+ if (get_db_mysql57(thd, &ev_302.database, &ev_302.database_length))
+ {
+ ev_302.database= 0;
+ ev_302.database_length= 0;
+ }
+ ev= &ev_302;
+ switch (subclass_orig)
+ {
+ case MYSQL_AUDIT_GENERAL_LOG:
+ subclass_v3= 0;
+ ev_302.event_subclass= 0;
+ break;
+ case MYSQL_AUDIT_GENERAL_ERROR:
+ subclass_v3= 1;
+ ev_302.event_subclass= 1;
+ break;
+ case MYSQL_AUDIT_GENERAL_RESULT:
+ subclass_v3= 2;
+ ev_302.event_subclass= 2;
+ break;
+ case MYSQL_AUDIT_GENERAL_STATUS:
+ {
+ subclass_v3= 3;
+ ev_302.event_subclass= 3;
+ break;
+ }
+ default:
+ return 0;
+ }
+ }
+ else /* if (class == MYSQL_AUDIT_CONNECTION_CLASS) */
+ {
+ switch (subclass_orig)
+ {
+ case MYSQL_AUDIT_CONNECTION_CONNECT:
+ subclass_v3= 0;
+ break;
+ case MYSQL_AUDIT_CONNECTION_DISCONNECT:
+ subclass_v3= 1;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ *subclass= subclass_v3;
+
+ auditing(thd, (int) class, ev);
+
+ *subclass= subclass_orig;
+ return 0;
+}
+
+
+static struct st_mysql_audit mysql_descriptor =
+{
+ MYSQL_AUDIT_INTERFACE_VERSION,
+ NULL,
+ auditing_v4,
+ { (unsigned long) MYSQL_AUDIT_GENERAL_ALL,
+ (unsigned long) MYSQL_AUDIT_CONNECTION_ALL,
+ (unsigned long) MYSQL_AUDIT_PARSE_ALL,
+ 0, /* This event class is currently not supported. */
+ 0, /* This event class is currently not supported. */
+ (unsigned long) MYSQL_AUDIT_GLOBAL_VARIABLE_ALL,
+ (unsigned long) MYSQL_AUDIT_SERVER_STARTUP_ALL,
+ (unsigned long) MYSQL_AUDIT_SERVER_SHUTDOWN_ALL,
+ (unsigned long) MYSQL_AUDIT_COMMAND_ALL,
+ (unsigned long) MYSQL_AUDIT_QUERY_ALL,
+ (unsigned long) MYSQL_AUDIT_STORED_PROGRAM_ALL }
+#ifdef WHEN_MYSQL_BUG_FIXED
+ /*
+ By this moment MySQL just sends no notifications at all
+ when we request only those we actually need.
+ So we have to request everything and filter them inside the
+ handling function.
+ */
+ { (unsigned long) MYSQL_AUDIT_GENERAL_ALL,
+ (unsigned long) (MYSQL_AUDIT_CONNECTION_CONNECT |
+ MYSQL_AUDIT_CONNECTION_DISCONNECT),
+ 0,
+ 0, /* This event class is currently not supported. */
+ 0, /* This event class is currently not supported. */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }
+#endif /*WHEN_MYSQL_BUG_FIXED*/
+};
+
+
+void *mysql_v4_descriptor= &mysql_descriptor;
+