diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index c9a22fd0c..9ec146d4f 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -24,17 +24,17 @@ * @verbatim * Revision History * - * Date Who Description - * 02/04/14 Mark Riddoch Initial implementation - * 25/05/15 Massimiliano Pinto Added BLRM_SLAVE_STOPPED state - * 05/06/15 Massimiliano Pinto Addition of m_errno, m_errmsg fields - * 08/06/15 Massimiliano Pinto Modification of MYSQL_ERROR_CODE and MYSQL_ERROR_MSG - * 11/05/15 Massimiliano Pinto Added mariadb10_compat to master and slave structs - * 12/06/15 Massimiliano Pinto Added mariadb10 new events - * 23/06/15 Massimiliano Pinto Addition of MASTER_SERVER_CFG struct - * 24/06/15 Massimiliano Pinto Added BLRM_UNCONFIGURED state - * 05/08/15 Massimiliano Pinto Initial implementation of transaction safety - * 23/10/15 Markus Makela Added current_safe_event + * Date Who Description + * 02/04/14 Mark Riddoch Initial implementation + * 25/05/15 Massimiliano Pinto Added BLRM_SLAVE_STOPPED state + * 05/06/15 Massimiliano Pinto Addition of m_errno, m_errmsg fields + * 08/06/15 Massimiliano Pinto Modification of MYSQL_ERROR_CODE and MYSQL_ERROR_MSG + * 11/05/15 Massimiliano Pinto Added mariadb10_compat to master and slave structs + * 12/06/15 Massimiliano Pinto Added mariadb10 new events + * 23/06/15 Massimiliano Pinto Addition of MASTER_SERVER_CFG struct + * 24/06/15 Massimiliano Pinto Added BLRM_UNCONFIGURED state + * 05/08/15 Massimiliano Pinto Initial implementation of transaction safety + * 23/10/15 Markus Makela Added current_safe_event * * @endverbatim */ @@ -46,158 +46,158 @@ #include #include -#define BINLOG_FNAMELEN 255 -#define BLR_PROTOCOL "MySQLBackend" -#define BINLOG_MAGIC { 0xfe, 0x62, 0x69, 0x6e } -#define BINLOG_MAGIC_SIZE 4 -#define BINLOG_NAMEFMT "%s.%06d" -#define BINLOG_NAME_ROOT "mysql-bin" +#define BINLOG_FNAMELEN 255 +#define BLR_PROTOCOL "MySQLBackend" +#define BINLOG_MAGIC { 0xfe, 0x62, 0x69, 0x6e } +#define BINLOG_MAGIC_SIZE 4 +#define BINLOG_NAMEFMT "%s.%06d" +#define BINLOG_NAME_ROOT "mysql-bin" -#define BINLOG_EVENT_HDR_LEN 19 +#define BINLOG_EVENT_HDR_LEN 19 /** * Binlog event types */ -#define START_EVENT_V3 0x01 -#define QUERY_EVENT 0x02 -#define STOP_EVENT 0x03 -#define ROTATE_EVENT 0x04 -#define INTVAR_EVENT 0x05 -#define LOAD_EVENT 0x06 -#define SLAVE_EVENT 0x07 -#define CREATE_FILE_EVENT 0x08 -#define APPEND_BLOCK_EVENT 0x09 -#define EXEC_LOAD_EVENT 0x0A -#define DELETE_FILE_EVENT 0x0B -#define NEW_LOAD_EVENT 0x0C -#define RAND_EVENT 0x0D -#define USER_VAR_EVENT 0x0E -#define FORMAT_DESCRIPTION_EVENT 0x0F -#define XID_EVENT 0x10 -#define BEGIN_LOAD_QUERY_EVENT 0x11 -#define EXECUTE_LOAD_QUERY_EVENT 0x12 -#define TABLE_MAP_EVENT 0x13 -#define WRITE_ROWS_EVENTv0 0x14 -#define UPDATE_ROWS_EVENTv0 0x15 -#define DELETE_ROWS_EVENTv0 0x16 -#define WRITE_ROWS_EVENTv1 0x17 -#define UPDATE_ROWS_EVENTv1 0x18 -#define DELETE_ROWS_EVENTv1 0x19 -#define INCIDENT_EVENT 0x1A -#define HEARTBEAT_EVENT 0x1B -#define IGNORABLE_EVENT 0x1C -#define ROWS_QUERY_EVENT 0x1D -#define WRITE_ROWS_EVENTv2 0x1E -#define UPDATE_ROWS_EVENTv2 0x1F -#define DELETE_ROWS_EVENTv2 0x20 -#define GTID_EVENT 0x21 -#define ANONYMOUS_GTID_EVENT 0x22 -#define PREVIOUS_GTIDS_EVENT 0x23 +#define START_EVENT_V3 0x01 +#define QUERY_EVENT 0x02 +#define STOP_EVENT 0x03 +#define ROTATE_EVENT 0x04 +#define INTVAR_EVENT 0x05 +#define LOAD_EVENT 0x06 +#define SLAVE_EVENT 0x07 +#define CREATE_FILE_EVENT 0x08 +#define APPEND_BLOCK_EVENT 0x09 +#define EXEC_LOAD_EVENT 0x0A +#define DELETE_FILE_EVENT 0x0B +#define NEW_LOAD_EVENT 0x0C +#define RAND_EVENT 0x0D +#define USER_VAR_EVENT 0x0E +#define FORMAT_DESCRIPTION_EVENT 0x0F +#define XID_EVENT 0x10 +#define BEGIN_LOAD_QUERY_EVENT 0x11 +#define EXECUTE_LOAD_QUERY_EVENT 0x12 +#define TABLE_MAP_EVENT 0x13 +#define WRITE_ROWS_EVENTv0 0x14 +#define UPDATE_ROWS_EVENTv0 0x15 +#define DELETE_ROWS_EVENTv0 0x16 +#define WRITE_ROWS_EVENTv1 0x17 +#define UPDATE_ROWS_EVENTv1 0x18 +#define DELETE_ROWS_EVENTv1 0x19 +#define INCIDENT_EVENT 0x1A +#define HEARTBEAT_EVENT 0x1B +#define IGNORABLE_EVENT 0x1C +#define ROWS_QUERY_EVENT 0x1D +#define WRITE_ROWS_EVENTv2 0x1E +#define UPDATE_ROWS_EVENTv2 0x1F +#define DELETE_ROWS_EVENTv2 0x20 +#define GTID_EVENT 0x21 +#define ANONYMOUS_GTID_EVENT 0x22 +#define PREVIOUS_GTIDS_EVENT 0x23 -#define MAX_EVENT_TYPE 0x23 +#define MAX_EVENT_TYPE 0x23 /* New MariaDB event numbers start from 0xa0 */ -#define MARIADB_NEW_EVENTS_BEGIN 0xa0 -#define MARIADB_ANNOTATE_ROWS_EVENT 0xa0 +#define MARIADB_NEW_EVENTS_BEGIN 0xa0 +#define MARIADB_ANNOTATE_ROWS_EVENT 0xa0 /* New MariaDB 10 event numbers start from here */ -#define MARIADB10_BINLOG_CHECKPOINT_EVENT 0xa1 -#define MARIADB10_GTID_EVENT 0xa2 -#define MARIADB10_GTID_GTID_LIST_EVENT 0xa3 +#define MARIADB10_BINLOG_CHECKPOINT_EVENT 0xa1 +#define MARIADB10_GTID_EVENT 0xa2 +#define MARIADB10_GTID_GTID_LIST_EVENT 0xa3 -#define MAX_EVENT_TYPE_MARIADB10 0xa3 +#define MAX_EVENT_TYPE_MARIADB10 0xa3 /* Maximum event type so far */ -#define MAX_EVENT_TYPE_END MAX_EVENT_TYPE_MARIADB10 +#define MAX_EVENT_TYPE_END MAX_EVENT_TYPE_MARIADB10 /** * Binlog event flags */ -#define LOG_EVENT_BINLOG_IN_USE_F 0x0001 -#define LOG_EVENT_FORCED_ROTATE_F 0x0002 -#define LOG_EVENT_THREAD_SPECIFIC_F 0x0004 -#define LOG_EVENT_SUPPRESS_USE_F 0x0008 -#define LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F 0x0010 -#define LOG_EVENT_ARTIFICIAL_F 0x0020 -#define LOG_EVENT_RELAY_LOG_F 0x0040 -#define LOG_EVENT_IGNORABLE_F 0x0080 -#define LOG_EVENT_NO_FILTER_F 0x0100 -#define LOG_EVENT_MTS_ISOLATE_F 0x0200 +#define LOG_EVENT_BINLOG_IN_USE_F 0x0001 +#define LOG_EVENT_FORCED_ROTATE_F 0x0002 +#define LOG_EVENT_THREAD_SPECIFIC_F 0x0004 +#define LOG_EVENT_SUPPRESS_USE_F 0x0008 +#define LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F 0x0010 +#define LOG_EVENT_ARTIFICIAL_F 0x0020 +#define LOG_EVENT_RELAY_LOG_F 0x0040 +#define LOG_EVENT_IGNORABLE_F 0x0080 +#define LOG_EVENT_NO_FILTER_F 0x0100 +#define LOG_EVENT_MTS_ISOLATE_F 0x0200 /** * How often to call the binlog status function (seconds) */ -#define BLR_STATS_FREQ 60 -#define BLR_NSTATS_MINUTES 30 +#define BLR_STATS_FREQ 60 +#define BLR_NSTATS_MINUTES 30 /** * High and Low water marks for the slave dcb. These values can be overriden * by the router options highwater and lowwater. */ -#define DEF_LOW_WATER 1000 -#define DEF_HIGH_WATER 10000 +#define DEF_LOW_WATER 1000 +#define DEF_HIGH_WATER 10000 /** * Default burst sizes for slave catchup */ -#define DEF_SHORT_BURST 15 -#define DEF_LONG_BURST 500 -#define DEF_BURST_SIZE 1024000 /* 1 Mb */ +#define DEF_SHORT_BURST 15 +#define DEF_LONG_BURST 500 +#define DEF_BURST_SIZE 1024000 /* 1 Mb */ /** * master reconnect backoff constants - * BLR_MASTER_BACKOFF_TIME The increments of the back off time (seconds) - * BLR_MAX_BACKOFF Maximum number of increments to backoff to + * BLR_MASTER_BACKOFF_TIME The increments of the back off time (seconds) + * BLR_MAX_BACKOFF Maximum number of increments to backoff to */ -#define BLR_MASTER_BACKOFF_TIME 10 -#define BLR_MAX_BACKOFF 60 +#define BLR_MASTER_BACKOFF_TIME 10 +#define BLR_MAX_BACKOFF 60 /* max size for error message returned to client */ -#define BINLOG_ERROR_MSG_LEN 385 +#define BINLOG_ERROR_MSG_LEN 385 /* network latency extra wait tme for heartbeat check */ -#define BLR_NET_LATENCY_WAIT_TIME 1 +#define BLR_NET_LATENCY_WAIT_TIME 1 /* default heartbeat interval in seconds */ -#define BLR_HEARTBEAT_DEFAULT_INTERVAL 300 +#define BLR_HEARTBEAT_DEFAULT_INTERVAL 300 /* strings and numbers in SQL replies */ -#define BLR_TYPE_STRING 0xf -#define BLR_TYPE_INT 0x03 +#define BLR_TYPE_STRING 0xf +#define BLR_TYPE_INT 0x03 /* string len for COM_STATISTICS output */ -#define BLRM_COM_STATISTICS_SIZE 1000 +#define BLRM_COM_STATISTICS_SIZE 1000 /* string len for strerror_r message */ -#define BLRM_STRERROR_R_MSG_SIZE 128 +#define BLRM_STRERROR_R_MSG_SIZE 128 /* string len for task message name */ -#define BLRM_TASK_NAME_LEN 80 +#define BLRM_TASK_NAME_LEN 80 /* string len for temp binlog filename */ -#define BLRM_BINLOG_NAME_STR_LEN 80 +#define BLRM_BINLOG_NAME_STR_LEN 80 /* string len for temp binlog filename */ -#define BLRM_SET_HEARTBEAT_QUERY_LEN 80 +#define BLRM_SET_HEARTBEAT_QUERY_LEN 80 /* string len for master registration query */ -#define BLRM_MASTER_REGITRATION_QUERY_LEN 255 +#define BLRM_MASTER_REGITRATION_QUERY_LEN 255 /* Read Binlog position states */ -#define SLAVE_POS_READ_OK 0x00 -#define SLAVE_POS_READ_ERR 0xff -#define SLAVE_POS_READ_UNSAFE 0xfe -#define SLAVE_POS_BAD_FD 0xfd -#define SLAVE_POS_BEYOND_EOF 0xfc +#define SLAVE_POS_READ_OK 0x00 +#define SLAVE_POS_READ_ERR 0xff +#define SLAVE_POS_READ_UNSAFE 0xfe +#define SLAVE_POS_BAD_FD 0xfd +#define SLAVE_POS_BEYOND_EOF 0xfc /** * Some useful macros for examining the MySQL Response packets */ -#define MYSQL_RESPONSE_OK(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4) == 0x00) -#define MYSQL_RESPONSE_EOF(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4) == 0xfe) -#define MYSQL_RESPONSE_ERR(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4) == 0xff) -#define MYSQL_ERROR_CODE(buf) ((uint8_t *)GWBUF_DATA(buf) + 5) -#define MYSQL_ERROR_MSG(buf) ((uint8_t *)GWBUF_DATA(buf) + 7) -#define MYSQL_COMMAND(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4)) +#define MYSQL_RESPONSE_OK(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4) == 0x00) +#define MYSQL_RESPONSE_EOF(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4) == 0xfe) +#define MYSQL_RESPONSE_ERR(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4) == 0xff) +#define MYSQL_ERROR_CODE(buf) ((uint8_t *)GWBUF_DATA(buf) + 5) +#define MYSQL_ERROR_MSG(buf) ((uint8_t *)GWBUF_DATA(buf) + 7) +#define MYSQL_COMMAND(buf) (*((uint8_t *)GWBUF_DATA(buf) + 4)) /** Possible states of an event sent by the master */ enum blr_event_state @@ -211,139 +211,164 @@ enum blr_event_state }; /* Master Server configuration struct */ -typedef struct master_server_config { - char *host; - unsigned short port; - char logfile[BINLOG_FNAMELEN+1]; - uint64_t pos; - uint64_t safe_pos; - char *user; - char *password; - char *filestem; +typedef struct master_server_config +{ + char *host; + unsigned short port; + char logfile[BINLOG_FNAMELEN+1]; + uint64_t pos; + uint64_t safe_pos; + char *user; + char *password; + char *filestem; } MASTER_SERVER_CFG; /* Config struct for CHANGE MASTER TO options */ -typedef struct change_master_options { - char *host; - char *port; - char *binlog_file; - char *binlog_pos; - char *user; - char *password; +typedef struct change_master_options +{ + char *host; + char *port; + char *binlog_file; + char *binlog_pos; + char *user; + char *password; } CHANGE_MASTER_OPTIONS; /** * Packet header for replication messages */ -typedef struct rep_header { - int payload_len; /*< Payload length (24 bits) */ - uint8_t seqno; /*< Response sequence number */ - uint8_t ok; /*< OK Byte from packet */ - uint32_t timestamp; /*< Timestamp - start of binlog record */ - uint8_t event_type; /*< Binlog event type */ - uint32_t serverid; /*< Server id of master */ - uint32_t event_size; /*< Size of header, post-header and body */ - uint32_t next_pos; /*< Position of next event */ - uint16_t flags; /*< Event flags */ +typedef struct rep_header +{ + int payload_len; /*< Payload length (24 bits) */ + uint8_t seqno; /*< Response sequence number */ + uint8_t ok; /*< OK Byte from packet */ + uint32_t timestamp; /*< Timestamp - start of binlog record */ + uint8_t event_type; /*< Binlog event type */ + uint32_t serverid; /*< Server id of master */ + uint32_t event_size; /*< Size of header, post-header and body */ + uint32_t next_pos; /*< Position of next event */ + uint16_t flags; /*< Event flags */ } REP_HEADER; /** * The binlog record structure. This contains the actual packet read from the binlog * file. */ -typedef struct { - unsigned long position; /*< binlog record position for this cache entry */ - GWBUF *pkt; /*< The packet received from the master */ - REP_HEADER hdr; /*< The packet header */ +typedef struct +{ + unsigned long position; /*< binlog record position for this cache entry */ + GWBUF *pkt; /*< The packet received from the master */ + REP_HEADER hdr; /*< The packet header */ } BLCACHE_RECORD; /** * The binlog cache. A cache exists for each file that hold cached bin log records. * Caches will be used for all files being read by more than 1 slave. */ -typedef struct { - BLCACHE_RECORD **records; /*< The actual binlog records */ - int current; /*< The next record that will be inserted */ - int cnt; /*< The number of records in the cache */ - SPINLOCK lock; /*< The spinlock for the cache */ +typedef struct +{ + BLCACHE_RECORD **records; /*< The actual binlog records */ + int current; /*< The next record that will be inserted */ + int cnt; /*< The number of records in the cache */ + SPINLOCK lock; /*< The spinlock for the cache */ } BLCACHE; -typedef struct blfile { - char binlogname[BINLOG_FNAMELEN+1]; /*< Name of the binlog file */ - int fd; /*< Actual file descriptor */ - int refcnt; /*< Reference count for file */ - BLCACHE *cache; /*< Record cache for this file */ - SPINLOCK lock; /*< The file lock */ - struct blfile *next; /*< Next file in list */ +typedef struct blfile +{ + char binlogname[BINLOG_FNAMELEN+1]; /*< Name of the binlog file */ + int fd; /*< Actual file descriptor */ + int refcnt; /*< Reference count for file */ + BLCACHE *cache; /*< Record cache for this file */ + SPINLOCK lock; /*< The file lock */ + struct blfile *next; /*< Next file in list */ } BLFILE; /** * Slave statistics */ -typedef struct { - int n_events; /*< Number of events sent */ - unsigned long n_bytes; /*< Number of bytes sent */ - int n_bursts; /*< Number of bursts sent */ - int n_requests; /*< Number of requests received */ - int n_flows; /*< Number of flow control restarts */ - int n_queries; /*< Number of SQL queries */ - int n_upd; - int n_cb; - int n_cbna; - int n_dcb; - int n_above; - int n_failed_read; - int n_overrun; - int n_caughtup; - int n_actions[3]; - uint64_t lastsample; - int minno; - int minavgs[BLR_NSTATS_MINUTES]; +typedef struct +{ + int n_events; /*< Number of events sent */ + unsigned long n_bytes; /*< Number of bytes sent */ + int n_bursts; /*< Number of bursts sent */ + int n_requests; /*< Number of requests received */ + int n_flows; /*< Number of flow control restarts */ + int n_queries; /*< Number of SQL queries */ + int n_upd; + int n_cb; + int n_cbna; + int n_dcb; + int n_above; + int n_failed_read; + int n_overrun; + int n_caughtup; + int n_actions[3]; + uint64_t lastsample; + int minno; + int minavgs[BLR_NSTATS_MINUTES]; } SLAVE_STATS; +typedef enum blr_thread_role +{ + BLR_THREAD_ROLE_MASTER_LARGE_NOTRX, + BLR_THREAD_ROLE_MASTER_NOTRX, + BLR_THREAD_ROLE_MASTER_TRX, + BLR_THREAD_ROLE_SLAVE +} blr_thread_role_t; + +#define ROLETOSTR(r) r == BLR_THREAD_ROLE_MASTER_LARGE_NOTRX ? "master (large event, no trx)" : \ +r == BLR_THREAD_ROLE_MASTER_NOTRX ? "master (no trx)" : \ +r == BLR_THREAD_ROLE_MASTER_TRX ? "master (trx)" : "slave" + /** * The client session structure used within this router. This represents * the slaves that are replicating binlogs from MaxScale. */ -typedef struct router_slave { +typedef struct router_slave +{ #if defined(SS_DEBUG) - skygw_chk_t rses_chk_top; + skygw_chk_t rses_chk_top; #endif - DCB *dcb; /*< The slave server DCB */ - int state; /*< The state of this slave */ - uint32_t binlog_pos; /*< Binlog position for this slave */ - char binlogfile[BINLOG_FNAMELEN+1]; - /*< Current binlog file for this slave */ - char *uuid; /*< Slave UUID */ + DCB *dcb; /*< The slave server DCB */ + int state; /*< The state of this slave */ + uint32_t binlog_pos; /*< Binlog position for this slave */ + char binlogfile[BINLOG_FNAMELEN+1]; + /*< Current binlog file for this slave */ + char *uuid; /*< Slave UUID */ #ifdef BLFILE_IN_SLAVE - BLFILE *file; /*< Currently open binlog file */ + BLFILE *file; /*< Currently open binlog file */ #endif - int serverid; /*< Server-id of the slave */ - char *hostname; /*< Hostname of the slave, if known */ - char *user; /*< Username if given */ - char *passwd; /*< Password if given */ - short port; /*< MySQL port */ - int nocrc; /*< Disable CRC */ - int overrun; - uint32_t rank; /*< Replication rank */ - uint8_t seqno; /*< Replication dump sequence no */ - uint32_t lastEventTimestamp;/*< Last event timestamp sent */ - SPINLOCK catch_lock; /*< Event catchup lock */ - unsigned int cstate; /*< Catch up state */ - bool mariadb10_compat;/*< MariaDB 10.0 compatibility */ - SPINLOCK rses_lock; /*< Protects rses_deleted */ - pthread_t pthread; - struct router_instance - *router; /*< Pointer to the owning router */ - struct router_slave *next; - SLAVE_STATS stats; /*< Slave statistics */ - time_t connect_time; /*< Connect time of slave */ - char *warning_msg; /*< Warning message */ - int heartbeat; /*< Heartbeat in seconds */ - uint8_t lastEventReceived; /*< Last event received */ - time_t lastReply; /*< Last event sent */ + int serverid; /*< Server-id of the slave */ + char *hostname; /*< Hostname of the slave, if known */ + char *user; /*< Username if given */ + char *passwd; /*< Password if given */ + short port; /*< MySQL port */ + int nocrc; /*< Disable CRC */ + int overrun; + uint32_t rank; /*< Replication rank */ + uint8_t seqno; /*< Replication dump sequence no */ + uint32_t lastEventTimestamp;/*< Last event timestamp sent */ + SPINLOCK catch_lock; /*< Event catchup lock */ + unsigned int cstate; /*< Catch up state */ + bool mariadb10_compat;/*< MariaDB 10.0 compatibility */ + SPINLOCK rses_lock; /*< Protects rses_deleted */ + pthread_t pthread; + struct router_instance + *router; /*< Pointer to the owning router */ + struct router_slave *next; + SLAVE_STATS stats; /*< Slave statistics */ + time_t connect_time; /*< Connect time of slave */ + char *warning_msg; /*< Warning message */ + int heartbeat; /*< Heartbeat in seconds */ + uint8_t lastEventReceived; /*< Last event received */ + time_t lastReply; /*< Last event sent */ + // lsi: Last Sent Information + blr_thread_role_t lsi_sender_role; /*< Master or slave code sent */ + THREAD lsi_sender_tid; /*< Who sent */ + char lsi_binlog_name[BINLOG_FNAMELEN + 1]; /*< Which binlog file */ + uint32_t lsi_binlog_pos; /*< What position */ #if defined(SS_DEBUG) - skygw_chk_t rses_chk_tail; + skygw_chk_t rses_chk_tail; #endif } ROUTER_SLAVE; @@ -351,215 +376,224 @@ typedef struct router_slave { /** * The statistics for this router instance */ -typedef struct { - int n_slaves; /*< Number slave sessions created */ - int n_reads; /*< Number of record reads */ - uint64_t n_binlogs; /*< Number of binlog records from master */ - uint64_t n_binlogs_ses; /*< Number of binlog records from master */ - uint64_t n_binlog_errors;/*< Number of binlog records from master */ - uint64_t n_rotates; /*< Number of binlog rotate events */ - uint64_t n_cachehits; /*< Number of hits on the binlog cache */ - uint64_t n_cachemisses; /*< Number of misses on the binlog cache */ - int n_registered; /*< Number of registered slaves */ - int n_masterstarts; /*< Number of times connection restarted */ - int n_delayedreconnects; - int n_residuals; /*< Number of times residual data was buffered */ - int n_heartbeats; /*< Number of heartbeat messages */ - time_t lastReply; - uint64_t n_fakeevents; /*< Fake events not written to disk */ - uint64_t n_artificial; /*< Artificial events not written to disk */ - int n_badcrc; /*< No. of bad CRC's from master */ - uint64_t events[MAX_EVENT_TYPE_END + 1]; /*< Per event counters */ - uint64_t lastsample; - int minno; - int minavgs[BLR_NSTATS_MINUTES]; +typedef struct +{ + int n_slaves; /*< Number slave sessions created */ + int n_reads; /*< Number of record reads */ + uint64_t n_binlogs; /*< Number of binlog records from master */ + uint64_t n_binlogs_ses; /*< Number of binlog records from master */ + uint64_t n_binlog_errors;/*< Number of binlog records from master */ + uint64_t n_rotates; /*< Number of binlog rotate events */ + uint64_t n_cachehits; /*< Number of hits on the binlog cache */ + uint64_t n_cachemisses; /*< Number of misses on the binlog cache */ + int n_registered; /*< Number of registered slaves */ + int n_masterstarts; /*< Number of times connection restarted */ + int n_delayedreconnects; + int n_residuals; /*< Number of times residual data was buffered */ + int n_heartbeats; /*< Number of heartbeat messages */ + time_t lastReply; + uint64_t n_fakeevents; /*< Fake events not written to disk */ + uint64_t n_artificial; /*< Artificial events not written to disk */ + int n_badcrc; /*< No. of bad CRC's from master */ + uint64_t events[MAX_EVENT_TYPE_END + 1]; /*< Per event counters */ + uint64_t lastsample; + int minno; + int minavgs[BLR_NSTATS_MINUTES]; } ROUTER_STATS; /** * Saved responses from the master that will be forwarded to slaves */ -typedef struct { - GWBUF *server_id; /*< Master server id */ - GWBUF *heartbeat; /*< Heartbeat period */ - GWBUF *chksum1; /*< Binlog checksum 1st response */ - GWBUF *chksum2; /*< Binlog checksum 2nd response */ - GWBUF *gtid_mode; /*< GTID Mode response */ - GWBUF *uuid; /*< Master UUID */ - GWBUF *setslaveuuid; /*< Set Slave UUID */ - GWBUF *setnames; /*< Set NAMES latin1 */ - GWBUF *utf8; /*< Set NAMES utf8 */ - GWBUF *select1; /*< select 1 */ - GWBUF *selectver; /*< select version() */ - GWBUF *selectvercom; /*< select @@version_comment */ - GWBUF *selecthostname;/*< select @@hostname */ - GWBUF *map; /*< select @@max_allowed_packet */ - GWBUF *mariadb10; /*< set @mariadb_slave_capability */ - uint8_t *fde_event; /*< Format Description Event */ - int fde_len; /*< Length of fde_event */ +typedef struct +{ + GWBUF *server_id; /*< Master server id */ + GWBUF *heartbeat; /*< Heartbeat period */ + GWBUF *chksum1; /*< Binlog checksum 1st response */ + GWBUF *chksum2; /*< Binlog checksum 2nd response */ + GWBUF *gtid_mode; /*< GTID Mode response */ + GWBUF *uuid; /*< Master UUID */ + GWBUF *setslaveuuid; /*< Set Slave UUID */ + GWBUF *setnames; /*< Set NAMES latin1 */ + GWBUF *utf8; /*< Set NAMES utf8 */ + GWBUF *select1; /*< select 1 */ + GWBUF *selectver; /*< select version() */ + GWBUF *selectvercom; /*< select @@version_comment */ + GWBUF *selecthostname;/*< select @@hostname */ + GWBUF *map; /*< select @@max_allowed_packet */ + GWBUF *mariadb10; /*< set @mariadb_slave_capability */ + uint8_t *fde_event; /*< Format Description Event */ + int fde_len; /*< Length of fde_event */ } MASTER_RESPONSES; /** * The per instance data for the router. */ -typedef struct router_instance { - SERVICE *service; /*< Pointer to the service using this router */ - ROUTER_SLAVE *slaves; /*< Link list of all the slave connections */ - SPINLOCK lock; /*< Spinlock for the instance data */ - char *uuid; /*< UUID for the router to use w/master */ - int masterid; /*< Set ID of the master, sent to slaves */ - int serverid; /*< ID for the router to use w/master */ - int initbinlog; /*< Initial binlog file number */ - char *user; /*< User name to use with master */ - char *password; /*< Password to use with master */ - char *fileroot; /*< Root of binlog filename */ - bool master_chksum; /*< Does the master provide checksums */ - bool mariadb10_compat; /*< MariaDB 10.0 compatibility */ - char *master_uuid; /*< Set UUID of the master, sent to slaves */ - DCB *master; /*< DCB for master connection */ - DCB *client; /*< DCB for dummy client */ - SESSION *session; /*< Fake session for master connection */ - unsigned int master_state; /*< State of the master FSM */ - uint8_t lastEventReceived; /*< Last even received */ - uint32_t lastEventTimestamp; /*< Timestamp from last event */ - GWBUF *residual; /*< Any residual binlog event */ - 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 trx_safe; /*< Detect and handle partial transactions */ - int pending_transaction; /*< Pending transaction */ - enum blr_event_state master_event_state; /*< Packet read state */ - uint32_t stored_checksum; /*< The current value of the checksum */ - uint8_t partial_checksum[MYSQL_CHECKSUM_LEN]; /*< The partial value of the checksum - * received from the master */ - uint8_t partial_checksum_bytes; /*< How many bytes of the checksum we have read */ - uint64_t checksum_size; /*< Data size for the checksum */ - REP_HEADER stored_header; /*< Relication header of the event the master is sending */ - uint64_t last_safe_pos; /* last committed transaction */ - char binlog_name[BINLOG_FNAMELEN+1]; - /*< Name of the current binlog file */ - uint64_t binlog_position; - /*< last committed transaction position */ - uint64_t current_pos; - /*< Current binlog position */ - int binlog_fd; /*< File descriptor of the binlog - * file being written - */ - uint64_t last_written; /*< Position of the last write operation */ - uint64_t last_event_pos; /*< Position of last event written */ - uint64_t current_safe_event; - /*< Position of the latest safe event being sent to slaves */ - char prevbinlog[BINLOG_FNAMELEN+1]; - int rotating; /*< Rotation in progress flag */ - BLFILE *files; /*< Files used by the slaves */ - SPINLOCK fileslock; /*< Lock for the files queue above */ - unsigned int low_water; /*< Low water mark for client DCB */ - unsigned int high_water; /*< High water mark for client DCB */ - unsigned int short_burst; /*< Short burst for slave catchup */ - unsigned int long_burst; /*< Long burst for slave catchup */ - unsigned long burst_size; /*< Maximum size of burst to send */ - unsigned long heartbeat; /*< Configured heartbeat value */ - ROUTER_STATS stats; /*< Statistics for this router */ - int active_logs; - int reconnect_pending; - int retry_backoff; - time_t connect_time; - int handling_threads; - unsigned long m_errno; /*< master response mysql errno */ - char *m_errmsg; /*< master response mysql error message */ - char *set_master_version; /*< Send custom Version to slaves */ - char *set_master_hostname; /*< Send custom Hostname to slaves */ - char *set_master_uuid; /*< Send custom Master UUID to slaves */ - char *set_master_server_id; /*< Send custom Master server_id to slaves */ - int send_slave_heartbeat; /*< Enable sending heartbeat to slaves */ - struct router_instance *next; +typedef struct router_instance +{ + SERVICE *service; /*< Pointer to the service using this router */ + ROUTER_SLAVE *slaves; /*< Link list of all the slave connections */ + SPINLOCK lock; /*< Spinlock for the instance data */ + char *uuid; /*< UUID for the router to use w/master */ + int masterid; /*< Set ID of the master, sent to slaves */ + int serverid; /*< ID for the router to use w/master */ + int initbinlog; /*< Initial binlog file number */ + char *user; /*< User name to use with master */ + char *password; /*< Password to use with master */ + char *fileroot; /*< Root of binlog filename */ + bool master_chksum; /*< Does the master provide checksums */ + bool mariadb10_compat; /*< MariaDB 10.0 compatibility */ + char *master_uuid; /*< Set UUID of the master, sent to slaves */ + DCB *master; /*< DCB for master connection */ + DCB *client; /*< DCB for dummy client */ + SESSION *session; /*< Fake session for master connection */ + unsigned int master_state; /*< State of the master FSM */ + uint8_t lastEventReceived; /*< Last even received */ + uint32_t lastEventTimestamp; /*< Timestamp from last event */ + GWBUF *residual; /*< Any residual binlog event */ + 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 trx_safe; /*< Detect and handle partial transactions */ + int pending_transaction; /*< Pending transaction */ + enum blr_event_state master_event_state; /*< Packet read state */ + uint32_t stored_checksum; /*< The current value of the checksum */ + uint8_t partial_checksum[MYSQL_CHECKSUM_LEN]; /*< The partial value of the checksum + * received from the master */ + uint8_t partial_checksum_bytes; /*< How many bytes of the checksum we have read */ + uint64_t checksum_size; /*< Data size for the checksum */ + REP_HEADER stored_header; /*< Relication header of the event the master is sending */ + uint64_t last_safe_pos; /* last committed transaction */ + char binlog_name[BINLOG_FNAMELEN+1]; + /*< Name of the current binlog file */ + uint64_t binlog_position; + /*< last committed transaction position */ + uint64_t current_pos; + /*< Current binlog position */ + int binlog_fd; /*< File descriptor of the binlog + * file being written + */ + uint64_t last_written; /*< Position of the last write operation */ + uint64_t last_event_pos; /*< Position of last event written */ + uint64_t current_safe_event; + /*< Position of the latest safe event being sent to slaves */ + char prevbinlog[BINLOG_FNAMELEN+1]; + int rotating; /*< Rotation in progress flag */ + BLFILE *files; /*< Files used by the slaves */ + SPINLOCK fileslock; /*< Lock for the files queue above */ + unsigned int low_water; /*< Low water mark for client DCB */ + unsigned int high_water; /*< High water mark for client DCB */ + unsigned int short_burst; /*< Short burst for slave catchup */ + unsigned int long_burst; /*< Long burst for slave catchup */ + unsigned long burst_size; /*< Maximum size of burst to send */ + unsigned long heartbeat; /*< Configured heartbeat value */ + ROUTER_STATS stats; /*< Statistics for this router */ + int active_logs; + int reconnect_pending; + int retry_backoff; + time_t connect_time; + int handling_threads; + unsigned long m_errno; /*< master response mysql errno */ + char *m_errmsg; /*< master response mysql error message */ + char *set_master_version; /*< Send custom Version to slaves */ + char *set_master_hostname; /*< Send custom Hostname to slaves */ + char *set_master_uuid; /*< Send custom Master UUID to slaves */ + char *set_master_server_id; /*< Send custom Master server_id to slaves */ + int send_slave_heartbeat; /*< Enable sending heartbeat to slaves */ + struct router_instance *next; } ROUTER_INSTANCE; /** * State machine for the master to MaxScale replication */ -#define BLRM_UNCONFIGURED 0x0000 -#define BLRM_UNCONNECTED 0x0001 -#define BLRM_CONNECTING 0x0002 -#define BLRM_AUTHENTICATED 0x0003 -#define BLRM_TIMESTAMP 0x0004 -#define BLRM_SERVERID 0x0005 -#define BLRM_HBPERIOD 0x0006 -#define BLRM_CHKSUM1 0x0007 -#define BLRM_CHKSUM2 0x0008 -#define BLRM_GTIDMODE 0x0009 -#define BLRM_MUUID 0x000A -#define BLRM_SUUID 0x000B -#define BLRM_LATIN1 0x000C -#define BLRM_UTF8 0x000D -#define BLRM_SELECT1 0x000E -#define BLRM_SELECTVER 0x000F -#define BLRM_SELECTVERCOM 0x0010 -#define BLRM_SELECTHOSTNAME 0x0011 -#define BLRM_MAP 0x0012 -#define BLRM_REGISTER 0x0013 -#define BLRM_BINLOGDUMP 0x0014 -#define BLRM_SLAVE_STOPPED 0x0015 -#define BLRM_MARIADB10 0x0016 +#define BLRM_UNCONFIGURED 0x0000 +#define BLRM_UNCONNECTED 0x0001 +#define BLRM_CONNECTING 0x0002 +#define BLRM_AUTHENTICATED 0x0003 +#define BLRM_TIMESTAMP 0x0004 +#define BLRM_SERVERID 0x0005 +#define BLRM_HBPERIOD 0x0006 +#define BLRM_CHKSUM1 0x0007 +#define BLRM_CHKSUM2 0x0008 +#define BLRM_GTIDMODE 0x0009 +#define BLRM_MUUID 0x000A +#define BLRM_SUUID 0x000B +#define BLRM_LATIN1 0x000C +#define BLRM_UTF8 0x000D +#define BLRM_SELECT1 0x000E +#define BLRM_SELECTVER 0x000F +#define BLRM_SELECTVERCOM 0x0010 +#define BLRM_SELECTHOSTNAME 0x0011 +#define BLRM_MAP 0x0012 +#define BLRM_REGISTER 0x0013 +#define BLRM_BINLOGDUMP 0x0014 +#define BLRM_SLAVE_STOPPED 0x0015 +#define BLRM_MARIADB10 0x0016 -#define BLRM_MAXSTATE 0x0016 +#define BLRM_MAXSTATE 0x0016 -static char *blrm_states[] = { "Unconfigured", "Unconnected", "Connecting", "Authenticated", "Timestamp retrieval", - "Server ID retrieval", "HeartBeat Period setup", "binlog checksum config", - "binlog checksum rerieval", "GTID Mode retrieval", "Master UUID retrieval", - "Set Slave UUID", "Set Names latin1", "Set Names utf8", "select 1", - "select version()", "select @@version_comment", "select @@hostname", - "select @@max_allowed_packet", "Register slave", "Binlog Dump", "Slave stopped", "Set MariaDB slave capability" }; +static char *blrm_states[] = +{ + "Unconfigured", "Unconnected", "Connecting", "Authenticated", "Timestamp retrieval", + "Server ID retrieval", "HeartBeat Period setup", "binlog checksum config", + "binlog checksum rerieval", "GTID Mode retrieval", "Master UUID retrieval", + "Set Slave UUID", "Set Names latin1", "Set Names utf8", "select 1", + "select version()", "select @@version_comment", "select @@hostname", + "select @@max_allowed_packet", "Register slave", "Binlog Dump", "Slave stopped", + "Set MariaDB slave capability" +}; -#define BLRS_CREATED 0x0000 -#define BLRS_UNREGISTERED 0x0001 -#define BLRS_REGISTERED 0x0002 -#define BLRS_DUMPING 0x0003 -#define BLRS_ERRORED 0x0004 +#define BLRS_CREATED 0x0000 +#define BLRS_UNREGISTERED 0x0001 +#define BLRS_REGISTERED 0x0002 +#define BLRS_DUMPING 0x0003 +#define BLRS_ERRORED 0x0004 -#define BLRS_MAXSTATE 0x0004 +#define BLRS_MAXSTATE 0x0004 -static char *blrs_states[] = { "Created", "Unregistered", "Registered", - "Sending binlogs", "Errored" }; +static char *blrs_states[] = +{ + "Created", "Unregistered", "Registered", "Sending binlogs", "Errored" +}; /** * Slave catch-up status */ -#define CS_UPTODATE 0x0004 -#define CS_EXPECTCB 0x0008 -#define CS_DIST 0x0010 -#define CS_DISTLATCH 0x0020 -#define CS_THRDWAIT 0x0040 -#define CS_BUSY 0x0100 -#define CS_HOLD 0x0200 +#define CS_UPTODATE 0x0004 +#define CS_EXPECTCB 0x0008 +#define CS_DIST 0x0010 +#define CS_DISTLATCH 0x0020 +#define CS_THRDWAIT 0x0040 +#define CS_BUSY 0x0100 +#define CS_HOLD 0x0200 /** * MySQL protocol OpCodes needed for replication */ -#define COM_QUIT 0x01 -#define COM_QUERY 0x03 -#define COM_STATISTICS 0x09 -#define COM_PING 0x0e -#define COM_REGISTER_SLAVE 0x15 -#define COM_BINLOG_DUMP 0x12 +#define COM_QUIT 0x01 +#define COM_QUERY 0x03 +#define COM_STATISTICS 0x09 +#define COM_PING 0x0e +#define COM_REGISTER_SLAVE 0x15 +#define COM_BINLOG_DUMP 0x12 /** * Macros to extract common fields */ -#define INLINE_EXTRACT 1 /* Set to 0 for debug purposes */ +#define INLINE_EXTRACT 1 /* Set to 0 for debug purposes */ #if INLINE_EXTRACT -#define EXTRACT16(x) (*(uint8_t *)(x) | (*((uint8_t *)(x) + 1) << 8)) -#define EXTRACT24(x) (*(uint8_t *)(x) | \ - (*((uint8_t *)(x) + 1) << 8) | \ - (*((uint8_t *)(x) + 2) << 16)) -#define EXTRACT32(x) (*(uint8_t *)(x) | \ - (*((uint8_t *)(x) + 1) << 8) | \ - (*((uint8_t *)(x) + 2) << 16) | \ - (*((uint8_t *)(x) + 3) << 24)) +#define EXTRACT16(x) (*(uint8_t *)(x) | (*((uint8_t *)(x) + 1) << 8)) +#define EXTRACT24(x) (*(uint8_t *)(x) | \ + (*((uint8_t *)(x) + 1) << 8) | \ + (*((uint8_t *)(x) + 2) << 16)) +#define EXTRACT32(x) (*(uint8_t *)(x) | \ + (*((uint8_t *)(x) + 1) << 8) | \ + (*((uint8_t *)(x) + 2) << 16) | \ + (*((uint8_t *)(x) + 3) << 24)) #else -#define EXTRACT16(x) extract_field((x), 16) -#define EXTRACT24(x) extract_field((x), 24) -#define EXTRACT32(x) extract_field((x), 32) +#define EXTRACT16(x) extract_field((x), 16) +#define EXTRACT24(x) extract_field((x), 24) +#define EXTRACT32(x) extract_field((x), 32) #endif /* @@ -588,4 +622,12 @@ extern int blr_ping(ROUTER_INSTANCE *, ROUTER_SLAVE *, GWBUF *); extern int blr_send_custom_error(DCB *, int, int, char *, char *, unsigned int); extern int blr_file_next_exists(ROUTER_INSTANCE *, ROUTER_SLAVE *); uint32_t extract_field(uint8_t *src, int bits); + +extern bool blr_send_event(blr_thread_role_t role, + const char* binlog_name, + uint32_t binlog_pos, + ROUTER_SLAVE *slave, + REP_HEADER *hdr, + uint8_t *buf); + #endif diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index cc777146f..25bdccc8a 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -31,26 +31,25 @@ * @verbatim * Revision History * - * Date Who Description - * 02/04/2014 Mark Riddoch Initial implementation - * 17/02/2015 Massimiliano Pinto Addition of slave port and username in diagnostics - * 18/02/2015 Massimiliano Pinto Addition of dcb_close in closeSession - * 07/05/2015 Massimiliano Pinto Addition of MariaDB 10 compatibility support - * 12/06/2015 Massimiliano Pinto Addition of MariaDB 10 events in diagnostics() - * 29/06/2015 Massimiliano Pinto Addition of master.ini for easy startup configuration - * If not found router goes into BLRM_UNCONFIGURED state. - * Cache dir is 'cache' under router->binlogdir. - * 07/08/2015 Massimiliano Pinto Addition of binlog check at startup if trx_safe is on - * 21/08/2015 Massimiliano Pinto Added support for new config options: - * master_uuid, master_hostname, master_version - * If set those values are sent to slaves instead of - * saved master responses - * 23/08/2015 Massimiliano Pinto Added strerror_r - * 09/09/2015 Martin Brampton Modify error handler - * 30/09/2015 Massimiliano Pinto Addition of send_slave_heartbeat option - * 23/10/2015 Markus Makela Added current_safe_event - * 27/10/2015 Martin Brampton Amend getCapabilities to return RCAP_TYPE_NO_RSESSION - * 26/11/2015 Massimiliano Pinto Added check for missing service listener + * Date Who Description + * 02/04/2014 Mark Riddoch Initial implementation + * 17/02/2015 Massimiliano Pinto Addition of slave port and username in diagnostics + * 18/02/2015 Massimiliano Pinto Addition of dcb_close in closeSession + * 07/05/2015 Massimiliano Pinto Addition of MariaDB 10 compatibility support + * 12/06/2015 Massimiliano Pinto Addition of MariaDB 10 events in diagnostics() + * 29/06/2015 Massimiliano Pinto Addition of master.ini for easy startup configuration + * If not found router goes into BLRM_UNCONFIGURED state. + * Cache dir is 'cache' under router->binlogdir. + * 07/08/2015 Massimiliano Pinto Addition of binlog check at startup if trx_safe is on + * 21/08/2015 Massimiliano Pinto Added support for new config options: + * master_uuid, master_hostname, master_version + * If set those values are sent to slaves instead of + * saved master responses + * 23/08/2015 Massimiliano Pinto Added strerror_r + * 09/09/2015 Martin Brampton Modify error handler + * 30/09/2015 Massimiliano Pinto Addition of send_slave_heartbeat option + * 23/10/2015 Markus Makela Added current_safe_event + * 27/10/2015 Martin Brampton Amend getCapabilities to return RCAP_TYPE_NO_RSESSION * * @endverbatim */ @@ -81,26 +80,24 @@ static char *version_str = "V2.0.0"; /* The router entry points */ -static ROUTER *createInstance(SERVICE *service, char **options); -static void *newSession(ROUTER *instance, SESSION *session); -static void closeSession(ROUTER *instance, void *router_session); -static void freeSession(ROUTER *instance, void *router_session); -static int routeQuery(ROUTER *instance, void *router_session, GWBUF *queue); -static void diagnostics(ROUTER *instance, DCB *dcb); -static void clientReply( - ROUTER *instance, - void *router_session, - GWBUF *queue, - DCB *backend_dcb); -static void errorReply( - ROUTER *instance, - void *router_session, - GWBUF *message, - DCB *backend_dcb, - error_action_t action, - bool *succp); +static ROUTER *createInstance(SERVICE *service, char **options); +static void *newSession(ROUTER *instance, SESSION *session); +static void closeSession(ROUTER *instance, void *router_session); +static void freeSession(ROUTER *instance, void *router_session); +static int routeQuery(ROUTER *instance, void *router_session, GWBUF *queue); +static void diagnostics(ROUTER *instance, DCB *dcb); +static void clientReply(ROUTER *instance, + void *router_session, + GWBUF *queue, + DCB *backend_dcb); +static void errorReply(ROUTER *instance, + void *router_session, + GWBUF *message, + DCB *backend_dcb, + error_action_t action, + bool *succp); -static int getCapabilities (); +static int getCapabilities(); static int blr_handler_config(void *userdata, const char *section, const char *name, const char *value); static int blr_handle_config_item(const char *name, const char *value, ROUTER_INSTANCE *inst); static int blr_set_service_mysql_user(SERVICE *service); @@ -113,10 +110,12 @@ extern char *create_hex_sha1_sha1_passwd(char *passwd); extern int blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug); void blr_master_close(ROUTER_INSTANCE *); char * blr_last_event_description(ROUTER_INSTANCE *router); -char *blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event); +extern int MaxScaleUptime(); +char *blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event); /** The module object definition */ -static ROUTER_OBJECT MyObject = { +static ROUTER_OBJECT MyObject = +{ createInstance, newSession, closeSession, @@ -128,7 +127,7 @@ static ROUTER_OBJECT MyObject = { getCapabilities }; -static void stats_func(void *); +static void stats_func(void *); static bool rses_begin_locked_router_action(ROUTER_SLAVE *); static void rses_end_locked_router_action(ROUTER_SLAVE *); @@ -136,7 +135,7 @@ void my_uuid_init(ulong seed1, ulong seed2); void my_uuid(unsigned char *guid); GWBUF *blr_cache_read_response(ROUTER_INSTANCE *router, char *response); -static SPINLOCK instlock; +static SPINLOCK instlock; static ROUTER_INSTANCE *instances; /** @@ -147,7 +146,7 @@ static ROUTER_INSTANCE *instances; char * version() { - return version_str; + return version_str; } /** @@ -157,9 +156,9 @@ version() void ModuleInit() { - MXS_NOTICE("Initialise binlog router module %s.\n", version_str); - spinlock_init(&instlock); - instances = NULL; + MXS_NOTICE("Initialise binlog router module %s.\n", version_str); + spinlock_init(&instlock); + instances = NULL; } /** @@ -173,7 +172,7 @@ ModuleInit() ROUTER_OBJECT * GetModuleObject() { - return &MyObject; + return &MyObject; } /** @@ -184,530 +183,561 @@ GetModuleObject() * with the master server and begin replication of the binlogs from * the master server to MaxScale. * - * @param service The service this router is being create for - * @param options An array of options for this query router + * @param service The service this router is being create for + * @param options An array of options for this query router * * @return The instance data for this new instance */ -static ROUTER * +static ROUTER * createInstance(SERVICE *service, char **options) { -ROUTER_INSTANCE *inst; -char *value; -int i; -unsigned char *defuuid; -char path[PATH_MAX+1] = ""; -char filename[PATH_MAX+1] = ""; -int rc = 0; -char task_name[BLRM_TASK_NAME_LEN+1] = ""; + ROUTER_INSTANCE *inst; + char *value; + int i; + unsigned char *defuuid; + char path[PATH_MAX + 1] = ""; + char filename[PATH_MAX + 1] = ""; + int rc = 0; + char task_name[BLRM_TASK_NAME_LEN + 1] = ""; - if(service->credentials.name == NULL || - service->credentials.authdata == NULL) - { - MXS_ERROR("%s: Error: Service is missing user credentials." - " Add the missing username or passwd parameter to the service.", - service->name); - return NULL; - } + if (service->credentials.name == NULL || + service->credentials.authdata == NULL) + { + MXS_ERROR("%s: Error: Service is missing user credentials." + " Add the missing username or passwd parameter to the service.", + service->name); + return NULL; + } - if(options == NULL || options[0] == NULL) - { - MXS_ERROR("%s: Error: No router options supplied for binlogrouter", - service->name); - return NULL; - } + if (options == NULL || options[0] == NULL) + { + MXS_ERROR("%s: Error: No router options supplied for binlogrouter", + service->name); + return NULL; + } - /* Check for listeners associated to this service */ - if (service->ports == NULL) - { - MXS_ERROR("%s: Error: No listener configured for binlogrouter. Add a listener section in config file.", - service->name); - return NULL; - } + /* + * We only support one server behind this router, since the server is + * the master from which we replicate binlog records. Therefore check + * that only one server has been defined. + */ + if (service->dbref != NULL) + { + MXS_WARNING("%s: backend database server is provided by master.ini file " + "for use with the binlog router." + " Server section is no longer required.", + service->name); - /* - * We only support one server behind this router, since the server is - * the master from which we replicate binlog records. Therefore check - * that only one server has been defined. - */ - if (service->dbref != NULL) - { - MXS_WARNING("%s: backend database server is provided by master.ini file " - "for use with the binlog router." - " Server section is no longer required.", - service->name); + server_free(service->dbref->server); + free(service->dbref); + service->dbref = NULL; + } - server_free(service->dbref->server); - free(service->dbref); - service->dbref = NULL; - } + if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) + { + MXS_ERROR("%s: Error: failed to allocate memory for router instance.", + service->name); - if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) { - MXS_ERROR("%s: Error: failed to allocate memory for router instance.", - service->name); + return NULL; + } - return NULL; - } + memset(&inst->stats, 0, sizeof(ROUTER_STATS)); + memset(&inst->saved_master, 0, sizeof(MASTER_RESPONSES)); - memset(&inst->stats, 0, sizeof(ROUTER_STATS)); - memset(&inst->saved_master, 0, sizeof(MASTER_RESPONSES)); + inst->service = service; + spinlock_init(&inst->lock); + inst->files = NULL; + spinlock_init(&inst->fileslock); + spinlock_init(&inst->binlog_lock); - inst->service = service; - spinlock_init(&inst->lock); - inst->files = NULL; - spinlock_init(&inst->fileslock); - spinlock_init(&inst->binlog_lock); + inst->binlog_fd = -1; + inst->master_chksum = true; + inst->master_uuid = NULL; - inst->binlog_fd = -1; - inst->master_chksum = true; - inst->master_uuid = NULL; + inst->master_state = BLRM_UNCONFIGURED; + inst->master = NULL; + inst->client = NULL; - inst->master_state = BLRM_UNCONFIGURED; - inst->master = NULL; - inst->client = NULL; + inst->low_water = DEF_LOW_WATER; + inst->high_water = DEF_HIGH_WATER; + inst->initbinlog = 0; + inst->short_burst = DEF_SHORT_BURST; + inst->long_burst = DEF_LONG_BURST; + inst->burst_size = DEF_BURST_SIZE; + inst->retry_backoff = 1; + inst->binlogdir = NULL; + inst->heartbeat = BLR_HEARTBEAT_DEFAULT_INTERVAL; + inst->mariadb10_compat = false; - inst->low_water = DEF_LOW_WATER; - inst->high_water = DEF_HIGH_WATER; - inst->initbinlog = 0; - inst->short_burst = DEF_SHORT_BURST; - inst->long_burst = DEF_LONG_BURST; - inst->burst_size = DEF_BURST_SIZE; - inst->retry_backoff = 1; - inst->binlogdir = NULL; - inst->heartbeat = BLR_HEARTBEAT_DEFAULT_INTERVAL; - inst->mariadb10_compat = false; + inst->user = strdup(service->credentials.name); + inst->password = strdup(service->credentials.authdata); - inst->user = strdup(service->credentials.name); - inst->password = strdup(service->credentials.authdata); + inst->m_errno = 0; + inst->m_errmsg = NULL; - inst->m_errno = 0; - inst->m_errmsg = NULL; + inst->trx_safe = 1; + inst->pending_transaction = 0; + inst->last_safe_pos = 0; + inst->last_event_pos = 0; - inst->trx_safe = 0; - inst->pending_transaction = 0; - inst->last_safe_pos = 0; + inst->set_master_version = NULL; + inst->set_master_hostname = NULL; + inst->set_master_uuid = NULL; + inst->set_master_server_id = NULL; + inst->send_slave_heartbeat = 0; - inst->set_master_version = NULL; - inst->set_master_hostname = NULL; - inst->set_master_uuid = NULL; - inst->set_master_server_id = NULL; - inst->send_slave_heartbeat = 0; + inst->serverid = 0; - inst->serverid = 0; + my_uuid_init((ulong)rand() * 12345, 12345); + if ((defuuid = (unsigned char *)malloc(20)) != NULL) + { + my_uuid(defuuid); + if ((inst->uuid = (char *)malloc(38)) != NULL) + { + sprintf(inst->uuid, + "%02hhx%02hhx%02hhx%02hhx-" + "%02hhx%02hhx-" + "%02hhx%02hhx-" + "%02hhx%02hhx-" + "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + defuuid[0], defuuid[1], defuuid[2], defuuid[3], + defuuid[4], defuuid[5], defuuid[6], defuuid[7], + defuuid[8], defuuid[9], defuuid[10], defuuid[11], + defuuid[12], defuuid[13], defuuid[14], defuuid[15]); + } + } - my_uuid_init((ulong)rand()*12345,12345); - if ((defuuid = (unsigned char *)malloc(20)) != NULL) - { - my_uuid(defuuid); - if ((inst->uuid = (char *)malloc(38)) != NULL) - sprintf(inst->uuid, - "%02hhx%02hhx%02hhx%02hhx-" - "%02hhx%02hhx-" - "%02hhx%02hhx-" - "%02hhx%02hhx-" - "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", - defuuid[0], defuuid[1], defuuid[2], defuuid[3], - defuuid[4], defuuid[5], defuuid[6], defuuid[7], - defuuid[8], defuuid[9], defuuid[10], defuuid[11], - defuuid[12], defuuid[13], defuuid[14], defuuid[15]); - } + /* + * Process the options. + * We have an array of attribute values passed to us that we must + * examine. Supported attributes are: + * uuid= + * server-id= + * user= + * password= + * master-id= + * filestem= + * lowwater= + * highwater= + */ + if (options) + { + for (i = 0; options[i]; i++) + { + if ((value = strchr(options[i], '=')) == NULL) + { + MXS_WARNING("Unsupported router " + "option %s for binlog router.", + options[i]); + } + else + { + *value = 0; + value++; + if (strcmp(options[i], "uuid") == 0) + { + inst->uuid = strdup(value); + } + else if ((strcmp(options[i], "server_id") == 0) || (strcmp(options[i], "server-id") == 0)) + { + inst->serverid = atoi(value); + if (strcmp(options[i], "server-id") == 0) + { + MXS_WARNING("Configuration setting '%s' in router_options is deprecated" + " and will be removed in a later version of MaxScale. " + "Please use the new setting '%s' instead.", + "server-id", "server_id"); + } - /* - * Process the options. - * We have an array of attribute values passed to us that we must - * examine. Supported attributes are: - * uuid= - * server-id= - * user= - * password= - * master-id= - * filestem= - * lowwater= - * highwater= - */ - if (options) - { - for (i = 0; options[i]; i++) - { - if ((value = strchr(options[i], '=')) == NULL) - { - MXS_WARNING("Unsupported router " - "option %s for binlog router.", - options[i]); - } - else - { - *value = 0; - value++; - if (strcmp(options[i], "uuid") == 0) - { - inst->uuid = strdup(value); - } - else if ( (strcmp(options[i], "server_id") == 0) || (strcmp(options[i], "server-id") == 0) ) - { - inst->serverid = atoi(value); - if (strcmp(options[i], "server-id") == 0) { - MXS_WARNING("Configuration setting '%s' in router_options is deprecated" - " and will be removed in a later version of MaxScale. " - "Please use the new setting '%s' instead.", - "server-id", "server_id"); - } + if (inst->serverid <= 0) + { + MXS_ERROR("Service %s, invalid server-id '%s'. " + "Please configure it with a unique positive integer value (1..2^32-1)", + service->name, value); - if (inst->serverid <= 0) { - MXS_ERROR("Service %s, invalid server-id '%s'. " - "Please configure it with a unique positive integer value (1..2^32-1)", - service->name, value); + free(inst); + return NULL; + } + } + else if (strcmp(options[i], "user") == 0) + { + inst->user = strdup(value); + } + else if (strcmp(options[i], "password") == 0) + { + inst->password = strdup(value); + } + else if (strcmp(options[i], "passwd") == 0) + { + inst->password = strdup(value); + } + else if ((strcmp(options[i], "master_id") == 0) || (strcmp(options[i], "master-id") == 0)) + { + int master_id = atoi(value); + if (master_id > 0) + { + inst->masterid = master_id; + inst->set_master_server_id = strdup(value); + } + if (strcmp(options[i], "master-id") == 0) + { + MXS_WARNING("Configuration setting '%s' in router_options is deprecated" + " and will be removed in a later version of MaxScale. " + "Please use the new setting '%s' instead.", + "master-id", "master_id"); + } + } + else if (strcmp(options[i], "master_uuid") == 0) + { + inst->set_master_uuid = strdup(value); + inst->master_uuid = inst->set_master_uuid; + } + else if (strcmp(options[i], "master_version") == 0) + { + inst->set_master_version = strdup(value); + } + else if (strcmp(options[i], "master_hostname") == 0) + { + inst->set_master_hostname = strdup(value); + } + else if (strcmp(options[i], "mariadb10-compatibility") == 0) + { + inst->mariadb10_compat = config_truth_value(value); + } + else if (strcmp(options[i], "filestem") == 0) + { + inst->fileroot = strdup(value); + } + else if (strcmp(options[i], "file") == 0) + { + inst->initbinlog = atoi(value); + } + else if (strcmp(options[i], "transaction_safety") == 0) + { + inst->trx_safe = config_truth_value(value); + } + else if (strcmp(options[i], "lowwater") == 0) + { + inst->low_water = atoi(value); + } + else if (strcmp(options[i], "highwater") == 0) + { + inst->high_water = atoi(value); + } + else if (strcmp(options[i], "shortburst") == 0) + { + inst->short_burst = atoi(value); + } + else if (strcmp(options[i], "longburst") == 0) + { + inst->long_burst = atoi(value); + } + else if (strcmp(options[i], "burstsize") == 0) + { + unsigned long size = atoi(value); + char *ptr = value; + while (*ptr && isdigit(*ptr)) + { + ptr++; + } + switch (*ptr) + { + case 'G': + case 'g': + size = size * 1024 * 1000 * 1000; + break; + case 'M': + case 'm': + size = size * 1024 * 1000; + break; + case 'K': + case 'k': + size = size * 1024; + break; + } + inst->burst_size = size; - free(inst); - return NULL; - } - } - else if (strcmp(options[i], "user") == 0) - { - inst->user = strdup(value); - } - else if (strcmp(options[i], "password") == 0) - { - inst->password = strdup(value); - } - else if (strcmp(options[i], "passwd") == 0) - { - inst->password = strdup(value); - } - else if ( (strcmp(options[i], "master_id") == 0) || (strcmp(options[i], "master-id") == 0) ) - { - int master_id = atoi(value); - if (master_id > 0) { - inst->masterid = master_id; - inst->set_master_server_id = strdup(value); - } - if (strcmp(options[i], "master-id") == 0) { - MXS_WARNING("Configuration setting '%s' in router_options is deprecated" - " and will be removed in a later version of MaxScale. " - "Please use the new setting '%s' instead.", - "master-id", "master_id"); - } - } - else if (strcmp(options[i], "master_uuid") == 0) - { - inst->set_master_uuid = strdup(value); - inst->master_uuid = inst->set_master_uuid; - } - else if (strcmp(options[i], "master_version") == 0) - { - inst->set_master_version = strdup(value); - } - else if (strcmp(options[i], "master_hostname") == 0) - { - inst->set_master_hostname = strdup(value); - } - else if (strcmp(options[i], "mariadb10-compatibility") == 0) - { - inst->mariadb10_compat = config_truth_value(value); - } - else if (strcmp(options[i], "filestem") == 0) - { - inst->fileroot = strdup(value); - } - else if (strcmp(options[i], "file") == 0) - { - inst->initbinlog = atoi(value); - } - else if (strcmp(options[i], "transaction_safety") == 0) - { - inst->trx_safe = config_truth_value(value); - } - else if (strcmp(options[i], "lowwater") == 0) - { - inst->low_water = atoi(value); - } - else if (strcmp(options[i], "highwater") == 0) - { - inst->high_water = atoi(value); - } - else if (strcmp(options[i], "shortburst") == 0) - { - inst->short_burst = atoi(value); - } - else if (strcmp(options[i], "longburst") == 0) - { - inst->long_burst = atoi(value); - } - else if (strcmp(options[i], "burstsize") == 0) - { - unsigned long size = atoi(value); - char *ptr = value; - while (*ptr && isdigit(*ptr)) - ptr++; - switch (*ptr) - { - case 'G': - case 'g': - size = size * 1024 * 1000 * 1000; - break; - case 'M': - case 'm': - size = size * 1024 * 1000; - break; - case 'K': - case 'k': - size = size * 1024; - break; - } - inst->burst_size = size; + } + else if (strcmp(options[i], "heartbeat") == 0) + { + int h_val = (int)strtol(value, NULL, 10); - } - else if (strcmp(options[i], "heartbeat") == 0) - { - int h_val = (int)strtol(value, NULL, 10); + if (h_val <= 0 || (errno == ERANGE)) + { + MXS_WARNING("Invalid heartbeat period %s." + " Setting it to default value %ld.", + value, inst->heartbeat); + } + else + { + inst->heartbeat = h_val; + } + } + else if (strcmp(options[i], "send_slave_heartbeat") == 0) + { + inst->send_slave_heartbeat = config_truth_value(value); + } + else if (strcmp(options[i], "binlogdir") == 0) + { + inst->binlogdir = strdup(value); + } + else + { + MXS_WARNING("Unsupported router " + "option %s for binlog router.", + options[i]); + } + } + } + } + else + { + MXS_ERROR("%s: Error: No router options supplied for binlogrouter", + service->name); + } - if (h_val <= 0 || (errno == ERANGE)) { - MXS_WARNING("Invalid heartbeat period %s." - " Setting it to default value %ld.", - value, inst->heartbeat); - } else { - inst->heartbeat = h_val; - } - } - else if (strcmp(options[i], "send_slave_heartbeat") == 0) - { - inst->send_slave_heartbeat = config_truth_value(value); - } - else if (strcmp(options[i], "binlogdir") == 0) - { - inst->binlogdir = strdup(value); - } - else - { - MXS_WARNING("Unsupported router " - "option %s for binlog router.", - options[i]); - } - } - } - } - else - { - MXS_ERROR("%s: Error: No router options supplied for binlogrouter", - service->name); - } + if (inst->fileroot == NULL) + { + inst->fileroot = strdup(BINLOG_NAME_ROOT); + } + inst->active_logs = 0; + inst->reconnect_pending = 0; + inst->handling_threads = 0; + inst->rotating = 0; + inst->residual = NULL; + inst->slaves = NULL; + inst->next = NULL; + inst->lastEventTimestamp = 0; - if (inst->fileroot == NULL) - inst->fileroot = strdup(BINLOG_NAME_ROOT); - inst->active_logs = 0; - inst->reconnect_pending = 0; - inst->handling_threads = 0; - inst->rotating = 0; - inst->residual = NULL; - inst->slaves = NULL; - inst->next = NULL; - inst->lastEventTimestamp = 0; + inst->binlog_position = 0; + inst->current_pos = 0; + inst->current_safe_event = 0; + inst->master_event_state = BLR_EVENT_DONE; - inst->binlog_position = 0; - inst->current_pos = 0; - inst->current_safe_event = 0; - inst->master_event_state = BLR_EVENT_DONE; + strcpy(inst->binlog_name, ""); + strcpy(inst->prevbinlog, ""); - strcpy(inst->binlog_name, ""); - strcpy(inst->prevbinlog, ""); + if ((inst->binlogdir == NULL) || (inst->binlogdir != NULL && !strlen(inst->binlogdir))) + { + MXS_ERROR("Service %s, binlog directory is not specified", + service->name); + free(inst); + return NULL; + } - if ((inst->binlogdir == NULL) || (inst->binlogdir != NULL && !strlen(inst->binlogdir))) { - MXS_ERROR("Service %s, binlog directory is not specified", - service->name); - free(inst); - return NULL; - } + if (inst->serverid <= 0) + { + MXS_ERROR("Service %s, server-id is not configured. " + "Please configure it with a unique positive integer value (1..2^32-1)", + service->name); + free(inst); + return NULL; + } - if (inst->serverid <= 0) { - MXS_ERROR("Service %s, server-id is not configured. " - "Please configure it with a unique positive integer value (1..2^32-1)", - service->name); - free(inst); - return NULL; - } + /** + * If binlogdir is not found create it + * On failure don't start the instance + */ + if (access(inst->binlogdir, R_OK) == -1) + { + int mkdir_rval; + mkdir_rval = mkdir(inst->binlogdir, 0700); + if (mkdir_rval == -1) + { + char err_msg[STRERROR_BUFLEN]; + MXS_ERROR("Service %s, Failed to create binlog directory '%s': [%d] %s", + service->name, + inst->binlogdir, + errno, + strerror_r(errno, err_msg, sizeof(err_msg))); - /** - * If binlogdir is not found create it - * On failure don't start the instance - */ - if (access(inst->binlogdir, R_OK) == -1) { - int mkdir_rval; - mkdir_rval = mkdir(inst->binlogdir, 0700); - if (mkdir_rval == -1) { - char err_msg[STRERROR_BUFLEN]; - MXS_ERROR("Service %s, Failed to create binlog directory '%s': [%d] %s", - service->name, - inst->binlogdir, - errno, - strerror_r(errno, err_msg, sizeof(err_msg))); + free(inst); + return NULL; + } + } - free(inst); - return NULL; - } - } + /* Allocate dbusers for this router here instead of serviceStartPort() */ + if (service->users == NULL) + { + service->users = (void *)mysql_users_alloc(); + if (service->users == NULL) + { + MXS_ERROR("%s: Error allocating dbusers in createInstance", + inst->service->name); - /* Allocate dbusers for this router here instead of serviceStartPort() */ - if (service->users == NULL) { - service->users = (void *)mysql_users_alloc(); - if (service->users == NULL) { - MXS_ERROR("%s: Error allocating dbusers in createInstance", - inst->service->name); + free(inst); + return NULL; + } + } - free(inst); - return NULL; - } - } - - /* Dynamically allocate master_host server struct, not written in anyfile */ - if (service->dbref == NULL) { - SERVICE *service = inst->service; - SERVER *server; - server = server_alloc("_none_", "MySQLBackend", (int)3306); - if (server == NULL) { - MXS_ERROR("%s: Error for server_alloc in createInstance", - inst->service->name); - if (service->users) { - users_free(service->users); + /* Dynamically allocate master_host server struct, not written in anyfile */ + if (service->dbref == NULL) + { + SERVICE *service = inst->service; + SERVER *server; + server = server_alloc("_none_", "MySQLBackend", (int)3306); + if (server == NULL) + { + MXS_ERROR("%s: Error for server_alloc in createInstance", + inst->service->name); + if (service->users) + { + users_free(service->users); service->users = NULL; - } + } - free(inst); - return NULL; - } - server_set_unique_name(server, "binlog_router_master_host"); - serviceAddBackend(service, server); - } + free(inst); + return NULL; + } + server_set_unique_name(server, "binlog_router_master_host"); + serviceAddBackend(service, server); + } - /* - * Check for master.ini file with master connection details - * If not found a CHANGE MASTER TO is required via mysqsl client. - * Use START SLAVE for replication startup. - * - * If existent master.ini will be used for - * automatic master replication start phase - */ + /* + * Check for master.ini file with master connection details + * If not found a CHANGE MASTER TO is required via mysqsl client. + * Use START SLAVE for replication startup. + * + * If existent master.ini will be used for + * automatic master replication start phase + */ - strncpy(path, inst->binlogdir, PATH_MAX); - snprintf(filename,PATH_MAX, "%s/master.ini", path); + strncpy(path, inst->binlogdir, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/master.ini", path); - rc = ini_parse(filename, blr_handler_config, inst); + rc = ini_parse(filename, blr_handler_config, inst); - MXS_INFO("%s: %s parse result is %d", - inst->service->name, - filename, - rc); + MXS_INFO("%s: %s parse result is %d", + inst->service->name, + filename, + rc); - /* - * retcode: - * -1 file not found, 0 parsing ok, > 0 error parsing the content - */ + /* + * retcode: + * -1 file not found, 0 parsing ok, > 0 error parsing the content + */ - if (rc != 0) { - if (rc == -1) { - MXS_ERROR("%s: master.ini file not found in %s." - " Master registration cannot be started." - " Configure with CHANGE MASTER TO ...", - inst->service->name, inst->binlogdir); - } else { - MXS_ERROR("%s: master.ini file with errors in %s." - " Master registration cannot be started." - " Fix errors in it or configure with CHANGE MASTER TO ...", - inst->service->name, inst->binlogdir); - } + if (rc != 0) + { + if (rc == -1) + { + MXS_ERROR("%s: master.ini file not found in %s." + " Master registration cannot be started." + " Configure with CHANGE MASTER TO ...", + inst->service->name, inst->binlogdir); + } + else + { + MXS_ERROR("%s: master.ini file with errors in %s." + " Master registration cannot be started." + " Fix errors in it or configure with CHANGE MASTER TO ...", + inst->service->name, inst->binlogdir); + } - /* Set service user or load db users */ - blr_set_service_mysql_user(inst->service); + /* Set service user or load db users */ + blr_set_service_mysql_user(inst->service); - } else { - inst->master_state = BLRM_UNCONNECTED; + } + else + { + inst->master_state = BLRM_UNCONNECTED; - /* Try loading dbusers */ - blr_load_dbusers(inst); - } + /* Try loading dbusers */ + blr_load_dbusers(inst); + } - /** - * Initialise the binlog router - */ - if (inst->master_state == BLRM_UNCONNECTED) { + /** + * Initialise the binlog router + */ + if (inst->master_state == BLRM_UNCONNECTED) + { - /* Read any cached response messages */ - blr_cache_read_master_data(inst); + /* Read any cached response messages */ + blr_cache_read_master_data(inst); - /* Find latest binlog file or create a new one (000001) */ - if (blr_file_init(inst) == 0) - { - MXS_ERROR("%s: Service not started due to lack of binlog directory %s", - service->name, - inst->binlogdir); + /* Find latest binlog file or create a new one (000001) */ + if (blr_file_init(inst) == 0) + { + MXS_ERROR("%s: Service not started due to lack of binlog directory %s", + service->name, + inst->binlogdir); - if (service->users) { - users_free(service->users); + if (service->users) + { + users_free(service->users); service->users = NULL; - } + } - if (service->dbref && service->dbref->server) { - server_free(service->dbref->server); - free(service->dbref); - } + if (service->dbref && service->dbref->server) + { + server_free(service->dbref->server); + free(service->dbref); + } - free(inst); - return NULL; - } - } + free(inst); + return NULL; + } + } - /** - * We have completed the creation of the instance data, so now - * insert this router instance into the linked list of routers - * that have been created with this module. - */ - spinlock_acquire(&instlock); - inst->next = instances; - instances = inst; - spinlock_release(&instlock); + /** + * We have completed the creation of the instance data, so now + * insert this router instance into the linked list of routers + * that have been created with this module. + */ + spinlock_acquire(&instlock); + inst->next = instances; + instances = inst; + spinlock_release(&instlock); - /* - * Initialise the binlog cache for this router instance - */ - blr_init_cache(inst); + /* + * Initialise the binlog cache for this router instance + */ + blr_init_cache(inst); - /* - * Add tasks for statistic computation - */ - snprintf(task_name, BLRM_TASK_NAME_LEN, "%s stats", service->name); - hktask_add(task_name, stats_func, inst, BLR_STATS_FREQ); + /* + * Add tasks for statistic computation + */ + snprintf(task_name, BLRM_TASK_NAME_LEN, "%s stats", service->name); + hktask_add(task_name, stats_func, inst, BLR_STATS_FREQ); - /* Log whether the transaction safety option value is on*/ - if (inst->trx_safe) { - MXS_INFO("%s: Service has transaction safety option set to ON", - service->name); - } + /* Log whether the transaction safety option value is on*/ + if (inst->trx_safe) + { + MXS_INFO("%s: Service has transaction safety option set to ON", + service->name); + } - /** - * Check whether replication can be started - */ - if (inst->master_state == BLRM_UNCONNECTED) { - /* Check current binlog */ - MXS_NOTICE("Validating binlog file '%s' ...", - inst->binlog_name); + /** + * Check whether replication can be started + */ + if (inst->master_state == BLRM_UNCONNECTED) + { + /* Check current binlog */ + MXS_NOTICE("Validating binlog file '%s' ...", + inst->binlog_name); - if (inst->trx_safe && !blr_check_binlog(inst)) { - /* Don't start replication, just return */ - return (ROUTER *)inst; - } + if (inst->trx_safe && !blr_check_binlog(inst)) + { + /* Don't start replication, just return */ + return (ROUTER *)inst; + } - if (!inst->trx_safe) { - MXS_INFO("Current binlog file is %s, current pos is %lu\n", - inst->binlog_name, inst->binlog_position); - } else { - MXS_INFO("Current binlog file is %s, safe pos %lu, current pos is %lu\n", - inst->binlog_name, inst->binlog_position, inst->current_pos); - } + if (!inst->trx_safe) + { + MXS_INFO("Current binlog file is %s, current pos is %lu\n", + inst->binlog_name, inst->binlog_position); + } + else + { + MXS_INFO("Current binlog file is %s, safe pos %lu, current pos is %lu\n", + inst->binlog_name, inst->binlog_position, inst->current_pos); + } - /* Start replication from master server */ - blr_start_master(inst); - } + /* Start replication from master server */ + blr_start_master(inst); + } - return (ROUTER *)inst; + return (ROUTER *)inst; } /** @@ -717,66 +747,65 @@ char task_name[BLRM_TASK_NAME_LEN+1] = ""; * connecting to MaxScale and requesting binlog records. We need to go * through the slave registration process for this new slave. * - * @param instance The router instance data - * @param session The session itself + * @param instance The router instance data + * @param session The session itself * @return Session specific data for this session */ -static void * +static void * newSession(ROUTER *instance, SESSION *session) { -ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance; -ROUTER_SLAVE *slave; + ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance; + ROUTER_SLAVE *slave; - MXS_DEBUG("binlog router: %lu [newSession] new router session with " - "session %p, and inst %p.", - pthread_self(), - session, - inst); + MXS_DEBUG("binlog router: %lu [newSession] new router session with " + "session %p, and inst %p.", + pthread_self(), + session, + inst); - - if ((slave = (ROUTER_SLAVE *)calloc(1, sizeof(ROUTER_SLAVE))) == NULL) - { - MXS_ERROR("Insufficient memory to create new slave session for binlog router"); - return NULL; - } + if ((slave = (ROUTER_SLAVE *)calloc(1, sizeof(ROUTER_SLAVE))) == NULL) + { + MXS_ERROR("Insufficient memory to create new slave session for binlog router"); + return NULL; + } #if defined(SS_DEBUG) - slave->rses_chk_top = CHK_NUM_ROUTER_SES; - slave->rses_chk_tail = CHK_NUM_ROUTER_SES; + slave->rses_chk_top = CHK_NUM_ROUTER_SES; + slave->rses_chk_tail = CHK_NUM_ROUTER_SES; #endif - memset(&slave->stats, 0, sizeof(SLAVE_STATS)); - atomic_add(&inst->stats.n_slaves, 1); - slave->state = BLRS_CREATED; /* Set initial state of the slave */ - slave->cstate = 0; - slave->pthread = 0; - slave->overrun = 0; - slave->uuid = NULL; - slave->hostname = NULL; - spinlock_init(&slave->catch_lock); - slave->dcb = session->client_dcb; - slave->router = inst; + memset(&slave->stats, 0, sizeof(SLAVE_STATS)); + atomic_add(&inst->stats.n_slaves, 1); + slave->state = BLRS_CREATED; /* Set initial state of the slave */ + slave->cstate = 0; + slave->pthread = 0; + slave->overrun = 0; + slave->uuid = NULL; + slave->hostname = NULL; + spinlock_init(&slave->catch_lock); + slave->dcb = session->client_dcb; + slave->router = inst; #ifdef BLFILE_IN_SLAVE - slave->file = NULL; + slave->file = NULL; #endif - strcpy(slave->binlogfile, "unassigned"); - slave->connect_time = time(0); - slave->lastEventTimestamp = 0; - slave->mariadb10_compat = false; - slave->heartbeat = 0; - slave->lastEventReceived = 0; + strcpy(slave->binlogfile, "unassigned"); + slave->connect_time = time(0); + slave->lastEventTimestamp = 0; + slave->mariadb10_compat = false; + slave->heartbeat = 0; + slave->lastEventReceived = 0; - /** + /** * Add this session to the list of active sessions. */ - spinlock_acquire(&inst->lock); - slave->next = inst->slaves; - inst->slaves = slave; - spinlock_release(&inst->lock); + spinlock_acquire(&inst->lock); + slave->next = inst->slaves; + inst->slaves = slave; + spinlock_release(&inst->lock); - CHK_CLIENT_RSES(slave); + CHK_CLIENT_RSES(slave); - return (void *)slave; + return (void *)slave; } /** @@ -787,55 +816,65 @@ ROUTER_SLAVE *slave; * and prevent any further forwarding of binlog records to that slave. * * Parameters: - * @param router_instance The instance of the router - * @param router_cli_ses The particular session to free + * @param router_instance The instance of the router + * @param router_cli_ses The particular session to free * */ -static void freeSession( - ROUTER* router_instance, - void* router_client_ses) +static void freeSession(ROUTER* router_instance, + void* router_client_ses) { -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_instance; -ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_client_ses; -int prev_val; + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_instance; + ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_client_ses; + int prev_val; - prev_val = atomic_add(&router->stats.n_slaves, -1); - ss_dassert(prev_val > 0); + prev_val = atomic_add(&router->stats.n_slaves, -1); + ss_dassert(prev_val > 0); - /* - * Remove the slave session form the list of slaves that are using the - * router currently. - */ - spinlock_acquire(&router->lock); - if (router->slaves == slave) { - router->slaves = slave->next; - } else { - ROUTER_SLAVE *ptr = router->slaves; + /* + * Remove the slave session form the list of slaves that are using the + * router currently. + */ + spinlock_acquire(&router->lock); + if (router->slaves == slave) + { + router->slaves = slave->next; + } + else + { + ROUTER_SLAVE *ptr = router->slaves; - while (ptr != NULL && ptr->next != slave) { - ptr = ptr->next; - } + while (ptr != NULL && ptr->next != slave) + { + ptr = ptr->next; + } - if (ptr != NULL) { - ptr->next = slave->next; - } - } - spinlock_release(&router->lock); + if (ptr != NULL) + { + ptr->next = slave->next; + } + } + spinlock_release(&router->lock); - MXS_DEBUG("%lu [freeSession] Unlinked router_client_session %p from " - "router %p. Connections : %d. ", - pthread_self(), - slave, - router, - prev_val-1); + MXS_DEBUG("%lu [freeSession] Unlinked router_client_session %p from " + "router %p. Connections : %d. ", + pthread_self(), + slave, + router, + prev_val - 1); - if (slave->hostname) - free(slave->hostname); - if (slave->user) - free(slave->user); - if (slave->passwd) - free(slave->passwd); - free(slave); + if (slave->hostname) + { + free(slave->hostname); + } + if (slave->user) + { + free(slave->user); + } + if (slave->passwd) + { + free(slave->passwd); + } + free(slave); } @@ -843,76 +882,81 @@ int prev_val; * Close a session with the router, this is the mechanism * by which a router may cleanup data structure etc. * - * @param instance The router instance data - * @param router_session The session being closed + * @param instance The router instance data + * @param router_session The session being closed */ -static void +static void closeSession(ROUTER *instance, void *router_session) { -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; -ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; + ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; - if (slave == NULL) - { - /* - * We must be closing the master session. - */ - MXS_NOTICE("%s: Master %s disconnected after %ld seconds. " - "%lu events read,", - router->service->name, router->service->dbref->server->name, - time(0) - router->connect_time, router->stats.n_binlogs_ses); - MXS_ERROR("Binlog router close session with master server %s", - router->service->dbref->server->unique_name); - blr_master_reconnect(router); - return; - } - CHK_CLIENT_RSES(slave); - - /** - * Lock router client session for secure read and update. + if (slave == NULL) + { + /* + * We must be closing the master session. */ - if (rses_begin_locked_router_action(slave)) + MXS_NOTICE("%s: Master %s disconnected after %ld seconds. " + "%lu events read,", + router->service->name, router->service->dbref->server->name, + time(0) - router->connect_time, router->stats.n_binlogs_ses); + MXS_ERROR("Binlog router close session with master server %s", + router->service->dbref->server->unique_name); + blr_master_reconnect(router); + return; + } + CHK_CLIENT_RSES(slave); + + /** + * Lock router client session for secure read and update. + */ + if (rses_begin_locked_router_action(slave)) + { + /* decrease server registered slaves counter */ + atomic_add(&router->stats.n_registered, -1); + + if (slave->state > 0) { - /* decrease server registered slaves counter */ - atomic_add(&router->stats.n_registered, -1); + MXS_NOTICE("%s: Slave %s:%d, server id %d, disconnected after %ld seconds. " + "%d SQL commands, %d events sent (%lu bytes), binlog '%s', " + "last position %lu", + router->service->name, slave->dcb->remote, ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + time(0) - slave->connect_time, + slave->stats.n_queries, + slave->stats.n_events, + slave->stats.n_bytes, + slave->binlogfile, + (unsigned long)slave->binlog_pos); + } + else + { + MXS_NOTICE("%s: Slave %s, server id %d, disconnected after %ld seconds. " + "%d SQL commands", + router->service->name, slave->dcb->remote, + slave->serverid, + time(0) - slave->connect_time, + slave->stats.n_queries); + } - if (slave->state > 0) { - MXS_NOTICE("%s: Slave %s:%d, server id %d, disconnected after %ld seconds. " - "%d SQL commands, %d events sent (%lu bytes), binlog '%s', " - "last position %lu", - router->service->name, slave->dcb->remote, ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - time(0) - slave->connect_time, - slave->stats.n_queries, - slave->stats.n_events, - slave->stats.n_bytes, - slave->binlogfile, - (unsigned long)slave->binlog_pos); - } else { - MXS_NOTICE("%s: Slave %s, server id %d, disconnected after %ld seconds. " - "%d SQL commands", - router->service->name, slave->dcb->remote, - slave->serverid, - time(0) - slave->connect_time, - slave->stats.n_queries); - } - - /* - * Mark the slave as unregistered to prevent the forwarding - * of any more binlog records to this slave. - */ - slave->state = BLRS_UNREGISTERED; + /* + * Mark the slave as unregistered to prevent the forwarding + * of any more binlog records to this slave. + */ + slave->state = BLRS_UNREGISTERED; #if BLFILE_IN_SLAVE - // TODO: Is it really certain the file can be closed here? If other - // TODO: threads are using the slave instance, bag things will happen. [JWi]. - if (slave->file) - blr_close_binlog(router, slave->file); + // TODO: Is it really certain the file can be closed here? If other + // TODO: threads are using the slave instance, bag things will happen. [JWi]. + if (slave->file) + { + blr_close_binlog(router, slave->file); + } #endif - /* Unlock */ - rses_end_locked_router_action(slave); - } + /* Unlock */ + rses_end_locked_router_action(slave); + } } /** @@ -922,383 +966,460 @@ ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; * the return of either predefined server responses that have been cached * or binlog records. * - * @param instance The router instance - * @param router_session The router session returned from the newSession call - * @param queue The queue of data buffers to route + * @param instance The router instance + * @param router_session The router session returned from the newSession call + * @param queue The queue of data buffers to route * @return The number of bytes sent */ -static int +static int routeQuery(ROUTER *instance, void *router_session, GWBUF *queue) { -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; -ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; + ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; - return blr_slave_request(router, slave, queue); + return blr_slave_request(router, slave, queue); } -static char *event_names[] = { - "Invalid", "Start Event V3", "Query Event", "Stop Event", "Rotate Event", - "Integer Session Variable", "Load Event", "Slave Event", "Create File Event", - "Append Block Event", "Exec Load Event", "Delete File Event", - "New Load Event", "Rand Event", "User Variable Event", "Format Description Event", - "Transaction ID Event (2 Phase Commit)", "Begin Load Query Event", - "Execute Load Query Event", "Table Map Event", "Write Rows Event (v0)", - "Update Rows Event (v0)", "Delete Rows Event (v0)", "Write Rows Event (v1)", - "Update Rows Event (v1)", "Delete Rows Event (v1)", "Incident Event", - "Heartbeat Event", "Ignorable Event", "Rows Query Event", "Write Rows Event (v2)", - "Update Rows Event (v2)", "Delete Rows Event (v2)", "GTID Event", - "Anonymous GTID Event", "Previous GTIDS Event" +static char *event_names[] = +{ + "Invalid", "Start Event V3", "Query Event", "Stop Event", "Rotate Event", + "Integer Session Variable", "Load Event", "Slave Event", "Create File Event", + "Append Block Event", "Exec Load Event", "Delete File Event", + "New Load Event", "Rand Event", "User Variable Event", "Format Description Event", + "Transaction ID Event (2 Phase Commit)", "Begin Load Query Event", + "Execute Load Query Event", "Table Map Event", "Write Rows Event (v0)", + "Update Rows Event (v0)", "Delete Rows Event (v0)", "Write Rows Event (v1)", + "Update Rows Event (v1)", "Delete Rows Event (v1)", "Incident Event", + "Heartbeat Event", "Ignorable Event", "Rows Query Event", "Write Rows Event (v2)", + "Update Rows Event (v2)", "Delete Rows Event (v2)", "GTID Event", + "Anonymous GTID Event", "Previous GTIDS Event" }; /* New MariaDB event numbers starts from 0xa0 */ -static char *event_names_mariadb10[] = { - "Annotate Rows Event", - /* New MariaDB 10.x event numbers */ - "Binlog Checkpoint Event", - "GTID Event", - "GTID List Event" +static char *event_names_mariadb10[] = +{ + "Annotate Rows Event", + /* New MariaDB 10.x event numbers */ + "Binlog Checkpoint Event", + "GTID Event", + "GTID List Event" }; /** * Display an entry from the spinlock statistics data * - * @param dcb The DCB to print to - * @param desc Description of the statistic - * @param value The statistic value + * @param dcb The DCB to print to + * @param desc Description of the statistic + * @param value The statistic value */ static void spin_reporter(void *dcb, char *desc, int value) { - dcb_printf((DCB *)dcb, "\t\t%-35s %d\n", desc, value); + dcb_printf((DCB *)dcb, "\t\t%-35s %d\n", desc, value); } /** * Display router diagnostics * - * @param instance Instance of the router - * @param dcb DCB to send diagnostics to + * @param instance Instance of the router + * @param dcb DCB to send diagnostics to */ -static void +static void diagnostics(ROUTER *router, DCB *dcb) { -ROUTER_INSTANCE *router_inst = (ROUTER_INSTANCE *)router; -ROUTER_SLAVE *session; -int i = 0, j; -int minno = 0; -double min5, min10, min15, min30; -char buf[40]; -struct tm tm; + ROUTER_INSTANCE *router_inst = (ROUTER_INSTANCE *)router; + ROUTER_SLAVE *session; + int i = 0, j; + int minno = 0; + double min5, min10, min15, min30; + char buf[40]; + struct tm tm; - spinlock_acquire(&router_inst->lock); - session = router_inst->slaves; - while (session) - { - i++; - session = session->next; - } - spinlock_release(&router_inst->lock); + spinlock_acquire(&router_inst->lock); + session = router_inst->slaves; + while (session) + { + i++; + session = session->next; + } + spinlock_release(&router_inst->lock); - minno = router_inst->stats.minno; - min30 = 0.0; - min15 = 0.0; - min10 = 0.0; - min5 = 0.0; - for (j = 0; j < 30; j++) - { - minno--; - if (minno < 0) - minno += 30; - min30 += router_inst->stats.minavgs[minno]; - if (j < 15) - min15 += router_inst->stats.minavgs[minno]; - if (j < 10) - min10 += router_inst->stats.minavgs[minno]; - if (j < 5) - min5 += router_inst->stats.minavgs[minno]; - } - min30 /= 30.0; - min15 /= 15.0; - min10 /= 10.0; - min5 /= 5.0; + minno = router_inst->stats.minno; + min30 = 0.0; + min15 = 0.0; + min10 = 0.0; + min5 = 0.0; + for (j = 0; j < 30; j++) + { + minno--; + if (minno < 0) + { + minno += 30; + } + min30 += router_inst->stats.minavgs[minno]; + if (j < 15) + { + min15 += router_inst->stats.minavgs[minno]; + } + if (j < 10) + { + min10 += router_inst->stats.minavgs[minno]; + } + if (j < 5) + { + min5 += router_inst->stats.minavgs[minno]; + } + } + min30 /= 30.0; + min15 /= 15.0; + min10 /= 10.0; + min5 /= 5.0; - if (router_inst->master) - dcb_printf(dcb, "\tMaster connection DCB: %p\n", - router_inst->master); - else - dcb_printf(dcb, "\tMaster connection DCB: 0x0\n"); + if (router_inst->master) + { + dcb_printf(dcb, "\tMaster connection DCB: %p\n", + router_inst->master); + } + else + { + dcb_printf(dcb, "\tMaster connection DCB: 0x0\n"); + } - dcb_printf(dcb, "\tMaster connection state: %s\n", - blrm_states[router_inst->master_state]); + dcb_printf(dcb, "\tMaster connection state: %s\n", + blrm_states[router_inst->master_state]); - localtime_r(&router_inst->stats.lastReply, &tm); - asctime_r(&tm, buf); + localtime_r(&router_inst->stats.lastReply, &tm); + asctime_r(&tm, buf); - dcb_printf(dcb, "\tBinlog directory: %s\n", - router_inst->binlogdir); - dcb_printf(dcb, "\tHeartbeat period (seconds): %lu\n", - router_inst->heartbeat); - dcb_printf(dcb, "\tNumber of master connects: %d\n", - router_inst->stats.n_masterstarts); - dcb_printf(dcb, "\tNumber of delayed reconnects: %d\n", - router_inst->stats.n_delayedreconnects); - dcb_printf(dcb, "\tCurrent binlog file: %s\n", - router_inst->binlog_name); - dcb_printf(dcb, "\tCurrent binlog position: %lu\n", - router_inst->current_pos); - if (router_inst->trx_safe) { - if (router_inst->pending_transaction) { - dcb_printf(dcb, "\tCurrent open transaction pos: %lu\n", - router_inst->binlog_position); - } - } - dcb_printf(dcb, "\tNumber of slave servers: %u\n", - router_inst->stats.n_slaves); - dcb_printf(dcb, "\tNo. of binlog events received this session: %u\n", - router_inst->stats.n_binlogs_ses); - dcb_printf(dcb, "\tTotal no. of binlog events received: %u\n", - router_inst->stats.n_binlogs); - dcb_printf(dcb, "\tNo. of bad CRC received from master: %u\n", - router_inst->stats.n_badcrc); - minno = router_inst->stats.minno - 1; - if (minno == -1) - minno = 30; - dcb_printf(dcb, "\tNumber of binlog events per minute\n"); - dcb_printf(dcb, "\tCurrent 5 10 15 30 Min Avg\n"); - dcb_printf(dcb, "\t %6d %8.1f %8.1f %8.1f %8.1f\n", - router_inst->stats.minavgs[minno], min5, min10, min15, min30); - dcb_printf(dcb, "\tNumber of fake binlog events: %u\n", - router_inst->stats.n_fakeevents); - dcb_printf(dcb, "\tNumber of artificial binlog events: %u\n", - router_inst->stats.n_artificial); - dcb_printf(dcb, "\tNumber of binlog events in error: %u\n", - router_inst->stats.n_binlog_errors); - dcb_printf(dcb, "\tNumber of binlog rotate events: %u\n", - router_inst->stats.n_rotates); - dcb_printf(dcb, "\tNumber of heartbeat events: %u\n", - router_inst->stats.n_heartbeats); - dcb_printf(dcb, "\tNumber of packets received: %u\n", - router_inst->stats.n_reads); - dcb_printf(dcb, "\tNumber of residual data packets: %u\n", - router_inst->stats.n_residuals); - dcb_printf(dcb, "\tAverage events per packet: %.1f\n", - router_inst->stats.n_reads != 0 ? ((double)router_inst->stats.n_binlogs / router_inst->stats.n_reads) : 0); + dcb_printf(dcb, "\tBinlog directory: %s\n", + router_inst->binlogdir); + dcb_printf(dcb, "\tHeartbeat period (seconds): %lu\n", + router_inst->heartbeat); + dcb_printf(dcb, "\tNumber of master connects: %d\n", + router_inst->stats.n_masterstarts); + dcb_printf(dcb, "\tNumber of delayed reconnects: %d\n", + router_inst->stats.n_delayedreconnects); + dcb_printf(dcb, "\tCurrent binlog file: %s\n", + router_inst->binlog_name); + dcb_printf(dcb, "\tCurrent binlog position: %lu\n", + router_inst->current_pos); + if (router_inst->trx_safe) + { + if (router_inst->pending_transaction) + { + dcb_printf(dcb, "\tCurrent open transaction pos: %lu\n", + router_inst->binlog_position); + } + } + dcb_printf(dcb, "\tNumber of slave servers: %u\n", + router_inst->stats.n_slaves); + dcb_printf(dcb, "\tNo. of binlog events received this session: %u\n", + router_inst->stats.n_binlogs_ses); + dcb_printf(dcb, "\tTotal no. of binlog events received: %u\n", + router_inst->stats.n_binlogs); + dcb_printf(dcb, "\tNo. of bad CRC received from master: %u\n", + router_inst->stats.n_badcrc); + minno = router_inst->stats.minno - 1; + if (minno == -1) + { + minno = 30; + } + dcb_printf(dcb, "\tNumber of binlog events per minute\n"); + dcb_printf(dcb, "\tCurrent 5 10 15 30 Min Avg\n"); + dcb_printf(dcb, "\t %6d %8.1f %8.1f %8.1f %8.1f\n", + router_inst->stats.minavgs[minno], min5, min10, min15, min30); + dcb_printf(dcb, "\tNumber of fake binlog events: %u\n", + router_inst->stats.n_fakeevents); + dcb_printf(dcb, "\tNumber of artificial binlog events: %u\n", + router_inst->stats.n_artificial); + dcb_printf(dcb, "\tNumber of binlog events in error: %u\n", + router_inst->stats.n_binlog_errors); + dcb_printf(dcb, "\tNumber of binlog rotate events: %u\n", + router_inst->stats.n_rotates); + dcb_printf(dcb, "\tNumber of heartbeat events: %u\n", + router_inst->stats.n_heartbeats); + dcb_printf(dcb, "\tNumber of packets received: %u\n", + router_inst->stats.n_reads); + dcb_printf(dcb, "\tNumber of residual data packets: %u\n", + router_inst->stats.n_residuals); + dcb_printf(dcb, "\tAverage events per packet: %.1f\n", + router_inst->stats.n_reads != 0 ? + ((double)router_inst->stats.n_binlogs / router_inst->stats.n_reads) : 0); - spinlock_acquire(&router_inst->lock); - if (router_inst->stats.lastReply) { - if (buf[strlen(buf)-1] == '\n') { - buf[strlen(buf)-1] = '\0'; - } - dcb_printf(dcb, "\tLast event from master at: %s (%d seconds ago)\n", - buf, time(0) - router_inst->stats.lastReply); + spinlock_acquire(&router_inst->lock); + if (router_inst->stats.lastReply) + { + if (buf[strlen(buf) - 1] == '\n') + { + buf[strlen(buf) - 1] = '\0'; + } + dcb_printf(dcb, "\tLast event from master at: %s (%d seconds ago)\n", + buf, time(0) - router_inst->stats.lastReply); - if (!router_inst->mariadb10_compat) { - dcb_printf(dcb, "\tLast event from master: 0x%x, %s\n", - router_inst->lastEventReceived, - (router_inst->lastEventReceived <= MAX_EVENT_TYPE) ? - event_names[router_inst->lastEventReceived] : "unknown"); - } else { - char *ptr = NULL; - if (router_inst->lastEventReceived <= MAX_EVENT_TYPE) { - ptr = event_names[router_inst->lastEventReceived]; - } else { - /* Check MariaDB 10 new events */ - if (router_inst->lastEventReceived >= MARIADB_NEW_EVENTS_BEGIN && router_inst->lastEventReceived <= MAX_EVENT_TYPE_MARIADB10) { - ptr = event_names_mariadb10[(router_inst->lastEventReceived - MARIADB_NEW_EVENTS_BEGIN)]; - } - } + if (!router_inst->mariadb10_compat) + { + dcb_printf(dcb, "\tLast event from master: 0x%x, %s\n", + router_inst->lastEventReceived, + (router_inst->lastEventReceived >= 0 && + router_inst->lastEventReceived <= MAX_EVENT_TYPE) ? + event_names[router_inst->lastEventReceived] : "unknown"); + } + else + { + char *ptr = NULL; + if (router_inst->lastEventReceived >= 0 && router_inst->lastEventReceived <= MAX_EVENT_TYPE) + { + ptr = event_names[router_inst->lastEventReceived]; + } + else + { + /* Check MariaDB 10 new events */ + if (router_inst->lastEventReceived >= MARIADB_NEW_EVENTS_BEGIN && + router_inst->lastEventReceived <= MAX_EVENT_TYPE_MARIADB10) + { + ptr = event_names_mariadb10[(router_inst->lastEventReceived - MARIADB_NEW_EVENTS_BEGIN)]; + } + } - dcb_printf(dcb, "\tLast event from master: 0x%x, %s\n", - router_inst->lastEventReceived, (ptr != NULL) ? ptr : "unknown"); - } + dcb_printf(dcb, "\tLast event from master: 0x%x, %s\n", + router_inst->lastEventReceived, (ptr != NULL) ? ptr : "unknown"); + } - if (router_inst->lastEventTimestamp) { - time_t last_event = (time_t)router_inst->lastEventTimestamp; - localtime_r(&last_event, &tm); - asctime_r(&tm, buf); - if (buf[strlen(buf)-1] == '\n') { - buf[strlen(buf)-1] = '\0'; - } - dcb_printf(dcb, "\tLast binlog event timestamp: %ld (%s)\n", - router_inst->lastEventTimestamp, buf); - } - } else { - dcb_printf(dcb, "\tNo events received from master yet\n"); - } - spinlock_release(&router_inst->lock); + if (router_inst->lastEventTimestamp) + { + time_t last_event = (time_t)router_inst->lastEventTimestamp; + localtime_r(&last_event, &tm); + asctime_r(&tm, buf); + if (buf[strlen(buf) - 1] == '\n') + { + buf[strlen(buf) - 1] = '\0'; + } + dcb_printf(dcb, "\tLast binlog event timestamp: %ld (%s)\n", + router_inst->lastEventTimestamp, buf); + } + } + else + { + dcb_printf(dcb, "\tNo events received from master yet\n"); + } + spinlock_release(&router_inst->lock); - if (router_inst->active_logs) - dcb_printf(dcb, "\tRouter processing binlog records\n"); - if (router_inst->reconnect_pending) - dcb_printf(dcb, "\tRouter pending reconnect to master\n"); - dcb_printf(dcb, "\tEvents received:\n"); - for (i = 0; i <= MAX_EVENT_TYPE; i++) - { - dcb_printf(dcb, "\t\t%-38s %u\n", event_names[i], router_inst->stats.events[i]); - } + if (router_inst->active_logs) + { + dcb_printf(dcb, "\tRouter processing binlog records\n"); + } + if (router_inst->reconnect_pending) + { + dcb_printf(dcb, "\tRouter pending reconnect to master\n"); + } + dcb_printf(dcb, "\tEvents received:\n"); + for (i = 0; i <= MAX_EVENT_TYPE; i++) + { + dcb_printf(dcb, "\t\t%-38s %u\n", event_names[i], router_inst->stats.events[i]); + } - if (router_inst->mariadb10_compat) { - /* Display MariaDB 10 new events */ - for (i = MARIADB_NEW_EVENTS_BEGIN; i <= MAX_EVENT_TYPE_MARIADB10; i++) - dcb_printf(dcb, "\t\tMariaDB 10 %-38s %u\n", event_names_mariadb10[(i - MARIADB_NEW_EVENTS_BEGIN)], router_inst->stats.events[i]); - } + if (router_inst->mariadb10_compat) + { + /* Display MariaDB 10 new events */ + for (i = MARIADB_NEW_EVENTS_BEGIN; i <= MAX_EVENT_TYPE_MARIADB10; i++) + { + dcb_printf(dcb, "\t\tMariaDB 10 %-38s %u\n", + event_names_mariadb10[(i - MARIADB_NEW_EVENTS_BEGIN)], + router_inst->stats.events[i]); + } + } #if SPINLOCK_PROFILE - dcb_printf(dcb, "\tSpinlock statistics (instlock):\n"); - spinlock_stats(&instlock, spin_reporter, dcb); - dcb_printf(dcb, "\tSpinlock statistics (instance lock):\n"); - spinlock_stats(&router_inst->lock, spin_reporter, dcb); - dcb_printf(dcb, "\tSpinlock statistics (binlog position lock):\n"); - spinlock_stats(&router_inst->binlog_lock, spin_reporter, dcb); + dcb_printf(dcb, "\tSpinlock statistics (instlock):\n"); + spinlock_stats(&instlock, spin_reporter, dcb); + dcb_printf(dcb, "\tSpinlock statistics (instance lock):\n"); + spinlock_stats(&router_inst->lock, spin_reporter, dcb); + dcb_printf(dcb, "\tSpinlock statistics (binlog position lock):\n"); + spinlock_stats(&router_inst->binlog_lock, spin_reporter, dcb); #endif - if (router_inst->slaves) - { - dcb_printf(dcb, "\tSlaves:\n"); - spinlock_acquire(&router_inst->lock); - session = router_inst->slaves; - while (session) - { + if (router_inst->slaves) + { + dcb_printf(dcb, "\tSlaves:\n"); + spinlock_acquire(&router_inst->lock); + session = router_inst->slaves; + while (session) + { - minno = session->stats.minno; - min30 = 0.0; - min15 = 0.0; - min10 = 0.0; - min5 = 0.0; - for (j = 0; j < 30; j++) - { - minno--; - if (minno < 0) - minno += 30; - min30 += session->stats.minavgs[minno]; - if (j < 15) - min15 += session->stats.minavgs[minno]; - if (j < 10) - min10 += session->stats.minavgs[minno]; - if (j < 5) - min5 += session->stats.minavgs[minno]; - } - min30 /= 30.0; - min15 /= 15.0; - min10 /= 10.0; - min5 /= 5.0; - dcb_printf(dcb, - "\t\tServer-id: %d\n", - session->serverid); - if (session->hostname) - dcb_printf(dcb, "\t\tHostname: %s\n", session->hostname); - if (session->uuid) - dcb_printf(dcb, "\t\tSlave UUID: %s\n", session->uuid); - dcb_printf(dcb, - "\t\tSlave_host_port: %s:%d\n", - session->dcb->remote, ntohs((session->dcb->ipv4).sin_port)); - dcb_printf(dcb, - "\t\tUsername: %s\n", - session->dcb->user); - dcb_printf(dcb, - "\t\tSlave DCB: %p\n", - session->dcb); - dcb_printf(dcb, - "\t\tNext Sequence No: %d\n", - session->seqno); - dcb_printf(dcb, - "\t\tState: %s\n", - blrs_states[session->state]); - dcb_printf(dcb, - "\t\tBinlog file: %s\n", - session->binlogfile); - dcb_printf(dcb, - "\t\tBinlog position: %u\n", - session->binlog_pos); - if (session->nocrc) - dcb_printf(dcb, - "\t\tMaster Binlog CRC: None\n"); - dcb_printf(dcb, - "\t\tNo. requests: %u\n", - session->stats.n_requests); - dcb_printf(dcb, - "\t\tNo. events sent: %u\n", - session->stats.n_events); - dcb_printf(dcb, - "\t\tNo. bytes sent: %u\n", - session->stats.n_bytes); - dcb_printf(dcb, - "\t\tNo. bursts sent: %u\n", - session->stats.n_bursts); - dcb_printf(dcb, - "\t\tNo. transitions to follow mode: %u\n", - session->stats.n_bursts); - if (router_inst->send_slave_heartbeat) - dcb_printf(dcb, "\t\tHeartbeat period (seconds): %lu\n", - session->heartbeat); + minno = session->stats.minno; + min30 = 0.0; + min15 = 0.0; + min10 = 0.0; + min5 = 0.0; + for (j = 0; j < 30; j++) + { + minno--; + if (minno < 0) + { + minno += 30; + } + min30 += session->stats.minavgs[minno]; + if (j < 15) + { + min15 += session->stats.minavgs[minno]; + } + if (j < 10) + { + min10 += session->stats.minavgs[minno]; + } + if (j < 5) + { + min5 += session->stats.minavgs[minno]; + } + } + min30 /= 30.0; + min15 /= 15.0; + min10 /= 10.0; + min5 /= 5.0; + dcb_printf(dcb, + "\t\tServer-id: %d\n", + session->serverid); + if (session->hostname) + { + dcb_printf(dcb, + "\t\tHostname: %s\n", session->hostname); + } + if (session->uuid) + { + dcb_printf(dcb, "\t\tSlave UUID: %s\n", session->uuid); + } + dcb_printf(dcb, + "\t\tSlave_host_port: %s:%d\n", + session->dcb->remote, ntohs((session->dcb->ipv4).sin_port)); + dcb_printf(dcb, + "\t\tUsername: %s\n", + session->dcb->user); + dcb_printf(dcb, + "\t\tSlave DCB: %p\n", + session->dcb); + dcb_printf(dcb, + "\t\tNext Sequence No: %d\n", + session->seqno); + dcb_printf(dcb, + "\t\tState: %s\n", + blrs_states[session->state]); + dcb_printf(dcb, + "\t\tBinlog file: %s\n", + session->binlogfile); + dcb_printf(dcb, + "\t\tBinlog position: %u\n", + session->binlog_pos); + if (session->nocrc) + { + dcb_printf(dcb, + "\t\tMaster Binlog CRC: None\n"); + } + dcb_printf(dcb, + "\t\tNo. requests: %u\n", + session->stats.n_requests); + dcb_printf(dcb, + "\t\tNo. events sent: %u\n", + session->stats.n_events); + dcb_printf(dcb, + "\t\tNo. bytes sent: %u\n", + session->stats.n_bytes); + dcb_printf(dcb, + "\t\tNo. bursts sent: %u\n", + session->stats.n_bursts); + dcb_printf(dcb, + "\t\tNo. transitions to follow mode: %u\n", + session->stats.n_bursts); + if (router_inst->send_slave_heartbeat) + { + dcb_printf(dcb, + "\t\tHeartbeat period (seconds): %lu\n", + session->heartbeat); + } - minno = session->stats.minno - 1; - if (minno == -1) - minno = 30; - dcb_printf(dcb, "\t\tNumber of binlog events per minute\n"); - dcb_printf(dcb, "\t\tCurrent 5 10 15 30 Min Avg\n"); - dcb_printf(dcb, "\t\t %6d %8.1f %8.1f %8.1f %8.1f\n", - session->stats.minavgs[minno], min5, min10, - min15, min30); - dcb_printf(dcb, "\t\tNo. flow control: %u\n", session->stats.n_flows); - dcb_printf(dcb, "\t\tNo. up to date: %u\n", session->stats.n_upd); - dcb_printf(dcb, "\t\tNo. of drained cbs %u\n", session->stats.n_dcb); - dcb_printf(dcb, "\t\tNo. of failed reads %u\n", session->stats.n_failed_read); + minno = session->stats.minno - 1; + if (minno == -1) + { + minno = 30; + } + dcb_printf(dcb, "\t\tNumber of binlog events per minute\n"); + dcb_printf(dcb, "\t\tCurrent 5 10 15 30 Min Avg\n"); + dcb_printf(dcb, "\t\t %6d %8.1f %8.1f %8.1f %8.1f\n", + session->stats.minavgs[minno], min5, min10, + min15, min30); + dcb_printf(dcb, "\t\tNo. flow control: %u\n", + session->stats.n_flows); + dcb_printf(dcb, "\t\tNo. up to date: %u\n", + session->stats.n_upd); + dcb_printf(dcb, "\t\tNo. of drained cbs %u\n", + session->stats.n_dcb); + dcb_printf(dcb, "\t\tNo. of failed reads %u\n", + session->stats.n_failed_read); #if DETAILED_DIAG - dcb_printf(dcb, "\t\tNo. of nested distribute events %u\n", session->stats.n_overrun); - dcb_printf(dcb, "\t\tNo. of distribute action 1 %u\n", session->stats.n_actions[0]); - dcb_printf(dcb, "\t\tNo. of distribute action 2 %u\n", session->stats.n_actions[1]); - dcb_printf(dcb, "\t\tNo. of distribute action 3 %u\n", session->stats.n_actions[2]); + dcb_printf(dcb, "\t\tNo. of nested distribute events %u\n", + session->stats.n_overrun); + dcb_printf(dcb, "\t\tNo. of distribute action 1 %u\n", + session->stats.n_actions[0]); + dcb_printf(dcb, "\t\tNo. of distribute action 2 %u\n", + session->stats.n_actions[1]); + dcb_printf(dcb, "\t\tNo. of distribute action 3 %u\n", + session->stats.n_actions[2]); #endif - if (session->lastEventTimestamp - && router_inst->lastEventTimestamp && session->lastEventReceived != HEARTBEAT_EVENT) - { - unsigned long seconds_behind; - time_t session_last_event = (time_t)session->lastEventTimestamp; + if (session->lastEventTimestamp + && router_inst->lastEventTimestamp && session->lastEventReceived != HEARTBEAT_EVENT) + { + unsigned long seconds_behind; + time_t session_last_event = (time_t)session->lastEventTimestamp; - if (router_inst->lastEventTimestamp > session->lastEventTimestamp) - seconds_behind = router_inst->lastEventTimestamp - session->lastEventTimestamp; - else - seconds_behind = 0; + if (router_inst->lastEventTimestamp > session->lastEventTimestamp) + { + seconds_behind = router_inst->lastEventTimestamp - session->lastEventTimestamp; + } + else + { + seconds_behind = 0; + } - localtime_r(&session_last_event, &tm); - asctime_r(&tm, buf); - dcb_printf(dcb, "\t\tLast binlog event timestamp %u, %s", session->lastEventTimestamp, buf); - dcb_printf(dcb, "\t\tSeconds behind master %lu\n", seconds_behind); - } + localtime_r(&session_last_event, &tm); + asctime_r(&tm, buf); + dcb_printf(dcb, "\t\tLast binlog event timestamp %u, %s", + session->lastEventTimestamp, buf); + dcb_printf(dcb, "\t\tSeconds behind master %lu\n", + seconds_behind); + } - if (session->state == 0) - { - dcb_printf(dcb, "\t\tSlave_mode: connected\n"); - } - else if ((session->cstate & CS_UPTODATE) == 0) - { - dcb_printf(dcb, "\t\tSlave_mode: catchup. %s%s\n", - ((session->cstate & CS_EXPECTCB) == 0 ? "" : - "Waiting for DCB queue to drain."), - ((session->cstate & CS_BUSY) == 0 ? "" : - " Busy in slave catchup.")); - } - else - { - dcb_printf(dcb, "\t\tSlave_mode: follow\n"); - if (session->binlog_pos != router_inst->binlog_position) - { - dcb_printf(dcb, "\t\tSlave reports up to date however " - "the slave binlog position does not match the master\n"); - } - } + if (session->state == 0) + { + dcb_printf(dcb, "\t\tSlave_mode: connected\n"); + } + else if ((session->cstate & CS_UPTODATE) == 0) + { + dcb_printf(dcb, "\t\tSlave_mode: catchup. %s%s\n", + ((session->cstate & CS_EXPECTCB) == 0 ? "" : + "Waiting for DCB queue to drain."), + ((session->cstate & CS_BUSY) == 0 ? "" : + " Busy in slave catchup.")); + } + else + { + dcb_printf(dcb, "\t\tSlave_mode: follow\n"); + if (session->binlog_pos != router_inst->binlog_position) + { + dcb_printf(dcb, "\t\tSlave reports up to date however " + "the slave binlog position does not match the master\n"); + } + } #if SPINLOCK_PROFILE - dcb_printf(dcb, "\tSpinlock statistics (catch_lock):\n"); - spinlock_stats(&session->catch_lock, spin_reporter, dcb); - dcb_printf(dcb, "\tSpinlock statistics (rses_lock):\n"); - spinlock_stats(&session->rses_lock, spin_reporter, dcb); + dcb_printf(dcb, "\tSpinlock statistics (catch_lock):\n"); + spinlock_stats(&session->catch_lock, spin_reporter, dcb); + dcb_printf(dcb, "\tSpinlock statistics (rses_lock):\n"); + spinlock_stats(&session->rses_lock, spin_reporter, dcb); #endif - dcb_printf(dcb, "\t\t--------------------\n\n"); - session = session->next; - } - spinlock_release(&router_inst->lock); - } + dcb_printf(dcb, "\t\t--------------------\n\n"); + session = session->next; + } + spinlock_release(&router_inst->lock); + } } /** @@ -1316,28 +1437,30 @@ struct tm tm; static void clientReply(ROUTER *instance, void *router_session, GWBUF *queue, DCB *backend_dcb) { -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; - atomic_add(&router->stats.n_reads, 1); - blr_master_response(router, queue); - router->stats.lastReply = time(0); + atomic_add(&router->stats.n_reads, 1); + blr_master_response(router, queue); + router->stats.lastReply = time(0); } static char * extract_message(GWBUF *errpkt) { -char *rval; -int len; + char *rval; + int len; - len = EXTRACT24(errpkt->start); - if ((rval = (char *)malloc(len)) == NULL) - return NULL; - memcpy(rval, (char *)(errpkt->start) + 7, 6); - rval[6] = ' '; - /* message size is len - (1 byte field count + 2 bytes errno + 6 bytes status) */ - memcpy(&rval[7], (char *)(errpkt->start) + 13, len - 9); - rval[len-2] = 0; - return rval; + len = EXTRACT24(errpkt->start); + if ((rval = (char *)malloc(len)) == NULL) + { + return NULL; + } + memcpy(rval, (char *)(errpkt->start) + 7, 6); + rval[6] = ' '; + /* message size is len - (1 byte field count + 2 bytes errno + 6 bytes status) */ + memcpy(&rval[7], (char *)(errpkt->start) + 13, len - 9); + rval[len - 2] = 0; + return rval; } @@ -1352,77 +1475,93 @@ int len; * @param router_session The router session * @param message The error message to reply * @param backend_dcb The backend DCB - * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT - * @param succp Result of action: true iff router can continue + * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT + * @param succp Result of action: true iff router can continue * */ -static void -errorReply(ROUTER *instance, void *router_session, GWBUF *message, DCB *backend_dcb, error_action_t action, bool *succp) +static void +errorReply(ROUTER *instance, + void *router_session, + GWBUF *message, + DCB *backend_dcb, + error_action_t action, + bool *succp) { -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; -int error; -socklen_t len; -char msg[STRERROR_BUFLEN + 1 + 5] = ""; -char *errmsg; -unsigned long mysql_errno; + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; + int error; + socklen_t len; + char msg[STRERROR_BUFLEN + 1 + 5] = ""; + char *errmsg; + unsigned long mysql_errno; - /** Don't handle same error twice on same DCB */ - if (backend_dcb->dcb_errhandle_called) - { - /** we optimistically assume that previous call succeed */ - *succp = true; - return; - } - else - { - backend_dcb->dcb_errhandle_called = true; - } + /** Don't handle same error twice on same DCB */ + if (backend_dcb->dcb_errhandle_called) + { + /** we optimistically assume that previous call succeed */ + *succp = true; + return; + } + else + { + backend_dcb->dcb_errhandle_called = true; + } - len = sizeof(error); - if (router->master && getsockopt(router->master->fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error != 0) - { - char errbuf[STRERROR_BUFLEN]; - sprintf(msg, "%s ", strerror_r(error, errbuf, sizeof(errbuf))); - } - else - strcpy(msg, ""); + len = sizeof(error); + if (router->master && + getsockopt(router->master->fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && + error != 0) + { + char errbuf[STRERROR_BUFLEN]; + sprintf(msg, "%s ", strerror_r(error, errbuf, sizeof(errbuf))); + } + else + { + strcpy(msg, ""); + } - mysql_errno = (unsigned long) extract_field((uint8_t *)(GWBUF_DATA(message) + 5), 16); - errmsg = extract_message(message); + mysql_errno = (unsigned long) extract_field((uint8_t *)(GWBUF_DATA(message) + 5), 16); + errmsg = extract_message(message); - if (router->master_state < BLRM_BINLOGDUMP || router->master_state != BLRM_SLAVE_STOPPED) { - /* set mysql_errno */ - router->m_errno = mysql_errno; + if (router->master_state < BLRM_BINLOGDUMP || router->master_state != BLRM_SLAVE_STOPPED) + { + /* set mysql_errno */ + router->m_errno = mysql_errno; - /* set io error message */ - if (router->m_errmsg) - free(router->m_errmsg); - router->m_errmsg = strdup(errmsg); + /* set io error message */ + if (router->m_errmsg) + { + free(router->m_errmsg); + } + router->m_errmsg = strdup(errmsg); - MXS_ERROR("%s: Master connection error %lu '%s' in state '%s', " - "%sattempting reconnect to master %s:%d", - router->service->name, mysql_errno, errmsg, - blrm_states[router->master_state], msg, - router->service->dbref->server->name, - router->service->dbref->server->port); - } else { - MXS_ERROR("%s: Master connection error %lu '%s' in state '%s', " - "%sattempting reconnect to master %s:%d", - router->service->name, router->m_errno, router->m_errmsg, - blrm_states[router->master_state], msg, - router->service->dbref->server->name, - router->service->dbref->server->port); - } + MXS_ERROR("%s: Master connection error %lu '%s' in state '%s', " + "%sattempting reconnect to master %s:%d", + router->service->name, mysql_errno, errmsg, + blrm_states[router->master_state], msg, + router->service->dbref->server->name, + router->service->dbref->server->port); + } + else + { + MXS_ERROR("%s: Master connection error %lu '%s' in state '%s', " + "%sattempting reconnect to master %s:%d", + router->service->name, router->m_errno, router->m_errmsg, + blrm_states[router->master_state], msg, + router->service->dbref->server->name, + router->service->dbref->server->port); + } - if (errmsg) - free(errmsg); - *succp = true; - dcb_close(backend_dcb); - MXS_NOTICE("%s: Master %s disconnected after %ld seconds. " - "%lu events read.", - router->service->name, router->service->dbref->server->name, - time(0) - router->connect_time, router->stats.n_binlogs_ses); - blr_master_reconnect(router); + if (errmsg) + { + free(errmsg); + } + *succp = true; + dcb_close(backend_dcb); + MXS_NOTICE("%s: Master %s disconnected after %ld seconds. " + "%lu events read.", + router->service->name, router->service->dbref->server->name, + time(0) - router->connect_time, router->stats.n_binlogs_ses); + blr_master_reconnect(router); } /** to be inline'd */ @@ -1443,14 +1582,14 @@ unsigned long mysql_errno; */ static bool rses_begin_locked_router_action(ROUTER_SLAVE *rses) { - bool succp = false; + bool succp = false; - CHK_CLIENT_RSES(rses); + CHK_CLIENT_RSES(rses); - spinlock_acquire(&rses->rses_lock); - succp = true; + spinlock_acquire(&rses->rses_lock); + succp = true; - return succp; + return succp; } /** to be inline'd */ @@ -1467,111 +1606,117 @@ static bool rses_begin_locked_router_action(ROUTER_SLAVE *rses) * @details (write detailed description here) * */ -static void rses_end_locked_router_action(ROUTER_SLAVE * rses) +static void rses_end_locked_router_action(ROUTER_SLAVE *rses) { - CHK_CLIENT_RSES(rses); - spinlock_release(&rses->rses_lock); + CHK_CLIENT_RSES(rses); + spinlock_release(&rses->rses_lock); } static int getCapabilities() { - return RCAP_TYPE_NO_RSESSION; + return RCAP_TYPE_NO_RSESSION; } /** * The stats gathering function called from the housekeeper so that we * can get timed averages of binlog records shippped * - * @param inst The router instance + * @param inst The router instance */ static void stats_func(void *inst) { -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)inst; -ROUTER_SLAVE *slave; + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)inst; + ROUTER_SLAVE *slave; - router->stats.minavgs[router->stats.minno++] - = router->stats.n_binlogs - router->stats.lastsample; - router->stats.lastsample = router->stats.n_binlogs; - if (router->stats.minno == BLR_NSTATS_MINUTES) - router->stats.minno = 0; + router->stats.minavgs[router->stats.minno++] = router->stats.n_binlogs - router->stats.lastsample; + router->stats.lastsample = router->stats.n_binlogs; + if (router->stats.minno == BLR_NSTATS_MINUTES) + { + router->stats.minno = 0; + } - spinlock_acquire(&router->lock); - slave = router->slaves; - while (slave) - { - slave->stats.minavgs[slave->stats.minno++] - = slave->stats.n_events - slave->stats.lastsample; - slave->stats.lastsample = slave->stats.n_events; - if (slave->stats.minno == BLR_NSTATS_MINUTES) - slave->stats.minno = 0; - slave = slave->next; - } - spinlock_release(&router->lock); + spinlock_acquire(&router->lock); + slave = router->slaves; + while (slave) + { + slave->stats.minavgs[slave->stats.minno++] = slave->stats.n_events - slave->stats.lastsample; + slave->stats.lastsample = slave->stats.n_events; + if (slave->stats.minno == BLR_NSTATS_MINUTES) + { + slave->stats.minno = 0; + } + slave = slave->next; + } + spinlock_release(&router->lock); } /** * Return some basic statistics from the router in response to a COM_STATISTICS * request. * - * @param router The router instance - * @param slave The "slave" connection that requested the statistics - * @param queue The statistics request + * @param router The router instance + * @param slave The "slave" connection that requested the statistics + * @param queue The statistics request * * @return non-zero on sucessful send */ int blr_statistics(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { -char result[BLRM_COM_STATISTICS_SIZE + 1] = ""; -char *ptr; -GWBUF *ret; -unsigned long len; + char result[BLRM_COM_STATISTICS_SIZE + 1] = ""; + char *ptr; + GWBUF *ret; + unsigned long len; - snprintf(result, BLRM_COM_STATISTICS_SIZE, - "Uptime: %u Threads: %u Events: %u Slaves: %u Master State: %s", - (unsigned int)(time(0) - router->connect_time), - (unsigned int)config_threadcount(), - (unsigned int)router->stats.n_binlogs_ses, - (unsigned int)router->stats.n_slaves, - blrm_states[router->master_state]); - if ((ret = gwbuf_alloc(4 + strlen(result))) == NULL) - return 0; - len = strlen(result); - ptr = GWBUF_DATA(ret); - *ptr++ = len & 0xff; - *ptr++ = (len & 0xff00) >> 8; - *ptr++ = (len & 0xff0000) >> 16; - *ptr++ = 1; - strncpy(ptr, result, len); + snprintf(result, BLRM_COM_STATISTICS_SIZE, + "Uptime: %u Threads: %u Events: %u Slaves: %u Master State: %s", + (unsigned int)(time(0) - router->connect_time), + (unsigned int)config_threadcount(), + (unsigned int)router->stats.n_binlogs_ses, + (unsigned int)router->stats.n_slaves, + blrm_states[router->master_state]); + if ((ret = gwbuf_alloc(4 + strlen(result))) == NULL) + { + return 0; + } + len = strlen(result); + ptr = GWBUF_DATA(ret); + *ptr++ = len & 0xff; + *ptr++ = (len & 0xff00) >> 8; + *ptr++ = (len & 0xff0000) >> 16; + *ptr++ = 1; + strncpy(ptr, result, len); - return slave->dcb->func.write(slave->dcb, ret); + return slave->dcb->func.write(slave->dcb, ret); } /** * Respond to a COM_PING command * - * @param router The router instance - * @param slave The "slave" connection that requested the ping - * @param queue The ping request + * @param router The router instance + * @param slave The "slave" connection that requested the ping + * @param queue The ping request */ int blr_ping(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { -char *ptr; -GWBUF *ret; + char *ptr; + GWBUF *ret; - if ((ret = gwbuf_alloc(5)) == NULL) - return 0; - ptr = GWBUF_DATA(ret); - *ptr++ = 0x01; - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 1; - *ptr = 0; // OK + if ((ret = gwbuf_alloc(5)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(ret); + *ptr++ = 0x01; + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 1; + *ptr = 0; // OK - return slave->dcb->func.write(slave->dcb, ret); + return slave->dcb->func.write(slave->dcb, ret); } @@ -1584,87 +1729,100 @@ GWBUF *ret; * @param dcb Owner_Dcb Control Block for the connection to which the error message is sent * @param packet_number * @param in_affected_rows - * @param msg Message to send - * @param statemsg MySQL State message - * @param errcode MySQL Error code + * @param msg Message to send + * @param statemsg MySQL State message + * @param errcode MySQL Error code * @return 1 Non-zero if data was sent * */ int -blr_send_custom_error(DCB *dcb, int packet_number, int affected_rows, char *msg, char *statemsg, unsigned int errcode) +blr_send_custom_error(DCB *dcb, int packet_number, + int affected_rows, + char *msg, + char *statemsg, + unsigned int errcode) { -uint8_t *outbuf = NULL; -uint32_t mysql_payload_size = 0; -uint8_t mysql_packet_header[4]; -uint8_t *mysql_payload = NULL; -uint8_t field_count = 0; -uint8_t mysql_err[2]; -uint8_t mysql_statemsg[6]; -unsigned int mysql_errno = 0; -const char *mysql_error_msg = NULL; -const char *mysql_state = NULL; -GWBUF *errbuf = NULL; + uint8_t *outbuf = NULL; + uint32_t mysql_payload_size = 0; + uint8_t mysql_packet_header[4]; + uint8_t *mysql_payload = NULL; + uint8_t field_count = 0; + uint8_t mysql_err[2]; + uint8_t mysql_statemsg[6]; + unsigned int mysql_errno = 0; + const char *mysql_error_msg = NULL; + const char *mysql_state = NULL; + GWBUF *errbuf = NULL; - if (errcode == 0) - mysql_errno = 1064; - else - mysql_errno = errcode; + if (errcode == 0) + { + mysql_errno = 1064; + } + else + { + mysql_errno = errcode; + } - mysql_error_msg = "An errorr occurred ..."; - if (statemsg == NULL) - mysql_state = "42000"; - else - mysql_state = statemsg; + mysql_error_msg = "An errorr occurred ..."; + if (statemsg == NULL) + { + mysql_state = "42000"; + } + else + { + mysql_state = statemsg; + } - field_count = 0xff; - gw_mysql_set_byte2(mysql_err, mysql_errno); - mysql_statemsg[0]='#'; - memcpy(mysql_statemsg+1, mysql_state, 5); + field_count = 0xff; + gw_mysql_set_byte2(mysql_err, mysql_errno); + mysql_statemsg[0] = '#'; + memcpy(mysql_statemsg + 1, mysql_state, 5); - if (msg != NULL) { - mysql_error_msg = msg; - } + if (msg != NULL) + { + mysql_error_msg = msg; + } - mysql_payload_size = sizeof(field_count) + - sizeof(mysql_err) + - sizeof(mysql_statemsg) + - strlen(mysql_error_msg); + mysql_payload_size = sizeof(field_count) + + sizeof(mysql_err) + + sizeof(mysql_statemsg) + + strlen(mysql_error_msg); - /** allocate memory for packet header + payload */ - errbuf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size); - ss_dassert(errbuf != NULL); + /** allocate memory for packet header + payload */ + errbuf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size); + ss_dassert(errbuf != NULL); - if (errbuf == NULL) - { - return 0; - } - outbuf = GWBUF_DATA(errbuf); + if (errbuf == NULL) + { + return 0; + } + outbuf = GWBUF_DATA(errbuf); - /** write packet header and packet number */ - gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size); - mysql_packet_header[3] = packet_number; + /** write packet header and packet number */ + gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size); + mysql_packet_header[3] = packet_number; - /** write header */ - memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header)); + /** write header */ + memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header)); - mysql_payload = outbuf + sizeof(mysql_packet_header); + mysql_payload = outbuf + sizeof(mysql_packet_header); - /** write field */ - memcpy(mysql_payload, &field_count, sizeof(field_count)); - mysql_payload = mysql_payload + sizeof(field_count); + /** write field */ + memcpy(mysql_payload, &field_count, sizeof(field_count)); + mysql_payload = mysql_payload + sizeof(field_count); - /** write errno */ - memcpy(mysql_payload, mysql_err, sizeof(mysql_err)); - mysql_payload = mysql_payload + sizeof(mysql_err); + /** write errno */ + memcpy(mysql_payload, mysql_err, sizeof(mysql_err)); + mysql_payload = mysql_payload + sizeof(mysql_err); - /** write sqlstate */ - memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg)); - mysql_payload = mysql_payload + sizeof(mysql_statemsg); + /** write sqlstate */ + memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg)); + mysql_payload = mysql_payload + sizeof(mysql_statemsg); - /** write error message */ - memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg)); + /** write error message */ + memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg)); - return dcb->func.write(dcb, errbuf); + return dcb->func.write(dcb, errbuf); } /** @@ -1680,59 +1838,76 @@ GWBUF *errbuf = NULL; static int blr_handler_config(void *userdata, const char *section, const char *name, const char *value) { -ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *) userdata; -SERVICE *service; + ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *) userdata; + SERVICE *service; - service = inst->service; + service = inst->service; - if (strcasecmp(section, "binlog_configuration") == 0) - { - return blr_handle_config_item(name, value, inst); - } else { - MXS_ERROR("master.ini has an invalid section [%s], it should be [binlog_configuration]. " - "Service %s", - section, - service->name); + if (strcasecmp(section, "binlog_configuration") == 0) + { + return blr_handle_config_item(name, value, inst); + } + else + { + MXS_ERROR("master.ini has an invalid section [%s], it should be [binlog_configuration]. " + "Service %s", + section, + service->name); - return 0; - } + return 0; + } } /** * Configuration handler for items in the [binlog_configuration] section * - * @param name The item name - * @param value The item value - * @param inst The current router instance + * @param name The item name + * @param value The item value + * @param inst The current router instance * @return 0 on error */ static int blr_handle_config_item(const char *name, const char *value, ROUTER_INSTANCE *inst) { -SERVICE *service; + SERVICE *service; - service = inst->service; + service = inst->service; - if (strcmp(name, "master_host") == 0) { - server_update_address(service->dbref->server, (char *)value); - } else if (strcmp(name, "master_port") == 0) { - server_update_port(service->dbref->server, (short)atoi(value)); - } else if (strcmp(name, "filestem") == 0) { - free(inst->fileroot); - inst->fileroot = strdup(value); - } else if (strcmp(name, "master_user") == 0) { - if (inst->user) - free(inst->user); - inst->user = strdup(value); - } else if (strcmp(name, "master_password") == 0) { - if (inst->password) - free(inst->password); - inst->password = strdup(value); - } else { - return 0; + if (strcmp(name, "master_host") == 0) + { + server_update_address(service->dbref->server, (char *)value); + } + else if (strcmp(name, "master_port") == 0) + { + server_update_port(service->dbref->server, (short)atoi(value)); + } + else if (strcmp(name, "filestem") == 0) + { + free(inst->fileroot); + inst->fileroot = strdup(value); + } + else if (strcmp(name, "master_user") == 0) + { + if (inst->user) + { + free(inst->user); } + inst->user = strdup(value); + } + else if (strcmp(name, "master_password") == 0) + { + if (inst->password) + { + free(inst->password); + } + inst->password = strdup(value); + } + else + { + return 0; + } - return 1; + return 1; } /** @@ -1740,179 +1915,191 @@ SERVICE *service; * via mysql_users_alloc and add_mysql_users_with_host_ipv4 * User is added for '%' and 'localhost' hosts * - * @param service The current service - * @return 0 on success, 1 on failure + * @param service The current service + * @return 0 on success, 1 on failure */ static int -blr_set_service_mysql_user(SERVICE *service) { -char *dpwd = NULL; -char *newpasswd = NULL; -char *service_user = NULL; -char *service_passwd = NULL; +blr_set_service_mysql_user(SERVICE *service) +{ + char *dpwd = NULL; + char *newpasswd = NULL; + char *service_user = NULL; + char *service_passwd = NULL; - if (serviceGetUser(service, &service_user, &service_passwd) == 0) { - MXS_ERROR("failed to get service user details for service %s", - service->name); + if (serviceGetUser(service, &service_user, &service_passwd) == 0) + { + MXS_ERROR("failed to get service user details for service %s", + service->name); - return 1; - } + return 1; + } - dpwd = decryptPassword(service->credentials.authdata); + dpwd = decryptPassword(service->credentials.authdata); - if (!dpwd) { - MXS_ERROR("decrypt password failed for service user %s, service %s", - service_user, - service->name); + if (!dpwd) + { + MXS_ERROR("decrypt password failed for service user %s, service %s", + service_user, + service->name); - return 1; - } + return 1; + } - newpasswd = create_hex_sha1_sha1_passwd(dpwd); + newpasswd = create_hex_sha1_sha1_passwd(dpwd); - if (!newpasswd) { - MXS_ERROR("create hex_sha1_sha1_password failed for service user %s", - service_user); + if (!newpasswd) + { + MXS_ERROR("create hex_sha1_sha1_password failed for service user %s", + service_user); - free(dpwd); - return 1; - } + free(dpwd); + return 1; + } - /* add service user for % and localhost */ - (void)add_mysql_users_with_host_ipv4(service->users, service->credentials.name, "%", newpasswd, "Y", ""); - (void)add_mysql_users_with_host_ipv4(service->users, service->credentials.name, "localhost", newpasswd, "Y", ""); + /* add service user for % and localhost */ + (void)add_mysql_users_with_host_ipv4(service->users, service->credentials.name, + "%", newpasswd, "Y", ""); + (void)add_mysql_users_with_host_ipv4(service->users, service->credentials.name, + "localhost", newpasswd, "Y", ""); - free(newpasswd); - free(dpwd); + free(newpasswd); + free(dpwd); - return 0; + return 0; } /** * Load mysql dbusers into (service->users) * - * @param router The router instance + * @param router The router instance * @return -1 on failure, 0 for no users found, > 0 for found users */ int blr_load_dbusers(ROUTER_INSTANCE *router) { -int loaded = -1; -char path[PATH_MAX+1] = ""; -SERVICE *service; - service = router->service; + int loaded = -1; + char path[PATH_MAX + 1] = ""; + SERVICE *service; + service = router->service; - /* File path for router cached authentication data */ - strncpy(path, router->binlogdir, PATH_MAX); - strncat(path, "/cache", PATH_MAX); - strncat(path, "/dbusers", PATH_MAX); + /* File path for router cached authentication data */ + strncpy(path, router->binlogdir, PATH_MAX); + strncat(path, "/cache", PATH_MAX); + strncat(path, "/dbusers", PATH_MAX); - /* Try loading dbusers from configured backends */ - loaded = load_mysql_users(service); + /* Try loading dbusers from configured backends */ + loaded = load_mysql_users(service); - if (loaded < 0) - { - MXS_ERROR("Unable to load users for service %s", - service->name); + if (loaded < 0) + { + MXS_ERROR("Unable to load users for service %s", + service->name); - /* Try loading authentication data from file cache */ + /* Try loading authentication data from file cache */ - loaded = dbusers_load(router->service->users, path); + loaded = dbusers_load(router->service->users, path); - if (loaded != -1) - { - MXS_ERROR("Service %s, Using cached credential information file %s.", - service->name, - path); - } else { - MXS_ERROR("Service %s, Unable to read cache credential information from %s." - " No database user added to service users table.", - service->name, - path); - } - } else { - /* don't update cache if no user was loaded */ - if (loaded == 0) - { - MXS_ERROR("Service %s: failed to load any user " - "information. Authentication will " - "probably fail as a result.", - service->name); - } else { - /* update cached data */ - blr_save_dbusers(router); - } - } + if (loaded != -1) + { + MXS_ERROR("Service %s, Using cached credential information file %s.", + service->name, + path); + } + else + { + MXS_ERROR("Service %s, Unable to read cache credential information from %s." + " No database user added to service users table.", + service->name, + path); + } + } + else + { + /* don't update cache if no user was loaded */ + if (loaded == 0) + { + MXS_ERROR("Service %s: failed to load any user " + "information. Authentication will " + "probably fail as a result.", + service->name); + } + else + { + /* update cached data */ + blr_save_dbusers(router); + } + } - /* At service start last update is set to USERS_REFRESH_TIME seconds earlier. - * This way MaxScale could try reloading users' just after startup - */ - service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME; - service->rate_limit.nloads=1; + /* At service start last update is set to USERS_REFRESH_TIME seconds earlier. + * This way MaxScale could try reloading users' just after startup + */ + service->rate_limit.last = time(NULL) - USERS_REFRESH_TIME; + service->rate_limit.nloads = 1; - return loaded; + return loaded; } /** * Save dbusers to cache file * - * @param router The router instance + * @param router The router instance * @return -1 on failure, >= 0 on success */ int blr_save_dbusers(ROUTER_INSTANCE *router) { -SERVICE *service; -char path[PATH_MAX+1] = ""; -int mkdir_rval = 0; + SERVICE *service; + char path[PATH_MAX + 1] = ""; + int mkdir_rval = 0; - service = router->service; + service = router->service; - /* File path for router cached authentication data */ - strncpy(path, router->binlogdir, PATH_MAX); - strncat(path, "/cache", PATH_MAX); + /* File path for router cached authentication data */ + strncpy(path, router->binlogdir, PATH_MAX); + strncat(path, "/cache", PATH_MAX); - /* check and create dir */ - if (access(path, R_OK) == -1) - { - mkdir_rval = mkdir(path, 0700); - } + /* check and create dir */ + if (access(path, R_OK) == -1) + { + mkdir_rval = mkdir(path, 0700); + } - if (mkdir_rval == -1) - { - char err_msg[STRERROR_BUFLEN]; - MXS_ERROR("Service %s, Failed to create directory '%s': [%d] %s", - service->name, - path, - errno, - strerror_r(errno, err_msg, sizeof(err_msg))); + if (mkdir_rval == -1) + { + char err_msg[STRERROR_BUFLEN]; + MXS_ERROR("Service %s, Failed to create directory '%s': [%d] %s", + service->name, + path, + errno, + strerror_r(errno, err_msg, sizeof(err_msg))); - return -1; - } + return -1; + } - /* set cache file name */ - strncat(path, "/dbusers", PATH_MAX); + /* set cache file name */ + strncat(path, "/dbusers", PATH_MAX); - return dbusers_save(service->users, path); + return dbusers_save(service->users, path); } /** * Extract a numeric field from a packet of the specified number of bits * - * @param src The raw packet source - * @param birs The number of bits to extract (multiple of 8) + * @param src The raw packet source + * @param birs The number of bits to extract (multiple of 8) */ uint32_t extract_field(uint8_t *src, int bits) { -uint32_t rval = 0, shift = 0; + uint32_t rval = 0, shift = 0; - while (bits > 0) - { - rval |= (*src++) << shift; - shift += 8; - bits -= 8; - } - return rval; + while (bits > 0) + { + rval |= (*src++) << shift; + shift += 8; + bits -= 8; + } + return rval; } /** @@ -1921,110 +2108,138 @@ uint32_t rval = 0, shift = 0; * If a partial transaction is found * inst->binlog_position is set the pos where it started * - * @param router The router instance - * @return 1 on success, 0 on failure + * @param router The router instance + * @return 1 on success, 0 on failure */ /** 1 is succes, 0 is faulure */ -static int blr_check_binlog(ROUTER_INSTANCE *router) { - int n; +static int blr_check_binlog(ROUTER_INSTANCE *router) +{ + int n; - /** blr_read_events_all() may set master_state - * to BLR_SLAVE_STOPPED state in case of found errors. - * In such conditions binlog file is NOT truncated and - * router state is set to BLR_SLAVE_STOPPED - * Last commited pos is set for both router->binlog_position - * and router->current_pos. - * - * If an open transaction is detected at pos XYZ - * inst->binlog_position will be set to XYZ while - * router->current_pos is the last event found. - */ + /** blr_read_events_all() may set master_state + * to BLR_SLAVE_STOPPED state in case of found errors. + * In such conditions binlog file is NOT truncated and + * router state is set to BLR_SLAVE_STOPPED + * Last commited pos is set for both router->binlog_position + * and router->current_pos. + * + * If an open transaction is detected at pos XYZ + * inst->binlog_position will be set to XYZ while + * router->current_pos is the last event found. + */ - n = blr_read_events_all_events(router, 0, 0); + n = blr_read_events_all_events(router, 0, 0); - MXS_DEBUG("blr_read_events_all_events() ret = %i\n", n); + MXS_DEBUG("blr_read_events_all_events() ret = %i\n", n); - if (n != 0) { - char msg_err[BINLOG_ERROR_MSG_LEN + 1] = ""; - router->master_state = BLRM_SLAVE_STOPPED; + if (n != 0) + { + char msg_err[BINLOG_ERROR_MSG_LEN + 1] = ""; + router->master_state = BLRM_SLAVE_STOPPED; - snprintf(msg_err, BINLOG_ERROR_MSG_LEN, "Error found in binlog %s. Safe pos is %lu", router->binlog_name, router->binlog_position); - /* set mysql_errno */ - router->m_errno = 2032; + snprintf(msg_err, BINLOG_ERROR_MSG_LEN, "Error found in binlog %s. Safe pos is %lu", + router->binlog_name, + router->binlog_position); + /* set mysql_errno */ + router->m_errno = 2032; - /* set io error message */ - router->m_errmsg = strdup(msg_err); + /* set io error message */ + router->m_errmsg = strdup(msg_err); - /* set last_safe_pos */ - router->last_safe_pos = router->binlog_position; + /* set last_safe_pos */ + router->last_safe_pos = router->binlog_position; - MXS_ERROR("Error found in binlog file %s. Safe starting pos is %lu", - router->binlog_name, - router->binlog_position); + MXS_ERROR("Error found in binlog file %s. Safe starting pos is %lu", + router->binlog_name, + router->binlog_position); - return 0; - } else { - return 1; - } + return 0; + } + else + { + return 1; + } } /** * Return last event description * - * @param router The router instance - * @return The event description or NULL + * @param router The router instance + * @return The event description or NULL */ char * -blr_last_event_description(ROUTER_INSTANCE *router) { -char *event_desc = NULL; +blr_last_event_description(ROUTER_INSTANCE *router) +{ + char *event_desc = NULL; - if (!router->mariadb10_compat) { - if (router->lastEventReceived <= MAX_EVENT_TYPE) { - event_desc = event_names[router->lastEventReceived]; - } - } else { - if (router->lastEventReceived <= MAX_EVENT_TYPE) { - event_desc = event_names[router->lastEventReceived]; - } else { - /* Check MariaDB 10 new events */ - if (router->lastEventReceived >= MARIADB_NEW_EVENTS_BEGIN && - router->lastEventReceived <= MAX_EVENT_TYPE_MARIADB10) { - event_desc = event_names_mariadb10[(router->lastEventReceived - MARIADB_NEW_EVENTS_BEGIN)]; - } - } - } + if (!router->mariadb10_compat) + { + if (router->lastEventReceived >= 0 && + router->lastEventReceived <= MAX_EVENT_TYPE) + { + event_desc = event_names[router->lastEventReceived]; + } + } + else + { + if (router->lastEventReceived >= 0 && + router->lastEventReceived <= MAX_EVENT_TYPE) + { + event_desc = event_names[router->lastEventReceived]; + } + else + { + /* Check MariaDB 10 new events */ + if (router->lastEventReceived >= MARIADB_NEW_EVENTS_BEGIN && + router->lastEventReceived <= MAX_EVENT_TYPE_MARIADB10) + { + event_desc = event_names_mariadb10[(router->lastEventReceived - MARIADB_NEW_EVENTS_BEGIN)]; + } + } + } - return event_desc; + return event_desc; } /** * Return the event description * - * @param router The router instance - * @param event The current event - * @return The event description or NULL + * @param router The router instance + * @param event The current event + * @return The event description or NULL */ char * -blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event) { -char *event_desc = NULL; +blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event) +{ + char *event_desc = NULL; - if (!router->mariadb10_compat) { - if (event <= MAX_EVENT_TYPE) { - event_desc = event_names[event]; - } - } else { - if (event <= MAX_EVENT_TYPE) { - event_desc = event_names[event]; - } else { - /* Check MariaDB 10 new events */ - if (event >= MARIADB_NEW_EVENTS_BEGIN && - event <= MAX_EVENT_TYPE_MARIADB10) { - event_desc = event_names_mariadb10[(event - MARIADB_NEW_EVENTS_BEGIN)]; - } - } - } + if (!router->mariadb10_compat) + { + if (event >= 0 && + event <= MAX_EVENT_TYPE) + { + event_desc = event_names[event]; + } + } + else + { + if (event >= 0 && + event <= MAX_EVENT_TYPE) + { + event_desc = event_names[event]; + } + else + { + /* Check MariaDB 10 new events */ + if (event >= MARIADB_NEW_EVENTS_BEGIN && + event <= MAX_EVENT_TYPE_MARIADB10) + { + event_desc = event_names_mariadb10[(event - MARIADB_NEW_EVENTS_BEGIN)]; + } + } + } - return event_desc; + return event_desc; } diff --git a/server/modules/routing/binlog/blr_cache.c b/server/modules/routing/binlog/blr_cache.c index 63b1e8f9e..3b761d01e 100644 --- a/server/modules/routing/binlog/blr_cache.c +++ b/server/modules/routing/binlog/blr_cache.c @@ -31,8 +31,8 @@ * @verbatim * Revision History * - * Date Who Description - * 07/04/2014 Mark Riddoch Initial implementation + * Date Who Description + * 07/04/2014 Mark Riddoch Initial implementation * * @endverbatim */ @@ -58,7 +58,7 @@ * effect also determine the binlog file to read and the position to read * from. * - * @param router The router instance + * @param router The router instance */ void blr_init_cache(ROUTER_INSTANCE *router) diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 89c4caa91..daafdc2af 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -23,21 +23,21 @@ * @verbatim * Revision History * - * Date Who Description - * 14/04/2014 Mark Riddoch Initial implementation - * 07/05/2015 Massimiliano Pinto Added MAX_EVENT_TYPE_MARIADB10 - * 08/06/2015 Massimiliano Pinto Addition of blr_cache_read_master_data() - * 15/06/2015 Massimiliano Pinto Addition of blr_file_get_next_binlogname() - * 23/06/2015 Massimiliano Pinto Addition of blr_file_use_binlog, blr_file_create_binlog - * 29/06/2015 Massimiliano Pinto Addition of blr_file_write_master_config() - * Cache directory is now 'cache' under router->binlogdir - * 05/08/2015 Massimiliano Pinto Initial implementation of transaction safety - * 24/08/2015 Massimiliano Pinto Added strerror_r - * 26/08/2015 Massimiliano Pinto Added MariaDB 10 GTID event check with flags = 0 - * This is the current supported condition for detecting - * MariaDB 10 transaction start point. - * It's no longer using QUERY_EVENT with BEGIN - * 23/10/15 Markus Makela Added current_safe_event + * Date Who Description + * 14/04/2014 Mark Riddoch Initial implementation + * 07/05/2015 Massimiliano Pinto Added MAX_EVENT_TYPE_MARIADB10 + * 08/06/2015 Massimiliano Pinto Addition of blr_cache_read_master_data() + * 15/06/2015 Massimiliano Pinto Addition of blr_file_get_next_binlogname() + * 23/06/2015 Massimiliano Pinto Addition of blr_file_use_binlog, blr_file_create_binlog + * 29/06/2015 Massimiliano Pinto Addition of blr_file_write_master_config() + * Cache directory is now 'cache' under router->binlogdir + * 05/08/2015 Massimiliano Pinto Initial implementation of transaction safety + * 24/08/2015 Massimiliano Pinto Added strerror_r + * 26/08/2015 Massimiliano Pinto Added MariaDB 10 GTID event check with flags = 0 + * This is the current supported condition for detecting + * MariaDB 10 transaction start point. + * It's no longer using QUERY_EVENT with BEGIN + * 23/10/15 Markus Makela Added current_safe_event * * @endverbatim */ @@ -73,114 +73,132 @@ void blr_file_use_binlog(ROUTER_INSTANCE *router, char *file); int blr_file_write_master_config(ROUTER_INSTANCE *router, char *error); extern uint32_t extract_field(uint8_t *src, int bits); static void blr_format_event_size(double *event_size, char *label); +extern int MaxScaleUptime(); extern char *blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event); -typedef struct binlog_event_desc { - unsigned long long event_pos; - uint8_t event_type; - time_t event_time; +typedef struct binlog_event_desc +{ + unsigned long long event_pos; + uint8_t event_type; + time_t event_time; } BINLOG_EVENT_DESC; -static void blr_print_binlog_details(ROUTER_INSTANCE *router, BINLOG_EVENT_DESC first_event_time, BINLOG_EVENT_DESC last_event_time); +static void blr_print_binlog_details(ROUTER_INSTANCE *router, + BINLOG_EVENT_DESC first_event_time, + BINLOG_EVENT_DESC last_event_time); /** * Initialise the binlog file for this instance. MaxScale will look * for all the binlogs that it has on local disk, determine the next - * binlog to use and initialise it for writing, determining the + * binlog to use and initialise it for writing, determining the * next record to be fetched from the real master. * - * @param router The router instance this defines the master for this replication chain + * @param router The router instance this defines the master for this replication chain */ int blr_file_init(ROUTER_INSTANCE *router) { -char *ptr; -char path[PATH_MAX+1] = ""; -char filename[PATH_MAX+1] = ""; -int file_found, n = 1; -int root_len, i; -DIR *dirp; -struct dirent *dp; + char *ptr; + char path[PATH_MAX + 1] = ""; + char filename[PATH_MAX + 1] = ""; + int file_found, n = 1; + int root_len, i; + DIR *dirp; + struct dirent *dp; - if (router->binlogdir == NULL) - { - strncpy(path, get_datadir(), PATH_MAX); - strncat(path,"/",PATH_MAX); - strncat(path, router->service->name,PATH_MAX); + if (router->binlogdir == NULL) + { + strncpy(path, get_datadir(), PATH_MAX); + strncat(path, "/", PATH_MAX); + strncat(path, router->service->name, PATH_MAX); - if (access(path, R_OK) == -1) - mkdir(path, 0700); + if (access(path, R_OK) == -1) + { + mkdir(path, 0700); + } - router->binlogdir = strdup(path); - } - else - { - strncpy(path, router->binlogdir, PATH_MAX); - } - if (access(router->binlogdir, R_OK) == -1) - { - MXS_ERROR("%s: Unable to read the binlog directory %s.", - router->service->name, router->binlogdir); - return 0; - } + router->binlogdir = strdup(path); + } + else + { + strncpy(path, router->binlogdir, PATH_MAX); + } + if (access(router->binlogdir, R_OK) == -1) + { + MXS_ERROR("%s: Unable to read the binlog directory %s.", + router->service->name, router->binlogdir); + return 0; + } - /* First try to find a binlog file number by reading the directory */ - root_len = strlen(router->fileroot); - if ((dirp = opendir(path)) == NULL) - { - char err_msg[BLRM_STRERROR_R_MSG_SIZE]; - MXS_ERROR("%s: Unable to read the binlog directory %s, %s.", - router->service->name, router->binlogdir, - strerror_r(errno, err_msg, sizeof(err_msg))); - return 0; - } - while ((dp = readdir(dirp)) != NULL) - { - if (strncmp(dp->d_name, router->fileroot, root_len) == 0) - { - i = atoi(dp->d_name + root_len + 1); - if (i > n) - n = i; - } - } - closedir(dirp); + /* First try to find a binlog file number by reading the directory */ + root_len = strlen(router->fileroot); + if ((dirp = opendir(path)) == NULL) + { + char err_msg[BLRM_STRERROR_R_MSG_SIZE]; + MXS_ERROR("%s: Unable to read the binlog directory %s, %s.", + router->service->name, router->binlogdir, + strerror_r(errno, err_msg, sizeof(err_msg))); + return 0; + } + while ((dp = readdir(dirp)) != NULL) + { + if (strncmp(dp->d_name, router->fileroot, root_len) == 0) + { + i = atoi(dp->d_name + root_len + 1); + if (i > n) + { + n = i; + } + } + } + closedir(dirp); - file_found = 0; - do { - snprintf(filename,PATH_MAX, "%s/" BINLOG_NAMEFMT, path, router->fileroot, n); - if (access(filename, R_OK) != -1) - { - file_found = 1; - n++; - } - else - file_found = 0; - } while (file_found); - n--; + file_found = 0; + do + { + snprintf(filename, PATH_MAX, "%s/" BINLOG_NAMEFMT, path, router->fileroot, n); + if (access(filename, R_OK) != -1) + { + file_found = 1; + n++; + } + else + { + file_found = 0; + } + } + while (file_found); + n--; - if (n == 0) // No binlog files found - { - if (router->initbinlog) - snprintf(filename,PATH_MAX, BINLOG_NAMEFMT, router->fileroot, - router->initbinlog); - else - snprintf(filename,PATH_MAX, BINLOG_NAMEFMT, router->fileroot, 1); - if (! blr_file_create(router, filename)) - return 0; - } - else - { - snprintf(filename,PATH_MAX, BINLOG_NAMEFMT, router->fileroot, n); - blr_file_append(router, filename); - } - return 1; + if (n == 0) // No binlog files found + { + if (router->initbinlog) + { + snprintf(filename, PATH_MAX, BINLOG_NAMEFMT, router->fileroot, + router->initbinlog); + } + else + { + snprintf(filename, PATH_MAX, BINLOG_NAMEFMT, router->fileroot, 1); + } + if (! blr_file_create(router, filename)) + { + return 0; + } + } + else + { + snprintf(filename, PATH_MAX, BINLOG_NAMEFMT, router->fileroot, n); + blr_file_append(router, filename); + } + return 1; } int blr_file_rotate(ROUTER_INSTANCE *router, char *file, uint64_t pos) { - return blr_file_create(router, file); + return blr_file_create(router, file); } @@ -188,8 +206,8 @@ blr_file_rotate(ROUTER_INSTANCE *router, char *file, uint64_t pos) * binlog files need an initial 4 magic bytes at the start. blr_file_add_magic() * adds them. * - * @param fd file descriptor to the open binlog file - * @return True if the magic string could be written to the file. + * @param fd file descriptor to the open binlog file + * @return True if the magic string could be written to the file. */ static bool blr_file_add_magic(int fd) @@ -221,7 +239,7 @@ blr_file_create(ROUTER_INSTANCE *router, char *file) strcat(path, "/"); strcat(path, file); - int fd = open(path, O_RDWR|O_CREAT, 0666); + int fd = open(path, O_RDWR | O_CREAT, 0666); if (fd != -1) { @@ -235,7 +253,6 @@ blr_file_create(ROUTER_INSTANCE *router, char *file) router->binlog_position = BINLOG_MAGIC_SIZE; router->current_safe_event = BINLOG_MAGIC_SIZE; router->last_written = BINLOG_MAGIC_SIZE; - router->last_event_pos = 0; spinlock_release(&router->binlog_lock); created = 1; @@ -265,421 +282,465 @@ blr_file_create(ROUTER_INSTANCE *router, char *file) /** * Prepare an existing binlog file to be appened to. * - * @param router The router instance - * @param file The binlog file name + * @param router The router instance + * @param file The binlog file name */ static void blr_file_append(ROUTER_INSTANCE *router, char *file) { -char path[PATH_MAX+1] = ""; -int fd; + char path[PATH_MAX + 1] = ""; + int fd; - strcpy(path, router->binlogdir); - strcat(path, "/"); - strcat(path, file); + strcpy(path, router->binlogdir); + strcat(path, "/"); + strcat(path, file); - if ((fd = open(path, O_RDWR|O_APPEND, 0666)) == -1) - { - MXS_ERROR("Failed to open binlog file %s for append.", - path); - return; - } - fsync(fd); - close(router->binlog_fd); - spinlock_acquire(&router->binlog_lock); - memmove(router->binlog_name, file, BINLOG_FNAMELEN); - router->current_pos = lseek(fd, 0L, SEEK_END); - if (router->current_pos < 4) { - if (router->current_pos == 0) { - if (blr_file_add_magic(fd)) - { - router->current_pos = BINLOG_MAGIC_SIZE; - router->binlog_position = BINLOG_MAGIC_SIZE; - router->current_safe_event = BINLOG_MAGIC_SIZE; - router->last_written = BINLOG_MAGIC_SIZE; - router->last_event_pos = 0; - } else { - MXS_ERROR("%s: Could not write magic to binlog file.", router->service->name); - } - } else { - /* If for any reason the file's length is between 1 and 3 bytes - * then report an error. */ - MXS_ERROR("%s: binlog file %s has an invalid length %lu.", - router->service->name, path, router->current_pos); - close(fd); - spinlock_release(&router->binlog_lock); - return; - } - } - router->binlog_fd = fd; - spinlock_release(&router->binlog_lock); + if ((fd = open(path, O_RDWR | O_APPEND, 0666)) == -1) + { + MXS_ERROR("Failed to open binlog file %s for append.", + path); + return; + } + fsync(fd); + close(router->binlog_fd); + spinlock_acquire(&router->binlog_lock); + memmove(router->binlog_name, file, BINLOG_FNAMELEN); + router->current_pos = lseek(fd, 0L, SEEK_END); + if (router->current_pos < 4) + { + if (router->current_pos == 0) + { + if (blr_file_add_magic(fd)) + { + router->current_pos = BINLOG_MAGIC_SIZE; + router->binlog_position = BINLOG_MAGIC_SIZE; + router->current_safe_event = BINLOG_MAGIC_SIZE; + router->last_written = BINLOG_MAGIC_SIZE; + } + else + { + MXS_ERROR("%s: Could not write magic to binlog file.", router->service->name); + } + } + else + { + /* If for any reason the file's length is between 1 and 3 bytes + * then report an error. */ + MXS_ERROR("%s: binlog file %s has an invalid length %lu.", + router->service->name, path, router->current_pos); + close(fd); + spinlock_release(&router->binlog_lock); + return; + } + } + router->binlog_fd = fd; + spinlock_release(&router->binlog_lock); } /** * Write a binlog entry to disk. * - * @param router The router instance - * @param buf The binlog record - * @param len The length of the binlog record - * @return Return the number of bytes written + * @param router The router instance + * @param buf The binlog record + * @param len The length of the binlog record + * @return Return the number of bytes written */ int blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, uint8_t *buf) { -int n; + int n; - if ((n = pwrite(router->binlog_fd, buf, size, - router->last_written)) != size) - { - char err_msg[STRERROR_BUFLEN]; - MXS_ERROR("%s: Failed to write binlog record at %lu of %s, %s. " - "Truncating to previous record.", - router->service->name, router->last_written, - router->binlog_name, - strerror_r(errno, err_msg, sizeof(err_msg))); - /* Remove any partial event that was written */ - if (ftruncate(router->binlog_fd, router->last_written)) - { - MXS_ERROR("%s: Failed to truncate binlog record at %lu of %s, %s. ", - router->service->name, router->last_written, - router->binlog_name, - strerror_r(errno, err_msg, sizeof(err_msg))); - } - return 0; - } - spinlock_acquire(&router->binlog_lock); - router->current_pos = hdr->next_pos; - router->last_written += size; - router->last_event_pos = hdr->next_pos - hdr->event_size; - spinlock_release(&router->binlog_lock); - return n; + if ((n = pwrite(router->binlog_fd, buf, size, + router->last_written)) != size) + { + char err_msg[STRERROR_BUFLEN]; + MXS_ERROR("%s: Failed to write binlog record at %lu of %s, %s. " + "Truncating to previous record.", + router->service->name, router->last_written, + router->binlog_name, + strerror_r(errno, err_msg, sizeof(err_msg))); + /* Remove any partial event that was written */ + if (ftruncate(router->binlog_fd, router->last_written)) + { + MXS_ERROR("%s: Failed to truncate binlog record at %lu of %s, %s. ", + router->service->name, router->last_written, + router->binlog_name, + strerror_r(errno, err_msg, sizeof(err_msg))); + } + return 0; + } + spinlock_acquire(&router->binlog_lock); + router->current_pos = hdr->next_pos; + router->last_written += size; + router->last_event_pos = hdr->next_pos - hdr->event_size; + spinlock_release(&router->binlog_lock); + return n; } /** * Flush the content of the binlog file to disk. * - * @param router The binlog router + * @param router The binlog router */ void blr_file_flush(ROUTER_INSTANCE *router) { - fsync(router->binlog_fd); + fsync(router->binlog_fd); } /** * Open a binlog file for reading binlog records * - * @param router The router instance - * @param binlog The binlog filename + * @param router The router instance + * @param binlog The binlog filename * @return a binlog file record */ BLFILE * blr_open_binlog(ROUTER_INSTANCE *router, char *binlog) { -char path[PATH_MAX + 1] = ""; -BLFILE *file; + char path[PATH_MAX + 1] = ""; + BLFILE *file; - spinlock_acquire(&router->fileslock); - file = router->files; - while (file && strcmp(file->binlogname, binlog) != 0) - file = file->next; + spinlock_acquire(&router->fileslock); + file = router->files; + while (file && strcmp(file->binlogname, binlog) != 0) + { + file = file->next; + } - if (file) - { - file->refcnt++; - spinlock_release(&router->fileslock); - return file; - } + if (file) + { + file->refcnt++; + spinlock_release(&router->fileslock); + return file; + } - if ((file = (BLFILE *)calloc(1, sizeof(BLFILE))) == NULL) - { - spinlock_release(&router->fileslock); - return NULL; - } - strncpy(file->binlogname, binlog, BINLOG_FNAMELEN); - file->refcnt = 1; - file->cache = 0; - spinlock_init(&file->lock); + if ((file = (BLFILE *)calloc(1, sizeof(BLFILE))) == NULL) + { + spinlock_release(&router->fileslock); + return NULL; + } + strncpy(file->binlogname, binlog, BINLOG_FNAMELEN); + file->refcnt = 1; + file->cache = 0; + spinlock_init(&file->lock); - strncpy(path, router->binlogdir, PATH_MAX); - strncat(path, "/", PATH_MAX); - strncat(path, binlog, PATH_MAX); + strncpy(path, router->binlogdir, PATH_MAX); + strncat(path, "/", PATH_MAX); + strncat(path, binlog, PATH_MAX); - if ((file->fd = open(path, O_RDONLY, 0666)) == -1) - { - MXS_ERROR("Failed to open binlog file %s", path); - free(file); - spinlock_release(&router->fileslock); - return NULL; - } + if ((file->fd = open(path, O_RDONLY, 0666)) == -1) + { + MXS_ERROR("Failed to open binlog file %s", path); + free(file); + spinlock_release(&router->fileslock); + return NULL; + } - file->next = router->files; - router->files = file; - spinlock_release(&router->fileslock); + file->next = router->files; + router->files = file; + spinlock_release(&router->fileslock); - return file; + return file; } /** * Read a replication event into a GWBUF structure. * - * @param router The router instance - * @param file File record - * @param pos Position of binlog record to read - * @param hdr Binlog header to populate - * @param errmsg Allocated BINLOG_ERROR_MSG_LEN bytes message error buffer - * @return The binlog record wrapped in a GWBUF structure + * @param router The router instance + * @param file File record + * @param pos Position of binlog record to read + * @param hdr Binlog header to populate + * @param errmsg Allocated BINLOG_ERROR_MSG_LEN bytes message error buffer + * @return The binlog record wrapped in a GWBUF structure */ GWBUF * blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned long pos, REP_HEADER *hdr, char *errmsg) { -uint8_t hdbuf[BINLOG_EVENT_HDR_LEN]; -GWBUF *result; -unsigned char *data; -int n; -unsigned long filelen = 0; -struct stat statb; + uint8_t hdbuf[BINLOG_EVENT_HDR_LEN]; + GWBUF *result; + unsigned char *data; + int n; + unsigned long filelen = 0; + struct stat statb; - memset(&hdbuf, '\0', BINLOG_EVENT_HDR_LEN); + memset(&hdbuf, '\0', BINLOG_EVENT_HDR_LEN); - /* set error indicator */ - hdr->ok = SLAVE_POS_READ_ERR; + /* set error indicator */ + hdr->ok = SLAVE_POS_READ_ERR; - if (!file) - { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Invalid file pointer for requested binlog at position %lu", pos); - return NULL; - } + if (!file) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Invalid file pointer for requested binlog at position %lu", pos); + return NULL; + } - spinlock_acquire(&file->lock); - if (fstat(file->fd, &statb) == 0) - filelen = statb.st_size; - else { - if (file->fd == -1) { - hdr->ok = SLAVE_POS_BAD_FD; - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "blr_read_binlog called with invalid file->fd, pos %lu", pos); - spinlock_release(&file->lock); - return NULL; - } - } - spinlock_release(&file->lock); + spinlock_acquire(&file->lock); + if (fstat(file->fd, &statb) == 0) + { + filelen = statb.st_size; + } + else + { + if (file->fd == -1) + { + hdr->ok = SLAVE_POS_BAD_FD; + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "blr_read_binlog called with invalid file->fd, pos %lu", pos); + spinlock_release(&file->lock); + return NULL; + } + } + spinlock_release(&file->lock); - if (pos > filelen) - { - spinlock_acquire(&router->binlog_lock); - spinlock_acquire(&file->lock); + if (pos > filelen) + { + spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&file->lock); - if (strcmp(router->binlog_name, file->binlogname) != 0) - { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested position %lu is beyond " - "'closed' binlog file '%s', size %lu. Generating Error '1236'", - pos, file->binlogname, filelen); - } else { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested position %lu is beyond " - "end of the latest binlog file '%s', size %lu. Disconnecting", - pos, file->binlogname, filelen); + if (strcmp(router->binlog_name, file->binlogname) != 0) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested position %lu is beyond " + "'closed' binlog file '%s', size %lu. Generating Error '1236'", + pos, file->binlogname, filelen); + } + else + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested position %lu is beyond " + "end of the latest binlog file '%s', size %lu. Disconnecting", + pos, file->binlogname, filelen); - /* Slave will be disconnected by the calling routine */ - hdr->ok = SLAVE_POS_BEYOND_EOF; + /* Slave will be disconnected by the calling routine */ + hdr->ok = SLAVE_POS_BEYOND_EOF; - } - - spinlock_release(&file->lock); - spinlock_release(&router->binlog_lock); - - return NULL; - } - - spinlock_acquire(&router->binlog_lock); - spinlock_acquire(&file->lock); - - if (strcmp(router->binlog_name, file->binlogname) == 0 && - pos >= router->binlog_position) - { - if (pos > router->binlog_position) - { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested binlog position %lu is unsafe. " - "Latest safe position %lu, end of binlog file %lu", - pos, router->binlog_position, router->current_pos); - - hdr->ok = SLAVE_POS_READ_UNSAFE; - } else { - /* accessing last position is ok */ - hdr->ok = SLAVE_POS_READ_OK; - } - - spinlock_release(&file->lock); - spinlock_release(&router->binlog_lock); - - return NULL; - } - spinlock_release(&file->lock); - spinlock_release(&router->binlog_lock); - - /* Read the header information from the file */ - if ((n = pread(file->fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN) - { - switch (n) - { - case 0: - MXS_DEBUG("Reached end of binlog file '%s' at %lu.", - file->binlogname, pos); - - /* set ok indicator */ - hdr->ok = SLAVE_POS_READ_OK; - - break; - case -1: - { - char err_msg[STRERROR_BUFLEN]; - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Failed to read binlog file '%s'; (%s), event at %lu", - file->binlogname, strerror_r(errno, err_msg, sizeof(err_msg)), pos); - - if (errno == EBADF) - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bad file descriptor for binlog file '%s', refcount %d, descriptor %d, event at %lu", - file->binlogname, file->refcnt, file->fd, pos); - break; - } - default: - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data in log event header; " - "expected %d bytes but read %d, position %lu, binlog file '%s'", - BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname); - break; - } - return NULL; - } - - 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]); - - /* event pos & size checks */ - if (hdr->event_size == 0 || ((hdr->next_pos != (pos + hdr->event_size)) && (hdr->event_type != ROTATE_EVENT))) { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Client requested master to start replication from invalid position %lu in binlog file '%s'", pos, file->binlogname); - return NULL; } - /* event type checks */ - if (router->mariadb10_compat) { - if (hdr->event_type > MAX_EVENT_TYPE_MARIADB10) { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Invalid MariaDB 10 event type 0x%x at %lu in binlog file '%s'", hdr->event_type, pos, file->binlogname); - return NULL; - } - } else { - if (hdr->event_type > MAX_EVENT_TYPE) { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Invalid event type 0x%x at %lu in binlog file '%s'", hdr->event_type, pos, file->binlogname); - return NULL; - } - } + spinlock_release(&file->lock); + spinlock_release(&router->binlog_lock); - if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT) - { - MXS_ERROR("Next position in header appears to be incorrect " - "rereading event header at pos %lu in file %s, " - "file size is %lu. Master will write %lu in %s next.", - pos, file->binlogname, filelen, router->binlog_position, - router->binlog_name); + return NULL; + } - if ((n = pread(file->fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN) - { - switch (n) - { - case 0: - MXS_DEBUG("Reached end of binlog file at %lu.", - pos); + spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&file->lock); - /* set ok indicator */ - hdr->ok = SLAVE_POS_READ_OK; + if (strcmp(router->binlog_name, file->binlogname) == 0 && + pos >= router->binlog_position) + { + if (pos > router->binlog_position) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Requested binlog position %lu is unsafe. " + "Latest safe position %lu, end of binlog file %lu", + pos, router->binlog_position, router->current_pos); - break; - case -1: - { - char err_msg[STRERROR_BUFLEN]; - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Failed to reread header in binlog file '%s'; (%s), event at %lu", - file->binlogname, strerror_r(errno, err_msg, sizeof(err_msg)), pos); + hdr->ok = SLAVE_POS_READ_UNSAFE; + } + else + { + /* accessing last position is ok */ + hdr->ok = SLAVE_POS_READ_OK; + } - if (errno == EBADF) - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bad file descriptor rereading header for binlog file '%s', " - "refcount %d, descriptor %d, event at %lu", - file->binlogname, file->refcnt, file->fd, pos); - break; - } - default: - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data rereading log event header; " - "expected %d bytes but read %d, position %lu in binlog file '%s'", - BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname); - break; - } - return NULL; - } + spinlock_release(&file->lock); + spinlock_release(&router->binlog_lock); - 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]); + return NULL; + } + spinlock_release(&file->lock); + spinlock_release(&router->binlog_lock); - if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT) - { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Next event position still incorrect after rereading, " - "event at %lu in binlog file '%s'", pos, file->binlogname); - return NULL; - } - else - { - MXS_ERROR("Next position corrected by " - "rereading"); - } - } - if ((result = gwbuf_alloc(hdr->event_size)) == NULL) - { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Failed to allocate memory for binlog entry, size %d, event at %lu in binlog file '%s'", - hdr->event_size, pos, file->binlogname); - return NULL; - } + /* Read the header information from the file */ + if ((n = pread(file->fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN) + { + switch (n) + { + case 0: + MXS_DEBUG("Reached end of binlog file '%s' at %lu.", + file->binlogname, pos); - data = GWBUF_DATA(result); + /* set ok indicator */ + hdr->ok = SLAVE_POS_READ_OK; - memcpy(data, hdbuf, BINLOG_EVENT_HDR_LEN); // Copy the header in + break; + case -1: + { + char err_msg[STRERROR_BUFLEN]; + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Failed to read binlog file '%s'; (%s), event at %lu", + file->binlogname, strerror_r(errno, err_msg, sizeof(err_msg)), pos); - if ((n = pread(file->fd, &data[BINLOG_EVENT_HDR_LEN], hdr->event_size - BINLOG_EVENT_HDR_LEN, pos + BINLOG_EVENT_HDR_LEN)) - != hdr->event_size - BINLOG_EVENT_HDR_LEN) // Read the balance - { - if (n == -1) - { - char err_msg[STRERROR_BUFLEN]; - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Error reading the binlog event at %lu in binlog file '%s';" - "(%s), expected %d bytes.", - pos, file->binlogname, strerror_r(errno, err_msg, sizeof(err_msg)), hdr->event_size - BINLOG_EVENT_HDR_LEN); - } - else - { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data in log event entry; " - "expected %d bytes but got %d, position %lu in binlog file '%s'", - hdr->event_size - BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname); + if (errno == EBADF) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Bad file descriptor for binlog file '%s', " + "refcount %d, descriptor %d, event at %lu", + file->binlogname, file->refcnt, file->fd, pos); + } + } + break; + default: + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data in log event header; " + "expected %d bytes but read %d, position %lu, binlog file '%s'", + BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname); + break; + } + return NULL; + } - if (filelen != 0 && filelen - pos < hdr->event_size) - { - snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Binlog event is close to the end of the binlog file; " - "current file size is %lu, event at %lu in binlog file '%s'", - filelen, pos, file->binlogname); - } - blr_log_header(LOG_ERR, "Possible malformed event header", hdbuf); - } + 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]); - gwbuf_free(result); + /* event pos & size checks */ + if (hdr->event_size == 0 || ((hdr->next_pos != (pos + hdr->event_size)) && + (hdr->event_type != ROTATE_EVENT))) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Client requested master to start replication from invalid " + "position %lu in binlog file '%s'", pos, + file->binlogname); + return NULL; + } - return NULL; - } + /* event type checks */ + if (router->mariadb10_compat) + { + if (hdr->event_type > MAX_EVENT_TYPE_MARIADB10) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Invalid MariaDB 10 event type 0x%x at %lu in binlog file '%s'", + hdr->event_type, pos, file->binlogname); + return NULL; + } + } + else + { + if (hdr->event_type > MAX_EVENT_TYPE) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Invalid event type 0x%x at %lu in binlog file '%s'", hdr->event_type, + pos, file->binlogname); + return NULL; + } + } - /* set OK indicator */ - hdr->ok = SLAVE_POS_READ_OK; + if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT) + { + MXS_ERROR("Next position in header appears to be incorrect " + "rereading event header at pos %lu in file %s, " + "file size is %lu. Master will write %lu in %s next.", + pos, file->binlogname, filelen, router->binlog_position, + router->binlog_name); - return result; + if ((n = pread(file->fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN) + { + switch (n) + { + case 0: + MXS_DEBUG("Reached end of binlog file at %lu.", + pos); + + /* set ok indicator */ + hdr->ok = SLAVE_POS_READ_OK; + break; + case -1: + { + char err_msg[STRERROR_BUFLEN]; + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Failed to reread header in binlog file '%s'; (%s), event at %lu", + file->binlogname, strerror_r(errno, err_msg, sizeof(err_msg)), pos); + + if (errno == EBADF) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Bad file descriptor rereading header for binlog file '%s', " + "refcount %d, descriptor %d, event at %lu", + file->binlogname, file->refcnt, file->fd, pos); + } + } + break; + default: + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data rereading log event header; " + "expected %d bytes but read %d, position %lu in binlog file '%s'", + BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname); + break; + } + return NULL; + } + + 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]); + + if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Next event position still incorrect after rereading, " + "event at %lu in binlog file '%s'", pos, file->binlogname); + return NULL; + } + else + { + MXS_ERROR("Next position corrected by " + "rereading"); + } + } + if ((result = gwbuf_alloc(hdr->event_size)) == NULL) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Failed to allocate memory for binlog entry, size %d, event at %lu in binlog file '%s'", + hdr->event_size, pos, file->binlogname); + return NULL; + } + + data = GWBUF_DATA(result); + + memcpy(data, hdbuf, BINLOG_EVENT_HDR_LEN); // Copy the header in + + if ((n = pread(file->fd, &data[BINLOG_EVENT_HDR_LEN], hdr->event_size - BINLOG_EVENT_HDR_LEN, + pos + BINLOG_EVENT_HDR_LEN)) + != hdr->event_size - BINLOG_EVENT_HDR_LEN) // Read the balance + { + if (n == -1) + { + char err_msg[STRERROR_BUFLEN]; + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, + "Error reading the binlog event at %lu in binlog file '%s';" + "(%s), expected %d bytes.", + pos, + file->binlogname, + strerror_r(errno, err_msg, sizeof(err_msg)), + hdr->event_size - BINLOG_EVENT_HDR_LEN); + } + else + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data in log event entry; " + "expected %d bytes but got %d, position %lu in binlog file '%s'", + hdr->event_size - BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname); + + if (filelen != 0 && filelen - pos < hdr->event_size) + { + snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Binlog event is close to the end of the binlog file; " + "current file size is %lu, event at %lu in binlog file '%s'", + filelen, pos, file->binlogname); + } + blr_log_header(LOG_ERR, "Possible malformed event header", hdbuf); + } + + gwbuf_free(result); + + return NULL; + } + + /* set OK indicator */ + hdr->ok = SLAVE_POS_READ_OK; + + return result; } /** @@ -688,75 +749,85 @@ struct stat statb; * The open binlog files are shared between multiple slaves that are * reading the same binlog file. * - * @param router The router instance - * @param file The file to close + * @param router The router instance + * @param file The file to close */ void blr_close_binlog(ROUTER_INSTANCE *router, BLFILE *file) { - spinlock_acquire(&router->fileslock); - file->refcnt--; - if (file->refcnt == 0) - { - if (router->files == file) - router->files = file->next; - else - { - BLFILE *ptr = router->files; - while (ptr && ptr->next != file) - ptr = ptr->next; - if (ptr) - ptr->next = file->next; - } - } - else - { - file = NULL; - } - spinlock_release(&router->fileslock); + spinlock_acquire(&router->fileslock); + file->refcnt--; + if (file->refcnt == 0) + { + if (router->files == file) + { + router->files = file->next; + } + else + { + BLFILE *ptr = router->files; + while (ptr && ptr->next != file) + { + ptr = ptr->next; + } + if (ptr) + { + ptr->next = file->next; + } + } + } + else + { + file = NULL; + } + spinlock_release(&router->fileslock); - if (file) - { - close(file->fd); - file->fd = -1; - free(file); - } + if (file) + { + close(file->fd); + file->fd = -1; + free(file); + } } /** * Log the event header of binlog event * - * @param priority The syslog priority of the message (LOG_ERR, LOG_WARNING, etc.) - * @param msg A message strign to preceed the header with - * @param ptr The event header raw data + * @param priority The syslog priority of the message (LOG_ERR, LOG_WARNING, etc.) + * @param msg A message strign to preceed the header with + * @param ptr The event header raw data */ static void blr_log_header(int priority, char *msg, uint8_t *ptr) { -char buf[400], *bufp; -int i; + char buf[400], *bufp; + int i; - bufp = buf; - bufp += sprintf(bufp, "%s: ", msg); - for (i = 0; i < BINLOG_EVENT_HDR_LEN; i++) - bufp += sprintf(bufp, "0x%02x ", ptr[i]); - MXS_LOG_MESSAGE(priority, "%s", buf); + bufp = buf; + bufp += sprintf(bufp, "%s: ", msg); + for (i = 0; i < BINLOG_EVENT_HDR_LEN; i++) + { + bufp += sprintf(bufp, "0x%02x ", ptr[i]); + } + MXS_LOG_MESSAGE(priority, "%s", buf); } /** * Return the size of the current binlog file * - * @param file The binlog file - * @return The current size of the binlog file + * @param file The binlog file + * @return The current size of the binlog file */ unsigned long blr_file_size(BLFILE *file) { -struct stat statb; + struct stat statb; - if (fstat(file->fd, &statb) == 0) - return statb.st_size; - return 0; + if (fstat(file->fd, &statb) == 0) + { + return statb.st_size; + } + return 0; } @@ -766,32 +837,35 @@ struct stat statb; * * cache dir is 'cache' under router->binlogdir * - * @param router The instance of the router - * @param response The name of the response, used to name the cached file - * @param buf The buffer to written to the cache + * @param router The instance of the router + * @param response The name of the response, used to name the cached file + * @param buf The buffer to written to the cache */ void blr_cache_response(ROUTER_INSTANCE *router, char *response, GWBUF *buf) { -char path[PATH_MAX+1] = ""; -char *ptr; -int fd; + char path[PATH_MAX + 1] = ""; + char *ptr; + int fd; - strncpy(path, router->binlogdir, PATH_MAX); - strncat(path, "/cache", PATH_MAX); + strncpy(path, router->binlogdir, PATH_MAX); + strncat(path, "/cache", PATH_MAX); - if (access(path, R_OK) == -1) { - mkdir(path, 0700); - } + if (access(path, R_OK) == -1) + { + mkdir(path, 0700); + } - strncat(path, "/", PATH_MAX); - strncat(path, response, PATH_MAX); + strncat(path, "/", PATH_MAX); + strncat(path, response, PATH_MAX); - if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) - return; - write(fd, GWBUF_DATA(buf), GWBUF_LENGTH(buf)); + if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) + { + return; + } + write(fd, GWBUF_DATA(buf), GWBUF_LENGTH(buf)); - close(fd); + close(fd); } /** @@ -801,63 +875,69 @@ int fd; * * cache dir is 'cache' under router->binlogdir * - * @param router The router instance structure - * @param response The name of the response + * @param router The router instance structure + * @param response The name of the response * @return A pointer to a GWBUF structure */ GWBUF * blr_cache_read_response(ROUTER_INSTANCE *router, char *response) { -struct stat statb; -char path[PATH_MAX+1] = ""; -char *ptr; -int fd; -GWBUF *buf; + struct stat statb; + char path[PATH_MAX + 1] = ""; + char *ptr; + int fd; + GWBUF *buf; - strncpy(path, router->binlogdir, PATH_MAX); - strncat(path, "/cache", PATH_MAX); - strncat(path, "/", PATH_MAX); - strncat(path, response, PATH_MAX); + strncpy(path, router->binlogdir, PATH_MAX); + strncat(path, "/cache", PATH_MAX); + strncat(path, "/", PATH_MAX); + strncat(path, response, PATH_MAX); - if ((fd = open(path, O_RDONLY)) == -1) - return NULL; + if ((fd = open(path, O_RDONLY)) == -1) + { + return NULL; + } - if (fstat(fd, &statb) != 0) - { - close(fd); - return NULL; - } - if ((buf = gwbuf_alloc(statb.st_size)) == NULL) - { - close(fd); - return NULL; - } - read(fd, GWBUF_DATA(buf), statb.st_size); - close(fd); - return buf; + if (fstat(fd, &statb) != 0) + { + close(fd); + return NULL; + } + if ((buf = gwbuf_alloc(statb.st_size)) == NULL) + { + close(fd); + return NULL; + } + read(fd, GWBUF_DATA(buf), statb.st_size); + close(fd); + return buf; } /** * Does the next binlog file in the sequence for the slave exist. * - * @param router The router instance - * @param slave The slave in question - * @retuen 0 if the next file does not exist + * @param router The router instance + * @param slave The slave in question + * @return 0 if the next file does not exist */ int blr_file_next_exists(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -char *sptr, buf[BLRM_BINLOG_NAME_STR_LEN], bigbuf[PATH_MAX + 1]; -int filenum; + char *sptr, buf[BLRM_BINLOG_NAME_STR_LEN], bigbuf[PATH_MAX + 1]; + int filenum; - if ((sptr = strrchr(slave->binlogfile, '.')) == NULL) - return 0; - filenum = atoi(sptr + 1); - sprintf(buf, BINLOG_NAMEFMT, router->fileroot, filenum + 1); - sprintf(bigbuf, "%s/%s", router->binlogdir, buf); - if (access(bigbuf, R_OK) == -1) - return 0; - return 1; + if ((sptr = strrchr(slave->binlogfile, '.')) == NULL) + { + return 0; + } + filenum = atoi(sptr + 1); + sprintf(buf, BINLOG_NAMEFMT, router->fileroot, filenum + 1); + sprintf(bigbuf, "%s/%s", router->binlogdir, buf); + if (access(bigbuf, R_OK) == -1) + { + return 0; + } + return 1; } /** @@ -865,704 +945,825 @@ int filenum; * * 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 + * @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[BINLOG_EVENT_HDR_LEN]; -uint8_t *data; -GWBUF *result; -unsigned long long pos = 4; -unsigned long long last_known_commit = 4; +blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug) +{ + unsigned long filelen = 0; + struct stat statb; + uint8_t hdbuf[BINLOG_EVENT_HDR_LEN]; + 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 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; -BINLOG_EVENT_DESC first_event; -BINLOG_EVENT_DESC last_event; -BINLOG_EVENT_DESC fde_event; -int fde_seen = 0; + 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 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; + BINLOG_EVENT_DESC first_event; + BINLOG_EVENT_DESC last_event; + BINLOG_EVENT_DESC fde_event; + int fde_seen = 0; - memset(&first_event, '\0', sizeof(first_event)); - memset(&last_event, '\0', sizeof(last_event)); - memset(&fde_event, '\0', sizeof(fde_event)); + memset(&first_event, '\0', sizeof(first_event)); + memset(&last_event, '\0', sizeof(last_event)); + memset(&fde_event, '\0', sizeof(fde_event)); - if (router->binlog_fd == -1) { - MXS_ERROR("Current binlog file %s is not open", - router->binlog_name); - return 1; + if (router->binlog_fd == -1) + { + MXS_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; + router->current_safe_event = 4; + + while (1) + { + + /* Read the header information from the file */ + if ((n = pread(router->binlog_fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN) + { + switch (n) + { + case 0: + MXS_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); + } + + /* Report Binlog First and Last event */ + if (pos > 4) + { + if (first_event.event_type == 0) + { + blr_print_binlog_details(router, fde_event, last_event); + } + else + { + blr_print_binlog_details(router, first_event, last_event); + } + } + + /* Report Transaction Summary */ + 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); + + MXS_NOTICE("Transaction Summary for binlog '%s'\n" + "\t\t\tDescription %17s%17s%17s\n\t\t\t" + "No. of Transactions %16lu\n\t\t\t" + "No. of Events %16lu %16.1f %16lu\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) + { + MXS_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); + MXS_ERROR("Failed to read binlog file %s at position %llu" + " (%s).", router->binlog_name, + pos, err_msg); + + if (errno == EBADF) + { + MXS_ERROR("Bad file descriptor in read binlog for file %s" + ", descriptor %d.", + router->binlog_name, router->binlog_fd); + } + } + break; + default: + MXS_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_safe_event = last_known_commit; + router->current_pos = pos; + router->pending_transaction = 1; + pending_transaction = 0; + + MXS_ERROR("Binlog '%s' ends at position %lu and has an incomplete transaction at %lu. ", + router->binlog_name, router->current_pos, router->binlog_position); + + return 0; + } + else + { + /* any error */ + if (n != 0) + { + router->binlog_position = last_known_commit; + router->current_safe_event = last_known_commit; + router->current_pos = pos; + + MXS_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) + { + MXS_NOTICE("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_safe_event = pos; + router->current_pos = pos; + + return 0; + } + } } - if (fstat(router->binlog_fd, &statb) == 0) - filelen = statb.st_size; + /* 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]); - router->current_pos = 4; - router->binlog_position = 4; - router->current_safe_event = 4; + /* Check event type against MAX_EVENT_TYPE */ - while (1){ + if (router->mariadb10_compat) + { + if (hdr.event_type > MAX_EVENT_TYPE_MARIADB10) + { + MXS_ERROR("Invalid MariaDB 10 event type 0x%x. " + "Binlog file is %s, position %llu", + hdr.event_type, + router->binlog_name, pos); - /* Read the header information from the file */ - if ((n = pread(router->binlog_fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN) { - switch (n) - { - case 0: - MXS_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); + event_error = 1; + } + } + else + { + if (hdr.event_type > MAX_EVENT_TYPE) + { + MXS_ERROR("Invalid event type 0x%x. " + "Binlog file is %s, position %llu", + hdr.event_type, + router->binlog_name, pos); - /* Report Binlog First and Last event */ - if (pos > 4) { - if (first_event.event_type == 0) - blr_print_binlog_details(router, fde_event, last_event); - else - blr_print_binlog_details(router, first_event, last_event); - } + event_error = 1; + } + } - /* Report Transaction Summary */ - 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; + if (event_error) + { + router->binlog_position = last_known_commit; + router->current_safe_event = last_known_commit; + router->current_pos = pos; - 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); + MXS_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); - MXS_NOTICE("Transaction Summary for binlog '%s'\n" - "\t\t\tDescription %17s%17s%17s\n\t\t\t" - "No. of Transactions %16lu\n\t\t\t" - "No. of Events %16lu %16.1f %16lu\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) { - MXS_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); - MXS_ERROR("Failed to read binlog file %s at position %llu" - " (%s).", router->binlog_name, - pos, err_msg); - - if (errno == EBADF) - MXS_ERROR("Bad file descriptor in read binlog for file %s" - ", descriptor %d.", - router->binlog_name, router->binlog_fd); - break; - } - default: - MXS_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_safe_event = last_known_commit; - router->current_pos = pos; - router->pending_transaction = 1; - pending_transaction = 0; - - MXS_ERROR("Binlog '%s' ends at position %lu and has an incomplete transaction at %lu. ", - router->binlog_name, router->current_pos, router->binlog_position); - - return 0; - } else { - /* any error */ - if (n != 0) { - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - - MXS_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) { - MXS_NOTICE("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_safe_event = pos; - router->current_pos = pos; - - return 0; - } - } + if (fix) + { + if (ftruncate(router->binlog_fd, router->binlog_position) == 0) + { + MXS_NOTICE("Binlog file %s has been truncated at %lu", + router->binlog_name, + router->binlog_position); + fsync(router->binlog_fd); } + } - /* 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]); + return 1; + } - /* Check event type against MAX_EVENT_TYPE */ + if (hdr.event_size <= 0) + { + MXS_ERROR("Event size error: " + "size %d at %llu.", + hdr.event_size, pos); - if (router->mariadb10_compat) { - if (hdr.event_type > MAX_EVENT_TYPE_MARIADB10) { - MXS_ERROR("Invalid MariaDB 10 event type 0x%x. " - "Binlog file is %s, position %llu", - hdr.event_type, - router->binlog_name, pos); + router->binlog_position = last_known_commit; + router->current_safe_event = last_known_commit; + router->current_pos = pos; - event_error = 1; - } - } else { - if (hdr.event_type > MAX_EVENT_TYPE) { - MXS_ERROR("Invalid event type 0x%x. " - "Binlog file is %s, position %llu", - hdr.event_type, - router->binlog_name, pos); - - event_error = 1; - } - } - - if (event_error) { - - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - - MXS_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) { - MXS_NOTICE("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) + MXS_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) { - MXS_ERROR("Event size error: " - "size %d at %llu.", - hdr.event_size, pos); - - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - - MXS_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) { - MXS_NOTICE("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) - { - MXS_ERROR("Failed to allocate memory for binlog entry, " - "size %d at %llu.", - hdr.event_size, pos); - - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - - MXS_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) { - MXS_NOTICE("Binlog file %s has been truncated at %lu", - router->binlog_name, - router->binlog_position); - fsync(router->binlog_fd); - } - } - - return 1; + MXS_NOTICE("Binlog file %s has been truncated at %lu", + router->binlog_name, + router->binlog_position); + fsync(router->binlog_fd); } + } - /* Copy the header in the buffer */ - data = GWBUF_DATA(result); - memcpy(data, hdbuf, BINLOG_EVENT_HDR_LEN);// Copy the header in + return 1; + } - /* Read event data */ - if ((n = pread(router->binlog_fd, &data[BINLOG_EVENT_HDR_LEN], hdr.event_size - BINLOG_EVENT_HDR_LEN, pos + BINLOG_EVENT_HDR_LEN)) != hdr.event_size - BINLOG_EVENT_HDR_LEN) + /* Allocate a GWBUF for the event */ + if ((result = gwbuf_alloc(hdr.event_size)) == NULL) + { + MXS_ERROR("Failed to allocate memory for binlog entry, " + "size %d at %llu.", + hdr.event_size, pos); + + router->binlog_position = last_known_commit; + router->current_safe_event = last_known_commit; + router->current_pos = pos; + + MXS_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) { - if (n == -1) - { - char err_msg[BLRM_STRERROR_R_MSG_SIZE+1] = ""; - strerror_r(errno, err_msg, BLRM_STRERROR_R_MSG_SIZE); - MXS_ERROR("Error reading the event at %llu in %s. " - "%s, expected %d bytes.", - pos, router->binlog_name, - err_msg, hdr.event_size - BINLOG_EVENT_HDR_LEN); - } - else - { - MXS_ERROR("Short read when reading the event at %llu in %s. " - "Expected %d bytes got %d bytes.", - pos, router->binlog_name, - hdr.event_size - BINLOG_EVENT_HDR_LEN, n); + MXS_NOTICE("Binlog file %s has been truncated at %lu", + router->binlog_name, + router->binlog_position); + fsync(router->binlog_fd); + } + } - if (filelen > 0 && filelen - pos < hdr.event_size) - { - MXS_ERROR("Binlog event is close to the end of the binlog file %s, " - " size is %lu.", - router->binlog_name, filelen); - } - } + return 1; + } + + /* Copy the header in the buffer */ + data = GWBUF_DATA(result); + memcpy(data, hdbuf, BINLOG_EVENT_HDR_LEN);// Copy the header in + + /* Read event data */ + if ((n = pread(router->binlog_fd, &data[BINLOG_EVENT_HDR_LEN], + hdr.event_size - BINLOG_EVENT_HDR_LEN, + pos + BINLOG_EVENT_HDR_LEN)) != hdr.event_size - BINLOG_EVENT_HDR_LEN) + { + if (n == -1) + { + char err_msg[BLRM_STRERROR_R_MSG_SIZE + 1] = ""; + strerror_r(errno, err_msg, BLRM_STRERROR_R_MSG_SIZE); + MXS_ERROR("Error reading the event at %llu in %s. " + "%s, expected %d bytes.", + pos, router->binlog_name, + err_msg, hdr.event_size - BINLOG_EVENT_HDR_LEN); + } + else + { + MXS_ERROR("Short read when reading the event at %llu in %s. " + "Expected %d bytes got %d bytes.", + pos, router->binlog_name, + hdr.event_size - BINLOG_EVENT_HDR_LEN, n); + + if (filelen > 0 && filelen - pos < hdr.event_size) + { + MXS_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_safe_event = last_known_commit; + router->current_pos = pos; + + MXS_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) + { + MXS_NOTICE("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 firts event timestamp, after FDE */ + if (fde_seen) + { + first_event.event_time = (unsigned long)hdr.timestamp; + first_event.event_type = hdr.event_type; + first_event.event_pos = pos; + fde_seen = 0; + } + + /* get event content */ + ptr = data + BINLOG_EVENT_HDR_LEN; + + /* 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; + char buf_t[40]; + struct tm tm_t; + + fde_seen = 1; + fde_event.event_time = (unsigned long)hdr.timestamp; + fde_event.event_type = hdr.event_type; + fde_event.event_pos = pos; + + localtime_r(&fde_event.event_time, &tm_t); + asctime_r(&tm_t, buf_t); + + if (buf_t[strlen(buf_t) - 1] == '\n') + { + buf_t[strlen(buf_t) - 1] = '\0'; + } + + if (debug) + { + MXS_DEBUG("- Format Description event FDE @ %llu, size %lu, time %lu (%s)", + pos, (unsigned long)hdr.event_size, fde_event.event_time, buf_t); + } + + 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) + { + MXS_DEBUG(" FDE ServerVersion [%50s]", ptr + 2); + + MXS_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) + { + MXS_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) + { + found_chksum = 1; + } + else + { + found_chksum = 0; + } + } + } + + /* set last event time, pos and type */ + last_event.event_time = (unsigned long)hdr.timestamp; + last_event.event_type = hdr.event_type; + last_event.event_pos = pos; + + /* 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 - BINLOG_EVENT_HDR_LEN; + 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) + { + MXS_DEBUG("- Rotate event @ %llu, next file is [%s] @ %lu", + 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) + { + MXS_ERROR("Transaction cannot be @ pos %llu: " + "Another MariaDB 10 transaction (GTID %u-%u-%lu)" + " was opened at %llu", + pos, domainid, hdr.serverid, + n_sequence, last_known_commit); gwbuf_free(result); - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - - MXS_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) { - MXS_NOTICE("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 firts event timestamp, after FDE */ - if (fde_seen) { - first_event.event_time = (unsigned long)hdr.timestamp; - first_event.event_type = hdr.event_type; - first_event.event_pos = pos; - fde_seen = 0; - } - - /* get event content */ - ptr = data+BINLOG_EVENT_HDR_LEN; - - /* 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; - char buf_t[40]; - struct tm tm_t; - - fde_seen = 1; - fde_event.event_time = (unsigned long)hdr.timestamp; - fde_event.event_type = hdr.event_type; - fde_event.event_pos = pos; - - localtime_r(&fde_event.event_time, &tm_t); - asctime_r(&tm_t, buf_t); - - if (buf_t[strlen(buf_t)-1] == '\n') { - buf_t[strlen(buf_t)-1] = '\0'; - } - - if(debug) - MXS_DEBUG("- Format Description event FDE @ %llu, size %lu, time %lu (%s)", - pos, (unsigned long)hdr.event_size, fde_event.event_time, buf_t); - - 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) { - MXS_DEBUG(" FDE ServerVersion [%50s]", ptr + 2); - - MXS_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) - MXS_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) { - found_chksum = 1; - } else { - found_chksum = 0; - } - } - } - - /* set last event time, pos and type */ - last_event.event_time = (unsigned long)hdr.timestamp; - last_event.event_type = hdr.event_type; - last_event.event_pos = pos; - - /* 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 - BINLOG_EVENT_HDR_LEN; - 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) - MXS_DEBUG("- Rotate event @ %llu, next file is [%s] @ %lu", - 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) { - MXS_ERROR("Transaction cannot be @ pos %llu: " - "Another MariaDB 10 transaction (GTID %u-%u-%lu)" - " 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) - MXS_DEBUG("> MariaDB 10 Transaction (GTID %u-%u-%lu)" - " 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 - BINLOG_EVENT_HDR_LEN - (4+4+1+2+2+var_block_len+1+db_name_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) { - MXS_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) - MXS_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) - MXS_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 */ - - if (pending_transaction > 0) { - pending_transaction = 2; - if (debug) - MXS_DEBUG(" Transaction XID @ pos %llu, closing @ %llu", - last_known_commit, pos); - } - } - - if (pending_transaction > 1) { - if (debug) - MXS_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) { - MXS_INFO("Binlog %s: next pos %u < pos %llu, truncating to %llu", - router->binlog_name, - hdr.next_pos, - pos, - pos); - - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - - MXS_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) { - MXS_NOTICE("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)) { - MXS_INFO("Binlog %s: next pos %u != (pos %llu + event_size %u), truncating to %llu", - router->binlog_name, - hdr.next_pos, - pos, - hdr.event_size, - pos); - - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - - MXS_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) { - MXS_NOTICE("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 { - - MXS_ERROR("Current event type %d @ %llu has nex pos = %u : exiting", - hdr.event_type, pos, hdr.next_pos); break; + } + else + { + pending_transaction = 1; + + transaction_events = 0; + event_bytes = 0; + + if (debug) + { + MXS_DEBUG("> MariaDB 10 Transaction (GTID %u-%u-%lu)" + " starts @ pos %llu", + domainid, hdr.serverid, n_sequence, pos); + } + } } - - transaction_events++; + } } - if (pending_transaction) { - MXS_INFO("Binlog %s contains an Open Transaction, truncating to %llu", - router->binlog_name, - last_known_commit); + /** + * Check QUERY_EVENT + * + * Check for BEGIN ( ONLY for mysql 5.6, mariadb 5.5 ) + * Check for COMMIT (not transactional engines) + */ - router->binlog_position = last_known_commit; - router->current_safe_event = last_known_commit; - router->current_pos = pos; - router->pending_transaction = 1; + if (hdr.event_type == QUERY_EVENT) + { + char *statement_sql; + db_name_len = ptr[4 + 4]; + var_block_len = ptr[4 + 4 + 1 + 2]; - MXS_WARNING("an error has been found. " - "Setting safe pos to %lu, current pos %lu", - router->binlog_position, router->current_pos); + statement_len = + hdr.event_size - + BINLOG_EVENT_HDR_LEN - + (4 + 4 + 1 + 2 + 2 + var_block_len + 1 + db_name_len); - return 0; - } else { - router->binlog_position = pos; - router->current_safe_event = pos; - router->current_pos = pos; + 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) + { + MXS_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) + { + MXS_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) + { + MXS_DEBUG(" Transaction @ pos %llu, closing @ %llu", + last_known_commit, pos); + } + } + } + free(statement_sql); - return 0; } + + if (hdr.event_type == XID_EVENT) + { + /* Commit received for a transactional tables, i.e. InnoDB */ + + if (pending_transaction > 0) + { + pending_transaction = 2; + if (debug) + { + MXS_DEBUG(" Transaction XID @ pos %llu, closing @ %llu", + last_known_commit, pos); + } + } + } + + if (pending_transaction > 1) + { + if (debug) + { + MXS_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) + { + MXS_INFO("Binlog %s: next pos %u < pos %llu, truncating to %llu", + router->binlog_name, + hdr.next_pos, + pos, + pos); + + router->binlog_position = last_known_commit; + router->current_safe_event = last_known_commit; + router->current_pos = pos; + + MXS_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) + { + MXS_NOTICE("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)) + { + MXS_INFO("Binlog %s: next pos %u != (pos %llu + event_size %u), truncating to %llu", + router->binlog_name, + hdr.next_pos, + pos, + hdr.event_size, + pos); + + router->binlog_position = last_known_commit; + router->current_safe_event = last_known_commit; + router->current_pos = pos; + + MXS_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) + { + MXS_NOTICE("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 + { + MXS_ERROR("Current event type %d @ %llu has nex pos = %u : exiting", + hdr.event_type, pos, hdr.next_pos); + break; + } + + transaction_events++; + } + + if (pending_transaction) + { + MXS_INFO("Binlog %s contains an Open Transaction, truncating to %llu", + router->binlog_name, + last_known_commit); + + router->binlog_position = last_known_commit; + router->current_safe_event = last_known_commit; + router->current_pos = pos; + router->pending_transaction = 1; + + MXS_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_safe_event = 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 + * @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'; + 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'; + } } /** @@ -1573,66 +1774,70 @@ blr_format_event_size(double *event_size, char *label) void blr_cache_read_master_data(ROUTER_INSTANCE *router) { - router->saved_master.server_id = blr_cache_read_response(router, "serverid"); - router->saved_master.heartbeat = blr_cache_read_response(router, "heartbeat"); - router->saved_master.chksum1 = blr_cache_read_response(router, "chksum1"); - router->saved_master.chksum2 = blr_cache_read_response(router, "chksum2"); - router->saved_master.gtid_mode = blr_cache_read_response(router, "gtidmode"); - router->saved_master.uuid = blr_cache_read_response(router, "uuid"); - router->saved_master.setslaveuuid = blr_cache_read_response(router, "ssuuid"); - router->saved_master.setnames = blr_cache_read_response(router, "setnames"); - router->saved_master.utf8 = blr_cache_read_response(router, "utf8"); - router->saved_master.select1 = blr_cache_read_response(router, "select1"); - router->saved_master.selectver = blr_cache_read_response(router, "selectver"); - router->saved_master.selectvercom = blr_cache_read_response(router, "selectvercom"); - router->saved_master.selecthostname = blr_cache_read_response(router, "selecthostname"); - router->saved_master.map = blr_cache_read_response(router, "map"); - router->saved_master.mariadb10 = blr_cache_read_response(router, "mariadb10"); + router->saved_master.server_id = blr_cache_read_response(router, "serverid"); + router->saved_master.heartbeat = blr_cache_read_response(router, "heartbeat"); + router->saved_master.chksum1 = blr_cache_read_response(router, "chksum1"); + router->saved_master.chksum2 = blr_cache_read_response(router, "chksum2"); + router->saved_master.gtid_mode = blr_cache_read_response(router, "gtidmode"); + router->saved_master.uuid = blr_cache_read_response(router, "uuid"); + router->saved_master.setslaveuuid = blr_cache_read_response(router, "ssuuid"); + router->saved_master.setnames = blr_cache_read_response(router, "setnames"); + router->saved_master.utf8 = blr_cache_read_response(router, "utf8"); + router->saved_master.select1 = blr_cache_read_response(router, "select1"); + router->saved_master.selectver = blr_cache_read_response(router, "selectver"); + router->saved_master.selectvercom = blr_cache_read_response(router, "selectvercom"); + router->saved_master.selecthostname = blr_cache_read_response(router, "selecthostname"); + router->saved_master.map = blr_cache_read_response(router, "map"); + router->saved_master.mariadb10 = blr_cache_read_response(router, "mariadb10"); } /** * Get the next binlog file name. * - * @param router The router instance - * @return 0 on error, >0 as sequence number + * @param router The router instance + * @return 0 on error, >0 as sequence number */ int blr_file_get_next_binlogname(ROUTER_INSTANCE *router) { -char *sptr; -int filenum; + char *sptr; + int filenum; - if ((sptr = strrchr(router->binlog_name, '.')) == NULL) - return 0; - filenum = atoi(sptr+1); - if (filenum) - filenum++; + if ((sptr = strrchr(router->binlog_name, '.')) == NULL) + { + return 0; + } + filenum = atoi(sptr + 1); + if (filenum) + { + filenum++; + } - return filenum; + return filenum; } /** * Create a new binlog file * - * @param router The router instance - * @param file The new binlog file - * @return 1 on success, 0 on failure + * @param router The router instance + * @param file The new binlog file + * @return 1 on success, 0 on failure */ int blr_file_new_binlog(ROUTER_INSTANCE *router, char *file) { - return blr_file_create(router, file); + return blr_file_create(router, file); } /** * Use current binlog file - * @param router The router instance - * @param file The binlog file + * @param router The router instance + * @param file The binlog file */ void blr_file_use_binlog(ROUTER_INSTANCE *router, char *file) { - return blr_file_append(router, file); + return blr_file_append(router, file); } /** @@ -1641,110 +1846,122 @@ blr_file_use_binlog(ROUTER_INSTANCE *router, char *file) * File is 'inst->binlogdir/master.ini.tmp' * When done it's renamed to 'inst->binlogdir/master.ini' * - * @param router The current router instance - * @param error Preallocated error message - * @return 0 on success, >0 on failure + * @param router The current router instance + * @param error Preallocated error message + * @return 0 on success, >0 on failure * */ int -blr_file_write_master_config(ROUTER_INSTANCE *router, char *error) { -char *section = "binlog_configuration"; -FILE *config_file; -int rc; -char path[(PATH_MAX - 15) + 1] = ""; -char filename[(PATH_MAX - 4) + 1] = ""; -char tmp_file[PATH_MAX + 1] = ""; -char err_msg[STRERROR_BUFLEN]; +blr_file_write_master_config(ROUTER_INSTANCE *router, char *error) +{ + char *section = "binlog_configuration"; + FILE *config_file; + int rc; + char path[(PATH_MAX - 15) + 1] = ""; + char filename[(PATH_MAX - 4) + 1] = ""; + char tmp_file[PATH_MAX + 1] = ""; + char err_msg[STRERROR_BUFLEN]; - strncpy(path, router->binlogdir, (PATH_MAX - 15)); + strncpy(path, router->binlogdir, (PATH_MAX - 15)); - snprintf(filename,(PATH_MAX - 4), "%s/master.ini", path); + snprintf(filename, (PATH_MAX - 4), "%s/master.ini", path); - snprintf(tmp_file, (PATH_MAX - 4), "%s", filename); + snprintf(tmp_file, (PATH_MAX - 4), "%s", filename); - strcat(tmp_file, ".tmp"); + strcat(tmp_file, ".tmp"); - /* open file for writing */ - config_file = fopen(tmp_file,"wb"); - if (config_file == NULL) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", strerror_r(errno, err_msg, sizeof(err_msg)), errno); - return 2; - } + /* open file for writing */ + config_file = fopen(tmp_file, "wb"); + if (config_file == NULL) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", + strerror_r(errno, err_msg, sizeof(err_msg)), errno); + return 2; + } - if(chmod(tmp_file, S_IRUSR | S_IWUSR) < 0) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", strerror_r(errno, err_msg, sizeof(err_msg)), errno); - fclose(config_file); - return 2; - } + if (chmod(tmp_file, S_IRUSR | S_IWUSR) < 0) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", + strerror_r(errno, err_msg, sizeof(err_msg)), errno); + return 2; + } - /* write ini file section */ - fprintf(config_file,"[%s]\n", section); + /* write ini file section */ + fprintf(config_file, "[%s]\n", section); - /* write ini file key=value */ - fprintf(config_file,"master_host=%s\n", router->service->dbref->server->name); - fprintf(config_file,"master_port=%d\n", router->service->dbref->server->port); - fprintf(config_file,"master_user=%s\n", router->user); - fprintf(config_file,"master_password=%s\n", router->password); - fprintf(config_file,"filestem=%s\n", router->fileroot); + /* write ini file key=value */ + fprintf(config_file, "master_host=%s\n", router->service->dbref->server->name); + fprintf(config_file, "master_port=%d\n", router->service->dbref->server->port); + fprintf(config_file, "master_user=%s\n", router->user); + fprintf(config_file, "master_password=%s\n", router->password); + fprintf(config_file, "filestem=%s\n", router->fileroot); - fclose(config_file); + fclose(config_file); - /* rename tmp file to right filename */ - rc = rename(tmp_file, filename); + /* rename tmp file to right filename */ + rc = rename(tmp_file, filename); - if (rc == -1) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", strerror_r(errno, err_msg, sizeof(err_msg)), errno); - return 3; - } + if (rc == -1) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", + strerror_r(errno, err_msg, sizeof(err_msg)), errno); + return 3; + } - if(chmod(filename, S_IRUSR | S_IWUSR) < 0) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", strerror_r(errno, err_msg, sizeof(err_msg)), errno); - return 3; - } + if (chmod(filename, S_IRUSR | S_IWUSR) < 0) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u", + strerror_r(errno, err_msg, sizeof(err_msg)), errno); + return 3; + } - return 0; + return 0; } /** Print Binlog Details * - * @param router The router instance - * @param first_event First Event details - * @param last_event First Event details + * @param router The router instance + * @param first_event First Event details + * @param last_event First Event details */ static void -blr_print_binlog_details(ROUTER_INSTANCE *router, BINLOG_EVENT_DESC first_event, BINLOG_EVENT_DESC last_event) +blr_print_binlog_details(ROUTER_INSTANCE *router, + BINLOG_EVENT_DESC first_event, + BINLOG_EVENT_DESC last_event) { -char buf_t[40]; -struct tm tm_t; -char *event_desc; + char buf_t[40]; + struct tm tm_t; + char *event_desc; - /* First Event */ - localtime_r(&first_event.event_time, &tm_t); - asctime_r(&tm_t, buf_t); + /* First Event */ + localtime_r(&first_event.event_time, &tm_t); + asctime_r(&tm_t, buf_t); - if (buf_t[strlen(buf_t)-1] == '\n') { - buf_t[strlen(buf_t)-1] = '\0'; - } + if (buf_t[strlen(buf_t) - 1] == '\n') + { + buf_t[strlen(buf_t) - 1] = '\0'; + } - event_desc = blr_get_event_description(router, first_event.event_type); + event_desc = blr_get_event_description(router, first_event.event_type); - MXS_NOTICE("%lu @ %llu, %s, (%s), First EventTime", - first_event.event_time, first_event.event_pos, - event_desc != NULL ? event_desc : "unknown", buf_t); + MXS_NOTICE("%lu @ %llu, %s, (%s), First EventTime", + first_event.event_time, first_event.event_pos, + event_desc != NULL ? event_desc : "unknown", buf_t); - /* Last Event */ - localtime_r(&last_event.event_time, &tm_t); - asctime_r(&tm_t, buf_t); + /* Last Event */ + localtime_r(&last_event.event_time, &tm_t); + asctime_r(&tm_t, buf_t); - if (buf_t[strlen(buf_t)-1] == '\n') { - buf_t[strlen(buf_t)-1] = '\0'; - } + if (buf_t[strlen(buf_t) - 1] == '\n') + { + buf_t[strlen(buf_t) - 1] = '\0'; + } - event_desc = blr_get_event_description(router, last_event.event_type); + event_desc = blr_get_event_description(router, last_event.event_type); - MXS_NOTICE("%lu @ %llu, %s, (%s), Last EventTime", - last_event.event_time, last_event.event_pos, - event_desc != NULL ? event_desc : "unknown", buf_t); + MXS_NOTICE("%lu @ %llu, %s, (%s), Last EventTime", + last_event.event_time, last_event.event_pos, + event_desc != NULL ? event_desc : "unknown", buf_t); } diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index adb535a3d..4b3bd7e0d 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -31,23 +31,23 @@ * @verbatim * Revision History * - * Date Who Description - * 02/04/2014 Mark Riddoch Initial implementation - * 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility - * 25/05/2015 Massimiliano Pinto Added BLRM_SLAVE_STOPPED state - * 08/06/2015 Massimiliano Pinto Added m_errno and m_errmsg - * 23/06/2015 Massimiliano Pinto Master communication goes into BLRM_SLAVE_STOPPED state - * when an error is encountered in BLRM_BINLOGDUMP state. - * Server error code and msg are reported via SHOW SLAVE STATUS - * 03/08/2015 Massimiliano Pinto Initial implementation of transaction safety - * 13/08/2015 Massimiliano Pinto Addition of heartbeat check - * 23/08/2015 Massimiliano Pinto Added strerror_r - * 26/08/2015 Massimiliano Pinto Added MariaDB 10 GTID event check with flags = 0 - * This is the current supported condition for detecting - * MariaDB 10 transaction start point. - * It's no longer using QUERY_EVENT with BEGIN - * 25/09/2015 Massimiliano Pinto Addition of lastEventReceived for slaves - * 23/10/15 Markus Makela Added current_safe_event + * Date Who Description + * 02/04/2014 Mark Riddoch Initial implementation + * 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility + * 25/05/2015 Massimiliano Pinto Added BLRM_SLAVE_STOPPED state + * 08/06/2015 Massimiliano Pinto Added m_errno and m_errmsg + * 23/06/2015 Massimiliano Pinto Master communication goes into BLRM_SLAVE_STOPPED state + * when an error is encountered in BLRM_BINLOGDUMP state. + * Server error code and msg are reported via SHOW SLAVE STATUS + * 03/08/2015 Massimiliano Pinto Initial implementation of transaction safety + * 13/08/2015 Massimiliano Pinto Addition of heartbeat check + * 23/08/2015 Massimiliano Pinto Added strerror_r + * 26/08/2015 Massimiliano Pinto Added MariaDB 10 GTID event check with flags = 0 + * This is the current supported condition for detecting + * MariaDB 10 transaction start point. + * It's no longer using QUERY_EVENT with BEGIN + * 25/09/2015 Massimiliano Pinto Addition of lastEventReceived for slaves + * 23/10/15 Markus Makela Added current_safe_event * * @endverbatim */ @@ -84,7 +84,8 @@ static GWBUF *blr_make_binlog_dump(ROUTER_INSTANCE *router); void encode_value(unsigned char *data, unsigned int value, int len); void blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt); static int blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *pkt, REP_HEADER *hdr); -void blr_distribute_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr); +void blr_distribute_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr, + blr_thread_role_t role); static void *CreateMySQLAuthData(char *username, char *password, char *database); void blr_extract_header(uint8_t *pkt, REP_HEADER *hdr); static void blr_log_packet(int priority, char *msg, uint8_t *ptr, int len); @@ -92,16 +93,18 @@ void blr_master_close(ROUTER_INSTANCE *); char *blr_extract_column(GWBUF *buf, int col); void blr_cache_response(ROUTER_INSTANCE *router, char *response, GWBUF *buf); void poll_fake_write_event(DCB *dcb); -GWBUF *blr_read_events_from_pos(ROUTER_INSTANCE *router, unsigned long long pos, REP_HEADER *hdr, unsigned long long pos_end); +GWBUF *blr_read_events_from_pos(ROUTER_INSTANCE *router, unsigned long long pos, REP_HEADER *hdr, + unsigned long long pos_end); static void blr_check_last_master_event(void *inst); extern int blr_check_heartbeat(ROUTER_INSTANCE *router); extern char * blr_last_event_description(ROUTER_INSTANCE *router); static void blr_log_identity(ROUTER_INSTANCE *router); -static void blr_distribute_error_message(ROUTER_INSTANCE *router, char *message, char *state, unsigned int err_code); +static void blr_distribute_error_message(ROUTER_INSTANCE *router, char *message, char *state, + unsigned int err_code); int blr_write_data_into_binlog(ROUTER_INSTANCE *router, uint32_t data_len, uint8_t *buf); -bool blr_send_event(ROUTER_SLAVE *slave, REP_HEADER *hdr, uint8_t *buf); void extract_checksum(ROUTER_INSTANCE* router, uint8_t *cksumptr, uint8_t len); +static void blr_terminate_master_replication(ROUTER_INSTANCE *router, uint8_t* ptr, int len); static int keepalive = 1; @@ -110,85 +113,91 @@ static int keepalive = 1; * master MySQL server and triggers the slave registration process for * the router. * - * @param router The router instance + * @param router The router instance */ void blr_start_master(void* data) { ROUTER_INSTANCE *router = (ROUTER_INSTANCE*)data; -DCB *client; + DCB *client; - router->stats.n_binlogs_ses = 0; - spinlock_acquire(&router->lock); - if (router->master_state != BLRM_UNCONNECTED) - { - if (router->master_state != BLRM_SLAVE_STOPPED) { - MXS_ERROR("%s: Master Connect: Unexpected master state %s\n", + router->stats.n_binlogs_ses = 0; + spinlock_acquire(&router->lock); + if (router->master_state != BLRM_UNCONNECTED) + { + if (router->master_state != BLRM_SLAVE_STOPPED) + { + MXS_ERROR("%s: Master Connect: Unexpected master state %s\n", router->service->name, blrm_states[router->master_state]); - } else { - MXS_NOTICE("%s: Master Connect: binlog state is %s\n", + } + else + { + MXS_NOTICE("%s: Master Connect: binlog state is %s\n", router->service->name, blrm_states[router->master_state]); - } - spinlock_release(&router->lock); - return; - } - router->master_state = BLRM_CONNECTING; + } + spinlock_release(&router->lock); + return; + } + router->master_state = BLRM_CONNECTING; - /* Discard the queued residual data */ - while (router->residual) - { - router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); - } - router->residual = NULL; + /* Discard the queued residual data */ + while (router->residual) + { + router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); + } + router->residual = NULL; - spinlock_release(&router->lock); - if ((client = dcb_alloc(DCB_ROLE_INTERNAL)) == NULL) - { - MXS_ERROR("Binlog router: failed to create DCB for dummy client"); - return; - } - router->client = client; - client->state = DCB_STATE_POLLING; /* Fake the client is reading */ - client->data = CreateMySQLAuthData(router->user, router->password, ""); - if ((router->session = session_alloc(router->service, client)) == NULL) - { - MXS_ERROR("Binlog router: failed to create session for connection to master"); - return; - } - client->session = router->session; - if ((router->master = dcb_connect(router->service->dbref->server, router->session, BLR_PROTOCOL)) == NULL) - { - char *name; - if ((name = malloc(strlen(router->service->name) - + strlen(" Master") + 1)) != NULL) - { - sprintf(name, "%s Master", router->service->name); - hktask_oneshot(name, blr_start_master, router, - BLR_MASTER_BACKOFF_TIME * router->retry_backoff++); - free(name); - } - if (router->retry_backoff > BLR_MAX_BACKOFF) - router->retry_backoff = BLR_MAX_BACKOFF; - MXS_ERROR("Binlog router: failed to connect to master server '%s'", + spinlock_release(&router->lock); + if ((client = dcb_alloc(DCB_ROLE_INTERNAL)) == NULL) + { + MXS_ERROR("Binlog router: failed to create DCB for dummy client"); + return; + } + router->client = client; + client->state = DCB_STATE_POLLING; /* Fake the client is reading */ + client->data = CreateMySQLAuthData(router->user, router->password, ""); + if ((router->session = session_alloc(router->service, client)) == NULL) + { + MXS_ERROR("Binlog router: failed to create session for connection to master"); + return; + } + client->session = router->session; + if ((router->master = dcb_connect(router->service->dbref->server, router->session, BLR_PROTOCOL)) == NULL) + { + char *name; + if ((name = malloc(strlen(router->service->name) + strlen(" Master") + 1)) != NULL) + { + sprintf(name, "%s Master", router->service->name); + hktask_oneshot(name, blr_start_master, router, + BLR_MASTER_BACKOFF_TIME * router->retry_backoff++); + free(name); + } + if (router->retry_backoff > BLR_MAX_BACKOFF) + { + router->retry_backoff = BLR_MAX_BACKOFF; + } + MXS_ERROR("Binlog router: failed to connect to master server '%s'", router->service->dbref->server->unique_name); - return; - } - router->master->remote = strdup(router->service->dbref->server->name); + return; + } + router->master->remote = strdup(router->service->dbref->server->name); - MXS_NOTICE("%s: attempting to connect to master server %s:%d, binlog %s, pos %lu", + MXS_NOTICE("%s: attempting to connect to master server %s:%d, binlog %s, pos %lu", router->service->name, router->service->dbref->server->name, router->service->dbref->server->port, router->binlog_name, router->current_pos); - router->connect_time = time(0); + router->connect_time = time(0); - if (setsockopt(router->master->fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive , sizeof(keepalive ))) - perror("setsockopt"); + if (setsockopt(router->master->fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive , sizeof(keepalive ))) + { + perror("setsockopt"); + } - router->master_state = BLRM_AUTHENTICATED; - router->master->func.write(router->master, blr_make_query("SELECT UNIX_TIMESTAMP()")); - router->master_state = BLRM_TIMESTAMP; + router->master_state = BLRM_AUTHENTICATED; + router->master->func.write(router->master, blr_make_query("SELECT UNIX_TIMESTAMP()")); + router->master_state = BLRM_TIMESTAMP; - router->stats.n_masterstarts++; + router->stats.n_masterstarts++; } /** @@ -197,47 +206,48 @@ DCB *client; * IMPORTANT - must be called with router->active_logs set by the * thread that set active_logs. * - * @param router The router instance + * @param router The router instance */ static void blr_restart_master(ROUTER_INSTANCE *router) { - dcb_close(router->client); + dcb_close(router->client); - /* Discard the queued residual data */ - while (router->residual) - { - router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); - } - router->residual = NULL; + /* Discard the queued residual data */ + while (router->residual) + { + router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); + } + router->residual = NULL; - /* Now it is safe to unleash other threads on this router instance */ - spinlock_acquire(&router->lock); - router->reconnect_pending = 0; - router->active_logs = 0; - spinlock_release(&router->lock); - if (router->master_state < BLRM_BINLOGDUMP) - { - char *name; + /* Now it is safe to unleash other threads on this router instance */ + spinlock_acquire(&router->lock); + router->reconnect_pending = 0; + router->active_logs = 0; + spinlock_release(&router->lock); + if (router->master_state < BLRM_BINLOGDUMP) + { + char *name; - router->master_state = BLRM_UNCONNECTED; + router->master_state = BLRM_UNCONNECTED; - if ((name = malloc(strlen(router->service->name) - + strlen(" Master")+1)) != NULL) - { - sprintf(name, "%s Master", router->service->name); - hktask_oneshot(name, blr_start_master, router, - BLR_MASTER_BACKOFF_TIME * router->retry_backoff++); - free(name); - } - if (router->retry_backoff > BLR_MAX_BACKOFF) - router->retry_backoff = BLR_MAX_BACKOFF; - } - else - { - router->master_state = BLRM_UNCONNECTED; - blr_start_master(router); - } + if ((name = malloc(strlen(router->service->name) + strlen(" Master") + 1)) != NULL) + { + sprintf(name, "%s Master", router->service->name); + hktask_oneshot(name, blr_start_master, router, + BLR_MASTER_BACKOFF_TIME * router->retry_backoff++); + free(name); + } + if (router->retry_backoff > BLR_MAX_BACKOFF) + { + router->retry_backoff = BLR_MAX_BACKOFF; + } + } + else + { + router->master_state = BLRM_UNCONNECTED; + blr_start_master(router); + } } /** @@ -248,72 +258,73 @@ blr_restart_master(ROUTER_INSTANCE *router) * threads are active then directly call the restart routine to * reconnect to the master. * - * @param router The router instance + * @param router The router instance */ void blr_master_reconnect(ROUTER_INSTANCE *router) { -int do_reconnect = 0; + int do_reconnect = 0; - if (router->master_state == BLRM_SLAVE_STOPPED) - return; + if (router->master_state == BLRM_SLAVE_STOPPED) + { + return; + } - spinlock_acquire(&router->lock); - if (router->active_logs) - { - /* Currently processing a response, set a flag - * and get the thread that is process a response - * to deal with the reconnect. - */ - router->reconnect_pending = 1; - router->stats.n_delayedreconnects++; - } - else - { - router->active_logs = 1; - do_reconnect = 1; - } - spinlock_release(&router->lock); - if (do_reconnect) - { - blr_restart_master(router); - spinlock_acquire(&router->lock); - router->active_logs = 0; - spinlock_release(&router->lock); - } + spinlock_acquire(&router->lock); + if (router->active_logs) + { + /* Currently processing a response, set a flag + * and get the thread that is process a response + * to deal with the reconnect. + */ + router->reconnect_pending = 1; + router->stats.n_delayedreconnects++; + } + else + { + router->active_logs = 1; + do_reconnect = 1; + } + spinlock_release(&router->lock); + if (do_reconnect) + { + blr_restart_master(router); + spinlock_acquire(&router->lock); + router->active_logs = 0; + spinlock_release(&router->lock); + } } /** * Shutdown a connection to the master * - * @param router The router instance + * @param router The router instance */ void blr_master_close(ROUTER_INSTANCE *router) { - dcb_close(router->master); - router->master_state = BLRM_UNCONNECTED; - router->master_event_state = BLR_EVENT_DONE; + dcb_close(router->master); + router->master_state = BLRM_UNCONNECTED; + router->master_event_state = BLR_EVENT_DONE; } /** * Mark this master connection for a delayed reconnect, used during * error recovery to cause a reconnect after 60 seconds. * - * @param router The router instance + * @param router The router instance */ void blr_master_delayed_connect(ROUTER_INSTANCE *router) { -char *name; + char *name; - if ((name = malloc(strlen(router->service->name) - + strlen(" Master Recovery")+1)) != NULL) - { - sprintf(name, "%s Master Recovery", router->service->name); - hktask_oneshot(name, blr_start_master, router, 60); - free(name); - } + if ((name = malloc(strlen(router->service->name) + strlen(" Master Recovery") + 1)) != NULL) + { + sprintf(name, "%s Master Recovery", router->service->name); + hktask_oneshot(name, blr_start_master, router, 60); + free(name); + } } /** @@ -322,405 +333,456 @@ char *name; * Handles an incoming response from the master server to the binlog * router. * - * @param router The router instance - * @param buf The incoming packet + * @param router The router instance + * @param buf The incoming packet */ void blr_master_response(ROUTER_INSTANCE *router, GWBUF *buf) { -char query[BLRM_MASTER_REGITRATION_QUERY_LEN+1]; -char task_name[BLRM_TASK_NAME_LEN + 1] = ""; + char query[BLRM_MASTER_REGITRATION_QUERY_LEN + 1]; + char task_name[BLRM_TASK_NAME_LEN + 1] = ""; - atomic_add(&router->handling_threads, 1); - ss_dassert(router->handling_threads == 1); - spinlock_acquire(&router->lock); - router->active_logs = 1; - spinlock_release(&router->lock); - if (router->master_state < 0 || router->master_state > BLRM_MAXSTATE) - { - MXS_ERROR("Invalid master state machine state (%d) for binlog router.", - router->master_state); - gwbuf_consume(buf, gwbuf_length(buf)); + atomic_add(&router->handling_threads, 1); + ss_dassert(router->handling_threads == 1); + spinlock_acquire(&router->lock); + router->active_logs = 1; + spinlock_release(&router->lock); + if (router->master_state < 0 || router->master_state > BLRM_MAXSTATE) + { + MXS_ERROR("Invalid master state machine state (%d) for binlog router.", + router->master_state); + gwbuf_consume(buf, gwbuf_length(buf)); - spinlock_acquire(&router->lock); - if (router->reconnect_pending) - { - router->active_logs = 0; - spinlock_release(&router->lock); - atomic_add(&router->handling_threads, -1); - MXS_ERROR("%s: Pending reconnect in state %s.", - router->service->name, - blrm_states[router->master_state]); - blr_restart_master(router); - return; - } - router->active_logs = 0; - spinlock_release(&router->lock); - atomic_add(&router->handling_threads, -1); - return; - } + spinlock_acquire(&router->lock); + if (router->reconnect_pending) + { + router->active_logs = 0; + spinlock_release(&router->lock); + atomic_add(&router->handling_threads, -1); + MXS_ERROR("%s: Pending reconnect in state %s.", + router->service->name, + blrm_states[router->master_state]); + blr_restart_master(router); + return; + } + router->active_logs = 0; + spinlock_release(&router->lock); + atomic_add(&router->handling_threads, -1); + return; + } - if (router->master_state == BLRM_GTIDMODE && MYSQL_RESPONSE_ERR(buf)) - { - /* - * If we get an error response to the GTID Mode then we - * asusme the server does not support GTID modes and - * continue. The error is saved and replayed to slaves if - * they also request the GTID mode. - */ - MXS_ERROR("%s: Master server does not support GTID Mode.", - router->service->name); - } - else if (router->master_state != BLRM_BINLOGDUMP && MYSQL_RESPONSE_ERR(buf)) - { - char *msg_err = NULL; - int msg_len=0; - int len = gwbuf_length(buf); - unsigned long mysql_errno = extract_field(MYSQL_ERROR_CODE(buf), 16); + if (router->master_state == BLRM_GTIDMODE && MYSQL_RESPONSE_ERR(buf)) + { + /* + * If we get an error response to the GTID Mode then we + * asusme the server does not support GTID modes and + * continue. The error is saved and replayed to slaves if + * they also request the GTID mode. + */ + MXS_ERROR("%s: Master server does not support GTID Mode.", + router->service->name); + } + else if (router->master_state != BLRM_BINLOGDUMP && MYSQL_RESPONSE_ERR(buf)) + { + char *msg_err = NULL; + int msg_len = 0; + int len = gwbuf_length(buf); + unsigned long mysql_errno = extract_field(MYSQL_ERROR_CODE(buf), 16); - msg_len = len-7-6; // +7 is where msg starts, 6 is skipped the status message (#42000) - msg_err = (char *)malloc(msg_len + 1); + msg_len = len - 7 - 6; // +7 is where msg starts, 6 is skipped the status message (#42000) + msg_err = (char *)malloc(msg_len + 1); - // skip status message only as MYSQL_RESPONSE_ERR(buf) points to GWBUF_DATA(buf) +7 - strncpy(msg_err, (char *)(MYSQL_ERROR_MSG(buf) + 6), msg_len); + // skip status message only as MYSQL_RESPONSE_ERR(buf) points to GWBUF_DATA(buf) +7 + strncpy(msg_err, (char *)(MYSQL_ERROR_MSG(buf) + 6), msg_len); - /* NULL terminated error string */ - *(msg_err+msg_len)='\0'; + /* NULL terminated error string */ + *(msg_err + msg_len) = '\0'; - MXS_ERROR("%s: Received error: %lu, '%s' from master during '%s' phase " + MXS_ERROR("%s: Received error: %lu, '%s' from master during '%s' phase " "of the master state machine.", router->service->name, mysql_errno, msg_err, blrm_states[router->master_state]); - gwbuf_consume(buf, gwbuf_length(buf)); + gwbuf_consume(buf, gwbuf_length(buf)); - spinlock_acquire(&router->lock); + spinlock_acquire(&router->lock); - /* set mysql errno */ - router->m_errno = mysql_errno; + /* set mysql errno */ + router->m_errno = mysql_errno; - /* set mysql error message */ - if (router->m_errmsg) - free(router->m_errmsg); - router->m_errmsg = msg_err; + /* set mysql error message */ + if (router->m_errmsg) + { + free(router->m_errmsg); + } + router->m_errmsg = msg_err; - router->active_logs = 0; - if (router->reconnect_pending) - { - spinlock_release(&router->lock); - atomic_add(&router->handling_threads, -1); - blr_restart_master(router); - return; - } - spinlock_release(&router->lock); - atomic_add(&router->handling_threads, -1); - return; - } - switch (router->master_state) - { - case BLRM_TIMESTAMP: - // Response to a timestamp message, no need to save this. - gwbuf_consume(buf, GWBUF_LENGTH(buf)); - buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_ID'"); - router->master_state = BLRM_SERVERID; - router->master->func.write(router->master, buf); - router->retry_backoff = 1; - break; - case BLRM_SERVERID: - { - char *val = blr_extract_column(buf, 2); + router->active_logs = 0; + if (router->reconnect_pending) + { + spinlock_release(&router->lock); + atomic_add(&router->handling_threads, -1); + blr_restart_master(router); + return; + } + spinlock_release(&router->lock); + atomic_add(&router->handling_threads, -1); + return; + } + switch (router->master_state) + { + case BLRM_TIMESTAMP: + // Response to a timestamp message, no need to save this. + gwbuf_consume(buf, GWBUF_LENGTH(buf)); + buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_ID'"); + router->master_state = BLRM_SERVERID; + router->master->func.write(router->master, buf); + router->retry_backoff = 1; + break; + case BLRM_SERVERID: + { + char *val = blr_extract_column(buf, 2); - // Response to fetch of master's server-id - if (router->saved_master.server_id) - GWBUF_CONSUME_ALL(router->saved_master.server_id); - router->saved_master.server_id = buf; - blr_cache_response(router, "serverid", buf); + // Response to fetch of master's server-id + if (router->saved_master.server_id) + { + GWBUF_CONSUME_ALL(router->saved_master.server_id); + } + router->saved_master.server_id = buf; + blr_cache_response(router, "serverid", buf); - // set router->masterid from master server-id if it's not set by the config option - if (router->masterid == 0) { - router->masterid = atoi(val); - } + // set router->masterid from master server-id if it's not set by the config option + if (router->masterid == 0) + { + router->masterid = atoi(val); + } - { - char str[BLRM_SET_HEARTBEAT_QUERY_LEN]; - sprintf(str, "SET @master_heartbeat_period = %lu000000000", router->heartbeat); - buf = blr_make_query(str); - } - router->master_state = BLRM_HBPERIOD; - router->master->func.write(router->master, buf); - free(val); - break; - } - case BLRM_HBPERIOD: - // Response to set the heartbeat period - if (router->saved_master.heartbeat) - GWBUF_CONSUME_ALL(router->saved_master.heartbeat); - router->saved_master.heartbeat = buf; - blr_cache_response(router, "heartbeat", buf); - buf = blr_make_query("SET @master_binlog_checksum = @@global.binlog_checksum"); - router->master_state = BLRM_CHKSUM1; - router->master->func.write(router->master, buf); - break; - case BLRM_CHKSUM1: - // Response to set the master binlog checksum - if (router->saved_master.chksum1) - GWBUF_CONSUME_ALL(router->saved_master.chksum1); - router->saved_master.chksum1 = buf; - blr_cache_response(router, "chksum1", buf); - buf = blr_make_query("SELECT @master_binlog_checksum"); - router->master_state = BLRM_CHKSUM2; - router->master->func.write(router->master, buf); - break; - case BLRM_CHKSUM2: - { - char *val = blr_extract_column(buf, 1); + { + char str[BLRM_SET_HEARTBEAT_QUERY_LEN]; + sprintf(str, "SET @master_heartbeat_period = %lu000000000", router->heartbeat); + buf = blr_make_query(str); + } + router->master_state = BLRM_HBPERIOD; + router->master->func.write(router->master, buf); + free(val); + break; + } + case BLRM_HBPERIOD: + // Response to set the heartbeat period + if (router->saved_master.heartbeat) + { + GWBUF_CONSUME_ALL(router->saved_master.heartbeat); + } + router->saved_master.heartbeat = buf; + blr_cache_response(router, "heartbeat", buf); + buf = blr_make_query("SET @master_binlog_checksum = @@global.binlog_checksum"); + router->master_state = BLRM_CHKSUM1; + router->master->func.write(router->master, buf); + break; + case BLRM_CHKSUM1: + // Response to set the master binlog checksum + if (router->saved_master.chksum1) + { + GWBUF_CONSUME_ALL(router->saved_master.chksum1); + } + router->saved_master.chksum1 = buf; + blr_cache_response(router, "chksum1", buf); + buf = blr_make_query("SELECT @master_binlog_checksum"); + router->master_state = BLRM_CHKSUM2; + router->master->func.write(router->master, buf); + break; + case BLRM_CHKSUM2: + { + char *val = blr_extract_column(buf, 1); - if (val && strncasecmp(val, "NONE", 4) == 0) - { - router->master_chksum = false; - } - if (val) - free(val); - // Response to the master_binlog_checksum, should be stored - if (router->saved_master.chksum2) - GWBUF_CONSUME_ALL(router->saved_master.chksum2); - router->saved_master.chksum2 = buf; - blr_cache_response(router, "chksum2", buf); + if (val && strncasecmp(val, "NONE", 4) == 0) + { + router->master_chksum = false; + } + if (val) + { + free(val); + } + // Response to the master_binlog_checksum, should be stored + if (router->saved_master.chksum2) + { + GWBUF_CONSUME_ALL(router->saved_master.chksum2); + } + router->saved_master.chksum2 = buf; + blr_cache_response(router, "chksum2", buf); - if (router->mariadb10_compat) { - buf = blr_make_query("SET @mariadb_slave_capability=4"); - router->master_state = BLRM_MARIADB10; - } else { - buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); - router->master_state = BLRM_GTIDMODE; - } - router->master->func.write(router->master, buf); - break; - } - case BLRM_MARIADB10: - // Response to the SET @mariadb_slave_capability=4, should be stored - if (router->saved_master.mariadb10) - GWBUF_CONSUME_ALL(router->saved_master.mariadb10); - router->saved_master.mariadb10 = buf; - blr_cache_response(router, "mariadb10", buf); - buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_UUID'"); - router->master_state = BLRM_MUUID; - router->master->func.write(router->master, buf); - break; - case BLRM_GTIDMODE: - // Response to the GTID_MODE, should be stored - if (router->saved_master.gtid_mode) - GWBUF_CONSUME_ALL(router->saved_master.gtid_mode); - router->saved_master.gtid_mode = buf; - blr_cache_response(router, "gtidmode", buf); - buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_UUID'"); - router->master_state = BLRM_MUUID; - router->master->func.write(router->master, buf); - break; - case BLRM_MUUID: - { - char *key; - char *val = NULL; + if (router->mariadb10_compat) + { + buf = blr_make_query("SET @mariadb_slave_capability=4"); + router->master_state = BLRM_MARIADB10; + } + else + { + buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); + router->master_state = BLRM_GTIDMODE; + } + router->master->func.write(router->master, buf); + break; + } + case BLRM_MARIADB10: + // Response to the SET @mariadb_slave_capability=4, should be stored + if (router->saved_master.mariadb10) + { + GWBUF_CONSUME_ALL(router->saved_master.mariadb10); + } + router->saved_master.mariadb10 = buf; + blr_cache_response(router, "mariadb10", buf); + buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_UUID'"); + router->master_state = BLRM_MUUID; + router->master->func.write(router->master, buf); + break; + case BLRM_GTIDMODE: + // Response to the GTID_MODE, should be stored + if (router->saved_master.gtid_mode) + { + GWBUF_CONSUME_ALL(router->saved_master.gtid_mode); + } + router->saved_master.gtid_mode = buf; + blr_cache_response(router, "gtidmode", buf); + buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_UUID'"); + router->master_state = BLRM_MUUID; + router->master->func.write(router->master, buf); + break; + case BLRM_MUUID: + { + char *key; + char *val = NULL; - key = blr_extract_column(buf, 1); - if (key && strlen(key)) - val = blr_extract_column(buf, 2); - if (key) - free(key); + key = blr_extract_column(buf, 1); + if (key && strlen(key)) + { + val = blr_extract_column(buf, 2); + } + if (key) + { + free(key); + } - /* set the master_uuid from master if not set by the option */ - if (router->set_master_uuid == NULL) { - free(router->master_uuid); - router->master_uuid = val; - } else { - router->master_uuid = router->set_master_uuid; - } + /* set the master_uuid from master if not set by the option */ + if (router->set_master_uuid == NULL) + { + free(router->master_uuid); + router->master_uuid = val; + } + else + { + router->master_uuid = router->set_master_uuid; + } - // Response to the SERVER_UUID, should be stored - if (router->saved_master.uuid) - GWBUF_CONSUME_ALL(router->saved_master.uuid); - router->saved_master.uuid = buf; - blr_cache_response(router, "uuid", buf); - sprintf(query, "SET @slave_uuid='%s'", router->uuid); - buf = blr_make_query(query); - router->master_state = BLRM_SUUID; - router->master->func.write(router->master, buf); - break; - } - case BLRM_SUUID: - // Response to the SET @server_uuid, should be stored - if (router->saved_master.setslaveuuid) - GWBUF_CONSUME_ALL(router->saved_master.setslaveuuid); - router->saved_master.setslaveuuid = buf; - blr_cache_response(router, "ssuuid", buf); - buf = blr_make_query("SET NAMES latin1"); - router->master_state = BLRM_LATIN1; - router->master->func.write(router->master, buf); - break; - case BLRM_LATIN1: - // Response to the SET NAMES latin1, should be stored - if (router->saved_master.setnames) - GWBUF_CONSUME_ALL(router->saved_master.setnames); - router->saved_master.setnames = buf; - blr_cache_response(router, "setnames", buf); - buf = blr_make_query("SET NAMES utf8"); - router->master_state = BLRM_UTF8; - router->master->func.write(router->master, buf); - break; - case BLRM_UTF8: - // Response to the SET NAMES utf8, should be stored - if (router->saved_master.utf8) - GWBUF_CONSUME_ALL(router->saved_master.utf8); - router->saved_master.utf8 = buf; - blr_cache_response(router, "utf8", buf); - buf = blr_make_query("SELECT 1"); - router->master_state = BLRM_SELECT1; - router->master->func.write(router->master, buf); - break; - case BLRM_SELECT1: - // Response to the SELECT 1, should be stored - if (router->saved_master.select1) - GWBUF_CONSUME_ALL(router->saved_master.select1); - router->saved_master.select1 = buf; - blr_cache_response(router, "select1", buf); - buf = blr_make_query("SELECT VERSION()"); - router->master_state = BLRM_SELECTVER; - router->master->func.write(router->master, buf); - break; - case BLRM_SELECTVER: - // Response to SELECT VERSION should be stored - if (router->saved_master.selectver) - GWBUF_CONSUME_ALL(router->saved_master.selectver); - router->saved_master.selectver = buf; - blr_cache_response(router, "selectver", buf); - buf = blr_make_query("SELECT @@version_comment limit 1"); - router->master_state = BLRM_SELECTVERCOM; - router->master->func.write(router->master, buf); - break; - case BLRM_SELECTVERCOM: - // Response to SELECT @@version_comment should be stored - if (router->saved_master.selectvercom) - GWBUF_CONSUME_ALL(router->saved_master.selectvercom); - router->saved_master.selectvercom = buf; - blr_cache_response(router, "selectvercom", buf); - buf = blr_make_query("SELECT @@hostname"); - router->master_state = BLRM_SELECTHOSTNAME; - router->master->func.write(router->master, buf); - break; - case BLRM_SELECTHOSTNAME: - // Response to SELECT @@hostname should be stored - if (router->saved_master.selecthostname) - GWBUF_CONSUME_ALL(router->saved_master.selecthostname); - router->saved_master.selecthostname = buf; - blr_cache_response(router, "selecthostname", buf); - buf = blr_make_query("SELECT @@max_allowed_packet"); - router->master_state = BLRM_MAP; - router->master->func.write(router->master, buf); - break; - case BLRM_MAP: - // Response to SELECT @@max_allowed_packet should be stored - if (router->saved_master.map) - GWBUF_CONSUME_ALL(router->saved_master.map); - router->saved_master.map = buf; - blr_cache_response(router, "map", buf); - buf = blr_make_registration(router); - router->master_state = BLRM_REGISTER; - router->master->func.write(router->master, buf); - break; - case BLRM_REGISTER: - // Request a dump of the binlog file - buf = blr_make_binlog_dump(router); - router->master_state = BLRM_BINLOGDUMP; - router->master->func.write(router->master, buf); - MXS_NOTICE("%s: Request binlog records from %s at " + // Response to the SERVER_UUID, should be stored + if (router->saved_master.uuid) + { + GWBUF_CONSUME_ALL(router->saved_master.uuid); + } + router->saved_master.uuid = buf; + blr_cache_response(router, "uuid", buf); + sprintf(query, "SET @slave_uuid='%s'", router->uuid); + buf = blr_make_query(query); + router->master_state = BLRM_SUUID; + router->master->func.write(router->master, buf); + break; + } + case BLRM_SUUID: + // Response to the SET @server_uuid, should be stored + if (router->saved_master.setslaveuuid) + { + GWBUF_CONSUME_ALL(router->saved_master.setslaveuuid); + } + router->saved_master.setslaveuuid = buf; + blr_cache_response(router, "ssuuid", buf); + buf = blr_make_query("SET NAMES latin1"); + router->master_state = BLRM_LATIN1; + router->master->func.write(router->master, buf); + break; + case BLRM_LATIN1: + // Response to the SET NAMES latin1, should be stored + if (router->saved_master.setnames) + { + GWBUF_CONSUME_ALL(router->saved_master.setnames); + } + router->saved_master.setnames = buf; + blr_cache_response(router, "setnames", buf); + buf = blr_make_query("SET NAMES utf8"); + router->master_state = BLRM_UTF8; + router->master->func.write(router->master, buf); + break; + case BLRM_UTF8: + // Response to the SET NAMES utf8, should be stored + if (router->saved_master.utf8) + { + GWBUF_CONSUME_ALL(router->saved_master.utf8); + } + router->saved_master.utf8 = buf; + blr_cache_response(router, "utf8", buf); + buf = blr_make_query("SELECT 1"); + router->master_state = BLRM_SELECT1; + router->master->func.write(router->master, buf); + break; + case BLRM_SELECT1: + // Response to the SELECT 1, should be stored + if (router->saved_master.select1) + { + GWBUF_CONSUME_ALL(router->saved_master.select1); + } + router->saved_master.select1 = buf; + blr_cache_response(router, "select1", buf); + buf = blr_make_query("SELECT VERSION()"); + router->master_state = BLRM_SELECTVER; + router->master->func.write(router->master, buf); + break; + case BLRM_SELECTVER: + // Response to SELECT VERSION should be stored + if (router->saved_master.selectver) + { + GWBUF_CONSUME_ALL(router->saved_master.selectver); + } + router->saved_master.selectver = buf; + blr_cache_response(router, "selectver", buf); + buf = blr_make_query("SELECT @@version_comment limit 1"); + router->master_state = BLRM_SELECTVERCOM; + router->master->func.write(router->master, buf); + break; + case BLRM_SELECTVERCOM: + // Response to SELECT @@version_comment should be stored + if (router->saved_master.selectvercom) + { + GWBUF_CONSUME_ALL(router->saved_master.selectvercom); + } + router->saved_master.selectvercom = buf; + blr_cache_response(router, "selectvercom", buf); + buf = blr_make_query("SELECT @@hostname"); + router->master_state = BLRM_SELECTHOSTNAME; + router->master->func.write(router->master, buf); + break; + case BLRM_SELECTHOSTNAME: + // Response to SELECT @@hostname should be stored + if (router->saved_master.selecthostname) + { + GWBUF_CONSUME_ALL(router->saved_master.selecthostname); + } + router->saved_master.selecthostname = buf; + blr_cache_response(router, "selecthostname", buf); + buf = blr_make_query("SELECT @@max_allowed_packet"); + router->master_state = BLRM_MAP; + router->master->func.write(router->master, buf); + break; + case BLRM_MAP: + // Response to SELECT @@max_allowed_packet should be stored + if (router->saved_master.map) + { + GWBUF_CONSUME_ALL(router->saved_master.map); + } + router->saved_master.map = buf; + blr_cache_response(router, "map", buf); + buf = blr_make_registration(router); + router->master_state = BLRM_REGISTER; + router->master->func.write(router->master, buf); + break; + case BLRM_REGISTER: + // Request a dump of the binlog file + buf = blr_make_binlog_dump(router); + router->master_state = BLRM_BINLOGDUMP; + router->master->func.write(router->master, buf); + MXS_NOTICE("%s: Request binlog records from %s at " "position %lu from master server %s:%d", router->service->name, router->binlog_name, router->current_pos, router->service->dbref->server->name, router->service->dbref->server->port); - /* Log binlog router identity */ - blr_log_identity(router); + /* Log binlog router identity */ + blr_log_identity(router); - break; - case BLRM_BINLOGDUMP: - /** - * Main body, we have received a binlog record from the master - */ - blr_handle_binlog_record(router, buf); + break; + case BLRM_BINLOGDUMP: + /** + * Main body, we have received a binlog record from the master + */ + blr_handle_binlog_record(router, buf); - /** - * Set heartbeat check task - */ - snprintf(task_name, BLRM_TASK_NAME_LEN, "%s heartbeat", router->service->name); - hktask_add(task_name, blr_check_last_master_event, router, router->heartbeat); + /** + * Set heartbeat check task + */ + snprintf(task_name, BLRM_TASK_NAME_LEN, "%s heartbeat", router->service->name); + hktask_add(task_name, blr_check_last_master_event, router, router->heartbeat); - break; - } + break; + } - if (router->reconnect_pending) - blr_restart_master(router); - spinlock_acquire(&router->lock); - router->active_logs = 0; - spinlock_release(&router->lock); - atomic_add(&router->handling_threads, -1); + if (router->reconnect_pending) + { + blr_restart_master(router); + } + spinlock_acquire(&router->lock); + router->active_logs = 0; + spinlock_release(&router->lock); + atomic_add(&router->handling_threads, -1); } /** * Build a MySQL query into a GWBUF that we can send to the master database * - * @param query The text of the query to send + * @param query The text of the query to send */ static GWBUF * blr_make_query(char *query) { -GWBUF *buf; -unsigned char *data; -int len; + GWBUF *buf; + unsigned char *data; + int len; - if ((buf = gwbuf_alloc(strlen(query) + 5)) == NULL) - return NULL; - data = GWBUF_DATA(buf); - len = strlen(query) + 1; - encode_value(&data[0], len, 24); // Payload length - data[3] = 0; // Sequence id - // Payload - data[4] = COM_QUERY; // Command - memcpy(&data[5], query, strlen(query)); + if ((buf = gwbuf_alloc(strlen(query) + 5)) == NULL) + { + return NULL; + } + data = GWBUF_DATA(buf); + len = strlen(query) + 1; + encode_value(&data[0], len, 24); // Payload length + data[3] = 0; // Sequence id + // Payload + data[4] = COM_QUERY; // Command + memcpy(&data[5], query, strlen(query)); - return buf; + return buf; } /** * Build a MySQL slave registration into a GWBUF that we can send to the * master database * - * @param router The router instance - * @return A MySQL Replication registration message in a GWBUF structure + * @param router The router instance + * @return A MySQL Replication registration message in a GWBUF structure */ static GWBUF * blr_make_registration(ROUTER_INSTANCE *router) { -GWBUF *buf; -unsigned char *data; -int len = 18; + GWBUF *buf; + unsigned char *data; + int len = 18; - if ((buf = gwbuf_alloc(len + 4)) == NULL) - return NULL; - data = GWBUF_DATA(buf); - encode_value(&data[0], len, 24); // Payload length - data[3] = 0; // Sequence ID - data[4] = COM_REGISTER_SLAVE; // Command - encode_value(&data[5], router->serverid, 32); // Slave Server ID - data[9] = 0; // Slave hostname length - data[10] = 0; // Slave username length - data[11] = 0; // Slave password length - encode_value(&data[12], - router->service->ports->port, 16); // Slave master port - encode_value(&data[14], 0, 32); // Replication rank - encode_value(&data[18], router->masterid, 32); // Master server-id + if ((buf = gwbuf_alloc(len + 4)) == NULL) + { + return NULL; + } + data = GWBUF_DATA(buf); + encode_value(&data[0], len, 24); // Payload length + data[3] = 0; // Sequence ID + data[4] = COM_REGISTER_SLAVE; // Command + encode_value(&data[5], router->serverid, 32); // Slave Server ID + data[9] = 0; // Slave hostname length + data[10] = 0; // Slave username length + data[11] = 0; // Slave password length + encode_value(&data[12], + router->service->ports->port, 16); // Slave master port + encode_value(&data[14], 0, 32); // Replication rank + encode_value(&data[18], router->masterid, 32); // Master server-id - return buf; + return buf; } @@ -728,253 +790,258 @@ int len = 18; * Build a Binlog dump command into a GWBUF that we can send to the * master database * - * @param router The router instance - * @return A MySQL Replication COM_BINLOG_DUMP message in a GWBUF structure + * @param router The router instance + * @return A MySQL Replication COM_BINLOG_DUMP message in a GWBUF structure */ static GWBUF * blr_make_binlog_dump(ROUTER_INSTANCE *router) { -GWBUF *buf; -unsigned char *data; -int binlog_file_len = strlen(router->binlog_name); -/* COM_BINLOG_DUMP needs 11 bytes + binlogname (terminating NULL is not required) */ -int len = 11 + binlog_file_len; + GWBUF *buf; + unsigned char *data; + int binlog_file_len = strlen(router->binlog_name); + /* COM_BINLOG_DUMP needs 11 bytes + binlogname (terminating NULL is not required) */ + int len = 11 + binlog_file_len; + if ((buf = gwbuf_alloc(len + 4)) == NULL) + { + return NULL; + } + data = GWBUF_DATA(buf); - if ((buf = gwbuf_alloc(len + 4)) == NULL) - return NULL; - data = GWBUF_DATA(buf); - - encode_value(&data[0], len,24); // Payload length - data[3] = 0; // Sequence ID - data[4] = COM_BINLOG_DUMP; // Command - encode_value(&data[5], - router->current_pos, 32); // binlog position - encode_value(&data[9], 0, 16); // Flags - encode_value(&data[11], - router->serverid, 32); // Server-id of MaxScale - memcpy((char *)&data[15], router->binlog_name, - binlog_file_len); // binlog filename - return buf; + encode_value(&data[0], len, 24); // Payload length + data[3] = 0; // Sequence ID + data[4] = COM_BINLOG_DUMP; // Command + encode_value(&data[5], + router->current_pos, 32); // binlog position + encode_value(&data[9], 0, 16); // Flags + encode_value(&data[11], + router->serverid, 32); // Server-id of MaxScale + memcpy((char *)&data[15], router->binlog_name, + binlog_file_len); // binlog filename + return buf; } /** * Encode a value into a number of bits in a MySQL packet * - * @param data Point to location in target packet - * @param value The value to pack - * @param len Number of bits to encode value into + * @param data Point to location in target packet + * @param value The value to pack + * @param len Number of bits to encode value into */ void encode_value(unsigned char *data, unsigned int value, int len) { - while (len > 0) - { - *data++ = value & 0xff; - value >>= 8; - len -= 8; - } + while (len > 0) + { + *data++ = value & 0xff; + value >>= 8; + len -= 8; + } } /** * blr_handle_binlog_record - we have received binlog records from * the master and we must now work out what to do with them. * - * @param router The router instance - * @param pkt The binlog records + * @param router The router instance + * @param pkt The binlog records */ void blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt) { -uint8_t *msg = NULL, *ptr, *pdata; -REP_HEADER hdr; -unsigned int len = 0, reslen; -unsigned int pkt_length; -int no_residual = 1; -int preslen = -1; -int prev_length = -1; -int n_bufs = -1, pn_bufs = -1; + uint8_t *msg = NULL, *ptr, *pdata; + REP_HEADER hdr; + unsigned int len = 0, reslen; + unsigned int pkt_length; + int no_residual = 1; + int preslen = -1; + int prev_length = -1; + int n_bufs = -1, pn_bufs = -1; - /* - * Prepend any residual buffer to the buffer chain we have - * been called with. - */ - if (router->residual) - { - pkt = gwbuf_append(router->residual, pkt); - router->residual = NULL; - no_residual = 0; - } + /* + * Prepend any residual buffer to the buffer chain we have + * been called with. + */ + if (router->residual) + { + pkt = gwbuf_append(router->residual, pkt); + router->residual = NULL; + no_residual = 0; + } - pkt_length = gwbuf_length(pkt); - /* - * Loop over all the packets while we still have some data - * and the packet length is enough to hold a replication event - * header. - */ - while (pkt && pkt_length > 24) - { - reslen = GWBUF_LENGTH(pkt); - pdata = GWBUF_DATA(pkt); - if (reslen < 3) // Payload length straddles buffers - { - /* Get the length of the packet from the residual and new packet */ - if (reslen >= 3) - { - len = EXTRACT24(pdata); - } - else if (reslen == 2) - { - len = EXTRACT16(pdata); - len |= (*(uint8_t *)GWBUF_DATA(pkt->next) << 16); - } - else if (reslen == 1) - { - len = *pdata; - len |= (EXTRACT16(GWBUF_DATA(pkt->next)) << 8); - } - len += 4; // Allow space for the header - } - else - { - len = EXTRACT24(pdata) + 4; - } - /* len is now the payload length for the packet we are working on */ + pkt_length = gwbuf_length(pkt); + /* + * Loop over all the packets while we still have some data + * and the packet length is enough to hold a replication event + * header. + */ + while (pkt && pkt_length > 24) + { + reslen = GWBUF_LENGTH(pkt); + pdata = GWBUF_DATA(pkt); + if (reslen < 3) // Payload length straddles buffers + { + /* Get the length of the packet from the residual and new packet */ + if (reslen >= 3) + { + len = EXTRACT24(pdata); + } + else if (reslen == 2) + { + len = EXTRACT16(pdata); + len |= (*(uint8_t *)GWBUF_DATA(pkt->next) << 16); + } + else if (reslen == 1) + { + len = *pdata; + len |= (EXTRACT16(GWBUF_DATA(pkt->next)) << 8); + } + len += 4; // Allow space for the header + } + else + { + len = EXTRACT24(pdata) + 4; + } + /* len is now the payload length for the packet we are working on */ - if (reslen < len && pkt_length >= len) - { - /* - * The message is contained in more than the current - * buffer, however we have the complete messasge in - * this buffer and the chain of remaining buffers. - * - * Allocate a contiguous buffer for the binlog message - * and copy the complete message into this buffer. - */ - int remainder = len; - GWBUF *p = pkt; + if (reslen < len && pkt_length >= len) + { + /* + * The message is contained in more than the current + * buffer, however we have the complete messasge in + * this buffer and the chain of remaining buffers. + * + * Allocate a contiguous buffer for the binlog message + * and copy the complete message into this buffer. + */ + int remainder = len; + GWBUF *p = pkt; - if ((msg = malloc(len)) == NULL) - { - MXS_ERROR("Insufficient memory to buffer event " - "of %d bytes. Binlog %s @ %lu.", - len, router->binlog_name, - router->current_pos); - break; - } + if ((msg = malloc(len)) == NULL) + { + MXS_ERROR("Insufficient memory to buffer event " + "of %d bytes. Binlog %s @ %lu.", + len, router->binlog_name, + router->current_pos); + break; + } - n_bufs = 0; - ptr = msg; - while (p && remainder > 0) - { - int plen = GWBUF_LENGTH(p); - int n = (remainder > plen ? plen : remainder); - memcpy(ptr, GWBUF_DATA(p), n); - remainder -= n; - ptr += n; - if (remainder > 0) - p = p->next; - n_bufs++; - } - if (remainder) - { - MXS_ERROR("Expected entire message in buffer " - "chain, but failed to create complete " - "message as expected. %s @ %lu", - router->binlog_name, - router->current_pos); - free(msg); - msg = NULL; - break; - } + n_bufs = 0; + ptr = msg; + while (p && remainder > 0) + { + int plen = GWBUF_LENGTH(p); + int n = (remainder > plen ? plen : remainder); + memcpy(ptr, GWBUF_DATA(p), n); + remainder -= n; + ptr += n; + if (remainder > 0) + { + p = p->next; + } + n_bufs++; + } + if (remainder) + { + MXS_ERROR("Expected entire message in buffer " + "chain, but failed to create complete " + "message as expected. %s @ %lu", + router->binlog_name, + router->current_pos); + free(msg); + msg = NULL; + break; + } - ptr = msg; - } - else if (reslen < len) - { - /* - * The message is not fully contained in the current - * and we do not have the complete message in the - * buffer chain. Therefore we must stop processing - * until we receive the next buffer. - */ - router->stats.n_residuals++; - MXS_DEBUG("Residual data left after %lu records. %s @ %lu", - router->stats.n_binlogs, - router->binlog_name, router->current_pos); - break; - } - else - { - /* - * The message is fully contained in the current buffer - */ - ptr = pdata; - n_bufs = 1; - } + ptr = msg; + } + else if (reslen < len) + { + /* + * The message is not fully contained in the current + * and we do not have the complete message in the + * buffer chain. Therefore we must stop processing + * until we receive the next buffer. + */ + router->stats.n_residuals++; + MXS_DEBUG("Residual data left after %lu records. %s @ %lu", + router->stats.n_binlogs, + router->binlog_name, router->current_pos); + break; + } + else + { + /* + * The message is fully contained in the current buffer + */ + ptr = pdata; + n_bufs = 1; + } - /* - * ptr now points at the current message in a contiguous buffer, - * this buffer is either within the GWBUF or in a malloc'd - * copy if the message straddles GWBUF's. - */ + /* + * ptr now points at the current message in a contiguous buffer, + * this buffer is either within the GWBUF or in a malloc'd + * copy if the message straddles GWBUF's. + */ - if (len < BINLOG_EVENT_HDR_LEN && router->master_event_state != BLR_EVENT_ONGOING) - { - char *msg = ""; + if (len < BINLOG_EVENT_HDR_LEN && router->master_event_state != BLR_EVENT_ONGOING) + { + char *msg = ""; - /* Packet is too small to be a binlog event */ - if (ptr[4] == 0xfe) /* EOF Packet */ - { - msg = "end of file"; - } - else if (ptr[4] == 0xff) /* EOF Packet */ - { - msg = "error"; - } - MXS_NOTICE("Non-event message (%s) from master.", msg); - } - else - { - if (router->master_event_state == BLR_EVENT_DONE) - { - router->stats.n_binlogs++; - router->stats.n_binlogs_ses++; + /* Packet is too small to be a binlog event */ + if (ptr[4] == 0xfe) /* EOF Packet */ + { + msg = "end of file"; + } + else if (ptr[4] == 0xff) /* EOF Packet */ + { + msg = "error"; + } + MXS_NOTICE("Non-event message (%s) from master.", msg); + } + else + { + if (router->master_event_state == BLR_EVENT_DONE) + { + spinlock_acquire(&router->lock); + router->stats.n_binlogs++; + router->stats.n_binlogs_ses++; + spinlock_release(&router->lock); - blr_extract_header(ptr, &hdr); + blr_extract_header(ptr, &hdr); - /* Sanity check */ - if (hdr.ok == 0) - { - if (hdr.event_size != len - 5 && (hdr.event_size + 1) < MYSQL_PACKET_LENGTH_MAX) - { - MXS_ERROR("Packet length is %d, but event size is %d, " - "binlog file %s position %lu " - "reslen is %d and preslen is %d, " - "length of previous event %d. %s", - len, hdr.event_size, - router->binlog_name, - router->current_pos, - reslen, preslen, prev_length, - (prev_length == -1 ? - (no_residual ? "No residual data from previous call" : - "Residual data from previous call") : "")); + /* Sanity check */ + if (hdr.ok == 0) + { + if (hdr.event_size != len - 5 && (hdr.event_size + 1) < MYSQL_PACKET_LENGTH_MAX) + { + MXS_ERROR("Packet length is %d, but event size is %d, " + "binlog file %s position %lu " + "reslen is %d and preslen is %d, " + "length of previous event %d. %s", + len, hdr.event_size, + router->binlog_name, + router->current_pos, + reslen, preslen, prev_length, + (prev_length == -1 ? + (no_residual ? "No residual data from previous call" : + "Residual data from previous call") : "")); - blr_log_packet(LOG_ERR, "Packet:", ptr, len); + blr_log_packet(LOG_ERR, "Packet:", ptr, len); - MXS_ERROR("This event (0x%x) was contained in %d GWBUFs, " - "the previous events was contained in %d GWBUFs", - router->lastEventReceived, n_bufs, pn_bufs); + MXS_ERROR("This event (0x%x) was contained in %d GWBUFs, " + "the previous events was contained in %d GWBUFs", + router->lastEventReceived, n_bufs, pn_bufs); - if (msg) - { - free(msg); - msg = NULL; - } + if (msg) + { + free(msg); + msg = NULL; + } - break; - } + break; + } else if ((hdr.event_size + 1) >= MYSQL_PACKET_LENGTH_MAX) { router->master_event_state = BLR_EVENT_STARTED; @@ -983,29 +1050,41 @@ int n_bufs = -1, pn_bufs = -1; memcpy(&router->stored_header, &hdr, sizeof(hdr)); } - /** Prepare the checksum variables for this event */ - router->stored_checksum = crc32(0L, NULL, 0); - router->checksum_size = hdr.event_size - MYSQL_CHECKSUM_LEN; - router->partial_checksum_bytes = 0; - } + /** Prepare the checksum variables for this event */ + router->stored_checksum = crc32(0L, NULL, 0); + router->checksum_size = hdr.event_size - MYSQL_CHECKSUM_LEN; + router->partial_checksum_bytes = 0; + } + else + { + blr_terminate_master_replication(router, ptr, len); + } - if (hdr.ok == 0) - { - spinlock_acquire(&router->lock); + if (hdr.ok == 0) + { + spinlock_acquire(&router->lock); - /* set mysql errno to 0 */ - router->m_errno = 0; + /* set mysql errno to 0 */ + router->m_errno = 0; - /* Remove error message */ - if (router->m_errmsg) - free(router->m_errmsg); - router->m_errmsg = NULL; + /* Remove error message */ + if (router->m_errmsg) + { + free(router->m_errmsg); + } + router->m_errmsg = NULL; - spinlock_release(&router->lock); + spinlock_release(&router->lock); #ifdef SHOW_EVENTS - printf("blr: len %lu, event type 0x%02x, flags 0x%04x, event size %d, event timestamp %lu\n", (unsigned long)len-4, hdr.event_type, hdr.flags, hdr.event_size, (unsigned long)hdr.timestamp); + printf("blr: len %lu, event type 0x%02x, flags 0x%04x, " + "event size %d, event timestamp %lu\n", + (unsigned long)len - 4, + hdr.event_type, + hdr.flags, + hdr.event_size, + (unsigned long)hdr.timestamp); #endif - } + } } /* pending large event */ @@ -1092,7 +1171,7 @@ int n_bufs = -1, pn_bufs = -1; router->checksum_size -= size; } - if(router->checksum_size == 0 && size < len - offset) + if (router->checksum_size == 0 && size < len - offset) { extract_checksum(router, ptr + offset + size, len - offset - size); } @@ -1106,7 +1185,7 @@ int n_bufs = -1, pn_bufs = -1; free(msg); msg = NULL; MXS_ERROR("%s: Checksum error in event from master, " - "binlog %s @ %lu. Closing master connection.", + "binlog %s @ %lu. Closing master connection.", router->service->name, router->binlog_name, router->current_pos); blr_master_close(router); @@ -1122,52 +1201,58 @@ int n_bufs = -1, pn_bufs = -1; } } - if (hdr.ok == 0) - { - router->lastEventReceived = hdr.event_type; - router->lastEventTimestamp = hdr.timestamp; + if (hdr.ok == 0) + { + router->lastEventReceived = hdr.event_type; + router->lastEventTimestamp = hdr.timestamp; - /** - * Check for an open transaction, if the option is set - * Only complete transactions should be sent to sleves - * - * If a trasaction is pending router->binlog_position - * won't be updated to router->current_pos - */ + /** + * Check for an open transaction, if the option is set + * Only complete transactions should be sent to sleves + * + * If a trasaction is pending router->binlog_position + * won't be updated to router->current_pos + */ - spinlock_acquire(&router->binlog_lock); - if (router->trx_safe == 0 || (router->trx_safe && router->pending_transaction == 0)) { - /* no pending transaction: set current_pos to binlog_position */ - router->binlog_position = router->current_pos; - router->current_safe_event = router->current_pos; - } - spinlock_release(&router->binlog_lock); + spinlock_acquire(&router->binlog_lock); + if (router->trx_safe == 0 || (router->trx_safe && router->pending_transaction == 0)) + { + /* no pending transaction: set current_pos to binlog_position */ + router->binlog_position = router->current_pos; + router->current_safe_event = router->current_pos; + } + spinlock_release(&router->binlog_lock); - /** - * Detect transactions in events - * Only complete transactions should be sent to sleves - */ + /** + * Detect transactions in events + * Only complete transactions should be sent to sleves + */ - /* 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->trx_safe && router->master_event_state == BLR_EVENT_DONE) { - if (router->mariadb10_compat) { - if (hdr.event_type == MARIADB10_GTID_EVENT) { - uint64_t n_sequence; - uint32_t domainid; - unsigned int flags; - n_sequence = extract_field(ptr+4+20, 64); - domainid = extract_field(ptr+4+20 + 8, 32); - flags = *(ptr+4+20 + 8 + 4); + /* 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->trx_safe && router->master_event_state == BLR_EVENT_DONE) + { + if (router->mariadb10_compat) + { + if (hdr.event_type == MARIADB10_GTID_EVENT) + { + uint64_t n_sequence; + uint32_t domainid; + unsigned int flags; + n_sequence = extract_field(ptr + 4 + 20, 64); + domainid = extract_field(ptr + 4 + 20 + 8, 32); + flags = *(ptr + 4 + 20 + 8 + 4); - if (flags == 0) { - spinlock_acquire(&router->binlog_lock); + if (flags == 0) + { + spinlock_acquire(&router->binlog_lock); - if (router->pending_transaction > 0) { - MXS_ERROR("A MariaDB 10 transaction " + if (router->pending_transaction > 0) + { + MXS_ERROR("A MariaDB 10 transaction " "is already open " "@ %lu (GTID %u-%u-%lu) and " "a new one starts @ %lu", @@ -1176,212 +1261,232 @@ int n_bufs = -1, pn_bufs = -1; n_sequence, router->current_pos); - // An action should be taken here - } + // An action should be taken here + } - router->pending_transaction = 1; + router->pending_transaction = 1; - spinlock_release(&router->binlog_lock); - } - } - } + spinlock_release(&router->binlog_lock); + } + } + } - /** - * look for QUERY_EVENT [BEGIN / COMMIT] and XID_EVENT - */ + /** + * look for QUERY_EVENT [BEGIN / COMMIT] and XID_EVENT + */ - if(hdr.event_type == QUERY_EVENT) { - char *statement_sql; - int db_name_len, var_block_len, statement_len; - db_name_len = ptr[4+20+ 4 + 4]; - var_block_len = ptr[4+20+ 4 + 4 + 1 + 2]; + if (hdr.event_type == QUERY_EVENT) + { + char *statement_sql; + int db_name_len, var_block_len, statement_len; + db_name_len = ptr[4 + 20 + 4 + 4]; + var_block_len = ptr[4 + 20 + 4 + 4 + 1 + 2]; - statement_len = len - (4+20+4+4+1+2+2+var_block_len+1+db_name_len); - statement_sql = calloc(1, statement_len+1); - strncpy(statement_sql, (char *)ptr+4+20+4+4+1+2+2+var_block_len+1+db_name_len, statement_len); + statement_len = len - (4 + 20 + 4 + 4 + 1 + 2 + 2 + var_block_len + 1 + db_name_len); + statement_sql = calloc(1, statement_len + 1); + strncpy(statement_sql, + (char *)ptr + 4 + 20 + 4 + 4 + 1 + 2 + 2 + var_block_len + 1 + db_name_len, + statement_len); - spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&router->binlog_lock); - /* Check for BEGIN (it comes for START TRANSACTION too) */ - if (strncmp(statement_sql, "BEGIN", 5) == 0) { - if (router->pending_transaction > 0) { - MXS_ERROR("A transaction is already open " + /* Check for BEGIN (it comes for START TRANSACTION too) */ + if (strncmp(statement_sql, "BEGIN", 5) == 0) + { + if (router->pending_transaction > 0) + { + MXS_ERROR("A transaction is already open " "@ %lu and a new one starts @ %lu", router->binlog_position, router->current_pos); - // An action should be taken here - } + // An action should be taken here + } - router->pending_transaction = 1; - } + router->pending_transaction = 1; + } - /* Check for COMMIT in non transactional store engines */ - if (strncmp(statement_sql, "COMMIT", 6) == 0) { + /* Check for COMMIT in non transactional store engines */ + if (strncmp(statement_sql, "COMMIT", 6) == 0) + { + router->pending_transaction = 2; + } - router->pending_transaction = 2; - } + spinlock_release(&router->binlog_lock); - spinlock_release(&router->binlog_lock); + free(statement_sql); + } - free(statement_sql); - } + /* Check for COMMIT in Transactional engines, i.e InnoDB */ + if (hdr.event_type == XID_EVENT) + { + spinlock_acquire(&router->binlog_lock); - /* Check for COMMIT in Transactional engines, i.e InnoDB */ - if(hdr.event_type == XID_EVENT) { - spinlock_acquire(&router->binlog_lock); + if (router->pending_transaction) + { + router->pending_transaction = 3; + } + spinlock_release(&router->binlog_lock); + } + } - if (router->pending_transaction) { - router->pending_transaction = 3; - } - spinlock_release(&router->binlog_lock); - } - } + /** Gather statistics about the replication event types */ + int event_limit = router->mariadb10_compat ? + MAX_EVENT_TYPE_MARIADB10 : MAX_EVENT_TYPE; - /** Gather statistics about the replication event types */ - int event_limit = router->mariadb10_compat ? - MAX_EVENT_TYPE_MARIADB10 : MAX_EVENT_TYPE; + if (hdr.event_type >= 0 && hdr.event_type <= event_limit) + { + router->stats.events[hdr.event_type]++; + } - if (hdr.event_type <= event_limit) - router->stats.events[hdr.event_type]++; - - if (hdr.event_type == FORMAT_DESCRIPTION_EVENT && hdr.next_pos == 0) - { - // Fake format description message - MXS_DEBUG("Replication fake event. " + if (hdr.event_type == FORMAT_DESCRIPTION_EVENT && hdr.next_pos == 0) + { + // Fake format description message + MXS_DEBUG("Replication fake event. " "Binlog %s @ %lu.", router->binlog_name, router->current_pos); - router->stats.n_fakeevents++; + router->stats.n_fakeevents++; - if (hdr.event_type == FORMAT_DESCRIPTION_EVENT) - { - uint8_t *new_fde; - unsigned int new_fde_len; - /* - * We need to save this to replay to new - * slaves that attach later. - */ - new_fde_len = hdr.event_size; - new_fde = malloc(hdr.event_size); - if (new_fde) - { - memcpy(new_fde, ptr + 5, hdr.event_size); - if (router->saved_master.fde_event) - free(router->saved_master.fde_event); - router->saved_master.fde_event = new_fde; - router->saved_master.fde_len = new_fde_len; - } - else - { - MXS_ERROR("%s: Received a format description " + if (hdr.event_type == FORMAT_DESCRIPTION_EVENT) + { + uint8_t *new_fde; + unsigned int new_fde_len; + /* + * We need to save this to replay to new + * slaves that attach later. + */ + new_fde_len = hdr.event_size; + new_fde = malloc(hdr.event_size); + if (new_fde) + { + memcpy(new_fde, ptr + 5, hdr.event_size); + if (router->saved_master.fde_event) + { + free(router->saved_master.fde_event); + } + router->saved_master.fde_event = new_fde; + router->saved_master.fde_len = new_fde_len; + } + else + { + MXS_ERROR("%s: Received a format description " "event that MaxScale was unable to " "record. Event length is %d.", router->service->name, hdr.event_size); - blr_log_packet(LOG_ERR, - "Format Description Event:", ptr, len); - } - } - } - else - { - if (hdr.event_type == HEARTBEAT_EVENT) - { + blr_log_packet(LOG_ERR, + "Format Description Event:", ptr, len); + } + } + } + else + { + if (hdr.event_type == HEARTBEAT_EVENT) + { #ifdef SHOW_EVENTS - printf("Replication heartbeat\n"); + printf("Replication heartbeat\n"); #endif - MXS_DEBUG("Replication heartbeat. " + MXS_DEBUG("Replication heartbeat. " "Binlog %s @ %lu.", router->binlog_name, router->current_pos); - router->stats.n_heartbeats++; + router->stats.n_heartbeats++; - if (router->pending_transaction) - router->stats.lastReply = time(0); - } - else if (hdr.flags != LOG_EVENT_ARTIFICIAL_F) - { - ptr = ptr + 4; // Skip header - uint32_t offset = 4; + if (router->pending_transaction) + { + router->stats.lastReply = time(0); + } + } + else if (hdr.flags != LOG_EVENT_ARTIFICIAL_F) + { + ptr = ptr + 4; // Skip header + uint32_t offset = 4; - if (router->master_event_state == BLR_EVENT_STARTED || - router->master_event_state == BLR_EVENT_DONE) - { - ptr++; - offset++; - } + if (router->master_event_state == BLR_EVENT_STARTED || + router->master_event_state == BLR_EVENT_DONE) + { + ptr++; + offset++; + } - if (hdr.event_type == ROTATE_EVENT) - { - spinlock_acquire(&router->binlog_lock); - router->rotating = 1; - spinlock_release(&router->binlog_lock); - } + if (hdr.event_type == ROTATE_EVENT) + { + spinlock_acquire(&router->binlog_lock); + router->rotating = 1; + spinlock_release(&router->binlog_lock); + } - /* Current event is being written to disk file. - * It is possible for an empty packet to be sent if an - * event is exactly 2^24 bytes long. In this case the - * empty packet should be discarded. */ + /* Current event is being written to disk file. + * It is possible for an empty packet to be sent if an + * event is exactly 2^24 bytes long. In this case the + * empty packet should be discarded. */ - if (len > MYSQL_HEADER_LEN && - blr_write_binlog_record(router, &hdr, len - offset, ptr) == 0) - { - /* - * Failed to write to the - * binlog file, destroy the - * buffer chain and close the - * connection with the master - */ - while ((pkt = gwbuf_consume(pkt, - GWBUF_LENGTH(pkt))) != NULL); - blr_master_close(router); - blr_master_delayed_connect(router); - return; - } + if (len > MYSQL_HEADER_LEN && + blr_write_binlog_record(router, &hdr, len - offset, ptr) == 0) + { + /* + * Failed to write to the + * binlog file, destroy the + * buffer chain and close the + * connection with the master + */ + while ((pkt = gwbuf_consume(pkt, GWBUF_LENGTH(pkt))) != NULL) + { + ; + } + blr_master_close(router); + blr_master_delayed_connect(router); + return; + } - /* Check for rotete event */ - if (hdr.event_type == ROTATE_EVENT) - { - if (!blr_rotate_event(router, ptr, &hdr)) - { - /* - * Failed to write to the - * binlog file, destroy the - * buffer chain and close the - * connection with the master - */ - while ((pkt = gwbuf_consume(pkt, - GWBUF_LENGTH(pkt))) != NULL); - blr_master_close(router); - blr_master_delayed_connect(router); - return; - } - } + /* Check for rotete event */ + if (hdr.event_type == ROTATE_EVENT) + { + if (!blr_rotate_event(router, ptr, &hdr)) + { + /* + * Failed to write to the + * binlog file, destroy the + * buffer chain and close the + * connection with the master + */ + while ((pkt = gwbuf_consume(pkt, GWBUF_LENGTH(pkt))) != NULL) + { + ; + } + blr_master_close(router); + blr_master_delayed_connect(router); + return; + } + } - /** - * Distributing binlog events to slaves - * may depend on pending transaction - */ + /** + * Distributing binlog events to slaves + * may depend on pending transaction + */ - spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&router->binlog_lock); - if (router->trx_safe == 0 || (router->trx_safe && router->pending_transaction == 0)) { + if (router->trx_safe == 0 || (router->trx_safe && router->pending_transaction == 0)) + { + router->binlog_position = router->current_pos; + router->current_safe_event = router->last_event_pos; - router->binlog_position = router->current_pos; - router->current_safe_event = router->current_pos; + spinlock_release(&router->binlog_lock); - spinlock_release(&router->binlog_lock); - - if (router->master_event_state == BLR_EVENT_COMPLETE) - { - /** Read the complete event from the disk */ - GWBUF *record = blr_read_events_from_pos(router, router->last_event_pos, &hdr, hdr.next_pos); + if (router->master_event_state == BLR_EVENT_COMPLETE) + { + /** Read the complete event from the disk */ + GWBUF *record = blr_read_events_from_pos(router, + router->last_event_pos, + &hdr, hdr.next_pos); if (record) { uint8_t *data = GWBUF_DATA(record); - blr_distribute_binlog_record(router, &hdr, data); + blr_distribute_binlog_record(router, &hdr, data, + BLR_THREAD_ROLE_MASTER_LARGE_NOTRX); gwbuf_free(record); } else @@ -1390,81 +1495,105 @@ int n_bufs = -1, pn_bufs = -1; "%lu with a size of %u bytes.", router->last_event_pos, hdr.event_size); } - } - else - { - /* Now distribute events */ - blr_distribute_binlog_record(router, &hdr, ptr); - } - } else { - /** - * If transaction is closed: - * - * 1) read current binlog starting - * from router->binlog_position - * - * 2) distribute read event - * - * 3) set router->binlog_position to - * router->current_pos - * - */ + } + else + { + /* Now distribute events */ + blr_distribute_binlog_record(router, &hdr, ptr, + BLR_THREAD_ROLE_MASTER_NOTRX); + } + } + else + { + /** + * If transaction is closed: + * + * 1) read current binlog starting + * from router->binlog_position + * + * 2) distribute read event + * + * 3) set router->binlog_position to + * router->current_pos + * + */ - if (router->pending_transaction > 1) { - unsigned long long pos; - unsigned long long end_pos; - GWBUF *record; - uint8_t *raw_data; - REP_HEADER new_hdr; - int i=0; + if (router->pending_transaction > 1) + { + unsigned long long pos; + unsigned long long end_pos; + GWBUF *record; + uint8_t *raw_data; + REP_HEADER new_hdr; + int i = 0; - pos = router->binlog_position; - end_pos = router->current_pos; + pos = router->binlog_position; + end_pos = router->current_pos; - spinlock_release(&router->binlog_lock); + spinlock_release(&router->binlog_lock); - while ((record = blr_read_events_from_pos(router, pos, &new_hdr, end_pos)) != NULL) { - i++; - raw_data = GWBUF_DATA(record); + while ((record = blr_read_events_from_pos(router, + pos, + &new_hdr, + end_pos)) != NULL) + { + i++; + raw_data = GWBUF_DATA(record); - /* distribute event */ - blr_distribute_binlog_record(router, &new_hdr, raw_data); + /* distribute event */ + blr_distribute_binlog_record(router, &new_hdr, raw_data, + BLR_THREAD_ROLE_MASTER_TRX); - spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&router->binlog_lock); - /** The current safe position is only updated - * if it points to the event we just distributed. */ - if(router->current_safe_event == pos) - { - router->current_safe_event = new_hdr.next_pos; - } + /** The current safe position is only updated + * if it points to the event we just distributed. */ + if (router->current_safe_event == pos) + { + router->current_safe_event = new_hdr.next_pos; + } + else + { + MXS_ERROR("Current safe event (%lu) does" + " not point at the event we " + "just sent (%llu) from binlog file %s. " + "Last commit at %lu, last write at %lu.", + router->current_safe_event, pos, + router->binlog_name, router->last_safe_pos, + router->last_written); + } - pos = new_hdr.next_pos; + pos = new_hdr.next_pos; - spinlock_release(&router->binlog_lock); + spinlock_release(&router->binlog_lock); - gwbuf_free(record); - } + gwbuf_free(record); + } - /* Check whether binlog records has been read in previous loop */ - if (pos < router->current_pos) { - char err_message[BINLOG_ERROR_MSG_LEN+1]; + /* Check whether binlog records has been read in previous loop */ + if (pos < router->current_pos) + { + char err_message[BINLOG_ERROR_MSG_LEN + 1]; - err_message[BINLOG_ERROR_MSG_LEN] = '\0'; + err_message[BINLOG_ERROR_MSG_LEN] = '\0'; - /* No event has been sent */ - if (pos == router->binlog_position) { - MXS_ERROR("No events distributed to slaves for a pending " + /* No event has been sent */ + if (pos == router->binlog_position) + { + MXS_ERROR("No events distributed to slaves for a pending " "transaction in %s at %lu. " "Last event from master at %lu", router->binlog_name, router->binlog_position, router->current_pos); - strncpy(err_message, "No transaction events sent", BINLOG_ERROR_MSG_LEN); - } else { - /* Some events have been sent */ - MXS_ERROR("Some events were not distributed to slaves for a " + strncpy(err_message, + "No transaction events sent", BINLOG_ERROR_MSG_LEN); + } + else + { + /* Some events have been sent */ + MXS_ERROR("Some events were not distributed to slaves for a " "pending transaction in %s at %lu. Last distributed " "even at %llu, last event from master at %lu", router->binlog_name, @@ -1472,29 +1601,32 @@ int n_bufs = -1, pn_bufs = -1; pos, router->current_pos); - strncpy(err_message, "Incomplete transaction events sent", BINLOG_ERROR_MSG_LEN); - } + strncpy(err_message, + "Incomplete transaction events sent", BINLOG_ERROR_MSG_LEN); + } - /* distribute error message to registered slaves */ - blr_distribute_error_message(router, err_message, "HY000", 1236); - } + /* distribute error message to registered slaves */ + blr_distribute_error_message(router, err_message, "HY000", 1236); + } - /* update binlog_position and set pending to 0 */ - spinlock_acquire(&router->binlog_lock); + /* update binlog_position and set pending to 0 */ + spinlock_acquire(&router->binlog_lock); - router->binlog_position = router->current_pos; - router->pending_transaction = 0; + router->binlog_position = router->current_pos; + router->pending_transaction = 0; - spinlock_release(&router->binlog_lock); - } else { - spinlock_release(&router->binlog_lock); - } - } - } - else - { - router->stats.n_artificial++; - MXS_DEBUG("Artificial event not written " + spinlock_release(&router->binlog_lock); + } + else + { + spinlock_release(&router->binlog_lock); + } + } + } + else + { + router->stats.n_artificial++; + MXS_DEBUG("Artificial event not written " "to disk or distributed. " "Type 0x%x, Length %d, Binlog " "%s @ %lu.", @@ -1502,204 +1634,185 @@ int n_bufs = -1, pn_bufs = -1; hdr.event_size, router->binlog_name, router->current_pos); - ptr += 5; - if (hdr.event_type == ROTATE_EVENT) - { - spinlock_acquire(&router->binlog_lock); - router->rotating = 1; - spinlock_release(&router->binlog_lock); - if (!blr_rotate_event(router, ptr, &hdr)) - { - /* - * Failed to write to the - * binlog file, destroy the - * buffer chain and close the - * connection with the master - */ - while ((pkt = gwbuf_consume(pkt, - GWBUF_LENGTH(pkt))) != NULL); - blr_master_close(router); - blr_master_delayed_connect(router); - return; - } - } - } - } + ptr += 5; + if (hdr.event_type == ROTATE_EVENT) + { + spinlock_acquire(&router->binlog_lock); + router->rotating = 1; + spinlock_release(&router->binlog_lock); + if (!blr_rotate_event(router, ptr, &hdr)) + { + /* + * Failed to write to the + * binlog file, destroy the + * buffer chain and close the + * connection with the master + */ + while ((pkt = gwbuf_consume(pkt, GWBUF_LENGTH(pkt))) != NULL) + { + ; + } + blr_master_close(router); + blr_master_delayed_connect(router); + return; + } + } + } + } - /** A large event is now fully received and processed */ - if(router->master_event_state == BLR_EVENT_COMPLETE) - { - router->master_event_state = BLR_EVENT_DONE; - } - } - else - { - unsigned long mysql_errno = extract_field(ptr+5, 16); - char *msg_err = NULL; - int msg_len=0; - msg_err = (char *)ptr+7+6; // err msg starts after 7 bytes + 6 of status message - msg_len = len-7-6; // msg len is decreased by 7 and 6 - msg_err = (char *)malloc(msg_len + 1); - strncpy(msg_err, (char *)ptr+7+6, msg_len); - /* NULL terminate error string */ - *(msg_err+msg_len)='\0'; + /** A large event is now fully received and processed */ + if (router->master_event_state == BLR_EVENT_COMPLETE) + { + router->master_event_state = BLR_EVENT_DONE; + } + } + else + { + blr_terminate_master_replication(router, ptr, len); + } + } - spinlock_acquire(&router->lock); + if (msg) + { + free(msg); + msg = NULL; + } + prev_length = len; + while (len > 0) + { + int n, plen; + plen = GWBUF_LENGTH(pkt); + n = (plen < len ? plen : len); + pkt = gwbuf_consume(pkt, n); + len -= n; + pkt_length -= n; + } + preslen = reslen; + pn_bufs = n_bufs; + } - /* set mysql_errno */ - router->m_errno = mysql_errno; - - /* set io error message */ - if (router->m_errmsg) - free(router->m_errmsg); - router->m_errmsg = msg_err; - - /* Force stopped state */ - router->master_state = BLRM_SLAVE_STOPPED; - - spinlock_release(&router->lock); - - MXS_ERROR("Error packet in binlog stream.%s @ %lu.", - router->binlog_name, - router->current_pos); - - router->stats.n_binlog_errors++; - } - } - - if (msg) - { - free(msg); - msg = NULL; - } - prev_length = len; - while (len > 0) - { - int n, plen; - plen = GWBUF_LENGTH(pkt); - n = (plen < len ? plen : len); - pkt = gwbuf_consume(pkt, n); - len -= n; - pkt_length -= n; - } - preslen = reslen; - pn_bufs = n_bufs; - } - - /* - * Check if we have a residual, part binlog message to deal with. - * Just simply store the GWBUF for next time - */ - if (pkt) - { - router->residual = pkt; - ss_dassert(pkt_length != 0); - } - else - { - ss_dassert(pkt_length == 0); - } - blr_file_flush(router); + /* + * Check if we have a residual, part binlog message to deal with. + * Just simply store the GWBUF for next time + */ + if (pkt) + { + router->residual = pkt; + ss_dassert(pkt_length != 0); + } + else + { + ss_dassert(pkt_length == 0); + } + blr_file_flush(router); } /** * Populate a header structure for a replication message from a GWBUF structure. * - * @param pkt The incoming packet in a GWBUF chain - * @param hdr The packet header to populate + * @param pkt The incoming packet in a GWBUF chain + * @param hdr The packet header to populate */ void blr_extract_header(register uint8_t *ptr, register REP_HEADER *hdr) { - hdr->payload_len = EXTRACT24(ptr); - hdr->seqno = ptr[3]; - hdr->ok = ptr[4]; - hdr->timestamp = EXTRACT32(&ptr[5]); - hdr->event_type = ptr[9]; - hdr->serverid = EXTRACT32(&ptr[10]); - hdr->event_size = EXTRACT32(&ptr[14]); - hdr->next_pos = EXTRACT32(&ptr[18]); - hdr->flags = EXTRACT16(&ptr[22]); + hdr->payload_len = EXTRACT24(ptr); + hdr->seqno = ptr[3]; + hdr->ok = ptr[4]; + hdr->timestamp = EXTRACT32(&ptr[5]); + hdr->event_type = ptr[9]; + hdr->serverid = EXTRACT32(&ptr[10]); + hdr->event_size = EXTRACT32(&ptr[14]); + hdr->next_pos = EXTRACT32(&ptr[18]); + hdr->flags = EXTRACT16(&ptr[22]); } /** * Process a binlog rotate event. * - * @param router The instance of the router - * @param ptr The packet containing the rotate event - * @param hdr The replication message header - * @return 1 if the file could be rotated, 0 otherwise. + * @param router The instance of the router + * @param ptr The packet containing the rotate event + * @param hdr The replication message header + * @return 1 if the file could be rotated, 0 otherwise. */ static int blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *ptr, REP_HEADER *hdr) { -int len, slen; -uint64_t pos; -char file[BINLOG_FNAMELEN+1]; + int len, slen; + uint64_t pos; + char file[BINLOG_FNAMELEN + 1]; - ptr += 19; // Skip event header - len = hdr->event_size - 19; // Event size minus header - pos = extract_field(ptr+4, 32); - pos <<= 32; - pos |= extract_field(ptr, 32); - slen = len - (8 + 4); // Allow for position and CRC - if (router->master_chksum == 0) - slen += 4; - if (slen > BINLOG_FNAMELEN) - slen = BINLOG_FNAMELEN; - memcpy(file, ptr + 8, slen); - file[slen] = 0; + ptr += 19; // Skip event header + len = hdr->event_size - 19; // Event size minus header + pos = extract_field(ptr + 4, 32); + pos <<= 32; + pos |= extract_field(ptr, 32); + slen = len - (8 + 4); // Allow for position and CRC + if (router->master_chksum == 0) + { + slen += 4; + } + if (slen > BINLOG_FNAMELEN) + { + slen = BINLOG_FNAMELEN; + } + memcpy(file, ptr + 8, slen); + file[slen] = 0; #ifdef VERBOSE_ROTATE - printf("binlog rotate: "); - while (len--) - printf("0x%02x ", *ptr++); - printf("\n"); - printf("New file: %s @ %ld\n", file, pos); + printf("binlog rotate: "); + while (len--) + { + printf("0x%02x ", *ptr++); + } + printf("\n"); + printf("New file: %s @ %ld\n", file, pos); #endif - strcpy(router->prevbinlog, router->binlog_name); + strcpy(router->prevbinlog, router->binlog_name); - int rotated = 1; + int rotated = 1; - if (strncmp(router->binlog_name, file, slen) != 0) - { - router->stats.n_rotates++; - if (blr_file_rotate(router, file, pos) == 0) - { - rotated = 0; - } - } - spinlock_acquire(&router->binlog_lock); - router->rotating = 0; - spinlock_release(&router->binlog_lock); - return rotated; + if (strncmp(router->binlog_name, file, slen) != 0) + { + router->stats.n_rotates++; + if (blr_file_rotate(router, file, pos) == 0) + { + rotated = 0; + } + } + spinlock_acquire(&router->binlog_lock); + router->rotating = 0; + spinlock_release(&router->binlog_lock); + return rotated; } /** * Create the auth data needed to be able to call dcb_connect. - * + * * This doesn't really belong here and should be moved at some stage. */ static void * CreateMySQLAuthData(char *username, char *password, char *database) { -MYSQL_session *auth_info; + MYSQL_session *auth_info; - if (username == NULL || password == NULL) - { - MXS_ERROR("You must specify both username and password for the binlog router.\n"); - return NULL; - } + if (username == NULL || password == NULL) + { + MXS_ERROR("You must specify both username and password for the binlog router.\n"); + return NULL; + } - if ((auth_info = calloc(1, sizeof(MYSQL_session))) == NULL) - return NULL; - strncpy(auth_info->user, username,MYSQL_USER_MAXLEN); - strncpy(auth_info->db, database,MYSQL_DATABASE_MAXLEN); - gw_sha1_str((const uint8_t *)password, strlen(password), auth_info->client_sha1); + if ((auth_info = calloc(1, sizeof(MYSQL_session))) == NULL) + { + return NULL; + } + strncpy(auth_info->user, username, MYSQL_USER_MAXLEN); + strncpy(auth_info->db, database, MYSQL_DATABASE_MAXLEN); + gw_sha1_str((const uint8_t *)password, strlen(password), auth_info->client_sha1); - return auth_info; + return auth_info; } /** Actions that can be taken when an event is being distributed to the slaves*/ @@ -1713,536 +1826,612 @@ typedef enum /** * Distribute the binlog record we have just received to all the registered slaves. * - * @param router The router instance - * @param hdr The replication event header - * @param ptr The raw replication event data + * @param router The router instance + * @param hdr The replication event header + * @param ptr The raw replication event data */ void -blr_distribute_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr) +blr_distribute_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr, + blr_thread_role_t role) { -GWBUF *pkt; -uint8_t *buf; -ROUTER_SLAVE *slave, *nextslave; -int action; -unsigned int cstate; + GWBUF *pkt; + uint8_t *buf; + ROUTER_SLAVE *slave, *nextslave; + int action; + unsigned int cstate; - spinlock_acquire(&router->lock); - slave = router->slaves; - while (slave) - { - if (slave->state != BLRS_DUMPING) - { - slave = slave->next; - continue; - } - spinlock_acquire(&slave->catch_lock); - if ((slave->cstate & (CS_UPTODATE|CS_BUSY)) == CS_UPTODATE) - { - /* - * This slave is reporting it is to date with the binlog of the - * master running on this slave. - * It has no thread running currently that is sending binlog - * events. - */ - action = 1; - slave->cstate |= CS_BUSY; - } - else if ((slave->cstate & (CS_UPTODATE|CS_BUSY)) == (CS_UPTODATE|CS_BUSY)) - { - /* - * The slave is up to date with the binlog and a process is - * running on this slave to send binlog events. - */ - slave->overrun = 1; - action = 2; - } - else if ((slave->cstate & CS_UPTODATE) == 0) - { - /* Slave is in catchup mode */ - action = 3; - } + spinlock_acquire(&router->lock); + slave = router->slaves; + while (slave) + { + if (slave->state != BLRS_DUMPING) + { + slave = slave->next; + continue; + } + spinlock_acquire(&slave->catch_lock); + if ((slave->cstate & (CS_UPTODATE | CS_BUSY)) == CS_UPTODATE) + { + /* + * This slave is reporting it is to date with the binlog of the + * master running on this slave. + * It has no thread running currently that is sending binlog + * events. + */ + action = 1; + slave->cstate |= CS_BUSY; + } + else if ((slave->cstate & (CS_UPTODATE | CS_BUSY)) == (CS_UPTODATE | CS_BUSY)) + { + /* + * The slave is up to date with the binlog and a process is + * running on this slave to send binlog events. + */ + slave->overrun = 1; + action = 2; + } + else if ((slave->cstate & CS_UPTODATE) == 0) + { + /* Slave is in catchup mode */ + action = 3; + } else { MXS_ERROR("slave->cstate does not contain a meaningful state %d", slave->cstate); action = 0; } - slave->stats.n_actions[action-1]++; - spinlock_release(&slave->catch_lock); + slave->stats.n_actions[action - 1]++; + spinlock_release(&slave->catch_lock); - if (action == 1) - { - spinlock_acquire(&router->binlog_lock); + if (action == 1) + { + spinlock_acquire(&router->binlog_lock); - slave_event_action_t slave_action = SLAVE_FORCE_CATCHUP; + slave_event_action_t slave_action = SLAVE_FORCE_CATCHUP; + const bool same_file = strcmp(slave->binlogfile, router->binlog_name) == 0; + const bool rotate = hdr->event_type == ROTATE_EVENT && + strcmp(slave->binlogfile, router->prevbinlog) == 0; - if(router->trx_safe && slave->binlog_pos == router->current_safe_event && - (strcmp(slave->binlogfile, router->binlog_name) == 0 || - (hdr->event_type == ROTATE_EVENT && - strcmp(slave->binlogfile, router->prevbinlog)))) - { - /** - * Slave needs the current event being distributed - */ - slave_action = SLAVE_SEND_EVENT; - } - else if (slave->binlog_pos == router->last_event_pos && - (strcmp(slave->binlogfile, router->binlog_name) == 0 || - (hdr->event_type == ROTATE_EVENT && - strcmp(slave->binlogfile, router->prevbinlog)))) - { - /** - * Transaction safety is off or there are no pending transactions - */ + if (router->trx_safe && (same_file || rotate) && + slave->binlog_pos == router->current_safe_event) + { + /** Slave needs the current event being distributed */ + slave_action = SLAVE_SEND_EVENT; + } + else if (!router->trx_safe && (same_file || rotate) && + slave->binlog_pos == router->last_event_pos) + { + /** Transaction safety is off */ + slave_action = SLAVE_SEND_EVENT; + } + else if (same_file) + { + if (slave->binlog_pos == hdr->next_pos) + { + /* + * Slave has already read record from file, no + * need to distrbute this event + */ + slave_action = SLAVE_EVENT_ALREADY_SENT; + } + else if ((slave->binlog_pos > hdr->next_pos - hdr->event_size)) + { + /* + * The slave is ahead of the master, this should never + * happen. Force the slave to catchup mode in order to + * try to resolve the issue. + */ + MXS_ERROR("Slave %s:%d server ID %d is ahead of expected position %s@%u. " + "Expected position %d", slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), slave->serverid, + slave->binlogfile, slave->binlog_pos, + hdr->next_pos - hdr->event_size); + } + } + else if (rotate) + { + /** Slave is more than one binlog file behind */ + MXS_ERROR("Slave %s:%d server ID %d is behind more than one binlog file " + "from the master. Slave is using '%s' with position %d " + "when master binlog file is '%s'.", slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), slave->serverid, + slave->binlogfile, slave->binlog_pos, router->binlog_name); + } + else + { + /** Slave is lagging behind */ + MXS_ERROR("Slave %s:%d server ID %d is using binlog file '%s' with " + "position %d. Master binlog file is '%s' at position %lu " + "with last safe event at %lu.", slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), slave->serverid, + slave->binlogfile, slave->binlog_pos, router->binlog_name, + router->current_pos, router->current_safe_event); + } - slave_action = SLAVE_SEND_EVENT; - } - else if (slave->binlog_pos == hdr->next_pos - && strcmp(slave->binlogfile, router->binlog_name) == 0) - { - /* - * Slave has already read record from file, no - * need to distrbute this event - */ - slave_action = SLAVE_EVENT_ALREADY_SENT; - } - else if ((slave->binlog_pos > hdr->next_pos - hdr->event_size) - && strcmp(slave->binlogfile, router->binlog_name) == 0) - { - /* - * The slave is ahead of the master, this should never - * happen. Force the slave to catchup mode in order to - * try to resolve the issue. - */ - MXS_ERROR("Slave %d is ahead of expected position %s@%lu. " - "Expected position %d", - slave->serverid, slave->binlogfile, - (unsigned long)slave->binlog_pos, - hdr->next_pos - hdr->event_size); - } + spinlock_release(&router->binlog_lock); - spinlock_release(&router->binlog_lock); + /* + * If slave_action is SLAVE_FORCE_CATCHUP then + * the slave is not at the position it should be. Force it into + * catchup mode rather than send this event. + */ - /* - * If slave_action is SLAVE_FORCE_CATCHUP then - * the slave is not at the position it should be. Force it into - * catchup mode rather than send this event. - */ + switch (slave_action) + { + char binlog_name[BINLOG_FNAMELEN + 1]; + uint32_t binlog_pos; - switch(slave_action) - { - case SLAVE_SEND_EVENT: - /* - * The slave should be up to date, check that the binlog - * position matches the event we have to distribute or - * this is a rotate event. Send the event directly from - * memory to the slave. - */ - slave->lastEventTimestamp = hdr->timestamp; - slave->lastEventReceived = hdr->event_type; + case SLAVE_SEND_EVENT: + /* + * The slave should be up to date, check that the binlog + * position matches the event we have to distribute or + * this is a rotate event. Send the event directly from + * memory to the slave. + */ + slave->lastEventTimestamp = hdr->timestamp; + slave->lastEventReceived = hdr->event_type; - /* set lastReply */ - if (router->send_slave_heartbeat) - slave->lastReply = time(0); + /* set lastReply */ + if (router->send_slave_heartbeat) + { + slave->lastReply = time(0); + } - if (hdr->event_type == ROTATE_EVENT) - { - blr_slave_rotate(router, slave, ptr); - } + strcpy(binlog_name, slave->binlogfile); + binlog_pos = slave->binlog_pos; - blr_send_event(slave, hdr, ptr); + if (hdr->event_type == ROTATE_EVENT) + { + blr_slave_rotate(router, slave, ptr); + } - spinlock_acquire(&slave->catch_lock); - if (hdr->event_type != ROTATE_EVENT) - { - slave->binlog_pos = hdr->next_pos; - } - if (slave->overrun) - { - slave->stats.n_overrun++; - slave->overrun = 0; - poll_fake_write_event(slave->dcb); - } - else - { - slave->cstate &= ~CS_BUSY; - } - spinlock_release(&slave->catch_lock); - break; + if (blr_send_event(role, binlog_name, binlog_pos, slave, hdr, ptr)) + { + spinlock_acquire(&slave->catch_lock); + if (hdr->event_type != ROTATE_EVENT) + { + slave->binlog_pos = hdr->next_pos; + } + if (slave->overrun) + { + slave->stats.n_overrun++; + slave->overrun = 0; + poll_fake_write_event(slave->dcb); + } + else + { + slave->cstate &= ~CS_BUSY; + } + spinlock_release(&slave->catch_lock); + } + else + { + MXS_WARNING("Slave %s:%i, server-id %d, binlog '%s, position %u: " + "Master-thread could not send event to slave, closing connection.", + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + binlog_name, + binlog_pos); + slave->state = BLRS_ERRORED; + dcb_close(slave->dcb); + } + break; - case SLAVE_EVENT_ALREADY_SENT: - spinlock_acquire(&slave->catch_lock); - slave->cstate &= ~CS_BUSY; - spinlock_release(&slave->catch_lock); - break; + case SLAVE_EVENT_ALREADY_SENT: + spinlock_acquire(&slave->catch_lock); + slave->cstate &= ~CS_BUSY; + spinlock_release(&slave->catch_lock); + break; - case SLAVE_FORCE_CATCHUP: - spinlock_acquire(&slave->catch_lock); - cstate = slave->cstate; - slave->cstate &= ~(CS_UPTODATE|CS_BUSY); - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - if ((cstate & CS_UPTODATE) == CS_UPTODATE) - { + case SLAVE_FORCE_CATCHUP: + spinlock_acquire(&slave->catch_lock); + cstate = slave->cstate; + slave->cstate &= ~(CS_UPTODATE | CS_BUSY); + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + if ((cstate & CS_UPTODATE) == CS_UPTODATE) + { #ifdef STATE_CHANGE_LOGGING_ENABLED - MXS_NOTICE("%s: Slave %s:%d, server-id %d transition from up-to-date to catch-up in blr_distribute_binlog_record, binlog file '%s', position %lu.", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, (unsigned long)slave->binlog_pos); + MXS_NOTICE("%s: Slave %s:%d, server-id %d transition from " + "up-to-date to catch-up in blr_distribute_binlog_record, " + "binlog file '%s', position %lu.", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, (unsigned long)slave->binlog_pos); #endif - } - poll_fake_write_event(slave->dcb); - break; - } - } - else if (action == 3) - { - /* Slave is not up to date - * Check if it is either expecting a callback or - * is busy processing a callback - */ - spinlock_acquire(&slave->catch_lock); - if ((slave->cstate & (CS_EXPECTCB|CS_BUSY)) == 0) - { - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); - } - else - spinlock_release(&slave->catch_lock); - } + } + poll_fake_write_event(slave->dcb); + break; + } + } + else if (action == 3) + { + /* Slave is not up to date + * Check if it is either expecting a callback or + * is busy processing a callback + */ + spinlock_acquire(&slave->catch_lock); + if ((slave->cstate & (CS_EXPECTCB | CS_BUSY)) == 0) + { + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); + } + else + { + spinlock_release(&slave->catch_lock); + } + } - slave = slave->next; - } - spinlock_release(&router->lock); + slave = slave->next; + } + spinlock_release(&router->lock); } /** * Write a raw event (the first 40 bytes at most) to a log file * * @param priority The syslog priority of the message (LOG_ERR, LOG_WARNING, etc.) - * @param msg A textual message to write before the packet - * @param ptr Pointer to the message buffer - * @param len Length of message packet + * @param msg A textual message to write before the packet + * @param ptr Pointer to the message buffer + * @param len Length of message packet */ static void blr_log_packet(int priority, char *msg, uint8_t *ptr, int len) { -char buf[400] = ""; -char *bufp; -int i; + char buf[400] = ""; + char *bufp; + int i; - bufp = buf; - bufp += sprintf(bufp, "%s length = %d: ", msg, len); - for (i = 0; i < len && i < 40; i++) - bufp += sprintf(bufp, "0x%02x ", ptr[i]); - if (i < len) - MXS_LOG_MESSAGE(priority, "%s...", buf); - else - MXS_LOG_MESSAGE(priority, "%s", buf); + bufp = buf; + bufp += sprintf(bufp, "%s length = %d: ", msg, len); + for (i = 0; i < len && i < 40; i++) + { + bufp += sprintf(bufp, "0x%02x ", ptr[i]); + } + if (i < len) + { + MXS_LOG_MESSAGE(priority, "%s...", buf); + } + else + { + MXS_LOG_MESSAGE(priority, "%s", buf); + } } /** * Check if the master connection is in place and we * are downlaoding binlogs * - * @param router The router instance + * @param router The router instance * @return non-zero if we are recivign binlog records */ int blr_master_connected(ROUTER_INSTANCE *router) { - return router->master_state == BLRM_BINLOGDUMP; + return router->master_state == BLRM_BINLOGDUMP; } /** - * Extract a result value from the set of messages that make up a + * Extract a result value from the set of messages that make up a * MySQL response packet. * - * @param buf The GWBUF containing the response - * @param col The column number to return - * @return The result form the column or NULL. The caller must free the result + * @param buf The GWBUF containing the response + * @param col The column number to return + * @return The result form the column or NULL. The caller must free the result */ char * blr_extract_column(GWBUF *buf, int col) { -uint8_t *ptr; -int len, ncol, collen; -char *rval; + uint8_t *ptr; + int len, ncol, collen; + char *rval; - if (buf == NULL) - return NULL; + if (buf == NULL) + { + return NULL; + } - ptr = (uint8_t *)GWBUF_DATA(buf); - /* First packet should be the column count */ - len = EXTRACT24(ptr); - ptr += 3; - if (*ptr != 1) // Check sequence number is 1 - return NULL; - ptr++; - ncol = *ptr++; - if (ncol < col) // Not that many column in result - return NULL; - // Now ptr points at the column definition - while (ncol-- > 0) - { - len = EXTRACT24(ptr); - ptr += 4; // Skip to payload - ptr += len; // Skip over payload - } - // Now we should have an EOF packet - len = EXTRACT24(ptr); - ptr += 4; // Skip to payload - if (*ptr != 0xfe) - return NULL; - ptr += len; + ptr = (uint8_t *)GWBUF_DATA(buf); + /* First packet should be the column count */ + len = EXTRACT24(ptr); + ptr += 3; + if (*ptr != 1) // Check sequence number is 1 + { + return NULL; + } + ptr++; + ncol = *ptr++; + if (ncol < col) // Not that many column in result + { + return NULL; + } + // Now ptr points at the column definition + while (ncol-- > 0) + { + len = EXTRACT24(ptr); + ptr += 4; // Skip to payload + ptr += len; // Skip over payload + } + // Now we should have an EOF packet + len = EXTRACT24(ptr); + ptr += 4; // Skip to payload + if (*ptr != 0xfe) + { + return NULL; + } + ptr += len; - // Finally we have reached the row - len = EXTRACT24(ptr); - ptr += 4; + // Finally we have reached the row + len = EXTRACT24(ptr); + ptr += 4; /** The first EOF packet signals the start of the resultset rows and the second EOF packet signals the end of the result set. If the resultset contains a second EOF packet right after the first one, the result set is empty and contains no rows. */ - if(len == 5 && *ptr == 0xfe) + if (len == 5 && *ptr == 0xfe) + { return NULL; + } - while (--col > 0) - { - collen = *ptr++; - ptr += collen; - } - collen = *ptr++; - if ((rval = malloc(collen + 1)) == NULL) - return NULL; - memcpy(rval, ptr, collen); - rval[collen] = 0; // NULL terminate + while (--col > 0) + { + collen = *ptr++; + ptr += collen; + } + collen = *ptr++; + if ((rval = malloc(collen + 1)) == NULL) + { + return NULL; + } + memcpy(rval, ptr, collen); + rval[collen] = 0; // NULL terminate - return rval; + return rval; } /** * Read a replication event form current opened binlog into a GWBUF structure. * - * @param router The router instance - * @param pos Position of binlog record to read - * @param hdr Binlog header to populate - * @return The binlog record wrapped in a GWBUF structure + * @param router The router instance + * @param pos Position of binlog record to read + * @param hdr Binlog header to populate + * @return The binlog record wrapped in a GWBUF structure */ GWBUF -*blr_read_events_from_pos(ROUTER_INSTANCE *router, unsigned long long pos, REP_HEADER *hdr, unsigned long long pos_end) { -unsigned long long end_pos = 0; -struct stat statb; -uint8_t hdbuf[19]; -uint8_t *data; -GWBUF *result; -int n; -int event_limit; +*blr_read_events_from_pos(ROUTER_INSTANCE *router, + unsigned long long pos, + REP_HEADER *hdr, + unsigned long long pos_end) +{ + unsigned long long end_pos = 0; + struct stat statb; + uint8_t hdbuf[19]; + uint8_t *data; + GWBUF *result; + int n; + int event_limit; - /* Get current binnlog position */ - end_pos = pos_end; + /* Get current binnlog position */ + end_pos = pos_end; - /* end of file reached, we're done */ - if (pos == end_pos) { - return NULL; - } + /* end of file reached, we're done */ + if (pos == end_pos) + { + return NULL; + } - /* error */ - if (pos > end_pos) - { + /* error */ + if (pos > end_pos) + { MXS_ERROR("Reading saved events, the specified pos %llu " "is ahead of current pos %lu for file %s", pos, router->current_pos, router->binlog_name); - return NULL; - } + return NULL; + } - /* Read the event header information from the file */ - if ((n = pread(router->binlog_fd, hdbuf, 19, pos)) != 19) - { - switch (n) - { - case 0: - MXS_DEBUG("Reading saved events: reached end of binlog file at %llu.", pos); - break; - case -1: - { - char err_msg[STRERROR_BUFLEN]; - MXS_ERROR("Reading saved events: failed to read binlog " - "file %s at position %llu" - " (%s).", router->binlog_name, - pos, strerror_r(errno, err_msg, sizeof(err_msg))); + /* Read the event header information from the file */ + if ((n = pread(router->binlog_fd, hdbuf, 19, pos)) != 19) + { + switch (n) + { + case 0: + MXS_DEBUG("Reading saved events: reached end of binlog file at %llu.", pos); + break; + case -1: + { + char err_msg[STRERROR_BUFLEN]; + MXS_ERROR("Reading saved events: failed to read binlog " + "file %s at position %llu" + " (%s).", router->binlog_name, + pos, strerror_r(errno, err_msg, sizeof(err_msg))); - if (errno == EBADF) - MXS_ERROR("Reading saved events: bad file descriptor for file %s" - ", descriptor %d.", - router->binlog_name, router->binlog_fd); - break; - } - default: - MXS_ERROR("Reading saved events: short read when reading the header. " + if (errno == EBADF) + { + MXS_ERROR("Reading saved events: bad file descriptor for file %s" + ", descriptor %d.", + router->binlog_name, router->binlog_fd); + } + } + break; + default: + MXS_ERROR("Reading saved events: 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; - } + break; + } - return NULL; - } + return NULL; + } - 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]); + 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]); - event_limit = router->mariadb10_compat ? MAX_EVENT_TYPE_MARIADB10 : MAX_EVENT_TYPE; + event_limit = router->mariadb10_compat ? MAX_EVENT_TYPE_MARIADB10 : MAX_EVENT_TYPE; - if (hdr->event_type > event_limit) - { - MXS_ERROR("Reading saved events: invalid event type 0x%x. " + if (hdr->event_type > event_limit) + { + MXS_ERROR("Reading saved events: invalid event type 0x%x. " "Binlog file is %s, position %llu", hdr->event_type, router->binlog_name, pos); - return NULL; - } + return NULL; + } - if ((result = gwbuf_alloc(hdr->event_size)) == NULL) - { - MXS_ERROR("Reading saved events: failed to allocate memory for binlog entry, " + if ((result = gwbuf_alloc(hdr->event_size)) == NULL) + { + MXS_ERROR("Reading saved events: failed to allocate memory for binlog entry, " "size %d at %llu.", hdr->event_size, pos); - return NULL; - } + return NULL; + } - /* Copy event header*/ - data = GWBUF_DATA(result); - memcpy(data, hdbuf, 19); + /* Copy event header*/ + data = GWBUF_DATA(result); + memcpy(data, hdbuf, 19); - /* Read event data and put int into buffer after header */ - if ((n = pread(router->binlog_fd, &data[19], hdr->event_size - 19, pos + 19)) != hdr->event_size - 19) - { - if (n == -1) - { - char err_msg[STRERROR_BUFLEN]; - MXS_ERROR("Reading saved events: the event at %llu in %s. " + /* Read event data and put int into buffer after header */ + if ((n = pread(router->binlog_fd, &data[19], hdr->event_size - 19, pos + 19)) != hdr->event_size - 19) + { + if (n == -1) + { + char err_msg[STRERROR_BUFLEN]; + MXS_ERROR("Reading saved events: the event at %llu in %s. " "%s, expected %d bytes.", pos, router->binlog_name, strerror_r(errno, err_msg, sizeof(err_msg)), hdr->event_size - 19); - } else { - MXS_ERROR("Reading saved events: short read when reading " + } + else + { + MXS_ERROR("Reading saved events: 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 (end_pos - pos < hdr->event_size) - { - MXS_ERROR("Reading saved events: binlog event " - "is close to the end of the binlog file, " - "current file size is %llu.", end_pos); - } - } + if (end_pos - pos < hdr->event_size) + { + MXS_ERROR("Reading saved events: binlog event " + "is close to the end of the binlog file, " + "current file size is %llu.", end_pos); + } + } - /* free buffer */ - gwbuf_free(result); + /* free buffer */ + gwbuf_free(result); - return NULL; - } + return NULL; + } - return result; + return result; } /** * Stop and start the master connection * - * @param router The router instance + * @param router The router instance */ void -blr_stop_start_master(ROUTER_INSTANCE *router) { - - if (router->master) { - if (router->master->fd != -1 && router->master->state == DCB_STATE_POLLING) { - blr_master_close(router); - } - } - - spinlock_acquire(&router->lock); - - router->master_state = BLRM_SLAVE_STOPPED; - - /* set last_safe_pos */ - router->last_safe_pos = router->binlog_position; - - /** - * Set router->prevbinlog to router->binlog_name - * The FDE event with current filename may arrive after STOP SLAVE is received - */ - - if (strcmp(router->binlog_name, router->prevbinlog) != 0) - strncpy(router->prevbinlog, router->binlog_name, BINLOG_FNAMELEN); - - if (router->client) { - if (router->client->fd != -1 && router->client->state == DCB_STATE_POLLING) { - dcb_close(router->client); - router->client = NULL; - } - } - - /* Discard the queued residual data */ - while (router->residual) +blr_stop_start_master(ROUTER_INSTANCE *router) +{ + if (router->master) + { + if (router->master->fd != -1 && router->master->state == DCB_STATE_POLLING) { - router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); + blr_master_close(router); } - router->residual = NULL; + } - router->master_state = BLRM_UNCONNECTED; - spinlock_release(&router->lock); + spinlock_acquire(&router->lock); - blr_master_reconnect(router); + router->master_state = BLRM_SLAVE_STOPPED; + + /* set last_safe_pos */ + router->last_safe_pos = router->binlog_position; + + /** + * Set router->prevbinlog to router->binlog_name + * The FDE event with current filename may arrive after STOP SLAVE is received + */ + + if (strcmp(router->binlog_name, router->prevbinlog) != 0) + { + strncpy(router->prevbinlog, router->binlog_name, BINLOG_FNAMELEN); + } + + if (router->client) + { + if (router->client->fd != -1 && router->client->state == DCB_STATE_POLLING) + { + dcb_close(router->client); + router->client = NULL; + } + } + + /* Discard the queued residual data */ + while (router->residual) + { + router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); + } + router->residual = NULL; + + router->master_state = BLRM_UNCONNECTED; + spinlock_release(&router->lock); + + blr_master_reconnect(router); } /** * The heartbeat check function called from the housekeeper. * We can try a new master connection if current one is seen out of date * - * @param router Current router instance + * @param router Current router instance */ static void -blr_check_last_master_event(void *inst) { -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)inst; -int master_check = 1; -int master_state = BLRM_UNCONNECTED; -char task_name[BLRM_TASK_NAME_LEN + 1] = ""; +blr_check_last_master_event(void *inst) +{ + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)inst; + int master_check = 1; + int master_state = BLRM_UNCONNECTED; + char task_name[BLRM_TASK_NAME_LEN + 1] = ""; - spinlock_acquire(&router->lock); + spinlock_acquire(&router->lock); - master_check = blr_check_heartbeat(router); + master_check = blr_check_heartbeat(router); - master_state = router->master_state; + master_state = router->master_state; - spinlock_release(&router->lock); + spinlock_release(&router->lock); - if (!master_check) { - /* - * stop current master connection - * and try a new connection - */ - blr_stop_start_master(router); - } + if (!master_check) + { + /* + * stop current master connection + * and try a new connection + */ + blr_stop_start_master(router); + } - if ( (!master_check) || (master_state != BLRM_BINLOGDUMP) ) { - /* - * Remove the task, it will be added again - * when master state is back to BLRM_BINLOGDUMP - * by blr_master_response() - */ - snprintf(task_name, BLRM_TASK_NAME_LEN, "%s heartbeat", router->service->name); + if ( (!master_check) || (master_state != BLRM_BINLOGDUMP) ) + { + /* + * Remove the task, it will be added again + * when master state is back to BLRM_BINLOGDUMP + * by blr_master_response() + */ + snprintf(task_name, BLRM_TASK_NAME_LEN, "%s heartbeat", router->service->name); - hktask_remove(task_name); - } + hktask_remove(task_name); + } } /** @@ -2251,154 +2440,171 @@ char task_name[BLRM_TASK_NAME_LEN + 1] = ""; * checked interval is againts (router->heartbeat + BLR_NET_LATENCY_WAIT_TIME) * that is currently set to 1 * - * @param router Current router instance - * @return 0 if master connection must be closed and opened again, 1 otherwise + * @param router Current router instance + * @return 0 if master connection must be closed and opened again, 1 otherwise */ int -blr_check_heartbeat(ROUTER_INSTANCE *router) { -time_t t_now = time(0); -char *event_desc = NULL; +blr_check_heartbeat(ROUTER_INSTANCE *router) +{ + time_t t_now = time(0); + char *event_desc = NULL; - if (router->master_state != BLRM_BINLOGDUMP) { - return 1; - } + if (router->master_state != BLRM_BINLOGDUMP) + { + return 1; + } - event_desc = blr_last_event_description(router); + event_desc = blr_last_event_description(router); - if (router->master_state == BLRM_BINLOGDUMP && router->lastEventReceived > 0) { - if ((t_now - router->stats.lastReply) > (router->heartbeat + BLR_NET_LATENCY_WAIT_TIME)) { - MXS_ERROR("No event received from master %s:%d in heartbeat period (%lu seconds), " - "last event (%s %d) received %lu seconds ago. Assuming connection is dead " - "and reconnecting.", - router->service->dbref->server->name, - router->service->dbref->server->port, - router->heartbeat, - event_desc != NULL ? event_desc : "unknown", - router->lastEventReceived, - t_now - router->stats.lastReply); + if (router->master_state == BLRM_BINLOGDUMP && router->lastEventReceived > 0) + { + if ((t_now - router->stats.lastReply) > (router->heartbeat + BLR_NET_LATENCY_WAIT_TIME)) + { + MXS_ERROR("No event received from master %s:%d in heartbeat period (%lu seconds), " + "last event (%s %d) received %lu seconds ago. Assuming connection is dead " + "and reconnecting.", + router->service->dbref->server->name, + router->service->dbref->server->port, + router->heartbeat, + event_desc != NULL ? event_desc : "unknown", + router->lastEventReceived, + t_now - router->stats.lastReply); - return 0; - } - } + return 0; + } + } - return 1; + return 1; } /** * Log binlog router identy after master registration, state is BLRM_BINLOG_DUMP * - * @param router The router instance + * @param router The router instance */ -static void blr_log_identity(ROUTER_INSTANCE *router) { +static void blr_log_identity(ROUTER_INSTANCE *router) +{ + char *master_uuid; + char *master_hostname; + char *master_version; - char *master_uuid; - char *master_hostname; - char *master_version; + if (router->set_master_version) + { + master_version = router->set_master_version; + } + else + { + master_version = blr_extract_column(router->saved_master.selectver, 1); + } - if (router->set_master_version) - master_version = router->set_master_version; - else { - master_version = blr_extract_column(router->saved_master.selectver, 1); - } + if (router->set_master_hostname) + { + master_hostname = router->set_master_hostname; + } + else + { + master_hostname = blr_extract_column(router->saved_master.selecthostname, 1); + } - if (router->set_master_hostname) - master_hostname = router->set_master_hostname; - else { - master_hostname = blr_extract_column(router->saved_master.selecthostname, 1); - } + if (router->set_master_uuid) + { + master_uuid = router->master_uuid; + } + else + { + master_uuid = blr_extract_column(router->saved_master.uuid, 2); + } - if (router->set_master_uuid) - master_uuid = router->master_uuid; - else { - master_uuid = blr_extract_column(router->saved_master.uuid, 2); - } - - /* Seen by the master */ - MXS_NOTICE("%s: identity seen by the master: " + /* Seen by the master */ + MXS_NOTICE("%s: identity seen by the master: " "server_id: %d, uuid: %s", router->service->name, router->serverid, (router->uuid == NULL ? "not available" : router->uuid)); - /* Seen by the slaves */ + /* Seen by the slaves */ - /* MariaDB 5.5 and MariaDB don't have the MASTER_UUID var */ - if (master_uuid == NULL) { - MXS_NOTICE("%s: identity seen by the slaves: " + /* MariaDB 5.5 and MariaDB don't have the MASTER_UUID var */ + if (master_uuid == NULL) + { + MXS_NOTICE("%s: identity seen by the slaves: " "server_id: %d, hostname: %s, MySQL version: %s", router->service->name, router->masterid, (master_hostname == NULL ? "not available" : master_hostname), (master_version == NULL ? "not available" : master_version)); - } else { - MXS_NOTICE("%s: identity seen by the slaves: " - "server_id: %d, uuid: %s, hostname: %s, MySQL version: %s", - router->service->name, - router->masterid, master_uuid, - (master_hostname == NULL ? "not available" : master_hostname), - (master_version == NULL ? "not available" : master_version)); - } + } + else + { + MXS_NOTICE("%s: identity seen by the slaves: " + "server_id: %d, uuid: %s, hostname: %s, MySQL version: %s", + router->service->name, + router->masterid, master_uuid, + (master_hostname == NULL ? "not available" : master_hostname), + (master_version == NULL ? "not available" : master_version)); + } } /** * Distribute an error message to all the registered slaves. * - * @param router The router instance - * @param message The message to send - * @param state The MySQL State for message - * @param err_code The MySQL error code for message + * @param router The router instance + * @param message The message to send + * @param state The MySQL State for message + * @param err_code The MySQL error code for message */ static void -blr_distribute_error_message(ROUTER_INSTANCE *router, char *message, char *state, unsigned int err_code) { -ROUTER_SLAVE *slave; +blr_distribute_error_message(ROUTER_INSTANCE *router, char *message, char *state, unsigned int err_code) +{ + ROUTER_SLAVE *slave; - spinlock_acquire(&router->lock); + spinlock_acquire(&router->lock); - slave = router->slaves; - while (slave) - { - if (slave->state != BLRS_DUMPING) - { - slave = slave->next; - continue; - } + slave = router->slaves; + while (slave) + { + if (slave->state != BLRS_DUMPING) + { + slave = slave->next; + continue; + } - /* send the error that stops slave replication */ - blr_send_custom_error(slave->dcb, slave->seqno++, 0, message, state, err_code); + /* send the error that stops slave replication */ + blr_send_custom_error(slave->dcb, slave->seqno++, 0, message, state, err_code); - slave = slave->next; - } + slave = slave->next; + } - spinlock_release(&router->lock); + spinlock_release(&router->lock); } int blr_write_data_into_binlog(ROUTER_INSTANCE *router, uint32_t data_len, uint8_t *buf) { -int n; + int n; - if ((n = pwrite(router->binlog_fd, buf, data_len, - router->last_written)) != data_len) + if ((n = pwrite(router->binlog_fd, buf, data_len, + router->last_written)) != data_len) + { + char err_msg[STRERROR_BUFLEN]; + MXS_ERROR("%s: Failed to write binlog record at %lu of %s, %s. " + "Truncating to previous record.", + router->service->name, router->last_written, + router->binlog_name, + strerror_r(errno, err_msg, sizeof(err_msg))); + + /* Remove any partial event that was written */ + if (ftruncate(router->binlog_fd, router->last_written)) { - char err_msg[STRERROR_BUFLEN]; - MXS_ERROR("%s: Failed to write binlog record at %lu of %s, %s. " - "Truncating to previous record.", - router->service->name, router->last_written, - router->binlog_name, - strerror_r(errno, err_msg, sizeof(err_msg))); - - /* Remove any partial event that was written */ - if (ftruncate(router->binlog_fd, router->last_written)) - { - MXS_ERROR("%s: Failed to truncate binlog record at %lu of %s, %s. ", - router->service->name, router->last_written, - router->binlog_name, - strerror_r(errno, err_msg, sizeof(err_msg))); - } - return 0; + MXS_ERROR("%s: Failed to truncate binlog record at %lu of %s, %s. ", + router->service->name, router->last_written, + router->binlog_name, + strerror_r(errno, err_msg, sizeof(err_msg))); } - router->last_written += data_len; - return n; + return 0; + } + router->last_written += data_len; + return n; } /** @@ -2456,15 +2662,44 @@ bool blr_send_packet(ROUTER_SLAVE *slave, uint8_t *buf, uint32_t len, bool first * This sends the complete replication event to a slave. If the event size exceeds * the maximum size of a MySQL packet, it will be sent in multiple packets. * + * @param role What is the role of the caller, slave or master. + * @param binlog_name The name of the binlogfile. + * @param binlog_pos The position in the binlogfile. * @param slave Slave where the event is sent to - * @param hdr Replication header - * @param buf Pointer to the replication event as it was read from the disk + * @param hdr Replication header + * @param buf Pointer to the replication event as it was read from the disk * @return True on success, false if memory allocation failed */ -bool blr_send_event(ROUTER_SLAVE *slave, REP_HEADER *hdr, uint8_t *buf) +bool blr_send_event(blr_thread_role_t role, + const char* binlog_name, + uint32_t binlog_pos, + ROUTER_SLAVE *slave, + REP_HEADER *hdr, + uint8_t *buf) { bool rval = true; + if ((strcmp(slave->lsi_binlog_name, binlog_name) == 0) && + (slave->lsi_binlog_pos == binlog_pos)) + { + MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s', position %u: " + "thread %lu in the role of %s could not send the event, " + "the event has already been sent by thread %lu in the role of %s. " + "%u bytes buffered for writing in DCB %p. %lu events received from master.", + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + binlog_name, + binlog_pos, + thread_self(), + ROLETOSTR(role), + slave->lsi_sender_tid, + ROLETOSTR(slave->lsi_sender_role), + gwbuf_length(slave->dcb->writeq), slave->dcb, + slave->router->stats.n_binlogs); + return false; + } + /** Check if the event and the OK byte fit into a single packet */ if (hdr->event_size + 1 < MYSQL_PACKET_LENGTH_MAX) { @@ -2479,7 +2714,7 @@ bool blr_send_event(ROUTER_SLAVE *slave, REP_HEADER *hdr, uint8_t *buf) while (rval && len > 0) { uint64_t payload_len = first ? MYSQL_PACKET_LENGTH_MAX - 1 : - MIN(MYSQL_PACKET_LENGTH_MAX, len); + MIN(MYSQL_PACKET_LENGTH_MAX, len); if (blr_send_packet(slave, buf, payload_len, first)) { @@ -2504,7 +2739,14 @@ bool blr_send_event(ROUTER_SLAVE *slave, REP_HEADER *hdr, uint8_t *buf) slave->stats.n_events++; - if (!rval) + if (rval) + { + strcpy(slave->lsi_binlog_name, binlog_name); + slave->lsi_binlog_pos = binlog_pos; + slave->lsi_sender_role = role; + slave->lsi_sender_tid = thread_self(); + } + else { MXS_ERROR("Failed to send an event of %u bytes to slave at %s:%d.", hdr->event_size, slave->dcb->remote, @@ -2532,3 +2774,35 @@ void extract_checksum(ROUTER_INSTANCE* router, uint8_t *cksumptr, uint8_t len) router->partial_checksum_bytes++; } } + +/** + * Stop the slave connection and log errors + * + * @param router Router instance + * @param ptr Pointer to the start of the packet + * @param len Length of the packet + */ +static void blr_terminate_master_replication(ROUTER_INSTANCE* router, uint8_t* ptr, int len) +{ + unsigned long mysql_errno = extract_field(ptr + 5, 16); + int msg_len = len - 7 - 6; // msg len is decreased by 7 and 6 + char *msg_err = (char *)malloc(msg_len + 1); + + strncpy(msg_err, (char *)ptr + 7 + 6, msg_len); + *(msg_err + msg_len) = '\0'; + + spinlock_acquire(&router->lock); + + char* old_errmsg = router->m_errmsg; + router->m_errmsg = msg_err; + router->m_errno = mysql_errno; + router->master_state = BLRM_SLAVE_STOPPED; + router->stats.n_binlog_errors++; + + spinlock_release(&router->lock); + + free(old_errmsg); + MXS_ERROR("Error packet in binlog stream.%s @ %lu.", + router->binlog_name, router->current_pos); + +} diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index e9ef28c46..e64da023d 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -29,37 +29,38 @@ * @verbatim * Revision History * - * Date Who Description - * 14/04/2014 Mark Riddoch Initial implementation - * 18/02/2015 Massimiliano Pinto Addition of DISCONNECT ALL and DISCONNECT SERVER server_id - * 18/03/2015 Markus Makela Better detection of CRC32 | NONE checksum - * 19/03/2015 Massimiliano Pinto Addition of basic MariaDB 10 compatibility support - * 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility - * 11/05/2015 Massimiliano Pinto Only MariaDB 10 Slaves can register to binlog router with a MariaDB 10 Master - * 25/05/2015 Massimiliano Pinto Addition of BLRM_SLAVE_STOPPED state and blr_start/stop_slave. - * New commands STOP SLAVE, START SLAVE added. - * 29/05/2015 Massimiliano Pinto Addition of CHANGE MASTER TO ... - * 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 - * 23/06/2015 Massimiliano Pinto Added utility routines for blr_handle_change_master - * Call create/use binlog in blr_start_slave() (START SLAVE) - * 29/06/2015 Massimiliano Pinto Successfully CHANGE MASTER results in updating master.ini - * in blr_handle_change_master() - * 20/08/2015 Massimiliano Pinto Added parsing and validation for CHANGE MASTER TO - * 21/08/2015 Massimiliano Pinto Added support for new config options: - * master_uuid, master_hostname, master_version - * If set those values are sent to slaves instead of - * saved master responses - * 03/09/2015 Massimiliano Pinto Added support for SHOW [GLOBAL] VARIABLES LIKE - * 04/09/2015 Massimiliano Pinto Added support for SHOW WARNINGS - * 15/09/2015 Massimiliano Pinto Added support for SHOW [GLOBAL] STATUS LIKE 'Uptime' - * 25/09/2015 Massimiliano Pinto Addition of slave heartbeat: - * the period set during registration is checked - * and heartbeat event might be sent to the affected slave. - * 25/09/2015 Martin Brampton Block callback processing when no router session in the DCB - * 23/10/15 Markus Makela Added current_safe_event + * Date Who Description + * 14/04/2014 Mark Riddoch Initial implementation + * 18/02/2015 Massimiliano Pinto Addition of DISCONNECT ALL and DISCONNECT SERVER server_id + * 18/03/2015 Markus Makela Better detection of CRC32 | NONE checksum + * 19/03/2015 Massimiliano Pinto Addition of basic MariaDB 10 compatibility support + * 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility + * 11/05/2015 Massimiliano Pinto Only MariaDB 10 Slaves can register to binlog router + * with a MariaDB 10 Master + * 25/05/2015 Massimiliano Pinto Addition of BLRM_SLAVE_STOPPED state and blr_start/stop_slave. + * New commands STOP SLAVE, START SLAVE added. + * 29/05/2015 Massimiliano Pinto Addition of CHANGE MASTER TO ... + * 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 + * 23/06/2015 Massimiliano Pinto Added utility routines for blr_handle_change_master + * Call create/use binlog in blr_start_slave() (START SLAVE) + * 29/06/2015 Massimiliano Pinto Successfully CHANGE MASTER results in updating master.ini + * in blr_handle_change_master() + * 20/08/2015 Massimiliano Pinto Added parsing and validation for CHANGE MASTER TO + * 21/08/2015 Massimiliano Pinto Added support for new config options: + * master_uuid, master_hostname, master_version + * If set those values are sent to slaves instead of + * saved master responses + * 03/09/2015 Massimiliano Pinto Added support for SHOW [GLOBAL] VARIABLES LIKE + * 04/09/2015 Massimiliano Pinto Added support for SHOW WARNINGS + * 15/09/2015 Massimiliano Pinto Added support for SHOW [GLOBAL] STATUS LIKE 'Uptime' + * 25/09/2015 Massimiliano Pinto Addition of slave heartbeat: + * the period set during registration is checked + * and heartbeat event might be sent to the affected slave. + * 25/09/2015 Martin Brampton Block callback processing when no router session in the DCB + * 23/10/15 Markus Makela Added current_safe_event * * @endverbatim */ @@ -68,7 +69,6 @@ #include #include #include -#include #include #include #include @@ -102,7 +102,7 @@ static int blr_slave_send_timestamp(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave static int blr_slave_register(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue); static int blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue); int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large); -uint8_t *blr_build_header(GWBUF *pkt, REP_HEADER *hdr); +uint8_t *blr_build_header(GWBUF *pkt, REP_HEADER *hdr); int blr_slave_callback(DCB *dcb, DCB_REASON reason, void *data); static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, BLFILE** filep); static void blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); @@ -113,9 +113,11 @@ static int blr_slave_send_master_status(ROUTER_INSTANCE *router, ROUTER_SLAVE *s static int blr_slave_send_slave_status(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_slave_send_slave_hosts(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_slave_send_fieldcount(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int count); -static int blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno); +static int blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, + int len, uint8_t seqno); static int blr_slave_send_eof(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int seqno); -static int blr_slave_send_disconnected_server(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int server_id, int found); +static int blr_slave_send_disconnected_server(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int server_id, + int found); static int blr_slave_disconnect_all(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_slave_disconnect_server(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int server_id); static int blr_slave_send_ok(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave); @@ -139,20 +141,25 @@ static int blr_set_master_password(ROUTER_INSTANCE *router, char *password); static int blr_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_OPTIONS *config); static int blr_handle_change_master_token(char *input, char *error, CHANGE_MASTER_OPTIONS *config); static void blr_master_free_parsed_options(CHANGE_MASTER_OPTIONS *options); -static int blr_slave_send_var_value(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, int column_type); -static int blr_slave_send_variable(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, int column_type); -static int blr_slave_send_columndef_with_info_schema(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno); +static int blr_slave_send_var_value(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, + int column_type); +static int blr_slave_send_variable(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, + int column_type); +static int blr_slave_send_columndef_with_info_schema(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, + int type, int len, uint8_t seqno); int blr_test_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_OPTIONS *config); char *blr_test_set_master_logfile(ROUTER_INSTANCE *router, char *filename, char *error); static int blr_slave_handle_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *stmt); static int blr_slave_send_warning_message(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave, char *message); static int blr_slave_show_warnings(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave); -static int blr_slave_send_status_variable(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, int column_type); +extern int maxscale_uptime(); +static int blr_slave_send_status_variable(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, + char *value, int column_type); static int blr_slave_handle_status_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *stmt); -static int blr_slave_send_columndef_with_status_schema(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno); +static int blr_slave_send_columndef_with_status_schema(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, + char *name, int type, int len, uint8_t seqno); static void blr_send_slave_heartbeat(void *inst); static int blr_slave_send_heartbeat(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); -bool blr_send_event(ROUTER_SLAVE *slave, REP_HEADER *hdr, uint8_t *buf); void poll_fake_write_event(DCB *dcb); @@ -168,98 +175,105 @@ void poll_fake_write_event(DCB *dcb); * if this is possible, if it is not then the router itself will synthesize a * response. * - * @param router The router instance this defines the master for this replication chain - * @param slave The slave specific data - * @param queue The incoming request packet + * @param router The router instance this defines the master for this replication chain + * @param slave The slave specific data + * @param queue The incoming request packet */ int blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { - if (slave->state < 0 || slave->state > BLRS_MAXSTATE) - { - MXS_ERROR("Invalid slave state machine state (%d) for binlog router.", - slave->state); - gwbuf_consume(queue, gwbuf_length(queue)); - return 0; - } + if (slave->state < 0 || slave->state > BLRS_MAXSTATE) + { + MXS_ERROR("Invalid slave state machine state (%d) for binlog router.", + slave->state); + gwbuf_consume(queue, gwbuf_length(queue)); + return 0; + } - slave->stats.n_requests++; - switch (MYSQL_COMMAND(queue)) - { - case COM_QUERY: - slave->stats.n_queries++; - return blr_slave_query(router, slave, queue); - break; - case COM_REGISTER_SLAVE: - if (router->master_state == BLRM_UNCONFIGURED) { - slave->state = BLRS_ERRORED; - blr_slave_send_error_packet(slave, - "Binlog router is not yet configured for replication", (unsigned int) 1597, NULL); + slave->stats.n_requests++; + switch (MYSQL_COMMAND(queue)) + { + case COM_QUERY: + slave->stats.n_queries++; + return blr_slave_query(router, slave, queue); + break; + case COM_REGISTER_SLAVE: + if (router->master_state == BLRM_UNCONFIGURED) + { + slave->state = BLRS_ERRORED; + blr_slave_send_error_packet(slave, + "Binlog router is not yet configured for replication", + (unsigned int) 1597, NULL); - MXS_ERROR("%s: Slave %s: Binlog router is not yet configured for replication", - router->service->name, - slave->dcb->remote); - dcb_close(slave->dcb); - return 1; - } + MXS_ERROR("%s: Slave %s: Binlog router is not yet configured for replication", + router->service->name, + slave->dcb->remote); + dcb_close(slave->dcb); + return 1; + } - /* - * If Master is MariaDB10 don't allow registration from - * MariaDB/Mysql 5 Slaves - */ + /* + * If Master is MariaDB10 don't allow registration from + * MariaDB/Mysql 5 Slaves + */ - if (router->mariadb10_compat && !slave->mariadb10_compat) { - slave->state = BLRS_ERRORED; - blr_send_custom_error(slave->dcb, 1, 0, - "MariaDB 10 Slave is required for Slave registration", "42000", 1064); + if (router->mariadb10_compat && !slave->mariadb10_compat) + { + slave->state = BLRS_ERRORED; + blr_send_custom_error(slave->dcb, 1, 0, + "MariaDB 10 Slave is required for Slave registration", "42000", 1064); - MXS_ERROR("%s: Slave %s: a MariaDB 10 Slave is required for Slave registration", - router->service->name, - slave->dcb->remote); + MXS_ERROR("%s: Slave %s: a MariaDB 10 Slave is required for Slave registration", + router->service->name, + slave->dcb->remote); - dcb_close(slave->dcb); - return 1; - } else { - /* Master and Slave version OK: continue with slave registration */ - return blr_slave_register(router, slave, queue); - } - break; - case COM_BINLOG_DUMP: - { - char task_name[BLRM_TASK_NAME_LEN+1]=""; - int rc = 0; + dcb_close(slave->dcb); + return 1; + } + else + { + /* Master and Slave version OK: continue with slave registration */ + return blr_slave_register(router, slave, queue); + } + break; + case COM_BINLOG_DUMP: + { + char task_name[BLRM_TASK_NAME_LEN + 1] = ""; + int rc = 0; - rc = blr_slave_binlog_dump(router, slave, queue); + rc = blr_slave_binlog_dump(router, slave, queue); - if (router->send_slave_heartbeat && rc && slave->heartbeat > 0) { - snprintf(task_name, BLRM_TASK_NAME_LEN, "%s slaves heartbeat send", router->service->name); + if (router->send_slave_heartbeat && rc && slave->heartbeat > 0) + { + snprintf(task_name, BLRM_TASK_NAME_LEN, "%s slaves heartbeat send", router->service->name); - /* Add slave heartbeat check task: it runs with 1 second frequency */ - hktask_add(task_name, blr_send_slave_heartbeat, router, 1); - } + /* Add slave heartbeat check task: it runs with 1 second frequency */ + hktask_add(task_name, blr_send_slave_heartbeat, router, 1); + } - return rc; - break; - } - case COM_STATISTICS: - return blr_statistics(router, slave, queue); - break; - case COM_PING: - return blr_ping(router, slave, queue); - break; - case COM_QUIT: - MXS_DEBUG("COM_QUIT received from slave with server_id %d", - slave->serverid); - break; - default: - blr_send_custom_error(slave->dcb, 1, 0, - "You have an error in your SQL syntax; Check the syntax the MaxScale binlog router accepts.", - "42000", 1064); - MXS_ERROR("Unexpected MySQL Command (%d) received from slave", - MYSQL_COMMAND(queue)); - break; - } - return 0; + return rc; + } + break; + case COM_STATISTICS: + return blr_statistics(router, slave, queue); + break; + case COM_PING: + return blr_ping(router, slave, queue); + break; + case COM_QUIT: + MXS_DEBUG("COM_QUIT received from slave with server_id %d", + slave->serverid); + break; + default: + blr_send_custom_error(slave->dcb, 1, 0, + "You have an error in your SQL syntax; Check the " + "syntax the MaxScale binlog router accepts.", + "42000", 1064); + MXS_ERROR("Unexpected MySQL Command (%d) received from slave", + MYSQL_COMMAND(queue)); + break; + } + return 0; } /** @@ -274,678 +288,795 @@ blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) * router. * * Twelve select statements are currently supported: - * SELECT UNIX_TIMESTAMP(); - * SELECT @master_binlog_checksum - * SELECT @@GLOBAL.GTID_MODE - * SELECT VERSION() - * SELECT 1 - * SELECT @@version_comment limit 1 - * SELECT @@hostname - * SELECT @@max_allowed_packet - * SELECT @@maxscale_version - * SELECT @@[GLOBAL.]server_id - * SELECT @@version - * SELECT @@[GLOBAL.]server_uuid + * SELECT UNIX_TIMESTAMP(); + * SELECT @master_binlog_checksum + * SELECT @@GLOBAL.GTID_MODE + * SELECT VERSION() + * SELECT 1 + * SELECT @@version_comment limit 1 + * SELECT @@hostname + * SELECT @@max_allowed_packet + * SELECT @@maxscale_version + * SELECT @@[GLOBAL.]server_id + * SELECT @@version + * SELECT @@[GLOBAL.]server_uuid * * Eight show commands are supported: - * SHOW [GLOBAL] VARIABLES LIKE 'SERVER_ID' - * SHOW [GLOBAL] VARIABLES LIKE 'SERVER_UUID' - * SHOW [GLOBAL] VARIABLES LIKE 'MAXSCALE%' - * SHOW SLAVE STATUS - * SHOW MASTER STATUS - * SHOW SLAVE HOSTS - * SHOW WARNINGS - * SHOW [GLOBAL] STATUS LIKE 'Uptime' + * SHOW [GLOBAL] VARIABLES LIKE 'SERVER_ID' + * SHOW [GLOBAL] VARIABLES LIKE 'SERVER_UUID' + * SHOW [GLOBAL] VARIABLES LIKE 'MAXSCALE%' + * SHOW SLAVE STATUS + * SHOW MASTER STATUS + * SHOW SLAVE HOSTS + * SHOW WARNINGS + * SHOW [GLOBAL] STATUS LIKE 'Uptime' * * Six set commands are supported: - * SET @master_binlog_checksum = @@global.binlog_checksum - * SET @master_heartbeat_period=... - * SET @slave_slave_uuid=... - * SET NAMES latin1 - * SET NAMES utf8 - * SET mariadb_slave_capability=... + * SET @master_binlog_checksum = @@global.binlog_checksum + * SET @master_heartbeat_period=... + * SET @slave_slave_uuid=... + * SET NAMES latin1 + * SET NAMES utf8 + * SET mariadb_slave_capability=... * * Four administrative commands are supported: - * STOP SLAVE - * START SLAVE - * CHANGE MASTER TO - * RESET SLAVE + * STOP SLAVE + * START SLAVE + * CHANGE MASTER TO + * RESET SLAVE * * @param router The router instance this defines the master for this replication chain * @param slave The slave specific data * @param queue The incoming request packet - * @return Non-zero if data has been sent + * @return Non-zero if data has been sent */ static int blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { -char *qtext, *query_text; -char *sep = " ,="; -char *word, *brkb; -int query_len; -char *ptr; -extern char *strcasestr(); + char *qtext, *query_text; + char *sep = " ,="; + char *word, *brkb; + int query_len; + char *ptr; + extern char *strcasestr(); - qtext = GWBUF_DATA(queue); - query_len = extract_field((uint8_t *)qtext, 24) - 1; - qtext += 5; // Skip header and first byte of the payload - query_text = strndup(qtext, query_len); + qtext = GWBUF_DATA(queue); + query_len = extract_field((uint8_t *)qtext, 24) - 1; + qtext += 5; // Skip header and first byte of the payload + query_text = strndup(qtext, query_len); - /* Don't log the full statement containg 'password', just trucate it */ - ptr = strcasestr(query_text, "password"); - if (ptr != NULL) { - char *new_text = strdup(query_text); - int trucate_at = (ptr - query_text); - if (trucate_at > 0) { - if ( (trucate_at + 3) <= strlen(new_text)) { - int i; - for (i = 0; i < 3; i++) { - new_text[trucate_at + i] = '.'; - } - new_text[trucate_at+3] = '\0'; - } else { - new_text[trucate_at] = '\0'; - } - } - - MXS_INFO("Execute statement (truncated, it contains password)" - " from the slave '%s'", new_text); - free(new_text); - } else { - MXS_INFO("Execute statement from the slave '%s'", query_text); - } - - /* - * Implement a very rudimental "parsing" of the query text by extarcting the - * words from the statement and matchng them against the subset of queries we - * are expecting from the slave. We already have responses to these commands, - * except for the select of UNIX_TIMESTAMP(), that we have saved from MaxScale's - * own interaction with the real master. We simply replay these saved responses - * to the slave. - */ - if ((word = strtok_r(query_text, sep, &brkb)) == NULL) - { - - MXS_ERROR("%s: Incomplete query.", router->service->name); - } - else if (strcasecmp(word, "SELECT") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete select query.", - router->service->name); - } - else if (strcasecmp(word, "UNIX_TIMESTAMP()") == 0) - { - free(query_text); - return blr_slave_send_timestamp(router, slave); - } - else if (strcasecmp(word, "@master_binlog_checksum") == 0) - { - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.chksum2); - } - else if (strcasecmp(word, "@@GLOBAL.GTID_MODE") == 0) - { - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.gtid_mode); - } - else if (strcasecmp(word, "1") == 0) - { - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.select1); - } - else if (strcasecmp(word, "VERSION()") == 0) - { - free(query_text); - if (router->set_master_version) - return blr_slave_send_var_value(router, slave, "VERSION()", router->set_master_version, BLR_TYPE_STRING); - else - return blr_slave_replay(router, slave, router->saved_master.selectver); - } - else if (strcasecmp(word, "@@version") == 0) - { - free(query_text); - if (router->set_master_version) - return blr_slave_send_var_value(router, slave, "@@version", router->set_master_version, BLR_TYPE_STRING); - else { - char *version = blr_extract_column(router->saved_master.selectver, 1); - - blr_slave_send_var_value(router, slave, "@@version", version == NULL ? "" : version, BLR_TYPE_STRING); - free(version); - return 1; - } - } - else if (strcasecmp(word, "@@version_comment") == 0) - { - free(query_text); - if (!router->saved_master.selectvercom) - /* This will allow mysql client to get in when @@version_comment is not available */ - return blr_slave_send_ok(router, slave); - else - return blr_slave_replay(router, slave, router->saved_master.selectvercom); - } - else if (strcasecmp(word, "@@hostname") == 0) - { - free(query_text); - if (router->set_master_hostname) - return blr_slave_send_var_value(router, slave, "@@hostname", router->set_master_hostname, BLR_TYPE_STRING); - else - return blr_slave_replay(router, slave, router->saved_master.selecthostname); - } - else if ((strcasecmp(word, "@@server_uuid") == 0) || (strcasecmp(word, "@@global.server_uuid") == 0)) - { - char heading[40]; /* to ensure we match the case in query and response */ - strcpy(heading, word); - - free(query_text); - if (router->set_master_uuid) - return blr_slave_send_var_value(router, slave, heading, router->master_uuid, BLR_TYPE_STRING); - else { - char *master_uuid = blr_extract_column(router->saved_master.uuid, 2); - blr_slave_send_var_value(router, slave, heading, master_uuid == NULL ? "" : master_uuid, BLR_TYPE_STRING); - free(master_uuid); - return 1; - } - } - else if (strcasecmp(word, "@@max_allowed_packet") == 0) - { - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.map); - } - else if (strcasecmp(word, "@@maxscale_version") == 0) - { - free(query_text); - return blr_slave_send_maxscale_version(router, slave); - } - else if ((strcasecmp(word, "@@server_id") == 0) || (strcasecmp(word, "@@global.server_id") == 0)) - { - char server_id[40]; - char heading[40]; /* to ensure we match the case in query and response */ - - sprintf(server_id, "%d", router->masterid); - strcpy(heading, word); - - free(query_text); - - return blr_slave_send_var_value(router, slave, heading, server_id, BLR_TYPE_INT); - } - } - else if (strcasecmp(word, "SHOW") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete show query.", - router->service->name); - } - else if (strcasecmp(word, "WARNINGS") == 0) - { - free(query_text); - return blr_slave_show_warnings(router, slave); - } - else if (strcasecmp(word, "GLOBAL") == 0) - { - if (router->master_state == BLRM_UNCONFIGURED) { - free(query_text); - return blr_slave_send_ok(router, slave); - } - - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Expected VARIABLES in SHOW GLOBAL", - router->service->name); - } - else if (strcasecmp(word, "VARIABLES") == 0) - { - int rc = blr_slave_handle_variables(router, slave, brkb); - - /* if no var found, send empty result set */ - if (rc == 0) - blr_slave_send_ok(router, slave); - - if (rc >= 0) { - free(query_text); - - return 1; - } else - MXS_ERROR("%s: Expected LIKE clause in SHOW GLOBAL VARIABLES.", - router->service->name); - } - else if (strcasecmp(word, "STATUS") == 0) - { - int rc = blr_slave_handle_status_variables(router, slave, brkb); - - /* if no var found, send empty result set */ - if (rc == 0) - blr_slave_send_ok(router, slave); - - if (rc >= 0) { - free(query_text); - - return 1; - } else - MXS_ERROR("%s: Expected LIKE clause in SHOW GLOBAL STATUS.", - router->service->name); - } - } - else if (strcasecmp(word, "VARIABLES") == 0) - { - int rc; - if (router->master_state == BLRM_UNCONFIGURED) { - free(query_text); - return blr_slave_send_ok(router, slave); - } - - rc = blr_slave_handle_variables(router, slave, brkb); - - /* if no var found, send empty result set */ - if (rc == 0) - blr_slave_send_ok(router, slave); - - if (rc >= 0) { - free(query_text); - - return 1; - } else - MXS_ERROR("%s: Expected LIKE clause in SHOW VARIABLES.", - router->service->name); - } - else if (strcasecmp(word, "MASTER") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Expected SHOW MASTER STATUS command", - router->service->name); - } - else if (strcasecmp(word, "STATUS") == 0) - { - free(query_text); - - /* if state is BLRM_UNCONFIGURED return empty result */ - - if (router->master_state > BLRM_UNCONFIGURED) - return blr_slave_send_master_status(router, slave); - else - return blr_slave_send_ok(router, slave); - } - } - else if (strcasecmp(word, "SLAVE") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Expected SHOW SLAVE STATUS command", - router->service->name); - } - else if (strcasecmp(word, "STATUS") == 0) - { - free(query_text); - /* if state is BLRM_UNCONFIGURED return empty result */ - if (router->master_state > BLRM_UNCONFIGURED) - return blr_slave_send_slave_status(router, slave); - else - return blr_slave_send_ok(router, slave); - } - else if (strcasecmp(word, "HOSTS") == 0) - { - free(query_text); - /* if state is BLRM_UNCONFIGURED return empty result */ - if (router->master_state > BLRM_UNCONFIGURED) - return blr_slave_send_slave_hosts(router, slave); - else - return blr_slave_send_ok(router, slave); - } - } - else if (strcasecmp(word, "STATUS") == 0) - { - int rc = blr_slave_handle_status_variables(router, slave, brkb); - - /* if no var found, send empty result set */ - if (rc == 0) - blr_slave_send_ok(router, slave); - - if (rc >= 0) { - free(query_text); - - return 1; - } else - MXS_ERROR("%s: Expected LIKE clause in SHOW STATUS.", - router->service->name); - } - } - else if (strcasecmp(query_text, "SET") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete set command.", - router->service->name); - } - else if (strcasecmp(word, "@master_heartbeat_period") == 0) - { - int slave_heartbeat; - int v_len = 0; - word = strtok_r(NULL, sep, &brkb); - if (word) { - char *new_val; - v_len = strlen(word); - if (v_len > 6) { - new_val = strndup(word, v_len - 6); - slave->heartbeat = atoi(new_val) / 1000; - } else { - new_val = strndup(word, v_len); - slave->heartbeat = atoi(new_val) / 1000000; - } - - free(new_val); - } - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.heartbeat); - } - else if (strcasecmp(word, "@mariadb_slave_capability") == 0) + /* Don't log the full statement containg 'password', just trucate it */ + ptr = strcasestr(query_text, "password"); + if (ptr != NULL) + { + char *new_text = strdup(query_text); + int trucate_at = (ptr - query_text); + if (trucate_at > 0) + { + if ( (trucate_at + 3) <= strlen(new_text)) + { + int i; + for (i = 0; i < 3; i++) { - /* mariadb10 compatibility is set for the slave */ - slave->mariadb10_compat=true; - - free(query_text); - if (router->mariadb10_compat) { - return blr_slave_replay(router, slave, router->saved_master.mariadb10); - } else { - return blr_slave_send_ok(router, slave); - } + new_text[trucate_at + i] = '.'; } - else if (strcasecmp(word, "@master_binlog_checksum") == 0) - { - word = strtok_r(NULL, sep, &brkb); - if (word && (strcasecmp(word, "'none'") == 0)) - slave->nocrc = 1; - else if (word && (strcasecmp(word, "@@global.binlog_checksum") == 0)) - slave->nocrc = !router->master_chksum; - else - slave->nocrc = 0; + new_text[trucate_at + 3] = '\0'; + } + else + { + new_text[trucate_at] = '\0'; + } + } - - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.chksum1); - } - else if (strcasecmp(word, "@slave_uuid") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) != NULL) { - int len = strlen(word); - char *word_ptr = word; - if (len) { - if (word[len-1] == '\'') - word[len-1] = '\0'; - if (word[0] == '\'') { - word[0] = '\0'; - word_ptr++; - } - } - slave->uuid = strdup(word_ptr); - } - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.setslaveuuid); - } - else if (strcasecmp(word, "NAMES") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Truncated SET NAMES command.", - router->service->name); - } - else if (strcasecmp(word, "latin1") == 0) - { - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.setnames); - } - else if (strcasecmp(word, "utf8") == 0) - { - free(query_text); - return blr_slave_replay(router, slave, router->saved_master.utf8); - } - } - } /* RESET current configured master */ - else if (strcasecmp(query_text, "RESET") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete RESET command.", - router->service->name); - } - else if (strcasecmp(word, "SLAVE") == 0) - { - free(query_text); + MXS_INFO("Execute statement (truncated, it contains password)" + " from the slave '%s'", new_text); + free(new_text); + } + else + { + MXS_INFO("Execute statement from the slave '%s'", query_text); + } - if (router->master_state == BLRM_SLAVE_STOPPED) { - char path[PATH_MAX + 1] = ""; - char error_string[BINLOG_ERROR_MSG_LEN + 1] = ""; - MASTER_SERVER_CFG *current_master = NULL; - int removed_cfg = 0; + /* + * Implement a very rudimental "parsing" of the query text by extarcting the + * words from the statement and matchng them against the subset of queries we + * are expecting from the slave. We already have responses to these commands, + * except for the select of UNIX_TIMESTAMP(), that we have saved from MaxScale's + * own interaction with the real master. We simply replay these saved responses + * to the slave. + */ + if ((word = strtok_r(query_text, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete query.", router->service->name); + } + else if (strcasecmp(word, "SELECT") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete select query.", router->service->name); + } + else if (strcasecmp(word, "UNIX_TIMESTAMP()") == 0) + { + free(query_text); + return blr_slave_send_timestamp(router, slave); + } + else if (strcasecmp(word, "@master_binlog_checksum") == 0) + { + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.chksum2); + } + else if (strcasecmp(word, "@@GLOBAL.GTID_MODE") == 0) + { + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.gtid_mode); + } + else if (strcasecmp(word, "1") == 0) + { + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.select1); + } + else if (strcasecmp(word, "VERSION()") == 0) + { + free(query_text); + if (router->set_master_version) + { + return blr_slave_send_var_value(router, slave, "VERSION()", + router->set_master_version, BLR_TYPE_STRING); + } + else + { + return blr_slave_replay(router, slave, router->saved_master.selectver); + } + } + else if (strcasecmp(word, "@@version") == 0) + { + free(query_text); + if (router->set_master_version) + { + return blr_slave_send_var_value(router, slave, "@@version", + router->set_master_version, BLR_TYPE_STRING); + } + else + { + char *version = blr_extract_column(router->saved_master.selectver, 1); - /* save current replication parameters */ - current_master = (MASTER_SERVER_CFG *)calloc(1, sizeof(MASTER_SERVER_CFG)); + blr_slave_send_var_value(router, slave, "@@version", + version == NULL ? "" : version, BLR_TYPE_STRING); + free(version); + return 1; + } + } + else if (strcasecmp(word, "@@version_comment") == 0) + { + free(query_text); + if (!router->saved_master.selectvercom) + /* This will allow mysql client to get in when @@version_comment is not available */ + { + return blr_slave_send_ok(router, slave); + } + else + { + return blr_slave_replay(router, slave, router->saved_master.selectvercom); + } + } + else if (strcasecmp(word, "@@hostname") == 0) + { + free(query_text); + if (router->set_master_hostname) + { + return blr_slave_send_var_value(router, slave, "@@hostname", + router->set_master_hostname, BLR_TYPE_STRING); + } + else + { + return blr_slave_replay(router, slave, router->saved_master.selecthostname); + } + } + else if ((strcasecmp(word, "@@server_uuid") == 0) || (strcasecmp(word, "@@global.server_uuid") == 0)) + { + char heading[40]; /* to ensure we match the case in query and response */ + strcpy(heading, word); - if (!current_master) { - snprintf(error_string, BINLOG_ERROR_MSG_LEN, "error allocating memory for blr_master_get_config"); - MXS_ERROR("%s: %s", router->service->name, error_string); - blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); + free(query_text); + if (router->set_master_uuid) + { + return blr_slave_send_var_value(router, slave, heading, router->master_uuid, BLR_TYPE_STRING); + } + else + { + char *master_uuid = blr_extract_column(router->saved_master.uuid, 2); + blr_slave_send_var_value(router, slave, heading, + master_uuid == NULL ? "" : master_uuid, BLR_TYPE_STRING); + free(master_uuid); + return 1; + } + } + else if (strcasecmp(word, "@@max_allowed_packet") == 0) + { + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.map); + } + else if (strcasecmp(word, "@@maxscale_version") == 0) + { + free(query_text); + return blr_slave_send_maxscale_version(router, slave); + } + else if ((strcasecmp(word, "@@server_id") == 0) || (strcasecmp(word, "@@global.server_id") == 0)) + { + char server_id[40]; + char heading[40]; /* to ensure we match the case in query and response */ - return 1; - } + sprintf(server_id, "%d", router->masterid); + strcpy(heading, word); - /* get current data */ - blr_master_get_config(router, current_master); + free(query_text); - MXS_NOTICE("%s: 'RESET SLAVE executed'. Previous state MASTER_HOST='%s', " - "MASTER_PORT=%i, MASTER_LOG_FILE='%s', MASTER_LOG_POS=%lu, " - "MASTER_USER='%s'", - router->service->name, - current_master->host, - current_master->port, - current_master->logfile, - current_master->pos, - current_master->user); + return blr_slave_send_var_value(router, slave, heading, server_id, BLR_TYPE_INT); + } + } + else if (strcasecmp(word, "SHOW") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete show query.", + router->service->name); + } + else if (strcasecmp(word, "WARNINGS") == 0) + { + free(query_text); + return blr_slave_show_warnings(router, slave); + } + else if (strcasecmp(word, "GLOBAL") == 0) + { + if (router->master_state == BLRM_UNCONFIGURED) + { + free(query_text); + return blr_slave_send_ok(router, slave); + } - /* remove master.ini */ - strncpy(path, router->binlogdir, PATH_MAX); + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Expected VARIABLES in SHOW GLOBAL", + router->service->name); + } + else if (strcasecmp(word, "VARIABLES") == 0) + { + int rc = blr_slave_handle_variables(router, slave, brkb); - strncat(path,"/master.ini", PATH_MAX); + /* if no var found, send empty result set */ + if (rc == 0) + { + blr_slave_send_ok(router, slave); + } - /* remove master.ini */ - removed_cfg = unlink(path); + if (rc >= 0) + { + free(query_text); - if (removed_cfg == -1) { - char err_msg[STRERROR_BUFLEN]; - snprintf(error_string, BINLOG_ERROR_MSG_LEN, "Error removing %s, %s, errno %u", path, strerror_r(errno, err_msg, sizeof(err_msg)), errno); - MXS_ERROR("%s: %s", router->service->name, error_string); - } + return 1; + } + else + MXS_ERROR("%s: Expected LIKE clause in SHOW GLOBAL VARIABLES.", + router->service->name); + } + else if (strcasecmp(word, "STATUS") == 0) + { + int rc = blr_slave_handle_status_variables(router, slave, brkb); - spinlock_acquire(&router->lock); + /* if no var found, send empty result set */ + if (rc == 0) + { + blr_slave_send_ok(router, slave); + } - router->master_state = BLRM_UNCONFIGURED; - blr_master_set_empty_config(router); - blr_master_free_config(current_master); + if (rc >= 0) + { + free(query_text); - spinlock_release(&router->lock); + return 1; + } + else + { + MXS_ERROR("%s: Expected LIKE clause in SHOW GLOBAL STATUS.", + router->service->name); + } + } + } + else if (strcasecmp(word, "VARIABLES") == 0) + { + int rc; + if (router->master_state == BLRM_UNCONFIGURED) + { + free(query_text); + return blr_slave_send_ok(router, slave); + } - if (removed_cfg == -1) { - blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); - return 1; - } else { - return blr_slave_send_ok(router, slave); - } - } else { - if (router->master_state == BLRM_UNCONFIGURED) - blr_slave_send_ok(router, slave); - else - blr_slave_send_error_packet(slave, "This operation cannot be performed with a running slave; run STOP SLAVE first", (unsigned int)1198, NULL); - return 1; - } - } - } - /* start replication from the current configured master */ - else if (strcasecmp(query_text, "START") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete START command.", - router->service->name); - } - else if (strcasecmp(word, "SLAVE") == 0) - { - free(query_text); - return blr_start_slave(router, slave); - } - } - /* stop replication from the current master*/ - else if (strcasecmp(query_text, "STOP") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete STOP command.", router->service->name); - } - else if (strcasecmp(word, "SLAVE") == 0) - { - free(query_text); - return blr_stop_slave(router, slave); - } - } - /* Change the server to replicate from */ - else if (strcasecmp(query_text, "CHANGE") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete CHANGE command.", router->service->name); - } - else if (strcasecmp(word, "MASTER") == 0) - { - if (router->master_state != BLRM_SLAVE_STOPPED && router->master_state != BLRM_UNCONFIGURED) - { - free(query_text); - blr_slave_send_error_packet(slave, "Cannot change master with a running slave; run STOP SLAVE first", (unsigned int)1198, NULL); - return 1; - } - else - { - int rc; - char error_string[BINLOG_ERROR_MSG_LEN + 1] = ""; - MASTER_SERVER_CFG *current_master = NULL; + rc = blr_slave_handle_variables(router, slave, brkb); - current_master = (MASTER_SERVER_CFG *)calloc(1, sizeof(MASTER_SERVER_CFG)); + /* if no var found, send empty result set */ + if (rc == 0) + { + blr_slave_send_ok(router, slave); + } - if (!current_master) { - free(query_text); - strcpy(error_string, "Error allocating memory for blr_master_get_config"); - MXS_ERROR("%s: %s", router->service->name, error_string); + if (rc >= 0) + { + free(query_text); - blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); + return 1; + } + else + MXS_ERROR("%s: Expected LIKE clause in SHOW VARIABLES.", + router->service->name); + } + else if (strcasecmp(word, "MASTER") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Expected SHOW MASTER STATUS command", + router->service->name); + } + else if (strcasecmp(word, "STATUS") == 0) + { + free(query_text); - return 1; - } + /* if state is BLRM_UNCONFIGURED return empty result */ - blr_master_get_config(router, current_master); + if (router->master_state > BLRM_UNCONFIGURED) + { + return blr_slave_send_master_status(router, slave); + } + else + { + return blr_slave_send_ok(router, slave); + } + } + } + else if (strcasecmp(word, "SLAVE") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Expected SHOW SLAVE STATUS command", + router->service->name); + } + else if (strcasecmp(word, "STATUS") == 0) + { + free(query_text); + /* if state is BLRM_UNCONFIGURED return empty result */ + if (router->master_state > BLRM_UNCONFIGURED) + { + return blr_slave_send_slave_status(router, slave); + } + else + { + return blr_slave_send_ok(router, slave); + } + } + else if (strcasecmp(word, "HOSTS") == 0) + { + free(query_text); + /* if state is BLRM_UNCONFIGURED return empty result */ + if (router->master_state > BLRM_UNCONFIGURED) + { + return blr_slave_send_slave_hosts(router, slave); + } + else + { + return blr_slave_send_ok(router, slave); + } + } + } + else if (strcasecmp(word, "STATUS") == 0) + { + int rc = blr_slave_handle_status_variables(router, slave, brkb); - rc = blr_handle_change_master(router, brkb, error_string); + /* if no var found, send empty result set */ + if (rc == 0) + { + blr_slave_send_ok(router, slave); + } - free(query_text); + if (rc >= 0) + { + free(query_text); - if (rc < 0) { - /* CHANGE MASTER TO has failed */ - blr_slave_send_error_packet(slave, error_string, (unsigned int)1234, "42000"); - blr_master_free_config(current_master); + return 1; + } + else + { + MXS_ERROR("%s: Expected LIKE clause in SHOW STATUS.", router->service->name); + } + } + } + else if (strcasecmp(query_text, "SET") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete set command.", router->service->name); + } + else if (strcasecmp(word, "@master_heartbeat_period") == 0) + { + int slave_heartbeat; + int v_len = 0; + word = strtok_r(NULL, sep, &brkb); + if (word) + { + char *new_val; + v_len = strlen(word); + if (v_len > 6) + { + new_val = strndup(word, v_len - 6); + slave->heartbeat = atoi(new_val) / 1000; + } + else + { + new_val = strndup(word, v_len); + slave->heartbeat = atoi(new_val) / 1000000; + } - return 1; - } else { - int ret; - char error[BINLOG_ERROR_MSG_LEN + 1]; + free(new_val); + } + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.heartbeat); + } + else if (strcasecmp(word, "@mariadb_slave_capability") == 0) + { + /* mariadb10 compatibility is set for the slave */ + slave->mariadb10_compat = true; - /* Write/Update master config into master.ini file */ - ret = blr_file_write_master_config(router, error); + free(query_text); + if (router->mariadb10_compat) + { + return blr_slave_replay(router, slave, router->saved_master.mariadb10); + } + else + { + return blr_slave_send_ok(router, slave); + } + } + else if (strcasecmp(word, "@master_binlog_checksum") == 0) + { + word = strtok_r(NULL, sep, &brkb); + if (word && (strcasecmp(word, "'none'") == 0)) + { + slave->nocrc = 1; + } + else if (word && (strcasecmp(word, "@@global.binlog_checksum") == 0)) + { + slave->nocrc = !router->master_chksum; + } + else + { + slave->nocrc = 0; + } - if (ret) { - /* file operation failure: restore config */ - spinlock_acquire(&router->lock); + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.chksum1); + } + else if (strcasecmp(word, "@slave_uuid") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) != NULL) + { + int len = strlen(word); + char *word_ptr = word; + if (len) + { + if (word[len - 1] == '\'') + { + word[len - 1] = '\0'; + } + if (word[0] == '\'') + { + word[0] = '\0'; + word_ptr++; + } + } + slave->uuid = strdup(word_ptr); + } + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.setslaveuuid); + } + else if (strcasecmp(word, "NAMES") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Truncated SET NAMES command.", router->service->name); + } + else if (strcasecmp(word, "latin1") == 0) + { + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.setnames); + } + else if (strcasecmp(word, "utf8") == 0) + { + free(query_text); + return blr_slave_replay(router, slave, router->saved_master.utf8); + } + } + } /* RESET current configured master */ + else if (strcasecmp(query_text, "RESET") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete RESET command.", router->service->name); + } + else if (strcasecmp(word, "SLAVE") == 0) + { + free(query_text); - blr_master_apply_config(router, current_master); - blr_master_free_config(current_master); + if (router->master_state == BLRM_SLAVE_STOPPED) + { + char path[PATH_MAX + 1] = ""; + char error_string[BINLOG_ERROR_MSG_LEN + 1] = ""; + MASTER_SERVER_CFG *current_master = NULL; + int removed_cfg = 0; - spinlock_release(&router->lock); + /* save current replication parameters */ + current_master = (MASTER_SERVER_CFG *)calloc(1, sizeof(MASTER_SERVER_CFG)); - snprintf(error_string, BINLOG_ERROR_MSG_LEN, "Error writing into %s/master.ini: %s", router->binlogdir, error); - MXS_ERROR("%s: %s", - router->service->name, error_string); + if (!current_master) + { + snprintf(error_string, + BINLOG_ERROR_MSG_LEN, "error allocating memory for blr_master_get_config"); + MXS_ERROR("%s: %s", router->service->name, error_string); + blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); - blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); + return 1; + } - return 1; - } + /* get current data */ + blr_master_get_config(router, current_master); - /** - * check if router is BLRM_UNCONFIGURED - * and change state to BLRM_SLAVE_STOPPED - */ - if (rc == 1 || router->master_state == BLRM_UNCONFIGURED) { - spinlock_acquire(&router->lock); + MXS_NOTICE("%s: 'RESET SLAVE executed'. Previous state MASTER_HOST='%s', " + "MASTER_PORT=%i, MASTER_LOG_FILE='%s', MASTER_LOG_POS=%lu, " + "MASTER_USER='%s'", + router->service->name, + current_master->host, + current_master->port, + current_master->logfile, + current_master->pos, + current_master->user); - router->master_state = BLRM_SLAVE_STOPPED; + /* remove master.ini */ + strncpy(path, router->binlogdir, PATH_MAX); - spinlock_release(&router->lock); - } + strncat(path, "/master.ini", PATH_MAX); - if (!router->trx_safe) - blr_master_free_config(current_master); + /* remove master.ini */ + removed_cfg = unlink(path); - if (router->trx_safe && router->pending_transaction) { - if (strcmp(router->binlog_name, router->prevbinlog) != 0) - { - char message[BINLOG_ERROR_MSG_LEN+1] = ""; - snprintf(message, BINLOG_ERROR_MSG_LEN, "1105:Partial transaction in file %s starting at pos %lu, ending at pos %lu will be lost with next START SLAVE command", current_master->logfile, current_master->safe_pos, current_master->pos); - blr_master_free_config(current_master); + if (removed_cfg == -1) + { + char err_msg[STRERROR_BUFLEN]; + snprintf(error_string, BINLOG_ERROR_MSG_LEN, + "Error removing %s, %s, errno %u", path, + strerror_r(errno, err_msg, sizeof(err_msg)), errno); + MXS_ERROR("%s: %s", router->service->name, error_string); + } - return blr_slave_send_warning_message(router, slave, message); - } else { - blr_master_free_config(current_master); - return blr_slave_send_ok(router, slave); - } + spinlock_acquire(&router->lock); - } else { - return blr_slave_send_ok(router, slave); - } - } - } - } - } - else if (strcasecmp(query_text, "DISCONNECT") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Incomplete DISCONNECT command.", - router->service->name); - } - else if (strcasecmp(word, "ALL") == 0) - { - free(query_text); - return blr_slave_disconnect_all(router, slave); - } - else if (strcasecmp(word, "SERVER") == 0) - { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) - { - MXS_ERROR("%s: Expected DISCONNECT SERVER $server_id", - router->service->name); - } else { - int serverid = atoi(word); - free(query_text); - return blr_slave_disconnect_server(router, slave, serverid); - } - } - } + router->master_state = BLRM_UNCONFIGURED; + blr_master_set_empty_config(router); + blr_master_free_config(current_master); - free(query_text); + spinlock_release(&router->lock); - query_text = strndup(qtext, query_len); - MXS_ERROR("Unexpected query from '%s'@'%s': %s", slave->dcb->user, slave->dcb->remote, query_text); - free(query_text); - blr_slave_send_error(router, slave, "You have an error in your SQL syntax; Check the syntax the MaxScale binlog router accepts."); - return 1; + if (removed_cfg == -1) + { + blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); + return 1; + } + else + { + return blr_slave_send_ok(router, slave); + } + } + else + { + if (router->master_state == BLRM_UNCONFIGURED) + { + blr_slave_send_ok(router, slave); + } + else + { + blr_slave_send_error_packet(slave, + "This operation cannot be performed " + "with a running slave; run STOP SLAVE first", + (unsigned int)1198, NULL); + } + return 1; + } + } + } + /* start replication from the current configured master */ + else if (strcasecmp(query_text, "START") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete START command.", + router->service->name); + } + else if (strcasecmp(word, "SLAVE") == 0) + { + free(query_text); + return blr_start_slave(router, slave); + } + } + /* stop replication from the current master*/ + else if (strcasecmp(query_text, "STOP") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete STOP command.", router->service->name); + } + else if (strcasecmp(word, "SLAVE") == 0) + { + free(query_text); + return blr_stop_slave(router, slave); + } + } + /* Change the server to replicate from */ + else if (strcasecmp(query_text, "CHANGE") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete CHANGE command.", router->service->name); + } + else if (strcasecmp(word, "MASTER") == 0) + { + if (router->master_state != BLRM_SLAVE_STOPPED && router->master_state != BLRM_UNCONFIGURED) + { + free(query_text); + blr_slave_send_error_packet(slave, "Cannot change master with a running slave; " + "run STOP SLAVE first", + (unsigned int)1198, NULL); + return 1; + } + else + { + int rc; + char error_string[BINLOG_ERROR_MSG_LEN + 1] = ""; + MASTER_SERVER_CFG *current_master = NULL; + + current_master = (MASTER_SERVER_CFG *)calloc(1, sizeof(MASTER_SERVER_CFG)); + + if (!current_master) + { + free(query_text); + strcpy(error_string, "Error allocating memory for blr_master_get_config"); + MXS_ERROR("%s: %s", router->service->name, error_string); + + blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); + + return 1; + } + + blr_master_get_config(router, current_master); + + rc = blr_handle_change_master(router, brkb, error_string); + + free(query_text); + + if (rc < 0) + { + /* CHANGE MASTER TO has failed */ + blr_slave_send_error_packet(slave, error_string, (unsigned int)1234, "42000"); + blr_master_free_config(current_master); + + return 1; + } + else + { + int ret; + char error[BINLOG_ERROR_MSG_LEN + 1]; + + /* Write/Update master config into master.ini file */ + ret = blr_file_write_master_config(router, error); + + if (ret) + { + /* file operation failure: restore config */ + spinlock_acquire(&router->lock); + + blr_master_apply_config(router, current_master); + blr_master_free_config(current_master); + + spinlock_release(&router->lock); + + snprintf(error_string, BINLOG_ERROR_MSG_LEN, + "Error writing into %s/master.ini: %s", router->binlogdir, + error); + MXS_ERROR("%s: %s", + router->service->name, error_string); + + blr_slave_send_error_packet(slave, error_string, (unsigned int)1201, NULL); + + return 1; + } + + /** + * check if router is BLRM_UNCONFIGURED + * and change state to BLRM_SLAVE_STOPPED + */ + if (rc == 1 || router->master_state == BLRM_UNCONFIGURED) + { + spinlock_acquire(&router->lock); + + router->master_state = BLRM_SLAVE_STOPPED; + + spinlock_release(&router->lock); + } + + if (!router->trx_safe) + { + blr_master_free_config(current_master); + } + + if (router->trx_safe && router->pending_transaction) + { + if (strcmp(router->binlog_name, router->prevbinlog) != 0) + { + char message[BINLOG_ERROR_MSG_LEN + 1] = ""; + snprintf(message, BINLOG_ERROR_MSG_LEN, + "1105:Partial transaction in file %s starting at pos %lu, " + "ending at pos %lu will be lost with next START SLAVE command", + current_master->logfile, current_master->safe_pos, current_master->pos); + blr_master_free_config(current_master); + + return blr_slave_send_warning_message(router, slave, message); + } + else + { + blr_master_free_config(current_master); + return blr_slave_send_ok(router, slave); + } + + } + else + { + return blr_slave_send_ok(router, slave); + } + } + } + } + } + else if (strcasecmp(query_text, "DISCONNECT") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Incomplete DISCONNECT command.", router->service->name); + } + else if (strcasecmp(word, "ALL") == 0) + { + free(query_text); + return blr_slave_disconnect_all(router, slave); + } + else if (strcasecmp(word, "SERVER") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Expected DISCONNECT SERVER $server_id", + router->service->name); + } + else + { + int serverid = atoi(word); + free(query_text); + return blr_slave_disconnect_server(router, slave, serverid); + } + } + } + + free(query_text); + + query_text = strndup(qtext, query_len); + MXS_ERROR("Unexpected query from '%s'@'%s': %s", slave->dcb->user, slave->dcb->remote, query_text); + free(query_text); + blr_slave_send_error(router, slave, + "You have an error in your SQL syntax; Check the syntax " + "the MaxScale binlog router accepts."); + return 1; } @@ -954,59 +1085,65 @@ extern char *strcasestr(); * is merely a copy of a previous message we received from the master when we * registered as a slave. Hence we just replay this saved reply. * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @param master The saved master response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @param master The saved master response + * @return Non-zero if data was sent */ static int blr_slave_replay(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *master) { -GWBUF *clone; + GWBUF *clone; - if (router->master_state == BLRM_UNCONFIGURED) - return blr_slave_send_ok(router, slave); + if (router->master_state == BLRM_UNCONFIGURED) + { + return blr_slave_send_ok(router, slave); + } - if (!master) - return 0; + if (!master) + { + return 0; + } - if ((clone = gwbuf_clone(master)) != NULL) - { - return slave->dcb->func.write(slave->dcb, clone); - } - else - { - MXS_ERROR("Failed to clone server response to send to slave."); - return 0; - } + if ((clone = gwbuf_clone(master)) != NULL) + { + return slave->dcb->func.write(slave->dcb, clone); + } + else + { + MXS_ERROR("Failed to clone server response to send to slave."); + return 0; + } } /** * Construct an error response * - * @param router The router instance - * @param slave The slave server instance - * @param msg The error message to send + * @param router The router instance + * @param slave The slave server instance + * @param msg The error message to send */ static void blr_slave_send_error(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *msg) { -GWBUF *pkt; -unsigned char *data; -int len; + GWBUF *pkt; + unsigned char *data; + int len; - if ((pkt = gwbuf_alloc(strlen(msg) + 13)) == NULL) - return; - data = GWBUF_DATA(pkt); - len = strlen(msg) + 9; - encode_value(&data[0], len, 24); // Payload length - data[3] = 1; // Sequence id - // Payload - data[4] = 0xff; // Error indicator - encode_value(&data[5], 1064, 16);// Error Code - strncpy((char *)&data[7], "#42000", 6); - memcpy(&data[13], msg, strlen(msg)); // Error Message - slave->dcb->func.write(slave->dcb, pkt); + if ((pkt = gwbuf_alloc(strlen(msg) + 13)) == NULL) + { + return; + } + data = GWBUF_DATA(pkt); + len = strlen(msg) + 9; + encode_value(&data[0], len, 24); // Payload length + data[3] = 1; // Sequence id + // Payload + data[4] = 0xff; // Error indicator + encode_value(&data[5], 1064, 16);// Error Code + strncpy((char *)&data[7], "#42000", 6); + memcpy(&data[13], msg, strlen(msg)); // Error Message + slave->dcb->func.write(slave->dcb, pkt); } /* @@ -1015,11 +1152,12 @@ int len; * a SELECT UNIX_TIMESTAMP() statement and the EOF packet that marks the end * of transmission of the result set. */ -static uint8_t timestamp_def[] = { - 0x01, 0x00, 0x00, 0x01, 0x01, 0x26, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, - 0x10, 0x55, 0x4e, 0x49, 0x58, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x28, - 0x29, 0x00, 0x0c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x81, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x02, 0x00 +static uint8_t timestamp_def[] = +{ + 0x01, 0x00, 0x00, 0x01, 0x01, 0x26, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, + 0x10, 0x55, 0x4e, 0x49, 0x58, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x28, + 0x29, 0x00, 0x0c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x81, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x02, 0x00 }; static uint8_t timestamp_eof[] = { 0x05, 0x00, 0x00, 0x05, 0xfe, 0x00, 0x00, 0x02, 0x00 }; @@ -1031,589 +1169,635 @@ static uint8_t timestamp_eof[] = { 0x05, 0x00, 0x00, 0x05, 0xfe, 0x00, 0x00, 0x0 * client and then we add a dynamic part that will insert the current timestamp data. * Finally we send a preprepaed EOF packet to end the response stream. * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_timestamp(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -GWBUF *pkt; -char timestamp[20]; -uint8_t *ptr; -int len, ts_len; + GWBUF *pkt; + char timestamp[20]; + uint8_t *ptr; + int len, ts_len; - sprintf(timestamp, "%ld", time(0)); - ts_len = strlen(timestamp); - len = sizeof(timestamp_def) + sizeof(timestamp_eof) + 5 + ts_len; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - memcpy(ptr, timestamp_def, sizeof(timestamp_def)); // Fixed preamble - ptr += sizeof(timestamp_def); - encode_value(ptr, ts_len + 1, 24); // Add length of data packet - ptr += 3; - *ptr++ = 0x04; // Sequence number in response - *ptr++ = ts_len; // Length of result string - strncpy((char *)ptr, timestamp, ts_len); // Result string - ptr += ts_len; - memcpy(ptr, timestamp_eof, sizeof(timestamp_eof)); // EOF packet to terminate result - return slave->dcb->func.write(slave->dcb, pkt); + sprintf(timestamp, "%ld", time(0)); + ts_len = strlen(timestamp); + len = sizeof(timestamp_def) + sizeof(timestamp_eof) + 5 + ts_len; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + memcpy(ptr, timestamp_def, sizeof(timestamp_def)); // Fixed preamble + ptr += sizeof(timestamp_def); + encode_value(ptr, ts_len + 1, 24); // Add length of data packet + ptr += 3; + *ptr++ = 0x04; // Sequence number in response + *ptr++ = ts_len; // Length of result string + strncpy((char *)ptr, timestamp, ts_len); // Result string + ptr += ts_len; + memcpy(ptr, timestamp_eof, sizeof(timestamp_eof)); // EOF packet to terminate result + return slave->dcb->func.write(slave->dcb, pkt); } /** * Send a response the the SQL command SELECT @@MAXSCALE_VERSION * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_maxscale_version(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -GWBUF *pkt; -char version[80] = ""; -uint8_t *ptr; -int len, vers_len; + GWBUF *pkt; + char version[80] = ""; + uint8_t *ptr; + int len, vers_len; - sprintf(version, "%s", MAXSCALE_VERSION); - vers_len = strlen(version); - blr_slave_send_fieldcount(router, slave, 1); - blr_slave_send_columndef(router, slave, "MAXSCALE_VERSION", BLR_TYPE_STRING, vers_len, 2); - blr_slave_send_eof(router, slave, 3); + sprintf(version, "%s", MAXSCALE_VERSION); + vers_len = strlen(version); + blr_slave_send_fieldcount(router, slave, 1); + blr_slave_send_columndef(router, slave, "MAXSCALE_VERSION", BLR_TYPE_STRING, vers_len, 2); + blr_slave_send_eof(router, slave, 3); - len = 5 + vers_len; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, vers_len + 1, 24); // Add length of data packet - ptr += 3; - *ptr++ = 0x04; // Sequence number in response - *ptr++ = vers_len; // Length of result string - strncpy((char *)ptr, version, vers_len); // Result string - ptr += vers_len; - slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, 5); + len = 5 + vers_len; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, vers_len + 1, 24); // Add length of data packet + ptr += 3; + *ptr++ = 0x04; // Sequence number in response + *ptr++ = vers_len; // Length of result string + strncpy((char *)ptr, version, vers_len); // Result string + ptr += vers_len; + slave->dcb->func.write(slave->dcb, pkt); + return blr_slave_send_eof(router, slave, 5); } /** * Send a response the the SQL command SELECT @@server_id * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_server_id(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -GWBUF *pkt; -char server_id[40]; -uint8_t *ptr; -int len, id_len; + GWBUF *pkt; + char server_id[40]; + uint8_t *ptr; + int len, id_len; - sprintf(server_id, "%d", router->masterid); - id_len = strlen(server_id); - blr_slave_send_fieldcount(router, slave, 1); - blr_slave_send_columndef(router, slave, "SERVER_ID", BLR_TYPE_INT, id_len, 2); - blr_slave_send_eof(router, slave, 3); + sprintf(server_id, "%d", router->masterid); + id_len = strlen(server_id); + blr_slave_send_fieldcount(router, slave, 1); + blr_slave_send_columndef(router, slave, "SERVER_ID", BLR_TYPE_INT, id_len, 2); + blr_slave_send_eof(router, slave, 3); - len = 5 + id_len; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, id_len + 1, 24); // Add length of data packet - ptr += 3; - *ptr++ = 0x04; // Sequence number in response - *ptr++ = id_len; // Length of result string - strncpy((char *)ptr, server_id, id_len); // Result string - ptr += id_len; - slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, 5); + len = 5 + id_len; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, id_len + 1, 24); // Add length of data packet + ptr += 3; + *ptr++ = 0x04; // Sequence number in response + *ptr++ = id_len; // Length of result string + strncpy((char *)ptr, server_id, id_len); // Result string + ptr += id_len; + slave->dcb->func.write(slave->dcb, pkt); + return blr_slave_send_eof(router, slave, 5); } /** * Send the response to the SQL command "SHOW VARIABLES LIKE 'MAXSCALE%' * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_maxscale_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -GWBUF *pkt; -char name[40]; -char version[80]; -uint8_t *ptr; -int len, vers_len, seqno = 2; + GWBUF *pkt; + char name[40]; + char version[80]; + uint8_t *ptr; + int len, vers_len, seqno = 2; - blr_slave_send_fieldcount(router, slave, 2); - blr_slave_send_columndef(router, slave, "Variable_name", BLR_TYPE_STRING, 40, seqno++); - blr_slave_send_columndef(router, slave, "Value", BLR_TYPE_STRING, 40, seqno++); - blr_slave_send_eof(router, slave, seqno++); + blr_slave_send_fieldcount(router, slave, 2); + blr_slave_send_columndef(router, slave, "Variable_name", BLR_TYPE_STRING, 40, seqno++); + blr_slave_send_columndef(router, slave, "Value", BLR_TYPE_STRING, 40, seqno++); + blr_slave_send_eof(router, slave, seqno++); - sprintf(version, "%s", MAXSCALE_VERSION); - vers_len = strlen(version); - strcpy(name, "MAXSCALE_VERSION"); - len = 5 + vers_len + strlen(name) + 1; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, vers_len + 2 + strlen(name), 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno++; // Sequence number in response - *ptr++ = strlen(name); // Length of result string - strncpy((char *)ptr, name, strlen(name)); // Result string - ptr += strlen(name); - *ptr++ = vers_len; // Length of result string - strncpy((char *)ptr, version, vers_len); // Result string - ptr += vers_len; - slave->dcb->func.write(slave->dcb, pkt); + sprintf(version, "%s", MAXSCALE_VERSION); + vers_len = strlen(version); + strcpy(name, "MAXSCALE_VERSION"); + len = 5 + vers_len + strlen(name) + 1; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, vers_len + 2 + strlen(name), 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno++; // Sequence number in response + *ptr++ = strlen(name); // Length of result string + strncpy((char *)ptr, name, strlen(name)); // Result string + ptr += strlen(name); + *ptr++ = vers_len; // Length of result string + strncpy((char *)ptr, version, vers_len); // Result string + ptr += vers_len; + slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, seqno++); + return blr_slave_send_eof(router, slave, seqno++); } /** * Send the response to the SQL command "SHOW MASTER STATUS" * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_master_status(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -GWBUF *pkt; -char file[40]; -char position[40]; -uint8_t *ptr; -int len, file_len; + GWBUF *pkt; + char file[40]; + char position[40]; + uint8_t *ptr; + int len, file_len; - blr_slave_send_fieldcount(router, slave, 5); - blr_slave_send_columndef(router, slave, "File", BLR_TYPE_STRING, 40, 2); - blr_slave_send_columndef(router, slave, "Position", BLR_TYPE_STRING, 40, 3); - blr_slave_send_columndef(router, slave, "Binlog_Do_DB", BLR_TYPE_STRING, 40, 4); - blr_slave_send_columndef(router, slave, "Binlog_Ignore_DB", BLR_TYPE_STRING, 40, 5); - blr_slave_send_columndef(router, slave, "Execute_Gtid_Set", BLR_TYPE_STRING, 40, 6); - blr_slave_send_eof(router, slave, 7); + blr_slave_send_fieldcount(router, slave, 5); + blr_slave_send_columndef(router, slave, "File", BLR_TYPE_STRING, 40, 2); + blr_slave_send_columndef(router, slave, "Position", BLR_TYPE_STRING, 40, 3); + blr_slave_send_columndef(router, slave, "Binlog_Do_DB", BLR_TYPE_STRING, 40, 4); + blr_slave_send_columndef(router, slave, "Binlog_Ignore_DB", BLR_TYPE_STRING, 40, 5); + blr_slave_send_columndef(router, slave, "Execute_Gtid_Set", BLR_TYPE_STRING, 40, 6); + blr_slave_send_eof(router, slave, 7); - sprintf(file, "%s", router->binlog_name); - file_len = strlen(file); + sprintf(file, "%s", router->binlog_name); + file_len = strlen(file); - sprintf(position, "%lu", router->binlog_position); + sprintf(position, "%lu", router->binlog_position); - len = 5 + file_len + strlen(position) + 1 + 3; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, len - 4, 24); // Add length of data packet - ptr += 3; - *ptr++ = 0x08; // Sequence number in response - *ptr++ = strlen(file); // Length of result string - strncpy((char *)ptr, file, strlen(file)); // Result string - ptr += strlen(file); - *ptr++ = strlen(position); // Length of result string - strncpy((char *)ptr, position, strlen(position)); // Result string - ptr += strlen(position); - *ptr++ = 0; // Send 3 empty values - *ptr++ = 0; - *ptr++ = 0; - slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, 9); + len = 5 + file_len + strlen(position) + 1 + 3; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, len - 4, 24); // Add length of data packet + ptr += 3; + *ptr++ = 0x08; // Sequence number in response + *ptr++ = strlen(file); // Length of result string + strncpy((char *)ptr, file, strlen(file)); // Result string + ptr += strlen(file); + *ptr++ = strlen(position); // Length of result string + strncpy((char *)ptr, position, strlen(position)); // Result string + ptr += strlen(position); + *ptr++ = 0; // Send 3 empty values + *ptr++ = 0; + *ptr++ = 0; + slave->dcb->func.write(slave->dcb, pkt); + return blr_slave_send_eof(router, slave, 9); } /* * Columns to send for a "SHOW SLAVE STATUS" command */ -static char *slave_status_columns[] = { - "Slave_IO_State", "Master_Host", "Master_User", "Master_Port", "Connect_Retry", - "Master_Log_File", "Read_Master_Log_Pos", "Relay_Log_File", "Relay_Log_Pos", - "Relay_Master_Log_File", "Slave_IO_Running", "Slave_SQL_Running", "Replicate_Do_DB", - "Replicate_Ignore_DB", "Replicate_Do_Table", - "Replicate_Ignore_Table", "Replicate_Wild_Do_Table", "Replicate_Wild_Ignore_Table", - "Last_Errno", "Last_Error", "Skip_Counter", "Exec_Master_Log_Pos", "Relay_Log_Space", - "Until_Condition", "Until_Log_File", "Until_Log_Pos", "Master_SSL_Allowed", - "Master_SSL_CA_File", "Master_SSL_CA_Path", "Master_SSL_Cert", "Master_SSL_Cipher", - "Master_SSL_Key", "Seconds_Behind_Master", - "Master_SSL_Verify_Server_Cert", "Last_IO_Errno", "Last_IO_Error", "Last_SQL_Errno", - "Last_SQL_Error", "Replicate_Ignore_Server_Ids", "Master_Server_Id", "Master_UUID", - "Master_Info_File", "SQL_Delay", "SQL_Remaining_Delay", "Slave_SQL_Running_State", - "Master_Retry_Count", "Master_Bind", "Last_IO_Error_TimeStamp", - "Last_SQL_Error_Timestamp", "Master_SSL_Crl", "Master_SSL_Crlpath", - "Retrieved_Gtid_Set", "Executed_Gtid_Set", "Auto_Position", NULL +static char *slave_status_columns[] = +{ + "Slave_IO_State", "Master_Host", "Master_User", "Master_Port", "Connect_Retry", + "Master_Log_File", "Read_Master_Log_Pos", "Relay_Log_File", "Relay_Log_Pos", + "Relay_Master_Log_File", "Slave_IO_Running", "Slave_SQL_Running", "Replicate_Do_DB", + "Replicate_Ignore_DB", "Replicate_Do_Table", + "Replicate_Ignore_Table", "Replicate_Wild_Do_Table", "Replicate_Wild_Ignore_Table", + "Last_Errno", "Last_Error", "Skip_Counter", "Exec_Master_Log_Pos", "Relay_Log_Space", + "Until_Condition", "Until_Log_File", "Until_Log_Pos", "Master_SSL_Allowed", + "Master_SSL_CA_File", "Master_SSL_CA_Path", "Master_SSL_Cert", "Master_SSL_Cipher", + "Master_SSL_Key", "Seconds_Behind_Master", + "Master_SSL_Verify_Server_Cert", "Last_IO_Errno", "Last_IO_Error", "Last_SQL_Errno", + "Last_SQL_Error", "Replicate_Ignore_Server_Ids", "Master_Server_Id", "Master_UUID", + "Master_Info_File", "SQL_Delay", "SQL_Remaining_Delay", "Slave_SQL_Running_State", + "Master_Retry_Count", "Master_Bind", "Last_IO_Error_TimeStamp", + "Last_SQL_Error_Timestamp", "Master_SSL_Crl", "Master_SSL_Crlpath", + "Retrieved_Gtid_Set", "Executed_Gtid_Set", "Auto_Position", NULL }; /** * Send the response to the SQL command "SHOW SLAVE STATUS" * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_slave_status(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -GWBUF *pkt; -char column[42]; -uint8_t *ptr; -int len, actual_len, col_len, seqno, ncols, i; -char *dyn_column=NULL; + GWBUF *pkt; + char column[42]; + uint8_t *ptr; + int len, actual_len, col_len, seqno, ncols, i; + char *dyn_column = NULL; - /* Count the columns */ - for (ncols = 0; slave_status_columns[ncols]; ncols++); + /* Count the columns */ + for (ncols = 0; slave_status_columns[ncols]; ncols++); - blr_slave_send_fieldcount(router, slave, ncols); - seqno = 2; - for (i = 0; slave_status_columns[i]; i++) - blr_slave_send_columndef(router, slave, slave_status_columns[i], BLR_TYPE_STRING, 40, seqno++); - blr_slave_send_eof(router, slave, seqno++); + blr_slave_send_fieldcount(router, slave, ncols); + seqno = 2; + for (i = 0; slave_status_columns[i]; i++) + { + blr_slave_send_columndef(router, slave, slave_status_columns[i], BLR_TYPE_STRING, 40, seqno++); + } + blr_slave_send_eof(router, slave, seqno++); - len = 5 + (ncols * 41) + 250; // Max length + 250 bytes error message + len = 5 + (ncols * 41) + 250; // Max length + 250 bytes error message - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, len - 4, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno++; // Sequence number in response + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, len - 4, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno++; // Sequence number in response - sprintf(column, "%s", blrm_states[router->master_state]); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%s", blrm_states[router->master_state]); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%s", router->service->dbref->server->name ? router->service->dbref->server->name : ""); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%s", router->service->dbref->server->name ? router->service->dbref->server->name : ""); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%s", router->user ? router->user : ""); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%s", router->user ? router->user : ""); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%d", router->service->dbref->server->port); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%d", router->service->dbref->server->port); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%d", 60); // Connect retry - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%d", 60); // Connect retry + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%s", router->binlog_name); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%s", router->binlog_name); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* if router->trx_safe report current_pos*/ - if (router->trx_safe) - sprintf(column, "%lu", router->current_pos); - else - sprintf(column, "%lu", router->binlog_position); + /* if router->trx_safe report current_pos*/ + if (router->trx_safe) + { + sprintf(column, "%lu", router->current_pos); + } + else + { + sprintf(column, "%lu", router->binlog_position); + } - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* We have no relay log, we relay the binlog, so we will send the same data */ - sprintf(column, "%s", router->binlog_name); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* We have no relay log, we relay the binlog, so we will send the same data */ + sprintf(column, "%s", router->binlog_name); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%ld", router->binlog_position); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%ld", router->binlog_position); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* We have no relay log, we relay the binlog, so we will send the same data */ - sprintf(column, "%s", router->binlog_name); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* We have no relay log, we relay the binlog, so we will send the same data */ + sprintf(column, "%s", router->binlog_name); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - if (router->master_state != BLRM_SLAVE_STOPPED) { - if (router->master_state < BLRM_BINLOGDUMP) - strcpy(column, "Connecting"); - else - strcpy(column, "Yes"); - } else { - strcpy(column, "No"); - } - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + if (router->master_state != BLRM_SLAVE_STOPPED) + { + if (router->master_state < BLRM_BINLOGDUMP) + { + strcpy(column, "Connecting"); + } + else + { + strcpy(column, "Yes"); + } + } + else + { + strcpy(column, "No"); + } + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - if (router->master_state != BLRM_SLAVE_STOPPED) - strcpy(column, "Yes"); - else - strcpy(column, "No"); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + if (router->master_state != BLRM_SLAVE_STOPPED) + { + strcpy(column, "Yes"); + } + else + { + strcpy(column, "No"); + } + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0; // Send 6 empty values - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; + *ptr++ = 0; // Send 6 empty values + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; - /* Last error information */ - sprintf(column, "%lu", router->m_errno); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Last error information */ + sprintf(column, "%lu", router->m_errno); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* Last error message */ - if (router->m_errmsg == NULL) { - *ptr++ = 0; - } else { - dyn_column = (char*)router->m_errmsg; - col_len = strlen(dyn_column); - if (col_len > 250) - col_len = 250; - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, dyn_column, col_len); // Result string - ptr += col_len; - } + /* Last error message */ + if (router->m_errmsg == NULL) + { + *ptr++ = 0; + } + else + { + dyn_column = (char*)router->m_errmsg; + col_len = strlen(dyn_column); + if (col_len > 250) + { + col_len = 250; + } + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, dyn_column, col_len); // Result string + ptr += col_len; + } - /* Skip_Counter */ - sprintf(column, "%d", 0); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Skip_Counter */ + sprintf(column, "%d", 0); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%ld", router->binlog_position); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%ld", router->binlog_position); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%ld", router->binlog_position); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%ld", router->binlog_position); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - strcpy(column, "None"); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + strcpy(column, "None"); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0; + *ptr++ = 0; - /* Until_Log_Pos */ - sprintf(column, "%d", 0); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Until_Log_Pos */ + sprintf(column, "%d", 0); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* Master_SSL_Allowed */ - strcpy(column, "No"); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Master_SSL_Allowed */ + strcpy(column, "No"); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0; // Empty SSL columns - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; + *ptr++ = 0; // Empty SSL columns + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; - /* Seconds_Behind_Master */ - sprintf(column, "%d", 0); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Seconds_Behind_Master */ + sprintf(column, "%d", 0); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* Master_SSL_Verify_Server_Cert */ - strcpy(column, "No"); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Master_SSL_Verify_Server_Cert */ + strcpy(column, "No"); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* Last_IO_Error */ - sprintf(column, "%d", 0); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Last_IO_Error */ + sprintf(column, "%d", 0); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0; + *ptr++ = 0; - /* Last_SQL_Error */ - sprintf(column, "%d", 0); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Last_SQL_Error */ + sprintf(column, "%d", 0); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0; - *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; - /* Master_Server_Id */ - sprintf(column, "%d", router->masterid); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Master_Server_Id */ + sprintf(column, "%d", router->masterid); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - sprintf(column, "%s", router->master_uuid ? - router->master_uuid : router->uuid); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + sprintf(column, "%s", router->master_uuid ? + router->master_uuid : router->uuid); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0; + *ptr++ = 0; - /* SQL_Delay*/ - sprintf(column, "%d", 0); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* SQL_Delay*/ + sprintf(column, "%d", 0); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0xfb; // NULL value + *ptr++ = 0xfb; // NULL value - /* Slave_Running_State */ - if (router->master_state == BLRM_SLAVE_STOPPED) - strcpy(column, "Slave stopped"); - else if (!router->m_errno) - strcpy(column, "Slave running"); - else { - if (router->master_state < BLRM_BINLOGDUMP) - strcpy(column, "Registering"); - else - strcpy(column, "Error"); - } - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Slave_Running_State */ + if (router->master_state == BLRM_SLAVE_STOPPED) + { + strcpy(column, "Slave stopped"); + } + else if (!router->m_errno) + { + strcpy(column, "Slave running"); + } + else + { + if (router->master_state < BLRM_BINLOGDUMP) + { + strcpy(column, "Registering"); + } + else + { + strcpy(column, "Error"); + } + } + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - /* Master_Retry_Count */ - sprintf(column, "%d", 1000); - col_len = strlen(column); - *ptr++ = col_len; // Length of result string - strncpy((char *)ptr, column, col_len); // Result string - ptr += col_len; + /* Master_Retry_Count */ + sprintf(column, "%d", 1000); + col_len = strlen(column); + *ptr++ = col_len; // Length of result string + strncpy((char *)ptr, column, col_len); // Result string + ptr += col_len; - *ptr++ = 0; // Send 5 empty values - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; + *ptr++ = 0; // Send 5 empty values + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; - // No GTID support send empty values - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 0; + // No GTID support send empty values + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; - actual_len = ptr - (uint8_t *)GWBUF_DATA(pkt); - ptr = GWBUF_DATA(pkt); - encode_value(ptr, actual_len - 4, 24); // Add length of data packet + actual_len = ptr - (uint8_t *)GWBUF_DATA(pkt); + ptr = GWBUF_DATA(pkt); + encode_value(ptr, actual_len - 4, 24); // Add length of data packet - pkt = gwbuf_rtrim(pkt, len - actual_len); // Trim the buffer to the actual size + pkt = gwbuf_rtrim(pkt, len - actual_len); // Trim the buffer to the actual size - slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, seqno++); + slave->dcb->func.write(slave->dcb, pkt); + return blr_slave_send_eof(router, slave, seqno++); } /** * Send the response to the SQL command "SHOW SLAVE HOSTS" * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_slave_hosts(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -GWBUF *pkt; -char server_id[40]; -char host[40]; -char port[40]; -char master_id[40]; -char slave_uuid[40]; -uint8_t *ptr; -int len, seqno; -ROUTER_SLAVE *sptr; + GWBUF *pkt; + char server_id[40]; + char host[40]; + char port[40]; + char master_id[40]; + char slave_uuid[40]; + uint8_t *ptr; + int len, seqno; + ROUTER_SLAVE *sptr; - blr_slave_send_fieldcount(router, slave, 5); - blr_slave_send_columndef(router, slave, "Server_id", BLR_TYPE_STRING, 40, 2); - blr_slave_send_columndef(router, slave, "Host", BLR_TYPE_STRING, 40, 3); - blr_slave_send_columndef(router, slave, "Port", BLR_TYPE_STRING, 40, 4); - blr_slave_send_columndef(router, slave, "Master_id", BLR_TYPE_STRING, 40, 5); - blr_slave_send_columndef(router, slave, "Slave_UUID", BLR_TYPE_STRING, 40, 6); - blr_slave_send_eof(router, slave, 7); + blr_slave_send_fieldcount(router, slave, 5); + blr_slave_send_columndef(router, slave, "Server_id", BLR_TYPE_STRING, 40, 2); + blr_slave_send_columndef(router, slave, "Host", BLR_TYPE_STRING, 40, 3); + blr_slave_send_columndef(router, slave, "Port", BLR_TYPE_STRING, 40, 4); + blr_slave_send_columndef(router, slave, "Master_id", BLR_TYPE_STRING, 40, 5); + blr_slave_send_columndef(router, slave, "Slave_UUID", BLR_TYPE_STRING, 40, 6); + blr_slave_send_eof(router, slave, 7); - seqno = 8; - spinlock_acquire(&router->lock); - sptr = router->slaves; - while (sptr) - { - if (sptr->state == BLRS_DUMPING || sptr->state == BLRS_REGISTERED) - { - sprintf(server_id, "%d", sptr->serverid); - sprintf(host, "%s", sptr->hostname ? sptr->hostname : ""); - sprintf(port, "%d", sptr->port); - sprintf(master_id, "%d", router->serverid); - sprintf(slave_uuid, "%s", sptr->uuid ? sptr->uuid : ""); - len = 4 + strlen(server_id) + strlen(host) + strlen(port) - + strlen(master_id) + strlen(slave_uuid) + 5; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, len - 4, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno++; // Sequence number in response - *ptr++ = strlen(server_id); // Length of result string - strncpy((char *)ptr, server_id, strlen(server_id)); // Result string - ptr += strlen(server_id); - *ptr++ = strlen(host); // Length of result string - strncpy((char *)ptr, host, strlen(host)); // Result string - ptr += strlen(host); - *ptr++ = strlen(port); // Length of result string - strncpy((char *)ptr, port, strlen(port)); // Result string - ptr += strlen(port); - *ptr++ = strlen(master_id); // Length of result string - strncpy((char *)ptr, master_id, strlen(master_id)); // Result string - ptr += strlen(master_id); - *ptr++ = strlen(slave_uuid); // Length of result string - strncpy((char *)ptr, slave_uuid, strlen(slave_uuid)); // Result string - ptr += strlen(slave_uuid); - slave->dcb->func.write(slave->dcb, pkt); - } - sptr = sptr->next; - } - spinlock_release(&router->lock); - return blr_slave_send_eof(router, slave, seqno); + seqno = 8; + spinlock_acquire(&router->lock); + sptr = router->slaves; + while (sptr) + { + if (sptr->state == BLRS_DUMPING || sptr->state == BLRS_REGISTERED) + { + sprintf(server_id, "%d", sptr->serverid); + sprintf(host, "%s", sptr->hostname ? sptr->hostname : ""); + sprintf(port, "%d", sptr->port); + sprintf(master_id, "%d", router->serverid); + sprintf(slave_uuid, "%s", sptr->uuid ? sptr->uuid : ""); + len = 4 + strlen(server_id) + strlen(host) + strlen(port) + + strlen(master_id) + strlen(slave_uuid) + 5; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, len - 4, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno++; // Sequence number in response + *ptr++ = strlen(server_id); // Length of result string + strncpy((char *)ptr, server_id, strlen(server_id)); // Result string + ptr += strlen(server_id); + *ptr++ = strlen(host); // Length of result string + strncpy((char *)ptr, host, strlen(host)); // Result string + ptr += strlen(host); + *ptr++ = strlen(port); // Length of result string + strncpy((char *)ptr, port, strlen(port)); // Result string + ptr += strlen(port); + *ptr++ = strlen(master_id); // Length of result string + strncpy((char *)ptr, master_id, strlen(master_id)); // Result string + ptr += strlen(master_id); + *ptr++ = strlen(slave_uuid); // Length of result string + strncpy((char *)ptr, slave_uuid, strlen(slave_uuid)); // Result string + ptr += strlen(slave_uuid); + slave->dcb->func.write(slave->dcb, pkt); + } + sptr = sptr->next; + } + spinlock_release(&router->lock); + return blr_slave_send_eof(router, slave, seqno); } /** @@ -1622,58 +1806,66 @@ ROUTER_SLAVE *sptr; * We store the various bits of information the slave gives us and generate * a reply message: OK packet. * - * @param router The router instance - * @param slave The slave server - * @param queue The BINLOG_DUMP packet - * @return Non-zero if data was sent + * @param router The router instance + * @param slave The slave server + * @param queue The BINLOG_DUMP packet + * @return Non-zero if data was sent */ static int blr_slave_register(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { -GWBUF *resp; -uint8_t *ptr; -int slen; + GWBUF *resp; + uint8_t *ptr; + int slen; - ptr = GWBUF_DATA(queue); - ptr += 4; // Skip length and sequence number - if (*ptr++ != COM_REGISTER_SLAVE) - return 0; - slave->serverid = extract_field(ptr, 32); - ptr += 4; - slen = *ptr++; - if (slen != 0) - { - slave->hostname = strndup((char *)ptr, slen); - ptr += slen; - } - else - slave->hostname = NULL; - slen = *ptr++; - if (slen != 0) - { - ptr += slen; - slave->user = strndup((char *)ptr, slen); - } - else - slave->user = NULL; - slen = *ptr++; - if (slen != 0) - { - slave->passwd = strndup((char *)ptr, slen); - ptr += slen; - } - else - slave->passwd = NULL; - slave->port = extract_field(ptr, 16); - ptr += 2; - slave->rank = extract_field(ptr, 32); + ptr = GWBUF_DATA(queue); + ptr += 4; // Skip length and sequence number + if (*ptr++ != COM_REGISTER_SLAVE) + { + return 0; + } + slave->serverid = extract_field(ptr, 32); + ptr += 4; + slen = *ptr++; + if (slen != 0) + { + slave->hostname = strndup((char *)ptr, slen); + ptr += slen; + } + else + { + slave->hostname = NULL; + } + slen = *ptr++; + if (slen != 0) + { + ptr += slen; + slave->user = strndup((char *)ptr, slen); + } + else + { + slave->user = NULL; + } + slen = *ptr++; + if (slen != 0) + { + slave->passwd = strndup((char *)ptr, slen); + ptr += slen; + } + else + { + slave->passwd = NULL; + } + slave->port = extract_field(ptr, 16); + ptr += 2; + slave->rank = extract_field(ptr, 32); - slave->state = BLRS_REGISTERED; + slave->state = BLRS_REGISTERED; - /* - * Send OK response - */ - return blr_slave_send_ok(router, slave); + /* + * Send OK response + */ + return blr_slave_send_ok(router, slave); } /** @@ -1685,227 +1877,235 @@ int slen; * * Once send MaxScale must continue to send binlog events to the slave. * - * @param router The router instance - * @param slave The slave server - * @param queue The BINLOG_DUMP packet - * @return The number of bytes written to the slave + * @param router The router instance + * @param slave The slave server + * @param queue The BINLOG_DUMP packet + * @return The number of bytes written to the slave */ static int blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { -GWBUF *resp; -uint8_t *ptr; -int len, rval, binlognamelen; -REP_HEADER hdr; -uint32_t chksum; + GWBUF *resp; + uint8_t *ptr; + int len, rval, binlognamelen; + REP_HEADER hdr; + uint32_t chksum; - ptr = GWBUF_DATA(queue); - len = extract_field(ptr, 24); - binlognamelen = len - 11; - if (binlognamelen > BINLOG_FNAMELEN) - { - MXS_ERROR("blr_slave_binlog_dump truncating binlog filename " - "from %d to %d", - binlognamelen, BINLOG_FNAMELEN); - binlognamelen = BINLOG_FNAMELEN; - } - ptr += 4; // Skip length and sequence number - if (*ptr++ != COM_BINLOG_DUMP) - { - MXS_ERROR("blr_slave_binlog_dump expected a COM_BINLOG_DUMP but received %d", - *(ptr-1)); - return 0; - } + ptr = GWBUF_DATA(queue); + len = extract_field(ptr, 24); + binlognamelen = len - 11; + if (binlognamelen > BINLOG_FNAMELEN) + { + MXS_ERROR("blr_slave_binlog_dump truncating binlog filename " + "from %d to %d", + binlognamelen, BINLOG_FNAMELEN); + binlognamelen = BINLOG_FNAMELEN; + } + ptr += 4; // Skip length and sequence number + if (*ptr++ != COM_BINLOG_DUMP) + { + MXS_ERROR("blr_slave_binlog_dump expected a COM_BINLOG_DUMP but received %d", + *(ptr - 1)); + return 0; + } - slave->binlog_pos = extract_field(ptr, 32); - ptr += 4; - ptr += 2; - ptr += 4; - strncpy(slave->binlogfile, (char *)ptr, binlognamelen); - slave->binlogfile[binlognamelen] = 0; + slave->binlog_pos = extract_field(ptr, 32); + ptr += 4; + ptr += 2; + ptr += 4; + strncpy(slave->binlogfile, (char *)ptr, binlognamelen); + slave->binlogfile[binlognamelen] = 0; - if (router->trx_safe) - { - /** - * Check for a pending transaction and possible unsafe position. - * Force slave disconnection if requested position is unsafe. - */ + if (router->trx_safe) + { + /** + * Check for a pending transaction and possible unsafe position. + * Force slave disconnection if requested position is unsafe. + */ - bool force_disconnect = false; + bool force_disconnect = false; - spinlock_acquire(&router->binlog_lock); - if (router->pending_transaction && strcmp(router->binlog_name, slave->binlogfile) == 0 && - (slave->binlog_pos > router->binlog_position)) - { - force_disconnect = true; - } - spinlock_release(&router->binlog_lock); + spinlock_acquire(&router->binlog_lock); + if (router->pending_transaction && strcmp(router->binlog_name, slave->binlogfile) == 0 && + (slave->binlog_pos > router->binlog_position)) + { + force_disconnect = true; + } + spinlock_release(&router->binlog_lock); - if (force_disconnect) - { - MXS_ERROR("%s: Slave %s:%i, server-id %d, binlog '%s', blr_slave_binlog_dump failure: " - "Requested binlog position %lu. Position is unsafe so disconnecting. " - "Latest safe position %lu, end of binlog file %lu", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, - (unsigned long)slave->binlog_pos, - router->binlog_position, - router->current_pos); + if (force_disconnect) + { + MXS_ERROR("%s: Slave %s:%i, server-id %d, binlog '%s', blr_slave_binlog_dump failure: " + "Requested binlog position %lu. Position is unsafe so disconnecting. " + "Latest safe position %lu, end of binlog file %lu", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + (unsigned long)slave->binlog_pos, + router->binlog_position, + router->current_pos); - /* - * Close the slave session and socket - * The slave will try to reconnect - */ - dcb_close(slave->dcb); + /* + * Close the slave session and socket + * The slave will try to reconnect + */ + dcb_close(slave->dcb); - return 1; - } - } + return 1; + } + } - MXS_DEBUG("%s: COM_BINLOG_DUMP: binlog name '%s', length %d, " - "from position %lu.", router->service->name, - slave->binlogfile, binlognamelen, - (unsigned long)slave->binlog_pos); + MXS_DEBUG("%s: COM_BINLOG_DUMP: binlog name '%s', length %d, " + "from position %lu.", router->service->name, + slave->binlogfile, binlognamelen, + (unsigned long)slave->binlog_pos); - slave->seqno = 1; + slave->seqno = 1; - if (slave->nocrc) - len = BINLOG_EVENT_HDR_LEN + 8 + binlognamelen; - else - len = BINLOG_EVENT_HDR_LEN + 8 + 4 + binlognamelen; + if (slave->nocrc) + { + len = BINLOG_EVENT_HDR_LEN + 8 + binlognamelen; + } + else + { + len = BINLOG_EVENT_HDR_LEN + 8 + 4 + binlognamelen; + } - // Build a fake rotate event - resp = gwbuf_alloc(len + 5); - hdr.payload_len = len + 1; - hdr.seqno = slave->seqno++; - hdr.ok = 0; - hdr.timestamp = 0L; - hdr.event_type = ROTATE_EVENT; - hdr.serverid = router->masterid; - hdr.event_size = len; - hdr.next_pos = 0; - hdr.flags = 0x20; - ptr = blr_build_header(resp, &hdr); - encode_value(ptr, slave->binlog_pos, 64); - ptr += 8; - memcpy(ptr, slave->binlogfile, binlognamelen); - ptr += binlognamelen; + // Build a fake rotate event + resp = gwbuf_alloc(len + 5); + hdr.payload_len = len + 1; + hdr.seqno = slave->seqno++; + hdr.ok = 0; + hdr.timestamp = 0L; + hdr.event_type = ROTATE_EVENT; + hdr.serverid = router->masterid; + hdr.event_size = len; + hdr.next_pos = 0; + hdr.flags = 0x20; + ptr = blr_build_header(resp, &hdr); + encode_value(ptr, slave->binlog_pos, 64); + ptr += 8; + memcpy(ptr, slave->binlogfile, binlognamelen); + ptr += binlognamelen; - if (!slave->nocrc) - { - /* - * Now add the CRC to the fake binlog rotate event. - * - * The algorithm is first to compute the checksum of an empty buffer - * and then the checksum of the event portion of the message, ie we do not - * include the length, sequence number and ok byte that makes up the first - * 5 bytes of the message. We also do not include the 4 byte checksum itself. - */ - chksum = crc32(0L, NULL, 0); - chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); - encode_value(ptr, chksum, 32); - } + if (!slave->nocrc) + { + /* + * Now add the CRC to the fake binlog rotate event. + * + * The algorithm is first to compute the checksum of an empty buffer + * and then the checksum of the event portion of the message, ie we do not + * include the length, sequence number and ok byte that makes up the first + * 5 bytes of the message. We also do not include the 4 byte checksum itself. + */ + chksum = crc32(0L, NULL, 0); + chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); + encode_value(ptr, chksum, 32); + } - /* Send Fake Rotate Event */ - rval = slave->dcb->func.write(slave->dcb, resp); + /* Send Fake Rotate Event */ + rval = slave->dcb->func.write(slave->dcb, resp); - /* set lastEventReceived */ - slave->lastEventReceived = ROTATE_EVENT; + /* set lastEventReceived */ + slave->lastEventReceived = ROTATE_EVENT; - /* set lastReply for slave heartbeat check */ - if (router->send_slave_heartbeat) - slave->lastReply = time(0); + /* set lastReply for slave heartbeat check */ + if (router->send_slave_heartbeat) + { + slave->lastReply = time(0); + } - /* Send the FORMAT_DESCRIPTION_EVENT */ - if (slave->binlog_pos != 4) - blr_slave_send_fde(router, slave); + /* Send the FORMAT_DESCRIPTION_EVENT */ + if (slave->binlog_pos != 4) + { + blr_slave_send_fde(router, slave); + } - /* set lastEventReceived */ - slave->lastEventReceived = FORMAT_DESCRIPTION_EVENT; + /* set lastEventReceived */ + slave->lastEventReceived = FORMAT_DESCRIPTION_EVENT; - slave->dcb->low_water = router->low_water; - slave->dcb->high_water = router->high_water; + slave->dcb->low_water = router->low_water; + slave->dcb->high_water = router->high_water; - dcb_add_callback(slave->dcb, DCB_REASON_DRAINED, blr_slave_callback, slave); + dcb_add_callback(slave->dcb, DCB_REASON_DRAINED, blr_slave_callback, slave); - slave->state = BLRS_DUMPING; + slave->state = BLRS_DUMPING; - MXS_NOTICE("%s: Slave %s:%d, server id %d requested binlog file %s from position %lu", - router->service->name, slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, (unsigned long)slave->binlog_pos); + MXS_NOTICE("%s: Slave %s:%d, server id %d requested binlog file %s from position %lu", + router->service->name, slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, (unsigned long)slave->binlog_pos); - if (slave->binlog_pos != router->binlog_position || - strcmp(slave->binlogfile, router->binlog_name) != 0) - { - spinlock_acquire(&slave->catch_lock); - slave->cstate &= ~CS_UPTODATE; - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); - } - return rval; + if (slave->binlog_pos != router->binlog_position || + strcmp(slave->binlogfile, router->binlog_name) != 0) + { + spinlock_acquire(&slave->catch_lock); + slave->cstate &= ~CS_UPTODATE; + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); + } + return rval; } /** * Encode a value into a number of bits in a MySQL packet * - * @param data Pointer to location in target packet - * @param value The value to encode into the buffer - * @param len Number of bits to encode value into + * @param data Pointer to location in target packet + * @param value The value to encode into the buffer + * @param len Number of bits to encode value into */ static void encode_value(unsigned char *data, unsigned int value, int len) { - while (len > 0) - { - *data++ = value & 0xff; - value >>= 8; - len -= 8; - } + while (len > 0) + { + *data++ = value & 0xff; + value >>= 8; + len -= 8; + } } /** * Populate a header structure for a replication message from a GWBUF structure. * - * @param pkt The incoming packet in a GWBUF chain - * @param hdr The packet header to populate - * @return A pointer to the first byte following the event header + * @param pkt The incoming packet in a GWBUF chain + * @param hdr The packet header to populate + * @return A pointer to the first byte following the event header */ uint8_t * -blr_build_header(GWBUF *pkt, REP_HEADER *hdr) +blr_build_header(GWBUF *pkt, REP_HEADER *hdr) { -uint8_t *ptr; + uint8_t *ptr; - ptr = GWBUF_DATA(pkt); + ptr = GWBUF_DATA(pkt); - encode_value(ptr, hdr->payload_len, 24); - ptr += 3; - *ptr++ = hdr->seqno; - *ptr++ = hdr->ok; - encode_value(ptr, hdr->timestamp, 32); - ptr += 4; - *ptr++ = hdr->event_type; - encode_value(ptr, hdr->serverid, 32); - ptr += 4; - encode_value(ptr, hdr->event_size, 32); - ptr += 4; - encode_value(ptr, hdr->next_pos, 32); - ptr += 4; - encode_value(ptr, hdr->flags, 16); - ptr += 2; + encode_value(ptr, hdr->payload_len, 24); + ptr += 3; + *ptr++ = hdr->seqno; + *ptr++ = hdr->ok; + encode_value(ptr, hdr->timestamp, 32); + ptr += 4; + *ptr++ = hdr->event_type; + encode_value(ptr, hdr->serverid, 32); + ptr += 4; + encode_value(ptr, hdr->event_size, 32); + ptr += 4; + encode_value(ptr, hdr->next_pos, 32); + ptr += 4; + encode_value(ptr, hdr->flags, 16); + ptr += 2; - return ptr; + return ptr; } /** - * We have a registered slave that is behind the current leading edge of the + * We have a registered slave that is behind the current leading edge of the * binlog. We must replay the log entries to bring this node up to speed. * * There may be a large number of records to send to the slave, the process @@ -1917,7 +2117,7 @@ uint8_t *ptr; * * The slave catchup routine will send a burst of replication events per single * call. The paramter "long" control the number of events in the burst. The - * short burst is intended to be used when the master receive an event and + * short burst is intended to be used when the master receive an event and * needs to put the slave into catchup mode. This prevents the slave taking * too much time away from the thread that is processing the master events. * @@ -1925,160 +2125,176 @@ uint8_t *ptr; * queue. This ensures that the slave callback for processing DCB write drain * will be called and future catchup requests will be handled on another thread. * - * @param router The binlog router - * @param slave The slave that is behind - * @param large Send a long or short burst of events - * @return The number of bytes written + * @param router The binlog router + * @param slave The slave that is behind + * @param large Send a long or short burst of events + * @return The number of bytes written */ int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large) { -GWBUF *head, *record; -REP_HEADER hdr; -int written, rval = 1, burst; -int rotating = 0; -long burst_size; -uint8_t *ptr; -char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; + GWBUF *head, *record; + REP_HEADER hdr; + int written, rval = 1, burst; + int rotating = 0; + long burst_size; + uint8_t *ptr; + char read_errmsg[BINLOG_ERROR_MSG_LEN + 1]; - read_errmsg[BINLOG_ERROR_MSG_LEN] = '\0'; + read_errmsg[BINLOG_ERROR_MSG_LEN] = '\0'; - if (large) - burst = router->long_burst; - else - burst = router->short_burst; + if (large) + { + burst = router->long_burst; + } + else + { + burst = router->short_burst; + } - burst_size = router->burst_size; + burst_size = router->burst_size; - int do_return; + int do_return; - spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&router->binlog_lock); - do_return = 0; + do_return = 0; - /* check for a pending transaction and safe position */ - if (router->pending_transaction && strcmp(router->binlog_name, slave->binlogfile) == 0 && - (slave->binlog_pos > router->binlog_position)) { - do_return = 1; - } + /* check for a pending transaction and safe position */ + if (router->pending_transaction && strcmp(router->binlog_name, slave->binlogfile) == 0 && + (slave->binlog_pos > router->binlog_position)) + { + do_return = 1; + } - spinlock_release(&router->binlog_lock); + spinlock_release(&router->binlog_lock); - if (do_return) { - spinlock_acquire(&slave->catch_lock); - slave->cstate &= ~CS_BUSY; - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); + if (do_return) + { + spinlock_acquire(&slave->catch_lock); + slave->cstate &= ~CS_BUSY; + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); - return 0; - } + return 0; + } - BLFILE *file; + BLFILE *file; #ifdef BLFILE_IN_SLAVE - file = slave->file; - slave->file = NULL; + file = slave->file; + slave->file = NULL; #else - file = NULL; + file = NULL; #endif - if (file == NULL) - { - rotating = router->rotating; - if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) - { - char err_msg[BINLOG_ERROR_MSG_LEN+1]; - err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; + if (file == NULL) + { + rotating = router->rotating; + if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) + { + char err_msg[BINLOG_ERROR_MSG_LEN + 1]; + err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; - if (rotating) - { - spinlock_acquire(&slave->catch_lock); - slave->cstate |= CS_EXPECTCB; - slave->cstate &= ~CS_BUSY; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); - return rval; - } - MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s': blr_slave_catchup " - "failed to open binlog file", - slave->dcb->remote, ntohs((slave->dcb->ipv4).sin_port), slave->serverid, - slave->binlogfile); + if (rotating) + { + spinlock_acquire(&slave->catch_lock); + slave->cstate |= CS_EXPECTCB; + slave->cstate &= ~CS_BUSY; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); + return rval; + } + MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s': blr_slave_catchup " + "failed to open binlog file", + slave->dcb->remote, ntohs((slave->dcb->ipv4).sin_port), slave->serverid, + slave->binlogfile); - slave->cstate &= ~CS_BUSY; - slave->state = BLRS_ERRORED; + slave->cstate &= ~CS_BUSY; + slave->state = BLRS_ERRORED; - snprintf(err_msg, BINLOG_ERROR_MSG_LEN, "Failed to open binlog '%s'", slave->binlogfile); + snprintf(err_msg, BINLOG_ERROR_MSG_LEN, "Failed to open binlog '%s'", slave->binlogfile); - /* Send error that stops slave replication */ - blr_send_custom_error(slave->dcb, slave->seqno++, 0, err_msg, "HY000", 1236); + /* Send error that stops slave replication */ + blr_send_custom_error(slave->dcb, slave->seqno++, 0, err_msg, "HY000", 1236); - dcb_close(slave->dcb); - return 0; - } - } + dcb_close(slave->dcb); + return 0; + } + } - slave->stats.n_bursts++; + slave->stats.n_bursts++; #ifdef BLSLAVE_IN_FILE - slave->file = file; + slave->file = file; #endif - int events_before = slave->stats.n_events; + int events_before = slave->stats.n_events; - while (burst-- && burst_size > 0 && - (record = blr_read_binlog(router, file, slave->binlog_pos, &hdr, read_errmsg)) != NULL) - { - if (hdr.event_type == ROTATE_EVENT) - { - unsigned long beat1 = hkheartbeat; - blr_close_binlog(router, file); - if (hkheartbeat - beat1 > 1) - MXS_ERROR("blr_close_binlog took %lu maxscale beats", - hkheartbeat - beat1); - blr_slave_rotate(router, slave, GWBUF_DATA(record)); - beat1 = hkheartbeat; + while (burst-- && burst_size > 0 && + (record = blr_read_binlog(router, file, slave->binlog_pos, &hdr, read_errmsg)) != NULL) + { + char binlog_name[BINLOG_FNAMELEN + 1]; + uint32_t binlog_pos; + + strcpy(binlog_name, slave->binlogfile); + binlog_pos = slave->binlog_pos; + + if (hdr.event_type == ROTATE_EVENT) + { + unsigned long beat1 = hkheartbeat; + blr_close_binlog(router, file); + if (hkheartbeat - beat1 > 1) + { + MXS_ERROR("blr_close_binlog took %lu maxscale beats", hkheartbeat - beat1); + } + blr_slave_rotate(router, slave, GWBUF_DATA(record)); + beat1 = hkheartbeat; #ifdef BLFILE_IN_SLAVE - if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) + if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) #else - if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) + if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) #endif - { - char err_msg[BINLOG_ERROR_MSG_LEN+1]; - err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; - if (rotating) - { - spinlock_acquire(&slave->catch_lock); - slave->cstate |= CS_EXPECTCB; - slave->cstate &= ~CS_BUSY; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); - return rval; - } - MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s': blr_slave_catchup " - "failed to open binlog file in rotate event", - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile); + { + char err_msg[BINLOG_ERROR_MSG_LEN + 1]; + err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; + if (rotating) + { + spinlock_acquire(&slave->catch_lock); + slave->cstate |= CS_EXPECTCB; + slave->cstate &= ~CS_BUSY; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); + return rval; + } + MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s': blr_slave_catchup " + "failed to open binlog file in rotate event", + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile); - slave->state = BLRS_ERRORED; + slave->state = BLRS_ERRORED; - snprintf(err_msg, BINLOG_ERROR_MSG_LEN, "Failed to open binlog '%s' in rotate event", slave->binlogfile); + snprintf(err_msg, BINLOG_ERROR_MSG_LEN, + "Failed to open binlog '%s' in rotate event", slave->binlogfile); - /* Send error that stops slave replication */ - blr_send_custom_error(slave->dcb, (slave->seqno - 1), 0, err_msg, "HY000", 1236); + /* Send error that stops slave replication */ + blr_send_custom_error(slave->dcb, (slave->seqno - 1), 0, err_msg, "HY000", 1236); - dcb_close(slave->dcb); - break; - } + dcb_close(slave->dcb); + break; + } #ifdef BLFILE_IN_SLAVE - file = slave->file; + file = slave->file; #endif - if (hkheartbeat - beat1 > 1) - MXS_ERROR("blr_open_binlog took %lu beats", - hkheartbeat - beat1); - } + if (hkheartbeat - beat1 > 1) + { + MXS_ERROR("blr_open_binlog took %lu beats", hkheartbeat - beat1); + } + } - if (blr_send_event(slave, &hdr, (uint8_t*) record->start)) + if (blr_send_event(BLR_THREAD_ROLE_SLAVE, binlog_name, binlog_pos, + slave, &hdr, (uint8_t*) record->start)) { if (hdr.event_type != ROTATE_EVENT) { @@ -2087,241 +2303,266 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; slave->stats.n_events++; burst_size -= hdr.event_size; } + else + { + MXS_WARNING("Slave %s:%i, server-id %d, binlog '%s, position %u: " + "Slave-thread could not send event to slave, closing connection.", + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + binlog_name, + binlog_pos); +#ifndef BLFILE_IN_SLAVE + blr_close_binlog(router, file); +#endif + slave->state = BLRS_ERRORED; + dcb_close(slave->dcb); + return 0; + } + gwbuf_free(record); record = NULL; - /* set lastReply for slave heartbeat check */ - if (router->send_slave_heartbeat) - slave->lastReply = time(0); - } - if (record == NULL) { - slave->stats.n_failed_read++; + /* set lastReply for slave heartbeat check */ + if (router->send_slave_heartbeat) + { + slave->lastReply = time(0); + } + } + if (record == NULL) + { + slave->stats.n_failed_read++; - if (hdr.ok == SLAVE_POS_BAD_FD) { - MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, - read_errmsg); - } + if (hdr.ok == SLAVE_POS_BAD_FD) + { + MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + read_errmsg); + } - if (hdr.ok == SLAVE_POS_BEYOND_EOF) { - MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, - read_errmsg); + if (hdr.ok == SLAVE_POS_BEYOND_EOF) + { + MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + read_errmsg); - /* - * Close the slave session and socket - * The slave will try to reconnect - */ - dcb_close(slave->dcb); + /* + * Close the slave session and socket + * The slave will try to reconnect + */ + dcb_close(slave->dcb); #ifndef BLFILE_IN_SLAVE - blr_close_binlog(router, file); + blr_close_binlog(router, file); #endif - return 0; - } + return 0; + } - if (hdr.ok == SLAVE_POS_READ_ERR) { - MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, - read_errmsg); + if (hdr.ok == SLAVE_POS_READ_ERR) + { + MXS_ERROR("%s Slave %s:%i, server-id %d, binlog '%s', %s", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + read_errmsg); - spinlock_acquire(&slave->catch_lock); + spinlock_acquire(&slave->catch_lock); - slave->state = BLRS_ERRORED; + slave->state = BLRS_ERRORED; - spinlock_release(&slave->catch_lock); + spinlock_release(&slave->catch_lock); - /* - * Send an error that will stop slave replication - */ - blr_send_custom_error(slave->dcb, slave->seqno++, 0, read_errmsg, "HY000", 1236); + /* + * Send an error that will stop slave replication + */ + blr_send_custom_error(slave->dcb, slave->seqno++, 0, read_errmsg, "HY000", 1236); - dcb_close(slave->dcb); + dcb_close(slave->dcb); #ifndef BLFILE_IN_SLAVE - blr_close_binlog(router, file); + blr_close_binlog(router, file); #endif - return 0; - } + return 0; + } - if (hdr.ok == SLAVE_POS_READ_UNSAFE) { + if (hdr.ok == SLAVE_POS_READ_UNSAFE) + { - MXS_NOTICE("%s: Slave %s:%i, server-id %d, binlog '%s', read %d events, " - "current committed transaction event being sent: %lu, %s", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, - slave->stats.n_events - events_before, - router->current_safe_event, - read_errmsg); - } - } - spinlock_acquire(&slave->catch_lock); - slave->cstate &= ~CS_BUSY; - spinlock_release(&slave->catch_lock); + MXS_NOTICE("%s: Slave %s:%i, server-id %d, binlog '%s', read %d events, " + "current committed transaction event being sent: %lu, %s", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + slave->stats.n_events - events_before, + router->current_safe_event, + read_errmsg); + } + } + spinlock_acquire(&slave->catch_lock); + slave->cstate &= ~CS_BUSY; + spinlock_release(&slave->catch_lock); - if (record) - { - slave->stats.n_flows++; - spinlock_acquire(&slave->catch_lock); - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); - } - else if (slave->binlog_pos == router->binlog_position && - strcmp(slave->binlogfile, router->binlog_name) == 0) - { - int state_change = 0; - unsigned int cstate =0; - spinlock_acquire(&router->binlog_lock); - spinlock_acquire(&slave->catch_lock); + if (record) + { + slave->stats.n_flows++; + spinlock_acquire(&slave->catch_lock); + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); + } + else if (slave->binlog_pos == router->binlog_position && + strcmp(slave->binlogfile, router->binlog_name) == 0) + { + int state_change = 0; + unsigned int cstate = 0; + spinlock_acquire(&router->binlog_lock); + spinlock_acquire(&slave->catch_lock); - cstate = slave->cstate; + cstate = slave->cstate; - /* - * Now check again since we hold the router->binlog_lock - * and slave->catch_lock. - */ - if (slave->binlog_pos != router->binlog_position || - strcmp(slave->binlogfile, router->binlog_name) != 0) - { - slave->cstate &= ~CS_UPTODATE; - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - spinlock_release(&router->binlog_lock); + /* + * Now check again since we hold the router->binlog_lock + * and slave->catch_lock. + */ + if (slave->binlog_pos != router->binlog_position || + strcmp(slave->binlogfile, router->binlog_name) != 0) + { + slave->cstate &= ~CS_UPTODATE; + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + spinlock_release(&router->binlog_lock); - if ((cstate & CS_UPTODATE) == CS_UPTODATE) - { + if ((cstate & CS_UPTODATE) == CS_UPTODATE) + { #ifdef STATE_CHANGE_LOGGING_ENABLED - MXS_NOTICE("%s: Slave %s:%d, server-id %d transition from up-to-date to catch-up in blr_slave_catchup, binlog file '%s', position %lu.", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, (unsigned long)slave->binlog_pos); + MXS_NOTICE("%s: Slave %s:%d, server-id %d transition from up-to-date to " + "catch-up in blr_slave_catchup, binlog file '%s', position %lu.", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, (unsigned long)slave->binlog_pos); #endif - } + } - poll_fake_write_event(slave->dcb); - } - else - { - if ((slave->cstate & CS_UPTODATE) == 0) - { - slave->stats.n_upd++; - slave->cstate |= CS_UPTODATE; - spinlock_release(&slave->catch_lock); - spinlock_release(&router->binlog_lock); - state_change = 1; - } - else - { - MXS_NOTICE("Execution entered branch were locks previously were NOT " - "released. Previously this would have caused a lock-up."); - spinlock_release(&slave->catch_lock); - spinlock_release(&router->binlog_lock); - } - } + poll_fake_write_event(slave->dcb); + } + else + { + if ((slave->cstate & CS_UPTODATE) == 0) + { + slave->stats.n_upd++; + slave->cstate |= CS_UPTODATE; + spinlock_release(&slave->catch_lock); + spinlock_release(&router->binlog_lock); + state_change = 1; + } + else + { + MXS_NOTICE("Execution entered branch were locks previously were NOT " + "released. Previously this would have caused a lock-up."); + spinlock_release(&slave->catch_lock); + spinlock_release(&router->binlog_lock); + } + } - if (state_change) - { - slave->stats.n_caughtup++; + if (state_change) + { + slave->stats.n_caughtup++; #ifdef STATE_CHANGE_LOGGING_ENABLED - // TODO: The % 50 should be removed. Now only every 50th state change is logged. - if (slave->stats.n_caughtup == 1) - { - MXS_NOTICE("%s: Slave %s:%d, server-id %d is now up to date '%s', position %lu.", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, (unsigned long)slave->binlog_pos); - } - else if ((slave->stats.n_caughtup % 50) == 0) - { - MXS_NOTICE("%s: Slave %s:%d, server-id %d is up to date '%s', position %lu.", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, (unsigned long)slave->binlog_pos); - } + // TODO: The % 50 should be removed. Now only every 50th state change is logged. + if (slave->stats.n_caughtup == 1) + { + MXS_NOTICE("%s: Slave %s:%d, server-id %d is now up to date '%s', position %lu.", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, (unsigned long)slave->binlog_pos); + } + else if ((slave->stats.n_caughtup % 50) == 0) + { + MXS_NOTICE("%s: Slave %s:%d, server-id %d is up to date '%s', position %lu.", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, (unsigned long)slave->binlog_pos); + } #endif - } - } - else - { - if (slave->binlog_pos >= blr_file_size(file) - && router->rotating == 0 - && strcmp(router->binlog_name, slave->binlogfile) != 0 - && (blr_master_connected(router) - || blr_file_next_exists(router, slave))) - { - /* We may have reached the end of file of a non-current - * binlog file. - * - * Note if the master is rotating there is a window during - * which the rotate event has been written to the old binlog - * but the new binlog file has not yet been created. Therefore - * we ignore these issues during the rotate processing. - */ - MXS_ERROR("%s: Slave %s:%d, server-id %d reached end of file for binlog file %s " - "at %lu which is not the file currently being downloaded. " - "Master binlog is %s, %lu. This may be caused by a " - "previous failure of the master.", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, (unsigned long)slave->binlog_pos, - router->binlog_name, router->binlog_position); + } + } + else + { + if (slave->binlog_pos >= blr_file_size(file) + && router->rotating == 0 + && strcmp(router->binlog_name, slave->binlogfile) != 0 + && (blr_master_connected(router) + || blr_file_next_exists(router, slave))) + { + /* We may have reached the end of file of a non-current + * binlog file. + * + * Note if the master is rotating there is a window during + * which the rotate event has been written to the old binlog + * but the new binlog file has not yet been created. Therefore + * we ignore these issues during the rotate processing. + */ + MXS_ERROR("%s: Slave %s:%d, server-id %d reached end of file for binlog file %s " + "at %lu which is not the file currently being downloaded. " + "Master binlog is %s, %lu. This may be caused by a " + "previous failure of the master.", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, (unsigned long)slave->binlog_pos, + router->binlog_name, router->binlog_position); #ifdef BLFILE_IN_SLAVE - if (blr_slave_fake_rotate(router, slave, &slave->file)) + if (blr_slave_fake_rotate(router, slave, &slave->file)) #else - if (blr_slave_fake_rotate(router, slave, &file)) + if (blr_slave_fake_rotate(router, slave, &file)) #endif - { - spinlock_acquire(&slave->catch_lock); - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); - } - else - { - slave->state = BLRS_ERRORED; - dcb_close(slave->dcb); - } - } - else if (blr_master_connected(router)) - { - spinlock_acquire(&slave->catch_lock); - slave->cstate |= CS_EXPECTCB; - spinlock_release(&slave->catch_lock); - poll_fake_write_event(slave->dcb); - } - } + { + spinlock_acquire(&slave->catch_lock); + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); + } + else + { + slave->state = BLRS_ERRORED; + dcb_close(slave->dcb); + } + } + else if (blr_master_connected(router)) + { + spinlock_acquire(&slave->catch_lock); + slave->cstate |= CS_EXPECTCB; + spinlock_release(&slave->catch_lock); + poll_fake_write_event(slave->dcb); + } + } #ifndef BLFILE_IN_SLAVE - if (file) - { - blr_close_binlog(router, file); - } + if (file) + { + blr_close_binlog(router, file); + } #endif - return rval; + return rval; } /** @@ -2330,16 +2571,16 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; * that is used to implement the flow control mechanism for the sending of * large quantities of binlog records during the catchup process. * - * @param dcb The DCB of the slave connection - * @param reason The reason the callback was called - * @param data The user data, in this case the server structure + * @param dcb The DCB of the slave connection + * @param reason The reason the callback was called + * @param data The user data, in this case the server structure */ int blr_slave_callback(DCB *dcb, DCB_REASON reason, void *data) { -ROUTER_SLAVE *slave = (ROUTER_SLAVE *)data; -ROUTER_INSTANCE *router = slave->router; -unsigned int cstate; + ROUTER_SLAVE *slave = (ROUTER_SLAVE *)data; + ROUTER_INSTANCE *router = slave->router; + unsigned int cstate; if (NULL == dcb->session->router_session) { @@ -2350,212 +2591,227 @@ unsigned int cstate; */ return 0; } - if (reason == DCB_REASON_DRAINED) - { - if (slave->state == BLRS_DUMPING) - { - spinlock_acquire(&slave->catch_lock); - if (slave->cstate & CS_BUSY) - { - spinlock_release(&slave->catch_lock); - return 0; - } - cstate = slave->cstate; - slave->cstate &= ~(CS_UPTODATE|CS_EXPECTCB); - slave->cstate |= CS_BUSY; - spinlock_release(&slave->catch_lock); + if (reason == DCB_REASON_DRAINED) + { + if (slave->state == BLRS_DUMPING) + { + spinlock_acquire(&slave->catch_lock); + if (slave->cstate & CS_BUSY) + { + spinlock_release(&slave->catch_lock); + return 0; + } + cstate = slave->cstate; + slave->cstate &= ~(CS_UPTODATE | CS_EXPECTCB); + slave->cstate |= CS_BUSY; + spinlock_release(&slave->catch_lock); - if ((cstate & CS_UPTODATE) == CS_UPTODATE) - { + if ((cstate & CS_UPTODATE) == CS_UPTODATE) + { #ifdef STATE_CHANGE_LOGGING_ENABLED - MXS_NOTICE("%s: Slave %s:%d, server-id %d transition from up-to-date to catch-up in blr_slave_callback, binlog file '%s', position %lu.", - router->service->name, - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, (unsigned long)slave->binlog_pos); + MXS_NOTICE("%s: Slave %s:%d, server-id %d transition from up-to-date to " + "catch-up in blr_slave_callback, binlog file '%s', position %lu.", + router->service->name, + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, (unsigned long)slave->binlog_pos); #endif - } + } - slave->stats.n_dcb++; - blr_slave_catchup(router, slave, true); - } - else - { - MXS_DEBUG("Ignored callback due to slave state %s", - blrs_states[slave->state]); - } - } + slave->stats.n_dcb++; + blr_slave_catchup(router, slave, true); + } + else + { + MXS_DEBUG("Ignored callback due to slave state %s", + blrs_states[slave->state]); + } + } - if (reason == DCB_REASON_LOW_WATER) - { - if (slave->state == BLRS_DUMPING) - { - slave->stats.n_cb++; - blr_slave_catchup(router, slave, true); - } - else - { - slave->stats.n_cbna++; - } - } - return 0; + if (reason == DCB_REASON_LOW_WATER) + { + if (slave->state == BLRS_DUMPING) + { + slave->stats.n_cb++; + blr_slave_catchup(router, slave, true); + } + else + { + slave->stats.n_cbna++; + } + } + return 0; } /** * Rotate the slave to the new binlog file * - * @param slave The slave instance - * @param ptr The rotate event (minus header and OK byte) + * @param slave The slave instance + * @param ptr The rotate event (minus header and OK byte) */ void blr_slave_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, uint8_t *ptr) { -int len = EXTRACT24(ptr + 9); // Extract the event length + int len = EXTRACT24(ptr + 9); // Extract the event length - len = len - (BINLOG_EVENT_HDR_LEN + 8); // Remove length of header and position - if (router->master_chksum) - len -= 4; - if (len > BINLOG_FNAMELEN) - len = BINLOG_FNAMELEN; - ptr += BINLOG_EVENT_HDR_LEN; // Skip header - slave->binlog_pos = extract_field(ptr, 32); - slave->binlog_pos += (((uint64_t)extract_field(ptr+4, 32)) << 32); - memcpy(slave->binlogfile, ptr + 8, len); - slave->binlogfile[len] = 0; + len = len - (BINLOG_EVENT_HDR_LEN + 8); // Remove length of header and position + if (router->master_chksum) + { + len -= 4; + } + if (len > BINLOG_FNAMELEN) + { + len = BINLOG_FNAMELEN; + } + ptr += BINLOG_EVENT_HDR_LEN; // Skip header + slave->binlog_pos = extract_field(ptr, 32); + slave->binlog_pos += (((uint64_t)extract_field(ptr + 4, 32)) << 32); + memcpy(slave->binlogfile, ptr + 8, len); + slave->binlogfile[len] = 0; } /** * Generate an internal rotate event that we can use to cause the slave to move beyond * a binlog file that is misisng the rotate eent at the end. * - * @param router The router instance - * @param slave The slave to rotate + * @param router The router instance + * @param slave The slave to rotate * @return Non-zero if the rotate took place */ static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, BLFILE** filep) { -char *sptr; -int filenum; -GWBUF *resp; -uint8_t *ptr; -int len, binlognamelen; -REP_HEADER hdr; -uint32_t chksum; + char *sptr; + int filenum; + GWBUF *resp; + uint8_t *ptr; + int len, binlognamelen; + REP_HEADER hdr; + uint32_t chksum; - if ((sptr = strrchr(slave->binlogfile, '.')) == NULL) - return 0; - blr_close_binlog(router, *filep); - filenum = atoi(sptr + 1); - sprintf(slave->binlogfile, BINLOG_NAMEFMT, router->fileroot, filenum + 1); - slave->binlog_pos = 4; - if ((*filep = blr_open_binlog(router, slave->binlogfile)) == NULL) - return 0; + if ((sptr = strrchr(slave->binlogfile, '.')) == NULL) + { + return 0; + } + blr_close_binlog(router, *filep); + filenum = atoi(sptr + 1); + sprintf(slave->binlogfile, BINLOG_NAMEFMT, router->fileroot, filenum + 1); + slave->binlog_pos = 4; + if ((*filep = blr_open_binlog(router, slave->binlogfile)) == NULL) + { + return 0; + } - binlognamelen = strlen(slave->binlogfile); - len = BINLOG_EVENT_HDR_LEN + 8 + 4 + binlognamelen; - /* no slave crc, remove 4 bytes */ - if (slave->nocrc) - len -= 4; + binlognamelen = strlen(slave->binlogfile); + len = BINLOG_EVENT_HDR_LEN + 8 + 4 + binlognamelen; + /* no slave crc, remove 4 bytes */ + if (slave->nocrc) + { + len -= 4; + } - // Build a fake rotate event - resp = gwbuf_alloc(len + 5); - hdr.payload_len = len + 1; - hdr.seqno = slave->seqno++; - hdr.ok = 0; - hdr.timestamp = 0L; - hdr.event_type = ROTATE_EVENT; - hdr.serverid = router->masterid; - hdr.event_size = len; - hdr.next_pos = 0; - hdr.flags = 0x20; - ptr = blr_build_header(resp, &hdr); - encode_value(ptr, slave->binlog_pos, 64); - ptr += 8; - memcpy(ptr, slave->binlogfile, binlognamelen); - ptr += binlognamelen; + // Build a fake rotate event + resp = gwbuf_alloc(len + 5); + hdr.payload_len = len + 1; + hdr.seqno = slave->seqno++; + hdr.ok = 0; + hdr.timestamp = 0L; + hdr.event_type = ROTATE_EVENT; + hdr.serverid = router->masterid; + hdr.event_size = len; + hdr.next_pos = 0; + hdr.flags = 0x20; + ptr = blr_build_header(resp, &hdr); + encode_value(ptr, slave->binlog_pos, 64); + ptr += 8; + memcpy(ptr, slave->binlogfile, binlognamelen); + ptr += binlognamelen; - /* if slave has crc add the chksum */ - if (!slave->nocrc) { - /* - * Now add the CRC to the fake binlog rotate event. - * - * The algorithm is first to compute the checksum of an empty buffer - * and then the checksum of the event portion of the message, ie we do not - * include the length, sequence number and ok byte that makes up the first - * 5 bytes of the message. We also do not include the 4 byte checksum itself. - */ - chksum = crc32(0L, NULL, 0); - chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); - encode_value(ptr, chksum, 32); - } + /* if slave has crc add the chksum */ + if (!slave->nocrc) + { + /* + * Now add the CRC to the fake binlog rotate event. + * + * The algorithm is first to compute the checksum of an empty buffer + * and then the checksum of the event portion of the message, ie we do not + * include the length, sequence number and ok byte that makes up the first + * 5 bytes of the message. We also do not include the 4 byte checksum itself. + */ + chksum = crc32(0L, NULL, 0); + chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); + encode_value(ptr, chksum, 32); + } - slave->dcb->func.write(slave->dcb, resp); - return 1; + slave->dcb->func.write(slave->dcb, resp); + return 1; } /** * Send a "fake" format description event to the newly connected slave * - * @param router The router instance - * @param slave The slave to send the event to + * @param router The router instance + * @param slave The slave to send the event to */ static void blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -BLFILE *file; -REP_HEADER hdr; -GWBUF *record, *head; -uint8_t *ptr; -uint32_t chksum; -char err_msg[BINLOG_ERROR_MSG_LEN+1]; + BLFILE *file; + REP_HEADER hdr; + GWBUF *record, *head; + uint8_t *ptr; + uint32_t chksum; + char err_msg[BINLOG_ERROR_MSG_LEN + 1]; - err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; + err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; - memset(&hdr, 0, BINLOG_EVENT_HDR_LEN); + memset(&hdr, 0, BINLOG_EVENT_HDR_LEN); - if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) - return; - if ((record = blr_read_binlog(router, file, 4, &hdr, err_msg)) == NULL) - { - if (hdr.ok != SLAVE_POS_READ_OK) { - MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s', blr_read_binlog failure: %s", - slave->dcb->remote, - ntohs((slave->dcb->ipv4).sin_port), - slave->serverid, - slave->binlogfile, - err_msg); - } + if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) + { + return; + } + if ((record = blr_read_binlog(router, file, 4, &hdr, err_msg)) == NULL) + { + if (hdr.ok != SLAVE_POS_READ_OK) + { + MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s', blr_read_binlog failure: %s", + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + err_msg); + } - blr_close_binlog(router, file); - return; - } - blr_close_binlog(router, file); - head = gwbuf_alloc(5); - ptr = GWBUF_DATA(head); - encode_value(ptr, hdr.event_size + 1, 24); // Payload length - ptr += 3; - *ptr++ = slave->seqno++; - *ptr++ = 0; // OK - head = gwbuf_append(head, record); - ptr = GWBUF_DATA(record); - encode_value(ptr, time(0), 32); // Overwrite timestamp - ptr += 13; - encode_value(ptr, 0, 32); // Set next position to 0 - /* - * Since we have changed the timestamp we must recalculate the CRC - * - * Position ptr to the start of the event header, - * calculate a new checksum - * and write it into the header - */ - ptr = GWBUF_DATA(record) + hdr.event_size - 4; - chksum = crc32(0L, NULL, 0); - chksum = crc32(chksum, GWBUF_DATA(record), hdr.event_size - 4); - encode_value(ptr, chksum, 32); + blr_close_binlog(router, file); + return; + } + blr_close_binlog(router, file); + head = gwbuf_alloc(5); + ptr = GWBUF_DATA(head); + encode_value(ptr, hdr.event_size + 1, 24); // Payload length + ptr += 3; + *ptr++ = slave->seqno++; + *ptr++ = 0; // OK + head = gwbuf_append(head, record); + ptr = GWBUF_DATA(record); + encode_value(ptr, time(0), 32); // Overwrite timestamp + ptr += 13; + encode_value(ptr, 0, 32); // Set next position to 0 + /* + * Since we have changed the timestamp we must recalculate the CRC + * + * Position ptr to the start of the event header, + * calculate a new checksum + * and write it into the header + */ + ptr = GWBUF_DATA(record) + hdr.event_size - 4; + chksum = crc32(0L, NULL, 0); + chksum = crc32(chksum, GWBUF_DATA(record), hdr.event_size - 4); + encode_value(ptr, chksum, 32); - slave->dcb->func.write(slave->dcb, head); + slave->dcb->func.write(slave->dcb, head); } @@ -2563,156 +2819,175 @@ char err_msg[BINLOG_ERROR_MSG_LEN+1]; /** * Send the field count packet in a response packet sequence. * - * @param router The router - * @param slave The slave connection - * @param count Number of columns in the result set - * @return Non-zero on success + * @param router The router + * @param slave The slave connection + * @param count Number of columns in the result set + * @return Non-zero on success */ static int blr_slave_send_fieldcount(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int count) { -GWBUF *pkt; -uint8_t *ptr; + GWBUF *pkt; + uint8_t *ptr; - if ((pkt = gwbuf_alloc(5)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, 1, 24); // Add length of data packet - ptr += 3; - *ptr++ = 0x01; // Sequence number in response - *ptr++ = count; // Length of result string - return slave->dcb->func.write(slave->dcb, pkt); + if ((pkt = gwbuf_alloc(5)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, 1, 24); // Add length of data packet + ptr += 3; + *ptr++ = 0x01; // Sequence number in response + *ptr++ = count; // Length of result string + return slave->dcb->func.write(slave->dcb, pkt); } /** * Send the column definition packet in a response packet sequence. * - * @param router The router - * @param slave The slave connection - * @param name Name of the column - * @param type Column type - * @param len Column length - * @param seqno Packet sequence number - * @return Non-zero on success + * @param router The router + * @param slave The slave connection + * @param name Name of the column + * @param type Column type + * @param len Column length + * @param seqno Packet sequence number + * @return Non-zero on success */ static int -blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno) +blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, + uint8_t seqno) { -GWBUF *pkt; -uint8_t *ptr; + GWBUF *pkt; + uint8_t *ptr; - if ((pkt = gwbuf_alloc(26 + strlen(name))) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, 22 + strlen(name), 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno; // Sequence number in response - *ptr++ = 3; // Catalog is always def - *ptr++ = 'd'; - *ptr++ = 'e'; - *ptr++ = 'f'; - *ptr++ = 0; // Schema name length - *ptr++ = 0; // virtual table name length - *ptr++ = 0; // Table name length - *ptr++ = strlen(name); // Column name length; - while (*name) - *ptr++ = *name++; // Copy the column name - *ptr++ = 0; // Orginal column name - *ptr++ = 0x0c; // Length of next fields always 12 - *ptr++ = 0x3f; // Character set - *ptr++ = 0; - encode_value(ptr, len, 32); // Add length of column - ptr += 4; - *ptr++ = type; - *ptr++ = 0x81; // Two bytes of flags - if (type == 0xfd) - *ptr++ = 0x1f; - else - *ptr++ = 0x00; - *ptr++= 0; - *ptr++= 0; - *ptr++= 0; - return slave->dcb->func.write(slave->dcb, pkt); + if ((pkt = gwbuf_alloc(26 + strlen(name))) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, 22 + strlen(name), 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno; // Sequence number in response + *ptr++ = 3; // Catalog is always def + *ptr++ = 'd'; + *ptr++ = 'e'; + *ptr++ = 'f'; + *ptr++ = 0; // Schema name length + *ptr++ = 0; // virtual table name length + *ptr++ = 0; // Table name length + *ptr++ = strlen(name); // Column name length; + while (*name) + { + *ptr++ = *name++; // Copy the column name + } + *ptr++ = 0; // Orginal column name + *ptr++ = 0x0c; // Length of next fields always 12 + *ptr++ = 0x3f; // Character set + *ptr++ = 0; + encode_value(ptr, len, 32); // Add length of column + ptr += 4; + *ptr++ = type; + *ptr++ = 0x81; // Two bytes of flags + if (type == 0xfd) + { + *ptr++ = 0x1f; + } + else + { + *ptr++ = 0x00; + } + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; + return slave->dcb->func.write(slave->dcb, pkt); } /** * Send an EOF packet in a response packet sequence. * - * @param router The router - * @param slave The slave connection - * @param seqno The sequence number of the EOF packet - * @return Non-zero on success + * @param router The router + * @param slave The slave connection + * @param seqno The sequence number of the EOF packet + * @return Non-zero on success */ static int blr_slave_send_eof(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int seqno) { -GWBUF *pkt; -uint8_t *ptr; + GWBUF *pkt; + uint8_t *ptr; - if ((pkt = gwbuf_alloc(9)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, 5, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno; // Sequence number in response - *ptr++ = 0xfe; // Length of result string - encode_value(ptr, 0, 16); // No errors - ptr += 2; - encode_value(ptr, 2, 16); // Autocommit enabled - return slave->dcb->func.write(slave->dcb, pkt); + if ((pkt = gwbuf_alloc(9)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, 5, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno; // Sequence number in response + *ptr++ = 0xfe; // Length of result string + encode_value(ptr, 0, 16); // No errors + ptr += 2; + encode_value(ptr, 2, 16); // Autocommit enabled + return slave->dcb->func.write(slave->dcb, pkt); } /** * Send the reply only to the SQL command "DISCONNECT SERVER $server_id' * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent */ static int blr_slave_send_disconnected_server(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int server_id, int found) { -GWBUF *pkt; -char state[40]; -char serverid[40]; -uint8_t *ptr; -int len, id_len, seqno = 2; + GWBUF *pkt; + char state[40]; + char serverid[40]; + uint8_t *ptr; + int len, id_len, seqno = 2; - sprintf(serverid, "%d", server_id); - if (found) - strcpy(state, "disconnected"); - else - strcpy(state, "not found"); + sprintf(serverid, "%d", server_id); + if (found) + { + strcpy(state, "disconnected"); + } + else + { + strcpy(state, "not found"); + } - id_len = strlen(serverid); - len = 4 + (1 + id_len) + (1 + strlen(state)); + id_len = strlen(serverid); + len = 4 + (1 + id_len) + (1 + strlen(state)); - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } - blr_slave_send_fieldcount(router, slave, 2); - blr_slave_send_columndef(router, slave, "server_id", BLR_TYPE_INT, 40, seqno++); - blr_slave_send_columndef(router, slave, "state", BLR_TYPE_STRING, 40, seqno++); - blr_slave_send_eof(router, slave, seqno++); + blr_slave_send_fieldcount(router, slave, 2); + blr_slave_send_columndef(router, slave, "server_id", BLR_TYPE_INT, 40, seqno++); + blr_slave_send_columndef(router, slave, "state", BLR_TYPE_STRING, 40, seqno++); + blr_slave_send_eof(router, slave, seqno++); - ptr = GWBUF_DATA(pkt); - encode_value(ptr, len - 4, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno++; // Sequence number in response + ptr = GWBUF_DATA(pkt); + encode_value(ptr, len - 4, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno++; // Sequence number in response - *ptr++ = id_len; // Length of result string - strncpy((char *)ptr, serverid, id_len); // Result string - ptr += id_len; + *ptr++ = id_len; // Length of result string + strncpy((char *)ptr, serverid, id_len); // Result string + ptr += id_len; - *ptr++ = strlen(state); // Length of result string - strncpy((char *)ptr, state, strlen(state)); // Result string - ptr += strlen(state); + *ptr++ = strlen(state); // Length of result string + strncpy((char *)ptr, state, strlen(state)); // Result string + ptr += strlen(state); - slave->dcb->func.write(slave->dcb, pkt); + slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, seqno++); + return blr_slave_send_eof(router, slave, seqno++); } @@ -2720,159 +2995,163 @@ int len, id_len, seqno = 2; * Send the response to the SQL command "DISCONNECT SERVER $server_id' * and close the connection to that server * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @param server_id The slave server_id to disconnect - * @return Non-zero if data was sent to the client + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @param server_id The slave server_id to disconnect + * @return Non-zero if data was sent to the client */ static int blr_slave_disconnect_server(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int server_id) { - ROUTER_OBJECT *router_obj= router->service->router; - ROUTER_SLAVE *sptr; - int n; - int server_found = 0; + ROUTER_OBJECT *router_obj = router->service->router; + ROUTER_SLAVE *sptr; + int n; + int server_found = 0; - spinlock_acquire(&router->lock); + spinlock_acquire(&router->lock); - sptr = router->slaves; - /* look for server_id among all registered slaves */ - while (sptr) - { - /* don't examine slaves with state = 0 */ - if ((sptr->state == BLRS_REGISTERED || sptr->state == BLRS_DUMPING) && - sptr->serverid == server_id) - { - /* server_id found */ - server_found = 1; - MXS_NOTICE("%s: Slave %s, server id %d, disconnected by %s@%s", - router->service->name, - sptr->dcb->remote, - server_id, - slave->dcb->user, - slave->dcb->remote); + sptr = router->slaves; + /* look for server_id among all registered slaves */ + while (sptr) + { + /* don't examine slaves with state = 0 */ + if ((sptr->state == BLRS_REGISTERED || sptr->state == BLRS_DUMPING) && + sptr->serverid == server_id) + { + /* server_id found */ + server_found = 1; + MXS_NOTICE("%s: Slave %s, server id %d, disconnected by %s@%s", + router->service->name, + sptr->dcb->remote, + server_id, + slave->dcb->user, + slave->dcb->remote); - /* send server_id with disconnect state to client */ - n = blr_slave_send_disconnected_server(router, slave, server_id, 1); + /* send server_id with disconnect state to client */ + n = blr_slave_send_disconnected_server(router, slave, server_id, 1); - sptr->state = BLRS_UNREGISTERED; - dcb_close(sptr->dcb); + sptr->state = BLRS_UNREGISTERED; + dcb_close(sptr->dcb); - break; - } else { - sptr = sptr->next; - } - } + break; + } + else + { + sptr = sptr->next; + } + } - spinlock_release(&router->lock); + spinlock_release(&router->lock); - /** server id was not found - * send server_id with not found state to the client - */ - if (!server_found) - { - n = blr_slave_send_disconnected_server(router, slave, server_id, 0); - } + /** server id was not found + * send server_id with not found state to the client + */ + if (!server_found) + { + n = blr_slave_send_disconnected_server(router, slave, server_id, 0); + } - if (n == 0) { - MXS_ERROR("gwbuf memory allocation in " - "DISCONNECT SERVER server_id [%d]", - sptr->serverid); + if (n == 0) + { + MXS_ERROR("gwbuf memory allocation in " + "DISCONNECT SERVER server_id [%d]", + sptr->serverid); - blr_slave_send_error(router, slave, "Memory allocation error for DISCONNECT SERVER"); - } + blr_slave_send_error(router, slave, "Memory allocation error for DISCONNECT SERVER"); + } - return 1; + return 1; } /** * Send the response to the SQL command "DISCONNECT ALL' * and close the connection to all slave servers * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Non-zero if data was sent to the client + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Non-zero if data was sent to the client */ static int blr_slave_disconnect_all(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { - ROUTER_OBJECT *router_obj= router->service->router; - ROUTER_SLAVE *sptr; - char server_id[40]; - char state[40]; - uint8_t *ptr; - int len, seqno; - GWBUF *pkt; + ROUTER_OBJECT *router_obj = router->service->router; + ROUTER_SLAVE *sptr; + char server_id[40]; + char state[40]; + uint8_t *ptr; + int len, seqno; + GWBUF *pkt; - /* preparing output result */ - blr_slave_send_fieldcount(router, slave, 2); - blr_slave_send_columndef(router, slave, "server_id", BLR_TYPE_INT, 40, 2); - blr_slave_send_columndef(router, slave, "state", BLR_TYPE_STRING, 40, 3); - blr_slave_send_eof(router, slave, 4); - seqno = 5; + /* preparing output result */ + blr_slave_send_fieldcount(router, slave, 2); + blr_slave_send_columndef(router, slave, "server_id", BLR_TYPE_INT, 40, 2); + blr_slave_send_columndef(router, slave, "state", BLR_TYPE_STRING, 40, 3); + blr_slave_send_eof(router, slave, 4); + seqno = 5; - spinlock_acquire(&router->lock); - sptr = router->slaves; + spinlock_acquire(&router->lock); + sptr = router->slaves; - while (sptr) - { - /* skip servers with state = 0 */ - if (sptr->state == BLRS_REGISTERED || sptr->state == BLRS_DUMPING) - { - sprintf(server_id, "%d", sptr->serverid); - sprintf(state, "disconnected"); + while (sptr) + { + /* skip servers with state = 0 */ + if (sptr->state == BLRS_REGISTERED || sptr->state == BLRS_DUMPING) + { + sprintf(server_id, "%d", sptr->serverid); + sprintf(state, "disconnected"); - len = 5 + strlen(server_id) + strlen(state) + 1; + len = 5 + strlen(server_id) + strlen(state) + 1; - if ((pkt = gwbuf_alloc(len)) == NULL) { - MXS_ERROR("gwbuf memory allocation in " - "DISCONNECT ALL for [%s], server_id [%d]", - sptr->dcb->remote, sptr->serverid); + if ((pkt = gwbuf_alloc(len)) == NULL) + { + MXS_ERROR("gwbuf memory allocation in " + "DISCONNECT ALL for [%s], server_id [%d]", + sptr->dcb->remote, sptr->serverid); - spinlock_release(&router->lock); + spinlock_release(&router->lock); - blr_slave_send_error(router, slave, "Memory allocation error for DISCONNECT ALL"); + blr_slave_send_error(router, slave, "Memory allocation error for DISCONNECT ALL"); - return 1; - } + return 1; + } - MXS_NOTICE("%s: Slave %s, server id %d, disconnected by %s@%s", - router->service->name, - sptr->dcb->remote, sptr->serverid, slave->dcb->user, slave->dcb->remote); + MXS_NOTICE("%s: Slave %s, server id %d, disconnected by %s@%s", + router->service->name, + sptr->dcb->remote, sptr->serverid, slave->dcb->user, slave->dcb->remote); - ptr = GWBUF_DATA(pkt); - encode_value(ptr, len - 4, 24); // Add length of data packet + ptr = GWBUF_DATA(pkt); + encode_value(ptr, len - 4, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno++; // Sequence number in response - *ptr++ = strlen(server_id); // Length of result string - strncpy((char *)ptr, server_id, strlen(server_id)); // Result string - ptr += strlen(server_id); - *ptr++ = strlen(state); // Length of result string - strncpy((char *)ptr, state, strlen(state)); // Result string - ptr += strlen(state); + ptr += 3; + *ptr++ = seqno++; // Sequence number in response + *ptr++ = strlen(server_id); // Length of result string + strncpy((char *)ptr, server_id, strlen(server_id)); // Result string + ptr += strlen(server_id); + *ptr++ = strlen(state); // Length of result string + strncpy((char *)ptr, state, strlen(state)); // Result string + ptr += strlen(state); - slave->dcb->func.write(slave->dcb, pkt); + slave->dcb->func.write(slave->dcb, pkt); - sptr->state = BLRS_UNREGISTERED; - dcb_close(sptr->dcb); + sptr->state = BLRS_UNREGISTERED; + dcb_close(sptr->dcb); - } - sptr = sptr->next; - } + } + sptr = sptr->next; + } - spinlock_release(&router->lock); + spinlock_release(&router->lock); - blr_slave_send_eof(router, slave, seqno); + blr_slave_send_eof(router, slave, seqno); - return 1; + return 1; } /** * Send a MySQL OK packet to the slave backend * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response * * @return result of a write call, non-zero if write was successful */ @@ -2880,26 +3159,30 @@ blr_slave_disconnect_all(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) static int blr_slave_send_ok(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave) { -GWBUF *pkt; -uint8_t *ptr; -uint8_t ok_packet[] = {7, 0, 0, // Payload length - 1, // Seqno, - 0, // OK, - 0, 0, 2, 0, 0, 0}; + GWBUF *pkt; + uint8_t *ptr; + uint8_t ok_packet[] = { + 7, 0, 0, // Payload length + 1, // Seqno, + 0, // OK, + 0, 0, 2, 0, 0, 0 + }; - if ((pkt = gwbuf_alloc(sizeof(ok_packet))) == NULL) - return 0; + if ((pkt = gwbuf_alloc(sizeof(ok_packet))) == NULL) + { + return 0; + } - memcpy(GWBUF_DATA(pkt), ok_packet, sizeof(ok_packet)); + memcpy(GWBUF_DATA(pkt), ok_packet, sizeof(ok_packet)); - return slave->dcb->func.write(slave->dcb, pkt); + return slave->dcb->func.write(slave->dcb, pkt); } /** * Send a MySQL OK packet with a message to the slave backend * * @param router The binlog router instance - * @param message The message to send + * @param message The message to send * @param slave The slave server to which we are sending the response * * @return result of a write call, non-zero if write was successful @@ -2908,1058 +3191,1241 @@ uint8_t ok_packet[] = {7, 0, 0, // Payload length static int blr_slave_send_ok_message(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave, char *message) { -GWBUF *pkt; -uint8_t *ptr; + GWBUF *pkt; + uint8_t *ptr; - if ((pkt = gwbuf_alloc(11+strlen(message)+1)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - *ptr++ = 7 + strlen(message) +1; // Payload length - *ptr++ = 0; - *ptr++ = 0; - *ptr++ = 1; // Seqno - *ptr++ = 0; // ok - *ptr++ = 0; - *ptr++ = 0; + if ((pkt = gwbuf_alloc(11 + strlen(message) + 1)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + *ptr++ = 7 + strlen(message) + 1; // Payload length + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 1; // Seqno + *ptr++ = 0; // ok + *ptr++ = 0; + *ptr++ = 0; - *ptr++ = 2; + *ptr++ = 2; + *ptr++ = 0; + + if (strlen(message) == 0) + { *ptr++ = 0; + *ptr++ = 0; + } + else + { + *ptr++ = 1; + *ptr++ = 0; + *ptr++ = strlen(message); + strcpy((char *)ptr, message); + } - if(strlen(message) == 0) { - *ptr++ = 0; - *ptr++ = 0; - } else { - *ptr++ = 1; - *ptr++ = 0; - *ptr++ = strlen(message); - strcpy((char *)ptr, message); - } - - return slave->dcb->func.write(slave->dcb, pkt); + return slave->dcb->func.write(slave->dcb, pkt); } /** * Stop current replication from master * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response* - * @return Always 1 for error, for send_ok the bytes sent + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response* + * @return Always 1 for error, for send_ok the bytes sent * */ static int blr_stop_slave(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave) { - /* if unconfigured return an error */ - if (router->master_state == BLRM_UNCONFIGURED) { - blr_slave_send_warning_message(router, slave, "1255:Slave already has been stopped"); + /* if unconfigured return an error */ + if (router->master_state == BLRM_UNCONFIGURED) + { + blr_slave_send_warning_message(router, slave, "1255:Slave already has been stopped"); - return 1; - } + return 1; + } - /* if already stopped return an error */ - if (router->master_state == BLRM_SLAVE_STOPPED) { - blr_slave_send_warning_message(router, slave, "1255:Slave already has been stopped"); + /* if already stopped return an error */ + if (router->master_state == BLRM_SLAVE_STOPPED) + { + blr_slave_send_warning_message(router, slave, "1255:Slave already has been stopped"); - return 1; - } + return 1; + } - if (router->master) { - if (router->master->fd != -1 && router->master->state == DCB_STATE_POLLING) { - blr_master_close(router); - } - } + if (router->master) + { + if (router->master->fd != -1 && router->master->state == DCB_STATE_POLLING) + { + blr_master_close(router); + } + } - spinlock_acquire(&router->lock); + spinlock_acquire(&router->lock); - router->master_state = BLRM_SLAVE_STOPPED; + router->master_state = BLRM_SLAVE_STOPPED; - /* set last_safe_pos */ - router->last_safe_pos = router->binlog_position; + /* set last_safe_pos */ + router->last_safe_pos = router->binlog_position; - /** - * Set router->prevbinlog to router->binlog_name - * The FDE event with current filename may arrive after STOP SLAVE is received - */ + /** + * Set router->prevbinlog to router->binlog_name + * The FDE event with current filename may arrive after STOP SLAVE is received + */ - if (strcmp(router->binlog_name, router->prevbinlog) != 0) - strncpy(router->prevbinlog, router->binlog_name, BINLOG_FNAMELEN); + if (strcmp(router->binlog_name, router->prevbinlog) != 0) + { + strncpy(router->prevbinlog, router->binlog_name, BINLOG_FNAMELEN); + } - if (router->client) { - if (router->client->fd != -1 && router->client->state == DCB_STATE_POLLING) { - dcb_close(router->client); - router->client = NULL; - } - } + if (router->client) + { + if (router->client->fd != -1 && router->client->state == DCB_STATE_POLLING) + { + dcb_close(router->client); + router->client = NULL; + } + } - /* Discard the queued residual data */ - while (router->residual) - { - router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); - } - router->residual = NULL; + /* Discard the queued residual data */ + while (router->residual) + { + router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual)); + } + router->residual = NULL; - /* Now it is safe to unleash other threads on this router instance */ - router->reconnect_pending = 0; - router->active_logs = 0; + /* Now it is safe to unleash other threads on this router instance */ + router->reconnect_pending = 0; + router->active_logs = 0; - spinlock_release(&router->lock); + spinlock_release(&router->lock); - MXS_NOTICE("%s: STOP SLAVE executed by %s@%s. Disconnecting from master %s:%d, " - "read up to log %s, pos %lu, transaction safe pos %lu", - router->service->name, - slave->dcb->user, - slave->dcb->remote, - router->service->dbref->server->name, - router->service->dbref->server->port, - router->binlog_name, router->current_pos, router->binlog_position); + MXS_NOTICE("%s: STOP SLAVE executed by %s@%s. Disconnecting from master %s:%d, " + "read up to log %s, pos %lu, transaction safe pos %lu", + router->service->name, + slave->dcb->user, + slave->dcb->remote, + router->service->dbref->server->name, + router->service->dbref->server->port, + router->binlog_name, router->current_pos, router->binlog_position); - if (router->trx_safe && router->pending_transaction) { - char message[BINLOG_ERROR_MSG_LEN+1] = ""; - snprintf(message, BINLOG_ERROR_MSG_LEN, "1105:Stopped slave mid-transaction in binlog file %s, pos %lu, incomplete transaction starts at pos %lu", router->binlog_name, router->current_pos, router->binlog_position); + if (router->trx_safe && router->pending_transaction) + { + char message[BINLOG_ERROR_MSG_LEN + 1] = ""; + snprintf(message, BINLOG_ERROR_MSG_LEN, + "1105:Stopped slave mid-transaction in binlog file %s, " + "pos %lu, incomplete transaction starts at pos %lu", + router->binlog_name, router->current_pos, router->binlog_position); - return blr_slave_send_warning_message(router, slave, message); - } else { - return blr_slave_send_ok(router, slave); - } + return blr_slave_send_warning_message(router, slave, message); + } + else + { + return blr_slave_send_ok(router, slave); + } } /** * Start replication from current configured master * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @return Always 1 for error, for send_ok the bytes sent - * + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @return Always 1 for error, for send_ok the bytes sent + * */ static int blr_start_slave(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave) { - char path[PATH_MAX+1]=""; - int loaded; + char path[PATH_MAX + 1] = ""; + int loaded; - /* if unconfigured return an error */ - if (router->master_state == BLRM_UNCONFIGURED) { - blr_slave_send_error_packet(slave, "The server is not configured as slave; fix in config file or with CHANGE MASTER TO", (unsigned int)1200, NULL); + /* if unconfigured return an error */ + if (router->master_state == BLRM_UNCONFIGURED) + { + blr_slave_send_error_packet(slave, + "The server is not configured as slave; " + "fix in config file or with CHANGE MASTER TO", (unsigned int)1200, + NULL); - return 1; - } + return 1; + } - /* if running return an error */ - if (router->master_state != BLRM_UNCONNECTED && router->master_state != BLRM_SLAVE_STOPPED) { - blr_slave_send_warning_message(router, slave, "1254:Slave is already running"); + /* if running return an error */ + if (router->master_state != BLRM_UNCONNECTED && router->master_state != BLRM_SLAVE_STOPPED) + { + blr_slave_send_warning_message(router, slave, "1254:Slave is already running"); - return 1; - } + return 1; + } - spinlock_acquire(&router->lock); - router->master_state = BLRM_UNCONNECTED; - spinlock_release(&router->lock); + spinlock_acquire(&router->lock); + router->master_state = BLRM_UNCONNECTED; + spinlock_release(&router->lock); - /* create a new binlog or just use current one */ - if (strlen(router->prevbinlog) && strcmp(router->prevbinlog, router->binlog_name)) { - if (router->trx_safe && router->pending_transaction) { - char msg[BINLOG_ERROR_MSG_LEN+1] = ""; - char file[PATH_MAX+1] = ""; - struct stat statb; - unsigned long filelen = 0; - - snprintf(file, PATH_MAX, "%s/%s", router->binlogdir, router->prevbinlog); - - /* Get file size */ - if (stat(file, &statb) == 0) - filelen = statb.st_size; - - /* Prepare warning message */ - snprintf(msg, BINLOG_ERROR_MSG_LEN, "1105:Truncated partial transaction in file %s, starting at pos %lu, ending at pos %lu. File %s now has length %lu.", router->prevbinlog, router->last_safe_pos, filelen, router->prevbinlog, router->last_safe_pos); - /* Truncate previous binlog file to last_safe pos */ - truncate(file, router->last_safe_pos); - - /* Log it */ - MXS_WARNING("A transaction is still opened at pos %lu" - " File %s will be truncated. " - "Next binlog file is %s at pos %d, " - "START SLAVE is required again.", - router->last_safe_pos, - router->prevbinlog, - router->binlog_name, - 4); - - spinlock_acquire(&router->lock); - - router->pending_transaction = 0; - router->last_safe_pos = 0; - router->master_state = BLRM_UNCONNECTED; - router->current_pos = 4; - router->binlog_position = 4; - router->current_safe_event = 4; - - spinlock_release(&router->lock); - - /* Send warning message to mysql command */ - blr_slave_send_warning_message(router, slave, msg); - } - - /* create new one */ - blr_file_new_binlog(router, router->binlog_name); - } else { - if (router->binlog_fd == -1) { - /* create new one */ - blr_file_new_binlog(router, router->binlog_name); - } else { - /* use existing one */ - blr_file_use_binlog(router, router->binlog_name); - } - } - - blr_start_master(router); - - MXS_NOTICE("%s: START SLAVE executed by %s@%s. Trying connection to master %s:%d, " - "binlog %s, pos %lu, transaction safe pos %lu", - router->service->name, - slave->dcb->user, - slave->dcb->remote, - router->service->dbref->server->name, - router->service->dbref->server->port, - router->binlog_name, - router->current_pos, router->binlog_position); - - /* File path for router cached authentication data */ - strcpy(path, router->binlogdir); - strncat(path, "/cache", PATH_MAX); - - strncat(path, "/dbusers", PATH_MAX); - - /* Try loading dbusers from configured backends */ - loaded = load_mysql_users(router->service); - - if (loaded < 0) + /* create a new binlog or just use current one */ + if (strlen(router->prevbinlog) && strcmp(router->prevbinlog, router->binlog_name)) + { + if (router->trx_safe && router->pending_transaction) { - MXS_ERROR("Unable to load users for service %s", - router->service->name); - } else { - /* update cached data */ - if (loaded > 0) - blr_save_dbusers(router); - } + char msg[BINLOG_ERROR_MSG_LEN + 1] = ""; + char file[PATH_MAX + 1] = ""; + struct stat statb; + unsigned long filelen = 0; - return blr_slave_send_ok(router, slave); + snprintf(file, PATH_MAX, "%s/%s", router->binlogdir, router->prevbinlog); + + /* Get file size */ + if (stat(file, &statb) == 0) + { + filelen = statb.st_size; + } + + /* Prepare warning message */ + snprintf(msg, BINLOG_ERROR_MSG_LEN, + "1105:Truncated partial transaction in file %s, starting at pos %lu, " + "ending at pos %lu. File %s now has length %lu.", + router->prevbinlog, + router->last_safe_pos, + filelen, + router->prevbinlog, + router->last_safe_pos); + /* Truncate previous binlog file to last_safe pos */ + truncate(file, router->last_safe_pos); + + /* Log it */ + MXS_WARNING("A transaction is still opened at pos %lu" + " File %s will be truncated. " + "Next binlog file is %s at pos %d, " + "START SLAVE is required again.", + router->last_safe_pos, + router->prevbinlog, + router->binlog_name, + 4); + + spinlock_acquire(&router->lock); + + router->pending_transaction = 0; + router->last_safe_pos = 0; + router->master_state = BLRM_UNCONNECTED; + router->current_pos = 4; + router->binlog_position = 4; + router->current_safe_event = 4; + + spinlock_release(&router->lock); + + /* Send warning message to mysql command */ + blr_slave_send_warning_message(router, slave, msg); + } + + /* create new one */ + blr_file_new_binlog(router, router->binlog_name); + } + else + { + if (router->binlog_fd == -1) + { + /* create new one */ + blr_file_new_binlog(router, router->binlog_name); + } + else + { + /* use existing one */ + blr_file_use_binlog(router, router->binlog_name); + } + } + + blr_start_master(router); + + MXS_NOTICE("%s: START SLAVE executed by %s@%s. Trying connection to master %s:%d, " + "binlog %s, pos %lu, transaction safe pos %lu", + router->service->name, + slave->dcb->user, + slave->dcb->remote, + router->service->dbref->server->name, + router->service->dbref->server->port, + router->binlog_name, + router->current_pos, router->binlog_position); + + /* File path for router cached authentication data */ + strcpy(path, router->binlogdir); + strncat(path, "/cache", PATH_MAX); + + strncat(path, "/dbusers", PATH_MAX); + + /* Try loading dbusers from configured backends */ + loaded = load_mysql_users(router->service); + + if (loaded < 0) + { + MXS_ERROR("Unable to load users for service %s", + router->service->name); + } + else + { + /* update cached data */ + if (loaded > 0) + { + blr_save_dbusers(router); + } + } + + return blr_slave_send_ok(router, slave); } /** * Construct an error packet reply with specified code and status * - * @param slave The slave server instance - * @param msg The error message to send - * @param err_num The error number to send - * @param status The error status + * @param slave The slave server instance + * @param msg The error message to send + * @param err_num The error number to send + * @param status The error status */ static void blr_slave_send_error_packet(ROUTER_SLAVE *slave, char *msg, unsigned int err_num, char *status) { -GWBUF *pkt; -unsigned char *data; -int len; -unsigned int mysql_errno = 0; -char *mysql_state; -uint8_t mysql_err[2]; + GWBUF *pkt; + unsigned char *data; + int len; + unsigned int mysql_errno = 0; + char *mysql_state; + uint8_t mysql_err[2]; - if ((pkt = gwbuf_alloc(strlen(msg) + 13)) == NULL) - return; + if ((pkt = gwbuf_alloc(strlen(msg) + 13)) == NULL) + { + return; + } - if (status != NULL) - mysql_state = status; - else - mysql_state = "HY000"; + if (status != NULL) + { + mysql_state = status; + } + else + { + mysql_state = "HY000"; + } - if (err_num > 0) - mysql_errno = err_num; - else - mysql_errno = (unsigned int)2003; + if (err_num > 0) + { + mysql_errno = err_num; + } + else + { + mysql_errno = (unsigned int)2003; + } - data = GWBUF_DATA(pkt); - len = strlen(msg) + 9; + data = GWBUF_DATA(pkt); + len = strlen(msg) + 9; - encode_value(&data[0], len, 24); // Payload length + encode_value(&data[0], len, 24); // Payload length - data[3] = 1; // Sequence id + data[3] = 1; // Sequence id - data[4] = 0xff; // Error indicator + data[4] = 0xff; // Error indicator - encode_value(&data[5], mysql_errno, 16);// Error Code + encode_value(&data[5], mysql_errno, 16);// Error Code - data[7] = '#'; // Status message first char - strncpy((char *)&data[8], mysql_state, 5); // Status message + data[7] = '#'; // Status message first char + strncpy((char *)&data[8], mysql_state, 5); // Status message - memcpy(&data[13], msg, strlen(msg)); // Error Message + memcpy(&data[13], msg, strlen(msg)); // Error Message - slave->dcb->func.write(slave->dcb, pkt); + slave->dcb->func.write(slave->dcb, pkt); } /** * handle a 'change master' operation * - * @param router The router instance - * @param command The change master SQL command - * @param error The error message, preallocated BINLOG_ERROR_MSG_LEN + 1 bytes - * @return 0 on success, 1 on success with new binlog, -1 on failure + * @param router The router instance + * @param command The change master SQL command + * @param error The error message, preallocated BINLOG_ERROR_MSG_LEN + 1 bytes + * @return 0 on success, 1 on success with new binlog, -1 on failure */ static -int blr_handle_change_master(ROUTER_INSTANCE* router, char *command, char *error) { - char *master_logfile = NULL; - char *master_log_pos = NULL; - int change_binlog = 0; - long long pos = 0; - MASTER_SERVER_CFG *current_master = NULL; - CHANGE_MASTER_OPTIONS change_master; - int parse_ret; - char *cmd_ptr; - char *cmd_string; - - if ((cmd_ptr = strcasestr(command, "TO")) == NULL) { - strncpy(error, "statement doesn't have the CHANGE MASTER TO syntax", BINLOG_ERROR_MSG_LEN); - return -1; - } +int blr_handle_change_master(ROUTER_INSTANCE* router, char *command, char *error) +{ + char *master_logfile = NULL; + char *master_log_pos = NULL; + int change_binlog = 0; + long long pos = 0; + MASTER_SERVER_CFG *current_master = NULL; + CHANGE_MASTER_OPTIONS change_master; + int parse_ret; + char *cmd_ptr; + char *cmd_string; - if ((cmd_string = strdup(cmd_ptr + 2)) == NULL) { - strncpy(error, "error allocating memory for statement parsing", BINLOG_ERROR_MSG_LEN); - MXS_ERROR("%s: %s", router->service->name, error); + if ((cmd_ptr = strcasestr(command, "TO")) == NULL) + { + strncpy(error, "statement doesn't have the CHANGE MASTER TO syntax", BINLOG_ERROR_MSG_LEN); + return -1; + } - return -1; - } + if ((cmd_string = strdup(cmd_ptr + 2)) == NULL) + { + strncpy(error, "error allocating memory for statement parsing", BINLOG_ERROR_MSG_LEN); + MXS_ERROR("%s: %s", router->service->name, error); - /* Parse SQL command and populate with found options the change_master struct */ - memset(&change_master, 0, sizeof(change_master)); + return -1; + } - parse_ret = blr_parse_change_master_command(cmd_string, error, &change_master); + /* Parse SQL command and populate with found options the change_master struct */ + memset(&change_master, 0, sizeof(change_master)); - free(cmd_string); + parse_ret = blr_parse_change_master_command(cmd_string, error, &change_master); - if (parse_ret) { - MXS_ERROR("%s CHANGE MASTER TO parse error: %s", router->service->name, error); + free(cmd_string); - blr_master_free_parsed_options(&change_master); + if (parse_ret) + { + MXS_ERROR("%s CHANGE MASTER TO parse error: %s", router->service->name, error); - return -1; - } + blr_master_free_parsed_options(&change_master); - /* allocate struct for current replication parameters */ - current_master = (MASTER_SERVER_CFG *)calloc(1, sizeof(MASTER_SERVER_CFG)); + return -1; + } - if (!current_master) { - strncpy(error, "error allocating memory for blr_master_get_config", BINLOG_ERROR_MSG_LEN); - MXS_ERROR("%s: %s", router->service->name, error); + /* allocate struct for current replication parameters */ + current_master = (MASTER_SERVER_CFG *)calloc(1, sizeof(MASTER_SERVER_CFG)); - blr_master_free_parsed_options(&change_master); + if (!current_master) + { + strncpy(error, "error allocating memory for blr_master_get_config", BINLOG_ERROR_MSG_LEN); + MXS_ERROR("%s: %s", router->service->name, error); - return -1; - } + blr_master_free_parsed_options(&change_master); - /* save data */ - blr_master_get_config(router, current_master); + return -1; + } - spinlock_acquire(&router->lock); + /* save data */ + blr_master_get_config(router, current_master); - /* - * Change values in the router->service->dbref->server structure - * Change filename and position in the router structure - */ + spinlock_acquire(&router->lock); - /* Set new binlog position from parsed SQL command */ - master_log_pos = change_master.binlog_pos; - if (master_log_pos == NULL) { - pos = 0; - } else { - pos = atoll(master_log_pos); + /* + * Change values in the router->service->dbref->server structure + * Change filename and position in the router structure + */ + + /* Set new binlog position from parsed SQL command */ + master_log_pos = change_master.binlog_pos; + if (master_log_pos == NULL) + { + pos = 0; + } + else + { + pos = atoll(master_log_pos); + } + + /* Change the replication user */ + blr_set_master_user(router, change_master.user); + + /* Change the replication password */ + blr_set_master_password(router, change_master.password); + + /* Change the master name/address */ + blr_set_master_hostname(router, change_master.host); + + /* Change the master port */ + blr_set_master_port(router, change_master.port); + + /** + * Change the binlog filename to request from master + * New binlog file could be the next one or current one + */ + master_logfile = blr_set_master_logfile(router, change_master.binlog_file, error); + + if (master_logfile == NULL && router->master_state == BLRM_UNCONFIGURED) + { + /* if there is another error message keep it */ + if (!strlen(error)) + { + strcpy(error, "Router is not configured for master connection, MASTER_LOG_FILE is required"); } - /* Change the replication user */ - blr_set_master_user(router, change_master.user); + MXS_ERROR("%s: %s", router->service->name, error); - /* Change the replication password */ - blr_set_master_password(router, change_master.password); + /* restore previous master_host and master_port */ + blr_master_restore_config(router, current_master); - /* Change the master name/address */ - blr_set_master_hostname(router, change_master.host); + blr_master_free_parsed_options(&change_master); - /* Change the master port */ - blr_set_master_port(router, change_master.port); + spinlock_release(&router->lock); - /** - * Change the binlog filename to request from master - * New binlog file could be the next one or current one - */ - master_logfile = blr_set_master_logfile(router, change_master.binlog_file, error); + return -1; + } - if (master_logfile == NULL && router->master_state == BLRM_UNCONFIGURED) { - /* if there is another error message keep it */ - if (!strlen(error)) { - strcpy(error, "Router is not configured for master connection, MASTER_LOG_FILE is required"); - } + /** + * If MASTER_LOG_FILE is not set + * and master connection is configured + * set master_logfile to current binlog_name + */ + if (master_logfile == NULL) + { + /* if errors returned */ + if (strlen(error)) + { - MXS_ERROR("%s: %s", router->service->name, error); + MXS_ERROR("%s: %s", router->service->name, error); - /* restore previous master_host and master_port */ - blr_master_restore_config(router, current_master); + /* restore previous master_host and master_port */ + blr_master_restore_config(router, current_master); - blr_master_free_parsed_options(&change_master); + blr_master_free_parsed_options(&change_master); - spinlock_release(&router->lock); + spinlock_release(&router->lock); - return -1; - } + return -1; + } + else + { + /* If not set by CHANGE MASTER, use current binlog if configured */ + if (router->master_state != BLRM_UNCONFIGURED) + { + master_logfile = strdup(router->binlog_name); + } + } + } - /** - * If MASTER_LOG_FILE is not set - * and master connection is configured - * set master_logfile to current binlog_name - */ - if (master_logfile == NULL) { - /* if errors returned */ - if (strlen(error)) { + /** + * If master connection is configured check new binlog name: + * If binlog name has changed to next one only position 4 is allowed + */ - MXS_ERROR("%s: %s", router->service->name, error); + if (strcmp(master_logfile, router->binlog_name) && router->master_state != BLRM_UNCONFIGURED) + { + int return_error = 0; + if (master_log_pos == NULL) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, + "Please provide an explicit MASTER_LOG_POS for new MASTER_LOG_FILE %s: " + "Permitted binlog pos is %d. Current master_log_file=%s, master_log_pos=%lu", + master_logfile, + 4, + router->binlog_name, + router->current_pos); - /* restore previous master_host and master_port */ - blr_master_restore_config(router, current_master); + return_error = 1; + } + else + { + if (pos != 4) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, + "Can not set MASTER_LOG_POS to %s for MASTER_LOG_FILE %s: " + "Permitted binlog pos is %d. Current master_log_file=%s, master_log_pos=%lu", + master_log_pos, + master_logfile, + 4, + router->binlog_name, + router->current_pos); - blr_master_free_parsed_options(&change_master); + return_error = 1; + } + } - spinlock_release(&router->lock); + /* return an error or set new binlog name at pos 4 */ + if (return_error) + { - return -1; - } else { - /* If not set by CHANGE MASTER, use current binlog if configured */ - if (router->master_state != BLRM_UNCONFIGURED) { - master_logfile = strdup(router->binlog_name); - } - } - } + MXS_ERROR("%s: %s", router->service->name, error); - /** - * If master connection is configured check new binlog name: - * If binlog name has changed to next one only position 4 is allowed - */ + /* restore previous master_host and master_port */ + blr_master_restore_config(router, current_master); - if (strcmp(master_logfile, router->binlog_name) && router->master_state != BLRM_UNCONFIGURED) { - int return_error = 0; - if (master_log_pos == NULL) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "Please provide an explicit MASTER_LOG_POS for new MASTER_LOG_FILE %s: " - "Permitted binlog pos is %d. Current master_log_file=%s, master_log_pos=%lu", - master_logfile, - 4, - router->binlog_name, - router->current_pos); + blr_master_free_parsed_options(&change_master); - return_error = 1; - } else { - if (pos != 4) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "Can not set MASTER_LOG_POS to %s for MASTER_LOG_FILE %s: " - "Permitted binlog pos is %d. Current master_log_file=%s, master_log_pos=%lu", - master_log_pos, - master_logfile, - 4, - router->binlog_name, - router->current_pos); + free(master_logfile); - return_error = 1; - } - } + spinlock_release(&router->lock); - /* return an error or set new binlog name at pos 4 */ - if (return_error) { + return -1; - MXS_ERROR("%s: %s", router->service->name, error); + } + else + { + /* set new filename at pos 4 */ + memset(router->binlog_name, '\0', sizeof(router->binlog_name)); + strncpy(router->binlog_name, master_logfile, BINLOG_FNAMELEN); - /* restore previous master_host and master_port */ - blr_master_restore_config(router, current_master); + router->current_pos = 4; + router->binlog_position = 4; + router->current_safe_event = 4; - blr_master_free_parsed_options(&change_master); + /* close current file binlog file, next start slave will create the new one */ + fsync(router->binlog_fd); + close(router->binlog_fd); + router->binlog_fd = -1; - free(master_logfile); + MXS_INFO("%s: New MASTER_LOG_FILE is [%s]", + router->service->name, + router->binlog_name); + } + } + else + { + /** + * Same binlog or master connection not configured + * Position cannot be different from current pos or 4 (if BLRM_UNCONFIGURED) + */ + int return_error = 0; - spinlock_release(&router->lock); + if (router->master_state == BLRM_UNCONFIGURED) + { + if (master_log_pos != NULL && pos != 4) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, "Can not set MASTER_LOG_POS to %s: " + "Permitted binlog pos is 4. Specified master_log_file=%s", + master_log_pos, + master_logfile); - return -1; + return_error = 1; + } - } else { - /* set new filename at pos 4 */ - memset(router->binlog_name, '\0', sizeof(router->binlog_name)); - strncpy(router->binlog_name, master_logfile, BINLOG_FNAMELEN); + } + else + { + if (master_log_pos != NULL && pos != router->current_pos) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, "Can not set MASTER_LOG_POS to %s: " + "Permitted binlog pos is %lu. Current master_log_file=%s, master_log_pos=%lu", + master_log_pos, + router->current_pos, + router->binlog_name, + router->current_pos); - router->current_pos = 4; - router->binlog_position = 4; - router->current_safe_event = 4; + return_error = 1; + } + } - /* close current file binlog file, next start slave will create the new one */ - fsync(router->binlog_fd); - close(router->binlog_fd); - router->binlog_fd = -1; + /* log error and return */ + if (return_error) + { + MXS_ERROR("%s: %s", router->service->name, error); - MXS_INFO("%s: New MASTER_LOG_FILE is [%s]", - router->service->name, - router->binlog_name); - } - } else { - /** - * Same binlog or master connection not configured - * Position cannot be different from current pos or 4 (if BLRM_UNCONFIGURED) - */ - int return_error = 0; + /* restore previous master_host and master_port */ + blr_master_restore_config(router, current_master); - if (router->master_state == BLRM_UNCONFIGURED) { - if (master_log_pos != NULL && pos != 4) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "Can not set MASTER_LOG_POS to %s: " - "Permitted binlog pos is 4. Specified master_log_file=%s", - master_log_pos, - master_logfile); - - return_error = 1; - } + blr_master_free_parsed_options(&change_master); - } else { - if (master_log_pos != NULL && pos != router->current_pos) { - snprintf(error, BINLOG_ERROR_MSG_LEN, "Can not set MASTER_LOG_POS to %s: " - "Permitted binlog pos is %lu. Current master_log_file=%s, master_log_pos=%lu", - master_log_pos, - router->current_pos, - router->binlog_name, - router->current_pos); + free(master_logfile); - return_error = 1; - } - } + spinlock_release(&router->lock); - /* log error and return */ - if (return_error) { - MXS_ERROR("%s: %s", router->service->name, error); + return -1; + } + else + { + /** + * no pos change, set it to 4 if BLRM_UNCONFIGURED + * Also set binlog name if UNCOFIGURED + */ + if (router->master_state == BLRM_UNCONFIGURED) + { + router->current_pos = 4; + router->binlog_position = 4; + router->current_safe_event = 4; + memset(router->binlog_name, '\0', sizeof(router->binlog_name)); + strncpy(router->binlog_name, master_logfile, BINLOG_FNAMELEN); - /* restore previous master_host and master_port */ - blr_master_restore_config(router, current_master); + MXS_INFO("%s: New MASTER_LOG_FILE is [%s]", + router->service->name, + router->binlog_name); + } - blr_master_free_parsed_options(&change_master); + MXS_INFO("%s: New MASTER_LOG_POS is [%lu]", + router->service->name, + router->current_pos); + } + } - free(master_logfile); + /* Log config changes (without passwords) */ - spinlock_release(&router->lock); + MXS_NOTICE("%s: 'CHANGE MASTER TO executed'. Previous state " + "MASTER_HOST='%s', MASTER_PORT=%i, MASTER_LOG_FILE='%s', " + "MASTER_LOG_POS=%lu, MASTER_USER='%s'. New state is MASTER_HOST='%s', " + "MASTER_PORT=%i, MASTER_LOG_FILE='%s', MASTER_LOG_POS=%lu, MASTER_USER='%s'", + router->service->name, + current_master->host, current_master->port, current_master->logfile, + current_master->pos, current_master->user, + router->service->dbref->server->name, + router->service->dbref->server->port, + router->binlog_name, + router->current_pos, + router->user); - return -1; - } else { - /** - * no pos change, set it to 4 if BLRM_UNCONFIGURED - * Also set binlog name if UNCOFIGURED - */ - if (router->master_state == BLRM_UNCONFIGURED) { - router->current_pos = 4; - router->binlog_position = 4; - router->current_safe_event = 4; - memset(router->binlog_name, '\0', sizeof(router->binlog_name)); - strncpy(router->binlog_name, master_logfile, BINLOG_FNAMELEN); + blr_master_free_config(current_master); - MXS_INFO("%s: New MASTER_LOG_FILE is [%s]", - router->service->name, - router->binlog_name); - } + blr_master_free_parsed_options(&change_master); - MXS_INFO("%s: New MASTER_LOG_POS is [%lu]", - router->service->name, - router->current_pos); - } - } + free(master_logfile); - /* Log config changes (without passwords) */ + if (router->master_state == BLRM_UNCONFIGURED) + { + change_binlog = 1; + } - MXS_NOTICE("%s: 'CHANGE MASTER TO executed'. Previous state " - "MASTER_HOST='%s', MASTER_PORT=%i, MASTER_LOG_FILE='%s', " - "MASTER_LOG_POS=%lu, MASTER_USER='%s'. New state is MASTER_HOST='%s', " - "MASTER_PORT=%i, MASTER_LOG_FILE='%s', MASTER_LOG_POS=%lu, MASTER_USER='%s'", - router->service->name, - current_master->host, current_master->port, current_master->logfile, - current_master->pos, current_master->user, - router->service->dbref->server->name, - router->service->dbref->server->port, - router->binlog_name, - router->current_pos, - router->user); + spinlock_release(&router->lock); - blr_master_free_config(current_master); - - blr_master_free_parsed_options(&change_master); - - free(master_logfile); - - if (router->master_state == BLRM_UNCONFIGURED) - change_binlog = 1; - - spinlock_release(&router->lock); - - return change_binlog; + return change_binlog; } /* * Set new master hostname * - * @param router Current router instance - * @param hostname The hostname to set - * @return 1 for applied change, 0 otherwise + * @param router Current router instance + * @param hostname The hostname to set + * @return 1 for applied change, 0 otherwise */ static int -blr_set_master_hostname(ROUTER_INSTANCE *router, char *hostname) { +blr_set_master_hostname(ROUTER_INSTANCE *router, char *hostname) +{ + if (hostname) + { + char *ptr; + char *end; + ptr = strchr(hostname, '\''); + if (ptr) + { + ptr++; + } + else + { + ptr = hostname; + } + end = strchr(ptr, '\''); + if (end) + { + *end = '\0'; + } - if (hostname) { - char *ptr; - char *end; - ptr = strchr(hostname, '\''); - if (ptr) - ptr++; - else - ptr = hostname; - end = strchr(ptr, '\''); - if (end) - *end ='\0'; + server_update_address(router->service->dbref->server, ptr); - server_update_address(router->service->dbref->server, ptr); + MXS_INFO("%s: New MASTER_HOST is [%s]", + router->service->name, + router->service->dbref->server->name); - MXS_INFO("%s: New MASTER_HOST is [%s]", - router->service->name, - router->service->dbref->server->name); + return 1; + } - return 1; - } - - return 0; + return 0; } /** * Set new master port * - * @param router Current router instance - * @param port The server TCP port - * @return 1 for applied change, 0 otherwise + * @param router Current router instance + * @param port The server TCP port + * @return 1 for applied change, 0 otherwise */ static int -blr_set_master_port(ROUTER_INSTANCE *router, char *port) { - unsigned short new_port; +blr_set_master_port(ROUTER_INSTANCE *router, char *port) +{ + unsigned short new_port; - if (port != NULL) { + if (port != NULL) + { + new_port = atoi(port); - new_port = atoi(port); + if (new_port) + { + server_update_port(router->service->dbref->server, new_port); - if (new_port) { - server_update_port(router->service->dbref->server, new_port); + MXS_INFO("%s: New MASTER_PORT is [%i]", + router->service->name, + router->service->dbref->server->port); - MXS_INFO("%s: New MASTER_PORT is [%i]", - router->service->name, - router->service->dbref->server->port); + return 1; + } + } - return 1; - } - } - - return 0; + return 0; } /* * Set new master binlog file * - * The routing must be called holding router->lock + * The routing must be called holding router->lock * - * @param router Current router instance - * @param filename Binlog file name - * @param error The error msg for command, pre-allocated BINLOG_ERROR_MSG_LEN + 1 bytes - * @return New binlog file or NULL on error + * @param router Current router instance + * @param filename Binlog file name + * @param error The error msg for command, pre-allocated BINLOG_ERROR_MSG_LEN + 1 bytes + * @return New binlog file or NULL on error */ static char * -blr_set_master_logfile(ROUTER_INSTANCE *router, char *filename, char *error) { - char *new_binlog_file = NULL; +blr_set_master_logfile(ROUTER_INSTANCE *router, char *filename, char *error) +{ + char *new_binlog_file = NULL; - if (filename) { - long next_binlog_seqname; - char *file_ptr; - char *end; + if (filename) + { + long next_binlog_seqname; + char *file_ptr; + char *end; - file_ptr = strchr(filename, '\''); - if (file_ptr) - file_ptr++; - else - file_ptr = filename; + file_ptr = strchr(filename, '\''); + if (file_ptr) + { + file_ptr++; + } + else + { + file_ptr = filename; + } - end = strchr(file_ptr, '\''); - if (end) - *end ='\0'; + end = strchr(file_ptr, '\''); + if (end) + { + *end = '\0'; + } - /* check binlog filename format */ - end = strchr(file_ptr, '.'); + /* check binlog filename format */ + end = strchr(file_ptr, '.'); - if (!end) { + if (!end) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, "%s: selected binlog [%s] is not in the format" + " '%s.yyyyyy'", + router->service->name, + file_ptr, + router->fileroot); - snprintf(error, BINLOG_ERROR_MSG_LEN, "%s: selected binlog [%s] is not in the format" - " '%s.yyyyyy'", - router->service->name, - file_ptr, - router->fileroot); + return NULL; + } - return NULL; - } + end++; - end++; + if (router->master_state == BLRM_UNCONFIGURED) + { + char *stem_end; + stem_end = strrchr(file_ptr, '.'); + /* set filestem */ + if (stem_end) + { + if (router->fileroot) + { + free(router->fileroot); + } + router->fileroot = strndup(file_ptr, stem_end - file_ptr); + } - if (router->master_state == BLRM_UNCONFIGURED) { - char *stem_end; - stem_end = strrchr(file_ptr, '.'); - /* set filestem */ - if (stem_end) { - if (router->fileroot) - free(router->fileroot); - router->fileroot = strndup(file_ptr, stem_end-file_ptr); - } + /* return new filename */ + new_binlog_file = strdup(file_ptr); - /* return new filename */ - new_binlog_file = strdup(file_ptr); + return new_binlog_file; + } - return new_binlog_file; - } - - /* get next binlog file name, assuming filestem is the same */ - next_binlog_seqname = blr_file_get_next_binlogname(router); + /* get next binlog file name, assuming filestem is the same */ + next_binlog_seqname = blr_file_get_next_binlogname(router); - if (!next_binlog_seqname) { + if (!next_binlog_seqname) + { - snprintf(error, BINLOG_ERROR_MSG_LEN, "%s: cannot get the next MASTER_LOG_FILE name from current binlog [%s]", - router->service->name, - router->binlog_name); + snprintf(error, BINLOG_ERROR_MSG_LEN, + "%s: cannot get the next MASTER_LOG_FILE name from current binlog [%s]", + router->service->name, + router->binlog_name); - return NULL; - } + return NULL; + } - /* Compare binlog file name with current one */ - if (strcmp(router->binlog_name, file_ptr) == 0) { - /* No binlog name change, eventually new position will be checked later */ - } else { - /* - * This is a new binlog file request - * If file is not the next one return an error - */ - if (atoi(end) != next_binlog_seqname) { + /* Compare binlog file name with current one */ + if (strcmp(router->binlog_name, file_ptr) == 0) + { + /* No binlog name change, eventually new position will be checked later */ + } + else + { + /* + * This is a new binlog file request + * If file is not the next one return an error + */ + if (atoi(end) != next_binlog_seqname) + { + snprintf(error, BINLOG_ERROR_MSG_LEN, + "Can not set MASTER_LOG_FILE to %s: Permitted binlog file names are " + "%s or %s.%06li. Current master_log_file=%s, master_log_pos=%lu", + file_ptr, + router->binlog_name, + router->fileroot, + next_binlog_seqname, + router->binlog_name, + router->current_pos); - snprintf(error, BINLOG_ERROR_MSG_LEN, "Can not set MASTER_LOG_FILE to %s: Permitted binlog file names are " - "%s or %s.%06li. Current master_log_file=%s, master_log_pos=%lu", - file_ptr, - router->binlog_name, - router->fileroot, - next_binlog_seqname, - router->binlog_name, - router->current_pos); + return NULL; + } - return NULL; - } + /* Binlog file name succesfully changed */ + } - /* Binlog file name succesfully changed */ - } + /* allocate new filename */ + new_binlog_file = strdup(file_ptr); - /* allocate new filename */ - new_binlog_file = strdup(file_ptr); + } - } - - return new_binlog_file; + return new_binlog_file; } /** * Get master configuration store it * - * @param router Current router instance - * @param curr_master Preallocated struct to fill + * @param router Current router instance + * @param curr_master Preallocated struct to fill */ static void -blr_master_get_config(ROUTER_INSTANCE *router, MASTER_SERVER_CFG *curr_master) { - curr_master->port = router->service->dbref->server->port; - curr_master->host = strdup(router->service->dbref->server->name); - curr_master->pos = router->current_pos; - curr_master->safe_pos = router->binlog_position; - strncpy(curr_master->logfile, router->binlog_name, BINLOG_FNAMELEN); - curr_master->user = strdup(router->user); - curr_master->password = strdup(router->password); - curr_master->filestem = strdup(router->fileroot); +blr_master_get_config(ROUTER_INSTANCE *router, MASTER_SERVER_CFG *curr_master) +{ + curr_master->port = router->service->dbref->server->port; + curr_master->host = strdup(router->service->dbref->server->name); + curr_master->pos = router->current_pos; + curr_master->safe_pos = router->binlog_position; + strncpy(curr_master->logfile, router->binlog_name, BINLOG_FNAMELEN); + curr_master->user = strdup(router->user); + curr_master->password = strdup(router->password); + curr_master->filestem = strdup(router->fileroot); } /** * Free a master configuration struct * - * @param master_cfg Saved master configuration to free + * @param master_cfg Saved master configuration to free */ static void -blr_master_free_config(MASTER_SERVER_CFG *master_cfg) { - free(master_cfg->host); - free(master_cfg->user); - free(master_cfg->password); - free(master_cfg->filestem); +blr_master_free_config(MASTER_SERVER_CFG *master_cfg) +{ + free(master_cfg->host); + free(master_cfg->user); + free(master_cfg->password); + free(master_cfg->filestem); - free(master_cfg); + free(master_cfg); } /** * Restore master configuration values for host and port * - * @param router Current router instance - * @param prev_master Previous saved master configuration + * @param router Current router instance + * @param prev_master Previous saved master configuration */ static void -blr_master_restore_config(ROUTER_INSTANCE *router, MASTER_SERVER_CFG *prev_master) { - server_update_address(router->service->dbref->server, prev_master->host); - server_update_port(router->service->dbref->server, prev_master->port); +blr_master_restore_config(ROUTER_INSTANCE *router, MASTER_SERVER_CFG *prev_master) +{ + server_update_address(router->service->dbref->server, prev_master->host); + server_update_port(router->service->dbref->server, prev_master->port); - blr_master_free_config(prev_master); + blr_master_free_config(prev_master); } /** * Set all the master configuration fields to empty values * - * @param router Current router instance + * @param router Current router instance */ static void -blr_master_set_empty_config(ROUTER_INSTANCE *router) { - server_update_address(router->service->dbref->server, "none"); - server_update_port(router->service->dbref->server, (unsigned short)3306); +blr_master_set_empty_config(ROUTER_INSTANCE *router) +{ + server_update_address(router->service->dbref->server, "none"); + server_update_port(router->service->dbref->server, (unsigned short)3306); - router->current_pos = 4; - router->binlog_position = 4; - router->current_safe_event = 4; - strcpy(router->binlog_name, ""); + router->current_pos = 4; + router->binlog_position = 4; + router->current_safe_event = 4; + strcpy(router->binlog_name, ""); } /** * Restore all master configuration values * - * @param router Current router instance - * @param prev_master Previous saved master configuration + * @param router Current router instance + * @param prev_master Previous saved master configuration */ static void -blr_master_apply_config(ROUTER_INSTANCE *router, MASTER_SERVER_CFG *prev_master) { - server_update_address(router->service->dbref->server, prev_master->host); - server_update_port(router->service->dbref->server, prev_master->port); - router->current_pos = prev_master->pos; - router->binlog_position = prev_master->safe_pos; - router->current_safe_event = prev_master->safe_pos; - strcpy(router->binlog_name, prev_master->logfile); - if (router->user) { - free(router->user); - router->user = strdup(prev_master->user); - } - if (router->password) { - free(router->password); - router->password = strdup(prev_master->password); - } - if (router->fileroot) { - free(router->fileroot); - router->fileroot = strdup(prev_master->filestem); - } +blr_master_apply_config(ROUTER_INSTANCE *router, MASTER_SERVER_CFG *prev_master) +{ + server_update_address(router->service->dbref->server, prev_master->host); + server_update_port(router->service->dbref->server, prev_master->port); + router->current_pos = prev_master->pos; + router->binlog_position = prev_master->safe_pos; + router->current_safe_event = prev_master->safe_pos; + strcpy(router->binlog_name, prev_master->logfile); + if (router->user) + { + free(router->user); + router->user = strdup(prev_master->user); + } + if (router->password) + { + free(router->password); + router->password = strdup(prev_master->password); + } + if (router->fileroot) + { + free(router->fileroot); + router->fileroot = strdup(prev_master->filestem); + } } /** * Change the replication user * - * @param router Current router instance - * @param user The userto set - * @return 1 for applied change, 0 otherwise + * @param router Current router instance + * @param user The userto set + * @return 1 for applied change, 0 otherwise */ static int -blr_set_master_user(ROUTER_INSTANCE *router, char *user) { - - if (user != NULL) { - char *ptr; - char *end; - ptr = strchr(user, '\''); - if (ptr) - ptr++; - else - ptr = user; - - end = strchr(ptr, '\''); - if (end) - *end ='\0'; - - if (router->user) { - free(router->user); - } - router->user = strdup(ptr); - - MXS_INFO("%s: New MASTER_USER is [%s]", - router->service->name, - router->user); - - return 1; +blr_set_master_user(ROUTER_INSTANCE *router, char *user) +{ + if (user != NULL) + { + char *ptr; + char *end; + ptr = strchr(user, '\''); + if (ptr) + { + ptr++; + } + else + { + ptr = user; } - return 0; + end = strchr(ptr, '\''); + if (end) + { + *end = '\0'; + } + + if (router->user) + { + free(router->user); + } + router->user = strdup(ptr); + + MXS_INFO("%s: New MASTER_USER is [%s]", + router->service->name, + router->user); + + return 1; + } + + return 0; } /** * Change the replication password * - * @param router Current router instance - * @param password The password to set - * @return 1 for applied change, 0 otherwise + * @param router Current router instance + * @param password The password to set + * @return 1 for applied change, 0 otherwise */ static int -blr_set_master_password(ROUTER_INSTANCE *router, char *password) { - if (password != NULL) { - char *ptr; - char *end; - ptr = strchr(password, '\''); - if (ptr) - ptr++; - else - ptr = password; - - end = strchr(ptr, '\''); - if (end) - *end ='\0'; - - if (router->password) { - free(router->password); - } - router->password = strdup(ptr); - - /* don't log new password */ - - return 1; +blr_set_master_password(ROUTER_INSTANCE *router, char *password) +{ + if (password != NULL) + { + char *ptr; + char *end; + ptr = strchr(password, '\''); + if (ptr) + { + ptr++; + } + else + { + ptr = password; } - return 0; + end = strchr(ptr, '\''); + if (end) + { + *end = '\0'; + } + + if (router->password) + { + free(router->password); + } + router->password = strdup(ptr); + + /* don't log new password */ + + return 1; + } + + return 0; } /** * Parse a CHANGE MASTER TO SQL command * - * @param input The command to be parsed - * @param error_string Pre-allocated string for error message, BINLOG_ERROR_MSG_LEN + 1 bytes - * @param config master option struct to fill - * @return 0 on success, 1 on failure + * @param input The command to be parsed + * @param error_string Pre-allocated string for error message, BINLOG_ERROR_MSG_LEN + 1 bytes + * @param config master option struct to fill + * @return 0 on success, 1 on failure */ static int -blr_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_OPTIONS *config) { -char *sep = ","; -char *word, *brkb; +blr_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_OPTIONS *config) +{ + char *sep = ","; + char *word, *brkb; - if ((word = strtok_r(input, sep, &brkb)) == NULL) { - sprintf(error_string, "Unable to parse query [%s]", input); - return 1; - } else { - /* parse options key=val */ - if (blr_handle_change_master_token(word, error_string, config)) - return 1; - } + if ((word = strtok_r(input, sep, &brkb)) == NULL) + { + sprintf(error_string, "Unable to parse query [%s]", input); + return 1; + } + else + { + /* parse options key=val */ + if (blr_handle_change_master_token(word, error_string, config)) + { + return 1; + } + } - while ((word = strtok_r(NULL, sep, &brkb)) != NULL) { - /* parse options key=val */ - if (blr_handle_change_master_token(word, error_string, config)) - return 1; - } + while ((word = strtok_r(NULL, sep, &brkb)) != NULL) + { + /* parse options key=val */ + if (blr_handle_change_master_token(word, error_string, config)) + { + return 1; + } + } - return 0; + return 0; } /** * Validate option and set the value for a change master option * - * @param input Current option with value - * @param error pre-allocted string for error message, BINLOG_ERROR_MSG_LEN + 1 bytes - * @param config master option struct to fill - * @return 0 on success, 1 on error + * @param input Current option with value + * @param error pre-allocted string for error message, BINLOG_ERROR_MSG_LEN + 1 bytes + * @param config master option struct to fill + * @return 0 on success, 1 on error */ static int -blr_handle_change_master_token(char *input, char *error, CHANGE_MASTER_OPTIONS *config) { -/* space+TAB+= */ -char *sep = " ="; -char *word, *brkb; -char *value = NULL; -char **option_field = NULL; +blr_handle_change_master_token(char *input, char *error, CHANGE_MASTER_OPTIONS *config) +{ + /* space+TAB+= */ + char *sep = " ="; + char *word, *brkb; + char *value = NULL; + char **option_field = NULL; - if ((word = strtok_r(input, sep, &brkb)) == NULL) { - sprintf(error, "error parsing %s", brkb); - return 1; - } else { - if ((option_field = blr_validate_change_master_option(word, config)) == NULL) { - sprintf(error, "option '%s' is not supported", word); + if ((word = strtok_r(input, sep, &brkb)) == NULL) + { + sprintf(error, "error parsing %s", brkb); + return 1; + } + else + { + if ((option_field = blr_validate_change_master_option(word, config)) == NULL) + { + sprintf(error, "option '%s' is not supported", word); - return 1; - } + return 1; + } - /* value must be freed after usage */ - if ((value = blr_get_parsed_command_value(brkb)) == NULL) { - sprintf(error, "missing value for '%s'", word); - return 1; - } else { - *option_field = value; - } - } + /* value must be freed after usage */ + if ((value = blr_get_parsed_command_value(brkb)) == NULL) + { + sprintf(error, "missing value for '%s'", word); + return 1; + } + else + { + *option_field = value; + } + } - return 0; + return 0; } /** * Get value of a change master option * - * @param input Current option with value - * @return The new allocated option value or NULL + * @param input Current option with value + * @return The new allocated option value or NULL */ static char * -blr_get_parsed_command_value(char *input) { -/* space+TAB+= */ -char *sep = " ="; -char *ret = NULL; -char *word; -char *value = NULL; +blr_get_parsed_command_value(char *input) +{ + /* space+TAB+= */ + char *sep = " ="; + char *ret = NULL; + char *word; + char *value = NULL; - if (strlen(input)) - value = strdup(input); - else - return ret; + if (strlen(input)) + { + value = strdup(input); + } + else + { + return ret; + } - if ((word = strtok_r(NULL, sep, &input)) != NULL) { - char *ptr; + if ((word = strtok_r(NULL, sep, &input)) != NULL) + { + char *ptr; - /* remove trailing spaces */ - ptr = value + strlen(value) - 1; - while (ptr > value && isspace(*ptr)) - *ptr-- = 0; + /* remove trailing spaces */ + ptr = value + strlen(value) - 1; + while (ptr > value && isspace(*ptr)) + { + *ptr-- = 0; + } - ret = strdup(strstr(value, word)); + ret = strdup(strstr(value, word)); - free(value); - } + free(value); + } - return ret; + return ret; } /** * Validate a change master option * - * @param option The option to check - * @param config The option structure - * @return A pointer to the field in the option strucure or NULL + * @param option The option to check + * @param config The option structure + * @return A pointer to the field in the option strucure or NULL */ static char -**blr_validate_change_master_option(char *option, CHANGE_MASTER_OPTIONS *config) { - if (strcasecmp(option, "master_host") == 0) { - return &config->host; - } else if (strcasecmp(option, "master_port") == 0) { - return &config->port; - } else if (strcasecmp(option, "master_log_file") == 0) { - return &config->binlog_file; - } else if (strcasecmp(option, "master_log_pos") == 0) { - return &config->binlog_pos; - } else if (strcasecmp(option, "master_user") == 0) { - return &config->user; - } else if (strcasecmp(option, "master_password") == 0) { - return &config->password; - } else { - return NULL; - } +**blr_validate_change_master_option(char *option, CHANGE_MASTER_OPTIONS *config) +{ + if (strcasecmp(option, "master_host") == 0) + { + return &config->host; + } + else if (strcasecmp(option, "master_port") == 0) + { + return &config->port; + } + else if (strcasecmp(option, "master_log_file") == 0) + { + return &config->binlog_file; + } + else if (strcasecmp(option, "master_log_pos") == 0) + { + return &config->binlog_pos; + } + else if (strcasecmp(option, "master_user") == 0) + { + return &config->user; + } + else if (strcasecmp(option, "master_password") == 0) + { + return &config->password; + } + else + { + return NULL; + } } /** @@ -3968,64 +4434,70 @@ static char * @param options Parsed option struct */ static void -blr_master_free_parsed_options(CHANGE_MASTER_OPTIONS *options) { - free(options->host); - options->host = NULL; +blr_master_free_parsed_options(CHANGE_MASTER_OPTIONS *options) +{ + free(options->host); + options->host = NULL; - free(options->port); - options->port = NULL; + free(options->port); + options->port = NULL; - free(options->user); - options->user = NULL; + free(options->user); + options->user = NULL; - free(options->password); - options->password = NULL; + free(options->password); + options->password = NULL; - free(options->binlog_file); - options->binlog_file = NULL; + free(options->binlog_file); + options->binlog_file = NULL; - free(options->binlog_pos); - options->binlog_pos = NULL; + free(options->binlog_pos); + options->binlog_pos = NULL; } /** * Send a MySQL protocol response for selected variable * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @param variable The variable name - * @param value The variable value + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @param variable The variable name + * @param value The variable value * @param column_type The variable value type (string or int) - * @return Non-zero if data was sent + * @return Non-zero if data was sent */ static int -blr_slave_send_var_value(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, int column_type) +blr_slave_send_var_value(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, + int column_type) { -GWBUF *pkt; -uint8_t *ptr; -int len, vers_len; + GWBUF *pkt; + uint8_t *ptr; + int len, vers_len; - if (value == NULL) - return blr_slave_send_ok(router, slave); + if (value == NULL) + { + return blr_slave_send_ok(router, slave); + } - vers_len = strlen(value); - blr_slave_send_fieldcount(router, slave, 1); - blr_slave_send_columndef(router, slave, variable, column_type, vers_len, 2); - blr_slave_send_eof(router, slave, 3); + vers_len = strlen(value); + blr_slave_send_fieldcount(router, slave, 1); + blr_slave_send_columndef(router, slave, variable, column_type, vers_len, 2); + blr_slave_send_eof(router, slave, 3); - len = 5 + vers_len; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, vers_len + 1, 24); // Add length of data packet - ptr += 3; - *ptr++ = 0x04; // Sequence number in response - *ptr++ = vers_len; // Length of result string - strncpy((char *)ptr, value, vers_len); // Result string - ptr += vers_len; - slave->dcb->func.write(slave->dcb, pkt); + len = 5 + vers_len; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, vers_len + 1, 24); // Add length of data packet + ptr += 3; + *ptr++ = 0x04; // Sequence number in response + *ptr++ = vers_len; // Length of result string + strncpy((char *)ptr, value, vers_len); // Result string + ptr += vers_len; + slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, 5); + return blr_slave_send_eof(router, slave, 5); } /** @@ -4034,62 +4506,75 @@ int len, vers_len; * @param router The binlog router instance * @param slave The slave server to which we are sending the response * @param variable The variable name - * @param value The variable value + * @param value The variable value * @param column_type The variable value type (string or int) * @return Non-zero if data was sent */ static int -blr_slave_send_variable(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, int column_type) +blr_slave_send_variable(ROUTER_INSTANCE *router, + ROUTER_SLAVE *slave, + char *variable, + char *value, + int column_type) { -GWBUF *pkt; -uint8_t *ptr; -int len, vers_len, seqno = 2; -char *p = strdup(variable); -int var_len; -char *old_ptr = p; + GWBUF *pkt; + uint8_t *ptr; + int len, vers_len, seqno = 2; + char *p = strdup(variable); + int var_len; + char *old_ptr = p; - if (value == NULL) - return 0; + if (value == NULL) + { + return 0; + } - /* Remove heading and trailing "'" */ - if(*p == '\'') - p++; - if (p[strlen(p)-1] == '\'') - p[strlen(p)-1] = '\0'; + /* Remove heading and trailing "'" */ + if (*p == '\'') + { + p++; + } + if (p[strlen(p) - 1] == '\'') + { + p[strlen(p) - 1] = '\0'; + } - var_len = strlen(p); + var_len = strlen(p); - /* force lowercase */ - for(int i = 0; i< var_len; i++) { - p[i] = tolower(p[i]); - } + /* force lowercase */ + for (int i = 0; i < var_len; i++) + { + p[i] = tolower(p[i]); + } - blr_slave_send_fieldcount(router, slave, 2); + blr_slave_send_fieldcount(router, slave, 2); - blr_slave_send_columndef_with_info_schema(router, slave, "Variable_name", BLR_TYPE_STRING, 40, seqno++); - blr_slave_send_columndef_with_info_schema(router, slave, "Value", column_type, 40, seqno++); + blr_slave_send_columndef_with_info_schema(router, slave, "Variable_name", BLR_TYPE_STRING, 40, seqno++); + blr_slave_send_columndef_with_info_schema(router, slave, "Value", column_type, 40, seqno++); - blr_slave_send_eof(router, slave, seqno++); + blr_slave_send_eof(router, slave, seqno++); - vers_len = strlen(value); - len = 5 + vers_len + var_len + 1; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, vers_len + 2 + var_len, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno++; // Sequence number in response - *ptr++ = var_len; // Length of result string - strncpy((char *)ptr, p, var_len); // Result string with var name - ptr += var_len; - *ptr++ = vers_len; // Length of result string - strncpy((char *)ptr, value, vers_len); // Result string with var value - ptr += vers_len; - slave->dcb->func.write(slave->dcb, pkt); + vers_len = strlen(value); + len = 5 + vers_len + var_len + 1; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, vers_len + 2 + var_len, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno++; // Sequence number in response + *ptr++ = var_len; // Length of result string + strncpy((char *)ptr, p, var_len); // Result string with var name + ptr += var_len; + *ptr++ = vers_len; // Length of result string + strncpy((char *)ptr, value, vers_len); // Result string with var value + ptr += vers_len; + slave->dcb->func.write(slave->dcb, pkt); - free(old_ptr); + free(old_ptr); - return blr_slave_send_eof(router, slave, seqno++); + return blr_slave_send_eof(router, slave, seqno++); } /** @@ -4097,152 +4582,194 @@ char *old_ptr = p; * * It adds information_schema and variables and variable_name * - * @param router The router - * @param slave The slave connection - * @param name Name of the column - * @param type Column type - * @param len Column length - * @param seqno Packet sequence number - * @return Non-zero on success + * @param router The router + * @param slave The slave connection + * @param name Name of the column + * @param type Column type + * @param len Column length + * @param seqno Packet sequence number + * @return Non-zero on success */ static int -blr_slave_send_columndef_with_info_schema(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno) +blr_slave_send_columndef_with_info_schema(ROUTER_INSTANCE *router, + ROUTER_SLAVE *slave, + char *name, + int type, + int len, + uint8_t seqno) { -GWBUF *pkt; -uint8_t *ptr; -int info_len = strlen("information_schema"); -int virtual_table_name_len = strlen("VARIABLES"); -int table_name_len = strlen("VARIABLES"); -int column_name_len = strlen(name); -int orig_column_name_len = strlen("VARIABLE_NAME"); -int packet_data_len = 22 + strlen(name) + info_len + virtual_table_name_len + table_name_len + orig_column_name_len; + GWBUF *pkt; + uint8_t *ptr; + int info_len = strlen("information_schema"); + int virtual_table_name_len = strlen("VARIABLES"); + int table_name_len = strlen("VARIABLES"); + int column_name_len = strlen(name); + int orig_column_name_len = strlen("VARIABLE_NAME"); + int packet_data_len = 22 + strlen(name) + info_len + virtual_table_name_len + table_name_len + + orig_column_name_len; - if ((pkt = gwbuf_alloc(4 + packet_data_len)) == NULL) - return 0; + if ((pkt = gwbuf_alloc(4 + packet_data_len)) == NULL) + { + return 0; + } - ptr = GWBUF_DATA(pkt); - encode_value(ptr, packet_data_len, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno; // Sequence number in response - *ptr++ = 3; // Catalog is always def - *ptr++ = 'd'; - *ptr++ = 'e'; - *ptr++ = 'f'; - *ptr++ = info_len; // Schema name length - strcpy((char *)ptr,"information_schema"); - ptr += info_len; - *ptr++ = virtual_table_name_len; // virtual table name length - strcpy((char *)ptr, "VARIABLES"); - ptr += virtual_table_name_len; - *ptr++ = table_name_len; // Table name length - strcpy((char *)ptr, "VARIABLES"); - ptr += table_name_len; - *ptr++ = column_name_len; // Column name length; - while (*name) - *ptr++ = *name++; // Copy the column name - *ptr++ = orig_column_name_len; // Orginal column name - strcpy((char *)ptr, "VARIABLE_NAME"); - ptr += orig_column_name_len; - *ptr++ = 0x0c; // Length of next fields always 12 - *ptr++ = 0x3f; // Character set - *ptr++ = 0; - encode_value(ptr, len, 32); // Add length of column - ptr += 4; - *ptr++ = type; - *ptr++ = 0x81; // Two bytes of flags - if (type == 0xfd) - *ptr++ = 0x1f; - else - *ptr++ = 0x00; - *ptr++= 0; - *ptr++= 0; - *ptr++= 0; + ptr = GWBUF_DATA(pkt); + encode_value(ptr, packet_data_len, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno; // Sequence number in response + *ptr++ = 3; // Catalog is always def + *ptr++ = 'd'; + *ptr++ = 'e'; + *ptr++ = 'f'; + *ptr++ = info_len; // Schema name length + strcpy((char *)ptr, "information_schema"); + ptr += info_len; + *ptr++ = virtual_table_name_len; // virtual table name length + strcpy((char *)ptr, "VARIABLES"); + ptr += virtual_table_name_len; + *ptr++ = table_name_len; // Table name length + strcpy((char *)ptr, "VARIABLES"); + ptr += table_name_len; + *ptr++ = column_name_len; // Column name length; + while (*name) + { + *ptr++ = *name++; // Copy the column name + } + *ptr++ = orig_column_name_len; // Orginal column name + strcpy((char *)ptr, "VARIABLE_NAME"); + ptr += orig_column_name_len; + *ptr++ = 0x0c; // Length of next fields always 12 + *ptr++ = 0x3f; // Character set + *ptr++ = 0; + encode_value(ptr, len, 32); // Add length of column + ptr += 4; + *ptr++ = type; + *ptr++ = 0x81; // Two bytes of flags + if (type == 0xfd) + { + *ptr++ = 0x1f; + } + else + { + *ptr++ = 0x00; + } + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; - return slave->dcb->func.write(slave->dcb, pkt); + return slave->dcb->func.write(slave->dcb, pkt); } /** * Interface for testing blr_parse_change_master_command() * - * @param input The command to be parsed - * @param error_string Pre-allocated string for error message, BINLOG_ERROR_MSG_LEN + 1 bytes - * @param config master option struct to fill - * @return 0 on success, 1 on failure + * @param input The command to be parsed + * @param error_string Pre-allocated string for error message, BINLOG_ERROR_MSG_LEN + 1 bytes + * @param config master option struct to fill + * @return 0 on success, 1 on failure */ int -blr_test_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_OPTIONS *config) { - return blr_parse_change_master_command(input, error_string, config); +blr_test_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_OPTIONS *config) +{ + return blr_parse_change_master_command(input, error_string, config); } /* * Interface for testing set new master binlog file * * - * @param router Current router instance - * @param filename Binlog file name - * @param error The error msg for command, pre-allocated BINLOG_ERROR_MSG_LEN + 1 bytes - * @return New binlog file or NULL on error + * @param router Current router instance + * @param filename Binlog file name + * @param error The error msg for command, pre-allocated BINLOG_ERROR_MSG_LEN + 1 bytes + * @return New binlog file or NULL on error */ char * -blr_test_set_master_logfile(ROUTER_INSTANCE *router, char *filename, char *error) { - return blr_set_master_logfile(router, filename, error); +blr_test_set_master_logfile(ROUTER_INSTANCE *router, char *filename, char *error) +{ + return blr_set_master_logfile(router, filename, error); } /** * Interface for testing a 'change master' operation * - * @param router The router instance - * @param command The change master SQL command - * @param error The error message, preallocated BINLOG_ERROR_MSG_LEN + 1 bytes - * @return 0 on success, 1 on success with new binlog, -1 on failure + * @param router The router instance + * @param command The change master SQL command + * @param error The error message, preallocated BINLOG_ERROR_MSG_LEN + 1 bytes + * @return 0 on success, 1 on success with new binlog, -1 on failure */ int -blr_test_handle_change_master(ROUTER_INSTANCE* router, char *command, char *error) { - return blr_handle_change_master(router, command, error); +blr_test_handle_change_master(ROUTER_INSTANCE* router, char *command, char *error) +{ + return blr_handle_change_master(router, command, error); } /** * Handle the response to the SQL command "SHOW GLOBAL VARIABLES LIKE or SHOW VARIABLES LIKE * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @param stmt The SQL statement - * @return Non-zero if the variable is handled, 0 if variable is unknown, -1 for syntax error + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @param stmt The SQL statement + * @return Non-zero if the variable is handled, 0 if variable is unknown, -1 for syntax error */ static int -blr_slave_handle_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *stmt) { -char *brkb; -char *word; -/* SPACE,TAB,= */ -char *sep = " ,="; +blr_slave_handle_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *stmt) +{ + char *brkb; + char *word; + /* SPACE,TAB,= */ + char *sep = " ,="; - if ((word = strtok_r(stmt, sep, &brkb)) == NULL) { - return -1; - } else if (strcasecmp(word, "LIKE") == 0) { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) { - MXS_ERROR("%s: Missing LIKE clause in SHOW [GLOBAL] VARIABLES.", - router->service->name); - return -1; - } else if (strcasecmp(word, "'SERVER_ID'") == 0) { - if (router->set_master_server_id) { - char server_id[40]; - sprintf(server_id, "%d", router->masterid); - return blr_slave_send_variable(router, slave, "'SERVER_ID'", server_id, BLR_TYPE_INT); - } else - return blr_slave_replay(router, slave, router->saved_master.server_id); - } else if (strcasecmp(word, "'SERVER_UUID'") == 0) { - if (router->set_master_uuid) { - return blr_slave_send_variable(router, slave, "'SERVER_UUID'", router->master_uuid, BLR_TYPE_STRING); - } - else - return blr_slave_replay(router, slave, router->saved_master.uuid); - } else if (strcasecmp(word, "'MAXSCALE%'") == 0) { - return blr_slave_send_maxscale_variables(router, slave); - } else - return 0; - } else - return -1; + if ((word = strtok_r(stmt, sep, &brkb)) == NULL) + { + return -1; + } + else if (strcasecmp(word, "LIKE") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Missing LIKE clause in SHOW [GLOBAL] VARIABLES.", + router->service->name); + return -1; + } + else if (strcasecmp(word, "'SERVER_ID'") == 0) + { + if (router->set_master_server_id) + { + char server_id[40]; + sprintf(server_id, "%d", router->masterid); + return blr_slave_send_variable(router, slave, "'SERVER_ID'", server_id, BLR_TYPE_INT); + } + else + { + return blr_slave_replay(router, slave, router->saved_master.server_id); + } + } + else if (strcasecmp(word, "'SERVER_UUID'") == 0) + { + if (router->set_master_uuid) + { + return blr_slave_send_variable(router, slave, "'SERVER_UUID'", + router->master_uuid, BLR_TYPE_STRING); + } + else + { + return blr_slave_replay(router, slave, router->saved_master.uuid); + } + } + else if (strcasecmp(word, "'MAXSCALE%'") == 0) + { + return blr_slave_send_maxscale_variables(router, slave); + } + else + { + return 0; + } + } + else + { + return -1; + } } /** @@ -4260,37 +4787,44 @@ char *sep = " ,="; static int blr_slave_send_warning_message(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave, char *message) { -GWBUF *pkt; -uint8_t *ptr; + GWBUF *pkt; + uint8_t *ptr; - if ((pkt = gwbuf_alloc(11)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - *ptr++ = 7; // Payload length + if ((pkt = gwbuf_alloc(11)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + *ptr++ = 7; // Payload length + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 1; // Seqno + *ptr++ = 0; // ok + *ptr++ = 0; + *ptr++ = 0; + + *ptr++ = 2; + *ptr++ = 0; + + if (strlen(message) == 0) + { *ptr++ = 0; *ptr++ = 0; - *ptr++ = 1; // Seqno - *ptr++ = 0; // ok - *ptr++ = 0; + } + else + { + *ptr++ = 1; /* warning byte set to 1 */ *ptr++ = 0; + } - *ptr++ = 2; - *ptr++ = 0; + /* set the new warning in this slave connection */ + if (slave->warning_msg) + { + free(slave->warning_msg); + } + slave->warning_msg = strdup(message); - if(strlen(message) == 0) { - *ptr++ = 0; - *ptr++ = 0; - } else { - *ptr++ = 1; /* warning byte set to 1 */ - *ptr++ = 0; - } - - /* set the new warning in this slave connection */ - if (slave->warning_msg) - free(slave->warning_msg); - slave->warning_msg = strdup(message); - - return slave->dcb->func.write(slave->dcb, pkt); + return slave->dcb->func.write(slave->dcb, pkt); } /** @@ -4308,106 +4842,129 @@ uint8_t *ptr; static int blr_slave_show_warnings(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave) { -GWBUF *pkt; -uint8_t *ptr; -int len; -int msg_len = 0; -int code_len = 0; -int level_len = 0; + GWBUF *pkt; + uint8_t *ptr; + int len; + int msg_len = 0; + int code_len = 0; + int level_len = 0; - /* check whether a warning message is available */ - if (slave->warning_msg) { - char *level="Warning"; - char *msg_ptr; - char err_code[16+1]=""; - msg_ptr = strchr(slave->warning_msg, ':'); - if (msg_ptr) { - strncpy(err_code, slave->warning_msg, - (msg_ptr-slave->warning_msg > 16) ? 16 : (msg_ptr-slave->warning_msg)); - code_len = strlen(err_code); + /* check whether a warning message is available */ + if (slave->warning_msg) + { + char *level = "Warning"; + char *msg_ptr; + char err_code[16 + 1] = ""; + msg_ptr = strchr(slave->warning_msg, ':'); + if (msg_ptr) + { + strncpy(err_code, slave->warning_msg, + (msg_ptr - slave->warning_msg > 16) ? 16 : (msg_ptr - slave->warning_msg)); + code_len = strlen(err_code); - msg_ptr++; - } else { - msg_ptr = slave->warning_msg; - } + msg_ptr++; + } + else + { + msg_ptr = slave->warning_msg; + } - msg_len = strlen(msg_ptr); - level_len = strlen(level); + msg_len = strlen(msg_ptr); + level_len = strlen(level); - blr_slave_send_fieldcount(router, slave, 3); // 3 columns + blr_slave_send_fieldcount(router, slave, 3); // 3 columns - blr_slave_send_columndef(router, slave, "Level", BLR_TYPE_STRING, 40, 2); - blr_slave_send_columndef(router, slave, "Code", BLR_TYPE_STRING, 40, 3); - blr_slave_send_columndef(router, slave, "Message", BLR_TYPE_STRING, 80, 4); + blr_slave_send_columndef(router, slave, "Level", BLR_TYPE_STRING, 40, 2); + blr_slave_send_columndef(router, slave, "Code", BLR_TYPE_STRING, 40, 3); + blr_slave_send_columndef(router, slave, "Message", BLR_TYPE_STRING, 80, 4); - blr_slave_send_eof(router, slave, 5); + blr_slave_send_eof(router, slave, 5); - len = 4 + (1 + level_len) + (1 + code_len) + (1 + msg_len); + len = 4 + (1 + level_len) + (1 + code_len) + (1 + msg_len); - if ((pkt = gwbuf_alloc(len)) == NULL) - return blr_slave_send_ok(router, slave); + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return blr_slave_send_ok(router, slave); + } - ptr = GWBUF_DATA(pkt); + ptr = GWBUF_DATA(pkt); - encode_value(ptr, len - 4, 24); // Add length of data packet - ptr += 3; + encode_value(ptr, len - 4, 24); // Add length of data packet + ptr += 3; - *ptr++ = 0x06; // Sequence number in response + *ptr++ = 0x06; // Sequence number in response - *ptr++ = level_len; // Length of result string - strncpy((char *)ptr, level, level_len); // Result string - ptr += level_len; + *ptr++ = level_len; // Length of result string + strncpy((char *)ptr, level, level_len); // Result string + ptr += level_len; - *ptr++ = code_len; // Length of result string - if (code_len) { - strncpy((char *)ptr, err_code, code_len); // Result string - ptr += code_len; - } + *ptr++ = code_len; // Length of result string + if (code_len) + { + strncpy((char *)ptr, err_code, code_len); // Result string + ptr += code_len; + } - *ptr++ = msg_len; // Length of result string - if (msg_len) { - strncpy((char *)ptr, msg_ptr, msg_len); // Result string - ptr += msg_len; - } + *ptr++ = msg_len; // Length of result string + if (msg_len) + { + strncpy((char *)ptr, msg_ptr, msg_len); // Result string + ptr += msg_len; + } - slave->dcb->func.write(slave->dcb, pkt); + slave->dcb->func.write(slave->dcb, pkt); - return blr_slave_send_eof(router, slave, 7); - } else { - return blr_slave_send_ok(router, slave); - } + return blr_slave_send_eof(router, slave, 7); + } + else + { + return blr_slave_send_ok(router, slave); + } } /** * Handle the response to the SQL command "SHOW [GLOBAL] STATUS LIKE or SHOW STATUS LIKE * - * @param router The binlog router instance - * @param slave The slave server to which we are sending the response - * @param stmt The SQL statement - * @return Non-zero if the variable is handled, 0 if variable is unknown, -1 for syntax error + * @param router The binlog router instance + * @param slave The slave server to which we are sending the response + * @param stmt The SQL statement + * @return Non-zero if the variable is handled, 0 if variable is unknown, -1 for syntax error */ static int -blr_slave_handle_status_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *stmt) { -char *brkb = NULL; -char *word = NULL; -/* SPACE,TAB,= */ -char *sep = " ,="; +blr_slave_handle_status_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *stmt) +{ + char *brkb = NULL; + char *word = NULL; + /* SPACE,TAB,= */ + char *sep = " ,="; - if ((word = strtok_r(stmt, sep, &brkb)) == NULL) { - return -1; - } else if (strcasecmp(word, "LIKE") == 0) { - if ((word = strtok_r(NULL, sep, &brkb)) == NULL) { - MXS_ERROR("%s: Missing LIKE clause in SHOW [GLOBAL] STATUS.", - router->service->name); - return -1; - } else if (strcasecmp(word, "'Uptime'") == 0) { - char uptime[41]=""; - snprintf(uptime, 40, "%d", maxscale_uptime()); - return blr_slave_send_status_variable(router, slave, "Uptime", uptime, BLR_TYPE_INT); - } else - return 0; - } else - return -1; + if ((word = strtok_r(stmt, sep, &brkb)) == NULL) + { + return -1; + } + else if (strcasecmp(word, "LIKE") == 0) + { + if ((word = strtok_r(NULL, sep, &brkb)) == NULL) + { + MXS_ERROR("%s: Missing LIKE clause in SHOW [GLOBAL] STATUS.", + router->service->name); + return -1; + } + else if (strcasecmp(word, "'Uptime'") == 0) + { + char uptime[41] = ""; + snprintf(uptime, 40, "%d", maxscale_uptime()); + return blr_slave_send_status_variable(router, slave, "Uptime", uptime, BLR_TYPE_INT); + } + else + { + return 0; + } + } + else + { + return -1; + } } /** @@ -4416,62 +4973,70 @@ char *sep = " ,="; * @param router The binlog router instance * @param slave The slave server to which we are sending the response * @param variable The variable name - * @param value The variable value + * @param value The variable value * @param column_type The variable value type (string or int) * @return Non-zero if data was sent */ static int -blr_slave_send_status_variable(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, int column_type) +blr_slave_send_status_variable(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *variable, char *value, + int column_type) { -GWBUF *pkt; -uint8_t *ptr; -int len, vers_len, seqno = 2; -char *p = strdup(variable); -int var_len; -char *old_ptr = p; + GWBUF *pkt; + uint8_t *ptr; + int len, vers_len, seqno = 2; + char *p = strdup(variable); + int var_len; + char *old_ptr = p; - /* Remove heading and trailing "'" */ - if(*p == '\'') - p++; - if (p[strlen(p)-1] == '\'') - p[strlen(p)-1] = '\0'; + /* Remove heading and trailing "'" */ + if (*p == '\'') + { + p++; + } + if (p[strlen(p) - 1] == '\'') + { + p[strlen(p) - 1] = '\0'; + } - var_len = strlen(p); + var_len = strlen(p); - /* force lowercase */ - for(int i = 0; i< var_len; i++) { - p[i] = tolower(p[i]); - } + /* force lowercase */ + for (int i = 0; i < var_len; i++) + { + p[i] = tolower(p[i]); + } - /* First char is uppercase */ - p[0]=toupper(p[0]); + /* First char is uppercase */ + p[0] = toupper(p[0]); - blr_slave_send_fieldcount(router, slave, 2); + blr_slave_send_fieldcount(router, slave, 2); - blr_slave_send_columndef_with_status_schema(router, slave, "Variable_name", BLR_TYPE_STRING, 40, seqno++); - blr_slave_send_columndef_with_status_schema(router, slave, "Value", column_type, 40, seqno++); + blr_slave_send_columndef_with_status_schema(router, slave, "Variable_name", BLR_TYPE_STRING, 40, seqno++); + blr_slave_send_columndef_with_status_schema(router, slave, "Value", column_type, 40, seqno++); - blr_slave_send_eof(router, slave, seqno++); + blr_slave_send_eof(router, slave, seqno++); - vers_len = strlen(value); - len = 5 + vers_len + var_len + 1; - if ((pkt = gwbuf_alloc(len)) == NULL) - return 0; - ptr = GWBUF_DATA(pkt); - encode_value(ptr, vers_len + 2 + var_len, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno++; // Sequence number in response - *ptr++ = var_len; // Length of result string - strncpy((char *)ptr, p, var_len); // Result string with var name - ptr += var_len; - *ptr++ = vers_len; // Length of result string - strncpy((char *)ptr, value, vers_len); // Result string with var value - ptr += vers_len; - slave->dcb->func.write(slave->dcb, pkt); + vers_len = strlen(value); + len = 5 + vers_len + var_len + 1; + if ((pkt = gwbuf_alloc(len)) == NULL) + { + return 0; + } + ptr = GWBUF_DATA(pkt); + encode_value(ptr, vers_len + 2 + var_len, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno++; // Sequence number in response + *ptr++ = var_len; // Length of result string + strncpy((char *)ptr, p, var_len); // Result string with var name + ptr += var_len; + *ptr++ = vers_len; // Length of result string + strncpy((char *)ptr, value, vers_len); // Result string with var value + ptr += vers_len; + slave->dcb->func.write(slave->dcb, pkt); - free(old_ptr); + free(old_ptr); - return blr_slave_send_eof(router, slave, seqno++); + return blr_slave_send_eof(router, slave, seqno++); } /** @@ -4479,78 +5044,98 @@ char *old_ptr = p; * * It adds information_schema.STATUS and variables and variable_name * - * @param router The router - * @param slave The slave connection - * @param name Name of the column - * @param type Column type - * @param len Column length - * @param seqno Packet sequence number - * @return Non-zero on success + * @param router The router + * @param slave The slave connection + * @param name Name of the column + * @param type Column type + * @param len Column length + * @param seqno Packet sequence number + * @return Non-zero on success */ static int -blr_slave_send_columndef_with_status_schema(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno) +blr_slave_send_columndef_with_status_schema(ROUTER_INSTANCE *router, + ROUTER_SLAVE *slave, + char *name, + int type, + int len, + uint8_t seqno) { -GWBUF *pkt; -uint8_t *ptr; -int info_len = strlen("information_schema"); -int virtual_table_name_len = strlen("STATUS"); -int table_name_len = strlen("STATUS"); -int column_name_len = strlen(name); -int orig_column_name_len = strlen("VARIABLE_NAME"); -int packet_data_len = 0; -char *ptr_name_start = name; + GWBUF *pkt; + uint8_t *ptr; + int info_len = strlen("information_schema"); + int virtual_table_name_len = strlen("STATUS"); + int table_name_len = strlen("STATUS"); + int column_name_len = strlen(name); + int orig_column_name_len = strlen("VARIABLE_NAME"); + int packet_data_len = 0; + char *ptr_name_start = name; - if (strcasecmp(ptr_name_start, "value") == 0) - orig_column_name_len = strlen("VARIABLE_VALUE"); + if (strcasecmp(ptr_name_start, "value") == 0) + { + orig_column_name_len = strlen("VARIABLE_VALUE"); + } - packet_data_len = 22 + strlen(name) + info_len + virtual_table_name_len + table_name_len + orig_column_name_len; + packet_data_len = 22 + strlen(name) + info_len + virtual_table_name_len + table_name_len + + orig_column_name_len; - if ((pkt = gwbuf_alloc(4 + packet_data_len)) == NULL) - return 0; + if ((pkt = gwbuf_alloc(4 + packet_data_len)) == NULL) + { + return 0; + } - ptr = GWBUF_DATA(pkt); - encode_value(ptr, packet_data_len, 24); // Add length of data packet - ptr += 3; - *ptr++ = seqno; // Sequence number in response - *ptr++ = 3; // Catalog is always def - *ptr++ = 'd'; - *ptr++ = 'e'; - *ptr++ = 'f'; - *ptr++ = info_len; // Schema name length - strcpy((char *)ptr,"information_schema"); - ptr += info_len; - *ptr++ = virtual_table_name_len; // virtual table name length - strcpy((char *)ptr, "STATUS"); - ptr += virtual_table_name_len; - *ptr++ = table_name_len; // Table name length - strcpy((char *)ptr, "STATUS"); - ptr += table_name_len; - *ptr++ = column_name_len; // Column name length; - while (*name) - *ptr++ = *name++; // Copy the column name - *ptr++ = orig_column_name_len; // Orginal column name + ptr = GWBUF_DATA(pkt); + encode_value(ptr, packet_data_len, 24); // Add length of data packet + ptr += 3; + *ptr++ = seqno; // Sequence number in response + *ptr++ = 3; // Catalog is always def + *ptr++ = 'd'; + *ptr++ = 'e'; + *ptr++ = 'f'; + *ptr++ = info_len; // Schema name length + strcpy((char *)ptr, "information_schema"); + ptr += info_len; + *ptr++ = virtual_table_name_len; // virtual table name length + strcpy((char *)ptr, "STATUS"); + ptr += virtual_table_name_len; + *ptr++ = table_name_len; // Table name length + strcpy((char *)ptr, "STATUS"); + ptr += table_name_len; + *ptr++ = column_name_len; // Column name length; + while (*name) + { + *ptr++ = *name++; // Copy the column name + } + *ptr++ = orig_column_name_len; // Orginal column name - if (strcasecmp(ptr_name_start, "value") == 0) - strcpy((char *)ptr, "VARIABLE_VALUE"); - else - strcpy((char *)ptr, "VARIABLE_NAME"); - ptr += orig_column_name_len; - *ptr++ = 0x0c; // Length of next fields always 12 - *ptr++ = 0x3f; // Character set - *ptr++ = 0; - encode_value(ptr, len, 32); // Add length of column - ptr += 4; - *ptr++ = type; - *ptr++ = 0x81; // Two bytes of flags - if (type == 0xfd) - *ptr++ = 0x1f; - else - *ptr++ = 0x00; - *ptr++= 0; - *ptr++= 0; - *ptr++= 0; + if (strcasecmp(ptr_name_start, "value") == 0) + { + strcpy((char *)ptr, "VARIABLE_VALUE"); + } + else + { + strcpy((char *)ptr, "VARIABLE_NAME"); + } + ptr += orig_column_name_len; + *ptr++ = 0x0c; // Length of next fields always 12 + *ptr++ = 0x3f; // Character set + *ptr++ = 0; + encode_value(ptr, len, 32); // Add length of column + ptr += 4; + *ptr++ = type; + *ptr++ = 0x81; // Two bytes of flags + if (type == 0xfd) + { + *ptr++ = 0x1f; + } + else + { + *ptr++ = 0x00; + } + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; - return slave->dcb->func.write(slave->dcb, pkt); + return slave->dcb->func.write(slave->dcb, pkt); } /** @@ -4560,36 +5145,38 @@ char *ptr_name_start = name; */ static void -blr_send_slave_heartbeat(void *inst) { -ROUTER_SLAVE *sptr = NULL; -ROUTER_INSTANCE *router = (ROUTER_INSTANCE *) inst; -time_t t_now = time(0); +blr_send_slave_heartbeat(void *inst) +{ + ROUTER_SLAVE *sptr = NULL; + ROUTER_INSTANCE *router = (ROUTER_INSTANCE *) inst; + time_t t_now = time(0); - spinlock_acquire(&router->lock); + spinlock_acquire(&router->lock); - sptr = router->slaves; + sptr = router->slaves; - while (sptr) - { + while (sptr) + { - /* skip servers with state = 0 */ - if ( (sptr->state == BLRS_DUMPING) && (sptr->heartbeat > 0) && ((t_now + 1 - sptr->lastReply) >= sptr->heartbeat) ) - { - MXS_NOTICE("Sending Heartbeat to slave server-id %d. " - "Heartbeat interval is %d, last event time is %lu", - sptr->serverid, sptr->heartbeat, - (unsigned long)sptr->lastReply); + /* skip servers with state = 0 */ + if ( (sptr->state == BLRS_DUMPING) && (sptr->heartbeat > 0) && + ((t_now + 1 - sptr->lastReply) >= sptr->heartbeat) ) + { + MXS_NOTICE("Sending Heartbeat to slave server-id %d. " + "Heartbeat interval is %d, last event time is %lu", + sptr->serverid, sptr->heartbeat, + (unsigned long)sptr->lastReply); - blr_slave_send_heartbeat(router, sptr); + blr_slave_send_heartbeat(router, sptr); - sptr->lastReply = t_now; + sptr->lastReply = t_now; - } + } - sptr = sptr->next; - } + sptr = sptr->next; + } - spinlock_release(&router->lock); + spinlock_release(&router->lock); } /** @@ -4598,74 +5185,76 @@ time_t t_now = time(0); static int blr_slave_send_heartbeat(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { -REP_HEADER hdr; -GWBUF *resp; -uint8_t *ptr; -int len = BINLOG_EVENT_HDR_LEN; -uint32_t chksum; -int filename_len = strlen(slave->binlogfile); + REP_HEADER hdr; + GWBUF *resp; + uint8_t *ptr; + int len = BINLOG_EVENT_HDR_LEN; + uint32_t chksum; + int filename_len = strlen(slave->binlogfile); - /* Add CRC32 4 bytes */ - if (!slave->nocrc) - len +=4; + /* Add CRC32 4 bytes */ + if (!slave->nocrc) + { + len += 4; + } - /* add binlogname to data content len */ - len += filename_len; + /* add binlogname to data content len */ + len += filename_len; - /** - * Alloc buffer for network binlog stream: - * - * 4 bytes header (3 for pkt len + 1 seq.no) - * 1 byte for Ok / ERR - * n bytes data content - * - * Total = 5 bytes + len - */ - resp = gwbuf_alloc(5 + len); + /** + * Alloc buffer for network binlog stream: + * + * 4 bytes header (3 for pkt len + 1 seq.no) + * 1 byte for Ok / ERR + * n bytes data content + * + * Total = 5 bytes + len + */ + resp = gwbuf_alloc(5 + len); - /* The OK/Err byte is part of payload */ - hdr.payload_len = len + 1; + /* The OK/Err byte is part of payload */ + hdr.payload_len = len + 1; - /* Add sequence no */ - hdr.seqno = slave->seqno++; + /* Add sequence no */ + hdr.seqno = slave->seqno++; - /* Add OK */ - hdr.ok = 0; + /* Add OK */ + hdr.ok = 0; - /* Add timestamp: 0 */ - hdr.timestamp = 0L; + /* Add timestamp: 0 */ + hdr.timestamp = 0L; - /* Set Event Type */ - hdr.event_type = HEARTBEAT_EVENT; + /* Set Event Type */ + hdr.event_type = HEARTBEAT_EVENT; - /* Add master server id */ - hdr.serverid = router->masterid; + /* Add master server id */ + hdr.serverid = router->masterid; - /* Add event size */ - hdr.event_size = len; + /* Add event size */ + hdr.event_size = len; - /* Add Next Pos */ - hdr.next_pos = slave->binlog_pos; + /* Add Next Pos */ + hdr.next_pos = slave->binlog_pos; - /* Add flags */ - hdr.flags = 0x20; + /* Add flags */ + hdr.flags = 0x20; - /* point just after the header */ - ptr = blr_build_header(resp, &hdr); + /* point just after the header */ + ptr = blr_build_header(resp, &hdr); - /* Copy binlog name */ - memcpy(ptr, slave->binlogfile, filename_len); + /* Copy binlog name */ + memcpy(ptr, slave->binlogfile, filename_len); - ptr += filename_len; + ptr += filename_len; - /* Add the CRC32 */ - if (!slave->nocrc) - { - chksum = crc32(0L, NULL, 0); - chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); - encode_value(ptr, chksum, 32); - } + /* Add the CRC32 */ + if (!slave->nocrc) + { + chksum = crc32(0L, NULL, 0); + chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); + encode_value(ptr, chksum, 32); + } - /* Write the packet */ - return slave->dcb->func.write(slave->dcb, resp); + /* Write the packet */ + return slave->dcb->func.write(slave->dcb, resp); } diff --git a/server/modules/routing/binlog/maxbinlogcheck.c b/server/modules/routing/binlog/maxbinlogcheck.c index 6ae35e1e4..194e8dd4f 100644 --- a/server/modules/routing/binlog/maxbinlogcheck.c +++ b/server/modules/routing/binlog/maxbinlogcheck.c @@ -24,13 +24,13 @@ * @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 + * 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 */ @@ -68,127 +68,150 @@ 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} +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.1.0"; -int main(int argc, char **argv) { - 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; +int +maxscale_uptime() +{ + return 1; +} - 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); - } - } +int main(int argc, char **argv) +{ + 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; - num_args = optind; + 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); + } + } - mxs_log_init(NULL, NULL, MXS_LOG_TARGET_DEFAULT); - mxs_log_set_augmentation(0); - mxs_log_set_priority_enabled(LOG_DEBUG, debug_out); + num_args = optind; - if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) { - MXS_ERROR("Memory allocation failed for ROUTER_INSTANCE"); + mxs_log_init(NULL, NULL, MXS_LOG_TARGET_DEFAULT); + mxs_log_set_augmentation(0); + mxs_log_set_priority_enabled(LOG_DEBUG, debug_out); - mxs_log_flush_sync(); - mxs_log_finish(); + if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) + { + MXS_ERROR("Memory allocation failed for ROUTER_INSTANCE"); - return 1; - } + mxs_log_flush_sync(); + mxs_log_finish(); - if (argv[num_args] == NULL) { - printf("ERROR: No binlog file was specified\n"); - exit(EXIT_FAILURE); - } + return 1; + } - strncpy(path, argv[num_args], PATH_MAX); + if (argv[num_args] == NULL) + { + printf("ERROR: No binlog file was specified\n"); + exit(EXIT_FAILURE); + } - if (fix_file) - fd = open(path, O_RDWR, 0666); - else - fd = open(path, O_RDONLY, 0666); + strncpy(path, argv[num_args], PATH_MAX); - if (fd == -1) - { - MXS_ERROR("Failed to open binlog file %s: %s", - path, strerror(errno)); - - mxs_log_flush_sync(); - mxs_log_finish(); + if (fix_file) + { + fd = open(path, O_RDWR, 0666); + } + else + { + fd = open(path, O_RDONLY, 0666); + } - free(inst); + if (fd == -1) + { + MXS_ERROR("Failed to open binlog file %s: %s", + path, strerror(errno)); - return 1; - } + mxs_log_flush_sync(); + mxs_log_finish(); - inst->binlog_fd = fd; + free(inst); - if (mariadb10_compat == 1) - inst->mariadb10_compat = 1; + return 1; + } - ptr = strrchr(path, '/'); - if (ptr) - strncpy(inst->binlog_name, ptr+1, BINLOG_FNAMELEN); - else - strncpy(inst->binlog_name, path, BINLOG_FNAMELEN); + inst->binlog_fd = fd; - MXS_NOTICE("maxbinlogcheck %s", binlog_check_version); + if (mariadb10_compat == 1) + { + inst->mariadb10_compat = 1; + } - if (fstat(inst->binlog_fd, &statb) == 0) - filelen = statb.st_size; + ptr = strrchr(path, '/'); + if (ptr) + { + strncpy(inst->binlog_name, ptr + 1, BINLOG_FNAMELEN); + } + else + { + strncpy(inst->binlog_name, path, BINLOG_FNAMELEN); + } - MXS_NOTICE("Checking %s (%s), size %lu bytes", path, inst->binlog_name, filelen); + MXS_NOTICE("maxbinlogcheck %s", binlog_check_version); - /* read binary log */ - ret = blr_read_events_all_events(inst, fix_file, debug_out); + if (fstat(inst->binlog_fd, &statb) == 0) + { + filelen = statb.st_size; + } - close(inst->binlog_fd); + MXS_NOTICE("Checking %s (%s), size %lu bytes", path, inst->binlog_name, filelen); - mxs_log_flush_sync(); + /* read binary log */ + ret = blr_read_events_all_events(inst, fix_file, debug_out); - MXS_NOTICE("Check retcode: %i, Binlog Pos = %lu", ret, inst->binlog_position); + close(inst->binlog_fd); - mxs_log_flush_sync(); - mxs_log_finish(); + mxs_log_flush_sync(); - free(inst); + MXS_NOTICE("Check retcode: %i, Binlog Pos = %lu", ret, inst->binlog_position); - return 0; + mxs_log_flush_sync(); + mxs_log_finish(); + + free(inst); + + return 0; } /** @@ -197,7 +220,7 @@ int main(int argc, char **argv) { static void printVersion(const char *progname) { - printf("%s Version %s\n", progname, binlog_check_version); + printf("%s Version %s\n", progname, binlog_check_version); } /** @@ -206,14 +229,14 @@ printVersion(const char *progname) static void printUsage(const char *progname) { - printVersion(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"); + 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"); }