340 lines
9.8 KiB
C
340 lines
9.8 KiB
C
/*
|
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
|
*
|
|
* Use of this software is governed by the Business Source License included
|
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
*
|
|
* Change Date: 2020-01-01
|
|
*
|
|
* On the date above, in accordance with the Business Source License, use
|
|
* of this software will be governed by version 2 or later of the General
|
|
* Public License.
|
|
*/
|
|
|
|
/**
|
|
* @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
|
|
* 26/04/2016 Massimiliano Pinto MariaDB 10.1 GTID flags are properly parsed
|
|
* 23/09/2016 Massimiliano Pinto MariaDB 10.1 encrypted binlog compatible:
|
|
* the output shows the START_ENCRYPTION_EVENT and follows
|
|
* binlog positions without dectypting events.
|
|
* 25/11/2016 Massimiliano Pinto MariaDB 10.1 encrypted files can be checked
|
|
* with Key and Algo options
|
|
* 06/12/2016 Massimiliano Pinto A new option allows displaying of replication header
|
|
*
|
|
*
|
|
* @endverbatim
|
|
*/
|
|
|
|
#include "blr.h"
|
|
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <maxscale/alloc.h>
|
|
#include <maxscale/log_manager.h>
|
|
|
|
|
|
static void printVersion(const char *progname);
|
|
static void printUsage(const char *progname);
|
|
static int set_encryption_options(ROUTER_INSTANCE *inst, char *key_file, char *aes_algo);
|
|
|
|
#ifdef HAVE_GLIBC
|
|
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'},
|
|
{"header", no_argument, 0, 'H'},
|
|
{"key_file", required_argument, 0, 'K'},
|
|
{"aes_algo", required_argument, 0, 'A'},
|
|
{"replace-event", required_argument, 0, 'R'},
|
|
{"remove-trx", required_argument, 0, 'T'},
|
|
{"help", no_argument, 0, '?'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
#endif
|
|
char *binlog_check_version = "2.2.1";
|
|
|
|
int
|
|
maxscale_uptime()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int option_index = 0;
|
|
int debug_out = 0;
|
|
int mariadb10_compat = 0;
|
|
char *key_file = NULL;
|
|
char *aes_algo = NULL;
|
|
int report_header = 0;
|
|
int c;
|
|
BINLOG_FILE_FIX binlog_file = {0, false, false};
|
|
|
|
#ifdef HAVE_GLIBC
|
|
while ((c = getopt_long(argc, argv, "dVfMHK:A:R:T:?", long_options, &option_index)) >= 0)
|
|
#else
|
|
while ((c = getopt(argc, argv, "dVfMHK:A:R:T:?")) >= 0)
|
|
#endif
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'd':
|
|
debug_out = 1;
|
|
break;
|
|
case 'H':
|
|
report_header = BLR_REPORT_REP_HEADER;
|
|
break;
|
|
case 'V':
|
|
printVersion(*argv);
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
case 'f':
|
|
binlog_file.fix = true;
|
|
break;
|
|
case 'M':
|
|
mariadb10_compat = 1;
|
|
break;
|
|
case 'K':
|
|
key_file = optarg;
|
|
break;
|
|
case 'A':
|
|
aes_algo = optarg;
|
|
break;
|
|
case 'R':
|
|
case 'T':
|
|
binlog_file.pos = atol(optarg);
|
|
binlog_file.replace_trx = (c == 'T') ? true : false;
|
|
break;
|
|
case '?':
|
|
printUsage(*argv);
|
|
exit(optopt ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
int num_args = optind;
|
|
|
|
if (argv[num_args] == NULL)
|
|
{
|
|
printf("ERROR: No binlog file was specified.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
size_t len = strlen(argv[num_args]);
|
|
if (len > PATH_MAX)
|
|
{
|
|
printf("ERROR: The length of the provided path exceeds %d characters.\n", PATH_MAX);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
char path[PATH_MAX + 1];
|
|
strcpy(path, argv[num_args]);
|
|
|
|
char *name = strrchr(path, '/');
|
|
if (name)
|
|
{
|
|
++name;
|
|
len = strlen(name);
|
|
}
|
|
else
|
|
{
|
|
name = path;
|
|
}
|
|
|
|
if ((len == 0) || (len > BINLOG_FNAMELEN))
|
|
{
|
|
printf("ERROR: The length of the binlog filename is 0 or exceeds %d characters.\n",
|
|
BINLOG_FNAMELEN);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE*)MXS_CALLOC(1, sizeof(ROUTER_INSTANCE));
|
|
if (!inst)
|
|
{
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int fd = open(path, binlog_file.fix ? O_RDWR : O_RDONLY, 0666);
|
|
if (fd == -1)
|
|
{
|
|
printf("ERROR: Failed to open binlog file %s: %s.\n",
|
|
path, strerror(errno));
|
|
MXS_FREE(inst);
|
|
mxs_log_flush_sync();
|
|
mxs_log_finish();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
inst->binlog_fd = fd;
|
|
inst->mariadb10_compat = mariadb10_compat;
|
|
strcpy(inst->binlog_name, name);
|
|
|
|
// We ignore potential errors.
|
|
mxs_log_init(NULL, NULL, MXS_LOG_TARGET_DEFAULT);
|
|
mxs_log_set_augmentation(0);
|
|
mxs_log_set_priority_enabled(LOG_DEBUG, debug_out);
|
|
|
|
MXS_NOTICE("maxbinlogcheck %s", binlog_check_version);
|
|
|
|
unsigned long filelen = 0;
|
|
struct stat statb;
|
|
if (fstat(inst->binlog_fd, &statb) == 0)
|
|
{
|
|
filelen = statb.st_size;
|
|
}
|
|
|
|
/* If encryption options are in use check and use them */
|
|
if (set_encryption_options(inst, key_file, aes_algo))
|
|
{
|
|
MXS_FREE(inst);
|
|
mxs_log_flush_sync();
|
|
mxs_log_finish();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
MXS_NOTICE("Checking %s (%s), size %lu bytes", path, inst->binlog_name, filelen);
|
|
|
|
/* Look first for a transaction that has an event at pos binlog_file.pos */
|
|
if (binlog_file.fix && binlog_file.pos && binlog_file.replace_trx)
|
|
{
|
|
/* Don't modify anything */
|
|
binlog_file.fix = false;
|
|
|
|
/* The routine call overwrites binlog_file.pos with transaction BEGIN pos */
|
|
blr_read_events_all_events(inst, &binlog_file, BLR_CHECK_ONLY);
|
|
|
|
binlog_file.fix = true;
|
|
|
|
mxs_log_flush_sync();
|
|
}
|
|
|
|
/* Now read/check/fix the binary log */
|
|
int ret = blr_read_events_all_events(inst, &binlog_file, debug_out | report_header);
|
|
|
|
mxs_log_flush_sync();
|
|
|
|
MXS_NOTICE("Check retcode: %i, Binlog Pos = %lu", ret, inst->binlog_position);
|
|
|
|
close(inst->binlog_fd);
|
|
MXS_FREE(inst);
|
|
|
|
mxs_log_flush_sync();
|
|
mxs_log_finish();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 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] [-M] [-d] [-V] [-H] [-K file] [-A algo] [-R pos] [-T pos] [<binlog file>]\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(" -K|--key_file AES Key file for MariaDB 10.1 binlog file decryption\n");
|
|
printf(" -A|--aes_algo AES Algorithm for MariaDB 10.1 binlog file decryption (default=AES_CBC, AES_CTR)\n");
|
|
printf(" -H|--header Print content of binlog event header\n");
|
|
printf(" -R|--replace-event Replace the event at pos with an IGNORABLE event\n");
|
|
printf(" -T|--remove-trx Replace all events in the transaction the specified pos belongs to, with IGNORABLE events\n");
|
|
printf(" -?|--help Print this help text\n");
|
|
}
|
|
|
|
/**
|
|
* Check and set the encryption options
|
|
*
|
|
* @param inst The current binlog instance
|
|
* @param key_file The AES Key filename
|
|
* @param aes_algo The AES algorithm
|
|
* @return 1 on failure, 0 on success
|
|
*/
|
|
static int set_encryption_options(ROUTER_INSTANCE *inst, char *key_file, char *aes_algo)
|
|
{
|
|
if (aes_algo && !key_file)
|
|
{
|
|
MXS_ERROR("AES algorithm set but no KEY file specified, exiting.");
|
|
return 1;
|
|
}
|
|
|
|
/* Get the encryption KEY */
|
|
if (key_file)
|
|
{
|
|
inst->encryption.key_management_filename = key_file;
|
|
if (!blr_get_encryption_key(inst))
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* Check aes algorithm */
|
|
if (aes_algo)
|
|
{
|
|
int ret = blr_check_encryption_algorithm(aes_algo);
|
|
if (ret > -1)
|
|
{
|
|
inst->encryption.encryption_algorithm = ret;
|
|
}
|
|
else
|
|
{
|
|
MXS_ERROR("Invalid encryption_algorithm '%s'. "
|
|
"Supported algorithms: %s",
|
|
aes_algo,
|
|
blr_encryption_algorithm_list());
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
inst->encryption.encryption_algorithm = BINLOG_DEFAULT_ENC_ALGO;
|
|
}
|
|
|
|
MXS_NOTICE("Decrypting binlog file with algorithm: %s,"
|
|
" KEY len %lu bits",
|
|
blr_get_encryption_algorithm(inst->encryption.encryption_algorithm),
|
|
8 * inst->encryption.key_len);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|