diff --git a/Documentation/Reference/MaxBinlogCheck.md b/Documentation/Reference/MaxBinlogCheck.md
new file mode 100644
index 000000000..3f1fc4c57
--- /dev/null
+++ b/Documentation/Reference/MaxBinlogCheck.md
@@ -0,0 +1,330 @@
+# Maxbinlogcheck
+
+# The MySQL/MariaDB binlog check utility
+
+Massimiliano Pinto
+
+Last Updated: 08th September 2015
+
+# Overview
+
+Maxbinlogcheck is a command line utility for checking binlogfiles downloaded by MaxScale binlog router or the MySQL/MariaDB binlog files stored in a database server acting as a master in a replication environment.
+It checks the binlog file against any corruption and incomplete transaction stored and reports a transaction summary after reading all the events.
+It may optionally truncate binlog file.
+
+Maxbinlogcheck supports
+
+* MariaDB 5.5 and MySQL 5.6
+
+* MariaDB 10.0 with a command line option
+
+# Running maxbinlogcheck
+```
+# /usr/local/bin/maxbinlogcheck /path_to_file/bin.000002
+```
+
+# Command Line Switches
+
+The maxbinlogcheck command accepts a number of switches
+
+
+
+ | Switch |
+ Long Option |
+ Description |
+
+
+ | -f |
+ --fix |
+ If the option is set the binlog file will be truncated at last safe transaction pos in case of any error |
+
+
+ | -M |
+ --mariadb10 |
+ Check the current binlog against MariaDB 10.0.x events |
+
+
+ | -d |
+ --debug |
+ Set the debug mode. If set the FD Events, Rotate events and opening/closing transactions are displayed. |
+
+
+ | -? |
+ --help |
+ Print usage information regarding maxbinlogcheck |
+
+
+ | -V |
+ --version |
+ Print the maxbinlogcheck version information |
+
+
+
+## Example without debug:
+
+1) No transactions
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000002
+2015-09-08 09:38:03 maxbinlogcheck 1.0.0
+2015-09-08 09:38:03 Checking /servers/binlogs/new-trx/mar-bin.000002 (mar-bin.000002), size 290 bytes
+2015-09-08 09:38:03 Check retcode: 0, Binlog Pos = 290
+```
+
+2) With complete transactions
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000004
+2015-09-08 09:38:35 maxbinlogcheck 1.0.0
+2015-09-08 09:38:35 Checking /servers/binlogs/new-trx/mar-bin.000004 (mar-bin.000004), size 38738629 bytes
+2015-09-08 09:38:36 Transaction Summary:
+ Description Total Average Max
+ No. of Transactions 21713
+ No. of Events 186599 8.6 5082
+ No. of Bytes 36M 1k 5M
+2015-09-08 09:38:36 Check retcode: 0, Binlog Pos = 38738629
+```
+
+## Example with debug:
+
+1) one complete transaction
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000001 -d
+2015-09-08 09:36:49 maxbinlogcheck 1.0.0
+2015-09-08 09:36:49 Checking /servers/binlogs/new-trx/mar-bin.000001 (mar-bin.000001), size 590760698 bytes
+2015-09-08 09:36:49 - Format Description event FDE @ 4, size 241
+2015-09-08 09:36:49 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 09:36:49 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 09:36:49 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 09:36:49 > Transaction starts @ pos 572
+2015-09-08 09:36:49 Transaction XID @ pos 572, closing @ 590760644
+2015-09-08 09:36:49 < Transaction @ pos 572, is now closed @ 590760644. 18001 events seen
+2015-09-08 09:36:49 End of binlog file [mar-bin.000001] at 590760698.
+2015-09-08 09:36:49 Transaction Summary:
+ Description Total Average Max
+ No. of Transactions 1
+ No. of Events 18001 18001.0 18001
+ No. of Bytes 563M 563M 563M
+2015-09-08 09:36:49 Check retcode: 0, Binlog Pos = 590760698
+```
+
+2) some transactions
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000004 -d
+2015-09-08 10:19:35 maxbinlogcheck 1.0.0
+2015-09-08 10:19:35 Checking /servers/binlogs/new-trx/mar-bin.000004 (mar-bin.000004), size 38738629 bytes
+2015-09-08 10:19:35 - Format Description event FDE @ 4, size 241
+2015-09-08 10:19:35 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 10:19:35 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 10:19:35 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 10:19:35 > Transaction starts @ pos 971
+2015-09-08 10:19:35 Transaction XID @ pos 971, closing @ 1128
+2015-09-08 10:19:35 < Transaction @ pos 971, is now closed @ 1128. 3 events seen
+...
+2015-09-08 10:19:51 > Transaction starts @ pos 33440561
+2015-09-08 10:19:51 Transaction XID @ pos 33440561, closing @ 33440763
+2015-09-08 10:19:51 < Transaction @ pos 33440561, is now closed @ 33440763. 3 events seen
+2015-09-08 10:19:51 > Transaction starts @ pos 33440794
+2015-09-08 10:19:51 Transaction XID @ pos 33440794, closing @ 38738553
+2015-09-08 10:19:51 < Transaction @ pos 33440794, is now closed @ 38738553. 5082 events seen
+2015-09-08 10:19:51 - Rotate event @ 38738584, next file is [mar-bin.000005] @ 4
+2015-09-08 10:19:51 End of binlog file [mar-bin.000004] at 38738629.
+2015-09-08 10:19:51 Transaction Summary:
+ Description Total Average Max
+ No. of Transactions 21713
+ No. of Events 186599 8.6 5082
+ No. of Bytes 36M 1k 5M
+2015-09-08 10:19:51 Check retcode: 0, Binlog Pos = 38738629
+```
+
+3) No transactions
+
+```
+2015-09-08 09:41:02 Check retcode: 0, Binlog Pos = 290
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000002 -d
+2015-09-08 09:41:08 maxbinlogcheck 1.0.0
+2015-09-08 09:41:08 Checking /servers/binlogs/new-trx/mar-bin.000002 (mar-bin.000002), size 290 bytes
+2015-09-08 09:41:08 - Format Description event FDE @ 4, size 241
+2015-09-08 09:41:08 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 09:41:08 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 09:41:08 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 09:41:08 - Rotate event @ 245, next file is [mar-bin.000003] @ 4
+2015-09-08 09:41:08 End of binlog file [mar-bin.000002] at 290.
+2015-09-08 09:41:08 Check retcode: 0, Binlog Pos = 290
+```
+
+## Fixing a corrupted binlog file
+
+This file is corrupted, as reported by the utility:
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/bin.000002
+2015-09-08 10:03:16 maxbinlogcheck 1.0.0
+2015-09-08 10:03:16 Checking /servers/binlogs/new-trx/bin.000002 (bin.000002), size 109498 bytes
+2015-09-08 10:03:16 Event size error: size 0 at 290.
+2015-09-08 10:03:16 warning : an error has been found. Setting safe pos to 245, current pos 290
+2015-09-08 10:03:16 Check retcode: 1, Binlog Pos = 245
+```
+
+The suggested safe pos is 245
+
+Use -f option for fix with debug:
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/bin.000002 -d -f
+2015-09-08 09:56:52 maxbinlogcheck 1.0.0
+2015-09-08 09:56:52 Checking /servers/binlogs/new-trx/bin.000002 (bin.000002), size 109498 bytes
+2015-09-08 09:56:52 - Format Description event FDE @ 4, size 241
+2015-09-08 09:56:52 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 09:56:52 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 09:56:52 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 09:56:52 - Rotate event @ 245, next file is [mar-bin.000003] @ 4
+2015-09-08 09:56:52 Event size error: size 0 at 290.
+2015-09-08 09:56:52 warning : an error has been found. Setting safe pos to 245, current pos 290
+2015-09-09 09:56:52 Binlog file bin.000002 has been truncated at 245
+2015-09-08 09:56:52 Check retcode: 1, Binlog Pos = 245
+```
+
+Check it again, last pos will be 245 and no errors will be reported:
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/bin.000002 -d
+2015-09-08 09:56:56 maxbinlogcheck 1.0.0
+2015-09-08 09:56:56 Checking /servers/binlogs/new-trx/bin.000002 (bin.000002), size 245 bytes
+2015-09-08 09:56:56 - Format Description event FDE @ 4, size 241
+2015-09-08 09:56:56 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 09:56:56 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 09:56:56 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 09:56:56 End of binlog file [bin.000002] at 245.
+2015-09-08 09:56:56 Check retcode: 0, Binlog Pos = 245
+```
+
+## Detection of an incomplete big transaction
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000003
+2015-09-08 10:10:21 maxbinlogcheck 1.0.0
+2015-09-08 10:10:21 Checking /servers/binlogs/new-trx/mar-bin.000003 (mar-bin.000003), size 16476284 bytes
+2015-09-08 10:10:21 Warning : pending transaction has been found. Setting safe pos to 572, current pos 16476284
+2015-09-08 10:10:21 Check retcode: 0, Binlog Pos = 572
+```
+
+with debug option:
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000003 -d
+2015-09-08 10:11:08 maxbinlogcheck 1.0.0
+2015-09-08 10:11:08 Checking /servers/binlogs/new-trx/mar-bin.000003 (mar-bin.000003), size 16476284 bytes
+2015-09-08 10:11:08 - Format Description event FDE @ 4, size 241
+2015-09-08 10:11:08 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 10:11:08 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 10:11:08 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 10:11:08 > Transaction starts @ pos 572
+2015-09-08 10:11:08 Warning : pending transaction has been found. Setting safe pos to 572, current pos 16476284
+2015-09-08 10:11:08 End of binlog file [mar-bin.000003] at 16476284.
+2015-09-08 10:11:08 Check retcode: 0, Binlog Pos = 572
+```
+
+Retcode is 0 as the transaction may proceed over time, example:
+
+Another check ...
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000003 -d
+2015-09-08 10:17:13 maxbinlogcheck 1.0.0
+2015-09-08 10:17:13 Checking /servers/binlogs/new-trx/mar-bin.000003 (mar-bin.000003), size 569296364 bytes
+2015-09-08 10:17:13 - Format Description event FDE @ 4, size 241
+2015-09-08 10:17:13 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 10:17:13 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 10:17:13 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 10:17:13 > Transaction starts @ pos 572
+2015-09-08 10:17:14 End of binlog file [mar-bin.000003] at 577567062.
+2015-09-08 10:17:14 Check retcode: 0, Binlog Pos = 572
+```
+
+And finally big transaction is now done.
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000003 -d
+2015-09-08 10:17:15 maxbinlogcheck 1.0.0
+2015-09-08 10:17:15 Checking /servers/binlogs/new-trx/mar-bin.000003 (mar-bin.000003), size 590760698 bytes
+2015-09-08 10:17:15 - Format Description event FDE @ 4, size 241
+2015-09-08 10:17:15 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 10:17:15 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 10:17:15 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 10:17:15 > Transaction starts @ pos 572
+2015-09-08 10:17:16 Transaction XID @ pos 572, closing @ 590760644
+2015-09-08 10:17:16 < Transaction @ pos 572, is now closed @ 590760644. 18001 events seen
+2015-09-08 10:17:16 End of binlog file [mar-bin.000003] at 590760698.
+2015-09-08 10:17:16 Transaction Summary:
+ Description Total Average Max
+ No. of Transactions 1
+ No. of Events 18001 18001.0 18001
+ No. of Bytes 563M 563M 563M
+2015-09-08 10:17:16 Check retcode: 0, Binlog Pos = 590760698
+```
+
+**Note**
+with current maxbinlogcheck it's not possible to fix a binlog with incomplete transaction and no other errors
+
+If that is really desired it will be possible with UNIX command line:
+
+```
+# truncate /servers/binlogs/new-trx/mar-bin.000003 --size=572
+```
+
+In case of an error and incomplete transaction, the fix will work
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000003 -d -f
+2015-09-08 10:35:57 maxbinlogcheck 1.0.0
+2015-09-08 10:35:57 Checking /servers/binlogs/new-trx/mar-bin.000003 (mar-bin.000003), size 282580902 bytes
+2015-09-08 10:35:57 - Format Description event FDE @ 4, size 241
+2015-09-08 10:35:57 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 10:35:57 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 10:35:57 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 10:35:57 > Transaction starts @ pos 572
+2015-09-08 10:35:57 Short read when reading the event at 304898502 in mar-bin.000003. Expected 65563 bytes got 15911 bytes.
+2015-09-08 10:35:57 warning : an error has been found. Setting safe pos to 572, current pos 304898502
+2015-09-09 10:35:57 Binlog file bin.000003 has been truncated at 572
+2015-09-08 10:35:57 Check retcode: 1, Binlog Pos = 572
+```
+
+Check result:
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck /servers/binlogs/new-trx/mar-bin.000003 -d
+2015-09-08 10:54:17 maxbinlogcheck 1.0.0
+2015-09-08 10:54:17 Checking /servers/binlogs/new-trx/mar-bin.000003 (mar-bin.000003), size 572 bytes
+2015-09-08 10:54:17 - Format Description event FDE @ 4, size 241
+2015-09-08 10:54:17 FDE ServerVersion [ 5.5.35-MariaDB-log]
+2015-09-08 10:54:17 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 160
+2015-09-08 10:54:17 FDE Checksum alg desc 1, alg type BINLOG_CHECKSUM_ALG_CRC32
+2015-09-08 10:54:17 End of binlog file [mar-bin.000003] at 572.
+2015-09-08 10:54:17 Check retcode: 0, Binlog Pos = 572
+```
+
+### MariaDB 10 binlog check
+
+```
+[root@maxscale-02 build]# /usr/local/bin/maxbinlogcheck -M -d /mariadb-10.0.11/data/mysql-bin.000008
+2015-09-08 12:49:18 maxbinlogcheck 1.0.0
+2015-09-08 12:49:18 Checking /mariadb-10.0.11/data/mysql-bin.000008 (mysql-bin.000008), size 1215327 bytes
+2015-09-08 12:49:18 - Format Description event FDE @ 4, size 244
+2015-09-08 12:49:18 FDE ServerVersion [ 10.0.11-MariaDB-log]
+2015-09-08 12:49:18 FDE Header EventLength 19, N. of supported MySQL/MariaDB events 163
+2015-09-08 12:49:18 FDE Checksum alg desc 0, alg type NONE or UNDEF
+2015-09-08 12:49:18 > MariaDB 10 Transaction (GTID 0-29-60) starts @ pos 802
+2015-09-08 12:49:18 Transaction XID @ pos 802, closing @ 1214943
+2015-09-08 12:49:18 < Transaction @ pos 802, is now closed @ 1214943. 76 events seen
+2015-09-08 12:49:18 End of binlog file [mysql-bin.000008] at 1215327.
+2015-09-08 12:49:18 Transaction Summary:
+ Description Total Average Max
+ No. of Transactions 1
+ No. of Events 76 76.0 76
+ No. of Bytes 1.2M 1.2M 1.2M
+2015-09-08 12:49:18 Check retcode: 0, Binlog Pos = 1215327
+```
+
diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h
index 7df2c395f..03a87a698 100644
--- a/server/modules/include/blr.h
+++ b/server/modules/include/blr.h
@@ -15,7 +15,7 @@
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright MariaDB Corporation Ab 2014
+ * Copyright MariaDB Corporation Ab 2014-2015
*/
/**
@@ -142,6 +142,9 @@
#define BLR_MASTER_BACKOFF_TIME 10
#define BLR_MAX_BACKOFF 60
+/* string len for strerror_r message */
+#define BLRM_STRERROR_R_MSG_SIZE 128
+
/**
* Some useful macros for examining the MySQL Response packets
*/
@@ -340,9 +343,12 @@ typedef struct router_instance {
MASTER_RESPONSES saved_master; /*< Saved master responses */
char *binlogdir; /*< The directory with the binlog files */
SPINLOCK binlog_lock; /*< Lock to control update of the binlog position */
+ int pending_transaction; /*< Pending transaction */
char binlog_name[BINLOG_FNAMELEN+1];
/*< Name of the current binlog file */
uint64_t binlog_position;
+ /*< Current binlog position, safe pos */
+ uint64_t current_pos;
/*< Current binlog position */
int binlog_fd; /*< File descriptor of the binlog
* file being written
diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt
index 9a0c245de..1e65eb504 100644
--- a/server/modules/routing/binlog/CMakeLists.txt
+++ b/server/modules/routing/binlog/CMakeLists.txt
@@ -2,3 +2,9 @@ add_library(binlogrouter SHARED blr.c blr_master.c blr_cache.c blr_slave.c blr_f
set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_LIBDIR})
target_link_libraries(binlogrouter ssl pthread log_manager)
install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR})
+
+add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c ${CMAKE_SOURCE_DIR}/server/core/service.c ${CMAKE_SOURCE_DIR}/server/core/spinlock.c ${CMAKE_SOURCE_DIR}/server/core/buffer.c ${CMAKE_SOURCE_DIR}/server/core/atomic.c ${CMAKE_SOURCE_DIR}/server/core/hint.c ${CMAKE_SOURCE_DIR}/server/core/gwdirs.c ${CMAKE_SOURCE_DIR}/server/core/server.c ${CMAKE_SOURCE_DIR}/server/core/dcb.c ${CMAKE_SOURCE_DIR}/server/core/users.c ${CMAKE_SOURCE_DIR}/server/core/dbusers.c ${CMAKE_SOURCE_DIR}/server/core/utils.c ${CMAKE_SOURCE_DIR}/server/core/hashtable.c ${CMAKE_SOURCE_DIR}/server/core/poll.c ${CMAKE_SOURCE_DIR}/server/core/gwbitmask.c ${CMAKE_SOURCE_DIR}/server/core/config.c ${CMAKE_SOURCE_DIR}/server/core/session.c ${CMAKE_SOURCE_DIR}/server/core/housekeeper.c ${CMAKE_SOURCE_DIR}/server/core/filter.c ${CMAKE_SOURCE_DIR}/server/core/resultset.c ${CMAKE_SOURCE_DIR}/server/core/load_utils.c ${CMAKE_SOURCE_DIR}/server/core/monitor.c ${CMAKE_SOURCE_DIR}/server/core/gw_utils.c ${CMAKE_SOURCE_DIR}/server/core/thread.c ${CMAKE_SOURCE_DIR}/server/core/secrets.c)
+
+target_link_libraries(maxbinlogcheck utils ssl pthread log_manager ${PCRE_LINK_FLAGS} aio rt crypt dl crypto inih z m stdc++ ${EMBEDDED_LIB} ${CURL_LIBRARIES})
+
+install(TARGETS maxbinlogcheck DESTINATION bin)
diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c
index a6aa50bdf..f6daf54d0 100644
--- a/server/modules/routing/binlog/blr_file.c
+++ b/server/modules/routing/binlog/blr_file.c
@@ -26,6 +26,8 @@
* Date Who Description
* 14/04/2014 Mark Riddoch Initial implementation
* 07/05/2015 Massimiliano Pinto Added MAX_EVENT_TYPE_MARIADB10
+ * 10/09/2015 Massimiliano Pinto Added blr_read_events_all_events()
+ * It's called in maxbinlogcheck utility
*
* @endverbatim
*/
@@ -59,6 +61,7 @@ extern __thread log_info_t tls_log_info;
static int blr_file_create(ROUTER_INSTANCE *router, char *file);
static void blr_file_append(ROUTER_INSTANCE *router, char *file);
static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr);
+static void blr_format_event_size(double *event_size, char *label);
/**
* Initialise the binlog file for this instance. MaxScale will look
@@ -731,3 +734,695 @@ int filenum;
return 0;
return 1;
}
+
+/**
+ * Read all replication events from a binlog file.
+ *
+ * Routine detects errors and pending transactions
+ *
+ * @param router The router instance
+ * @param fix Whether to fix or not errors
+ * @param debug Whether to enable or not the debug for events
+ * @return 0 on success, >0 on failure
+ */
+int
+blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug) {
+unsigned long filelen = 0;
+struct stat statb;
+uint8_t hdbuf[19];
+uint8_t *data;
+GWBUF *result;
+unsigned long long pos = 4;
+unsigned long long last_known_commit = 4;
+
+REP_HEADER hdr;
+int pending_transaction = 0;
+int n;
+int db_name_len;
+char *statement_sql;
+uint8_t *ptr;
+int len;
+int var_block_len;
+int statement_len;
+int checksum_len=0;
+int found_chksum = 0;
+int event_error = 0;
+unsigned long transaction_events = 0;
+unsigned long total_events = 0;
+unsigned long total_bytes = 0;
+unsigned long n_transactions = 0;
+unsigned long max_events = 0;
+unsigned long event_bytes = 0;
+unsigned long max_bytes = 0;
+double average_events = 0;
+double average_bytes = 0;
+
+ if (router->binlog_fd == -1) {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "ERROR: Current binlog file %s is not open",
+ router->binlog_name)));
+ return 1;
+ }
+
+ if (fstat(router->binlog_fd, &statb) == 0)
+ filelen = statb.st_size;
+
+ router->current_pos = 4;
+ router->binlog_position = 4;
+
+ while (1){
+
+ /* Read the header information from the file */
+ if ((n = pread(router->binlog_fd, hdbuf, 19, pos)) != 19) {
+ switch (n)
+ {
+ case 0:
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ "End of binlog file [%s] at %llu.",
+ router->binlog_name,
+ pos)));
+ if (n_transactions)
+ average_events = (double)((double)total_events / (double)n_transactions) * (1.0);
+ if (n_transactions)
+ average_bytes = (double)((double)total_bytes / (double)n_transactions) * (1.0);
+
+ if (n_transactions != 0) {
+ char total_label[2]="";
+ char average_label[2]="";
+ char max_label[2]="";
+ double format_total_bytes = total_bytes;
+ double format_max_bytes = max_bytes;
+
+ blr_format_event_size(&format_total_bytes, total_label);
+ blr_format_event_size(&average_bytes, average_label);
+ blr_format_event_size(&format_max_bytes, max_label);
+
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Transaction Summary for binlog '%s'\n"
+ "\t\t\tDescription %17s%17s%17s\n\t\t\t"
+ "No. of Transactions %16llu\n\t\t\t"
+ "No. of Events %16llu %16.1f %16llu\n\t\t\t"
+ "No. of Bytes %16.1f%s%16.1f%s%16.1f%s", router->binlog_name,
+ "Total", "Average", "Max",
+ n_transactions, total_events,
+ average_events, max_events,
+ format_total_bytes, total_label, average_bytes, average_label, format_max_bytes, max_label)));
+ }
+
+ if (pending_transaction) {
+ LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE,
+ "Warning : Binlog file %s contains a previous Opened Transaction"
+ " @ %llu. This pos is safe for slaves",
+ router->binlog_name,
+ last_known_commit)));
+
+ }
+
+ break;
+ case -1:
+ {
+ char err_msg[BLRM_STRERROR_R_MSG_SIZE+1] = "";
+ strerror_r(errno, err_msg, BLRM_STRERROR_R_MSG_SIZE);
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "ERROR: Failed to read binlog file %s at position %llu"
+ " (%s).", router->binlog_name,
+ pos, err_msg)));
+
+ if (errno == EBADF)
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "ERROR: Bad file descriptor in read binlog for file %s"
+ ", descriptor %d.",
+ router->binlog_name, router->binlog_fd)));
+ break;
+ }
+ default:
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "ERROR: Short read when reading the header. "
+ "Expected 19 bytes but got %d bytes. "
+ "Binlog file is %s, position %llu",
+ n, router->binlog_name, pos)));
+ break;
+ }
+
+ /**
+ * Check for errors and force last_known_commit position
+ * and current pos
+ */
+
+ if (pending_transaction) {
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+ router->pending_transaction = 1;
+ pending_transaction = 0;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "Warning : pending transaction has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+
+ return 0;
+ } else {
+ /* any error */
+ if (n != 0) {
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+ if (fix) {
+ if (ftruncate(router->binlog_fd, router->binlog_position) == 0) {
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Binlog file %s has been truncated at %lu",
+ router->binlog_name,
+ router->binlog_position)));
+ fsync(router->binlog_fd);
+ }
+ }
+
+ return 1;
+ } else {
+ router->binlog_position = pos;
+ router->current_pos = pos;
+
+ return 0;
+ }
+ }
+ }
+
+ /* fill replication header struct */
+ hdr.timestamp = EXTRACT32(hdbuf);
+ hdr.event_type = hdbuf[4];
+ hdr.serverid = EXTRACT32(&hdbuf[5]);
+ hdr.event_size = extract_field(&hdbuf[9], 32);
+ hdr.next_pos = EXTRACT32(&hdbuf[13]);
+ hdr.flags = EXTRACT16(&hdbuf[17]);
+
+ /* Check event type against MAX_EVENT_TYPE */
+
+ if (router->mariadb10_compat) {
+ if (hdr.event_type > MAX_EVENT_TYPE_MARIADB10) {
+ LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
+ "Invalid MariaDB 10 event type 0x%x. "
+ "Binlog file is %s, position %d",
+ hdr.event_type,
+ router->binlog_name, pos)));
+
+ event_error = 1;
+ }
+ } else {
+ if (hdr.event_type > MAX_EVENT_TYPE) {
+ LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
+ "Invalid event type 0x%x. "
+ "Binlog file is %s, position %d",
+ hdr.event_type,
+ router->binlog_name, pos)));
+
+ event_error = 1;
+ }
+ }
+
+ if (event_error) {
+
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found in %s. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_name,
+ router->binlog_position,
+ router->current_pos)));
+
+ if (fix) {
+ if (ftruncate(router->binlog_fd, router->binlog_position) == 0) {
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Binlog file %s has been truncated at %lu",
+ router->binlog_name,
+ router->binlog_position)));
+ fsync(router->binlog_fd);
+ }
+ }
+
+ return 1;
+ }
+
+ if (hdr.event_size <= 0)
+ {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "Event size error: "
+ "size %d at %llu.",
+ hdr.event_size, pos)));
+
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+ if (fix) {
+ if (ftruncate(router->binlog_fd, router->binlog_position) == 0) {
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Binlog file %s has been truncated at %lu",
+ router->binlog_name,
+ router->binlog_position)));
+ fsync(router->binlog_fd);
+ }
+ }
+
+ return 1;
+ }
+
+ /* Allocate a GWBUF for the event */
+ if ((result = gwbuf_alloc(hdr.event_size)) == NULL)
+ {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "ERROR: Failed to allocate memory for binlog entry, "
+ "size %d at %llu.",
+ hdr.event_size, pos)));
+
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+
+ if (fix) {
+ if (ftruncate(router->binlog_fd, router->binlog_position) == 0) {
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Binlog file %s has been truncated at %lu",
+ router->binlog_name,
+ router->binlog_position)));
+ fsync(router->binlog_fd);
+ }
+ }
+
+ return 1;
+ }
+
+ /* Copy the header in the buffer */
+ data = GWBUF_DATA(result);
+ memcpy(data, hdbuf, 19);// Copy the header in
+
+ /* Read event data */
+ if ((n = pread(router->binlog_fd, &data[19], hdr.event_size - 19, pos + 19)) != hdr.event_size - 19)
+ {
+ if (n == -1)
+ {
+ char err_msg[BLRM_STRERROR_R_MSG_SIZE+1] = "";
+ strerror_r(errno, err_msg, BLRM_STRERROR_R_MSG_SIZE);
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "Error reading the event at %llu in %s. "
+ "%s, expected %d bytes.",
+ pos, router->binlog_name,
+ err_msg, hdr.event_size - 19)));
+ }
+ else
+ {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "Short read when reading the event at %llu in %s. "
+ "Expected %d bytes got %d bytes.",
+ pos, router->binlog_name, hdr.event_size - 19, n)));
+
+ if (filelen > 0 && filelen - pos < hdr.event_size)
+ {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "Binlog event is close to the end of the binlog file %s, "
+ " size is %lu.",
+ router->binlog_name, filelen)));
+ }
+ }
+
+ gwbuf_free(result);
+
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+ if (fix) {
+ if (ftruncate(router->binlog_fd, router->binlog_position) == 0) {
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Binlog file %s has been truncated at %lu",
+ router->binlog_name,
+ router->binlog_position)));
+ fsync(router->binlog_fd);
+ }
+ }
+
+ return 1;
+ }
+
+ /* check for pending transaction */
+ if (pending_transaction == 0) {
+ last_known_commit = pos;
+ }
+
+ /* get event content */
+ ptr = data+19;
+
+ /* check for FORMAT DESCRIPTION EVENT */
+ if(hdr.event_type == FORMAT_DESCRIPTION_EVENT) {
+ int event_header_length;
+ int event_header_ntypes;
+ int n_events;
+ int check_alg;
+ uint8_t *checksum;
+
+ if(debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ "- Format Description event FDE @ %llu, size %lu",
+ pos, (unsigned long)hdr.event_size)));
+
+ event_header_length = ptr[2 + 50 + 4];
+ event_header_ntypes = hdr.event_size - event_header_length - (2 + 50 + 4 + 1);
+
+ if (event_header_ntypes == 168) {
+ /* mariadb 10 LOG_EVENT_TYPES*/
+ event_header_ntypes -= 163;
+ } else {
+ if (event_header_ntypes == 165) {
+ /* mariadb 5 LOG_EVENT_TYPES*/
+ event_header_ntypes -= 160;
+ } else {
+ /* mysql 5.6 LOG_EVENT_TYPES = 35 */
+ event_header_ntypes -= 35;
+ }
+ }
+
+ n_events = hdr.event_size - event_header_length - (2 + 50 + 4 + 1);
+
+ if(debug) {
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ " FDE ServerVersion [%50s]", ptr + 2)));
+
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ " FDE Header EventLength %i"
+ ", N. of supported MySQL/MariaDB events %i",
+ event_header_length,
+ (n_events - event_header_ntypes))));
+ }
+
+ if (event_header_ntypes < n_events) {
+ checksum = ptr + hdr.event_size - event_header_length - event_header_ntypes;
+ check_alg = checksum[0];
+
+ if(debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ " FDE Checksum alg desc %i, alg type %s",
+ check_alg,
+ check_alg == 1 ? "BINLOG_CHECKSUM_ALG_CRC32" : "NONE or UNDEF")));
+ if (check_alg == 1) {
+ checksum_len = 4;
+ found_chksum = 1;
+ } else {
+ found_chksum = 0;
+ }
+ }
+ }
+ /* Decode ROTATE EVENT */
+ if(hdr.event_type == ROTATE_EVENT) {
+ int len, slen;
+ uint64_t new_pos;
+ char file[BINLOG_FNAMELEN+1];
+
+ len = hdr.event_size - 19;
+ new_pos = extract_field(ptr+4, 32);
+ new_pos <<= 32;
+ new_pos |= extract_field(ptr, 32);
+ slen = len - (8 + 4); // Allow for position and CRC
+ if (found_chksum == 0)
+ slen += 4;
+ if (slen > BINLOG_FNAMELEN)
+ slen = BINLOG_FNAMELEN;
+ memcpy(file, ptr + 8, slen);
+ file[slen] = 0;
+
+ if(debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ "- Rotate event @ %llu, next file is [%s] @ %llu",
+ pos, file, new_pos)));
+ }
+
+ /* If MariaDB 10 compatibility:
+ * check for MARIADB10_GTID_EVENT with flags = 0
+ * This marks the transaction starts instead of
+ * QUERY_EVENT with "BEGIN"
+ */
+
+ if (router->mariadb10_compat) {
+ if (hdr.event_type == MARIADB10_GTID_EVENT) {
+ uint64_t n_sequence; /* 8 bytes */
+ uint32_t domainid; /* 4 bytes */
+ unsigned int flags; /* 1 byte */
+ n_sequence = extract_field(ptr, 64);
+ domainid = extract_field(ptr + 8, 32);
+ flags = *(ptr + 8 + 4);
+
+ if (flags == 0) {
+ if (pending_transaction > 0) {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "ERROR: Transaction cannot be @ pos %llu: "
+ "Another MariaDB 10 transaction (GTID %lu-%lu-%llu)"
+ " was opened at %llu",
+ pos, domainid, hdr.serverid, n_sequence, last_known_commit)));
+
+ gwbuf_free(result);
+
+ break;
+ } else {
+ pending_transaction = 1;
+
+ transaction_events = 0;
+ event_bytes = 0;
+
+ if (debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ "> MariaDB 10 Transaction (GTID %lu-%lu-%llu)"
+ " starts @ pos %llu",
+ domainid, hdr.serverid, n_sequence, pos)));
+ }
+ }
+ }
+ }
+
+ /**
+ * Check QUERY_EVENT
+ *
+ * Check for BEGIN ( ONLY for mysql 5.6, mariadb 5.5 )
+ * Check for COMMIT (not transactional engines)
+ */
+
+ if(hdr.event_type == QUERY_EVENT) {
+ char *statement_sql;
+ db_name_len = ptr[4 + 4];
+ var_block_len = ptr[4 + 4 + 1 + 2];
+
+ statement_len = hdr.event_size - 19 - (4+4+1+2+2+var_block_len+1+db_name_len);
+ //if (checksum_len)
+ // statement_len -= checksum_len;
+
+ statement_sql = calloc(1, statement_len+1);
+ strncpy(statement_sql, (char *)ptr+4+4+1+2+2+var_block_len+1+db_name_len, statement_len);
+
+ /* A transaction starts with this event */
+ if (strncmp(statement_sql, "BEGIN", 5) == 0) {
+ if (pending_transaction > 0) {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "ERROR: Transaction cannot be @ pos %llu: "
+ "Another transaction was opened at %llu",
+ pos, last_known_commit)));
+
+ free(statement_sql);
+ gwbuf_free(result);
+
+ break;
+ } else {
+ pending_transaction = 1;
+
+ transaction_events = 0;
+ event_bytes = 0;
+
+ if (debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ "> Transaction starts @ pos %llu", pos)));
+ }
+ }
+
+ /* Commit received for non transactional tables, i.e. MyISAM */
+ if (strncmp(statement_sql, "COMMIT", 6) == 0) {
+ if (pending_transaction > 0) {
+ pending_transaction = 3;
+
+ if (debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ " Transaction @ pos %llu, closing @ %llu", last_known_commit, pos)));
+ }
+ }
+ free(statement_sql);
+
+ }
+
+ if(hdr.event_type == XID_EVENT) {
+ /* Commit received for a transactional tables, i.e. InnoDB */
+ uint64_t xid;
+ xid = extract_field(ptr, 64);
+
+ if (pending_transaction > 0) {
+ pending_transaction = 2;
+ if (debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ " Transaction XID @ pos %llu, closing @ %llu", last_known_commit, pos)));
+ }
+ }
+
+ if (pending_transaction > 1) {
+ if (debug)
+ LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
+ "< Transaction @ pos %llu, is now closed @ %llu. %lu events seen", last_known_commit, pos, transaction_events)));
+ pending_transaction = 0;
+ last_known_commit = pos;
+
+ total_events += transaction_events;
+
+ if (transaction_events > max_events)
+ max_events = transaction_events;
+
+ n_transactions++;
+ }
+
+ gwbuf_free(result);
+
+ /* pos and next_pos sanity checks */
+ if (hdr.next_pos > 0 && hdr.next_pos < pos) {
+ LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE,
+ "Binlog %s: next pos %llu < pos %llu, truncating to %llu",
+ router->binlog_name,
+ hdr.next_pos,
+ pos,
+ pos)));
+
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+ if (fix) {
+ if (ftruncate(router->binlog_fd, router->binlog_position) == 0) {
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Binlog file %s has been truncated at %lu",
+ router->binlog_name,
+ router->binlog_position)));
+ fsync(router->binlog_fd);
+ }
+ }
+
+ return 2;
+ }
+
+ if (hdr.next_pos > 0 && hdr.next_pos != (pos + hdr.event_size)) {
+ LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE,
+ "Binlog %s: next pos %llu != (pos %llu + event_size %llu), truncating to %llu",
+ router->binlog_name,
+ hdr.next_pos,
+ pos,
+ hdr.event_size,
+ pos)));
+
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+
+ if (fix) {
+ if (ftruncate(router->binlog_fd, router->binlog_position) == 0) {
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Binlog file %s has been truncated at %lu",
+ router->binlog_name,
+ router->binlog_position)));
+ fsync(router->binlog_fd);
+ }
+ }
+
+ return 2;
+ }
+
+ /* set pos to new value */
+ if (hdr.next_pos > 0) {
+
+ if (pending_transaction) {
+ total_bytes += hdr.event_size;
+ event_bytes += hdr.event_size;
+
+ if (event_bytes > max_bytes)
+ max_bytes = event_bytes;
+ }
+
+ pos = hdr.next_pos;
+ } else {
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "Current event type %lu @ %llu has nex pos = %llu : exiting", hdr.event_type, pos, hdr.next_pos)));
+ break;
+ }
+
+ transaction_events++;
+ }
+
+ if (pending_transaction) {
+ LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE,
+ "Binlog %s contains an Open Transaction, truncating to %llu",
+ router->binlog_name,
+ last_known_commit)));
+
+ router->binlog_position = last_known_commit;
+ router->current_pos = pos;
+ router->pending_transaction = 1;
+
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "warning : an error has been found. "
+ "Setting safe pos to %lu, current pos %lu",
+ router->binlog_position, router->current_pos)));
+
+ return 0;
+ } else {
+ router->binlog_position = pos;
+ router->current_pos = pos;
+
+ return 0;
+ }
+}
+
+/**
+ * Format a number to G, M, k, or B size
+ *
+ * @param event_size The number to format
+ * @param label Label to use for display the formattted number
+ */
+static void
+blr_format_event_size(double *event_size, char *label)
+{
+ if (*event_size > (1024 * 1024 * 1024)) {
+ *event_size = *event_size / (1024 * 1024 * 1024);
+ label[0] = 'G';
+ } else if (*event_size > (1024 * 1024)) {
+ *event_size = *event_size / (1024 * 1024);
+ label[0] = 'M';
+ } else if (*event_size > 1024) {
+ *event_size = *event_size / (1024);
+ label[0] = 'k';
+ } else
+ label[0] = 'B';
+}
+
diff --git a/server/modules/routing/binlog/maxbinlogcheck.c b/server/modules/routing/binlog/maxbinlogcheck.c
new file mode 100644
index 000000000..79cc30382
--- /dev/null
+++ b/server/modules/routing/binlog/maxbinlogcheck.c
@@ -0,0 +1,249 @@
+/*
+ * GNU General Public License as published by the Free Software Foundation,
+ * version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright MariaDB Corporation Ab 2015
+ */
+
+/**
+ * @file maxbinlogcheck.c - The MaxScale binlog check utility
+ *
+ * This utility checks a MySQL 5.6 and MariaDB 10.0.X binlog file and reports
+ * any found error or an incomplete transaction.
+ * It suggests the pos the file should be trucatetd at.
+ *
+ * @verbatim
+ * Revision History
+ *
+ * Date Who Description
+ * 24/07/2015 Massimiliano Pinto Initial implementation
+ * 26/08/2015 Massimiliano Pinto Added mariadb10 option
+ * for MariaDB 10 binlog compatibility
+ * Currently MariadDB 10 starting transactions
+ * are detected checking GTID event
+ * with flags = 0
+ *
+ * @endverbatim
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+extern int lm_enabled_logfiles_bitmask;
+extern size_t log_ses_count[];
+extern __thread log_info_t tls_log_info;
+extern int blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug);
+extern uint32_t extract_field(uint8_t *src, int bits);
+static void printVersion(const char *progname);
+static void printUsage(const char *progname);
+
+static struct option long_options[] = {
+ {"debug", no_argument, 0, 'd'},
+ {"version", no_argument, 0, 'V'},
+ {"fix", no_argument, 0, 'f'},
+ {"mariadb10", no_argument, 0, 'M'},
+ {"help", no_argument, 0, '?'},
+ {0, 0, 0, 0}
+};
+
+char *binlog_check_version = "1.0.0";
+
+int main(int argc, char **argv) {
+ char** arg_vector;
+ int arg_count = 4;
+ ROUTER_INSTANCE *inst;
+ int fd;
+ int ret;
+ char *ptr;
+ char path[PATH_MAX+1] = "";
+ unsigned long filelen = 0;
+ struct stat statb;
+ char c;
+ int option_index = 0;
+ int num_args = 0;
+ int debug_out = 0;
+ int fix_file = 0;
+ int mariadb10_compat = 0;
+
+ while ((c = getopt_long(argc, argv, "dVfM?", long_options, &option_index)) >= 0)
+ {
+ switch (c) {
+ case 'd':
+ debug_out = 1;
+ break;
+ case 'V':
+ printVersion(*argv);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'f':
+ fix_file = 1;
+ break;
+ case 'M':
+ mariadb10_compat = 1;
+ break;
+ case '?':
+ printUsage(*argv);
+ exit(optopt ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+ }
+
+ num_args = optind;
+
+ arg_vector = malloc(sizeof(char*)*(arg_count + 1));
+
+ if(arg_vector == NULL)
+ {
+ fprintf(stderr,"Error: Memory allocation failed for log manager arg_vector.\n");
+ return 1;
+ }
+
+ arg_vector[0] = "logmanager";
+ arg_vector[1] = "-j";
+ arg_vector[2] = "/tmp/maxbinlogcheck";
+ arg_vector[3] = "-o";
+ arg_vector[4] = NULL;
+ skygw_logmanager_init(arg_count,arg_vector);
+
+ skygw_log_set_augmentation(0);
+
+ free(arg_vector);
+
+ if (!debug_out)
+ skygw_log_disable(LOGFILE_DEBUG);
+ else
+ skygw_log_enable(LOGFILE_DEBUG);
+
+ if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) {
+ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
+ "Error: Memory allocation failed for ROUTER_INSTANCE")));
+
+ skygw_log_sync_all();
+ skygw_logmanager_done();
+
+ return 1;
+ }
+
+ if (argv[num_args] == NULL) {
+ printf("ERROR: No binlog file was specified\n");
+ exit(EXIT_FAILURE);
+ }
+
+ strncpy(path, argv[num_args], PATH_MAX);
+
+ if (fix_file)
+ fd = open(path, O_RDWR, 0666);
+ else
+ fd = open(path, O_RDONLY, 0666);
+
+ if (fd == -1)
+ {
+ LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
+ "Failed to open binlog file %s: %s",
+ path, strerror(errno))));
+
+ skygw_log_sync_all();
+ skygw_logmanager_done();
+
+ free(inst);
+
+ return 1;
+ }
+
+ inst->binlog_fd = fd;
+
+ if (mariadb10_compat == 1)
+ inst->mariadb10_compat = 1;
+
+ ptr = strrchr(path, '/');
+ if (ptr)
+ strncpy(inst->binlog_name, ptr+1, BINLOG_FNAMELEN);
+ else
+ strncpy(inst->binlog_name, path, BINLOG_FNAMELEN);
+
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "maxbinlogcheck %s", binlog_check_version)));
+
+ if (fstat(inst->binlog_fd, &statb) == 0)
+ filelen = statb.st_size;
+
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Checking %s (%s), size %lu bytes", path, inst->binlog_name, filelen)));
+
+ /* read binary log */
+ ret = blr_read_events_all_events(inst, fix_file, debug_out);
+
+ close(inst->binlog_fd);
+
+ skygw_log_sync_all();
+
+ LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
+ "Check retcode: %i, Binlog Pos = %llu", ret, inst->binlog_position)));
+
+ skygw_log_sync_all();
+ skygw_logmanager_done();
+
+ free(inst);
+
+ return 0;
+}
+
+/**
+ * Print version information
+ */
+static void
+printVersion(const char *progname)
+{
+ printf("%s Version %s\n", progname, binlog_check_version);
+}
+
+/**
+ * Display the --help text.
+ */
+static void
+printUsage(const char *progname)
+{
+ printVersion(progname);
+
+ printf("The MaxScale binlog check utility.\n\n");
+ printf("Usage: %s [-f] [-d] [-v] []\n\n", progname);
+ printf(" -f|--fix Fix binlog file, require write permissions (truncate)\n");
+ printf(" -d|--debug Print debug messages\n");
+ printf(" -M|--mariadb10 MariaDB 10 binlog compatibility\n");
+ printf(" -V|--version print version information and exit\n");
+ printf(" -?|--help Print this help text\n");
+}
+