From 7d00a3670df9e6a9f1e18ccdf8c589d5e530a2c4 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 10 Sep 2015 14:37:49 +0200 Subject: [PATCH] Addition of maxbinlogcheck, a new utility in bin Addition of maxbinlogcheck, a new utility in bin --- Documentation/Reference/MaxBinlogCheck.md | 330 +++++++++ server/modules/include/blr.h | 8 +- server/modules/routing/binlog/CMakeLists.txt | 6 + server/modules/routing/binlog/blr_file.c | 695 ++++++++++++++++++ .../modules/routing/binlog/maxbinlogcheck.c | 249 +++++++ 5 files changed, 1287 insertions(+), 1 deletion(-) create mode 100644 Documentation/Reference/MaxBinlogCheck.md create mode 100644 server/modules/routing/binlog/maxbinlogcheck.c 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SwitchLong OptionDescription
-f--fixIf the option is set the binlog file will be truncated at last safe transaction pos in case of any error
-M--mariadb10Check the current binlog against MariaDB 10.0.x events
-d--debugSet the debug mode. If set the FD Events, Rotate events and opening/closing transactions are displayed.
-?--helpPrint usage information regarding maxbinlogcheck
-V--versionPrint 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"); +} +