From 4952dc48bb79f2e594d51e26cc32baf65df9e392 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 15 Jun 2015 15:03:27 +0200 Subject: [PATCH] Added constraints to CHANGE MASTER TO options Added constraints to CHANGE MASTER TO options MASTER_LOG_FILE and MASTER_LOG_POS --- server/modules/routing/binlog/blr_file.c | 22 ++ server/modules/routing/binlog/blr_slave.c | 235 ++++++++++++++++++---- 2 files changed, 221 insertions(+), 36 deletions(-) diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 50d75feff..926ac61a6 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -26,6 +26,7 @@ * Date Who Description * 14/04/2014 Mark Riddoch Initial implementation * 08/06/2015 Massimiliano Pinto Addition of blr_cache_read_master_data() + * 15/06/2015 Massimiliano Pinto Addition of blr_file_get_next_binlogname() * * @endverbatim */ @@ -61,6 +62,7 @@ static void blr_file_append(ROUTER_INSTANCE *router, char *file); static uint32_t extract_field(uint8_t *src, int bits); static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr); void blr_cache_read_master_data(ROUTER_INSTANCE *router); +int blr_file_get_next_binlogname(ROUTER_INSTANCE *router); /** * Initialise the binlog file for this instance. MaxScale will look @@ -778,3 +780,23 @@ blr_cache_read_master_data(ROUTER_INSTANCE *router) router->saved_master.selecthostname = blr_cache_read_response(router, "selecthostname"); router->saved_master.map = blr_cache_read_response(router, "map"); } + + +/** + * Get the next binlog file name. + * + * @param router The router instance + * @return 0 on error, >0 as sequence number + */ +int +blr_file_get_next_binlogname(ROUTER_INSTANCE *router) +{ +char *sptr, buf[80], bigbuf[4096]; +int filenum; + + if ((sptr = strrchr(router->binlog_name, '.')) == NULL) + return 0; + filenum = atoi(sptr+1) + 1; + + return filenum; +} diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 8cdf08fd8..bafed77bc 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -1,6 +1,4 @@ /* - * This file is distributed as part of MaxScale. It is free - * software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, * version 2. * @@ -42,6 +40,7 @@ * 05/06/2015 Massimiliano Pinto router->service->dbref->sever->name instead of master->remote * in blr_slave_send_slave_status() * 08/06/2015 Massimiliano Pinto blr_slave_send_slave_status() shows mysql_errno and error_msg + * 15/06/2015 Massimiliano Pinto Added constraints to CHANGE MASTER TO MASTER_LOG_FILE/POS * * @endverbatim */ @@ -62,8 +61,10 @@ #include #include #include +#include extern void blr_master_close(ROUTER_INSTANCE* router); +int blr_file_get_next_binlogname(ROUTER_INSTANCE *router); static uint32_t extract_field(uint8_t *src, int bits); static void encode_value(unsigned char *data, unsigned int value, int len); static int blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue); @@ -94,7 +95,7 @@ static int blr_stop_slave(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave); static int blr_start_slave(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave); static void blr_slave_send_error_packet(ROUTER_SLAVE *slave, char *msg, unsigned int err_num, char *status); static char *get_change_master_option(char *input, char *option); -static void blr_handle_change_master(ROUTER_INSTANCE* router, char *command); +static int blr_handle_change_master(ROUTER_INSTANCE* router, char *command); extern int lm_enabled_logfiles_bitmask; @@ -476,11 +477,17 @@ int query_len; } else { - blr_handle_change_master(router, brkb); + int rc; + + rc = blr_handle_change_master(router, brkb); free(query_text); - return blr_slave_send_ok(router, slave); + if (rc) { + blr_slave_send_error_packet(slave, "Incorrect arguments to CHANGE MASTER TO. Check error log", (unsigned int)1210, NULL); + return 1; + } else + return blr_slave_send_ok(router, slave); } } } @@ -2390,32 +2397,40 @@ char *get_change_master_option(char *input, char *option_field) { * * @param router The router instance * @param command The change master SQL command + * @return 0 on success, != 0 on failure */ static -void blr_handle_change_master(ROUTER_INSTANCE* router, char *command) { +int blr_handle_change_master(ROUTER_INSTANCE* router, char *command) { char *master_host=NULL; char *master_port = NULL; char *master_logfile = NULL; char *master_log_pos = NULL; char *master_user = NULL; char *master_password = NULL; + int change_binlog = 0; + char *new_binlog_file = NULL; + char *prev_host; + int prev_port; + char *prev_binlog_file; + unsigned long long prev_pos; + char *prev_user; + char *prev_passwd; - /* fetch options from SQL command */ + /* save current replication parameters */ + prev_port = router->service->dbref->server->port; + prev_host = strdup(router->service->dbref->server->name); + prev_pos = router->binlog_position; + prev_binlog_file = strdup(router->binlog_name); + prev_user = strdup(router->user); + prev_passwd = strdup(router->password); + + /* fetch new options from SQL command */ master_host = get_change_master_option(command, "MASTER_HOST"); master_port = get_change_master_option(command, "MASTER_PORT"); master_logfile = get_change_master_option(command, "MASTER_LOG_FILE"); master_log_pos = get_change_master_option(command, "MASTER_LOG_POS"); master_user = get_change_master_option(command, "MASTER_USER"); master_password = get_change_master_option(command, "MASTER_PASSWORD"); - - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "%s: CHANGE MASTER: MASTER_HOST=[%s], MASTER_PORT=[%s], MASTER_LOG_FILE=[%s], MASTER_LOG_POS=[%s], MASTER_USER=[%s], MASTER_PASSWORD=[%s]", - router->service->name, - master_host != NULL ? (master_host + 12) : "null", - master_port != NULL ? (master_port + 12) : "null", - master_logfile != NULL ? (master_logfile + 16) : "null", - master_log_pos != NULL ? (master_log_pos + 15) : "null", - master_user != NULL ? (master_user + 12) : "null", - master_password != NULL ? (master_password+ 16) : "null"))); /* * Change values in the router->service->dbref->server structure @@ -2455,10 +2470,37 @@ void blr_handle_change_master(ROUTER_INSTANCE* router, char *command) { free(master_port); } - /* Change the binlog filename to request from master */ + /** + * Change the binlog filename to request from master + * New binlog file should only be the next one + */ if (master_logfile) { char *ptr; char *end; + int next_binlog_seqname; + + next_binlog_seqname = blr_file_get_next_binlogname(router); + if (!next_binlog_seqname) { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "%s: cannot get the next MASTER_LOG_FILE name from current binlog [%s]: return an error", + router->service->name, + router->binlog_name))); + + free(master_logfile); + + /* restore previous master_host and master_port */ + server_update_address(router->service->dbref->server, prev_host); + server_update_port(router->service->dbref->server, prev_port); + + free(prev_host); + free(prev_binlog_file); + free(prev_user); + free(prev_passwd); + + spinlock_release(&router->lock); + + return 1; + } + ptr = strchr(master_logfile, '\''); if (ptr) ptr++; @@ -2469,28 +2511,131 @@ void blr_handle_change_master(ROUTER_INSTANCE* router, char *command) { if (end) *end ='\0'; - memset(router->binlog_name, '\0', sizeof(router->binlog_name)); - strncpy(router->binlog_name, ptr, BINLOG_FNAMELEN); - - LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s: New MASTER_LOG_FILE is [%s]", - router->service->name, - router->binlog_name))); - - free(master_logfile); - } - - /* Change the position in the current binlog filename */ - if (master_log_pos) { - long pos = atol(master_log_pos + 15); - if (pos <= 0) { - LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "%s: cannot set MASTER_LOG_POS to [%u] for binlog [%s]. Set to 4", + end = strchr(ptr, '.'); + if (!end) { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "%s: current binlog name [%s] has not the format '%s.yyyyyy'", router->service->name, - pos, + router->fileroot, router->binlog_name))); - router->binlog_position = 4; + free(master_logfile); + + /* restore previous master_host and master_port */ + server_update_address(router->service->dbref->server, prev_host); + server_update_port(router->service->dbref->server, prev_port); + + free(prev_host); + free(prev_binlog_file); + free(prev_user); + free(prev_passwd); + + spinlock_release(&router->lock); + + return 2; + } + + end++; + + if (atoi(end) != next_binlog_seqname) { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "%s: cannot set MASTER_LOG_FILE to %s: only next one %s.%06d is allowed", + router->service->name, + ptr, + router->fileroot, + next_binlog_seqname))); + + free(master_logfile); + + /* restore previous master_host and master_port */ + server_update_address(router->service->dbref->server, prev_host); + server_update_port(router->service->dbref->server, prev_port); + + free(prev_host); + free(prev_binlog_file); + free(prev_user); + free(prev_passwd); + + spinlock_release(&router->lock); + + return 3; + } + + /* save new filename pointer and don't free master_logfile pointer !*/ + new_binlog_file = ptr; + + /* Binlog file name succesfully changed */ + change_binlog = 1; + } + + /* Change the position in the current or new binlog filename */ + if (master_log_pos) { + char *passed_pos = master_log_pos + 15; + long long pos = atoll(passed_pos); + + /* if binlog name has changed to next one only position 4 is allowed */ + if (change_binlog) { + if (pos != 4) { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "%s: cannot set MASTER_LOG_POS to %s for new binlog file [%s]", + router->service->name, + passed_pos, + new_binlog_file))); + + if (master_logfile) + free(master_logfile); + + /* restore previous master_host and master_port */ + server_update_address(router->service->dbref->server, prev_host); + server_update_port(router->service->dbref->server, prev_port); + + free(prev_host); + free(prev_binlog_file); + free(prev_user); + free(prev_passwd); + + spinlock_release(&router->lock); + + return 4; + + } else { + /* set new filename and pos */ + memset(router->binlog_name, '\0', sizeof(router->binlog_name)); + strncpy(router->binlog_name, new_binlog_file, BINLOG_FNAMELEN); + + router->binlog_position = 4; + + free(master_logfile); + + LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s: New MASTER_LOG_FILE is [%s]", + router->service->name, + router->binlog_name))); + } } else { - router->binlog_position = atol(master_log_pos + 15); + if (master_logfile) + free(master_logfile); + + /* Position cannot be different from current pos */ + if (pos != router->binlog_position) { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "%s: cannot set MASTER_LOG_POS to %s for current binlog file [%s]", + router->service->name, + passed_pos, + router->binlog_name))); + + /* restore previous master_host and master_port */ + server_update_address(router->service->dbref->server, prev_host); + server_update_port(router->service->dbref->server, prev_port); + + free(prev_host); + free(prev_binlog_file); + free(prev_user); + free(prev_passwd); + + spinlock_release(&router->lock); + + return 5; + + } else { + /* set new position */ + router->binlog_position = pos; + } } LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s: New MASTER_LOG_POS is [%u]", @@ -2549,5 +2694,23 @@ void blr_handle_change_master(ROUTER_INSTANCE* router, char *command) { free(master_password); } - spinlock_release(&router->lock); + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "%s: 'CHANGE MASTER TO executed'. Previous state MASTER_HOST='%s', MASTER_PORT=%i, MASTER_LOG_FILE='%s', MASTER_LOG_POS=%lu, MASTER_USER='%s', MASTER_PASSWORD='%s'. New state is MASTER_HOST='%s', MASTER_PORT=%i, MASTER_LOG_FILE='%s', MASTER_LOG_POS=%lu, MASTER_USER='%s', MASTER_PASSWORD='%s'", + router->service->name, + prev_host,prev_port,prev_binlog_file, prev_pos, prev_user, prev_passwd, + router->service->dbref->server->name, + router->service->dbref->server->port, + router->binlog_name, + router->binlog_position, + router->user, + router->password))); + + + free(prev_host); + free(prev_binlog_file); + free(prev_user); + free(prev_passwd); + + spinlock_release(&router->lock); + + return 0; }