From 06b59ffe2e9a56a379baca03d18398d19fcb8aa4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 22 Feb 2015 04:10:57 +0200 Subject: [PATCH 001/273] Testing of plain routing with MongoDB. --- server/modules/protocol/CMakeLists.txt | 9 + server/modules/protocol/mongo_backend.c | 1204 +++++++++++++++++++++++ server/modules/protocol/mongo_client.c | 1004 +++++++++++++++++++ server/modules/routing/CMakeLists.txt | 4 + server/modules/routing/plainroute.c | 923 +++++++++++++++++ 5 files changed, 3144 insertions(+) create mode 100644 server/modules/protocol/mongo_backend.c create mode 100644 server/modules/protocol/mongo_client.c create mode 100644 server/modules/routing/plainroute.c diff --git a/server/modules/protocol/CMakeLists.txt b/server/modules/protocol/CMakeLists.txt index fa1c2ab34..0bea95832 100644 --- a/server/modules/protocol/CMakeLists.txt +++ b/server/modules/protocol/CMakeLists.txt @@ -6,6 +6,15 @@ add_library(MySQLBackend SHARED mysql_backend.c mysql_common.c) target_link_libraries(MySQLBackend log_manager utils) install(TARGETS MySQLBackend DESTINATION modules) +add_library(mongoclient SHARED mongo_client.c mysql_common.c) +target_link_libraries(mongoclient log_manager utils) +install(TARGETS mongoclient DESTINATION modules) + +add_library(mongobackend SHARED mongo_backend.c mysql_common.c) +target_link_libraries(mongobackend log_manager utils) +install(TARGETS mongobackend DESTINATION modules) + + add_library(telnetd SHARED telnetd.c) target_link_libraries(telnetd log_manager utils) install(TARGETS telnetd DESTINATION modules) diff --git a/server/modules/protocol/mongo_backend.c b/server/modules/protocol/mongo_backend.c new file mode 100644 index 000000000..4fe1ff0b8 --- /dev/null +++ b/server/modules/protocol/mongo_backend.c @@ -0,0 +1,1204 @@ +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +#include "mysql_client_server_protocol.h" +#include +#include +#include +#include + +/* + * MySQL Protocol module for handling the protocol between the gateway + * and the backend MySQL database. + * + * Revision History + * Date Who Description + * 14/06/2013 Mark Riddoch Initial version + * 17/06/2013 Massimiliano Pinto Added MaxScale To Backends routines + * 01/07/2013 Massimiliano Pinto Put Log Manager example code behind SS_DEBUG macros. + * 03/07/2013 Massimiliano Pinto Added delayq for incoming data before mysql connection + * 04/07/2013 Massimiliano Pinto Added asyncrhronous MySQL protocol connection to backend + * 05/07/2013 Massimiliano Pinto Added closeSession if backend auth fails + * 12/07/2013 Massimiliano Pinto Added Mysql Change User via dcb->func.auth() + * 15/07/2013 Massimiliano Pinto Added Mysql session change via dcb->func.session() + * 17/07/2013 Massimiliano Pinto Added dcb->command update from gwbuf->command for proper routing + server replies to client via router->clientReply + * 04/09/2013 Massimiliano Pinto Added dcb->session and dcb->session->client checks for NULL + * 12/09/2013 Massimiliano Pinto Added checks in gw_read_backend_event() for gw_read_backend_handshake + * 27/09/2013 Massimiliano Pinto Changed in gw_read_backend_event the check for dcb_read(), now is if rc < 0 + * 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support + * 10/11/2014 Massimiliano Pinto Client charset is passed to backend + * + */ +#include + +MODULE_INFO info = { + MODULE_API_PROTOCOL, + MODULE_GA, + GWPROTOCOL_VERSION, + "The MySQL to backend server protocol" +}; + +/** Defined in log_manager.cc */ +extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; + +static char *version_str = "V2.0.0"; +static int gw_create_backend_connection(DCB *backend, SERVER *server, SESSION *in_session); +static int gw_read_backend_event(DCB* dcb); +static int gw_write_backend_event(DCB *dcb); +static int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue); +static int gw_error_backend_event(DCB *dcb); +static int gw_backend_close(DCB *dcb); +static int gw_backend_hangup(DCB *dcb); +static int backend_write_delayqueue(DCB *dcb); +static void backend_set_delayqueue(DCB *dcb, GWBUF *queue); +static int gw_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue); +static GWBUF* process_response_data (DCB* dcb, GWBUF* readbuf, int nbytes_to_process); +extern char* create_auth_failed_msg( GWBUF* readbuf, char* hostaddr, uint8_t* sha1); +extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db); +static bool sescmd_response_complete(DCB* dcb); + + +#if defined(NOT_USED) + static int gw_session(DCB *backend_dcb, void *data); +#endif +static MYSQL_session* gw_get_shared_session_auth_info(DCB* dcb); + +static GWPROTOCOL MyObject = { + gw_read_backend_event, /* Read - EPOLLIN handler */ + gw_MySQLWrite_backend, /* Write - data from gateway */ + gw_write_backend_event, /* WriteReady - EPOLLOUT handler */ + gw_error_backend_event, /* Error - EPOLLERR handler */ + gw_backend_hangup, /* HangUp - EPOLLHUP handler */ + NULL, /* Accept */ + gw_create_backend_connection, /* Connect */ + gw_backend_close, /* Close */ + NULL, /* Listen */ + gw_change_user, /* Authentication */ + NULL /* Session */ +}; + +/* + * Implementation of the mandatory version entry point + * + * @return version string of the module + */ +char * +version() +{ + return version_str; +} + +/* + * The module initialisation routine, called when the module + * is first loaded. + */ +void +ModuleInit() +{ +} + +/* + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +GWPROTOCOL * +GetModuleObject() +{ + return &MyObject; +} + + +static MYSQL_session* gw_get_shared_session_auth_info( + DCB* dcb) +{ + MYSQL_session* auth_info = NULL; + CHK_DCB(dcb); + CHK_SESSION(dcb->session); + + spinlock_acquire(&dcb->session->ses_lock); + + if (dcb->session->state != SESSION_STATE_ALLOC) { + auth_info = dcb->session->data; + } else { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [gw_get_shared_session_auth_info] Couldn't get " + "session authentication info. Session in a wrong state %d.", + pthread_self(), + dcb->session->state))); + } + spinlock_release(&dcb->session->ses_lock); + + return auth_info; +} + +/** + * Backend Read Event for EPOLLIN on the MySQL backend protocol module + * @param dcb The backend Descriptor Control Block + * @return 1 on operation, 0 for no action + */ +static int gw_read_backend_event(DCB *dcb) { + MySQLProtocol *client_protocol = NULL; + MySQLProtocol *backend_protocol = NULL; + MYSQL_session *current_session = NULL; + int rc = 0; + + + backend_protocol = (MySQLProtocol *) dcb->protocol; + CHK_PROTOCOL(backend_protocol); + + + /* reading MySQL command output from backend and writing to the client */ + { + GWBUF *read_buffer = NULL; + ROUTER_OBJECT *router = NULL; + ROUTER *router_instance = NULL; + SESSION *session = dcb->session; + int nbytes_read = 0; + + CHK_SESSION(session); + router = session->service->router; + router_instance = session->service->router_instance; + + /* read available backend data */ + rc = dcb_read(dcb, &read_buffer); + + if (rc < 0) + { + GWBUF* errbuf; + bool succp; + + errbuf = mysql_create_custom_error( + 1, + 0, + "Read from backend failed"); + + router->handleError( + router_instance, + session->router_session, + errbuf, + dcb, + ERRACT_NEW_CONNECTION, + &succp); + gwbuf_free(errbuf); + + if (!succp) + { + spinlock_acquire(&session->ses_lock); + session->state = SESSION_STATE_STOPPING; + spinlock_release(&session->ses_lock); + } + ss_dassert(dcb->dcb_errhandle_called); + dcb_close(dcb); + rc = 0; + goto return_rc; + } + nbytes_read = gwbuf_length(read_buffer); + + if (nbytes_read == 0 && dcb->dcb_readqueue == NULL) + { + goto return_rc; + } + else + { + ss_dassert(read_buffer != NULL || dcb->dcb_readqueue != NULL); + } + + /** Packet prefix was read earlier */ + if (dcb->dcb_readqueue) + { + if (read_buffer != NULL) + { + read_buffer = gwbuf_append(dcb->dcb_readqueue, read_buffer); + } + else + { + read_buffer = dcb->dcb_readqueue; + } + nbytes_read = gwbuf_length(read_buffer); + + if (nbytes_read < 5) /*< read at least command type */ + { + rc = 0; + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "%p [gw_read_backend_event] Read %d bytes " + "from DCB %p, fd %d, session %s. " + "Returning to poll wait.\n", + pthread_self(), + nbytes_read, + dcb, + dcb->fd, + dcb->session))); + goto return_rc; + } + /** There is at least length and command type. */ + else + { + dcb->dcb_readqueue = NULL; + } + } + /** This may be either short prefix of a packet, or the tail of it. */ + else + { + if (nbytes_read < 5) + { + dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); + rc = 0; + goto return_rc; + } + } + + + if (dcb->session->state == SESSION_STATE_ROUTER_READY && + dcb->session->client != NULL && + dcb->session->client->state == DCB_STATE_POLLING) + { + client_protocol = SESSION_PROTOCOL(dcb->session, + MySQLProtocol); + + { + gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL); + router->clientReply(router_instance, session->router_session, read_buffer, dcb); + rc = 1; + } + } + else /*< session is closing; replying to client isn't possible */ + { + gwbuf_free(read_buffer); + } + } + +return_rc: + return rc; + +return_with_lock: + + goto return_rc; +} + +/* + * EPOLLOUT handler for the MySQL Backend protocol module. + * + * @param dcb The descriptor control block + * @return 1 in success, 0 in case of failure, + */ +static int gw_write_backend_event(DCB *dcb) { + int rc = 0; + MySQLProtocol *backend_protocol = dcb->protocol; + + /*< + * Don't write to backend if backend_dcb is not in poll set anymore. + */ + if (dcb->state != DCB_STATE_POLLING) { + uint8_t* data; + + if (dcb->writeq != NULL) + { + data = (uint8_t *)GWBUF_DATA(dcb->writeq); + + + } + else + { + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_write_backend_event] Dcb %p in state %s " + "but there's nothing to write either.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); + rc = 1; + } + goto return_rc; + } + + dcb_drain_writeq(dcb); + rc = 1; +return_rc: + + + return rc; +} + +/* + * Write function for backend DCB. Store command to protocol. + * + * @param dcb The DCB of the backend + * @param queue Queue of buffers to write + * @return 0 on failure, 1 on success + */ +static int +gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue) +{ + MySQLProtocol *backend_protocol = dcb->protocol; + int rc = 0; + + rc = dcb_write(dcb, queue); + + +return_rc: + return rc; +} + +/** + * Error event handler. + * Create error message, pass it to router's error handler and if error + * handler fails in providing enough backend servers, mark session being + * closed and call DCB close function which triggers closing router session + * and related backends (if any exists. + */ +static int gw_error_backend_event(DCB *dcb) +{ + SESSION* session; + void* rsession; + ROUTER_OBJECT* router; + ROUTER* router_instance; + GWBUF* errbuf; + bool succp; + session_state_t ses_state; + + CHK_DCB(dcb); + session = dcb->session; + CHK_SESSION(session); + rsession = session->router_session; + router = session->service->router; + router_instance = session->service->router_instance; + + /** + * Avoid running redundant error handling procedure. + * dcb_close is already called for the DCB. Thus, either connection is + * closed by router and COM_QUIT sent or there was an error which + * have already been handled. + */ + if (dcb->state != DCB_STATE_POLLING) + { + int error, len; + char buf[100]; + + len = sizeof(error); + + if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) + { + if (error != 0) + { + strerror_r(error, buf, 100); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "DCB in state %s got error '%s'.", + STRDCBSTATE(dcb->state), + buf))); + } + } + return 1; + } + errbuf = mysql_create_custom_error( + 1, + 0, + "Lost connection to backend server."); + + spinlock_acquire(&session->ses_lock); + ses_state = session->state; + spinlock_release(&session->ses_lock); + + /** + * Session might be initialized when DCB already is in the poll set. + * Thus hangup can occur in the middle of session initialization. + * Only complete and successfully initialized sessions allow for + * calling error handler. + */ + while (ses_state == SESSION_STATE_READY) + { + spinlock_acquire(&session->ses_lock); + ses_state = session->state; + spinlock_release(&session->ses_lock); + } + + if (ses_state != SESSION_STATE_ROUTER_READY) + { + int error, len; + char buf[100]; + + len = sizeof(error); + if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) + { + if (error != 0) + { + strerror_r(error, buf, 100); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error '%s' in session that is not ready for routing.", + buf))); + } + } + gwbuf_free(errbuf); + goto retblock; + } + +#if defined(SS_DEBUG) + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Backend error event handling."))); +#endif + router->handleError(router_instance, + rsession, + errbuf, + dcb, + ERRACT_NEW_CONNECTION, + &succp); + gwbuf_free(errbuf); + + /** + * If error handler fails it means that routing session can't continue + * and it must be closed. In success, only this DCB is closed. + */ + if (!succp) + { + spinlock_acquire(&session->ses_lock); + session->state = SESSION_STATE_STOPPING; + spinlock_release(&session->ses_lock); + } + ss_dassert(dcb->dcb_errhandle_called); + dcb_close(dcb); + +retblock: + return 1; +} + +/* + * Create a new backend connection. + * + * This routine will connect to a backend server and it is called by dbc_connect + * in router->newSession + * + * @param backend_dcb, in, out, use - backend DCB allocated from dcb_connect + * @param server, in, use - server to connect to + * @param session, in use - current session from client DCB + * @return 0/1 on Success and -1 on Failure. + * If succesful, returns positive fd to socket which is connected to + * backend server. Positive fd is copied to protocol and to dcb. + * If fails, fd == -1 and socket is closed. + */ +static int gw_create_backend_connection( + DCB *backend_dcb, + SERVER *server, + SESSION *session) +{ + MySQLProtocol *protocol = NULL; + int rv = -1; + int fd = -1; + + protocol = mysql_protocol_init(backend_dcb, -1); + ss_dassert(protocol != NULL); + + if (protocol == NULL) { + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_create_backend_connection] Failed to create " + "protocol object for backend connection.", + pthread_self()))); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Failed to create " + "protocol object for backend connection."))); + goto return_fd; + } + + /*< if succeed, fd > 0, -1 otherwise */ + rv = gw_do_connect_to_backend(server->name, server->port, &fd); + /*< Assign protocol with backend_dcb */ + backend_dcb->protocol = protocol; + + /*< Set protocol state */ + switch (rv) { + case 0: + ss_dassert(fd > 0); + protocol->fd = fd; + protocol->protocol_auth_state = MYSQL_CONNECTED; + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_create_backend_connection] Established " + "connection to %s:%i, protocol fd %d client " + "fd %d.", + pthread_self(), + server->name, + server->port, + protocol->fd, + session->client->fd))); + break; + + case 1: + ss_dassert(fd > 0); + protocol->protocol_auth_state = MYSQL_PENDING_CONNECT; + protocol->fd = fd; + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_create_backend_connection] Connection " + "pending to %s:%i, protocol fd %d client fd %d.", + pthread_self(), + server->name, + server->port, + protocol->fd, + session->client->fd))); + break; + + default: + ss_dassert(fd == -1); + ss_dassert(protocol->protocol_auth_state == MYSQL_ALLOC); + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_create_backend_connection] Connection " + "failed to %s:%i, protocol fd %d client fd %d.", + pthread_self(), + server->name, + server->port, + protocol->fd, + session->client->fd))); + break; + } /*< switch */ + +return_fd: + return fd; +} + + +/** + * Error event handler. + * Create error message, pass it to router's error handler and if error + * handler fails in providing enough backend servers, mark session being + * closed and call DCB close function which triggers closing router session + * and related backends (if any exists. + * + * @param dcb The current Backend DCB + * @return 1 always + */ +static int +gw_backend_hangup(DCB *dcb) +{ + SESSION* session; + void* rsession; + ROUTER_OBJECT* router; + ROUTER* router_instance; + bool succp; + GWBUF* errbuf; + session_state_t ses_state; + + CHK_DCB(dcb); + session = dcb->session; + CHK_SESSION(session); + + rsession = session->router_session; + router = session->service->router; + router_instance = session->service->router_instance; + + errbuf = mysql_create_custom_error( + 1, + 0, + "Lost connection to backend server."); + + spinlock_acquire(&session->ses_lock); + ses_state = session->state; + spinlock_release(&session->ses_lock); + + /** + * Session might be initialized when DCB already is in the poll set. + * Thus hangup can occur in the middle of session initialization. + * Only complete and successfully initialized sessions allow for + * calling error handler. + */ + while (ses_state == SESSION_STATE_READY) + { + spinlock_acquire(&session->ses_lock); + ses_state = session->state; + spinlock_release(&session->ses_lock); + } + + if (ses_state != SESSION_STATE_ROUTER_READY) + { + int error, len; + char buf[100]; + + len = sizeof(error); + if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) + { + if (error != 0) + { + strerror_r(error, buf, 100); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Hangup in session that is not ready for routing, " + "Error reported is '%s'.", + buf))); + } + } + gwbuf_free(errbuf); + goto retblock; + } +#if defined(SS_DEBUG) + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Backend hangup error handling."))); +#endif + + router->handleError(router_instance, + rsession, + errbuf, + dcb, + ERRACT_NEW_CONNECTION, + &succp); + + gwbuf_free(errbuf); + /** There are no required backends available, close session. */ + if (!succp) + { +#if defined(SS_DEBUG) + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Backend hangup -> closing session."))); +#endif + spinlock_acquire(&session->ses_lock); + session->state = SESSION_STATE_STOPPING; + spinlock_release(&session->ses_lock); + } + ss_dassert(dcb->dcb_errhandle_called); + dcb_close(dcb); + +retblock: + return 1; +} + +/** + * Send COM_QUIT to backend so that it can be closed. + * @param dcb The current Backend DCB + * @return 1 always + */ +static int +gw_backend_close(DCB *dcb) +{ + DCB* client_dcb; + SESSION* session; + GWBUF* quitbuf; + + CHK_DCB(dcb); + session = dcb->session; + CHK_SESSION(session); + + LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, + "%lu [gw_backend_close]", + pthread_self()))); + + quitbuf = mysql_create_com_quit(NULL, 0); + gwbuf_set_type(quitbuf, GWBUF_TYPE_MYSQL); + + /** Send COM_QUIT to the backend being closed */ + mysql_send_com_quit(dcb, 0, quitbuf); + + mysql_protocol_done(dcb); + /** + * The lock is needed only to protect the read of session->state and + * session->client values. Client's state may change by other thread + * but client's close and adding client's DCB to zombies list is executed + * only if client's DCB's state does _not_ change in parallel. + */ + spinlock_acquire(&session->ses_lock); + /** + * If session->state is STOPPING, start closing client session. + * Otherwise only this backend connection is closed. + */ + if (session != NULL && + session->state == SESSION_STATE_STOPPING && + session->client != NULL) + { + if (session->client->state == DCB_STATE_POLLING) + { + spinlock_release(&session->ses_lock); + + /** Close client DCB */ + dcb_close(session->client); + } + else + { + spinlock_release(&session->ses_lock); + } + } + else + { + spinlock_release(&session->ses_lock); + } + return 1; +} + +/** + * This routine put into the delay queue the input queue + * The input is what backend DCB is receiving + * The routine is called from func.write() when mysql backend connection + * is not yet complete buu there are inout data from client + * + * @param dcb The current backend DCB + * @param queue Input data in the GWBUF struct + */ +static void backend_set_delayqueue(DCB *dcb, GWBUF *queue) { + spinlock_acquire(&dcb->delayqlock); + + if (dcb->delayq) { + /* Append data */ + dcb->delayq = gwbuf_append(dcb->delayq, queue); + } else { + if (queue != NULL) { + /* create the delay queue */ + dcb->delayq = queue; + } + } + spinlock_release(&dcb->delayqlock); +} + +/** + * This routine writes the delayq via dcb_write + * The dcb->delayq contains data received from the client before + * mysql backend authentication succeded + * + * @param dcb The current backend DCB + * @return The dcb_write status + */ +static int backend_write_delayqueue(DCB *dcb) +{ + GWBUF *localq = NULL; + int rc; + + spinlock_acquire(&dcb->delayqlock); + + if (dcb->delayq == NULL) + { + spinlock_release(&dcb->delayqlock); + rc = 1; + } + else + { + localq = dcb->delayq; + dcb->delayq = NULL; + spinlock_release(&dcb->delayqlock); + + if (MYSQL_IS_CHANGE_USER(((uint8_t *)GWBUF_DATA(localq)))) + { + MYSQL_session* mses; + GWBUF* new_packet; + + mses = (MYSQL_session *)dcb->session->client->data; + new_packet = gw_create_change_user_packet( + mses, + (MySQLProtocol *)dcb->protocol); + /** + * Remove previous packet which lacks scramble + * and append the new. + */ + localq = gwbuf_consume(localq, GWBUF_LENGTH(localq)); + localq = gwbuf_append(localq, new_packet); + } + rc = dcb_write(dcb, localq); + } + + if (rc == 0) + { + GWBUF* errbuf; + bool succp; + ROUTER_OBJECT *router = NULL; + ROUTER *router_instance = NULL; + void *rsession = NULL; + SESSION *session = dcb->session; + + CHK_SESSION(session); + + if (session != NULL) + { + router = session->service->router; + router_instance = session->service->router_instance; + rsession = session->router_session; +#if defined(SS_DEBUG) + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Backend write delayqueue error handling."))); +#endif + errbuf = mysql_create_custom_error( + 1, + 0, + "Failed to write buffered data to back-end server. " + "Buffer was empty or back-end was disconnected during " + "operation. Attempting to find a new backend."); + + router->handleError(router_instance, + rsession, + errbuf, + dcb, + ERRACT_NEW_CONNECTION, + &succp); + gwbuf_free(errbuf); + + if (!succp) + { + spinlock_acquire(&session->ses_lock); + session->state = SESSION_STATE_STOPPING; + spinlock_release(&session->ses_lock); + ss_dassert(dcb->dcb_errhandle_called); + dcb_close(dcb); + } + } + } + return rc; +} + +/** + * This routine handles the COM_CHANGE_USER command + * + * @param dcb The current backend DCB + * @param server The backend server pointer + * @param in_session The current session data (MYSQL_session) + * @param queue The GWBUF containing the COM_CHANGE_USER receveid + * @return 1 on success and 0 on failure + */ +static int gw_change_user( + DCB *backend, + SERVER *server, + SESSION *in_session, + GWBUF *queue) +{ + MYSQL_session *current_session = NULL; + MySQLProtocol *backend_protocol = NULL; + MySQLProtocol *client_protocol = NULL; + char username[MYSQL_USER_MAXLEN+1]=""; + char database[MYSQL_DATABASE_MAXLEN+1]=""; + char current_database[MYSQL_DATABASE_MAXLEN+1]=""; + uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]=""; + uint8_t *client_auth_packet = GWBUF_DATA(queue); + unsigned int auth_token_len = 0; + uint8_t *auth_token = NULL; + int rv = -1; + int auth_ret = 1; + + current_session = (MYSQL_session *)in_session->client->data; + backend_protocol = backend->protocol; + client_protocol = in_session->client->protocol; + + /* now get the user, after 4 bytes header and 1 byte command */ + client_auth_packet += 5; + strncpy(username, (char *)client_auth_packet,MYSQL_USER_MAXLEN); + client_auth_packet += strlen(username) + 1; + + /* get the auth token len */ + memcpy(&auth_token_len, client_auth_packet, 1); + + client_auth_packet++; + + /* allocate memory for token only if auth_token_len > 0 */ + if (auth_token_len > 0) { + auth_token = (uint8_t *)malloc(auth_token_len); + ss_dassert(auth_token != NULL); + + if (auth_token == NULL) + return rv; + memcpy(auth_token, client_auth_packet, auth_token_len); + client_auth_packet += auth_token_len; + } + + /* get new database name */ + strncpy(database, (char *)client_auth_packet,MYSQL_DATABASE_MAXLEN); + + /* get character set */ + if (strlen(database)) { + client_auth_packet += strlen(database) + 1; + } else { + client_auth_packet++; + } + + if (client_auth_packet && *client_auth_packet) + memcpy(&backend_protocol->charset, client_auth_packet, sizeof(int)); + + /* save current_database name */ + strncpy(current_database, current_session->db,MYSQL_DATABASE_MAXLEN); + + /* + * Now clear database name in dcb as we don't do local authentication on db name for change user. + * Local authentication only for user@host and if successful the database name change is sent to backend. + */ + strcpy(current_session->db, ""); + + /* + * decode the token and check the password. + * Note: if auth_token_len == 0 && auth_token == NULL, user is without password + */ + auth_ret = gw_check_mysql_scramble_data(backend->session->client, + auth_token, + auth_token_len, + client_protocol->scramble, + sizeof(client_protocol->scramble), + username, + client_sha1); + + if (auth_ret != 0) { + if (!service_refresh_users(backend->session->client->service)) { + /* Try authentication again with new repository data */ + /* Note: if no auth client authentication will fail */ + auth_ret = gw_check_mysql_scramble_data( + backend->session->client, + auth_token, auth_token_len, + client_protocol->scramble, + sizeof(client_protocol->scramble), + username, + client_sha1); + } + } + + /* copy back current datbase to client session */ + strcpy(current_session->db, current_database); + + /* let's free the auth_token now */ + if (auth_token) + free(auth_token); + + if (auth_ret != 0) { + char *password_set = NULL; + char *message = NULL; + GWBUF* buf; + + if (auth_token_len > 0) + password_set = (char *)client_sha1; + else + password_set = ""; + + /** + * Create an error message and make it look like legit reply + * from backend server. Then make it look like an incoming event + * so that thread gets new task of it, calls clientReply + * which filters out duplicate errors from same cause and forward + * reply to the client. + */ + message = create_auth_fail_str(username, + backend->session->client->remote, + password_set, + ""); + if (message == NULL) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Creating error message failed."))); + rv = 0; + goto retblock; + } + /** + * Add command to backend's protocol, create artificial reply + * packet and add it to client's read buffer. + */ + protocol_add_srv_command((MySQLProtocol*)backend->protocol, + MYSQL_COM_CHANGE_USER); + modutil_reply_auth_error(backend, message, 0); + rv = 1; + } else { + rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol); + /* + * Now copy new data into user session + */ + strcpy(current_session->user, username); + strcpy(current_session->db, database); + memcpy(current_session->client_sha1, client_sha1, sizeof(current_session->client_sha1)); + } + +retblock: + gwbuf_free(queue); + + return rv; +} + + +/** + * Move packets or parts of packets from readbuf to outbuf as the packet headers + * and lengths have been noticed and counted. + * Session commands need to be marked so that they can be handled properly in + * the router's clientReply. + * + * @param dcb Backend's DCB where data was read from + * @param readbuf GWBUF where data was read to + * @param nbytes_to_process Number of bytes that has been read and need to be processed + * + * @return GWBUF which includes complete MySQL packet + */ +static GWBUF* process_response_data ( + DCB* dcb, + GWBUF* readbuf, + int nbytes_to_process) +{ + int npackets_left = 0; /*< response's packet count */ + ssize_t nbytes_left = 0; /*< nbytes to be read for the packet */ + MySQLProtocol* p; + GWBUF* outbuf = NULL; + + /** Get command which was stored in gw_MySQLWrite_backend */ + p = DCB_PROTOCOL(dcb, MySQLProtocol); + if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(p); + + /** All buffers processed here are sescmd responses */ + gwbuf_set_type(readbuf, GWBUF_TYPE_SESCMD_RESPONSE); + + /** + * Now it is known how many packets there should be and how much + * is read earlier. + */ + while (nbytes_to_process != 0) + { + mysql_server_cmd_t srvcmd; + bool succp; + + srvcmd = protocol_get_srv_command(p, false); + + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [process_response_data] Read command %s for DCB %p fd %d.", + pthread_self(), + STRPACKETTYPE(srvcmd), + dcb, + dcb->fd))); + /** + * Read values from protocol structure, fails if values are + * uninitialized. + */ + if (npackets_left == 0) + { + succp = protocol_get_response_status(p, &npackets_left, &nbytes_left); + + if (!succp || npackets_left == 0) + { + /** + * Examine command type and the readbuf. Conclude response + * packet count from the command type or from the first + * packet content. Fails if read buffer doesn't include + * enough data to read the packet length. + */ + init_response_status(readbuf, srvcmd, &npackets_left, &nbytes_left); + } + } + /** Only session commands with responses should be processed */ + ss_dassert(npackets_left > 0); + + /** Read incomplete packet. */ + if (nbytes_left > nbytes_to_process) + { + /** Includes length info so it can be processed */ + if (nbytes_to_process >= 5) + { + /** discard source buffer */ + readbuf = gwbuf_consume(readbuf, GWBUF_LENGTH(readbuf)); + nbytes_left -= nbytes_to_process; + } + nbytes_to_process = 0; + } + /** Packet was read. All bytes belonged to the last packet. */ + else if (nbytes_left == nbytes_to_process) + { + nbytes_left = 0; + nbytes_to_process = 0; + ss_dassert(npackets_left > 0); + npackets_left -= 1; + outbuf = gwbuf_append(outbuf, readbuf); + readbuf = NULL; + } + /** + * Packet was read. There should be more since bytes were + * left over. + * Move the next packet to its own buffer and add that next + * to the prev packet's buffer. + */ + else /*< nbytes_left < nbytes_to_process */ + { + ss_dassert(nbytes_left >= 0); + nbytes_to_process -= nbytes_left; + + /** Move the prefix of the buffer to outbuf from redbuf */ + outbuf = gwbuf_append(outbuf, + gwbuf_clone_portion(readbuf, 0, (size_t)nbytes_left)); + readbuf = gwbuf_consume(readbuf, (size_t)nbytes_left); + ss_dassert(npackets_left > 0); + npackets_left -= 1; + nbytes_left = 0; + } + + /** Store new status to protocol structure */ + protocol_set_response_status(p, npackets_left, nbytes_left); + + /** A complete packet was read */ + if (nbytes_left == 0) + { + /** No more packets in this response */ + if (npackets_left == 0 && outbuf != NULL) + { + GWBUF* b = outbuf; + + while (b->next != NULL) + { + b = b->next; + } + /** Mark last as end of response */ + gwbuf_set_type(b, GWBUF_TYPE_RESPONSE_END); + + /** Archive the command */ + protocol_archive_srv_command(p); + } + /** Read next packet */ + else + { + uint8_t* data; + + /** Read next packet length */ + data = GWBUF_DATA(readbuf); + nbytes_left = MYSQL_GET_PACKET_LEN(data)+MYSQL_HEADER_LEN; + /** Store new status to protocol structure */ + protocol_set_response_status(p, npackets_left, nbytes_left); + } + } + } + return outbuf; +} + + +static bool sescmd_response_complete( + DCB* dcb) +{ + int npackets_left; + ssize_t nbytes_left; + MySQLProtocol* p; + bool succp; + + p = DCB_PROTOCOL(dcb, MySQLProtocol); + if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(p); + + protocol_get_response_status(p, &npackets_left, &nbytes_left); + + if (npackets_left == 0) + { + succp = true; + } + else + { + succp = false; + } + return succp; +} diff --git a/server/modules/protocol/mongo_client.c b/server/modules/protocol/mongo_client.c new file mode 100644 index 000000000..1de7d237d --- /dev/null +++ b/server/modules/protocol/mongo_client.c @@ -0,0 +1,1004 @@ +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2015 + */ + +/** + * @file mongo_client.c + * + * Revision History + * Date Who Description + + * + */ +#include +#include +#include +#include +#include +#include + +#include + +MODULE_INFO info = { + MODULE_API_PROTOCOL, + MODULE_GA, + GWPROTOCOL_VERSION, + "The client to plain protocol implementation" +}; + +/** Defined in log_manager.cc */ +extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; + +static char *version_str = "V1.0.0"; + +static int plain_accept(DCB *listener); +static int plain_listen(DCB *listener, char *config_bind); +static int plain_read(DCB* dcb); +static int plain_write_ready(DCB *dcb); +static int plain_write(DCB *dcb, GWBUF *queue); +static int plain_client_error(DCB *dcb); +static int plain_client_close(DCB *dcb); +static int plain_client_hangup_event(DCB *dcb); + +/* + * The "module object" for the mysqld client protocol module. + */ +static GWPROTOCOL MyObject = { + plain_read, /* Read - EPOLLIN handler */ + plain_write, /* Write - data from gateway */ + plain_write_ready, /* WriteReady - EPOLLOUT handler */ + plain_client_error, /* Error - EPOLLERR handler */ + plain_client_hangup_event, /* HangUp - EPOLLHUP handler */ + plain_accept, /* Accept */ + NULL, /* Connect */ + plain_client_close, /* Close */ + plain_listen, /* Listen */ + NULL, /* Authentication */ + NULL /* Session */ +}; + +/** + * Implementation of the mandatory version entry point + * + * @return version string of the module + */ +char * +version() +{ + return version_str; +} + +/** + * The module initialisation routine, called when the module + * is first loaded. + */ +void +ModuleInit() +{ +} + +/** + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +GWPROTOCOL * +GetModuleObject() +{ + return &MyObject; +} + +/** + * mysql_send_ok + * + * Send a MySQL protocol OK message to the dcb (client) + * + * @param dcb Descriptor Control Block for the connection to which the OK is sent + * @param packet_number + * @param in_affected_rows + * @param mysql_message + * @return packet length + * + */ +int +mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) { + return 1; +} + +/** + * MySQLSendHandshake + * + * @param dcb The descriptor control block to use for sending the handshake request + * @return The packet length sent + */ +int +MySQLSendHandshake(DCB* dcb) +{ + return 1; +} + +/** + * gw_mysql_do_authentication + * + * Performs the MySQL protocol 4.1 authentication, using data in GWBUF *queue + * + * (MYSQL_session*)client_data including: user, db, client_sha1 are copied into + * the dcb->data and later to dcb->session->data. + * + * client_capabilitiesa are copied into the dcb->protocol + * + * @param dcb Descriptor Control Block of the client + * @param queue The GWBUF with data from client + * @return 0 If succeed, otherwise non-zero value + * + * @note in case of failure, dcb->data is freed before returning. If succeed, + * dcb->data is freed in session.c:session_free. + */ +static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { + MySQLProtocol *protocol = NULL; + /* int compress = -1; */ + int connect_with_db = -1; + uint8_t *client_auth_packet = GWBUF_DATA(queue); + int client_auth_packet_size = 0; + char *username = NULL; + char *database = NULL; + unsigned int auth_token_len = 0; + uint8_t *auth_token = NULL; + uint8_t *stage1_hash = NULL; + int auth_ret = -1; + MYSQL_session *client_data = NULL; + + CHK_DCB(dcb); + + protocol = DCB_PROTOCOL(dcb, MySQLProtocol); + CHK_PROTOCOL(protocol); + client_data = (MYSQL_session *)calloc(1, sizeof(MYSQL_session)); +#if defined(SS_DEBUG) + client_data->myses_chk_top = CHK_NUM_MYSQLSES; + client_data->myses_chk_tail = CHK_NUM_MYSQLSES; +#endif + /** + * Assign authentication structure with client DCB. + */ + dcb->data = client_data; + + stage1_hash = client_data->client_sha1; + username = client_data->user; + + client_auth_packet_size = gwbuf_length(queue); + + /* For clients supporting CLIENT_PROTOCOL_41 + * the Handshake Response Packet is: + * + * 4 bytes mysql protocol heade + * 4 bytes capability flags + * 4 max-packet size + * 1 byte character set + * string[23] reserved (all [0]) + * ... + * ... + */ + + /* Detect now if there are enough bytes to continue */ + if (client_auth_packet_size < (4 + 4 + 4 + 1 + 23)) + { + return 1; + } + + memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4); + + connect_with_db = + GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB & gw_mysql_get_byte4( + (uint32_t *)&protocol->client_capabilities); + /* + compress = + GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4( + &protocol->client_capabilities); + */ + + username = get_username_from_auth(username, client_auth_packet); + + if (username == NULL) + { + return 1; + } + + /* get charset */ + memcpy(&protocol->charset, client_auth_packet + 4 + 4 + 4, sizeof (int)); + + /* get the auth token len */ + memcpy(&auth_token_len, + client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1, + 1); + + /* + * Note: some clients may pass empty database, connect_with_db !=0 but database ="" + */ + if (connect_with_db) { + database = client_data->db; + strncpy(database, + (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + + 1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN); + } + + /* allocate memory for token only if auth_token_len > 0 */ + if (auth_token_len) { + auth_token = (uint8_t *)malloc(auth_token_len); + memcpy(auth_token, + client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1, + auth_token_len); + } + + /* + * Decode the token and check the password + * Note: if auth_token_len == 0 && auth_token == NULL, user is without password + */ + + auth_ret = gw_check_mysql_scramble_data(dcb, + auth_token, + auth_token_len, + protocol->scramble, + sizeof(protocol->scramble), + username, + stage1_hash); + + /* check for database name match in resource hashtable */ + auth_ret = check_db_name_after_auth(dcb, database, auth_ret); + + /* On failed auth try to load users' table from backend database */ + if (auth_ret != 0) { + if (!service_refresh_users(dcb->service)) { + /* Try authentication again with new repository data */ + /* Note: if no auth client authentication will fail */ + auth_ret = gw_check_mysql_scramble_data( + dcb, + auth_token, + auth_token_len, + protocol->scramble, + sizeof(protocol->scramble), + username, + stage1_hash); + } + else + { + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "%s: login attempt for user %s, user not " + "found.", + dcb->service->name, username))); + } + } + + /* Do again the database check */ + auth_ret = check_db_name_after_auth(dcb, database, auth_ret); + + /* on succesful auth set user into dcb field */ + if (auth_ret == 0) { + dcb->user = strdup(client_data->user); + } + + /* let's free the auth_token now */ + if (auth_token) { + free(auth_token); + } + + return auth_ret; +} + +/** + * Write function for client DCB: writes data from MaxScale to Client + * + * @param dcb The DCB of the client + * @param queue Queue of buffers to write + */ +int +plain_write(DCB *dcb, GWBUF *queue) +{ + return dcb_write(dcb, queue); +} + +/** + * Client read event triggered by EPOLLIN + * + * @param dcb Descriptor control block + * @return 0 if succeed, 1 otherwise + */ +int plain_read( + DCB* dcb) +{ + SESSION *session = NULL; + ROUTER_OBJECT *router = NULL; + ROUTER *router_instance = NULL; + void *rsession = NULL; + MySQLProtocol *protocol = NULL; + GWBUF *read_buffer = NULL; + int rc = 0; + int nbytes_read = 0; + uint8_t cap = 0; + bool stmt_input = false; /*< router input type */ + + CHK_DCB(dcb); + protocol = DCB_PROTOCOL(dcb, MySQLProtocol); + CHK_PROTOCOL(protocol); + rc = dcb_read(dcb, &read_buffer); + + if (rc < 0) + { + dcb_close(dcb); + } + nbytes_read = gwbuf_length(read_buffer); + + if (nbytes_read == 0) + { + goto return_rc; + } + + if(dcb->session == NULL) + { + dcb->session = session_alloc(dcb->service,dcb); + } + + rc = SESSION_ROUTE_QUERY(dcb->session, read_buffer); + + +return_rc: + + return rc; +} + +/////////////////////////////////////////////// +// client write event to Client triggered by EPOLLOUT +////////////////////////////////////////////// +/** + * @node Client's fd became writable, and EPOLLOUT event + * arrived. As a consequence, client input buffer (writeq) is flushed. + * + * Parameters: + * @param dcb - in, use + * client dcb + * + * @return constantly 1 + * + * + * @details (write detailed description here) + * + */ +int plain_write_ready(DCB *dcb) +{ + MySQLProtocol *protocol = NULL; + + CHK_DCB(dcb); + + ss_dassert(dcb->state != DCB_STATE_DISCONNECTED); + + if (dcb == NULL) { + goto return_1; + } + + if (dcb->state == DCB_STATE_DISCONNECTED) { + goto return_1; + } + + if (dcb->protocol == NULL) { + goto return_1; + } + protocol = (MySQLProtocol *)dcb->protocol; + + + dcb_drain_writeq(dcb); + goto return_1; + +return_1: + + return 1; +} + +/** + * set listener for mysql protocol, retur 1 on success and 0 in failure + */ +int plain_listen( + DCB *listen_dcb, + char *config_bind) +{ + int l_so; + int syseno = 0; + struct sockaddr_in serv_addr; + struct sockaddr_un local_addr; + struct sockaddr *current_addr; + int one = 1; + int rc; + + if (strchr(config_bind, '/')) { + char *tmp = strrchr(config_bind, ':'); + if (tmp) + *tmp = '\0'; + + // UNIX socket create + if ((l_so = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, + "\n* Error: can't create UNIX socket due " + "error %i, %s.\n\n\t", + errno, + strerror(errno)); + return 0; + } + memset(&local_addr, 0, sizeof(local_addr)); + local_addr.sun_family = AF_UNIX; + strncpy(local_addr.sun_path, config_bind, sizeof(local_addr.sun_path) - 1); + + current_addr = (struct sockaddr *) &local_addr; + + } else { + /* MaxScale, as default, will bind on port 4406 */ + if (!parse_bindconfig(config_bind, 4406, &serv_addr)) { + fprintf(stderr, "Error in parse_bindconfig for [%s]\n", config_bind); + return 0; + } + // TCP socket create + if ((l_so = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, + "\n* Error: can't create socket due " + "error %i, %s.\n\n\t", + errno, + strerror(errno)); + return 0; + } + + current_addr = (struct sockaddr *) &serv_addr; + } + + listen_dcb->fd = -1; + + // socket options + if((syseno = setsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))) != 0){ + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); + } + + + // set NONBLOCKING mode + setnonblocking(l_so); + + /* get the right socket family for bind */ + switch (current_addr->sa_family) { + case AF_UNIX: + rc = unlink(config_bind); + if ( (rc == -1) && (errno!=ENOENT) ) { + fprintf(stderr, "Error unlink Unix Socket %s\n", config_bind); + } + + if (bind(l_so, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { + fprintf(stderr, + "\n* Bind failed due error %i, %s.\n", + errno, + strerror(errno)); + fprintf(stderr, "* Can't bind to %s\n\n", config_bind); + close(l_so); + return 0; + } + + /* set permission for all users */ + if (chmod(config_bind, 0777) < 0) { + fprintf(stderr, + "\n* chmod failed for %s due error %i, %s.\n\n", + config_bind, + errno, + strerror(errno)); + } + + break; + + case AF_INET: + if (bind(l_so, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + fprintf(stderr, + "\n* Bind failed due error %i, %s.\n", + errno, + strerror(errno)); + fprintf(stderr, "* Can't bind to %s\n\n", config_bind); + close(l_so); + return 0; + } + break; + + default: + fprintf(stderr, "* Socket Family %i not supported\n", current_addr->sa_family); + close(l_so); + return 0; + } + + rc = listen(l_so, 10 * SOMAXCONN); + + if (rc == 0) { + LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening MySQL connections at %s", config_bind))); + } else { + int eno = errno; + errno = 0; + fprintf(stderr, + "\n* Failed to start listening MySQL due error %d, %s\n\n", + eno, + strerror(eno)); + close(l_so); + return 0; + } + // assign l_so to dcb + listen_dcb->fd = l_so; + + // add listening socket to poll structure + if (poll_add_dcb(listen_dcb) == -1) { + fprintf(stderr, + "\n* Failed to start polling the socket due error " + "%i, %s.\n\n", + errno, + strerror(errno)); + return 0; + } +#if defined(FAKE_CODE) + conn_open[l_so] = true; +#endif /* FAKE_CODE */ + listen_dcb->func.accept = plain_accept; + + return 1; +} + + +/** + * @node (write brief function description here) + * + * Parameters: + * @param listener - + * + * + * @return 0 in success, 1 in failure + * + * + * @details (write detailed description here) + * + */ +int plain_accept(DCB *listener) +{ + int rc = 0; + DCB *client_dcb; + MySQLProtocol *protocol; + int c_sock; + struct sockaddr client_conn; + socklen_t client_len = sizeof(struct sockaddr_storage); + int sendbuf = GW_BACKEND_SO_SNDBUF; + socklen_t optlen = sizeof(sendbuf); + int eno = 0; + int syseno = 0; + int i = 0; + + CHK_DCB(listener); + + while (1) { + + retry_accept: + +#if defined(FAKE_CODE) + if (fail_next_accept > 0) + { + c_sock = -1; + eno = fail_accept_errno; + fail_next_accept -= 1; + } else { + fail_accept_errno = 0; +#endif /* FAKE_CODE */ + // new connection from client + c_sock = accept(listener->fd, + (struct sockaddr *) &client_conn, + &client_len); + eno = errno; + errno = 0; +#if defined(FAKE_CODE) + } +#endif /* FAKE_CODE */ + + if (c_sock == -1) { + + if (eno == EAGAIN || eno == EWOULDBLOCK) + { + /** + * We have processed all incoming connections. + */ + rc = 1; + goto return_rc; + } + else if (eno == ENFILE || eno == EMFILE) + { + struct timespec ts1; + ts1.tv_sec = 0; + /** + * Exceeded system's (ENFILE) or processes + * (EMFILE) max. number of files limit. + */ + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [plain_accept] Error %d, %s. ", + pthread_self(), + eno, + strerror(eno)))); + + if (i == 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error %d, %s. " + "Failed to accept new client " + "connection.", + eno, + strerror(eno)))); + } + i++; + ts1.tv_nsec = 100*i*i*1000000; + nanosleep(&ts1, NULL); + + if (i<10) { + goto retry_accept; + } + rc = 1; + goto return_rc; + } + else + { + /** + * Other error. + */ + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [plain_accept] Error %d, %s.", + pthread_self(), + eno, + strerror(eno)))); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Failed to accept new client " + "connection due to %d, %s.", + eno, + strerror(eno)))); + rc = 1; + goto return_rc; + } /* if (eno == ..) */ + } /* if (c_sock == -1) */ + /* reset counter */ + i = 0; + + listener->stats.n_accepts++; +#if defined(SS_DEBUG) + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "%lu [plain_accept] Accepted fd %d.", + pthread_self(), + c_sock))); +#endif /* SS_DEBUG */ +#if defined(FAKE_CODE) + conn_open[c_sock] = true; +#endif /* FAKE_CODE */ + /* set nonblocking */ + sendbuf = GW_CLIENT_SO_SNDBUF; + + if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen)) != 0){ + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); + } + + sendbuf = GW_CLIENT_SO_RCVBUF; + + if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_RCVBUF, &sendbuf, optlen)) != 0){ + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); + } + setnonblocking(c_sock); + + client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); + + if (client_dcb == NULL) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Failed to create " + "DCB object for client connection."))); + close(c_sock); + rc = 1; + goto return_rc; + } + + client_dcb->service = listener->session->service; + client_dcb->fd = c_sock; + + // get client address + if ( client_conn.sa_family == AF_UNIX) + { + // client address + client_dcb->remote = strdup("localhost_from_socket"); + // set localhost IP for user authentication + (client_dcb->ipv4).sin_addr.s_addr = 0x0100007F; + } + else + { + /* client IPv4 in raw data*/ + memcpy(&client_dcb->ipv4, + (struct sockaddr_in *)&client_conn, + sizeof(struct sockaddr_in)); + /* client IPv4 in string representation */ + client_dcb->remote = (char *)calloc(INET_ADDRSTRLEN+1, sizeof(char)); + + if (client_dcb->remote != NULL) + { + inet_ntop(AF_INET, + &(client_dcb->ipv4).sin_addr, + client_dcb->remote, + INET_ADDRSTRLEN); + } + } + protocol = mysql_protocol_init(client_dcb, c_sock); + ss_dassert(protocol != NULL); + + if (protocol == NULL) { + /** delete client_dcb */ + dcb_close(client_dcb); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [plain_accept] Failed to create " + "protocol object for client connection.", + pthread_self()))); + rc = 1; + goto return_rc; + } + client_dcb->protocol = protocol; + // assign function poiters to "func" field + memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); + + // client protocol state change + protocol->protocol_auth_state = MYSQL_IDLE; + + /** + * Set new descriptor to event set. At the same time, + * change state to DCB_STATE_POLLING so that + * thread which wakes up sees correct state. + */ + if (poll_add_dcb(client_dcb) == -1) + { + /* Send a custom error as MySQL command reply */ + mysql_send_custom_error( + client_dcb, + 1, + 0, + "MaxScale internal error."); + + /** close client_dcb */ + dcb_close(client_dcb); + + /** Previous state is recovered in poll_add_dcb. */ + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [plain_accept] Failed to add dcb %p for " + "fd %d to epoll set.", + pthread_self(), + client_dcb, + client_dcb->fd))); + rc = 1; + goto return_rc; + } + else + { + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [plain_accept] Added dcb %p for fd " + "%d to epoll set.", + pthread_self(), + client_dcb, + client_dcb->fd))); + } + } /**< while 1 */ +#if defined(SS_DEBUG) + if (rc == 0) { + CHK_DCB(client_dcb); + CHK_PROTOCOL(((MySQLProtocol *)client_dcb->protocol)); + } +#endif +return_rc: + + return rc; +} + +static int plain_client_error( + DCB* dcb) +{ + SESSION* session; + + CHK_DCB(dcb); + + session = dcb->session; + + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [plain_client_error] Error event handling for DCB %p " + "in state %s, session %p.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + (session != NULL ? session : NULL)))); + + if (session != NULL && session->state == SESSION_STATE_STOPPING) + { + goto retblock; + } + + dcb_close(dcb); + +retblock: + return 1; +} + +static int +plain_client_close(DCB *dcb) +{ + SESSION* session; + ROUTER_OBJECT* router; + void* router_instance; +#if defined(SS_DEBUG) + MySQLProtocol* protocol = (MySQLProtocol *)dcb->protocol; + if (dcb->state == DCB_STATE_POLLING || + dcb->state == DCB_STATE_NOPOLLING || + dcb->state == DCB_STATE_ZOMBIE) + { + if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(protocol); + } +#endif + LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, + "%lu [plain_client_close]", + pthread_self()))); + mysql_protocol_done(dcb); + session = dcb->session; + /** + * session may be NULL if session_alloc failed. + * In that case, router session wasn't created. + */ + if (session != NULL) + { + CHK_SESSION(session); + spinlock_acquire(&session->ses_lock); + + if (session->state != SESSION_STATE_STOPPING) + { + session->state = SESSION_STATE_STOPPING; + } + router_instance = session->service->router_instance; + router = session->service->router; + /** + * If router session is being created concurrently router + * session might be NULL and it shouldn't be closed. + */ + if (session->router_session != NULL) + { + spinlock_release(&session->ses_lock); + /** Close router session and all its connections */ + router->closeSession(router_instance, session->router_session); + } + else + { + spinlock_release(&session->ses_lock); + } + } + return 1; +} + +/** + * Handle a hangup event on the client side descriptor. + * + * We simply close the DCB, this will propogate the closure to any + * backend descriptors and perform the session cleanup. + * + * @param dcb The DCB of the connection + */ +static int +plain_client_hangup_event(DCB *dcb) +{ + SESSION* session; + + CHK_DCB(dcb); + session = dcb->session; + + if (session != NULL && session->state == SESSION_STATE_ROUTER_READY) + { + CHK_SESSION(session); + } + + if (session != NULL && session->state == SESSION_STATE_STOPPING) + { + goto retblock; + } + + dcb_close(dcb); + +retblock: + return 1; +} + + +/** + * Detect if buffer includes partial mysql packet or multiple packets. + * Store partial packet to dcb_readqueue. Send complete packets one by one + * to router. + * + * It is assumed readbuf includes at least one complete packet. + * Return 1 in success. If the last packet is incomplete return success but + * leave incomplete packet to readbuf. + * + * @param session Session pointer + * @param p_readbuf Pointer to the address of GWBUF including the query + * + * @return 1 if succeed, + */ +static int route_by_statement( + SESSION* session, + GWBUF** p_readbuf) +{ + int rc; + GWBUF* packetbuf; +#if defined(SS_DEBUG) + GWBUF* tmpbuf; + + tmpbuf = *p_readbuf; + while (tmpbuf != NULL) + { + ss_dassert(GWBUF_IS_TYPE_MYSQL(tmpbuf)); + tmpbuf=tmpbuf->next; + } +#endif + do + { + ss_dassert(GWBUF_IS_TYPE_MYSQL((*p_readbuf))); + + /** + * Collect incoming bytes to a buffer until complete packet has + * arrived and then return the buffer. + */ + packetbuf = gw_MySQL_get_next_packet(p_readbuf); + + if (packetbuf != NULL) + { + CHK_GWBUF(packetbuf); + ss_dassert(GWBUF_IS_TYPE_MYSQL(packetbuf)); + /** + * This means that buffer includes exactly one MySQL + * statement. + * backend func.write uses the information. MySQL backend + * protocol, for example, stores the command identifier + * to protocol structure. When some other thread reads + * the corresponding response the command tells how to + * handle response. + * + * Set it here instead of plain_read to make + * sure it is set to each (MySQL) packet. + */ + gwbuf_set_type(packetbuf, GWBUF_TYPE_SINGLE_STMT); + /** Route query */ + rc = SESSION_ROUTE_QUERY(session, packetbuf); + } + else + { + rc = 1; + goto return_rc; + } + } + while (rc == 1 && *p_readbuf != NULL); + +return_rc: + return rc; +} + diff --git a/server/modules/routing/CMakeLists.txt b/server/modules/routing/CMakeLists.txt index 4cf341d83..a14ca00ad 100644 --- a/server/modules/routing/CMakeLists.txt +++ b/server/modules/routing/CMakeLists.txt @@ -9,6 +9,10 @@ add_library(readconnroute SHARED readconnroute.c) target_link_libraries(readconnroute log_manager utils) install(TARGETS readconnroute DESTINATION modules) +add_library(plainroute SHARED plainroute.c) +target_link_libraries(plainroute log_manager utils) +install(TARGETS plainroute DESTINATION modules) + add_library(debugcli SHARED debugcli.c debugcmd.c) target_link_libraries(debugcli log_manager utils) install(TARGETS debugcli DESTINATION modules) diff --git a/server/modules/routing/plainroute.c b/server/modules/routing/plainroute.c new file mode 100644 index 000000000..245a3427a --- /dev/null +++ b/server/modules/routing/plainroute.c @@ -0,0 +1,923 @@ +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +/** + * @file readconnroute.c - Read Connection Load Balancing Query Router + * + * This is the implementation of a simple query router that balances + * read connections. It assumes the service is configured with a set + * of slaves and that the application clients already split read and write + * queries. It offers a service to balance the client read connections + * over this set of slave servers. It does this once only, at the time + * the connection is made. It chooses the server that currently has the least + * number of connections by keeping a count for each server of how + * many connections the query router has made to the server. + * + * When two servers have the same number of current connections the one with + * the least number of connections since startup will be used. + * + * The router may also have options associated to it that will limit the + * choice of backend server. Currently two options are supported, the "master" + * option will cause the router to only connect to servers marked as masters + * and the "slave" option will limit connections to routers that are marked + * as slaves. If neither option is specified the router will connect to either + * masters or slaves. + * + * @verbatim + * Revision History + * + * Date Who Description + * 14/06/2013 Mark Riddoch Initial implementation + * 25/06/2013 Mark Riddoch Addition of checks for current server state + * 26/06/2013 Mark Riddoch Use server with least connections since + * startup if the number of current + * connections is the same for two servers + * Addition of master and slave options + * 27/06/2013 Vilho Raatikka Added skygw_log_write command as an example + * and necessary headers. + * 17/07/2013 Massimiliano Pinto Added clientReply routine: + * called by backend server to send data to client + * Included mysql_client_server_protocol.h + * with macros and MySQL commands with MYSQL_ prefix + * avoiding any conflict with the standard ones + * in mysql.h + * 22/07/2013 Mark Riddoch Addition of joined router option for Galera + * clusters + * 31/07/2013 Massimiliano Pinto Added a check for candidate server, if NULL return + * 12/08/2013 Mark Riddoch Log unsupported router options + * 04/09/2013 Massimiliano Pinto Added client NULL check in clientReply + * 22/10/2013 Massimiliano Pinto errorReply called from backend, for client error reply + * or take different actions such as open a new backend connection + * 20/02/2014 Massimiliano Pinto If router_options=slave, route traffic to master if no slaves available + * 06/03/2014 Massimiliano Pinto Server connection counter is now updated in closeSession + * 24/06/2014 Massimiliano Pinto New rules for selecting the Master server + * 27/06/2014 Mark Riddoch Addition of server weighting + * + * @endverbatim + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/** Defined in log_manager.cc */ +extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; + +MODULE_INFO info = { + MODULE_API_ROUTER, + MODULE_GA, + ROUTER_VERSION, + "A connection based router to load balance based on connections" +}; + +static char *version_str = "V1.1.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 handleError( + ROUTER *instance, + void *router_session, + GWBUF *errbuf, + DCB *backend_dcb, + error_action_t action, + bool *succp); +static uint8_t getCapabilities (ROUTER* inst, void* router_session); + + +/** The module object definition */ +static ROUTER_OBJECT MyObject = { + createInstance, + newSession, + closeSession, + freeSession, + routeQuery, + diagnostics, + clientReply, + handleError, + getCapabilities +}; + +static bool rses_begin_locked_router_action( + ROUTER_CLIENT_SES* rses); + +static void rses_end_locked_router_action( + ROUTER_CLIENT_SES* rses); + +static BACKEND *get_root_master( + BACKEND **servers); +static int handle_state_switch( + DCB* dcb,DCB_REASON reason, void * routersession); +static SPINLOCK instlock; +static ROUTER_INSTANCE *instances; + +/** + * Implementation of the mandatory version entry point + * + * @return version string of the module + */ +char * +version() +{ + return version_str; +} + +/** + * The module initialisation routine, called when the module + * is first loaded. + */ +void +ModuleInit() +{ + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "Initialise readconnroute router module %s.\n", version_str))); + spinlock_init(&instlock); + instances = NULL; +} + +/** + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +ROUTER_OBJECT * +GetModuleObject() +{ + return &MyObject; +} + +/** + * Create an instance of the router for a particular service + * within the gateway. + * + * @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 * +createInstance(SERVICE *service, char **options) +{ +ROUTER_INSTANCE *inst; +SERVER *server; +SERVER_REF *sref; +int i, n; +BACKEND *backend; +char *weightby; + + if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) { + return NULL; + } + + inst->service = service; + spinlock_init(&inst->lock); + + /* + * We need an array of the backend servers in the instance structure so + * that we can maintain a count of the number of connections to each + * backend server. + */ + for (sref = service->dbref, n = 0; sref; sref = sref->next) + n++; + + inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *)); + if (!inst->servers) + { + free(inst); + return NULL; + } + + for (sref = service->dbref, n = 0; sref; sref = sref->next) + { + if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL) + { + for (i = 0; i < n; i++) + free(inst->servers[i]); + free(inst->servers); + free(inst); + return NULL; + } + inst->servers[n]->server = sref->server; + inst->servers[n]->current_connection_count = 0; + inst->servers[n]->weight = 1000; + n++; + } + inst->servers[n] = NULL; + + if ((weightby = serviceGetWeightingParameter(service)) != NULL) + { + int total = 0; + for (n = 0; inst->servers[n]; n++) + { + backend = inst->servers[n]; + total += atoi(serverGetParameter(backend->server, + weightby)); + } + if (total == 0) + { + LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, + "WARNING: Weighting Parameter for service '%s' " + "will be ignored as no servers have values " + "for the parameter '%s'.\n", + service->name, weightby))); + } + else + { + for (n = 0; inst->servers[n]; n++) + { + int perc, wght; + backend = inst->servers[n]; + perc = ((wght = atoi(serverGetParameter(backend->server, + weightby))) * 1000) / total; + if (perc == 0 && wght != 0) + perc = 1; + backend->weight = perc; + if (perc == 0) + { + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "Server '%s' has no value " + "for weighting parameter '%s', " + "no queries will be routed to " + "this server.\n", + inst->servers[n]->server->unique_name, + weightby))); + } + + } + } + } + + /* + * Process the options + */ + inst->bitmask = 0; + inst->bitvalue = 0; + if (options) + { + for (i = 0; options[i]; i++) + { + if (!strcasecmp(options[i], "master")) + { + inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE); + inst->bitvalue |= SERVER_MASTER; + } + else if (!strcasecmp(options[i], "slave")) + { + inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE); + inst->bitvalue |= SERVER_SLAVE; + } + else if (!strcasecmp(options[i], "synced")) + { + inst->bitmask |= (SERVER_JOINED); + inst->bitvalue |= SERVER_JOINED; + } + else if (!strcasecmp(options[i], "ndb")) + { + inst->bitmask |= (SERVER_NDB); + inst->bitvalue |= SERVER_NDB; + } + else + { + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "* Warning : Unsupported router " + "option \'%s\' for readconnroute. " + "Expected router options are " + "[slave|master|synced|ndb]", + options[i]))); + } + } + } + + /* + * 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); + + return (ROUTER *)inst; +} + +/** + * Associate a new session with this instance of the router. + * + * @param instance The router instance data + * @param session The session itself + * @return Session specific data for this session + */ +static void * +newSession(ROUTER *instance, SESSION *session) +{ +ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance; +ROUTER_CLIENT_SES *client_rses; +BACKEND *candidate = NULL; +int i; + + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "%lu [newSession] new router session with session " + "%p, and inst %p.", + pthread_self(), + session, + inst))); + + + client_rses = (ROUTER_CLIENT_SES *)calloc(1, sizeof(ROUTER_CLIENT_SES)); + + if (client_rses == NULL) { + return NULL; + } + +#if defined(SS_DEBUG) + client_rses->rses_chk_top = CHK_NUM_ROUTER_SES; + client_rses->rses_chk_tail = CHK_NUM_ROUTER_SES; +#endif + + + /** + * Find a backend server to connect to. This is the extent of the + * load balancing algorithm we need to implement for this simple + * connection router. + */ + + /* + * Loop over all the servers and find any that have fewer connections + * than the candidate server. + * + * If a server has less connections than the current candidate we mark this + * as the new candidate to connect to. + * + * If a server has the same number of connections currently as the candidate + * and has had less connections over time than the candidate it will also + * become the new candidate. This has the effect of spreading the + * connections over different servers during periods of very low load. + */ + for (i = 0; inst->servers[i]; i++) { + + + if (SERVER_IN_MAINT(inst->servers[i]->server)) + continue; + + if (inst->servers[i]->weight == 0) + continue; + + + + /* If no candidate set, set first running server as + our initial candidate server */ + if(candidate == NULL) + { + candidate = inst->servers[i]; + } + else if((inst->servers[i]->current_connection_count + * 1000) / inst->servers[i]->weight < + (candidate->current_connection_count * + 1000) / candidate->weight) + { + /* This running server has fewer + connections, set it as a new candidate */ + candidate = inst->servers[i]; + } + else if((inst->servers[i]->current_connection_count + * 1000) / inst->servers[i]->weight == + (candidate->current_connection_count * + 1000) / candidate->weight && + inst->servers[i]->server->stats.n_connections < + candidate->server->stats.n_connections) + { + /* This running server has the same number + of connections currently as the candidate + but has had fewer connections over time + than candidate, set this server to candidate*/ + candidate = inst->servers[i]; + } + } + + + + client_rses->rses_capabilities = RCAP_TYPE_PACKET_INPUT; + + /* + * We now have the server with the least connections. + * Bump the connection count for this server + */ + atomic_add(&candidate->current_connection_count, 1); + client_rses->backend = candidate; + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [newSession] Selected server in port %d. " + "Connections : %d\n", + pthread_self(), + candidate->server->port, + candidate->current_connection_count))); + /* + * Open a backend connection, putting the DCB for this + * connection in the client_rses->backend_dcb + */ + client_rses->backend_dcb = dcb_connect(candidate->server, + session, + candidate->server->protocol); + if (client_rses->backend_dcb == NULL) + { + atomic_add(&candidate->current_connection_count, -1); + free(client_rses); + return NULL; + } + dcb_add_callback( + client_rses->backend_dcb, + DCB_REASON_NOT_RESPONDING, + &handle_state_switch, + client_rses); + inst->stats.n_sessions++; + + /** + * Add this session to the list of active sessions. + */ + spinlock_acquire(&inst->lock); + client_rses->next = inst->connections; + inst->connections = client_rses; + spinlock_release(&inst->lock); + + + return (void *)client_rses; +} + +/** + * @node Unlink from backend server, unlink from router's connection list, + * and free memory of a router client session. + * + * Parameters: + * @param router - + * + * + * @param router_cli_ses - + * + * + * @return void + * + * + * @details (write detailed description here) + * + */ +static void freeSession( + ROUTER* router_instance, + void* router_client_ses) +{ + ROUTER_INSTANCE* router = (ROUTER_INSTANCE *)router_instance; + ROUTER_CLIENT_SES* router_cli_ses = + (ROUTER_CLIENT_SES *)router_client_ses; + int prev_val; + + prev_val = atomic_add(&router_cli_ses->backend->current_connection_count, -1); + ss_dassert(prev_val > 0); + + spinlock_acquire(&router->lock); + + if (router->connections == router_cli_ses) { + router->connections = router_cli_ses->next; + } else { + ROUTER_CLIENT_SES *ptr = router->connections; + + while (ptr != NULL && ptr->next != router_cli_ses) { + ptr = ptr->next; + } + + if (ptr != NULL) { + ptr->next = router_cli_ses->next; + } + } + spinlock_release(&router->lock); + + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "%lu [freeSession] Unlinked router_client_session %p from " + "router %p and from server on port %d. Connections : %d. ", + pthread_self(), + router_cli_ses, + router, + router_cli_ses->backend->server->port, + prev_val-1))); + + free(router_cli_ses); +} + + +/** + * 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 + */ +static void +closeSession(ROUTER *instance, void *router_session) +{ +ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session; +DCB* backend_dcb; + + CHK_CLIENT_RSES(router_cli_ses); + /** + * Lock router client session for secure read and update. + */ + if (rses_begin_locked_router_action(router_cli_ses)) + { + /* decrease server current connection counter */ + atomic_add(&router_cli_ses->backend->server->stats.n_current, -1); + + backend_dcb = router_cli_ses->backend_dcb; + router_cli_ses->backend_dcb = NULL; + router_cli_ses->rses_closed = true; + /** Unlock */ + rses_end_locked_router_action(router_cli_ses); + + /** + * Close the backend server connection + */ + if (backend_dcb != NULL) { + CHK_DCB(backend_dcb); + dcb_close(backend_dcb); + } + } +} + +/** + * We have data from the client, we must route it to the backend. + * This is simply a case of sending it to the connection that was + * chosen when we started the client session. + * + * @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 if succeed 1, otherwise 0 + */ +static int +routeQuery(ROUTER *instance, void *router_session, GWBUF *queue) +{ + ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance; + ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session; + uint8_t *payload = GWBUF_DATA(queue); + int mysql_command; + int rc; + DCB* backend_dcb; + bool rses_is_closed; + + inst->stats.n_queries++; + + /** Dirty read for quick check if router is closed. */ + if (router_cli_ses->rses_closed) + { + rses_is_closed = true; + } + else + { + /** + * Lock router client session for secure read of DCBs + */ + rses_is_closed = !(rses_begin_locked_router_action(router_cli_ses)); + } + + if (!rses_is_closed) + { + backend_dcb = router_cli_ses->backend_dcb; + /** unlock */ + rses_end_locked_router_action(router_cli_ses); + } + + if (rses_is_closed || backend_dcb == NULL) + { + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Error : Failed to route MySQL command %d to backend " + "server.", + mysql_command))); + rc = 0; + goto return_rc; + } + + + rc = backend_dcb->func.write(backend_dcb, queue); + +return_rc: + return rc; +} + +/** + * Display router diagnostics + * + * @param instance Instance of the router + * @param dcb DCB to send diagnostics to + */ +static void +diagnostics(ROUTER *router, DCB *dcb) +{ +ROUTER_INSTANCE *router_inst = (ROUTER_INSTANCE *)router; +ROUTER_CLIENT_SES *session; +int i = 0; +BACKEND *backend; +char *weightby; + + spinlock_acquire(&router_inst->lock); + session = router_inst->connections; + while (session) + { + i++; + session = session->next; + } + spinlock_release(&router_inst->lock); + + dcb_printf(dcb, "\tNumber of router sessions: %d\n", + router_inst->stats.n_sessions); + dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i); + dcb_printf(dcb, "\tNumber of queries forwarded: %d\n", + router_inst->stats.n_queries); + if ((weightby = serviceGetWeightingParameter(router_inst->service)) + != NULL) + { + dcb_printf(dcb, "\tConnection distribution based on %s " + "server parameter.\n", + weightby); + dcb_printf(dcb, + "\t\tServer Target %% Connections\n"); + for (i = 0; router_inst->servers[i]; i++) + { + backend = router_inst->servers[i]; + dcb_printf(dcb, "\t\t%-20s %3.1f%% %d\n", + backend->server->unique_name, + (float)backend->weight / 10, + backend->current_connection_count); + } + + } +} + +/** + * Client Reply routine + * + * The routine will reply to client data from backend server + * + * @param instance The router instance + * @param router_session The router session + * @param backend_dcb The backend DCB + * @param queue The GWBUF with reply data + */ +static void +clientReply( + ROUTER *instance, + void *router_session, + GWBUF *queue, + DCB *backend_dcb) +{ + DCB *client ; + + client = backend_dcb->session->client; + + ss_dassert(client != NULL); + + SESSION_ROUTE_REPLY(backend_dcb->session, queue); +} + +/** + * Error Handler routine + * + * The routine will handle errors that occurred in backend writes. + * + * @param instance The router instance + * @param router_session The router session + * @param message The error message to reply + * @param backend_dcb The backend DCB + * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION + * + */ +static void handleError( + ROUTER *instance, + void *router_session, + GWBUF *errbuf, + DCB *backend_dcb, + error_action_t action, + bool *succp) + +{ + DCB *client_dcb; + SESSION *session = backend_dcb->session; + session_state_t sesstate; + + /** Reset error handle flag from a given DCB */ + if (action == ERRACT_RESET) + { + backend_dcb->dcb_errhandle_called = false; + return; + } + + /** 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; + } + spinlock_acquire(&session->ses_lock); + sesstate = session->state; + client_dcb = session->client; + + if (sesstate == SESSION_STATE_ROUTER_READY) + { + CHK_DCB(client_dcb); + spinlock_release(&session->ses_lock); + client_dcb->func.write(client_dcb, gwbuf_clone(errbuf)); + } + else + { + spinlock_release(&session->ses_lock); + } + + /** false because connection is not available anymore */ + *succp = false; +} + +/** to be inline'd */ +/** + * @node Acquires lock to router client session if it is not closed. + * + * Parameters: + * @param rses - in, use + * + * + * @return true if router session was not closed. If return value is true + * it means that router is locked, and must be unlocked later. False, if + * router was closed before lock was acquired. + * + * + * @details (write detailed description here) + * + */ +static bool rses_begin_locked_router_action( + ROUTER_CLIENT_SES* rses) +{ + bool succp = false; + + CHK_CLIENT_RSES(rses); + + if (rses->rses_closed) { + goto return_succp; + } + spinlock_acquire(&rses->rses_lock); + if (rses->rses_closed) { + spinlock_release(&rses->rses_lock); + goto return_succp; + } + succp = true; + +return_succp: + return succp; +} + +/** to be inline'd */ +/** + * @node Releases router client session lock. + * + * Parameters: + * @param rses - + * + * + * @return void + * + * + * @details (write detailed description here) + * + */ +static void rses_end_locked_router_action( + ROUTER_CLIENT_SES* rses) +{ + CHK_CLIENT_RSES(rses); + spinlock_release(&rses->rses_lock); +} + + +static uint8_t getCapabilities( + ROUTER* inst, + void* router_session) +{ + return 0; +} + +/******************************** + * This routine returns the root master server from MySQL replication tree + * Get the root Master rule: + * + * find server with the lowest replication depth level + * and the SERVER_MASTER bitval + * Servers are checked even if they are in 'maintenance' + * + * @param servers The list of servers + * @return The Master found + * + */ + +static BACKEND *get_root_master(BACKEND **servers) { + int i = 0; + BACKEND *master_host = NULL; + + for (i = 0; servers[i]; i++) { + if (servers[i] && (servers[i]->server->status & (SERVER_MASTER|SERVER_MAINT)) == SERVER_MASTER) { + if (master_host && servers[i]->server->depth < master_host->server->depth) { + master_host = servers[i]; + } else { + if (master_host == NULL) { + master_host = servers[i]; + } + } + } + } + return master_host; +} + +static int handle_state_switch(DCB* dcb,DCB_REASON reason, void * routersession) +{ + ss_dassert(dcb != NULL); + SESSION* session = dcb->session; + ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES*)routersession; + SERVICE* service = session->service; + ROUTER* router = (ROUTER *)service->router; + + switch(reason) + { + case DCB_REASON_CLOSE: + dcb->func.close(dcb); + break; + case DCB_REASON_DRAINED: + /** Do we need to do anything? */ + break; + case DCB_REASON_HIGH_WATER: + /** Do we need to do anything? */ + break; + case DCB_REASON_LOW_WATER: + /** Do we need to do anything? */ + break; + case DCB_REASON_ERROR: + dcb->func.error(dcb); + break; + case DCB_REASON_HUP: + dcb->func.hangup(dcb); + break; + case DCB_REASON_NOT_RESPONDING: + dcb->func.hangup(dcb); + break; + default: + break; + } + + return 0; +} From b11d66148a23f588254e5da40ed8a566831c18bd Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 24 Mar 2015 19:23:37 +0200 Subject: [PATCH 002/273] Updated plain protocol. --- server/modules/include/plainprotocol.h | 260 +++++++++ server/modules/protocol/mongo_backend.c | 736 +++++++----------------- server/modules/protocol/mongo_client.c | 314 +--------- 3 files changed, 485 insertions(+), 825 deletions(-) create mode 100644 server/modules/include/plainprotocol.h diff --git a/server/modules/include/plainprotocol.h b/server/modules/include/plainprotocol.h new file mode 100644 index 000000000..35ecd1cd0 --- /dev/null +++ b/server/modules/include/plainprotocol.h @@ -0,0 +1,260 @@ +#ifndef _MYSQL_PROTOCOL_H +#define _MYSQL_PROTOCOL_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2015 + */ + +/* + * Revision History + * + * Date Who Description + * 24-03-2015 Markus Makela Initial implementation + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define GW_MYSQL_VERSION "MaxScale " MAXSCALE_VERSION +#define GW_MYSQL_LOOP_TIMEOUT 300000000 +#define GW_MYSQL_READ 0 +#define GW_MYSQL_WRITE 1 +#define MYSQL_HEADER_LEN 4L + +#define GW_MYSQL_PROTOCOL_VERSION 10 // version is 10 +#define GW_MYSQL_HANDSHAKE_FILLER 0x00 +#define GW_MYSQL_SERVER_CAPABILITIES_BYTE1 0xff +#define GW_MYSQL_SERVER_CAPABILITIES_BYTE2 0xf7 +#define GW_MYSQL_SERVER_LANGUAGE 0x08 +#define GW_MYSQL_MAX_PACKET_LEN 0xffffffL; +#define GW_MYSQL_SCRAMBLE_SIZE 20 +#define GW_SCRAMBLE_LENGTH_323 8 + +#ifndef MYSQL_SCRAMBLE_LEN +# define MYSQL_SCRAMBLE_LEN GW_MYSQL_SCRAMBLE_SIZE +#endif + +#define GW_NOINTR_CALL(A) do { errno = 0; A; } while (errno == EINTR) +#define SMALL_CHUNK 1024 +#define MAX_CHUNK SMALL_CHUNK * 8 * 4 +#define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10) +struct dcb; + +/** Protocol packing macros. */ +#define gw_mysql_set_byte2(__buffer, __int) do { \ + (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ + (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); } while (0) +#define gw_mysql_set_byte3(__buffer, __int) do { \ + (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ + (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \ + (__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); } while (0) +#define gw_mysql_set_byte4(__buffer, __int) do { \ + (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ + (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \ + (__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); \ + (__buffer)[3]= (uint8_t)(((__int) >> 24) & 0xFF); } while (0) + +/** Protocol unpacking macros. */ +#define gw_mysql_get_byte2(__buffer) \ + (uint16_t)((__buffer)[0] | \ + ((__buffer)[1] << 8)) +#define gw_mysql_get_byte3(__buffer) \ + (uint32_t)((__buffer)[0] | \ + ((__buffer)[1] << 8) | \ + ((__buffer)[2] << 16)) +#define gw_mysql_get_byte4(__buffer) \ + (uint32_t)((__buffer)[0] | \ + ((__buffer)[1] << 8) | \ + ((__buffer)[2] << 16) | \ + ((__buffer)[3] << 24)) +#define gw_mysql_get_byte8(__buffer) \ + ((uint64_t)(__buffer)[0] | \ + ((uint64_t)(__buffer)[1] << 8) | \ + ((uint64_t)(__buffer)[2] << 16) | \ + ((uint64_t)(__buffer)[3] << 24) | \ + ((uint64_t)(__buffer)[4] << 32) | \ + ((uint64_t)(__buffer)[5] << 40) | \ + ((uint64_t)(__buffer)[6] << 48) | \ + ((uint64_t)(__buffer)[7] << 56)) + +/** MySQL protocol constants */ +typedef enum +{ + GW_MYSQL_CAPABILITIES_NONE= 0, + GW_MYSQL_CAPABILITIES_LONG_PASSWORD= (1 << 0), + GW_MYSQL_CAPABILITIES_FOUND_ROWS= (1 << 1), + GW_MYSQL_CAPABILITIES_LONG_FLAG= (1 << 2), + GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB= (1 << 3), + GW_MYSQL_CAPABILITIES_NO_SCHEMA= (1 << 4), + GW_MYSQL_CAPABILITIES_COMPRESS= (1 << 5), + GW_MYSQL_CAPABILITIES_ODBC= (1 << 6), + GW_MYSQL_CAPABILITIES_LOCAL_FILES= (1 << 7), + GW_MYSQL_CAPABILITIES_IGNORE_SPACE= (1 << 8), + GW_MYSQL_CAPABILITIES_PROTOCOL_41= (1 << 9), + GW_MYSQL_CAPABILITIES_INTERACTIVE= (1 << 10), + GW_MYSQL_CAPABILITIES_SSL= (1 << 11), + GW_MYSQL_CAPABILITIES_IGNORE_SIGPIPE= (1 << 12), + GW_MYSQL_CAPABILITIES_TRANSACTIONS= (1 << 13), + GW_MYSQL_CAPABILITIES_RESERVED= (1 << 14), + GW_MYSQL_CAPABILITIES_SECURE_CONNECTION= (1 << 15), + GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS= (1 << 16), + GW_MYSQL_CAPABILITIES_MULTI_RESULTS= (1 << 17), + GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS= (1 << 18), + GW_MYSQL_CAPABILITIES_PLUGIN_AUTH= (1 << 19), + GW_MYSQL_CAPABILITIES_SSL_VERIFY_SERVER_CERT= (1 << 30), + GW_MYSQL_CAPABILITIES_REMEMBER_OPTIONS= (1 << 31), + GW_MYSQL_CAPABILITIES_CLIENT= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD | + GW_MYSQL_CAPABILITIES_FOUND_ROWS | + GW_MYSQL_CAPABILITIES_LONG_FLAG | + GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB | + GW_MYSQL_CAPABILITIES_LOCAL_FILES | + GW_MYSQL_CAPABILITIES_PLUGIN_AUTH | + GW_MYSQL_CAPABILITIES_TRANSACTIONS | + GW_MYSQL_CAPABILITIES_PROTOCOL_41 | + GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS | + GW_MYSQL_CAPABILITIES_MULTI_RESULTS | + GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS | + GW_MYSQL_CAPABILITIES_SECURE_CONNECTION), + GW_MYSQL_CAPABILITIES_CLIENT_COMPRESS= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD | + GW_MYSQL_CAPABILITIES_FOUND_ROWS | + GW_MYSQL_CAPABILITIES_LONG_FLAG | + GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB | + GW_MYSQL_CAPABILITIES_LOCAL_FILES | + GW_MYSQL_CAPABILITIES_PLUGIN_AUTH | + GW_MYSQL_CAPABILITIES_TRANSACTIONS | + GW_MYSQL_CAPABILITIES_PROTOCOL_41 | + GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS | + GW_MYSQL_CAPABILITIES_MULTI_RESULTS | + GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS | + GW_MYSQL_CAPABILITIES_COMPRESS + ), +} gw_mysql_capabilities_t; + +/** Copy from enum in mariadb-5.5 mysql_com.h */ +typedef enum mysql_server_cmd { + MYSQL_COM_UNDEFINED = -1, + MYSQL_COM_SLEEP = 0, + MYSQL_COM_QUIT, + MYSQL_COM_INIT_DB, + MYSQL_COM_QUERY, + MYSQL_COM_FIELD_LIST, + MYSQL_COM_CREATE_DB, + MYSQL_COM_DROP_DB, + MYSQL_COM_REFRESH, + MYSQL_COM_SHUTDOWN, + MYSQL_COM_STATISTICS, + MYSQL_COM_PROCESS_INFO, + MYSQL_COM_CONNECT, + MYSQL_COM_PROCESS_KILL, + MYSQL_COM_DEBUG, + MYSQL_COM_PING, + MYSQL_COM_TIME, + MYSQL_COM_DELAYED_INSERT, + MYSQL_COM_CHANGE_USER, + MYSQL_COM_BINLOG_DUMP, + MYSQL_COM_TABLE_DUMP, + MYSQL_COM_CONNECT_OUT, + MYSQL_COM_REGISTER_SLAVE, + MYSQL_COM_STMT_PREPARE, + MYSQL_COM_STMT_EXECUTE, + MYSQL_COM_STMT_SEND_LONG_DATA, + MYSQL_COM_STMT_CLOSE, + MYSQL_COM_STMT_RESET, + MYSQL_COM_SET_OPTION, + MYSQL_COM_STMT_FETCH, + MYSQL_COM_DAEMON, + MYSQL_COM_END /*< Must be the last */ +} mysql_server_cmd_t; + +/** + * MySQL Protocol specific state data. + * + * Protocol carries information from client side to backend side, such as + * MySQL session command information and history of earlier session commands. + */ +typedef struct { +#if defined(SS_DEBUG) + skygw_chk_t protocol_chk_top; +#endif + int fd; /*< The socket descriptor */ + struct dcb *owner_dcb; /*< The DCB of the socket + * we are running on */ + SPINLOCK protocol_lock; + uint8_t scramble[MYSQL_SCRAMBLE_LEN]; /*< server scramble, + * created or received */ + uint32_t server_capabilities; /*< server capabilities, + * created or received */ + uint32_t client_capabilities; /*< client capabilities, + * created or received */ + unsigned long tid; /*< MySQL Thread ID, in + * handshake */ + unsigned int charset; /*< MySQL character set at connect time */ +#if defined(SS_DEBUG) + skygw_chk_t protocol_chk_tail; +#endif +} PlainProtocol; + + + +#define MYSQL_GET_COMMAND(payload) (payload[4]) +#define MYSQL_GET_PACKET_NO(payload) (payload[3]) +#define MYSQL_GET_PACKET_LEN(payload) (gw_mysql_get_byte3(payload)) +#define MYSQL_GET_ERRCODE(payload) (gw_mysql_get_byte2(&payload[5])) +#define MYSQL_GET_STMTOK_NPARAM(payload) (gw_mysql_get_byte2(&payload[9])) +#define MYSQL_GET_STMTOK_NATTR(payload) (gw_mysql_get_byte2(&payload[11])) +#define MYSQL_IS_ERROR_PACKET(payload) (MYSQL_GET_COMMAND(payload)==0xff) +#define MYSQL_IS_COM_QUIT(payload) (MYSQL_GET_COMMAND(payload)==0x01) +#define MYSQL_IS_COM_INIT_DB(payload) (MYSQL_GET_COMMAND(payload)==0x02) +#define MYSQL_IS_CHANGE_USER(payload) (MYSQL_GET_COMMAND(payload)==0x11) +#define MYSQL_GET_NATTR(payload) ((int)payload[4]) + +#endif /** _MYSQL_PROTOCOL_H */ + +PlainProtocol* mysql_protocol_init(DCB* dcb, int fd); +void mysql_protocol_done (DCB* dcb); +PlainProtocol *gw_mysql_init(PlainProtocol *data); +int gw_receive_backend_auth(PlainProtocol *protocol); +int gw_decode_mysql_server_handshake(PlainProtocol *protocol, uint8_t *payload); +int gw_read_backend_handshake(PlainProtocol *protocol); +int gw_send_authentication_to_backend( + char *dbname, + char *user, + uint8_t *passwd, + PlainProtocol *protocol); + +int plain_do_connect_to_backend(char *host, int port, int* fd); diff --git a/server/modules/protocol/mongo_backend.c b/server/modules/protocol/mongo_backend.c index 4fe1ff0b8..b52663b6a 100644 --- a/server/modules/protocol/mongo_backend.c +++ b/server/modules/protocol/mongo_backend.c @@ -16,12 +16,14 @@ * Copyright MariaDB Corporation Ab 2013-2014 */ -#include "mysql_client_server_protocol.h" +#include #include #include #include #include +#define PLAIN_BACKEND_SO_SNDBUF (128 * 1024) +#define PLAIN_BACKEND_SO_RCVBUF (128 * 1024) /* * MySQL Protocol module for handling the protocol between the gateway * and the backend MySQL database. @@ -51,7 +53,7 @@ MODULE_INFO info = { MODULE_API_PROTOCOL, MODULE_GA, GWPROTOCOL_VERSION, - "The MySQL to backend server protocol" + "The plain protocol" }; /** Defined in log_manager.cc */ @@ -60,38 +62,32 @@ extern size_t log_ses_count[]; extern __thread log_info_t tls_log_info; static char *version_str = "V2.0.0"; -static int gw_create_backend_connection(DCB *backend, SERVER *server, SESSION *in_session); -static int gw_read_backend_event(DCB* dcb); -static int gw_write_backend_event(DCB *dcb); -static int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue); -static int gw_error_backend_event(DCB *dcb); -static int gw_backend_close(DCB *dcb); -static int gw_backend_hangup(DCB *dcb); +static int plain_create_backend_connection(DCB *backend, SERVER *server, SESSION *in_session); +static int plain_read_backend_event(DCB* dcb); +static int plain_write_ready_backend_event(DCB *dcb); +static int plain_write_backend(DCB *dcb, GWBUF *queue); +static int plain_error_backend_event(DCB *dcb); +static int plain_backend_close(DCB *dcb); +static int plain_backend_hangup(DCB *dcb); static int backend_write_delayqueue(DCB *dcb); static void backend_set_delayqueue(DCB *dcb, GWBUF *queue); -static int gw_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue); +static int plain_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue); static GWBUF* process_response_data (DCB* dcb, GWBUF* readbuf, int nbytes_to_process); extern char* create_auth_failed_msg( GWBUF* readbuf, char* hostaddr, uint8_t* sha1); extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db); static bool sescmd_response_complete(DCB* dcb); - -#if defined(NOT_USED) - static int gw_session(DCB *backend_dcb, void *data); -#endif -static MYSQL_session* gw_get_shared_session_auth_info(DCB* dcb); - static GWPROTOCOL MyObject = { - gw_read_backend_event, /* Read - EPOLLIN handler */ - gw_MySQLWrite_backend, /* Write - data from gateway */ - gw_write_backend_event, /* WriteReady - EPOLLOUT handler */ - gw_error_backend_event, /* Error - EPOLLERR handler */ - gw_backend_hangup, /* HangUp - EPOLLHUP handler */ + plain_read_backend_event, /* Read - EPOLLIN handler */ + plain_write_backend, /* Write - data from gateway */ + plain_write_ready_backend_event, /* WriteReady - EPOLLOUT handler */ + plain_error_backend_event, /* Error - EPOLLERR handler */ + plain_backend_hangup, /* HangUp - EPOLLHUP handler */ NULL, /* Accept */ - gw_create_backend_connection, /* Connect */ - gw_backend_close, /* Close */ + plain_create_backend_connection, /* Connect */ + plain_backend_close, /* Close */ NULL, /* Listen */ - gw_change_user, /* Authentication */ + NULL, /* Authentication */ NULL /* Session */ }; @@ -130,43 +126,63 @@ GetModuleObject() } -static MYSQL_session* gw_get_shared_session_auth_info( - DCB* dcb) +/** + * Creates MySQL protocol structure + * + * @param dcb * Must be non-NULL. + * @param fd + * + * @return + * + * + * @details Protocol structure does not have fd because dcb is not + * connected yet. + * + */ +PlainProtocol* plain_protocol_init( + DCB* dcb, + int fd) { - MYSQL_session* auth_info = NULL; - CHK_DCB(dcb); - CHK_SESSION(dcb->session); + PlainProtocol* p; - spinlock_acquire(&dcb->session->ses_lock); + p = (PlainProtocol *) calloc(1, sizeof(PlainProtocol)); + ss_dassert(p != NULL); - if (dcb->session->state != SESSION_STATE_ALLOC) { - auth_info = dcb->session->data; - } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_get_shared_session_auth_info] Couldn't get " - "session authentication info. Session in a wrong state %d.", - pthread_self(), - dcb->session->state))); + if (p == NULL) { + int eno = errno; + errno = 0; + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [mysql_init_protocol] MySQL protocol init failed : " + "memory allocation due error %d, %s.", + pthread_self(), + eno, + strerror(eno)))); + goto return_p; } - spinlock_release(&dcb->session->ses_lock); - return auth_info; + /*< Assign fd with protocol */ + p->fd = fd; + p->owner_dcb = dcb; + +return_p: + return p; } + /** * Backend Read Event for EPOLLIN on the MySQL backend protocol module * @param dcb The backend Descriptor Control Block * @return 1 on operation, 0 for no action */ -static int gw_read_backend_event(DCB *dcb) { - MySQLProtocol *client_protocol = NULL; - MySQLProtocol *backend_protocol = NULL; - MYSQL_session *current_session = NULL; +static int plain_read_backend_event(DCB *dcb) { + PlainProtocol *client_protocol = NULL; + PlainProtocol *backend_protocol = NULL; + int rc = 0; - backend_protocol = (MySQLProtocol *) dcb->protocol; + backend_protocol = (PlainProtocol *) dcb->protocol; CHK_PROTOCOL(backend_protocol); @@ -226,58 +242,12 @@ static int gw_read_backend_event(DCB *dcb) { ss_dassert(read_buffer != NULL || dcb->dcb_readqueue != NULL); } - /** Packet prefix was read earlier */ - if (dcb->dcb_readqueue) - { - if (read_buffer != NULL) - { - read_buffer = gwbuf_append(dcb->dcb_readqueue, read_buffer); - } - else - { - read_buffer = dcb->dcb_readqueue; - } - nbytes_read = gwbuf_length(read_buffer); - - if (nbytes_read < 5) /*< read at least command type */ - { - rc = 0; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%p [gw_read_backend_event] Read %d bytes " - "from DCB %p, fd %d, session %s. " - "Returning to poll wait.\n", - pthread_self(), - nbytes_read, - dcb, - dcb->fd, - dcb->session))); - goto return_rc; - } - /** There is at least length and command type. */ - else - { - dcb->dcb_readqueue = NULL; - } - } - /** This may be either short prefix of a packet, or the tail of it. */ - else - { - if (nbytes_read < 5) - { - dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); - rc = 0; - goto return_rc; - } - } - - if (dcb->session->state == SESSION_STATE_ROUTER_READY && dcb->session->client != NULL && dcb->session->client->state == DCB_STATE_POLLING) { client_protocol = SESSION_PROTOCOL(dcb->session, - MySQLProtocol); + PlainProtocol); { gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL); @@ -305,9 +275,9 @@ return_with_lock: * @param dcb The descriptor control block * @return 1 in success, 0 in case of failure, */ -static int gw_write_backend_event(DCB *dcb) { +static int plain_write_ready_backend_event(DCB *dcb) { int rc = 0; - MySQLProtocol *backend_protocol = dcb->protocol; + PlainProtocol *backend_protocol = dcb->protocol; /*< * Don't write to backend if backend_dcb is not in poll set anymore. @@ -343,6 +313,138 @@ return_rc: return rc; } + +/** + * plain_do_connect_to_backend + * + * This routine creates socket and connects to a backend server. + * Connect it non-blocking operation. If connect fails, socket is closed. + * + * @param host The host to connect to + * @param port The host TCP/IP port + * @param *fd where connected fd is copied + * @return 0/1 on success and -1 on failure + * If successful, fd has file descriptor to socket which is connected to + * backend server. In failure, fd == -1 and socket is closed. + * + */ +int plain_do_connect_to_backend( + char *host, + int port, + int *fd) +{ + struct sockaddr_in serv_addr; + int rv; + int so = 0; + int bufsize; + + memset(&serv_addr, 0, sizeof serv_addr); + serv_addr.sin_family = AF_INET; + so = socket(AF_INET,SOCK_STREAM,0); + + if (so < 0) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Establishing connection to backend server " + "%s:%d failed.\n\t\t Socket creation failed " + "due %d, %s.", + host, + port, + errno, + strerror(errno)))); + rv = -1; + goto return_rv; + } + /* prepare for connect */ + setipaddress(&serv_addr.sin_addr, host); + serv_addr.sin_port = htons(port); + bufsize = PLAIN_BACKEND_SO_SNDBUF; + + if(setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) != 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Failed to set socket options " + "%s:%d failed.\n\t\t Socket configuration failed " + "due %d, %s.", + host, + port, + errno, + strerror(errno)))); + rv = -1; + /** Close socket */ + goto close_so; + } + bufsize = PLAIN_BACKEND_SO_RCVBUF; + + if(setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) != 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Failed to set socket options " + "%s:%d failed.\n\t\t Socket configuration failed " + "due %d, %s.", + host, + port, + errno, + strerror(errno)))); + rv = -1; + /** Close socket */ + goto close_so; + } + + /* set socket to as non-blocking here */ + setnonblocking(so); + rv = connect(so, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + + if (rv != 0) + { + if (errno == EINPROGRESS) + { + rv = 1; + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Failed to connect backend server %s:%d, " + "due %d, %s.", + host, + port, + errno, + strerror(errno)))); + /** Close socket */ + goto close_so; + } + } + *fd = so; + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "%lu [plain_do_connect_to_backend] Connected to backend server " + "%s:%d, fd %d.", + pthread_self(), + host, + port, + so))); + +return_rv: + return rv; + +close_so: + /*< Close newly created socket. */ + if (close(so) != 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Failed to " + "close socket %d due %d, %s.", + so, + errno, + strerror(errno)))); + } + goto return_rv; +} + /* * Write function for backend DCB. Store command to protocol. * @@ -351,15 +453,13 @@ return_rc: * @return 0 on failure, 1 on success */ static int -gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue) +plain_write_backend(DCB *dcb, GWBUF *queue) { - MySQLProtocol *backend_protocol = dcb->protocol; + PlainProtocol *backend_protocol = dcb->protocol; int rc = 0; rc = dcb_write(dcb, queue); - -return_rc: return rc; } @@ -370,7 +470,7 @@ return_rc: * closed and call DCB close function which triggers closing router session * and related backends (if any exists. */ -static int gw_error_backend_event(DCB *dcb) +static int plain_error_backend_event(DCB *dcb) { SESSION* session; void* rsession; @@ -414,11 +514,7 @@ static int gw_error_backend_event(DCB *dcb) } return 1; } - errbuf = mysql_create_custom_error( - 1, - 0, - "Lost connection to backend server."); - + spinlock_acquire(&session->ses_lock); ses_state = session->state; spinlock_release(&session->ses_lock); @@ -501,12 +597,12 @@ retblock: * backend server. Positive fd is copied to protocol and to dcb. * If fails, fd == -1 and socket is closed. */ -static int gw_create_backend_connection( +static int plain_create_backend_connection( DCB *backend_dcb, SERVER *server, SESSION *session) { - MySQLProtocol *protocol = NULL; + PlainProtocol *protocol = NULL; int rv = -1; int fd = -1; @@ -527,7 +623,8 @@ static int gw_create_backend_connection( } /*< if succeed, fd > 0, -1 otherwise */ - rv = gw_do_connect_to_backend(server->name, server->port, &fd); + + rv = plain_do_connect_to_backend(server->name, server->port, &fd); /*< Assign protocol with backend_dcb */ backend_dcb->protocol = protocol; @@ -536,7 +633,7 @@ static int gw_create_backend_connection( case 0: ss_dassert(fd > 0); protocol->fd = fd; - protocol->protocol_auth_state = MYSQL_CONNECTED; + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_create_backend_connection] Established " @@ -551,7 +648,7 @@ static int gw_create_backend_connection( case 1: ss_dassert(fd > 0); - protocol->protocol_auth_state = MYSQL_PENDING_CONNECT; + protocol->fd = fd; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, @@ -566,7 +663,7 @@ static int gw_create_backend_connection( default: ss_dassert(fd == -1); - ss_dassert(protocol->protocol_auth_state == MYSQL_ALLOC); + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_create_backend_connection] Connection " @@ -595,7 +692,7 @@ return_fd: * @return 1 always */ static int -gw_backend_hangup(DCB *dcb) +plain_backend_hangup(DCB *dcb) { SESSION* session; void* rsession; @@ -612,12 +709,7 @@ gw_backend_hangup(DCB *dcb) rsession = session->router_session; router = session->service->router; router_instance = session->service->router_instance; - - errbuf = mysql_create_custom_error( - 1, - 0, - "Lost connection to backend server."); - + spinlock_acquire(&session->ses_lock); ses_state = session->state; spinlock_release(&session->ses_lock); @@ -695,7 +787,7 @@ retblock: * @return 1 always */ static int -gw_backend_close(DCB *dcb) +plain_backend_close(DCB *dcb) { DCB* client_dcb; SESSION* session; @@ -705,17 +797,6 @@ gw_backend_close(DCB *dcb) session = dcb->session; CHK_SESSION(session); - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [gw_backend_close]", - pthread_self()))); - - quitbuf = mysql_create_com_quit(NULL, 0); - gwbuf_set_type(quitbuf, GWBUF_TYPE_MYSQL); - - /** Send COM_QUIT to the backend being closed */ - mysql_send_com_quit(dcb, 0, quitbuf); - - mysql_protocol_done(dcb); /** * The lock is needed only to protect the read of session->state and * session->client values. Client's state may change by other thread @@ -796,409 +877,18 @@ static int backend_write_delayqueue(DCB *dcb) } else { - localq = dcb->delayq; - dcb->delayq = NULL; - spinlock_release(&dcb->delayqlock); - - if (MYSQL_IS_CHANGE_USER(((uint8_t *)GWBUF_DATA(localq)))) - { - MYSQL_session* mses; - GWBUF* new_packet; - - mses = (MYSQL_session *)dcb->session->client->data; - new_packet = gw_create_change_user_packet( - mses, - (MySQLProtocol *)dcb->protocol); - /** - * Remove previous packet which lacks scramble - * and append the new. - */ - localq = gwbuf_consume(localq, GWBUF_LENGTH(localq)); - localq = gwbuf_append(localq, new_packet); - } + rc = dcb_write(dcb, localq); } if (rc == 0) { - GWBUF* errbuf; - bool succp; - ROUTER_OBJECT *router = NULL; - ROUTER *router_instance = NULL; - void *rsession = NULL; - SESSION *session = dcb->session; - - CHK_SESSION(session); - - if (session != NULL) - { - router = session->service->router; - router_instance = session->service->router_instance; - rsession = session->router_session; #if defined(SS_DEBUG) LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Backend write delayqueue error handling."))); #endif - errbuf = mysql_create_custom_error( - 1, - 0, - "Failed to write buffered data to back-end server. " - "Buffer was empty or back-end was disconnected during " - "operation. Attempting to find a new backend."); - - router->handleError(router_instance, - rsession, - errbuf, - dcb, - ERRACT_NEW_CONNECTION, - &succp); - gwbuf_free(errbuf); - - if (!succp) - { - spinlock_acquire(&session->ses_lock); - session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); - } - } + } return rc; } - -/** - * This routine handles the COM_CHANGE_USER command - * - * @param dcb The current backend DCB - * @param server The backend server pointer - * @param in_session The current session data (MYSQL_session) - * @param queue The GWBUF containing the COM_CHANGE_USER receveid - * @return 1 on success and 0 on failure - */ -static int gw_change_user( - DCB *backend, - SERVER *server, - SESSION *in_session, - GWBUF *queue) -{ - MYSQL_session *current_session = NULL; - MySQLProtocol *backend_protocol = NULL; - MySQLProtocol *client_protocol = NULL; - char username[MYSQL_USER_MAXLEN+1]=""; - char database[MYSQL_DATABASE_MAXLEN+1]=""; - char current_database[MYSQL_DATABASE_MAXLEN+1]=""; - uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]=""; - uint8_t *client_auth_packet = GWBUF_DATA(queue); - unsigned int auth_token_len = 0; - uint8_t *auth_token = NULL; - int rv = -1; - int auth_ret = 1; - - current_session = (MYSQL_session *)in_session->client->data; - backend_protocol = backend->protocol; - client_protocol = in_session->client->protocol; - - /* now get the user, after 4 bytes header and 1 byte command */ - client_auth_packet += 5; - strncpy(username, (char *)client_auth_packet,MYSQL_USER_MAXLEN); - client_auth_packet += strlen(username) + 1; - - /* get the auth token len */ - memcpy(&auth_token_len, client_auth_packet, 1); - - client_auth_packet++; - - /* allocate memory for token only if auth_token_len > 0 */ - if (auth_token_len > 0) { - auth_token = (uint8_t *)malloc(auth_token_len); - ss_dassert(auth_token != NULL); - - if (auth_token == NULL) - return rv; - memcpy(auth_token, client_auth_packet, auth_token_len); - client_auth_packet += auth_token_len; - } - - /* get new database name */ - strncpy(database, (char *)client_auth_packet,MYSQL_DATABASE_MAXLEN); - - /* get character set */ - if (strlen(database)) { - client_auth_packet += strlen(database) + 1; - } else { - client_auth_packet++; - } - - if (client_auth_packet && *client_auth_packet) - memcpy(&backend_protocol->charset, client_auth_packet, sizeof(int)); - - /* save current_database name */ - strncpy(current_database, current_session->db,MYSQL_DATABASE_MAXLEN); - - /* - * Now clear database name in dcb as we don't do local authentication on db name for change user. - * Local authentication only for user@host and if successful the database name change is sent to backend. - */ - strcpy(current_session->db, ""); - - /* - * decode the token and check the password. - * Note: if auth_token_len == 0 && auth_token == NULL, user is without password - */ - auth_ret = gw_check_mysql_scramble_data(backend->session->client, - auth_token, - auth_token_len, - client_protocol->scramble, - sizeof(client_protocol->scramble), - username, - client_sha1); - - if (auth_ret != 0) { - if (!service_refresh_users(backend->session->client->service)) { - /* Try authentication again with new repository data */ - /* Note: if no auth client authentication will fail */ - auth_ret = gw_check_mysql_scramble_data( - backend->session->client, - auth_token, auth_token_len, - client_protocol->scramble, - sizeof(client_protocol->scramble), - username, - client_sha1); - } - } - - /* copy back current datbase to client session */ - strcpy(current_session->db, current_database); - - /* let's free the auth_token now */ - if (auth_token) - free(auth_token); - - if (auth_ret != 0) { - char *password_set = NULL; - char *message = NULL; - GWBUF* buf; - - if (auth_token_len > 0) - password_set = (char *)client_sha1; - else - password_set = ""; - - /** - * Create an error message and make it look like legit reply - * from backend server. Then make it look like an incoming event - * so that thread gets new task of it, calls clientReply - * which filters out duplicate errors from same cause and forward - * reply to the client. - */ - message = create_auth_fail_str(username, - backend->session->client->remote, - password_set, - ""); - if (message == NULL) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Creating error message failed."))); - rv = 0; - goto retblock; - } - /** - * Add command to backend's protocol, create artificial reply - * packet and add it to client's read buffer. - */ - protocol_add_srv_command((MySQLProtocol*)backend->protocol, - MYSQL_COM_CHANGE_USER); - modutil_reply_auth_error(backend, message, 0); - rv = 1; - } else { - rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol); - /* - * Now copy new data into user session - */ - strcpy(current_session->user, username); - strcpy(current_session->db, database); - memcpy(current_session->client_sha1, client_sha1, sizeof(current_session->client_sha1)); - } - -retblock: - gwbuf_free(queue); - - return rv; -} - - -/** - * Move packets or parts of packets from readbuf to outbuf as the packet headers - * and lengths have been noticed and counted. - * Session commands need to be marked so that they can be handled properly in - * the router's clientReply. - * - * @param dcb Backend's DCB where data was read from - * @param readbuf GWBUF where data was read to - * @param nbytes_to_process Number of bytes that has been read and need to be processed - * - * @return GWBUF which includes complete MySQL packet - */ -static GWBUF* process_response_data ( - DCB* dcb, - GWBUF* readbuf, - int nbytes_to_process) -{ - int npackets_left = 0; /*< response's packet count */ - ssize_t nbytes_left = 0; /*< nbytes to be read for the packet */ - MySQLProtocol* p; - GWBUF* outbuf = NULL; - - /** Get command which was stored in gw_MySQLWrite_backend */ - p = DCB_PROTOCOL(dcb, MySQLProtocol); - if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(p); - - /** All buffers processed here are sescmd responses */ - gwbuf_set_type(readbuf, GWBUF_TYPE_SESCMD_RESPONSE); - - /** - * Now it is known how many packets there should be and how much - * is read earlier. - */ - while (nbytes_to_process != 0) - { - mysql_server_cmd_t srvcmd; - bool succp; - - srvcmd = protocol_get_srv_command(p, false); - - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [process_response_data] Read command %s for DCB %p fd %d.", - pthread_self(), - STRPACKETTYPE(srvcmd), - dcb, - dcb->fd))); - /** - * Read values from protocol structure, fails if values are - * uninitialized. - */ - if (npackets_left == 0) - { - succp = protocol_get_response_status(p, &npackets_left, &nbytes_left); - - if (!succp || npackets_left == 0) - { - /** - * Examine command type and the readbuf. Conclude response - * packet count from the command type or from the first - * packet content. Fails if read buffer doesn't include - * enough data to read the packet length. - */ - init_response_status(readbuf, srvcmd, &npackets_left, &nbytes_left); - } - } - /** Only session commands with responses should be processed */ - ss_dassert(npackets_left > 0); - - /** Read incomplete packet. */ - if (nbytes_left > nbytes_to_process) - { - /** Includes length info so it can be processed */ - if (nbytes_to_process >= 5) - { - /** discard source buffer */ - readbuf = gwbuf_consume(readbuf, GWBUF_LENGTH(readbuf)); - nbytes_left -= nbytes_to_process; - } - nbytes_to_process = 0; - } - /** Packet was read. All bytes belonged to the last packet. */ - else if (nbytes_left == nbytes_to_process) - { - nbytes_left = 0; - nbytes_to_process = 0; - ss_dassert(npackets_left > 0); - npackets_left -= 1; - outbuf = gwbuf_append(outbuf, readbuf); - readbuf = NULL; - } - /** - * Packet was read. There should be more since bytes were - * left over. - * Move the next packet to its own buffer and add that next - * to the prev packet's buffer. - */ - else /*< nbytes_left < nbytes_to_process */ - { - ss_dassert(nbytes_left >= 0); - nbytes_to_process -= nbytes_left; - - /** Move the prefix of the buffer to outbuf from redbuf */ - outbuf = gwbuf_append(outbuf, - gwbuf_clone_portion(readbuf, 0, (size_t)nbytes_left)); - readbuf = gwbuf_consume(readbuf, (size_t)nbytes_left); - ss_dassert(npackets_left > 0); - npackets_left -= 1; - nbytes_left = 0; - } - - /** Store new status to protocol structure */ - protocol_set_response_status(p, npackets_left, nbytes_left); - - /** A complete packet was read */ - if (nbytes_left == 0) - { - /** No more packets in this response */ - if (npackets_left == 0 && outbuf != NULL) - { - GWBUF* b = outbuf; - - while (b->next != NULL) - { - b = b->next; - } - /** Mark last as end of response */ - gwbuf_set_type(b, GWBUF_TYPE_RESPONSE_END); - - /** Archive the command */ - protocol_archive_srv_command(p); - } - /** Read next packet */ - else - { - uint8_t* data; - - /** Read next packet length */ - data = GWBUF_DATA(readbuf); - nbytes_left = MYSQL_GET_PACKET_LEN(data)+MYSQL_HEADER_LEN; - /** Store new status to protocol structure */ - protocol_set_response_status(p, npackets_left, nbytes_left); - } - } - } - return outbuf; -} - - -static bool sescmd_response_complete( - DCB* dcb) -{ - int npackets_left; - ssize_t nbytes_left; - MySQLProtocol* p; - bool succp; - - p = DCB_PROTOCOL(dcb, MySQLProtocol); - if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(p); - - protocol_get_response_status(p, &npackets_left, &nbytes_left); - - if (npackets_left == 0) - { - succp = true; - } - else - { - succp = false; - } - return succp; -} diff --git a/server/modules/protocol/mongo_client.c b/server/modules/protocol/mongo_client.c index 1de7d237d..450634e12 100644 --- a/server/modules/protocol/mongo_client.c +++ b/server/modules/protocol/mongo_client.c @@ -30,14 +30,13 @@ #include #include #include - -#include +#include MODULE_INFO info = { MODULE_API_PROTOCOL, MODULE_GA, GWPROTOCOL_VERSION, - "The client to plain protocol implementation" + "The plain client protocol" }; /** Defined in log_manager.cc */ @@ -107,201 +106,6 @@ GetModuleObject() return &MyObject; } -/** - * mysql_send_ok - * - * Send a MySQL protocol OK message to the dcb (client) - * - * @param dcb Descriptor Control Block for the connection to which the OK is sent - * @param packet_number - * @param in_affected_rows - * @param mysql_message - * @return packet length - * - */ -int -mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) { - return 1; -} - -/** - * MySQLSendHandshake - * - * @param dcb The descriptor control block to use for sending the handshake request - * @return The packet length sent - */ -int -MySQLSendHandshake(DCB* dcb) -{ - return 1; -} - -/** - * gw_mysql_do_authentication - * - * Performs the MySQL protocol 4.1 authentication, using data in GWBUF *queue - * - * (MYSQL_session*)client_data including: user, db, client_sha1 are copied into - * the dcb->data and later to dcb->session->data. - * - * client_capabilitiesa are copied into the dcb->protocol - * - * @param dcb Descriptor Control Block of the client - * @param queue The GWBUF with data from client - * @return 0 If succeed, otherwise non-zero value - * - * @note in case of failure, dcb->data is freed before returning. If succeed, - * dcb->data is freed in session.c:session_free. - */ -static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { - MySQLProtocol *protocol = NULL; - /* int compress = -1; */ - int connect_with_db = -1; - uint8_t *client_auth_packet = GWBUF_DATA(queue); - int client_auth_packet_size = 0; - char *username = NULL; - char *database = NULL; - unsigned int auth_token_len = 0; - uint8_t *auth_token = NULL; - uint8_t *stage1_hash = NULL; - int auth_ret = -1; - MYSQL_session *client_data = NULL; - - CHK_DCB(dcb); - - protocol = DCB_PROTOCOL(dcb, MySQLProtocol); - CHK_PROTOCOL(protocol); - client_data = (MYSQL_session *)calloc(1, sizeof(MYSQL_session)); -#if defined(SS_DEBUG) - client_data->myses_chk_top = CHK_NUM_MYSQLSES; - client_data->myses_chk_tail = CHK_NUM_MYSQLSES; -#endif - /** - * Assign authentication structure with client DCB. - */ - dcb->data = client_data; - - stage1_hash = client_data->client_sha1; - username = client_data->user; - - client_auth_packet_size = gwbuf_length(queue); - - /* For clients supporting CLIENT_PROTOCOL_41 - * the Handshake Response Packet is: - * - * 4 bytes mysql protocol heade - * 4 bytes capability flags - * 4 max-packet size - * 1 byte character set - * string[23] reserved (all [0]) - * ... - * ... - */ - - /* Detect now if there are enough bytes to continue */ - if (client_auth_packet_size < (4 + 4 + 4 + 1 + 23)) - { - return 1; - } - - memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4); - - connect_with_db = - GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB & gw_mysql_get_byte4( - (uint32_t *)&protocol->client_capabilities); - /* - compress = - GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4( - &protocol->client_capabilities); - */ - - username = get_username_from_auth(username, client_auth_packet); - - if (username == NULL) - { - return 1; - } - - /* get charset */ - memcpy(&protocol->charset, client_auth_packet + 4 + 4 + 4, sizeof (int)); - - /* get the auth token len */ - memcpy(&auth_token_len, - client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1, - 1); - - /* - * Note: some clients may pass empty database, connect_with_db !=0 but database ="" - */ - if (connect_with_db) { - database = client_data->db; - strncpy(database, - (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + - 1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN); - } - - /* allocate memory for token only if auth_token_len > 0 */ - if (auth_token_len) { - auth_token = (uint8_t *)malloc(auth_token_len); - memcpy(auth_token, - client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1, - auth_token_len); - } - - /* - * Decode the token and check the password - * Note: if auth_token_len == 0 && auth_token == NULL, user is without password - */ - - auth_ret = gw_check_mysql_scramble_data(dcb, - auth_token, - auth_token_len, - protocol->scramble, - sizeof(protocol->scramble), - username, - stage1_hash); - - /* check for database name match in resource hashtable */ - auth_ret = check_db_name_after_auth(dcb, database, auth_ret); - - /* On failed auth try to load users' table from backend database */ - if (auth_ret != 0) { - if (!service_refresh_users(dcb->service)) { - /* Try authentication again with new repository data */ - /* Note: if no auth client authentication will fail */ - auth_ret = gw_check_mysql_scramble_data( - dcb, - auth_token, - auth_token_len, - protocol->scramble, - sizeof(protocol->scramble), - username, - stage1_hash); - } - else - { - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "%s: login attempt for user %s, user not " - "found.", - dcb->service->name, username))); - } - } - - /* Do again the database check */ - auth_ret = check_db_name_after_auth(dcb, database, auth_ret); - - /* on succesful auth set user into dcb field */ - if (auth_ret == 0) { - dcb->user = strdup(client_data->user); - } - - /* let's free the auth_token now */ - if (auth_token) { - free(auth_token); - } - - return auth_ret; -} /** * Write function for client DCB: writes data from MaxScale to Client @@ -328,7 +132,7 @@ int plain_read( ROUTER_OBJECT *router = NULL; ROUTER *router_instance = NULL; void *rsession = NULL; - MySQLProtocol *protocol = NULL; + PlainProtocol *protocol = NULL; GWBUF *read_buffer = NULL; int rc = 0; int nbytes_read = 0; @@ -336,7 +140,7 @@ int plain_read( bool stmt_input = false; /*< router input type */ CHK_DCB(dcb); - protocol = DCB_PROTOCOL(dcb, MySQLProtocol); + protocol = DCB_PROTOCOL(dcb, PlainProtocol); CHK_PROTOCOL(protocol); rc = dcb_read(dcb, &read_buffer); @@ -383,7 +187,7 @@ return_rc: */ int plain_write_ready(DCB *dcb) { - MySQLProtocol *protocol = NULL; + PlainProtocol *protocol = NULL; CHK_DCB(dcb); @@ -400,7 +204,7 @@ int plain_write_ready(DCB *dcb) if (dcb->protocol == NULL) { goto return_1; } - protocol = (MySQLProtocol *)dcb->protocol; + protocol = (PlainProtocol *)dcb->protocol; dcb_drain_writeq(dcb); @@ -575,7 +379,7 @@ int plain_accept(DCB *listener) { int rc = 0; DCB *client_dcb; - MySQLProtocol *protocol; + PlainProtocol *protocol; int c_sock; struct sockaddr client_conn; socklen_t client_len = sizeof(struct sockaddr_storage); @@ -591,24 +395,12 @@ int plain_accept(DCB *listener) retry_accept: -#if defined(FAKE_CODE) - if (fail_next_accept > 0) - { - c_sock = -1; - eno = fail_accept_errno; - fail_next_accept -= 1; - } else { - fail_accept_errno = 0; -#endif /* FAKE_CODE */ // new connection from client c_sock = accept(listener->fd, (struct sockaddr *) &client_conn, &client_len); eno = errno; errno = 0; -#if defined(FAKE_CODE) - } -#endif /* FAKE_CODE */ if (c_sock == -1) { @@ -687,9 +479,7 @@ int plain_accept(DCB *listener) pthread_self(), c_sock))); #endif /* SS_DEBUG */ -#if defined(FAKE_CODE) - conn_open[c_sock] = true; -#endif /* FAKE_CODE */ + /* set nonblocking */ sendbuf = GW_CLIENT_SO_SNDBUF; @@ -762,9 +552,7 @@ int plain_accept(DCB *listener) // assign function poiters to "func" field memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); - // client protocol state change - protocol->protocol_auth_state = MYSQL_IDLE; - + /** * Set new descriptor to event set. At the same time, * change state to DCB_STATE_POLLING so that @@ -773,12 +561,7 @@ int plain_accept(DCB *listener) if (poll_add_dcb(client_dcb) == -1) { /* Send a custom error as MySQL command reply */ - mysql_send_custom_error( - client_dcb, - 1, - 0, - "MaxScale internal error."); - + /** close client_dcb */ dcb_close(client_dcb); @@ -807,7 +590,7 @@ int plain_accept(DCB *listener) #if defined(SS_DEBUG) if (rc == 0) { CHK_DCB(client_dcb); - CHK_PROTOCOL(((MySQLProtocol *)client_dcb->protocol)); + CHK_PROTOCOL(((PlainProtocol *)client_dcb->protocol)); } #endif return_rc: @@ -851,7 +634,7 @@ plain_client_close(DCB *dcb) ROUTER_OBJECT* router; void* router_instance; #if defined(SS_DEBUG) - MySQLProtocol* protocol = (MySQLProtocol *)dcb->protocol; + PlainProtocol* protocol = (PlainProtocol *)dcb->protocol; if (dcb->state == DCB_STATE_POLLING || dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE) @@ -929,76 +712,3 @@ retblock: return 1; } - -/** - * Detect if buffer includes partial mysql packet or multiple packets. - * Store partial packet to dcb_readqueue. Send complete packets one by one - * to router. - * - * It is assumed readbuf includes at least one complete packet. - * Return 1 in success. If the last packet is incomplete return success but - * leave incomplete packet to readbuf. - * - * @param session Session pointer - * @param p_readbuf Pointer to the address of GWBUF including the query - * - * @return 1 if succeed, - */ -static int route_by_statement( - SESSION* session, - GWBUF** p_readbuf) -{ - int rc; - GWBUF* packetbuf; -#if defined(SS_DEBUG) - GWBUF* tmpbuf; - - tmpbuf = *p_readbuf; - while (tmpbuf != NULL) - { - ss_dassert(GWBUF_IS_TYPE_MYSQL(tmpbuf)); - tmpbuf=tmpbuf->next; - } -#endif - do - { - ss_dassert(GWBUF_IS_TYPE_MYSQL((*p_readbuf))); - - /** - * Collect incoming bytes to a buffer until complete packet has - * arrived and then return the buffer. - */ - packetbuf = gw_MySQL_get_next_packet(p_readbuf); - - if (packetbuf != NULL) - { - CHK_GWBUF(packetbuf); - ss_dassert(GWBUF_IS_TYPE_MYSQL(packetbuf)); - /** - * This means that buffer includes exactly one MySQL - * statement. - * backend func.write uses the information. MySQL backend - * protocol, for example, stores the command identifier - * to protocol structure. When some other thread reads - * the corresponding response the command tells how to - * handle response. - * - * Set it here instead of plain_read to make - * sure it is set to each (MySQL) packet. - */ - gwbuf_set_type(packetbuf, GWBUF_TYPE_SINGLE_STMT); - /** Route query */ - rc = SESSION_ROUTE_QUERY(session, packetbuf); - } - else - { - rc = 1; - goto return_rc; - } - } - while (rc == 1 && *p_readbuf != NULL); - -return_rc: - return rc; -} - From c3cb8df8a03705477dd8f31d20a7db161fe4951c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 31 Mar 2015 15:12:50 +0300 Subject: [PATCH 003/273] Renamed mongo protocol to plain protocol. --- server/modules/protocol/{mongo_backend.c => plainbackend.c} | 0 server/modules/protocol/{mongo_client.c => plainclient.c} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename server/modules/protocol/{mongo_backend.c => plainbackend.c} (100%) rename server/modules/protocol/{mongo_client.c => plainclient.c} (100%) diff --git a/server/modules/protocol/mongo_backend.c b/server/modules/protocol/plainbackend.c similarity index 100% rename from server/modules/protocol/mongo_backend.c rename to server/modules/protocol/plainbackend.c diff --git a/server/modules/protocol/mongo_client.c b/server/modules/protocol/plainclient.c similarity index 100% rename from server/modules/protocol/mongo_client.c rename to server/modules/protocol/plainclient.c From 341c04aa483a6d2bd92558f161cd8a5857bfcc98 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 31 Mar 2015 17:27:25 +0300 Subject: [PATCH 004/273] First tests. --- server/modules/monitor/CMakeLists.txt | 2 +- server/modules/monitor/mon_exec.c | 38 ++++++++++++++++++++++++++ server/modules/monitor/mon_exec.h | 13 +++++++++ server/modules/monitor/mysql_mon.c | 14 +++++++++- server/modules/monitor/mysqlmon.h | 1 + server/modules/protocol/CMakeLists.txt | 12 ++++---- 6 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 server/modules/monitor/mon_exec.c create mode 100644 server/modules/monitor/mon_exec.h diff --git a/server/modules/monitor/CMakeLists.txt b/server/modules/monitor/CMakeLists.txt index a99a66142..3d3f83580 100644 --- a/server/modules/monitor/CMakeLists.txt +++ b/server/modules/monitor/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(mysqlmon SHARED mysql_mon.c) +add_library(mysqlmon SHARED mysql_mon.c mon_exec.c) target_link_libraries(mysqlmon log_manager utils) install(TARGETS mysqlmon DESTINATION modules) diff --git a/server/modules/monitor/mon_exec.c b/server/modules/monitor/mon_exec.c new file mode 100644 index 000000000..122390da9 --- /dev/null +++ b/server/modules/monitor/mon_exec.c @@ -0,0 +1,38 @@ +#include + +/** Defined in log_manager.cc */ +extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; + +/** + *Execute a command in a separate process. + *@param cmd Command to execute + *@return 0 on success, -1 on error. + */ +int monitor_exec_cmd(char* cmd) +{ + int rval = 0; + pid_t pid; + + pid = fork(); + + if(pid < 0) + { + skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s', fork failed: [%d] %s", + cmd,errno,strerror(errno)); + rval = -1; + } + else if(pid == 0) + { + /** Child process, execute command */ + execl(cmd,cmd,NULL); + } + else + { + LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd)); + } + + return rval; + +} diff --git a/server/modules/monitor/mon_exec.h b/server/modules/monitor/mon_exec.h new file mode 100644 index 000000000..ed277ab3e --- /dev/null +++ b/server/modules/monitor/mon_exec.h @@ -0,0 +1,13 @@ +#ifndef MON_EXEC_HG +#define MON_EXEC_HG + +#include +#include +#include +#include +#include +#include + +int monitor_exec_cmd(char* cmd); + +#endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 9945086fb..51720c225 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -64,6 +64,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -184,6 +185,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT; handle->read_timeout=DEFAULT_READ_TIMEOUT; handle->write_timeout=DEFAULT_WRITE_TIMEOUT; + handle->script = NULL; spinlock_init(&handle->lock); } @@ -193,6 +195,8 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->detectStaleMaster = config_truth_value(params->value); else if(!strcmp(params->name,"detect_replication_lag")) handle->replicationHeartbeat = config_truth_value(params->value); + else if(!strcmp(params->name,"script")) + handle->script = strdup(params->value); params = params->next; } @@ -696,7 +700,15 @@ int log_no_master = 1; !(SERVER_IS_IN_CLUSTER(ptr->server))) { dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING); - } + } + +/* + if(handle->script) + { + if(monitor_exec_cmd(handle->script)) + skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s' on server state change.",handle->script); + } +*/ } if (mon_status_changed(ptr)) diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index a7d7fb419..3b5658a5a 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -77,6 +77,7 @@ typedef struct { int write_timeout; /**< Timeout in seconds for each attempt to write to the server. * There are retries and the total effective timeout value is two times the option value. */ + char *script;0 } MYSQL_MONITOR; #define MONITOR_RUNNING 1 diff --git a/server/modules/protocol/CMakeLists.txt b/server/modules/protocol/CMakeLists.txt index 0bea95832..7d118c3ec 100644 --- a/server/modules/protocol/CMakeLists.txt +++ b/server/modules/protocol/CMakeLists.txt @@ -6,13 +6,13 @@ add_library(MySQLBackend SHARED mysql_backend.c mysql_common.c) target_link_libraries(MySQLBackend log_manager utils) install(TARGETS MySQLBackend DESTINATION modules) -add_library(mongoclient SHARED mongo_client.c mysql_common.c) -target_link_libraries(mongoclient log_manager utils) -install(TARGETS mongoclient DESTINATION modules) +add_library(plainclient SHARED plainclient.c mysql_common.c) +target_link_libraries(plainclient log_manager utils) +install(TARGETS plainclient DESTINATION modules) -add_library(mongobackend SHARED mongo_backend.c mysql_common.c) -target_link_libraries(mongobackend log_manager utils) -install(TARGETS mongobackend DESTINATION modules) +add_library(plainbackend SHARED plainbackend.c mysql_common.c) +target_link_libraries(plainbackend log_manager utils) +install(TARGETS plainbackend DESTINATION modules) add_library(telnetd SHARED telnetd.c) From 15164224587231a27afadb0f26441fc32581f864 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Apr 2015 04:35:32 +0300 Subject: [PATCH 005/273] A script can now be called in mysql_mon when a master server loses master status. --- server/modules/monitor/mysql_mon.c | 34 ++++++++++++++++++++---------- server/modules/monitor/mysqlmon.h | 3 ++- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 51720c225..0e035d98e 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -22,7 +22,7 @@ * @verbatim * Revision History * - * Date Who Description + * Date Who Demaster_down_scription * 08/07/13 Mark Riddoch Initial implementation * 11/07/13 Mark Riddoch Addition of code to check replication * status @@ -185,7 +185,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT; handle->read_timeout=DEFAULT_READ_TIMEOUT; handle->write_timeout=DEFAULT_WRITE_TIMEOUT; - handle->script = NULL; + handle->master_down_script = NULL; spinlock_init(&handle->lock); } @@ -195,8 +195,18 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->detectStaleMaster = config_truth_value(params->value); else if(!strcmp(params->name,"detect_replication_lag")) handle->replicationHeartbeat = config_truth_value(params->value); - else if(!strcmp(params->name,"script")) - handle->script = strdup(params->value); + else if(!strcmp(params->name,"master_down_script")) + { + if(access(params->value,F_OK) == 0) + { + handle->master_down_script = strdup(params->value); + handle->master_down_script_called = 0; + } + else + { + skygw_log_write(LOGFILE_ERROR,"Error: could not find master_down_script file: %s",params->value); + } + } params = params->next; } @@ -688,6 +698,11 @@ int log_no_master = 1; "Server %s:%d lost the master status.", ptr->server->name, ptr->server->port))); + if(handle->master_down_script) + { + if(monitor_exec_cmd(handle->master_down_script)) + skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s' on server state change.",handle->master_down_script); + } } /** * Here we say: If the server's state changed @@ -700,15 +715,12 @@ int log_no_master = 1; !(SERVER_IS_IN_CLUSTER(ptr->server))) { dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING); + } -/* - if(handle->script) - { - if(monitor_exec_cmd(handle->script)) - skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s' on server state change.",handle->script); - } -*/ + + + } if (mon_status_changed(ptr)) diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 3b5658a5a..969334b3b 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -77,7 +77,8 @@ typedef struct { int write_timeout; /**< Timeout in seconds for each attempt to write to the server. * There are retries and the total effective timeout value is two times the option value. */ - char *script;0 + char *master_down_script; + int master_down_script_called; } MYSQL_MONITOR; #define MONITOR_RUNNING 1 From eec053924c0fd1085023458ade00098c7110fb39 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 24 Apr 2015 22:11:49 +0300 Subject: [PATCH 006/273] Updated various MaxScale command line and configuration options to allow custom log, module, data, cache and language directories. --- CMakeLists.txt | 30 ++++-- server/core/gateway.c | 226 +++++++++++++++++++++++++++------------ server/core/load_utils.c | 9 +- server/include/gw.h | 1 + 4 files changed, 185 insertions(+), 81 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94842ebaa..e654e766e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,19 +137,29 @@ ${CMAKE_SOURCE_DIR}/Documentation/Release-Notes/MaxScale-1.1-Release-Notes.md execute_process(COMMAND perl ${CMAKE_SOURCE_DIR}/Documentation/format.pl ${CMAKE_SOURCE_DIR}/Documentation/Upgrading-To-MaxScale-1.1.0.md ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt) -install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION .) -install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION .) -install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION .) message(STATUS "Installing MaxScale to: ${CMAKE_INSTALL_PREFIX}/") -install(FILES server/MaxScale_template.cnf DESTINATION etc) -install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION etc) -install(FILES ${ERRMSG} DESTINATION mysql) -install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION .) -install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION .) -install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION .) - +if(PACKAGE) + install(FILES server/MaxScale_template.cnf DESTINATION /etc) + install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION /etc) + install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION /usr/share/maxscale) + install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION /usr/share/maxscale) + install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION /usr/share/maxscale) + install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION /usr/share/maxscale) + install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION /usr/share/maxscale) + install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION /usr/share/maxscale) +else() + install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION .) + install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION .) + install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION .) + install(FILES server/MaxScale_template.cnf DESTINATION etc) + install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION etc) + install(FILES ${ERRMSG} DESTINATION mysql) + install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION .) + install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION .) + install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION .) +endif() # Install startup scripts and ldconfig files if(WITH_SCRIPTS) configure_file(${CMAKE_SOURCE_DIR}/maxscale.conf.in ${CMAKE_BINARY_DIR}/maxscale.conf @ONLY) diff --git a/server/core/gateway.c b/server/core/gateway.c index ed69d3e3e..c06b9f309 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -113,12 +113,15 @@ static char* server_options[] = { const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1; -const char* default_cnf_fname = "etc/MaxScale.cnf"; +const char* default_cnf_fname = "MaxScale.cnf"; const char* default_configdir = "/etc/"; const char* default_logdir = "/var/log/maxscale/"; -const char* default_libdir = "/lib64/maxscale/lib/"; -const char* default_moddir = "/lib64/maxscale/modules/"; +const char* default_datadir = "/var/lib/maxscale/"; +const char* default_moduledir = "/lib64/maxscale/modules/"; +const char* default_cachedir = "/var/cache/maxscale/"; +const char* default_langdir = "/usr/share/mysql/english/"; +const char* default_piddir = "/var/run/maxscale/"; static char* server_groups[] = { "embedded", @@ -132,14 +135,15 @@ static char* server_groups[] = { /* The data directory we created for this gateway instance */ static char datadir[PATH_MAX+1] = ""; - +static bool datadir_defined = false; /*< If the datadir was already set */ /* The data directory we created for this gateway instance */ static char pidfile[PATH_MAX+1] = ""; static char* configdir = NULL; static char* logdir = NULL; -static char* libdir = NULL; -static char* moddir = NULL; +static char* moduledir = NULL; +static char* cachedir = NULL; +static char* langdir = NULL; /** * exit flag for log flusher. */ @@ -161,9 +165,14 @@ static struct option long_options[] = { {"config", required_argument, 0, 'f'}, {"nodaemon", no_argument, 0, 'd'}, {"log", required_argument, 0, 'l'}, - {"logdir", required_argument, 0, 'L'}, + {"logdir", required_argument, 0, 'L'}, + {"datadir", required_argument, 0, 'D'}, + {"configdir",required_argument, 0, 'C'}, + {"moduledir",required_argument, 0, 'B'}, + {"cachedir",required_argument, 0, 'A'}, + {"language",required_argument, 0, 'N'}, {"syslog", required_argument, 0, 's'}, - {"maxscalelog", required_argument, 0, 'S'}, + {"maxscalelog",required_argument,0,'S'}, {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, '?'}, {0, 0, 0, 0} @@ -201,6 +210,14 @@ static bool resolve_maxscale_homedir( static char* check_dir_access(char* dirname,bool,bool); +/** + * Get the directory with all the modules. + * @return The module directory + */ +char* get_moduledir() +{ + return moduledir; +} /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. @@ -369,11 +386,10 @@ void datadir_cleanup() { int depth = 1; int flags = FTW_CHDIR|FTW_DEPTH|FTW_MOUNT; - int rc; if (datadir[0] != 0 && access(datadir, F_OK) == 0) { - rc = nftw(datadir, ntfw_cb, depth, flags); + nftw(datadir, ntfw_cb, depth, flags); } } @@ -394,14 +410,13 @@ static bool file_write_footer( FILE* outfile) { bool succp = false; - size_t wbytes1; size_t len1; const char* header_buf1; header_buf1 = "------------------------------------------------------" "\n\n"; len1 = strlen(header_buf1); - wbytes1=fwrite((void*)header_buf1, len1, 1, outfile); + fwrite((void*)header_buf1, len1, 1, outfile); succp = true; @@ -412,9 +427,6 @@ static bool file_write_header( FILE* outfile) { bool succp = false; - size_t wbytes1; - size_t wbytes2; - size_t wbytes3; size_t len1; size_t len2; size_t len3; @@ -458,9 +470,9 @@ static bool file_write_header( #if defined(LAPTOP_TEST) nanosleep(&ts1, NULL); #else - wbytes1=fwrite((void*)header_buf1, len1, 1, outfile); - wbytes2=fwrite((void*)header_buf2, len2, 1, outfile); - wbytes3=fwrite((void*)header_buf3, len3, 1, outfile); + fwrite((void*)header_buf1, len1, 1, outfile); + fwrite((void*)header_buf2, len2, 1, outfile); + fwrite((void*)header_buf3, len3, 1, outfile); #endif succp = true; @@ -1015,20 +1027,28 @@ static void usage(void) { fprintf(stderr, "\nUsage : %s [-h] | [-d] [-c ] [-f ]\n\n" - " -d|--nodaemon enable running in terminal process (default:disabled)\n" - " -c|--homedir=... relative|absolute MaxScale home directory\n" - " -f|--config=... relative|absolute pathname of MaxScale configuration file\n" - " (default: $MAXSCALE_HOME/etc/MaxScale.cnf)\n" - " -l|--log=... log to file or shared memory\n" - " -lfile or -lshm - defaults to shared memory\n" - " -L|--logdir=... path to log file directory\n" - " (default: /var/log/maxscale)\n" - " -s|--syslog= log messages to syslog." - " True or false - defaults to true\n" - " -S|--maxscalelog= log messages to MaxScale log." - " True or false - defaults to true\n" - " -v|--version print version info and exit\n" - " -?|--help show this help\n" + " -d|--nodaemon enable running in terminal process (default:disabled)\n" + " -c|--homedir=... relative|absolute MaxScale home directory\n" + " -f|--config=... relative|absolute pathname of MaxScale configuration file\n" + " (default: $MAXSCALE_HOME/etc/MaxScale.cnf)\n" + " -l|--log=... log to file or shared memory\n" + " -lfile or -lshm - defaults to shared memory\n" + " -L|--logdir=... path to log file directory\n" + " (default: /var/log/maxscale)\n" + " -D|--datadir=... path to data directory\n" + " (default: /var/lib/maxscale)\n" + " -C|--configdir=... path to configuration file directory\n" + " (default: /etc/)\n" + " -B|--moduledir=... path to module directory\n" + " (default: /var/lib/maxscale)\n" + " -A|--cachedir=... path to cache directory\n" + " (default: /var/cache/maxscale)\n" + " -s|--syslog= log messages to syslog.\n" + " True or false - defaults to true\n" + " -S|--maxscalelog= log messages to MaxScale log.\n" + " True or false - defaults to true\n" + " -v|--version print version info and exit\n" + " -?|--help show this help\n" , progname); } @@ -1103,9 +1123,8 @@ int main(int argc, char **argv) NULL}; sigemptyset(&sigpipe_mask); sigaddset(&sigpipe_mask, SIGPIPE); - progname = *argv; - + sprintf(datadir, "%s", default_datadir); #if defined(FAKE_CODE) memset(conn_open, 0, sizeof(bool)*10240); memset(dcb_fake_write_errno, 0, sizeof(unsigned char)*10240); @@ -1133,7 +1152,7 @@ int main(int argc, char **argv) } } - while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:", + while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:D:C:B:", long_options, &option_index)) != -1) { bool succp = true; @@ -1245,6 +1264,33 @@ int main(int argc, char **argv) logdir = tmp_path; } + break; + case 'N': + if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) + { + langdir = tmp_path; + } + break; + case 'D': + sprintf(datadir,"%s",optarg); + datadir_defined = true; + break; + case 'C': + if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) + { + configdir = tmp_path; + } + break; + case 'B': + if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) + { + moduledir = tmp_path; + } + case 'A': + if(handle_path_arg(&tmp_path,optarg,NULL,true,true)) + { + cachedir = tmp_path; + } break; case 'S': if(strstr(optarg,"=")) @@ -1547,11 +1593,17 @@ int main(int argc, char **argv) goto return_main; } - /*< + /** Use the cache dir for the mysql folder of the embedded library */ + sprintf(mysql_home, "%s/mysql", cachedir); + setenv("MYSQL_HOME", mysql_home, 1); + + /*< * If MaxScale home directory wasn't set by command-line argument. * Next, resolve it from environment variable and further on, * try to use default. */ +/* + if (home_dir == NULL) { if (!resolve_maxscale_homedir(&home_dir)) @@ -1560,8 +1612,7 @@ int main(int argc, char **argv) rc = MAXSCALE_HOMELESS; goto return_main; } - sprintf(mysql_home, "%s/mysql", home_dir); - setenv("MYSQL_HOME", mysql_home, 1); + } else { @@ -1601,13 +1652,28 @@ int main(int argc, char **argv) } free(log_context); } +*/ - char pbuf[PATH_MAX]; + /** + * Resolve the full pathname for configuration file and check for + * read accessibility. + */ + char pathbuf[PATH_MAX+1]; + snprintf(pathbuf,PATH_MAX,"%s",configdir ? configdir:default_configdir); + if(pathbuf[strlen(pathbuf)-1] != '/') + strcat(pathbuf,"/"); - sprintf(pbuf,"%s/etc/MaxScale.cnf",home_dir); + if (!resolve_maxscale_conf_fname(&cnf_file_path, pathbuf, cnf_file_arg)) + { + ss_dassert(cnf_file_path == NULL); + rc = MAXSCALE_BADCONFIG; + goto return_main; + } - ini_parse(pbuf,cnf_preparser,NULL); + ini_parse(cnf_file_path,cnf_preparser,NULL); + if(!datadir_defined) + sprintf(datadir,"%s",default_datadir); /** * Init Log Manager for MaxScale. * If $MAXSCALE_HOME is set then write the logs into $MAXSCALE_HOME/log. @@ -1678,25 +1744,25 @@ int main(int argc, char **argv) goto return_main; } } - /** - * Resolve the full pathname for configuration file and check for - * read accessibility. - */ - if (!resolve_maxscale_conf_fname(&cnf_file_path, home_dir, cnf_file_arg)) - { - ss_dassert(cnf_file_path == NULL); - rc = MAXSCALE_BADCONFIG; - goto return_main; - } - /*< + + + if(cachedir == NULL) + cachedir = strdup(default_cachedir); + + if(langdir == NULL) + langdir = strdup(default_langdir); + + /** * Set a data directory for the mysqld library, we use * a unique directory name to avoid clauses if multiple * instances of the gateway are beign run on the same * machine. */ - sprintf(datadir, "%s/data", home_dir); + if(datadir[strlen(datadir)-1] != '/') + strcat(datadir,"/"); + strcat(datadir,"data"); if(mkdir(datadir, 0777) != 0){ if(errno != EEXIST){ @@ -1706,7 +1772,7 @@ int main(int argc, char **argv) } } - sprintf(datadir, "%s/data/data%d", home_dir, getpid()); + sprintf(datadir, "%s/data%d", datadir, getpid()); if(mkdir(datadir, 0777) != 0){ @@ -1720,27 +1786,29 @@ int main(int argc, char **argv) if (!daemon_mode) { fprintf(stderr, - "Home directory : %s" - "\nConfiguration file : %s" + //"Home directory : %s" + "Configuration file : %s" "\nLog directory : %s" "\nData directory : %s\n\n", - home_dir, + //home_dir, cnf_file_path, logdir, datadir); } +/* LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, "Home directory : %s", home_dir))); +*/ LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, "Data directory : %s", datadir))); LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, - "Log directory : %s/log", - home_dir))); + "Log directory : %s/", + logdir))); LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, "Configuration file : %s", @@ -1758,8 +1826,8 @@ int main(int argc, char **argv) { snprintf(language_arg, 11+PATH_MAX+1, - "--language=%s/mysql", - home_dir); + "--language=%s", + langdir); server_options[i] = language_arg; } } @@ -1815,6 +1883,9 @@ int main(int argc, char **argv) } libmysqld_started = TRUE; + if(moduledir == NULL) + moduledir = strdup(default_moduledir); + if (!config_load(cnf_file_path)) { char* fprerr = "Failed to load MaxScale configuration " @@ -2015,7 +2086,7 @@ static int write_pid_file(char *home_dir) { int fd = -1; - snprintf(pidfile, PATH_MAX, "%s/data/maxscale.pid", home_dir); + snprintf(pidfile, PATH_MAX, "%smaxscale.pid",default_piddir); fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); if (fd == -1) { @@ -2100,21 +2171,42 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons char pathbuffer[PATH_MAX]; char* errstr; + /** These are read from the configuration file. These will not override + * command line parameters but will override default values. */ if(strcasecmp(section,"maxscale") == 0) { if(strcmp(name, "logdir") == 0) { - /** logdir is only NULL if no command line parameter was given */ if(logdir == NULL) handle_path_arg(&logdir,(char*)value,NULL,true,true); } - else if(strcmp(name, "moddir") == 0) + else if(strcmp(name, "moduledir") == 0) { - handle_path_arg(&moddir,(char*)value,NULL,true,false); + if(moduledir == NULL) + handle_path_arg(&moduledir,(char*)value,NULL,true,false); } - else if(strcmp(name, "libdir") == 0) + else if(strcmp(name, "datadir") == 0) { - handle_path_arg(&libdir,(char*)value,NULL,true,false); + if(!datadir_defined) + { + char* tmp; + if(handle_path_arg(&tmp,(char*)value,NULL,true,false)) + { + sprintf(datadir,"%s",tmp); + datadir_defined = true; + free(tmp); + } + } + } + else if(strcmp(name, "cachedir") == 0) + { + if(cachedir == NULL) + handle_path_arg((char**)&datadir,(char*)value,NULL,true,false); + } + else if(strcmp(name, "language") == 0) + { + if(langdir == NULL) + handle_path_arg((char**)&langdir,(char*)value,NULL,true,false); } } diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 172f24ad0..c2716befa 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -48,6 +48,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -146,16 +147,16 @@ MODULE_INFO *mod_info = NULL; if (access(fname, F_OK) == -1) { - home = get_maxscale_home (); - snprintf(fname, MAXPATHLEN+1,"%s/modules/lib%s.so", home, module); + //home = get_maxscale_home (); + snprintf(fname, MAXPATHLEN+1,"%s/lib%s.so", get_moduledir(), module); if (access(fname, F_OK) == -1) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to find library for " - "module: %s.", - module))); + "module: %s. Module dir: %s", + module, get_moduledir()))); return NULL; } } diff --git a/server/include/gw.h b/server/include/gw.h index 9c8507210..e2c69aa7c 100644 --- a/server/include/gw.h +++ b/server/include/gw.h @@ -65,3 +65,4 @@ int gw_write(DCB *dcb, const void *buf, size_t nbytes); int gw_getsockerrno(int fd); int parse_bindconfig(char *, unsigned short, struct sockaddr_in *); int setipaddress(struct in_addr *, char *); +char* get_moduledir(); \ No newline at end of file From df9bc65430c8f786fd71b52dbd0f22dcba911cc1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 25 Apr 2015 06:53:59 +0300 Subject: [PATCH 007/273] Added variables that are used when installing instead of relative paths. --- CMakeLists.txt | 35 ++++------ client/CMakeLists.txt | 2 +- log_manager/CMakeLists.txt | 4 +- macros.cmake | 28 ++++++++ query_classifier/CMakeLists.txt | 2 +- server/core/CMakeLists.txt | 6 +- server/core/gateway.c | 65 +++++++++---------- server/core/load_utils.c | 1 - server/include/gw.h | 17 ++++- server/modules/filter/CMakeLists.txt | 18 ++--- server/modules/filter/hint/CMakeLists.txt | 2 +- server/modules/monitor/CMakeLists.txt | 8 +-- server/modules/protocol/CMakeLists.txt | 12 ++-- server/modules/routing/CMakeLists.txt | 12 ++-- server/modules/routing/binlog/CMakeLists.txt | 4 +- server/modules/routing/maxinfo/CMakeLists.txt | 4 +- .../routing/readwritesplit/CMakeLists.txt | 2 +- 17 files changed, 122 insertions(+), 100 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e654e766e..48bf38e8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,6 @@ if(NOT CURL_FOUND) message(FATAL_ERROR "Failed to locate dependency: libcurl") endif() -set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/modules) # Make sure the release notes for this release are present if it is a stable one if(${MAXSCALE_VERSION} MATCHES "-stable") @@ -47,6 +46,7 @@ if(${MAXSCALE_VERSION} MATCHES "-stable") message(FATAL_ERROR "Could not find the release notes for this stable release: ${MAXSCALE_VERSION_NUMERIC}") endif() endif() +set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/${MAXSCALE_MODULE_INSTALL}:${CMAKE_INSTALL_PREFIX}/${MAXSCALE_LIBRARY_INSTALL}) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/server/include) configure_file(${CMAKE_SOURCE_DIR}/server/include/version.h.in ${CMAKE_BINARY_DIR}/server/include/version.h) @@ -127,7 +127,7 @@ if(NOT WITHOUT_MAXADMIN) add_subdirectory(client) endif() - +# Generate text versions of some documents execute_process(COMMAND perl ${CMAKE_SOURCE_DIR}/Documentation/format.pl ${CMAKE_SOURCE_DIR}/Documentation/Changelog.md ${CMAKE_BINARY_DIR}/Changelog.txt) @@ -138,28 +138,17 @@ execute_process(COMMAND perl ${CMAKE_SOURCE_DIR}/Documentation/format.pl ${CMAKE_SOURCE_DIR}/Documentation/Upgrading-To-MaxScale-1.1.0.md ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt) -message(STATUS "Installing MaxScale to: ${CMAKE_INSTALL_PREFIX}/") -if(PACKAGE) - install(FILES server/MaxScale_template.cnf DESTINATION /etc) - install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION /etc) - install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION /usr/share/maxscale) - install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION /usr/share/maxscale) - install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION /usr/share/maxscale) - install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION /usr/share/maxscale) - install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION /usr/share/maxscale) - install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION /usr/share/maxscale) -else() - install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION .) - install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION .) - install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION .) - install(FILES server/MaxScale_template.cnf DESTINATION etc) - install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION etc) - install(FILES ${ERRMSG} DESTINATION mysql) - install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION .) - install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION .) - install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION .) -endif() +install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION ${MAXSCALE_SHARE_DIR}) +install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHARE_DIR}) +install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHARE_DIR}) +install(FILES server/MaxScale_template.cnf DESTINATION ${MAXSCALE_CONFIG_DIR}) +install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION ${MAXSCALE_CONFIG_DIR}) +install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_LANG_DIR}) +install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHARE_DIR}) +install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHARE_DIR}) +install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHARE_DIR}) + # Install startup scripts and ldconfig files if(WITH_SCRIPTS) configure_file(${CMAKE_SOURCE_DIR}/maxscale.conf.in ${CMAKE_BINARY_DIR}/maxscale.conf @ONLY) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 755f2eac8..9b679aae0 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -7,4 +7,4 @@ if(HIST) else() message(STATUS "Could not find editline library. MaxAdmin will be built without it.") endif() -install(TARGETS maxadmin DESTINATION bin) +install(TARGETS maxadmin DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) diff --git a/log_manager/CMakeLists.txt b/log_manager/CMakeLists.txt index fdef33f6c..607341d9a 100644 --- a/log_manager/CMakeLists.txt +++ b/log_manager/CMakeLists.txt @@ -3,7 +3,7 @@ if(LOG_DEBUG) endif() add_library(log_manager SHARED log_manager.cc) target_link_libraries(log_manager pthread aio stdc++) -install(TARGETS log_manager DESTINATION lib) +install(TARGETS log_manager DESTINATION ${MAXSCALE_LIBRARY_INSTALL}) if(BUILD_TESTS) add_subdirectory(test) -endif() \ No newline at end of file +endif() diff --git a/macros.cmake b/macros.cmake index 440c3fed5..d80202534 100644 --- a/macros.cmake +++ b/macros.cmake @@ -74,6 +74,34 @@ macro(set_variables) # Build extra tools set(BUILD_TOOLS FALSE CACHE BOOL "Build extra utility tools") + # Change installation directories to standard locations for packages + if(PACKAGE) + set(MAXSCALE_MODULE_INSTALL /lib64/maxscale CACHE PATH "Module installation path") + set(MAXSCALE_LIBRARY_INSTALL /lib64/maxscale CACHE PATH "Library installation path") + message(STATUS "Installing MaxScale modules to: ${MAXSCALE_MODULE_INSTALL}/") + set(MAXSCALE_EXECUTABLE_INSTALL /bin CACHE PATH "Executable installation path") + message(STATUS "Installing MaxScale binaries to: ${MAXSCALE_EXECUTABLE_INSTALL}/") + set(MAXSCALE_CONFIG_DIR /etc CACHE PATH "Configuration file installation path") + set(MAXSCALE_DOC_DIR /usr/share/maxscale/doc CACHE PATH "Documentation installation path") + set(MAXSCALE_LOG_DIR /var/log/maxscale CACHE PATH "Log file directory") + message(STATUS "MaxScale log directory: ${MAXSCALE_LOG_DIR}/") + set(MAXSCALE_CACHE_DIR /var/cache/maxscale CACHE PATH "Service cache file directory") + set(MAXSCALE_LANG_DIR /var/cache/maxscale CACHE PATH "Language file directory(errmsg.sys)") + set(MAXSCALE_SHARE_DIR /usr/share/maxscale CACHE PATH "Share file installation path") + set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_DIR}:${MAXSCALE_LIBRARY_DIR}) + else() + message(STATUS "Installing MaxScale to: ${CMAKE_INSTALL_PREFIX}/") + set(MAXSCALE_MODULE_INSTALL lib64/maxscale CACHE PATH "Module installation path") + set(MAXSCALE_LIBRARY_INSTALL lib64/maxscale CACHE PATH "Library installation path") + set(MAXSCALE_EXECUTABLE_INSTALL bin CACHE PATH "Executable installation path") + set(MAXSCALE_CONFIG_DIR etc CACHE PATH "Configuration file installation path") + set(MAXSCALE_DOC_DIR doc CACHE PATH "Documentation installation path") + set(MAXSCALE_LOG_DIR log CACHE PATH "Log file directory") + set(MAXSCALE_CACHE_DIR cache CACHE PATH "Service cache file directory") + set(MAXSCALE_LANG_DIR cache CACHE PATH "Log file directory") + set(MAXSCALE_SHARE_DIR . CACHE PATH "Share file installation path") + endif() + endmacro() macro(check_deps) diff --git a/query_classifier/CMakeLists.txt b/query_classifier/CMakeLists.txt index 42270cd2c..7fb36d6c6 100644 --- a/query_classifier/CMakeLists.txt +++ b/query_classifier/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(query_classifier SHARED query_classifier.cc) -install(TARGETS query_classifier DESTINATION lib) +install(TARGETS query_classifier COMPONENT lib DESTINATION ${MAXSCALE_LIBRARY_INSTALL}) if(BUILD_TESTS) add_subdirectory(test) endif() diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 630d4c034..ee01396d8 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -18,15 +18,15 @@ if(WITH_TCMALLOC) endif() target_link_libraries(maxscale ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++) -install(TARGETS maxscale DESTINATION bin) +install(TARGETS maxscale DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) add_executable(maxkeys maxkeys.c secrets.c utils.c) target_link_libraries(maxkeys log_manager utils pthread crypt crypto) -install(TARGETS maxkeys DESTINATION bin) +install(TARGETS maxkeys DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) add_executable(maxpasswd maxpasswd.c secrets.c utils.c) target_link_libraries(maxpasswd log_manager utils pthread crypt crypto) -install(TARGETS maxpasswd DESTINATION bin) +install(TARGETS maxpasswd DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) if(BUILD_TESTS) add_subdirectory(test) diff --git a/server/core/gateway.c b/server/core/gateway.c index c06b9f309..d2bcd13a9 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -113,16 +113,6 @@ static char* server_options[] = { const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1; -const char* default_cnf_fname = "MaxScale.cnf"; - -const char* default_configdir = "/etc/"; -const char* default_logdir = "/var/log/maxscale/"; -const char* default_datadir = "/var/lib/maxscale/"; -const char* default_moduledir = "/lib64/maxscale/modules/"; -const char* default_cachedir = "/var/cache/maxscale/"; -const char* default_langdir = "/usr/share/mysql/english/"; -const char* default_piddir = "/var/run/maxscale/"; - static char* server_groups[] = { "embedded", "server", @@ -1752,7 +1742,8 @@ int main(int argc, char **argv) if(langdir == NULL) langdir = strdup(default_langdir); - + if(moduledir == NULL) + moduledir = strdup(default_moduledir); /** * Set a data directory for the mysqld library, we use * a unique directory name to avoid clauses if multiple @@ -1786,33 +1777,35 @@ int main(int argc, char **argv) if (!daemon_mode) { fprintf(stderr, - //"Home directory : %s" - "Configuration file : %s" - "\nLog directory : %s" - "\nData directory : %s\n\n", - //home_dir, + "Configuration file : %s\n" + "Log directory : %s\n" + "Data directory : %s\n" + "Module directory : %s\n\n", cnf_file_path, logdir, - datadir); + datadir, + moduledir); } -/* - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Home directory : %s", - home_dir))); -*/ - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Data directory : %s", - datadir))); - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Log directory : %s/", - logdir))); - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Configuration file : %s", - cnf_file_path))); + + LOGIF(LM, + (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Configuration file: %s", + cnf_file_path))); + LOGIF(LM, + (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Log directory: %s/", + logdir))); + LOGIF(LM, + (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Data directory: %s", + datadir))); + LOGIF(LM, + (skygw_log_write_flush(LOGFILE_MESSAGE, + "Module directory: %s", + moduledir))); /*< Update the server options */ for (i = 0; server_options[i]; i++) @@ -2211,4 +2204,4 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons } return 1; -} \ No newline at end of file +} diff --git a/server/core/load_utils.c b/server/core/load_utils.c index c2716befa..c1ae57a3e 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -147,7 +147,6 @@ MODULE_INFO *mod_info = NULL; if (access(fname, F_OK) == -1) { - //home = get_maxscale_home (); snprintf(fname, MAXPATHLEN+1,"%s/lib%s.so", get_moduledir(), module); if (access(fname, F_OK) == -1) diff --git a/server/include/gw.h b/server/include/gw.h index e2c69aa7c..64db00204 100644 --- a/server/include/gw.h +++ b/server/include/gw.h @@ -1,3 +1,5 @@ +#ifndef _GW_HG +#define _GW_HG #include #include #include @@ -16,9 +18,19 @@ #include #include #include - #include +/** Default file locations */ +static const char* default_cnf_fname = "MaxScale.cnf"; +static const char* default_configdir = "/etc/"; +static const char* default_logdir = "/var/log/maxscale/"; +static const char* default_datadir = "/var/cache/maxscale/"; +static const char* default_moduledir = "/lib64/maxscale/"; +static const char* default_cachedir = "/var/cache/maxscale/"; +static const char* default_langdir = "/usr/share/mysql/english/"; /*< This is where the MariaDB + * server installs errmsg.sys */ +static const char* default_piddir = "/var/run/maxscale/"; + #define EXIT_FAILURE 1 // network buffer is 32K @@ -65,4 +77,5 @@ int gw_write(DCB *dcb, const void *buf, size_t nbytes); int gw_getsockerrno(int fd); int parse_bindconfig(char *, unsigned short, struct sockaddr_in *); int setipaddress(struct in_addr *, char *); -char* get_moduledir(); \ No newline at end of file +char* get_moduledir(); +#endif diff --git a/server/modules/filter/CMakeLists.txt b/server/modules/filter/CMakeLists.txt index 0af1ea566..77ebc7d0c 100644 --- a/server/modules/filter/CMakeLists.txt +++ b/server/modules/filter/CMakeLists.txt @@ -3,41 +3,41 @@ if(BUILD_RABBITMQ) include_directories(${RABBITMQ_HEADERS}) add_library(mqfilter SHARED mqfilter.c) target_link_libraries(mqfilter query_classifier log_manager utils ${RABBITMQ_LIBRARIES}) - install(TARGETS mqfilter DESTINATION modules) + install(TARGETS mqfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) endif() add_library(regexfilter SHARED regexfilter.c) target_link_libraries(regexfilter log_manager utils) -install(TARGETS regexfilter DESTINATION modules) +install(TARGETS regexfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(testfilter SHARED testfilter.c) target_link_libraries(testfilter log_manager utils) -install(TARGETS testfilter DESTINATION modules) +install(TARGETS testfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(qlafilter SHARED qlafilter.c) target_link_libraries(qlafilter log_manager utils) -install(TARGETS qlafilter DESTINATION modules) +install(TARGETS qlafilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(tee SHARED tee.c) target_link_libraries(tee log_manager utils) -install(TARGETS tee DESTINATION modules) +install(TARGETS tee DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(topfilter SHARED topfilter.c) target_link_libraries(topfilter log_manager utils) -install(TARGETS topfilter DESTINATION modules) +install(TARGETS topfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(dbfwfilter SHARED dbfwfilter.c) target_link_libraries(dbfwfilter log_manager utils query_classifier) -install(TARGETS dbfwfilter DESTINATION modules) +install(TARGETS dbfwfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(namedserverfilter SHARED namedserverfilter.c) target_link_libraries(namedserverfilter log_manager utils) -install(TARGETS namedserverfilter DESTINATION modules) +install(TARGETS namedserverfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) if(BUILD_SLAVELAG) add_library(slavelag SHARED slavelag.c) target_link_libraries(slavelag log_manager utils query_classifier) - install(TARGETS slavelag DESTINATION modules) + install(TARGETS slavelag DESTINATION ${MAXSCALE_MODULE_INSTALL}) endif() if(BUILD_TOOLS) diff --git a/server/modules/filter/hint/CMakeLists.txt b/server/modules/filter/hint/CMakeLists.txt index f47cb637e..ecc3cbb85 100644 --- a/server/modules/filter/hint/CMakeLists.txt +++ b/server/modules/filter/hint/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(hintfilter SHARED hintfilter.c hintparser.c) set_target_properties(hintfilter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib) target_link_libraries(hintfilter ssl log_manager utils) -install(TARGETS hintfilter DESTINATION modules) \ No newline at end of file +install(TARGETS hintfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) diff --git a/server/modules/monitor/CMakeLists.txt b/server/modules/monitor/CMakeLists.txt index a99a66142..74a6f2870 100644 --- a/server/modules/monitor/CMakeLists.txt +++ b/server/modules/monitor/CMakeLists.txt @@ -1,16 +1,16 @@ add_library(mysqlmon SHARED mysql_mon.c) target_link_libraries(mysqlmon log_manager utils) -install(TARGETS mysqlmon DESTINATION modules) +install(TARGETS mysqlmon DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(galeramon SHARED galera_mon.c) target_link_libraries(galeramon log_manager utils) -install(TARGETS galeramon DESTINATION modules) +install(TARGETS galeramon DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(ndbclustermon SHARED ndbcluster_mon.c) target_link_libraries(ndbclustermon log_manager utils) -install(TARGETS ndbclustermon DESTINATION modules) +install(TARGETS ndbclustermon DESTINATION ${MAXSCALE_MODULE_INSTALL}) if(BUILD_MMMON) add_library(mmmon SHARED mm_mon.c) target_link_libraries(mmmon log_manager utils) - install(TARGETS mmmon DESTINATION modules) + install(TARGETS mmmon DESTINATION ${MAXSCALE_MODULE_INSTALL}) endif() diff --git a/server/modules/protocol/CMakeLists.txt b/server/modules/protocol/CMakeLists.txt index fa1c2ab34..488f2e4b6 100644 --- a/server/modules/protocol/CMakeLists.txt +++ b/server/modules/protocol/CMakeLists.txt @@ -1,27 +1,27 @@ add_library(MySQLClient SHARED mysql_client.c mysql_common.c) target_link_libraries(MySQLClient log_manager utils) -install(TARGETS MySQLClient DESTINATION modules) +install(TARGETS MySQLClient DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(MySQLBackend SHARED mysql_backend.c mysql_common.c) target_link_libraries(MySQLBackend log_manager utils) -install(TARGETS MySQLBackend DESTINATION modules) +install(TARGETS MySQLBackend DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(telnetd SHARED telnetd.c) target_link_libraries(telnetd log_manager utils) -install(TARGETS telnetd DESTINATION modules) +install(TARGETS telnetd DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(HTTPD SHARED httpd.c) target_link_libraries(HTTPD log_manager utils) -install(TARGETS HTTPD DESTINATION modules) +install(TARGETS HTTPD DESTINATION ${MAXSCALE_MODULE_INSTALL}) if(BUILD_TESTS) add_library(testprotocol SHARED testprotocol.c) - install(TARGETS testprotocol DESTINATION modules) + install(TARGETS testprotocol DESTINATION ${MAXSCALE_MODULE_INSTALL}) endif() add_library(maxscaled SHARED maxscaled.c) target_link_libraries(maxscaled log_manager utils) -install(TARGETS maxscaled DESTINATION modules) +install(TARGETS maxscaled DESTINATION ${MAXSCALE_MODULE_INSTALL}) diff --git a/server/modules/routing/CMakeLists.txt b/server/modules/routing/CMakeLists.txt index 77abe6cd1..b73edfa8c 100644 --- a/server/modules/routing/CMakeLists.txt +++ b/server/modules/routing/CMakeLists.txt @@ -2,28 +2,28 @@ if(BUILD_TESTS) add_subdirectory(test) add_library(testroute SHARED testroute.c) target_link_libraries(testroute log_manager utils) - install(TARGETS testroute DESTINATION modules) + install(TARGETS testroute DESTINATION ${MAXSCALE_MODULE_INSTALL}) endif() add_library(schemarouter SHARED schemarouter/schemarouter.c) target_link_libraries(schemarouter log_manager utils query_classifier) -install(TARGETS schemarouter DESTINATION modules) +install(TARGETS schemarouter DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(shardrouter SHARED schemarouter/shardrouter.c) target_link_libraries(shardrouter log_manager utils query_classifier) -install(TARGETS shardrouter DESTINATION modules) +install(TARGETS shardrouter DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(readconnroute SHARED readconnroute.c) target_link_libraries(readconnroute log_manager utils) -install(TARGETS readconnroute DESTINATION modules) +install(TARGETS readconnroute DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(debugcli SHARED debugcli.c debugcmd.c) target_link_libraries(debugcli log_manager utils) -install(TARGETS debugcli DESTINATION modules) +install(TARGETS debugcli DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_library(cli SHARED cli.c debugcmd.c) target_link_libraries(cli log_manager utils) -install(TARGETS cli DESTINATION modules) +install(TARGETS cli DESTINATION ${MAXSCALE_MODULE_INSTALL}) add_subdirectory(readwritesplit) add_subdirectory(schemarouter/test) diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index 4de2a35b4..9fcd8536e 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(binlogrouter SHARED blr.c blr_master.c blr_cache.c blr_slave.c blr_file.c) -set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib) +set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_INSTALL}:${MAXSCALE_LIBRARY_INSTALL}) target_link_libraries(binlogrouter ssl pthread log_manager) -install(TARGETS binlogrouter DESTINATION modules) +install(TARGETS binlogrouter DESTINATION ${MAXSCALE_MODULE_INSTALL}) diff --git a/server/modules/routing/maxinfo/CMakeLists.txt b/server/modules/routing/maxinfo/CMakeLists.txt index 898df9b73..2585f2a2a 100644 --- a/server/modules/routing/maxinfo/CMakeLists.txt +++ b/server/modules/routing/maxinfo/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(maxinfo SHARED maxinfo.c maxinfo_parse.c maxinfo_error.c maxinfo_exec.c) -set_target_properties(maxinfo PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib) +set_target_properties(maxinfo PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_INSTALL}:MAXSCALE_LIBRARY_INSTALL) target_link_libraries(maxinfo pthread log_manager) -install(TARGETS maxinfo DESTINATION modules) +install(TARGETS maxinfo DESTINATION ${MAXSCALE_MODULE_INSTALL}) diff --git a/server/modules/routing/readwritesplit/CMakeLists.txt b/server/modules/routing/readwritesplit/CMakeLists.txt index 6960d4c8d..fe56582dc 100644 --- a/server/modules/routing/readwritesplit/CMakeLists.txt +++ b/server/modules/routing/readwritesplit/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(readwritesplit SHARED readwritesplit.c) target_link_libraries(readwritesplit ssl pthread log_manager utils query_classifier) -install(TARGETS readwritesplit DESTINATION modules) +install(TARGETS readwritesplit DESTINATION ${MAXSCALE_MODULE_INSTALL}) if(BUILD_TESTS) add_subdirectory(test) endif() From ed426ca30da5c61b9aaf7805bdd5c0166c2d0cd1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 26 Apr 2015 06:21:28 +0300 Subject: [PATCH 008/273] Updated packaging install layout, moved some cmake files around. --- CMakeLists.txt | 14 +++++++++----- cmake/install_layout.cmake | 29 +++++++++++++++++++++++++++++ macros.cmake => cmake/macros.cmake | 28 ---------------------------- etc/postinst.in | 3 +++ plugins/CMakeLists.txt | 10 +++++----- server/core/gateway.c | 14 ++++++++++---- server/core/service.c | 8 +++++--- 7 files changed, 61 insertions(+), 45 deletions(-) create mode 100644 cmake/install_layout.cmake rename macros.cmake => cmake/macros.cmake (83%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48bf38e8e..bf7e135e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,16 @@ endif() message(STATUS "CMake version: ${CMAKE_VERSION}") -include(macros.cmake) - +include(${CMAKE_SOURCE_DIR}/cmake/macros.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/install_layout.cmake) enable_testing() set_variables() set_maxscale_version() -set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") + +set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, RPM, DEB") +set_install_layout(${INSTALL_LAYOUT}) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") @@ -37,6 +40,7 @@ if(NOT CURL_FOUND) message(FATAL_ERROR "Failed to locate dependency: libcurl") endif() +set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_DIR}:${MAXSCALE_LIBRARY_DIR}) # Make sure the release notes for this release are present if it is a stable one if(${MAXSCALE_VERSION} MATCHES "-stable") @@ -144,7 +148,7 @@ install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHARE_ install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHARE_DIR}) install(FILES server/MaxScale_template.cnf DESTINATION ${MAXSCALE_CONFIG_DIR}) install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION ${MAXSCALE_CONFIG_DIR}) -install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_LANG_DIR}) +install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_SHARE_DIR}) install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHARE_DIR}) install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHARE_DIR}) install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHARE_DIR}) @@ -225,7 +229,7 @@ if(PACKAGE) endif() endif() -add_custom_target(buildtests +add_custom_target(buildtestsx COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} COMMAND make COMMENT "Building test suite..." VERBATIM diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake new file mode 100644 index 000000000..0d594188f --- /dev/null +++ b/cmake/install_layout.cmake @@ -0,0 +1,29 @@ +# Set the install layout +# Possible values: +# STANDALONE - Installs to /usr/local/mariadb-maxscale +# RPM - Installs to /usr +# DEB - Installs to /usr +function(set_install_layout TYPE) + + if(${TYPE} MATCHES "STANDALONE") + + set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") + +# RPM and DEB are the same until differences are found + elseif(${TYPE} MATCHES "RPM") + + set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") + + elseif(${TYPE} MATCHES "DEB") + + set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") + + endif() + + set(MAXSCALE_MODULE_INSTALL lib64/maxscale CACHE PATH "Module installation path") + set(MAXSCALE_LIBRARY_INSTALL lib64/maxscale CACHE PATH "Library installation path") + set(MAXSCALE_EXECUTABLE_INSTALL bin CACHE PATH "Executable installation path") + set(MAXSCALE_SHARE_DIR share/maxscale CACHE PATH "Share file installation path, includes licence and readme files") + set(MAXSCALE_DOC_DIR ${MAXSCALE_SHARE_DIR}/doc CACHE PATH "Documentation installation path, text versions only") + set(MAXSCALE_CONFIG_DIR ${MAXSCALE_SHARE_DIR}/etc CACHE PATH "Configuration file installation path, example configurations will be placed here") +endfunction() diff --git a/macros.cmake b/cmake/macros.cmake similarity index 83% rename from macros.cmake rename to cmake/macros.cmake index d80202534..440c3fed5 100644 --- a/macros.cmake +++ b/cmake/macros.cmake @@ -74,34 +74,6 @@ macro(set_variables) # Build extra tools set(BUILD_TOOLS FALSE CACHE BOOL "Build extra utility tools") - # Change installation directories to standard locations for packages - if(PACKAGE) - set(MAXSCALE_MODULE_INSTALL /lib64/maxscale CACHE PATH "Module installation path") - set(MAXSCALE_LIBRARY_INSTALL /lib64/maxscale CACHE PATH "Library installation path") - message(STATUS "Installing MaxScale modules to: ${MAXSCALE_MODULE_INSTALL}/") - set(MAXSCALE_EXECUTABLE_INSTALL /bin CACHE PATH "Executable installation path") - message(STATUS "Installing MaxScale binaries to: ${MAXSCALE_EXECUTABLE_INSTALL}/") - set(MAXSCALE_CONFIG_DIR /etc CACHE PATH "Configuration file installation path") - set(MAXSCALE_DOC_DIR /usr/share/maxscale/doc CACHE PATH "Documentation installation path") - set(MAXSCALE_LOG_DIR /var/log/maxscale CACHE PATH "Log file directory") - message(STATUS "MaxScale log directory: ${MAXSCALE_LOG_DIR}/") - set(MAXSCALE_CACHE_DIR /var/cache/maxscale CACHE PATH "Service cache file directory") - set(MAXSCALE_LANG_DIR /var/cache/maxscale CACHE PATH "Language file directory(errmsg.sys)") - set(MAXSCALE_SHARE_DIR /usr/share/maxscale CACHE PATH "Share file installation path") - set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_DIR}:${MAXSCALE_LIBRARY_DIR}) - else() - message(STATUS "Installing MaxScale to: ${CMAKE_INSTALL_PREFIX}/") - set(MAXSCALE_MODULE_INSTALL lib64/maxscale CACHE PATH "Module installation path") - set(MAXSCALE_LIBRARY_INSTALL lib64/maxscale CACHE PATH "Library installation path") - set(MAXSCALE_EXECUTABLE_INSTALL bin CACHE PATH "Executable installation path") - set(MAXSCALE_CONFIG_DIR etc CACHE PATH "Configuration file installation path") - set(MAXSCALE_DOC_DIR doc CACHE PATH "Documentation installation path") - set(MAXSCALE_LOG_DIR log CACHE PATH "Log file directory") - set(MAXSCALE_CACHE_DIR cache CACHE PATH "Service cache file directory") - set(MAXSCALE_LANG_DIR cache CACHE PATH "Log file directory") - set(MAXSCALE_SHARE_DIR . CACHE PATH "Share file installation path") - endif() - endmacro() macro(check_deps) diff --git a/etc/postinst.in b/etc/postinst.in index 4bd193958..46772ca16 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -1,5 +1,8 @@ #!/bin/sh +mkdir -p /var/log/maxscale +mkdir -p /var/cache/maxscale +mkdir -p /var/run/maxscale cp @CMAKE_INSTALL_PREFIX@/maxscale /etc/init.d/ cp @CMAKE_INSTALL_PREFIX@/maxscale.conf /etc/ld.so.conf.d/ /sbin/ldconfig diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3ea1eb028..1936ebe84 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,5 +1,5 @@ -install(FILES nagios/check_maxscale_monitors.pl DESTINATION plugins/nagios) -install(FILES nagios/check_maxscale_resources.pl DESTINATION plugins/nagios) -install(FILES nagios/check_maxscale_threads.pl DESTINATION plugins/nagios) -install(FILES nagios/maxscale_commands.cfg DESTINATION plugins/nagios) -install(FILES nagios/server1.cfg DESTINATION plugins/nagios) +install(FILES nagios/check_maxscale_monitors.pl DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) +install(FILES nagios/check_maxscale_resources.pl DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) +install(FILES nagios/check_maxscale_threads.pl DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) +install(FILES nagios/maxscale_commands.cfg DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) +install(FILES nagios/server1.cfg DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) diff --git a/server/core/gateway.c b/server/core/gateway.c index d2bcd13a9..6b55b2661 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1276,6 +1276,7 @@ int main(int argc, char **argv) { moduledir = tmp_path; } + break; case 'A': if(handle_path_arg(&tmp_path,optarg,NULL,true,true)) { @@ -1739,7 +1740,6 @@ int main(int argc, char **argv) if(cachedir == NULL) cachedir = strdup(default_cachedir); - if(langdir == NULL) langdir = strdup(default_langdir); if(moduledir == NULL) @@ -1780,11 +1780,13 @@ int main(int argc, char **argv) "Configuration file : %s\n" "Log directory : %s\n" "Data directory : %s\n" - "Module directory : %s\n\n", + "Module directory : %s\n" + "Service cache : %s\n\n", cnf_file_path, logdir, datadir, - moduledir); + moduledir, + cachedir); } LOGIF(LM, @@ -1806,6 +1808,10 @@ int main(int argc, char **argv) (skygw_log_write_flush(LOGFILE_MESSAGE, "Module directory: %s", moduledir))); + LOGIF(LM, + (skygw_log_write_flush(LOGFILE_MESSAGE, + "Service cache: %s", + cachedir))); /*< Update the server options */ for (i = 0; server_options[i]; i++) @@ -1882,7 +1888,7 @@ int main(int argc, char **argv) if (!config_load(cnf_file_path)) { char* fprerr = "Failed to load MaxScale configuration " - "file. Exiting."; + "file. Exiting. See the error log for details."; print_log_n_stderr(false, !daemon_mode, fprerr, fprerr, 0); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, diff --git a/server/core/service.c b/server/core/service.c index 4584ded24..9455921b7 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -61,6 +61,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -112,7 +113,7 @@ SERVICE *service; return NULL; if ((service->router = load_module(router, MODULE_ROUTER)) == NULL) { - char* home = get_maxscale_home(); + char* home = get_moduledir(); char* ldpath = getenv("LD_LIBRARY_PATH"); LOGIF(LE, (skygw_log_write_flush( @@ -120,12 +121,13 @@ SERVICE *service; "Error : Unable to load %s module \"%s\".\n\t\t\t" " Ensure that lib%s.so exists in one of the " "following directories :\n\t\t\t " - "- %s/modules\n\t\t\t - %s", + "- %s/modules\n%s%s", MODULE_ROUTER, router, router, home, - ldpath))); + ldpath?"\t\t\t - ":"", + ldpath?ldpath:""))); free(service); return NULL; } From 10e9203b5be2cc2cb7d5bfade09259fb6a12e667 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 26 Apr 2015 11:39:00 +0300 Subject: [PATCH 009/273] Updated postinst script --- CMakeLists.txt | 9 +++++---- etc/postinst.in | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf7e135e7..78d205936 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,13 +175,14 @@ if(WITH_SCRIPTS) endif() if(PACKAGE) - install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION . + # Install the files copied by the postinst script into the share folder + install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION ${MAXSCALE_SHARE_DIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION . + install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION ${MAXSCALE_SHARE_DIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install(FILES ${CMAKE_BINARY_DIR}/postinst DESTINATION . + install(FILES ${CMAKE_BINARY_DIR}/postinst DESTINATION ${MAXSCALE_SHARE_DIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install(FILES ${CMAKE_BINARY_DIR}/postrm DESTINATION . + install(FILES ${CMAKE_BINARY_DIR}/postrm DESTINATION ${MAXSCALE_SHARE_DIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(${CMAKE_VERSION} VERSION_LESS 2.8.12) message(WARNING "CMake version is ${CMAKE_VERSION}. Building of packages requires version 2.8.12 or greater.") diff --git a/etc/postinst.in b/etc/postinst.in index 46772ca16..c04850019 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -3,6 +3,6 @@ mkdir -p /var/log/maxscale mkdir -p /var/cache/maxscale mkdir -p /var/run/maxscale -cp @CMAKE_INSTALL_PREFIX@/maxscale /etc/init.d/ -cp @CMAKE_INSTALL_PREFIX@/maxscale.conf /etc/ld.so.conf.d/ +cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/maxscale /etc/init.d/ +cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/maxscale.conf /etc/ld.so.conf.d/ /sbin/ldconfig From 17a7b2addbebc2ac8d19b897e195014830b87431 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 26 Apr 2015 20:08:27 +0300 Subject: [PATCH 010/273] Updated more variables. --- CMakeLists.txt | 39 ++++++++++--------- client/CMakeLists.txt | 2 +- cmake/install_layout.cmake | 31 ++++++--------- log_manager/CMakeLists.txt | 2 +- plugins/CMakeLists.txt | 10 ++--- query_classifier/CMakeLists.txt | 2 +- server/core/CMakeLists.txt | 6 +-- server/core/gateway.c | 30 +++++++------- server/core/load_utils.c | 4 +- server/core/service.c | 2 +- server/include/gw.h | 4 +- server/modules/filter/CMakeLists.txt | 20 +++++----- server/modules/filter/hint/CMakeLists.txt | 4 +- server/modules/monitor/CMakeLists.txt | 8 ++-- server/modules/protocol/CMakeLists.txt | 12 +++--- server/modules/routing/CMakeLists.txt | 12 +++--- server/modules/routing/binlog/CMakeLists.txt | 4 +- server/modules/routing/maxinfo/CMakeLists.txt | 4 +- .../routing/readwritesplit/CMakeLists.txt | 2 +- 19 files changed, 95 insertions(+), 103 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78d205936..ba669131c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,23 +6,24 @@ endif() message(STATUS "CMake version: ${CMAKE_VERSION}") + include(${CMAKE_SOURCE_DIR}/cmake/macros.cmake) + +# Set the installation layout +set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, RPM, DEB") include(${CMAKE_SOURCE_DIR}/cmake/install_layout.cmake) + enable_testing() set_variables() set_maxscale_version() - -set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, RPM, DEB") -set_install_layout(${INSTALL_LAYOUT}) - set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") project(MaxScale) -#Disabled for now pending evaluation +#Do the platform check include(cmake/CheckPlatform.cmake) check_deps() @@ -40,7 +41,7 @@ if(NOT CURL_FOUND) message(FATAL_ERROR "Failed to locate dependency: libcurl") endif() -set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_DIR}:${MAXSCALE_LIBRARY_DIR}) +set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/${MAXSCALE_LIBDIR}) # Make sure the release notes for this release are present if it is a stable one if(${MAXSCALE_VERSION} MATCHES "-stable") @@ -143,15 +144,15 @@ ${CMAKE_SOURCE_DIR}/Documentation/Upgrading-To-MaxScale-1.1.0.md ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt) -install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION ${MAXSCALE_SHARE_DIR}) -install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHARE_DIR}) -install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHARE_DIR}) -install(FILES server/MaxScale_template.cnf DESTINATION ${MAXSCALE_CONFIG_DIR}) -install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION ${MAXSCALE_CONFIG_DIR}) -install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_SHARE_DIR}) -install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHARE_DIR}) -install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHARE_DIR}) -install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHARE_DIR}) +install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES server/MaxScale_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHAREDIR}) # Install startup scripts and ldconfig files if(WITH_SCRIPTS) @@ -176,13 +177,13 @@ endif() if(PACKAGE) # Install the files copied by the postinst script into the share folder - install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION ${MAXSCALE_SHARE_DIR} + install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION ${MAXSCALE_SHARE_DIR} + install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install(FILES ${CMAKE_BINARY_DIR}/postinst DESTINATION ${MAXSCALE_SHARE_DIR} + install(FILES ${CMAKE_BINARY_DIR}/postinst DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install(FILES ${CMAKE_BINARY_DIR}/postrm DESTINATION ${MAXSCALE_SHARE_DIR} + install(FILES ${CMAKE_BINARY_DIR}/postrm DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(${CMAKE_VERSION} VERSION_LESS 2.8.12) message(WARNING "CMake version is ${CMAKE_VERSION}. Building of packages requires version 2.8.12 or greater.") diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 9b679aae0..32ab702ea 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -7,4 +7,4 @@ if(HIST) else() message(STATUS "Could not find editline library. MaxAdmin will be built without it.") endif() -install(TARGETS maxadmin DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) +install(TARGETS maxadmin DESTINATION ${MAXSCALE_BINDIR}) diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index 0d594188f..821b2c474 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -3,27 +3,18 @@ # STANDALONE - Installs to /usr/local/mariadb-maxscale # RPM - Installs to /usr # DEB - Installs to /usr -function(set_install_layout TYPE) +if(${TYPE} MATCHES "STANDALONE") - if(${TYPE} MATCHES "STANDALONE") + set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") - set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") + # RPM and DEB are the same until differences are found +else() + set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") +endif() -# RPM and DEB are the same until differences are found - elseif(${TYPE} MATCHES "RPM") +set(MAXSCALE_LIBDIR lib64/maxscale CACHE PATH "Library installation path") +set(MAXSCALE_BINDIR bin CACHE PATH "Executable installation path") +set(MAXSCALE_SHAREDIR share/maxscale CACHE PATH "Share file installation path, includes licence and readme files") +set(MAXSCALE_DOCDIR share/doc/maxscale CACHE PATH "Documentation installation path, text versions only") +set(MAXSCALE_CONFDIR etc CACHE PATH "Configuration file installation path, this is not usually needed") - set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") - - elseif(${TYPE} MATCHES "DEB") - - set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") - - endif() - - set(MAXSCALE_MODULE_INSTALL lib64/maxscale CACHE PATH "Module installation path") - set(MAXSCALE_LIBRARY_INSTALL lib64/maxscale CACHE PATH "Library installation path") - set(MAXSCALE_EXECUTABLE_INSTALL bin CACHE PATH "Executable installation path") - set(MAXSCALE_SHARE_DIR share/maxscale CACHE PATH "Share file installation path, includes licence and readme files") - set(MAXSCALE_DOC_DIR ${MAXSCALE_SHARE_DIR}/doc CACHE PATH "Documentation installation path, text versions only") - set(MAXSCALE_CONFIG_DIR ${MAXSCALE_SHARE_DIR}/etc CACHE PATH "Configuration file installation path, example configurations will be placed here") -endfunction() diff --git a/log_manager/CMakeLists.txt b/log_manager/CMakeLists.txt index 607341d9a..3dfd883de 100644 --- a/log_manager/CMakeLists.txt +++ b/log_manager/CMakeLists.txt @@ -3,7 +3,7 @@ if(LOG_DEBUG) endif() add_library(log_manager SHARED log_manager.cc) target_link_libraries(log_manager pthread aio stdc++) -install(TARGETS log_manager DESTINATION ${MAXSCALE_LIBRARY_INSTALL}) +install(TARGETS log_manager DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_TESTS) add_subdirectory(test) endif() diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1936ebe84..fc18cc602 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,5 +1,5 @@ -install(FILES nagios/check_maxscale_monitors.pl DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) -install(FILES nagios/check_maxscale_resources.pl DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) -install(FILES nagios/check_maxscale_threads.pl DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) -install(FILES nagios/maxscale_commands.cfg DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) -install(FILES nagios/server1.cfg DESTINATION ${MAXSCALE_SHARE_DIR}/plugins/nagios) +install(FILES nagios/check_maxscale_monitors.pl DESTINATION ${MAXSCALE_SHAREDIR}/plugins/nagios) +install(FILES nagios/check_maxscale_resources.pl DESTINATION ${MAXSCALE_SHAREDIR}/plugins/nagios) +install(FILES nagios/check_maxscale_threads.pl DESTINATION ${MAXSCALE_SHAREDIR}/plugins/nagios) +install(FILES nagios/maxscale_commands.cfg DESTINATION ${MAXSCALE_SHAREDIR}/plugins/nagios) +install(FILES nagios/server1.cfg DESTINATION ${MAXSCALE_SHAREDIR}/plugins/nagios) diff --git a/query_classifier/CMakeLists.txt b/query_classifier/CMakeLists.txt index 7fb36d6c6..19820ccdd 100644 --- a/query_classifier/CMakeLists.txt +++ b/query_classifier/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(query_classifier SHARED query_classifier.cc) -install(TARGETS query_classifier COMPONENT lib DESTINATION ${MAXSCALE_LIBRARY_INSTALL}) +install(TARGETS query_classifier COMPONENT lib DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_TESTS) add_subdirectory(test) endif() diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index ee01396d8..ead8560d3 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -18,15 +18,15 @@ if(WITH_TCMALLOC) endif() target_link_libraries(maxscale ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++) -install(TARGETS maxscale DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) +install(TARGETS maxscale DESTINATION ${MAXSCALE_BINDIR}) add_executable(maxkeys maxkeys.c secrets.c utils.c) target_link_libraries(maxkeys log_manager utils pthread crypt crypto) -install(TARGETS maxkeys DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) +install(TARGETS maxkeys DESTINATION ${MAXSCALE_BINDIR}) add_executable(maxpasswd maxpasswd.c secrets.c utils.c) target_link_libraries(maxpasswd log_manager utils pthread crypt crypto) -install(TARGETS maxpasswd DESTINATION ${MAXSCALE_EXECUTABLE_INSTALL}) +install(TARGETS maxpasswd DESTINATION ${MAXSCALE_BINDIR}) if(BUILD_TESTS) add_subdirectory(test) diff --git a/server/core/gateway.c b/server/core/gateway.c index 6b55b2661..a4901ec23 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -131,7 +131,7 @@ static char pidfile[PATH_MAX+1] = ""; static char* configdir = NULL; static char* logdir = NULL; -static char* moduledir = NULL; +static char* libdir = NULL; static char* cachedir = NULL; static char* langdir = NULL; /** @@ -158,7 +158,7 @@ static struct option long_options[] = { {"logdir", required_argument, 0, 'L'}, {"datadir", required_argument, 0, 'D'}, {"configdir",required_argument, 0, 'C'}, - {"moduledir",required_argument, 0, 'B'}, + {"libdir",required_argument, 0, 'B'}, {"cachedir",required_argument, 0, 'A'}, {"language",required_argument, 0, 'N'}, {"syslog", required_argument, 0, 's'}, @@ -204,9 +204,9 @@ static char* check_dir_access(char* dirname,bool,bool); * Get the directory with all the modules. * @return The module directory */ -char* get_moduledir() +char* get_libdir() { - return moduledir; + return libdir; } /** * Handler for SIGHUP signal. Reload the configuration for the @@ -1029,7 +1029,7 @@ static void usage(void) " (default: /var/lib/maxscale)\n" " -C|--configdir=... path to configuration file directory\n" " (default: /etc/)\n" - " -B|--moduledir=... path to module directory\n" + " -B|--libdir=... path to module directory\n" " (default: /var/lib/maxscale)\n" " -A|--cachedir=... path to cache directory\n" " (default: /var/cache/maxscale)\n" @@ -1274,7 +1274,7 @@ int main(int argc, char **argv) case 'B': if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) { - moduledir = tmp_path; + libdir = tmp_path; } break; case 'A': @@ -1742,8 +1742,8 @@ int main(int argc, char **argv) cachedir = strdup(default_cachedir); if(langdir == NULL) langdir = strdup(default_langdir); - if(moduledir == NULL) - moduledir = strdup(default_moduledir); + if(libdir == NULL) + libdir = strdup(default_libdir); /** * Set a data directory for the mysqld library, we use * a unique directory name to avoid clauses if multiple @@ -1785,7 +1785,7 @@ int main(int argc, char **argv) cnf_file_path, logdir, datadir, - moduledir, + libdir, cachedir); } @@ -1807,7 +1807,7 @@ int main(int argc, char **argv) LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE, "Module directory: %s", - moduledir))); + libdir))); LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE, "Service cache: %s", @@ -1882,8 +1882,8 @@ int main(int argc, char **argv) } libmysqld_started = TRUE; - if(moduledir == NULL) - moduledir = strdup(default_moduledir); + if(libdir == NULL) + libdir = strdup(default_libdir); if (!config_load(cnf_file_path)) { @@ -2179,10 +2179,10 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons if(logdir == NULL) handle_path_arg(&logdir,(char*)value,NULL,true,true); } - else if(strcmp(name, "moduledir") == 0) + else if(strcmp(name, "libdir") == 0) { - if(moduledir == NULL) - handle_path_arg(&moduledir,(char*)value,NULL,true,false); + if(libdir == NULL) + handle_path_arg(&libdir,(char*)value,NULL,true,false); } else if(strcmp(name, "datadir") == 0) { diff --git a/server/core/load_utils.c b/server/core/load_utils.c index c1ae57a3e..9451f6838 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -147,7 +147,7 @@ MODULE_INFO *mod_info = NULL; if (access(fname, F_OK) == -1) { - snprintf(fname, MAXPATHLEN+1,"%s/lib%s.so", get_moduledir(), module); + snprintf(fname, MAXPATHLEN+1,"%s/lib%s.so", get_libdir(), module); if (access(fname, F_OK) == -1) { @@ -155,7 +155,7 @@ MODULE_INFO *mod_info = NULL; LOGFILE_ERROR, "Error : Unable to find library for " "module: %s. Module dir: %s", - module, get_moduledir()))); + module, get_libdir()))); return NULL; } } diff --git a/server/core/service.c b/server/core/service.c index 9455921b7..bd22117ec 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -113,7 +113,7 @@ SERVICE *service; return NULL; if ((service->router = load_module(router, MODULE_ROUTER)) == NULL) { - char* home = get_moduledir(); + char* home = get_libdir(); char* ldpath = getenv("LD_LIBRARY_PATH"); LOGIF(LE, (skygw_log_write_flush( diff --git a/server/include/gw.h b/server/include/gw.h index 64db00204..e210fcfa2 100644 --- a/server/include/gw.h +++ b/server/include/gw.h @@ -25,7 +25,7 @@ static const char* default_cnf_fname = "MaxScale.cnf"; static const char* default_configdir = "/etc/"; static const char* default_logdir = "/var/log/maxscale/"; static const char* default_datadir = "/var/cache/maxscale/"; -static const char* default_moduledir = "/lib64/maxscale/"; +static const char* default_libdir = "/lib64/maxscale/"; static const char* default_cachedir = "/var/cache/maxscale/"; static const char* default_langdir = "/usr/share/mysql/english/"; /*< This is where the MariaDB * server installs errmsg.sys */ @@ -77,5 +77,5 @@ int gw_write(DCB *dcb, const void *buf, size_t nbytes); int gw_getsockerrno(int fd); int parse_bindconfig(char *, unsigned short, struct sockaddr_in *); int setipaddress(struct in_addr *, char *); -char* get_moduledir(); +char* get_libdir(); #endif diff --git a/server/modules/filter/CMakeLists.txt b/server/modules/filter/CMakeLists.txt index 77ebc7d0c..6bf6a5daa 100644 --- a/server/modules/filter/CMakeLists.txt +++ b/server/modules/filter/CMakeLists.txt @@ -3,48 +3,48 @@ if(BUILD_RABBITMQ) include_directories(${RABBITMQ_HEADERS}) add_library(mqfilter SHARED mqfilter.c) target_link_libraries(mqfilter query_classifier log_manager utils ${RABBITMQ_LIBRARIES}) - install(TARGETS mqfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) + install(TARGETS mqfilter DESTINATION ${MAXSCALE_LIBDIR}) endif() add_library(regexfilter SHARED regexfilter.c) target_link_libraries(regexfilter log_manager utils) -install(TARGETS regexfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS regexfilter DESTINATION ${MAXSCALE_LIBDIR}) add_library(testfilter SHARED testfilter.c) target_link_libraries(testfilter log_manager utils) -install(TARGETS testfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS testfilter DESTINATION ${MAXSCALE_LIBDIR}) add_library(qlafilter SHARED qlafilter.c) target_link_libraries(qlafilter log_manager utils) -install(TARGETS qlafilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS qlafilter DESTINATION ${MAXSCALE_LIBDIR}) add_library(tee SHARED tee.c) target_link_libraries(tee log_manager utils) -install(TARGETS tee DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS tee DESTINATION ${MAXSCALE_LIBDIR}) add_library(topfilter SHARED topfilter.c) target_link_libraries(topfilter log_manager utils) -install(TARGETS topfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS topfilter DESTINATION ${MAXSCALE_LIBDIR}) add_library(dbfwfilter SHARED dbfwfilter.c) target_link_libraries(dbfwfilter log_manager utils query_classifier) -install(TARGETS dbfwfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS dbfwfilter DESTINATION ${MAXSCALE_LIBDIR}) add_library(namedserverfilter SHARED namedserverfilter.c) target_link_libraries(namedserverfilter log_manager utils) -install(TARGETS namedserverfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS namedserverfilter DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_SLAVELAG) add_library(slavelag SHARED slavelag.c) target_link_libraries(slavelag log_manager utils query_classifier) - install(TARGETS slavelag DESTINATION ${MAXSCALE_MODULE_INSTALL}) + install(TARGETS slavelag DESTINATION ${MAXSCALE_LIBDIR}) endif() if(BUILD_TOOLS) add_executable(ruleparser dbfwfilter.c) target_compile_definitions(ruleparser PUBLIC "BUILD_RULE_PARSER") target_link_libraries(ruleparser ${EMBEDDED_LIB} log_manager utils query_classifier fullcore) - install(TARGETS ruleparser DESTINATION tools) + install(TARGETS ruleparser DESTINATION ${MAXSCALE_BINDIR}) endif() add_subdirectory(hint) diff --git a/server/modules/filter/hint/CMakeLists.txt b/server/modules/filter/hint/CMakeLists.txt index ecc3cbb85..c2d5e9ea1 100644 --- a/server/modules/filter/hint/CMakeLists.txt +++ b/server/modules/filter/hint/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(hintfilter SHARED hintfilter.c hintparser.c) -set_target_properties(hintfilter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib) +set_target_properties(hintfilter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_LIBDIR}) target_link_libraries(hintfilter ssl log_manager utils) -install(TARGETS hintfilter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS hintfilter DESTINATION ${MAXSCALE_LIBDIR}) diff --git a/server/modules/monitor/CMakeLists.txt b/server/modules/monitor/CMakeLists.txt index 74a6f2870..b4d641934 100644 --- a/server/modules/monitor/CMakeLists.txt +++ b/server/modules/monitor/CMakeLists.txt @@ -1,16 +1,16 @@ add_library(mysqlmon SHARED mysql_mon.c) target_link_libraries(mysqlmon log_manager utils) -install(TARGETS mysqlmon DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS mysqlmon DESTINATION ${MAXSCALE_LIBDIR}) add_library(galeramon SHARED galera_mon.c) target_link_libraries(galeramon log_manager utils) -install(TARGETS galeramon DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS galeramon DESTINATION ${MAXSCALE_LIBDIR}) add_library(ndbclustermon SHARED ndbcluster_mon.c) target_link_libraries(ndbclustermon log_manager utils) -install(TARGETS ndbclustermon DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS ndbclustermon DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_MMMON) add_library(mmmon SHARED mm_mon.c) target_link_libraries(mmmon log_manager utils) - install(TARGETS mmmon DESTINATION ${MAXSCALE_MODULE_INSTALL}) + install(TARGETS mmmon DESTINATION ${MAXSCALE_LIBDIR}) endif() diff --git a/server/modules/protocol/CMakeLists.txt b/server/modules/protocol/CMakeLists.txt index 488f2e4b6..4ae3b8f2c 100644 --- a/server/modules/protocol/CMakeLists.txt +++ b/server/modules/protocol/CMakeLists.txt @@ -1,27 +1,27 @@ add_library(MySQLClient SHARED mysql_client.c mysql_common.c) target_link_libraries(MySQLClient log_manager utils) -install(TARGETS MySQLClient DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS MySQLClient DESTINATION ${MAXSCALE_LIBDIR}) add_library(MySQLBackend SHARED mysql_backend.c mysql_common.c) target_link_libraries(MySQLBackend log_manager utils) -install(TARGETS MySQLBackend DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS MySQLBackend DESTINATION ${MAXSCALE_LIBDIR}) add_library(telnetd SHARED telnetd.c) target_link_libraries(telnetd log_manager utils) -install(TARGETS telnetd DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS telnetd DESTINATION ${MAXSCALE_LIBDIR}) add_library(HTTPD SHARED httpd.c) target_link_libraries(HTTPD log_manager utils) -install(TARGETS HTTPD DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS HTTPD DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_TESTS) add_library(testprotocol SHARED testprotocol.c) - install(TARGETS testprotocol DESTINATION ${MAXSCALE_MODULE_INSTALL}) + install(TARGETS testprotocol DESTINATION ${MAXSCALE_LIBDIR}) endif() add_library(maxscaled SHARED maxscaled.c) target_link_libraries(maxscaled log_manager utils) -install(TARGETS maxscaled DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS maxscaled DESTINATION ${MAXSCALE_LIBDIR}) diff --git a/server/modules/routing/CMakeLists.txt b/server/modules/routing/CMakeLists.txt index b73edfa8c..2933e5ec8 100644 --- a/server/modules/routing/CMakeLists.txt +++ b/server/modules/routing/CMakeLists.txt @@ -2,28 +2,28 @@ if(BUILD_TESTS) add_subdirectory(test) add_library(testroute SHARED testroute.c) target_link_libraries(testroute log_manager utils) - install(TARGETS testroute DESTINATION ${MAXSCALE_MODULE_INSTALL}) + install(TARGETS testroute DESTINATION ${MAXSCALE_LIBDIR}) endif() add_library(schemarouter SHARED schemarouter/schemarouter.c) target_link_libraries(schemarouter log_manager utils query_classifier) -install(TARGETS schemarouter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS schemarouter DESTINATION ${MAXSCALE_LIBDIR}) add_library(shardrouter SHARED schemarouter/shardrouter.c) target_link_libraries(shardrouter log_manager utils query_classifier) -install(TARGETS shardrouter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS shardrouter DESTINATION ${MAXSCALE_LIBDIR}) add_library(readconnroute SHARED readconnroute.c) target_link_libraries(readconnroute log_manager utils) -install(TARGETS readconnroute DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS readconnroute DESTINATION ${MAXSCALE_LIBDIR}) add_library(debugcli SHARED debugcli.c debugcmd.c) target_link_libraries(debugcli log_manager utils) -install(TARGETS debugcli DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS debugcli DESTINATION ${MAXSCALE_LIBDIR}) add_library(cli SHARED cli.c debugcmd.c) target_link_libraries(cli log_manager utils) -install(TARGETS cli DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS cli DESTINATION ${MAXSCALE_LIBDIR}) add_subdirectory(readwritesplit) add_subdirectory(schemarouter/test) diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index 9fcd8536e..9a0c245de 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(binlogrouter SHARED blr.c blr_master.c blr_cache.c blr_slave.c blr_file.c) -set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_INSTALL}:${MAXSCALE_LIBRARY_INSTALL}) +set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_LIBDIR}) target_link_libraries(binlogrouter ssl pthread log_manager) -install(TARGETS binlogrouter DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR}) diff --git a/server/modules/routing/maxinfo/CMakeLists.txt b/server/modules/routing/maxinfo/CMakeLists.txt index 2585f2a2a..8e07a34a7 100644 --- a/server/modules/routing/maxinfo/CMakeLists.txt +++ b/server/modules/routing/maxinfo/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(maxinfo SHARED maxinfo.c maxinfo_parse.c maxinfo_error.c maxinfo_exec.c) -set_target_properties(maxinfo PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_MODULE_INSTALL}:MAXSCALE_LIBRARY_INSTALL) +set_target_properties(maxinfo PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_LIBDIR}) target_link_libraries(maxinfo pthread log_manager) -install(TARGETS maxinfo DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS maxinfo DESTINATION ${MAXSCALE_LIBDIR}) diff --git a/server/modules/routing/readwritesplit/CMakeLists.txt b/server/modules/routing/readwritesplit/CMakeLists.txt index fe56582dc..c7e387290 100644 --- a/server/modules/routing/readwritesplit/CMakeLists.txt +++ b/server/modules/routing/readwritesplit/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(readwritesplit SHARED readwritesplit.c) target_link_libraries(readwritesplit ssl pthread log_manager utils query_classifier) -install(TARGETS readwritesplit DESTINATION ${MAXSCALE_MODULE_INSTALL}) +install(TARGETS readwritesplit DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_TESTS) add_subdirectory(test) endif() From c500d23d04b2566d6acecc4b6ee777540752b1f0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 04:48:50 +0300 Subject: [PATCH 011/273] Moved default file search paths to gwdirs.h which is configured by CMake from gwdirs.h.in. --- CMakeLists.txt | 21 +-- Documentation/About/SETUP.md | 32 ++--- .../Getting-Started/Configuration-Guide.md | 8 +- .../Getting-Started-With-MaxScale.md | 6 +- .../Tutorials/Administration-Tutorial.md | 4 +- ...era-Cluster-Connection-Routing-Tutorial.md | 2 +- ...a-Cluster-Read-Write-Splitting-Tutorial.md | 2 +- .../Tutorials/MaxScale-Information-Schema.md | 53 +++++++- ...Replication-Connection-Routing-Tutorial.md | 121 +++++++----------- ...plication-Read-Write-Splitting-Tutorial.md | 2 +- ...eplication-Proxy-Binlog-Router-Tutorial.md | 2 +- Documentation/filters/Query-Log-All-Filter.md | 2 +- Documentation/filters/RabbitMQ-Filter.md | 2 +- Documentation/filters/Regex-Filter.md | 2 +- Documentation/filters/Tee-Filter.md | 2 +- Documentation/filters/Top-N-Filter.md | 2 +- cmake/install_layout.cmake | 20 +-- server/core/gateway.c | 71 +--------- server/include/gw.h | 32 +++-- server/include/gwdirs.h.in | 33 +++++ 20 files changed, 198 insertions(+), 221 deletions(-) create mode 100644 server/include/gwdirs.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index ba669131c..564cb43a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,23 +6,28 @@ endif() message(STATUS "CMake version: ${CMAKE_VERSION}") - include(${CMAKE_SOURCE_DIR}/cmake/macros.cmake) - -# Set the installation layout -set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, RPM, DEB") -include(${CMAKE_SOURCE_DIR}/cmake/install_layout.cmake) - enable_testing() + +set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, RPM, DEB") +if(${INSTALL_LAYOUT} MATCHES "STANDALONE") + set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") +else() + set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") +endif() + +# Set default values for cache entries and set the MaxScale version set_variables() set_maxscale_version() set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") project(MaxScale) +# Set the installation layout +include(${CMAKE_SOURCE_DIR}/cmake/install_layout.cmake) + #Do the platform check include(cmake/CheckPlatform.cmake) @@ -51,10 +56,10 @@ if(${MAXSCALE_VERSION} MATCHES "-stable") message(FATAL_ERROR "Could not find the release notes for this stable release: ${MAXSCALE_VERSION_NUMERIC}") endif() endif() -set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/${MAXSCALE_MODULE_INSTALL}:${CMAKE_INSTALL_PREFIX}/${MAXSCALE_LIBRARY_INSTALL}) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/server/include) configure_file(${CMAKE_SOURCE_DIR}/server/include/version.h.in ${CMAKE_BINARY_DIR}/server/include/version.h) +configure_file(${CMAKE_SOURCE_DIR}/server/include/gwdirs.h.in ${CMAKE_BINARY_DIR}/server/include/gwdirs.h) configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.h.in ${CMAKE_BINARY_DIR}/server/include/maxscale_test.h) configure_file(${CMAKE_SOURCE_DIR}/etc/postinst.in ${CMAKE_BINARY_DIR}/postinst) configure_file(${CMAKE_SOURCE_DIR}/etc/postrm.in ${CMAKE_BINARY_DIR}/postrm) diff --git a/Documentation/About/SETUP.md b/Documentation/About/SETUP.md index 2be316a4d..9cb5dd11b 100644 --- a/Documentation/About/SETUP.md +++ b/Documentation/About/SETUP.md @@ -1,37 +1,25 @@ Installation and startup Untar the binary distribution in the desired location, -e.g. /usr/local/mariadb +e.g. /usr/local/mariadb-maxscale Alternatively build from the source code using the instructions -in the README file and execute make install. +in the [Building MaxScale from Source Code](../Getting-Started/Building-MaxScale-from-Source-Code.md) document. -Simply set the environment variable MAXSCALE_HOME to point to the -MaxScale directory, found inside the path into which the files have been copied, -e.g. MAXSCALE_HOME=/usr/local/mariadb-maxscale - -Also you will need to optionally set LD_LIBRARY_PATH to include the 'lib' folder, -found inside the path into which the files have been copied, -e.g. LD_LIBRARY_PATH=/usr/local/mariadb-maxscale/lib - -Because we need the libmysqld library for parsing we must create a -valid my.cnf file to enable the library to be used. Copy the my.cnf -to $MAXSCALE_HOME/mysql/my.cnf. - -To start MaxScale execute the command 'maxscale' from the bin folder, -e.g. /usr/local/mariadb-maxscale/bin/maxscale +You can start MaxScale using `service maxscale start` or `systemctl start maxscale` if you installed the init.d scripts +or by manually starting the process from the bin folder of the installation directory. Configuration -You need to edit the file MaxScale.cnf in $MAXSCALE_HOME/etc, you should -define the set of server definitions you require, with the addresses -and ports of those servers. Also define the listening ports for your -various services. +You need to create or edit the MaxScale.cnf file in the /etc folder. +Define the services you wish to provide, the set of server definitions +you require, with the addresses and ports of those servers and also +define the listening ports for your various services. -In order to view the internal activity of the gateway you can telnet to +In order to view the internal activity of MaxScale you can either use +the maxadmin client interface with the cli routing module or telnet to the port defined for the telnet listener. Initially you may login with the user name of "admin" and the password "mariadb". Once connected type help for an overview of the commands and help for the more detailed help on commands. Use the add user command to add a new user, this will also remove the admin/mariadb user. - diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 5ce081a82..81d3c90e2 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -27,11 +27,9 @@ connection failover| When a connection currently being used between MaxScale and The MaxScale configuration is read from a file which can be located in a number of placing, MaxScale will search for the configuration file in a number of locations. -1. If the environment variable `MAXSCALE_HOME` is set then MaxScale will look for a configuration file called `MaxScale.cnf` in the directory `$MAXSCALE_HOME/etc`. +1. Location given with the --configdir= command line argument -2. If `MAXSCALE_HOME` is not set or the configuration file is not in the location above MaxScale will look for a file in `/etc/MaxScale.cnf`. - -Alternatively MaxScale can be started with the `-c` flag and the path of the MaxScale home directory tree. +2. MaxScale will look for a configuration file called `MaxScale.cnf` in the directory `/etc/MaxScale.cnf` An explicit path to a configuration file can be passed by using the `-f` option to MaxScale. @@ -524,7 +522,7 @@ Default value is `2`. Write Timeout is the timeout in seconds for each attempt t ## Protocol Modules -The protocols supported by MaxScale are implemented as external modules that are loaded dynamically into the MaxScale core. These modules reside in the directory `$MAXSCALE_HOME/modules`, if the environment variable `$MAXSCALE_HOME` is not set it defaults to `/usr/local/mariadb-maxscale`. It may also be set by passing the `-c` option on the MaxScale command line. +The protocols supported by MaxScale are implemented as external modules that are loaded dynamically into the MaxScale core. These modules reside in the directory `/usr/lib64/maxscale`. The location can be overridden with the `libdir=PATH` parameter under the `[maxscale]` section. It may also be set by passing the `-B PATH` or `--libdir=PATH` option on the MaxScale command line. ### MySQLClient diff --git a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md index 24ee54b22..6b84d8d58 100644 --- a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md +++ b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md @@ -62,11 +62,9 @@ shared objects that are loaded on demand. In order for MaxScale to find these modules it will search using a predescribed search path. The rules are: 1. Look in the current directory for the module - 2. Look in $MAXSCALE_HOME/modules - 3. Look in /usr/local/mariadb-maxscale/modules - -Configuration is read by default from the file $MAXSCALE_HOME/etc/MaxScale.cnf, /etc/MaxScale.cnf. An example file is included in in the installation and can be found in the etc/ folder within the MaxScale installation. The default value of MAXSCALE_HOME can be overridden by using the -c flag on the command line. This should be immediately followed by the path to the MaxScale home directory. The -f flag can be used on the command line to set the name and the location of the configuration file. Without path expression the file is read from \$MAXSCALE_HOME/etc directory. + 2. Look in /usr/lib64/maxscale +Configuration is read by default from the file /etc/MaxScale.cnf. An example file is included in in the installation and can be found in the /usr/share/maxscale folder within the MaxScale installation. The -f flag can be used on the command line to set the name and the location of the configuration file. Without path expression the file is read from the /etc directory. ## Administration Of MaxScale diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index 929850eb5..894f89f94 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -45,7 +45,7 @@ Options may be passed to the MaxScale binary that alter this default behavior, t -f --config= - Use the filename passed as an argument instead of looking in $MAXSCALE_HOME/etc/MaxScale.cnf + Use the filename passed as an argument instead of looking in /etc/MaxScale.cnf -l| @@ -79,7 +79,7 @@ or MaxScale will also stop gracefully if it received a hangup signal, to find the process id of the MaxScale server use the ps command or read the contents of the maxscale.pid file located in the same directory as the logs. - $ kill -HUP `cat $MAXSCALE_HOME/log/maxscale.pid` + $ kill -HUP `cat /log/maxscale.pid` In order to shutdown MaxScale using the maxadmin command you may either connect with maxadmin in interactive mode or pass the "shutdown maxscale" command you wish to execute as an argument to maxadmin. diff --git a/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md b/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md index d75c6162a..d58ebc32c 100644 --- a/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md +++ b/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md @@ -60,7 +60,7 @@ If you wish to use two different usernames for the two different roles of monito ### Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory $MAXSCALE_HOME/etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. diff --git a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md index df57e9390..8717591d8 100644 --- a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md @@ -62,7 +62,7 @@ If you wish to use two different usernames for the two different roles of monito ### Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory $MAXSCALE_HOME/etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. diff --git a/Documentation/Tutorials/MaxScale-Information-Schema.md b/Documentation/Tutorials/MaxScale-Information-Schema.md index c8ce5c08a..9872556b3 100644 --- a/Documentation/Tutorials/MaxScale-Information-Schema.md +++ b/Documentation/Tutorials/MaxScale-Information-Schema.md @@ -11,48 +11,59 @@ The service entry needs to define the service name, the type as service and the The specified user, with the password (plain or encrypted via maxpassword utility) is allowed to connect via MySQL protocol. Currently the user can connect to maxinfo from any remote IP and to localhost as well. +``` [MaxInfo] type=service router=maxinfo user=monitor passwd=EBD2F49C3B375812A8CDEBA632ED8BBC +``` The listener section defines the protocol, port and other information needed to create a listener for the service. To listen on a port using the MySQL protocol a section as shown below should be added to the configuration file. +``` [MaxInfo Listener] type=listener service=MaxInfo protocol=MySQLClient port=9003 +``` To listen with the HTTP protocol and hence return JSON documents a section as should below is required. +``` [MaxInfo JSON Listener] type=listener service=MaxInfo protocol=HTTPD port=8003 +``` + If both the MySQL and JSON responses are required then a single service can be configured with both types of listener. As with any other listeners within MaxScale the listeners can be bound to a particular interface by use of the address= parameter. This allows the access to the maxinfo data to be limited to the localhost by adding an address=localhost parameter in the configuration file. +``` [MaxInfo Listener] type=listener service=MaxInfo protocol=MySQLClient address=localhost port=9003 +``` # MySQL Interface to maxinfo The maxinfo supports a small subset of SQL statements in addition to the MySQL status and ping requests. These may be used for simple monitoring of MaxScale. +``` % mysqladmin -hmaxscale.mariadb.com -P9003 -umonitor -pxyz ping mysqld is alive % mysqladmin -hmaxscale.mariadb.com -P9003 -umonitor -pxyz status Uptime: 72 Threads: 1 Sessions: 11 % +``` The SQL command used to interact with maxinfo is the show command, a variety of show commands are available and will be described in the following sections. @@ -60,6 +71,7 @@ The SQL command used to interact with maxinfo is the show command, a variety of The show variables command will display a set of name and value pairs for a number of MaxScale system variables. +``` mysql> show variables; +--------------------+-------------------------+ | Variable_name | Value | @@ -77,9 +89,11 @@ The show variables command will display a set of name and value pairs for a numb 9 rows in set (0.02 sec) mysql> +``` The show variables command can also accept a limited like clause. This like clause must either be a literal string to match, a pattern starting with a %, a pattern ending with a % or a string with a % at both the start and the end. +``` mysql> show variables like 'version'; +---------------+----------------+ | Variable_name | Value | @@ -116,11 +130,13 @@ The show variables command can also accept a limited like clause. This like clau 3 rows in set (0.02 sec) mysql> +``` ## Show status The show status command displays a set of status counters, as with show variables the show status command can be passed a simplified like clause to limit the values returned. +``` mysql> show status; +---------------------------+-------+ | Variable_name | Value | @@ -151,11 +167,13 @@ The show status command displays a set of status counters, as with show variable 22 rows in set (0.02 sec) mysql> +``` ## Show services The show services command will return a set of basic statistics regarding each of the configured services within MaxScale. +``` mysql> show services; +----------------+----------------+--------------+----------------+ | Service Name | Router Module | No. Sessions | Total Sessions | @@ -172,6 +190,7 @@ The show services command will return a set of basic statistics regarding each o 8 rows in set (0.02 sec) mysql> +``` The show services command does not accept a like clause and will ignore any like clause that is given. @@ -179,6 +198,7 @@ The show services command does not accept a like clause and will ignore any like The show listeners command will return a set of status information for every listener defined within the MaxScale configuration file. +``` mysql> show listeners; +----------------+-----------------+-----------+------+---------+ | Service Name | Protocol Module | Address | Port | State | @@ -196,6 +216,7 @@ The show listeners command will return a set of status information for every lis 9 rows in set (0.02 sec) mysql> +``` The show listeners command will ignore any like clause passed to it. @@ -203,6 +224,7 @@ The show listeners command will ignore any like clause passed to it. The show sessions command returns information on every active session within MaxScale. It will ignore any like clause passed to it. +``` mysql> show sessions; +-----------+---------------+----------------+---------------------------+ | Session | Client | Service | State | @@ -222,11 +244,13 @@ The show sessions command returns information on every active session within Max 11 rows in set (0.02 sec) mysql> +``` ## Show clients The show clients command reports a row for every client application connected to MaxScale. Like clauses are not available of the show clients command. +``` mysql> show clients; +-----------+---------------+---------+---------------------------+ | Session | Client | Service | State | @@ -237,11 +261,13 @@ The show clients command reports a row for every client application connected to 2 rows in set (0.02 sec) mysql> +``` ## Show servers The show servers command returns data for each backend server configured within the MaxScale configuration file. This data includes the current number of connections MaxScale has to that server and the state of that server as monitored by MaxScale. +``` mysql> show servers; +---------+-----------+------+-------------+---------+ | Server | Address | Port | Connections | Status | @@ -254,11 +280,13 @@ The show servers command returns data for each backend server configured within 4 rows in set (0.02 sec) mysql> +``` ## Show modules The show modules command reports the information on the modules currently loaded into MaxScale. This includes the name type and version of each module. It also includes the API version the module has been written against and the current release status of the module. +``` mysql> show modules; +----------------+-------------+---------+-------------+----------------+ | Module Name | Module Type | Version | API Version | Status | @@ -277,12 +305,13 @@ The show modules command reports the information on the modules currently loaded 10 rows in set (0.02 sec) mysql> - +``` ## Show monitors The show monitors command reports each monitor configured within the system and the state of that monitor. +``` mysql> show monitors; +---------------+---------+ | Monitor | Status | @@ -292,12 +321,13 @@ The show monitors command reports each monitor configured within the system and 1 row in set (0.02 sec) mysql> - +``` ## Show eventTimes The show eventTimes command returns a table of statistics that reflect the performance of the event queuing and execution portion of the MaxScale core. +``` mysql> show eventTimes; +---------------+-------------------+---------------------+ | Duration | No. Events Queued | No. Events Executed | @@ -336,6 +366,7 @@ The show eventTimes command returns a table of statistics that reflect the perfo 30 rows in set (0.02 sec) mysql> +``` Each row represents a time interval, in 100ms increments, with the counts representing the number of events that were in the event queue for the length of time that row represents and the number of events that were executing of the time indicated by the row. @@ -347,6 +378,7 @@ The simplified JSON interface takes the URL of the request made to maxinfo and m The /variables URL will return the MaxScale variables, these variables can not be filtered via this interface. +``` $ curl http://maxscale.mariadb.com:8003/variables [ { "Variable_name" : "version", "Value" : "1.0.6-unstable"}, { "Variable_name" : "version_comment", "Value" : "MariaDB MaxScale"}, @@ -358,11 +390,13 @@ The /variables URL will return the MaxScale variables, these variables can not b { "Variable_name" : "MAXSCALE_UPTIME", "Value" : 3948}, { "Variable_name" : "MAXSCALE_SESSIONS", "Value" : 12}] $ +``` ## Status Use of the /status URI will return the status information that would normally be returned by the show status command. No filtering of the status information is available via this interface +``` $ curl http://maxscale.mariadb.com:8003/status [ { "Variable_name" : "Uptime", "Value" : 3831}, { "Variable_name" : "Uptime_since_flush_status", "Value" : 3831}, @@ -387,11 +421,13 @@ Use of the /status URI will return the status information that would normally be { "Variable_name" : "Max_event_queue_time", "Value" : 0}, { "Variable_name" : "Max_event_execution_time", "Value" : 1}] $ +``` ## Services The /services URI returns the data regarding the services defined within the configuration of MaxScale. Two counters are returned, the current number of sessions attached to this service and the total number connected since the service started. +``` $ curl http://maxscale.mariadb.com:8003/services [ { "Service Name" : "Test Service", "Router Module" : "readconnroute", "No. Sessions" : 1, "Total Sessions" : 1}, { "Service Name" : "Split Service", "Router Module" : "readwritesplit", "No. Sessions" : 1, "Total Sessions" : 1}, @@ -402,11 +438,13 @@ The /services URI returns the data regarding the services defined within the con { "Service Name" : "CLI", "Router Module" : "cli", "No. Sessions" : 1, "Total Sessions" : 1}, { "Service Name" : "MaxInfo", "Router Module" : "maxinfo", "No. Sessions" : 5, "Total Sessions" : 20}] $ +``` ## Listeners The /listeners URI will return a JSON array with one entry per listener, each entry is a JSON object that describes the configuration and state of that listener. +``` $ curl http://maxscale.mariadb.com:8003/listeners [ { "Service Name" : "Test Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4006, "State" : "Running"}, { "Service Name" : "Split Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4007, "State" : "Running"}, @@ -418,11 +456,13 @@ The /listeners URI will return a JSON array with one entry per listener, each en { "Service Name" : "MaxInfo", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 9003, "State" : "Running"}, { "Service Name" : "MaxInfo", "Protocol Module" : "HTTPD", "Address" : "*", "Port" : 8003, "State" : "Running"}] $ +``` ## Modules The /modules URI returns data for each plugin that has been loaded into MaxScale. The plugin name, type and version are returned as is the version of the plugin API that the plugin was built against and the release status of the plugin. +``` $ curl http://maxscale.mariadb.com:8003/modules [ { "Module Name" : "HTTPD", "Module Type" : "Protocol", "Version" : "V1.0.1", "API Version" : "1.0.0", "Status" : "In Development"}, { "Module Name" : "maxscaled", "Module Type" : "Protocol", "Version" : "V1.0.0", "API Version" : "1.0.0", "Status" : "GA"}, @@ -435,11 +475,13 @@ The /modules URI returns data for each plugin that has been loaded into MaxScale { "Module Name" : "cli", "Module Type" : "Router", "Version" : "V1.0.0", "API Version" : "1.0.0", "Status" : "GA"}, { "Module Name" : "maxinfo", "Module Type" : "Router", "Version" : "V1.0.0", "API Version" : "1.0.0", "Status" : "Alpha"}] $ +``` ## Sessions The /sessions URI returns a JSON array with an object for each active session within MaxScale. +``` $ curl http://maxscale.mariadb.com:8003/sessions [ { "Session" : "0x1a8e9a0", "Client" : "80.176.79.245", "Service" : "MaxInfo", "State" : "Session ready for routing"}, { "Session" : "0x1a8e6d0", "Client" : "80.240.130.35", "Service" : "MaxInfo", "State" : "Session ready for routing"}, @@ -453,32 +495,38 @@ The /sessions URI returns a JSON array with an object for each active session wi { "Session" : "0x1a5c530", "Client" : , "Service" : "Split Service", "State" : "Listener Session"}, { "Session" : "0x19ac1c0", "Client" : , "Service" : "Test Service", "State" : "Listener Session"}] $ +``` ## Clients The /clients URI is a limited version of the /sessions, in this case it only returns an entry for a session that represents a client connection. +``` $ curl http://maxscale.mariadb.com:8003/clients [ { "Session" : "0x1a90be0", "Client" : "80.176.79.245", "Service" : "MaxInfo", "State" : "Session ready for routing"}, { "Session" : "0x1a8e9a0", "Client" : "127.0.0.1", "Service" : "MaxInfo", "State" : "Session ready for routing"}, { "Session" : "0x1a8e6d0", "Client" : "80.240.130.35", "Service" : "MaxInfo", "State" : "Session ready for routing"}] $ +``` ## Servers The /servers URI is used to retrieve information for each of the servers defined within the MaxScale configuration. This information includes the connection count and the current status as monitored by MaxScale. The connection count is only those connections made by MaxScale to those servers. +``` $ curl http://maxscale.mariadb.com:8003/servers [ { "Server" : "server1", "Address" : "127.0.0.1", "Port" : 3306, "Connections" : 0, "Status" : "Running"}, { "Server" : "server2", "Address" : "127.0.0.1", "Port" : 3307, "Connections" : 0, "Status" : "Down"}, { "Server" : "server3", "Address" : "127.0.0.1", "Port" : 3308, "Connections" : 0, "Status" : "Down"}, { "Server" : "server4", "Address" : "127.0.0.1", "Port" : 3309, "Connections" : 0, "Status" : "Down"}] $ +``` ## Event Times The /event/times URI returns an array of statistics that reflect the performance of the event queuing and execution portion of the MaxScale core. Each element is an object that represents a time bucket, in 100ms increments, with the counts representing the number of events that were in the event queue for the length of time that row represents and the number of events that were executing of the time indicated by the object. +``` $ curl http://maxscale.mariadb.com:8003/event/times [ { "Duration" : "< 100ms", "No. Events Queued" : 64, "No. Events Executed" : 63}, { "Duration" : " 100 - 200ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, @@ -510,3 +558,4 @@ The /event/times URI returns an array of statistics that reflect the performance { "Duration" : "2700 - 2800ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, { "Duration" : "2800 - 2900ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, { "Duration" : "> 3000ms", "No. Events Queued" : 0, "No. Events Executed" : 0}] +``` diff --git a/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md b/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md index aac084f7b..8158bfa76 100644 --- a/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md +++ b/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md @@ -36,6 +36,7 @@ The first user required must be able to select data from the table mysql.user, t 2. Create the user, substituting the username, password and host on which maxscale runs within your environment +``` MariaDB [(none)]> create user '*username*'@'*maxscalehost*' identified by '*password*'; **Query OK, 0 rows affected (0.00 sec)** @@ -45,9 +46,11 @@ MariaDB [(none)]> create user '*username*'@'*maxscalehost*' identified by '*pass MariaDB [(none)]> grant SELECT on mysql.user to '*username*'@'*maxscalehost*'; **Query OK, 0 rows affected (0.03 sec)** +``` Additionally, GRANT SELECT on the mysql.db table and SHOW DATABASES privileges are required in order to load databases name and grants suitable for database name authorization. +``` MariaDB [(none)]> GRANT SELECT ON mysql.db TO 'username'@'maxscalehost'; **Query OK, 0 rows affected (0.00 sec)** @@ -55,9 +58,11 @@ MariaDB [(none)]> GRANT SELECT ON mysql.db TO 'username'@'maxscalehost'; MariaDB [(none)]> GRANT SHOW DATABASES ON *.* TO 'username'@'maxscalehost'; **Query OK, 0 rows affected (0.00 sec)** +``` The second user is used to monitored the state of the cluster. This user, which may be the same username as the first, requires permissions to access the various sources of monitoring data. In order to monitor a replication cluster this user must be granted the roles REPLICATION SLAVE and REPLICATION CLIENT +``` MariaDB [(none)]> grant REPLICATION SLAVE on *.* to '*username*'@'*maxscalehost*'; **Query OK, 0 rows affected (0.00 sec)** @@ -65,220 +70,174 @@ MariaDB [(none)]> grant REPLICATION SLAVE on *.* to '*username*'@'*maxscalehost* MariaDB [(none)]> grant REPLICATION CLIENT on *.* to '*username*'@'*maxscalehost*'; **Query OK, 0 rows affected (0.00 sec)** +``` If you wish to use two different usernames for the two different roles of monitoring and collecting user information then create a different username using the first two steps from above. ## Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory $MAXSCALE_HOME/etc, if you have installed in the default location then this file is available in /usr/local/mariadb/maxscle/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc. This is not created as part of the installation process and must be manually created. A template file does exist in the `/usr/share/maxscale` folder that can be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. +``` [maxscale] - threads=4 +``` Since we are using MySQL Replication and connection routing we want two different ports to which the client application can connect; one that will be directed to the current master within the replication cluster and another that will load balance between the slaves. To achieve this within MaxScale we need to define two services in the ini file; one for the read/write operations that should be executed on the master server and another for connections to one of the slaves. Create a section for each in your MaxScale.ini file and set the type to service, the section names are the names of the services themselves and should be meaningful to the administrator. Names may contain whitespace. +``` [Write Service] - type=service [Read Service] - type=service +``` The router for these two sections is identical, the readconnroute module, also the services should be provided with the list of servers that will be part of the cluster. The server names given here are actually the names of server sections in the configuration file and not the physical hostnames or addresses of the servers. +``` [Write Service] - type=service - router=readconnroute - servers=dbserv1, dbserv2, dbserv3 [Read Service] - type=service - router=readconnroute - servers=dbserv1, dbserv2, dbserv3 +``` In order to instruct the router to which servers it should route we must add router options to the service. The router options are compared to the status that the monitor collects from the servers and used to restrict the eligible set of servers to which that service may route. In our case we use the two options master and slave for our two services. +``` [Write Service] - type=service - router=readconnroute - router_options=master - servers=dbserv1, dbserv2, dbserv3 [Read Service] - type=service - router=readconnroute - router_options=slave - servers=dbserv1, dbserv2, dbserv3 +``` The final step in the service sections is to add the username and password that will be used to populate the user data from the database cluster. There are two options for representing the password, either plain text or encrypted passwords may be used. In order to use encrypted passwords a set of keys must be generated that will be used by the encryption and decryption process. To generate the keys use the maxkeys command and pass the name of the secrets file in which the keys are stored. -% maxkeys /usr/local/mariadb-maxscale/etc/.secrets - -% +``` +maxkeys /usr/local/mariadb-maxscale/etc/.secrets +``` Once the keys have been created the maxpasswd command can be used to generate the encrypted password. -% maxpasswd plainpassword - +``` +maxpasswd plainpassword 96F99AA1315BDC3604B006F427DD9484 - -% +``` The username and password, either encrypted or plain text, are stored in the service section using the user and passwd parameters. +``` [Write Service] - type=service - router=readconnroute - router_options=master - servers=dbserv1, dbserv2, dbserv3 - user=maxscale - passwd=96F99AA1315BDC3604B006F427DD9484 [Read Service] - type=service - router=readconnroute - router_options=slave - servers=dbserv1, dbserv2, dbserv3 - user=maxscale - passwd=96F99AA1315BDC3604B006F427DD9484 +``` This completes the definitions required by the services, however listening ports must be associated with the services in order to allow network connections. This is done by creating a series of listener sections. These sections again are named for the convenience of the administrator and should be of type listener with an entry labeled service which contains the name of the service to associate the listener with. Each service may have multiple listeners. +``` [Write Listener] - type=listener - service=Write Service [Read Listener] - type=listener - service=Read Service +``` A listener must also define the protocol module it will use for the incoming network protocol, currently this should be the MySQLClient protocol for all database listeners. The listener may then supply a network port to listen on and/or a socket within the file system. +``` [Write Listener] - type=listener - service=Write Service - protocol=MySQLClient - port=4306 - socket=/tmp/ClusterMaster [Read Listener] - type=listener - service=Read Service - protocol=MySQLClient - port=4307 +``` An address parameter may be given if the listener is required to bind to a particular network address when using hosts with multiple network addresses. The default behavior is to listen on all network interfaces. The next stage is the configuration is to define the server information. This defines how to connect to each of the servers within the cluster, again a section is created for each server, with the type set to server, the network address and port to connect to and the protocol to use to connect to the server. Currently the protocol for all database connections in MySQLBackend. +``` [dbserv1] - type=server - address=192.168.2.1 - port=3306 - protocol=MySQLBackend [dbserv2] - type=server - address=192.168.2.2 - port=3306 - protocol=MySQLBackend [dbserv3] - type=server - address=192.168.2.3 - port=3306 - protocol=MySQLBackend +``` In order for MaxScale to monitor the servers using the correct monitoring mechanisms a section should be provided that defines the monitor to use and the servers to monitor. Once again a section is created with a symbolic name for the monitor, with the type set to monitor. Parameters are added for the module to use, the list of servers to monitor and the username and password to use when connecting to the the servers with the monitor. +``` [Replication Monitor] - type=monitor - module=mysqlmon - servers=dbserv1, dbserv2, dbserv3 - user=maxscale - passwd=96F99AA1315BDC3604B006F427DD9484 +``` As with the password definition in the server either plain text or encrypted passwords may be used. The final stage in the configuration is to add the option service which is used by the maxadmin command to connect to MaxScale for monitoring and administration purposes. This creates a service section and a listener section. +``` [CLI] - type=service - router=cli [CLI Listener] - type=listener - service=CLI - protocol=maxscaled - address=localhost - port=6603 +``` In the case of the example above it should be noted that an address parameter has been given to the listener, this limits connections to maxadmin commands that are executed on the same machine that hosts MaxScale. @@ -286,14 +245,19 @@ In the case of the example above it should be noted that an address parameter ha Upon completion of the configuration process MaxScale is ready to be started for the first time. This may either be done manually by running the maxscale command or via the service interface. -% maxscale +``` +maxscale +``` or -% service maxscale start +``` +service maxscale start +``` -Check the error log in /usr/local/mariadb-maxscale/log to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. +Check the error log in /var/log/lomaxscale/ to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. +``` % maxadmin -pmariadb list services Services. @@ -349,6 +313,7 @@ CLI | maxscaled | localhost | 6603 | Running ---------------------+--------------------+-----------------+-------+-------- % +``` -MaxScale is now ready to start accepting client connections and routing them to the master or slaves within your cluster. Other configuration options are available that can alter the criteria used for routing, these include monitoring the replication lag within the cluster and routing only to slaves that are within a predetermined delay from the current master or using weights to obtain unequal balancing operations. These options may be found in the MaxScale Configuration Guide. More detail on the use of maxadmin can be found in the document "MaxAdmin - The MaxScale Administration & Monitoring Client Application". +MaxScale is now ready to start accepting client connections and routing them to the master or slaves within your cluster. Other configuration options are available that can alter the criteria used for routing, these include monitoring the replication lag within the cluster and routing only to slaves that are within a predetermined delay from the current master or using weights to obtain unequal balancing operations. These options may be found in the MaxScale Configuration Guide. More detail on the use of maxadmin can be found in the document [MaxAdmin - The MaxScale Administration & Monitoring Client Application](Administration-Tutorial.md). diff --git a/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md index 54e5412be..b34fcf71e 100644 --- a/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md @@ -70,7 +70,7 @@ If you wish to use two different usernames for the two different roles of monito ## Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory $MAXSCALE_HOME/etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. diff --git a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md index f07fd13a3..3f61638d1 100644 --- a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md +++ b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md @@ -84,7 +84,7 @@ This optional parameter allows for the administrator to define the number of the ### binlogdir -This parameter allows the location that MaxScale uses to store binlog files to be set. If this parameter is not set to a directory name then MaxScale will store the binlog files in the directory $MAXSCALE_HOME/. +This parameter allows the location that MaxScale uses to store binlog files to be set. If this parameter is not set to a directory name then MaxScale will store the binlog files in the directory /var/cache/maxscale/. ### heartbeat diff --git a/Documentation/filters/Query-Log-All-Filter.md b/Documentation/filters/Query-Log-All-Filter.md index e0c71e13f..87ea620c8 100644 --- a/Documentation/filters/Query-Log-All-Filter.md +++ b/Documentation/filters/Query-Log-All-Filter.md @@ -6,7 +6,7 @@ The Query Log All (QLA) filter is a filter module for MaxScale that is ## Configuration -The configuration block for the QLA filter requires the minimal filter options in it's section within the MaxScale.cnf file, stored in $MAXSCALE_HOME/etc/MaxScale.cnf. +The configuration block for the QLA filter requires the minimal filter options in it's section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. ``` [MyLogFilter] type=filter diff --git a/Documentation/filters/RabbitMQ-Filter.md b/Documentation/filters/RabbitMQ-Filter.md index f23313d0d..4068ceec5 100644 --- a/Documentation/filters/RabbitMQ-Filter.md +++ b/Documentation/filters/RabbitMQ-Filter.md @@ -5,7 +5,7 @@ This filter is designed to extract queries and transform them into a canonical f ## Configuration -The configuration block for the **mqfilter** filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in $MAXSCALE_HOME/etc/MaxScale.cnf. Although the filter will start, it will use the default values which only work with a freshly installed RabbitMQ server and use its default values. This setup is mostly intended for testing the filter. +The configuration block for the **mqfilter** filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. Although the filter will start, it will use the default values which only work with a freshly installed RabbitMQ server and use its default values. This setup is mostly intended for testing the filter. The following is an example of a mqfilter configuration in the MaxScale.cnf file used for actual logging of queries to a RabbitMQ broker on a different host. diff --git a/Documentation/filters/Regex-Filter.md b/Documentation/filters/Regex-Filter.md index 2d5ecd96d..e5a6744f2 100644 --- a/Documentation/filters/Regex-Filter.md +++ b/Documentation/filters/Regex-Filter.md @@ -6,7 +6,7 @@ The regex filter is a filter module for MaxScale that is able to rewrite query c # Configuration -The configuration block for the Regex filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in $MAXSCALE_HOME/etc/MaxScale.cnf. +The configuration block for the Regex filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. ``` [MyRegexFilter] diff --git a/Documentation/filters/Tee-Filter.md b/Documentation/filters/Tee-Filter.md index a8a95a5cc..868886ceb 100644 --- a/Documentation/filters/Tee-Filter.md +++ b/Documentation/filters/Tee-Filter.md @@ -6,7 +6,7 @@ The tee filter is a filter module for MaxScale is a "plumbing" fitting in the Ma # Configuration -The configuration block for the TEE filter requires the minimal filter parameters in it’s section within the MaxScale.cnf file, stored in $MAXSCALE_HOME/etc/MaxScale.cnf, that defines the filter to load and the service to send the duplicates to. Currently the tee filter does not support multi-statements. +The configuration block for the TEE filter requires the minimal filter parameters in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf, that defines the filter to load and the service to send the duplicates to. Currently the tee filter does not support multi-statements. ``` [DataMartFilter] diff --git a/Documentation/filters/Top-N-Filter.md b/Documentation/filters/Top-N-Filter.md index d51ca43db..f52a6bedd 100644 --- a/Documentation/filters/Top-N-Filter.md +++ b/Documentation/filters/Top-N-Filter.md @@ -6,7 +6,7 @@ The top filter is a filter module for MaxScale that monitors every SQL statement # Configuration -The configuration block for the TOP filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in $MAXSCALE_HOME/etc/MaxScale.cnf. +The configuration block for the TOP filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. ``` [MyLogFilter] diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index 821b2c474..1425b4d49 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -3,18 +3,12 @@ # STANDALONE - Installs to /usr/local/mariadb-maxscale # RPM - Installs to /usr # DEB - Installs to /usr -if(${TYPE} MATCHES "STANDALONE") +include(GNUInstallDirs) - set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") - - # RPM and DEB are the same until differences are found -else() - set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") -endif() - -set(MAXSCALE_LIBDIR lib64/maxscale CACHE PATH "Library installation path") -set(MAXSCALE_BINDIR bin CACHE PATH "Executable installation path") -set(MAXSCALE_SHAREDIR share/maxscale CACHE PATH "Share file installation path, includes licence and readme files") -set(MAXSCALE_DOCDIR share/doc/maxscale CACHE PATH "Documentation installation path, text versions only") -set(MAXSCALE_CONFDIR etc CACHE PATH "Configuration file installation path, this is not usually needed") +set(MAXSCALE_LIBDIR ${CMAKE_INSTALL_LIBDIR}/maxscale CACHE PATH "Library installation path") +set(MAXSCALE_BINDIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Executable installation path") +set(MAXSCALE_SHAREDIR ${CMAKE_INSTALL_DATADIR}/maxscale CACHE PATH "Share file installation path, includes licence and readme files") +set(MAXSCALE_DOCDIR ${CMAKE_INSTALL_DOCDIR}/maxscale CACHE PATH "Documentation installation path, text versions only") +set(MAXSCALE_CONFDIR ${CMAKE_INSTALL_SYSCONFDIR} CACHE PATH "Configuration file installation path, this is not usually needed") +set(MAXSCALE_VARDIR /var CACHE PATH "Data file path (usually /var/)") diff --git a/server/core/gateway.c b/server/core/gateway.c index a4901ec23..f24cf8a11 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -505,15 +505,9 @@ static bool resolve_maxscale_conf_fname( * directory. * '-f MaxScale.cnf' */ - home_etc_dir = (char*)malloc(strlen(home_dir)+strlen("/etc")+1); - snprintf(home_etc_dir, - strlen(home_dir)+strlen("/etc")+1, - "%s/etc", - home_dir); *cnf_full_path = get_expanded_pathname(NULL, - home_etc_dir, + home_dir, cnf_file_arg); - free(home_etc_dir); if (*cnf_full_path != NULL) { @@ -1026,11 +1020,11 @@ static void usage(void) " -L|--logdir=... path to log file directory\n" " (default: /var/log/maxscale)\n" " -D|--datadir=... path to data directory\n" - " (default: /var/lib/maxscale)\n" + " (default: /usr/lib64/maxscale)\n" " -C|--configdir=... path to configuration file directory\n" " (default: /etc/)\n" - " -B|--libdir=... path to module directory\n" - " (default: /var/lib/maxscale)\n" + " -B|--libdir=... path to module directory\n" + " (default: /usr/lib64/maxscale)\n" " -A|--cachedir=... path to cache directory\n" " (default: /var/cache/maxscale)\n" " -s|--syslog= log messages to syslog.\n" @@ -1588,63 +1582,6 @@ int main(int argc, char **argv) sprintf(mysql_home, "%s/mysql", cachedir); setenv("MYSQL_HOME", mysql_home, 1); - /*< - * If MaxScale home directory wasn't set by command-line argument. - * Next, resolve it from environment variable and further on, - * try to use default. - */ -/* - - if (home_dir == NULL) - { - if (!resolve_maxscale_homedir(&home_dir)) - { - ss_dassert(home_dir != NULL); - rc = MAXSCALE_HOMELESS; - goto return_main; - } - - } - else - { - char* log_context = strdup("Home directory command-line argument"); - char* errstr; - - errstr = check_dir_access(home_dir,true,true); - - if (errstr != NULL) - { - char* logstr = (char*)malloc(strlen(log_context)+ - 1+ - strlen(errstr)+ - 1); - - snprintf(logstr, - strlen(log_context)+ - 1+ - strlen(errstr)+1, - "%s: %s", - log_context, - errstr); - - print_log_n_stderr(true, true, logstr, logstr, 0); - - free(errstr); - free(logstr); - rc = MAXSCALE_HOMELESS; - goto return_main; - } - else if (!daemon_mode) - { - fprintf(stderr, - "Using %s as MAXSCALE_HOME = %s\n", - log_context, - home_dir); - } - free(log_context); - } -*/ - /** * Resolve the full pathname for configuration file and check for * read accessibility. diff --git a/server/include/gw.h b/server/include/gw.h index e210fcfa2..b51bb0e2a 100644 --- a/server/include/gw.h +++ b/server/include/gw.h @@ -1,5 +1,25 @@ #ifndef _GW_HG #define _GW_HG + +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + + #include #include #include @@ -19,17 +39,7 @@ #include #include #include - -/** Default file locations */ -static const char* default_cnf_fname = "MaxScale.cnf"; -static const char* default_configdir = "/etc/"; -static const char* default_logdir = "/var/log/maxscale/"; -static const char* default_datadir = "/var/cache/maxscale/"; -static const char* default_libdir = "/lib64/maxscale/"; -static const char* default_cachedir = "/var/cache/maxscale/"; -static const char* default_langdir = "/usr/share/mysql/english/"; /*< This is where the MariaDB - * server installs errmsg.sys */ -static const char* default_piddir = "/var/run/maxscale/"; +#include #define EXIT_FAILURE 1 diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in new file mode 100644 index 000000000..3bc6c3f67 --- /dev/null +++ b/server/include/gwdirs.h.in @@ -0,0 +1,33 @@ +#ifndef _GW_DIRS_HG +#define _GW_DIRS_HG + +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2015 + */ + +/** Default file locations, configured by CMake */ +static const char* default_cnf_fname = "MaxScale.cnf"; +static const char* default_configdir = "/etc/"; +static const char* default_piddir = "/var/run/maxscale/"; +static const char* default_logdir = "/var/log/maxscale/"; +static const char* default_datadir = "/var/cache/maxscale/"; +static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@"; +static const char* default_cachedir = "/var/cache/maxscale/"; +static const char* default_langdir = "/usr/share/mysql/english/"; /*< This is where the MariaDB + * server installs errmsg.sys */ + +#endif From 9eeec2e9d3f131b038c2977f24c8d39f199c71aa Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 26 Apr 2015 11:41:02 +0300 Subject: [PATCH 012/273] Added FindJemalloc.cmake --- CMakeLists.txt | 2 +- cmake/FindJemalloc.cmake | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 cmake/FindJemalloc.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 564cb43a5..34d2767ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ find_package(MySQLClient) find_package(MySQL) find_package(Pandoc) find_package(TCMalloc) - +find_package(Jemalloc) # You can find the variables set by this in the FindCURL.cmake file # which is a default module in CMake. find_package(CURL) diff --git a/cmake/FindJemalloc.cmake b/cmake/FindJemalloc.cmake new file mode 100644 index 000000000..3bea2d5a0 --- /dev/null +++ b/cmake/FindJemalloc.cmake @@ -0,0 +1,11 @@ +# this CMake file defines the following variables +# JEMALLOC_FOUND - Jemalloc was found +# JEMALLOC_LIBRARIES - Jemalloc library +find_library(JEMALLOC_LIBRARIES NAMES jemalloc libjemalloc.so.4 libjemalloc.so.4.2.2) +if(JEMALLOC_LIBRARIES) + set(JEMALLOC_FOUND TRUE CACHE INTERNAL "") + message(STATUS "Found libjemalloc: ${JEMALLOC_LIBRARIES}") +else() + set(JEMALLOC_FOUND FALSE CACHE INTERNAL "") + message(STATUS "Could not find libjemalloc, using system default malloc instead.") +endif() From 49907ffdea3f5caf62bd6dddcf6d1fb87de81e7b Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 05:57:16 +0300 Subject: [PATCH 013/273] Added optional jemalloc linkage. --- server/core/CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index ead8560d3..36d87865f 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,7 +1,9 @@ if(BUILD_TESTS OR BUILD_TOOLS) file(GLOB FULLCORE_SRC *.c) add_library(fullcore STATIC ${FULLCORE_SRC}) - if(WITH_TCMALLOC) + if(WITH_JEMALLOC) + target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) + elseif(WITH_TCMALLOC) target_link_libraries(fullcore ${TCMALLOC_LIBRARIES}) endif() target_link_libraries(fullcore ${CURL_LIBRARIES} log_manager utils pthread ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ssl aio rt crypt dl crypto inih z m stdc++) @@ -13,7 +15,9 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c monitor.c adminusers.c secrets.c filter.c modutil.c hint.c housekeeper.c memlog.c resultset.c) -if(WITH_TCMALLOC) +if(WITH_JEMALLOC) + target_link_libraries(maxscale ${JEMALLOC_LIBRARIES}) +elseif(WITH_TCMALLOC) target_link_libraries(maxscale ${TCMALLOC_LIBRARIES}) endif() From 41ddc6fbfda25356be73aa9050e9176544ca7117 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 09:43:22 +0300 Subject: [PATCH 014/273] Switched over to gwbuf_clone_portion in modutils_get_complete_packets. Conflicts: server/core/modutil.c --- server/core/modutil.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/core/modutil.c b/server/core/modutil.c index 82890e334..c01c7623a 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -538,7 +538,7 @@ return_packetbuf: GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf) { GWBUF *buff = NULL, *packet; - uint8_t *ptr,*end; + uint8_t *ptr; int len,blen,total = 0; if(p_readbuf == NULL || (*p_readbuf) == NULL || @@ -583,7 +583,6 @@ GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf) "Error: Failed to partially clone buffer."); return NULL; } - gwbuf_consume(packet,total); return buff; } @@ -841,4 +840,4 @@ int modutil_count_statements(GWBUF* buffer) } return num; -} \ No newline at end of file +} From d5682bf7c089a486bcc18cb2f04c79331182fe58 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 10:10:55 +0300 Subject: [PATCH 015/273] Removed file globbing in the core CMake file and used explicit names instead. --- server/core/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 36d87865f..218d2b0c4 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,6 +1,5 @@ if(BUILD_TESTS OR BUILD_TOOLS) - file(GLOB FULLCORE_SRC *.c) - add_library(fullcore STATIC ${FULLCORE_SRC}) + add_library(fullcore STATIC adminusers.c atomic.c config.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.x modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) From 97653e98faa1cb73113354a7bfad0e5de712f188 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 10:14:47 +0300 Subject: [PATCH 016/273] Fixed a typo in the core CMakeLists.txt --- server/core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 218d2b0c4..25bf74c35 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,5 +1,5 @@ if(BUILD_TESTS OR BUILD_TOOLS) - add_library(fullcore STATIC adminusers.c atomic.c config.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.x modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c) + add_library(fullcore STATIC adminusers.c atomic.c config.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) From 2fce1144830a06c474c3a6a19b4a73c0849e0408 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 10:17:55 +0300 Subject: [PATCH 017/273] Added missing buffer.c from fullcore static lib. --- server/core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 25bf74c35..52ebf2b1e 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,5 +1,5 @@ if(BUILD_TESTS OR BUILD_TOOLS) - add_library(fullcore STATIC adminusers.c atomic.c config.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c) + add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) From 0cfcad55dad5e03cb6ee4b62ab23c07368b5d3c3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 11:06:11 +0300 Subject: [PATCH 018/273] Moved get_libdir function to its own file. --- server/core/CMakeLists.txt | 4 ++-- server/core/gateway.c | 15 ++------------- server/core/gwdirs.c | 10 ++++++++++ server/include/gwdirs.h.in | 10 ++++++++++ 4 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 server/core/gwdirs.c diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 52ebf2b1e..c952f5a1a 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,5 +1,5 @@ if(BUILD_TESTS OR BUILD_TOOLS) - add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c) + add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) @@ -12,7 +12,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c monitor.c adminusers.c secrets.c filter.c modutil.c hint.c - housekeeper.c memlog.c resultset.c) + housekeeper.c memlog.c resultset.c gwdirs.c) if(WITH_JEMALLOC) target_link_libraries(maxscale ${JEMALLOC_LIBRARIES}) diff --git a/server/core/gateway.c b/server/core/gateway.c index f24cf8a11..77afe994b 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -129,11 +129,7 @@ static bool datadir_defined = false; /*< If the datadir was already set */ /* The data directory we created for this gateway instance */ static char pidfile[PATH_MAX+1] = ""; -static char* configdir = NULL; -static char* logdir = NULL; -static char* libdir = NULL; -static char* cachedir = NULL; -static char* langdir = NULL; + /** * exit flag for log flusher. */ @@ -200,14 +196,7 @@ static bool resolve_maxscale_homedir( static char* check_dir_access(char* dirname,bool,bool); -/** - * Get the directory with all the modules. - * @return The module directory - */ -char* get_libdir() -{ - return libdir; -} + /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. diff --git a/server/core/gwdirs.c b/server/core/gwdirs.c new file mode 100644 index 000000000..af7edd9de --- /dev/null +++ b/server/core/gwdirs.c @@ -0,0 +1,10 @@ +#include + +/** + * Get the directory with all the modules. + * @return The module directory + */ +char* get_libdir() +{ + return libdir?libdir:(char*)default_libdir; +} diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index 3bc6c3f67..40bf3ba26 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -19,6 +19,8 @@ * Copyright MariaDB Corporation Ab 2015 */ +#include + /** Default file locations, configured by CMake */ static const char* default_cnf_fname = "MaxScale.cnf"; static const char* default_configdir = "/etc/"; @@ -30,4 +32,12 @@ static const char* default_cachedir = "/var/cache/maxscale/"; static const char* default_langdir = "/usr/share/mysql/english/"; /*< This is where the MariaDB * server installs errmsg.sys */ +static char* configdir = NULL; +static char* logdir = NULL; +static char* libdir = NULL; +static char* cachedir = NULL; +static char* langdir = NULL; + +char* get_libdir(); + #endif From bbcecc0db53b54087ad242405fb587c807ccf9a9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 13:10:06 +0300 Subject: [PATCH 019/273] Changed all MaxScale.cnf code references to maxscale.cnf --- CMakeLists.txt | 8 ++-- cmake/testall.cmake | 2 +- server/core/gateway.c | 42 +++++++++---------- server/core/gwdirs.c | 9 ++++ server/core/service.c | 15 +++---- server/include/gwdirs.h.in | 2 +- ...cnf => maxscale_binlogserver_template.cnf} | 0 ...ale_template.cnf => maxscale_template.cnf} | 0 server/modules/filter/test/tee_recursion.sh | 4 +- .../{MaxScale_test.cnf => maxscale_test.cnf} | 4 ++ 10 files changed, 46 insertions(+), 40 deletions(-) rename server/{MaxScale_BinlogServer_template.cnf => maxscale_binlogserver_template.cnf} (100%) rename server/{MaxScale_template.cnf => maxscale_template.cnf} (100%) rename server/test/{MaxScale_test.cnf => maxscale_test.cnf} (92%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34d2767ea..550724505 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ configure_file(${CMAKE_SOURCE_DIR}/server/include/gwdirs.h.in ${CMAKE_BINARY_DIR configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.h.in ${CMAKE_BINARY_DIR}/server/include/maxscale_test.h) configure_file(${CMAKE_SOURCE_DIR}/etc/postinst.in ${CMAKE_BINARY_DIR}/postinst) configure_file(${CMAKE_SOURCE_DIR}/etc/postrm.in ${CMAKE_BINARY_DIR}/postrm) +configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.cnf ${CMAKE_BINARY_DIR}/maxscale.cnf) set(FLAGS "-Wall -Wno-unused-variable -Wno-unused-function -fPIC" CACHE STRING "Compilation flags") set(DEBUG_FLAGS "-ggdb -pthread -pipe -Wformat -fstack-protector --param=ssp-buffer-size=4" CACHE STRING "Debug compilation flags") @@ -152,9 +153,9 @@ ${CMAKE_SOURCE_DIR}/Documentation/Upgrading-To-MaxScale-1.1.0.md install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHAREDIR}) -install(FILES server/MaxScale_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) -install(FILES server/MaxScale_BinlogServer_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) -install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES server/maxscale_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES server/maxscale_binlogserver_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES ${ERRMSG} DESTINATION /usr/share/mysql/english/) install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHAREDIR}) @@ -245,7 +246,6 @@ add_custom_target(buildtestsx add_custom_target(testall COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} COMMAND make install - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/server/test/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/testall.cmake COMMENT "Running full test suite..." VERBATIM) diff --git a/cmake/testall.cmake b/cmake/testall.cmake index fedbdf273..1a438bc1a 100644 --- a/cmake/testall.cmake +++ b/cmake/testall.cmake @@ -1,4 +1,4 @@ -execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -c ${CMAKE_BINARY_DIR} &>/dev/null 2> /dev/null > /dev/null") +execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -f ${CMAKE_BINARY_DIR}/etc/maxscale.cnf &>/dev/null 2> /dev/null > /dev/null") execute_process(COMMAND make test RESULT_VARIABLE RVAL) execute_process(COMMAND killall maxscale) if(NOT RVAL EQUAL 0) diff --git a/server/core/gateway.c b/server/core/gateway.c index 77afe994b..ae6f1c5c0 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -999,29 +999,25 @@ return_cnf_file_buf: static void usage(void) { fprintf(stderr, - "\nUsage : %s [-h] | [-d] [-c ] [-f ]\n\n" - " -d|--nodaemon enable running in terminal process (default:disabled)\n" - " -c|--homedir=... relative|absolute MaxScale home directory\n" - " -f|--config=... relative|absolute pathname of MaxScale configuration file\n" - " (default: $MAXSCALE_HOME/etc/MaxScale.cnf)\n" - " -l|--log=... log to file or shared memory\n" - " -lfile or -lshm - defaults to shared memory\n" - " -L|--logdir=... path to log file directory\n" - " (default: /var/log/maxscale)\n" - " -D|--datadir=... path to data directory\n" - " (default: /usr/lib64/maxscale)\n" - " -C|--configdir=... path to configuration file directory\n" - " (default: /etc/)\n" - " -B|--libdir=... path to module directory\n" - " (default: /usr/lib64/maxscale)\n" - " -A|--cachedir=... path to cache directory\n" - " (default: /var/cache/maxscale)\n" - " -s|--syslog= log messages to syslog.\n" - " True or false - defaults to true\n" - " -S|--maxscalelog= log messages to MaxScale log.\n" - " True or false - defaults to true\n" - " -v|--version print version info and exit\n" - " -?|--help show this help\n" + "\nUsage : %s [OPTION]...\n\n" + " -d, --nodaemon enable running in terminal process (default:disabled)\n" + " -f, --config=FILE relative|absolute pathname of MaxScale configuration file\n" + " (default:/etc/maxscale.cnf)\n" + " -l, --log=[file|shm] log to file or shared memory (default: shm)\n" + " -L, --logdir=PATH path to log file directory\n" + " (default: /var/log/maxscale)\n" + " -D, --datadir=PATH path to data directory\n" + " (default: /usr/lib64/maxscale)\n" + " -C, --configdir=PATH path to configuration file directory\n" + " (default: /etc/)\n" + " -B, --libdir=PATH path to module directory\n" + " (default: /usr/lib64/maxscale)\n" + " -A, --cachedir=PATH path to cache directory\n" + " (default: /var/cache/maxscale)\n" + " -s, --syslog=[yes|no] log messages to syslog (default:yes)\n" + " -S, --maxscalelog=[yes|no] log messages to MaxScale log (default: yes)\n" + " -v, --version print version info and exit\n" + " -?, --help show this help\n" , progname); } diff --git a/server/core/gwdirs.c b/server/core/gwdirs.c index af7edd9de..9bf0185cd 100644 --- a/server/core/gwdirs.c +++ b/server/core/gwdirs.c @@ -8,3 +8,12 @@ char* get_libdir() { return libdir?libdir:(char*)default_libdir; } + +/** + * Get the service cache directory + * @return The path to the cache directory + */ +char* get_cachedir() +{ + return cachedir?cachedir:(char*)default_cachedir; +} diff --git a/server/core/service.c b/server/core/service.c index bd22117ec..d529cc54a 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -62,6 +62,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -259,15 +260,11 @@ GWPROTOCOL *funcs; else { /* Save authentication data to file cache */ - char *ptr, path[4097]; + char *ptr, path[PATH_MAX + 1]; int mkdir_rval = 0; - strcpy(path, "/usr/local/mariadb-maxscale"); - if ((ptr = getenv("MAXSCALE_HOME")) != NULL) - { - strncpy(path, ptr, 4096); - } + strcpy(path, get_cachedir()); strncat(path, "/", 4096); - strncat(path, service->name, 4096); + strncat(path, service->name, PATH_MAX); if (access(path, R_OK) == -1) { mkdir_rval = mkdir(path, 0777); @@ -282,7 +279,7 @@ GWPROTOCOL *funcs; mkdir_rval = 0; } - strncat(path, "/.cache", 4096); + strncat(path, "/.cache", PATH_MAX); if (access(path, R_OK) == -1) { mkdir_rval = mkdir(path, 0777); @@ -296,7 +293,7 @@ GWPROTOCOL *funcs; strerror(errno)); mkdir_rval = 0; } - strncat(path, "/dbusers", 4096); + strncat(path, "/dbusers", PATH_MAX); dbusers_save(service->users, path); } if (loaded == 0) diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index 40bf3ba26..e62696aab 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -22,7 +22,7 @@ #include /** Default file locations, configured by CMake */ -static const char* default_cnf_fname = "MaxScale.cnf"; +static const char* default_cnf_fname = "maxscale.cnf"; static const char* default_configdir = "/etc/"; static const char* default_piddir = "/var/run/maxscale/"; static const char* default_logdir = "/var/log/maxscale/"; diff --git a/server/MaxScale_BinlogServer_template.cnf b/server/maxscale_binlogserver_template.cnf similarity index 100% rename from server/MaxScale_BinlogServer_template.cnf rename to server/maxscale_binlogserver_template.cnf diff --git a/server/MaxScale_template.cnf b/server/maxscale_template.cnf similarity index 100% rename from server/MaxScale_template.cnf rename to server/maxscale_template.cnf diff --git a/server/modules/filter/test/tee_recursion.sh b/server/modules/filter/test/tee_recursion.sh index 015bb9a8e..82a8080e2 100755 --- a/server/modules/filter/test/tee_recursion.sh +++ b/server/modules/filter/test/tee_recursion.sh @@ -49,8 +49,8 @@ USER=$3 PWD=$4 HOST=$5 PORT=$6 -CONF=$BINDIR/etc/MaxScale.cnf -OLDCONF=$BINDIR/etc/MaxScale.cnf.old +CONF=$BINDIR/etc/maxscale.cnf +OLDCONF=$BINDIR/etc/maxscale.cnf.old MAXPID=$BINDIR/log/$(ls -1 $BINDIR/log|grep maxscale) TEST1=$SRCDIR/server/modules/filter/test/tee_recursion1.cnf TEST2=$SRCDIR/server/modules/filter/test/tee_recursion2.cnf diff --git a/server/test/MaxScale_test.cnf b/server/test/maxscale_test.cnf similarity index 92% rename from server/test/MaxScale_test.cnf rename to server/test/maxscale_test.cnf index 06b783ca5..2fd6ad0d8 100644 --- a/server/test/MaxScale_test.cnf +++ b/server/test/maxscale_test.cnf @@ -1,5 +1,9 @@ [maxscale] threads=4 +libdir=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@ +logdir=@CMAKE_INSTALL_PREFIX@/log +datadir=@CMAKE_INSTALL_PREFIX@/data +cachedir=@CMAKE_INSTALL_PREFIX@/cache [feedback] feedback_enable=true From 55249193a9f9687f3e63b8e2fbc766b47c08938a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 14:30:47 +0300 Subject: [PATCH 020/273] Updated post-install scripts and binlogrouter Binlogrouter: Switched hard-coded maximum path values to PATH_MAX and used cache directory instead of MAXSCALE_HOME Scripts: Updated with new executable file paths --- etc/init.d/maxscale.in | 13 ++++---- etc/postinst.in | 1 + etc/ubuntu/init.d/maxscale.in | 9 +++-- server/core/gateway.c | 4 +-- server/modules/routing/binlog/blr_file.c | 42 +++++++++--------------- 5 files changed, 28 insertions(+), 41 deletions(-) diff --git a/etc/init.d/maxscale.in b/etc/init.d/maxscale.in index 88f026e18..c8822109c 100755 --- a/etc/init.d/maxscale.in +++ b/etc/init.d/maxscale.in @@ -18,12 +18,11 @@ ### END INIT INFO ############################################# -# MaxScale HOME, PIDFILE, LIB +# MaxScale PIDFILE and LIB ############################################# -export MAXSCALE_HOME=@CMAKE_INSTALL_PREFIX@ -export MAXSCALE_PIDFILE=$MAXSCALE_HOME/log/maxscale.pid -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$MAXSCALE_HOME/lib +export MAXSCALE_PIDFILE=/var/run/maxscale/maxscale.pid +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@/maxscale ############################### # LSB Exit codes (non-Status) @@ -56,7 +55,7 @@ start() { CHECK_RET=$? [ $CHECK_RET -eq 0 ] && echo -n " found $my_check" && success && CHECK_RET=0 - daemon --pidfile $MAXSCALE_PIDFILE $MAXSCALE_HOME/bin/maxscale >& /dev/null + daemon --pidfile $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale >& /dev/null RETVAL=$? [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$servicename @@ -68,7 +67,7 @@ start() { [ $CHECK_RET -eq 0 ] && echo -n $my_check && success || failure fi - # Return rigth code + # Return right code if [ $RETVAL -ne 0 ]; then failure RETVAL=$_RETVAL_NOT_RUNNING @@ -100,7 +99,7 @@ stop() { reload() { echo -n $"Reloading MaxScale: " - killproc -p $MAXSCALE_PIDFILE $MAXSCALE_HOME/bin/maxscale -HUP + killproc -p $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale -HUP RETVAL=$? echo } diff --git a/etc/postinst.in b/etc/postinst.in index c04850019..464e240f1 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -3,6 +3,7 @@ mkdir -p /var/log/maxscale mkdir -p /var/cache/maxscale mkdir -p /var/run/maxscale +mkdir -p @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/ cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/maxscale /etc/init.d/ cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/maxscale.conf /etc/ld.so.conf.d/ /sbin/ldconfig diff --git a/etc/ubuntu/init.d/maxscale.in b/etc/ubuntu/init.d/maxscale.in index caf7d1408..9b8ff84e9 100755 --- a/etc/ubuntu/init.d/maxscale.in +++ b/etc/ubuntu/init.d/maxscale.in @@ -21,9 +21,8 @@ # MaxScale HOME, PIDFILE, LIB ############################################# -export MAXSCALE_HOME=@CMAKE_INSTALL_PREFIX@ -export MAXSCALE_PIDFILE=$MAXSCALE_HOME/log/maxscale.pid -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$MAXSCALE_HOME/lib +export MAXSCALE_PIDFILE=/var/run/maxscale/maxscale.pid +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@/maxscale ############################### # LSB Exit codes (non-Status) @@ -45,7 +44,7 @@ _RETVAL_STATUS_NOT_RUNNING=3 # stop/start/status related vars ################################# NAME=maxscale -DAEMON=$MAXSCALE_HOME/bin/maxscale +DAEMON=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale # Source function library. . /lib/lsb/init-functions @@ -82,7 +81,7 @@ reload() { } maxscale_wait_stop() { - PIDTMP=$(pidofproc -p $MAXSCALE_PIDFILE $MAXSCALE_HOME/bin/maxscale) + PIDTMP=$(pidofproc -p $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale) kill -TERM "${PIDTMP:-}" 2> /dev/null; if [ -n "${PIDTMP:-}" ] && kill -0 "${PIDTMP:-}" 2> /dev/null; then local i=0 diff --git a/server/core/gateway.c b/server/core/gateway.c index ae6f1c5c0..a5ad3e2fe 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1006,8 +1006,8 @@ static void usage(void) " -l, --log=[file|shm] log to file or shared memory (default: shm)\n" " -L, --logdir=PATH path to log file directory\n" " (default: /var/log/maxscale)\n" - " -D, --datadir=PATH path to data directory\n" - " (default: /usr/lib64/maxscale)\n" + " -D, --datadir=PATH path to data directory, stored embedded mysql tables\n" + " (default: /var/cache/maxscale)\n" " -C, --configdir=PATH path to configuration file directory\n" " (default: /etc/)\n" " -B, --libdir=PATH path to module directory\n" diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index b8e201227..32fbad28c 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -71,7 +71,7 @@ static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr); int blr_file_init(ROUTER_INSTANCE *router) { -char *ptr, path[PATH_MAX], filename[PATH_MAX]; +char *ptr, path[PATH_MAX+1], filename[PATH_MAX+1]; int file_found, n = 1; int root_len, i; DIR *dirp; @@ -79,12 +79,8 @@ struct dirent *dp; if (router->binlogdir == NULL) { - strcpy(path, "/usr/local/mariadb-maxscale"); - if ((ptr = getenv("MAXSCALE_HOME")) != NULL) - { - strncpy(path, ptr,PATH_MAX); - } - strncat(path, "/",PATH_MAX); + strcpy(path, get_cachedir()); + strncat(path,"/",PATH_MAX); strncat(path, router->service->name,PATH_MAX); if (access(path, R_OK) == -1) @@ -659,24 +655,20 @@ struct stat statb; void blr_cache_response(ROUTER_INSTANCE *router, char *response, GWBUF *buf) { -char path[4097], *ptr; +char path[PATH_MAX+1], *ptr; int fd; - strcpy(path, "/usr/local/mariadb-maxscale"); - if ((ptr = getenv("MAXSCALE_HOME")) != NULL) - { - strncpy(path, ptr, 4096); - } - strncat(path, "/", 4096); - strncat(path, router->service->name, 4096); + strcpy(path,get_cachedir()); + strncat(path,"/",PATH_MAX); + strncat(path, router->service->name, PATH_MAX); if (access(path, R_OK) == -1) mkdir(path, 0777); - strncat(path, "/.cache", 4096); + strncat(path, "/.cache", PATH_MAX); if (access(path, R_OK) == -1) mkdir(path, 0777); strncat(path, "/", 4096); - strncat(path, response, 4096); + strncat(path, response, PATH_MAX); if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) return; @@ -697,19 +689,15 @@ GWBUF * blr_cache_read_response(ROUTER_INSTANCE *router, char *response) { struct stat statb; -char path[4097], *ptr; +char path[PATH_MAX+1], *ptr; int fd; GWBUF *buf; - strcpy(path, "/usr/local/mariadb-maxscale"); - if ((ptr = getenv("MAXSCALE_HOME")) != NULL) - { - strncpy(path, ptr, 4096); - } - strncat(path, "/", 4096); - strncat(path, router->service->name, 4096); - strncat(path, "/.cache/", 4096); - strncat(path, response, 4096); + strcpy(path, get_cachedir()); + strncat(path, "/", PATH_MAX); + strncat(path, router->service->name, PATH_MAX); + strncat(path, "/.cache/", PATH_MAX); + strncat(path, response, PATH_MAX); if ((fd = open(path, O_RDONLY)) == -1) return NULL; From 1cc6ced505c47e18757d09388b5923b4a2677abc Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 15:06:45 +0300 Subject: [PATCH 021/273] Updated old references to MAXSCALE_HOME --- server/core/adminusers.c | 26 +++++--------------------- server/core/load_utils.c | 11 ----------- server/core/maxkeys.c | 24 +++++++++++++----------- server/include/modules.h | 1 - 4 files changed, 18 insertions(+), 44 deletions(-) diff --git a/server/core/adminusers.c b/server/core/adminusers.c index 64ea5f224..e20f9d1c3 100644 --- a/server/core/adminusers.c +++ b/server/core/adminusers.c @@ -26,6 +26,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -119,12 +120,7 @@ char fname[1024], *home; char uname[80], passwd[80]; initialise(); - if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){ - sprintf(fname, "%s/etc/passwd", home); - } - else{ - sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd"); - } + sprintf(fname, "%s/passwd", get_cachedir()); if ((fp = fopen(fname, "r")) == NULL) return NULL; if ((rval = users_alloc()) == NULL) @@ -155,12 +151,7 @@ FILE *fp; char fname[1024], *home, *cpasswd; initialise(); - if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){ - sprintf(fname, "%s/etc/passwd", home); - } - else{ - sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd"); - } + sprintf(fname, "%s/passwd", get_cachedir()); if (users == NULL) { @@ -253,15 +244,8 @@ char* admin_remove_user( /** * Open passwd file and remove user from the file. */ - if ((home = getenv("MAXSCALE_HOME")) != NULL && - strnlen(home,PATH_MAX) < PATH_MAX && - strnlen(home,PATH_MAX) > 0) { - sprintf(fname, "%s/etc/passwd", home); - sprintf(fname_tmp, "%s/etc/passwd_tmp", home); - } else { - sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd"); - sprintf(fname_tmp, "/usr/local/mariadb-maxscale/etc/passwd_tmp"); - } + sprintf(fname, "%s/passwd", get_cachedir()); + sprintf(fname_tmp, "%s/passwd_tmp", get_cachedir()); /** * Rewrite passwd file from memory. */ diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 9451f6838..7e4a10c37 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -105,17 +105,6 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) return realsize; } -char* get_maxscale_home(void) -{ - char* home = getenv("MAXSCALE_HOME"); - if (home == NULL) - { - home = "/usr/local/mariadb-maxscale"; - } - return home; -} - - /** * Load the dynamic library related to a gateway module. The routine * will look for library files in the current directory, diff --git a/server/core/maxkeys.c b/server/core/maxkeys.c index 0d5c938ff..6cb4501ec 100644 --- a/server/core/maxkeys.c +++ b/server/core/maxkeys.c @@ -31,6 +31,7 @@ #include #include #include +#include int main(int argc, char **argv) { int arg_count = 3; @@ -52,19 +53,20 @@ int main(int argc, char **argv) return 1; } + if(access("/var/log/maxscale/maxkeys/",F_OK) != 0) + { + if(mkdir("/var/log/maxscale/maxkeys/",0777) == -1) + { + if(errno != EEXIST) + { + fprintf(stderr,"Error: %d - %s",errno,strerror(errno)); + return 1; + } + } + } arg_vector[0] = strdup("logmanager"); arg_vector[1] = strdup("-j"); - - if ((home = getenv("MAXSCALE_HOME")) != NULL) - { - arg_vector[2] = (char*)malloc((strlen(home) + strlen("/log"))*sizeof(char)); - sprintf(arg_vector[2],"%s/log",home); - } - else - { - arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log"); - } - + arg_vector[2] = strdup("/var/log/maxscale/maxkeys"); arg_vector[3] = NULL; skygw_logmanager_init(arg_count,arg_vector); skygw_log_enable(LOGFILE_TRACE); diff --git a/server/include/modules.h b/server/include/modules.h index 51e10b29d..96d322402 100644 --- a/server/include/modules.h +++ b/server/include/modules.h @@ -68,7 +68,6 @@ extern void unload_all_modules(); extern void printModules(); extern void dprintAllModules(DCB *); extern RESULTSET *moduleGetList(); -extern char *get_maxscale_home(void); extern void module_feedback_send(void*); extern void moduleShowFeedbackReport(DCB *dcb); From 9e0a2bfc12f5d962b16f28b023de697f090ef0e7 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 21:38:49 +0300 Subject: [PATCH 022/273] Changed relevant files to use get_cachedir() instead of using the now deprecated MAXSCALE_HOME. --- etc/init.d/maxscale.in | 6 +- etc/ubuntu/init.d/maxscale.in | 2 +- server/core/CMakeLists.txt | 4 +- server/core/gateway.c | 206 +--------------------- server/core/load_utils.c | 3 +- server/core/maxpasswd.c | 11 +- server/core/secrets.c | 10 +- server/core/service.c | 6 +- server/core/test/CMakeLists.txt | 15 -- server/core/test/testadminusers.c | 9 +- server/core/test/testfeedback.c | 13 +- server/include/gwdirs.h.in | 2 +- server/maxscale_binlogserver_template.cnf | 5 +- server/modules/filter/dbfwfilter.c | 12 +- server/modules/filter/test/CMakeLists.txt | 10 -- 15 files changed, 29 insertions(+), 285 deletions(-) diff --git a/etc/init.d/maxscale.in b/etc/init.d/maxscale.in index c8822109c..4ee5ce0f4 100755 --- a/etc/init.d/maxscale.in +++ b/etc/init.d/maxscale.in @@ -38,7 +38,7 @@ _RETVAL_STATUS_OK=0 _RETVAL_STATUS_NOT_RUNNING=3 # Sanity checks. -[ -x $MAXSCALE_HOME/bin/maxscale ] || exit $_RETVAL_NOT_INSTALLED +[ -x @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale ] || exit $_RETVAL_NOT_INSTALLED # Source function library. . /etc/rc.d/init.d/functions @@ -51,7 +51,7 @@ RETVAL=0 start() { echo -n $"Starting MaxScale: " - my_check=`status -p $MAXSCALE_PIDFILE $MAXSCALE_HOME/bin/maxscale` + my_check=`status -p $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale` CHECK_RET=$? [ $CHECK_RET -eq 0 ] && echo -n " found $my_check" && success && CHECK_RET=0 @@ -62,7 +62,7 @@ start() { if [ $CHECK_RET -ne 0 ]; then sleep 2 - my_check=`status -p $MAXSCALE_PIDFILE $MAXSCALE_HOME/bin/maxscale` + my_check=`status -p $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale` CHECK_RET=$? [ $CHECK_RET -eq 0 ] && echo -n $my_check && success || failure fi diff --git a/etc/ubuntu/init.d/maxscale.in b/etc/ubuntu/init.d/maxscale.in index 9b8ff84e9..27a73092c 100755 --- a/etc/ubuntu/init.d/maxscale.in +++ b/etc/ubuntu/init.d/maxscale.in @@ -38,7 +38,7 @@ _RETVAL_STATUS_OK=0 _RETVAL_STATUS_NOT_RUNNING=3 # Sanity checks. -[ -x $MAXSCALE_HOME/bin/maxscale ] || exit $_RETVAL_NOT_INSTALLED +[ -x @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale ] || exit $_RETVAL_NOT_INSTALLED ################################# # stop/start/status related vars diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index c952f5a1a..39983e6ff 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -23,11 +23,11 @@ endif() target_link_libraries(maxscale ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++) install(TARGETS maxscale DESTINATION ${MAXSCALE_BINDIR}) -add_executable(maxkeys maxkeys.c secrets.c utils.c) +add_executable(maxkeys maxkeys.c secrets.c utils.c gwdirs.c) target_link_libraries(maxkeys log_manager utils pthread crypt crypto) install(TARGETS maxkeys DESTINATION ${MAXSCALE_BINDIR}) -add_executable(maxpasswd maxpasswd.c secrets.c utils.c) +add_executable(maxpasswd maxpasswd.c secrets.c utils.c gwdirs.c) target_link_libraries(maxpasswd log_manager utils pthread crypt crypto) install(TARGETS maxpasswd DESTINATION ${MAXSCALE_BINDIR}) diff --git a/server/core/gateway.c b/server/core/gateway.c index a5ad3e2fe..cf0f41525 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -191,8 +191,6 @@ static bool resolve_maxscale_conf_fname( char** cnf_full_path, char* home_dir, char* cnf_file_arg); -static bool resolve_maxscale_homedir( - char** p_home_dir); static char* check_dir_access(char* dirname,bool,bool); @@ -575,149 +573,6 @@ return_succp: return succp; } - -static bool resolve_maxscale_homedir( - char** p_home_dir) -{ - bool succp = false; - char* tmp; - char* tmp2; - char* log_context = NULL; - - ss_dassert(*p_home_dir == NULL); - - if (*p_home_dir != NULL) - { - log_context = strdup("Command-line argument"); - tmp = NULL; - goto check_home_dir; - } - /*< - * 1. if home dir wasn't specified by a command-line argument, - * read env. variable MAXSCALE_HOME. - */ - if (getenv("MAXSCALE_HOME") != NULL) - { - tmp = strndup(getenv("MAXSCALE_HOME"), PATH_MAX); - get_expanded_pathname(p_home_dir, tmp, NULL); - - if (*p_home_dir != NULL) - { - log_context = strdup("MAXSCALE_HOME"); - goto check_home_dir; - } - free(tmp); - } - else - { - fprintf(stderr, "\n*\n* Warning : MAXSCALE_HOME environment variable " - "is not set.\n*\n"); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Warning : MAXSCALE_HOME environment " - "variable is not set."))); - } - /*< - * 2. if home dir wasn't specified in MAXSCALE_HOME, - * try access /etc/MaxScale/ - */ - tmp = strdup("/etc/MaxScale"); - get_expanded_pathname(p_home_dir, tmp, NULL); - - if (*p_home_dir != NULL) - { - log_context = strdup("/etc/MaxScale"); - goto check_home_dir; - } - free(tmp); - /*< - * 3. if /etc/MaxScale/MaxScale.cnf didn't exist or wasn't accessible, home - * isn't specified. Thus, try to access $PWD/MaxScale.cnf . - */ - char *pwd = getenv("PWD"); - tmp = strndup(pwd ? pwd : "PWD_NOT_SET", PATH_MAX); - tmp2 = get_expanded_pathname(p_home_dir, tmp, default_cnf_fname); - free(tmp2); /*< full path isn't needed so simply free it */ - - if (*p_home_dir != NULL) - { - log_context = strdup("Current working directory"); - } - -check_home_dir: - if (*p_home_dir != NULL) - { - if (!file_is_readable(*p_home_dir)) - { - char* tailstr = "MaxScale doesn't have read permission " - "to MAXSCALE_HOME."; - char* logstr = (char*)malloc(strlen(log_context)+ - 1+ - strlen(tailstr)+ - 1); - snprintf(logstr, - strlen(log_context)+ - 1+ - strlen(tailstr)+1, - "%s:%s", - log_context, - tailstr); - print_log_n_stderr(true, true, logstr, logstr, 0); - free(logstr); - goto return_succp; - } - -#if WRITABLE_HOME - if (!file_is_writable(*p_home_dir)) - { - char* tailstr = "MaxScale doesn't have write permission " - "to MAXSCALE_HOME. Exiting."; - char* logstr = (char*)malloc(strlen(log_context)+ - 1+ - strlen(tailstr)+ - 1); - snprintf(logstr, - strlen(log_context)+ - 1+ - strlen(tailstr)+1, - "%s:%s", - log_context, - tailstr); - print_log_n_stderr(true, true, logstr, logstr, 0); - free(logstr); - goto return_succp; - } -#endif - if (!daemon_mode) - { - fprintf(stderr, - "Using %s as MAXSCALE_HOME = %s\n", - log_context, - tmp); - } - succp = true; - goto return_succp; - } - -return_succp: - free (tmp); - - - if (log_context != NULL) - { - free(log_context); - } - - if (!succp) - { - char* logstr = "MaxScale was unable to locate home directory " - "with read and write permissions. \n*\n* Exiting."; - print_log_n_stderr(true, true, logstr, logstr, 0); - usage(); - } - return succp; -} - /** * Check read and write accessibility to a directory. * @param dirname directory to be checked @@ -1042,19 +897,13 @@ static void usage(void) * This is not obvious solution because stderr is often directed to somewhere, * but currently this is the case. * - * The configuration file is by default \/etc/MaxScale.cnf + * The configuration file is by default /etc/maxscale.cnf * The name of configuration file and its location can be specified by * command-line argument. * - * \ is resolved in the following order: - * 1. from '-c ' command-line argument - * 2. from MAXSCALE_HOME environment variable - * 3. /etc/ if MaxScale.cnf is found from there - * 4. current working directory if MaxScale.cnf is found from there - * * \ is resolved in the following order: * 1. from '-f \' command-line argument - * 2. by using default value "MaxScale.cnf" + * 2. by using default value "maxscale.cnf" * */ int main(int argc, char **argv) @@ -1131,56 +980,6 @@ int main(int argc, char **argv) /*< Debug mode, maxscale runs in this same process */ daemon_mode = false; break; - - case 'c': - /*< - * Create absolute path pointing to MaxScale home - * directory. User-provided home directory may be - * either absolute or relative. If latter, it is - * expanded and stored in home_dir if succeed. - */ - if (optarg[0] != '-') - { - struct stat sb; - - if (stat(optarg, &sb) != -1 - && (! S_ISDIR(sb.st_mode))) - { - char* logerr = "Home directory argument " - "identifier \'-c\' was specified but " - "the argument didn't specify a valid " - "a directory."; - print_log_n_stderr(true, true, logerr, logerr, 0); - usage(); - succp = false; - } - else - { - get_expanded_pathname(&home_dir, optarg, NULL); - } - } - - if (home_dir != NULL) - { - /*< - * MAXSCALE_HOME is set. - * It is used to assist in finding the modules - * to be loaded into MaxScale. - */ - setenv("MAXSCALE_HOME", home_dir, 1); - } - else - { - char* logerr = "Home directory argument " - "identifier \'-c\' was specified but " - "the argument didn't specify \n a valid " - "home directory or the argument was " - "missing."; - print_log_n_stderr(true, true, logerr, logerr, 0); - usage(); - succp = false; - } - break; case 'f': /*< @@ -1589,7 +1388,6 @@ int main(int argc, char **argv) sprintf(datadir,"%s",default_datadir); /** * Init Log Manager for MaxScale. - * If $MAXSCALE_HOME is set then write the logs into $MAXSCALE_HOME/log. * The skygw_logmanager_init expects to take arguments as passed to main * and proesses them with getopt, therefore we need to give it a dummy * argv[0] diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 7e4a10c37..5ecbe7f34 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -49,6 +49,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -108,7 +109,7 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) /** * Load the dynamic library related to a gateway module. The routine * will look for library files in the current directory, - * $MAXSCALE_HOME/modules and /usr/local/mariadb-maxscale/modules. + * the configured folder and /usr/lib64/maxscale. * * @param module Name of the module to load * @param type Type of module, used purely for registration diff --git a/server/core/maxpasswd.c b/server/core/maxpasswd.c index 4da1dbde1..159fbae49 100644 --- a/server/core/maxpasswd.c +++ b/server/core/maxpasswd.c @@ -62,16 +62,7 @@ main(int argc, char **argv) arg_vector[0] = strdup("logmanager"); arg_vector[1] = strdup("-j"); - - if ((home = getenv("MAXSCALE_HOME")) != NULL) - { - arg_vector[2] = (char*)malloc((strlen(home) + strlen("/log"))*sizeof(char)); - sprintf(arg_vector[2],"%s/log",home); - } - else - { - arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log"); - } + arg_vector[2] = strdup("/var/log/maxscale"); arg_vector[3] = NULL; skygw_logmanager_init(arg_count,arg_vector); diff --git a/server/core/secrets.c b/server/core/secrets.c index 3eae2cc48..b0ddc084b 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -22,6 +22,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -62,7 +63,7 @@ int i; static MAXKEYS * secrets_readKeys() { -char secret_file[255]; +char secret_file[PATH_MAX+1]; char *home; MAXKEYS *keys; struct stat secret_stats; @@ -70,12 +71,7 @@ int fd; int len; static int reported = 0; - home = getenv("MAXSCALE_HOME"); - - if (home == NULL) { - home = "/usr/local/mariadb-maxscale"; - } - snprintf(secret_file, 255, "%s/etc/.secrets", home); + snprintf(secret_file, PATH_MAX, "%s/.secrets", get_cachedir()); /* Try to access secrets file */ if (access(secret_file, R_OK) == -1) diff --git a/server/core/service.c b/server/core/service.c index d529cc54a..5ba5d539d 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -232,11 +232,7 @@ GWPROTOCOL *funcs; { /* Try loading authentication data from file cache */ char *ptr, path[4097]; - strcpy(path, "/usr/local/mariadb-maxscale"); - if ((ptr = getenv("MAXSCALE_HOME")) != NULL) - { - strncpy(path, ptr, 4096); - } + strcpy(path, get_cachedir()); strncat(path, "/", 4096); strncat(path, service->name, 4096); strncat(path, "/.cache/dbusers", 4096); diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 626a75f3e..a1933aa0d 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -44,19 +44,4 @@ add_test(Internal-TestUsers test_users) add_test(Internal-TestAdminUsers test_adminusers) add_test(Internal-TestMemlog testmemlog) add_test(TestFeedback testfeedback) -set_tests_properties(Internal-TestMySQLUsers - Internal-TestHash - Internal-TestHint - Internal-TestSpinlock - Internal-TestFilter - Internal-TestBuffer - Internal-TestDCB - Internal-TestModutil - Internal-TestPoll - Internal-TestService - Internal-TestServer - Internal-TestUsers - Internal-TestAdminUsers - Internal-TestMemlog - TestFeedback PROPERTIES ENVIRONMENT MAXSCALE_HOME=${CMAKE_BINARY_DIR}/) set_tests_properties(TestFeedback PROPERTIES TIMEOUT 30) diff --git a/server/core/test/testadminusers.c b/server/core/test/testadminusers.c index ae52bd8b7..f9c34e500 100644 --- a/server/core/test/testadminusers.c +++ b/server/core/test/testadminusers.c @@ -30,7 +30,7 @@ #include #include #include - +#include #include @@ -40,7 +40,7 @@ * Test that the username password admin/mariadb is accepted if no users * have been created and that no other users are accepted * - * WARNING: $MAXSCALE_HOME/etc/passwd must be removed before this test is run + * WARNING: The passwd file must be removed before this test is run */ static int test1() @@ -269,9 +269,8 @@ int result = 0; char *home, buf[1024]; /* Unlink any existing password file before running this test */ - if ((home = getenv("MAXSCALE_HOME")) == NULL || strlen(home) >= 1024) - home = "/usr/local/mariadb-maxscale"; - sprintf(buf, "%s/etc/passwd", home); + + sprintf(buf, "%s/passwd", default_cachedir); if(!is_valid_posix_path(buf)) exit(1); if (strcmp(buf, "/etc/passwd") != 0) diff --git a/server/core/test/testfeedback.c b/server/core/test/testfeedback.c index 6549305c3..3b281537e 100644 --- a/server/core/test/testfeedback.c +++ b/server/core/test/testfeedback.c @@ -73,17 +73,8 @@ int main(int argc, char** argv) char* cnf; hkinit(); - home = getenv("MAXSCALE_HOME"); - if(home == NULL) - { - FAILTEST("MAXSCALE_HOME was not defined."); - } - printf("Home: %s\n",home); - - cnf = malloc(strlen(home) + strlen("/etc/MaxScale.cnf") + 1); - strcpy(cnf,home); - strcat(cnf,"/etc/MaxScale.cnf"); + cnf = strdup("/etc/MaxScale.cnf"); printf("Config: %s\n",cnf); @@ -116,4 +107,4 @@ int main(int argc, char** argv) } mysql_library_end(); return 0; -} \ No newline at end of file +} diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index e62696aab..bd878bb59 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -39,5 +39,5 @@ static char* cachedir = NULL; static char* langdir = NULL; char* get_libdir(); - +char* get_cachedir(); #endif diff --git a/server/maxscale_binlogserver_template.cnf b/server/maxscale_binlogserver_template.cnf index dddd74163..f9cc112aa 100644 --- a/server/maxscale_binlogserver_template.cnf +++ b/server/maxscale_binlogserver_template.cnf @@ -1,5 +1,5 @@ # -# Example MaxScale.cnf for the Binlog Server. +# Example maxscale.cnf for the Binlog Server. # # @@ -37,14 +37,13 @@ threads=6 # The MaxScale Binlog Server Service. # # The name of this service will be used as the directory name -# in $MAXSCALE_HOME where the binlogs will be saved. +# in the cache directory where the binlogs will be saved. # If this name is changed, it must be changed in the listener # configuration below. [Binlog_Service] # type must be service # router must be binlogrouter -# (corresponding to the so file in $MAXSCALE_HOME/modules). type=service router=binlogrouter diff --git a/server/modules/filter/dbfwfilter.c b/server/modules/filter/dbfwfilter.c index 9cdc2816f..d796bb3c0 100644 --- a/server/modules/filter/dbfwfilter.c +++ b/server/modules/filter/dbfwfilter.c @@ -2133,15 +2133,13 @@ int main(int argc, char** argv) return 1; } - if((home = getenv("MAXSCALE_HOME")) == NULL) + home = malloc(sizeof(char)*(PATH_MAX+1)); + if(getcwd(home,PATH_MAX) == NULL) { - home = malloc(sizeof(char)*(PATH_MAX+1)); - if(getcwd(home,PATH_MAX) == NULL) - { - free(home); - home = NULL; - } + free(home); + home = NULL; } + printf("Log files written to: %s\n",home?home:"/tpm"); int argc_ = 11; diff --git a/server/modules/filter/test/CMakeLists.txt b/server/modules/filter/test/CMakeLists.txt index 2ad0c2201..4e1006bfb 100644 --- a/server/modules/filter/test/CMakeLists.txt +++ b/server/modules/filter/test/CMakeLists.txt @@ -1,10 +1,3 @@ -aux_source_directory(${CMAKE_SOURCE_DIR}/server/core CORE_ALL) -foreach(VAR ${CORE_ALL}) - if(NOT( (${VAR} MATCHES "max[a-z_]*.c") OR (${VAR} MATCHES "gateway.c"))) - list(APPEND CORE ${VAR}) - endif() -endforeach() - include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_executable(harness_ui harness_ui.c harness_common.c) add_executable(harness harness_util.c harness_common.c ${CORE}) @@ -32,6 +25,3 @@ add_test(TestTeeRecursion ${CMAKE_CURRENT_SOURCE_DIR}/tee_recursion.sh ${TEST_HOST} ${TEST_PORT}) -set_tests_properties(TestHintfilter TestRegexfilter TestFwfilter1 TestFwfilter2 TestTeeRecursion -PROPERTIES -ENVIRONMENT MAXSCALE_HOME=${CMAKE_BINARY_DIR}/) From e681d18fdd011c4dbcc0da3403b888cd84832302 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 27 Apr 2015 23:11:49 +0300 Subject: [PATCH 023/273] Split packaging to RPM and DEB cmake files. --- CMakeLists.txt | 71 +++++++++++++++++++---------------------- cmake/package_deb.cmake | 6 ++++ cmake/package_rpm.cmake | 23 +++++++++++++ 3 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 cmake/package_deb.cmake create mode 100644 cmake/package_rpm.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 550724505..eff114eb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,14 @@ message(STATUS "CMake version: ${CMAKE_VERSION}") include(${CMAKE_SOURCE_DIR}/cmake/macros.cmake) enable_testing() -set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, RPM, DEB") +# Packaging builds install to /usr and other builds to /usr/local/mariadb-maxscale +if(PACKAGE) + set(INSTALL_LAYOUT "PACKAGE" CACHE STRING "Install layout, options are: STANDALONE, PACKAGE") +else() + set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, PACKAGE") +endif() + +# Installation prefixes for different layouts if(${INSTALL_LAYOUT} MATCHES "STANDALONE") set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") else() @@ -58,12 +65,12 @@ if(${MAXSCALE_VERSION} MATCHES "-stable") endif() file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/server/include) -configure_file(${CMAKE_SOURCE_DIR}/server/include/version.h.in ${CMAKE_BINARY_DIR}/server/include/version.h) -configure_file(${CMAKE_SOURCE_DIR}/server/include/gwdirs.h.in ${CMAKE_BINARY_DIR}/server/include/gwdirs.h) -configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.h.in ${CMAKE_BINARY_DIR}/server/include/maxscale_test.h) -configure_file(${CMAKE_SOURCE_DIR}/etc/postinst.in ${CMAKE_BINARY_DIR}/postinst) -configure_file(${CMAKE_SOURCE_DIR}/etc/postrm.in ${CMAKE_BINARY_DIR}/postrm) -configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.cnf ${CMAKE_BINARY_DIR}/maxscale.cnf) +configure_file(${CMAKE_SOURCE_DIR}/server/include/version.h.in ${CMAKE_BINARY_DIR}/server/include/version.h @ONLY) +configure_file(${CMAKE_SOURCE_DIR}/server/include/gwdirs.h.in ${CMAKE_BINARY_DIR}/server/include/gwdirs.h @ONLY) +configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.h.in ${CMAKE_BINARY_DIR}/server/include/maxscale_test.h @ONLY) +configure_file(${CMAKE_SOURCE_DIR}/etc/postinst.in ${CMAKE_BINARY_DIR}/postinst @ONLY) +configure_file(${CMAKE_SOURCE_DIR}/etc/postrm.in ${CMAKE_BINARY_DIR}/postrm @ONLY) +configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.cnf ${CMAKE_BINARY_DIR}/maxscale.cnf @ONLY) set(FLAGS "-Wall -Wno-unused-variable -Wno-unused-function -fPIC" CACHE STRING "Compilation flags") set(DEBUG_FLAGS "-ggdb -pthread -pipe -Wformat -fstack-protector --param=ssp-buffer-size=4" CACHE STRING "Debug compilation flags") @@ -81,7 +88,7 @@ if(CMAKE_VERSION VERSION_GREATER 2.6) endif() -IF(DEFINED OLEVEL ) +IF(DEFINED OLEVEL) if((OLEVEL GREATER -1) AND (OLEVEL LESS 4) ) set(FLAGS "${FLAGS} -O${OLEVEL}" CACHE STRING "Compilation flags") message(STATUS "Optimization level at: ${OLEVEL}") @@ -181,7 +188,9 @@ if(WITH_SCRIPTS) endif() endif() +# Only do packaging if configured if(PACKAGE) + # Install the files copied by the postinst script into the share folder install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) @@ -194,23 +203,8 @@ if(PACKAGE) if(${CMAKE_VERSION} VERSION_LESS 2.8.12) message(WARNING "CMake version is ${CMAKE_VERSION}. Building of packages requires version 2.8.12 or greater.") else() - # See if we are on a RPM-capable or DEB-capable system - find_program(RPMBUILD rpmbuild) - find_program(DEBBUILD dpkg-buildpackage) - set(CPACK_GENERATOR "TGZ") - if(NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) ) - message(STATUS "Generating RPM packages") - set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM") - endif() - - if(NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" ) ) - set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB") - execute_process(COMMAND dpgk --print-architecture OUTPUT_VARIABLE DEB_ARCHITECTURE) - set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEB_ARCHITECTURE}) - set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) - message(STATUS "Generating DEB packages for ${DEB_ARCHITECTURE}") - endif() + # Generic CPack configuration variables set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale") set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}") @@ -221,23 +215,25 @@ if(PACKAGE) set(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab") set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/etc/DESCRIPTION) set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") - set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/postinst;{CMAKE_BINARY_DIR}/postrm") - set(CPACK_RPM_PACKAGE_RELEASE ${MAXSCALE_BUILD_NUMBER}) - set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/postinst) - set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/postrm) - set(CPACK_RPM_PACKAGE_NAME "maxscale") - set(CPACK_RPM_PACKAGE_VENDOR "MariaDB Corporation Ab") - set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") - set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/etc /etc/ld.so.conf.d /etc/init.d /etc/rc.d/init.d") - set(CPACK_RPM_SPEC_MORE_DEFINE "%define ignore \#") - set(CPACK_RPM_USER_FILELIST "%ignore /etc/init.d") - set(CPACK_RPM_USER_FILELIST "%ignore /etc/ld.so.conf.d") - set(CPACK_RPM_USER_FILELIST "%ignore /etc") + + # See if we are on a RPM-capable or DEB-capable system + find_program(RPMBUILD rpmbuild) + find_program(DEBBUILD dpkg-buildpackage) + set(CPACK_GENERATOR "TGZ") + + if(NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) ) + include(cmake/package_rpm.cmake) + message(STATUS "Generating RPM packages") + elseif(NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" ) ) + include(cmake/package_deb.cmake) + message(STATUS "Generating DEB packages for ${DEB_ARCHITECTURE}") + endif() + include(CPack) endif() endif() -add_custom_target(buildtestsx +add_custom_target(buildtests COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} COMMAND make COMMENT "Building test suite..." VERBATIM @@ -252,7 +248,6 @@ add_custom_target(testall add_custom_target(testcore COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} COMMAND make install - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/server/test/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf COMMAND ctest -R Internal COMMENT "Running core test suite..." VERBATIM) diff --git a/cmake/package_deb.cmake b/cmake/package_deb.cmake new file mode 100644 index 000000000..90d66d4bc --- /dev/null +++ b/cmake/package_deb.cmake @@ -0,0 +1,6 @@ +# DEB specific CPack configuration parameters +set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB") +set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/postinst;{CMAKE_BINARY_DIR}/postrm") +execute_process(COMMAND dpgk --print-architecture OUTPUT_VARIABLE DEB_ARCHITECTURE) +set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEB_ARCHITECTURE}) +set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) diff --git a/cmake/package_rpm.cmake b/cmake/package_rpm.cmake new file mode 100644 index 000000000..0f65bcdd2 --- /dev/null +++ b/cmake/package_rpm.cmake @@ -0,0 +1,23 @@ +# RPM specific CPack configuration parameters +set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale") +set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${MAXSCALE_VERSION_PATCH}") +set(CPACK_PACKAGE_CONTACT "MariaDB Corporation Ab") +set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}") +set(CPACK_PACKAGE_NAME "maxscale") +set(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab") +set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/etc/DESCRIPTION) +set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") +set(CPACK_RPM_PACKAGE_RELEASE ${MAXSCALE_BUILD_NUMBER}) +set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/postinst) +set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/postrm) +set(CPACK_RPM_PACKAGE_NAME "maxscale") +set(CPACK_RPM_PACKAGE_VENDOR "MariaDB Corporation Ab") +set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") +set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/etc /etc/ld.so.conf.d /etc/init.d /etc/rc.d/init.d") +set(CPACK_RPM_SPEC_MORE_DEFINE "%define ignore \#") +set(CPACK_RPM_USER_FILELIST "%ignore /etc/init.d") +set(CPACK_RPM_USER_FILELIST "%ignore /etc/ld.so.conf.d") +set(CPACK_RPM_USER_FILELIST "%ignore /etc") From 71531cde3ed0e69d826721ab00e806c91dece731 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 28 Apr 2015 11:09:49 +0300 Subject: [PATCH 024/273] Updated package installation and started documentation update. --- CMakeLists.txt | 1 + .../Getting-Started/Configuration-Guide.md | 14 +++-- cmake/install_layout.cmake | 3 +- cmake/macros.cmake | 62 ------------------- etc/maxscale.service.in | 11 ++++ 5 files changed, 21 insertions(+), 70 deletions(-) create mode 100644 etc/maxscale.service.in diff --git a/CMakeLists.txt b/CMakeLists.txt index eff114eb5..43ede7f92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,7 @@ install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHAREDIR}) # Install startup scripts and ldconfig files if(WITH_SCRIPTS) configure_file(${CMAKE_SOURCE_DIR}/maxscale.conf.in ${CMAKE_BINARY_DIR}/maxscale.conf @ONLY) + configure_file(${CMAKE_SOURCE_DIR}/etc/maxscale.service.in ${CMAKE_BINARY_DIR}/maxscale.service @ONLY) if(DEB_BASED) configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_BINARY_DIR}/maxscale @ONLY) else() diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 81d3c90e2..3f3dee484 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -1244,7 +1244,7 @@ In addition parameters may be added to define patterns to match against to eithe The top filter is a filter module for MaxScale that monitors every SQL statement that passes through the filter. It measures the duration of that statement, the time between the statement being sent and the first result being returned. The top N times are kept, along with the SQL text itself and a list sorted on the execution times of the query is written to a file upon closure of the client session. -The configuration block for the **top** filter requires the minimal filter options in its section within the `MaxScale.cnf` file, stored in `$MAXSCALE_HOME/etc/MaxScale.cnf`. +The configuration block for the **top** filter requires the minimal filter options in its section within the `MaxScale.cnf` file, stored in `/etc/MaxScale.cnf`. ``` [MyLogFilter] @@ -1258,9 +1258,11 @@ In addition parameters may be added to define patterns to match against to eithe ## Encrypting Passwords -Passwords stored in the MaxScale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. +Passwords stored in the MaxScale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. The default location MaxScale stores the keys is `/var/cache/maxscale`. - maxkeys $MAXSCALE_HOME/etc/.secrets +``` +maxkeys /var/cache/maxscale/.secrets +``` Changing the encryption key for MaxScale will invalidate any currently encrypted keys stored in the MaxScale.cnf file. @@ -1352,7 +1354,7 @@ and short notations ## Error Reporting -MaxScale is designed to be executed as a service, therefore all error reports, including configuration errors, are written to the MaxScale error log file. MaxScale will log to a set of files in the directory `$MAXSCALE_HOME/log`, the only exception to this is if the log directory is not writable, in which case a message is sent to the standard error descriptor. +MaxScale is designed to be executed as a service, therefore all error reports, including configuration errors, are written to the MaxScale error log file. By default, MaxScale will log to a set of files in the directory `/var/log/maxscale`, the only exception to this is if the log directory is not writable, in which case a message is sent to the standard error descriptor. ### Troubleshooting @@ -1367,11 +1369,11 @@ Example: ``` [Galera Listener] type=listener -address=192.1681.3.33 +address=192.168.3.33 port=4408 socket=/servers/maxscale/galera.sock ``` -TCP/IP Traffic must be permitted to 192.1681.3.33 port 4408 +TCP/IP Traffic must be permitted to 192.168.3.33 port 4408 For Unix socket, the socket file path (example: `/servers/maxscale/galera.sock`) must be writable by the Unix user MaxScale runs as. diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index 1425b4d49..f7ce45441 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -1,8 +1,7 @@ # Set the install layout # Possible values: # STANDALONE - Installs to /usr/local/mariadb-maxscale -# RPM - Installs to /usr -# DEB - Installs to /usr +# PACKAGE - Installs to /usr include(GNUInstallDirs) set(MAXSCALE_LIBDIR ${CMAKE_INSTALL_LIBDIR}/maxscale CACHE PATH "Library installation path") diff --git a/cmake/macros.cmake b/cmake/macros.cmake index 440c3fed5..aaed9cd69 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -196,68 +196,6 @@ debugmsg("Search returned: ${MYSQL_DIR_LOC}") set(ERRMSG ${ERRMSG_FILE} CACHE FILEPATH "Path to the errmsg.sys file." FORCE) unset(ERRMSG_FILE) - # Find the embedded mysql library - - # if (DEFINED EMBEDDED_LIB) - # if( NOT (IS_DIRECTORY ${EMBEDDED_LIB}) ) - # debugmsg("EMBEDDED_LIB is not a directory: ${EMBEDDED_LIB}") - # if(${CMAKE_VERSION} VERSION_LESS 2.8.12 ) - # set(COMP_VAR PATH) - # else() - # set(COMP_VAR DIRECTORY) - # endif() - # get_filename_component(EMBEDDED_LIB ${EMBEDDED_LIB} ${COMP_VAR}) - # debugmsg("EMBEDDED_LIB directory component: ${EMBEDDED_LIB}") - # endif() - # debugmsg("Searching for the embedded library at: ${EMBEDDED_LIB}") - # endif() - - # if(STATIC_EMBEDDED) - - # debugmsg("Using the static embedded library...") - # set(OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - # set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - # if (DEFINED EMBEDDED_LIB) - # debugmsg("Searching for libmysqld.a at: ${EMBEDDED_LIB}") - # find_library(EMBEDDED_LIB_STATIC libmysqld.a PATHS ${EMBEDDED_LIB} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) - # else() - # find_library(EMBEDDED_LIB_STATIC libmysqld.a PATH_SUFFIXES mysql mariadb) - # endif() - # debugmsg("Search returned: ${EMBEDDED_LIB_STATIC}") - - # set(EMBEDDED_LIB ${EMBEDDED_LIB_STATIC} CACHE FILEPATH "Path to libmysqld" FORCE) - # set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) - - # else() - # debugmsg("Using the dynamic embedded library...") - # set(OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - # set(CMAKE_FIND_LIBRARY_SUFFIXES ".so") - # if (DEFINED EMBEDDED_LIB) - # debugmsg("Searching for libmysqld.so at: ${EMBEDDED_LIB}") - # find_library(EMBEDDED_LIB_DYNAMIC mysqld PATHS ${EMBEDDED_LIB} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) - # else() - # find_library(EMBEDDED_LIB_DYNAMIC mysqld PATH_SUFFIXES mysql mariadb) - # endif() - # debugmsg("Search returned: ${EMBEDDED_LIB_DYNAMIC}") - # set(EMBEDDED_LIB ${EMBEDDED_LIB_DYNAMIC} CACHE FILEPATH "Path to libmysqld" FORCE) - # set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) - - # endif() - - # unset(EMBEDDED_LIB_DYNAMIC) - # unset(EMBEDDED_LIB_STATIC) - # unset(OLD_SUFFIXES) - - # # Inform the user about the embedded library - # if( (${EMBEDDED_LIB} MATCHES "NOTFOUND") OR (${EMBEDDED_LIB} MATCHES "NOTFOUND")) - # set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.") - # message(FATAL_ERROR "Library not found: libmysqld. If your install of MySQL is in a non-default location, please provide the location with -DEMBEDDED_LIB=") - # else() - # get_filename_component(EMBEDDED_LIB ${EMBEDDED_LIB} REALPATH) - # message(STATUS "Using embedded library: ${EMBEDDED_LIB}") - # endif() - - # Check which init.d script to install find_file(RPM_FNC functions PATHS /etc/rc.d/init.d) if(${RPM_FNC} MATCHES "RPM_FNC-NOTFOUND") diff --git a/etc/maxscale.service.in b/etc/maxscale.service.in new file mode 100644 index 000000000..56b49d98f --- /dev/null +++ b/etc/maxscale.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=MariaDB MaxScale Database Proxy +After=network.target + +[Service] +Type=forking +PIDFile=/var/run/maxscale/maxscale.pid +ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale + +[Install] +WantedBy=multi-user.target From 3501ffb689f02d54824291e65eb8473da1c8642d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 28 Apr 2015 16:39:52 +0300 Subject: [PATCH 025/273] Updated documentation --- .../Getting-Started/Configuration-Guide.md | 2 +- .../Reference/Debug-And-Diagnostic-Support.md | 10 ++-- .../Tutorials/Administration-Tutorial.md | 53 ++++++------------- 3 files changed, 22 insertions(+), 43 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 3f3dee484..19d4c2b6e 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -1268,7 +1268,7 @@ Changing the encryption key for MaxScale will invalidate any currently encrypted ### Creating Encrypted Passwords -Encrypted passwords are created by executing the maxpasswd command with the password you require to encrypt as an argument. The environment variable `MAXSCALE_HOME` must be set, or MaxScale must be installed in the default location before maxpasswd can be executed. +Encrypted passwords are created by executing the maxpasswd command with the password you require to encrypt as an argument. maxpasswd MaxScalePw001 61DD955512C39A4A8BC4BB1E5F116705 diff --git a/Documentation/Reference/Debug-And-Diagnostic-Support.md b/Documentation/Reference/Debug-And-Diagnostic-Support.md index 34fda6653..d9bffa268 100644 --- a/Documentation/Reference/Debug-And-Diagnostic-Support.md +++ b/Documentation/Reference/Debug-And-Diagnostic-Support.md @@ -1711,7 +1711,7 @@ User admin already exists. **MaxScale>**** ** -If you should forget or lose the the account details you may simply remove the passwd file in $MAXSCALE_HOME/etc and the system will revert to the default behavior with admin/mariadb as the account. +If you should forget or lose the the account details you may simply remove the passwd file in /var/cache/maxscale and the system will revert to the default behavior with admin/mariadb as the account. ## Enable/disable log @@ -1745,7 +1745,7 @@ MaxScale generates output of its behavior to four distinct logs, error, messages ## Log contents -By default all log files are located in : $MAXSCALE_HOME/log and named as : +By default all log files are located in : /var/log/maxscale and named as : skygw_errW.log, skygw_msgX.log, skygw_traceY.log and skygw_debugZ.log @@ -1837,7 +1837,7 @@ MariaDB Corporation MaxScale /home/jdoe/bin/develop/log/skygw_msg1.log Tue Dec ### Trace log -Trace log includes information about available servers and their states, client sessions, queries being executed, routing decisions and other routing related data. Trace log can be found from the same directory with other logs but it is physically stored elsewhere, to OSs shared memory to reduce the latency caused by logging. The location of physical file is : /dev/shm//skygw_traceX.log where ‘X’ is the same sequence number as in the file name in the $MAXSCALE_HOME/log directory. +Trace log includes information about available servers and their states, client sessions, queries being executed, routing decisions and other routing related data. Trace log can be found from the same directory with other logs but it is physically stored elsewhere, to OSs shared memory to reduce the latency caused by logging. The location of physical file is : /dev/shm//skygw_traceX.log where ‘X’ is the same sequence number as in the file name in the /var/log/maxscale directory. Individual trace log entry looks similar to those in other logs but there is some difference too. Some log entries include a number within square brackets to specify which client session they belong to. For example: @@ -1923,11 +1923,11 @@ In the log, session’s life cycle is covered by annotating its beginning and th The log files are located in -$MAXSCALE_HOME/log +/var/log/maxscale by default. If, however, trace and debug logs are enabled, only a soft link is created there. MaxScale process creates a directory under -/dev/shm/ +/dev/shm/maxscale. where it stores the physical trace and debug log files. Link and physical files share the same name. These logs consume the main memory of the host they run on so it is important to archive or remove them periodically to avoid unnecessary main-memory consumption. diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index 894f89f94..943ecaa27 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -26,43 +26,20 @@ It is also possible to start MaxScale by executing the maxscale command itself, Options may be passed to the MaxScale binary that alter this default behavior, this options are documented in the table below. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SwitchLong OptionDescription
-d--nodaemonRun MaxScale attached to the terminal rather than as a daemon process. This is useful for debugging purposes.
-c--homedir=Ignore the environment variable MAXSCALE_HOME and use the supplied argument instead.
-f--config=Use the filename passed as an argument instead of looking in /etc/MaxScale.cnf
-l|--log=Control where logs are written for the debug and trace level log messages. the default is to write these to a shared memory device, however using the -lfile or --log=file option will forced these to be written to regular files.
-v--versionPrint version information for MaxScale
-?--helpPrint usage information for MaxScale
+Switch|Long Option|Description +------|-----------|----------- +`-d`|`--nodaemon`|enable running in terminal process (default:disabled) +`-f FILE`|`--config=FILE`|relative or absolute pathname of MaxScale configuration file (default:/etc/maxscale.cnf) +`-l[file shm]`|`--log=[file shm]`|log to file or shared memory (default: shm) +`-L PATH`|`--logdir=PATH`|path to log file directory (default: /var/log/maxscale) +`-D PATH`|`--datadir=PATH`|path to data directory, stored embedded mysql tables (default: /var/cache/maxscale) +`-C PATH`|`--configdir=PATH`|path to configuration file directory (default: /etc/) +`-B PATH`|`--libdir=PATH`|path to module directory (default: /usr/lib64/maxscale) +`-A PATH`|`--cachedir=PATH`|path to cache directory (default: /var/cache/maxscale) +`-s [yes no]`|`--syslog=[yes no]`|log messages to syslog (default:yes) +`-S [yes no]`|`--maxscalelog=[yes no]`|log messages to MaxScale log (default: yes) +`-v`|`--version`|print version info and exit +`-?`|`--help`|show this help ### Stopping MaxScale @@ -90,6 +67,7 @@ In order to shutdown MaxScale using the maxadmin command you may either connect It is possible to use the maxadmin command to obtain statistics regarding the services that are configured within your MaxScale configuration file. The maxadmin command "list services" will give very basic information regarding the services that are define. This command may be either run in interactive mode or passed on the maxadmin command line. +``` $ maxadmin -pmariadb MaxScale> list services @@ -110,6 +88,7 @@ It is possible to use the maxadmin command to obtain statistics regarding the se --------------------------+----------------------+--------+--------------- MaxScale> +``` It should be noted that network listeners count as a user of the service, therefore there will always be one user per network port in which the service listens. More detail can be obtained by use of the "show service" command which is passed a service name. From 5394b4d0fab6d71272f0f1da595f71c17baf4e69 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 29 Apr 2015 05:42:58 +0300 Subject: [PATCH 026/273] Changed standard installation directory from /usr/local/mariadb-maxscale to /usr/local. --- CMakeLists.txt | 11 +---------- cmake/install_layout.cmake | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43ede7f92..28b3229d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,17 +9,8 @@ message(STATUS "CMake version: ${CMAKE_VERSION}") include(${CMAKE_SOURCE_DIR}/cmake/macros.cmake) enable_testing() -# Packaging builds install to /usr and other builds to /usr/local/mariadb-maxscale +# Packaging builds install to /usr and other builds to /usr/local if(PACKAGE) - set(INSTALL_LAYOUT "PACKAGE" CACHE STRING "Install layout, options are: STANDALONE, PACKAGE") -else() - set(INSTALL_LAYOUT "STANDALONE" CACHE STRING "Install layout, options are: STANDALONE, PACKAGE") -endif() - -# Installation prefixes for different layouts -if(${INSTALL_LAYOUT} MATCHES "STANDALONE") - set(CMAKE_INSTALL_PREFIX "/usr/local/mariadb-maxscale" CACHE PATH "Prefix prepended to install directories.") -else() set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Prefix prepended to install directories.") endif() diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index f7ce45441..c55a25a32 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -1,6 +1,6 @@ # Set the install layout # Possible values: -# STANDALONE - Installs to /usr/local/mariadb-maxscale +# STANDALONE - Installs to /usr/local # PACKAGE - Installs to /usr include(GNUInstallDirs) From c035f4b8b75eb9a11b7cea9ef334ba8d2d632083 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 29 Apr 2015 07:03:05 +0300 Subject: [PATCH 027/273] Removed current directory from the module search path. --- server/core/load_utils.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 5ecbe7f34..9d7d69220 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -133,21 +133,17 @@ MODULE_INFO *mod_info = NULL; * * Search of the shared object. */ - snprintf(fname,MAXPATHLEN+1, "./lib%s.so", module); - + + snprintf(fname, MAXPATHLEN+1,"%s/lib%s.so", get_libdir(), module); + if (access(fname, F_OK) == -1) { - snprintf(fname, MAXPATHLEN+1,"%s/lib%s.so", get_libdir(), module); - - if (access(fname, F_OK) == -1) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to find library for " - "module: %s. Module dir: %s", - module, get_libdir()))); - return NULL; - } + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Unable to find library for " + "module: %s. Module dir: %s", + module, get_libdir()))); + return NULL; } if ((dlhandle = dlopen(fname, RTLD_NOW|RTLD_LOCAL)) == NULL) From 416ffea9b76fd45a4d643dcecd8da61d2f2326fb Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 30 Apr 2015 10:37:06 +0300 Subject: [PATCH 028/273] Changed errmsg.sys installation location to /var/lib/maxscale --- CMakeLists.txt | 2 +- server/include/gwdirs.h.in | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28b3229d8..33627ba15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,7 @@ install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHARED install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES server/maxscale_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES server/maxscale_binlogserver_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) -install(FILES ${ERRMSG} DESTINATION /usr/share/mysql/english/) +install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_VARDIR}/lib/maxscale) install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHAREDIR}) diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index bd878bb59..ac8fcf4af 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -29,8 +29,7 @@ static const char* default_logdir = "/var/log/maxscale/"; static const char* default_datadir = "/var/cache/maxscale/"; static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@"; static const char* default_cachedir = "/var/cache/maxscale/"; -static const char* default_langdir = "/usr/share/mysql/english/"; /*< This is where the MariaDB - * server installs errmsg.sys */ +static const char* default_langdir = "@MAXSCALE_VARDIR@/lib/maxscale/"; static char* configdir = NULL; static char* logdir = NULL; From cad59abbd1163bb9b81775bfc32438d7e6698277 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 5 May 2015 22:36:47 +0300 Subject: [PATCH 029/273] Added parameters for commands and changed the execv call to execvp. --- server/modules/monitor/mon_exec.c | 122 +++++++++++++++++++++++++++-- server/modules/monitor/mon_exec.h | 10 ++- server/modules/monitor/mysql_mon.c | 19 ++--- server/modules/monitor/mysqlmon.h | 5 +- 4 files changed, 135 insertions(+), 21 deletions(-) diff --git a/server/modules/monitor/mon_exec.c b/server/modules/monitor/mon_exec.c index 122390da9..bae0d31fc 100644 --- a/server/modules/monitor/mon_exec.c +++ b/server/modules/monitor/mon_exec.c @@ -5,12 +5,123 @@ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; extern __thread log_info_t tls_log_info; +/** + * Tokenize a string into arguments suitable for a execvp call. + * @param args Argument string + * @param argv Array of char pointers to be filled with tokenized arguments + * @return 0 on success, -1 on error + */ +int tokenize_arguments(char* args, char** argv) +{ + int i = 0; + bool quoted = false; + bool read = false; + bool escaped = false; + char *ptr,*start; + char qc; + + start = args; + ptr = start; + + while(*ptr != '\0' && i < MAXSCALE_EXTCMD_ARG_MAX) + { + if(escaped) + { + escaped = false; + } + else + { + if(*ptr == '\\') + { + escaped = true; + } + else if(quoted && !escaped && *ptr == qc) /** End of quoted string */ + { + *ptr = '\0'; + argv[i++] = strdup(start); + read = false; + quoted = false; + } + else if (!quoted) + { + if(isspace(*ptr)) + { + *ptr = '\0'; + if(read) /** New token */ + { + argv[i++] = strdup(start); + read = false; + } + } + else if( *ptr == '\"' || *ptr == '\'') + { + /** New quoted token, strip quotes */ + quoted = true; + qc = *ptr; + start = ptr + 1; + } + else if(!read) + { + start = ptr; + read = true; + } + } + } + ptr++; + } + if(read) + argv[i++] = strdup(start); + argv[i] = NULL; + + return 0; +} + +/** + * Allocate a new external command. + * The name and parameters are copied into the external command structure so + * the original memory can be freed if needed. + * @param command Command to execute with the parameters + * @return Pointer to new external command struct or NULL if an error occurred + */ +EXTERNCMD* externcmd_allocate(char* argstr) +{ + EXTERNCMD* cmd; + + if(argstr == NULL) + return NULL; + + if((cmd = (EXTERNCMD*)malloc(sizeof(EXTERNCMD))) != NULL) + { + if(tokenize_arguments(argstr,cmd->parameters) == -1) + { + free(cmd); + return NULL; + } + } + return cmd; +} + +/** + * Free a previously allocated external command. + * @param cmd Command to free + */ +void externcmd_free(EXTERNCMD* cmd) +{ + int i; + + for(i = 0;cmd->parameters[i] != NULL;i++) + { + free(cmd->parameters[i]); + } + free(cmd); +} + /** *Execute a command in a separate process. *@param cmd Command to execute *@return 0 on success, -1 on error. */ -int monitor_exec_cmd(char* cmd) +int externcmd_execute(EXTERNCMD* cmd) { int rval = 0; pid_t pid; @@ -20,19 +131,20 @@ int monitor_exec_cmd(char* cmd) if(pid < 0) { skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s', fork failed: [%d] %s", - cmd,errno,strerror(errno)); + cmd->parameters[0],errno,strerror(errno)); rval = -1; } else if(pid == 0) { /** Child process, execute command */ - execl(cmd,cmd,NULL); + execvp(cmd->parameters[0],cmd->parameters); } else { - LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd)); + cmd->n_exec++; + LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd)); } return rval; - } + diff --git a/server/modules/monitor/mon_exec.h b/server/modules/monitor/mon_exec.h index ed277ab3e..e1ef7ad37 100644 --- a/server/modules/monitor/mon_exec.h +++ b/server/modules/monitor/mon_exec.h @@ -1,13 +1,19 @@ #ifndef MON_EXEC_HG #define MON_EXEC_HG -#include #include #include #include #include #include +#define MAXSCALE_EXTCMD_ARG_MAX 256 -int monitor_exec_cmd(char* cmd); +typedef struct extern_cmd_t{ + char* parameters[MAXSCALE_EXTCMD_ARG_MAX]; /*< Command arguments */ + int n_exec; /*< Number of times executed */ +}EXTERNCMD; +EXTERNCMD* externcmd_allocate(char* argstr); +void externcmd_free(EXTERNCMD* cmd); +int externcmd_execute(EXTERNCMD* cmd); #endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index d52a6902d..aced27a13 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -197,15 +197,9 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->replicationHeartbeat = config_truth_value(params->value); else if(!strcmp(params->name,"master_down_script")) { - if(access(params->value,F_OK) == 0) - { - handle->master_down_script = strdup(params->value); - handle->master_down_script_called = 0; - } - else - { - skygw_log_write(LOGFILE_ERROR,"Error: could not find master_down_script file: %s",params->value); - } + if(handle->master_down_script) + externcmd_free(handle->master_down_script); + handle->master_down_script = externcmd_allocate(params->value); } params = params->next; } @@ -700,8 +694,11 @@ int log_no_master = 1; ptr->server->port))); if(handle->master_down_script) { - if(monitor_exec_cmd(handle->master_down_script)) - skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s' on server state change.",handle->master_down_script); + if(externcmd_execute(handle->master_down_script)) + skygw_log_write(LOGFILE_ERROR, + "Error: Failed to execute command " + "'%s' on server state change.", + handle->master_down_script->parameters[0]); } } /** diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 966bebd15..d6bed5578 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -20,7 +20,7 @@ #include #include #include - +#include /** * @file mysqlmon.h - The MySQL monitor functionality within the gateway * @@ -81,8 +81,7 @@ typedef struct { int write_timeout; /**< Timeout in seconds for each attempt to write to the server. * There are retries and the total effective timeout value is two times the option value. */ - char *master_down_script; - int master_down_script_called; + EXTERNCMD* master_down_script; } MYSQL_MONITOR; #define MONITOR_RUNNING 1 From fb8359b5ca0120f73e1c5be1fa09089f07ac997f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 6 May 2015 12:34:10 +0300 Subject: [PATCH 030/273] Fixed missing installation of init.d and ldconfig files. --- CMakeLists.txt | 4 ++++ etc/postinst.in | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33627ba15..3e975c4f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,10 @@ if(WITH_SCRIPTS) if(PACKAGE) message(STATUS "maxscale.conf will unpack to: /etc/ld.so.conf.d") message(STATUS "startup scripts will unpack to to: /etc/init.d") + install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION ${MAXSCALE_SHAREDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION ${MAXSCALE_SHAREDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) else() install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION /etc/init.d PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/etc/postinst.in b/etc/postinst.in index 464e240f1..fa0c4271c 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -4,6 +4,6 @@ mkdir -p /var/log/maxscale mkdir -p /var/cache/maxscale mkdir -p /var/run/maxscale mkdir -p @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/ -cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/maxscale /etc/init.d/ -cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/maxscale.conf /etc/ld.so.conf.d/ +cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale /etc/init.d/ +cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf /etc/ld.so.conf.d/ /sbin/ldconfig From 5c7b2a68e55d57f18bfd3c1fc824f636e7526f51 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Wed, 6 May 2015 12:19:18 +0200 Subject: [PATCH 031/273] mariadb10 compatibility test without GTID First implementation of mariadb10 compatibility test without GTID State machine to be modified for mysql5.6/mariadb10 compatibility router options for mariadb10 slave registration still missing --- server/modules/include/blr.h | 2 +- server/modules/routing/binlog/blr_master.c | 3 ++- server/modules/routing/binlog/blr_slave.c | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 62cda59a3..5c9c70181 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -395,7 +395,7 @@ static char *blrs_states[] = { "Created", "Unregistered", "Registered", #define ANONYMOUS_GTID_EVENT 0x22 #define PREVIOUS_GTIDS_EVENT 0x23 -#define MAX_EVENT_TYPE 0x23 +#define MAX_EVENT_TYPE 0xa3 /** * Binlog event flags diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 2b164c349..70cd78686 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -448,7 +448,8 @@ char query[128]; GWBUF_CONSUME_ALL(router->saved_master.chksum2); router->saved_master.chksum2 = buf; blr_cache_response(router, "chksum2", buf); - buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); + //buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); + buf = blr_make_query("SET @mariadb_slave_capability=4); router->master_state = BLRM_GTIDMODE; router->master->func.write(router->master, buf); break; diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 4de69b8d7..8a008e35f 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -369,7 +369,8 @@ int query_len; else if (strcasecmp(word, "@mariadb_slave_capability") == 0) { free(query_text); - return blr_slave_send_ok(router, slave); + return blr_slave_replay(router, slave, router->saved_master.gtid_mode); + //return blr_slave_send_ok(router, slave); } else if (strcasecmp(word, "@master_binlog_checksum") == 0) { From da6d59798768a5a02c6331621c76e6341947a783 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 6 May 2015 17:27:30 +0300 Subject: [PATCH 032/273] Moved common monitor structures and definitions to a separate header and added master down script to galeramon. --- server/core/CMakeLists.txt | 4 +- server/{modules/monitor => core}/mon_exec.c | 0 .../{modules/monitor => include}/mon_exec.h | 0 server/modules/monitor/CMakeLists.txt | 4 +- .../monitor/{galera_mon.c => galeramon.c} | 51 ++++++++++----- server/modules/monitor/galeramon.h | 65 +++++++++++++++++++ server/modules/monitor/monitor_common.h | 57 ++++++++++++++++ server/modules/monitor/mysqlmon.h | 28 +------- 8 files changed, 163 insertions(+), 46 deletions(-) rename server/{modules/monitor => core}/mon_exec.c (100%) rename server/{modules/monitor => include}/mon_exec.h (100%) rename server/modules/monitor/{galera_mon.c => galeramon.c} (94%) create mode 100644 server/modules/monitor/galeramon.h create mode 100644 server/modules/monitor/monitor_common.h diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 4a9828ea3..e60734237 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,5 +1,5 @@ if(BUILD_TESTS OR BUILD_TOOLS) - add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c) + add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c mon_exec.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) @@ -12,7 +12,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c monitor.c adminusers.c secrets.c filter.c modutil.c hint.c - housekeeper.c memlog.c resultset.c) + housekeeper.c memlog.c resultset.c mon_exec.c) if(WITH_JEMALLOC) target_link_libraries(maxscale ${JEMALLOC_LIBRARIES}) diff --git a/server/modules/monitor/mon_exec.c b/server/core/mon_exec.c similarity index 100% rename from server/modules/monitor/mon_exec.c rename to server/core/mon_exec.c diff --git a/server/modules/monitor/mon_exec.h b/server/include/mon_exec.h similarity index 100% rename from server/modules/monitor/mon_exec.h rename to server/include/mon_exec.h diff --git a/server/modules/monitor/CMakeLists.txt b/server/modules/monitor/CMakeLists.txt index 3d3f83580..f53083882 100644 --- a/server/modules/monitor/CMakeLists.txt +++ b/server/modules/monitor/CMakeLists.txt @@ -1,8 +1,8 @@ -add_library(mysqlmon SHARED mysql_mon.c mon_exec.c) +add_library(mysqlmon SHARED mysql_mon.c) target_link_libraries(mysqlmon log_manager utils) install(TARGETS mysqlmon DESTINATION modules) -add_library(galeramon SHARED galera_mon.c) +add_library(galeramon SHARED galeramon.c) target_link_libraries(galeramon log_manager utils) install(TARGETS galeramon DESTINATION modules) diff --git a/server/modules/monitor/galera_mon.c b/server/modules/monitor/galeramon.c similarity index 94% rename from server/modules/monitor/galera_mon.c rename to server/modules/monitor/galeramon.c index ec9e46a61..0991e72bb 100644 --- a/server/modules/monitor/galera_mon.c +++ b/server/modules/monitor/galeramon.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -60,7 +60,7 @@ extern __thread log_info_t tls_log_info; static void monitorMain(void *); -static char *version_str = "V1.4.0"; +static char *version_str = "V1.5.0"; MODULE_INFO info = { MODULE_API_MONITOR, @@ -142,16 +142,16 @@ GetModuleObject() static void * startMonitor(void *arg,void* opt) { -MYSQL_MONITOR *handle; +GALERA_MONITOR *handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; if (arg != NULL) { - handle = (MYSQL_MONITOR *)arg; + handle = (GALERA_MONITOR *)arg; handle->shutdown = 0; } else { - if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) + if ((handle = (GALERA_MONITOR *)malloc(sizeof(GALERA_MONITOR))) == NULL) return NULL; handle->databases = NULL; handle->shutdown = 0; @@ -161,11 +161,12 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->interval = MONITOR_INTERVAL; handle->disableMasterFailback = 0; handle->availableWhenDonor = 0; - handle->disableMasterRoleSetting = 0; + handle->disableMasterRoleSetting = 0; handle->master = NULL; handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT; handle->read_timeout=DEFAULT_READ_TIMEOUT; handle->write_timeout=DEFAULT_WRITE_TIMEOUT; + handle->master_down_script = NULL; spinlock_init(&handle->lock); } @@ -178,6 +179,12 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->availableWhenDonor = config_truth_value(params->value); else if(!strcmp(params->name,"disable_master_role_setting")) handle->disableMasterRoleSetting = config_truth_value(params->value); + else if(!strcmp(params->name,"master_down_script")) + { + if(handle->master_down_script) + externcmd_free(handle->master_down_script); + handle->master_down_script = externcmd_allocate(params->value); + } params = params->next; } @@ -193,7 +200,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; static void stopMonitor(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; handle->shutdown = 1; thread_wait((void *)handle->tid); @@ -209,7 +216,7 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; static void registerServer(void *arg, SERVER *server) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; MONITOR_SERVERS *ptr, *db; if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL) @@ -239,7 +246,7 @@ MONITOR_SERVERS *ptr, *db; static void unregisterServer(void *arg, SERVER *server) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; MONITOR_SERVERS *ptr, *lptr; spinlock_acquire(&handle->lock); @@ -278,7 +285,7 @@ MONITOR_SERVERS *ptr, *lptr; static void diagnostics(DCB *dcb, void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; MONITOR_SERVERS *db; char *sep; @@ -326,7 +333,7 @@ char *sep; static void defaultUsers(void *arg, char *uname, char *passwd) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; if (handle->defaultUser) free(handle->defaultUser); @@ -343,7 +350,7 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; * @param database The database to probe */ static void -monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database) +monitorDatabase(GALERA_MONITOR *handle, MONITOR_SERVERS *database) { MYSQL_ROW row; MYSQL_RES *result; @@ -497,7 +504,7 @@ char *server_string; static void monitorMain(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; MONITOR_SERVERS *ptr; size_t nrounds = 0; MONITOR_SERVERS *candidate_master = NULL; @@ -550,6 +557,15 @@ int log_no_members = 1; { monitorDatabase(handle, ptr); + if(ptr->mon_prev_status & SERVER_MASTER && + SERVER_IS_DOWN(ptr->server)) + { + if(externcmd_execute(handle->master_down_script) == -1) + { + skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute master server failure script in galeramon."); + } + } + /* clear bits for non member nodes */ if ( ! SERVER_IN_MAINT(ptr->server) && (ptr->server->node_id < 0 || ! SERVER_IS_JOINED(ptr->server))) { ptr->server->depth = -1; @@ -584,6 +600,7 @@ int log_no_members = 1; /** Increase this server'e error count */ dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING); ptr->mon_err_count += 1; + } else { @@ -673,7 +690,7 @@ int log_no_members = 1; static void setInterval(void *arg, size_t interval) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; memcpy(&handle->interval, &interval, sizeof(unsigned long)); } @@ -759,7 +776,7 @@ static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *current_master, MONI static void disableMasterFailback(void *arg, int disable) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; memcpy(&handle->disableMasterFailback, &disable, sizeof(int)); } @@ -775,7 +792,7 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; static void availableWhenDonor(void *arg, int disable) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; memcpy(&handle->availableWhenDonor, &disable, sizeof(int)); } @@ -789,7 +806,7 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; static void setNetworkTimeout(void *arg, int type, int value) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; int max_timeout = (int)(handle->interval/1000); int new_timeout = max_timeout -1; diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h new file mode 100644 index 000000000..2b804965d --- /dev/null +++ b/server/modules/monitor/galeramon.h @@ -0,0 +1,65 @@ +#ifndef _GALERAMON_H +#define _GALERAMON_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +#include +#include +#include + +/** + * @file galeramon.h - The Galera cluster monitor + * + * @verbatim + * Revision History + * + * Date Who Description + * 07/05/15 Markus Makela Initial Implementation of galeramon.h + * @endverbatim + */ + + +/** + * The handle for an instance of a MySQL Monitor module + */ +typedef struct { + SPINLOCK lock; /**< The monitor spinlock */ + pthread_t tid; /**< id of monitor thread */ + int shutdown; /**< Flag to shutdown the monitor thread */ + int status; /**< Monitor status */ + char *defaultUser; /**< Default username for monitoring */ + char *defaultPasswd; /**< Default password for monitoring */ + unsigned long interval; /**< Monitor sampling interval */ + unsigned long id; /**< Monitor ID */ + int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ + int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ + int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ + int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ + MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ + MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */ + int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */ + int read_timeout; /**< Timeout in seconds to read from the server. + * There are retries and the total effective timeout value is three times the option value. + */ + int write_timeout; /**< Timeout in seconds for each attempt to write to the server. + * There are retries and the total effective timeout value is two times the option value. + */ + EXTERNCMD* master_down_script; +} GALERA_MONITOR; + +#endif diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h new file mode 100644 index 000000000..1efbf1dbc --- /dev/null +++ b/server/modules/monitor/monitor_common.h @@ -0,0 +1,57 @@ +#ifndef _MONITOR_COMMON_HG +#define _MONITOR_COMMON_HG +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +#include +#include + +/** + * @file monitor_common.h - The generic monitor structures all monitors use + * + * Revision History + * + * Date Who Description + * 07/05/15 Markus Makela Initial Implementation of galeramon.h + * @endverbatim + */ + +#define MONITOR_RUNNING 1 +#define MONITOR_STOPPING 2 +#define MONITOR_STOPPED 3 + +#define MONITOR_INTERVAL 10000 // in milliseconds +#define MONITOR_DEFAULT_ID 1UL // unsigned long value +#define MONITOR_MAX_NUM_SLAVES 20 //number of MySQL slave servers associated to a MySQL master server + + +/** + * The linked list of servers that are being monitored by the MySQL + * Monitor module. + */ +typedef struct monitor_servers { + SERVER *server; /**< The server being monitored */ + MYSQL *con; /**< The MySQL connection */ + int mon_err_count; + unsigned int mon_prev_status; + unsigned int pending_status; /**< Pending Status flag bitmap */ + struct monitor_servers + *next; /**< The next server in the list */ +} MONITOR_SERVERS; + +#endif \ No newline at end of file diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index d6bed5578..93ba2093e 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -17,9 +17,9 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ -#include + #include -#include +#include #include /** * @file mysqlmon.h - The MySQL monitor functionality within the gateway @@ -37,24 +37,10 @@ * 07/11/14 Massimiliano Pinto Addition of NetworkTimeout: connect, read, write * 20/04/15 Guillaume Lefranc Addition of availableWhenDonor * 22/04/15 Martin Brampton Addition of disableMasterRoleSetting - * + * 07/05/15 Markus Makela Addition of command execution on Master server failure * @endverbatim */ -/** - * The linked list of servers that are being monitored by the MySQL - * Monitor module. - */ -typedef struct monitor_servers { - SERVER *server; /**< The server being monitored */ - MYSQL *con; /**< The MySQL connection */ - int mon_err_count; - unsigned int mon_prev_status; - unsigned int pending_status; /**< Pending Status flag bitmap */ - struct monitor_servers - *next; /**< The next server in the list */ -} MONITOR_SERVERS; - /** * The handle for an instance of a MySQL Monitor module */ @@ -84,12 +70,4 @@ typedef struct { EXTERNCMD* master_down_script; } MYSQL_MONITOR; -#define MONITOR_RUNNING 1 -#define MONITOR_STOPPING 2 -#define MONITOR_STOPPED 3 - -#define MONITOR_INTERVAL 10000 // in milliseconds -#define MONITOR_DEFAULT_ID 1UL // unsigned long value -#define MONITOR_MAX_NUM_SLAVES 20 //number of MySQL slave servers associated to a MySQL master server - #endif From bc7cc2a4666a9ab8946b7134da072bf2703c002d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 7 May 2015 12:56:58 +0300 Subject: [PATCH 033/273] Added variables for MariaDB 10 compatibility. --- server/modules/include/blr.h | 7 +++++-- server/modules/routing/binlog/blr.c | 5 +++++ server/modules/routing/binlog/blr_master.c | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 5c9c70181..48c48a194 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -233,6 +233,7 @@ typedef struct { GWBUF *selectvercom; /*< select @@version_comment */ GWBUF *selecthostname;/*< select @@hostname */ GWBUF *map; /*< select @@max_allowed_packet */ + GWBUF *mariadb10; /*< set @mariadb_slave_capability=4 */ uint8_t *fde_event; /*< Format Description Event */ int fde_len; /*< Length of fde_event */ } MASTER_RESPONSES; @@ -252,6 +253,7 @@ typedef struct router_instance { 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; /*< UUID of the master */ DCB *master; /*< DCB for master connection */ DCB *client; /*< DCB for dummy client */ @@ -314,15 +316,16 @@ typedef struct router_instance { #define BLRM_MAP 0x0011 #define BLRM_REGISTER 0x0012 #define BLRM_BINLOGDUMP 0x0013 - +#define BLRM_MARIADB10 0x0014 #define BLRM_MAXSTATE 0x0013 +#define BLRM_MAXSTATE_MARIADB10 0x0014 static char *blrm_states[] = { "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 @@mx_allowed_packet", "Register slave", "Binlog Dump" }; + "select @@mx_allowed_packet", "Register slave", "Binlog Dump","Set MariaDB slave capability" }; #define BLRS_CREATED 0x0000 #define BLRS_UNREGISTERED 0x0001 diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 2ba89689f..2f5a38bee 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -195,6 +195,7 @@ unsigned char *defuuid; inst->retry_backoff = 1; inst->binlogdir = NULL; inst->heartbeat = 300; // Default is every 5 minutes + inst->mariadb10_compat = false; inst->user = strdup(service->credentials.name); inst->password = strdup(service->credentials.authdata); @@ -282,6 +283,10 @@ unsigned char *defuuid; { inst->masterid = atoi(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); diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 70cd78686..bc1e676d0 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -449,7 +449,7 @@ char query[128]; router->saved_master.chksum2 = buf; blr_cache_response(router, "chksum2", buf); //buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); - buf = blr_make_query("SET @mariadb_slave_capability=4); + buf = blr_make_query("SET @mariadb_slave_capability=4"); router->master_state = BLRM_GTIDMODE; router->master->func.write(router->master, buf); break; From 8afa46b8b2436dd9bb3680b5642b1c0d3cec7f32 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 7 May 2015 13:00:34 +0300 Subject: [PATCH 034/273] Removed BLRM_MAXSTATE_MARIADB10 and set BLRM_MAXSTATE to 0x014 --- server/modules/include/blr.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 48c48a194..fa377c7e5 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -317,8 +317,7 @@ typedef struct router_instance { #define BLRM_REGISTER 0x0012 #define BLRM_BINLOGDUMP 0x0013 #define BLRM_MARIADB10 0x0014 -#define BLRM_MAXSTATE 0x0013 -#define BLRM_MAXSTATE_MARIADB10 0x0014 +#define BLRM_MAXSTATE 0x0014 static char *blrm_states[] = { "Unconnected", "Connecting", "Authenticated", "Timestamp retrieval", "Server ID retrieval", "HeartBeat Period setup", "binlog checksum config", From e9391ef48639c6607bfcd5472d8996cb74c41614 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 7 May 2015 15:16:37 +0200 Subject: [PATCH 035/273] MariaDB 10 optional compatibility MariaDB 10 optional compatibility with mariadb10-compatibility=1 --- server/modules/include/blr.h | 94 +++++++++++----------- server/modules/routing/binlog/blr_master.c | 21 ++++- server/modules/routing/binlog/blr_slave.c | 6 +- 3 files changed, 68 insertions(+), 53 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index fa377c7e5..8aecd83f0 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -233,7 +233,7 @@ typedef struct { GWBUF *selectvercom; /*< select @@version_comment */ GWBUF *selecthostname;/*< select @@hostname */ GWBUF *map; /*< select @@max_allowed_packet */ - GWBUF *mariadb10; /*< set @mariadb_slave_capability=4 */ + GWBUF *mariadb10; /*< set @mariadb_slave_capability */ uint8_t *fde_event; /*< Format Description Event */ int fde_len; /*< Length of fde_event */ } MASTER_RESPONSES; @@ -242,55 +242,54 @@ typedef struct { * 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; /*< Server ID of the master */ - int serverid; /*< Server ID to use with 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; /*< UUID of the master */ - 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; - 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 */ - char binlog_name[BINLOG_FNAMELEN+1]; + 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; /*< Server ID of the master */ + int serverid; /*< Server ID to use with 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; /*< UUID of the master */ + 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; + 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 */ + char binlog_name[BINLOG_FNAMELEN+1]; /*< Name of the current binlog file */ - uint64_t binlog_position; + uint64_t binlog_position; /*< Current binlog position */ - int binlog_fd; /*< File descriptor of the binlog + int binlog_fd; /*< File descriptor of the binlog * file being written */ - uint64_t last_written; /*< Position of last event written */ - 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; - struct router_instance - *next; + uint64_t last_written; /*< Position of last event written */ + 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; + struct router_instance *next; } ROUTER_INSTANCE; /** @@ -317,6 +316,7 @@ typedef struct router_instance { #define BLRM_REGISTER 0x0012 #define BLRM_BINLOGDUMP 0x0013 #define BLRM_MARIADB10 0x0014 + #define BLRM_MAXSTATE 0x0014 static char *blrm_states[] = { "Unconnected", "Connecting", "Authenticated", "Timestamp retrieval", @@ -324,7 +324,7 @@ static char *blrm_states[] = { "Unconnected", "Connecting", "Authenticated", "Ti "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 @@mx_allowed_packet", "Register slave", "Binlog Dump","Set MariaDB slave capability" }; + "select @@mx_allowed_packet", "Register slave", "Binlog Dump", "Set MariaDB slave capability" }; #define BLRS_CREATED 0x0000 #define BLRS_UNREGISTERED 0x0001 diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index bc1e676d0..2efa417e4 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -448,12 +448,27 @@ char query[128]; GWBUF_CONSUME_ALL(router->saved_master.chksum2); router->saved_master.chksum2 = buf; blr_cache_response(router, "chksum2", buf); - //buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); - buf = blr_make_query("SET @mariadb_slave_capability=4"); - router->master_state = BLRM_GTIDMODE; + + 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_registration(router); + router->master_state = BLRM_REGISTER; + 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) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 8a008e35f..fe2bc2dff 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -366,11 +366,11 @@ int query_len; free(query_text); return blr_slave_replay(router, slave, router->saved_master.heartbeat); } - else if (strcasecmp(word, "@mariadb_slave_capability") == 0) + else if (strcasecmp(word, "@mariadb_slave_capability") == 0) { free(query_text); - return blr_slave_replay(router, slave, router->saved_master.gtid_mode); - //return blr_slave_send_ok(router, slave); + if (router->mariadb10_compat) + return blr_slave_replay(router, slave, router->saved_master.mariadb10); } else if (strcasecmp(word, "@master_binlog_checksum") == 0) { From 3f2876bde35d11464638c88cc3c0f52ff13316ee Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 7 May 2015 15:32:12 +0200 Subject: [PATCH 036/273] Fixed buffer free Fixed buffer free --- server/modules/routing/binlog/blr_slave.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index fe2bc2dff..1928e5d9d 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -368,9 +368,10 @@ int query_len; } else if (strcasecmp(word, "@mariadb_slave_capability") == 0) { - free(query_text); - if (router->mariadb10_compat) + if (router->mariadb10_compat) { + free(query_text); return blr_slave_replay(router, slave, router->saved_master.mariadb10); + } } else if (strcasecmp(word, "@master_binlog_checksum") == 0) { From 2c2a03a6f696894cb015f32a0ae9d2ca50123806 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 7 May 2015 16:10:35 +0200 Subject: [PATCH 037/273] Always reply to SET @mariadb_slave_capability Always reply to SET @mariadb_slave_capability, with saved master reply for mariadb10 master or with OK otherwise --- server/modules/routing/binlog/blr_slave.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 1928e5d9d..c963d8ab4 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -368,9 +368,11 @@ int query_len; } else if (strcasecmp(word, "@mariadb_slave_capability") == 0) { + free(query_text); if (router->mariadb10_compat) { - free(query_text); 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) From 7d48779913c7ebf62bd1eed7ce5a3f437bfee0ef Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 7 May 2015 17:02:33 +0200 Subject: [PATCH 038/273] Added MAX_EVENT_TYPE_MARIADB10 check Added MAX_EVENT_TYPE_MARIADB10 check for router->mariadb10_compat --- server/modules/include/blr.h | 3 ++- server/modules/routing/binlog/blr_file.c | 23 +++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 8aecd83f0..7b57fd15f 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -397,7 +397,8 @@ static char *blrs_states[] = { "Created", "Unregistered", "Registered", #define ANONYMOUS_GTID_EVENT 0x22 #define PREVIOUS_GTIDS_EVENT 0x23 -#define MAX_EVENT_TYPE 0xa3 +#define MAX_EVENT_TYPE 0x23 +#define MAX_EVENT_TYPE_MARIADB10 0xa3 /** * Binlog event flags diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 2473909ec..ec7864d5e 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -443,15 +443,26 @@ struct stat statb; hdr->next_pos = EXTRACT32(&hdbuf[13]); hdr->flags = EXTRACT16(&hdbuf[17]); - if (hdr->event_type > MAX_EVENT_TYPE) - { - LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, - "Invalid event type 0x%x. " + if (router->mariadb10_compat) { + if (hdr->event_type > MAX_EVENT_TYPE_MARIADB10) { + LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, + "Invalid MariaDB 10 event type 0x%x. " "Binlog file is %s, position %d", hdr->event_type, file->binlogname, pos))); - return NULL; - } + return NULL; + } + } else { + if (hdr->event_type > MAX_EVENT_TYPE) { + LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, + "Invalid event type 0x%x. " + "Binlog file is %s, position %d", + hdr->event_type, + file->binlogname, pos))); + + return NULL; + } + } if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT) { From 5d1e09ca4f22ba0a5402a089173cf4652ad71e08 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 7 May 2015 17:14:39 +0200 Subject: [PATCH 039/273] Added MariaDB 10 Compatibility without GTID Added MariaDB 10 Compatibility without GTID --- server/modules/routing/binlog/blr.c | 2 ++ server/modules/routing/binlog/blr_file.c | 3 ++- server/modules/routing/binlog/blr_master.c | 3 ++- server/modules/routing/binlog/blr_slave.c | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 2f5a38bee..071563952 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -35,6 +35,8 @@ * 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 + * * @endverbatim */ diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index ec7864d5e..e59a7be19 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -23,8 +23,9 @@ * @verbatim * Revision History * - * Date Who Description + * Date Who Description * 14/04/2014 Mark Riddoch Initial implementation + * 07/05/2015 Massimiliano Pinto Added MAX_EVENT_TYPE_MARIADB10 * * @endverbatim */ diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 2efa417e4..f230442f1 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -31,8 +31,9 @@ * @verbatim * Revision History * - * Date Who Description + * Date Who Description * 02/04/2014 Mark Riddoch Initial implementation + * 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility * * @endverbatim */ diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index c963d8ab4..141ff3a03 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -36,9 +36,11 @@ * 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 * * @endverbatim */ + #include #include #include From 230f88737cca9e6a5811f7261a2bc63eff0adf6a Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 7 May 2015 18:05:04 +0200 Subject: [PATCH 040/273] Added reading saved mariadb10 data Added reading saved mariadb10 data --- server/modules/routing/binlog/blr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 071563952..becea18b0 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -395,6 +395,7 @@ unsigned char *defuuid; inst->saved_master.selectvercom = blr_cache_read_response(inst, "selectvercom"); inst->saved_master.selecthostname = blr_cache_read_response(inst, "selecthostname"); inst->saved_master.map = blr_cache_read_response(inst, "map"); + inst->saved_master.mariadb10 = blr_cache_read_response(inst, "mariadb10"); /* * Initialise the binlog file and position From 5a3ed0de9bf8309a8dfc8d338cb8bddd78178165 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 03:54:54 +0300 Subject: [PATCH 041/273] Moved common monitor functionality to the MONITOR type Common variables, like databases, timeouts and interval, and functionality was moved to the MONITOR type. This reduces the redundant functionality of the monitor API's functions like registerServer and setInterval. --- server/core/monitor.c | 98 +++++- server/include/monitor.h | 42 ++- server/modules/monitor/galeramon.c | 252 ++------------- server/modules/monitor/galeramon.h | 13 +- server/modules/monitor/mm_mon.c | 180 ++--------- server/modules/monitor/monitor_common.h | 23 -- server/modules/monitor/mysql_mon.c | 409 +++++++----------------- server/modules/monitor/mysqlmon.h | 13 +- server/modules/monitor/ndbcluster_mon.c | 229 ++----------- 9 files changed, 323 insertions(+), 936 deletions(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index a8401e6bb..70f3f9efa 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -80,7 +80,13 @@ MONITOR *mon; } mon->handle = NULL; - + mon->databases = NULL; + mon->name = NULL; + mon->password = NULL; + mon->read_timeout = DEFAULT_READ_TIMEOUT; + mon->write_timeout = DEFAULT_WRITE_TIMEOUT; + mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT; + spinlock_init(&mon->lock); spinlock_acquire(&monLock); mon->next = allMonitors; allMonitors = mon; @@ -100,7 +106,7 @@ monitor_free(MONITOR *mon) { MONITOR *ptr; - mon->module->stopMonitor(mon->handle); + mon->module->stopMonitor(mon); mon->state = MONITOR_STATE_FREED; spinlock_acquire(&monLock); if (allMonitors == mon) @@ -127,7 +133,7 @@ MONITOR *ptr; void monitorStart(MONITOR *monitor, void* params) { - monitor->handle = (*monitor->module->startMonitor)(monitor->handle,params); + monitor->handle = (*monitor->module->startMonitor)(monitor,params); monitor->state = MONITOR_STATE_RUNNING; } @@ -142,7 +148,7 @@ monitorStop(MONITOR *monitor) if(monitor->state != MONITOR_STATE_STOPPED) { monitor->state = MONITOR_STATE_STOPPING; - monitor->module->stopMonitor(monitor->handle); + monitor->module->stopMonitor(monitor); monitor->state = MONITOR_STATE_STOPPED; } } @@ -175,7 +181,30 @@ MONITOR *ptr; void monitorAddServer(MONITOR *mon, SERVER *server) { - mon->module->registerServer(mon->handle, server); + MONITOR_SERVERS *ptr, *db; + + if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL) + return; + db->server = server; + db->con = NULL; + db->next = NULL; + db->mon_err_count = 0; + db->mon_prev_status = 0; + /* pending status is updated by get_replication_tree */ + db->pending_status = 0; + + spinlock_acquire(&mon->lock); + + if (mon->databases == NULL) + mon->databases = db; + else + { + ptr = mon->databases; + while (ptr->next != NULL) + ptr = ptr->next; + ptr->next = db; + } + spinlock_release(&mon->lock); } /** @@ -189,7 +218,9 @@ monitorAddServer(MONITOR *mon, SERVER *server) void monitorAddUser(MONITOR *mon, char *user, char *passwd) { - mon->module->defaultUser(mon->handle, user, passwd); + mon->user = strdup(user); + mon->password = strdup(passwd); + //mon->module->defaultUser(mon->handle, user, passwd); } /** @@ -288,10 +319,7 @@ MONITOR *ptr; void monitorSetInterval (MONITOR *mon, unsigned long interval) { - if (mon->module->setInterval != NULL) { - mon->interval = interval; - mon->module->setInterval(mon->handle, interval); - } + mon->interval = interval; } /** @@ -303,9 +331,55 @@ monitorSetInterval (MONITOR *mon, unsigned long interval) */ void monitorSetNetworkTimeout(MONITOR *mon, int type, int value) { - if (mon->module->setNetworkTimeout != NULL) { - mon->module->setNetworkTimeout(mon->handle, type, value); + + int max_timeout = (int)(mon->interval/1000); + int new_timeout = max_timeout -1; + + if (new_timeout <= 0) + new_timeout = DEFAULT_CONNECT_TIMEOUT; + + switch(type) { + case MONITOR_CONNECT_TIMEOUT: + if (value < max_timeout) { + memcpy(&mon->connect_timeout, &value, sizeof(int)); + } else { + memcpy(&mon->connect_timeout, &new_timeout, sizeof(int)); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds" + ", lowering to %i seconds", value, max_timeout, new_timeout))); } + break; + + case MONITOR_READ_TIMEOUT: + if (value < max_timeout) { + memcpy(&mon->read_timeout, &value, sizeof(int)); + } else { + memcpy(&mon->read_timeout, &new_timeout, sizeof(int)); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds" + ", lowering to %i seconds", value, max_timeout, new_timeout))); + } + break; + + case MONITOR_WRITE_TIMEOUT: + if (value < max_timeout) { + memcpy(&mon->write_timeout, &value, sizeof(int)); + } else { + memcpy(&mon->write_timeout, &new_timeout, sizeof(int)); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds" + ", lowering to %i seconds", value, max_timeout, new_timeout))); + } + break; + default: + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Monitor setNetworkTimeout received an unsupported action type %i", type))); + break; + } } /** diff --git a/server/include/monitor.h b/server/include/monitor.h index e114f3424..681f34bac 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -17,6 +17,7 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ +#include #include #include #include @@ -69,19 +70,14 @@ typedef struct { void *(*startMonitor)(void *, void*); void (*stopMonitor)(void *); - void (*registerServer)(void *, SERVER *); - void (*unregisterServer)(void *, SERVER *); - void (*defaultUser)(void *, char *, char *); void (*diagnostics)(DCB *, void *); - void (*setInterval)(void *, size_t); - void (*setNetworkTimeout)(void *, int, int); } MONITOR_OBJECT; /** * The monitor API version number. Any change to the monitor module API * must change these versions usign the rules defined in modinfo.h */ -#define MONITOR_VERSION {2, 0, 0} +#define MONITOR_VERSION {3, 0, 0} /** Monitor's poll frequency */ #define MON_BASE_INTERVAL_MS 100 @@ -112,12 +108,46 @@ typedef enum #define DEFAULT_READ_TIMEOUT 1 #define DEFAULT_WRITE_TIMEOUT 2 + +#define MONITOR_RUNNING 1 +#define MONITOR_STOPPING 2 +#define MONITOR_STOPPED 3 + +#define MONITOR_INTERVAL 10000 // in milliseconds +#define MONITOR_DEFAULT_ID 1UL // unsigned long value +#define MONITOR_MAX_NUM_SLAVES 20 //number of MySQL slave servers associated to a MySQL master server + + +/** + * The linked list of servers that are being monitored by the monitor module. + */ +typedef struct monitor_servers { + SERVER *server; /**< The server being monitored */ + MYSQL *con; /**< The MySQL connection */ + int mon_err_count; + unsigned int mon_prev_status; + unsigned int pending_status; /**< Pending Status flag bitmap */ + struct monitor_servers + *next; /**< The next server in the list */ +} MONITOR_SERVERS; + /** * Representation of the running monitor. */ typedef struct monitor { char *name; /**< The name of the monitor module */ + char* user; /*< Monitor username */ + char* password; /*< Monitor password */ + SPINLOCK lock; + MONITOR_SERVERS* databases; /*< List of databases the monitor monitors */ monitor_state_t state; /**< The state of the monitor */ + int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */ + int read_timeout; /**< Timeout in seconds to read from the server. + * There are retries and the total effective timeout value is three times the option value. + */ + int write_timeout; /**< Timeout in seconds for each attempt to write to the server. + * There are retries and the total effective timeout value is two times the option value. + */ MONITOR_OBJECT *module; /**< The "monitor object" */ void *handle; /**< Handle returned from startMonitor */ size_t interval; /**< The monitor interval */ diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 0991e72bb..fae50ef4e 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -60,7 +60,7 @@ extern __thread log_info_t tls_log_info; static void monitorMain(void *); -static char *version_str = "V1.5.0"; +static char *version_str = "V2.0.0"; MODULE_INFO info = { MODULE_API_MONITOR, @@ -85,13 +85,8 @@ static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); static MONITOR_OBJECT MyObject = { startMonitor, - stopMonitor, - registerServer, - unregisterServer, - defaultUsers, - diagnostics, - setInterval, - setNetworkTimeout + stopMonitor, + diagnostics }; /** @@ -142,30 +137,23 @@ GetModuleObject() static void * startMonitor(void *arg,void* opt) { -GALERA_MONITOR *handle; + MONITOR* mon = arg; +GALERA_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (arg != NULL) + if (handle != NULL) { - handle = (GALERA_MONITOR *)arg; handle->shutdown = 0; } else { if ((handle = (GALERA_MONITOR *)malloc(sizeof(GALERA_MONITOR))) == NULL) return NULL; - handle->databases = NULL; handle->shutdown = 0; - handle->defaultUser = NULL; - handle->defaultPasswd = NULL; handle->id = MONITOR_DEFAULT_ID; - handle->interval = MONITOR_INTERVAL; handle->disableMasterFailback = 0; handle->availableWhenDonor = 0; - handle->disableMasterRoleSetting = 0; + handle->disableMasterRoleSetting = 0; handle->master = NULL; - handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT; - handle->read_timeout=DEFAULT_READ_TIMEOUT; - handle->write_timeout=DEFAULT_WRITE_TIMEOUT; handle->master_down_script = NULL; spinlock_init(&handle->lock); } @@ -200,82 +188,13 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; static void stopMonitor(void *arg) { -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle; handle->shutdown = 1; thread_wait((void *)handle->tid); } -/** - * Register a server that must be added to the monitored servers for - * a monitoring module. - * - * @param arg A handle on the running monitor module - * @param server The server to add - */ -static void -registerServer(void *arg, SERVER *server) -{ -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; -MONITOR_SERVERS *ptr, *db; - - if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL) - return; - db->server = server; - db->con = NULL; - db->next = NULL; - spinlock_acquire(&handle->lock); - if (handle->databases == NULL) - handle->databases = db; - else - { - ptr = handle->databases; - while (ptr->next != NULL) - ptr = ptr->next; - ptr->next = db; - } - spinlock_release(&handle->lock); -} - -/** - * Remove a server from those being monitored by a monitoring module - * - * @param arg A handle on the running monitor module - * @param server The server to remove - */ -static void -unregisterServer(void *arg, SERVER *server) -{ -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; -MONITOR_SERVERS *ptr, *lptr; - - spinlock_acquire(&handle->lock); - if (handle->databases == NULL) - { - spinlock_release(&handle->lock); - return; - } - if (handle->databases->server == server) - { - ptr = handle->databases; - handle->databases = handle->databases->next; - free(ptr); - } - else - { - ptr = handle->databases; - while (ptr->next != NULL && ptr->next->server != server) - ptr = ptr->next; - if (ptr->next) - { - lptr = ptr->next; - ptr->next = ptr->next->next; - free(lptr); - } - } - spinlock_release(&handle->lock); -} - /** * Diagnostic interface * @@ -285,7 +204,8 @@ MONITOR_SERVERS *ptr, *lptr; static void diagnostics(DCB *dcb, void *arg) { -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle; MONITOR_SERVERS *db; char *sep; @@ -302,16 +222,16 @@ char *sep; break; } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval); + dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); dcb_printf(dcb,"\tMaster Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on"); dcb_printf(dcb,"\tAvailable when Donor:\t%s\n", (handle->availableWhenDonor == 1) ? "on" : "off"); dcb_printf(dcb,"\tMaster Role Setting Disabled:\t%s\n", (handle->disableMasterRoleSetting == 1) ? "on" : "off"); - dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout); - dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout); - dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout); + dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); + dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); + dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); dcb_printf(dcb, "\tMonitored servers: "); - db = handle->databases; + db = mon->databases; sep = ""; while (db) { @@ -322,27 +242,6 @@ char *sep; dcb_printf(dcb, "\n"); } -/** - * Set the default username and password to use to monitor if the server does not - * override this. - * - * @param arg The handle allocated by startMonitor - * @param uname The default user name - * @param passwd The default password - */ -static void -defaultUsers(void *arg, char *uname, char *passwd) -{ -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; - - if (handle->defaultUser) - free(handle->defaultUser); - if (handle->defaultPasswd) - free(handle->defaultPasswd); - handle->defaultUser = strdup(uname); - handle->defaultPasswd = strdup(passwd); -} - /** * Monitor an individual server * @@ -350,14 +249,14 @@ GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; * @param database The database to probe */ static void -monitorDatabase(GALERA_MONITOR *handle, MONITOR_SERVERS *database) +monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) { + GALERA_MONITOR* handle = (GALERA_MONITOR*)mon->handle; MYSQL_ROW row; MYSQL_RES *result; -int num_fields; int isjoined = 0; -char *uname = handle->defaultUser; -char *passwd = handle->defaultPasswd; +char *uname = mon->user; +char *passwd = mon->password; unsigned long int server_version = 0; char *server_string; @@ -379,16 +278,15 @@ char *server_string; if (database->con == NULL || mysql_ping(database->con) != 0) { char *dpwd = decryptPassword(passwd); - int rc; - int connect_timeout = handle->connect_timeout; - int read_timeout = handle->read_timeout; - int write_timeout = handle->write_timeout; + int connect_timeout = mon->connect_timeout; + int read_timeout = mon->read_timeout; + int write_timeout = mon->write_timeout; database->con = mysql_init(NULL); - rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); - rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); - rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); + mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); + mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); + mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); if (mysql_real_connect(database->con, database->server->name, uname, dpwd, NULL, database->server->port, NULL, 0) == NULL) @@ -433,9 +331,6 @@ char *server_string; /* If we get this far then we have a working connection */ server_set_status(database->server, SERVER_RUNNING); - /* get server version from current server */ - server_version = mysql_get_server_version(database->con); - /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); if (server_string) { @@ -448,7 +343,6 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state'") == 0 && (result = mysql_store_result(database->con)) != NULL) { - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { if (strcmp(row[1], "4") == 0) @@ -459,7 +353,6 @@ char *server_string; if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0 && (result = mysql_store_result(database->con)) != NULL) { - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { if (strncmp(row[1], "xtrabackup", 10) == 0) @@ -476,7 +369,6 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long local_index = -1; - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { local_index = strtol(row[1], NULL, 10); @@ -504,7 +396,8 @@ char *server_string; static void monitorMain(void *arg) { -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; +GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle; MONITOR_SERVERS *ptr; size_t nrounds = 0; MONITOR_SERVERS *candidate_master = NULL; @@ -540,7 +433,7 @@ int log_no_members = 1; * round. */ - if (nrounds != 0 && ((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >= MON_BASE_INTERVAL_MS) + if (nrounds != 0 && ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= MON_BASE_INTERVAL_MS) { nrounds += 1; continue; @@ -551,11 +444,11 @@ int log_no_members = 1; /* reset cluster members counter */ is_cluster=0; - ptr = handle->databases; + ptr = mon->databases; while (ptr) { - monitorDatabase(handle, ptr); + monitorDatabase(mon, ptr); if(ptr->mon_prev_status & SERVER_MASTER && SERVER_IS_DOWN(ptr->server)) @@ -619,7 +512,7 @@ int log_no_members = 1; */ /* get the candidate master, following MIN(node_id) rule */ - candidate_master = get_candidate_master(handle->databases); + candidate_master = get_candidate_master(mon->databases); /* Select the master, based on master_stickiness */ if (1 == handle->disableMasterRoleSetting) { @@ -629,7 +522,7 @@ int log_no_members = 1; handle->master = set_cluster_master(handle->master, candidate_master, master_stickiness); } - ptr = handle->databases; + ptr = mon->databases; while (ptr) { if (!SERVER_IS_JOINED(ptr->server) || SERVER_IN_MAINT(ptr->server)) { @@ -681,19 +574,6 @@ int log_no_members = 1; } } -/** - * Set the monitor sampling interval. - * - * @param arg The handle allocated by startMonitor - * @param interval The interval to set in monitor struct, in milliseconds - */ -static void -setInterval(void *arg, size_t interval) -{ -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; - memcpy(&handle->interval, &interval, sizeof(unsigned long)); -} - /** * get candidate master from all nodes * @@ -796,67 +676,6 @@ GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; memcpy(&handle->availableWhenDonor, &disable, sizeof(int)); } -/** - * Set the timeouts to use in the monitor. - * - * @param arg The handle allocated by startMonitor - * @param type The connect timeout type - * @param value The timeout value to set - */ -static void -setNetworkTimeout(void *arg, int type, int value) -{ -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; -int max_timeout = (int)(handle->interval/1000); -int new_timeout = max_timeout -1; - - if (new_timeout <= 0) - new_timeout = DEFAULT_CONNECT_TIMEOUT; - - switch(type) { - case MONITOR_CONNECT_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->connect_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->connect_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - - case MONITOR_READ_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->read_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->read_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - - case MONITOR_WRITE_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->write_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->write_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - default: - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Monitor setNetworkTimeout received an unsupported action type %i", type))); - break; - } -} - /** * Check if current monitored server status has changed * @@ -890,10 +709,7 @@ static bool mon_print_fail_status( { bool succp; int errcount = mon_srv->mon_err_count; - uint8_t modval; - - modval = 1<<(MIN(errcount/10, 7)); - + if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0) { succp = true; diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h index 2b804965d..956be528f 100644 --- a/server/modules/monitor/galeramon.h +++ b/server/modules/monitor/galeramon.h @@ -42,24 +42,13 @@ typedef struct { pthread_t tid; /**< id of monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ int status; /**< Monitor status */ - char *defaultUser; /**< Default username for monitoring */ - char *defaultPasswd; /**< Default password for monitoring */ - unsigned long interval; /**< Monitor sampling interval */ unsigned long id; /**< Monitor ID */ int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ - MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */ - int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */ - int read_timeout; /**< Timeout in seconds to read from the server. - * There are retries and the total effective timeout value is three times the option value. - */ - int write_timeout; /**< Timeout in seconds for each attempt to write to the server. - * There are retries and the total effective timeout value is two times the option value. - */ - EXTERNCMD* master_down_script; + EXTERNCMD* master_down_script; } GALERA_MONITOR; #endif diff --git a/server/modules/monitor/mm_mon.c b/server/modules/monitor/mm_mon.c index 60acea825..705583f6c 100644 --- a/server/modules/monitor/mm_mon.c +++ b/server/modules/monitor/mm_mon.c @@ -49,7 +49,7 @@ extern __thread log_info_t tls_log_info; static void monitorMain(void *); -static char *version_str = "V1.0.1"; +static char *version_str = "V1.1.1"; MODULE_INFO info = { MODULE_API_MONITOR, @@ -60,26 +60,18 @@ MODULE_INFO info = { static void *startMonitor(void *,void*); static void stopMonitor(void *); -static void registerServer(void *, SERVER *); -static void unregisterServer(void *, SERVER *); -static void defaultUser(void *, char *, char *); static void diagnostics(DCB *, void *); -static void setInterval(void *, size_t); static void detectStaleMaster(void *, int); static bool mon_status_changed(MONITOR_SERVERS* mon_srv); static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); -static MONITOR_SERVERS *get_current_master(MYSQL_MONITOR *); +static MONITOR_SERVERS *get_current_master(MONITOR *); static void monitor_set_pending_status(MONITOR_SERVERS *, int); static void monitor_clear_pending_status(MONITOR_SERVERS *, int); static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, - registerServer, - unregisterServer, - defaultUser, - diagnostics, - setInterval + diagnostics }; /** @@ -131,23 +123,19 @@ GetModuleObject() static void * startMonitor(void *arg,void* opt) { -MYSQL_MONITOR *handle; + MONITOR* mon = (MONITOR*)arg; +MYSQL_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (arg) + if (handle) { - handle = arg; /* Must be a restart */ handle->shutdown = 0; } else { if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) return NULL; - handle->databases = NULL; handle->shutdown = 0; - handle->defaultUser = NULL; - handle->defaultPasswd = NULL; handle->id = MONITOR_DEFAULT_ID; - handle->interval = MONITOR_INTERVAL; handle->replicationHeartbeat = 0; handle->detectStaleMaster = 0; handle->master = NULL; @@ -179,103 +167,6 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; thread_wait((void *)handle->tid); } -/** - * Register a server that must be added to the monitored servers for - * a monitoring module. - * - * @param arg A handle on the running monitor module - * @param server The server to add - */ -static void -registerServer(void *arg, SERVER *server) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -MONITOR_SERVERS *ptr, *db; - - if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL) - return; - db->server = server; - db->con = NULL; - db->next = NULL; - db->mon_err_count = 0; - db->mon_prev_status = 0; - /* pending status is updated by monitorMain */ - db->pending_status = 0; - - spinlock_acquire(&handle->lock); - - if (handle->databases == NULL) - handle->databases = db; - else - { - ptr = handle->databases; - while (ptr->next != NULL) - ptr = ptr->next; - ptr->next = db; - } - spinlock_release(&handle->lock); -} - -/** - * Remove a server from those being monitored by a monitoring module - * - * @param arg A handle on the running monitor module - * @param server The server to remove - */ -static void -unregisterServer(void *arg, SERVER *server) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -MONITOR_SERVERS *ptr, *lptr; - - spinlock_acquire(&handle->lock); - if (handle->databases == NULL) - { - spinlock_release(&handle->lock); - return; - } - if (handle->databases->server == server) - { - ptr = handle->databases; - handle->databases = handle->databases->next; - free(ptr); - } - else - { - ptr = handle->databases; - while (ptr->next != NULL && ptr->next->server != server) - ptr = ptr->next; - if (ptr->next) - { - lptr = ptr->next; - ptr->next = ptr->next->next; - free(lptr); - } - } - spinlock_release(&handle->lock); -} - -/** - * Set the default username and password to use to monitor if the server does not - * override this. - * - * @param arg The handle allocated by startMonitor - * @param uname The default user name - * @param passwd The default password - */ -static void -defaultUser(void *arg, char *uname, char *passwd) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - - if (handle->defaultUser) - free(handle->defaultUser); - if (handle->defaultPasswd) - free(handle->defaultPasswd); - handle->defaultUser = strdup(uname); - handle->defaultPasswd = strdup(passwd); -} - /** * Daignostic interface * @@ -284,7 +175,8 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; */ static void diagnostics(DCB *dcb, void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; MONITOR_SERVERS *db; char *sep; @@ -301,11 +193,11 @@ char *sep; break; } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval); + dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); dcb_printf(dcb, "\tMonitored servers: "); - db = handle->databases; + db = mon->databases; sep = ""; while (db) { @@ -327,15 +219,16 @@ char *sep; * @param database The database to probe */ static void -monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database) +monitorDatabase(MONITOR* mon, MONITOR_SERVERS *database) { + MYSQL_MONITOR *handle = mon->handle; MYSQL_ROW row; MYSQL_RES *result; int num_fields; int isslave = 0; int ismaster = 0; -char *uname = handle->defaultUser; -char *passwd = handle->defaultPasswd; +char *uname = mon->user; +char *passwd = mon->password; unsigned long int server_version = 0; char *server_string; @@ -358,12 +251,11 @@ char *server_string; if (database->con == NULL || mysql_ping(database->con) != 0) { char *dpwd = decryptPassword(passwd); - int rc; int read_timeout = 1; database->con = mysql_init(NULL); - rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); + mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); if (mysql_real_connect(database->con, database->server->name, @@ -437,7 +329,7 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long server_id = -1; - num_fields = mysql_num_fields(result); + while ((row = mysql_fetch_row(result))) { server_id = strtol(row[0], NULL, 10); @@ -463,7 +355,7 @@ char *server_string; { int i = 0; long master_id = -1; - num_fields = mysql_num_fields(result); + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -502,7 +394,7 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long master_id = -1; - num_fields = mysql_num_fields(result); + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -534,7 +426,7 @@ char *server_string; if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0 && (result = mysql_store_result(database->con)) != NULL) { - num_fields = mysql_num_fields(result); + while ((row = mysql_fetch_row(result))) { if (strncasecmp(row[1], "OFF", 3) == 0) { @@ -584,7 +476,8 @@ char *server_string; static void monitorMain(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; MONITOR_SERVERS *ptr; int detect_stale_master = handle->detectStaleMaster; MONITOR_SERVERS *root_master; @@ -619,7 +512,7 @@ size_t nrounds = 0; * round. */ if (nrounds != 0 && - ((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >= + ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= MON_BASE_INTERVAL_MS) { nrounds += 1; @@ -628,7 +521,7 @@ size_t nrounds = 0; nrounds += 1; /* start from the first server in the list */ - ptr = handle->databases; + ptr = mon->databases; while (ptr) { @@ -636,7 +529,7 @@ size_t nrounds = 0; ptr->pending_status = ptr->server->status; /* monitor current node */ - monitorDatabase(handle, ptr); + monitorDatabase(mon, ptr); if (mon_status_changed(ptr)) { @@ -668,11 +561,11 @@ size_t nrounds = 0; } /* Get Master server pointer */ - root_master = get_current_master(handle); + root_master = get_current_master(mon); /* Update server status from monitor pending status on that server*/ - ptr = handle->databases; + ptr = mon->databases; while (ptr) { if (! SERVER_IN_MAINT(ptr->server)) { @@ -691,19 +584,6 @@ size_t nrounds = 0; } } } - -/** - * Set the monitor sampling interval. - * - * @param arg The handle allocated by startMonitor - * @param interval The interval to set in monitor struct, in milliseconds - */ -static void -setInterval(void *arg, size_t interval) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - memcpy(&handle->interval, &interval, sizeof(unsigned long)); -} /** * Enable/Disable the MySQL Replication Stale Master dectection, allowing a previouvsly detected master to still act as a Master. @@ -716,7 +596,8 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; static void detectStaleMaster(void *arg, int enable) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; memcpy(&handle->detectStaleMaster, &enable, sizeof(int)); } @@ -790,10 +671,11 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) * @return The server at root level with SERVER_MASTER bit */ -static MONITOR_SERVERS *get_current_master(MYSQL_MONITOR *handle) { +static MONITOR_SERVERS *get_current_master(MONITOR *mon) { + MYSQL_MONITOR* handle = mon->handle; MONITOR_SERVERS *ptr; - ptr = handle->databases; + ptr = mon->databases; while (ptr) { diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h index 1efbf1dbc..e3ba513e4 100644 --- a/server/modules/monitor/monitor_common.h +++ b/server/modules/monitor/monitor_common.h @@ -31,27 +31,4 @@ * @endverbatim */ -#define MONITOR_RUNNING 1 -#define MONITOR_STOPPING 2 -#define MONITOR_STOPPED 3 - -#define MONITOR_INTERVAL 10000 // in milliseconds -#define MONITOR_DEFAULT_ID 1UL // unsigned long value -#define MONITOR_MAX_NUM_SLAVES 20 //number of MySQL slave servers associated to a MySQL master server - - -/** - * The linked list of servers that are being monitored by the MySQL - * Monitor module. - */ -typedef struct monitor_servers { - SERVER *server; /**< The server being monitored */ - MYSQL *con; /**< The MySQL connection */ - int mon_err_count; - unsigned int mon_prev_status; - unsigned int pending_status; /**< Pending Status flag bitmap */ - struct monitor_servers - *next; /**< The next server in the list */ -} MONITOR_SERVERS; - #endif \ No newline at end of file diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index aced27a13..19eb91b96 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -84,20 +84,15 @@ MODULE_INFO info = { static void *startMonitor(void *,void*); static void stopMonitor(void *); -static void registerServer(void *, SERVER *); -static void unregisterServer(void *, SERVER *); -static void defaultUser(void *, char *, char *); static void diagnostics(DCB *, void *); -static void setInterval(void *, size_t); static void defaultId(void *, unsigned long); -static void setNetworkTimeout(void *, int, int); static bool mon_status_changed(MONITOR_SERVERS* mon_srv); static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long); static MONITOR_SERVERS *getSlaveOfNodeId(MONITOR_SERVERS *, long); -static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *, int); +static MONITOR_SERVERS *get_replication_tree(MONITOR *, int); static void set_master_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *); -static void set_slave_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *); +static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *); static int add_slave_to_master(long *, int, long); static void monitor_set_pending_status(MONITOR_SERVERS *, int); static void monitor_clear_pending_status(MONITOR_SERVERS *, int); @@ -105,12 +100,7 @@ static void monitor_clear_pending_status(MONITOR_SERVERS *, int); static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, - registerServer, - unregisterServer, - defaultUser, - diagnostics, - setInterval, - setNetworkTimeout + diagnostics }; /** @@ -157,55 +147,50 @@ GetModuleObject() * This function creates a thread to execute the actual monitoring. * * @param arg The current handle - NULL if first start + * @param opt Configuration parameters * @return A handle to use when interacting with the monitor */ static void * startMonitor(void *arg, void* opt) { -MYSQL_MONITOR *handle; -CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (arg) - { - handle = arg; /* Must be a restart */ - handle->shutdown = 0; - } - else - { - if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) - return NULL; - handle->databases = NULL; - handle->shutdown = 0; - handle->defaultUser = NULL; - handle->defaultPasswd = NULL; - handle->id = config_get_gateway_id(); - handle->interval = MONITOR_INTERVAL; - handle->replicationHeartbeat = 0; - handle->detectStaleMaster = 0; - handle->master = NULL; - handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT; - handle->read_timeout=DEFAULT_READ_TIMEOUT; - handle->write_timeout=DEFAULT_WRITE_TIMEOUT; - handle->master_down_script = NULL; - spinlock_init(&handle->lock); - } + MONITOR* monitor = (MONITOR*)arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR*)monitor->handle; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; + if (arg) + { + handle = arg; /* Must be a restart */ + handle->shutdown = 0; + } + else + { + if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) + return NULL; + handle->shutdown = 0; + handle->id = config_get_gateway_id(); + handle->replicationHeartbeat = 0; + handle->detectStaleMaster = 0; + handle->master = NULL; + handle->master_down_script = NULL; + spinlock_init(&handle->lock); + } - while(params) + while(params) + { + if(!strcmp(params->name,"detect_stale_master")) + handle->detectStaleMaster = config_truth_value(params->value); + else if(!strcmp(params->name,"detect_replication_lag")) + handle->replicationHeartbeat = config_truth_value(params->value); + else if(!strcmp(params->name,"master_down_script")) { - if(!strcmp(params->name,"detect_stale_master")) - handle->detectStaleMaster = config_truth_value(params->value); - else if(!strcmp(params->name,"detect_replication_lag")) - handle->replicationHeartbeat = config_truth_value(params->value); - else if(!strcmp(params->name,"master_down_script")) - { - if(handle->master_down_script) - externcmd_free(handle->master_down_script); - handle->master_down_script = externcmd_allocate(params->value); - } - params = params->next; + if(handle->master_down_script) + externcmd_free(handle->master_down_script); + handle->master_down_script = externcmd_allocate(params->value); } + params = params->next; + } - handle->tid = (THREAD)thread_start(monitorMain, handle); - return handle; + handle->tid = (THREAD)thread_start(monitorMain, handle); + return handle; } /** @@ -216,107 +201,11 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; static void stopMonitor(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; - handle->shutdown = 1; - thread_wait((void *)handle->tid); -} - -/** - * Register a server that must be added to the monitored servers for - * a monitoring module. - * - * @param arg A handle on the running monitor module - * @param server The server to add - */ -static void -registerServer(void *arg, SERVER *server) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -MONITOR_SERVERS *ptr, *db; - - if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL) - return; - db->server = server; - db->con = NULL; - db->next = NULL; - db->mon_err_count = 0; - db->mon_prev_status = 0; - /* pending status is updated by get_replication_tree */ - db->pending_status = 0; - - spinlock_acquire(&handle->lock); - - if (handle->databases == NULL) - handle->databases = db; - else - { - ptr = handle->databases; - while (ptr->next != NULL) - ptr = ptr->next; - ptr->next = db; - } - spinlock_release(&handle->lock); -} - -/** - * Remove a server from those being monitored by a monitoring module - * - * @param arg A handle on the running monitor module - * @param server The server to remove - */ -static void -unregisterServer(void *arg, SERVER *server) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -MONITOR_SERVERS *ptr, *lptr; - - spinlock_acquire(&handle->lock); - if (handle->databases == NULL) - { - spinlock_release(&handle->lock); - return; - } - if (handle->databases->server == server) - { - ptr = handle->databases; - handle->databases = handle->databases->next; - free(ptr); - } - else - { - ptr = handle->databases; - while (ptr->next != NULL && ptr->next->server != server) - ptr = ptr->next; - if (ptr->next) - { - lptr = ptr->next; - ptr->next = ptr->next->next; - free(lptr); - } - } - spinlock_release(&handle->lock); -} - -/** - * Set the default username and password to use to monitor if the server does not - * override this. - * - * @param arg The handle allocated by startMonitor - * @param uname The default user name - * @param passwd The default password - */ -static void -defaultUser(void *arg, char *uname, char *passwd) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - - if (handle->defaultUser) - free(handle->defaultUser); - if (handle->defaultPasswd) - free(handle->defaultPasswd); - handle->defaultUser = strdup(uname); - handle->defaultPasswd = strdup(passwd); + handle->shutdown = 1; + thread_wait((void *)handle->tid); } /** @@ -327,45 +216,46 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; */ static void diagnostics(DCB *dcb, void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -MONITOR_SERVERS *db; -char *sep; + MONITOR* mon = (MONITOR*)arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; + MONITOR_SERVERS *db; + char *sep; - switch (handle->status) - { - case MONITOR_RUNNING: - dcb_printf(dcb, "\tMonitor running\n"); - break; - case MONITOR_STOPPING: - dcb_printf(dcb, "\tMonitor stopping\n"); - break; - case MONITOR_STOPPED: - dcb_printf(dcb, "\tMonitor stopped\n"); - break; - } + switch (handle->status) + { + case MONITOR_RUNNING: + dcb_printf(dcb, "\tMonitor running\n"); + break; + case MONITOR_STOPPING: + dcb_printf(dcb, "\tMonitor stopping\n"); + break; + case MONITOR_STOPPED: + dcb_printf(dcb, "\tMonitor stopped\n"); + break; + } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval); - dcb_printf(dcb,"\tMaxScale MonitorId:\t%lu\n", handle->id); - dcb_printf(dcb,"\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled"); - dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); - dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout); - dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout); - dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout); - dcb_printf(dcb, "\tMonitored servers: "); - - db = handle->databases; - sep = ""; - while (db) - { - dcb_printf(dcb, - "%s%s:%d", - sep, - db->server->name, - db->server->port); - sep = ", "; - db = db->next; - } - dcb_printf(dcb, "\n"); + dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); + dcb_printf(dcb,"\tMaxScale MonitorId:\t%lu\n", handle->id); + dcb_printf(dcb,"\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled"); + dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); + dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); + dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); + dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); + dcb_printf(dcb, "\tMonitored servers: "); + + db = mon->databases; + sep = ""; + while (db) + { + dcb_printf(dcb, + "%s%s:%d", + sep, + db->server->name, + db->server->port); + sep = ", "; + db = db->next; + } + dcb_printf(dcb, "\n"); } /** @@ -375,14 +265,14 @@ char *sep; * @param database The database to probe */ static void -monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database) +monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) { + MYSQL_MONITOR* handle = mon->handle; MYSQL_ROW row; MYSQL_RES *result; -int num_fields; int isslave = 0; -char *uname = handle->defaultUser; -char *passwd = handle->defaultPasswd; +char *uname = mon->user; +char *passwd = mon->password; unsigned long int server_version = 0; char *server_string; @@ -405,16 +295,15 @@ char *server_string; if (database->con == NULL || mysql_ping(database->con) != 0) { char *dpwd = decryptPassword(passwd); - int rc; - int connect_timeout = handle->connect_timeout; - int read_timeout = handle->read_timeout; - int write_timeout = handle->write_timeout; + int connect_timeout = mon->connect_timeout; + int read_timeout = mon->read_timeout; + int write_timeout = mon->write_timeout; database->con = mysql_init(NULL); - rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); - rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); - rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); + mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); + mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); + mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); if (mysql_real_connect(database->con, database->server->name, @@ -493,7 +382,6 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long server_id = -1; - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { server_id = strtol(row[0], NULL, 10); @@ -519,7 +407,6 @@ char *server_string; { int i = 0; long master_id = -1; - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -558,7 +445,6 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long master_id = -1; - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -615,7 +501,8 @@ char *server_string; static void monitorMain(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = (MONITOR*) arg; +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; MONITOR_SERVERS *ptr; int replication_heartbeat = handle->replicationHeartbeat; int detect_stale_master = handle->detectStaleMaster; @@ -652,7 +539,7 @@ int log_no_master = 1; * round. */ if (nrounds != 0 && - ((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >= + ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= MON_BASE_INTERVAL_MS) { nrounds += 1; @@ -663,7 +550,7 @@ int log_no_master = 1; num_servers = 0; /* start from the first server in the list */ - ptr = handle->databases; + ptr = mon->databases; while (ptr) { @@ -671,7 +558,7 @@ int log_no_master = 1; ptr->pending_status = ptr->server->status; /* monitor current node */ - monitorDatabase(handle, ptr); + monitorDatabase(mon, ptr); /* reset the slave list of current node */ if (ptr->server->slaves) { @@ -753,7 +640,7 @@ int log_no_master = 1; ptr = ptr->next; } - ptr = handle->databases; + ptr = mon->databases; /* if only one server is configured, that's is Master */ if (num_servers == 1) { if (SERVER_IS_RUNNING(ptr->server)) { @@ -770,12 +657,12 @@ int log_no_master = 1; } } else { /* Compute the replication tree */ - root_master = get_replication_tree(handle, num_servers); + root_master = get_replication_tree(mon, num_servers); } /* Update server status from monitor pending status on that server*/ - ptr = handle->databases; + ptr = mon->databases; while (ptr) { if (! SERVER_IN_MAINT(ptr->server)) { @@ -852,7 +739,7 @@ int log_no_master = 1; SERVER_IS_RELAY_SERVER(root_master->server))) { set_master_heartbeat(handle, root_master); - ptr = handle->databases; + ptr = mon->databases; while (ptr) { if( (! SERVER_IN_MAINT(ptr->server)) && SERVER_IS_RUNNING(ptr->server)) @@ -861,7 +748,7 @@ int log_no_master = 1; (SERVER_IS_SLAVE(ptr->server) || SERVER_IS_RELAY_SERVER(ptr->server))) { - set_slave_heartbeat(handle, ptr); + set_slave_heartbeat(mon, ptr); } } ptr = ptr->next; @@ -883,19 +770,6 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; memcpy(&handle->id, &id, sizeof(unsigned long)); } -/** - * Set the monitor sampling interval. - * - * @param arg The handle allocated by startMonitor - * @param interval The interval to set in monitor struct, in milliseconds - */ -static void -setInterval(void *arg, size_t interval) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - memcpy(&handle->interval, &interval, sizeof(unsigned long)); -} - /** * Enable/Disable the MySQL Replication hearbeat, detecting slave lag behind master. * @@ -957,9 +831,6 @@ static bool mon_print_fail_status( { bool succp; int errcount = mon_srv->mon_err_count; - uint8_t modval; - - modval = 1<<(MIN(errcount/10, 7)); if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0) { @@ -1133,13 +1004,13 @@ static void set_master_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *databas * @param handle The monitor handle * @param database The number database server */ -static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database) { +static void set_slave_heartbeat(MONITOR* mon, MONITOR_SERVERS *database) { + MYSQL_MONITOR *handle = (MYSQL_MONITOR*)mon->handle; unsigned long id = handle->id; time_t heartbeat; char select_heartbeat_query[256] = ""; MYSQL_ROW row; MYSQL_RES *result; - int num_fields; if (handle->master == NULL) { LOGIF(LE, (skygw_log_write_flush( @@ -1159,7 +1030,6 @@ static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database if (handle->master !=NULL && (mysql_query(database->con, select_heartbeat_query) == 0 && (result = mysql_store_result(database->con)) != NULL)) { int rows_found = 0; - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { int rlag = -1; @@ -1184,7 +1054,7 @@ static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database if (rlag >= 0) { /* store rlag only if greater than monitor sampling interval */ - database->server->rlag = (rlag > (handle->interval / 1000)) ? rlag : 0; + database->server->rlag = (rlag > (mon->interval / 1000)) ? rlag : 0; } else { database->server->rlag = -1; } @@ -1238,7 +1108,8 @@ static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database * @return The server at root level with SERVER_MASTER bit */ -static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_servers) { +static MONITOR_SERVERS *get_replication_tree(MONITOR *mon, int num_servers) { + MYSQL_MONITOR* handle = (MYSQL_MONITOR*)mon->handle; MONITOR_SERVERS *ptr; MONITOR_SERVERS *backend; SERVER *current; @@ -1246,7 +1117,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv long node_id; int root_level; - ptr = handle->databases; + ptr = mon->databases; root_level = num_servers; while (ptr) @@ -1265,7 +1136,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv node_id = current->master_id; if (node_id < 1) { MONITOR_SERVERS *find_slave; - find_slave = getSlaveOfNodeId(handle->databases, current->node_id); + find_slave = getSlaveOfNodeId(mon->databases, current->node_id); if (find_slave == NULL) { current->depth = -1; @@ -1285,7 +1156,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv root_level = current->depth; handle->master = ptr; } - backend = getServerByNodeId(handle->databases, node_id); + backend = getServerByNodeId(mon->databases, node_id); if (backend) { node_id = backend->server->master_id; @@ -1301,7 +1172,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv MONITOR_SERVERS *master; current->depth = depth; - master = getServerByNodeId(handle->databases, current->master_id); + master = getServerByNodeId(mon->databases, current->master_id); if (master && master->server && master->server->node_id > 0) { add_slave_to_master(master->server->slaves, MONITOR_MAX_NUM_SLAVES, current->node_id); master->server->depth = current->depth -1; @@ -1383,65 +1254,3 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) { ptr->pending_status &= ~bit; } - -/** - * Set the default id to use in the monitor. - * - * @param arg The handle allocated by startMonitor - * @param type The connect timeout type - * @param value The timeout value to set - */ -static void -setNetworkTimeout(void *arg, int type, int value) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -int max_timeout = (int)(handle->interval/1000); -int new_timeout = max_timeout -1; - - if (new_timeout <= 0) - new_timeout = DEFAULT_CONNECT_TIMEOUT; - - switch(type) { - case MONITOR_CONNECT_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->connect_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->connect_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - - case MONITOR_READ_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->read_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->read_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - - case MONITOR_WRITE_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->write_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->write_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - default: - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Monitor setNetworkTimeout received an unsupported action type %i", type))); - break; - } -} - diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 93ba2093e..ff2bf0d3e 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -49,9 +49,6 @@ typedef struct { pthread_t tid; /**< id of monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ int status; /**< Monitor status */ - char *defaultUser; /**< Default username for monitoring */ - char *defaultPasswd; /**< Default password for monitoring */ - unsigned long interval; /**< Monitor sampling interval */ unsigned long id; /**< Monitor ID */ int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */ int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ @@ -59,15 +56,7 @@ typedef struct { int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ - MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */ - int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */ - int read_timeout; /**< Timeout in seconds to read from the server. - * There are retries and the total effective timeout value is three times the option value. - */ - int write_timeout; /**< Timeout in seconds for each attempt to write to the server. - * There are retries and the total effective timeout value is two times the option value. - */ - EXTERNCMD* master_down_script; + EXTERNCMD* master_down_script; } MYSQL_MONITOR; #endif diff --git a/server/modules/monitor/ndbcluster_mon.c b/server/modules/monitor/ndbcluster_mon.c index 8f7d00964..ca9ff4210 100644 --- a/server/modules/monitor/ndbcluster_mon.c +++ b/server/modules/monitor/ndbcluster_mon.c @@ -50,7 +50,7 @@ extern __thread log_info_t tls_log_info; static void monitorMain(void *); -static char *version_str = "V1.1.0"; +static char *version_str = "V2.1.0"; MODULE_INFO info = { MODULE_API_MONITOR, @@ -61,22 +61,12 @@ MODULE_INFO info = { static void *startMonitor(void *,void*); static void stopMonitor(void *); -static void registerServer(void *, SERVER *); -static void unregisterServer(void *, SERVER *); -static void defaultUsers(void *, char *, char *); static void diagnostics(DCB *, void *); -static void setInterval(void *, size_t); -static void setNetworkTimeout(void *arg, int type, int value); static MONITOR_OBJECT MyObject = { startMonitor, - stopMonitor, - registerServer, - unregisterServer, - defaultUsers, - diagnostics, - setInterval, - setNetworkTimeout + stopMonitor, + diagnostics }; /** @@ -127,26 +117,19 @@ GetModuleObject() static void * startMonitor(void *arg,void* opt) { -MYSQL_MONITOR *handle; + MONITOR* mon = (MONITOR*)arg; +MYSQL_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (arg != NULL) + if (handle != NULL) { - handle = (MYSQL_MONITOR *)arg; handle->shutdown = 0; } else { if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) return NULL; - handle->databases = NULL; handle->shutdown = 0; - handle->defaultUser = NULL; - handle->defaultPasswd = NULL; handle->id = MONITOR_DEFAULT_ID; - handle->interval = MONITOR_INTERVAL; - handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT; - handle->read_timeout=DEFAULT_READ_TIMEOUT; - handle->write_timeout=DEFAULT_WRITE_TIMEOUT; spinlock_init(&handle->lock); } @@ -162,82 +145,13 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; static void stopMonitor(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = (MONITOR*)arg; +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; handle->shutdown = 1; thread_wait((void *)handle->tid); } -/** - * Register a server that must be added to the monitored servers for - * a monitoring module. - * - * @param arg A handle on the running monitor module - * @param server The server to add - */ -static void -registerServer(void *arg, SERVER *server) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -MONITOR_SERVERS *ptr, *db; - - if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL) - return; - db->server = server; - db->con = NULL; - db->next = NULL; - spinlock_acquire(&handle->lock); - if (handle->databases == NULL) - handle->databases = db; - else - { - ptr = handle->databases; - while (ptr->next != NULL) - ptr = ptr->next; - ptr->next = db; - } - spinlock_release(&handle->lock); -} - -/** - * Remove a server from those being monitored by a monitoring module - * - * @param arg A handle on the running monitor module - * @param server The server to remove - */ -static void -unregisterServer(void *arg, SERVER *server) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -MONITOR_SERVERS *ptr, *lptr; - - spinlock_acquire(&handle->lock); - if (handle->databases == NULL) - { - spinlock_release(&handle->lock); - return; - } - if (handle->databases->server == server) - { - ptr = handle->databases; - handle->databases = handle->databases->next; - free(ptr); - } - else - { - ptr = handle->databases; - while (ptr->next != NULL && ptr->next->server != server) - ptr = ptr->next; - if (ptr->next) - { - lptr = ptr->next; - ptr->next = ptr->next->next; - free(lptr); - } - } - spinlock_release(&handle->lock); -} - /** * Diagnostic interface * @@ -247,7 +161,8 @@ MONITOR_SERVERS *ptr, *lptr; static void diagnostics(DCB *dcb, void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = arg; +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; MONITOR_SERVERS *db; char *sep; @@ -264,13 +179,13 @@ char *sep; break; } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval); - dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout); - dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout); - dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout); + dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); + dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); + dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); + dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); dcb_printf(dcb, "\tMonitored servers: "); - db = handle->databases; + db = mon->databases; sep = ""; while (db) { @@ -281,35 +196,15 @@ char *sep; dcb_printf(dcb, "\n"); } -/** - * Set the default username and password to use to monitor if the server does not - * override this. - * - * @param arg The handle allocated by startMonitor - * @param uname The default user name - * @param passwd The default password - */ -static void -defaultUsers(void *arg, char *uname, char *passwd) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - - if (handle->defaultUser) - free(handle->defaultUser); - if (handle->defaultPasswd) - free(handle->defaultPasswd); - handle->defaultUser = strdup(uname); - handle->defaultPasswd = strdup(passwd); -} - /** * Monitor an individual server * * @param database The database to probe */ static void -monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd, MYSQL_MONITOR *handle) +monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd, MONITOR *mon) { + MYSQL_MONITOR* handle = mon->handle; MYSQL_ROW row; MYSQL_RES *result; int num_fields; @@ -334,9 +229,9 @@ char *server_string; { char *dpwd = decryptPassword(passwd); int rc; - int connect_timeout = handle->connect_timeout; - int read_timeout = handle->read_timeout; - int write_timeout = handle->write_timeout; + int connect_timeout = mon->connect_timeout; + int read_timeout = mon->read_timeout; + int write_timeout = mon->write_timeout; database->con = mysql_init(NULL); @@ -433,7 +328,8 @@ char *server_string; static void monitorMain(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + MONITOR* mon = arg; +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; MONITOR_SERVERS *ptr; long master_id; size_t nrounds = 0; @@ -467,7 +363,7 @@ size_t nrounds = 0; * round. */ if (nrounds != 0 && - ((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >= + ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= MON_BASE_INTERVAL_MS) { nrounds += 1; @@ -475,12 +371,12 @@ size_t nrounds = 0; } nrounds += 1; master_id = -1; - ptr = handle->databases; + ptr = mon->databases; while (ptr) { unsigned int prev_status = ptr->server->status; - monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd,handle); + monitorDatabase(ptr, mon->user, mon->password,mon); if (ptr->server->status != prev_status || SERVER_IS_DOWN(ptr->server)) @@ -497,78 +393,3 @@ size_t nrounds = 0; } } } - -/** - * Set the monitor sampling interval. - * - * @param arg The handle allocated by startMonitor - * @param interval The interval to set in monitor struct, in milliseconds - */ -static void -setInterval(void *arg, size_t interval) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - memcpy(&handle->interval, &interval, sizeof(unsigned long)); -} - -/** - * Set the timeouts to use in the monitor. - * - * @param arg The handle allocated by startMonitor - * @param type The connect timeout type - * @param value The timeout value to set - */ -static void -setNetworkTimeout(void *arg, int type, int value) -{ -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; -int max_timeout = (int)(handle->interval/1000); -int new_timeout = max_timeout -1; - - if (new_timeout <= 0) - new_timeout = DEFAULT_CONNECT_TIMEOUT; - - switch(type) { - case MONITOR_CONNECT_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->connect_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->connect_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - - case MONITOR_READ_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->read_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->read_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - - case MONITOR_WRITE_TIMEOUT: - if (value < max_timeout) { - memcpy(&handle->write_timeout, &value, sizeof(int)); - } else { - memcpy(&handle->write_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); - } - break; - default: - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Monitor setNetworkTimeout received an unsupported action type %i", type))); - break; - } -} - From ba3c29c72b28fcbf84a511700d384689a42cd074 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 11:01:16 +0300 Subject: [PATCH 042/273] Fixed old function calls using monitor handle instead of MONITOR type. --- server/core/monitor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index 70f3f9efa..9981b2188 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -28,6 +28,7 @@ * and monitor id * 30/10/14 Massimiliano Pinto Addition of disable_master_failback parameter * 07/11/14 Massimiliano Pinto Addition of monitor network timeouts + * 08/05/15 Markus Makela Moved common monitor variables to MONITOR struct * * @endverbatim */ @@ -220,7 +221,6 @@ monitorAddUser(MONITOR *mon, char *user, char *passwd) { mon->user = strdup(user); mon->password = strdup(passwd); - //mon->module->defaultUser(mon->handle, user, passwd); } /** @@ -240,7 +240,7 @@ MONITOR *ptr; dcb_printf(dcb, "Monitor: %p\n", ptr); dcb_printf(dcb, "\tName: %s\n", ptr->name); if (ptr->module->diagnostics) - ptr->module->diagnostics(dcb, ptr->handle); + ptr->module->diagnostics(dcb, ptr); ptr = ptr->next; } spinlock_release(&monLock); @@ -258,7 +258,7 @@ monitorShow(DCB *dcb, MONITOR *monitor) dcb_printf(dcb, "Monitor: %p\n", monitor); dcb_printf(dcb, "\tName: %s\n", monitor->name); if (monitor->module->diagnostics) - monitor->module->diagnostics(dcb, monitor->handle); + monitor->module->diagnostics(dcb, monitor); } /** From 6e1a69df7e4d544c618b482acafad37f4018788e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 14:03:52 +0300 Subject: [PATCH 043/273] Changed callable monitor scripts to be called with command line parameters instead of having multiple scripts. This reduces the amount of guesswork the monitor has to do when executing the script and places that burden on the called script. --- server/core/monitor.c | 3 +- server/modules/monitor/mysql_mon.c | 138 +++++++++++++++++++++++++---- server/modules/monitor/mysqlmon.h | 2 +- 3 files changed, 123 insertions(+), 20 deletions(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index 9981b2188..b2ed8d1a2 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -190,7 +190,8 @@ monitorAddServer(MONITOR *mon, SERVER *server) db->con = NULL; db->next = NULL; db->mon_err_count = 0; - db->mon_prev_status = 0; + /** Server status is uninitialized */ + db->mon_prev_status = -1; /* pending status is updated by get_replication_tree */ db->pending_status = 0; diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index f3a383c2e..5f6fe6535 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -22,7 +22,7 @@ * @verbatim * Revision History * - * Date Who Demaster_down_scription + * Date Who Description * 08/07/13 Mark Riddoch Initial implementation * 11/07/13 Mark Riddoch Addition of code to check replication * status @@ -46,6 +46,7 @@ * 10/11/14 Massimiliano Pinto Addition of setNetworkTimeout for connect, read, write * 18/11/14 Massimiliano Pinto One server only in configuration becomes master. * servers=server1 must be present in mysql_mon and in router sections as well. + * 08/05/15 Markus Makela Added launchable scripts * * @endverbatim */ @@ -66,6 +67,8 @@ #include #include +#define MON_ARG_MAX 8192 + /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; @@ -96,6 +99,8 @@ static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *); static int add_slave_to_master(long *, int, long); static void monitor_set_pending_status(MONITOR_SERVERS *, int); static void monitor_clear_pending_status(MONITOR_SERVERS *, int); +char* mon_get_event_type(MONITOR_SERVERS*); +void mon_append_node_names(MONITOR_SERVERS*,char*,int); static MONITOR_OBJECT MyObject = { startMonitor, @@ -156,9 +161,8 @@ startMonitor(void *arg, void* opt) MONITOR* monitor = (MONITOR*)arg; MYSQL_MONITOR *handle = (MYSQL_MONITOR*)monitor->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (arg) + if (handle) { - handle = arg; /* Must be a restart */ handle->shutdown = 0; } else @@ -170,7 +174,7 @@ startMonitor(void *arg, void* opt) handle->replicationHeartbeat = 0; handle->detectStaleMaster = 0; handle->master = NULL; - handle->master_down_script = NULL; + handle->script = NULL; spinlock_init(&handle->lock); } @@ -180,16 +184,16 @@ startMonitor(void *arg, void* opt) handle->detectStaleMaster = config_truth_value(params->value); else if(!strcmp(params->name,"detect_replication_lag")) handle->replicationHeartbeat = config_truth_value(params->value); - else if(!strcmp(params->name,"master_down_script")) + else if(!strcmp(params->name,"script")) { - if(handle->master_down_script) - externcmd_free(handle->master_down_script); - handle->master_down_script = externcmd_allocate(params->value); + if(handle->script) + free(handle->script); + handle->script = strdup(params->value); } params = params->next; } - handle->tid = (THREAD)thread_start(monitorMain, handle); + handle->tid = (THREAD)thread_start(monitorMain, monitor); return handle; } @@ -554,6 +558,8 @@ int log_no_master = 1; while (ptr) { + ptr->mon_prev_status = ptr->server->status; + /* copy server status into monitor pending_status */ ptr->pending_status = ptr->server->status; @@ -579,14 +585,6 @@ int log_no_master = 1; "Server %s:%d lost the master status.", ptr->server->name, ptr->server->port))); - if(handle->master_down_script) - { - if(externcmd_execute(handle->master_down_script)) - skygw_log_write(LOGFILE_ERROR, - "Error: Failed to execute command " - "'%s' on server state change.", - handle->master_down_script->parameters[0]); - } } /** * Here we say: If the server's state changed @@ -694,6 +692,33 @@ int log_no_master = 1; } } else { ptr->server->status = ptr->pending_status; + + if(mon_status_changed(ptr)) + { + /** Execute monitor script */ + if(handle->script && strcmp(mon_get_event_type(ptr),"unknown") != 0) + { + char argstr[PATH_MAX + MON_ARG_MAX + 1]; + snprintf(argstr,PATH_MAX + MON_ARG_MAX, + "%s --event=%s --node=%s --nodelist=", + handle->script, + mon_get_event_type(ptr), + ptr->server->unique_name); + mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX + 1); + + EXTERNCMD* cmd = externcmd_allocate(argstr); + if(externcmd_execute(cmd)) + { + skygw_log_write(LOGFILE_ERROR, + "Error: Failed to execute script " + "'%s' on server state change.", + handle->script); + } + externcmd_free(cmd); + skygw_log_write(LOGFILE_TRACE,"monitor_state_change: %s: %s", + ptr->server->unique_name,mon_get_event_type(ptr)); + } + } } } ptr = ptr->next; @@ -808,7 +833,11 @@ static bool mon_status_changed( MONITOR_SERVERS* mon_srv) { bool succp; - + + /** This is the first time the server was set with a status*/ + if (mon_srv->mon_prev_status == -1) + return false; + if (mon_srv->mon_prev_status != mon_srv->server->status) { succp = true; @@ -1255,3 +1284,76 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) ptr->pending_status &= ~bit; } +char* mon_get_event_type(MONITOR_SERVERS* node) +{ + unsigned int prev = node->mon_prev_status; + + if((prev & (SERVER_MASTER|SERVER_RUNNING)) == (SERVER_MASTER|SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) + { + return "master_down"; + } + if((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) + { + return "master_up"; + } + if((prev & (SERVER_SLAVE|SERVER_RUNNING)) == (SERVER_SLAVE|SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) + { + return "slave_down"; + } + if((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) + { + return "slave_up"; + } + if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && + SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) + { + return "new_master"; + } + if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && + SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) + { + return "new_slave"; + } + if((prev & (SERVER_RUNNING|SERVER_MASTER)) == (SERVER_RUNNING|SERVER_MASTER) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_MASTER(node->server)) + { + return "lost_master"; + } + if((prev & (SERVER_RUNNING|SERVER_SLAVE)) == (SERVER_RUNNING|SERVER_SLAVE) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_SLAVE(node->server)) + { + return "lost_slave"; + } + if((prev & SERVER_RUNNING) == 0 && + SERVER_IS_RUNNING(node->server)) + { + return "server_up"; + } + if((prev & SERVER_RUNNING) == SERVER_RUNNING && + SERVER_IS_DOWN(node->server)) + { + return "server_down"; + } + return "unknown"; +} + +void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) +{ + MONITOR_SERVERS* ptr = start; + bool first = true; + + while(ptr) + { + if(!first) + { + strncat(str,",",len); + } + first = false; + strncat(str,ptr->server->unique_name,len); + ptr = ptr->next; + } +} \ No newline at end of file diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index ff2bf0d3e..836131a63 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -56,7 +56,7 @@ typedef struct { int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ - EXTERNCMD* master_down_script; + char* script; /*< Script to call when state changes occur on servers */ } MYSQL_MONITOR; #endif From 8af302d785a3c3fde9e6cf716e6474d8cdaf8543 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 14:19:25 +0300 Subject: [PATCH 044/273] Removed unused variables from monitors. --- server/modules/monitor/CMakeLists.txt | 8 +- server/modules/monitor/galeramon.c | 14 +-- server/modules/monitor/galeramon.h | 19 +++- server/modules/monitor/mm_mon.c | 15 +-- server/modules/monitor/monitor_common.c | 115 +++++++++++++++++++++++ server/modules/monitor/monitor_common.h | 16 ++-- server/modules/monitor/mysql_mon.c | 118 +----------------------- server/modules/monitor/mysqlmon.h | 20 +++- server/modules/monitor/ndbcluster_mon.c | 31 +------ 9 files changed, 171 insertions(+), 185 deletions(-) create mode 100644 server/modules/monitor/monitor_common.c diff --git a/server/modules/monitor/CMakeLists.txt b/server/modules/monitor/CMakeLists.txt index f53083882..8f364fe5b 100644 --- a/server/modules/monitor/CMakeLists.txt +++ b/server/modules/monitor/CMakeLists.txt @@ -1,16 +1,16 @@ -add_library(mysqlmon SHARED mysql_mon.c) +add_library(mysqlmon SHARED mysql_mon.c monitor_common.c) target_link_libraries(mysqlmon log_manager utils) install(TARGETS mysqlmon DESTINATION modules) -add_library(galeramon SHARED galeramon.c) +add_library(galeramon SHARED galeramon.c monitor_common.c) target_link_libraries(galeramon log_manager utils) install(TARGETS galeramon DESTINATION modules) -add_library(ndbclustermon SHARED ndbcluster_mon.c) +add_library(ndbclustermon SHARED ndbcluster_mon.c monitor_common.c) target_link_libraries(ndbclustermon log_manager utils) install(TARGETS ndbclustermon DESTINATION modules) if(BUILD_MMMON) - add_library(mmmon SHARED mm_mon.c) + add_library(mmmon SHARED mm_mon.c monitor_common.c) target_link_libraries(mmmon log_manager utils) install(TARGETS mmmon DESTINATION modules) endif() diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 0bc393580..e04fcef8d 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -38,20 +38,8 @@ * @endverbatim */ -#include -#include -#include -#include + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h index 956be528f..8a9b95fc9 100644 --- a/server/modules/monitor/galeramon.h +++ b/server/modules/monitor/galeramon.h @@ -18,9 +18,22 @@ * Copyright MariaDB Corporation Ab 2013-2014 */ -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /** * @file galeramon.h - The Galera cluster monitor diff --git a/server/modules/monitor/mm_mon.c b/server/modules/monitor/mm_mon.c index 4c6050cab..acb5b20b1 100644 --- a/server/modules/monitor/mm_mon.c +++ b/server/modules/monitor/mm_mon.c @@ -28,20 +28,9 @@ * @endverbatim */ -#include -#include -#include -#include + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include + /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c new file mode 100644 index 000000000..b9c6ce28e --- /dev/null +++ b/server/modules/monitor/monitor_common.c @@ -0,0 +1,115 @@ +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2015 + */ + +#include + +/** + * Set a pending status bit in the monitor server + * + * @param server The server to update + * @param bit The bit to clear for the server + */ +void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit) +{ + ptr->pending_status |= bit; +} + +/** + * Clear a pending status bit in the monitor server + * + * @param server The server to update + * @param bit The bit to clear for the server + */ +void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) +{ + ptr->pending_status &= ~bit; +} + +char* mon_get_event_type(MONITOR_SERVERS* node) +{ + unsigned int prev = node->mon_prev_status; + + if((prev & (SERVER_MASTER|SERVER_RUNNING)) == (SERVER_MASTER|SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) + { + return "master_down"; + } + if((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) + { + return "master_up"; + } + if((prev & (SERVER_SLAVE|SERVER_RUNNING)) == (SERVER_SLAVE|SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) + { + return "slave_down"; + } + if((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) + { + return "slave_up"; + } + if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && + SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) + { + return "new_master"; + } + if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && + SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) + { + return "new_slave"; + } + if((prev & (SERVER_RUNNING|SERVER_MASTER)) == (SERVER_RUNNING|SERVER_MASTER) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_MASTER(node->server)) + { + return "lost_master"; + } + if((prev & (SERVER_RUNNING|SERVER_SLAVE)) == (SERVER_RUNNING|SERVER_SLAVE) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_SLAVE(node->server)) + { + return "lost_slave"; + } + if((prev & SERVER_RUNNING) == 0 && + SERVER_IS_RUNNING(node->server)) + { + return "server_up"; + } + if((prev & SERVER_RUNNING) == SERVER_RUNNING && + SERVER_IS_DOWN(node->server)) + { + return "server_down"; + } + return "unknown"; +} + +void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) +{ + MONITOR_SERVERS* ptr = start; + bool first = true; + + while(ptr) + { + if(!first) + { + strncat(str,",",len); + } + first = false; + strncat(str,ptr->server->unique_name,len); + ptr = ptr->next; + } +} \ No newline at end of file diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h index e3ba513e4..2d56c7e81 100644 --- a/server/modules/monitor/monitor_common.h +++ b/server/modules/monitor/monitor_common.h @@ -15,20 +15,24 @@ * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright MariaDB Corporation Ab 2013-2014 + * Copyright MariaDB Corporation Ab 2013-2015 */ -#include -#include - +#include +#include +#include /** * @file monitor_common.h - The generic monitor structures all monitors use * * Revision History * * Date Who Description - * 07/05/15 Markus Makela Initial Implementation of galeramon.h + * 07/05/15 Markus Makela Initial Implementation * @endverbatim */ -#endif \ No newline at end of file +void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len); +char* mon_get_event_type(MONITOR_SERVERS* node); +void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit); +void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit); +#endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 5f6fe6535..8cd83763e 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -51,21 +51,9 @@ * @endverbatim */ -#include -#include -#include -#include + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + #define MON_ARG_MAX 8192 @@ -97,10 +85,6 @@ static MONITOR_SERVERS *get_replication_tree(MONITOR *, int); static void set_master_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *); static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *); static int add_slave_to_master(long *, int, long); -static void monitor_set_pending_status(MONITOR_SERVERS *, int); -static void monitor_clear_pending_status(MONITOR_SERVERS *, int); -char* mon_get_event_type(MONITOR_SERVERS*); -void mon_append_node_names(MONITOR_SERVERS*,char*,int); static MONITOR_OBJECT MyObject = { startMonitor, @@ -1259,101 +1243,3 @@ static int add_slave_to_master(long *slaves_list, int list_size, long node_id) { } return 0; } - -/** - * Set a pending status bit in the monior server - * - * @param server The server to update - * @param bit The bit to clear for the server - */ -static void -monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit) -{ - ptr->pending_status |= bit; -} - -/** - * Clear a pending status bit in the monior server - * - * @param server The server to update - * @param bit The bit to clear for the server - */ -static void -monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) -{ - ptr->pending_status &= ~bit; -} - -char* mon_get_event_type(MONITOR_SERVERS* node) -{ - unsigned int prev = node->mon_prev_status; - - if((prev & (SERVER_MASTER|SERVER_RUNNING)) == (SERVER_MASTER|SERVER_RUNNING) && - SERVER_IS_DOWN(node->server)) - { - return "master_down"; - } - if((prev & (SERVER_RUNNING)) == 0 && - SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) - { - return "master_up"; - } - if((prev & (SERVER_SLAVE|SERVER_RUNNING)) == (SERVER_SLAVE|SERVER_RUNNING) && - SERVER_IS_DOWN(node->server)) - { - return "slave_down"; - } - if((prev & (SERVER_RUNNING)) == 0 && - SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) - { - return "slave_up"; - } - if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && - SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) - { - return "new_master"; - } - if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && - SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) - { - return "new_slave"; - } - if((prev & (SERVER_RUNNING|SERVER_MASTER)) == (SERVER_RUNNING|SERVER_MASTER) && - SERVER_IS_RUNNING(node->server) && !SERVER_IS_MASTER(node->server)) - { - return "lost_master"; - } - if((prev & (SERVER_RUNNING|SERVER_SLAVE)) == (SERVER_RUNNING|SERVER_SLAVE) && - SERVER_IS_RUNNING(node->server) && !SERVER_IS_SLAVE(node->server)) - { - return "lost_slave"; - } - if((prev & SERVER_RUNNING) == 0 && - SERVER_IS_RUNNING(node->server)) - { - return "server_up"; - } - if((prev & SERVER_RUNNING) == SERVER_RUNNING && - SERVER_IS_DOWN(node->server)) - { - return "server_down"; - } - return "unknown"; -} - -void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) -{ - MONITOR_SERVERS* ptr = start; - bool first = true; - - while(ptr) - { - if(!first) - { - strncat(str,",",len); - } - first = false; - strncat(str,ptr->server->unique_name,len); - ptr = ptr->next; - } -} \ No newline at end of file diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 836131a63..f5d0e2a37 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -17,10 +17,22 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ - -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /** * @file mysqlmon.h - The MySQL monitor functionality within the gateway * diff --git a/server/modules/monitor/ndbcluster_mon.c b/server/modules/monitor/ndbcluster_mon.c index 7ea091264..26d3db708 100644 --- a/server/modules/monitor/ndbcluster_mon.c +++ b/server/modules/monitor/ndbcluster_mon.c @@ -29,20 +29,9 @@ * @endverbatim */ -#include -#include -#include -#include + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include + /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; @@ -207,10 +196,8 @@ monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPassw MYSQL_MONITOR* handle = mon->handle; MYSQL_ROW row; MYSQL_RES *result; -int num_fields; int isjoined = 0; char *uname = defaultUser, *passwd = defaultPasswd; -unsigned long int server_version = 0; char *server_string; if (database->server->monuser != NULL) @@ -228,7 +215,6 @@ char *server_string; if (database->con == NULL || mysql_ping(database->con) != 0) { char *dpwd = decryptPassword(passwd); - int rc; int connect_timeout = mon->connect_timeout; int read_timeout = mon->read_timeout; int write_timeout = mon->write_timeout; @@ -237,9 +223,9 @@ char *server_string; mysql_close(database->con); database->con = mysql_init(NULL); - rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); - rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); - rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); + mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); + mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); + mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); if (mysql_real_connect(database->con, database->server->name, uname, dpwd, NULL, database->server->port, NULL, 0) == NULL) @@ -270,9 +256,6 @@ char *server_string; /* If we get this far then we have a working connection */ server_set_status(database->server, SERVER_RUNNING); - /* get server version from current server */ - server_version = mysql_get_server_version(database->con); - /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); if (server_string) { @@ -285,7 +268,6 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0 && (result = mysql_store_result(database->con)) != NULL) { - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { if (atoi(row[1]) > 0) @@ -299,7 +281,6 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long cluster_node_id = -1; - num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result))) { cluster_node_id = strtol(row[1], NULL, 10); @@ -333,7 +314,6 @@ monitorMain(void *arg) MONITOR* mon = arg; MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; MONITOR_SERVERS *ptr; -long master_id; size_t nrounds = 0; if (mysql_thread_init()) @@ -372,7 +352,6 @@ size_t nrounds = 0; continue; } nrounds += 1; - master_id = -1; ptr = mon->databases; while (ptr) From 0c15812340d60f0325d34ffe663c09d85d196506 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 14:29:26 +0300 Subject: [PATCH 045/273] Moved common functions to monitor_common.c --- server/modules/monitor/galeramon.c | 71 +++---------------------- server/modules/monitor/galeramon.h | 2 +- server/modules/monitor/mm_mon.c | 7 +-- server/modules/monitor/monitor_common.c | 51 +++++++++++++++++- server/modules/monitor/monitor_common.h | 2 + server/modules/monitor/mysql_mon.c | 51 ------------------ server/modules/monitor/ndbcluster_mon.c | 6 +-- 7 files changed, 63 insertions(+), 127 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index e04fcef8d..ea45e8ff1 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -59,17 +59,10 @@ MODULE_INFO info = { static void *startMonitor(void *,void*); static void stopMonitor(void *); -static void registerServer(void *, SERVER *); -static void unregisterServer(void *, SERVER *); -static void defaultUsers(void *, char *, char *); static void diagnostics(DCB *, void *); -static void setInterval(void *, size_t); static MONITOR_SERVERS *get_candidate_master(MONITOR_SERVERS *); static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *, MONITOR_SERVERS *, int); static void disableMasterFailback(void *, int); -static void setNetworkTimeout(void *arg, int type, int value); -static bool mon_status_changed(MONITOR_SERVERS* mon_srv); -static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); static MONITOR_OBJECT MyObject = { startMonitor, @@ -142,7 +135,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->availableWhenDonor = 0; handle->disableMasterRoleSetting = 0; handle->master = NULL; - handle->master_down_script = NULL; + handle->script = NULL; spinlock_init(&handle->lock); } @@ -157,9 +150,9 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->disableMasterRoleSetting = config_truth_value(params->value); else if(!strcmp(params->name,"master_down_script")) { - if(handle->master_down_script) - externcmd_free(handle->master_down_script); - handle->master_down_script = externcmd_allocate(params->value); + if(handle->script) + free(handle->script); + handle->script = strdup(params->value); } params = params->next; } @@ -438,16 +431,9 @@ int log_no_members = 1; while (ptr) { - monitorDatabase(mon, ptr); + ptr->mon_prev_status = ptr->server->status; - if(ptr->mon_prev_status & SERVER_MASTER && - SERVER_IS_DOWN(ptr->server)) - { - if(externcmd_execute(handle->master_down_script) == -1) - { - skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute master server failure script in galeramon."); - } - } + monitorDatabase(mon, ptr); /* clear bits for non member nodes */ if ( ! SERVER_IN_MAINT(ptr->server) && (ptr->server->node_id < 0 || ! SERVER_IS_JOINED(ptr->server))) { @@ -665,48 +651,3 @@ availableWhenDonor(void *arg, int disable) GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; memcpy(&handle->availableWhenDonor, &disable, sizeof(int)); } - -/** - * Check if current monitored server status has changed - * - * @param mon_srv The monitored server - * @return true if status has changed or false - */ -static bool mon_status_changed( - MONITOR_SERVERS* mon_srv) -{ - bool succp; - - if (mon_srv->mon_prev_status != mon_srv->server->status) - { - succp = true; - } - else - { - succp = false; - } - return succp; -} - -/** - * Check if current monitored server has a loggable failure status - * - * @param mon_srv The monitored server - * @return true if failed status can be logged or false - */ -static bool mon_print_fail_status( - MONITOR_SERVERS* mon_srv) -{ - bool succp; - int errcount = mon_srv->mon_err_count; - - if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0) - { - succp = true; - } - else - { - succp = false; - } - return succp; -} diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h index 8a9b95fc9..3a9c41e7f 100644 --- a/server/modules/monitor/galeramon.h +++ b/server/modules/monitor/galeramon.h @@ -61,7 +61,7 @@ typedef struct { int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ - EXTERNCMD* master_down_script; + char* script; } GALERA_MONITOR; #endif diff --git a/server/modules/monitor/mm_mon.c b/server/modules/monitor/mm_mon.c index acb5b20b1..46adc68b5 100644 --- a/server/modules/monitor/mm_mon.c +++ b/server/modules/monitor/mm_mon.c @@ -51,11 +51,7 @@ static void *startMonitor(void *,void*); static void stopMonitor(void *); static void diagnostics(DCB *, void *); static void detectStaleMaster(void *, int); -static bool mon_status_changed(MONITOR_SERVERS* mon_srv); -static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); static MONITOR_SERVERS *get_current_master(MONITOR *); -static void monitor_set_pending_status(MONITOR_SERVERS *, int); -static void monitor_clear_pending_status(MONITOR_SERVERS *, int); static MONITOR_OBJECT MyObject = { startMonitor, @@ -138,7 +134,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; params = params->next; } - handle->tid = (THREAD)thread_start(monitorMain, handle); + handle->tid = (THREAD)thread_start(monitorMain, mon); return handle; } @@ -213,7 +209,6 @@ monitorDatabase(MONITOR* mon, MONITOR_SERVERS *database) MYSQL_MONITOR *handle = mon->handle; MYSQL_ROW row; MYSQL_RES *result; -int num_fields; int isslave = 0; int ismaster = 0; char *uname = mon->user; diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index b9c6ce28e..2001b2bec 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -112,4 +112,53 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) strncat(str,ptr->server->unique_name,len); ptr = ptr->next; } -} \ No newline at end of file +} + +/** + * Check if current monitored server status has changed + * + * @param mon_srv The monitored server + * @return true if status has changed or false + */ +bool mon_status_changed( + MONITOR_SERVERS* mon_srv) +{ + bool succp; + + /** This is the first time the server was set with a status*/ + if (mon_srv->mon_prev_status == -1) + return false; + + if (mon_srv->mon_prev_status != mon_srv->server->status) + { + succp = true; + } + else + { + succp = false; + } + return succp; +} + +/** + * Check if current monitored server has a loggable failure status + * + * @param mon_srv The monitored server + * @return true if failed status can be logged or false + */ +bool mon_print_fail_status( + MONITOR_SERVERS* mon_srv) +{ + bool succp; + int errcount = mon_srv->mon_err_count; + + if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0) + { + succp = true; + } + else + { + succp = false; + } + return succp; +} diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h index 2d56c7e81..1c493b10a 100644 --- a/server/modules/monitor/monitor_common.h +++ b/server/modules/monitor/monitor_common.h @@ -35,4 +35,6 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len); char* mon_get_event_type(MONITOR_SERVERS* node); void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit); void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit); +bool mon_status_changed(MONITOR_SERVERS* mon_srv); +bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); #endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 8cd83763e..a60e10e5e 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -77,8 +77,6 @@ static void *startMonitor(void *,void*); static void stopMonitor(void *); static void diagnostics(DCB *, void *); static void defaultId(void *, unsigned long); -static bool mon_status_changed(MONITOR_SERVERS* mon_srv); -static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long); static MONITOR_SERVERS *getSlaveOfNodeId(MONITOR_SERVERS *, long); static MONITOR_SERVERS *get_replication_tree(MONITOR *, int); @@ -807,55 +805,6 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; memcpy(&handle->detectStaleMaster, &enable, sizeof(int)); } -/** - * Check if current monitored server status has changed - * - * @param mon_srv The monitored server - * @return true if status has changed or false - */ -static bool mon_status_changed( - MONITOR_SERVERS* mon_srv) -{ - bool succp; - - /** This is the first time the server was set with a status*/ - if (mon_srv->mon_prev_status == -1) - return false; - - if (mon_srv->mon_prev_status != mon_srv->server->status) - { - succp = true; - } - else - { - succp = false; - } - return succp; -} - -/** - * Check if current monitored server has a loggable failure status - * - * @param mon_srv The monitored server - * @return true if failed status can be logged or false - */ -static bool mon_print_fail_status( - MONITOR_SERVERS* mon_srv) -{ - bool succp; - int errcount = mon_srv->mon_err_count; - - if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0) - { - succp = true; - } - else - { - succp = false; - } - return succp; -} - /** * Fetch a MySQL node by node_id * diff --git a/server/modules/monitor/ndbcluster_mon.c b/server/modules/monitor/ndbcluster_mon.c index 26d3db708..235eb7f00 100644 --- a/server/modules/monitor/ndbcluster_mon.c +++ b/server/modules/monitor/ndbcluster_mon.c @@ -122,7 +122,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; spinlock_init(&handle->lock); } - handle->tid = (THREAD)thread_start(monitorMain, handle); + handle->tid = (THREAD)thread_start(monitorMain, mon); return handle; } @@ -356,10 +356,10 @@ size_t nrounds = 0; while (ptr) { - unsigned int prev_status = ptr->server->status; + ptr->mon_prev_status = ptr->server->status; monitorDatabase(ptr, mon->user, mon->password,mon); - if (ptr->server->status != prev_status || + if (ptr->server->status != ptr->mon_prev_status || SERVER_IS_DOWN(ptr->server)) { LOGIF(LD, (skygw_log_write_flush( From 9845923f816355acd3786c2cb1315a820643f4e8 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 17:37:34 +0300 Subject: [PATCH 046/273] Added full implementation of MXS-121 to all monitors. --- server/modules/monitor/galeramon.c | 24 +++- server/modules/monitor/mm_mon.c | 76 +++++++---- server/modules/monitor/monitor_common.c | 174 ++++++++++++++++++++++-- server/modules/monitor/monitor_common.h | 36 ++++- server/modules/monitor/mysql_mon.c | 51 +++---- server/modules/monitor/ndbcluster_mon.c | 57 ++++++-- 6 files changed, 334 insertions(+), 84 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index ea45e8ff1..a99ea952b 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -148,7 +148,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; handle->availableWhenDonor = config_truth_value(params->value); else if(!strcmp(params->name,"disable_master_role_setting")) handle->disableMasterRoleSetting = config_truth_value(params->value); - else if(!strcmp(params->name,"master_down_script")) + else if(!strcmp(params->name,"script")) { if(handle->script) free(handle->script); @@ -157,7 +157,7 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; params = params->next; } - handle->tid = (THREAD)thread_start(monitorMain, handle); + handle->tid = (THREAD)thread_start(monitorMain, mon); return handle; } @@ -547,6 +547,26 @@ int log_no_members = 1; log_no_members = 1; } } + + + ptr = mon->databases; + + while(ptr) + { + /** Execute monitor script if a server state has changed */ + if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + { + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script) + { + monitor_launch_script(mon,ptr,handle->script); + } + } + ptr = ptr->next; + } } } diff --git a/server/modules/monitor/mm_mon.c b/server/modules/monitor/mm_mon.c index 46adc68b5..71bf0022b 100644 --- a/server/modules/monitor/mm_mon.c +++ b/server/modules/monitor/mm_mon.c @@ -109,33 +109,44 @@ static void * startMonitor(void *arg,void* opt) { MONITOR* mon = (MONITOR*)arg; -MYSQL_MONITOR *handle = mon->handle; -CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (handle) - { - handle->shutdown = 0; - } - else - { - if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) - return NULL; - handle->shutdown = 0; - handle->id = MONITOR_DEFAULT_ID; - handle->replicationHeartbeat = 0; - handle->detectStaleMaster = 0; - handle->master = NULL; - spinlock_init(&handle->lock); - } + MYSQL_MONITOR *handle = mon->handle; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; + if (handle) + { + handle->shutdown = 0; + } + else + { + if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) + return NULL; + handle->shutdown = 0; + handle->id = MONITOR_DEFAULT_ID; + handle->replicationHeartbeat = 0; + handle->detectStaleMaster = 0; + handle->master = NULL; + spinlock_init(&handle->lock); + } - while(params) + while(params) + { + if(!strcmp(params->name,"detect_stale_master")) { - if(!strcmp(params->name,"detect_stale_master")) - handle->detectStaleMaster = config_truth_value(params->value); - params = params->next; + handle->detectStaleMaster = config_truth_value(params->value); } + else if(!strcmp(params->name,"script")) + { + if(handle->script) + { + free(handle->script); + } - handle->tid = (THREAD)thread_start(monitorMain, mon); - return handle; + handle->script = strdup(params->value); + } + params = params->next; + } + + handle->tid = (THREAD)thread_start(monitorMain, mon); + return handle; } /** @@ -567,6 +578,25 @@ size_t nrounds = 0; } ptr = ptr->next; } + + ptr = mon->databases; + + while(ptr) + { + /** Execute monitor script if a server state has changed */ + if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + { + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script) + { + monitor_launch_script(mon,ptr,handle->script); + } + } + ptr = ptr->next; + } } } diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index 2001b2bec..37a8e6de2 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -40,61 +40,187 @@ void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) ptr->pending_status &= ~bit; } -char* mon_get_event_type(MONITOR_SERVERS* node) + + monitor_event_t mon_get_event_type(MONITOR_SERVERS* node) { unsigned int prev = node->mon_prev_status; if((prev & (SERVER_MASTER|SERVER_RUNNING)) == (SERVER_MASTER|SERVER_RUNNING) && SERVER_IS_DOWN(node->server)) { - return "master_down"; + return MASTER_DOWN_EVENT; } if((prev & (SERVER_RUNNING)) == 0 && SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) { - return "master_up"; + return MASTER_UP_EVENT; } if((prev & (SERVER_SLAVE|SERVER_RUNNING)) == (SERVER_SLAVE|SERVER_RUNNING) && SERVER_IS_DOWN(node->server)) { - return "slave_down"; + return SLAVE_DOWN_EVENT; } if((prev & (SERVER_RUNNING)) == 0 && SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) { - return "slave_up"; + return SLAVE_UP_EVENT; } + + /** Galera specific events */ + if((prev & (SERVER_JOINED|SERVER_RUNNING)) == (SERVER_JOINED|SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) + { + return SYNCED_DOWN_EVENT; + } + if((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_JOINED(node->server)) + { + return SYNCED_UP_EVENT; + } + + /** NDB events*/ + if((prev & (SERVER_NDB|SERVER_RUNNING)) == (SERVER_NDB|SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) + { + return NDB_DOWN_EVENT; + } + if((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_NDB(node->server)) + { + return NDB_UP_EVENT; + } + if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) { - return "new_master"; + return NEW_MASTER_EVENT; } if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) { - return "new_slave"; + return NEW_SLAVE_EVENT; } + + /** Status loss events */ if((prev & (SERVER_RUNNING|SERVER_MASTER)) == (SERVER_RUNNING|SERVER_MASTER) && SERVER_IS_RUNNING(node->server) && !SERVER_IS_MASTER(node->server)) { - return "lost_master"; + return LOST_MASTER_EVENT; } if((prev & (SERVER_RUNNING|SERVER_SLAVE)) == (SERVER_RUNNING|SERVER_SLAVE) && SERVER_IS_RUNNING(node->server) && !SERVER_IS_SLAVE(node->server)) { - return "lost_slave"; + return LOST_SLAVE_EVENT; } + if((prev & (SERVER_RUNNING|SERVER_JOINED)) == (SERVER_RUNNING|SERVER_JOINED) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_JOINED(node->server)) + { + return LOST_SYNCED_EVENT; + } + if((prev & (SERVER_RUNNING|SERVER_NDB)) == (SERVER_RUNNING|SERVER_NDB) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_NDB(node->server)) + { + return LOST_NDB_EVENT; + } + + + /** Generic server failure */ if((prev & SERVER_RUNNING) == 0 && SERVER_IS_RUNNING(node->server)) { - return "server_up"; + return SERVER_UP_EVENT; } if((prev & SERVER_RUNNING) == SERVER_RUNNING && SERVER_IS_DOWN(node->server)) { - return "server_down"; + return SERVER_DOWN_EVENT; } - return "unknown"; + + /** Something else, most likely a state that does not matter. + * For example SERVER_DOWN -> SERVER_MASTER|SERVER_DOWN still results in a + * server state equal to not running.*/ + return UNDEFINED_MONITOR_EVENT; +} + +char* mon_get_event_name(MONITOR_SERVERS* node) +{ + switch(mon_get_event_type(node)) + { +case UNDEFINED_MONITOR_EVENT: + return "undefined"; + +case MASTER_DOWN_EVENT: + return "master_down"; + +case MASTER_UP_EVENT: + return "master_up"; + +case SLAVE_DOWN_EVENT: + return "slave_down"; + +case SLAVE_UP_EVENT: + return "slave_up"; + +case SERVER_DOWN_EVENT: + return "server_down"; + +case SERVER_UP_EVENT: + return "server_up"; + +case SYNCED_DOWN_EVENT: + return "synced_down"; + +case SYNCED_UP_EVENT: + return "synced_up"; + +case DONOR_DOWN_EVENT: + return "donor_down"; + +case DONOR_UP_EVENT: + return "donor_up"; + +case NDB_DOWN_EVENT: + return "ndb_down"; + +case NDB_UP_EVENT: + return "ndb_up"; + +case LOST_MASTER_EVENT: + return "lost_master"; + +case LOST_SLAVE_EVENT: + return "lost_slave"; + +case LOST_SYNCED_EVENT: + return "lost_synced"; + +case LOST_DONOR_EVENT: + return "lost_donor"; + +case LOST_NDB_EVENT: + return "lost_ndb"; + +case NEW_MASTER_EVENT: + return "new_master"; + +case NEW_SLAVE_EVENT: + return "new_slave"; + +case NEW_SYNCED_EVENT: + return "new_synced"; + +case NEW_DONOR_EVENT: + return "new_donor"; + + case NEW_NDB_EVENT: + return "new_ndb"; + + default: + return "MONITOR_EVENT_FAILURE"; + + } + + } void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) @@ -162,3 +288,27 @@ bool mon_print_fail_status( } return succp; } + +void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script) +{ + char argstr[PATH_MAX + MON_ARG_MAX + 1]; + EXTERNCMD* cmd; + + snprintf(argstr,PATH_MAX + MON_ARG_MAX, + "%s --event=%s --node=%s --nodelist=", + script, + mon_get_event_name(ptr), + ptr->server->unique_name); + + mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX + 1); + cmd = externcmd_allocate(argstr); + + if(externcmd_execute(cmd)) + { + skygw_log_write(LOGFILE_ERROR, + "Error: Failed to execute script " + "'%s' on server state change event %s.", + script,mon_get_event_type(ptr)); + } + externcmd_free(cmd); +} \ No newline at end of file diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h index 1c493b10a..c171998b2 100644 --- a/server/modules/monitor/monitor_common.h +++ b/server/modules/monitor/monitor_common.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include /** * @file monitor_common.h - The generic monitor structures all monitors use * @@ -31,10 +33,42 @@ * @endverbatim */ +#define MON_ARG_MAX 8192 + +/** Monitor events that are caused by servers moving from + * one state to another.*/ +typedef enum { + UNDEFINED_MONITOR_EVENT, + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + SYNCED_DOWN_EVENT, + SYNCED_UP_EVENT, + DONOR_DOWN_EVENT, + DONOR_UP_EVENT, + NDB_DOWN_EVENT, + NDB_UP_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + LOST_SYNCED_EVENT, + LOST_DONOR_EVENT, + LOST_NDB_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + NEW_SYNCED_EVENT, + NEW_DONOR_EVENT, + NEW_NDB_EVENT + +}monitor_event_t; void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len); -char* mon_get_event_type(MONITOR_SERVERS* node); +monitor_event_t mon_get_event_type(MONITOR_SERVERS* node); +char* mon_get_event_name(MONITOR_SERVERS* node); void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit); void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit); bool mon_status_changed(MONITOR_SERVERS* mon_srv); bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); +void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script); #endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index a60e10e5e..f935fcd13 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -54,9 +54,6 @@ #include - -#define MON_ARG_MAX 8192 - /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; @@ -674,39 +671,31 @@ int log_no_master = 1; } } else { ptr->server->status = ptr->pending_status; - - if(mon_status_changed(ptr)) - { - /** Execute monitor script */ - if(handle->script && strcmp(mon_get_event_type(ptr),"unknown") != 0) - { - char argstr[PATH_MAX + MON_ARG_MAX + 1]; - snprintf(argstr,PATH_MAX + MON_ARG_MAX, - "%s --event=%s --node=%s --nodelist=", - handle->script, - mon_get_event_type(ptr), - ptr->server->unique_name); - mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX + 1); - - EXTERNCMD* cmd = externcmd_allocate(argstr); - if(externcmd_execute(cmd)) - { - skygw_log_write(LOGFILE_ERROR, - "Error: Failed to execute script " - "'%s' on server state change.", - handle->script); - } - externcmd_free(cmd); - skygw_log_write(LOGFILE_TRACE,"monitor_state_change: %s: %s", - ptr->server->unique_name,mon_get_event_type(ptr)); - } - } } } ptr = ptr->next; } - /* log master detection failure od first master becomes available after failure */ + ptr = mon->databases; + + while(ptr) + { + /** Execute monitor script if a server state has changed */ + if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + { + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script) + { + monitor_launch_script(mon,ptr,handle->script); + } + } + ptr = ptr->next; + } + + /* log master detection failure of first master becomes available after failure */ if (root_master && mon_status_changed(root_master) && !(root_master->server->status & SERVER_STALE_STATUS)) diff --git a/server/modules/monitor/ndbcluster_mon.c b/server/modules/monitor/ndbcluster_mon.c index 235eb7f00..6917f414d 100644 --- a/server/modules/monitor/ndbcluster_mon.c +++ b/server/modules/monitor/ndbcluster_mon.c @@ -107,23 +107,31 @@ static void * startMonitor(void *arg,void* opt) { MONITOR* mon = (MONITOR*)arg; -MYSQL_MONITOR *handle = mon->handle; -CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (handle != NULL) + MYSQL_MONITOR *handle = mon->handle; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; + if (handle != NULL) + { + handle->shutdown = 0; + } + else + { + if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) + return NULL; + handle->shutdown = 0; + handle->id = MONITOR_DEFAULT_ID; + spinlock_init(&handle->lock); + } + while(params) + { + if(!strcmp(params->name,"script")) { - handle->shutdown = 0; + if(handle->script) + free(handle->script); + handle->script = strdup(params->value); } - else - { - if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) - return NULL; - handle->shutdown = 0; - handle->id = MONITOR_DEFAULT_ID; - spinlock_init(&handle->lock); - } - - handle->tid = (THREAD)thread_start(monitorMain, mon); - return handle; + } + handle->tid = (THREAD)thread_start(monitorMain, mon); + return handle; } /** @@ -372,5 +380,24 @@ size_t nrounds = 0; ptr = ptr->next; } + + ptr = mon->databases; + + while(ptr) + { + /** Execute monitor script if a server state has changed */ + if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + { + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script) + { + monitor_launch_script(mon,ptr,handle->script); + } + } + ptr = ptr->next; + } } } From 49faa88d14320a195dd5f6ef0b5e04543c6e2d5a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 19:03:27 +0300 Subject: [PATCH 047/273] Added MySQL monitor documentation. --- Documentation/monitors/MySQL-Monitor.md | 115 ++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Documentation/monitors/MySQL-Monitor.md diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md new file mode 100644 index 000000000..ca04d5ae8 --- /dev/null +++ b/Documentation/monitors/MySQL-Monitor.md @@ -0,0 +1,115 @@ +# MySQL Monitor + +## Overview + +The MySQL monitor is a monitoring module for MaxScale that monitors a Master-Slave replication cluster. It assigns master and slave roles inside MaxScale according to the acutal replication tree in the cluster. + +## Configuration + +A minimal configuration of the MySQL monitor requires a set of servers for monitoring and a username and a password to connect to these servers. The user requires the REPLICATION CLIENT privilege to successfully monitor the state of the servers. + +``` +[MySQL Monitor] +type=monitor +module=mysqlmon +servers=server1,server2,server3 +user=myuser +passwd=mypwd + +``` + +## Optional parameters + +Here are optional parameters for the MySQL monitor that change the way it behaves. + +### `monitor_interval` + +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. + +``` +monitor_interval=2500 +``` + +### `backend_connect_timeout` + +This parameter controls the timeout for connecting to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 3 seconds. + +``` +backend_connect_timeout=6 +``` + +### `backend_write_timeout` + +This parameter controls the timeout for writing to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 2 seconds. + +``` +backend_write_timeout=4 +``` + +### `backend_read_timeout` + +This parameter controls the timeout for reading from a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 1 seconds. + +``` +backend_read_timeout=2 +``` + +### `detect_replication_lag` + +Detect replication lag between the master and the slaves. This allows the routers to route read queries to only slaves that are up to date. + +``` +detect_replication_lag=true +``` + +### `detect_stale_master` + +Allow previous master to be available even in case of stopped or misconfigured +replication. This allows services that depend on master and slave roles to continue functioning as long as the master server is available. + +This is a situation which can happen if all slave servers are unreachable or the replication breaks for some reason. + +``` +detect_stale_master=true +``` + +### `script` + +This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. + +``` +script=/home/user/script.sh +``` + +This script will be called with the following command line arguments. + +``` + --event= --node= --nodelist= +``` +Here is a table of all possible event names and their descriptions. + +Event Name|Description +----------|---------- +master_down|A Master server has gone down +master_up|A Master server has come up +slave_down|A Slave server has gone down +slave_up|A Slave server has come up +server_down|A server with no assigned role has done down +server_up|A server with no assigned role has come up +synced_down|A synced Galera node has come up +synced_up|A synced Galera node has gone down +donor_down|A donor Galera node has come up +donor_up|A donor Galera node has gone down +ndb_down|A MySQL Cluster node has gone down +ndb_up|A MySQL Cluster node has come up +lost_master|A server lost Master status +lost_slave|A server lost Slave status +lost_synced|A Galera node lost synced status +lost_donor|A Galera node lost donor status +lost_ndb|A MySQL Cluster node lost node membership +new_master|A new Master was detected +new_slave|A new Slave was detected +new_synced|A new synced Galera node was detected +new_donor|A new donor Galera node was detected +new_ndb|A new MySQL Cluster node was found + From 1d0740c1c17a3b7129bcc18e7b0618962fb7ea31 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 9 May 2015 21:16:35 +0300 Subject: [PATCH 048/273] Renamed node to initiator in the arguments a monitor calls the script. --- Documentation/monitors/MySQL-Monitor.md | 2 +- server/modules/monitor/monitor_common.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index ca04d5ae8..be8a7b20c 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -84,7 +84,7 @@ script=/home/user/script.sh This script will be called with the following command line arguments. ``` - --event= --node= --nodelist= + --event= --initiator= --nodelist= ``` Here is a table of all possible event names and their descriptions. diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index 37a8e6de2..352626984 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -295,7 +295,7 @@ void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script) EXTERNCMD* cmd; snprintf(argstr,PATH_MAX + MON_ARG_MAX, - "%s --event=%s --node=%s --nodelist=", + "%s --event=%s --initiator=%s --nodelist=", script, mon_get_event_name(ptr), ptr->server->unique_name); From 63060d123334893a6cef833b72932fd4a2ec138e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 10 May 2015 06:23:50 +0300 Subject: [PATCH 049/273] Added possibility to run as non-root user. --- server/core/gateway.c | 68 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index cf0f41525..0dbffc637 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -159,6 +159,7 @@ static struct option long_options[] = { {"language",required_argument, 0, 'N'}, {"syslog", required_argument, 0, 's'}, {"maxscalelog",required_argument,0,'S'}, + {"user",required_argument,0,'U'}, {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, '?'}, {0, 0, 0, 0} @@ -193,7 +194,7 @@ static bool resolve_maxscale_conf_fname( char* cnf_file_arg); static char* check_dir_access(char* dirname,bool,bool); - +static int set_user(); /** * Handler for SIGHUP signal. Reload the configuration for the @@ -861,14 +862,16 @@ static void usage(void) " -l, --log=[file|shm] log to file or shared memory (default: shm)\n" " -L, --logdir=PATH path to log file directory\n" " (default: /var/log/maxscale)\n" - " -D, --datadir=PATH path to data directory, stored embedded mysql tables\n" - " (default: /var/cache/maxscale)\n" - " -C, --configdir=PATH path to configuration file directory\n" - " (default: /etc/)\n" - " -B, --libdir=PATH path to module directory\n" - " (default: /usr/lib64/maxscale)\n" " -A, --cachedir=PATH path to cache directory\n" " (default: /var/cache/maxscale)\n" + " -B, --libdir=PATH path to module directory\n" + " (default: /usr/lib64/maxscale)\n" + " -C, --configdir=PATH path to configuration file directory\n" + " (default: /etc/)\n" + " -D, --datadir=PATH path to data directory, stored embedded mysql tables\n" + " (default: /var/cache/maxscale)\n" + " -U, --user=USER run MaxScale as another user.\n" + " The user ID and group ID of this user are used to run MaxScale." " -s, --syslog=[yes|no] log messages to syslog (default:yes)\n" " -S, --maxscalelog=[yes|no] log messages to MaxScale log (default: yes)\n" " -v, --version print version info and exit\n" @@ -939,6 +942,9 @@ int main(int argc, char **argv) datadir_cleanup, write_footer, NULL}; + + + sigemptyset(&sigpipe_mask); sigaddset(&sigpipe_mask, SIGPIPE); progname = *argv; @@ -970,7 +976,7 @@ int main(int argc, char **argv) } } - while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:D:C:B:", + while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:D:C:B:U:A:", long_options, &option_index)) != -1) { bool succp = true; @@ -1083,6 +1089,12 @@ int main(int argc, char **argv) syslog_enabled = config_truth_value(optarg); } break; + case 'U': + if(set_user(optarg) != 0) + { + succp = false; + } + break; case '?': usage(); rc = EXIT_SUCCESS; @@ -1931,3 +1943,43 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons return 1; } + +static int set_user(char* user) +{ + errno = 0; + struct passwd *pwname; + int rval; + + pwname = getpwnam(user); + if(pwname == NULL) + { + printf("Error: Failed to retrieve user information for '%s': %d %s\n", + user,errno,errno == 0 ? "User not found" : strerror(errno)); + return -1; + } + + rval = setgid(pwname->pw_gid); + if(rval != 0) + { + printf("Error: Failed to change group to '%d': %d %s\n", + pwname->pw_gid,errno,strerror(errno)); + return rval; + } + + rval = setuid(pwname->pw_uid); + if(rval != 0) + { + printf("Error: Failed to change user to '%s': %d %s\n", + pwname->pw_name,errno,strerror(errno)); + return rval; + } +#ifdef SS_DEBUG + else + { + printf("Running MaxScale as: %s %d:%d\n",pwname->pw_name,pwname->pw_uid,pwname->pw_gid); + } +#endif + + + return rval; +} \ No newline at end of file From 2b457bf372ef9c81d3a29582ff00898dc0299e2b Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 11 May 2015 06:19:06 +0300 Subject: [PATCH 050/273] Updated init.d and systemd scripts. --- cmake/install_layout.cmake | 5 ++--- etc/init.d/maxscale.in | 4 ++-- etc/maxscale.service.in | 2 +- etc/postinst.in | 24 ++++++++++++++++++++---- etc/ubuntu/init.d/maxscale.in | 4 ++-- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index c55a25a32..e25bd0a8d 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -1,7 +1,4 @@ # Set the install layout -# Possible values: -# STANDALONE - Installs to /usr/local -# PACKAGE - Installs to /usr include(GNUInstallDirs) set(MAXSCALE_LIBDIR ${CMAKE_INSTALL_LIBDIR}/maxscale CACHE PATH "Library installation path") @@ -9,5 +6,7 @@ set(MAXSCALE_BINDIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Executable installation set(MAXSCALE_SHAREDIR ${CMAKE_INSTALL_DATADIR}/maxscale CACHE PATH "Share file installation path, includes licence and readme files") set(MAXSCALE_DOCDIR ${CMAKE_INSTALL_DOCDIR}/maxscale CACHE PATH "Documentation installation path, text versions only") set(MAXSCALE_CONFDIR ${CMAKE_INSTALL_SYSCONFDIR} CACHE PATH "Configuration file installation path, this is not usually needed") + +# This is the only hard-coded absolute path set(MAXSCALE_VARDIR /var CACHE PATH "Data file path (usually /var/)") diff --git a/etc/init.d/maxscale.in b/etc/init.d/maxscale.in index 4ee5ce0f4..70cdf5b3d 100755 --- a/etc/init.d/maxscale.in +++ b/etc/init.d/maxscale.in @@ -21,7 +21,7 @@ # MaxScale PIDFILE and LIB ############################################# -export MAXSCALE_PIDFILE=/var/run/maxscale/maxscale.pid +export MAXSCALE_PIDFILE=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@/maxscale ############################### @@ -55,7 +55,7 @@ start() { CHECK_RET=$? [ $CHECK_RET -eq 0 ] && echo -n " found $my_check" && success && CHECK_RET=0 - daemon --pidfile $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale >& /dev/null + daemon --pidfile $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale >& /dev/null RETVAL=$? [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$servicename diff --git a/etc/maxscale.service.in b/etc/maxscale.service.in index 56b49d98f..50e781f30 100644 --- a/etc/maxscale.service.in +++ b/etc/maxscale.service.in @@ -4,7 +4,7 @@ After=network.target [Service] Type=forking -PIDFile=/var/run/maxscale/maxscale.pid +PIDFile=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale [Install] diff --git a/etc/postinst.in b/etc/postinst.in index fa0c4271c..f5b85f537 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -1,9 +1,25 @@ #!/bin/sh -mkdir -p /var/log/maxscale -mkdir -p /var/cache/maxscale -mkdir -p /var/run/maxscale -mkdir -p @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHARE_DIR@/ + +# Create directories +mkdir -p @MAXSCALE_LIBDIR@ +mkdir -p @MAXSCALE_BINDIR@ +mkdir -p @MAXSCALE_SHAREDIR@ +mkdir -p @MAXSCALE_DOCDIR@ +mkdir -p @MAXSCALE_CONFDIR@ +mkdir -p @MAXSCALE_VARDIR@/log/maxscale +mkdir -p @MAXSCALE_VARDIR@/lib/maxscale +mkdir -p @MAXSCALE_VARDIR@/cache/maxscale +mkdir -p @MAXSCALE_VARDIR@/run/maxscale + +# Create MaxScale user +if [ -f "/etc/passwd" -a "$(grep -c 'maxscale' /etc/passwd)" -eq 0 ] +then + useradd -s -r /bin/false maxscale + groupadd maxscale +fi + +# Copy init.d script and ldconfig file cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale /etc/init.d/ cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf /etc/ld.so.conf.d/ /sbin/ldconfig diff --git a/etc/ubuntu/init.d/maxscale.in b/etc/ubuntu/init.d/maxscale.in index 27a73092c..315f16c5d 100755 --- a/etc/ubuntu/init.d/maxscale.in +++ b/etc/ubuntu/init.d/maxscale.in @@ -21,7 +21,7 @@ # MaxScale HOME, PIDFILE, LIB ############################################# -export MAXSCALE_PIDFILE=/var/run/maxscale/maxscale.pid +export MAXSCALE_PIDFILE=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@/maxscale ############################### @@ -44,7 +44,7 @@ _RETVAL_STATUS_NOT_RUNNING=3 # stop/start/status related vars ################################# NAME=maxscale -DAEMON=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale +DAEMON=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale # Source function library. . /lib/lsb/init-functions From 8f019b451420b4136511081f947822afb46b288c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 11 May 2015 06:45:37 +0300 Subject: [PATCH 051/273] Added Galera monitor documentation. --- Documentation/monitors/Galera-Monitor.md | 124 +++++++++++++++++++++++ Documentation/monitors/MySQL-Monitor.md | 14 ++- 2 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 Documentation/monitors/Galera-Monitor.md diff --git a/Documentation/monitors/Galera-Monitor.md b/Documentation/monitors/Galera-Monitor.md new file mode 100644 index 000000000..3872c30f7 --- /dev/null +++ b/Documentation/monitors/Galera-Monitor.md @@ -0,0 +1,124 @@ +# Galera Monitor + +## Overview + +The Galera Monitor is a monitoring module for MaxScale that monitors a Galera cluster. It detects whether nodes are a part of the cluster and if they are in sync with the rest of the cluster. It can also assign master and slave roles inside MaxScale, allowing Galera clusters to be used with modules designed for traditional master-slave clusters. + +## Configuration + +A minimal configuration for a monitor requires a set of servers for monitoring and a username and a password to connect to these servers. The user requires the REPLICATION CLIENT privilege to successfully monitor the state of the servers. + +``` +[MySQL Monitor] +type=monitor +module=galeramon +servers=server1,server2,server3 +user=myuser +passwd=mypwd + +``` + +## Optional parameters for all monitors + +Here are optional parameters that are common for all the monitors. + +### `monitor_interval` + +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. + +``` +monitor_interval=2500 +``` + +### `backend_connect_timeout` + +This parameter controls the timeout for connecting to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 3 seconds. + +``` +backend_connect_timeout=6 +``` + +### `backend_write_timeout` + +This parameter controls the timeout for writing to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 2 seconds. + +``` +backend_write_timeout=4 +``` + +### `backend_read_timeout` + +This parameter controls the timeout for reading from a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 1 seconds. + +``` +backend_read_timeout=2 +``` + +## Galera Monitor optional parameters + +These are optional parameters specific to the Galera Monitor. + +### `disable_master_failback` + +If a node marked as master inside MaxScale happens to fail and the master status is assigned to another node MaxScale will normally return the master status to the original node after it comes back up. With this option enabled, if the master status is assigned to a new node it will not be reassigned to the original node for as long as the new master node is running. + +``` +disable_master_failback=true +``` + +### `available_when_donor` + +This option only has an effect if there is a single Galera node being backed up an XtraBackup instance. This causes the initial node to go into Donor state which would normally prevent if from being marked as a valid server inside MaxScale. If this option is enabled, a single node in Donor state where the method is XtraBackup will be kept in Synced state. + +``` +available_when_donor=true +``` + +### `disable_master_role_setting` + +This disables the assingment of master and slave roles to the Galera cluster nodes. If this option is enabled, Synced is the only status assigned by this monitor. + +``` +disable_master_role_setting=true +``` + +### `script` + +This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. + +``` +script=/home/user/script.sh +``` + +This script will be called with the following command line arguments. + +``` + --event= --initiator= --nodelist= +``` +Here is a table of all possible event types and their descriptions. + +Event Name|Description +----------|---------- +master_down|A Master server has gone down +master_up|A Master server has come up +slave_down|A Slave server has gone down +slave_up|A Slave server has come up +server_down|A server with no assigned role has done down +server_up|A server with no assigned role has come up +synced_down|A synced Galera node has come up +synced_up|A synced Galera node has gone down +donor_down|A donor Galera node has come up +donor_up|A donor Galera node has gone down +ndb_down|A MySQL Cluster node has gone down +ndb_up|A MySQL Cluster node has come up +lost_master|A server lost Master status +lost_slave|A server lost Slave status +lost_synced|A Galera node lost synced status +lost_donor|A Galera node lost donor status +lost_ndb|A MySQL Cluster node lost node membership +new_master|A new Master was detected +new_slave|A new Slave was detected +new_synced|A new synced Galera node was detected +new_donor|A new donor Galera node was detected +new_ndb|A new MySQL Cluster node was found + diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index be8a7b20c..4e6e949e0 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -2,11 +2,11 @@ ## Overview -The MySQL monitor is a monitoring module for MaxScale that monitors a Master-Slave replication cluster. It assigns master and slave roles inside MaxScale according to the acutal replication tree in the cluster. +The MySQL Monitor is a monitoring module for MaxScale that monitors a Master-Slave replication cluster. It assigns master and slave roles inside MaxScale according to the acutal replication tree in the cluster. ## Configuration -A minimal configuration of the MySQL monitor requires a set of servers for monitoring and a username and a password to connect to these servers. The user requires the REPLICATION CLIENT privilege to successfully monitor the state of the servers. +A minimal configuration for a monitor requires a set of servers for monitoring and a username and a password to connect to these servers. The user requires the REPLICATION CLIENT privilege to successfully monitor the state of the servers. ``` [MySQL Monitor] @@ -18,9 +18,9 @@ passwd=mypwd ``` -## Optional parameters +## Optional parameters for all monitors -Here are optional parameters for the MySQL monitor that change the way it behaves. +Here are optional parameters that are common for all the monitors. ### `monitor_interval` @@ -54,6 +54,10 @@ This parameter controls the timeout for reading from a monitored server. It is i backend_read_timeout=2 ``` +## MySQL Monitor optional parameters + +These are optional parameters specific to the MySQL Monitor. + ### `detect_replication_lag` Detect replication lag between the master and the slaves. This allows the routers to route read queries to only slaves that are up to date. @@ -86,7 +90,7 @@ This script will be called with the following command line arguments. ``` --event= --initiator= --nodelist= ``` -Here is a table of all possible event names and their descriptions. +Here is a table of all possible event types and their descriptions. Event Name|Description ----------|---------- From f991e58b5714b82952b19d4283b0117f414e01d6 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 11 May 2015 11:43:21 +0200 Subject: [PATCH 052/273] MariaDB 10 master requires MariaDB 10 slaves Only MariaDB 10 slaves can register to binblog server with a MariaDB 10 Master --- server/modules/include/blr.h | 6 ++++-- server/modules/routing/binlog/blr.c | 1 + server/modules/routing/binlog/blr_slave.c | 26 ++++++++++++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 7b57fd15f..3dbc43853 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -24,8 +24,9 @@ * @verbatim * Revision History * - * Date Who Description - * 02/04/14 Mark Riddoch Initial implementation + * Date Who Description + * 02/04/14 Mark Riddoch Initial implementation + * 11/05/15 Massimilaino Pinto Added mariadb10_compat to master and slave structs * * @endverbatim */ @@ -175,6 +176,7 @@ typedef struct router_slave { 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 diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index becea18b0..8dce5ea58 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -498,6 +498,7 @@ ROUTER_SLAVE *slave; strcpy(slave->binlogfile, "unassigned"); slave->connect_time = time(0); slave->lastEventTimestamp = 0; + slave->mariadb10_compat = false; /** * Add this session to the list of active sessions. diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 141ff3a03..b932000d3 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -37,6 +37,7 @@ * 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 * * @endverbatim */ @@ -125,7 +126,27 @@ blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) return blr_slave_query(router, slave, queue); break; case COM_REGISTER_SLAVE: - return blr_slave_register(router, slave, queue); + /* + * 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"); + + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "MariaDB 10 Slave is required for Slave registration", + MYSQL_COMMAND(queue)))); + + 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: return blr_slave_binlog_dump(router, slave, queue); @@ -370,6 +391,9 @@ int query_len; } else if (strcasecmp(word, "@mariadb_slave_capability") == 0) { + /* 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); From a48e694dba044ae25debf846585c4079f17a679b Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 11 May 2015 12:42:14 +0200 Subject: [PATCH 053/273] Fix for log messages Fix for log messages about MariaDB 10 registration and unexpected query --- server/modules/routing/binlog/blr_slave.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index b932000d3..87b795ea6 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -138,8 +138,9 @@ blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, - "MariaDB 10 Slave is required for Slave registration", - MYSQL_COMMAND(queue)))); + "%s: Slave %s: a MariaDB 10 Slave is required for Slave registration", + router->service->name, + slave->dcb->remote))); dcb_close(slave->dcb); return 1; @@ -472,7 +473,7 @@ int query_len; query_text = strndup(qtext, query_len); LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, "Unexpected query from slave server %s", query_text))); + LOGFILE_ERROR, "Unexpected query from slave %s: %s", slave->dcb->remote, query_text))); free(query_text); blr_slave_send_error(router, slave, "Unexpected SQL query received from slave."); return 1; From 1aba13177be93f2b8fb05219692ff1d1f1f3cd91 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 11 May 2015 19:12:45 +0300 Subject: [PATCH 054/273] Added ndbclustermon and mmmon headers. --- server/core/mon_exec.c | 1 + server/include/mon_exec.h | 1 + server/modules/monitor/CMakeLists.txt | 4 +- server/modules/monitor/galeramon.c | 1 + server/modules/monitor/galeramon.h | 1 - server/modules/monitor/{mm_mon.c => mmmon.c} | 89 +++---------------- server/modules/monitor/mmmon.h | 51 +++++++++++ server/modules/monitor/mysqlmon.h | 2 +- .../{ndbcluster_mon.c => ndbclustermon.c} | 1 + server/modules/monitor/ndbclustermon.h | 55 ++++++++++++ 10 files changed, 127 insertions(+), 79 deletions(-) rename server/modules/monitor/{mm_mon.c => mmmon.c} (90%) create mode 100644 server/modules/monitor/mmmon.h rename server/modules/monitor/{ndbcluster_mon.c => ndbclustermon.c} (99%) create mode 100644 server/modules/monitor/ndbclustermon.h diff --git a/server/core/mon_exec.c b/server/core/mon_exec.c index bae0d31fc..01640bcd6 100644 --- a/server/core/mon_exec.c +++ b/server/core/mon_exec.c @@ -141,6 +141,7 @@ int externcmd_execute(EXTERNCMD* cmd) } else { + cmd->child = pid; cmd->n_exec++; LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd)); } diff --git a/server/include/mon_exec.h b/server/include/mon_exec.h index e1ef7ad37..25390bb82 100644 --- a/server/include/mon_exec.h +++ b/server/include/mon_exec.h @@ -11,6 +11,7 @@ typedef struct extern_cmd_t{ char* parameters[MAXSCALE_EXTCMD_ARG_MAX]; /*< Command arguments */ int n_exec; /*< Number of times executed */ + pid_t child; /*< PID of the child process */ }EXTERNCMD; EXTERNCMD* externcmd_allocate(char* argstr); diff --git a/server/modules/monitor/CMakeLists.txt b/server/modules/monitor/CMakeLists.txt index 8f364fe5b..9f08a5b32 100644 --- a/server/modules/monitor/CMakeLists.txt +++ b/server/modules/monitor/CMakeLists.txt @@ -6,11 +6,11 @@ add_library(galeramon SHARED galeramon.c monitor_common.c) target_link_libraries(galeramon log_manager utils) install(TARGETS galeramon DESTINATION modules) -add_library(ndbclustermon SHARED ndbcluster_mon.c monitor_common.c) +add_library(ndbclustermon SHARED ndbclustermon.c monitor_common.c) target_link_libraries(ndbclustermon log_manager utils) install(TARGETS ndbclustermon DESTINATION modules) if(BUILD_MMMON) - add_library(mmmon SHARED mm_mon.c monitor_common.c) + add_library(mmmon SHARED mmmon.c monitor_common.c) target_link_libraries(mmmon log_manager utils) install(TARGETS mmmon DESTINATION modules) endif() diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index a99ea952b..66ccc28ce 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -34,6 +34,7 @@ * 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write * 20/04/15 Guillaume Lefranc Added availableWhenDonor feature * 22/04/15 Martin Brampton Addition of disableMasterRoleSetting + * 08/05/15 Markus Makela Addition of launchable scripts * * @endverbatim */ diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h index 3a9c41e7f..30b642921 100644 --- a/server/modules/monitor/galeramon.h +++ b/server/modules/monitor/galeramon.h @@ -56,7 +56,6 @@ typedef struct { int shutdown; /**< Flag to shutdown the monitor thread */ int status; /**< Monitor status */ unsigned long id; /**< Monitor ID */ - int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ diff --git a/server/modules/monitor/mm_mon.c b/server/modules/monitor/mmmon.c similarity index 90% rename from server/modules/monitor/mm_mon.c rename to server/modules/monitor/mmmon.c index 71bf0022b..d4e04d288 100644 --- a/server/modules/monitor/mm_mon.c +++ b/server/modules/monitor/mmmon.c @@ -17,19 +17,20 @@ */ /** - * @file mysql_mon.c - A MySQL Multi Muster cluster monitor + * @file mm_mon.c - A Multi-Master Multi Muster cluster monitor * * @verbatim * Revision History * * Date Who Description * 08/09/14 Massimiliano Pinto Initial implementation + * 08/05/15 Markus Makela Addition of launchable scripts * * @endverbatim */ -#include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -44,7 +45,7 @@ MODULE_INFO info = { MODULE_API_MONITOR, MODULE_BETA_RELEASE, MONITOR_VERSION, - "A MySQL Multi Master monitor" + "A Multi-Master Multi Master monitor" }; static void *startMonitor(void *,void*); @@ -79,7 +80,7 @@ ModuleInit() { LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, - "Initialise the MySQL Monitor module %s.", + "Initialise the Multi-Master Monitor module %s.", version_str))); } @@ -109,7 +110,7 @@ static void * startMonitor(void *arg,void* opt) { MONITOR* mon = (MONITOR*)arg; - MYSQL_MONITOR *handle = mon->handle; + MM_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; if (handle) { @@ -117,12 +118,10 @@ startMonitor(void *arg,void* opt) } else { - if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) + if ((handle = (MM_MONITOR *)malloc(sizeof(MM_MONITOR))) == NULL) return NULL; handle->shutdown = 0; handle->id = MONITOR_DEFAULT_ID; - handle->replicationHeartbeat = 0; - handle->detectStaleMaster = 0; handle->master = NULL; spinlock_init(&handle->lock); } @@ -157,7 +156,7 @@ startMonitor(void *arg,void* opt) static void stopMonitor(void *arg) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +MM_MONITOR *handle = (MM_MONITOR *)arg; handle->shutdown = 1; thread_wait((void *)handle->tid); @@ -172,7 +171,7 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; static void diagnostics(DCB *dcb, void *arg) { MONITOR* mon = (MONITOR*)arg; -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; +MM_MONITOR *handle = (MM_MONITOR *)mon->handle; MONITOR_SERVERS *db; char *sep; @@ -217,7 +216,7 @@ char *sep; static void monitorDatabase(MONITOR* mon, MONITOR_SERVERS *database) { - MYSQL_MONITOR *handle = mon->handle; + MM_MONITOR *handle = mon->handle; MYSQL_ROW row; MYSQL_RES *result; int isslave = 0; @@ -240,7 +239,7 @@ char *server_string; if (SERVER_IN_MAINT(database->server)) return; - /** Store prevous status */ + /** Store previous status */ database->mon_prev_status = database->server->status; if (database->con == NULL || mysql_ping(database->con) != 0) @@ -473,7 +472,7 @@ static void monitorMain(void *arg) { MONITOR* mon = (MONITOR*)arg; -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; +MM_MONITOR *handle = (MM_MONITOR *)mon->handle; MONITOR_SERVERS *ptr; int detect_stale_master = handle->detectStaleMaster; MONITOR_SERVERS *root_master; @@ -612,70 +611,10 @@ static void detectStaleMaster(void *arg, int enable) { MONITOR* mon = (MONITOR*)arg; -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; +MM_MONITOR *handle = (MM_MONITOR *)mon->handle; memcpy(&handle->detectStaleMaster, &enable, sizeof(int)); } -static bool mon_status_changed( - MONITOR_SERVERS* mon_srv) -{ - bool succp; - - if (mon_srv->mon_prev_status != mon_srv->server->status) - { - succp = true; - } - else - { - succp = false; - } - return succp; -} - -static bool mon_print_fail_status( - MONITOR_SERVERS* mon_srv) -{ - bool succp; - int errcount = mon_srv->mon_err_count; - uint8_t modval; - - modval = 1<<(MIN(errcount/10, 7)); - - if (SERVER_IS_DOWN(mon_srv->server) && errcount%modval == 0) - { - succp = true; - } - else - { - succp = false; - } - return succp; -} - -/** - * Set a pending status bit in the monior server - * - * @param server The server to update - * @param bit The bit to clear for the server - */ -static void -monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit) -{ - ptr->pending_status |= bit; -} - -/** - * Clear a pending status bit in the monior server - * - * @param server The server to update - * @param bit The bit to clear for the server - */ -static void -monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) -{ - ptr->pending_status &= ~bit; -} - /******* * This function returns the master server * from a set of MySQL Multi Master monitored servers @@ -687,7 +626,7 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) */ static MONITOR_SERVERS *get_current_master(MONITOR *mon) { - MYSQL_MONITOR* handle = mon->handle; + MM_MONITOR* handle = mon->handle; MONITOR_SERVERS *ptr; ptr = mon->databases; diff --git a/server/modules/monitor/mmmon.h b/server/modules/monitor/mmmon.h new file mode 100644 index 000000000..fb035a594 --- /dev/null +++ b/server/modules/monitor/mmmon.h @@ -0,0 +1,51 @@ +#ifndef _MMMON_H +#define _MMMON_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2015 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * The handle for an instance of a Multi-Master Monitor module + */ +typedef struct { + SPINLOCK lock; /**< The monitor spinlock */ + pthread_t tid; /**< id of monitor thread */ + int shutdown; /**< Flag to shutdown the monitor thread */ + int status; /**< Monitor status */ + unsigned long id; /**< Monitor ID */ + int detectStaleMaster; /**< Monitor flag for Stale Master detection */ + MONITOR_SERVERS *master; /**< Master server for Master/Slave replication */ + char* script; /*< Script to call when state changes occur on servers */ +} MM_MONITOR; + +#endif diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index f5d0e2a37..9bd6a2da9 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -49,7 +49,7 @@ * 07/11/14 Massimiliano Pinto Addition of NetworkTimeout: connect, read, write * 20/04/15 Guillaume Lefranc Addition of availableWhenDonor * 22/04/15 Martin Brampton Addition of disableMasterRoleSetting - * 07/05/15 Markus Makela Addition of command execution on Master server failure + * 07/05/15 Markus Makela Addition of command execution on Master server failure * @endverbatim */ diff --git a/server/modules/monitor/ndbcluster_mon.c b/server/modules/monitor/ndbclustermon.c similarity index 99% rename from server/modules/monitor/ndbcluster_mon.c rename to server/modules/monitor/ndbclustermon.c index 6917f414d..8385131e3 100644 --- a/server/modules/monitor/ndbcluster_mon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -25,6 +25,7 @@ * Date Who Description * 25/07/14 Massimiliano Pinto Initial implementation * 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write + * 08/05/15 Markus Makela Addition of launchable scripts * * @endverbatim */ diff --git a/server/modules/monitor/ndbclustermon.h b/server/modules/monitor/ndbclustermon.h new file mode 100644 index 000000000..7066a5a4e --- /dev/null +++ b/server/modules/monitor/ndbclustermon.h @@ -0,0 +1,55 @@ +#ifndef _MYSQLMON_H +#define _MYSQLMON_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @file ndbclustermon.h - The NDB Cluster monitor + * + */ + +/** + * The handle for an instance of a NDB Cluster Monitor module + */ +typedef struct { + SPINLOCK lock; /**< The monitor spinlock */ + pthread_t tid; /**< id of monitor thread */ + int shutdown; /**< Flag to shutdown the monitor thread */ + int status; /**< Monitor status */ + unsigned long id; /**< Monitor ID */ + MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ + char* script; /*< Script to call when state changes occur on servers */ +} MYSQL_MONITOR; + +#endif From 3fcc1778bbda69f5618c188b7881a398f6b58a00 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 12 May 2015 06:43:36 +0300 Subject: [PATCH 055/273] Added changing of directory ownership. --- etc/postinst.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/etc/postinst.in b/etc/postinst.in index f5b85f537..8d81907ed 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -19,6 +19,12 @@ then groupadd maxscale fi +# Change the owner of the directories to maxscale:maxscale +chown maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale +chown maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale +chown maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale +chown maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale + # Copy init.d script and ldconfig file cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale /etc/init.d/ cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf /etc/ld.so.conf.d/ From b41130b533c91fcf6d73fe5a1760abf273fb0eea Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 12 May 2015 08:11:10 +0300 Subject: [PATCH 056/273] Fixed the useradd command in the postinst script having he command line arguments in the wrong order. --- etc/postinst.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/postinst.in b/etc/postinst.in index 8d81907ed..e04a34f5f 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -15,7 +15,7 @@ mkdir -p @MAXSCALE_VARDIR@/run/maxscale # Create MaxScale user if [ -f "/etc/passwd" -a "$(grep -c 'maxscale' /etc/passwd)" -eq 0 ] then - useradd -s -r /bin/false maxscale + useradd -r -s /bin/false maxscale groupadd maxscale fi From 455b132a2271c3bfd445b7713e51f94be7f967c9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 13 May 2015 22:46:05 +0300 Subject: [PATCH 057/273] Fixed a non-static variable causing multiple declarations when FAKE_CODE was defined --- utils/skygw_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/skygw_debug.h b/utils/skygw_debug.h index 89f7a4e00..99f7807fc 100644 --- a/utils/skygw_debug.h +++ b/utils/skygw_debug.h @@ -567,7 +567,7 @@ typedef enum skygw_chk_t { #if defined(FAKE_CODE) -bool conn_open[10240]; +static bool conn_open[10240]; #endif /* FAKE_CODE */ #endif /* SKYGW_DEBUG_H */ From 7d3c8673ecad26162e392910c1e85271174b3057 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 14 May 2015 11:02:45 +0200 Subject: [PATCH 058/273] Added unique_name to new server when reloading config Added unique_name to new server when reloading config --- server/core/config.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/core/config.c b/server/core/config.c index d22bb777e..44de91b47 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1721,6 +1721,9 @@ SERVER *server; obj->element = server_alloc(address, protocol, atoi(port)); + + server_set_unique_name(obj->element, obj->object); + if (obj->element && monuser && monpw) { serverAddMonUser(obj->element, From b94f494d8052ca52414448f68cb445444c395be6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 14 May 2015 12:05:58 +0300 Subject: [PATCH 059/273] Added filtering of monitor events. --- server/core/monitor.c | 6 +- server/modules/monitor/galeramon.c | 89 +++++++++++++++---------- server/modules/monitor/galeramon.h | 1 + server/modules/monitor/mmmon.c | 20 ++++-- server/modules/monitor/mmmon.h | 1 + server/modules/monitor/monitor_common.c | 86 +++++++++++++++++++++++- server/modules/monitor/monitor_common.h | 5 +- server/modules/monitor/mysql_mon.c | 20 ++++-- server/modules/monitor/mysqlmon.h | 1 + server/modules/monitor/ndbclustermon.c | 19 +++++- server/modules/monitor/ndbclustermon.h | 1 + 11 files changed, 195 insertions(+), 54 deletions(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index b2ed8d1a2..9d945572a 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -66,8 +66,6 @@ MONITOR *mon; { return NULL; } - mon->state = MONITOR_STATE_ALLOC; - mon->name = strdup(name); if ((mon->module = load_module(module, MODULE_MONITOR)) == NULL) { @@ -79,10 +77,10 @@ MONITOR *mon; free(mon); return NULL; } - + mon->state = MONITOR_STATE_ALLOC; + mon->name = strdup(name); mon->handle = NULL; mon->databases = NULL; - mon->name = NULL; mon->password = NULL; mon->read_timeout = DEFAULT_READ_TIMEOUT; mon->write_timeout = DEFAULT_WRITE_TIMEOUT; diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 66ccc28ce..eb75c2160 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -120,46 +120,59 @@ static void * startMonitor(void *arg,void* opt) { MONITOR* mon = arg; -GALERA_MONITOR *handle = mon->handle; -CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - if (handle != NULL) - { - handle->shutdown = 0; - } - else - { - if ((handle = (GALERA_MONITOR *)malloc(sizeof(GALERA_MONITOR))) == NULL) - return NULL; - handle->shutdown = 0; - handle->id = MONITOR_DEFAULT_ID; - handle->disableMasterFailback = 0; - handle->availableWhenDonor = 0; - handle->disableMasterRoleSetting = 0; - handle->master = NULL; - handle->script = NULL; - spinlock_init(&handle->lock); - } + GALERA_MONITOR *handle = mon->handle; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; + bool have_events = false; + if (handle != NULL) + { + handle->shutdown = 0; + } + else + { + if ((handle = (GALERA_MONITOR *)malloc(sizeof(GALERA_MONITOR))) == NULL) + return NULL; + handle->shutdown = 0; + handle->id = MONITOR_DEFAULT_ID; + handle->disableMasterFailback = 0; + handle->availableWhenDonor = 0; + handle->disableMasterRoleSetting = 0; + handle->master = NULL; + handle->script = NULL; + memset(handle->events,false,sizeof(handle->events)); + spinlock_init(&handle->lock); + } - while(params) + while(params) + { + if(!strcmp(params->name,"disable_master_failback")) + handle->disableMasterFailback = config_truth_value(params->value); + else if(!strcmp(params->name,"available_when_donor")) + handle->availableWhenDonor = config_truth_value(params->value); + else if(!strcmp(params->name,"disable_master_role_setting")) + handle->disableMasterRoleSetting = config_truth_value(params->value); + else if(!strcmp(params->name,"script")) { - if(!strcmp(params->name,"disable_master_failback")) - handle->disableMasterFailback = config_truth_value(params->value); - else if(!strcmp(params->name,"available_when_donor")) - handle->availableWhenDonor = config_truth_value(params->value); - else if(!strcmp(params->name,"disable_master_role_setting")) - handle->disableMasterRoleSetting = config_truth_value(params->value); - else if(!strcmp(params->name,"script")) - { - if(handle->script) - free(handle->script); - handle->script = strdup(params->value); - } - params = params->next; + if(handle->script) + free(handle->script); + handle->script = strdup(params->value); } + else if(!strcmp(params->name,"events")) + { + mon_parse_event_string(&handle->events,sizeof(handle->events),params->value); + have_events = true; + } + params = params->next; + } - handle->tid = (THREAD)thread_start(monitorMain, mon); - return handle; + /** If no specific events are given, enable them all */ + if(!have_events) + { + memset(handle->events,true,sizeof(handle->events)); + } + + handle->tid = (THREAD)thread_start(monitorMain, mon); + return handle; } /** @@ -388,6 +401,7 @@ MONITOR_SERVERS *candidate_master = NULL; int master_stickiness = handle->disableMasterFailback; int is_cluster=0; int log_no_members = 1; +monitor_event_t evtype; if (mysql_thread_init()) { @@ -554,14 +568,15 @@ int log_no_members = 1; while(ptr) { + /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) { skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name,ptr->server->port, mon_get_event_name(ptr)); - if(handle->script) + if(handle->script && handle->events[evtype]) { monitor_launch_script(mon,ptr,handle->script); } diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h index 30b642921..02d8f08c7 100644 --- a/server/modules/monitor/galeramon.h +++ b/server/modules/monitor/galeramon.h @@ -61,6 +61,7 @@ typedef struct { int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ char* script; + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } GALERA_MONITOR; #endif diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index d4e04d288..8188c40c9 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -112,6 +112,8 @@ startMonitor(void *arg,void* opt) MONITOR* mon = (MONITOR*)arg; MM_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; + bool have_events = false; + if (handle) { handle->shutdown = 0; @@ -123,6 +125,7 @@ startMonitor(void *arg,void* opt) handle->shutdown = 0; handle->id = MONITOR_DEFAULT_ID; handle->master = NULL; + memset(handle->events,false,sizeof(handle->events)); spinlock_init(&handle->lock); } @@ -141,9 +144,18 @@ startMonitor(void *arg,void* opt) handle->script = strdup(params->value); } + else if(!strcmp(params->name,"events")) + { + mon_parse_event_string(&handle->events,sizeof(handle->events),params->value); + have_events = true; + } params = params->next; } - + /** If no specific events are given, enable them all */ + if(!have_events) + { + memset(handle->events,true,sizeof(handle->events)); + } handle->tid = (THREAD)thread_start(monitorMain, mon); return handle; } @@ -579,17 +591,17 @@ size_t nrounds = 0; } ptr = mon->databases; - + monitor_event_t evtype; while(ptr) { /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) { skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name,ptr->server->port, mon_get_event_name(ptr)); - if(handle->script) + if(handle->script && handle->events[evtype]) { monitor_launch_script(mon,ptr,handle->script); } diff --git a/server/modules/monitor/mmmon.h b/server/modules/monitor/mmmon.h index fb035a594..e2532d407 100644 --- a/server/modules/monitor/mmmon.h +++ b/server/modules/monitor/mmmon.h @@ -46,6 +46,7 @@ typedef struct { int detectStaleMaster; /**< Monitor flag for Stale Master detection */ MONITOR_SERVERS *master; /**< Master server for Master/Slave replication */ char* script; /*< Script to call when state changes occur on servers */ + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } MM_MONITOR; #endif diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index 352626984..6f9be583b 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -18,6 +18,8 @@ #include +monitor_event_t mon_name_to_event(char* tok); + /** * Set a pending status bit in the monitor server * @@ -311,4 +313,86 @@ void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script) script,mon_get_event_type(ptr)); } externcmd_free(cmd); -} \ No newline at end of file +} + +/** + * Parse a string of event names to an array with enabled events. + * @param events Pointer to an array of boolean values + * @param count Size of the array + * @param string String to parse + * @return 0 on success. 1 when an error has occurred or an unexpected event was + * found. + */ +int mon_parse_event_string(bool* events, size_t count,char* string) +{ + char *tok,*saved; + monitor_event_t event; + + tok = strtok_r(string,",| ",&saved); + + if(tok == NULL) + return -1; + + while(tok) + { + event = mon_name_to_event(tok); + if(event == UNDEFINED_MONITOR_EVENT) + return -1; + events[event] = true; + tok = strtok_r(NULL,",| ",&saved); + } + + return 0; +} + +monitor_event_t mon_name_to_event(char* tok) +{ + if(!strcasecmp("master_down",tok)) + return MASTER_DOWN_EVENT; + else if(!strcasecmp("master_up",tok)) + return MASTER_UP_EVENT; + else if(!strcasecmp("slave_down",tok)) + return SLAVE_DOWN_EVENT; + else if(!strcasecmp("slave_up",tok)) + return SLAVE_UP_EVENT; + else if(!strcasecmp("server_down",tok)) + return SERVER_DOWN_EVENT; + else if(!strcasecmp("server_up",tok)) + return SERVER_UP_EVENT; + else if(!strcasecmp("synced_down",tok)) + return SYNCED_DOWN_EVENT; + else if(!strcasecmp("synced_up",tok)) + return SYNCED_UP_EVENT; + else if(!strcasecmp("donor_down",tok)) + return DONOR_DOWN_EVENT; + else if(!strcasecmp("donor_up",tok)) + return DONOR_UP_EVENT; + else if(!strcasecmp("ndb_down",tok)) + return NDB_DOWN_EVENT; + else if(!strcasecmp("ndb_up",tok)) + return NDB_UP_EVENT; + else if(!strcasecmp("lost_master",tok)) + return LOST_MASTER_EVENT; + else if(!strcasecmp("lost_slave",tok)) + return LOST_SLAVE_EVENT; + else if(!strcasecmp("lost_synced",tok)) + return LOST_SYNCED_EVENT; + else if(!strcasecmp("lost_donor",tok)) + return LOST_DONOR_EVENT; + else if(!strcasecmp("lost_ndb",tok)) + return LOST_NDB_EVENT; + else if(!strcasecmp("new_master",tok)) + return NEW_MASTER_EVENT; + else if(!strcasecmp("new_slave",tok)) + return NEW_SLAVE_EVENT; + else if(!strcasecmp("new_synced",tok)) + return NEW_SYNCED_EVENT; + else if(!strcasecmp("new_donor",tok)) + return NEW_DONOR_EVENT; + else if(!strcasecmp("new_ndb",tok)) + return NEW_NDB_EVENT; + else + return UNDEFINED_MONITOR_EVENT; + + } + diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h index c171998b2..e4cd864d1 100644 --- a/server/modules/monitor/monitor_common.h +++ b/server/modules/monitor/monitor_common.h @@ -60,8 +60,8 @@ typedef enum { NEW_SLAVE_EVENT, NEW_SYNCED_EVENT, NEW_DONOR_EVENT, - NEW_NDB_EVENT - + NEW_NDB_EVENT, + MAX_MONITOR_EVENT }monitor_event_t; void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len); monitor_event_t mon_get_event_type(MONITOR_SERVERS* node); @@ -71,4 +71,5 @@ void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit); bool mon_status_changed(MONITOR_SERVERS* mon_srv); bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script); +int mon_parse_event_string(bool* events, size_t count,char* string); #endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index f935fcd13..3a6d22391 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -140,6 +140,8 @@ startMonitor(void *arg, void* opt) MONITOR* monitor = (MONITOR*)arg; MYSQL_MONITOR *handle = (MYSQL_MONITOR*)monitor->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; + bool have_events = false; + if (handle) { handle->shutdown = 0; @@ -154,6 +156,7 @@ startMonitor(void *arg, void* opt) handle->detectStaleMaster = 0; handle->master = NULL; handle->script = NULL; + memset(handle->events,false,sizeof(handle->events)); spinlock_init(&handle->lock); } @@ -169,9 +172,18 @@ startMonitor(void *arg, void* opt) free(handle->script); handle->script = strdup(params->value); } + else if(!strcmp(params->name,"events")) + { + mon_parse_event_string(handle->events,sizeof(handle->events),params->value); + have_events = true; + } params = params->next; } - + /** If no specific events are given, enable them all */ + if(!have_events) + { + memset(handle->events,true,sizeof(handle->events)); + } handle->tid = (THREAD)thread_start(monitorMain, monitor); return handle; } @@ -677,17 +689,17 @@ int log_no_master = 1; } ptr = mon->databases; - + monitor_event_t evtype; while(ptr) { /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) { skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name,ptr->server->port, mon_get_event_name(ptr)); - if(handle->script) + if(handle->script && handle->events[evtype]) { monitor_launch_script(mon,ptr,handle->script); } diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 9bd6a2da9..574ae7c88 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -69,6 +69,7 @@ typedef struct { int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ char* script; /*< Script to call when state changes occur on servers */ + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } MYSQL_MONITOR; #endif diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index 8385131e3..ad41ab1e8 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -110,6 +110,8 @@ startMonitor(void *arg,void* opt) MONITOR* mon = (MONITOR*)arg; MYSQL_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; + bool have_events = false; + if (handle != NULL) { handle->shutdown = 0; @@ -120,6 +122,7 @@ startMonitor(void *arg,void* opt) return NULL; handle->shutdown = 0; handle->id = MONITOR_DEFAULT_ID; + memset(handle->events,false,sizeof(handle->events)); spinlock_init(&handle->lock); } while(params) @@ -130,6 +133,17 @@ startMonitor(void *arg,void* opt) free(handle->script); handle->script = strdup(params->value); } + else if(!strcmp(params->name,"events")) + { + mon_parse_event_string(&handle->events,sizeof(handle->events),params->value); + have_events = true; + } + params = params->next; + } + /** If no specific events are given, enable them all */ + if(!have_events) + { + memset(handle->events,true,sizeof(handle->events)); } handle->tid = (THREAD)thread_start(monitorMain, mon); return handle; @@ -383,17 +397,18 @@ size_t nrounds = 0; } ptr = mon->databases; + monitor_event_t evtype; while(ptr) { /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && mon_get_event_type(ptr) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) { skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", ptr->server->unique_name, ptr->server->name,ptr->server->port, mon_get_event_name(ptr)); - if(handle->script) + if(handle->script && handle->events[evtype]) { monitor_launch_script(mon,ptr,handle->script); } diff --git a/server/modules/monitor/ndbclustermon.h b/server/modules/monitor/ndbclustermon.h index 7066a5a4e..081f5e3da 100644 --- a/server/modules/monitor/ndbclustermon.h +++ b/server/modules/monitor/ndbclustermon.h @@ -50,6 +50,7 @@ typedef struct { unsigned long id; /**< Monitor ID */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ char* script; /*< Script to call when state changes occur on servers */ + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } MYSQL_MONITOR; #endif From 1edf7a32cc004082c47585329d34a165f0d92f7a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 14 May 2015 13:43:23 +0300 Subject: [PATCH 060/273] Added missing strip_db_esc to dbusers.c --- server/core/dbusers.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 8e9d8a715..c8d1a78ec 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -1118,6 +1118,7 @@ getUsers(SERVICE *service, USERS *users) MYSQL_DATABASE_MAXLEN; int dbnames = 0; int db_grants = 0; + char dbnm[MYSQL_DATABASE_MAXLEN+1]; if (serviceGetUser(service, &service_user, &service_passwd) == 0) { @@ -1464,16 +1465,32 @@ getUsers(SERVICE *service, USERS *users) */ if (db_grants) { - /* we have dbgrants, store them */ + bool havedb = false; + /* we have dbgrants, store them */ + if(row[5]){ + unsigned long *rowlen = mysql_fetch_lengths(result); + memcpy(dbnm,row[5],rowlen[5]); + memset(dbnm + rowlen[5],0,1); + havedb = true; + if(service->strip_db_esc) { + strip_escape_chars(dbnm); + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "[%s]: %s -> %s", + service->name, + row[5], + dbnm))); + } + } - if(service->optimize_wildcard && wildcard_db_grant(row[5])) + if(service->optimize_wildcard && havedb && wildcard_db_grant(row[5])) { - rc = add_wildcard_users(users, row[0], row[1], password, row[4], row[5], service->resources); + rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,row[5],rc); } else { - rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], row[5]); + rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], havedb ? dbnm : NULL); } } else { From 055950acedec78e2eb1191afef424cac3268b2b8 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 14 May 2015 15:19:11 +0300 Subject: [PATCH 061/273] Updated monitor documentation. --- Documentation/monitors/Galera-Monitor.md | 20 ++-- Documentation/monitors/MM-Monitor.md | 107 ++++++++++++++++++ Documentation/monitors/MySQL-Monitor.md | 22 ++-- Documentation/monitors/NDB-Cluster-Monitor.md | 102 +++++++++++++++++ 4 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 Documentation/monitors/MM-Monitor.md create mode 100644 Documentation/monitors/NDB-Cluster-Monitor.md diff --git a/Documentation/monitors/Galera-Monitor.md b/Documentation/monitors/Galera-Monitor.md index 3872c30f7..8e1a403c9 100644 --- a/Documentation/monitors/Galera-Monitor.md +++ b/Documentation/monitors/Galera-Monitor.md @@ -9,7 +9,7 @@ The Galera Monitor is a monitoring module for MaxScale that monitors a Galera cl A minimal configuration for a monitor requires a set of servers for monitoring and a username and a password to connect to these servers. The user requires the REPLICATION CLIENT privilege to successfully monitor the state of the servers. ``` -[MySQL Monitor] +[Galera Monitor] type=monitor module=galeramon servers=server1,server2,server3 @@ -90,11 +90,16 @@ This script will be executed when a server changes its state. The parameter shou script=/home/user/script.sh ``` -This script will be called with the following command line arguments. +### `events` + +A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. ``` - --event= --initiator= --nodelist= +events=master_down,slave_down ``` + +## Script events + Here is a table of all possible event types and their descriptions. Event Name|Description @@ -107,18 +112,9 @@ server_down|A server with no assigned role has done down server_up|A server with no assigned role has come up synced_down|A synced Galera node has come up synced_up|A synced Galera node has gone down -donor_down|A donor Galera node has come up -donor_up|A donor Galera node has gone down -ndb_down|A MySQL Cluster node has gone down -ndb_up|A MySQL Cluster node has come up lost_master|A server lost Master status lost_slave|A server lost Slave status lost_synced|A Galera node lost synced status -lost_donor|A Galera node lost donor status -lost_ndb|A MySQL Cluster node lost node membership new_master|A new Master was detected new_slave|A new Slave was detected new_synced|A new synced Galera node was detected -new_donor|A new donor Galera node was detected -new_ndb|A new MySQL Cluster node was found - diff --git a/Documentation/monitors/MM-Monitor.md b/Documentation/monitors/MM-Monitor.md new file mode 100644 index 000000000..c9b4a852d --- /dev/null +++ b/Documentation/monitors/MM-Monitor.md @@ -0,0 +1,107 @@ +# Multi-Master Monitor + +## Overview + +The Multi-Master Monitor is a monitoring module for MaxScale that monitors Master-Master replicatio. It assigns master and slave roles inside MaxScale based on whether the read_only parameter on a server is set to off or on. + +## Configuration + +A minimal configuration for a monitor requires a set of servers for monitoring and a username and a password to connect to these servers. The user requires the REPLICATION CLIENT privilege to successfully monitor the state of the servers. + +``` +[Multi-Master Monitor] +type=monitor +module=mmmon +servers=server1,server2,server3 +user=myuser +passwd=mypwd + +``` + +## Optional parameters for all monitors + +Here are optional parameters that are common for all the monitors. + +### `monitor_interval` + +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. + +``` +monitor_interval=2500 +``` + +### `backend_connect_timeout` + +This parameter controls the timeout for connecting to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 3 seconds. + +``` +backend_connect_timeout=6 +``` + +### `backend_write_timeout` + +This parameter controls the timeout for writing to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 2 seconds. + +``` +backend_write_timeout=4 +``` + +### `backend_read_timeout` + +This parameter controls the timeout for reading from a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 1 seconds. + +``` +backend_read_timeout=2 +``` + +## Multi-Master Monitor optional parameters + +These are optional parameters specific to the Multi-Master Monitor. + +### `detect_stale_master` + +Allow previous master to be available even in case of stopped or misconfigured replication. This allows services that depend on master and slave roles to continue functioning as long as the master server is available. + +This is a situation which can happen if all slave servers are unreachable or the replication breaks for some reason. + +``` +detect_stale_master=true +``` + +### `script` + +This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. + +``` +script=/home/user/script.sh +``` + +This script will be called with the following command line arguments. + +``` + --event= --initiator= --nodelist= +``` +### `events` + +A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. + +``` +events=master_down,slave_down +``` + +## Script events + +Here is a table of all possible event types and their descriptions. + +Event Name|Description +----------|---------- +master_down|A Master server has gone down +master_up|A Master server has come up +slave_down|A Slave server has gone down +slave_up|A Slave server has come up +server_down|A server with no assigned role has done down +server_up|A server with no assigned role has come up +lost_master|A server lost Master status +lost_slave|A server lost Slave status +new_master|A new Master was detected +new_slave|A new Slave was detected diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index 4e6e949e0..a3560c548 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -90,6 +90,16 @@ This script will be called with the following command line arguments. ``` --event= --initiator= --nodelist= ``` +### `events` + +A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. + +``` +events=master_down,slave_down +``` + +## Script events + Here is a table of all possible event types and their descriptions. Event Name|Description @@ -100,20 +110,8 @@ slave_down|A Slave server has gone down slave_up|A Slave server has come up server_down|A server with no assigned role has done down server_up|A server with no assigned role has come up -synced_down|A synced Galera node has come up -synced_up|A synced Galera node has gone down -donor_down|A donor Galera node has come up -donor_up|A donor Galera node has gone down -ndb_down|A MySQL Cluster node has gone down -ndb_up|A MySQL Cluster node has come up lost_master|A server lost Master status lost_slave|A server lost Slave status -lost_synced|A Galera node lost synced status -lost_donor|A Galera node lost donor status -lost_ndb|A MySQL Cluster node lost node membership new_master|A new Master was detected new_slave|A new Slave was detected -new_synced|A new synced Galera node was detected -new_donor|A new donor Galera node was detected -new_ndb|A new MySQL Cluster node was found diff --git a/Documentation/monitors/NDB-Cluster-Monitor.md b/Documentation/monitors/NDB-Cluster-Monitor.md new file mode 100644 index 000000000..a2e608b7e --- /dev/null +++ b/Documentation/monitors/NDB-Cluster-Monitor.md @@ -0,0 +1,102 @@ +# NDB Cluster Monitor + +## Overview + +The MySQL Cluster Monitor is a monitoring module for MaxScale that monitors Master-Master replicatio. It assigns master and slave roles inside MaxScale based on whether the read_only parameter on a server is set to off or on. + +## Configuration + +A minimal configuration for a monitor requires a set of servers for monitoring and a username and a password to connect to these servers. The user requires the REPLICATION CLIENT privilege to successfully monitor the state of the servers. + +``` +[MySQL Cluster Monitor] +type=monitor +module=ndbclustermon +servers=server1,server2,server3 +user=myuser +passwd=mypwd + +``` + +## Optional parameters for all monitors + +Here are optional parameters that are common for all the monitors. + +### `monitor_interval` + +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. + +``` +monitor_interval=2500 +``` + +### `backend_connect_timeout` + +This parameter controls the timeout for connecting to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 3 seconds. + +``` +backend_connect_timeout=6 +``` + +### `backend_write_timeout` + +This parameter controls the timeout for writing to a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 2 seconds. + +``` +backend_write_timeout=4 +``` + +### `backend_read_timeout` + +This parameter controls the timeout for reading from a monitored server. It is in seconds and the minimum value is 1 second. The default value for this parameter is 1 seconds. + +``` +backend_read_timeout=2 +``` + +## MySQL Cluster Monitor optional parameters + +These are optional parameters specific to the MySQL Cluster Monitor. + +### `script` + +This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. + +``` +script=/home/user/script.sh +``` + +This script will be called with the following command line arguments. + +``` + --event= --initiator= --nodelist= +``` +### `events` + +A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. + +``` +events=master_down,slave_down +``` + +## Script events + +Here is a table of all possible event types and their descriptions that the MySQL Cluster monitor can be called with. + +Event Name|Description +----------|---------- +master_down|A Master server has gone down +master_up|A Master server has come up +slave_down|A Slave server has gone down +slave_up|A Slave server has come up +server_down|A server with no assigned role has done down +server_up|A server with no assigned role has come up +ndb_down|A MySQL Cluster node has gone down +ndb_up|A MySQL Cluster node has come up +lost_master|A server lost Master status +lost_slave|A server lost Slave status +lost_ndb|A MySQL Cluster node lost node membership +new_master|A new Master was detected +new_slave|A new Slave was detected +new_ndb|A new MySQL Cluster node was found + From c3a243cd30f50e1ff9a23144c9c03205e1ec4e8a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 14 May 2015 15:32:57 +0300 Subject: [PATCH 062/273] Fixed typos. --- Documentation/Documentation-Contents.md | 6 ++++++ Documentation/monitors/Galera-Monitor.md | 4 ++-- Documentation/monitors/MM-Monitor.md | 4 ++-- Documentation/monitors/MySQL-Monitor.md | 4 ++-- Documentation/monitors/NDB-Cluster-Monitor.md | 4 ++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 74969c7d1..47e2c77b2 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -53,6 +53,12 @@ Here are detailed documents about the filters MaxScale offers. They contain conf - [Database Firewall Filter](filters/Database-Firewall-Filter.md) - [RabbitMQ Filter](filters/RabbitMQ-Filter.md) +## Monitors + - [MySQL Monitor](monitors/MySQL-Monitor.md) + - [Galera Monitor](monitors/Galera-Monitor.md) + - [Multi-Master Monitor](monitors/MM-Monitor.md) + - [MySQL Cluster Monitor](monitors/NDB-Cluster-Monitor.md) + ## Utilities - [RabbitMQ Consumer Client](filters/RabbitMQ-Consumer-Client.md) diff --git a/Documentation/monitors/Galera-Monitor.md b/Documentation/monitors/Galera-Monitor.md index 8e1a403c9..ffe54fb83 100644 --- a/Documentation/monitors/Galera-Monitor.md +++ b/Documentation/monitors/Galera-Monitor.md @@ -24,7 +24,7 @@ Here are optional parameters that are common for all the monitors. ### `monitor_interval` -This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 milliseconds. ``` monitor_interval=2500 @@ -76,7 +76,7 @@ available_when_donor=true ### `disable_master_role_setting` -This disables the assingment of master and slave roles to the Galera cluster nodes. If this option is enabled, Synced is the only status assigned by this monitor. +This disables the assignment of master and slave roles to the Galera cluster nodes. If this option is enabled, Synced is the only status assigned by this monitor. ``` disable_master_role_setting=true diff --git a/Documentation/monitors/MM-Monitor.md b/Documentation/monitors/MM-Monitor.md index c9b4a852d..00375d4bf 100644 --- a/Documentation/monitors/MM-Monitor.md +++ b/Documentation/monitors/MM-Monitor.md @@ -2,7 +2,7 @@ ## Overview -The Multi-Master Monitor is a monitoring module for MaxScale that monitors Master-Master replicatio. It assigns master and slave roles inside MaxScale based on whether the read_only parameter on a server is set to off or on. +The Multi-Master Monitor is a monitoring module for MaxScale that monitors Master-Master replication. It assigns master and slave roles inside MaxScale based on whether the read_only parameter on a server is set to off or on. ## Configuration @@ -24,7 +24,7 @@ Here are optional parameters that are common for all the monitors. ### `monitor_interval` -This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 milliseconds. ``` monitor_interval=2500 diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index a3560c548..ab643109e 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -2,7 +2,7 @@ ## Overview -The MySQL Monitor is a monitoring module for MaxScale that monitors a Master-Slave replication cluster. It assigns master and slave roles inside MaxScale according to the acutal replication tree in the cluster. +The MySQL Monitor is a monitoring module for MaxScale that monitors a Master-Slave replication cluster. It assigns master and slave roles inside MaxScale according to the actual replication tree in the cluster. ## Configuration @@ -24,7 +24,7 @@ Here are optional parameters that are common for all the monitors. ### `monitor_interval` -This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 milliseconds. ``` monitor_interval=2500 diff --git a/Documentation/monitors/NDB-Cluster-Monitor.md b/Documentation/monitors/NDB-Cluster-Monitor.md index a2e608b7e..a9f453678 100644 --- a/Documentation/monitors/NDB-Cluster-Monitor.md +++ b/Documentation/monitors/NDB-Cluster-Monitor.md @@ -2,7 +2,7 @@ ## Overview -The MySQL Cluster Monitor is a monitoring module for MaxScale that monitors Master-Master replicatio. It assigns master and slave roles inside MaxScale based on whether the read_only parameter on a server is set to off or on. +The MySQL Cluster Monitor is a monitoring module for MaxScale that monitors a MySQL Cluster. It assigns a NDB status for the server if it is a part of a MySQL Cluster. ## Configuration @@ -24,7 +24,7 @@ Here are optional parameters that are common for all the monitors. ### `monitor_interval` -This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 illiseconds. +This is the time the monitor waits between each cycle of monitoring. The default value of 10000 milliseconds (10 seconds) should be lowered if you want a faster response to changes in the server states. The value is defined in milliseconds and the smallest possible value is 100 milliseconds. ``` monitor_interval=2500 From a6701ee7861ac9bd0e0f51996ca3a09b606dd3e3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 14 May 2015 15:42:49 +0300 Subject: [PATCH 063/273] Added missing newline to usage output. --- server/core/gateway.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 0dbffc637..462d28e8e 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -871,7 +871,7 @@ static void usage(void) " -D, --datadir=PATH path to data directory, stored embedded mysql tables\n" " (default: /var/cache/maxscale)\n" " -U, --user=USER run MaxScale as another user.\n" - " The user ID and group ID of this user are used to run MaxScale." + " The user ID and group ID of this user are used to run MaxScale.\n" " -s, --syslog=[yes|no] log messages to syslog (default:yes)\n" " -S, --maxscalelog=[yes|no] log messages to MaxScale log (default: yes)\n" " -v, --version print version info and exit\n" From 545586b47b436b0a49d0b18341b4bb1bd3bdf939 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 16 May 2015 10:40:42 +0300 Subject: [PATCH 064/273] Added readconnroute documentation. --- Documentation/Documentation-Contents.md | 1 + Documentation/routers/ReadConnRoute.md | 74 +++++++++++++++++++++++++ Documentation/routers/ReadWriteSplit.md | 4 +- 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 Documentation/routers/ReadConnRoute.md diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index dfbef26dc..0e553e2dc 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -45,6 +45,7 @@ ## Routers - [Read Write Split](routers/ReadWriteSplit.md) + - [Read Connnection Router](routers/ReadConnRoute.md) - [Schemarouter](routers/SchemaRouter.md) ## Filters diff --git a/Documentation/routers/ReadConnRoute.md b/Documentation/routers/ReadConnRoute.md new file mode 100644 index 000000000..901b72e48 --- /dev/null +++ b/Documentation/routers/ReadConnRoute.md @@ -0,0 +1,74 @@ +# Readconnroute + +This document provides anoverview of the **readconnroute** router module and its intended use case scenarios. It also displays all router configuration parameters with their descriptions. + +## Overview + +The readconnroute router provides simple and lightweight load balancing across a set of servers. The router can be configured to + +## Configuration + +Readconnroute router-specific settings are specified in the configuration file of MaxScale in its specific section. The section can be freely named but the name is used later as a reference from listener section. + +The configuration consists of mandatory and optional parameters. + +## Mandatory parameters + +**`type`** specifies the type of service. For readconnroute module the type is `router`: + + type=router + +**`router`** specifies the router module to be used. For readconnroute the value is `readconnroute`: + + router=readconnroute + +**`servers`** provides a list of servers, which the router will connect to: + + servers=server1,server2,server3 + +**NOTE: Each server on the list must have its own section in the configuration file where it is defined.** + +**`user`** is the username the router session uses for accessing backends in order to load the content of the `mysql.user` table (and `mysql.db` and database names as well) and optionally for creating, and using `maxscale_schema.replication_heartbeat` table. + +**`passwd`** specifies corresponding password for the user. Syntax for user and passwd is: + +``` +user= +passwd= +``` + +## Optional parameters + +**`router_options`** can contain a list of valid server roles. These roles are used as the valid types of servers the router will form connections to when new sessions are created. +``` + router_options=slave +``` +Here is a list of all possible values for the `router_options`. + +Role|Description +------|--------- +master|A server assigned as a master by one of MaxScale monitors. Depending on the monitor implementation, this could be a master server of a Master-Slave replication cluster or a Write-Master of a Galera cluster. +slave|A server assigned as a slave of a master. +synced| A Galera cluster node which is in a synced state with the cluster. +ndb|A MySQL Replication Cluster node +running|A server that is up and running. All servers that MaxScale can connect to are labeled as running. + +If no `router_options` parameter is configured in the service definition, the router will use the default value of `running`. This means that it will load balance connections across all running servers defined in the `servers` parameter of the service. + +## Examples + +The most common use for the readconnroute is to provide either a read or write port for an application. This provides a more lightweight routing solution than the more complex readwritesplit router but requires the application to be able to use distinct write and read ports. + +To configure a read-only service that tolerates master failures, we first need to add a new section in to the configuration file. + +``` +[Read Service] +type=service +router=readconnroute +servers=slave1,slave2,slave3 +router_options=slave +``` + +Here the `router_options`designates slaves as the only valid server type. With this configuration, the queries are load balanced across the slave servers. + +For more complex examples of the readconnroute router, take a look at the examples in the [Tutorials](../Tutorials) folder. diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md index 52b1c11b2..6058e0390 100644 --- a/Documentation/routers/ReadWriteSplit.md +++ b/Documentation/routers/ReadWriteSplit.md @@ -18,9 +18,9 @@ The configuration consists of mandatory and optional parameters. type=router -**`service`** specifies the router module to be used. For **readwritesplit** the value is `readwritesplit`: +**`router`** specifies the router module to be used. For **readwritesplit** the value is `readwritesplit`: - service=readwritesplit + router=readwritesplit **`servers`** provides a list of servers, which must include one master and available slaves: From 36b963805b7150d9181241c4937a1b3bd67b1355 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 16 May 2015 11:01:30 +0300 Subject: [PATCH 065/273] Added missing weightby parameter into readconnroute documentation. --- Documentation/routers/ReadConnRoute.md | 32 +++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/routers/ReadConnRoute.md b/Documentation/routers/ReadConnRoute.md index 901b72e48..50d5b80ae 100644 --- a/Documentation/routers/ReadConnRoute.md +++ b/Documentation/routers/ReadConnRoute.md @@ -4,7 +4,7 @@ This document provides anoverview of the **readconnroute** router module and its ## Overview -The readconnroute router provides simple and lightweight load balancing across a set of servers. The router can be configured to +The readconnroute router provides simple and lightweight load balancing across a set of servers. The router can also be configured to balance connections based on a weighting parameter defined in the server's section. ## Configuration @@ -39,6 +39,36 @@ passwd= ## Optional parameters +The **`weightby`** parameter defines the name of the value which is used to calculate the weights of the servers. Here is an example server configuration with the `serv_weight` parameter used as the weighting parameter. + +``` +[server1] +type=server +address=127.0.0.1 +port=3000 +protocol=MySQLBackend +serv_weight=3 + +[server2] +type=server +address=127.0.0.1 +port=3001 +protocol=MySQLBackend +serv_weight=1 + +[Read Service] +type=service +router=readconnroute +servers=server1,server2 +weightby=serv_weight +``` + +With this configuration and a heavy query load, the server *server1* will get most of the connections and about a third of the remaining queries are routed to the second server. With server weights, you can assing secondary servers that are only used when the primary server is under heavy load. + +Without the weightby parameter, each connection counts as a single connection. With a weighting parameter, a single connection received its weight from the server's own weighting parameter divided by the sum of all weighting parameters in all the configured servers. + +If we use the previous configuration as an example, the sum of the `serv_weight` parameter is 4. Server1 would receive a weight of `3/4=75%` and server2 would get `1/4=25%`. This means that server1 would get 75% of the connections and server2 would get 25% of the connections. + **`router_options`** can contain a list of valid server roles. These roles are used as the valid types of servers the router will form connections to when new sessions are created. ``` router_options=slave From 68c5dedeec78b04075808e26e99844645b6ef7ee Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 18 May 2015 16:25:50 +0300 Subject: [PATCH 066/273] Removed plainrouter from MXS-121 branch. --- server/modules/include/plainprotocol.h | 260 ------- server/modules/protocol/CMakeLists.txt | 9 - server/modules/protocol/plainbackend.c | 894 ------------------------ server/modules/protocol/plainclient.c | 714 ------------------- server/modules/routing/plainroute.c | 923 ------------------------- 5 files changed, 2800 deletions(-) delete mode 100644 server/modules/include/plainprotocol.h delete mode 100644 server/modules/protocol/plainbackend.c delete mode 100644 server/modules/protocol/plainclient.c delete mode 100644 server/modules/routing/plainroute.c diff --git a/server/modules/include/plainprotocol.h b/server/modules/include/plainprotocol.h deleted file mode 100644 index 35ecd1cd0..000000000 --- a/server/modules/include/plainprotocol.h +++ /dev/null @@ -1,260 +0,0 @@ -#ifndef _MYSQL_PROTOCOL_H -#define _MYSQL_PROTOCOL_H -/* - * This file is distributed as part of the MariaDB Corporation MaxScale. It is free - * software: you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation, - * version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright MariaDB Corporation Ab 2013-2015 - */ - -/* - * Revision History - * - * Date Who Description - * 24-03-2015 Markus Makela Initial implementation - - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define GW_MYSQL_VERSION "MaxScale " MAXSCALE_VERSION -#define GW_MYSQL_LOOP_TIMEOUT 300000000 -#define GW_MYSQL_READ 0 -#define GW_MYSQL_WRITE 1 -#define MYSQL_HEADER_LEN 4L - -#define GW_MYSQL_PROTOCOL_VERSION 10 // version is 10 -#define GW_MYSQL_HANDSHAKE_FILLER 0x00 -#define GW_MYSQL_SERVER_CAPABILITIES_BYTE1 0xff -#define GW_MYSQL_SERVER_CAPABILITIES_BYTE2 0xf7 -#define GW_MYSQL_SERVER_LANGUAGE 0x08 -#define GW_MYSQL_MAX_PACKET_LEN 0xffffffL; -#define GW_MYSQL_SCRAMBLE_SIZE 20 -#define GW_SCRAMBLE_LENGTH_323 8 - -#ifndef MYSQL_SCRAMBLE_LEN -# define MYSQL_SCRAMBLE_LEN GW_MYSQL_SCRAMBLE_SIZE -#endif - -#define GW_NOINTR_CALL(A) do { errno = 0; A; } while (errno == EINTR) -#define SMALL_CHUNK 1024 -#define MAX_CHUNK SMALL_CHUNK * 8 * 4 -#define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10) -struct dcb; - -/** Protocol packing macros. */ -#define gw_mysql_set_byte2(__buffer, __int) do { \ - (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ - (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); } while (0) -#define gw_mysql_set_byte3(__buffer, __int) do { \ - (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ - (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \ - (__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); } while (0) -#define gw_mysql_set_byte4(__buffer, __int) do { \ - (__buffer)[0]= (uint8_t)((__int) & 0xFF); \ - (__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \ - (__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); \ - (__buffer)[3]= (uint8_t)(((__int) >> 24) & 0xFF); } while (0) - -/** Protocol unpacking macros. */ -#define gw_mysql_get_byte2(__buffer) \ - (uint16_t)((__buffer)[0] | \ - ((__buffer)[1] << 8)) -#define gw_mysql_get_byte3(__buffer) \ - (uint32_t)((__buffer)[0] | \ - ((__buffer)[1] << 8) | \ - ((__buffer)[2] << 16)) -#define gw_mysql_get_byte4(__buffer) \ - (uint32_t)((__buffer)[0] | \ - ((__buffer)[1] << 8) | \ - ((__buffer)[2] << 16) | \ - ((__buffer)[3] << 24)) -#define gw_mysql_get_byte8(__buffer) \ - ((uint64_t)(__buffer)[0] | \ - ((uint64_t)(__buffer)[1] << 8) | \ - ((uint64_t)(__buffer)[2] << 16) | \ - ((uint64_t)(__buffer)[3] << 24) | \ - ((uint64_t)(__buffer)[4] << 32) | \ - ((uint64_t)(__buffer)[5] << 40) | \ - ((uint64_t)(__buffer)[6] << 48) | \ - ((uint64_t)(__buffer)[7] << 56)) - -/** MySQL protocol constants */ -typedef enum -{ - GW_MYSQL_CAPABILITIES_NONE= 0, - GW_MYSQL_CAPABILITIES_LONG_PASSWORD= (1 << 0), - GW_MYSQL_CAPABILITIES_FOUND_ROWS= (1 << 1), - GW_MYSQL_CAPABILITIES_LONG_FLAG= (1 << 2), - GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB= (1 << 3), - GW_MYSQL_CAPABILITIES_NO_SCHEMA= (1 << 4), - GW_MYSQL_CAPABILITIES_COMPRESS= (1 << 5), - GW_MYSQL_CAPABILITIES_ODBC= (1 << 6), - GW_MYSQL_CAPABILITIES_LOCAL_FILES= (1 << 7), - GW_MYSQL_CAPABILITIES_IGNORE_SPACE= (1 << 8), - GW_MYSQL_CAPABILITIES_PROTOCOL_41= (1 << 9), - GW_MYSQL_CAPABILITIES_INTERACTIVE= (1 << 10), - GW_MYSQL_CAPABILITIES_SSL= (1 << 11), - GW_MYSQL_CAPABILITIES_IGNORE_SIGPIPE= (1 << 12), - GW_MYSQL_CAPABILITIES_TRANSACTIONS= (1 << 13), - GW_MYSQL_CAPABILITIES_RESERVED= (1 << 14), - GW_MYSQL_CAPABILITIES_SECURE_CONNECTION= (1 << 15), - GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS= (1 << 16), - GW_MYSQL_CAPABILITIES_MULTI_RESULTS= (1 << 17), - GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS= (1 << 18), - GW_MYSQL_CAPABILITIES_PLUGIN_AUTH= (1 << 19), - GW_MYSQL_CAPABILITIES_SSL_VERIFY_SERVER_CERT= (1 << 30), - GW_MYSQL_CAPABILITIES_REMEMBER_OPTIONS= (1 << 31), - GW_MYSQL_CAPABILITIES_CLIENT= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD | - GW_MYSQL_CAPABILITIES_FOUND_ROWS | - GW_MYSQL_CAPABILITIES_LONG_FLAG | - GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB | - GW_MYSQL_CAPABILITIES_LOCAL_FILES | - GW_MYSQL_CAPABILITIES_PLUGIN_AUTH | - GW_MYSQL_CAPABILITIES_TRANSACTIONS | - GW_MYSQL_CAPABILITIES_PROTOCOL_41 | - GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS | - GW_MYSQL_CAPABILITIES_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_SECURE_CONNECTION), - GW_MYSQL_CAPABILITIES_CLIENT_COMPRESS= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD | - GW_MYSQL_CAPABILITIES_FOUND_ROWS | - GW_MYSQL_CAPABILITIES_LONG_FLAG | - GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB | - GW_MYSQL_CAPABILITIES_LOCAL_FILES | - GW_MYSQL_CAPABILITIES_PLUGIN_AUTH | - GW_MYSQL_CAPABILITIES_TRANSACTIONS | - GW_MYSQL_CAPABILITIES_PROTOCOL_41 | - GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS | - GW_MYSQL_CAPABILITIES_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS | - GW_MYSQL_CAPABILITIES_COMPRESS - ), -} gw_mysql_capabilities_t; - -/** Copy from enum in mariadb-5.5 mysql_com.h */ -typedef enum mysql_server_cmd { - MYSQL_COM_UNDEFINED = -1, - MYSQL_COM_SLEEP = 0, - MYSQL_COM_QUIT, - MYSQL_COM_INIT_DB, - MYSQL_COM_QUERY, - MYSQL_COM_FIELD_LIST, - MYSQL_COM_CREATE_DB, - MYSQL_COM_DROP_DB, - MYSQL_COM_REFRESH, - MYSQL_COM_SHUTDOWN, - MYSQL_COM_STATISTICS, - MYSQL_COM_PROCESS_INFO, - MYSQL_COM_CONNECT, - MYSQL_COM_PROCESS_KILL, - MYSQL_COM_DEBUG, - MYSQL_COM_PING, - MYSQL_COM_TIME, - MYSQL_COM_DELAYED_INSERT, - MYSQL_COM_CHANGE_USER, - MYSQL_COM_BINLOG_DUMP, - MYSQL_COM_TABLE_DUMP, - MYSQL_COM_CONNECT_OUT, - MYSQL_COM_REGISTER_SLAVE, - MYSQL_COM_STMT_PREPARE, - MYSQL_COM_STMT_EXECUTE, - MYSQL_COM_STMT_SEND_LONG_DATA, - MYSQL_COM_STMT_CLOSE, - MYSQL_COM_STMT_RESET, - MYSQL_COM_SET_OPTION, - MYSQL_COM_STMT_FETCH, - MYSQL_COM_DAEMON, - MYSQL_COM_END /*< Must be the last */ -} mysql_server_cmd_t; - -/** - * MySQL Protocol specific state data. - * - * Protocol carries information from client side to backend side, such as - * MySQL session command information and history of earlier session commands. - */ -typedef struct { -#if defined(SS_DEBUG) - skygw_chk_t protocol_chk_top; -#endif - int fd; /*< The socket descriptor */ - struct dcb *owner_dcb; /*< The DCB of the socket - * we are running on */ - SPINLOCK protocol_lock; - uint8_t scramble[MYSQL_SCRAMBLE_LEN]; /*< server scramble, - * created or received */ - uint32_t server_capabilities; /*< server capabilities, - * created or received */ - uint32_t client_capabilities; /*< client capabilities, - * created or received */ - unsigned long tid; /*< MySQL Thread ID, in - * handshake */ - unsigned int charset; /*< MySQL character set at connect time */ -#if defined(SS_DEBUG) - skygw_chk_t protocol_chk_tail; -#endif -} PlainProtocol; - - - -#define MYSQL_GET_COMMAND(payload) (payload[4]) -#define MYSQL_GET_PACKET_NO(payload) (payload[3]) -#define MYSQL_GET_PACKET_LEN(payload) (gw_mysql_get_byte3(payload)) -#define MYSQL_GET_ERRCODE(payload) (gw_mysql_get_byte2(&payload[5])) -#define MYSQL_GET_STMTOK_NPARAM(payload) (gw_mysql_get_byte2(&payload[9])) -#define MYSQL_GET_STMTOK_NATTR(payload) (gw_mysql_get_byte2(&payload[11])) -#define MYSQL_IS_ERROR_PACKET(payload) (MYSQL_GET_COMMAND(payload)==0xff) -#define MYSQL_IS_COM_QUIT(payload) (MYSQL_GET_COMMAND(payload)==0x01) -#define MYSQL_IS_COM_INIT_DB(payload) (MYSQL_GET_COMMAND(payload)==0x02) -#define MYSQL_IS_CHANGE_USER(payload) (MYSQL_GET_COMMAND(payload)==0x11) -#define MYSQL_GET_NATTR(payload) ((int)payload[4]) - -#endif /** _MYSQL_PROTOCOL_H */ - -PlainProtocol* mysql_protocol_init(DCB* dcb, int fd); -void mysql_protocol_done (DCB* dcb); -PlainProtocol *gw_mysql_init(PlainProtocol *data); -int gw_receive_backend_auth(PlainProtocol *protocol); -int gw_decode_mysql_server_handshake(PlainProtocol *protocol, uint8_t *payload); -int gw_read_backend_handshake(PlainProtocol *protocol); -int gw_send_authentication_to_backend( - char *dbname, - char *user, - uint8_t *passwd, - PlainProtocol *protocol); - -int plain_do_connect_to_backend(char *host, int port, int* fd); diff --git a/server/modules/protocol/CMakeLists.txt b/server/modules/protocol/CMakeLists.txt index 7d118c3ec..fa1c2ab34 100644 --- a/server/modules/protocol/CMakeLists.txt +++ b/server/modules/protocol/CMakeLists.txt @@ -6,15 +6,6 @@ add_library(MySQLBackend SHARED mysql_backend.c mysql_common.c) target_link_libraries(MySQLBackend log_manager utils) install(TARGETS MySQLBackend DESTINATION modules) -add_library(plainclient SHARED plainclient.c mysql_common.c) -target_link_libraries(plainclient log_manager utils) -install(TARGETS plainclient DESTINATION modules) - -add_library(plainbackend SHARED plainbackend.c mysql_common.c) -target_link_libraries(plainbackend log_manager utils) -install(TARGETS plainbackend DESTINATION modules) - - add_library(telnetd SHARED telnetd.c) target_link_libraries(telnetd log_manager utils) install(TARGETS telnetd DESTINATION modules) diff --git a/server/modules/protocol/plainbackend.c b/server/modules/protocol/plainbackend.c deleted file mode 100644 index b52663b6a..000000000 --- a/server/modules/protocol/plainbackend.c +++ /dev/null @@ -1,894 +0,0 @@ -/* - * This file is distributed as part of the MariaDB Corporation MaxScale. It is free - * software: you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation, - * version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright MariaDB Corporation Ab 2013-2014 - */ - -#include -#include -#include -#include -#include - -#define PLAIN_BACKEND_SO_SNDBUF (128 * 1024) -#define PLAIN_BACKEND_SO_RCVBUF (128 * 1024) -/* - * MySQL Protocol module for handling the protocol between the gateway - * and the backend MySQL database. - * - * Revision History - * Date Who Description - * 14/06/2013 Mark Riddoch Initial version - * 17/06/2013 Massimiliano Pinto Added MaxScale To Backends routines - * 01/07/2013 Massimiliano Pinto Put Log Manager example code behind SS_DEBUG macros. - * 03/07/2013 Massimiliano Pinto Added delayq for incoming data before mysql connection - * 04/07/2013 Massimiliano Pinto Added asyncrhronous MySQL protocol connection to backend - * 05/07/2013 Massimiliano Pinto Added closeSession if backend auth fails - * 12/07/2013 Massimiliano Pinto Added Mysql Change User via dcb->func.auth() - * 15/07/2013 Massimiliano Pinto Added Mysql session change via dcb->func.session() - * 17/07/2013 Massimiliano Pinto Added dcb->command update from gwbuf->command for proper routing - server replies to client via router->clientReply - * 04/09/2013 Massimiliano Pinto Added dcb->session and dcb->session->client checks for NULL - * 12/09/2013 Massimiliano Pinto Added checks in gw_read_backend_event() for gw_read_backend_handshake - * 27/09/2013 Massimiliano Pinto Changed in gw_read_backend_event the check for dcb_read(), now is if rc < 0 - * 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support - * 10/11/2014 Massimiliano Pinto Client charset is passed to backend - * - */ -#include - -MODULE_INFO info = { - MODULE_API_PROTOCOL, - MODULE_GA, - GWPROTOCOL_VERSION, - "The plain protocol" -}; - -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - -static char *version_str = "V2.0.0"; -static int plain_create_backend_connection(DCB *backend, SERVER *server, SESSION *in_session); -static int plain_read_backend_event(DCB* dcb); -static int plain_write_ready_backend_event(DCB *dcb); -static int plain_write_backend(DCB *dcb, GWBUF *queue); -static int plain_error_backend_event(DCB *dcb); -static int plain_backend_close(DCB *dcb); -static int plain_backend_hangup(DCB *dcb); -static int backend_write_delayqueue(DCB *dcb); -static void backend_set_delayqueue(DCB *dcb, GWBUF *queue); -static int plain_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue); -static GWBUF* process_response_data (DCB* dcb, GWBUF* readbuf, int nbytes_to_process); -extern char* create_auth_failed_msg( GWBUF* readbuf, char* hostaddr, uint8_t* sha1); -extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db); -static bool sescmd_response_complete(DCB* dcb); - -static GWPROTOCOL MyObject = { - plain_read_backend_event, /* Read - EPOLLIN handler */ - plain_write_backend, /* Write - data from gateway */ - plain_write_ready_backend_event, /* WriteReady - EPOLLOUT handler */ - plain_error_backend_event, /* Error - EPOLLERR handler */ - plain_backend_hangup, /* HangUp - EPOLLHUP handler */ - NULL, /* Accept */ - plain_create_backend_connection, /* Connect */ - plain_backend_close, /* Close */ - NULL, /* Listen */ - NULL, /* Authentication */ - NULL /* Session */ -}; - -/* - * Implementation of the mandatory version entry point - * - * @return version string of the module - */ -char * -version() -{ - return version_str; -} - -/* - * The module initialisation routine, called when the module - * is first loaded. - */ -void -ModuleInit() -{ -} - -/* - * The module entry point routine. It is this routine that - * must populate the structure that is referred to as the - * "module object", this is a structure with the set of - * external entry points for this module. - * - * @return The module object - */ -GWPROTOCOL * -GetModuleObject() -{ - return &MyObject; -} - - -/** - * Creates MySQL protocol structure - * - * @param dcb * Must be non-NULL. - * @param fd - * - * @return - * - * - * @details Protocol structure does not have fd because dcb is not - * connected yet. - * - */ -PlainProtocol* plain_protocol_init( - DCB* dcb, - int fd) -{ - PlainProtocol* p; - - p = (PlainProtocol *) calloc(1, sizeof(PlainProtocol)); - ss_dassert(p != NULL); - - if (p == NULL) { - int eno = errno; - errno = 0; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [mysql_init_protocol] MySQL protocol init failed : " - "memory allocation due error %d, %s.", - pthread_self(), - eno, - strerror(eno)))); - goto return_p; - } - - /*< Assign fd with protocol */ - p->fd = fd; - p->owner_dcb = dcb; - -return_p: - return p; -} - - -/** - * Backend Read Event for EPOLLIN on the MySQL backend protocol module - * @param dcb The backend Descriptor Control Block - * @return 1 on operation, 0 for no action - */ -static int plain_read_backend_event(DCB *dcb) { - PlainProtocol *client_protocol = NULL; - PlainProtocol *backend_protocol = NULL; - - int rc = 0; - - - backend_protocol = (PlainProtocol *) dcb->protocol; - CHK_PROTOCOL(backend_protocol); - - - /* reading MySQL command output from backend and writing to the client */ - { - GWBUF *read_buffer = NULL; - ROUTER_OBJECT *router = NULL; - ROUTER *router_instance = NULL; - SESSION *session = dcb->session; - int nbytes_read = 0; - - CHK_SESSION(session); - router = session->service->router; - router_instance = session->service->router_instance; - - /* read available backend data */ - rc = dcb_read(dcb, &read_buffer); - - if (rc < 0) - { - GWBUF* errbuf; - bool succp; - - errbuf = mysql_create_custom_error( - 1, - 0, - "Read from backend failed"); - - router->handleError( - router_instance, - session->router_session, - errbuf, - dcb, - ERRACT_NEW_CONNECTION, - &succp); - gwbuf_free(errbuf); - - if (!succp) - { - spinlock_acquire(&session->ses_lock); - session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); - } - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); - rc = 0; - goto return_rc; - } - nbytes_read = gwbuf_length(read_buffer); - - if (nbytes_read == 0 && dcb->dcb_readqueue == NULL) - { - goto return_rc; - } - else - { - ss_dassert(read_buffer != NULL || dcb->dcb_readqueue != NULL); - } - - if (dcb->session->state == SESSION_STATE_ROUTER_READY && - dcb->session->client != NULL && - dcb->session->client->state == DCB_STATE_POLLING) - { - client_protocol = SESSION_PROTOCOL(dcb->session, - PlainProtocol); - - { - gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL); - router->clientReply(router_instance, session->router_session, read_buffer, dcb); - rc = 1; - } - } - else /*< session is closing; replying to client isn't possible */ - { - gwbuf_free(read_buffer); - } - } - -return_rc: - return rc; - -return_with_lock: - - goto return_rc; -} - -/* - * EPOLLOUT handler for the MySQL Backend protocol module. - * - * @param dcb The descriptor control block - * @return 1 in success, 0 in case of failure, - */ -static int plain_write_ready_backend_event(DCB *dcb) { - int rc = 0; - PlainProtocol *backend_protocol = dcb->protocol; - - /*< - * Don't write to backend if backend_dcb is not in poll set anymore. - */ - if (dcb->state != DCB_STATE_POLLING) { - uint8_t* data; - - if (dcb->writeq != NULL) - { - data = (uint8_t *)GWBUF_DATA(dcb->writeq); - - - } - else - { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_write_backend_event] Dcb %p in state %s " - "but there's nothing to write either.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - rc = 1; - } - goto return_rc; - } - - dcb_drain_writeq(dcb); - rc = 1; -return_rc: - - - return rc; -} - - -/** - * plain_do_connect_to_backend - * - * This routine creates socket and connects to a backend server. - * Connect it non-blocking operation. If connect fails, socket is closed. - * - * @param host The host to connect to - * @param port The host TCP/IP port - * @param *fd where connected fd is copied - * @return 0/1 on success and -1 on failure - * If successful, fd has file descriptor to socket which is connected to - * backend server. In failure, fd == -1 and socket is closed. - * - */ -int plain_do_connect_to_backend( - char *host, - int port, - int *fd) -{ - struct sockaddr_in serv_addr; - int rv; - int so = 0; - int bufsize; - - memset(&serv_addr, 0, sizeof serv_addr); - serv_addr.sin_family = AF_INET; - so = socket(AF_INET,SOCK_STREAM,0); - - if (so < 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Establishing connection to backend server " - "%s:%d failed.\n\t\t Socket creation failed " - "due %d, %s.", - host, - port, - errno, - strerror(errno)))); - rv = -1; - goto return_rv; - } - /* prepare for connect */ - setipaddress(&serv_addr.sin_addr, host); - serv_addr.sin_port = htons(port); - bufsize = PLAIN_BACKEND_SO_SNDBUF; - - if(setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) != 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to set socket options " - "%s:%d failed.\n\t\t Socket configuration failed " - "due %d, %s.", - host, - port, - errno, - strerror(errno)))); - rv = -1; - /** Close socket */ - goto close_so; - } - bufsize = PLAIN_BACKEND_SO_RCVBUF; - - if(setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) != 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to set socket options " - "%s:%d failed.\n\t\t Socket configuration failed " - "due %d, %s.", - host, - port, - errno, - strerror(errno)))); - rv = -1; - /** Close socket */ - goto close_so; - } - - /* set socket to as non-blocking here */ - setnonblocking(so); - rv = connect(so, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); - - if (rv != 0) - { - if (errno == EINPROGRESS) - { - rv = 1; - } - else - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to connect backend server %s:%d, " - "due %d, %s.", - host, - port, - errno, - strerror(errno)))); - /** Close socket */ - goto close_so; - } - } - *fd = so; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [plain_do_connect_to_backend] Connected to backend server " - "%s:%d, fd %d.", - pthread_self(), - host, - port, - so))); - -return_rv: - return rv; - -close_so: - /*< Close newly created socket. */ - if (close(so) != 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to " - "close socket %d due %d, %s.", - so, - errno, - strerror(errno)))); - } - goto return_rv; -} - -/* - * Write function for backend DCB. Store command to protocol. - * - * @param dcb The DCB of the backend - * @param queue Queue of buffers to write - * @return 0 on failure, 1 on success - */ -static int -plain_write_backend(DCB *dcb, GWBUF *queue) -{ - PlainProtocol *backend_protocol = dcb->protocol; - int rc = 0; - - rc = dcb_write(dcb, queue); - - return rc; -} - -/** - * Error event handler. - * Create error message, pass it to router's error handler and if error - * handler fails in providing enough backend servers, mark session being - * closed and call DCB close function which triggers closing router session - * and related backends (if any exists. - */ -static int plain_error_backend_event(DCB *dcb) -{ - SESSION* session; - void* rsession; - ROUTER_OBJECT* router; - ROUTER* router_instance; - GWBUF* errbuf; - bool succp; - session_state_t ses_state; - - CHK_DCB(dcb); - session = dcb->session; - CHK_SESSION(session); - rsession = session->router_session; - router = session->service->router; - router_instance = session->service->router_instance; - - /** - * Avoid running redundant error handling procedure. - * dcb_close is already called for the DCB. Thus, either connection is - * closed by router and COM_QUIT sent or there was an error which - * have already been handled. - */ - if (dcb->state != DCB_STATE_POLLING) - { - int error, len; - char buf[100]; - - len = sizeof(error); - - if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) - { - if (error != 0) - { - strerror_r(error, buf, 100); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "DCB in state %s got error '%s'.", - STRDCBSTATE(dcb->state), - buf))); - } - } - return 1; - } - - spinlock_acquire(&session->ses_lock); - ses_state = session->state; - spinlock_release(&session->ses_lock); - - /** - * Session might be initialized when DCB already is in the poll set. - * Thus hangup can occur in the middle of session initialization. - * Only complete and successfully initialized sessions allow for - * calling error handler. - */ - while (ses_state == SESSION_STATE_READY) - { - spinlock_acquire(&session->ses_lock); - ses_state = session->state; - spinlock_release(&session->ses_lock); - } - - if (ses_state != SESSION_STATE_ROUTER_READY) - { - int error, len; - char buf[100]; - - len = sizeof(error); - if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) - { - if (error != 0) - { - strerror_r(error, buf, 100); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error '%s' in session that is not ready for routing.", - buf))); - } - } - gwbuf_free(errbuf); - goto retblock; - } - -#if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend error event handling."))); -#endif - router->handleError(router_instance, - rsession, - errbuf, - dcb, - ERRACT_NEW_CONNECTION, - &succp); - gwbuf_free(errbuf); - - /** - * If error handler fails it means that routing session can't continue - * and it must be closed. In success, only this DCB is closed. - */ - if (!succp) - { - spinlock_acquire(&session->ses_lock); - session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); - } - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); - -retblock: - return 1; -} - -/* - * Create a new backend connection. - * - * This routine will connect to a backend server and it is called by dbc_connect - * in router->newSession - * - * @param backend_dcb, in, out, use - backend DCB allocated from dcb_connect - * @param server, in, use - server to connect to - * @param session, in use - current session from client DCB - * @return 0/1 on Success and -1 on Failure. - * If succesful, returns positive fd to socket which is connected to - * backend server. Positive fd is copied to protocol and to dcb. - * If fails, fd == -1 and socket is closed. - */ -static int plain_create_backend_connection( - DCB *backend_dcb, - SERVER *server, - SESSION *session) -{ - PlainProtocol *protocol = NULL; - int rv = -1; - int fd = -1; - - protocol = mysql_protocol_init(backend_dcb, -1); - ss_dassert(protocol != NULL); - - if (protocol == NULL) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Failed to create " - "protocol object for backend connection.", - pthread_self()))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to create " - "protocol object for backend connection."))); - goto return_fd; - } - - /*< if succeed, fd > 0, -1 otherwise */ - - rv = plain_do_connect_to_backend(server->name, server->port, &fd); - /*< Assign protocol with backend_dcb */ - backend_dcb->protocol = protocol; - - /*< Set protocol state */ - switch (rv) { - case 0: - ss_dassert(fd > 0); - protocol->fd = fd; - - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Established " - "connection to %s:%i, protocol fd %d client " - "fd %d.", - pthread_self(), - server->name, - server->port, - protocol->fd, - session->client->fd))); - break; - - case 1: - ss_dassert(fd > 0); - - protocol->fd = fd; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Connection " - "pending to %s:%i, protocol fd %d client fd %d.", - pthread_self(), - server->name, - server->port, - protocol->fd, - session->client->fd))); - break; - - default: - ss_dassert(fd == -1); - - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Connection " - "failed to %s:%i, protocol fd %d client fd %d.", - pthread_self(), - server->name, - server->port, - protocol->fd, - session->client->fd))); - break; - } /*< switch */ - -return_fd: - return fd; -} - - -/** - * Error event handler. - * Create error message, pass it to router's error handler and if error - * handler fails in providing enough backend servers, mark session being - * closed and call DCB close function which triggers closing router session - * and related backends (if any exists. - * - * @param dcb The current Backend DCB - * @return 1 always - */ -static int -plain_backend_hangup(DCB *dcb) -{ - SESSION* session; - void* rsession; - ROUTER_OBJECT* router; - ROUTER* router_instance; - bool succp; - GWBUF* errbuf; - session_state_t ses_state; - - CHK_DCB(dcb); - session = dcb->session; - CHK_SESSION(session); - - rsession = session->router_session; - router = session->service->router; - router_instance = session->service->router_instance; - - spinlock_acquire(&session->ses_lock); - ses_state = session->state; - spinlock_release(&session->ses_lock); - - /** - * Session might be initialized when DCB already is in the poll set. - * Thus hangup can occur in the middle of session initialization. - * Only complete and successfully initialized sessions allow for - * calling error handler. - */ - while (ses_state == SESSION_STATE_READY) - { - spinlock_acquire(&session->ses_lock); - ses_state = session->state; - spinlock_release(&session->ses_lock); - } - - if (ses_state != SESSION_STATE_ROUTER_READY) - { - int error, len; - char buf[100]; - - len = sizeof(error); - if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) - { - if (error != 0) - { - strerror_r(error, buf, 100); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Hangup in session that is not ready for routing, " - "Error reported is '%s'.", - buf))); - } - } - gwbuf_free(errbuf); - goto retblock; - } -#if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend hangup error handling."))); -#endif - - router->handleError(router_instance, - rsession, - errbuf, - dcb, - ERRACT_NEW_CONNECTION, - &succp); - - gwbuf_free(errbuf); - /** There are no required backends available, close session. */ - if (!succp) - { -#if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend hangup -> closing session."))); -#endif - spinlock_acquire(&session->ses_lock); - session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); - } - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); - -retblock: - return 1; -} - -/** - * Send COM_QUIT to backend so that it can be closed. - * @param dcb The current Backend DCB - * @return 1 always - */ -static int -plain_backend_close(DCB *dcb) -{ - DCB* client_dcb; - SESSION* session; - GWBUF* quitbuf; - - CHK_DCB(dcb); - session = dcb->session; - CHK_SESSION(session); - - /** - * The lock is needed only to protect the read of session->state and - * session->client values. Client's state may change by other thread - * but client's close and adding client's DCB to zombies list is executed - * only if client's DCB's state does _not_ change in parallel. - */ - spinlock_acquire(&session->ses_lock); - /** - * If session->state is STOPPING, start closing client session. - * Otherwise only this backend connection is closed. - */ - if (session != NULL && - session->state == SESSION_STATE_STOPPING && - session->client != NULL) - { - if (session->client->state == DCB_STATE_POLLING) - { - spinlock_release(&session->ses_lock); - - /** Close client DCB */ - dcb_close(session->client); - } - else - { - spinlock_release(&session->ses_lock); - } - } - else - { - spinlock_release(&session->ses_lock); - } - return 1; -} - -/** - * This routine put into the delay queue the input queue - * The input is what backend DCB is receiving - * The routine is called from func.write() when mysql backend connection - * is not yet complete buu there are inout data from client - * - * @param dcb The current backend DCB - * @param queue Input data in the GWBUF struct - */ -static void backend_set_delayqueue(DCB *dcb, GWBUF *queue) { - spinlock_acquire(&dcb->delayqlock); - - if (dcb->delayq) { - /* Append data */ - dcb->delayq = gwbuf_append(dcb->delayq, queue); - } else { - if (queue != NULL) { - /* create the delay queue */ - dcb->delayq = queue; - } - } - spinlock_release(&dcb->delayqlock); -} - -/** - * This routine writes the delayq via dcb_write - * The dcb->delayq contains data received from the client before - * mysql backend authentication succeded - * - * @param dcb The current backend DCB - * @return The dcb_write status - */ -static int backend_write_delayqueue(DCB *dcb) -{ - GWBUF *localq = NULL; - int rc; - - spinlock_acquire(&dcb->delayqlock); - - if (dcb->delayq == NULL) - { - spinlock_release(&dcb->delayqlock); - rc = 1; - } - else - { - - rc = dcb_write(dcb, localq); - } - - if (rc == 0) - { -#if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend write delayqueue error handling."))); -#endif - - } - return rc; -} diff --git a/server/modules/protocol/plainclient.c b/server/modules/protocol/plainclient.c deleted file mode 100644 index 450634e12..000000000 --- a/server/modules/protocol/plainclient.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * This file is distributed as part of the MariaDB Corporation MaxScale. It is free - * software: you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation, - * version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright MariaDB Corporation Ab 2013-2015 - */ - -/** - * @file mongo_client.c - * - * Revision History - * Date Who Description - - * - */ -#include -#include -#include -#include -#include -#include -#include - -MODULE_INFO info = { - MODULE_API_PROTOCOL, - MODULE_GA, - GWPROTOCOL_VERSION, - "The plain client protocol" -}; - -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - -static char *version_str = "V1.0.0"; - -static int plain_accept(DCB *listener); -static int plain_listen(DCB *listener, char *config_bind); -static int plain_read(DCB* dcb); -static int plain_write_ready(DCB *dcb); -static int plain_write(DCB *dcb, GWBUF *queue); -static int plain_client_error(DCB *dcb); -static int plain_client_close(DCB *dcb); -static int plain_client_hangup_event(DCB *dcb); - -/* - * The "module object" for the mysqld client protocol module. - */ -static GWPROTOCOL MyObject = { - plain_read, /* Read - EPOLLIN handler */ - plain_write, /* Write - data from gateway */ - plain_write_ready, /* WriteReady - EPOLLOUT handler */ - plain_client_error, /* Error - EPOLLERR handler */ - plain_client_hangup_event, /* HangUp - EPOLLHUP handler */ - plain_accept, /* Accept */ - NULL, /* Connect */ - plain_client_close, /* Close */ - plain_listen, /* Listen */ - NULL, /* Authentication */ - NULL /* Session */ -}; - -/** - * Implementation of the mandatory version entry point - * - * @return version string of the module - */ -char * -version() -{ - return version_str; -} - -/** - * The module initialisation routine, called when the module - * is first loaded. - */ -void -ModuleInit() -{ -} - -/** - * The module entry point routine. It is this routine that - * must populate the structure that is referred to as the - * "module object", this is a structure with the set of - * external entry points for this module. - * - * @return The module object - */ -GWPROTOCOL * -GetModuleObject() -{ - return &MyObject; -} - - -/** - * Write function for client DCB: writes data from MaxScale to Client - * - * @param dcb The DCB of the client - * @param queue Queue of buffers to write - */ -int -plain_write(DCB *dcb, GWBUF *queue) -{ - return dcb_write(dcb, queue); -} - -/** - * Client read event triggered by EPOLLIN - * - * @param dcb Descriptor control block - * @return 0 if succeed, 1 otherwise - */ -int plain_read( - DCB* dcb) -{ - SESSION *session = NULL; - ROUTER_OBJECT *router = NULL; - ROUTER *router_instance = NULL; - void *rsession = NULL; - PlainProtocol *protocol = NULL; - GWBUF *read_buffer = NULL; - int rc = 0; - int nbytes_read = 0; - uint8_t cap = 0; - bool stmt_input = false; /*< router input type */ - - CHK_DCB(dcb); - protocol = DCB_PROTOCOL(dcb, PlainProtocol); - CHK_PROTOCOL(protocol); - rc = dcb_read(dcb, &read_buffer); - - if (rc < 0) - { - dcb_close(dcb); - } - nbytes_read = gwbuf_length(read_buffer); - - if (nbytes_read == 0) - { - goto return_rc; - } - - if(dcb->session == NULL) - { - dcb->session = session_alloc(dcb->service,dcb); - } - - rc = SESSION_ROUTE_QUERY(dcb->session, read_buffer); - - -return_rc: - - return rc; -} - -/////////////////////////////////////////////// -// client write event to Client triggered by EPOLLOUT -////////////////////////////////////////////// -/** - * @node Client's fd became writable, and EPOLLOUT event - * arrived. As a consequence, client input buffer (writeq) is flushed. - * - * Parameters: - * @param dcb - in, use - * client dcb - * - * @return constantly 1 - * - * - * @details (write detailed description here) - * - */ -int plain_write_ready(DCB *dcb) -{ - PlainProtocol *protocol = NULL; - - CHK_DCB(dcb); - - ss_dassert(dcb->state != DCB_STATE_DISCONNECTED); - - if (dcb == NULL) { - goto return_1; - } - - if (dcb->state == DCB_STATE_DISCONNECTED) { - goto return_1; - } - - if (dcb->protocol == NULL) { - goto return_1; - } - protocol = (PlainProtocol *)dcb->protocol; - - - dcb_drain_writeq(dcb); - goto return_1; - -return_1: - - return 1; -} - -/** - * set listener for mysql protocol, retur 1 on success and 0 in failure - */ -int plain_listen( - DCB *listen_dcb, - char *config_bind) -{ - int l_so; - int syseno = 0; - struct sockaddr_in serv_addr; - struct sockaddr_un local_addr; - struct sockaddr *current_addr; - int one = 1; - int rc; - - if (strchr(config_bind, '/')) { - char *tmp = strrchr(config_bind, ':'); - if (tmp) - *tmp = '\0'; - - // UNIX socket create - if ((l_so = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, - "\n* Error: can't create UNIX socket due " - "error %i, %s.\n\n\t", - errno, - strerror(errno)); - return 0; - } - memset(&local_addr, 0, sizeof(local_addr)); - local_addr.sun_family = AF_UNIX; - strncpy(local_addr.sun_path, config_bind, sizeof(local_addr.sun_path) - 1); - - current_addr = (struct sockaddr *) &local_addr; - - } else { - /* MaxScale, as default, will bind on port 4406 */ - if (!parse_bindconfig(config_bind, 4406, &serv_addr)) { - fprintf(stderr, "Error in parse_bindconfig for [%s]\n", config_bind); - return 0; - } - // TCP socket create - if ((l_so = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, - "\n* Error: can't create socket due " - "error %i, %s.\n\n\t", - errno, - strerror(errno)); - return 0; - } - - current_addr = (struct sockaddr *) &serv_addr; - } - - listen_dcb->fd = -1; - - // socket options - if((syseno = setsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))) != 0){ - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); - } - - - // set NONBLOCKING mode - setnonblocking(l_so); - - /* get the right socket family for bind */ - switch (current_addr->sa_family) { - case AF_UNIX: - rc = unlink(config_bind); - if ( (rc == -1) && (errno!=ENOENT) ) { - fprintf(stderr, "Error unlink Unix Socket %s\n", config_bind); - } - - if (bind(l_so, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { - fprintf(stderr, - "\n* Bind failed due error %i, %s.\n", - errno, - strerror(errno)); - fprintf(stderr, "* Can't bind to %s\n\n", config_bind); - close(l_so); - return 0; - } - - /* set permission for all users */ - if (chmod(config_bind, 0777) < 0) { - fprintf(stderr, - "\n* chmod failed for %s due error %i, %s.\n\n", - config_bind, - errno, - strerror(errno)); - } - - break; - - case AF_INET: - if (bind(l_so, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - fprintf(stderr, - "\n* Bind failed due error %i, %s.\n", - errno, - strerror(errno)); - fprintf(stderr, "* Can't bind to %s\n\n", config_bind); - close(l_so); - return 0; - } - break; - - default: - fprintf(stderr, "* Socket Family %i not supported\n", current_addr->sa_family); - close(l_so); - return 0; - } - - rc = listen(l_so, 10 * SOMAXCONN); - - if (rc == 0) { - LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening MySQL connections at %s", config_bind))); - } else { - int eno = errno; - errno = 0; - fprintf(stderr, - "\n* Failed to start listening MySQL due error %d, %s\n\n", - eno, - strerror(eno)); - close(l_so); - return 0; - } - // assign l_so to dcb - listen_dcb->fd = l_so; - - // add listening socket to poll structure - if (poll_add_dcb(listen_dcb) == -1) { - fprintf(stderr, - "\n* Failed to start polling the socket due error " - "%i, %s.\n\n", - errno, - strerror(errno)); - return 0; - } -#if defined(FAKE_CODE) - conn_open[l_so] = true; -#endif /* FAKE_CODE */ - listen_dcb->func.accept = plain_accept; - - return 1; -} - - -/** - * @node (write brief function description here) - * - * Parameters: - * @param listener - - * - * - * @return 0 in success, 1 in failure - * - * - * @details (write detailed description here) - * - */ -int plain_accept(DCB *listener) -{ - int rc = 0; - DCB *client_dcb; - PlainProtocol *protocol; - int c_sock; - struct sockaddr client_conn; - socklen_t client_len = sizeof(struct sockaddr_storage); - int sendbuf = GW_BACKEND_SO_SNDBUF; - socklen_t optlen = sizeof(sendbuf); - int eno = 0; - int syseno = 0; - int i = 0; - - CHK_DCB(listener); - - while (1) { - - retry_accept: - - // new connection from client - c_sock = accept(listener->fd, - (struct sockaddr *) &client_conn, - &client_len); - eno = errno; - errno = 0; - - if (c_sock == -1) { - - if (eno == EAGAIN || eno == EWOULDBLOCK) - { - /** - * We have processed all incoming connections. - */ - rc = 1; - goto return_rc; - } - else if (eno == ENFILE || eno == EMFILE) - { - struct timespec ts1; - ts1.tv_sec = 0; - /** - * Exceeded system's (ENFILE) or processes - * (EMFILE) max. number of files limit. - */ - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [plain_accept] Error %d, %s. ", - pthread_self(), - eno, - strerror(eno)))); - - if (i == 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error %d, %s. " - "Failed to accept new client " - "connection.", - eno, - strerror(eno)))); - } - i++; - ts1.tv_nsec = 100*i*i*1000000; - nanosleep(&ts1, NULL); - - if (i<10) { - goto retry_accept; - } - rc = 1; - goto return_rc; - } - else - { - /** - * Other error. - */ - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [plain_accept] Error %d, %s.", - pthread_self(), - eno, - strerror(eno)))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to accept new client " - "connection due to %d, %s.", - eno, - strerror(eno)))); - rc = 1; - goto return_rc; - } /* if (eno == ..) */ - } /* if (c_sock == -1) */ - /* reset counter */ - i = 0; - - listener->stats.n_accepts++; -#if defined(SS_DEBUG) - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [plain_accept] Accepted fd %d.", - pthread_self(), - c_sock))); -#endif /* SS_DEBUG */ - - /* set nonblocking */ - sendbuf = GW_CLIENT_SO_SNDBUF; - - if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen)) != 0){ - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); - } - - sendbuf = GW_CLIENT_SO_RCVBUF; - - if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_RCVBUF, &sendbuf, optlen)) != 0){ - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); - } - setnonblocking(c_sock); - - client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); - - if (client_dcb == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to create " - "DCB object for client connection."))); - close(c_sock); - rc = 1; - goto return_rc; - } - - client_dcb->service = listener->session->service; - client_dcb->fd = c_sock; - - // get client address - if ( client_conn.sa_family == AF_UNIX) - { - // client address - client_dcb->remote = strdup("localhost_from_socket"); - // set localhost IP for user authentication - (client_dcb->ipv4).sin_addr.s_addr = 0x0100007F; - } - else - { - /* client IPv4 in raw data*/ - memcpy(&client_dcb->ipv4, - (struct sockaddr_in *)&client_conn, - sizeof(struct sockaddr_in)); - /* client IPv4 in string representation */ - client_dcb->remote = (char *)calloc(INET_ADDRSTRLEN+1, sizeof(char)); - - if (client_dcb->remote != NULL) - { - inet_ntop(AF_INET, - &(client_dcb->ipv4).sin_addr, - client_dcb->remote, - INET_ADDRSTRLEN); - } - } - protocol = mysql_protocol_init(client_dcb, c_sock); - ss_dassert(protocol != NULL); - - if (protocol == NULL) { - /** delete client_dcb */ - dcb_close(client_dcb); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [plain_accept] Failed to create " - "protocol object for client connection.", - pthread_self()))); - rc = 1; - goto return_rc; - } - client_dcb->protocol = protocol; - // assign function poiters to "func" field - memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); - - - /** - * Set new descriptor to event set. At the same time, - * change state to DCB_STATE_POLLING so that - * thread which wakes up sees correct state. - */ - if (poll_add_dcb(client_dcb) == -1) - { - /* Send a custom error as MySQL command reply */ - - /** close client_dcb */ - dcb_close(client_dcb); - - /** Previous state is recovered in poll_add_dcb. */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [plain_accept] Failed to add dcb %p for " - "fd %d to epoll set.", - pthread_self(), - client_dcb, - client_dcb->fd))); - rc = 1; - goto return_rc; - } - else - { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [plain_accept] Added dcb %p for fd " - "%d to epoll set.", - pthread_self(), - client_dcb, - client_dcb->fd))); - } - } /**< while 1 */ -#if defined(SS_DEBUG) - if (rc == 0) { - CHK_DCB(client_dcb); - CHK_PROTOCOL(((PlainProtocol *)client_dcb->protocol)); - } -#endif -return_rc: - - return rc; -} - -static int plain_client_error( - DCB* dcb) -{ - SESSION* session; - - CHK_DCB(dcb); - - session = dcb->session; - - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [plain_client_error] Error event handling for DCB %p " - "in state %s, session %p.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - (session != NULL ? session : NULL)))); - - if (session != NULL && session->state == SESSION_STATE_STOPPING) - { - goto retblock; - } - - dcb_close(dcb); - -retblock: - return 1; -} - -static int -plain_client_close(DCB *dcb) -{ - SESSION* session; - ROUTER_OBJECT* router; - void* router_instance; -#if defined(SS_DEBUG) - PlainProtocol* protocol = (PlainProtocol *)dcb->protocol; - if (dcb->state == DCB_STATE_POLLING || - dcb->state == DCB_STATE_NOPOLLING || - dcb->state == DCB_STATE_ZOMBIE) - { - if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(protocol); - } -#endif - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [plain_client_close]", - pthread_self()))); - mysql_protocol_done(dcb); - session = dcb->session; - /** - * session may be NULL if session_alloc failed. - * In that case, router session wasn't created. - */ - if (session != NULL) - { - CHK_SESSION(session); - spinlock_acquire(&session->ses_lock); - - if (session->state != SESSION_STATE_STOPPING) - { - session->state = SESSION_STATE_STOPPING; - } - router_instance = session->service->router_instance; - router = session->service->router; - /** - * If router session is being created concurrently router - * session might be NULL and it shouldn't be closed. - */ - if (session->router_session != NULL) - { - spinlock_release(&session->ses_lock); - /** Close router session and all its connections */ - router->closeSession(router_instance, session->router_session); - } - else - { - spinlock_release(&session->ses_lock); - } - } - return 1; -} - -/** - * Handle a hangup event on the client side descriptor. - * - * We simply close the DCB, this will propogate the closure to any - * backend descriptors and perform the session cleanup. - * - * @param dcb The DCB of the connection - */ -static int -plain_client_hangup_event(DCB *dcb) -{ - SESSION* session; - - CHK_DCB(dcb); - session = dcb->session; - - if (session != NULL && session->state == SESSION_STATE_ROUTER_READY) - { - CHK_SESSION(session); - } - - if (session != NULL && session->state == SESSION_STATE_STOPPING) - { - goto retblock; - } - - dcb_close(dcb); - -retblock: - return 1; -} - diff --git a/server/modules/routing/plainroute.c b/server/modules/routing/plainroute.c deleted file mode 100644 index 245a3427a..000000000 --- a/server/modules/routing/plainroute.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - * This file is distributed as part of the MariaDB Corporation MaxScale. It is free - * software: you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation, - * version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright MariaDB Corporation Ab 2013-2014 - */ - -/** - * @file readconnroute.c - Read Connection Load Balancing Query Router - * - * This is the implementation of a simple query router that balances - * read connections. It assumes the service is configured with a set - * of slaves and that the application clients already split read and write - * queries. It offers a service to balance the client read connections - * over this set of slave servers. It does this once only, at the time - * the connection is made. It chooses the server that currently has the least - * number of connections by keeping a count for each server of how - * many connections the query router has made to the server. - * - * When two servers have the same number of current connections the one with - * the least number of connections since startup will be used. - * - * The router may also have options associated to it that will limit the - * choice of backend server. Currently two options are supported, the "master" - * option will cause the router to only connect to servers marked as masters - * and the "slave" option will limit connections to routers that are marked - * as slaves. If neither option is specified the router will connect to either - * masters or slaves. - * - * @verbatim - * Revision History - * - * Date Who Description - * 14/06/2013 Mark Riddoch Initial implementation - * 25/06/2013 Mark Riddoch Addition of checks for current server state - * 26/06/2013 Mark Riddoch Use server with least connections since - * startup if the number of current - * connections is the same for two servers - * Addition of master and slave options - * 27/06/2013 Vilho Raatikka Added skygw_log_write command as an example - * and necessary headers. - * 17/07/2013 Massimiliano Pinto Added clientReply routine: - * called by backend server to send data to client - * Included mysql_client_server_protocol.h - * with macros and MySQL commands with MYSQL_ prefix - * avoiding any conflict with the standard ones - * in mysql.h - * 22/07/2013 Mark Riddoch Addition of joined router option for Galera - * clusters - * 31/07/2013 Massimiliano Pinto Added a check for candidate server, if NULL return - * 12/08/2013 Mark Riddoch Log unsupported router options - * 04/09/2013 Massimiliano Pinto Added client NULL check in clientReply - * 22/10/2013 Massimiliano Pinto errorReply called from backend, for client error reply - * or take different actions such as open a new backend connection - * 20/02/2014 Massimiliano Pinto If router_options=slave, route traffic to master if no slaves available - * 06/03/2014 Massimiliano Pinto Server connection counter is now updated in closeSession - * 24/06/2014 Massimiliano Pinto New rules for selecting the Master server - * 27/06/2014 Mark Riddoch Addition of server weighting - * - * @endverbatim - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - -MODULE_INFO info = { - MODULE_API_ROUTER, - MODULE_GA, - ROUTER_VERSION, - "A connection based router to load balance based on connections" -}; - -static char *version_str = "V1.1.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 handleError( - ROUTER *instance, - void *router_session, - GWBUF *errbuf, - DCB *backend_dcb, - error_action_t action, - bool *succp); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); - - -/** The module object definition */ -static ROUTER_OBJECT MyObject = { - createInstance, - newSession, - closeSession, - freeSession, - routeQuery, - diagnostics, - clientReply, - handleError, - getCapabilities -}; - -static bool rses_begin_locked_router_action( - ROUTER_CLIENT_SES* rses); - -static void rses_end_locked_router_action( - ROUTER_CLIENT_SES* rses); - -static BACKEND *get_root_master( - BACKEND **servers); -static int handle_state_switch( - DCB* dcb,DCB_REASON reason, void * routersession); -static SPINLOCK instlock; -static ROUTER_INSTANCE *instances; - -/** - * Implementation of the mandatory version entry point - * - * @return version string of the module - */ -char * -version() -{ - return version_str; -} - -/** - * The module initialisation routine, called when the module - * is first loaded. - */ -void -ModuleInit() -{ - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Initialise readconnroute router module %s.\n", version_str))); - spinlock_init(&instlock); - instances = NULL; -} - -/** - * The module entry point routine. It is this routine that - * must populate the structure that is referred to as the - * "module object", this is a structure with the set of - * external entry points for this module. - * - * @return The module object - */ -ROUTER_OBJECT * -GetModuleObject() -{ - return &MyObject; -} - -/** - * Create an instance of the router for a particular service - * within the gateway. - * - * @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 * -createInstance(SERVICE *service, char **options) -{ -ROUTER_INSTANCE *inst; -SERVER *server; -SERVER_REF *sref; -int i, n; -BACKEND *backend; -char *weightby; - - if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) { - return NULL; - } - - inst->service = service; - spinlock_init(&inst->lock); - - /* - * We need an array of the backend servers in the instance structure so - * that we can maintain a count of the number of connections to each - * backend server. - */ - for (sref = service->dbref, n = 0; sref; sref = sref->next) - n++; - - inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *)); - if (!inst->servers) - { - free(inst); - return NULL; - } - - for (sref = service->dbref, n = 0; sref; sref = sref->next) - { - if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL) - { - for (i = 0; i < n; i++) - free(inst->servers[i]); - free(inst->servers); - free(inst); - return NULL; - } - inst->servers[n]->server = sref->server; - inst->servers[n]->current_connection_count = 0; - inst->servers[n]->weight = 1000; - n++; - } - inst->servers[n] = NULL; - - if ((weightby = serviceGetWeightingParameter(service)) != NULL) - { - int total = 0; - for (n = 0; inst->servers[n]; n++) - { - backend = inst->servers[n]; - total += atoi(serverGetParameter(backend->server, - weightby)); - } - if (total == 0) - { - LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, - "WARNING: Weighting Parameter for service '%s' " - "will be ignored as no servers have values " - "for the parameter '%s'.\n", - service->name, weightby))); - } - else - { - for (n = 0; inst->servers[n]; n++) - { - int perc, wght; - backend = inst->servers[n]; - perc = ((wght = atoi(serverGetParameter(backend->server, - weightby))) * 1000) / total; - if (perc == 0 && wght != 0) - perc = 1; - backend->weight = perc; - if (perc == 0) - { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Server '%s' has no value " - "for weighting parameter '%s', " - "no queries will be routed to " - "this server.\n", - inst->servers[n]->server->unique_name, - weightby))); - } - - } - } - } - - /* - * Process the options - */ - inst->bitmask = 0; - inst->bitvalue = 0; - if (options) - { - for (i = 0; options[i]; i++) - { - if (!strcasecmp(options[i], "master")) - { - inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE); - inst->bitvalue |= SERVER_MASTER; - } - else if (!strcasecmp(options[i], "slave")) - { - inst->bitmask |= (SERVER_MASTER|SERVER_SLAVE); - inst->bitvalue |= SERVER_SLAVE; - } - else if (!strcasecmp(options[i], "synced")) - { - inst->bitmask |= (SERVER_JOINED); - inst->bitvalue |= SERVER_JOINED; - } - else if (!strcasecmp(options[i], "ndb")) - { - inst->bitmask |= (SERVER_NDB); - inst->bitvalue |= SERVER_NDB; - } - else - { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "* Warning : Unsupported router " - "option \'%s\' for readconnroute. " - "Expected router options are " - "[slave|master|synced|ndb]", - options[i]))); - } - } - } - - /* - * 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); - - return (ROUTER *)inst; -} - -/** - * Associate a new session with this instance of the router. - * - * @param instance The router instance data - * @param session The session itself - * @return Session specific data for this session - */ -static void * -newSession(ROUTER *instance, SESSION *session) -{ -ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance; -ROUTER_CLIENT_SES *client_rses; -BACKEND *candidate = NULL; -int i; - - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [newSession] new router session with session " - "%p, and inst %p.", - pthread_self(), - session, - inst))); - - - client_rses = (ROUTER_CLIENT_SES *)calloc(1, sizeof(ROUTER_CLIENT_SES)); - - if (client_rses == NULL) { - return NULL; - } - -#if defined(SS_DEBUG) - client_rses->rses_chk_top = CHK_NUM_ROUTER_SES; - client_rses->rses_chk_tail = CHK_NUM_ROUTER_SES; -#endif - - - /** - * Find a backend server to connect to. This is the extent of the - * load balancing algorithm we need to implement for this simple - * connection router. - */ - - /* - * Loop over all the servers and find any that have fewer connections - * than the candidate server. - * - * If a server has less connections than the current candidate we mark this - * as the new candidate to connect to. - * - * If a server has the same number of connections currently as the candidate - * and has had less connections over time than the candidate it will also - * become the new candidate. This has the effect of spreading the - * connections over different servers during periods of very low load. - */ - for (i = 0; inst->servers[i]; i++) { - - - if (SERVER_IN_MAINT(inst->servers[i]->server)) - continue; - - if (inst->servers[i]->weight == 0) - continue; - - - - /* If no candidate set, set first running server as - our initial candidate server */ - if(candidate == NULL) - { - candidate = inst->servers[i]; - } - else if((inst->servers[i]->current_connection_count - * 1000) / inst->servers[i]->weight < - (candidate->current_connection_count * - 1000) / candidate->weight) - { - /* This running server has fewer - connections, set it as a new candidate */ - candidate = inst->servers[i]; - } - else if((inst->servers[i]->current_connection_count - * 1000) / inst->servers[i]->weight == - (candidate->current_connection_count * - 1000) / candidate->weight && - inst->servers[i]->server->stats.n_connections < - candidate->server->stats.n_connections) - { - /* This running server has the same number - of connections currently as the candidate - but has had fewer connections over time - than candidate, set this server to candidate*/ - candidate = inst->servers[i]; - } - } - - - - client_rses->rses_capabilities = RCAP_TYPE_PACKET_INPUT; - - /* - * We now have the server with the least connections. - * Bump the connection count for this server - */ - atomic_add(&candidate->current_connection_count, 1); - client_rses->backend = candidate; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [newSession] Selected server in port %d. " - "Connections : %d\n", - pthread_self(), - candidate->server->port, - candidate->current_connection_count))); - /* - * Open a backend connection, putting the DCB for this - * connection in the client_rses->backend_dcb - */ - client_rses->backend_dcb = dcb_connect(candidate->server, - session, - candidate->server->protocol); - if (client_rses->backend_dcb == NULL) - { - atomic_add(&candidate->current_connection_count, -1); - free(client_rses); - return NULL; - } - dcb_add_callback( - client_rses->backend_dcb, - DCB_REASON_NOT_RESPONDING, - &handle_state_switch, - client_rses); - inst->stats.n_sessions++; - - /** - * Add this session to the list of active sessions. - */ - spinlock_acquire(&inst->lock); - client_rses->next = inst->connections; - inst->connections = client_rses; - spinlock_release(&inst->lock); - - - return (void *)client_rses; -} - -/** - * @node Unlink from backend server, unlink from router's connection list, - * and free memory of a router client session. - * - * Parameters: - * @param router - - * - * - * @param router_cli_ses - - * - * - * @return void - * - * - * @details (write detailed description here) - * - */ -static void freeSession( - ROUTER* router_instance, - void* router_client_ses) -{ - ROUTER_INSTANCE* router = (ROUTER_INSTANCE *)router_instance; - ROUTER_CLIENT_SES* router_cli_ses = - (ROUTER_CLIENT_SES *)router_client_ses; - int prev_val; - - prev_val = atomic_add(&router_cli_ses->backend->current_connection_count, -1); - ss_dassert(prev_val > 0); - - spinlock_acquire(&router->lock); - - if (router->connections == router_cli_ses) { - router->connections = router_cli_ses->next; - } else { - ROUTER_CLIENT_SES *ptr = router->connections; - - while (ptr != NULL && ptr->next != router_cli_ses) { - ptr = ptr->next; - } - - if (ptr != NULL) { - ptr->next = router_cli_ses->next; - } - } - spinlock_release(&router->lock); - - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [freeSession] Unlinked router_client_session %p from " - "router %p and from server on port %d. Connections : %d. ", - pthread_self(), - router_cli_ses, - router, - router_cli_ses->backend->server->port, - prev_val-1))); - - free(router_cli_ses); -} - - -/** - * 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 - */ -static void -closeSession(ROUTER *instance, void *router_session) -{ -ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session; -DCB* backend_dcb; - - CHK_CLIENT_RSES(router_cli_ses); - /** - * Lock router client session for secure read and update. - */ - if (rses_begin_locked_router_action(router_cli_ses)) - { - /* decrease server current connection counter */ - atomic_add(&router_cli_ses->backend->server->stats.n_current, -1); - - backend_dcb = router_cli_ses->backend_dcb; - router_cli_ses->backend_dcb = NULL; - router_cli_ses->rses_closed = true; - /** Unlock */ - rses_end_locked_router_action(router_cli_ses); - - /** - * Close the backend server connection - */ - if (backend_dcb != NULL) { - CHK_DCB(backend_dcb); - dcb_close(backend_dcb); - } - } -} - -/** - * We have data from the client, we must route it to the backend. - * This is simply a case of sending it to the connection that was - * chosen when we started the client session. - * - * @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 if succeed 1, otherwise 0 - */ -static int -routeQuery(ROUTER *instance, void *router_session, GWBUF *queue) -{ - ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance; - ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session; - uint8_t *payload = GWBUF_DATA(queue); - int mysql_command; - int rc; - DCB* backend_dcb; - bool rses_is_closed; - - inst->stats.n_queries++; - - /** Dirty read for quick check if router is closed. */ - if (router_cli_ses->rses_closed) - { - rses_is_closed = true; - } - else - { - /** - * Lock router client session for secure read of DCBs - */ - rses_is_closed = !(rses_begin_locked_router_action(router_cli_ses)); - } - - if (!rses_is_closed) - { - backend_dcb = router_cli_ses->backend_dcb; - /** unlock */ - rses_end_locked_router_action(router_cli_ses); - } - - if (rses_is_closed || backend_dcb == NULL) - { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Failed to route MySQL command %d to backend " - "server.", - mysql_command))); - rc = 0; - goto return_rc; - } - - - rc = backend_dcb->func.write(backend_dcb, queue); - -return_rc: - return rc; -} - -/** - * Display router diagnostics - * - * @param instance Instance of the router - * @param dcb DCB to send diagnostics to - */ -static void -diagnostics(ROUTER *router, DCB *dcb) -{ -ROUTER_INSTANCE *router_inst = (ROUTER_INSTANCE *)router; -ROUTER_CLIENT_SES *session; -int i = 0; -BACKEND *backend; -char *weightby; - - spinlock_acquire(&router_inst->lock); - session = router_inst->connections; - while (session) - { - i++; - session = session->next; - } - spinlock_release(&router_inst->lock); - - dcb_printf(dcb, "\tNumber of router sessions: %d\n", - router_inst->stats.n_sessions); - dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i); - dcb_printf(dcb, "\tNumber of queries forwarded: %d\n", - router_inst->stats.n_queries); - if ((weightby = serviceGetWeightingParameter(router_inst->service)) - != NULL) - { - dcb_printf(dcb, "\tConnection distribution based on %s " - "server parameter.\n", - weightby); - dcb_printf(dcb, - "\t\tServer Target %% Connections\n"); - for (i = 0; router_inst->servers[i]; i++) - { - backend = router_inst->servers[i]; - dcb_printf(dcb, "\t\t%-20s %3.1f%% %d\n", - backend->server->unique_name, - (float)backend->weight / 10, - backend->current_connection_count); - } - - } -} - -/** - * Client Reply routine - * - * The routine will reply to client data from backend server - * - * @param instance The router instance - * @param router_session The router session - * @param backend_dcb The backend DCB - * @param queue The GWBUF with reply data - */ -static void -clientReply( - ROUTER *instance, - void *router_session, - GWBUF *queue, - DCB *backend_dcb) -{ - DCB *client ; - - client = backend_dcb->session->client; - - ss_dassert(client != NULL); - - SESSION_ROUTE_REPLY(backend_dcb->session, queue); -} - -/** - * Error Handler routine - * - * The routine will handle errors that occurred in backend writes. - * - * @param instance The router instance - * @param router_session The router session - * @param message The error message to reply - * @param backend_dcb The backend DCB - * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION - * - */ -static void handleError( - ROUTER *instance, - void *router_session, - GWBUF *errbuf, - DCB *backend_dcb, - error_action_t action, - bool *succp) - -{ - DCB *client_dcb; - SESSION *session = backend_dcb->session; - session_state_t sesstate; - - /** Reset error handle flag from a given DCB */ - if (action == ERRACT_RESET) - { - backend_dcb->dcb_errhandle_called = false; - return; - } - - /** 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; - } - spinlock_acquire(&session->ses_lock); - sesstate = session->state; - client_dcb = session->client; - - if (sesstate == SESSION_STATE_ROUTER_READY) - { - CHK_DCB(client_dcb); - spinlock_release(&session->ses_lock); - client_dcb->func.write(client_dcb, gwbuf_clone(errbuf)); - } - else - { - spinlock_release(&session->ses_lock); - } - - /** false because connection is not available anymore */ - *succp = false; -} - -/** to be inline'd */ -/** - * @node Acquires lock to router client session if it is not closed. - * - * Parameters: - * @param rses - in, use - * - * - * @return true if router session was not closed. If return value is true - * it means that router is locked, and must be unlocked later. False, if - * router was closed before lock was acquired. - * - * - * @details (write detailed description here) - * - */ -static bool rses_begin_locked_router_action( - ROUTER_CLIENT_SES* rses) -{ - bool succp = false; - - CHK_CLIENT_RSES(rses); - - if (rses->rses_closed) { - goto return_succp; - } - spinlock_acquire(&rses->rses_lock); - if (rses->rses_closed) { - spinlock_release(&rses->rses_lock); - goto return_succp; - } - succp = true; - -return_succp: - return succp; -} - -/** to be inline'd */ -/** - * @node Releases router client session lock. - * - * Parameters: - * @param rses - - * - * - * @return void - * - * - * @details (write detailed description here) - * - */ -static void rses_end_locked_router_action( - ROUTER_CLIENT_SES* rses) -{ - CHK_CLIENT_RSES(rses); - spinlock_release(&rses->rses_lock); -} - - -static uint8_t getCapabilities( - ROUTER* inst, - void* router_session) -{ - return 0; -} - -/******************************** - * This routine returns the root master server from MySQL replication tree - * Get the root Master rule: - * - * find server with the lowest replication depth level - * and the SERVER_MASTER bitval - * Servers are checked even if they are in 'maintenance' - * - * @param servers The list of servers - * @return The Master found - * - */ - -static BACKEND *get_root_master(BACKEND **servers) { - int i = 0; - BACKEND *master_host = NULL; - - for (i = 0; servers[i]; i++) { - if (servers[i] && (servers[i]->server->status & (SERVER_MASTER|SERVER_MAINT)) == SERVER_MASTER) { - if (master_host && servers[i]->server->depth < master_host->server->depth) { - master_host = servers[i]; - } else { - if (master_host == NULL) { - master_host = servers[i]; - } - } - } - } - return master_host; -} - -static int handle_state_switch(DCB* dcb,DCB_REASON reason, void * routersession) -{ - ss_dassert(dcb != NULL); - SESSION* session = dcb->session; - ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES*)routersession; - SERVICE* service = session->service; - ROUTER* router = (ROUTER *)service->router; - - switch(reason) - { - case DCB_REASON_CLOSE: - dcb->func.close(dcb); - break; - case DCB_REASON_DRAINED: - /** Do we need to do anything? */ - break; - case DCB_REASON_HIGH_WATER: - /** Do we need to do anything? */ - break; - case DCB_REASON_LOW_WATER: - /** Do we need to do anything? */ - break; - case DCB_REASON_ERROR: - dcb->func.error(dcb); - break; - case DCB_REASON_HUP: - dcb->func.hangup(dcb); - break; - case DCB_REASON_NOT_RESPONDING: - dcb->func.hangup(dcb); - break; - default: - break; - } - - return 0; -} From a73df62ebda6205c830102da131016fd13d44acd Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 18 May 2015 18:34:43 +0300 Subject: [PATCH 067/273] Renamed mon_exec files to externcmd. --- server/core/CMakeLists.txt | 4 ++-- server/core/{mon_exec.c => externcmd.c} | 2 +- server/include/{mon_exec.h => externcmd.h} | 4 ++-- server/modules/monitor/galeramon.h | 2 +- server/modules/monitor/mmmon.h | 2 +- server/modules/monitor/mysqlmon.h | 2 +- server/modules/monitor/ndbclustermon.h | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename server/core/{mon_exec.c => externcmd.c} (99%) rename server/include/{mon_exec.h => externcmd.h} (91%) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index e60734237..88f6a9c22 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,5 +1,5 @@ if(BUILD_TESTS OR BUILD_TOOLS) - add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c mon_exec.c) + add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c externcmd.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) @@ -12,7 +12,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c monitor.c adminusers.c secrets.c filter.c modutil.c hint.c - housekeeper.c memlog.c resultset.c mon_exec.c) + housekeeper.c memlog.c resultset.c externcmd.c) if(WITH_JEMALLOC) target_link_libraries(maxscale ${JEMALLOC_LIBRARIES}) diff --git a/server/core/mon_exec.c b/server/core/externcmd.c similarity index 99% rename from server/core/mon_exec.c rename to server/core/externcmd.c index 01640bcd6..09c5727c4 100644 --- a/server/core/mon_exec.c +++ b/server/core/externcmd.c @@ -1,4 +1,4 @@ -#include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; diff --git a/server/include/mon_exec.h b/server/include/externcmd.h similarity index 91% rename from server/include/mon_exec.h rename to server/include/externcmd.h index 25390bb82..1ab44f908 100644 --- a/server/include/mon_exec.h +++ b/server/include/externcmd.h @@ -1,5 +1,5 @@ -#ifndef MON_EXEC_HG -#define MON_EXEC_HG +#ifndef _EXTERN_CMD_HG +#define _EXTERN_CMD_HG #include #include diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h index 02d8f08c7..7838f05a1 100644 --- a/server/modules/monitor/galeramon.h +++ b/server/modules/monitor/galeramon.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/server/modules/monitor/mmmon.h b/server/modules/monitor/mmmon.h index e2532d407..8a9edfb56 100644 --- a/server/modules/monitor/mmmon.h +++ b/server/modules/monitor/mmmon.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include /** * The handle for an instance of a Multi-Master Monitor module diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 574ae7c88..0510a13cf 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include /** * @file mysqlmon.h - The MySQL monitor functionality within the gateway * diff --git a/server/modules/monitor/ndbclustermon.h b/server/modules/monitor/ndbclustermon.h index 081f5e3da..56b961c45 100644 --- a/server/modules/monitor/ndbclustermon.h +++ b/server/modules/monitor/ndbclustermon.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include /** * @file ndbclustermon.h - The NDB Cluster monitor From d8f055858f735bd7636d5a73c0b5aec0ca4a0fb7 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 19 May 2015 14:22:54 +0300 Subject: [PATCH 068/273] Removed plainroute from the CMakeLists.txt. --- server/modules/routing/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/modules/routing/CMakeLists.txt b/server/modules/routing/CMakeLists.txt index 35d56fe1c..02535b7b9 100644 --- a/server/modules/routing/CMakeLists.txt +++ b/server/modules/routing/CMakeLists.txt @@ -9,10 +9,6 @@ add_library(readconnroute SHARED readconnroute.c) target_link_libraries(readconnroute log_manager utils) install(TARGETS readconnroute DESTINATION modules) -add_library(plainroute SHARED plainroute.c) -target_link_libraries(plainroute log_manager utils) -install(TARGETS plainroute DESTINATION modules) - add_library(debugcli SHARED debugcli.c debugcmd.c) target_link_libraries(debugcli log_manager utils) install(TARGETS debugcli DESTINATION modules) From 1403b3dfeb0fb90f1255f0a15c1b66a0a4a7438e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 19 May 2015 14:40:24 +0300 Subject: [PATCH 069/273] Fixed old header names form monitor_common.h --- server/modules/monitor/monitor_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h index e4cd864d1..bd4acc956 100644 --- a/server/modules/monitor/monitor_common.h +++ b/server/modules/monitor/monitor_common.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include /** * @file monitor_common.h - The generic monitor structures all monitors use * From c4f51c0b8c576bdf750b93635227485aa99e3fee Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 19 May 2015 15:42:11 +0300 Subject: [PATCH 070/273] Fixed a possible bug with multiple monitors monitoring the same servers. --- server/modules/monitor/galeramon.c | 56 ++++++++++++++++++++++---- server/modules/monitor/mmmon.c | 48 ++++++++++++++++++---- server/modules/monitor/mysql_mon.c | 49 ++++++++++++++++++---- server/modules/monitor/ndbclustermon.c | 54 +++++++++++++++++++++---- 4 files changed, 177 insertions(+), 30 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index eb75c2160..a46df8f77 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -64,6 +64,7 @@ static void diagnostics(DCB *, void *); static MONITOR_SERVERS *get_candidate_master(MONITOR_SERVERS *); static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *, MONITOR_SERVERS *, int); static void disableMasterFailback(void *, int); +bool isGaleraEvent(monitor_event_t event); static MONITOR_OBJECT MyObject = { startMonitor, @@ -570,15 +571,19 @@ monitor_event_t evtype; { /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr)) { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) + evtype = mon_get_event_type(ptr); + if(isGaleraEvent(evtype)) { - monitor_launch_script(mon,ptr,handle->script); + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script && handle->events[evtype]) + { + monitor_launch_script(mon,ptr,handle->script); + } } } ptr = ptr->next; @@ -687,3 +692,40 @@ availableWhenDonor(void *arg, int disable) GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; memcpy(&handle->availableWhenDonor, &disable, sizeof(int)); } + +static monitor_event_t galera_events[] = { + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + SYNCED_DOWN_EVENT, + SYNCED_UP_EVENT, + DONOR_DOWN_EVENT, + DONOR_UP_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + LOST_SYNCED_EVENT, + LOST_DONOR_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + NEW_SYNCED_EVENT, + NEW_DONOR_EVENT, + MAX_MONITOR_EVENT +}; +/** + * Check if the Galera monitor is monitoring this event type. + * @param event Event to check + * @return True if the event is monitored, false if it is not + * */ +bool isGaleraEvent(monitor_event_t event) +{ + int i; + for(i = 0;galera_events[i] != MAX_MONITOR_EVENT;i++) + { + if(event == galera_events[i]) + return true; + } + return false; +} \ No newline at end of file diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 8188c40c9..0e54d5299 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -594,16 +594,19 @@ size_t nrounds = 0; monitor_event_t evtype; while(ptr) { - /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr)) { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) + evtype = mon_get_event_type(ptr); + if(isMySQLEvent(evtype)) { - monitor_launch_script(mon,ptr,handle->script); + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script && handle->events[evtype]) + { + monitor_launch_script(mon,ptr,handle->script); + } } } ptr = ptr->next; @@ -678,3 +681,32 @@ MONITOR_SERVERS *ptr; } } + +static monitor_event_t mysql_events[] = { + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + MAX_MONITOR_EVENT +}; +/** + * Check if the MM monitor is monitoring this event type. + * @param event Event to check + * @return True if the event is monitored, false if it is not + * */ +bool isMySQLEvent(monitor_event_t event) +{ + int i; + for(i = 0;mysql_events[i] != MAX_MONITOR_EVENT;i++) + { + if(event == mysql_events[i]) + return true; + } + return false; +} \ No newline at end of file diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 3a6d22391..75f7027c4 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -80,7 +80,7 @@ static MONITOR_SERVERS *get_replication_tree(MONITOR *, int); static void set_master_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *); static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *); static int add_slave_to_master(long *, int, long); - +bool isMySQLEvent(monitor_event_t event); static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, @@ -693,15 +693,19 @@ int log_no_master = 1; while(ptr) { /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr)) { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) + evtype = mon_get_event_type(ptr); + if(isMySQLEvent(evtype)) { - monitor_launch_script(mon,ptr,handle->script); + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script && handle->events[evtype]) + { + monitor_launch_script(mon,ptr,handle->script); + } } } ptr = ptr->next; @@ -1193,3 +1197,32 @@ static int add_slave_to_master(long *slaves_list, int list_size, long node_id) { } return 0; } + +static monitor_event_t mysql_events[] = { + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + MAX_MONITOR_EVENT +}; +/** + * Check if the MySQL monitor is monitoring this event type. + * @param event Event to check + * @return True if the event is monitored, false if it is not + * */ +bool isMySQLEvent(monitor_event_t event) +{ + int i; + for(i = 0;mysql_events[i] != MAX_MONITOR_EVENT;i++) + { + if(event == mysql_events[i]) + return true; + } + return false; +} \ No newline at end of file diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index ad41ab1e8..88444aaaf 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -52,6 +52,7 @@ MODULE_INFO info = { static void *startMonitor(void *,void*); static void stopMonitor(void *); static void diagnostics(DCB *, void *); +bool isNdbEvent(monitor_event_t event); static MONITOR_OBJECT MyObject = { startMonitor, @@ -402,18 +403,57 @@ size_t nrounds = 0; while(ptr) { /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr) && (evtype = mon_get_event_type(ptr)) != UNDEFINED_MONITOR_EVENT) + if(mon_status_changed(ptr)) { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) + evtype = mon_get_event_type(ptr); + if(isNdbEvent(evtype)) { - monitor_launch_script(mon,ptr,handle->script); + skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name,ptr->server->port, + mon_get_event_name(ptr)); + if(handle->script && handle->events[evtype]) + { + monitor_launch_script(mon,ptr,handle->script); + } } } ptr = ptr->next; } } } + + +static monitor_event_t ndb_events[] = { + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + NDB_UP_EVENT, + NDB_DOWN_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + LOST_NDB_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + NEW_NDB_EVENT, + MAX_MONITOR_EVENT +}; + +/** + * Check if the event type is one the ndbcustermonitor is interested in. + * @param event Event to check + * @return True if the event is monitored, false if it is not + */ +bool isNdbEvent(monitor_event_t event) +{ + int i; + for(i = 0;ndb_events[i] != MAX_MONITOR_EVENT;i++) + { + if(event == ndb_events[i]) + return true; + } + return false; +} \ No newline at end of file From 7c7b322e1b54bcd1d5a27a2c904d93d1b84bc965 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 19 May 2015 16:20:04 +0300 Subject: [PATCH 071/273] Added missing default monitor interval. --- server/core/monitor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/core/monitor.c b/server/core/monitor.c index 9d945572a..1013cf01b 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -85,6 +85,7 @@ MONITOR *mon; mon->read_timeout = DEFAULT_READ_TIMEOUT; mon->write_timeout = DEFAULT_WRITE_TIMEOUT; mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT; + mon->interval = MONITOR_INTERVAL; spinlock_init(&mon->lock); spinlock_acquire(&monLock); mon->next = allMonitors; From a1270ef4475cabb51f31ae1f0091dd1dda2744d3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 19 May 2015 16:30:01 +0300 Subject: [PATCH 072/273] Added new monitor parameters to config.c --- server/core/config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/core/config.c b/server/core/config.c index 44de91b47..60c99bf87 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1932,6 +1932,8 @@ static char *monitor_params[] = "servers", "user", "passwd", + "script", + "events", "monitor_interval", "detect_replication_lag", "detect_stale_master", From e794c0953c0a1f216cebefd0b74417355022aac4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 19 May 2015 18:17:42 +0300 Subject: [PATCH 073/273] Added systemd script installation --- CMakeLists.txt | 2 ++ etc/postinst.in | 1 + 2 files changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e975c4f7..44516cdfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,8 @@ if(WITH_SCRIPTS) PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + install(FILES ${CMAKE_BINARY_DIR}/maxscale.service DESTINATION ${MAXSCALE_SHAREDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) else() install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION /etc/init.d PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/etc/postinst.in b/etc/postinst.in index e04a34f5f..dca7215df 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -28,4 +28,5 @@ chown maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale # Copy init.d script and ldconfig file cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale /etc/init.d/ cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf /etc/ld.so.conf.d/ +cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service /usr/lib/systemd/system /sbin/ldconfig From 8f20d23d4c99599e0f6c69626473982de3415125 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 19 May 2015 19:20:59 +0300 Subject: [PATCH 074/273] Added missing function prototype to mmmon.c --- server/modules/monitor/mmmon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 0e54d5299..bd1c6bb85 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -53,6 +53,7 @@ static void stopMonitor(void *); static void diagnostics(DCB *, void *); static void detectStaleMaster(void *, int); static MONITOR_SERVERS *get_current_master(MONITOR *); +bool isMySQLEvent(monitor_event_t event); static MONITOR_OBJECT MyObject = { startMonitor, @@ -146,7 +147,7 @@ startMonitor(void *arg,void* opt) } else if(!strcmp(params->name,"events")) { - mon_parse_event_string(&handle->events,sizeof(handle->events),params->value); + mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value); have_events = true; } params = params->next; From b815a796289d11ba3bfa7df9172d3477ea6e347c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 20 May 2015 13:06:08 +0300 Subject: [PATCH 075/273] Fixed failing execvp calls leaving a process running. --- server/core/externcmd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/core/externcmd.c b/server/core/externcmd.c index 09c5727c4..cc7989fc6 100644 --- a/server/core/externcmd.c +++ b/server/core/externcmd.c @@ -138,6 +138,7 @@ int externcmd_execute(EXTERNCMD* cmd) { /** Child process, execute command */ execvp(cmd->parameters[0],cmd->parameters); + exit(1); } else { From 4eddec7989d4254cd20b696e92e5d2671068c90b Mon Sep 17 00:00:00 2001 From: Sriram Patil Date: Wed, 20 May 2015 17:47:58 +0530 Subject: [PATCH 076/273] Fixed MXS - 165: Concurrency issue while incrementing sessions in qlafilter --- server/modules/filter/qlafilter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/modules/filter/qlafilter.c b/server/modules/filter/qlafilter.c index 5362cf61e..3c358cda1 100644 --- a/server/modules/filter/qlafilter.c +++ b/server/modules/filter/qlafilter.c @@ -50,6 +50,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -304,7 +305,9 @@ char *remote, *userName; sprintf(my_session->filename, "%s.%d", my_instance->filebase, my_instance->sessions); - my_instance->sessions++; + + // Multiple sessions can try to update my_instance->sessions simultaneously + atomic_add(&(my_instance->sessions), 1); if (my_session->active) { From 7dbc0211496dd6e217507293a2579811c7b385c5 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 20 May 2015 16:16:49 +0300 Subject: [PATCH 077/273] Added more logging when a monitor script cannot be executed. --- server/core/externcmd.c | 19 ++++++++++++++++++- server/modules/monitor/galeramon.c | 12 +++++++++++- server/modules/monitor/mmmon.c | 13 +++++++++++-- server/modules/monitor/monitor_common.c | 6 +++++- server/modules/monitor/mysql_mon.c | 12 +++++++++++- server/modules/monitor/ndbclustermon.c | 12 +++++++++++- 6 files changed, 67 insertions(+), 7 deletions(-) diff --git a/server/core/externcmd.c b/server/core/externcmd.c index cc7989fc6..93579faef 100644 --- a/server/core/externcmd.c +++ b/server/core/externcmd.c @@ -97,6 +97,23 @@ EXTERNCMD* externcmd_allocate(char* argstr) free(cmd); return NULL; } + if(access(cmd->parameters[0],F_OK) != 0) + { + skygw_log_write(LE, + "Error: Cannot find file: %s", + cmd->parameters[0]); + externcmd_free(cmd); + return NULL; + } + + if(access(cmd->parameters[0],X_OK) != 0) + { + skygw_log_write(LE, + "Error: Cannot execute file: %s", + cmd->parameters[0]); + externcmd_free(cmd); + return NULL; + } } return cmd; } @@ -138,7 +155,7 @@ int externcmd_execute(EXTERNCMD* cmd) { /** Child process, execute command */ execvp(cmd->parameters[0],cmd->parameters); - exit(1); + _exit(1); } else { diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index a46df8f77..404448d82 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -156,7 +156,17 @@ startMonitor(void *arg,void* opt) { if(handle->script) free(handle->script); - handle->script = strdup(params->value); + if(access(params->value,X_OK) == 0) + { + handle->script = strdup(params->value); + } + else + { + skygw_log_write(LE, + "Error: The file cannot be executed: %s", + params->value); + handle->script = NULL; + } } else if(!strcmp(params->name,"events")) { diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index bd1c6bb85..4ca8d6048 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -142,8 +142,17 @@ startMonitor(void *arg,void* opt) { free(handle->script); } - - handle->script = strdup(params->value); + if(access(params->value,X_OK) == 0) + { + handle->script = strdup(params->value); + } + else + { + skygw_log_write(LE, + "Error: The file cannot be executed: %s", + params->value); + handle->script = NULL; + } } else if(!strcmp(params->name,"events")) { diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index 6f9be583b..fb59eadbd 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -303,7 +303,11 @@ void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script) ptr->server->unique_name); mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX + 1); - cmd = externcmd_allocate(argstr); + if((cmd = externcmd_allocate(argstr)) == NULL) + { + skygw_log_write(LE,"Failed to execute script: %s",script); + return; + } if(externcmd_execute(cmd)) { diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 75f7027c4..1e7fde4e6 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -170,7 +170,17 @@ startMonitor(void *arg, void* opt) { if(handle->script) free(handle->script); - handle->script = strdup(params->value); + if(access(params->value,X_OK) == 0) + { + handle->script = strdup(params->value); + } + else + { + skygw_log_write(LE, + "Error: The file cannot be executed: %s", + params->value); + handle->script = NULL; + } } else if(!strcmp(params->name,"events")) { diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index 88444aaaf..4488786af 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -132,7 +132,17 @@ startMonitor(void *arg,void* opt) { if(handle->script) free(handle->script); - handle->script = strdup(params->value); + if(access(params->value,X_OK) == 0) + { + handle->script = strdup(params->value); + } + else + { + skygw_log_write(LE, + "Error: The file cannot be executed: %s", + params->value); + handle->script = NULL; + } } else if(!strcmp(params->name,"events")) { From 91bc7b2792e1df38f3efcb4fd36547c055eb1497 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 21 May 2015 11:41:11 +0300 Subject: [PATCH 078/273] Added missing spinlock calls to all monitors. --- server/core/monitor.c | 2 ++ server/modules/monitor/galeramon.c | 8 ++++++-- server/modules/monitor/mmmon.c | 9 +++++++-- server/modules/monitor/mysql_mon.c | 12 +++++++++--- server/modules/monitor/ndbclustermon.c | 6 +++++- 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index 1013cf01b..30fdc756c 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -133,8 +133,10 @@ MONITOR *ptr; void monitorStart(MONITOR *monitor, void* params) { + spinlock_acquire(&monitor->lock); monitor->handle = (*monitor->module->startMonitor)(monitor,params); monitor->state = MONITOR_STATE_RUNNING; + spinlock_release(&monitor->lock); } /** diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 404448d82..f1246afa7 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -405,15 +405,19 @@ static void monitorMain(void *arg) { MONITOR* mon = (MONITOR*)arg; -GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle; +GALERA_MONITOR *handle; MONITOR_SERVERS *ptr; size_t nrounds = 0; MONITOR_SERVERS *candidate_master = NULL; -int master_stickiness = handle->disableMasterFailback; +int master_stickiness; int is_cluster=0; int log_no_members = 1; monitor_event_t evtype; + spinlock_acquire(&mon->lock); + handle = (GALERA_MONITOR *)mon->handle; + spinlock_release(&mon->lock); + master_stickiness = handle->disableMasterFailback; if (mysql_thread_init()) { LOGIF(LE, (skygw_log_write_flush( diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 4ca8d6048..f56c77022 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -494,12 +494,17 @@ static void monitorMain(void *arg) { MONITOR* mon = (MONITOR*)arg; -MM_MONITOR *handle = (MM_MONITOR *)mon->handle; +MM_MONITOR *handle; MONITOR_SERVERS *ptr; -int detect_stale_master = handle->detectStaleMaster; +int detect_stale_master; MONITOR_SERVERS *root_master; size_t nrounds = 0; +spinlock_acquire(&mon->lock); +handle = (MM_MONITOR *)mon->handle; +spinlock_release(&mon->lock); +detect_stale_master = handle->detectStaleMaster; + if (mysql_thread_init()) { LOGIF(LE, (skygw_log_write_flush( diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 1e7fde4e6..3295d23e9 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -507,15 +507,21 @@ static void monitorMain(void *arg) { MONITOR* mon = (MONITOR*) arg; -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; +MYSQL_MONITOR *handle; MONITOR_SERVERS *ptr; -int replication_heartbeat = handle->replicationHeartbeat; -int detect_stale_master = handle->detectStaleMaster; +int replication_heartbeat; +int detect_stale_master; int num_servers=0; MONITOR_SERVERS *root_master = NULL; size_t nrounds = 0; int log_no_master = 1; +spinlock_acquire(&mon->lock); +handle = (MYSQL_MONITOR *)mon->handle; +spinlock_release(&mon->lock); +replication_heartbeat = handle->replicationHeartbeat; +detect_stale_master = handle->detectStaleMaster; + if (mysql_thread_init()) { LOGIF(LE, (skygw_log_write_flush( diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index 4488786af..a22daecbc 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -346,10 +346,14 @@ static void monitorMain(void *arg) { MONITOR* mon = arg; -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; +MYSQL_MONITOR *handle; MONITOR_SERVERS *ptr; size_t nrounds = 0; +spinlock_acquire(&mon->lock); +handle = (MYSQL_MONITOR *)mon->handle; +spinlock_release(&mon->lock); + if (mysql_thread_init()) { LOGIF(LE, (skygw_log_write_flush( From 6d9765f736ef51baf8b8e2b67f68a4fcf6301300 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 21 May 2015 15:22:18 +0300 Subject: [PATCH 079/273] Added copying of old configuration files to new directories. --- etc/postrm.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/etc/postrm.in b/etc/postrm.in index b1e86fca5..65c0f7116 100755 --- a/etc/postrm.in +++ b/etc/postrm.in @@ -3,4 +3,9 @@ if [ "$1" -eq 0 ] then rm -f /etc/init.d/maxscale rm -f /etc/ld.so.conf.d/maxscale.conf +else + if [ -f "/usr/local/mariadb-maxscale/etc/MaxScale.cnf" ] + then + cp "/usr/local/mariadb-maxscale/etc/MaxScale.cnf" "/etc/maxscale.cnf" + fi fi From 229cdb73e75e2740269056c702f8f683e5a19e37 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 21 May 2015 15:30:52 +0300 Subject: [PATCH 080/273] Fixed concurrency issues in filters. --- server/modules/filter/qlafilter.c | 3 ++- server/modules/filter/regexfilter.c | 7 ++++++- server/modules/filter/testfilter.c | 3 ++- server/modules/filter/topfilter.c | 3 ++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/server/modules/filter/qlafilter.c b/server/modules/filter/qlafilter.c index 5362cf61e..a2d16d914 100644 --- a/server/modules/filter/qlafilter.c +++ b/server/modules/filter/qlafilter.c @@ -50,6 +50,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -304,7 +305,7 @@ char *remote, *userName; sprintf(my_session->filename, "%s.%d", my_instance->filebase, my_instance->sessions); - my_instance->sessions++; + atomic_add(&my_instance->sessions,1); if (my_session->active) { diff --git a/server/modules/filter/regexfilter.c b/server/modules/filter/regexfilter.c index 9c301870b..c7fb91879 100644 --- a/server/modules/filter/regexfilter.c +++ b/server/modules/filter/regexfilter.c @@ -23,7 +23,7 @@ #include #include #include - +#include #include "maxconfig.h" /** Defined in log_manager.cc */ @@ -97,6 +97,7 @@ typedef struct { */ typedef struct { DOWNSTREAM down; /* The downstream filter */ + SPINLOCK lock; int no_change; /* No. of unchanged requests */ int replacements; /* No. of changed requests */ int active; /* Is filter active */ @@ -356,13 +357,17 @@ char *sql, *newsql; { queue = modutil_replace_SQL(queue, newsql); queue = gwbuf_make_contiguous(queue); + spinlock_acquire(&my_session->lock); log_match(my_instance,my_instance->match,sql,newsql); + spinlock_release(&my_session->lock); free(newsql); my_session->replacements++; } else { + spinlock_acquire(&my_session->lock); log_nomatch(my_instance,my_instance->match,sql); + spinlock_release(&my_session->lock); my_session->no_change++; } free(sql); diff --git a/server/modules/filter/testfilter.c b/server/modules/filter/testfilter.c index 8f0c0f573..2f7fd7532 100644 --- a/server/modules/filter/testfilter.c +++ b/server/modules/filter/testfilter.c @@ -19,6 +19,7 @@ #include #include #include +#include /** * @file testfilter.c - a very simple test filter. @@ -145,7 +146,7 @@ TEST_SESSION *my_session; if ((my_session = calloc(1, sizeof(TEST_SESSION))) != NULL) { - my_instance->sessions++; + atomic_add(&my_instance->sessions,1); my_session->count = 0; } diff --git a/server/modules/filter/topfilter.c b/server/modules/filter/topfilter.c index 737dd790a..c60408708 100644 --- a/server/modules/filter/topfilter.c +++ b/server/modules/filter/topfilter.c @@ -47,6 +47,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -296,7 +297,7 @@ char *remote, *user; } sprintf(my_session->filename, "%s.%d", my_instance->filebase, my_instance->sessions); - my_instance->sessions++; + atomic_add(&my_instance->sessions,1); my_session->top = (TOPNQ **)calloc(my_instance->topN + 1, sizeof(TOPNQ *)); for (i = 0; i < my_instance->topN; i++) From 3301ffc76dd6457ced6cb3e089856de18770f4c0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 21 May 2015 17:40:33 +0300 Subject: [PATCH 081/273] Added different log message when the script is missing for a monitor. --- server/modules/monitor/galeramon.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index f1246afa7..6a842b8c1 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -156,21 +156,31 @@ startMonitor(void *arg,void* opt) { if(handle->script) free(handle->script); + if(access(params->value,X_OK) == 0) { handle->script = strdup(params->value); } else { + if(access(params->value,F_OK) == 0) + { skygw_log_write(LE, "Error: The file cannot be executed: %s", params->value); + } + else + { + skygw_log_write(LE, + "Error: The file cannot be found: %s", + params->value); + } handle->script = NULL; } } else if(!strcmp(params->name,"events")) { - mon_parse_event_string(&handle->events,sizeof(handle->events),params->value); + mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value); have_events = true; } params = params->next; From 9f024b5389802777bbf0dbd236efeb21baf6489d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 21 May 2015 18:08:45 +0300 Subject: [PATCH 082/273] Added missing parenthesis. --- server/modules/protocol/mysql_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 0e85f51ed..ffd72c034 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -287,7 +287,7 @@ int gw_read_backend_handshake( pthread_self(), conn->owner_dcb->fd, pthread_self()))); - while(head = gwbuf_consume(head, GWBUF_LENGTH(head))); + while((head = gwbuf_consume(head, GWBUF_LENGTH(head)))); return 1; } From 9c461450e2001b407c0810844c9a75dcebda0165 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 22 May 2015 05:36:58 +0300 Subject: [PATCH 083/273] Fixes to Coverity defects. --- server/core/monitor.c | 1 - server/modules/monitor/monitor_common.c | 4 +++- server/modules/monitor/ndbclustermon.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index 30fdc756c..ae9a64794 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -73,7 +73,6 @@ MONITOR *mon; LOGFILE_ERROR, "Error : Unable to load monitor module '%s'.", name))); - free(mon->name); free(mon); return NULL; } diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index fb59eadbd..e4b8e7dac 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -229,8 +229,9 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) { MONITOR_SERVERS* ptr = start; bool first = true; + int slen = strlen(str); - while(ptr) + while(ptr && slen < len) { if(!first) { @@ -239,6 +240,7 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) first = false; strncat(str,ptr->server->unique_name,len); ptr = ptr->next; + slen = strlen(str); } } diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index cceaacd58..c8790be59 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -123,6 +123,8 @@ startMonitor(void *arg,void* opt) return NULL; handle->shutdown = 0; handle->id = MONITOR_DEFAULT_ID; + handle->script = NULL; + handle->master = NULL; memset(handle->events,false,sizeof(handle->events)); spinlock_init(&handle->lock); } From 0d85ae66039e52f1407500de18af6d8da461c86d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 22 May 2015 11:31:22 +0300 Subject: [PATCH 084/273] Changed data directory to /var/lib/maxscale. --- server/core/adminusers.c | 8 ++++---- server/core/gateway.c | 1 + server/core/gwdirs.c | 10 ++++++++++ server/core/secrets.c | 2 +- server/include/gwdirs.h.in | 4 +++- server/modules/routing/binlog/blr_file.c | 6 +++--- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/server/core/adminusers.c b/server/core/adminusers.c index e20f9d1c3..53734e9cc 100644 --- a/server/core/adminusers.c +++ b/server/core/adminusers.c @@ -120,7 +120,7 @@ char fname[1024], *home; char uname[80], passwd[80]; initialise(); - sprintf(fname, "%s/passwd", get_cachedir()); + sprintf(fname, "%s/passwd", get_datadir()); if ((fp = fopen(fname, "r")) == NULL) return NULL; if ((rval = users_alloc()) == NULL) @@ -151,7 +151,7 @@ FILE *fp; char fname[1024], *home, *cpasswd; initialise(); - sprintf(fname, "%s/passwd", get_cachedir()); + sprintf(fname, "%s/passwd", get_datadir()); if (users == NULL) { @@ -244,8 +244,8 @@ char* admin_remove_user( /** * Open passwd file and remove user from the file. */ - sprintf(fname, "%s/passwd", get_cachedir()); - sprintf(fname_tmp, "%s/passwd_tmp", get_cachedir()); + sprintf(fname, "%s/passwd", get_datadir()); + sprintf(fname_tmp, "%s/passwd_tmp", get_datadir()); /** * Rewrite passwd file from memory. */ diff --git a/server/core/gateway.c b/server/core/gateway.c index 462d28e8e..3ab50609b 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1047,6 +1047,7 @@ int main(int argc, char **argv) break; case 'D': sprintf(datadir,"%s",optarg); + maxscaledatadir = strdup(optarg); datadir_defined = true; break; case 'C': diff --git a/server/core/gwdirs.c b/server/core/gwdirs.c index 9bf0185cd..1fe8af008 100644 --- a/server/core/gwdirs.c +++ b/server/core/gwdirs.c @@ -17,3 +17,13 @@ char* get_cachedir() { return cachedir?cachedir:(char*)default_cachedir; } + + +/** + * Get the service cache directory + * @return The path to the cache directory + */ +char* get_datadir() +{ + return maxscaledatadir?maxscaledatadir:(char*)default_datadir; +} diff --git a/server/core/secrets.c b/server/core/secrets.c index b0ddc084b..2ec3e75a9 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -71,7 +71,7 @@ int fd; int len; static int reported = 0; - snprintf(secret_file, PATH_MAX, "%s/.secrets", get_cachedir()); + snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir()); /* Try to access secrets file */ if (access(secret_file, R_OK) == -1) diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index ac8fcf4af..66e34426f 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -26,7 +26,7 @@ static const char* default_cnf_fname = "maxscale.cnf"; static const char* default_configdir = "/etc/"; static const char* default_piddir = "/var/run/maxscale/"; static const char* default_logdir = "/var/log/maxscale/"; -static const char* default_datadir = "/var/cache/maxscale/"; +static const char* default_datadir = "/var/lib/maxscale/"; static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@"; static const char* default_cachedir = "/var/cache/maxscale/"; static const char* default_langdir = "@MAXSCALE_VARDIR@/lib/maxscale/"; @@ -35,8 +35,10 @@ static char* configdir = NULL; static char* logdir = NULL; static char* libdir = NULL; static char* cachedir = NULL; +static char* maxscaledatadir = NULL; static char* langdir = NULL; char* get_libdir(); +char* get_datadir(); char* get_cachedir(); #endif diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 32fbad28c..91944eea1 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -79,7 +79,7 @@ struct dirent *dp; if (router->binlogdir == NULL) { - strcpy(path, get_cachedir()); + strcpy(path, get_datadir()); strncat(path,"/",PATH_MAX); strncat(path, router->service->name,PATH_MAX); @@ -658,7 +658,7 @@ blr_cache_response(ROUTER_INSTANCE *router, char *response, GWBUF *buf) char path[PATH_MAX+1], *ptr; int fd; - strcpy(path,get_cachedir()); + strcpy(path,get_datadir()); strncat(path,"/",PATH_MAX); strncat(path, router->service->name, PATH_MAX); @@ -693,7 +693,7 @@ char path[PATH_MAX+1], *ptr; int fd; GWBUF *buf; - strcpy(path, get_cachedir()); + strcpy(path, get_datadir()); strncat(path, "/", PATH_MAX); strncat(path, router->service->name, PATH_MAX); strncat(path, "/.cache/", PATH_MAX); From 72066a4262325545f6ac190ef0abfcda186c5d2c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 22 May 2015 14:12:40 +0300 Subject: [PATCH 085/273] Added a conditional for systemd script installation and changed directory rights for the maxscale user. --- etc/postinst.in | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/etc/postinst.in b/etc/postinst.in index dca7215df..abf2db1ef 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -24,9 +24,16 @@ chown maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale chown maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale chown maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale chown maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale +chmod 0755 @MAXSCALE_VARDIR@/log/maxscale +chmod 0755 @MAXSCALE_VARDIR@/lib/maxscale +chmod 0755 @MAXSCALE_VARDIR@/cache/maxscale +chmod 0755 @MAXSCALE_VARDIR@/run/maxscale # Copy init.d script and ldconfig file cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale /etc/init.d/ cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf /etc/ld.so.conf.d/ -cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service /usr/lib/systemd/system +if [ -d "/usr/lib/systemd/system" ] +then + cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service /usr/lib/systemd/system +fi /sbin/ldconfig From 04aaaea717214f2cca247cd7a0b494a18efd4d71 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 22 May 2015 18:26:20 +0300 Subject: [PATCH 086/273] Added 1.2 release notes. --- .../MaxScale-1.2.0-Release-Notes.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md new file mode 100644 index 000000000..f85382b3c --- /dev/null +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -0,0 +1,26 @@ +# MaxScale Release Notes + +## 1.2 GA + +This document details the changes in version 1.2 since the release of the 1.1.1 GA Release of the MaxScale product. + +###***PLEASE NOTICE: MaxScale installation directories have changed in this version*** +The 1.2 version of MaxScale differs from previos versions in its installation layout. Please take great care when upgrading MaxScale from previous versions to version 1.2. An automatic upgrade will not work due to the severe changes in the installation layout. + +## New Features + +### Non-root MaxScale +You can now run MaxScale as any user. The standard installation of a MaxScale package now creates the maxscale user and the maxscale group. + +### FHS-compliant installation +The 1.2 version of MaxScale now complies to the Filesystem Hierarchy Standard. This means that MAXSCALE_HOME is no longer necessary and directories can be moved to different locations. + +A quick list of changes in installation directories and file names: + + * Binaries go into `/usr/bin` + * Configuration files to `/etc` and the configuration file is now lower case: `maxscale.cnf` + * Logs to `/var/log/maxscale` + * The module and library directory have been combined into a single directory in `/usr/lib64/maxscale`. If you have custom modules please make sure they are located there. + * Data directory is `/var/lib/maxscale`. This is the default location for MaxScale-specific data. + * PID file can be found at `/var/run/maxscale` + From 15da20184c080364f9c695d2ddfa39407bebfb0f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 26 May 2015 14:04:07 +0300 Subject: [PATCH 087/273] Fixed old references in documentation. --- Documentation/Getting-Started/Configuration-Guide.md | 4 +++- .../Getting-Started/Getting-Started-With-MaxScale.md | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 7bc7abee6..a031bb03d 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -29,12 +29,14 @@ The MaxScale configuration is read from a file which can be located in a number 1. Location given with the --configdir= command line argument -2. MaxScale will look for a configuration file called `MaxScale.cnf` in the directory `/etc/MaxScale.cnf` +2. MaxScale will look for a configuration file called `maxscale.cnf` in the directory `/etc/maxscale.cnf` An explicit path to a configuration file can be passed by using the `-f` option to MaxScale. The configuration file itself is based on the ".ini" file format and consists of various sections that are used to build the configuration, these sections define services, servers, listeners, monitors and global settings. +Please see the section about [Protocol Modules](## Protocol Modules) for more details about MaxScale and the default directories where modules will be searched for. + ### Global Settings The global settings, in a section named `[MaxScale]`, allow various parameters that affect MaxScale as a whole to be tuned. Currently the only setting that is supported is the number of threads to use to handle the network traffic. MaxScale will also accept the section name of `[gateway]` for global settings. This is for backward compatibility with versions prior to the naming of MaxScale. diff --git a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md index 6b84d8d58..ed41da728 100644 --- a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md +++ b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md @@ -59,12 +59,13 @@ As well as the four major configuration choices outlined above there are also ot MaxScale consists of a core executable and a number of modules that implement the different protocols and routing algorithms. These modules are built as shared objects that are loaded on demand. In order for MaxScale to find these -modules it will search using a predescribed search path. The rules are: +modules it will search using a configurable search path. The priority of these paths are: - 1. Look in the current directory for the module - 2. Look in /usr/lib64/maxscale + 1. Look in the directory defined with --libdir=PATH during startup + 2. Look in the directory defined with libdir=PATH in the configuration file under the [maxscale] section + 3. Look in default directory in /usr/lib64/maxscale -Configuration is read by default from the file /etc/MaxScale.cnf. An example file is included in in the installation and can be found in the /usr/share/maxscale folder within the MaxScale installation. The -f flag can be used on the command line to set the name and the location of the configuration file. Without path expression the file is read from the /etc directory. +Configuration is read by default from the file /etc/MaxScale.cnf. An example file is included in in the installation and can be found in the /usr/share/maxscale folder within the MaxScale installation. The -f flag can be used on the command line to set the name and the location of the configuration file. The -C flag can be used to set the directory where the configuration file is searched for. Without the -f or -C flags, the file is read from the /etc directory. ## Administration Of MaxScale From 47407405c58e24db01c4ebb80aa6a727d9dc00ad Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 26 May 2015 14:05:18 +0300 Subject: [PATCH 088/273] Fixed broken link. --- Documentation/Getting-Started/Configuration-Guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index a031bb03d..92b86c0ae 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -35,7 +35,7 @@ An explicit path to a configuration file can be passed by using the `-f` option The configuration file itself is based on the ".ini" file format and consists of various sections that are used to build the configuration, these sections define services, servers, listeners, monitors and global settings. -Please see the section about [Protocol Modules](## Protocol Modules) for more details about MaxScale and the default directories where modules will be searched for. +Please see the section about [Protocol Modules](# Protocol Modules) for more details about MaxScale and the default directories where modules will be searched for. ### Global Settings From 6be6c741e905b6d63a9b766e9532714db21d4635 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 26 May 2015 14:07:39 +0300 Subject: [PATCH 089/273] Fixed spaces braking links. --- Documentation/Getting-Started/Configuration-Guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 92b86c0ae..a5b449d16 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -35,7 +35,7 @@ An explicit path to a configuration file can be passed by using the `-f` option The configuration file itself is based on the ".ini" file format and consists of various sections that are used to build the configuration, these sections define services, servers, listeners, monitors and global settings. -Please see the section about [Protocol Modules](# Protocol Modules) for more details about MaxScale and the default directories where modules will be searched for. +Please see the section about [Protocol Modules](#protocol-modules) for more details about MaxScale and the default directories where modules will be searched for. ### Global Settings From 099fc0ba3c52242b483cf30167b146108d7a898a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 26 May 2015 14:12:33 +0300 Subject: [PATCH 090/273] Fix to MXS-162 Changed router to monitor in documentation. --- Documentation/Getting-Started/Configuration-Guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index a5b449d16..332266ab5 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -1076,7 +1076,7 @@ Please note, those two options are not enabled by default. ### galeramon -The Galeramon monitor is a simple router designed for use with MySQL Galera cluster. To execute the galeramon monitor an entry as shown below should be added to the MaxScale configuration file. +The Galeramon monitor is a simple monitor designed for use with MySQL Galera cluster. To execute the galeramon monitor an entry as shown below should be added to the MaxScale configuration file. ``` [Galera Monitor] From 5f49a62724f45ec7e303e3387a88493c2e877fba Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 26 May 2015 14:41:20 +0300 Subject: [PATCH 091/273] Fix to MXS-166 Added a call to thd->end_statement in parsing_info_done --- query_classifier/query_classifier.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index bb6b1c6da..d598ab5d2 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -1485,7 +1485,8 @@ void parsing_info_done( void* ptr) { parsing_info_t* pi; - + THD* thd; + if (ptr) { pi = (parsing_info_t *)ptr; @@ -1496,6 +1497,8 @@ void parsing_info_done( if (mysql->thd != NULL) { + thd = (THD*)mysql->thd; + thd->end_statement (); (*mysql->methods->free_embedded_thd)(mysql); mysql->thd = NULL; } From 4cbcc4ecb6b6d68b3c90054d777c201e5d2ec01c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 26 May 2015 15:12:29 +0300 Subject: [PATCH 092/273] Removed old unused cmake variables, updated documentation and fixed testall suite. --- CMakeLists.txt | 8 +++---- .../Getting-Started/Configuration-Guide.md | 8 +++++++ .../Tutorials/Administration-Tutorial.md | 2 ++ cmake/macros.cmake | 23 ------------------- server/core/gateway.c | 18 +++++++++++++-- server/include/gwdirs.h.in | 2 +- server/modules/filter/test/tee_recursion.sh | 4 ++-- server/test/maxscale_test.cnf | 7 +++--- 8 files changed, 37 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa8bde511..376df9b0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,19 +233,19 @@ if(PACKAGE) endif() add_custom_target(buildtests - COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} COMMAND make COMMENT "Building test suite..." VERBATIM ) add_custom_target(testall - COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR} COMMAND make install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/testall.cmake COMMENT "Running full test suite..." VERBATIM) add_custom_target(testcore - COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR} COMMAND make install COMMAND ctest -R Internal COMMENT "Running core test suite..." VERBATIM) @@ -276,7 +276,7 @@ endif() # Testall target with Valgrind if(VALGRIND_FOUND) add_custom_target(testall-valgrind - COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N ${CMAKE_SOURCE_DIR} COMMAND make install COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/server/test/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf COMMAND /bin/sh -c "valgrind --track-fds=yes --leak-check=full --show-leak-kinds=all --log-file=${CMAKE_BINARY_DIR}/valgrind.log ${CMAKE_BINARY_DIR}/bin/maxscale -c ${CMAKE_BINARY_DIR} &>/dev/null" diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 332266ab5..535eca2cc 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -133,6 +133,14 @@ Configure the directory MaxScale uses to store cached data. An example of cached cachedir=/tmp/maxscale_cache/ ``` +#### `piddir` + +Configure the directory for the PID file for MaxScale. This file contains the Process ID for the running MaxScale process. + +``` +piddir=/tmp/maxscale_cache/ +``` + #### `language` Set the folder where the errmsg.sys file is located in. MaxScale will look for the errmsg.sys file installed with MaxScale from this folder. diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index 943ecaa27..6913431bc 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -36,6 +36,8 @@ Switch|Long Option|Description `-C PATH`|`--configdir=PATH`|path to configuration file directory (default: /etc/) `-B PATH`|`--libdir=PATH`|path to module directory (default: /usr/lib64/maxscale) `-A PATH`|`--cachedir=PATH`|path to cache directory (default: /var/cache/maxscale) +`P PATH`|`--piddir=PATH`|PID file directory +`-U USER`|`--user=USER`|run MaxScale as another user. The user ID and group ID of this user are used to run MaxScale. `-s [yes no]`|`--syslog=[yes no]`|log messages to syslog (default:yes) `-S [yes no]`|`--maxscalelog=[yes no]`|log messages to MaxScale log (default: yes) `-v`|`--version`|print version info and exit diff --git a/cmake/macros.cmake b/cmake/macros.cmake index 43c366ef6..c287343fb 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -132,29 +132,9 @@ macro(check_deps) endif() - # set(MAXSCALE_DEPS aio ssl crypt crypto z m dl rt pthread) - # foreach(lib ${MAXSCALE_DEPS}) - # find_library(lib${lib} ${lib}) - # if((DEFINED lib${lib}) AND (${lib${lib}} MATCHES "NOTFOUND")) - # set(DEPS_ERROR TRUE) - # set(FAILED_DEPS "${FAILED_DEPS} lib${lib}") - # elseif(DEBUG_OUTPUT) - # message(STATUS "Library was found at: ${lib${lib}}") - # endif() - # endforeach() - - # if(DEPS_ERROR) - # set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.") - # message(FATAL_ERROR "Cannot find dependencies: ${FAILED_DEPS}") - # endif() - endmacro() macro(check_dirs) - - # This variable is used to prevent redundant checking of dependencies - set(DEPS_OK TRUE CACHE BOOL "If all the dependencies were found.") - # Find the MySQL headers if they were not defined if(DEFINED MYSQL_DIR) @@ -168,7 +148,6 @@ macro(check_dirs) debugmsg("Search returned: ${MYSQL_DIR_LOC}") if(${MYSQL_DIR_LOC} MATCHES "NOTFOUND") - set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.") message(FATAL_ERROR "Fatal Error: MySQL headers were not found.") else() set(MYSQL_DIR ${MYSQL_DIR_LOC} CACHE PATH "Path to MySQL headers" FORCE) @@ -193,7 +172,6 @@ debugmsg("Search returned: ${MYSQL_DIR_LOC}") else() find_file(ERRMSG_FILE errmsg.sys PATHS /usr/share /usr/share/mysql /usr/local/share/mysql PATH_SUFFIXES english mysql/english) if(${ERRMSG_FILE} MATCHES "NOTFOUND") - set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.") message(FATAL_ERROR "Fatal Error: The errmsg.sys file was not found, please define the path to it by using -DERRMSG=") else() message(STATUS "Using errmsg.sys found at: ${ERRMSG_FILE}") @@ -208,7 +186,6 @@ debugmsg("Search returned: ${MYSQL_DIR_LOC}") if(${RPM_FNC} MATCHES "RPM_FNC-NOTFOUND") find_file(DEB_FNC init-functions PATHS /lib/lsb) if(${DEB_FNC} MATCHES "DEB_FNC-NOTFOUND") - set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.") message(FATAL_ERROR "Cannot find required init-functions in /lib/lsb/ or /etc/rc.d/init.d/, please confirm that your system files are OK.") else() set(DEB_BASED TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") diff --git a/server/core/gateway.c b/server/core/gateway.c index 5e17fe04f..9845713e1 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -154,6 +154,7 @@ static struct option long_options[] = { {"logdir", required_argument, 0, 'L'}, {"datadir", required_argument, 0, 'D'}, {"configdir",required_argument, 0, 'C'}, + {"piddir",required_argument, 0, 'P'}, {"libdir",required_argument, 0, 'B'}, {"cachedir",required_argument, 0, 'A'}, {"language",required_argument, 0, 'N'}, @@ -870,6 +871,8 @@ static void usage(void) " (default: /etc/)\n" " -D, --datadir=PATH path to data directory, stored embedded mysql tables\n" " (default: /var/cache/maxscale)\n" + " -P, --piddir=PATH path to PID file directory\n" + " (default: /var/run/maxscale)\n" " -U, --user=USER run MaxScale as another user.\n" " The user ID and group ID of this user are used to run MaxScale.\n" " -s, --syslog=[yes|no] log messages to syslog (default:yes)\n" @@ -976,7 +979,7 @@ int main(int argc, char **argv) } } - while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:D:C:B:U:A:", + while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:D:C:B:U:A:P:", long_options, &option_index)) != -1) { bool succp = true; @@ -1045,6 +1048,12 @@ int main(int argc, char **argv) langdir = tmp_path; } break; + case 'P': + if(handle_path_arg(&tmp_path,optarg,NULL,true,true)) + { + piddir = tmp_path; + } + break; case 'D': sprintf(datadir,"%s",optarg); maxscaledatadir = strdup(optarg); @@ -1821,7 +1830,7 @@ static int write_pid_file(char *home_dir) { int fd = -1; - snprintf(pidfile, PATH_MAX, "%smaxscale.pid",default_piddir); + snprintf(pidfile, PATH_MAX, "%smaxscale.pid",piddir?piddir:default_piddir); fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); if (fd == -1) { @@ -1920,6 +1929,11 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons if(libdir == NULL) handle_path_arg(&libdir,(char*)value,NULL,true,false); } + else if(strcmp(name, "piddir") == 0) + { + if(piddir == NULL) + handle_path_arg(&piddir,(char*)value,NULL,true,true); + } else if(strcmp(name, "datadir") == 0) { if(!datadir_defined) diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index 66e34426f..b8044484b 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -37,7 +37,7 @@ static char* libdir = NULL; static char* cachedir = NULL; static char* maxscaledatadir = NULL; static char* langdir = NULL; - +static char* piddir = NULL; char* get_libdir(); char* get_datadir(); char* get_cachedir(); diff --git a/server/modules/filter/test/tee_recursion.sh b/server/modules/filter/test/tee_recursion.sh index 82a8080e2..a37dae0c4 100755 --- a/server/modules/filter/test/tee_recursion.sh +++ b/server/modules/filter/test/tee_recursion.sh @@ -17,8 +17,8 @@ function execute_test() return 1 fi - LAST_LOG=$(ls $BINDIR/log -1|grep err|sort|uniq|tail -n 1) - TEST_RESULT=$(cat $BINDIR/log/$LAST_LOG | grep -i recursive) + LAST_LOG=$(ls $BINDIR/ -1|grep error|sort|uniq|tail -n 1) + TEST_RESULT=$(cat $BINDIR/$LAST_LOG | grep -i recursive) if [[ "$TEST_RESULT" != "" ]] then return 0 diff --git a/server/test/maxscale_test.cnf b/server/test/maxscale_test.cnf index 83f52b23c..fdc843d8d 100644 --- a/server/test/maxscale_test.cnf +++ b/server/test/maxscale_test.cnf @@ -1,10 +1,11 @@ [maxscale] threads=4 libdir=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@ -logdir=@CMAKE_INSTALL_PREFIX@/log -datadir=@CMAKE_INSTALL_PREFIX@/data -cachedir=@CMAKE_INSTALL_PREFIX@/cache +logdir=@CMAKE_INSTALL_PREFIX@/ +datadir=@CMAKE_INSTALL_PREFIX@/ +cachedir=@CMAKE_INSTALL_PREFIX@/ language=@CMAKE_INSTALL_PREFIX@/lib/maxscale/ +piddir=@CMAKE_INSTALL_PREFIX@/ [feedback] feedback_enable=true From 06bece82a80cf1895291550999fdd93e157c54e0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 26 May 2015 23:08:29 +0300 Subject: [PATCH 093/273] Fixed multi-character wildcard database grants not working with schemarouter. --- server/core/dbusers.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index c8d1a78ec..f4e0ed080 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -974,19 +974,27 @@ getAllUsers(SERVICE *service, USERS *users) } } - if(service->optimize_wildcard && havedb && wildcard_db_grant(dbnm)) + if(havedb && wildcard_db_grant(dbnm)) { - rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); - skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); + if(service->optimize_wildcard) + { + rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); + skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); + } + else + { + /** Use ANYDB for wildcard grants */ + rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL); + } } else { rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], havedb ? dbnm : NULL); } - skygw_log_write(LOGFILE_DEBUG,"%s: Adding user:%s host:%s anydb:%s db:%s.", + LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG,"%s: Adding user:%s host:%s anydb:%s db:%s.", service->name,row[0],row[1],row[4], - havedb ? dbnm : NULL); + havedb ? dbnm : NULL))); } else { /* we don't have dbgrants, simply set ANY DB for the user */ rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL); @@ -1483,10 +1491,18 @@ getUsers(SERVICE *service, USERS *users) } } - if(service->optimize_wildcard && havedb && wildcard_db_grant(row[5])) + if(havedb && wildcard_db_grant(row[5])) { - rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); - skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,row[5],rc); + if(service->optimize_wildcard) + { + rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); + skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,row[5],rc); + } + else + { + /** Use ANYDB for wildcard grants */ + rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL); + } } else { From 4365a04d2c0f36d079c6428f705e38d504be4031 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 10:30:21 +0300 Subject: [PATCH 094/273] Added 5.5.5- string to the start of MariaDB 10.0 version strings. --- server/core/config.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index 60c99bf87..274522468 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -231,7 +231,7 @@ int rval; strcpy(version_string,tmp); } - ptr = strstr(tmp, "-embedded"); + ptr = strstr(version_string, "-embedded"); if (ptr) { *ptr = '\0'; } @@ -417,7 +417,21 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); } if (version_string) { + + /** Add the 5.5.5- string to the start of the version string if + * the version string starts with "10.". + * This mimics MariaDB 10.0 replication which adds 5.5.5- for backwards compatibility. */ + if(strncmp(version_string,"10.",3) == 0) + { + ((SERVICE *)(obj->element))->version_string = malloc((strlen(version_string) + + strlen("5.5.5-") + 1) * sizeof(char)); + strcpy(((SERVICE *)(obj->element))->version_string,"5.5.5-"); + strcat(((SERVICE *)(obj->element))->version_string,version_string); + } + else + { ((SERVICE *)(obj->element))->version_string = strdup(version_string); + } } else { if (gateway.version_string) ((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string); From 16d6bd6d2c7f4d0b40eee154d6656902a238187d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 11:56:14 +0300 Subject: [PATCH 095/273] Added service SSL mode variables. --- server/core/config.c | 2 +- server/core/service.c | 13 +++++++++- server/include/service.h | 7 ++++++ .../include/mysql_client_server_protocol.h | 4 ++++ server/modules/protocol/mysql_client.c | 24 ++++++++++++++++--- 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 274522468..ccbeee0e0 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -420,7 +420,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); /** Add the 5.5.5- string to the start of the version string if * the version string starts with "10.". - * This mimics MariaDB 10.0 replication which adds 5.5.5- for backwards compatibility. */ + * This mimics MariaDB 10.0 behavior which adds 5.5.5- for backwards compatibility. */ if(strncmp(version_string,"10.",3) == 0) { ((SERVICE *)(obj->element))->version_string = malloc((strlen(version_string) + diff --git a/server/core/service.c b/server/core/service.c index 5ba5d539d..8297ea6fd 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -136,7 +136,8 @@ SERVICE *service; service->routerModule = strdup(router); service->users_from_all = false; service->resources = NULL; - + service->ssl_mode = SSL_REQUIRED; + if (service->name == NULL || service->routerModule == NULL) { if (service->name) @@ -855,6 +856,16 @@ serviceOptimizeWildcard(SERVICE *service, int action) return 1; } +/** Enable or disable the service SSL capability*/ +int +serviceSetSSL(SERVICE *service, int action) +{ + if(action) + service->ssl_mode = SSL_REQUIRED; + else + service->ssl_mode = SSL_DISABLED; +} + /** * Whether to strip escape characters from the name of the database the client * is connecting to. diff --git a/server/include/service.h b/server/include/service.h index f26c99806..a6fea6d56 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -105,6 +105,12 @@ typedef struct server_ref_t{ SERVER* server; }SERVER_REF; +typedef enum { + SSL_DISABLED, + SSL_ENABLED, + SSL_REQUIRED +} ssl_mode_t; + /** * Defines a service within the gateway. * @@ -149,6 +155,7 @@ typedef struct service { FILTER_DEF **filters; /**< Ordered list of filters */ int n_filters; /**< Number of filters */ int conn_timeout; /*< Session timeout in seconds */ + ssl_mode_t ssl_mode; /*< one of DISABLED, ENABLED or REQUIRED */ char *weightby; struct service *next; /**< The next service in the linked list */ } SERVICE; diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index 46bbe296c..de8118659 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -97,6 +97,10 @@ typedef enum { MYSQL_AUTH_RECV, MYSQL_AUTH_FAILED, MYSQL_HANDSHAKE_FAILED, + MYSQL_AUTH_SSL_REQ, /*< client requested SSL */ + MYSQL_AUTH_SSL_EXCHANGE_DONE, /*< SSL handshake done */ + MYSQL_AUTH_SSL_EXCHANGE_ERR, /*< SSL handshake failure */ + MYSQL_AUTH_SSL_RECV, /*< */ MYSQL_IDLE } mysql_auth_state_t; diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index abdb4422c..d1e188281 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -242,7 +242,7 @@ MySQLSendHandshake(DCB* dcb) char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1]=""; char *version_string; int len_version_string=0; - + MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol); GWBUF *buf; @@ -319,7 +319,16 @@ MySQLSendHandshake(DCB* dcb) mysql_server_capabilities_one[0] &= ~GW_MYSQL_CAPABILITIES_COMPRESS; - mysql_server_capabilities_one[0] &= ~GW_MYSQL_CAPABILITIES_SSL; + + if(dcb->service->ssl_mode != SSL_DISABLED) + { + mysql_server_capabilities_one[1] |= GW_MYSQL_CAPABILITIES_SSL >> 8; + } + else + { + mysql_server_capabilities_one[0] &= ~GW_MYSQL_CAPABILITIES_SSL; + } + memcpy(mysql_handshake_payload, mysql_server_capabilities_one, sizeof(mysql_server_capabilities_one)); mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_capabilities_one); @@ -402,7 +411,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { uint8_t *stage1_hash = NULL; int auth_ret = -1; MYSQL_session *client_data = NULL; - + int ssl = 0; CHK_DCB(dcb); protocol = DCB_PROTOCOL(dcb, MySQLProtocol); @@ -451,6 +460,15 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { &protocol->client_capabilities); */ + + ssl = protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL; + + /** Client didn't requested SSL when SSL mode was required*/ + if(!ssl && protocol->owner_dcb->service->ssl_mode == SSL_REQUIRED) + { + return 1; + } + username = get_username_from_auth(username, client_auth_packet); if (username == NULL) From f4dee6d584dbc6c21284cdd07ba44c52a506b174 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 12:11:49 +0300 Subject: [PATCH 096/273] Fixed systemd service file not starting with the maxscale user. --- etc/maxscale.service.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/maxscale.service.in b/etc/maxscale.service.in index 50e781f30..6717f6b25 100644 --- a/etc/maxscale.service.in +++ b/etc/maxscale.service.in @@ -5,7 +5,7 @@ After=network.target [Service] Type=forking PIDFile=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid -ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale +ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale [Install] WantedBy=multi-user.target From b24da2c540d09d28943b39c6e3cd1cdb1b0199df Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 12:47:26 +0300 Subject: [PATCH 097/273] Changed errmsg.sys file to be world executable when installed. This way the maxscale user can read it when the package is installed. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 376df9b0f..03cb541b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,7 +154,8 @@ install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHARED install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES server/maxscale_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES server/maxscale_binlogserver_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) -install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_VARDIR}/lib/maxscale) +install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_VARDIR}/lib/maxscale + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHAREDIR}) From 3d6259cb00d32818b9bc98f154a772543e524509 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 16:33:51 +0300 Subject: [PATCH 098/273] Added configuration options for different SSL modes. --- server/core/config.c | 9 ++++++++- server/core/service.c | 20 ++++++++++++++++---- server/include/service.h | 1 + server/modules/protocol/mysql_client.c | 12 ++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index ccbeee0e0..f6721a28e 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -345,6 +345,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); char *weightby; char *version_string; char *subservices; + char* ssl; bool is_rwsplit = false; bool is_schemarouter = false; char *allow_localhost_match_wildcard_host; @@ -353,6 +354,8 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); user = config_get_value(obj->parameters, "user"); auth = config_get_value(obj->parameters, "passwd"); subservices = config_get_value(obj->parameters, "subservices"); + ssl = config_get_value(obj->parameters, "ssl"); + enable_root_user = config_get_value( obj->parameters, "enable_root_user"); @@ -443,7 +446,11 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); max_slave_rlag_str = config_get_value(obj->parameters, "max_slave_replication_lag"); - + + if(ssl) + if(serviceSetSSL(obj->element,ssl) != 0) + skygw_log_write(LE,"Error: Unknown parameter for service '%s': %s",obj->object,ssl); + if (enable_root_user) serviceEnableRootUser( obj->element, diff --git a/server/core/service.c b/server/core/service.c index 8297ea6fd..4ef9b3515 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -136,7 +136,7 @@ SERVICE *service; service->routerModule = strdup(router); service->users_from_all = false; service->resources = NULL; - service->ssl_mode = SSL_REQUIRED; + service->ssl_mode = SSL_DISABLED; if (service->name == NULL || service->routerModule == NULL) { @@ -858,12 +858,20 @@ serviceOptimizeWildcard(SERVICE *service, int action) /** Enable or disable the service SSL capability*/ int -serviceSetSSL(SERVICE *service, int action) +serviceSetSSL(SERVICE *service, char* action) { - if(action) + int rval = 0; + + if(strcasecmp(action,"required") == 0) service->ssl_mode = SSL_REQUIRED; - else + else if(strcasecmp(action,"enabled") == 0) + service->ssl_mode = SSL_ENABLED; + else if(strcasecmp(action,"disabled") == 0) service->ssl_mode = SSL_DISABLED; + else + rval = -1; + + return rval; } /** @@ -1029,6 +1037,8 @@ int i; printf("\tUsers data: %p\n", (void *)service->users); printf("\tTotal connections: %d\n", service->stats.n_sessions); printf("\tCurrently connected: %d\n", service->stats.n_current); + printf("\tSSL: %s\n", service->ssl_mode == SSL_DISABLED ? "Disabled": + (service->ssl_mode == SSL_ENABLED ? "Enabled":"Required")); } /** @@ -1138,6 +1148,8 @@ int i; service->stats.n_sessions); dcb_printf(dcb, "\tCurrently connected: %d\n", service->stats.n_current); + dcb_printf(dcb,"\tSSL: %s\n", service->ssl_mode == SSL_DISABLED ? "Disabled": + (service->ssl_mode == SSL_ENABLED ? "Enabled":"Required")); } /** diff --git a/server/include/service.h b/server/include/service.h index a6fea6d56..e0ae151cf 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -185,6 +185,7 @@ extern int serviceRestart(SERVICE *); extern int serviceSetUser(SERVICE *, char *, char *); extern int serviceGetUser(SERVICE *, char **, char **); extern void serviceSetFilters(SERVICE *, char *); +extern int serviceSetSSL(SERVICE *service, char* action); extern int serviceEnableRootUser(SERVICE *, int ); extern int serviceSetTimeout(SERVICE *, int ); extern void serviceWeightBy(SERVICE *, char *); diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index d1e188281..eaf061334 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -466,9 +466,21 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { /** Client didn't requested SSL when SSL mode was required*/ if(!ssl && protocol->owner_dcb->service->ssl_mode == SSL_REQUIRED) { + LOGIF(LT,(skygw_log_write(LT,"User %s@%s connected to service '%s' without SSL when SSL was required.", + protocol->owner_dcb->user, + protocol->owner_dcb->remote, + protocol->owner_dcb->service->name))); return 1; } + if(LOG_IS_ENABLED(LT)) + { + skygw_log_write(LT,"User %s@%s connected to service '%s' with SSL.", + protocol->owner_dcb->user, + protocol->owner_dcb->remote, + protocol->owner_dcb->service->name); + } + username = get_username_from_auth(username, client_auth_packet); if (username == NULL) From 449c186a668a6d48f9de484ada049af703fe43b0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 18:13:45 +0300 Subject: [PATCH 099/273] Added OpenSSL init function call. --- server/modules/protocol/mysql_client.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index eaf061334..0d5c47b48 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -46,7 +46,9 @@ #include #include #include - +#include +#include +#include MODULE_INFO info = { MODULE_API_PROTOCOL, MODULE_GA, @@ -113,6 +115,7 @@ version() void ModuleInit() { + SSL_library_init(); } /** @@ -473,7 +476,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { return 1; } - if(LOG_IS_ENABLED(LT)) + if(LOG_IS_ENABLED(LT) && ssl) { skygw_log_write(LT,"User %s@%s connected to service '%s' with SSL.", protocol->owner_dcb->user, @@ -481,6 +484,11 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { protocol->owner_dcb->service->name); } + if(ssl && protocol->owner_dcb->service->ssl_mode != SSL_DISABLED) + { + + } + username = get_username_from_auth(username, client_auth_packet); if (username == NULL) From a572166ffd0a3152fdf7229e17fcf0242a633587 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 22:19:50 +0300 Subject: [PATCH 100/273] Added ssl handshake to mysql_client --- server/core/config.c | 30 +++++++++-- server/core/service.c | 50 +++++++++++++++++++ server/include/service.h | 15 +++++- .../include/mysql_client_server_protocol.h | 5 +- server/modules/protocol/mysql_client.c | 44 ++++++++++++++-- 5 files changed, 134 insertions(+), 10 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index f6721a28e..12d4c099e 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -345,7 +345,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); char *weightby; char *version_string; char *subservices; - char* ssl; + char *ssl,*ssl_cert,*ssl_key,*ssl_ca_cert; bool is_rwsplit = false; bool is_schemarouter = false; char *allow_localhost_match_wildcard_host; @@ -355,7 +355,9 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); auth = config_get_value(obj->parameters, "passwd"); subservices = config_get_value(obj->parameters, "subservices"); ssl = config_get_value(obj->parameters, "ssl"); - + ssl_cert = config_get_value(obj->parameters, "ssl_cert"); + ssl_key = config_get_value(obj->parameters, "ssl_key"); + ssl_ca_cert = config_get_value(obj->parameters, "ssl_ca_cert"); enable_root_user = config_get_value( obj->parameters, "enable_root_user"); @@ -448,8 +450,28 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); "max_slave_replication_lag"); if(ssl) - if(serviceSetSSL(obj->element,ssl) != 0) - skygw_log_write(LE,"Error: Unknown parameter for service '%s': %s",obj->object,ssl); + { + if(ssl_cert == NULL) + skygw_log_write(LE,"Error: Server certificate missing for service '%s'.",obj->object); + if(ssl_ca_cert == NULL) + skygw_log_write(LE,"Error: CA Certificate missing for service '%s'.",obj->object); + if(ssl_key == NULL) + skygw_log_write(LE,"Error: Server private key missing for service '%s'.",obj->object); + + if(ssl_ca_cert != NULL && ssl_cert != NULL && ssl_key != NULL) + { + + if(serviceSetSSL(obj->element,ssl) != 0) + { + skygw_log_write(LE,"Error: Unknown parameter for service '%s': %s",obj->object,ssl); + } + else + { + serviceSetCertificates(obj->element,ssl_cert,ssl_key,ssl_ca_cert); + } + } + + } if (enable_root_user) serviceEnableRootUser( diff --git a/server/core/service.c b/server/core/service.c index 4ef9b3515..ee6d1cf26 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -137,6 +137,10 @@ SERVICE *service; service->users_from_all = false; service->resources = NULL; service->ssl_mode = SSL_DISABLED; + service->ssl_init_done = false; + service->ssl_ca_cert = NULL; + service->ssl_cert = NULL; + service->ssl_key = NULL; if (service->name == NULL || service->routerModule == NULL) { @@ -856,6 +860,14 @@ serviceOptimizeWildcard(SERVICE *service, int action) return 1; } +void +serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert) +{ + service->ssl_cert = strdup(cert); + service->ssl_key = strdup(key); + service->ssl_ca_cert = strdup(ca_cert); +} + /** Enable or disable the service SSL capability*/ int serviceSetSSL(SERVICE *service, char* action) @@ -1798,3 +1810,41 @@ int *data; return set; } + + +int serviceInitSSL(SERVICE* service) +{ + if(!service->ssl_init_done) + { + service->method = (SSL_METHOD*)SSLv23_server_method(); + service->ctx = SSL_CTX_new(service->method); + + if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { + return -1; + } + + /* Load the private-key corresponding to the server certificate */ + if (SSL_CTX_use_PrivateKey_file(service->ctx, service->ssl_key, SSL_FILETYPE_PEM) <= 0) { + return -1; + } + + /* Check if the server certificate and private-key matches */ + if (!SSL_CTX_check_private_key(service->ctx)) { + return -1; + } + + + /* Load the RSA CA certificate into the SSL_CTX structure */ + if (!SSL_CTX_load_verify_locations(service->ctx, service->ssl_ca_cert, NULL)) { + return -1; + } + + /* Set to require peer (client) certificate verification */ + SSL_CTX_set_verify(service->ctx,SSL_VERIFY_PEER,NULL); + + /* Set the verification depth to 1 */ + SSL_CTX_set_verify_depth(service->ctx,10); + service->ssl_init_done = true; + } + return 0; +} diff --git a/server/include/service.h b/server/include/service.h index e0ae151cf..1c6614656 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -26,7 +26,9 @@ #include #include #include - +#include +#include +#include /** * @file service.h * @@ -158,6 +160,15 @@ typedef struct service { ssl_mode_t ssl_mode; /*< one of DISABLED, ENABLED or REQUIRED */ char *weightby; struct service *next; /**< The next service in the linked list */ + SSL_CTX *ctx; + SSL *ssl; + SSL_METHOD *method; /*< SSLv2/3 or TLSv1/2 methods + * see: https://www.openssl.org/docs/ssl/SSL_CTX_new.html */ + char* ssl_cert; + char* ssl_key; + char* ssl_ca_cert; + bool ssl_init_done; + } SERVICE; typedef enum count_spec_t {COUNT_NONE=0, COUNT_ATLEAST, COUNT_EXACT, COUNT_ATMOST} count_spec_t; @@ -186,6 +197,8 @@ extern int serviceSetUser(SERVICE *, char *, char *); extern int serviceGetUser(SERVICE *, char **, char **); extern void serviceSetFilters(SERVICE *, char *); extern int serviceSetSSL(SERVICE *service, char* action); +extern int serviceInitSSL(SERVICE* service); +extern void serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert); extern int serviceEnableRootUser(SERVICE *, int ); extern int serviceSetTimeout(SERVICE *, int ); extern void serviceWeightBy(SERVICE *, char *); diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index de8118659..87dbc50ee 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -54,7 +54,9 @@ #include #include #include - +#include +#include +#include #include #include #include @@ -294,6 +296,7 @@ typedef struct { unsigned long tid; /*< MySQL Thread ID, in * handshake */ unsigned int charset; /*< MySQL character set at connect time */ + SSL* ssl; /*< SSL struct for client connection */ #if defined(SS_DEBUG) skygw_chk_t protocol_chk_tail; #endif diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 0d5c47b48..5adb8bcd0 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -46,9 +46,7 @@ #include #include #include -#include -#include -#include + MODULE_INFO info = { MODULE_API_PROTOCOL, MODULE_GA, @@ -116,6 +114,7 @@ void ModuleInit() { SSL_library_init(); + SSL_load_error_strings(); } /** @@ -484,9 +483,47 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { protocol->owner_dcb->service->name); } + /** Do the SSL Handshake */ if(ssl && protocol->owner_dcb->service->ssl_mode != SSL_DISABLED) { + if(serviceInitSSL(protocol->owner_dcb->service) != 0) + { + skygw_log_write(LOGFILE_ERROR,"Error: SSL initialization for service '%s' failed.", + protocol->owner_dcb->service->name); + return 1; + } + protocol->ssl = SSL_new(protocol->owner_dcb->service->ctx); + SSL_set_fd(protocol->ssl,dcb->fd); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; + printf("%s\n",SSL_get_version(protocol->ssl)); + int errnum,rval; + char errbuf[1024]; + switch((rval = SSL_accept(protocol->ssl))) + { + case 0: + errnum = SSL_get_error(protocol->ssl,rval); + ERR_error_string(errnum,errbuf); + skygw_log_write_flush(LOGFILE_ERROR,"SSL_accept: %s",errbuf); + ERR_print_errors_fp(stdout); + ERR_error_string(errnum,errbuf); + printf("%s\n",errbuf); + fflush(stdout); + break; + case 1: + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; + break; + default: + errnum = SSL_get_error(protocol->ssl,rval); + ERR_print_errors_fp(stdout); + ERR_error_string(errnum,errbuf); + printf("%s\n",errbuf); + fflush(stdout); + skygw_log_write_flush(LOGFILE_ERROR,"Error: Fatal error in SSL_accept: %s",errbuf); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_ERR; + break; + + } } username = get_username_from_auth(username, client_auth_packet); @@ -1700,4 +1737,3 @@ return_str: return str; } #endif - From f946a44620b9f709d01dbcdb11c0769f1dab733d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 23:11:32 +0300 Subject: [PATCH 101/273] Added handling of partial SSL handshakes. --- server/modules/protocol/mysql_client.c | 91 +++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 5adb8bcd0..387b59aa0 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -78,6 +78,8 @@ extern char* get_username_from_auth(char* ptr, uint8_t* data); extern int check_db_name_after_auth(DCB *, char *, int); extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db); +int do_ssl_accept(MySQLProtocol* protocol); + /* * The "module object" for the mysqld client protocol module. */ @@ -498,6 +500,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { printf("%s\n",SSL_get_version(protocol->ssl)); int errnum,rval; char errbuf[1024]; + switch((rval = SSL_accept(protocol->ssl))) { case 0: @@ -515,12 +518,22 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { default: errnum = SSL_get_error(protocol->ssl,rval); - ERR_print_errors_fp(stdout); - ERR_error_string(errnum,errbuf); - printf("%s\n",errbuf); - fflush(stdout); - skygw_log_write_flush(LOGFILE_ERROR,"Error: Fatal error in SSL_accept: %s",errbuf); - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_ERR; + if(errnum == SSL_ERROR_WANT_READ) + { + /** Not all of the data has been read. Go back to the poll + queue and wait for more.*/ + protocol->protocol_auth_state = MYSQL_AUTH_SSL_RECV; + return 0; + } + else + { + ERR_print_errors_fp(stdout); + ERR_error_string(errnum,errbuf); + printf("%s\n",errbuf); + fflush(stdout); + skygw_log_write_flush(LOGFILE_ERROR,"Error: Fatal error in SSL_accept: %s",errbuf); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_ERR; + } break; } @@ -655,6 +668,10 @@ int gw_read_client_event( CHK_DCB(dcb); protocol = DCB_PROTOCOL(dcb, MySQLProtocol); CHK_PROTOCOL(protocol); + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_RECV) + { + goto do_auth; + } rc = dcb_read(dcb, &read_buffer); @@ -794,7 +811,7 @@ int gw_read_client_event( } } - + do_auth: /** * Now there should be at least one complete mysql packet in read_buffer. */ @@ -805,7 +822,10 @@ int gw_read_client_event( int auth_val; auth_val = gw_mysql_do_authentication(dcb, read_buffer); - + + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_RECV) + break; + if (auth_val == 0) { SESSION *session; @@ -899,6 +919,15 @@ int gw_read_client_event( } break; + case MYSQL_AUTH_SSL_RECV: + { + if(do_ssl_accept(protocol) == 1) + { + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; + } + } + break; + case MYSQL_IDLE: { uint8_t* payload = NULL; @@ -1737,3 +1766,49 @@ return_str: return str; } #endif + +int do_ssl_accept(MySQLProtocol* protocol) +{ + int rval,errnum; + char errbuf[2014]; + + switch((rval = SSL_accept(protocol->ssl))) + { + case 0: + errnum = SSL_get_error(protocol->ssl,rval); + ERR_error_string(errnum,errbuf); + skygw_log_write_flush(LOGFILE_ERROR,"SSL_accept: %s",errbuf); + ERR_print_errors_fp(stdout); + ERR_error_string(errnum,errbuf); + printf("%s\n",errbuf); + fflush(stdout); + break; + case 1: + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; + rval = 1; + break; + + default: + errnum = SSL_get_error(protocol->ssl,rval); + if(errnum == SSL_ERROR_WANT_READ) + { + /** Not all of the data has been read. Go back to the poll + queue and wait for more.*/ + rval = 0; + } + else + { + ERR_print_errors_fp(stdout); + ERR_error_string(errnum,errbuf); + printf("%s\n",errbuf); + fflush(stdout); + skygw_log_write_flush(LOGFILE_ERROR,"Error: Fatal error in SSL_accept: %s",errbuf); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_ERR; + rval = -1; + } + break; + + } + + return rval; +} \ No newline at end of file From 0f814d3e73c8e57f1f78ef91a8999bf5672f099a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 29 May 2015 13:00:37 +0300 Subject: [PATCH 102/273] Added SSL write and read functions. --- server/core/config.c | 19 +- server/core/dcb.c | 529 +++++++++++++++++- server/include/dcb.h | 8 +- .../include/mysql_client_server_protocol.h | 1 + server/modules/protocol/mysql_client.c | 245 +++++--- server/modules/protocol/mysql_common.c | 5 +- 6 files changed, 726 insertions(+), 81 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 12d4c099e..640437270 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -452,11 +452,17 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); if(ssl) { if(ssl_cert == NULL) - skygw_log_write(LE,"Error: Server certificate missing for service '%s'.",obj->object); + skygw_log_write(LE,"Error: Server certificate missing for service '%s'." + "Please provide the path to the server certificate by adding the ssl_cert= parameter", + obj->object); if(ssl_ca_cert == NULL) - skygw_log_write(LE,"Error: CA Certificate missing for service '%s'.",obj->object); + skygw_log_write(LE,"Error: CA Certificate missing for service '%s'." + "Please provide the path to the certificate authority certificate by adding the ssl_ca_cert= parameter", + obj->object); if(ssl_key == NULL) - skygw_log_write(LE,"Error: Server private key missing for service '%s'.",obj->object); + skygw_log_write(LE,"Error: Server private key missing for service '%s'. " + "Please provide the path to the server certificate key by adding the ssl_key= parameter" + ,obj->object); if(ssl_ca_cert != NULL && ssl_cert != NULL && ssl_key != NULL) { @@ -470,6 +476,13 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); serviceSetCertificates(obj->element,ssl_cert,ssl_key,ssl_ca_cert); } } + else + { + /** If SSL was configured wrong, the + * service needs to fail.*/ + skygw_log_write_flush(LE,"Error: Missing SSL certificate paths found in the configuration. " + "This service will not use SSL."); + } } diff --git a/server/core/dcb.c b/server/core/dcb.c index 6717aea41..ba2028de2 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -49,6 +49,7 @@ * backend * 07/05/2014 Mark Riddoch Addition of callback mechanism * 20/06/2014 Mark Riddoch Addition of dcb_clone + * 29/05/2015 Markus Makela Addition of dcb_write_SSL * * @endverbatim */ @@ -880,6 +881,152 @@ return_n: return n; } + +/** + * General purpose read routine to read data from a socket in the + * Descriptor Control Block and append it to a linked list of buffers. + * The list may be empty, in which case *head == NULL + * + * @param dcb The DCB to read from + * @param head Pointer to linked list to append data to + * @return -1 on error, otherwise the number of read bytes on the last + * iteration of while loop. 0 is returned if no data available. + */ +int dcb_read_SSL( + DCB *dcb, + SSL* ssl, + GWBUF **head) +{ + GWBUF *buffer = NULL; + int b; + int rc; + int n; + int nread = 0; + + CHK_DCB(dcb); + + if (dcb->fd <= 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Read failed, dcb is %s.", + dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not readable"))); + n = 0; + goto return_n; + } + + while (true) + { + int bufsize; + + rc = ioctl(dcb->fd, FIONREAD, &b); + + if (rc == -1) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : ioctl FIONREAD for dcb %p in " + "state %s fd %d failed due error %d, %s.", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + errno, + strerror(errno)))); + n = -1; + goto return_n; + } + + if (b == 0 && nread == 0) + { + /** Handle closed client socket */ + if (dcb_isclient(dcb)) + { + char c; + int l_errno = 0; + int r = -1; + + /* try to read 1 byte, without consuming the socket buffer */ + r = recv(dcb->fd, &c, sizeof(char), MSG_PEEK); + l_errno = errno; + + if (r <= 0 && + l_errno != EAGAIN && + l_errno != EWOULDBLOCK && + l_errno != 0) + { + n = -1; + goto return_n; + } + } + n = 0; + goto return_n; + } + else if (b == 0) + { + n = 0; + goto return_n; + } + + dcb->last_read = hkheartbeat; + + bufsize = MIN(b, MAX_BUFFER_SIZE); + + if ((buffer = gwbuf_alloc(bufsize)) == NULL) + { + /*< + * This is a fatal error which should cause shutdown. + * Todo shutdown if memory allocation fails. + */ + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Failed to allocate read buffer " + "for dcb %p fd %d, due %d, %s.", + dcb, + dcb->fd, + errno, + strerror(errno)))); + + n = -1; + goto return_n; + } + GW_NOINTR_CALL(n = SSL_read(ssl, GWBUF_DATA(buffer), bufsize); + dcb->stats.n_reads++); + + if (n <= 0) + { + int ssl_errno = ERR_get_error(); + if(ssl_errno != SSL_ERROR_WANT_READ) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Read failed, dcb %p in state " + "%s fd %d: %s.", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ERR_error_string(ssl_errno,NULL)))); + + gwbuf_free(buffer); + goto return_n; + } + } + nread += n; + + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_read] Read %d bytes from dcb %p in state %s " + "fd %d.", + pthread_self(), + n, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + /*< Append read data to the gwbuf */ + *head = gwbuf_append(*head, buffer); + } /*< while (true) */ +return_n: + return n; +} /** * General purpose routine to write to a DCB * @@ -905,11 +1052,11 @@ int below_water; return 0; } /** - * SESSION_STATE_STOPPING means that one of the backends is closing - * the router session. Some backends may have not completed + * SESSION_STATE_STOPPING means that one of the backends is closing + * the router session. Some backends may have not completed * authentication yet and thus they have no information about router * being closed. Session state is changed to SESSION_STATE_STOPPING - * before router's closeSession is called and that tells that DCB may + * before router's closeSession is called and that tells that DCB may * still be writable. */ if (queue == NULL || @@ -932,9 +1079,9 @@ int below_water; //ss_dassert(false); return 0; } - + spinlock_acquire(&dcb->writeqlock); - + if (dcb->writeq != NULL) { /* @@ -949,7 +1096,7 @@ int below_water; if (queue) { int qlen; - + qlen = gwbuf_length(queue); atomic_add(&dcb->writeqlen, qlen); dcb->writeq = gwbuf_append(dcb->writeq, queue); @@ -998,7 +1145,7 @@ int below_water; w = gw_write(dcb, GWBUF_DATA(queue), qlen); dcb->stats.n_writes++; ); - + if (w < 0) { saved_errno = errno; @@ -1006,7 +1153,7 @@ int below_water; if (LOG_IS_ENABLED(LOGFILE_DEBUG)) { - if (saved_errno == EPIPE) + if (saved_errno == EPIPE) { LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, @@ -1019,9 +1166,9 @@ int below_water; dcb->fd, saved_errno, strerror(saved_errno)))); - } + } } - + if (LOG_IS_ENABLED(LOGFILE_ERROR)) { if (saved_errno != EPIPE && @@ -1066,7 +1213,7 @@ int below_water; if (queue) { int qlen; - + qlen = gwbuf_length(queue); atomic_add(&dcb->writeqlen, qlen); dcb->stats.n_buffered++; @@ -1086,7 +1233,7 @@ int below_water; if (GWBUF_IS_TYPE_MYSQL(queue)) { uint8_t* data = GWBUF_DATA(queue); - + if (data[4] == 0x01) { dolog = false; @@ -1116,6 +1263,262 @@ int below_water; return 1; } +/** + * General purpose routine to write to an SSL enabled DCB + * + * @param dcb The DCB of the client + * @param ssl The SSL structure for this DCB + * @param queue Queue of buffers to write + * @return 0 on failure, 1 on success + */ +int +dcb_write_SSL(DCB *dcb, SSL* ssl, GWBUF *queue) +{ + int w; + int saved_errno = 0; + int below_water; + + below_water = (dcb->high_water && dcb->writeqlen < dcb->high_water) ? 1 : 0; + ss_dassert(queue != NULL); + + if (dcb->fd <= 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Write failed, dcb is %s.", + dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not writable"))); + return 0; + } + + /** + * SESSION_STATE_STOPPING means that one of the backends is closing + * the router session. Some backends may have not completed + * authentication yet and thus they have no information about router + * being closed. Session state is changed to SESSION_STATE_STOPPING + * before router's closeSession is called and that tells that DCB may + * still be writable. + */ + if (queue == NULL || + (dcb->state != DCB_STATE_ALLOC && + dcb->state != DCB_STATE_POLLING && + dcb->state != DCB_STATE_LISTENING && + dcb->state != DCB_STATE_NOPOLLING && + (dcb->session == NULL || + dcb->session->state != SESSION_STATE_STOPPING))) + { + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write aborted to dcb %p because " + "it is in state %s", + pthread_self(), + dcb->stats.n_buffered, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + //ss_dassert(false); + return 0; + } + + spinlock_acquire(&dcb->writeqlock); + + if (dcb->writeq != NULL) + { + /* + * We have some queued data, so add our data to + * the write queue and return. + * The assumption is that there will be an EPOLLOUT + * event to drain what is already queued. We are protected + * by the spinlock, which will also be acquired by the + * the routine that drains the queue data, so we should + * not have a race condition on the event. + */ + if (queue) + { + int qlen; + + qlen = gwbuf_length(queue); + atomic_add(&dcb->writeqlen, qlen); + dcb->writeq = gwbuf_append(dcb->writeq, queue); + dcb->stats.n_buffered++; + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Append to writequeue. %d writes " + "buffered for dcb %p in state %s fd %d", + pthread_self(), + dcb->stats.n_buffered, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + } + } + else + { + /* + * Loop over the buffer chain that has been passed to us + * from the reading side. + * Send as much of the data in that chain as possible and + * add any balance to the write queue. + */ + while (queue != NULL) + { + int qlen; +#if defined(FAKE_CODE) + if (dcb->dcb_role == DCB_ROLE_REQUEST_HANDLER && + dcb->session != NULL) + { + if (dcb_isclient(dcb) && fail_next_client_fd) { + dcb_fake_write_errno[dcb->fd] = 32; + dcb_fake_write_ev[dcb->fd] = 29; + fail_next_client_fd = false; + } else if (!dcb_isclient(dcb) && + fail_next_backend_fd) + { + dcb_fake_write_errno[dcb->fd] = 32; + dcb_fake_write_ev[dcb->fd] = 29; + fail_next_backend_fd = false; + } + } +#endif /* FAKE_CODE */ + qlen = GWBUF_LENGTH(queue); + GW_NOINTR_CALL( + w = gw_write_SSL(ssl, GWBUF_DATA(queue), qlen); + dcb->stats.n_writes++; + ); + + if (w < 0) + { + int ssl_errno = ERR_get_error(); + + if (LOG_IS_ENABLED(LOGFILE_DEBUG)) + { + switch(ssl_errno) + { + case SSL_ERROR_WANT_READ: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_READ", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + break; + case SSL_ERROR_WANT_WRITE: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_WRITE", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + break; + default: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error %d", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd,ssl_errno))); + } + } + + if (LOG_IS_ENABLED(LOGFILE_ERROR)) + { + if (ssl_errno != 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Write to dcb %p in " + "state %s fd %d failed due " + "SSL error %d", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno))); + } + } + break; + } + /* + * Pull the number of bytes we have written from + * queue with have. + */ + queue = gwbuf_consume(queue, w); + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Wrote %d Bytes to dcb %p in " + "state %s fd %d", + pthread_self(), + w, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + } /*< while (queue != NULL) */ + /*< + * What wasn't successfully written is stored to write queue + * for suspended write. + */ + dcb->writeq = queue; + + if (queue) + { + int qlen; + + qlen = gwbuf_length(queue); + atomic_add(&dcb->writeqlen, qlen); + dcb->stats.n_buffered++; + } + } /* if (dcb->writeq) */ + + if (saved_errno != 0 && + queue != NULL && + saved_errno != EAGAIN && + saved_errno != EWOULDBLOCK) + { + bool dolog = true; + + /** + * Do not log if writing COM_QUIT to backend failed. + */ + if (GWBUF_IS_TYPE_MYSQL(queue)) + { + uint8_t* data = GWBUF_DATA(queue); + + if (data[4] == 0x01) + { + dolog = false; + } + } + if (dolog) + { + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Writing to %s socket failed due %d, %s.", + pthread_self(), + dcb_isclient(dcb) ? "client" : "backend server", + saved_errno, + strerror(saved_errno)))); + } + spinlock_release(&dcb->writeqlock); + return 0; + } + spinlock_release(&dcb->writeqlock); + + if (dcb->high_water && dcb->writeqlen > dcb->high_water && below_water) + { + atomic_add(&dcb->stats.n_high_water, 1); + dcb_call_callback(dcb, DCB_REASON_HIGH_WATER); + } + + return 1; +} + /** * Drain the write queue of a DCB. This is called as part of the EPOLLOUT handling * of a socket and will try to send any buffered data from the write queue @@ -1208,6 +1611,85 @@ int above_water; return n; } + +/** + * Drain the write queue of a DCB. This is called as part of the EPOLLOUT handling + * of a socket and will try to send any buffered data from the write queue + * up until the point the write would block. + * + * @param dcb DCB to drain the write queue of + * @return The number of bytes written + */ +int +dcb_drain_writeq_SSL(DCB *dcb, SSL* ssl) +{ + int n = 0; + int w; + int saved_errno = 0; + int above_water; + + above_water = (dcb->low_water && dcb->writeqlen > dcb->low_water) ? 1 : 0; + + spinlock_acquire(&dcb->writeqlock); + + if (dcb->writeq) + { + int len; + /* + * Loop over the buffer chain in the pending writeq + * Send as much of the data in that chain as possible and + * leave any balance on the write queue. + */ + while (dcb->writeq != NULL) + { + len = GWBUF_LENGTH(dcb->writeq); + GW_NOINTR_CALL(w = gw_write_SSL(ssl, GWBUF_DATA(dcb->writeq), len);); + + if (w < 0) + { + int ssl_errno = ERR_get_error(); + + if(ssl_errno == SSL_ERROR_WANT_WRITE || + ssl_errno == SSL_ERROR_WANT_ACCEPT || + ssl_errno == SSL_ERROR_WANT_READ) + { + break; + } + + skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Write to dcb %p " + "in state %s fd %d failed: %s", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ERR_error_string(ssl_errno,NULL)); + break; + } + /* + * Pull the number of bytes we have written from + * queue with have. + */ + dcb->writeq = gwbuf_consume(dcb->writeq, w); + n += w; + } + } + spinlock_release(&dcb->writeqlock); + atomic_add(&dcb->writeqlen, -n); + + /* The write queue has drained, potentially need to call a callback function */ + if (dcb->writeq == NULL) + dcb_call_callback(dcb, DCB_REASON_DRAINED); + + if (above_water && dcb->writeqlen < dcb->low_water) + { + atomic_add(&dcb->stats.n_low_water, 1); + dcb_call_callback(dcb, DCB_REASON_LOW_WATER); + } + + return n; +} + /** * Removes dcb from poll set, and adds it to zombies list. As a consequense, * dcb first moves to DCB_STATE_NOPOLLING, and then to DCB_STATE_ZOMBIE state. @@ -1792,6 +2274,29 @@ static bool dcb_set_state_nomutex( return succp; } +/** + * Write data to a DCB + * + * @param ssl The SSL to write the buffer to + * @param buf Buffer to write + * @param nbytes Number of bytes to write + * @return Number of written bytes + */ +int +gw_write_SSL(SSL* ssl, const void *buf, size_t nbytes) +{ + int w = 0; + int fd = SSL_get_fd(ssl); + + if (fd > 0) + { + w = SSL_write(ssl, buf, nbytes); + } + return w; +} + + + /** * Write data to a DCB * diff --git a/server/include/dcb.h b/server/include/dcb.h index 7eedfbec3..38ebdc299 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #define ERRHANDLE @@ -337,7 +340,10 @@ bool dcb_set_state(DCB* dcb, dcb_state_t new_state, dcb_state_t* old_state); void dcb_call_foreach (struct server* server, DCB_REASON reason); size_t dcb_get_session_id(DCB* dcb); bool dcb_get_ses_log_info(DCB* dcb, size_t* sesid, int* enabled_logs); - +int gw_write_SSL(SSL* ssl, const void *buf, size_t nbytes); +int dcb_write_SSL(DCB *dcb, SSL* ssl, GWBUF *queue); +int dcb_read_SSL(DCB *dcb,SSL* ssl,GWBUF **head); +int dcb_drain_writeq_SSL(DCB *dcb, SSL* ssl); /** diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index 87dbc50ee..6c841d9c9 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -297,6 +297,7 @@ typedef struct { * handshake */ unsigned int charset; /*< MySQL character set at connect time */ SSL* ssl; /*< SSL struct for client connection */ + bool use_ssl; #if defined(SS_DEBUG) skygw_chk_t protocol_chk_tail; #endif diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 387b59aa0..f1aa0aa94 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -37,7 +37,7 @@ * 09/09/2014 Massimiliano Pinto Added: 777 permission for socket path * 13/10/2014 Massimiliano Pinto Added: dbname authentication check * 10/11/2014 Massimiliano Pinto Added: client charset added to protocol struct - * + * 29/05/2015 Markus Makela Added SSL support */ #include #include @@ -69,7 +69,9 @@ static int gw_MySQLWrite_client(DCB *dcb, GWBUF *queue); static int gw_error_client_event(DCB *dcb); static int gw_client_close(DCB *dcb); static int gw_client_hangup_event(DCB *dcb); - +int gw_read_client_event_SSL(DCB* dcb); +int gw_MySQLWrite_client_SSL(DCB *dcb, GWBUF *queue); +int gw_write_client_event_SSL(DCB *dcb); int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message); int MySQLSendHandshake(DCB* dcb); static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue); @@ -464,6 +466,8 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { &protocol->client_capabilities); */ + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_EXCHANGE_DONE) + goto ssl_hs_done; ssl = protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL; @@ -497,48 +501,19 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { protocol->ssl = SSL_new(protocol->owner_dcb->service->ctx); SSL_set_fd(protocol->ssl,dcb->fd); protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; - printf("%s\n",SSL_get_version(protocol->ssl)); - int errnum,rval; - char errbuf[1024]; - switch((rval = SSL_accept(protocol->ssl))) + if(do_ssl_accept(protocol) < 0) { - case 0: - errnum = SSL_get_error(protocol->ssl,rval); - ERR_error_string(errnum,errbuf); - skygw_log_write_flush(LOGFILE_ERROR,"SSL_accept: %s",errbuf); - ERR_print_errors_fp(stdout); - ERR_error_string(errnum,errbuf); - printf("%s\n",errbuf); - fflush(stdout); - break; - case 1: - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; - break; - - default: - errnum = SSL_get_error(protocol->ssl,rval); - if(errnum == SSL_ERROR_WANT_READ) - { - /** Not all of the data has been read. Go back to the poll - queue and wait for more.*/ - protocol->protocol_auth_state = MYSQL_AUTH_SSL_RECV; - return 0; - } - else - { - ERR_print_errors_fp(stdout); - ERR_error_string(errnum,errbuf); - printf("%s\n",errbuf); - fflush(stdout); - skygw_log_write_flush(LOGFILE_ERROR,"Error: Fatal error in SSL_accept: %s",errbuf); - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_ERR; - } - break; - + return 1; + } + else + { + return 0; } } + ssl_hs_done: + username = get_username_from_auth(username, client_auth_packet); if (username == NULL) @@ -645,6 +620,65 @@ gw_MySQLWrite_client(DCB *dcb, GWBUF *queue) return dcb_write(dcb, queue); } + +/** + * Write function for client DCB: writes data from MaxScale to Client + * + * @param dcb The DCB of the client + * @param queue Queue of buffers to write + */ +int +gw_MySQLWrite_client_SSL(DCB *dcb, GWBUF *queue) +{ + MySQLProtocol *protocol = NULL; + CHK_DCB(dcb); + protocol = DCB_PROTOCOL(dcb, MySQLProtocol); + CHK_PROTOCOL(protocol); + return dcb_write_SSL(dcb, protocol->ssl, queue); +} + + +int gw_read_client_event_SSL( +DCB* dcb) +{ + SESSION *session = NULL; + ROUTER_OBJECT *router = NULL; + ROUTER *router_instance = NULL; + void *rsession = NULL; + MySQLProtocol *protocol = NULL; + GWBUF *read_buffer = NULL; + int rc = 0; + int nbytes_read = 0; + uint8_t cap = 0; + bool stmt_input = false; /*< router input type */ + + CHK_DCB(dcb); + protocol = DCB_PROTOCOL(dcb, MySQLProtocol); + CHK_PROTOCOL(protocol); + + + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) + { + if(do_ssl_accept(protocol) == 1) + { + spinlock_acquire(&protocol->protocol_lock); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; + spinlock_release(&protocol->protocol_lock); + + spinlock_acquire(&dcb->authlock); + dcb->func.read = gw_read_client_event_SSL; + dcb->func.write = gw_MySQLWrite_client_SSL; + dcb->func.write_ready = gw_write_client_event; + spinlock_release(&dcb->authlock); + } + goto return_rc; + } + + return_rc: + + return rc; +} + /** * Client read event triggered by EPOLLIN * @@ -668,13 +702,36 @@ int gw_read_client_event( CHK_DCB(dcb); protocol = DCB_PROTOCOL(dcb, MySQLProtocol); CHK_PROTOCOL(protocol); - if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_RECV) + + /** Let the OpenSSL API do the reading from the socket */ + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) { - goto do_auth; + if(do_ssl_accept(protocol) == 1) + { + spinlock_acquire(&protocol->protocol_lock); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; + protocol->use_ssl = true; + spinlock_release(&protocol->protocol_lock); + + spinlock_acquire(&dcb->authlock); + //dcb->func.read = gw_read_client_event_SSL; + //dcb->func.write = gw_MySQLWrite_client_SSL; + //dcb->func.write_ready = gw_write_client_event_SSL; + spinlock_release(&dcb->authlock); + } + goto return_rc; } - rc = dcb_read(dcb, &read_buffer); - - + + + if(protocol->use_ssl) + { + rc = dcb_read_SSL(dcb,protocol->ssl, &read_buffer); + } + else + { + rc = dcb_read(dcb, &read_buffer); + } + if (rc < 0) { dcb_close(dcb); @@ -811,7 +868,7 @@ int gw_read_client_event( } } - do_auth: + /** * Now there should be at least one complete mysql packet in read_buffer. */ @@ -823,7 +880,7 @@ int gw_read_client_event( auth_val = gw_mysql_do_authentication(dcb, read_buffer); - if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_RECV) + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) break; if (auth_val == 0) @@ -919,12 +976,9 @@ int gw_read_client_event( } break; - case MYSQL_AUTH_SSL_RECV: + case MYSQL_AUTH_SSL_EXCHANGE_DONE: { - if(do_ssl_accept(protocol) == 1) - { - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; - } + protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; } break; @@ -1047,6 +1101,64 @@ return_rc: return rc; } + +/////////////////////////////////////////////// +// client write event to Client triggered by EPOLLOUT +////////////////////////////////////////////// +/** + * @node Client's fd became writable, and EPOLLOUT event + * arrived. As a consequence, client input buffer (writeq) is flushed. + * + * Parameters: + * @param dcb - in, use + * client dcb + * + * @return constantly 1 + * + * + * @details (write detailed description here) + * + */ +int gw_write_client_event(DCB *dcb) +{ + MySQLProtocol *protocol = NULL; + + CHK_DCB(dcb); + + ss_dassert(dcb->state != DCB_STATE_DISCONNECTED); + + if (dcb == NULL) { + goto return_1; + } + + if (dcb->state == DCB_STATE_DISCONNECTED) { + goto return_1; + } + + if (dcb->protocol == NULL) { + goto return_1; + } + protocol = (MySQLProtocol *)dcb->protocol; + CHK_PROTOCOL(protocol); + + if (protocol->protocol_auth_state == MYSQL_IDLE) + { + dcb_drain_writeq(dcb); + goto return_1; + } + +return_1: +#if defined(SS_DEBUG) + if (dcb->state == DCB_STATE_POLLING || + dcb->state == DCB_STATE_NOPOLLING || + dcb->state == DCB_STATE_ZOMBIE) + { + CHK_PROTOCOL(protocol); + } +#endif + return 1; +} + /////////////////////////////////////////////// // client write event to Client triggered by EPOLLOUT ////////////////////////////////////////////// @@ -1064,7 +1176,7 @@ return_rc: * @details (write detailed description here) * */ -int gw_write_client_event(DCB *dcb) +int gw_write_client_event_SSL(DCB *dcb) { MySQLProtocol *protocol = NULL; @@ -1088,7 +1200,7 @@ int gw_write_client_event(DCB *dcb) if (protocol->protocol_auth_state == MYSQL_IDLE) { - dcb_drain_writeq(dcb); + dcb_drain_writeq_SSL(dcb,protocol->ssl); goto return_1; } @@ -1776,16 +1888,17 @@ int do_ssl_accept(MySQLProtocol* protocol) { case 0: errnum = SSL_get_error(protocol->ssl,rval); - ERR_error_string(errnum,errbuf); - skygw_log_write_flush(LOGFILE_ERROR,"SSL_accept: %s",errbuf); - ERR_print_errors_fp(stdout); - ERR_error_string(errnum,errbuf); - printf("%s\n",errbuf); - fflush(stdout); + skygw_log_write_flush(LT,"SSL_accept ongoing for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); break; case 1: protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; rval = 1; + protocol->use_ssl = true; + skygw_log_write_flush(LT,"SSL_accept done for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); break; default: @@ -1795,14 +1908,18 @@ int do_ssl_accept(MySQLProtocol* protocol) /** Not all of the data has been read. Go back to the poll queue and wait for more.*/ rval = 0; + protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; + skygw_log_write_flush(LT,"SSL_accept partially done for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); } else { - ERR_print_errors_fp(stdout); - ERR_error_string(errnum,errbuf); - printf("%s\n",errbuf); - fflush(stdout); - skygw_log_write_flush(LOGFILE_ERROR,"Error: Fatal error in SSL_accept: %s",errbuf); + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_accept for %s@%s: %s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote, + ERR_error_string(errnum,NULL)); protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_ERR; rval = -1; } diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index ffd72c034..654d24692 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -137,7 +137,10 @@ void mysql_protocol_done ( goto retblock; } scmd = p->protocol_cmd_history; - + if(p->ssl) + { + SSL_free(p->ssl); + } while (scmd != NULL) { scmd2 = scmd->scom_next; From a2768955e75134914198bf956dc1a44cefd03b75 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 1 Jun 2015 13:50:22 +0300 Subject: [PATCH 103/273] SSL handshake now successfully completes when a client connects with SSL enabled. --- Documentation/Reference/MaxScale-and-SSL.md | 13 + server/core/dcb.c | 24 +- server/core/gateway.c | 37 +- server/core/service.c | 2 +- .../include/mysql_client_server_protocol.h | 9 +- server/modules/protocol/mysql_client.c | 347 ++++++++++-------- 6 files changed, 253 insertions(+), 179 deletions(-) create mode 100644 Documentation/Reference/MaxScale-and-SSL.md diff --git a/Documentation/Reference/MaxScale-and-SSL.md b/Documentation/Reference/MaxScale-and-SSL.md new file mode 100644 index 000000000..4c583793b --- /dev/null +++ b/Documentation/Reference/MaxScale-and-SSL.md @@ -0,0 +1,13 @@ +# MaxScale and SSL + +MaxScale supports client side SSL connections. Enabling is done on a per service basis and each service has its own set of certificates. + +## SSL Options + +Here are the options which relate to SSL and certificates. +Parameter|Values|Description +---------------------------- +ssl | disabled, enabled, required |`disable` disables SSL, `enabled` enables SSL for client connections but still allows non-SSL connections and `required` requires SSL from all client connections. With the `required` option, client connections that do not use SSL will be rejected. +ssl_cert | |Path to server certificate +ssl_key | |Path to server private key +ssl_ca_cert | |Path to Certificate Authority file diff --git a/server/core/dcb.c b/server/core/dcb.c index ba2028de2..562929774 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -941,18 +941,12 @@ int dcb_read_SSL( /** Handle closed client socket */ if (dcb_isclient(dcb)) { - char c; - int l_errno = 0; + char c = 0; int r = -1; /* try to read 1 byte, without consuming the socket buffer */ - r = recv(dcb->fd, &c, sizeof(char), MSG_PEEK); - l_errno = errno; - - if (r <= 0 && - l_errno != EAGAIN && - l_errno != EWOULDBLOCK && - l_errno != 0) + r = SSL_peek(ssl, &c, sizeof(char)); + if (r <= 0) { n = -1; goto return_n; @@ -989,13 +983,15 @@ int dcb_read_SSL( n = -1; goto return_n; } - GW_NOINTR_CALL(n = SSL_read(ssl, GWBUF_DATA(buffer), bufsize); - dcb->stats.n_reads++); + n = SSL_read(ssl, GWBUF_DATA(buffer), bufsize); + dcb->stats.n_reads++; + int ssl_errno = 0; if (n <= 0) { - int ssl_errno = ERR_get_error(); - if(ssl_errno != SSL_ERROR_WANT_READ) + ssl_errno = ERR_get_error(); + + if(ssl_errno != SSL_ERROR_WANT_READ && ssl_errno != SSL_ERROR_NONE) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -1023,6 +1019,8 @@ int dcb_read_SSL( dcb->fd))); /*< Append read data to the gwbuf */ *head = gwbuf_append(*head, buffer); + if(ssl_errno == SSL_ERROR_WANT_READ || ssl_errno == SSL_ERROR_NONE) + break; } /*< while (true) */ return_n: return n; diff --git a/server/core/gateway.c b/server/core/gateway.c index 9845713e1..0b07f507b 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -196,7 +196,9 @@ static bool resolve_maxscale_conf_fname( static char* check_dir_access(char* dirname,bool,bool); static int set_user(); - +static void maxscale_ssl_lock(int mode,int n,const char* file, int line); +static unsigned long maxscale_ssl_id(); +static SPINLOCK* ssl_locks; /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. @@ -1370,7 +1372,23 @@ int main(int argc, char **argv) rc = MAXSCALE_INTERNALERROR; goto return_main; } + + /** OpenSSL initialization */ + SSL_library_init(); + SSL_load_error_strings(); + int n_locks = CRYPTO_num_locks(); + if((ssl_locks = malloc(n_locks*sizeof(SPINLOCK))) == NULL) + { + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + + for(i = 0;ictx,SSL_VERIFY_PEER,NULL); /* Set the verification depth to 1 */ - SSL_CTX_set_verify_depth(service->ctx,10); + SSL_CTX_set_verify_depth(service->ctx,1); service->ssl_init_done = true; } return 0; diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index 6c841d9c9..94db6e664 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -99,10 +99,11 @@ typedef enum { MYSQL_AUTH_RECV, MYSQL_AUTH_FAILED, MYSQL_HANDSHAKE_FAILED, - MYSQL_AUTH_SSL_REQ, /*< client requested SSL */ - MYSQL_AUTH_SSL_EXCHANGE_DONE, /*< SSL handshake done */ - MYSQL_AUTH_SSL_EXCHANGE_ERR, /*< SSL handshake failure */ - MYSQL_AUTH_SSL_RECV, /*< */ + MYSQL_AUTH_SSL_REQ, /*< client requested SSL but SSL_accept hasn't beed called */ + MYSQL_AUTH_SSL_HANDSHAKE_DONE, /*< SSL handshake has been fully completed */ + MYSQL_AUTH_SSL_HANDSHAKE_FAILED, /*< SSL handshake failed for any reason */ + MYSQL_AUTH_SSL_HANDSHAKE_ONGOING, /*< SSL_accept has been called but the + * SSL handshake hasn't been completed */ MYSQL_IDLE } mysql_auth_state_t; diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index f1aa0aa94..8ee4b5687 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -117,8 +117,6 @@ version() void ModuleInit() { - SSL_library_init(); - SSL_load_error_strings(); } /** @@ -466,7 +464,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { &protocol->client_capabilities); */ - if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_EXCHANGE_DONE) + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_DONE) goto ssl_hs_done; ssl = protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL; @@ -637,48 +635,6 @@ gw_MySQLWrite_client_SSL(DCB *dcb, GWBUF *queue) return dcb_write_SSL(dcb, protocol->ssl, queue); } - -int gw_read_client_event_SSL( -DCB* dcb) -{ - SESSION *session = NULL; - ROUTER_OBJECT *router = NULL; - ROUTER *router_instance = NULL; - void *rsession = NULL; - MySQLProtocol *protocol = NULL; - GWBUF *read_buffer = NULL; - int rc = 0; - int nbytes_read = 0; - uint8_t cap = 0; - bool stmt_input = false; /*< router input type */ - - CHK_DCB(dcb); - protocol = DCB_PROTOCOL(dcb, MySQLProtocol); - CHK_PROTOCOL(protocol); - - - if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) - { - if(do_ssl_accept(protocol) == 1) - { - spinlock_acquire(&protocol->protocol_lock); - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; - spinlock_release(&protocol->protocol_lock); - - spinlock_acquire(&dcb->authlock); - dcb->func.read = gw_read_client_event_SSL; - dcb->func.write = gw_MySQLWrite_client_SSL; - dcb->func.write_ready = gw_write_client_event; - spinlock_release(&dcb->authlock); - } - goto return_rc; - } - - return_rc: - - return rc; -} - /** * Client read event triggered by EPOLLIN * @@ -703,26 +659,26 @@ int gw_read_client_event( protocol = DCB_PROTOCOL(dcb, MySQLProtocol); CHK_PROTOCOL(protocol); - /** Let the OpenSSL API do the reading from the socket */ - if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_ONGOING || + protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) { - if(do_ssl_accept(protocol) == 1) + switch(do_ssl_accept(protocol)) { - spinlock_acquire(&protocol->protocol_lock); - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; - protocol->use_ssl = true; - spinlock_release(&protocol->protocol_lock); - - spinlock_acquire(&dcb->authlock); - //dcb->func.read = gw_read_client_event_SSL; - //dcb->func.write = gw_MySQLWrite_client_SSL; - //dcb->func.write_ready = gw_write_client_event_SSL; - spinlock_release(&dcb->authlock); + case 0: + return 0; + break; + case 1: + return 0; + break; + case -1: + return 1; + break; + default: + return 1; + break; } - goto return_rc; } - if(protocol->use_ssl) { rc = dcb_read_SSL(dcb,protocol->ssl, &read_buffer); @@ -880,8 +836,13 @@ int gw_read_client_event( auth_val = gw_mysql_do_authentication(dcb, read_buffer); - if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ || + protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_ONGOING || + protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_DONE || + protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_FAILED) + { break; + } if (auth_val == 0) { @@ -976,9 +937,103 @@ int gw_read_client_event( } break; - case MYSQL_AUTH_SSL_EXCHANGE_DONE: + case MYSQL_AUTH_SSL_HANDSHAKE_DONE: { - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; + int auth_val; + + auth_val = gw_mysql_do_authentication(dcb, read_buffer); + + + if (auth_val == 0) + { + SESSION *session; + + protocol->protocol_auth_state = MYSQL_AUTH_RECV; + /** + * Create session, and a router session for it. + * If successful, there will be backend connection(s) + * after this point. + */ + session = session_alloc(dcb->service, dcb); + + if (session != NULL) + { + CHK_SESSION(session); + ss_dassert(session->state != SESSION_STATE_ALLOC); + + protocol->protocol_auth_state = MYSQL_IDLE; + /** + * Send an AUTH_OK packet to the client, + * packet sequence is # 2 + */ + mysql_send_ok(dcb, 2, 0, NULL); + } + else + { + protocol->protocol_auth_state = MYSQL_AUTH_FAILED; + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_read_client_event] session " + "creation failed. fd %d, " + "state = MYSQL_AUTH_FAILED.", + pthread_self(), + protocol->owner_dcb->fd))); + + /** Send ERR 1045 to client */ + mysql_send_auth_error( + dcb, + 2, + 0, + "failed to create new session"); + + dcb_close(dcb); + } + } + else + { + char* fail_str = NULL; + + protocol->protocol_auth_state = MYSQL_AUTH_FAILED; + + if (auth_val == 2) { + /** Send error 1049 to client */ + int message_len = 25 + MYSQL_DATABASE_MAXLEN; + + fail_str = calloc(1, message_len+1); + snprintf(fail_str, message_len, "Unknown database '%s'", + (char*)((MYSQL_session *)dcb->data)->db); + + modutil_send_mysql_err_packet(dcb, 2, 0, 1049, "42000", fail_str); + } else { + /** Send error 1045 to client */ + fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user, + dcb->remote, + (char*)((MYSQL_session *)dcb->data)->client_sha1, + (char*)((MYSQL_session *)dcb->data)->db); + modutil_send_mysql_err_packet(dcb, 2, 0, 1045, "28000", fail_str); + } + if (fail_str) + free(fail_str); + + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_read_client_event] after " + "gw_mysql_do_authentication, fd %d, " + "state = MYSQL_AUTH_FAILED.", + protocol->owner_dcb->fd, + pthread_self()))); + /** + * Release MYSQL_session since it is not used anymore. + */ + if (!DCB_IS_CLONE(dcb)) + { + free(dcb->data); + } + dcb->data = NULL; + + dcb_close(dcb); + } + read_buffer = gwbuf_consume(read_buffer, nbytes_read); } break; @@ -1821,111 +1876,83 @@ return_rc: return rc; } - /** - * Create a character array including the query string. - * GWBUF given as input includes either one complete or partial query. - * Length of buffer is at most the query length+4 (length of packet header). + * Do the SSL authentication handshake. + * This functions + * @param protocol + * @return */ -#if defined(NOT_USED) -static char* gw_get_or_create_querystr ( - void* data, - bool* new_allocation) -{ - GWBUF* buf = (GWBUF *)data; - size_t buflen; /*< first gw buffer data length */ - size_t packetlen; /*< length of mysql packet */ - size_t querylen; /*< total buffer length- */ - size_t nbytes_copied; - char* startpos; /*< first byte of query in gw buffer */ - char* str; /*< resulting query string */ - - CHK_GWBUF(buf); - packetlen = MYSQL_GET_PACKET_LEN((uint8_t *)GWBUF_DATA(buf)); - str = (char *)malloc(packetlen); /*< leave space for terminating null */ - - if (str == NULL) - { - goto return_str; - } - *new_allocation = true; - /** - * First buffer includes 4 bytes header and a type indicator byte. - */ - buflen = GWBUF_LENGTH(buf); - querylen = packetlen-1; - ss_dassert(buflen<=querylen+5); /*< 5 == header+type indicator */ - startpos = (char *)GWBUF_DATA(buf)+5; - nbytes_copied = MIN(querylen, buflen-5); - memcpy(str, startpos, nbytes_copied); - memset(&str[querylen-1], 0, 1); - buf = gwbuf_consume(buf, querylen-1); - - /** - * In case of multi-packet statement whole buffer consists of query - * string. - */ - while (buf != NULL) - { - buflen = GWBUF_LENGTH(buf); - memcpy(str+nbytes_copied, GWBUF_DATA(buf), buflen); - nbytes_copied += buflen; - buf = gwbuf_consume(buf, buflen); - } - ss_dassert(str[querylen-1] == 0); - -return_str: - return str; -} -#endif - int do_ssl_accept(MySQLProtocol* protocol) { int rval,errnum; char errbuf[2014]; - - switch((rval = SSL_accept(protocol->ssl))) - { - case 0: - errnum = SSL_get_error(protocol->ssl,rval); - skygw_log_write_flush(LT,"SSL_accept ongoing for %s@%s", - protocol->owner_dcb->user, - protocol->owner_dcb->remote); - break; - case 1: - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_DONE; - rval = 1; - protocol->use_ssl = true; - skygw_log_write_flush(LT,"SSL_accept done for %s@%s", - protocol->owner_dcb->user, - protocol->owner_dcb->remote); - break; + DCB* dcb; - default: - errnum = SSL_get_error(protocol->ssl,rval); - if(errnum == SSL_ERROR_WANT_READ) - { - /** Not all of the data has been read. Go back to the poll - queue and wait for more.*/ - rval = 0; - protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; - skygw_log_write_flush(LT,"SSL_accept partially done for %s@%s", - protocol->owner_dcb->user, - protocol->owner_dcb->remote); - } - else - { - skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept for %s@%s: %s", - protocol->owner_dcb->user, - protocol->owner_dcb->remote, - ERR_error_string(errnum,NULL)); - protocol->protocol_auth_state = MYSQL_AUTH_SSL_EXCHANGE_ERR; - rval = -1; - } - break; + rval = SSL_accept(protocol->ssl); - } + switch(rval) + { + case 0: + errnum = SSL_get_error(protocol->ssl,rval); + skygw_log_write_flush(LT,"SSL_accept shutdown for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); + return -1; + break; + case 1: + spinlock_acquire(&protocol->protocol_lock); + dcb = protocol->owner_dcb; + protocol->protocol_auth_state = MYSQL_AUTH_SSL_HANDSHAKE_DONE; + protocol->use_ssl = true; + spinlock_release(&protocol->protocol_lock); + + spinlock_acquire(&dcb->authlock); + dcb->func.write = gw_MySQLWrite_client_SSL; + dcb->func.write_ready = gw_write_client_event_SSL; + spinlock_release(&dcb->authlock); + + rval = 1; + + skygw_log_write_flush(LT,"SSL_accept done for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); + break; + + case -1: + errnum = SSL_get_error(protocol->ssl,rval); + + if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE || + errnum == SSL_ERROR_WANT_X509_LOOKUP) + { + /** Not all of the data has been read. Go back to the poll + queue and wait for more.*/ + + rval = 0; + skygw_log_write_flush(LT,"SSL_accept ongoing for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); + } + else + { + spinlock_acquire(&protocol->protocol_lock); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_HANDSHAKE_FAILED; + spinlock_release(&protocol->protocol_lock); + rval = -1; + + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_accept for %s@%s: %s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote, + ERR_error_string(errnum,NULL)); + } + break; + + default: + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_accept, returned value was %d.", + rval); + break; + } return rval; } \ No newline at end of file From 4d5291c26329ebee8dd04a51bf0468a5f162727a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 1 Jun 2015 13:58:26 +0300 Subject: [PATCH 104/273] Fixed wrong packet sequence number causing a disconnect from the client. --- server/modules/protocol/mysql_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 8ee4b5687..2d2633b90 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -966,7 +966,7 @@ int gw_read_client_event( * Send an AUTH_OK packet to the client, * packet sequence is # 2 */ - mysql_send_ok(dcb, 2, 0, NULL); + mysql_send_ok(dcb, 3, 0, NULL); } else { From d7232d8b6ecc2fab121b1326a7f489b84bb0722d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 1 Jun 2015 20:51:26 +0300 Subject: [PATCH 105/273] Moved SSL structure to the DCB instead of the MySQL protocol. This allows for non-MySQL SSL connections. --- server/core/dcb.c | 139 ++++++++++++++++-- server/core/service.c | 6 +- server/include/dcb.h | 9 +- .../include/mysql_client_server_protocol.h | 1 - server/modules/protocol/mysql_client.c | 64 +++----- server/modules/protocol/mysql_common.c | 5 +- 6 files changed, 164 insertions(+), 60 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 562929774..2948dae2e 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -73,6 +73,8 @@ #include #include +#include "mysql_client_server_protocol.h" + /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; @@ -433,7 +435,8 @@ DCB_CALLBACK *cb; free(cb); } spinlock_release(&dcb->cb_lock); - + if(dcb->ssl) + SSL_free(dcb->ssl); bitmask_free(&dcb->memdata.bitmask); free(dcb); } @@ -894,7 +897,6 @@ return_n: */ int dcb_read_SSL( DCB *dcb, - SSL* ssl, GWBUF **head) { GWBUF *buffer = NULL; @@ -945,7 +947,7 @@ int dcb_read_SSL( int r = -1; /* try to read 1 byte, without consuming the socket buffer */ - r = SSL_peek(ssl, &c, sizeof(char)); + r = SSL_peek(dcb->ssl, &c, sizeof(char)); if (r <= 0) { n = -1; @@ -983,11 +985,18 @@ int dcb_read_SSL( n = -1; goto return_n; } - n = SSL_read(ssl, GWBUF_DATA(buffer), bufsize); - dcb->stats.n_reads++; + + int npending; + n = 0; + do + { + n += SSL_read(dcb->ssl, GWBUF_DATA(buffer), bufsize); + dcb->stats.n_reads++; + }while((npending = SSL_pending(dcb->ssl)) > 0); int ssl_errno = 0; - if (n <= 0) + + if (n <= 0) { ssl_errno = ERR_get_error(); @@ -1006,6 +1015,15 @@ int dcb_read_SSL( goto return_n; } } + + if(n < b) + { + gwbuf_rtrim(buffer,b - n); + ss_dassert(gwbuf_length(buffer) == n); + LOGIF(LD,(skygw_log_write(LD,"[%lu] SSL: Truncated buffer to correct size from %d to %d bytes.\n", + b,gwbuf_length(buffer)))); + } + nread += n; LOGIF(LD, (skygw_log_write( @@ -1019,7 +1037,8 @@ int dcb_read_SSL( dcb->fd))); /*< Append read data to the gwbuf */ *head = gwbuf_append(*head, buffer); - if(ssl_errno == SSL_ERROR_WANT_READ || ssl_errno == SSL_ERROR_NONE) + if(ssl_errno == SSL_ERROR_WANT_READ || ssl_errno == SSL_ERROR_NONE || + ssl_errno == SSL_ERROR_WANT_X509_LOOKUP || SSL_ERROR_WANT_WRITE) break; } /*< while (true) */ return_n: @@ -1270,7 +1289,7 @@ int below_water; * @return 0 on failure, 1 on success */ int -dcb_write_SSL(DCB *dcb, SSL* ssl, GWBUF *queue) +dcb_write_SSL(DCB *dcb, GWBUF *queue) { int w; int saved_errno = 0; @@ -1379,7 +1398,7 @@ dcb_write_SSL(DCB *dcb, SSL* ssl, GWBUF *queue) #endif /* FAKE_CODE */ qlen = GWBUF_LENGTH(queue); GW_NOINTR_CALL( - w = gw_write_SSL(ssl, GWBUF_DATA(queue), qlen); + w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), qlen); dcb->stats.n_writes++; ); @@ -1619,7 +1638,7 @@ int above_water; * @return The number of bytes written */ int -dcb_drain_writeq_SSL(DCB *dcb, SSL* ssl) +dcb_drain_writeq_SSL(DCB *dcb) { int n = 0; int w; @@ -1641,7 +1660,7 @@ dcb_drain_writeq_SSL(DCB *dcb, SSL* ssl) while (dcb->writeq != NULL) { len = GWBUF_LENGTH(dcb->writeq); - GW_NOINTR_CALL(w = gw_write_SSL(ssl, GWBUF_DATA(dcb->writeq), len);); + GW_NOINTR_CALL(w = gw_write_SSL(dcb->ssl, GWBUF_DATA(dcb->writeq), len);); if (w < 0) { @@ -2728,3 +2747,101 @@ DCB *ptr; spinlock_release(&dcbspin); return rval; } + +/** + * Create the SSL structure for this DCB. + * This function creates the SSL structure for the given SSL context. This context + * should be the service's context + * @param dcb + * @param context + * @return + */ +int dcb_create_SSL(DCB* dcb) +{ + + if(serviceInitSSL(dcb->service) != 0) + { + return -1; + } + + if((dcb->ssl = SSL_new(dcb->service->ctx)) == NULL) + { + skygw_log_write(LE,"Error: Failed to initialize SSL connection."); + return -1; + } + + if(SSL_set_fd(dcb->ssl,dcb->fd) == 0) + { + skygw_log_write(LE,"Error: Failed to set file descriptor for SSL connection."); + return -1; + } + + return 0; +} +/** + * Accept a SSL connection and do the SSL authentication handshake. + * This function accepts a client connection to a DCB. It assumes that the SSL + * structure has the underlying method of communication set and this method is ready + * for usage. It then proceeds with the SSL handshake and stops only if an error + * occurs or the client has not yet written enough data to complete the handshake. + * @param dcb DCB which should accept the SSL connection + * @return 1 if the handshake was successfully completed, 0 if the handshake is + * still ongoing and another call to dcb_SSL_accept should be made or -1 if an + * error occurred during the handshake and the connection should be terminated. + */ +int dcb_accept_SSL(DCB* dcb) +{ + int rval,errnum; + + rval = SSL_accept(dcb->ssl); + + switch(rval) + { + case 0: + errnum = SSL_get_error(dcb->ssl,rval); + LOGIF(LD,(skygw_log_write_flush(LD,"SSL_accept shutdown for %s@%s", + dcb->user, + dcb->remote))); + return -1; + break; + case 1: + rval = 1; + LOGIF(LD,(skygw_log_write_flush(LD,"SSL_accept done for %s@%s", + dcb->user, + dcb->remote))); + break; + + case -1: + errnum = SSL_get_error(dcb->ssl,rval); + + if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE || + errnum == SSL_ERROR_WANT_X509_LOOKUP) + { + /** Not all of the data has been read. Go back to the poll + queue and wait for more.*/ + + rval = 0; + LOGIF(LD,(skygw_log_write_flush(LD,"SSL_accept ongoing for %s@%s", + dcb->user, + dcb->remote))); + } + else + { + rval = -1; + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_accept for %s@%s: %s", + dcb->user, + dcb->remote, + ERR_error_string(errnum,NULL)); + } + break; + + default: + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_accept, returned value was %d.", + rval); + break; + } + + return rval; +} \ No newline at end of file diff --git a/server/core/service.c b/server/core/service.c index 77b581e81..f8b9d0e99 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -1818,24 +1818,28 @@ int serviceInitSSL(SERVICE* service) { service->method = (SSL_METHOD*)SSLv23_server_method(); service->ctx = SSL_CTX_new(service->method); - + SSL_CTX_set_read_ahead(service->ctx,1); if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { + skygw_log_write(LE,"Error: Failed to set server SSL certificate."); return -1; } /* Load the private-key corresponding to the server certificate */ if (SSL_CTX_use_PrivateKey_file(service->ctx, service->ssl_key, SSL_FILETYPE_PEM) <= 0) { + skygw_log_write(LE,"Error: Failed to set server SSL key."); return -1; } /* Check if the server certificate and private-key matches */ if (!SSL_CTX_check_private_key(service->ctx)) { + skygw_log_write(LE,"Error: Server SSL certificate and key do not match."); return -1; } /* Load the RSA CA certificate into the SSL_CTX structure */ if (!SSL_CTX_load_verify_locations(service->ctx, service->ssl_ca_cert, NULL)) { + skygw_log_write(LE,"Error: Failed to set Certificate Authority file."); return -1; } diff --git a/server/include/dcb.h b/server/include/dcb.h index 38ebdc299..58a4eb532 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -268,6 +268,7 @@ typedef struct dcb { unsigned int high_water; /**< High water mark */ unsigned int low_water; /**< Low water mark */ struct server *server; /**< The associated backend server */ + SSL* ssl; /*< SSL struct for connection */ #if defined(SS_DEBUG) int dcb_port; /**< port of target server */ skygw_chk_t dcb_chk_tail; @@ -340,10 +341,12 @@ bool dcb_set_state(DCB* dcb, dcb_state_t new_state, dcb_state_t* old_state); void dcb_call_foreach (struct server* server, DCB_REASON reason); size_t dcb_get_session_id(DCB* dcb); bool dcb_get_ses_log_info(DCB* dcb, size_t* sesid, int* enabled_logs); +int dcb_create_SSL(DCB* dcb); +int dcb_accept_SSL(DCB* dcb); int gw_write_SSL(SSL* ssl, const void *buf, size_t nbytes); -int dcb_write_SSL(DCB *dcb, SSL* ssl, GWBUF *queue); -int dcb_read_SSL(DCB *dcb,SSL* ssl,GWBUF **head); -int dcb_drain_writeq_SSL(DCB *dcb, SSL* ssl); +int dcb_write_SSL(DCB *dcb,GWBUF *queue); +int dcb_read_SSL(DCB *dcb,GWBUF **head); +int dcb_drain_writeq_SSL(DCB *dcb); /** diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index 94db6e664..e5fd954a4 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -297,7 +297,6 @@ typedef struct { unsigned long tid; /*< MySQL Thread ID, in * handshake */ unsigned int charset; /*< MySQL character set at connect time */ - SSL* ssl; /*< SSL struct for client connection */ bool use_ssl; #if defined(SS_DEBUG) skygw_chk_t protocol_chk_tail; diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 2d2633b90..b930d8c77 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -490,14 +490,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { /** Do the SSL Handshake */ if(ssl && protocol->owner_dcb->service->ssl_mode != SSL_DISABLED) { - if(serviceInitSSL(protocol->owner_dcb->service) != 0) - { - skygw_log_write(LOGFILE_ERROR,"Error: SSL initialization for service '%s' failed.", - protocol->owner_dcb->service->name); - return 1; - } - protocol->ssl = SSL_new(protocol->owner_dcb->service->ctx); - SSL_set_fd(protocol->ssl,dcb->fd); + protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; if(do_ssl_accept(protocol) < 0) @@ -632,7 +625,7 @@ gw_MySQLWrite_client_SSL(DCB *dcb, GWBUF *queue) CHK_DCB(dcb); protocol = DCB_PROTOCOL(dcb, MySQLProtocol); CHK_PROTOCOL(protocol); - return dcb_write_SSL(dcb, protocol->ssl, queue); + return dcb_write_SSL(dcb, queue); } /** @@ -681,7 +674,7 @@ int gw_read_client_event( if(protocol->use_ssl) { - rc = dcb_read_SSL(dcb,protocol->ssl, &read_buffer); + rc = dcb_read_SSL(dcb, &read_buffer); } else { @@ -795,7 +788,7 @@ int gw_read_client_event( dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); nbytes_read = gwbuf_length(dcb->dcb_readqueue); data = (uint8_t *)GWBUF_DATA(dcb->dcb_readqueue); - + int plen = MYSQL_GET_PACKET_LEN(data); if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data)) { rc = 0; @@ -1255,7 +1248,7 @@ int gw_write_client_event_SSL(DCB *dcb) if (protocol->protocol_auth_state == MYSQL_IDLE) { - dcb_drain_writeq_SSL(dcb,protocol->ssl); + dcb_drain_writeq_SSL(dcb); goto return_1; } @@ -1878,30 +1871,38 @@ return_rc: /** * Do the SSL authentication handshake. - * This functions - * @param protocol - * @return + * This creates the DCB SSL structure if one has not been created and starts the + * SSL handshake handling. + * @param protocol Protocol to connect with SSL + * @return 1 on success, 0 when the handshake is ongoing or -1 on error */ int do_ssl_accept(MySQLProtocol* protocol) { int rval,errnum; char errbuf[2014]; - DCB* dcb; - - rval = SSL_accept(protocol->ssl); + DCB* dcb = protocol->owner_dcb; + if(dcb->ssl == NULL) + { + if(dcb_create_SSL(dcb) != 0) + return -1; + } + rval = dcb_accept_SSL(dcb); + switch(rval) { case 0: - errnum = SSL_get_error(protocol->ssl,rval); - skygw_log_write_flush(LT,"SSL_accept shutdown for %s@%s", + /** Not all of the data has been read. Go back to the poll + queue and wait for more.*/ + + rval = 0; + skygw_log_write_flush(LT,"SSL_accept ongoing for %s@%s", protocol->owner_dcb->user, protocol->owner_dcb->remote); - return -1; + return 0; break; case 1: spinlock_acquire(&protocol->protocol_lock); - dcb = protocol->owner_dcb; protocol->protocol_auth_state = MYSQL_AUTH_SSL_HANDSHAKE_DONE; protocol->use_ssl = true; spinlock_release(&protocol->protocol_lock); @@ -1919,32 +1920,15 @@ int do_ssl_accept(MySQLProtocol* protocol) break; case -1: - errnum = SSL_get_error(protocol->ssl,rval); - if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE || - errnum == SSL_ERROR_WANT_X509_LOOKUP) - { - /** Not all of the data has been read. Go back to the poll - queue and wait for more.*/ - - rval = 0; - skygw_log_write_flush(LT,"SSL_accept ongoing for %s@%s", - protocol->owner_dcb->user, - protocol->owner_dcb->remote); - } - else - { spinlock_acquire(&protocol->protocol_lock); protocol->protocol_auth_state = MYSQL_AUTH_SSL_HANDSHAKE_FAILED; spinlock_release(&protocol->protocol_lock); rval = -1; - skygw_log_write_flush(LE, "Error: Fatal error in SSL_accept for %s@%s: %s", protocol->owner_dcb->user, - protocol->owner_dcb->remote, - ERR_error_string(errnum,NULL)); - } + protocol->owner_dcb->remote); break; default: diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 654d24692..56988dc9a 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -137,10 +137,7 @@ void mysql_protocol_done ( goto retblock; } scmd = p->protocol_cmd_history; - if(p->ssl) - { - SSL_free(p->ssl); - } + while (scmd != NULL) { scmd2 = scmd->scom_next; From 76655e7136790bc189e678584681143108ada50b Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 2 Jun 2015 06:04:06 +0300 Subject: [PATCH 106/273] Added a call to a library function which adds all algorithms to OpenSSL to the SSL initialization code. --- server/core/gateway.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/core/gateway.c b/server/core/gateway.c index 0b07f507b..67d5ecf62 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1377,6 +1377,8 @@ int main(int argc, char **argv) SSL_library_init(); SSL_load_error_strings(); + OPENSSL_add_all_algorithms_noconf(); + int n_locks = CRYPTO_num_locks(); if((ssl_locks = malloc(n_locks*sizeof(SPINLOCK))) == NULL) { From 6e01757455dd4ec94eeca482e85c616bf1572918 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 2 Jun 2015 06:39:51 +0300 Subject: [PATCH 107/273] Added error message to users when trying to connect to a MaxScale service that only allows SSL connections. --- .../include/mysql_client_server_protocol.h | 4 +++ server/modules/protocol/mysql_backend.c | 4 +-- server/modules/protocol/mysql_client.c | 27 ++++++++++++------- server/modules/protocol/mysql_common.c | 11 +++++++- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index e5fd954a4..f72416491 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -91,6 +91,10 @@ #define COM_QUIT_PACKET_SIZE (4+1) struct dcb; +#define MYSQL_FAILED_AUTH 1 +#define MYSQL_FAILED_AUTH_DB 2 +#define MYSQL_FAILED_AUTH_SSL 3 + typedef enum { MYSQL_ALLOC, MYSQL_PENDING_CONNECT, diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index ba5786851..d000474ab 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -72,7 +72,7 @@ static void backend_set_delayqueue(DCB *dcb, GWBUF *queue); static int gw_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue); static GWBUF* process_response_data (DCB* dcb, GWBUF* readbuf, int nbytes_to_process); extern char* create_auth_failed_msg( GWBUF* readbuf, char* hostaddr, uint8_t* sha1); -extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db); +extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db,int); static bool sescmd_response_complete(DCB* dcb); @@ -1433,7 +1433,7 @@ static int gw_change_user( message = create_auth_fail_str(username, backend->session->client->remote, password_set, - ""); + "",auth_ret); if (message == NULL) { LOGIF(LE, (skygw_log_write_flush( diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index b930d8c77..6aeadd180 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -78,7 +78,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue); static int route_by_statement(SESSION *, GWBUF **); extern char* get_username_from_auth(char* ptr, uint8_t* data); extern int check_db_name_after_auth(DCB *, char *, int); -extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db); +extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db,int); int do_ssl_accept(MySQLProtocol* protocol); @@ -450,7 +450,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { /* Detect now if there are enough bytes to continue */ if (client_auth_packet_size < (4 + 4 + 4 + 1 + 23)) { - return 1; + return MYSQL_FAILED_AUTH; } memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4); @@ -476,7 +476,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { protocol->owner_dcb->user, protocol->owner_dcb->remote, protocol->owner_dcb->service->name))); - return 1; + return MYSQL_FAILED_AUTH_SSL; } if(LOG_IS_ENABLED(LT) && ssl) @@ -495,7 +495,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { if(do_ssl_accept(protocol) < 0) { - return 1; + return MYSQL_FAILED_AUTH; } else { @@ -509,7 +509,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { if (username == NULL) { - return 1; + return MYSQL_FAILED_AUTH; } /* get charset */ @@ -902,7 +902,7 @@ int gw_read_client_event( fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user, dcb->remote, (char*)((MYSQL_session *)dcb->data)->client_sha1, - (char*)((MYSQL_session *)dcb->data)->db); + (char*)((MYSQL_session *)dcb->data)->db,auth_val); modutil_send_mysql_err_packet(dcb, 2, 0, 1045, "28000", fail_str); } if (fail_str) @@ -996,14 +996,21 @@ int gw_read_client_event( snprintf(fail_str, message_len, "Unknown database '%s'", (char*)((MYSQL_session *)dcb->data)->db); - modutil_send_mysql_err_packet(dcb, 2, 0, 1049, "42000", fail_str); - } else { + modutil_send_mysql_err_packet(dcb, 3, 0, 1049, "42000", fail_str); + }else if(auth_val == 3){ /** Send error 1045 to client */ fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user, dcb->remote, (char*)((MYSQL_session *)dcb->data)->client_sha1, - (char*)((MYSQL_session *)dcb->data)->db); - modutil_send_mysql_err_packet(dcb, 2, 0, 1045, "28000", fail_str); + (char*)((MYSQL_session *)dcb->data)->db,auth_val); + modutil_send_mysql_err_packet(dcb, 3, 0, 1045, "28000", fail_str); + }else { + /** Send error 1045 to client */ + fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user, + dcb->remote, + (char*)((MYSQL_session *)dcb->data)->client_sha1, + (char*)((MYSQL_session *)dcb->data)->db,auth_val); + modutil_send_mysql_err_packet(dcb, 3, 0, 1045, "28000", fail_str); } if (fail_str) free(fail_str); diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 56988dc9a..0a1d2195b 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -2199,7 +2199,8 @@ char *create_auth_fail_str( char *username, char *hostaddr, char *sha1, - char *db) + char *db, + int errcode) { char* errstr; const char* ferrstr; @@ -2214,6 +2215,10 @@ char *create_auth_fail_str( { ferrstr = "Access denied for user '%s'@'%s' (using password: %s) to database '%s'"; } + else if(errcode == MYSQL_FAILED_AUTH_SSL) + { + ferrstr = "Access without SSL denied"; + } else { ferrstr = "Access denied for user '%s'@'%s' (using password: %s)"; @@ -2233,6 +2238,10 @@ char *create_auth_fail_str( { sprintf(errstr, ferrstr, username, hostaddr, (*sha1 == '\0' ? "NO" : "YES"), db); } + else if(errcode == MYSQL_FAILED_AUTH_SSL) + { + sprintf(errstr, ferrstr); + } else { sprintf(errstr, ferrstr, username, hostaddr, (*sha1 == '\0' ? "NO" : "YES")); From 08e0a318268cd42fa0f70442c5642e9da682f1f2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 2 Jun 2015 06:42:41 +0300 Subject: [PATCH 108/273] Fixed session creation failure messages using the wrong packet number when an SSL connection was made. --- server/modules/protocol/mysql_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 6aeadd180..45fc65ee0 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -975,7 +975,7 @@ int gw_read_client_event( /** Send ERR 1045 to client */ mysql_send_auth_error( dcb, - 2, + 3, 0, "failed to create new session"); From fc8918b1f2d77d32880ca0297dcb069fccc1e0e1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 2 Jun 2015 09:15:08 +0300 Subject: [PATCH 109/273] Added a dcb_connect_SSL function which starts a client SSL connection. --- server/core/dcb.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ server/include/dcb.h | 1 + 2 files changed, 67 insertions(+) diff --git a/server/core/dcb.c b/server/core/dcb.c index 2948dae2e..2dcbd9b4c 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -2843,5 +2843,71 @@ int dcb_accept_SSL(DCB* dcb) break; } + return rval; +} + +/** + * Initiate an SSL client connection to a server + * + * This functions starts an SSL client connection to a server which is expecting + * an SSL handshake. The DCB should already have a TCP connection to the server and + * this connection should be in a state that expects an SSL handshake. + * @param dcb DCB to connect + * @return 1 on success, -1 on error and 0 if the SSL handshake is still ongoing + */ +int dcb_connect_SSL(DCB* dcb) +{ + int rval,errnum; + + rval = SSL_connect(dcb->ssl); + + switch(rval) + { + case 0: + errnum = SSL_get_error(dcb->ssl,rval); + LOGIF(LD,(skygw_log_write_flush(LD,"SSL_connect shutdown for %s@%s", + dcb->user, + dcb->remote))); + return -1; + break; + case 1: + rval = 1; + LOGIF(LD,(skygw_log_write_flush(LD,"SSL_connect done for %s@%s", + dcb->user, + dcb->remote))); + break; + + case -1: + errnum = SSL_get_error(dcb->ssl,rval); + + if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE || + errnum == SSL_ERROR_WANT_X509_LOOKUP) + { + /** Not all of the data has been read. Go back to the poll + queue and wait for more.*/ + + rval = 0; + LOGIF(LD,(skygw_log_write_flush(LD,"SSL_connect ongoing for %s@%s", + dcb->user, + dcb->remote))); + } + else + { + rval = -1; + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_connect for %s@%s: %s", + dcb->user, + dcb->remote, + ERR_error_string(errnum,NULL)); + } + break; + + default: + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_connect, returned value was %d.", + rval); + break; + } + return rval; } \ No newline at end of file diff --git a/server/include/dcb.h b/server/include/dcb.h index 58a4eb532..cc96a2c0e 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -343,6 +343,7 @@ size_t dcb_get_session_id(DCB* dcb); bool dcb_get_ses_log_info(DCB* dcb, size_t* sesid, int* enabled_logs); int dcb_create_SSL(DCB* dcb); int dcb_accept_SSL(DCB* dcb); +int dcb_connect_SSL(DCB* dcb); int gw_write_SSL(SSL* ssl, const void *buf, size_t nbytes); int dcb_write_SSL(DCB *dcb,GWBUF *queue); int dcb_read_SSL(DCB *dcb,GWBUF **head); From 2f2c9c8cbc7d4f6fc343f99c8c5e9f1bd0f7b279 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Tue, 2 Jun 2015 09:45:26 +0200 Subject: [PATCH 110/273] Fix for MariaDB10 state machine Fix for MariaDB10 state machine --- server/modules/routing/binlog/blr_master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index f230442f1..3a0519108 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -466,8 +466,8 @@ char query[128]; GWBUF_CONSUME_ALL(router->saved_master.mariadb10); router->saved_master.mariadb10 = buf; blr_cache_response(router, "mariadb10", buf); - buf = blr_make_registration(router); - router->master_state = BLRM_REGISTER; + 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: From 94ba445d2fad80fd9dc7075db792ced8a3ea2001 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 28 May 2015 10:30:21 +0300 Subject: [PATCH 111/273] Added 5.5.5- string to the start of MariaDB 10.0 version strings. --- server/core/config.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index d22bb777e..a2414ea7a 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -231,7 +231,7 @@ int rval; strcpy(version_string,tmp); } - ptr = strstr(tmp, "-embedded"); + ptr = strstr(version_string, "-embedded"); if (ptr) { *ptr = '\0'; } @@ -417,7 +417,21 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); } if (version_string) { + + /** Add the 5.5.5- string to the start of the version string if + * the version string starts with "10.". + * This mimics MariaDB 10.0 replication which adds 5.5.5- for backwards compatibility. */ + if(strncmp(version_string,"10.",3) == 0) + { + ((SERVICE *)(obj->element))->version_string = malloc((strlen(version_string) + + strlen("5.5.5-") + 1) * sizeof(char)); + strcpy(((SERVICE *)(obj->element))->version_string,"5.5.5-"); + strcat(((SERVICE *)(obj->element))->version_string,version_string); + } + else + { ((SERVICE *)(obj->element))->version_string = strdup(version_string); + } } else { if (gateway.version_string) ((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string); From 57060cafecff9141e1883879c11cfe46d0b120d3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 2 Jun 2015 17:00:39 +0300 Subject: [PATCH 112/273] Added SSL level configuration to services. --- .../Getting-Started/Configuration-Guide.md | 48 +++++++++++++++++ Documentation/Reference/MaxScale-and-SSL.md | 3 +- server/core/config.c | 16 ++++-- server/core/service.c | 51 ++++++++++++++++++- server/include/service.h | 12 +++++ 5 files changed, 124 insertions(+), 6 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 535eca2cc..e3cc4871b 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -326,6 +326,54 @@ Example: connection_timeout=300 ``` +### Service and SSL + +This section describes configuration parameters for services that control the SSL/TLS encrption method and the various certificate files involved in it. To enable SSL, you must configure the `ssl` parameter with either `enabled` or `required` and provide the three files for `ssl_cert`, `ssl_key` and `ssl_ca_cert`. After this, MySQL connections to this service can be encrypted with SSL. + +#### `ssl` + +This enables SSL connections to the service. If this parameter is set to either `required` or `enabled` and the three certificate files can be found (these are explained afterwards), then client connections will be encrypted with SSL. If the parameter is `enabled` then both SSL and non-SSL connections can connect to this service. If the parameter is set to `required` then only SSL connections can be used for this service and non-SSL connections will get an error when they try to connect to the service. + +#### `ssl_key` + +The SSL private key the service should use. This will be the private key that is used as the server side private key during a client-server SSL handshake. This is a required parameter for SSL enabled services. + +#### `ssl_cert` + +The SSL certificate the service should use. This will be the public certificate that is used as the server side certificate during a client-server SSL handshake. This is a required parameter for SSL enabled services. + +#### `ssl_ca_cert` + +This is the Certificate Authority file. It will be used to verify that both the client and the server certificates are valid. This is a required parameter for SSL enabled services. + +### `ssl_version` + +This parameter controls the level of encryption used. Accepted values are: + * SSLv2 + * SSLv3 + * TLSv10 + * TLSv11 + * TLSv12 + * MAX + +Example SSL enabled service configuration: + +``` +[ReadWriteSplitService] +type=service +router=readwritesplit +servers=server1,server2,server3 +user=myuser +passwd=mypasswd +ssl=required +ssl_cert=/home/markus/certs/server-cert.pem +ssl_key=/home/markus/certs/server-key.pem +ssl_ca_cert=/home/markus/certs/ca.pem +ssl_version=TLSv12 +``` + +This configuration requires all connections to be encryped with SSL. It also specifies that TLSv1.2 should be used as the encryption method. The paths to the server certificate files and the Certificate Authority file are also provided. + ### Server Server sections are used to define the backend database servers that can be formed into a service. A server may be a member of one or more services within MaxScale. Servers are identified by a server name which is the section name in the configuration file. Servers have a type parameter of server, plus address port and protocol parameters. diff --git a/Documentation/Reference/MaxScale-and-SSL.md b/Documentation/Reference/MaxScale-and-SSL.md index a4210b0de..ca61d52e2 100644 --- a/Documentation/Reference/MaxScale-and-SSL.md +++ b/Documentation/Reference/MaxScale-and-SSL.md @@ -5,9 +5,10 @@ MaxScale supports client side SSL connections. Enabling is done on a per service ## SSL Options Here are the options which relate to SSL and certificates. -Parameter|Values|Description +Parameter|Values |Description ---------|-----------|-------- ssl | disabled, enabled, required |`disable` disables SSL, `enabled` enables SSL for client connections but still allows non-SSL connections and `required` requires SSL from all client connections. With the `required` option, client connections that do not use SSL will be rejected. ssl_cert | |Path to server certificate ssl_key | |Path to server private key ssl_ca_cert | |Path to Certificate Authority file +ssl_version|SSLV2,SSLV3,TLSV10,TLSV11,TLSV12,MAX| The SSL method level, defaults to highest available encryption level which is TLSv1.2 diff --git a/server/core/config.c b/server/core/config.c index 640437270..5e03a3b19 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -345,7 +345,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); char *weightby; char *version_string; char *subservices; - char *ssl,*ssl_cert,*ssl_key,*ssl_ca_cert; + char *ssl,*ssl_cert,*ssl_key,*ssl_ca_cert,*ssl_version; bool is_rwsplit = false; bool is_schemarouter = false; char *allow_localhost_match_wildcard_host; @@ -358,6 +358,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); ssl_cert = config_get_value(obj->parameters, "ssl_cert"); ssl_key = config_get_value(obj->parameters, "ssl_key"); ssl_ca_cert = config_get_value(obj->parameters, "ssl_ca_cert"); + ssl_version = config_get_value(obj->parameters, "ssl_version"); enable_root_user = config_get_value( obj->parameters, "enable_root_user"); @@ -474,6 +475,10 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); else { serviceSetCertificates(obj->element,ssl_cert,ssl_key,ssl_ca_cert); + if(ssl_version) + { + serviceSetSSLVersion(obj->element,ssl_version); + } } } else @@ -1381,7 +1386,7 @@ int i; } else if (strcmp(name, "ms_timestamp") == 0) { - skygw_set_highp(config_truth_value(value)); + skygw_set_highp(config_truth_value((char*)value)); } else { @@ -1389,7 +1394,7 @@ int i; { if (strcasecmp(name, lognames[i].logname) == 0) { - if (config_truth_value(value)) + if (config_truth_value((char*)value)) skygw_log_enable(lognames[i].logfile); else skygw_log_disable(lognames[i].logfile); @@ -1967,6 +1972,11 @@ static char *service_params[] = "version_string", "filters", "weightby", + "ssl_cert", + "ssl_ca_cert", + "ssl", + "ssl_key", + "ssl_version", NULL }; diff --git a/server/core/service.c b/server/core/service.c index f8b9d0e99..a8df37f5b 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -141,7 +141,8 @@ SERVICE *service; service->ssl_ca_cert = NULL; service->ssl_cert = NULL; service->ssl_key = NULL; - + /** Use the highest possible SSL/TLS methods available */ + service->ssl_method_type = SERVICE_SSL_TLS_MAX; if (service->name == NULL || service->routerModule == NULL) { if (service->name) @@ -868,6 +869,22 @@ serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert) service->ssl_ca_cert = strdup(ca_cert); } +void +serviceSetSSLVersion(SERVICE *service, char* version) +{ + if(strcasecmp(version,"SSLV2") == 0) + service->ssl_method_type = SERVICE_SSLV2; + else if(strcasecmp(version,"SSLV3") == 0) + service->ssl_method_type = SERVICE_SSLV3; + else if(strcasecmp(version,"TLSV10") == 0) + service->ssl_method_type = SERVICE_TLS10; + else if(strcasecmp(version,"TLSV11") == 0) + service->ssl_method_type = SERVICE_TLS11; + else if(strcasecmp(version,"TLSV12") == 0) + service->ssl_method_type = SERVICE_TLS12; + else if(strcasecmp(version,"MAX") == 0) + service->ssl_method_type = SERVICE_SSL_TLS_MAX; +} /** Enable or disable the service SSL capability*/ int serviceSetSSL(SERVICE *service, char* action) @@ -1816,7 +1833,37 @@ int serviceInitSSL(SERVICE* service) { if(!service->ssl_init_done) { - service->method = (SSL_METHOD*)SSLv23_server_method(); + switch(service->ssl_method_type) + { + case SERVICE_SSLV2: + service->method = (SSL_METHOD*)SSLv2_server_method(); + break; + case SERVICE_SSLV3: + service->method = (SSL_METHOD*)SSLv3_server_method(); + break; + case SERVICE_TLS10: + service->method = (SSL_METHOD*)TLSv1_server_method(); + break; + case SERVICE_TLS11: + service->method = (SSL_METHOD*)TLSv1_1_server_method(); + break; + case SERVICE_TLS12: + service->method = (SSL_METHOD*)TLSv1_2_server_method(); + break; + case SERVICE_SSL_MAX: + service->method = (SSL_METHOD*)SSLv23_server_method(); + break; + case SERVICE_TLS_MAX: + service->method = (SSL_METHOD*)SSLv23_server_method(); + break; + case SERVICE_SSL_TLS_MAX: + service->method = (SSL_METHOD*)SSLv23_server_method(); + break; + default: + service->method = (SSL_METHOD*)SSLv23_server_method(); + break; + } + service->ctx = SSL_CTX_new(service->method); SSL_CTX_set_read_ahead(service->ctx,1); if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { diff --git a/server/include/service.h b/server/include/service.h index 1c6614656..7975518e7 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -113,6 +113,17 @@ typedef enum { SSL_REQUIRED } ssl_mode_t; +enum{ + SERVICE_SSLV2, + SERVICE_SSLV3, + SERVICE_TLS10, + SERVICE_TLS11, + SERVICE_TLS12, + SERVICE_SSL_MAX, + SERVICE_TLS_MAX, + SERVICE_SSL_TLS_MAX +}; + /** * Defines a service within the gateway. * @@ -164,6 +175,7 @@ typedef struct service { SSL *ssl; SSL_METHOD *method; /*< SSLv2/3 or TLSv1/2 methods * see: https://www.openssl.org/docs/ssl/SSL_CTX_new.html */ + int ssl_method_type; /*< Which of the SSLv2/3 or TLS1.0/1.1/1.2 methods to use */ char* ssl_cert; char* ssl_key; char* ssl_ca_cert; From dbcb9a450fa10febe7982140068cd177d53030da Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Jun 2015 10:15:53 +0300 Subject: [PATCH 113/273] Added note about GCC versions to the source building document. --- .../Getting-Started/Building-MaxScale-from-Source-Code.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md index 30a06fcdb..f83a6e3ae 100644 --- a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md +++ b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md @@ -4,7 +4,7 @@ You will need a number of tools and libraries in order to achieve this. * cmake version 2.8.12 or later -* gcc recommended version 4.4.7 or later +* gcc recommended version 4.4.7 or later (MariaDB 10 libraries require gcc 4.7 or newer) * libaio From 6e2611f24141bf934f06fa96901a7bbe0fda76ef Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Jun 2015 11:07:06 +0300 Subject: [PATCH 114/273] Updated documentation with new configuration file name. --- Documentation/About/SETUP.md | 4 ++-- .../Getting-Started/Configuration-Guide.md | 16 ++++++++-------- .../Getting-Started-With-MaxScale.md | 2 +- .../Reference/Debug-And-Diagnostic-Support.md | 4 ++-- Documentation/Reference/Hint-Syntax.md | 2 +- Documentation/Reference/MaxAdmin.md | 8 ++++---- .../MaxScale-1.0.1-Release-Notes.md | 4 ++-- .../Tutorials/Administration-Tutorial.md | 14 +++++++------- ...Galera-Cluster-Connection-Routing-Tutorial.md | 6 +++--- ...lera-Cluster-Read-Write-Splitting-Tutorial.md | 6 +++--- .../Tutorials/MaxScale-Information-Schema.md | 2 +- Documentation/Tutorials/MySQL-Cluster-Setup.md | 4 ++-- ...QL-Replication-Connection-Routing-Tutorial.md | 4 ++-- ...-Replication-Read-Write-Splitting-Tutorial.md | 6 +++--- Documentation/Tutorials/Nagios-Plugins.md | 6 +++--- .../RabbitMQ-Setup-And-MaxScale-Integration.md | 2 +- .../Replication-Proxy-Binlog-Router-Tutorial.md | 2 +- .../Tutorials/Simple-Sharding-Tutorial.md | 4 ++-- .../filters/Database-Firewall-Filter.md | 2 +- Documentation/filters/Query-Log-All-Filter.md | 2 +- Documentation/filters/RabbitMQ-Filter.md | 4 ++-- Documentation/filters/Regex-Filter.md | 2 +- Documentation/filters/Tee-Filter.md | 2 +- Documentation/filters/Top-N-Filter.md | 2 +- 24 files changed, 55 insertions(+), 55 deletions(-) diff --git a/Documentation/About/SETUP.md b/Documentation/About/SETUP.md index 9cb5dd11b..e28fb0d40 100644 --- a/Documentation/About/SETUP.md +++ b/Documentation/About/SETUP.md @@ -11,7 +11,7 @@ or by manually starting the process from the bin folder of the installation dire Configuration -You need to create or edit the MaxScale.cnf file in the /etc folder. +You need to create or edit the maxscale.cnf file in the /etc folder. Define the services you wish to provide, the set of server definitions you require, with the addresses and ports of those servers and also define the listening ports for your various services. @@ -22,4 +22,4 @@ the port defined for the telnet listener. Initially you may login with the user name of "admin" and the password "mariadb". Once connected type help for an overview of the commands and help for the more detailed help on commands. Use the add user command to add a new user, -this will also remove the admin/mariadb user. +this will also remove the admin/mariadb user. For a detailed guide about using the MaxAdmin interface, take a look at the [MaxAdmin](../Reference/MaxAdmin.md) guide. diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 535eca2cc..15293d1aa 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -241,7 +241,7 @@ Query OK, 0 rows affected (0.00 sec) #### `passwd` -The passwd parameter provides the password information for the above user and may be either a plain text password or it may be an encrypted password. See the section on encrypting passwords for use in the MaxScale.cnf file. This user must be capable of connecting to the backend database and executing these SQL statements to load database names and grants from the backends: +The passwd parameter provides the password information for the above user and may be either a plain text password or it may be an encrypted password. See the section on encrypting passwords for use in the maxscale.cnf file. This user must be capable of connecting to the backend database and executing these SQL statements to load database names and grants from the backends: * `SELECT user, host, password,Select_priv FROM mysql.user`. * `SELECT user, host, db FROM mysql.db` @@ -366,7 +366,7 @@ The monitor has a username and password that is used to connect to all servers f monitorpw=mymonitorpasswd ``` -The monpasswd parameter may be either a plain text password or it may be an encrypted password. See the section on encrypting passwords for use in the MaxScale.cnf file. +The monpasswd parameter may be either a plain text password or it may be an encrypted password. See the section on encrypting passwords for use in the maxscale.cnf file. ### Listener @@ -515,7 +515,7 @@ Individual servers may define override values for the user and password the moni #### `passwd` -The password parameter may be either a plain text password or it may be an encrypted password. See the section on encrypting passwords for use in the `MaxScale.cnf` file. +The password parameter may be either a plain text password or it may be an encrypted password. See the section on encrypting passwords for use in the `maxscale.cnf` file. #### `monitor_interval` @@ -1307,7 +1307,7 @@ before being sent to the server. Note that the text in the match string is case- The **tee** filter is a filter module for MaxScale that acts as a "plumbing" fitting in the MaxScale filter toolkit. It can be used in a filter pipeline of a service to make a copy of requests from the client and dispatch a copy of the request to another service within MaxScale. -The configuration block for the **tee** filter requires the minimal filter parameters in its section within the `MaxScale.cnf` file that defines the filter to load and the service to send the duplicates to. +The configuration block for the **tee** filter requires the minimal filter parameters in its section within the `maxscale.cnf` file that defines the filter to load and the service to send the duplicates to. ``` [ArchiveFilter] @@ -1322,7 +1322,7 @@ In addition parameters may be added to define patterns to match against to eithe The top filter is a filter module for MaxScale that monitors every SQL statement that passes through the filter. It measures the duration of that statement, the time between the statement being sent and the first result being returned. The top N times are kept, along with the SQL text itself and a list sorted on the execution times of the query is written to a file upon closure of the client session. -The configuration block for the **top** filter requires the minimal filter options in its section within the `MaxScale.cnf` file, stored in `/etc/MaxScale.cnf`. +The configuration block for the **top** filter requires the minimal filter options in its section within the `maxscale.cnf` file, stored in `/etc/maxscale.cnf`. ``` [MyLogFilter] @@ -1336,13 +1336,13 @@ In addition parameters may be added to define patterns to match against to eithe ## Encrypting Passwords -Passwords stored in the MaxScale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. The default location MaxScale stores the keys is `/var/cache/maxscale`. +Passwords stored in the maxscale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. The default location MaxScale stores the keys is `/var/cache/maxscale`. ``` maxkeys /var/cache/maxscale/.secrets ``` -Changing the encryption key for MaxScale will invalidate any currently encrypted keys stored in the MaxScale.cnf file. +Changing the encryption key for MaxScale will invalidate any currently encrypted keys stored in the maxscale.cnf file. ### Creating Encrypted Passwords @@ -1351,7 +1351,7 @@ Encrypted passwords are created by executing the maxpasswd command with the pass maxpasswd MaxScalePw001 61DD955512C39A4A8BC4BB1E5F116705 -The output of the maxpasswd command is a hexadecimal string, this should be inserted into the MaxScale.cnf file in place of the ordinary, plain text, password. MaxScale will determine this as an encrypted password and automatically decrypt it before sending it the database server. +The output of the maxpasswd command is a hexadecimal string, this should be inserted into the maxscale.cnf file in place of the ordinary, plain text, password. MaxScale will determine this as an encrypted password and automatically decrypt it before sending it the database server. ``` [Split Service] diff --git a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md index ed41da728..344ac500d 100644 --- a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md +++ b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md @@ -65,7 +65,7 @@ modules it will search using a configurable search path. The priority of these p 2. Look in the directory defined with libdir=PATH in the configuration file under the [maxscale] section 3. Look in default directory in /usr/lib64/maxscale -Configuration is read by default from the file /etc/MaxScale.cnf. An example file is included in in the installation and can be found in the /usr/share/maxscale folder within the MaxScale installation. The -f flag can be used on the command line to set the name and the location of the configuration file. The -C flag can be used to set the directory where the configuration file is searched for. Without the -f or -C flags, the file is read from the /etc directory. +Configuration is read by default from the file /etc/maxscale.cnf. An example file is included in in the installation and can be found in the /usr/share/maxscale folder within the MaxScale installation. The -f flag can be used on the command line to set the name and the location of the configuration file. The -C flag can be used to set the directory where the configuration file is searched for. Without the -f or -C flags, the file is read from the /etc directory. ## Administration Of MaxScale diff --git a/Documentation/Reference/Debug-And-Diagnostic-Support.md b/Documentation/Reference/Debug-And-Diagnostic-Support.md index d9bffa268..12298624c 100644 --- a/Documentation/Reference/Debug-And-Diagnostic-Support.md +++ b/Documentation/Reference/Debug-And-Diagnostic-Support.md @@ -1679,7 +1679,7 @@ Loaded 34 users. ## Reload config -The reload config command can be used to force MaxScale to re-read the MaxScale.cnf and update itself to the latest configuration defined in that configuration file. It is also possible to force the reading of the configuration file by sending a HangUp signal (SIGHUP) to the maxscale process. +The reload config command can be used to force MaxScale to re-read the maxscale.cnf and update itself to the latest configuration defined in that configuration file. It is also possible to force the reading of the configuration file by sending a HangUp signal (SIGHUP) to the maxscale process. **MaxScale>** reload config @@ -1789,7 +1789,7 @@ MariaDB Corporation MaxScale /home/jdoe/bin/develop/log/skygw_msg1.log Tue Dec 2014-12-09 14:47:05 Log directory : /home/jdoe/bin/develop/log -2014-12-09 14:47:05 Configuration file : /home/jdoe/bin/develop/etc/MaxScale.cnf +2014-12-09 14:47:05 Configuration file : /home/jdoe/bin/develop/etc/maxscale.cnf 2014-12-09 14:47:05 Initialise CLI router module V1.0.0. diff --git a/Documentation/Reference/Hint-Syntax.md b/Documentation/Reference/Hint-Syntax.md index 0cb7cbaee..264d264f6 100644 --- a/Documentation/Reference/Hint-Syntax.md +++ b/Documentation/Reference/Hint-Syntax.md @@ -15,7 +15,7 @@ name-value pairs. Routing queries to a server: -- maxscale route to [master | slave | server ] -The name of the server is the same as in MaxScale.cnf +The name of the server is the same as in maxscale.cnf Creating a name-value pair: -- maxscale = diff --git a/Documentation/Reference/MaxAdmin.md b/Documentation/Reference/MaxAdmin.md index b8889192a..6244a33d6 100644 --- a/Documentation/Reference/MaxAdmin.md +++ b/Documentation/Reference/MaxAdmin.md @@ -591,7 +591,7 @@ Some monitors provide a replication heartbeat mechanism that monitors the delay MaxScale> enable heartbeat "MySQL Monitor" MaxScale> -Please note that changes made via this interface will not persist across restarts of MaxScale. To make a permanent change edit the MaxScale.cnf file. +Please note that changes made via this interface will not persist across restarts of MaxScale. To make a permanent change edit the maxscale.cnf file. Enabling the replication heartbeat mechanism will add the display of heartbeat information in the show server output @@ -786,11 +786,11 @@ Two commands are provided to change the logging levels within MaxScale, disable MaxScale> disable log debug MaxScale> -Please note that changes made via this interface will not persist across restarts of MaxScale. To make a permanent change edit the MaxScale.cnf file. +Please note that changes made via this interface will not persist across restarts of MaxScale. To make a permanent change edit the maxscale.cnf file. ## Reloading The Configuration -A command, reload config, is available that will cause MaxScale to reload the MaxScale.cnf configuration file. +A command, reload config, is available that will cause MaxScale to reload the maxscale.cnf configuration file. ## Shutting Down MaxScale @@ -798,7 +798,7 @@ The MaxScale server may be shutdown using the shutdown maxscale command. # Configuring MaxScale to Accept MaxAdmin Connections -In order to allow the use of the MaxAdmin client interface the service must be added to the MaxScale.cnf file of the Maxscale server. The CLI service itself must be added and a listener for the maxscaled protocol. +In order to allow the use of the MaxAdmin client interface the service must be added to the maxscale.cnf file of the Maxscale server. The CLI service itself must be added and a listener for the maxscaled protocol. The default entries required are shown below. diff --git a/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md index 6a34ec3a9..a82083ac9 100644 --- a/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md @@ -107,11 +107,11 @@ A number of bug fixes have been applied between the 1.0 beta release and this re 479 - Undefined filter reference in MaxScale.cnf causes a crash + Undefined filter reference in maxscale.cnf causes a crash 410 - MaxScale.cnf server option is not parsed for spaces + maxscale.cnf server option is not parsed for spaces 417 diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index 6913431bc..e3253fca6 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -22,7 +22,7 @@ or $ /etc/init.d/maxscale start -It is also possible to start MaxScale by executing the maxscale command itself, in this case you must ensure that the environment is correctly setup or command line options are passed. The major elements to consider are the correct setting of the MAXSCALE\_HOME directory and to ensure that LD\_LIBRARY\_PATH. The LD\_LIBRARY\_PATH should include the lib directory that was installed as part of the MaxScale installation, the MAXSCALE\_HOME should point to /usr/local/mariadb-maxscale if a default installation has been created or to the directory this was relocated to. Running the executable $MAXSCALE\_HOME/bin/maxscale will result in MaxScale running as a daemon process, unattached to the terminal in which it was started and using configuration files that it finds in the $MAXSCALE\_HOME directory. +It is also possible to start MaxScale by executing the maxscale command itself. Running the executable /usr/bin/maxscale will result in MaxScale running as a daemon process, unattached to the terminal in which it was started and using configuration files that it finds in the /etc directory. Options may be passed to the MaxScale binary that alter this default behavior, this options are documented in the table below. @@ -56,9 +56,9 @@ or $ /etc/init.d/maxscale stop -MaxScale will also stop gracefully if it received a hangup signal, to find the process id of the MaxScale server use the ps command or read the contents of the maxscale.pid file located in the same directory as the logs. +MaxScale will also stop gracefully if it received a terminate signal, to find the process id of the MaxScale server use the ps command or read the contents of the maxscale.pid file located in the /var/run/maxscale directory. - $ kill -HUP `cat /log/maxscale.pid` + $ kill `cat /var/run/maxscale/maxscale.pid` In order to shutdown MaxScale using the maxadmin command you may either connect with maxadmin in interactive mode or pass the "shutdown maxscale" command you wish to execute as an argument to maxadmin. @@ -135,7 +135,7 @@ This may be integrated into the Linux logrotate mechanism by adding a configurat - @@ -156,14 +156,14 @@ One disadvantage with this is that the password used for the maxadmin command ha
/usr/local/mariadb-maxscale/log/*.log { + /var/log/maxscale/*.log { monthly rotate 5 missingok @@ -144,7 +144,7 @@ sharedscripts postrotate \# run if maxscale is running if test -n "`ps acx|grep maxscale`"; then -/usr/local/mariadb-maxscale/bin/maxadmin -pmariadb flush logs +/usr/bin/maxadmin -pmariadb flush logs fi endscript }
- diff --git a/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md b/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md index d58ebc32c..0ebe57a2b 100644 --- a/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md +++ b/Documentation/Tutorials/Galera-Cluster-Connection-Routing-Tutorial.md @@ -60,7 +60,7 @@ If you wish to use two different usernames for the two different roles of monito ### Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file maxscale.cnf in the directory /etc, if you have installed in the default location then this file is available in /etc/maxscale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within the /usr/share/maxscale directory that may be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. @@ -89,7 +89,7 @@ In order to instruct the router to which servers it should route we must add rou The final step in the service section is to add the username and password that will be used to populate the user data from the database cluster. There are two options for representing the password, either plain text or encrypted passwords may be used. In order to use encrypted passwords a set of keys must be generated that will be used by the encryption and decryption process. To generate the keys use the maxkeys command and pass the name of the secrets file in which the keys are stored. - % maxkeys /usr/local/mariadb-maxscale/etc/.secrets + % maxkeys /var/lib/maxscale/.secrets % Once the keys have been created the maxpasswd command can be used to generate the encrypted password. @@ -178,7 +178,7 @@ or % service maxscale start -Check the error log in /usr/local/mariadb-maxscale/log to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. +Check the error log in /var/log/maxscale to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. % maxadmin -pmariadb list services diff --git a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md index 8717591d8..4da69f27e 100644 --- a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md @@ -62,7 +62,7 @@ If you wish to use two different usernames for the two different roles of monito ### Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file maxscale.cnf in the directory /etc, if you have installed in the default location then this file is available in /etc/maxscale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within the /usr/share/maxscale directory that may be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. @@ -83,7 +83,7 @@ The router for we need to use for this configuration is the readwritesplit modul The final step in the service sections is to add the username and password that will be used to populate the user data from the database cluster. There are two options for representing the password, either plain text or encrypted passwords may be used. In order to use encrypted passwords a set of keys must be generated that will be used by the encryption and decryption process. To generate the keys use the maxkeys command and pass the name of the secrets file in which the keys are stored. - % maxkeys /usr/local/mariadb-maxscale/etc/.secrets + % maxkeys /var/lib/maxscale/.secrets % Once the keys have been created the maxpasswd command can be used to generate the encrypted password. @@ -183,7 +183,7 @@ or % service maxscale start -Check the error log in /usr/local/mariadb-maxscale/log to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. +Check the error log in /var/log/maxscale to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. % maxadmin -pmariadb list services diff --git a/Documentation/Tutorials/MaxScale-Information-Schema.md b/Documentation/Tutorials/MaxScale-Information-Schema.md index 9872556b3..370960287 100644 --- a/Documentation/Tutorials/MaxScale-Information-Schema.md +++ b/Documentation/Tutorials/MaxScale-Information-Schema.md @@ -5,7 +5,7 @@ The plugin is capable of returning data in one of two ways, either as MySQL resu # Configuration -The plugin is configured in the MaxScale.cnf plugin in much the same way as any other router service is configured, there needs to be a service section in the configuration file and also listeners defined for that service. The service does not however require any backend servers to be associated with it, or any monitors. +The plugin is configured in the maxscale.cnf plugin in much the same way as any other router service is configured, there needs to be a service section in the configuration file and also listeners defined for that service. The service does not however require any backend servers to be associated with it, or any monitors. The service entry needs to define the service name, the type as service and the router module to load. The specified user, with the password (plain or encrypted via maxpassword utility) is allowed to connect via MySQL protocol. diff --git a/Documentation/Tutorials/MySQL-Cluster-Setup.md b/Documentation/Tutorials/MySQL-Cluster-Setup.md index fd6c038ca..a168fcfd4 100644 --- a/Documentation/Tutorials/MySQL-Cluster-Setup.md +++ b/Documentation/Tutorials/MySQL-Cluster-Setup.md @@ -207,7 +207,7 @@ The SQL node is referenced here as [mysqld(API)], which reflects the fact that t ## Configuring MaxScale for connection load balancing of SQL nodes -Add these sections in MaxScale.cnf config file: +Add these sections in maxscale.cnf config file: [Cluster Service] type=service @@ -249,7 +249,7 @@ Add these sections in MaxScale.cnf config file: Assuming MaxScale is installed in server1, start it - [root@server1 ~]# cd /usr/local/mariadb-maxscale/bin + [root@server1 ~]# cd /usr/bin [root@server1 bin]# ./maxscale -c ../ diff --git a/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md b/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md index 8158bfa76..45de0a791 100644 --- a/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md +++ b/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md @@ -76,7 +76,7 @@ If you wish to use two different usernames for the two different roles of monito ## Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc. This is not created as part of the installation process and must be manually created. A template file does exist in the `/usr/share/maxscale` folder that can be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file maxscale.cnf in the directory /etc. This is not created as part of the installation process and must be manually created. A template file does exist in the `/usr/share/maxscale` folder that can be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. @@ -128,7 +128,7 @@ servers=dbserv1, dbserv2, dbserv3 The final step in the service sections is to add the username and password that will be used to populate the user data from the database cluster. There are two options for representing the password, either plain text or encrypted passwords may be used. In order to use encrypted passwords a set of keys must be generated that will be used by the encryption and decryption process. To generate the keys use the maxkeys command and pass the name of the secrets file in which the keys are stored. ``` -maxkeys /usr/local/mariadb-maxscale/etc/.secrets +maxkeys /var/lib/maxscale/.secrets ``` Once the keys have been created the maxpasswd command can be used to generate the encrypted password. diff --git a/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md index b34fcf71e..93ecdd931 100644 --- a/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md @@ -70,7 +70,7 @@ If you wish to use two different usernames for the two different roles of monito ## Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file MaxScale.cnf in the directory /etc, if you have installed in the default location then this file is available in /usr/local/mariadb-maxscale/etc/MaxScale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within this directory that may be use as a basis for your configuration. +MaxScale configuration is held in an ini file that is located in the file maxscale.cnf in the directory /etc, if you have installed in the default location then this file is available in /etc/maxscale.cnf. This is not created as part of the installation process and must be manually created. A template file does exist within the /usr/share/maxscale directory that may be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. @@ -96,7 +96,7 @@ servers=dbserv1, dbserv2, dbserv3 The final step in the service sections is to add the username and password that will be used to populate the user data from the database cluster. There are two options for representing the password, either plain text or encrypted passwords may be used. In order to use encrypted passwords a set of keys must be generated that will be used by the encryption and decryption process. To generate the keys use the maxkeys command and pass the name of the secrets file in which the keys are stored. -% maxkeys /usr/local/mariadb-maxscale/etc/.secrets +% maxkeys /var/lib/maxscale/.secrets % @@ -226,7 +226,7 @@ or % service maxscale start -Check the error log in /usr/local/mariadb-maxscale/log to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. +Check the error log in /var/log/maxscale to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. % maxadmin -pmariadb list services diff --git a/Documentation/Tutorials/Nagios-Plugins.md b/Documentation/Tutorials/Nagios-Plugins.md index 9ee3a87c7..6c8cef9c2 100644 --- a/Documentation/Tutorials/Nagios-Plugins.md +++ b/Documentation/Tutorials/Nagios-Plugins.md @@ -44,7 +44,7 @@ In order to use these scripts on your Nagios Server, you need to copy them from MaxScale must be configured with 'maxscaled' protocol for the administration interface: -Example of MaxScale.cnf file: +Example of maxscale.cnf file: [AdminInterface] type=service @@ -73,9 +73,9 @@ and add (just after localhost.cfg or commnads.cfg) - modify server IP address in server1.cfg, pointing to MaxScale server - maxadmin executable must be in the nagios server - default MaxScale AdminInterface port is 6603 -- default maxadmin executable path is /usr/local/mariadb-maxscale/bin/maxadmin +- default maxadmin executable path is /usr/bin/maxadmin It can be changed by -m option -- maxadmin executable could be copied from an existing maxscale installation (default location is /usr/local/mariadb-maxscale/bin/maxadmin) +- maxadmin executable could be copied from an existing maxscale installation (default location is /usr/bin/maxadmin) This example shows configuration that needs to be done on Nagios server in order to communicate to MaxScale server that is running on host server1. In this example we are using the check_maxscale_resource as the check command diff --git a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md index 2f2903861..c66d8c1cb 100644 --- a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md +++ b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md @@ -193,7 +193,7 @@ This generates RPM or DEB packages based on your system. These packages can then ## Step 5 - Configure new applications -The new filter needs to be configured in MaxScale.cnf. +The new filter needs to be configured in maxscale.cnf. [Test Service] type=service diff --git a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md index 3f61638d1..b455ea86f 100644 --- a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md +++ b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md @@ -28,7 +28,7 @@ Using MaxScale as a replication proxy is much the same as using MaxScale as a pr ## Service Configuration -As with any MaxScale configuration a good starting point is with the service definition with the MaxScale.cnf file. The service requires a name which is the section name in the ini file, a type parameter with a value of service and the name of the router plugin that should be loaded. In the case of replication proxies this router name is binlogrouter. +As with any MaxScale configuration a good starting point is with the service definition with the maxscale.cnf file. The service requires a name which is the section name in the ini file, a type parameter with a value of service and the name of the router plugin that should be loaded. In the case of replication proxies this router name is binlogrouter. [Replication] diff --git a/Documentation/Tutorials/Simple-Sharding-Tutorial.md b/Documentation/Tutorials/Simple-Sharding-Tutorial.md index 6c16fa9f6..70b972de2 100644 --- a/Documentation/Tutorials/Simple-Sharding-Tutorial.md +++ b/Documentation/Tutorials/Simple-Sharding-Tutorial.md @@ -32,7 +32,7 @@ Upon successful completion of the installation command you will have MaxScale in ### Creating Your MaxScale Configuration -The first step in the creation of your MaxScale.cnf file is to define the global maxscale section. This section configures the number of threads MaxScale uses. A good rule of thumb is to use at most as may threads as you have CPUs. MaxScale uses few threads for internal operations so one or two threads less than the maximum should be enough. +The first step in the creation of your maxscale.cnf file is to define the global maxscale section. This section configures the number of threads MaxScale uses. A good rule of thumb is to use at most as may threads as you have CPUs. MaxScale uses few threads for internal operations so one or two threads less than the maximum should be enough. ``` [maxscale] @@ -92,7 +92,7 @@ After this we have a fully working configuration and we can move on to starting Upon completion of the configuration process MaxScale is ready to be started . This may either be done manually by running the maxscale command or via the service interface. The service scripts are located in the `/etc/init.d/` folder and are accessible through both the `service` and `systemctl` commands. -After starting MaxScale check the error log in /usr/local/mariadb-maxscale/log to see if any errors are detected in the configuration file. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. +After starting MaxScale check the error log in /var/log/maxscale to see if any errors are detected in the configuration file. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. MaxScale is now ready to start accepting client connections and routing them. Queries are routed to the right servers based on the database they target and switching between the shards is seamless since MaxScale keeps the session state intact between servers. diff --git a/Documentation/filters/Database-Firewall-Filter.md b/Documentation/filters/Database-Firewall-Filter.md index b448b65be..58eac1b6c 100644 --- a/Documentation/filters/Database-Firewall-Filter.md +++ b/Documentation/filters/Database-Firewall-Filter.md @@ -5,7 +5,7 @@ The database firewall filter is used to block queries that match a set of rules. ## Configuration -The database firewall filter only requires minimal configuration in the MaxScale.cnf file. The actual rules of the database firewall filter are located in a separate text file. The following is an example of a database firewall filter configuration in MaxScale.cnf. +The database firewall filter only requires minimal configuration in the maxscale.cnf file. The actual rules of the database firewall filter are located in a separate text file. The following is an example of a database firewall filter configuration in maxscale.cnf. ``` [DatabaseFirewall] diff --git a/Documentation/filters/Query-Log-All-Filter.md b/Documentation/filters/Query-Log-All-Filter.md index 87ea620c8..c06b97f6b 100644 --- a/Documentation/filters/Query-Log-All-Filter.md +++ b/Documentation/filters/Query-Log-All-Filter.md @@ -6,7 +6,7 @@ The Query Log All (QLA) filter is a filter module for MaxScale that is ## Configuration -The configuration block for the QLA filter requires the minimal filter options in it's section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. +The configuration block for the QLA filter requires the minimal filter options in it's section within the maxscale.cnf file, stored in /etc/maxscale.cnf. ``` [MyLogFilter] type=filter diff --git a/Documentation/filters/RabbitMQ-Filter.md b/Documentation/filters/RabbitMQ-Filter.md index 4068ceec5..3d04702ac 100644 --- a/Documentation/filters/RabbitMQ-Filter.md +++ b/Documentation/filters/RabbitMQ-Filter.md @@ -5,9 +5,9 @@ This filter is designed to extract queries and transform them into a canonical f ## Configuration -The configuration block for the **mqfilter** filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. Although the filter will start, it will use the default values which only work with a freshly installed RabbitMQ server and use its default values. This setup is mostly intended for testing the filter. +The configuration block for the **mqfilter** filter requires the minimal filter options in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf. Although the filter will start, it will use the default values which only work with a freshly installed RabbitMQ server and use its default values. This setup is mostly intended for testing the filter. -The following is an example of a mqfilter configuration in the MaxScale.cnf file used for actual logging of queries to a RabbitMQ broker on a different host. +The following is an example of a mqfilter configuration in the maxscale.cnf file used for actual logging of queries to a RabbitMQ broker on a different host. ``` [RabbitMQ] diff --git a/Documentation/filters/Regex-Filter.md b/Documentation/filters/Regex-Filter.md index 7b09ffcae..ce0f54358 100644 --- a/Documentation/filters/Regex-Filter.md +++ b/Documentation/filters/Regex-Filter.md @@ -6,7 +6,7 @@ The regex filter is a filter module for MaxScale that is able to rewrite query c # Configuration -The configuration block for the Regex filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. +The configuration block for the Regex filter requires the minimal filter options in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf. ``` [MyRegexFilter] diff --git a/Documentation/filters/Tee-Filter.md b/Documentation/filters/Tee-Filter.md index 868886ceb..c90fd814f 100644 --- a/Documentation/filters/Tee-Filter.md +++ b/Documentation/filters/Tee-Filter.md @@ -6,7 +6,7 @@ The tee filter is a filter module for MaxScale is a "plumbing" fitting in the Ma # Configuration -The configuration block for the TEE filter requires the minimal filter parameters in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf, that defines the filter to load and the service to send the duplicates to. Currently the tee filter does not support multi-statements. +The configuration block for the TEE filter requires the minimal filter parameters in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf, that defines the filter to load and the service to send the duplicates to. Currently the tee filter does not support multi-statements. ``` [DataMartFilter] diff --git a/Documentation/filters/Top-N-Filter.md b/Documentation/filters/Top-N-Filter.md index f52a6bedd..f20d9cc3c 100644 --- a/Documentation/filters/Top-N-Filter.md +++ b/Documentation/filters/Top-N-Filter.md @@ -6,7 +6,7 @@ The top filter is a filter module for MaxScale that monitors every SQL statement # Configuration -The configuration block for the TOP filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in /etc/MaxScale.cnf. +The configuration block for the TOP filter requires the minimal filter options in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf. ``` [MyLogFilter] From 49e72df4ab2a104be27bd02000fc57db01c8e7d6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Jun 2015 11:14:37 +0300 Subject: [PATCH 115/273] Fixed formatting. --- .../Tutorials/Administration-Tutorial.md | 65 +++++++++---------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index e3253fca6..17d7efe37 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -15,13 +15,13 @@ The purpose of this tutorial is to introduce the MaxScale Administrator to a few ### Starting MaxScale There are several ways to start MaxScale, the most convenient mechanism is probably using the Linux service interface. When a MaxScale package is installed the package manager will also installed a script in /etc/init.d which may be used to start and stop MaxScale either directly or via the service interface. - +``` $ service maxscale start - +``` or - +``` $ /etc/init.d/maxscale start - +``` It is also possible to start MaxScale by executing the maxscale command itself. Running the executable /usr/bin/maxscale will result in MaxScale running as a daemon process, unattached to the terminal in which it was started and using configuration files that it finds in the /etc directory. Options may be passed to the MaxScale binary that alter this default behavior, this options are documented in the table below. @@ -49,21 +49,21 @@ Switch|Long Option|Description There are numerous ways in which MaxScale can be stopped; using the service interface, killing the process or by use of the maxadmin utility. Stopping MaxScale with the service interface is simply a case of using the service stop command or calling the init.d script with the stop argument. - +``` $ service maxscale stop - +``` or - +``` $ /etc/init.d/maxscale stop - +``` MaxScale will also stop gracefully if it received a terminate signal, to find the process id of the MaxScale server use the ps command or read the contents of the maxscale.pid file located in the /var/run/maxscale directory. - +``` $ kill `cat /var/run/maxscale/maxscale.pid` - +``` In order to shutdown MaxScale using the maxadmin command you may either connect with maxadmin in interactive mode or pass the "shutdown maxscale" command you wish to execute as an argument to maxadmin. - +``` $ maxadmin -pmariadb shutdown maxscale - +``` ### Checking The Status Of The MaxScale Services @@ -98,7 +98,7 @@ It should be noted that network listeners count as a user of the service, theref ### What Clients Are Connected To MaxScale To determine what client are currently connected to MaxScale you can use the "list clients" command within maxadmin. This will give you IP address and the ID’s of the DCB and session for that connection. As with any maxadmin command this can be passed on the command line or typed interactively in maxadmin. - +``` $ maxadmin -pmariadb list clients Client Connections @@ -114,28 +114,27 @@ To determine what client are currently connected to MaxScale you can use the "li -----------------+------------------+----------------------+------------ $ - +``` ### Rotating Log Files MaxScale write log data into four log files with varying degrees of detail. With the exception of the error log, which can not be disabled, these log files may be enabled and disabled via the maxadmin interface or in the configuration file. The default behavior of MaxScale is to grow the log files indefinitely, the administrator must take action to prevent this. It is possible to rotate either a single log file or all the log files with a single command. When the logfile is rotated, the current log file is closed and a new log file, with an increased sequence number in its name, is created. Log file rotation is achieved by use of the "flush log" or “flush logs” command in maxadmin. - +``` $ maxadmin -pmariadb flush logs - +``` Flushes all of the logs, whereas an individual log may be flushed with the "flush log" command. - +``` $ maxadmin -pmariadb MaxScale> flush log error MaxScale> flush log trace MaxScale> - +``` This may be integrated into the Linux logrotate mechanism by adding a configuration file to the /etc/logrotate.d directory. If we assume we want to rotate the log files once per month and wish to keep 5 log files worth of history, the configuration file would look like the following. -
/usr/local/mariadb-maxscale/log/*.log { + /var/log/maxscale/*.log { monthly rotate 5 missingok nocompress sharedscripts postrotate -kill -USR1 `cat /usr/local/mariadb-maxscale/log/maxscale.pid` +kill -USR1 `cat /var/run/maxscale/maxscale.pid` endscript }
- - - -
/var/log/maxscale/*.log { +``` +/var/log/maxscale/*.log { monthly rotate 5 missingok @@ -147,16 +146,13 @@ if test -n "`ps acx|grep maxscale`"; then /usr/bin/maxadmin -pmariadb flush logs fi endscript -}
- +} +``` One disadvantage with this is that the password used for the maxadmin command has to be embedded in the log rotate configuration file. MaxScale will also rotate all of its log files if it receives the USR1 signal. Using this the logrotate configuration script can be rewritten as - - - - -
/var/log/maxscale/*.log { +``` +/var/log/maxscale/*.log { monthly rotate 5 missingok @@ -165,9 +161,8 @@ sharedscripts postrotate kill -USR1 `cat /var/run/maxscale/maxscale.pid` endscript -}
+} +``` ### Taking A Database Server Out Of Use @@ -175,16 +170,16 @@ endscript MaxScale supports the concept of maintenance mode for servers within a cluster, this allows for planned, temporary removal of a database from the cluster within the need to change the MaxScale configuration. To achieve the removal of a database server you can use the set server command in the maxadmin utility to set the maintenance mode flag for the server. This may be done interactively within maxadmin or by passing the command on the command line. - +``` MaxScale> set server dbserver3 maintenance MaxScale> - +``` This will cause MaxScale to stop routing any new requests to the server, however if there are currently requests executing on the server these will not be interrupted. To bring the server back into service use the "clear server" command to clear the maintenance mode bit for that server. - +``` MaxScale> clear server dbserver3 maintenance MaxScale> - +``` Note that maintenance mode is not persistent, if MaxScale restarts when a node is in maintenance mode a new instance of MaxScale will not honour this mode. If multiple MaxScale instances are configured to use the node them maintenance mode must be set within each MaxScale instance. However if multiple services within one MaxScale instance are using the server then you only need set the maintenance mode once on the server for all services to take note of the mode change. From 72d14f88940b452f0750f1c7f0b97cef929d5767 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Wed, 3 Jun 2015 10:15:48 +0200 Subject: [PATCH 116/273] Addition of server_update_address/port --- server/core/server.c | 39 +++++++++++++++++++++++++++++++++++++++ server/include/server.h | 3 +++ 2 files changed, 42 insertions(+) diff --git a/server/core/server.c b/server/core/server.c index 425089d93..9455adb94 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -32,6 +32,7 @@ * 26/06/14 Mark Riddoch Addition of server parameters * 30/08/14 Massimiliano Pinto Addition of new service status description * 30/10/14 Massimiliano Pinto Addition of SERVER_MASTER_STICKINESS description + * 01/06/15 Massimiliano Pinto Addition of server_update_address/port * * @endverbatim */ @@ -731,3 +732,41 @@ int *data; return set; } + +/* + * Update the address value of a specific server + * + * @param server The server to update + * @param address The new address + * + */ +void +server_update_address(SERVER *server, char *address) +{ + spinlock_acquire(&server_spin); + if (server && address) { + if (server->name) { + free(server->name); + } + server->name = strdup(address); + } + spinlock_release(&server_spin); +} + +/* + * Update the port value of a specific server + * + * @param server The server to update + * @param port The new port value + * + */ +void +server_update_port(SERVER *server, unsigned short port) +{ + spinlock_acquire(&server_spin); + if (server && port > 0) { + server->port = port; + } + spinlock_release(&server_spin); +} + diff --git a/server/include/server.h b/server/include/server.h index 734f30365..28011927f 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -43,6 +43,7 @@ * 30/08/14 Massimiliano Pinto Addition of SERVER_STALE_STATUS * 27/10/14 Massimiliano Pinto Addition of SERVER_MASTER_STICKINESS * 19/02/15 Mark Riddoch Addition of serverGetList + * 01/06/15 Massimiliano Pinto Addition of server_update_address/port * * @endverbatim */ @@ -189,5 +190,7 @@ extern void serverAddParameter(SERVER *, char *, char *); extern char *serverGetParameter(SERVER *, char *); extern void server_update(SERVER *, char *, char *, char *); extern void server_set_unique_name(SERVER *, char *); +extern void server_update_address(SERVER *, char *); +extern void server_update_port(SERVER *, unsigned short); extern RESULTSET *serverGetList(); #endif From 19ac70fc2fdb5f4276cd0ea4e62f85534c21b7e9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Jun 2015 13:15:45 +0300 Subject: [PATCH 117/273] Added unit tests for SSL. --- server/core/config.c | 46 ++++++++--- server/core/service.c | 12 ++- server/include/service.h | 1 + server/modules/protocol/CMakeLists.txt | 1 + server/modules/protocol/test/CMakeLists.txt | 10 +++ server/modules/protocol/test/bad_ca.cnf | 28 +++++++ server/modules/protocol/test/bad_cert.cnf | 28 +++++++ server/modules/protocol/test/bad_key.cnf | 28 +++++++ server/modules/protocol/test/bad_ssl.cnf | 28 +++++++ server/modules/protocol/test/no_ca.cnf | 28 +++++++ .../modules/protocol/test/no_server_cert.cnf | 28 +++++++ .../modules/protocol/test/no_server_key.cnf | 28 +++++++ server/modules/protocol/test/ok.cnf | 28 +++++++ server/modules/protocol/test/test_ssl.sh | 76 +++++++++++++++++++ 14 files changed, 360 insertions(+), 10 deletions(-) create mode 100644 server/modules/protocol/test/CMakeLists.txt create mode 100644 server/modules/protocol/test/bad_ca.cnf create mode 100644 server/modules/protocol/test/bad_cert.cnf create mode 100644 server/modules/protocol/test/bad_key.cnf create mode 100644 server/modules/protocol/test/bad_ssl.cnf create mode 100644 server/modules/protocol/test/no_ca.cnf create mode 100644 server/modules/protocol/test/no_server_cert.cnf create mode 100644 server/modules/protocol/test/no_server_key.cnf create mode 100644 server/modules/protocol/test/ok.cnf create mode 100755 server/modules/protocol/test/test_ssl.sh diff --git a/server/core/config.c b/server/core/config.c index 5e03a3b19..40be2c704 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -453,41 +453,69 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); if(ssl) { if(ssl_cert == NULL) + { + error_count++; skygw_log_write(LE,"Error: Server certificate missing for service '%s'." "Please provide the path to the server certificate by adding the ssl_cert= parameter", obj->object); + } if(ssl_ca_cert == NULL) + { + error_count++; skygw_log_write(LE,"Error: CA Certificate missing for service '%s'." "Please provide the path to the certificate authority certificate by adding the ssl_ca_cert= parameter", obj->object); + } if(ssl_key == NULL) + { + error_count++; skygw_log_write(LE,"Error: Server private key missing for service '%s'. " "Please provide the path to the server certificate key by adding the ssl_key= parameter" ,obj->object); + } - if(ssl_ca_cert != NULL && ssl_cert != NULL && ssl_key != NULL) + if(access(ssl_ca_cert,F_OK) != 0) { + skygw_log_write(LE,"Error: Certificate authority file for service '%s' not found: %s", + obj->object, + ssl_ca_cert); + error_count++; + } + if(access(ssl_cert,F_OK) != 0) + { + skygw_log_write(LE,"Error: Server certificate file for service '%s' not found: %s", + obj->object, + ssl_cert); + error_count++; + } + if(access(ssl_key,F_OK) != 0) + { + skygw_log_write(LE,"Error: Server private key file for service '%s' not found: %s", + obj->object, + ssl_key); + error_count++; + } + if(error_count == 0) + { if(serviceSetSSL(obj->element,ssl) != 0) { skygw_log_write(LE,"Error: Unknown parameter for service '%s': %s",obj->object,ssl); + error_count++; } else { serviceSetCertificates(obj->element,ssl_cert,ssl_key,ssl_ca_cert); if(ssl_version) { - serviceSetSSLVersion(obj->element,ssl_version); + if(serviceSetSSLVersion(obj->element,ssl_version) != 0) + { + skygw_log_write(LE,"Error: Unknown parameter value for 'ssl_version' for service '%s': %s",obj->object,ssl_version); + error_count++; + } } } } - else - { - /** If SSL was configured wrong, the - * service needs to fail.*/ - skygw_log_write_flush(LE,"Error: Missing SSL certificate paths found in the configuration. " - "This service will not use SSL."); - } } diff --git a/server/core/service.c b/server/core/service.c index a8df37f5b..7fc931297 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -864,12 +864,20 @@ serviceOptimizeWildcard(SERVICE *service, int action) void serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert) { + if(service->ssl_cert) + free(service->ssl_cert); service->ssl_cert = strdup(cert); + + if(service->ssl_key) + free(service->ssl_key); service->ssl_key = strdup(key); + + if(service->ssl_ca_cert) + free(service->ssl_ca_cert); service->ssl_ca_cert = strdup(ca_cert); } -void +int serviceSetSSLVersion(SERVICE *service, char* version) { if(strcasecmp(version,"SSLV2") == 0) @@ -884,6 +892,8 @@ serviceSetSSLVersion(SERVICE *service, char* version) service->ssl_method_type = SERVICE_TLS12; else if(strcasecmp(version,"MAX") == 0) service->ssl_method_type = SERVICE_SSL_TLS_MAX; + else return -1; + return 0; } /** Enable or disable the service SSL capability*/ int diff --git a/server/include/service.h b/server/include/service.h index 7975518e7..af71bfe7d 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -210,6 +210,7 @@ extern int serviceGetUser(SERVICE *, char **, char **); extern void serviceSetFilters(SERVICE *, char *); extern int serviceSetSSL(SERVICE *service, char* action); extern int serviceInitSSL(SERVICE* service); +extern int serviceSetSSLVersion(SERVICE *service, char* version); extern void serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert); extern int serviceEnableRootUser(SERVICE *, int ); extern int serviceSetTimeout(SERVICE *, int ); diff --git a/server/modules/protocol/CMakeLists.txt b/server/modules/protocol/CMakeLists.txt index 4ae3b8f2c..124071c44 100644 --- a/server/modules/protocol/CMakeLists.txt +++ b/server/modules/protocol/CMakeLists.txt @@ -17,6 +17,7 @@ install(TARGETS HTTPD DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_TESTS) add_library(testprotocol SHARED testprotocol.c) install(TARGETS testprotocol DESTINATION ${MAXSCALE_LIBDIR}) + add_subdirectory(test) endif() add_library(maxscaled SHARED maxscaled.c) diff --git a/server/modules/protocol/test/CMakeLists.txt b/server/modules/protocol/test/CMakeLists.txt new file mode 100644 index 000000000..f3eb3aa2b --- /dev/null +++ b/server/modules/protocol/test/CMakeLists.txt @@ -0,0 +1,10 @@ +configure_file(test_ssl.sh ${CMAKE_CURRENT_BINARY_DIR}/test_ssl.sh @ONLY) +configure_file(no_ca.cnf ${CMAKE_CURRENT_BINARY_DIR}/no_ca.cnf @ONLY) +configure_file(no_server_cert.cnf ${CMAKE_CURRENT_BINARY_DIR}/no_server_cert.cnf @ONLY) +configure_file(no_server_key.cnf ${CMAKE_CURRENT_BINARY_DIR}/no_server_key.cnf @ONLY) +configure_file(bad_ca.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_ca.cnf @ONLY) +configure_file(bad_cert.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_cert.cnf @ONLY) +configure_file(bad_key.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_key.cnf @ONLY) +configure_file(bad_ssl.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_ssl.cnf @ONLY) +configure_file(ok.cnf ${CMAKE_CURRENT_BINARY_DIR}/ok.cnf @ONLY) +add_test(NAME SSLTest COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_ssl.sh) diff --git a/server/modules/protocol/test/bad_ca.cnf b/server/modules/protocol/test/bad_ca.cnf new file mode 100644 index 000000000..9206679a9 --- /dev/null +++ b/server/modules/protocol/test/bad_ca.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +ssl_ca_cert=This is not a value +ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/bad_cert.cnf b/server/modules/protocol/test/bad_cert.cnf new file mode 100644 index 000000000..1b4c776cc --- /dev/null +++ b/server/modules/protocol/test/bad_cert.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +ssl_cert=This is not a value +ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/bad_key.cnf b/server/modules/protocol/test/bad_key.cnf new file mode 100644 index 000000000..4e0be5f05 --- /dev/null +++ b/server/modules/protocol/test/bad_key.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +ssl_key=This is not a value + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/bad_ssl.cnf b/server/modules/protocol/test/bad_ssl.cnf new file mode 100644 index 000000000..f6dcff1a1 --- /dev/null +++ b/server/modules/protocol/test/bad_ssl.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=testing +ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/no_ca.cnf b/server/modules/protocol/test/no_ca.cnf new file mode 100644 index 000000000..56f603f6a --- /dev/null +++ b/server/modules/protocol/test/no_ca.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +#ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/no_server_cert.cnf b/server/modules/protocol/test/no_server_cert.cnf new file mode 100644 index 000000000..f714a0b3f --- /dev/null +++ b/server/modules/protocol/test/no_server_cert.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +#ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/no_server_key.cnf b/server/modules/protocol/test/no_server_key.cnf new file mode 100644 index 000000000..a820ee414 --- /dev/null +++ b/server/modules/protocol/test/no_server_key.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +#ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/ok.cnf b/server/modules/protocol/test/ok.cnf new file mode 100644 index 000000000..089025c0d --- /dev/null +++ b/server/modules/protocol/test/ok.cnf @@ -0,0 +1,28 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/test_ssl.sh b/server/modules/protocol/test/test_ssl.sh new file mode 100755 index 000000000..632da0f3a --- /dev/null +++ b/server/modules/protocol/test/test_ssl.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +function create_certs() +{ + echo "CA cert" > @CMAKE_CURRENT_BINARY_DIR@/ca.pem + echo "Server Certificate" > @CMAKE_CURRENT_BINARY_DIR@/server-cert.pem + echo "Server Key" > @CMAKE_CURRENT_BINARY_DIR@/server-key.pem +} + +function start_maxscale () +{ + local result=$(@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale -d -f $1 &> $1.log;echo $?) + if [[ $result == "0" ]] + then + echo "Error: $1 exited with status $result!" + exit 1 + fi +} + +# No CA defined +printf "Testing No CA defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/no_ca.cnf +echo " OK" + +# No cert defined +printf "Testing No cert defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/no_cert.cnf +echo " OK" + +# No key defined +printf "Testing No key defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/no_key.cnf +echo " OK" + +# Bad SSL value +printf "Testing Bad SSL defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_ssl.cnf +echo " OK" + +# Bad CA defined +printf "Testing Bad CA defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_ca.cnf +echo " OK" + +# Bad cert defined +printf "Testing Bad cert defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_cert.cnf +echo " OK" + +# Bad key defined +printf "Testing Bad key defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_key.cnf +echo " OK" + +# No CA file +printf "Testing No CA file" +create_certs +rm @CMAKE_CURRENT_BINARY_DIR@/ca.pem +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/ok.cnf +echo " OK" + +# No cert file +printf "Testing No cert file" +create_certs +rm @CMAKE_CURRENT_BINARY_DIR@/server-cert.pem +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/ok.cnf +echo " OK" + +# No key file +printf "Testing No key file" +create_certs +rm @CMAKE_CURRENT_BINARY_DIR@/server-key.pem +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/ok.cnf +echo " OK" + +exit 0 From 4d30cd5fd3b9bdb9a8bb03ec742b158dd558ae94 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Jun 2015 13:28:35 +0300 Subject: [PATCH 118/273] Added unit test for SSL version. --- server/modules/protocol/test/CMakeLists.txt | 1 + .../modules/protocol/test/bad_ssl_version.cnf | 29 +++++++++++++++++++ server/modules/protocol/test/test_ssl.sh | 17 +++++++---- 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 server/modules/protocol/test/bad_ssl_version.cnf diff --git a/server/modules/protocol/test/CMakeLists.txt b/server/modules/protocol/test/CMakeLists.txt index f3eb3aa2b..26653901c 100644 --- a/server/modules/protocol/test/CMakeLists.txt +++ b/server/modules/protocol/test/CMakeLists.txt @@ -6,5 +6,6 @@ configure_file(bad_ca.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_ca.cnf @ONLY) configure_file(bad_cert.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_cert.cnf @ONLY) configure_file(bad_key.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_key.cnf @ONLY) configure_file(bad_ssl.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_ssl.cnf @ONLY) +configure_file(bad_ssl_version.cnf ${CMAKE_CURRENT_BINARY_DIR}/bad_ssl_version.cnf @ONLY) configure_file(ok.cnf ${CMAKE_CURRENT_BINARY_DIR}/ok.cnf @ONLY) add_test(NAME SSLTest COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_ssl.sh) diff --git a/server/modules/protocol/test/bad_ssl_version.cnf b/server/modules/protocol/test/bad_ssl_version.cnf new file mode 100644 index 000000000..6849b1904 --- /dev/null +++ b/server/modules/protocol/test/bad_ssl_version.cnf @@ -0,0 +1,29 @@ +[maxscale] +threads=1 +logdir=@CMAKE_CURRENT_BINARY_DIR@ +datadir=@CMAKE_CURRENT_BINARY_DIR@ +piddir=@CMAKE_CURRENT_BINARY_DIR@ +cachedir=@CMAKE_CURRENT_BINARY_DIR@ + +[Testservice] +type=service +router=readconnroute +servers=server1 +user=user +passwd=pwd +ssl=enabled +ssl_ca_cert=@CMAKE_CURRENT_BINARY_DIR@/ca +ssl_cert=@CMAKE_CURRENT_BINARY_DIR@/server-cert +ssl_key=@CMAKE_CURRENT_BINARY_DIR@/server-key +ssl_version=Don't use SSL, it's not needed! + +[Testlistener] +type=listener +service=Testservice +protocol=MySQLBackend +port=12345 + +[server1] +type=server +address=127.0.0.1 +port=4321 diff --git a/server/modules/protocol/test/test_ssl.sh b/server/modules/protocol/test/test_ssl.sh index 632da0f3a..b4b4c4d46 100755 --- a/server/modules/protocol/test/test_ssl.sh +++ b/server/modules/protocol/test/test_ssl.sh @@ -32,22 +32,27 @@ printf "Testing No key defined" start_maxscale @CMAKE_CURRENT_BINARY_DIR@/no_key.cnf echo " OK" -# Bad SSL value +# Bad SSL value defined printf "Testing Bad SSL defined" start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_ssl.cnf echo " OK" -# Bad CA defined +# Bad SSL version defined +printf "Testing Bad SSL version defined" +start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_ssl_version.cnf +echo " OK" + +# Bad CA value defined printf "Testing Bad CA defined" start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_ca.cnf echo " OK" -# Bad cert defined +# Bad server certificate defined printf "Testing Bad cert defined" start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_cert.cnf echo " OK" -# Bad key defined +# Bad server key defined printf "Testing Bad key defined" start_maxscale @CMAKE_CURRENT_BINARY_DIR@/bad_key.cnf echo " OK" @@ -59,14 +64,14 @@ rm @CMAKE_CURRENT_BINARY_DIR@/ca.pem start_maxscale @CMAKE_CURRENT_BINARY_DIR@/ok.cnf echo " OK" -# No cert file +# No server certificate file printf "Testing No cert file" create_certs rm @CMAKE_CURRENT_BINARY_DIR@/server-cert.pem start_maxscale @CMAKE_CURRENT_BINARY_DIR@/ok.cnf echo " OK" -# No key file +# No server key file printf "Testing No key file" create_certs rm @CMAKE_CURRENT_BINARY_DIR@/server-key.pem From a033cbf200ae059374f78fd09deef645306f4a86 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Jun 2015 14:14:05 +0300 Subject: [PATCH 119/273] Added more informative error messages when SSL handshake fails. --- server/core/dcb.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index ade2affed..95130299f 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -2789,7 +2789,7 @@ int dcb_create_SSL(DCB* dcb) int dcb_accept_SSL(DCB* dcb) { int rval,errnum; - + char errbuf[140]; rval = SSL_accept(dcb->ssl); switch(rval) @@ -2819,23 +2819,25 @@ int dcb_accept_SSL(DCB* dcb) rval = 0; LOGIF(LD,(skygw_log_write_flush(LD,"SSL_accept ongoing for %s@%s", - dcb->user, + dcb->user?dcb->user:"a connection from ", dcb->remote))); } else { rval = -1; + ERR_error_string(errnum,errbuf); skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept for %s@%s: %s", + "Error: Fatal error in SSL_accept for %s@%s: (SSL error code: %d) %s", dcb->user, dcb->remote, - ERR_error_string(errnum,NULL)); + errnum, + errbuf); } break; default: skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept, returned value was %d.", + "Error: Fatal library error in SSL_accept, returned value was %d.", rval); break; } @@ -2855,7 +2857,7 @@ int dcb_accept_SSL(DCB* dcb) int dcb_connect_SSL(DCB* dcb) { int rval,errnum; - + char errbuf[140]; rval = SSL_connect(dcb->ssl); switch(rval) @@ -2891,11 +2893,13 @@ int dcb_connect_SSL(DCB* dcb) else { rval = -1; + ERR_error_string(errnum,errbuf); skygw_log_write_flush(LE, - "Error: Fatal error in SSL_connect for %s@%s: %s", + "Error: Fatal error in SSL_accept for %s@%s: (SSL error code: %d) %s", dcb->user, dcb->remote, - ERR_error_string(errnum,NULL)); + errnum, + errbuf); } break; From fe5e40e397bf67b7f4a34d68721b5dc1805d5926 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Jun 2015 16:08:11 +0300 Subject: [PATCH 120/273] Added missing initialization of user and password variables in monitor_alloc. --- server/core/monitor.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/core/monitor.c b/server/core/monitor.c index ae9a64794..0f24e691d 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -81,6 +81,8 @@ MONITOR *mon; mon->handle = NULL; mon->databases = NULL; mon->password = NULL; + mon->user = NULL; + mon->password = NULL; mon->read_timeout = DEFAULT_READ_TIMEOUT; mon->write_timeout = DEFAULT_WRITE_TIMEOUT; mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT; From 98d98c589e5326306f6481c299f6ecb6ac2e0b35 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 13:30:36 +0300 Subject: [PATCH 121/273] Added an Lsyncd configuration file. --- etc/lsyncd.conf | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 etc/lsyncd.conf diff --git a/etc/lsyncd.conf b/etc/lsyncd.conf new file mode 100644 index 000000000..40e860675 --- /dev/null +++ b/etc/lsyncd.conf @@ -0,0 +1,30 @@ + +-- Lsyncd will log to these two files. +settings{ + logfile = "/var/log/maxscale/maxscale-ha.log", + statusFile = "/var/log/maxscale/maxscale-ha-status.log" +} + +-- Copy and paste the sync section and change the host value to add new remote targets. +sync{ +default.rsyncssh, + +-- This is where the maxscale.cnf file is copied from. +source="/etc", + +-- This is the user and host where the maxscale.cnf is copied to. +host="user@127.0.0.1", + +-- This is where the maxscale.cnf is copied to on the remote host. +targetdir="/etc", + +-- This is an optional section which defines a custom SSH port. Uncomment to enable. +-- ssh={port=2222}, + +-- These are values passed to rsync. Only change these if you know what you are doing. +rsync={ +compress=true, +_extra = {[[--filter=+ *maxscale.cnf]], + [[--filter=- **]]} +} +} From a032c94d25c6557cfce3bd835d8c89d10bc22f26 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 16:49:39 +0300 Subject: [PATCH 122/273] Added comments to SSL tests. --- server/modules/protocol/test/test_ssl.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/modules/protocol/test/test_ssl.sh b/server/modules/protocol/test/test_ssl.sh index b4b4c4d46..f14670689 100755 --- a/server/modules/protocol/test/test_ssl.sh +++ b/server/modules/protocol/test/test_ssl.sh @@ -17,6 +17,8 @@ function start_maxscale () fi } +# All test cases expect that MaxScale will not start with a bad configuration or missing certificates + # No CA defined printf "Testing No CA defined" start_maxscale @CMAKE_CURRENT_BINARY_DIR@/no_ca.cnf From dceccce2ef919d69b952fca810af621e6e68ebec Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 16:52:43 +0300 Subject: [PATCH 123/273] Changed gwbuf_length function to GWBUF_LENGTH macro in dcb_read_SSL. --- server/core/dcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 95130299f..ae77707b9 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1017,7 +1017,7 @@ int dcb_read_SSL( if(n < b) { gwbuf_rtrim(buffer,b - n); - ss_dassert(gwbuf_length(buffer) == n); + ss_dassert(GWBUF_LENGTH(buffer) == n); LOGIF(LD,(skygw_log_write(LD,"[%lu] SSL: Truncated buffer to correct size from %d to %d bytes.\n", b,gwbuf_length(buffer)))); } From 6f58f29494c2989330d02b4a36c7d2789dc53b56 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 19:14:53 +0300 Subject: [PATCH 124/273] Added a guide on how to have a HA setup of MaxScale with lsyncd. --- CMakeLists.txt | 1 + Documentation/Documentation-Contents.md | 1 + .../Reference/MaxScale-HA-with-lsyncd.md | 117 ++++++++++++++++++ etc/{lsyncd.conf => lsyncd_example.conf} | 0 4 files changed, 119 insertions(+) create mode 100644 Documentation/Reference/MaxScale-HA-with-lsyncd.md rename etc/{lsyncd.conf => lsyncd_example.conf} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 03cb541b6..d12de2908 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,7 @@ install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_VARDIR}/lib/maxscale install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES etc/lsyncd_example.conf DESTINATION ${MAXSCALE_SHAREDIR}) # Install startup scripts and ldconfig files if(WITH_SCRIPTS) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 1af3750c5..99f8ba38e 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -25,6 +25,7 @@ - [MaxAdmin](Reference/MaxAdmin.md) - [MaxScale HA with Corosync-Pacemaker](Reference/MaxScale-HA-with-Corosync-Pacemaker.md) + - [MaxScale HA with Lsyncd](Reference/MaxScale-HA-with-lsyncd.md) - [How Errors are Handled in MaxScale](Reference/How-errors-are-handled-in-MaxScale.md) - [Debug and Diagnostic Support](Reference/Debug-And-Diagnostic-Support.md) diff --git a/Documentation/Reference/MaxScale-HA-with-lsyncd.md b/Documentation/Reference/MaxScale-HA-with-lsyncd.md new file mode 100644 index 000000000..18ef233db --- /dev/null +++ b/Documentation/Reference/MaxScale-HA-with-lsyncd.md @@ -0,0 +1,117 @@ +# MaxScale HA with Lsyncd + +This document guides you in setting up multiple MaxScale instances and synchronizing the configuration files with Lsyncd. Lsyncd is a rsync wrapper which can synchronize files across the network. The lsyncd daemon uses a configuration file to control the files to synchronize and the remote targets where these files are synchronized to. + +Copying the configuration file and running the lsyncd daemon on all the hosts keeps all the configuration files in sync. Modifications in the configuration file on one of the hosts will be copied on the other hosts. This allows adinistrators to easily provide a highly available, disaster resistant MaxScale installation with up-to-date configuration files on all the hosts. + + +## Installing Lsyncd + +You can install lsyncd with either a package manager or by building from source code. This guide demonstrates installation using a package manager and those looking to build lsyncd from source should refer to its documentation: https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.1.x + +Installing with Yum: + +``` +yum install lsyncd +``` + +Installing with Apt: + +``` +apt-get install lsyncd +``` + +Lsyncd needs no further configuration after installation. All necessary options are configured in the configuration file passed to the lsyncd daemon. + +## Creating the Lsyncd configuration file + +Lsyncd uses a configuration file to determine where to read files from and where to synchronize them if changes in them occur. Lsyncd is written in Lua and the configuration file is also valid Lua code. + +Here is an example configuration file with descriptions on the meaning of the values in it. + +``` +-- Lsyncd will log to these two files. +settings{ + logfile = "/var/log/maxscale/maxscale-ha.log", + statusFile = "/var/log/maxscale/maxscale-ha-status.log" +} + +-- Copy and paste the sync section and change the host value to add new remote targets. +sync{ +default.rsyncssh, + +-- This is where the maxscale.cnf file is copied from. +source="/etc", + +-- This is the user and host where the maxscale.cnf is copied to. +host="user@127.0.0.1", + +-- This is where the maxscale.cnf is copied to on the remote host. +targetdir="/etc", + +-- This is an optional section which defines a custom SSH port. Uncomment to enable. +-- ssh={port=2222}, + +-- These are values passed to rsync. Only change these if you know what you are doing. +rsync={ + compress=true, + _extra = {[[--filter=+ *maxscale.cnf]], + [[--filter=- **]] + } + } +} +``` + +The most important part is the `sync` section which defines a target for synchronization. The `default.rsyncssh` tells lsyncd to synchronize files using SSH. + +The `source` parameter tells lsyncd where to read the files from. This should be the location of the maxscale.cnf file. The `host` parameter defines the host where the files should be synchronized to and the user account lsyncd should use when synchronizing the files. The `targetdir` parameter defines the local directory on the remote target where the files should be synchronized to. This value should be the location on the remote host where the maxscale.cnf file is searched from. By default, this is the `/etc` folder. + +The optional `ssh` parameter and its sub-parameter `port`define a custom port for the SSH connection. Most users do not need this parameterer. The `rsycn` parameter contains an arra of options that are passed to the rsycn executable. These should not be changed unless you specifically know what you are doing. For more information on the options passed to rsync read the rsync(1) manpage. + +You can add multiple remote targets by defining multiple `sync` sections. Here is an example with two sync sections defining different hosts that have MaxScale installed and whose configuration files should be kep in sync. + +``` +settings{ + logfile = "/var/log/maxscale/maxscale-ha.log", + statusFile = "/var/log/maxscale/maxscale-ha-status.log" +} + +sync{ +default.rsyncssh, +source="/etc", +host="maxuser@192.168.0.50", +targetdir="/etc", +rsync={ + compress=true, + _extra = {[[--filter=+ *maxscale.cnf]], + [[--filter=- **]] + } + } +} + + +sync{ +default.rsyncssh, +source="/etc", +host="syncuser@192.168.122.105", +targetdir="/etc", +rsync={ + compress=true, + _extra = {[[--filter=+ *maxscale.cnf]], + [[--filter=- **]] + } + } +} +``` + +## Starting Lsyncd + +Starting lsyncd can be done from the command line or through a init script. To start syncd from the command like, execute the `lsyncd` command and pass the configuration file as the only parameter. + +Here is an example which start lsyncd and reads the configuration options from the `lsyncd.cnf` file. + +``` +lsyncd lsyncd.cnf +``` + +For more information on the lsyncd executable and its options, please see the --help output of lsyncd or the lsyncd(1) manpage. diff --git a/etc/lsyncd.conf b/etc/lsyncd_example.conf similarity index 100% rename from etc/lsyncd.conf rename to etc/lsyncd_example.conf From ce570685cda436d6a9b48e7ca39adde7c5b162c6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 19:31:58 +0300 Subject: [PATCH 125/273] Moved assertions around. --- server/core/dcb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index ae77707b9..653799fa6 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1014,12 +1014,14 @@ int dcb_read_SSL( } } - if(n < b) + if(n > 0 && b > 0 && n < b) { gwbuf_rtrim(buffer,b - n); - ss_dassert(GWBUF_LENGTH(buffer) == n); LOGIF(LD,(skygw_log_write(LD,"[%lu] SSL: Truncated buffer to correct size from %d to %d bytes.\n", b,gwbuf_length(buffer)))); + LOGIF(LD,(skygw_log_sync_all())); + ss_dassert(GWBUF_LENGTH(buffer) == n); + } nread += n; From cba37d2ac398ef31bf916ceb611b1ca45f922eb3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 19:38:33 +0300 Subject: [PATCH 126/273] Generated packages now have debug symbols. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 03cb541b6..4f35031d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,7 @@ if(PACKAGE) else() # Generic CPack configuration variables + set(CPACK_STRIP_FILES FALSE) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale") set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}") From 1f45eff135c4e657e6b0513790d25b3ff784561e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 19:38:33 +0300 Subject: [PATCH 127/273] Generated packages now have debug symbols. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 03cb541b6..4f35031d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,7 @@ if(PACKAGE) else() # Generic CPack configuration variables + set(CPACK_STRIP_FILES FALSE) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale") set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}") From cc1f720ea3e3fc6b992c75af28f200d35f2770de Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 4 Jun 2015 21:12:16 +0300 Subject: [PATCH 128/273] Removed log flushing on every dcb_read_SSL if debug log is enabled. --- server/core/dcb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 653799fa6..1a7727ff8 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1019,7 +1019,11 @@ int dcb_read_SSL( gwbuf_rtrim(buffer,b - n); LOGIF(LD,(skygw_log_write(LD,"[%lu] SSL: Truncated buffer to correct size from %d to %d bytes.\n", b,gwbuf_length(buffer)))); - LOGIF(LD,(skygw_log_sync_all())); + LOGIF(LD, + if(GWBUF_LENGTH(buffer) != n){ + skygw_log_sync_all(); + } + ); ss_dassert(GWBUF_LENGTH(buffer) == n); } From e83799648a757d31c4b641365516de34b71ae321 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 5 Jun 2015 11:00:51 +0300 Subject: [PATCH 129/273] Fixed queries getting stuck when the SSL records were of the maximum allowed size. --- server/core/dcb.c | 85 ++++++++++++++------------ server/core/service.c | 1 - server/modules/protocol/mysql_client.c | 2 +- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 1a7727ff8..c1e77b58d 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -898,7 +898,7 @@ int dcb_read_SSL( GWBUF **head) { GWBUF *buffer = NULL; - int b; + int b,pending; int rc; int n; int nread = 0; @@ -918,9 +918,9 @@ int dcb_read_SSL( while (true) { int bufsize; - + int ssl_errno = 0; rc = ioctl(dcb->fd, FIONREAD, &b); - + pending = SSL_pending(dcb->ssl); if (rc == -1) { LOGIF(LE, (skygw_log_write_flush( @@ -936,7 +936,7 @@ int dcb_read_SSL( goto return_n; } - if (b == 0 && nread == 0) + if (b == 0 && pending == 0 && nread == 0) { /** Handle closed client socket */ if (dcb_isclient(dcb)) @@ -948,14 +948,20 @@ int dcb_read_SSL( r = SSL_peek(dcb->ssl, &c, sizeof(char)); if (r <= 0) { + ssl_errno = SSL_get_error(dcb->ssl,r); + if(ssl_errno != SSL_ERROR_WANT_READ && + ssl_errno != SSL_ERROR_WANT_WRITE && + ssl_errno != SSL_ERROR_NONE) + { n = -1; - goto return_n; + } + goto return_n; } } n = 0; goto return_n; } - else if (b == 0) + else if (b == 0 && pending == 0) { n = 0; goto return_n; @@ -984,39 +990,36 @@ int dcb_read_SSL( goto return_n; } - int npending; - n = 0; - do - { - n += SSL_read(dcb->ssl, GWBUF_DATA(buffer), bufsize); + n = SSL_read(dcb->ssl, GWBUF_DATA(buffer), bufsize); dcb->stats.n_reads++; - }while((npending = SSL_pending(dcb->ssl)) > 0); - int ssl_errno = 0; - - if (n <= 0) - { - ssl_errno = ERR_get_error(); - - if(ssl_errno != SSL_ERROR_WANT_READ && ssl_errno != SSL_ERROR_NONE) + if (n <= 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Read failed, dcb %p in state " - "%s fd %d: %s.", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ERR_error_string(ssl_errno,NULL)))); + ssl_errno = SSL_get_error(dcb->ssl,n); - gwbuf_free(buffer); - goto return_n; - } + if(ssl_errno != SSL_ERROR_WANT_READ && + ssl_errno != SSL_ERROR_WANT_WRITE && + ssl_errno != SSL_ERROR_NONE) + { + char errbuf[200]; + ERR_error_string(ssl_errno,errbuf); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Read failed, dcb %p in state " + "%s fd %d, SSL error %d: %s.", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno, + errbuf))); + + gwbuf_free(buffer); + goto return_n; + } } - if(n > 0 && b > 0 && n < b) - { - gwbuf_rtrim(buffer,b - n); + buffer->end = buffer->start + n; +#ifdef SS_DEBUG LOGIF(LD,(skygw_log_write(LD,"[%lu] SSL: Truncated buffer to correct size from %d to %d bytes.\n", b,gwbuf_length(buffer)))); LOGIF(LD, @@ -1025,9 +1028,7 @@ int dcb_read_SSL( } ); ss_dassert(GWBUF_LENGTH(buffer) == n); - - } - +#endif nread += n; LOGIF(LD, (skygw_log_write( @@ -1039,11 +1040,19 @@ int dcb_read_SSL( dcb, STRDCBSTATE(dcb->state), dcb->fd))); + /*< Append read data to the gwbuf */ *head = gwbuf_append(*head, buffer); - if(ssl_errno == SSL_ERROR_WANT_READ || ssl_errno == SSL_ERROR_NONE || - ssl_errno == SSL_ERROR_WANT_X509_LOOKUP || SSL_ERROR_WANT_WRITE) + rc = ioctl(dcb->fd, FIONREAD, &b); + pending = SSL_pending(dcb->ssl); + + if(ssl_errno == SSL_ERROR_WANT_READ || + ssl_errno == SSL_ERROR_WANT_WRITE || + (b == 0 && pending == 0)) + { break; + } + } /*< while (true) */ return_n: return n; diff --git a/server/core/service.c b/server/core/service.c index 7fc931297..f4880107d 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -1875,7 +1875,6 @@ int serviceInitSSL(SERVICE* service) } service->ctx = SSL_CTX_new(service->method); - SSL_CTX_set_read_ahead(service->ctx,1); if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { skygw_log_write(LE,"Error: Failed to set server SSL certificate."); return -1; diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 45fc65ee0..5a54ca4b3 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -789,7 +789,7 @@ int gw_read_client_event( nbytes_read = gwbuf_length(dcb->dcb_readqueue); data = (uint8_t *)GWBUF_DATA(dcb->dcb_readqueue); int plen = MYSQL_GET_PACKET_LEN(data); - if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data)) + if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data) + 4) { rc = 0; goto return_rc; From 518ef5050e5ef687c05eb83aa5cb068d78a7f31a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 5 Jun 2015 12:15:19 +0300 Subject: [PATCH 130/273] Fixed debug asserts. --- server/core/dcb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index c1e77b58d..c2f48b39d 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1018,15 +1018,16 @@ int dcb_read_SSL( } } - buffer->end = buffer->start + n; + gwbuf_rtrim(buffer,bufsize - n); #ifdef SS_DEBUG LOGIF(LD,(skygw_log_write(LD,"[%lu] SSL: Truncated buffer to correct size from %d to %d bytes.\n", - b,gwbuf_length(buffer)))); + b,GWBUF_LENGTH(buffer)))); LOGIF(LD, if(GWBUF_LENGTH(buffer) != n){ skygw_log_sync_all(); } ); + ss_info_dassert((buffer->start <= buffer->end),"Buffer start has passed end."); ss_dassert(GWBUF_LENGTH(buffer) == n); #endif nread += n; From 61b1f3467160df1cb1f6e364ac4b860eaf81d0c0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 5 Jun 2015 18:52:44 +0300 Subject: [PATCH 131/273] Added more descriptive debug output. --- server/core/dcb.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index c2f48b39d..728211abe 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1020,13 +1020,14 @@ int dcb_read_SSL( gwbuf_rtrim(buffer,bufsize - n); #ifdef SS_DEBUG - LOGIF(LD,(skygw_log_write(LD,"[%lu] SSL: Truncated buffer to correct size from %d to %d bytes.\n", - b,GWBUF_LENGTH(buffer)))); - LOGIF(LD, - if(GWBUF_LENGTH(buffer) != n){ - skygw_log_sync_all(); - } - ); + skygw_log_write(LD,"[%lu] SSL: Truncated buffer from %d to %d bytes. " + "Read %d bytes, %d bytes waiting.\n", + bufsize,GWBUF_LENGTH(buffer),n,b); + + if(GWBUF_LENGTH(buffer) != n){ + skygw_log_sync_all(); + } + ss_info_dassert((buffer->start <= buffer->end),"Buffer start has passed end."); ss_dassert(GWBUF_LENGTH(buffer) == n); #endif From 1989a1482c3516cea06162b1aec640beadb6f1f3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 5 Jun 2015 19:37:33 +0300 Subject: [PATCH 132/273] Fixed empty reads causing a debug assert with large packets. --- server/core/dcb.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 728211abe..6beaec55d 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -966,6 +966,13 @@ int dcb_read_SSL( n = 0; goto return_n; } +#ifdef SS_DEBUG + else + { + skygw_log_write_flush(LD,"Total: %d Socket: %d Pending: %d", + nread,b,pending); + } +#endif dcb->last_read = hkheartbeat; @@ -993,7 +1000,7 @@ int dcb_read_SSL( n = SSL_read(dcb->ssl, GWBUF_DATA(buffer), bufsize); dcb->stats.n_reads++; - if (n <= 0) + if (n < 0) { ssl_errno = SSL_get_error(dcb->ssl,n); @@ -1016,12 +1023,17 @@ int dcb_read_SSL( gwbuf_free(buffer); goto return_n; } - } + } + else if(n == 0) + { + gwbuf_free(buffer); + goto return_n; + } gwbuf_rtrim(buffer,bufsize - n); #ifdef SS_DEBUG skygw_log_write(LD,"[%lu] SSL: Truncated buffer from %d to %d bytes. " - "Read %d bytes, %d bytes waiting.\n", + "Read %d bytes, %d bytes waiting.\n",pthread_self(), bufsize,GWBUF_LENGTH(buffer),n,b); if(GWBUF_LENGTH(buffer) != n){ @@ -1029,7 +1041,7 @@ int dcb_read_SSL( } ss_info_dassert((buffer->start <= buffer->end),"Buffer start has passed end."); - ss_dassert(GWBUF_LENGTH(buffer) == n); + ss_info_dassert(GWBUF_LENGTH(buffer) == n,"Buffer size not equal to read bytes."); #endif nread += n; From d19ccc6f846ec6d54f6ad2171acdee08a48aef63 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 5 Jun 2015 20:36:04 +0300 Subject: [PATCH 133/273] Fixed SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE and SSL_ERROR_NONE causing a debug assert. --- server/core/dcb.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 6beaec55d..538b0531a 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1002,13 +1002,21 @@ int dcb_read_SSL( if (n < 0) { + char errbuf[200]; ssl_errno = SSL_get_error(dcb->ssl,n); - - if(ssl_errno != SSL_ERROR_WANT_READ && - ssl_errno != SSL_ERROR_WANT_WRITE && - ssl_errno != SSL_ERROR_NONE) +#ifdef SS_DEBUG + ERR_error_string(ssl_errno,errbuf); + skygw_log_write_flush(LD,"[%lu]SSL error %d: %s", + pthread_self(),ssl_errno,errbuf); +#endif + if(ssl_errno == SSL_ERROR_WANT_READ || + ssl_errno == SSL_ERROR_WANT_WRITE || + ssl_errno == SSL_ERROR_NONE) + { + n = 0; + } + else { - char errbuf[200]; ERR_error_string(ssl_errno,errbuf); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -1019,10 +1027,10 @@ int dcb_read_SSL( dcb->fd, ssl_errno, errbuf))); - - gwbuf_free(buffer); - goto return_n; } + + gwbuf_free(buffer); + goto return_n; } else if(n == 0) { From b8e55fe28d24327fd1b7d1641cdd2c736e8f26ad Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 7 Jun 2015 12:37:45 +0300 Subject: [PATCH 134/273] Fixed SSL_accept failing when more data was in the socket buffer than was used. --- server/core/dcb.c | 98 +++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 538b0531a..e7862dbd4 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -2825,60 +2825,66 @@ int dcb_create_SSL(DCB* dcb) */ int dcb_accept_SSL(DCB* dcb) { - int rval,errnum; + int rval = 0,ssl_rval,errnum,fd,b = 0; char errbuf[140]; - rval = SSL_accept(dcb->ssl); - - switch(rval) + fd = dcb->fd; + ioctl(fd,FIONREAD,&b); + while(b > 0 && rval != -1) { - case 0: - errnum = SSL_get_error(dcb->ssl,rval); - LOGIF(LD,(skygw_log_write_flush(LD,"SSL_accept shutdown for %s@%s", - dcb->user, - dcb->remote))); - return -1; - break; - case 1: - rval = 1; - LOGIF(LD,(skygw_log_write_flush(LD,"SSL_accept done for %s@%s", - dcb->user, - dcb->remote))); - break; + ssl_rval = SSL_accept(dcb->ssl); - case -1: - errnum = SSL_get_error(dcb->ssl,rval); - - if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE || - errnum == SSL_ERROR_WANT_X509_LOOKUP) + switch(ssl_rval) { - /** Not all of the data has been read. Go back to the poll - queue and wait for more.*/ - - rval = 0; - LOGIF(LD,(skygw_log_write_flush(LD,"SSL_accept ongoing for %s@%s", - dcb->user?dcb->user:"a connection from ", - dcb->remote))); - } - else - { - rval = -1; + case 0: + errnum = SSL_get_error(dcb->ssl,ssl_rval); ERR_error_string(errnum,errbuf); + LOGIF(LD,(skygw_log_write_flush(LD,"[%p] SSL_accept shutdown for %s:%s", + dcb, + dcb->remote, + errbuf))); + rval = -1; + break; + case 1: + rval = 1; + LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept done for %s", + dcb->remote))); + break; + + case -1: + errnum = SSL_get_error(dcb->ssl,ssl_rval); + + if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE) + { + /** Not all of the data has been read. Go back to the poll + queue and wait for more.*/ + rval = 0; + LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept ongoing for %s", + dcb->remote))); + return rval; + } + else + { + rval = -1; + ERR_error_string(errnum,errbuf); + skygw_log_write_flush(LE, + "Error: Fatal error in SSL_accept for %s: (SSL error code: %d) %s", + dcb->remote, + errnum, + errbuf); + } + break; + + default: skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept for %s@%s: (SSL error code: %d) %s", - dcb->user, - dcb->remote, - errnum, - errbuf); + "Error: Fatal library error in SSL_accept, returned value was %d.", + ssl_rval); + rval = -1; + break; } - break; - - default: - skygw_log_write_flush(LE, - "Error: Fatal library error in SSL_accept, returned value was %d.", - rval); - break; + ioctl(fd,FIONREAD,&b); + if(LOG_IS_ENABLED(LD) && b > 0) + skygw_log_write_flush(LD,"[dcb_accept_SSL] FD %d has %d bytes ",fd,b); } - return rval; } From 61ea0861ff8f403f14915218ceee3226e5691005 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 8 Jun 2015 14:35:31 +0300 Subject: [PATCH 135/273] Fixed some connections hanging with SSL. --- server/modules/protocol/mysql_client.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 5a54ca4b3..dc5c08d89 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -661,7 +661,6 @@ int gw_read_client_event( return 0; break; case 1: - return 0; break; case -1: return 1; @@ -1946,4 +1945,4 @@ int do_ssl_accept(MySQLProtocol* protocol) } return rval; -} \ No newline at end of file +} From 1c36cfb28592a549d526a97ea47a6064afba278b Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 8 Jun 2015 18:04:43 +0300 Subject: [PATCH 136/273] Added more debug output. --- server/core/dcb.c | 15 +++++++++++---- .../include/mysql_client_server_protocol.h | 4 ++-- server/modules/protocol/mysql_client.c | 11 +++++++++++ server/modules/protocol/mysql_common.c | 6 +++++- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index e7862dbd4..cc8d77259 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -2825,14 +2825,19 @@ int dcb_create_SSL(DCB* dcb) */ int dcb_accept_SSL(DCB* dcb) { - int rval = 0,ssl_rval,errnum,fd,b = 0; + int rval = 0,ssl_rval,errnum = 0,fd,b = 0; char errbuf[140]; fd = dcb->fd; ioctl(fd,FIONREAD,&b); +#ifdef SS_DEBUG + skygw_log_write(LD,"[dcb_accept_SSL] fd %d bytes: %d",fd,b); +#endif while(b > 0 && rval != -1) { ssl_rval = SSL_accept(dcb->ssl); - +#ifdef SS_DEBUG + skygw_log_write(LD,"[dcb_accept_SSL] SSL_accept returned %d.",ssl_rval); +#endif switch(ssl_rval) { case 0: @@ -2882,8 +2887,10 @@ int dcb_accept_SSL(DCB* dcb) break; } ioctl(fd,FIONREAD,&b); - if(LOG_IS_ENABLED(LD) && b > 0) - skygw_log_write_flush(LD,"[dcb_accept_SSL] FD %d has %d bytes ",fd,b); +#ifdef SS_DEBUG + skygw_log_write_flush(LD,"[dcb_accept_SSL] fd %d: %d bytes",fd,b); + skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL error: %d",errnum); +#endif } return rval; } diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index f72416491..905f57dd3 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -321,7 +321,7 @@ typedef struct { #define MYSQL_IS_CHANGE_USER(payload) (MYSQL_GET_COMMAND(payload)==0x11) #define MYSQL_GET_NATTR(payload) ((int)payload[4]) -#endif /** _MYSQL_PROTOCOL_H */ + MySQLProtocol* mysql_protocol_init(DCB* dcb, int fd); void mysql_protocol_done (DCB* dcb); @@ -417,4 +417,4 @@ void init_response_status ( int* npackets, ssize_t* nbytes); - +#endif /** _MYSQL_PROTOCOL_H */ \ No newline at end of file diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index dc5c08d89..fcbb1958e 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -652,9 +652,16 @@ int gw_read_client_event( protocol = DCB_PROTOCOL(dcb, MySQLProtocol); CHK_PROTOCOL(protocol); +#ifdef SS_DEBUG + skygw_log_write(LD,"[gw_read_client_event] Protocol state: %s", + gw_mysql_protocol_state2string(protocol->protocol_auth_state)); + +#endif + if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_ONGOING || protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) { + switch(do_ssl_accept(protocol)) { case 0: @@ -1943,6 +1950,10 @@ int do_ssl_accept(MySQLProtocol* protocol) rval); break; } +#ifdef SS_DEBUG + skygw_log_write(LD,"[do_ssl_accept] Protocol state: %s", + gw_mysql_protocol_state2string(protocol->protocol_auth_state)); +#endif return rval; } diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 0a1d2195b..d8f9a96ba 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -890,7 +890,11 @@ gw_mysql_protocol_state2string (int state) { case MYSQL_AUTH_FAILED: return "MySQL Authentication failed"; case MYSQL_IDLE: - return "MySQL authentication is succesfully done."; + return "MySQL authentication is succesfully done."; + case MYSQL_AUTH_SSL_REQ: return "MYSQL_AUTH_SSL_REQ"; + case MYSQL_AUTH_SSL_HANDSHAKE_DONE: return "MYSQL_AUTH_SSL_HANDSHAKE_DONE"; + case MYSQL_AUTH_SSL_HANDSHAKE_FAILED: return "MYSQL_AUTH_SSL_HANDSHAKE_FAILED"; + case MYSQL_AUTH_SSL_HANDSHAKE_ONGOING: return "MYSQL_AUTH_SSL_HANDSHAKE_ONGOING"; default: return "MySQL (unknown protocol state)"; } From 06c5da7b1728609f3d05610cfaf09a74c2dea4bc Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 9 Jun 2015 02:56:55 +0300 Subject: [PATCH 137/273] Minor fix to SSL authentication. --- server/core/dcb.c | 19 +++++++------------ server/modules/protocol/mysql_client.c | 13 +++++++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index cc8d77259..cfee65371 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1055,7 +1055,7 @@ int dcb_read_SSL( LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, - "%lu [dcb_read] Read %d bytes from dcb %p in state %s " + "%lu [dcb_read_SSL] Read %d bytes from dcb %p in state %s " "fd %d.", pthread_self(), n, @@ -2800,7 +2800,7 @@ int dcb_create_SSL(DCB* dcb) if((dcb->ssl = SSL_new(dcb->service->ctx)) == NULL) { - skygw_log_write(LE,"Error: Failed to initialize SSL connection."); + skygw_log_write(LE,"Error: Failed to initialize SSL for connection."); return -1; } @@ -2828,16 +2828,10 @@ int dcb_accept_SSL(DCB* dcb) int rval = 0,ssl_rval,errnum = 0,fd,b = 0; char errbuf[140]; fd = dcb->fd; - ioctl(fd,FIONREAD,&b); -#ifdef SS_DEBUG - skygw_log_write(LD,"[dcb_accept_SSL] fd %d bytes: %d",fd,b); -#endif - while(b > 0 && rval != -1) + + do { ssl_rval = SSL_accept(dcb->ssl); -#ifdef SS_DEBUG - skygw_log_write(LD,"[dcb_accept_SSL] SSL_accept returned %d.",ssl_rval); -#endif switch(ssl_rval) { case 0: @@ -2889,9 +2883,10 @@ int dcb_accept_SSL(DCB* dcb) ioctl(fd,FIONREAD,&b); #ifdef SS_DEBUG skygw_log_write_flush(LD,"[dcb_accept_SSL] fd %d: %d bytes",fd,b); - skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL error: %d",errnum); + skygw_log_write(LD,"[dcb_accept_SSL] SSL_accept returned %d, SSL error: %d",ssl_rval,errnum); #endif - } + }while(b > 0 && rval != -1); + return rval; } diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index fcbb1958e..da5f04732 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -668,7 +668,18 @@ int gw_read_client_event( return 0; break; case 1: + { + int b = 0; + ioctl(dcb->fd,FIONREAD,&b); + if(b == 0) + { + skygw_log_write(LD, + "[gw_read_client_event] No data in socket after SSL auth"); + return 0; + } break; + } + case -1: return 1; break; @@ -1897,7 +1908,9 @@ int do_ssl_accept(MySQLProtocol* protocol) if(dcb->ssl == NULL) { if(dcb_create_SSL(dcb) != 0) + { return -1; + } } rval = dcb_accept_SSL(dcb); From 528e69b726229f9489f4f7edf81eb17bd73820d2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 9 Jun 2015 12:51:43 +0300 Subject: [PATCH 138/273] Added a warning about 1.2 changes to the postinstall script --- etc/postinst.in | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/etc/postinst.in b/etc/postinst.in index abf2db1ef..82886990f 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -37,3 +37,17 @@ then cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service /usr/lib/systemd/system fi /sbin/ldconfig + +cat <& 2 +********** Notice: MaxScale 1.2 Changes ************** + +MaxScale 1.2 has changed the default installation locations +and various files have changed locations. The configuration +file is now read from /etc/maxscale.cnf (note the lower case name) +and MaxScale data is in /var/lib/maxscale/. + +The default location of binary log files and the authentication cache changed from +/usr/local/mariadb-maxscale/ to /var/lib/maxscale/. + +****************************************************** +EOF From ab120cb1de746b321f5ac662daf39aafddf40465 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 9 Jun 2015 17:04:51 +0300 Subject: [PATCH 139/273] Added Diffie-Hellman key exchange for MaxScale. --- server/core/service.c | 53 +++++++++++++++++++++++++++++++++++++++- server/include/service.h | 1 + 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/server/core/service.c b/server/core/service.c index f4880107d..826c79e66 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -91,7 +91,7 @@ static SPINLOCK service_spin = SPINLOCK_INIT; static SERVICE *allServices = NULL; static int find_type(typelib_t* tl, const char* needle, int maxlen); - +DH *ssl_get_dh2236(); static void service_add_qualified_param( SERVICE* svc, CONFIG_PARAMETER* param); @@ -1841,6 +1841,8 @@ int *data; int serviceInitSSL(SERVICE* service) { + DH* dh; + if(!service->ssl_init_done) { switch(service->ssl_method_type) @@ -1875,6 +1877,14 @@ int serviceInitSSL(SERVICE* service) } service->ctx = SSL_CTX_new(service->method); + + /** Enable the Diffie-Hellman algorithms */ + if((dh = ssl_get_dh2236()) != NULL) + { + SSL_CTX_set_tmp_dh(service->ctx,dh); + DH_free(dh); + } + if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { skygw_log_write(LE,"Error: Failed to set server SSL certificate."); return -1; @@ -1908,3 +1918,44 @@ int serviceInitSSL(SERVICE* service) } return 0; } + +DH *ssl_get_dh2236() +{ + static unsigned char dh2236_p[]={ + 0x0B,0xC3,0xEC,0x3F,0xCB,0xD0,0x2E,0x43,0x7B,0x13,0xF9,0x0C, + 0x4D,0xE5,0xA3,0xA4,0xDB,0x68,0x13,0xBD,0xFC,0xD2,0x35,0x05, + 0xCB,0x62,0xA1,0x85,0x33,0x20,0xC4,0x88,0x3B,0x2B,0xD5,0x76, + 0x94,0xCD,0xEB,0x9C,0x5A,0xD1,0x16,0xDB,0x51,0x82,0x7A,0x1E, + 0xC6,0xC3,0xD9,0x52,0x8F,0x54,0x33,0xF4,0x50,0x96,0x01,0xF4, + 0x71,0xA1,0x8B,0x9B,0x43,0x85,0x9C,0x95,0xFF,0x53,0x1D,0x8D, + 0xDF,0xBC,0x60,0xEB,0x4D,0x96,0xD1,0x05,0x98,0x4A,0xEB,0xC9, + 0x33,0xF6,0xE9,0x74,0x73,0x29,0x27,0xCA,0x0D,0x6D,0xEA,0x36, + 0xB9,0x3B,0x54,0xF6,0x34,0x68,0x13,0xFA,0xAC,0x3B,0x57,0x55, + 0x76,0x41,0x67,0x48,0xEF,0x3C,0xE1,0xE1,0xAF,0x3C,0x68,0x05, + 0x9C,0x32,0xD9,0x14,0x8F,0xB2,0xEE,0xEE,0xBA,0x9F,0x0D,0x75, + 0xA7,0x33,0x1F,0x3A,0x0E,0xD1,0xA6,0x5A,0x29,0xC7,0x9B,0x5E, + 0x46,0xB1,0xA6,0xA5,0x1E,0x32,0xDB,0xAF,0x23,0x83,0x94,0x12, + 0x4F,0xE4,0xC2,0x8B,0x1B,0x2C,0x01,0x79,0x92,0x21,0xFF,0x01, + 0xED,0x46,0x27,0xF0,0x70,0x2A,0xA1,0xFD,0x5C,0x8F,0x8B,0x0C, + 0xC6,0x8F,0xFF,0x4C,0x99,0xAE,0x19,0xDB,0x58,0x4C,0xC0,0xE8, + 0x70,0xCC,0x7C,0x17,0xE8,0xBD,0x6B,0x19,0x93,0xB9,0x66,0xA9, + 0xD0,0x05,0x21,0x04,0x4C,0x7E,0x87,0x9F,0xF4,0xE9,0x23,0xE1, + 0x29,0x37,0xC5,0xE2,0x0A,0xC5,0xC1,0x92,0xC7,0x69,0xB4,0xFB, + 0x84,0x06,0xCE,0x0E,0xFC,0x65,0x70,0x2F,0xF6,0xB8,0x11,0xF9, + 0x0F,0x60,0x10,0xCA,0x94,0x29,0x44,0x5E,0x4A,0x05,0x46,0xE5, + 0xE6,0xA0,0xBD,0x14,0x45,0xA6,0xA7,0xCA,0x63,0x57,0xC6,0xB0, + 0x47,0xF9,0x71,0x24,0x19,0x75,0xD2,0x64,0x16,0xB1,0xBA,0x08, + 0xE9,0xE9,0xFB,0xF3, + }; + static unsigned char dh2236_g[]={ + 0x02, + }; + DH *dh; + + if ((dh=DH_new()) == NULL) return(NULL); + dh->p=BN_bin2bn(dh2236_p,sizeof(dh2236_p),NULL); + dh->g=BN_bin2bn(dh2236_g,sizeof(dh2236_g),NULL); + if ((dh->p == NULL) || (dh->g == NULL)) + { DH_free(dh); return(NULL); } + return(dh); +} \ No newline at end of file diff --git a/server/include/service.h b/server/include/service.h index af71bfe7d..936a73058 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -29,6 +29,7 @@ #include #include #include +#include /** * @file service.h * From 1ad1a31ed7a76ffe0345f7394202639df8094c1e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 9 Jun 2015 17:18:25 +0300 Subject: [PATCH 140/273] Fixed the OpenSSL error stack being printed wrong. --- server/core/dcb.c | 35 +++++++++++++++++++++++++++++------ server/core/gateway.c | 2 +- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index cfee65371..a91645417 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1026,7 +1026,19 @@ int dcb_read_SSL( STRDCBSTATE(dcb->state), dcb->fd, ssl_errno, - errbuf))); + strerror(errno)))); + + if(ssl_errno == SSL_ERROR_SSL || + ssl_errno == SSL_ERROR_SYSCALL) + { + while((ssl_errno = ERR_get_error()) != 0) + { + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE, + "%s", + errbuf); + } + } } gwbuf_free(buffer); @@ -2850,6 +2862,7 @@ int dcb_accept_SSL(DCB* dcb) break; case -1: + errnum = SSL_get_error(dcb->ssl,ssl_rval); if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE) @@ -2864,12 +2877,22 @@ int dcb_accept_SSL(DCB* dcb) else { rval = -1; - ERR_error_string(errnum,errbuf); - skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept for %s: (SSL error code: %d) %s", - dcb->remote, - errnum, + skygw_log_write(LE, + "Error: Fatal error in SSL_accept for %s: (SSL error code: %d):%s", + dcb->remote, + errnum, + strerror(errno)); + if(errnum == SSL_ERROR_SSL || + errnum == SSL_ERROR_SYSCALL) + { + while((errnum = ERR_get_error()) != 0) + { + ERR_error_string(errnum,errbuf); + skygw_log_write(LE, + "%s", errbuf); + } + } } break; diff --git a/server/core/gateway.c b/server/core/gateway.c index 67d5ecf62..794ed5304 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -2038,4 +2038,4 @@ static void maxscale_ssl_lock(int mode,int n,const char* file, int line) static unsigned long maxscale_ssl_id() { return (unsigned long)pthread_self(); -} \ No newline at end of file +} From 1275a594acf71d276f6c3390cceb9ec58cd4f151 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 7 May 2015 05:56:28 +0300 Subject: [PATCH 141/273] Added missing utils library link from testmodutils. --- server/core/test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 82e58919f..5c1f9a8ad 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -21,7 +21,7 @@ target_link_libraries(test_spinlock fullcore log_manager) target_link_libraries(test_filter fullcore) target_link_libraries(test_buffer fullcore log_manager) target_link_libraries(test_dcb fullcore) -target_link_libraries(test_modutil fullcore) +target_link_libraries(test_modutil fullcore utils log_manager) target_link_libraries(test_poll fullcore) target_link_libraries(test_service fullcore) target_link_libraries(test_server fullcore) From 196d41cb88c7858a31f83a530a8a4712447a340a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 9 Jun 2015 20:02:45 +0300 Subject: [PATCH 142/273] More debug output. --- server/core/dcb.c | 53 ++++++++++++++++---------- server/modules/protocol/mysql_client.c | 3 +- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index a91645417..0ce9ec053 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1005,9 +1005,18 @@ int dcb_read_SSL( char errbuf[200]; ssl_errno = SSL_get_error(dcb->ssl,n); #ifdef SS_DEBUG - ERR_error_string(ssl_errno,errbuf); - skygw_log_write_flush(LD,"[%lu]SSL error %d: %s", - pthread_self(),ssl_errno,errbuf); + if(ssl_errno == SSL_ERROR_SSL || + ssl_errno == SSL_ERROR_SYSCALL) + { + int eno; + while((eno = ERR_get_error()) != 0) + { + ERR_error_string(eno,errbuf); + skygw_log_write(LE, + "%s", + errbuf); + } + } #endif if(ssl_errno == SSL_ERROR_WANT_READ || ssl_errno == SSL_ERROR_WANT_WRITE || @@ -1017,7 +1026,6 @@ int dcb_read_SSL( } else { - ERR_error_string(ssl_errno,errbuf); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Read failed, dcb %p in state " @@ -1052,7 +1060,7 @@ int dcb_read_SSL( gwbuf_rtrim(buffer,bufsize - n); #ifdef SS_DEBUG - skygw_log_write(LD,"[%lu] SSL: Truncated buffer from %d to %d bytes. " + skygw_log_write(LD,"%lu SSL: Truncated buffer from %d to %d bytes. " "Read %d bytes, %d bytes waiting.\n",pthread_self(), bufsize,GWBUF_LENGTH(buffer),n,b); @@ -1080,13 +1088,6 @@ int dcb_read_SSL( rc = ioctl(dcb->fd, FIONREAD, &b); pending = SSL_pending(dcb->ssl); - if(ssl_errno == SSL_ERROR_WANT_READ || - ssl_errno == SSL_ERROR_WANT_WRITE || - (b == 0 && pending == 0)) - { - break; - } - } /*< while (true) */ return_n: return n; @@ -2837,22 +2838,34 @@ int dcb_create_SSL(DCB* dcb) */ int dcb_accept_SSL(DCB* dcb) { - int rval = 0,ssl_rval,errnum = 0,fd,b = 0; + int rval = 0,ssl_rval,errnum = 0,fd,b = 0,pending; char errbuf[140]; fd = dcb->fd; do { ssl_rval = SSL_accept(dcb->ssl); + errnum = SSL_get_error(dcb->ssl,ssl_rval); + LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept %d, error %d", + ssl_rval,errnum))); switch(ssl_rval) { case 0: errnum = SSL_get_error(dcb->ssl,ssl_rval); - ERR_error_string(errnum,errbuf); - LOGIF(LD,(skygw_log_write_flush(LD,"[%p] SSL_accept shutdown for %s:%s", + skygw_log_write(LE,"Error: SSL authentication failed (SSL error %d):", dcb, dcb->remote, - errbuf))); + errnum); + + if(errnum == SSL_ERROR_SSL || + errnum == SSL_ERROR_SYSCALL) + { + while((errnum = ERR_get_error()) != 0) + { + ERR_error_string(errnum,errbuf); + skygw_log_write(LE,"%s",errbuf); + } + } rval = -1; break; case 1: @@ -2871,7 +2884,7 @@ int dcb_accept_SSL(DCB* dcb) queue and wait for more.*/ rval = 0; LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept ongoing for %s", - dcb->remote))); + dcb->remote))); return rval; } else @@ -2904,11 +2917,11 @@ int dcb_accept_SSL(DCB* dcb) break; } ioctl(fd,FIONREAD,&b); + pending = SSL_pending(dcb->ssl); #ifdef SS_DEBUG - skygw_log_write_flush(LD,"[dcb_accept_SSL] fd %d: %d bytes",fd,b); - skygw_log_write(LD,"[dcb_accept_SSL] SSL_accept returned %d, SSL error: %d",ssl_rval,errnum); + skygw_log_write_flush(LD,"[dcb_accept_SSL] fd %d: %d bytes, %d pending",fd,b,pending); #endif - }while(b > 0 && rval != -1); + }while((b > 0 || pending > 0) && rval != -1); return rval; } diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index da5f04732..187c33910 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1952,8 +1952,7 @@ int do_ssl_accept(MySQLProtocol* protocol) spinlock_release(&protocol->protocol_lock); rval = -1; skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept for %s@%s: %s", - protocol->owner_dcb->user, + "Error: Fatal error in SSL_accept for %s", protocol->owner_dcb->remote); break; From de2910f75b2164ba6c5b606b036f5c7e46fd8a90 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 9 Jun 2015 22:27:15 +0300 Subject: [PATCH 143/273] Fixed SSL_accept failing if the GWBUF with the initial MySQL auth packet contains some of the SSL authentication data. --- server/core/dcb.c | 144 +++++++++++++++++++++++++ server/include/dcb.h | 1 + server/modules/protocol/mysql_client.c | 6 +- 3 files changed, 150 insertions(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 0ce9ec053..4c9dd0324 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -883,6 +883,150 @@ return_n: } +/** + * General purpose read routine to read data from a socket in the + * Descriptor Control Block and append it to a linked list of buffers. + * The list may be empty, in which case *head == NULL + * + * @param dcb The DCB to read from + * @param head Pointer to linked list to append data to + * @return -1 on error, otherwise the number of read bytes on the last + * iteration of while loop. 0 is returned if no data available. + */ +int dcb_read_n( + DCB *dcb, + GWBUF **head, + int nbytes) +{ + GWBUF *buffer = NULL; + int b; + int rc; + int n; + int nread = 0; + + CHK_DCB(dcb); + + if (dcb->fd <= 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Read failed, dcb is %s.", + dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not readable"))); + n = 0; + goto return_n; + } + + int bufsize; + + rc = ioctl(dcb->fd, FIONREAD, &b); + + if (rc == -1) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : ioctl FIONREAD for dcb %p in " + "state %s fd %d failed due error %d, %s.", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + errno, + strerror(errno)))); + n = -1; + goto return_n; + } + + if (b == 0 && nread == 0) + { + /** Handle closed client socket */ + if (dcb_isclient(dcb)) + { + char c; + int l_errno = 0; + int r = -1; + + /* try to read 1 byte, without consuming the socket buffer */ + r = recv(dcb->fd, &c, sizeof(char), MSG_PEEK); + l_errno = errno; + + if (r <= 0 && + l_errno != EAGAIN && + l_errno != EWOULDBLOCK && + l_errno != 0) + { + n = -1; + goto return_n; + } + } + n = 0; + goto return_n; + } + else if (b == 0) + { + n = 0; + goto return_n; + } + + dcb->last_read = hkheartbeat; + + bufsize = MIN(b, nbytes); + + if ((buffer = gwbuf_alloc(bufsize)) == NULL) + { + /*< + * This is a fatal error which should cause shutdown. + * Todo shutdown if memory allocation fails. + */ + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Failed to allocate read buffer " + "for dcb %p fd %d, due %d, %s.", + dcb, + dcb->fd, + errno, + strerror(errno)))); + + n = -1; + goto return_n; + } + GW_NOINTR_CALL(n = read(dcb->fd, GWBUF_DATA(buffer), bufsize); + dcb->stats.n_reads++); + + if (n <= 0) + { + if (errno != 0 && errno != EAGAIN && errno != EWOULDBLOCK) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Read failed, dcb %p in state " + "%s fd %d, due %d, %s.", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + errno, + strerror(errno)))); + } + gwbuf_free(buffer); + goto return_n; + } + nread += n; + + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_read] Read %d bytes from dcb %p in state %s " + "fd %d.", + pthread_self(), + n, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + /*< Append read data to the gwbuf */ + *head = gwbuf_append(*head, buffer); + +return_n: + return n; +} + + /** * General purpose read routine to read data from a socket in the * Descriptor Control Block and append it to a linked list of buffers. diff --git a/server/include/dcb.h b/server/include/dcb.h index cc96a2c0e..19f1e72ea 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -316,6 +316,7 @@ void dcb_free(DCB *); DCB *dcb_connect(struct server *, struct session *, const char *); DCB *dcb_clone(DCB *); int dcb_read(DCB *, GWBUF **); +int dcb_read_n(DCB*,GWBUF **,int); int dcb_drain_writeq(DCB *); void dcb_close(DCB *); DCB *dcb_process_zombies(int); /* Process Zombies except the one behind the pointer */ diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 187c33910..15c572a0f 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -490,7 +490,6 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { /** Do the SSL Handshake */ if(ssl && protocol->owner_dcb->service->ssl_mode != SSL_DISABLED) { - protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; if(do_ssl_accept(protocol) < 0) @@ -693,6 +692,11 @@ int gw_read_client_event( { rc = dcb_read_SSL(dcb, &read_buffer); } + else if(dcb->service->ssl_mode != SSL_DISABLED && + protocol->protocol_auth_state == MYSQL_AUTH_SENT) + { + rc = dcb_read_n(dcb, &read_buffer,(4 + 4 + 4 + 1 + 23)); + } else { rc = dcb_read(dcb, &read_buffer); From 3fb1213dee9ffc9759ecc3430109ea76323eb931 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 10 Jun 2015 06:09:42 +0300 Subject: [PATCH 144/273] Added more error logging when monitor scripts fail. --- server/modules/monitor/galeramon.c | 18 +++++++++++++----- server/modules/monitor/mmmon.c | 16 +++++++++++++--- server/modules/monitor/monitor_common.c | 3 +++ server/modules/monitor/mysql_mon.c | 16 +++++++++++++--- server/modules/monitor/ndbclustermon.c | 16 +++++++++++++--- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 6a842b8c1..07d9fd848 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -123,7 +123,7 @@ startMonitor(void *arg,void* opt) MONITOR* mon = arg; GALERA_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false; + bool have_events = false,script_error = false; if (handle != NULL) { handle->shutdown = 0; @@ -163,6 +163,7 @@ startMonitor(void *arg,void* opt) } else { + script_error = true; if(access(params->value,F_OK) == 0) { skygw_log_write(LE, @@ -175,17 +176,24 @@ startMonitor(void *arg,void* opt) "Error: The file cannot be found: %s", params->value); } - handle->script = NULL; } } else if(!strcmp(params->name,"events")) { - mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value); - have_events = true; + if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) + script_error = true; + else + have_events = true; } params = params->next; } - + if(script_error) + { + skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.",mon->name); + free(handle->script); + handle->script = NULL; + } /** If no specific events are given, enable them all */ if(!have_events) { diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 130cdb279..c0d54595d 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -113,7 +113,7 @@ startMonitor(void *arg,void* opt) MONITOR* mon = (MONITOR*)arg; MM_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false; + bool have_events = false,script_error = false; if (handle) { @@ -148,6 +148,7 @@ startMonitor(void *arg,void* opt) } else { + script_error = true; if(access(params->value,F_OK) == 0) { skygw_log_write(LE, @@ -165,11 +166,20 @@ startMonitor(void *arg,void* opt) } else if(!strcmp(params->name,"events")) { - mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value); - have_events = true; + if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) + script_error = true; + else + have_events = true; } params = params->next; } + if(script_error) + { + skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.",mon->name); + free(handle->script); + handle->script = NULL; + } /** If no specific events are given, enable them all */ if(!have_events) { diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index e4b8e7dac..f4566c35a 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -343,7 +343,10 @@ int mon_parse_event_string(bool* events, size_t count,char* string) { event = mon_name_to_event(tok); if(event == UNDEFINED_MONITOR_EVENT) + { + skygw_log_write(LE,"Error: Invalid event name %s",tok); return -1; + } events[event] = true; tok = strtok_r(NULL,",| ",&saved); } diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 6f10b0ac8..2e16afc7b 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -140,7 +140,7 @@ startMonitor(void *arg, void* opt) MONITOR* monitor = (MONITOR*)arg; MYSQL_MONITOR *handle = (MYSQL_MONITOR*)monitor->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false; + bool have_events = false,script_error = false; if (handle) { @@ -176,6 +176,7 @@ startMonitor(void *arg, void* opt) } else { + script_error = true; if(access(params->value,F_OK) == 0) { skygw_log_write(LE, @@ -193,11 +194,20 @@ startMonitor(void *arg, void* opt) } else if(!strcmp(params->name,"events")) { - mon_parse_event_string(handle->events,sizeof(handle->events),params->value); - have_events = true; + if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) + script_error = true; + else + have_events = true; } params = params->next; } + if(script_error) + { + skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.",monitor->name); + free(handle->script); + handle->script = NULL; + } /** If no specific events are given, enable them all */ if(!have_events) { diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index c8790be59..9561e275e 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -111,7 +111,7 @@ startMonitor(void *arg,void* opt) MONITOR* mon = (MONITOR*)arg; MYSQL_MONITOR *handle = mon->handle; CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false; + bool have_events = false,script_error = false; if (handle != NULL) { @@ -140,6 +140,7 @@ startMonitor(void *arg,void* opt) } else { + script_error = true; if(access(params->value,F_OK) == 0) { skygw_log_write(LE, @@ -157,10 +158,19 @@ startMonitor(void *arg,void* opt) } else if(!strcmp(params->name,"events")) { - mon_parse_event_string(&handle->events,sizeof(handle->events),params->value); - have_events = true; + if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) + script_error = true; + else + have_events = true; } params = params->next; + } + if(script_error) + { + skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.",mon->name); + free(handle->script); + handle->script = NULL; } /** If no specific events are given, enable them all */ if(!have_events) From c15469013e89d31b9dd5878792d4013304716f90 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 10 Jun 2015 15:05:39 +0300 Subject: [PATCH 145/273] Fixed non-SSL connections to SSL enabled services failing. --- server/modules/protocol/mysql_client.c | 83 +++++++++++++++----------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 15c572a0f..dd6be0de2 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -74,7 +74,7 @@ int gw_MySQLWrite_client_SSL(DCB *dcb, GWBUF *queue); int gw_write_client_event_SSL(DCB *dcb); int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message); int MySQLSendHandshake(DCB* dcb); -static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue); +static int gw_mysql_do_authentication(DCB *dcb, GWBUF **queue); static int route_by_statement(SESSION *, GWBUF **); extern char* get_username_from_auth(char* ptr, uint8_t* data); extern int check_db_name_after_auth(DCB *, char *, int); @@ -402,7 +402,8 @@ MySQLSendHandshake(DCB* dcb) * @note in case of failure, dcb->data is freed before returning. If succeed, * dcb->data is freed in session.c:session_free. */ -static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { +static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { + GWBUF* queue = *buf; MySQLProtocol *protocol = NULL; /* int compress = -1; */ int connect_with_db = -1; @@ -464,46 +465,58 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { &protocol->client_capabilities); */ - if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_DONE) - goto ssl_hs_done; - - ssl = protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL; - - /** Client didn't requested SSL when SSL mode was required*/ - if(!ssl && protocol->owner_dcb->service->ssl_mode == SSL_REQUIRED) + /** Skip this if the SSL handshake is already done. + * If not, start the SSL handshake. */ + if(protocol->protocol_auth_state != MYSQL_AUTH_SSL_HANDSHAKE_DONE) { - LOGIF(LT,(skygw_log_write(LT,"User %s@%s connected to service '%s' without SSL when SSL was required.", - protocol->owner_dcb->user, - protocol->owner_dcb->remote, - protocol->owner_dcb->service->name))); - return MYSQL_FAILED_AUTH_SSL; - } - if(LOG_IS_ENABLED(LT) && ssl) - { - skygw_log_write(LT,"User %s@%s connected to service '%s' with SSL.", - protocol->owner_dcb->user, - protocol->owner_dcb->remote, - protocol->owner_dcb->service->name); - } + ssl = protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL; - /** Do the SSL Handshake */ - if(ssl && protocol->owner_dcb->service->ssl_mode != SSL_DISABLED) - { - protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; - - if(do_ssl_accept(protocol) < 0) + /** Client didn't requested SSL when SSL mode was required*/ + if(!ssl && protocol->owner_dcb->service->ssl_mode == SSL_REQUIRED) { - return MYSQL_FAILED_AUTH; + LOGIF(LT,(skygw_log_write(LT,"User %s@%s connected to service '%s' without SSL when SSL was required.", + protocol->owner_dcb->user, + protocol->owner_dcb->remote, + protocol->owner_dcb->service->name))); + return MYSQL_FAILED_AUTH_SSL; } - else + + if(LOG_IS_ENABLED(LT) && ssl) { - return 0; + skygw_log_write(LT,"User %s@%s connected to service '%s' with SSL.", + protocol->owner_dcb->user, + protocol->owner_dcb->remote, + protocol->owner_dcb->service->name); + } + + /** Do the SSL Handshake */ + if(ssl && protocol->owner_dcb->service->ssl_mode != SSL_DISABLED) + { + protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ; + + if(do_ssl_accept(protocol) < 0) + { + return MYSQL_FAILED_AUTH; + } + else + { + return 0; + } + } + else if(dcb->service->ssl_mode == SSL_ENABLED) + { + /** This is a non-SSL connection to a SSL enabled service + * and we need to read the rest of the packet from the socket for the username */ + int bytes = dcb_read(dcb,&queue); + queue = gwbuf_make_contiguous(queue); + client_auth_packet = GWBUF_DATA(queue); + client_auth_packet_size = gwbuf_length(queue); + *buf = queue; + LOGIF(LD,(skygw_log_write(LD,"%lu Read %d bytes from fd %d",pthread_self(),bytes,dcb->fd))); } } - ssl_hs_done: - username = get_username_from_auth(username, client_auth_packet); if (username == NULL) @@ -848,7 +861,7 @@ int gw_read_client_event( { int auth_val; - auth_val = gw_mysql_do_authentication(dcb, read_buffer); + auth_val = gw_mysql_do_authentication(dcb, &read_buffer); if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ || protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_ONGOING || @@ -955,7 +968,7 @@ int gw_read_client_event( { int auth_val; - auth_val = gw_mysql_do_authentication(dcb, read_buffer); + auth_val = gw_mysql_do_authentication(dcb, &read_buffer); if (auth_val == 0) From 8ac79cf2adaec88704140731f3b93eddf48e6836 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 10:28:35 +0300 Subject: [PATCH 146/273] Fixed some hard-coded paths being used instead of CMake variable values. --- etc/postinst.in | 11 ++++++----- server/include/gwdirs.h.in | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/etc/postinst.in b/etc/postinst.in index 82886990f..1a2c68ca3 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -2,11 +2,12 @@ # Create directories -mkdir -p @MAXSCALE_LIBDIR@ -mkdir -p @MAXSCALE_BINDIR@ -mkdir -p @MAXSCALE_SHAREDIR@ -mkdir -p @MAXSCALE_DOCDIR@ -mkdir -p @MAXSCALE_CONFDIR@ +mkdir -p @CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@ +mkdir -p @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@ +mkdir -p @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@ +mkdir -p @CMAKE_INSTALL_PREFIX@/@MAXSCALE_DOCDIR@ + +# MAXSCALE_VARDIR is an absolute path to /var by default mkdir -p @MAXSCALE_VARDIR@/log/maxscale mkdir -p @MAXSCALE_VARDIR@/lib/maxscale mkdir -p @MAXSCALE_VARDIR@/cache/maxscale diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index b8044484b..cf1f47987 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -24,11 +24,11 @@ /** Default file locations, configured by CMake */ static const char* default_cnf_fname = "maxscale.cnf"; static const char* default_configdir = "/etc/"; -static const char* default_piddir = "/var/run/maxscale/"; -static const char* default_logdir = "/var/log/maxscale/"; -static const char* default_datadir = "/var/lib/maxscale/"; +static const char* default_piddir = "@MAXSCALE_VARDIR@/run/maxscale/"; +static const char* default_logdir = "@MAXSCALE_VARDIR@/log/maxscale/"; +static const char* default_datadir = "@MAXSCALE_VARDIR@/lib/maxscale/"; static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@"; -static const char* default_cachedir = "/var/cache/maxscale/"; +static const char* default_cachedir = "@MAXSCALE_VARDIR@/cache/maxscale/"; static const char* default_langdir = "@MAXSCALE_VARDIR@/lib/maxscale/"; static char* configdir = NULL; From bb427128a9f3a80f3545943e676d5422137770f6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 12:00:03 +0300 Subject: [PATCH 147/273] Fixed successful SSL_accept calls causing another call to SSL_accept. --- server/core/dcb.c | 34 ++++++++++------ server/core/gateway.c | 90 ++++++++++++++++++++++++++++--------------- 2 files changed, 81 insertions(+), 43 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 4c9dd0324..e7c74b6bc 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1046,7 +1046,7 @@ int dcb_read_SSL( int rc; int n; int nread = 0; - + int ssl_errno = 0; CHK_DCB(dcb); if (dcb->fd <= 0) @@ -1062,7 +1062,7 @@ int dcb_read_SSL( while (true) { int bufsize; - int ssl_errno = 0; + ssl_errno = 0; rc = ioctl(dcb->fd, FIONREAD, &b); pending = SSL_pending(dcb->ssl); if (rc == -1) @@ -1096,9 +1096,9 @@ int dcb_read_SSL( if(ssl_errno != SSL_ERROR_WANT_READ && ssl_errno != SSL_ERROR_WANT_WRITE && ssl_errno != SSL_ERROR_NONE) - { n = -1; - } + else + n = 0; goto return_n; } } @@ -1192,7 +1192,7 @@ int dcb_read_SSL( } } } - + n = -1; gwbuf_free(buffer); goto return_n; } @@ -1595,7 +1595,7 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) if (w < 0) { - int ssl_errno = ERR_get_error(); + int ssl_errno = SSL_get_error(dcb->ssl,w); if (LOG_IS_ENABLED(LOGFILE_DEBUG)) { @@ -1633,6 +1633,17 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) dcb, STRDCBSTATE(dcb->state), dcb->fd,ssl_errno))); + if(ssl_errno == SSL_ERROR_SSL || + ssl_errno == SSL_ERROR_SYSCALL) + { + while((ssl_errno = ERR_get_error()) != 0) + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%s",errbuf); + } + } + break; } } @@ -3016,7 +3027,7 @@ int dcb_accept_SSL(DCB* dcb) rval = 1; LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept done for %s", dcb->remote))); - break; + return rval; case -1: @@ -3035,10 +3046,10 @@ int dcb_accept_SSL(DCB* dcb) { rval = -1; skygw_log_write(LE, - "Error: Fatal error in SSL_accept for %s: (SSL error code: %d):%s", + "Error: Fatal error in SSL_accept for %s: (SSL version: %s SSL error code: %d)", dcb->remote, - errnum, - strerror(errno)); + SSL_get_version(dcb->ssl), + errnum); if(errnum == SSL_ERROR_SSL || errnum == SSL_ERROR_SYSCALL) { @@ -3104,8 +3115,7 @@ int dcb_connect_SSL(DCB* dcb) case -1: errnum = SSL_get_error(dcb->ssl,rval); - if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE || - errnum == SSL_ERROR_WANT_X509_LOOKUP) + if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE) { /** Not all of the data has been read. Go back to the poll queue and wait for more.*/ diff --git a/server/core/gateway.c b/server/core/gateway.c index 794ed5304..1805e483d 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -40,7 +40,16 @@ * @endverbatim */ #define _XOPEN_SOURCE 700 +#define OPENSSL_THREAD_DEFINES #include + + #include + #if defined(OPENSSL_THREADS) +#define HAVE_OPENSSL_THREADS 1 + #else +#define HAVE_OPENSSL_THREADS 0 + #endif + #include #include #include @@ -196,9 +205,46 @@ static bool resolve_maxscale_conf_fname( static char* check_dir_access(char* dirname,bool,bool); static int set_user(); -static void maxscale_ssl_lock(int mode,int n,const char* file, int line); -static unsigned long maxscale_ssl_id(); -static SPINLOCK* ssl_locks; + +/** SSL multi-threading functions and structures */ + +struct CRYPTO_dynlock_value +{ + SPINLOCK lock; +}; + +static struct CRYPTO_dynlock_value *ssl_create_dynlock(const char* file, int line) +{ + struct CRYPTO_dynlock_value* lock = malloc(sizeof(struct CRYPTO_dynlock_value)); + if(lock) + { + spinlock_init(&lock->lock); + } + return lock; +} + +static void ssl_lock_dynlock(int mode,struct CRYPTO_dynlock_value * n,const char* file, int line) +{ + if(mode & CRYPTO_LOCK) + { + spinlock_acquire(&n->lock); + } + else + { + spinlock_release(&n->lock); + } +} + +static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, int line) +{ + free(n); +} + +static void maxscale_ssl_id(CRYPTO_THREADID* id) +{ + CRYPTO_THREADID_set_numeric(id,pthread_self()); +} + /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. @@ -1374,23 +1420,21 @@ int main(int argc, char **argv) } /** OpenSSL initialization */ - - SSL_library_init(); - SSL_load_error_strings(); - OPENSSL_add_all_algorithms_noconf(); - - int n_locks = CRYPTO_num_locks(); - if((ssl_locks = malloc(n_locks*sizeof(SPINLOCK))) == NULL) + if(!HAVE_OPENSSL_THREADS) { + char* logerr = "OpenSSL library does not support multi-threading"; + print_log_n_stderr(true, true, logerr, logerr, eno); rc = MAXSCALE_INTERNALERROR; goto return_main; } + SSL_library_init(); + SSL_load_error_strings(); + OPENSSL_add_all_algorithms_noconf(); + CRYPTO_set_dynlock_create_callback(ssl_create_dynlock); + CRYPTO_set_dynlock_destroy_callback(ssl_free_dynlock); + CRYPTO_set_dynlock_lock_callback(ssl_lock_dynlock); + CRYPTO_THREADID_set_callback(maxscale_ssl_id); - for(i = 0;i Date: Thu, 11 Jun 2015 13:22:18 +0300 Subject: [PATCH 148/273] Added RSA key generator. --- server/core/service.c | 72 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/server/core/service.c b/server/core/service.c index 826c79e66..fbffd27bd 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -69,6 +69,9 @@ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; extern __thread log_info_t tls_log_info; +static RSA *rsa_512 = NULL; +static RSA *rsa_1024 = NULL; + /** To be used with configuration type checks */ typedef struct typelib_st { int tl_nelems; @@ -418,6 +421,17 @@ serviceStart(SERVICE *service) SERV_PROTOCOL *port; int listeners = 0; +if(service->ssl_mode != SSL_DISABLED) +{ + if(serviceInitSSL(service) != 0) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "%s: SSL initialization failed. Service not started.", + service->name))); + service->state = SERVICE_STATE_FAILED; + return 0; + } +} if ((service->router_instance = service->router->createInstance(service, service->routerOptions)) == NULL) { @@ -1839,9 +1853,44 @@ int *data; } +/** + * + * @param s + * @param is_export + * @param keylength + * @return + */ + RSA *tmp_rsa_callback(SSL *s, int is_export, int keylength) + { + RSA *rsa_tmp=NULL; + + switch (keylength) { + case 512: + if (rsa_512) + rsa_tmp = rsa_512; + else { /* generate on the fly, should not happen in this example */ + rsa_tmp = RSA_generate_key(keylength,RSA_F4,NULL,NULL); + rsa_512 = rsa_tmp; /* Remember for later reuse */ + } + break; + case 1024: + if (rsa_1024) + rsa_tmp=rsa_1024; + break; + default: + /* Generating a key on the fly is very costly, so use what is there */ + if (rsa_1024) + rsa_tmp=rsa_1024; + else + rsa_tmp=rsa_512; /* Use at least a shorter key */ + } + return(rsa_tmp); + } + int serviceInitSSL(SERVICE* service) { DH* dh; + RSA* rsa; if(!service->ssl_init_done) { @@ -1878,12 +1927,21 @@ int serviceInitSSL(SERVICE* service) service->ctx = SSL_CTX_new(service->method); - /** Enable the Diffie-Hellman algorithms */ - if((dh = ssl_get_dh2236()) != NULL) + if(rsa_512 == NULL) { - SSL_CTX_set_tmp_dh(service->ctx,dh); - DH_free(dh); + rsa_512 = RSA_generate_key(512,RSA_F4,NULL,NULL); + if (rsa_512 == NULL) + skygw_log_write(LE,"Error: 512-bit RSA key generation failed."); } + if(rsa_1024 == NULL) + { + rsa_1024 = RSA_generate_key(1024,RSA_F4,NULL,NULL); + if (rsa_1024 == NULL) + skygw_log_write(LE,"Error: 1024-bit RSA key generation failed."); + } + + if(rsa_512 != NULL && rsa_1024 != NULL) + SSL_CTX_set_tmp_rsa_callback(service->ctx,tmp_rsa_callback); if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { skygw_log_write(LE,"Error: Failed to set server SSL certificate."); @@ -1919,6 +1977,10 @@ int serviceInitSSL(SERVICE* service) return 0; } +/** + * Generated by OpenSSL. + * @return + */ DH *ssl_get_dh2236() { static unsigned char dh2236_p[]={ @@ -1958,4 +2020,4 @@ DH *ssl_get_dh2236() if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); -} \ No newline at end of file +} From 3f34d237cae6a367ac20c448d91e8d307e79e998 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 15:26:05 +0300 Subject: [PATCH 149/273] enabled all bug fixes for OpenSSL. --- server/core/service.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/core/service.c b/server/core/service.c index fbffd27bd..bd09f06ee 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -1926,6 +1926,7 @@ int serviceInitSSL(SERVICE* service) } service->ctx = SSL_CTX_new(service->method); + SSL_CTX_set_options(service->ctx,SSL_OP_ALL); if(rsa_512 == NULL) { From 1c68a9a8729d625e495a6bf133f6c194d9b8dfaf Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 15:54:09 +0300 Subject: [PATCH 150/273] Fixed dcb_connect_SSL calling SSL_connect again after a successful connection was already made. --- server/core/dcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index e7c74b6bc..51dbb412f 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -3110,7 +3110,7 @@ int dcb_connect_SSL(DCB* dcb) LOGIF(LD,(skygw_log_write_flush(LD,"SSL_connect done for %s@%s", dcb->user, dcb->remote))); - break; + return rval; case -1: errnum = SSL_get_error(dcb->ssl,rval); From f24da8712b4c4cbf5c1bf731a6133cae39d62245 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 18:25:42 +0300 Subject: [PATCH 151/273] Fixed a segfault and disabled syslog by default. --- server/core/gateway.c | 46 +++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 9845713e1..9d0e6f908 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -935,7 +935,7 @@ int main(int argc, char **argv) char* tmp_var; int option_index; int logtofile = 0; /* Use shared memory or file */ - int syslog_enabled = 1; /** Log to syslog */ + int syslog_enabled = 0; /** Log to syslog */ int maxscalelog_enabled = 1; /** Log with MaxScale */ ssize_t log_flush_timeout_ms = 0; sigset_t sigset; @@ -1078,26 +1078,34 @@ int main(int argc, char **argv) } break; case 'S': - if(strstr(optarg,"=")) - { - strtok(optarg,"= "); - maxscalelog_enabled = config_truth_value(strtok(NULL,"= ")); - } - else - { - maxscalelog_enabled = config_truth_value(optarg); - } + { + char* tok = strstr(optarg,"="); + if(tok) + { + tok++; + if(tok) + maxscalelog_enabled = config_truth_value(tok); + } + else + { + maxscalelog_enabled = config_truth_value(optarg); + } + } break; case 's': - if(strstr(optarg,"=")) - { - strtok(optarg,"= "); - syslog_enabled = config_truth_value(strtok(NULL,"= ")); - } - else - { - syslog_enabled = config_truth_value(optarg); - } + { + char* tok = strstr(optarg,"="); + if(tok) + { + tok++; + if(tok) + syslog_enabled = config_truth_value(tok); + } + else + { + syslog_enabled = config_truth_value(optarg); + } + } break; case 'U': if(set_user(optarg) != 0) From 2b2e81feb26665b938eb4acb8a60adb124bb6fdc Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 19:05:05 +0300 Subject: [PATCH 152/273] Fix to MXS-181: https://mariadb.atlassian.net/browse/MXS-181 Added TCP_NODELAY to socket options. --- server/modules/protocol/mysql_client.c | 4 ++++ server/modules/protocol/mysql_common.c | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index abdb4422c..ea0b05b79 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -46,6 +46,7 @@ #include #include #include +#include MODULE_INFO info = { MODULE_API_PROTOCOL, @@ -1064,6 +1065,9 @@ int gw_MySQLListener( LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); } + if((syseno = setsockopt(l_so, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one))) != 0){ + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); + } // set NONBLOCKING mode setnonblocking(l_so); diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index ffd72c034..0a7f22f5e 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -44,6 +44,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -812,6 +813,23 @@ int gw_do_connect_to_backend( goto close_so; } + int one = 1; + if(setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Failed to set socket options " + "%s:%d failed.\n\t\t Socket configuration failed " + "due %d, %s.", + host, + port, + errno, + strerror(errno)))); + rv = -1; + /** Close socket */ + goto close_so; + } + /* set socket to as non-blocking here */ setnonblocking(so); rv = connect(so, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); From 6f0e3937eba9e4466a9b4645deca9064c76cde47 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 19:43:22 +0300 Subject: [PATCH 153/273] Added missing include to gwdirs.h. --- server/include/gwdirs.h.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index cf1f47987..fe911c71b 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -18,9 +18,11 @@ * * Copyright MariaDB Corporation Ab 2015 */ - +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif #include - +#include /** Default file locations, configured by CMake */ static const char* default_cnf_fname = "maxscale.cnf"; static const char* default_configdir = "/etc/"; From fe2062b5b09d18f4bfb078f646fd941bdd09338b Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 19:50:51 +0300 Subject: [PATCH 154/273] Fixed a regression in mysql_mon.c which caused a memory leak --- server/modules/monitor/mysql_mon.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 2e16afc7b..e310e2ede 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -323,6 +323,8 @@ char *server_string; int read_timeout = mon->read_timeout; int write_timeout = mon->write_timeout; + if(database->con) + mysql_close(database->con); database->con = mysql_init(NULL); mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); From 68d5054afe3977717af708834b1fa15d50c44963 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 20:58:27 +0300 Subject: [PATCH 155/273] dcb_alloc now explicitly sets the server and service pointers to NULL. --- server/core/dcb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 6717aea41..128ffed21 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -193,7 +193,8 @@ DCB *rval; rval->polloutbusy = 0; rval->writecheck = 0; rval->fd = DCBFD_CLOSED; - + rval->server = NULL; + rval->service = NULL; rval->evq.next = NULL; rval->evq.prev = NULL; rval->evq.pending_events = 0; From 521e1aaf3b9126a7c21c31219871f3eb25f959e8 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 12 Jun 2015 02:44:43 +0300 Subject: [PATCH 156/273] Added man page for maxscale. --- CMakeLists.txt | 1 + Documentation/maxscale.1 | 69 ++++++++++++++++++++++++++++++++++++++++ etc/postinst.in | 2 +- 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 Documentation/maxscale.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f35031d8..a21e9c745 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,7 @@ install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_VARDIR}/lib/maxscale install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${MAXSCALE_SHAREDIR}) +install(FILES Documentation/maxscale.1 DESTINATION ${CMAKE_INSTALL_DATADIR}/man/man1) # Install startup scripts and ldconfig files if(WITH_SCRIPTS) diff --git a/Documentation/maxscale.1 b/Documentation/maxscale.1 new file mode 100644 index 000000000..104eaa235 --- /dev/null +++ b/Documentation/maxscale.1 @@ -0,0 +1,69 @@ +.TH maxscale 1 +.SH NAME +maxscale - The intelligent proxy +.SH SYNOPSIS +.B maxscale +[\fIOPTIONS...\fR] +.SH DESCRIPTION +The MariaDB Corporation MaxScale is an intelligent proxy that allows forwarding of +database statements to one or more database servers using complex rules, +a semantic understanding of the database statements and the roles of +the various servers within the backend cluster of databases. + +MaxScale is designed to provide load balancing and high availability +functionality transparently to the applications. In addition it provides +a highly scalable and flexible architecture, with plugin components to +support different protocols and routing decisions. + +.SH OPTIONS +.TP +.BR "-d, --nodaemon" +Run MaxScale in the terminal process +.TP +.BR -f " \fIFILE\fB, --config=\fIFILE\fR" +Relative or absolute pathname of MaxScale configuration file to load. +.TP +.BR -l "[\fIfile|shm\fB], --log=[\fIfile|shm\fB]" +Log trace and debug logs to file or shared memory. The debug and trace logs are disabled by default and if enabled, will log to shared memory. +.TP +.BR -L " \fIPATH\fB, --logdir=\fIPATH\fB" +Path to log file directory. +.TP +.BR -D " \fIPATH\fB, --datadir=\fIPATH\fB" +Path to data directory. This is where the embedded mysql tables are stored in addition to other MaxScale specific data. +.TP +.BR -C " \fIPATH\fB, --configdir=\fIPATH\fB" +Path to configuration file directory. MaxScale will look for the \fImaxscale.cnf\fR file from this folder. +.TP +.BR -B " \fIPATH\fB, --libdir=\fIPATH\fB" +Path to module directory. Modules are only searched from this folder. +.TP +.BR -A " \fIPATH\fB, --cachedir=\fIPATH\fB" +Path to cache directory. This is where MaxScale stores cached authentication data. +.TP +.BR -P " \fIPATH\fB, --piddir=\fIPATH\fB" +Location of MaxScale's PID file. +.TP +.BR -U " \fIUSER\fB, --user=\fIUSER\fB" +Run MaxScale as another user. The user ID and group ID of this user are used to run MaxScale. +.TP +.BR -s " [\fIyes\fB|\fIno\fB], --syslog=[\fIyes\fB|\fIno\fB]" +Log messages to syslog. +.TP +.BR -S " [\fIyes\fB|\fIno\fB], \fB--maxscalelog=[\fIyes\fB|\fIno\fB]" +Log messages to MaxScale's own log files. +.TP +.BR "-v, --version" +Print version information and exit. +.TP +.BR "-?, --help" +Show the help information for MaxScale and exit. + +.SH EXAMPLES +Tutorials on GitHub: +.UR https://github.com/mariadb-corporation/MaxScale/blob/master/Documentation/Documentation-Contents.md#tutorials +.UE +.SH SEE ALSO +The MaxScale documentation on GitHub: +.UR https://github.com/mariadb-corporation/MaxScale/blob/master/Documentation/Documentation-Contents.md +.UE diff --git a/etc/postinst.in b/etc/postinst.in index 1a2c68ca3..e1d3dc8ad 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -38,7 +38,7 @@ then cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service /usr/lib/systemd/system fi /sbin/ldconfig - +mandb cat <& 2 ********** Notice: MaxScale 1.2 Changes ************** From d3cc9be52ec445602180abcfb78ccaf226ef4b5d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 12 Jun 2015 10:05:50 +0300 Subject: [PATCH 157/273] Added libcurl-devel and pcre-devel to build dependencies. --- .../Getting-Started/Building-MaxScale-from-Source-Code.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md index f83a6e3ae..95236ed7e 100644 --- a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md +++ b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md @@ -28,7 +28,7 @@ You will need to install all of the following packages for all versions of RHEL, ``` gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc perl make libtool -openssl-devel libaio libaio-devel librabbitmq-devel +openssl-devel libaio libaio-devel librabbitmq-devel libcurl-devel pcre-devel ``` In addition, if you wish to to build an RPM package include: @@ -68,7 +68,7 @@ These packages are required on all versions of Ubuntu and Debian. ``` build-essential libssl-dev libaio-dev ncurses-dev bison - cmake perl libtool librabbitmq-dev + cmake perl libtool librabbitmq-dev libcurl-dev libpcre3-dev ``` If you want to build a DEB package, you will also need: From f602121459b021c4f597229add2dffbd2851aa34 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 12 Jun 2015 21:21:06 +0300 Subject: [PATCH 158/273] Added configurable SSL certificate verification depth and updated the documentation in the code. --- .../Getting-Started/Configuration-Guide.md | 9 ++ Documentation/Reference/MaxScale-and-SSL.md | 7 +- server/core/config.c | 11 ++ server/core/dcb.c | 13 +- server/core/gateway.c | 27 ++++ server/core/service.c | 120 ++++++++++-------- server/include/service.h | 13 +- server/modules/protocol/mysql_client.c | 53 ++++---- 8 files changed, 163 insertions(+), 90 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 23a9df70d..113e28c60 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -356,6 +356,15 @@ This parameter controls the level of encryption used. Accepted values are: * TLSv12 * MAX +### `ssl_cert_verification_depth` + +The maximum length of the certificate authority chain that will be accepted. Accepted values are positive integers. + +``` +# Example +ssl_cert_verification_depth=10 +``` + Example SSL enabled service configuration: ``` diff --git a/Documentation/Reference/MaxScale-and-SSL.md b/Documentation/Reference/MaxScale-and-SSL.md index ca61d52e2..d03a5af52 100644 --- a/Documentation/Reference/MaxScale-and-SSL.md +++ b/Documentation/Reference/MaxScale-and-SSL.md @@ -8,7 +8,8 @@ Here are the options which relate to SSL and certificates. Parameter|Values |Description ---------|-----------|-------- ssl | disabled, enabled, required |`disable` disables SSL, `enabled` enables SSL for client connections but still allows non-SSL connections and `required` requires SSL from all client connections. With the `required` option, client connections that do not use SSL will be rejected. -ssl_cert | |Path to server certificate -ssl_key | |Path to server private key -ssl_ca_cert | |Path to Certificate Authority file +ssl_cert | path to file |Path to server certificate +ssl_key | path to file |Path to server private key +ssl_ca_cert | path to file |Path to Certificate Authority file ssl_version|SSLV2,SSLV3,TLSV10,TLSV11,TLSV12,MAX| The SSL method level, defaults to highest available encryption level which is TLSv1.2 +ssl_cert_verify_depth|integer|Certificate authority certificate verification depth, default is 100. diff --git a/server/core/config.c b/server/core/config.c index 40be2c704..3e778d077 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -346,6 +346,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); char *version_string; char *subservices; char *ssl,*ssl_cert,*ssl_key,*ssl_ca_cert,*ssl_version; + char* ssl_cert_verify_depth; bool is_rwsplit = false; bool is_schemarouter = false; char *allow_localhost_match_wildcard_host; @@ -359,6 +360,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); ssl_key = config_get_value(obj->parameters, "ssl_key"); ssl_ca_cert = config_get_value(obj->parameters, "ssl_ca_cert"); ssl_version = config_get_value(obj->parameters, "ssl_version"); + ssl_cert_verify_depth = config_get_value(obj->parameters, "ssl_cert_verify_depth"); enable_root_user = config_get_value( obj->parameters, "enable_root_user"); @@ -514,6 +516,14 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); error_count++; } } + if(ssl_cert_verify_depth) + { + if(serviceSetSSLVerifyDepth(obj->element,atoi(ssl_cert_verify_depth)) != 0) + { + skygw_log_write(LE,"Error: Invalid parameter value for 'ssl_cert_verify_depth' for service '%s': %s",obj->object,ssl_cert_verify_depth); + error_count++; + } + } } } @@ -2005,6 +2015,7 @@ static char *service_params[] = "ssl", "ssl_key", "ssl_version", + "ssl_cert_verify_depth", NULL }; diff --git a/server/core/dcb.c b/server/core/dcb.c index 05114f48f..f600239ca 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -887,10 +887,13 @@ return_n: /** * General purpose read routine to read data from a socket in the * Descriptor Control Block and append it to a linked list of buffers. - * The list may be empty, in which case *head == NULL + * This function will read at most nbytes of data. + * + * The list may be empty, in which case *head == NULL. This * * @param dcb The DCB to read from * @param head Pointer to linked list to append data to + * @param nbytes Maximum number of bytes read * @return -1 on error, otherwise the number of read bytes on the last * iteration of while loop. 0 is returned if no data available. */ @@ -1835,7 +1838,8 @@ int above_water; /** * Drain the write queue of a DCB. This is called as part of the EPOLLOUT handling * of a socket and will try to send any buffered data from the write queue - * up until the point the write would block. + * up until the point the write would block. This function uses SSL encryption + * and the SSL handshake should have been completed prior to calling this function. * * @param dcb DCB to drain the write queue of * @return The number of bytes written @@ -2495,9 +2499,10 @@ static bool dcb_set_state_nomutex( } /** - * Write data to a DCB + * Write data to a socket through an SSL structure. The SSL structure is linked to a DCB's socket + * and all communication is encrypted and done via the SSL structure. * - * @param ssl The SSL to write the buffer to + * @param ssl The SSL structure to use for writing * @param buf Buffer to write * @param nbytes Number of bytes to write * @return Number of written bytes diff --git a/server/core/gateway.c b/server/core/gateway.c index 412714fbb..21c4472c0 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -208,11 +208,21 @@ static int set_user(); /** SSL multi-threading functions and structures */ +/** + * OpenSSL requires this struct to be defined in order to use dynamic locks + */ struct CRYPTO_dynlock_value { SPINLOCK lock; }; +/** + * Create a dynamic OpenSSL lock. The dynamic lock is just a wrapper structure + * around a SPINLOCK structure. + * @param file File name + * @param line Line number + * @return Pointer to new lock or NULL of an error occurred + */ static struct CRYPTO_dynlock_value *ssl_create_dynlock(const char* file, int line) { struct CRYPTO_dynlock_value* lock = malloc(sizeof(struct CRYPTO_dynlock_value)); @@ -223,6 +233,13 @@ static struct CRYPTO_dynlock_value *ssl_create_dynlock(const char* file, int lin return lock; } +/** + * Lock a dynamic lock for OpenSSL. + * @param mode + * @param n pointer to lock + * @param file File name + * @param line Line number + */ static void ssl_lock_dynlock(int mode,struct CRYPTO_dynlock_value * n,const char* file, int line) { if(mode & CRYPTO_LOCK) @@ -235,11 +252,21 @@ static void ssl_lock_dynlock(int mode,struct CRYPTO_dynlock_value * n,const char } } +/** + * Free a dynamic OpenSSL lock. + * @param n Lock to free + * @param file File name + * @param line Line number + */ static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, int line) { free(n); } +/** + * The thread ID callback function for OpenSSL dynamic locks. + * @param id Id to modify + */ static void maxscale_ssl_id(CRYPTO_THREADID* id) { CRYPTO_THREADID_set_numeric(id,pthread_self()); diff --git a/server/core/service.c b/server/core/service.c index bd09f06ee..4215ce725 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -94,7 +94,7 @@ static SPINLOCK service_spin = SPINLOCK_INIT; static SERVICE *allServices = NULL; static int find_type(typelib_t* tl, const char* needle, int maxlen); -DH *ssl_get_dh2236(); + static void service_add_qualified_param( SERVICE* svc, CONFIG_PARAMETER* param); @@ -144,7 +144,8 @@ SERVICE *service; service->ssl_ca_cert = NULL; service->ssl_cert = NULL; service->ssl_key = NULL; - /** Use the highest possible SSL/TLS methods available */ + service->ssl_cert_verify_depth = DEFAULT_SSL_CERT_VERIFY_DEPTH; + /** Support the highest possible SSL/TLS methods available as the default */ service->ssl_method_type = SERVICE_SSL_TLS_MAX; if (service->name == NULL || service->routerModule == NULL) { @@ -875,6 +876,14 @@ serviceOptimizeWildcard(SERVICE *service, int action) return 1; } +/** + * Set the locations of the server's SSL certificate, server's private key and the CA + * certificate which both the client and the server should trust. + * @param service Service to configure + * @param cert SSL certificate + * @param key SSL private key + * @param ca_cert SSL CA certificate + */ void serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert) { @@ -891,6 +900,12 @@ serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert) service->ssl_ca_cert = strdup(ca_cert); } +/** + * Set the maximum SSL/TLS version the service will support + * @param service Service to configure + * @param version SSL/TLS version string + * @return 0 on success, -1 on invalid version string + */ int serviceSetSSLVersion(SERVICE *service, char* version) { @@ -909,7 +924,34 @@ serviceSetSSLVersion(SERVICE *service, char* version) else return -1; return 0; } -/** Enable or disable the service SSL capability*/ + +/** + * Set the service's SSL certificate verification depth. Depth of 0 means the peer + * certificate, 1 is the CA and 2 is a higher CA and so on. + * @param service Service to configure + * @param depth Certificate verification depth + * @return 0 on success, -1 on incorrect depth value + */ +int serviceSetSSLVerifyDepth(SERVICE* service, int depth) +{ + if(depth < 0) + return -1; + + service->ssl_cert_verify_depth = depth; + return 0; +} + +/** + * Enable or disable the service SSL capability of a service. + * The SSL mode string passed as a parameter should be one of required, enabled + * or disabled. Required requires all connections to use SSL encryption, enabled + * allows both SSL and non-SSL connections and disabled does not use SSL encryption. + * If the service SSL mode is set to enabled, then the client will decide whether + * SSL encryption is used. + * @param service Service to configure + * @param action Mode string. One of required, enabled or disabled. + * @return 0 on success, -1 on error + */ int serviceSetSSL(SERVICE *service, char* action) { @@ -1854,11 +1896,11 @@ int *data; /** - * - * @param s - * @param is_export - * @param keylength - * @return + * The RSA ket generation callback function for OpenSSL. + * @param s SSL structure + * @param is_export Not used + * @param keylength Length of the key + * @return Pointer to RSA structure */ RSA *tmp_rsa_callback(SSL *s, int is_export, int keylength) { @@ -1887,6 +1929,13 @@ int *data; return(rsa_tmp); } + /** + * Initialize the servce's SSL context. This sets up the generated RSA + * encryption keys, chooses the server encryption level and configures the server + * certificate, private key and certificate authority file. + * @param service + * @return + */ int serviceInitSSL(SERVICE* service) { DH* dh; @@ -1911,6 +1960,8 @@ int serviceInitSSL(SERVICE* service) case SERVICE_TLS12: service->method = (SSL_METHOD*)TLSv1_2_server_method(); break; + + /** Rest of these use the maximum available SSL/TLS methods */ case SERVICE_SSL_MAX: service->method = (SSL_METHOD*)SSLv23_server_method(); break; @@ -1926,8 +1977,11 @@ int serviceInitSSL(SERVICE* service) } service->ctx = SSL_CTX_new(service->method); + + /** Enable all OpenSSL bug fixes */ SSL_CTX_set_options(service->ctx,SSL_OP_ALL); + /** Generate the 512-bit and 1024-bit RSA keys */ if(rsa_512 == NULL) { rsa_512 = RSA_generate_key(512,RSA_F4,NULL,NULL); @@ -1944,6 +1998,7 @@ int serviceInitSSL(SERVICE* service) if(rsa_512 != NULL && rsa_1024 != NULL) SSL_CTX_set_tmp_rsa_callback(service->ctx,tmp_rsa_callback); + /** Load the server sertificate */ if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { skygw_log_write(LE,"Error: Failed to set server SSL certificate."); return -1; @@ -1971,54 +2026,9 @@ int serviceInitSSL(SERVICE* service) /* Set to require peer (client) certificate verification */ SSL_CTX_set_verify(service->ctx,SSL_VERIFY_PEER,NULL); - /* Set the verification depth to 1 */ - SSL_CTX_set_verify_depth(service->ctx,1); + /* Set the verification depth */ + SSL_CTX_set_verify_depth(service->ctx,service->ssl_cert_verify_depth); service->ssl_init_done = true; } return 0; } - -/** - * Generated by OpenSSL. - * @return - */ -DH *ssl_get_dh2236() -{ - static unsigned char dh2236_p[]={ - 0x0B,0xC3,0xEC,0x3F,0xCB,0xD0,0x2E,0x43,0x7B,0x13,0xF9,0x0C, - 0x4D,0xE5,0xA3,0xA4,0xDB,0x68,0x13,0xBD,0xFC,0xD2,0x35,0x05, - 0xCB,0x62,0xA1,0x85,0x33,0x20,0xC4,0x88,0x3B,0x2B,0xD5,0x76, - 0x94,0xCD,0xEB,0x9C,0x5A,0xD1,0x16,0xDB,0x51,0x82,0x7A,0x1E, - 0xC6,0xC3,0xD9,0x52,0x8F,0x54,0x33,0xF4,0x50,0x96,0x01,0xF4, - 0x71,0xA1,0x8B,0x9B,0x43,0x85,0x9C,0x95,0xFF,0x53,0x1D,0x8D, - 0xDF,0xBC,0x60,0xEB,0x4D,0x96,0xD1,0x05,0x98,0x4A,0xEB,0xC9, - 0x33,0xF6,0xE9,0x74,0x73,0x29,0x27,0xCA,0x0D,0x6D,0xEA,0x36, - 0xB9,0x3B,0x54,0xF6,0x34,0x68,0x13,0xFA,0xAC,0x3B,0x57,0x55, - 0x76,0x41,0x67,0x48,0xEF,0x3C,0xE1,0xE1,0xAF,0x3C,0x68,0x05, - 0x9C,0x32,0xD9,0x14,0x8F,0xB2,0xEE,0xEE,0xBA,0x9F,0x0D,0x75, - 0xA7,0x33,0x1F,0x3A,0x0E,0xD1,0xA6,0x5A,0x29,0xC7,0x9B,0x5E, - 0x46,0xB1,0xA6,0xA5,0x1E,0x32,0xDB,0xAF,0x23,0x83,0x94,0x12, - 0x4F,0xE4,0xC2,0x8B,0x1B,0x2C,0x01,0x79,0x92,0x21,0xFF,0x01, - 0xED,0x46,0x27,0xF0,0x70,0x2A,0xA1,0xFD,0x5C,0x8F,0x8B,0x0C, - 0xC6,0x8F,0xFF,0x4C,0x99,0xAE,0x19,0xDB,0x58,0x4C,0xC0,0xE8, - 0x70,0xCC,0x7C,0x17,0xE8,0xBD,0x6B,0x19,0x93,0xB9,0x66,0xA9, - 0xD0,0x05,0x21,0x04,0x4C,0x7E,0x87,0x9F,0xF4,0xE9,0x23,0xE1, - 0x29,0x37,0xC5,0xE2,0x0A,0xC5,0xC1,0x92,0xC7,0x69,0xB4,0xFB, - 0x84,0x06,0xCE,0x0E,0xFC,0x65,0x70,0x2F,0xF6,0xB8,0x11,0xF9, - 0x0F,0x60,0x10,0xCA,0x94,0x29,0x44,0x5E,0x4A,0x05,0x46,0xE5, - 0xE6,0xA0,0xBD,0x14,0x45,0xA6,0xA7,0xCA,0x63,0x57,0xC6,0xB0, - 0x47,0xF9,0x71,0x24,0x19,0x75,0xD2,0x64,0x16,0xB1,0xBA,0x08, - 0xE9,0xE9,0xFB,0xF3, - }; - static unsigned char dh2236_g[]={ - 0x02, - }; - DH *dh; - - if ((dh=DH_new()) == NULL) return(NULL); - dh->p=BN_bin2bn(dh2236_p,sizeof(dh2236_p),NULL); - dh->g=BN_bin2bn(dh2236_g,sizeof(dh2236_g),NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - { DH_free(dh); return(NULL); } - return(dh); -} diff --git a/server/include/service.h b/server/include/service.h index 936a73058..085c0c595 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -125,6 +125,8 @@ enum{ SERVICE_SSL_TLS_MAX }; +#define DEFAULT_SSL_CERT_VERIFY_DEPTH 100 /*< The default certificate verification depth */ + /** * Defines a service within the gateway. * @@ -173,14 +175,14 @@ typedef struct service { char *weightby; struct service *next; /**< The next service in the linked list */ SSL_CTX *ctx; - SSL *ssl; SSL_METHOD *method; /*< SSLv2/3 or TLSv1/2 methods * see: https://www.openssl.org/docs/ssl/SSL_CTX_new.html */ + int ssl_cert_verify_depth; /*< SSL certificate verification depth */ int ssl_method_type; /*< Which of the SSLv2/3 or TLS1.0/1.1/1.2 methods to use */ - char* ssl_cert; - char* ssl_key; - char* ssl_ca_cert; - bool ssl_init_done; + char* ssl_cert; /*< SSL certificate */ + char* ssl_key; /*< SSL private key */ + char* ssl_ca_cert; /*< SSL CA certificate */ + bool ssl_init_done; /*< If SSL has already been initialized for this service */ } SERVICE; @@ -212,6 +214,7 @@ extern void serviceSetFilters(SERVICE *, char *); extern int serviceSetSSL(SERVICE *service, char* action); extern int serviceInitSSL(SERVICE* service); extern int serviceSetSSLVersion(SERVICE *service, char* version); +extern int serviceSetSSLVerifyDepth(SERVICE* service, int depth); extern void serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert); extern int serviceEnableRootUser(SERVICE *, int ); extern int serviceSetTimeout(SERVICE *, int ); diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 3164f1642..c3e463139 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -389,15 +389,17 @@ MySQLSendHandshake(DCB* dcb) /** * gw_mysql_do_authentication * - * Performs the MySQL protocol 4.1 authentication, using data in GWBUF *queue + * Performs the MySQL protocol 4.1 authentication, using data in GWBUF **queue. * * (MYSQL_session*)client_data including: user, db, client_sha1 are copied into - * the dcb->data and later to dcb->session->data. - * - * client_capabilitiesa are copied into the dcb->protocol + * the dcb->data and later to dcb->session->data. client_capabilities are copied + * into the dcb->protocol. + * + * If SSL is enabled for the service, the SSL handshake will be done before the + * MySQL authentication. * * @param dcb Descriptor Control Block of the client - * @param queue The GWBUF with data from client + * @param queue Pointer to the location of the GWBUF with data from client * @return 0 If succeed, otherwise non-zero value * * @note in case of failure, dcb->data is freed before returning. If succeed, @@ -507,8 +509,11 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { } else if(dcb->service->ssl_mode == SSL_ENABLED) { - /** This is a non-SSL connection to a SSL enabled service - * and we need to read the rest of the packet from the socket for the username */ + /** This is a non-SSL connection to a SSL enabled service. + * We have only read enough of the packet to know that the client + * is not requesting SSL and the rest of the auth packet is still + * waiting in the socket. We need to read the data from the socket + * to find out the username of the connecting client. */ int bytes = dcb_read(dcb,&queue); queue = gwbuf_make_contiguous(queue); client_auth_packet = GWBUF_DATA(queue); @@ -626,7 +631,8 @@ gw_MySQLWrite_client(DCB *dcb, GWBUF *queue) /** - * Write function for client DCB: writes data from MaxScale to Client + * Write function for client DCB: writes data from MaxScale to Client using SSL + * encryption. The SSH handshake must have already been done. * * @param dcb The DCB of the client * @param queue Queue of buffers to write @@ -671,6 +677,8 @@ int gw_read_client_event( #endif + /** SSL authentication is still going on, we need to call do_ssl_accept + * until it return 1 for success or -1 for error */ if(protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_ONGOING || protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ) { @@ -704,15 +712,21 @@ int gw_read_client_event( if(protocol->use_ssl) { + /** SSL handshake is done, communication is now encrypted with SSL */ rc = dcb_read_SSL(dcb, &read_buffer); } else if(dcb->service->ssl_mode != SSL_DISABLED && protocol->protocol_auth_state == MYSQL_AUTH_SENT) { + /** The service allows both SSL and non-SSL connections. + * read only enough of the auth packet to know if the client is + * requesting SSL. If the client is not requesting SSL the rest of + the auth packet will be read later. */ rc = dcb_read_n(dcb, &read_buffer,(4 + 4 + 4 + 1 + 23)); } else { + /** Normal non-SSL connection */ rc = dcb_read(dcb, &read_buffer); } @@ -869,6 +883,9 @@ int gw_read_client_event( protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_DONE || protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_FAILED) { + /** SSL was requested and the handshake is either done or + * still ongoing. After the handshake is done, the client + * will send another auth packet. */ break; } @@ -1249,22 +1266,12 @@ return_1: return 1; } -/////////////////////////////////////////////// -// client write event to Client triggered by EPOLLOUT -////////////////////////////////////////////// -/** - * @node Client's fd became writable, and EPOLLOUT event - * arrived. As a consequence, client input buffer (writeq) is flushed. - * - * Parameters: - * @param dcb - in, use - * client dcb - * +/** + * EPOLLOUT event arrived and as a consequence, client input buffer (writeq) is + * flushed. The data is encrypted and SSL is used. The SSL handshake must have + * been successfully completed prior to this function being called. + * @param client dcb * @return constantly 1 - * - * - * @details (write detailed description here) - * */ int gw_write_client_event_SSL(DCB *dcb) { From 9b0a5f13285902a7ef7cdfd121f8d837ef53cb1f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 15 Jun 2015 16:16:48 +0300 Subject: [PATCH 159/273] Added more comments. --- server/core/dcb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index f600239ca..7f3651953 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1032,9 +1032,10 @@ return_n: /** - * General purpose read routine to read data from a socket in the - * Descriptor Control Block and append it to a linked list of buffers. - * The list may be empty, in which case *head == NULL + * General purpose read routine to read data from a socket through the SSL + * structure lined with this DCB and append it to a linked list of buffers. + * The list may be empty, in which case *head == NULL. The SSL structure should + * be initialized and the SSL handshake should be done. * * @param dcb The DCB to read from * @param head Pointer to linked list to append data to From 2775145f377b9c3c66fb60b366c7561f7d502db6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 16 Jun 2015 12:59:48 +0300 Subject: [PATCH 160/273] Fixed spelling errors. --- Documentation/Getting-Started/Configuration-Guide.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 113e28c60..0ca3843c4 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -119,7 +119,7 @@ datadir=/home/user/maxscale_data/ #### `libdir` -Set the directory where MaxScale looks for modules. The library director is the only directory that MaxScale uses when it searches for modules. If you have custom modules for MaxScale, make sure you have them in this folder. +Set the directory where MaxScale looks for modules. The library directory is the only directory that MaxScale uses when it searches for modules. If you have custom modules for MaxScale, make sure you have them in this folder. ``` libdir=/home/user/lib64/ @@ -328,7 +328,7 @@ connection_timeout=300 ### Service and SSL -This section describes configuration parameters for services that control the SSL/TLS encrption method and the various certificate files involved in it. To enable SSL, you must configure the `ssl` parameter with either `enabled` or `required` and provide the three files for `ssl_cert`, `ssl_key` and `ssl_ca_cert`. After this, MySQL connections to this service can be encrypted with SSL. +This section describes configuration parameters for services that control the SSL/TLS encryption method and the various certificate files involved in it. To enable SSL, you must configure the `ssl` parameter with either `enabled` or `required` and provide the three files for `ssl_cert`, `ssl_key` and `ssl_ca_cert`. After this, MySQL connections to this service can be encrypted with SSL. #### `ssl` @@ -381,7 +381,7 @@ ssl_ca_cert=/home/markus/certs/ca.pem ssl_version=TLSv12 ``` -This configuration requires all connections to be encryped with SSL. It also specifies that TLSv1.2 should be used as the encryption method. The paths to the server certificate files and the Certificate Authority file are also provided. +This configuration requires all connections to be encrypted with SSL. It also specifies that TLSv1.2 should be used as the encryption method. The paths to the server certificate files and the Certificate Authority file are also provided. ### Server @@ -1129,7 +1129,7 @@ MariaDB [mysql]> grant REPLICATION CLIENT on *.* to 'maxscalemon'@'maxscalehost' Query OK, 0 rows affected (0.00 sec) ``` -MySQL monitor fetches the `@@server_id` variable and other informations from `SHOW SLAVE STATUS` in order to compute the replication topology tree that may include intermediate master servers, called relay servers. +MySQL monitor fetches the `@@server_id` variable and other information from `SHOW SLAVE STATUS` in order to compute the replication topology tree that may include intermediate master servers, called relay servers. The *Master* server used by router modules is the so called "root master": a server that has the `SERVER_MASTER` status bit set and it's at the lowest level of the replication depth. From 2ea7ff61a21880890e0f92dccc9ec31eaebc2c97 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 16 Jun 2015 14:07:34 +0300 Subject: [PATCH 161/273] Fixed init.d and systemd scripts. --- etc/maxscale.service.in | 1 + etc/ubuntu/init.d/maxscale.in | 14 +++++++------- server/include/gwdirs.h.in | 4 +++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/etc/maxscale.service.in b/etc/maxscale.service.in index 6717f6b25..1d0597c76 100644 --- a/etc/maxscale.service.in +++ b/etc/maxscale.service.in @@ -4,6 +4,7 @@ After=network.target [Service] Type=forking +Restart=on-failure PIDFile=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale diff --git a/etc/ubuntu/init.d/maxscale.in b/etc/ubuntu/init.d/maxscale.in index f23ed144f..a60425890 100755 --- a/etc/ubuntu/init.d/maxscale.in +++ b/etc/ubuntu/init.d/maxscale.in @@ -44,8 +44,8 @@ _RETVAL_STATUS_NOT_RUNNING=3 # stop/start/status related vars ################################# NAME=maxscale -DAEMON=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale - +DAEMON=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale +DAEMON_OPTS= --user=maxscale # Source function library. . /lib/lsb/init-functions @@ -57,11 +57,11 @@ RETVAL=0 start() { log_daemon_msg "Starting MaxScale" - start_daemon -p $MAXSCALE_PIDFILE $DAEMON 2> /dev/null > /dev/null + start_daemon -p "$MAXSCALE_PIDFILE" "$DAEMON" "$DAEMON_OPTS" 2> /dev/null > /dev/null sleep 2 - status_of_proc -p $MAXSCALE_PIDFILE $DAEMON $NAME + status_of_proc -p "$MAXSCALE_PIDFILE" "$DAEMON" $NAME log_end_msg $? } @@ -77,13 +77,13 @@ stop() { reload() { log_daemon_msg "Reloading MaxScale" - kill -HUP $(cat $MAXSCALE_PIDFILE) + kill -HUP $(cat "$MAXSCALE_PIDFILE") log_end_msg $? } maxscale_wait_stop() { - PIDTMP=$(pidofproc -p $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale) + PIDTMP=$(pidofproc -p "$MAXSCALE_PIDFILE" "$DAEMON") kill -TERM "${PIDTMP:-}" 2> /dev/null; if [ -n "${PIDTMP:-}" ] && kill -0 "${PIDTMP:-}" 2> /dev/null; then local i=0 @@ -115,7 +115,7 @@ case "$1" in # return 3 on any error log_daemon_msg "Checking MaxScale" - status_of_proc -p $MAXSCALE_PIDFILE $DAEMON $NAME + status_of_proc -p "$MAXSCALE_PIDFILE" "$DAEMON" $NAME RETVAL=$? if [ $RETVAL -ne 0 ]; then diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index fe911c71b..d9c17e152 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -26,7 +26,9 @@ /** Default file locations, configured by CMake */ static const char* default_cnf_fname = "maxscale.cnf"; static const char* default_configdir = "/etc/"; -static const char* default_piddir = "@MAXSCALE_VARDIR@/run/maxscale/"; +static const char* default_piddir = "@MAXSCALE_VARDIR@/run/maxscale/"; /*< This should be changed to just /run eventually, + * the /var/run folder is an old standard and the newe FSH 3.0 + * uses /run for PID files.*/ static const char* default_logdir = "@MAXSCALE_VARDIR@/log/maxscale/"; static const char* default_datadir = "@MAXSCALE_VARDIR@/lib/maxscale/"; static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@"; From 97a06e4006436cfd949ae54e3a6d9eac4b34fd7a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 16 Jun 2015 14:24:38 +0300 Subject: [PATCH 162/273] Updated function documentation. --- server/core/service.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/core/service.c b/server/core/service.c index 4215ce725..4d2507cd6 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -1373,7 +1373,14 @@ void *router_obj; } } - +/** + * Refresh the database users for the service + * This function replaces the MySQL users used by the service with the latest + * version found on the backend servers. There is a limit on how often the users + * can be reloaded and if this limit is exceeded, the reload will fail. + * @param service Service to reload + * @return 0 on success and 1 on error + */ int service_refresh_users(SERVICE *service) { int ret = 1; /* check for another running getUsers request */ From 466ee46d8263de83c0d6f512df63a94f37095626 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 16 Jun 2015 17:10:00 +0300 Subject: [PATCH 163/273] Fix to MXS-125: https://mariadb.atlassian.net/browse/MXS-125 Unified the usages of maxkeys and maxpasswd. --- .../Getting-Started/Configuration-Guide.md | 14 ++++--- server/core/maxkeys.c | 38 ++++++++----------- server/core/maxpasswd.c | 8 ++-- server/core/secrets.c | 31 ++++++++------- server/include/secrets.h | 2 +- 5 files changed, 47 insertions(+), 46 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 0ca3843c4..afda397fe 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -1393,20 +1393,24 @@ In addition parameters may be added to define patterns to match against to eithe ## Encrypting Passwords -Passwords stored in the maxscale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. The default location MaxScale stores the keys is `/var/cache/maxscale`. +Passwords stored in the maxscale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. The default location MaxScale stores the keys is `/var/lib/maxscale`. ``` -maxkeys /var/cache/maxscale/.secrets + # Usage: maxkeys [PATH] +maxkeys /var/lib/maxscale/ ``` Changing the encryption key for MaxScale will invalidate any currently encrypted keys stored in the maxscale.cnf file. ### Creating Encrypted Passwords -Encrypted passwords are created by executing the maxpasswd command with the password you require to encrypt as an argument. +Encrypted passwords are created by executing the maxpasswd command with the location of the .secrets file and the password you require to encrypt as an argument. - maxpasswd MaxScalePw001 - 61DD955512C39A4A8BC4BB1E5F116705 +``` +# Usage: maxpasswd PATH PASSWORD +maxpasswd /var/lib/maxscale/ MaxScalePw001 +61DD955512C39A4A8BC4BB1E5F116705 +``` The output of the maxpasswd command is a hexadecimal string, this should be inserted into the maxscale.cnf file in place of the ordinary, plain text, password. MaxScale will determine this as an encrypted password and automatically decrypt it before sending it the database server. diff --git a/server/core/maxkeys.c b/server/core/maxkeys.c index 557920dbe..27b5c8382 100644 --- a/server/core/maxkeys.c +++ b/server/core/maxkeys.c @@ -32,19 +32,24 @@ #include #include #include + int main(int argc, char **argv) { - int arg_count = 6; + int arg_count = 4; char *home; + char *keyfile; char** arg_vector; int rval = 0; - if (argc != 2) + if (argc < 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; + keyfile = "/var/lib/maxscale/"; + fprintf(stderr, "Generating .secrets file in /var/lib/maxscale/ ...\n"); + } + else + { + keyfile = argv[1]; } - arg_vector = malloc(sizeof(char*)*(arg_count + 1)); if(arg_vector == NULL) @@ -53,27 +58,16 @@ int main(int argc, char **argv) return 1; } - if(access("/var/log/maxscale/maxkeys/",F_OK) != 0) - { - if(mkdir("/var/log/maxscale/maxkeys/",0777) == -1) - { - if(errno != EEXIST) - { - fprintf(stderr,"Error: %d - %s",errno,strerror(errno)); - return 1; - } - } - } - arg_vector[0] = strdup("logmanager"); - arg_vector[1] = strdup("-j"); - arg_vector[2] = strdup("/var/log/maxscale/maxkeys"); - arg_vector[3] = NULL; + arg_vector[0] = "logmanager"; + arg_vector[1] = "-j"; + arg_vector[2] = "/var/log/maxscale/maxkeys"; + arg_vector[3] = "-o"; + arg_vector[4] = NULL; skygw_logmanager_init(arg_count,arg_vector); - free(arg_vector[2]); free(arg_vector); - if (secrets_writeKeys(argv[1])) + if (secrets_writeKeys(keyfile)) { fprintf(stderr, "Failed to encode the password\n"); rval = 1; diff --git a/server/core/maxpasswd.c b/server/core/maxpasswd.c index 5c761f378..0a3d5ddea 100644 --- a/server/core/maxpasswd.c +++ b/server/core/maxpasswd.c @@ -46,9 +46,9 @@ main(int argc, char **argv) char** arg_vector; int rval = 0; - if (argc != 2) + if (argc != 3) { - fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } @@ -79,9 +79,9 @@ main(int argc, char **argv) return 1; } - strncpy(pw,argv[1],80); + strncpy(pw,argv[2],80); - if ((enc = encryptPassword(pw)) != NULL){ + if ((enc = encryptPassword(argv[1],pw)) != NULL){ printf("%s\n", enc); }else{ fprintf(stderr, "Failed to encode the password\n"); diff --git a/server/core/secrets.c b/server/core/secrets.c index 2ec3e75a9..97cb49806 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -53,15 +53,14 @@ int i; } /** - * secrets_readKeys - * - * This routine reads data from a binary file and extracts the AES encryption key - * and the AES Init Vector - * + * This routine reads data from a binary file named ".secrets" and extracts the AES encryption key + * and the AES Init Vector. + * If the path parameter is not null the custom path is interpreted as a folder + * containing the .secrets file. Otherwise the default location is used. * @return The keys structure or NULL on error */ static MAXKEYS * -secrets_readKeys() +secrets_readKeys(char* path) { char secret_file[PATH_MAX+1]; char *home; @@ -70,9 +69,10 @@ struct stat secret_stats; int fd; int len; static int reported = 0; - - snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir()); - + if(path != NULL) + snprintf(secret_file, PATH_MAX, "%s/.secrets", path); + else + snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir()); /* Try to access secrets file */ if (access(secret_file, R_OK) == -1) { @@ -221,11 +221,14 @@ static int reported = 0; * @param secret_file The file with secret keys * @return 0 on success and 1 on failure */ -int secrets_writeKeys(char *secret_file) +int secrets_writeKeys(char *path) { int fd,randfd; unsigned int randval; MAXKEYS key; +char secret_file[PATH_MAX + 10]; + + sprintf(secret_file,"%s/.secrets",path); /* Open for writing | Create | Truncate the file for writing */ if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0) @@ -328,7 +331,7 @@ char *ptr; unsigned char encrypted[80]; int enlen; - keys = secrets_readKeys(); + keys = secrets_readKeys(NULL); if (!keys) return strdup(crypt); /* @@ -365,12 +368,12 @@ int enlen; * Encrypt a password that can be stored in the MaxScale configuration file. * * Note the return is always a malloc'd string that the caller must free - * + * @param path Path the the .secrets file * @param password The password to encrypt * @return The encrypted password */ char * -encryptPassword(char *password) +encryptPassword(char* path, char *password) { MAXKEYS *keys; AES_KEY aeskey; @@ -379,7 +382,7 @@ char *hex_output; unsigned char padded_passwd[80]; unsigned char encrypted[80]; - if ((keys = secrets_readKeys()) == NULL) + if ((keys = secrets_readKeys(path)) == NULL) return NULL; memset(padded_passwd, 0, 80); diff --git a/server/include/secrets.h b/server/include/secrets.h index 505e79154..cb2912827 100644 --- a/server/include/secrets.h +++ b/server/include/secrets.h @@ -53,5 +53,5 @@ typedef struct maxkeys { extern int secrets_writeKeys(char *filename); extern char *decryptPassword(char *); -extern char *encryptPassword(char *); +extern char *encryptPassword(char*,char *); #endif From 2d63aa2842b0da54a8cb988963e7f461227314b3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 16 Jun 2015 22:36:53 +0300 Subject: [PATCH 164/273] Fixed typo and added an example script. --- Documentation/monitors/Galera-Monitor.md | 2 +- Documentation/monitors/MM-Monitor.md | 2 +- Documentation/monitors/MySQL-Monitor.md | 30 ++++++++++++++++++- Documentation/monitors/NDB-Cluster-Monitor.md | 2 +- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Documentation/monitors/Galera-Monitor.md b/Documentation/monitors/Galera-Monitor.md index ffe54fb83..6a66fdcfc 100644 --- a/Documentation/monitors/Galera-Monitor.md +++ b/Documentation/monitors/Galera-Monitor.md @@ -108,7 +108,7 @@ master_down|A Master server has gone down master_up|A Master server has come up slave_down|A Slave server has gone down slave_up|A Slave server has come up -server_down|A server with no assigned role has done down +server_down|A server with no assigned role has gone down server_up|A server with no assigned role has come up synced_down|A synced Galera node has come up synced_up|A synced Galera node has gone down diff --git a/Documentation/monitors/MM-Monitor.md b/Documentation/monitors/MM-Monitor.md index 00375d4bf..ad9ee2c07 100644 --- a/Documentation/monitors/MM-Monitor.md +++ b/Documentation/monitors/MM-Monitor.md @@ -99,7 +99,7 @@ master_down|A Master server has gone down master_up|A Master server has come up slave_down|A Slave server has gone down slave_up|A Slave server has come up -server_down|A server with no assigned role has done down +server_down|A server with no assigned role has gone down server_up|A server with no assigned role has come up lost_master|A server lost Master status lost_slave|A server lost Slave status diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index ab643109e..1bfc06109 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -108,10 +108,38 @@ master_down|A Master server has gone down master_up|A Master server has come up slave_down|A Slave server has gone down slave_up|A Slave server has come up -server_down|A server with no assigned role has done down +server_down|A server with no assigned role has gone down server_up|A server with no assigned role has come up lost_master|A server lost Master status lost_slave|A server lost Slave status new_master|A new Master was detected new_slave|A new Slave was detected + +## Example 1 - Monitor script + +Here is an example shell script which sends an email to an admin when a server goes down. + +``` +#!/usr/bin/env bash + +#This script assumes that the local mail server is configured properly + +message="A server has gone down at `date`." +echo $message|mail -s "A server has gone down" admin@my.org + +``` + +Here is a monitor configuration that only triggers the script when a master or a slave server goes down. + +``` +[Database Monitor] +type=monitor +module=mysqlmon +servers=server1,server2 +script=mail_to_admin.sh +events=master_down,slave_down +``` + +When a master or a slave server goes down, the script is executed, a mail is sent and the administrator will be immediately notified of any possible problems. +This is just a simple example showing what you can do with MaxScale and monitor scripts. diff --git a/Documentation/monitors/NDB-Cluster-Monitor.md b/Documentation/monitors/NDB-Cluster-Monitor.md index a9f453678..9468ec323 100644 --- a/Documentation/monitors/NDB-Cluster-Monitor.md +++ b/Documentation/monitors/NDB-Cluster-Monitor.md @@ -89,7 +89,7 @@ master_down|A Master server has gone down master_up|A Master server has come up slave_down|A Slave server has gone down slave_up|A Slave server has come up -server_down|A server with no assigned role has done down +server_down|A server with no assigned role has gone down server_up|A server with no assigned role has come up ndb_down|A MySQL Cluster node has gone down ndb_up|A MySQL Cluster node has come up From f0aed1f66628c12fd3de1d1c6c759eac6ffb4a5d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 17 Jun 2015 10:05:56 +0300 Subject: [PATCH 165/273] Fixed possible null pointer dereferences in mysql_backend. --- server/modules/protocol/mysql_backend.c | 46 +++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index d000474ab..477ca4cf0 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -1159,30 +1159,32 @@ gw_backend_close(DCB *dcb) * but client's close and adding client's DCB to zombies list is executed * only if client's DCB's state does _not_ change in parallel. */ - spinlock_acquire(&session->ses_lock); - /** - * If session->state is STOPPING, start closing client session. - * Otherwise only this backend connection is closed. - */ - if (session != NULL && - session->state == SESSION_STATE_STOPPING && - session->client != NULL) - { - if (session->client->state == DCB_STATE_POLLING) - { - spinlock_release(&session->ses_lock); - - /** Close client DCB */ - dcb_close(session->client); - } - else - { - spinlock_release(&session->ses_lock); - } - } - else + if(session != NULL) { + spinlock_acquire(&session->ses_lock); + /** + * If session->state is STOPPING, start closing client session. + * Otherwise only this backend connection is closed. + */ + if (session->state == SESSION_STATE_STOPPING && + session->client != NULL) + { + if (session->client->state == DCB_STATE_POLLING) + { + spinlock_release(&session->ses_lock); + + /** Close client DCB */ + dcb_close(session->client); + } + else + { + spinlock_release(&session->ses_lock); + } + } + else + { spinlock_release(&session->ses_lock); + } } return 1; } From 425dd8cb3b7c93b698a3395022638f299a6a4861 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 17 Jun 2015 10:20:00 +0300 Subject: [PATCH 166/273] Removed SSLv2 methods from serviceInitSSL because OpenSSL 1.1.0 does not support them. --- Documentation/Getting-Started/Configuration-Guide.md | 1 - Documentation/Reference/MaxScale-and-SSL.md | 2 +- server/core/service.c | 7 +------ server/include/service.h | 5 ++--- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index afda397fe..c2f815d09 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -349,7 +349,6 @@ This is the Certificate Authority file. It will be used to verify that both the ### `ssl_version` This parameter controls the level of encryption used. Accepted values are: - * SSLv2 * SSLv3 * TLSv10 * TLSv11 diff --git a/Documentation/Reference/MaxScale-and-SSL.md b/Documentation/Reference/MaxScale-and-SSL.md index d03a5af52..f293085c1 100644 --- a/Documentation/Reference/MaxScale-and-SSL.md +++ b/Documentation/Reference/MaxScale-and-SSL.md @@ -11,5 +11,5 @@ ssl | disabled, enabled, required |`disable` disables SSL, `enabled` ena ssl_cert | path to file |Path to server certificate ssl_key | path to file |Path to server private key ssl_ca_cert | path to file |Path to Certificate Authority file -ssl_version|SSLV2,SSLV3,TLSV10,TLSV11,TLSV12,MAX| The SSL method level, defaults to highest available encryption level which is TLSv1.2 +ssl_version|SSLV3,TLSV10,TLSV11,TLSV12,MAX| The SSL method level, defaults to highest available encryption level which is TLSv1.2 ssl_cert_verify_depth|integer|Certificate authority certificate verification depth, default is 100. diff --git a/server/core/service.c b/server/core/service.c index 4d2507cd6..b0959e6c3 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -909,9 +909,7 @@ serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert) int serviceSetSSLVersion(SERVICE *service, char* version) { - if(strcasecmp(version,"SSLV2") == 0) - service->ssl_method_type = SERVICE_SSLV2; - else if(strcasecmp(version,"SSLV3") == 0) + if(strcasecmp(version,"SSLV3") == 0) service->ssl_method_type = SERVICE_SSLV3; else if(strcasecmp(version,"TLSV10") == 0) service->ssl_method_type = SERVICE_TLS10; @@ -1952,9 +1950,6 @@ int serviceInitSSL(SERVICE* service) { switch(service->ssl_method_type) { - case SERVICE_SSLV2: - service->method = (SSL_METHOD*)SSLv2_server_method(); - break; case SERVICE_SSLV3: service->method = (SSL_METHOD*)SSLv3_server_method(); break; diff --git a/server/include/service.h b/server/include/service.h index 085c0c595..3337ebfc0 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -115,7 +115,6 @@ typedef enum { } ssl_mode_t; enum{ - SERVICE_SSLV2, SERVICE_SSLV3, SERVICE_TLS10, SERVICE_TLS11, @@ -175,10 +174,10 @@ typedef struct service { char *weightby; struct service *next; /**< The next service in the linked list */ SSL_CTX *ctx; - SSL_METHOD *method; /*< SSLv2/3 or TLSv1/2 methods + SSL_METHOD *method; /*< SSLv3 or TLS1.0/1.1/1.2 methods * see: https://www.openssl.org/docs/ssl/SSL_CTX_new.html */ int ssl_cert_verify_depth; /*< SSL certificate verification depth */ - int ssl_method_type; /*< Which of the SSLv2/3 or TLS1.0/1.1/1.2 methods to use */ + int ssl_method_type; /*< Which of the SSLv3 or TLS1.0/1.1/1.2 methods to use */ char* ssl_cert; /*< SSL certificate */ char* ssl_key; /*< SSL private key */ char* ssl_ca_cert; /*< SSL CA certificate */ From acd649cdc4b7909cf8f144819f175215eb70d8e1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 17 Jun 2015 18:18:40 +0300 Subject: [PATCH 167/273] Reverted back to older OpenSSL functions for compatibility support. --- server/core/gateway.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 21c4472c0..f206ca30e 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -263,15 +263,6 @@ static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, i free(n); } -/** - * The thread ID callback function for OpenSSL dynamic locks. - * @param id Id to modify - */ -static void maxscale_ssl_id(CRYPTO_THREADID* id) -{ - CRYPTO_THREADID_set_numeric(id,pthread_self()); -} - /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. @@ -1468,7 +1459,7 @@ int main(int argc, char **argv) CRYPTO_set_dynlock_create_callback(ssl_create_dynlock); CRYPTO_set_dynlock_destroy_callback(ssl_free_dynlock); CRYPTO_set_dynlock_lock_callback(ssl_lock_dynlock); - CRYPTO_THREADID_set_callback(maxscale_ssl_id); + CRYPTO_set_id_callback(pthread_self); /* register exit function for embedded MySQL library */ l = atexit(libmysqld_done); From fd42395d009208bc24ca51372a838c9e1174fbd1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 18 Jun 2015 08:09:59 +0300 Subject: [PATCH 168/273] Removed mandb call from postinstall script and added cmake messages about systemd service file installation. --- CMakeLists.txt | 4 ++++ etc/postinst.in | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a21e9c745..f77fc79d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,7 @@ if(WITH_SCRIPTS) if(PACKAGE) message(STATUS "maxscale.conf will unpack to: /etc/ld.so.conf.d") message(STATUS "startup scripts will unpack to to: /etc/init.d") + message(STATUS "systemd service files will unpack to to: /usr/lib/systemd/system") install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION ${MAXSCALE_SHAREDIR} @@ -184,8 +185,11 @@ if(WITH_SCRIPTS) PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES ${CMAKE_BINARY_DIR}/maxscale.conf DESTINATION /etc/ld.so.conf.d PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + install(FILES ${CMAKE_BINARY_DIR}/maxscale.service DESTINATION /usr/lib/systemd/system + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) message(STATUS "Installing maxscale.conf to: /etc/ld.so.conf.d") message(STATUS "Installing startup scripts to: /etc/init.d") + message(STATUS "Installing systemd service files to: /usr/lib/systemd/system") endif() endif() diff --git a/etc/postinst.in b/etc/postinst.in index e1d3dc8ad..30e45f806 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -31,14 +31,22 @@ chmod 0755 @MAXSCALE_VARDIR@/cache/maxscale chmod 0755 @MAXSCALE_VARDIR@/run/maxscale # Copy init.d script and ldconfig file -cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale /etc/init.d/ -cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf /etc/ld.so.conf.d/ -if [ -d "/usr/lib/systemd/system" ] +if [ -f "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale" ] +then + cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale /etc/init.d/ +fi + +if [ -f "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf" ] +then + cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.conf /etc/init.d/ +fi + +if [ -d "/usr/lib/systemd/system" -a -f @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service ] then cp @CMAKE_INSTALL_PREFIX@/@MAXSCALE_SHAREDIR@/maxscale.service /usr/lib/systemd/system fi /sbin/ldconfig -mandb + cat <& 2 ********** Notice: MaxScale 1.2 Changes ************** From 09ffeff17e46cc4bd2e75e68bdf3cbb7448ab419 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 18 Jun 2015 13:25:30 +0300 Subject: [PATCH 169/273] Updated lsyncd document. --- .../Reference/MaxScale-HA-with-lsyncd.md | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/Documentation/Reference/MaxScale-HA-with-lsyncd.md b/Documentation/Reference/MaxScale-HA-with-lsyncd.md index 18ef233db..d81d26790 100644 --- a/Documentation/Reference/MaxScale-HA-with-lsyncd.md +++ b/Documentation/Reference/MaxScale-HA-with-lsyncd.md @@ -1,9 +1,55 @@ # MaxScale HA with Lsyncd -This document guides you in setting up multiple MaxScale instances and synchronizing the configuration files with Lsyncd. Lsyncd is a rsync wrapper which can synchronize files across the network. The lsyncd daemon uses a configuration file to control the files to synchronize and the remote targets where these files are synchronized to. +This document guides you in setting up multiple MaxScale instances and synchronizing the configuration files with Lsyncd. Lsyncd is a rsync wrapper which can synchronize files across the network. The lsyncd daemon uses a configuration file to control the files to synchronize and the remote targets where these files are synchronized to. This guide was writted for lsyncd 2.1.5. Copying the configuration file and running the lsyncd daemon on all the hosts keeps all the configuration files in sync. Modifications in the configuration file on one of the hosts will be copied on the other hosts. This allows adinistrators to easily provide a highly available, disaster resistant MaxScale installation with up-to-date configuration files on all the hosts. +You will need to have SSH access to the remote servers for. + +## Creating SSH keys + +For lsyncd to work, we will need to either use an existing set of SSH keys or to create a new set of keys. If you already have a SSH key generated, you can skip this next step and go to the Copying Keys part. + +### Generating keys + +To generate a new set of SSH keys, we will use `ssh-keygen`. + +``` +[root@localhost ~]# ssh-keygen +Generating public/private rsa key pair. +Enter file in which to save the key (/root/.ssh/id_rsa): +Enter passphrase (empty for no passphrase): +Enter same passphrase again: +Your identification has been saved in /root/.ssh/id_rsa. +Your public key has been saved in /root/.ssh/id_rsa.pub. +The key fingerprint is: +f4:99:0a:cc:d4:ac:ea:ed:ff:0d:bb:e5:87:3e:38:df root@localhost.localdomain +The key's randomart image is: ++--[ RSA 2048]----+ +| | +| o | +| . + | +| + o . o | +| = S + | +| . . . | +| . . .... | +| . . o*o.. | +| ..o...+==oE | ++-----------------+ + +``` + +The keys will be generated in the .ssh folder and will automatically be used by ssh. + +### Copying keys + +To copy the SSH keys to the remote host we will use `ssh-copy-id`. + +Use the username and host of the remote server you wish to synchronize MaxScale's configuration files to. For example, if the server's address is 192.168.122.100 and the user we use for synchronization us `user` we can use the following command. + +``` +ssh-copy-id user@192.168.122.100 +``` ## Installing Lsyncd @@ -21,8 +67,6 @@ Installing with Apt: apt-get install lsyncd ``` -Lsyncd needs no further configuration after installation. All necessary options are configured in the configuration file passed to the lsyncd daemon. - ## Creating the Lsyncd configuration file Lsyncd uses a configuration file to determine where to read files from and where to synchronize them if changes in them occur. Lsyncd is written in Lua and the configuration file is also valid Lua code. @@ -44,6 +88,7 @@ default.rsyncssh, source="/etc", -- This is the user and host where the maxscale.cnf is copied to. +-- Change this to the user and destination host where you want maxscale.cnf to be synchronized to. host="user@127.0.0.1", -- This is where the maxscale.cnf is copied to on the remote host. @@ -108,6 +153,12 @@ rsync={ Starting lsyncd can be done from the command line or through a init script. To start syncd from the command like, execute the `lsyncd` command and pass the configuration file as the only parameter. +By default lsyncd will search for the configuration file in `/etc/lsyncd.conf`. By placing the configuration file we created in the `/etc` folder, we can start lsyncd with the following command. + +``` +service lsyncd start +``` + Here is an example which start lsyncd and reads the configuration options from the `lsyncd.cnf` file. ``` From e6b16121eb5f253fc93c163b4015713625e498d9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 18 Jun 2015 13:49:12 +0300 Subject: [PATCH 170/273] Added instructions for SSH key generation. --- .../Reference/MaxScale-HA-with-lsyncd.md | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Documentation/Reference/MaxScale-HA-with-lsyncd.md b/Documentation/Reference/MaxScale-HA-with-lsyncd.md index d81d26790..c9e6ac332 100644 --- a/Documentation/Reference/MaxScale-HA-with-lsyncd.md +++ b/Documentation/Reference/MaxScale-HA-with-lsyncd.md @@ -1,14 +1,26 @@ # MaxScale HA with Lsyncd -This document guides you in setting up multiple MaxScale instances and synchronizing the configuration files with Lsyncd. Lsyncd is a rsync wrapper which can synchronize files across the network. The lsyncd daemon uses a configuration file to control the files to synchronize and the remote targets where these files are synchronized to. This guide was writted for lsyncd 2.1.5. +***This guide was written for lsyncd 2.1.5.*** + +This document guides you in setting up multiple MaxScale instances and synchronizing the configuration files with lsyncd. Lsyncd is a rsync wrapper which can synchronize files across the network. The lsyncd daemon uses a configuration file to control the files to synchronize and the remote targets where these files are synchronized to. Copying the configuration file and running the lsyncd daemon on all the hosts keeps all the configuration files in sync. Modifications in the configuration file on one of the hosts will be copied on the other hosts. This allows adinistrators to easily provide a highly available, disaster resistant MaxScale installation with up-to-date configuration files on all the hosts. -You will need to have SSH access to the remote servers for. +### Requirements +You will need: + +* Access to the remote hosts. +* MaxScale installed on all systems +* Configured maxscale.cnf file in /etc +* SSH daemon and clients installed on all hosts + +The installation and configuration of MaxScale is covered in other documents. ## Creating SSH keys -For lsyncd to work, we will need to either use an existing set of SSH keys or to create a new set of keys. If you already have a SSH key generated, you can skip this next step and go to the Copying Keys part. +For lsyncd to work, we will need to either use an existing set of SSH keys or to create a new set of keys. The creation and copying of keys needs to be repeated on all of the hosts. + +If you already have a SSH key generated, you can skip this next step and go to the Copying Keys part. ### Generating keys @@ -51,7 +63,11 @@ Use the username and host of the remote server you wish to synchronize MaxScale' ssh-copy-id user@192.168.122.100 ``` -## Installing Lsyncd +Repeat the last command with the usernames and addresses of all the remote hosts you want to synchronize the configuration files to. + +## Installing lsyncd + +You will need to install lsyncd on all of the hosts for changes in the configuration file on one of the nodes to be synchronized to the other nodes. You can install lsyncd with either a package manager or by building from source code. This guide demonstrates installation using a package manager and those looking to build lsyncd from source should refer to its documentation: https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.1.x @@ -89,7 +105,7 @@ source="/etc", -- This is the user and host where the maxscale.cnf is copied to. -- Change this to the user and destination host where you want maxscale.cnf to be synchronized to. -host="user@127.0.0.1", +host="user@192.168.122.100", -- This is where the maxscale.cnf is copied to on the remote host. targetdir="/etc", From f6369a1661a3ec785db2fd96c7b54881366deaad Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 18 Jun 2015 18:03:55 +0300 Subject: [PATCH 171/273] Changed from server unique name to ip:port in monitor script initiaion. --- server/modules/monitor/monitor_common.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index f4566c35a..19980c5dd 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -230,7 +230,7 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) MONITOR_SERVERS* ptr = start; bool first = true; int slen = strlen(str); - + char arr[256]; while(ptr && slen < len) { if(!first) @@ -238,7 +238,8 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) strncat(str,",",len); } first = false; - strncat(str,ptr->server->unique_name,len); + sprintf(arr,"%s:%d",ptr->server->name,ptr->server->port); + strcat(str,arr); ptr = ptr->next; slen = strlen(str); } @@ -299,10 +300,11 @@ void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script) EXTERNCMD* cmd; snprintf(argstr,PATH_MAX + MON_ARG_MAX, - "%s --event=%s --initiator=%s --nodelist=", + "%s --event=%s --initiator=%s:%d --nodelist=", script, mon_get_event_name(ptr), - ptr->server->unique_name); + ptr->server->name, + ptr->server->port); mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX + 1); if((cmd = externcmd_allocate(argstr)) == NULL) From 0cc2c13d7e124ee10deb837fe7d7e4a73c8c1928 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 18 Jun 2015 18:13:50 +0300 Subject: [PATCH 172/273] Updated documentation. --- Documentation/monitors/MySQL-Monitor.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index 1bfc06109..7228c1b10 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -124,9 +124,11 @@ Here is an example shell script which sends an email to an admin when a server g #!/usr/bin/env bash #This script assumes that the local mail server is configured properly - +#The second argument is the event type +event=${$2/.*=/} +server=${$3/.*=/} message="A server has gone down at `date`." -echo $message|mail -s "A server has gone down" admin@my.org +echo $message|mail -s "The event was $event for server $server." admin@my.org ``` From 79c20bfae82a2d0347b808c98ef42ec1f5d878d5 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 19 Jun 2015 04:45:02 +0300 Subject: [PATCH 173/273] Fixed trace and debug logs not being created in shared memory. --- log_manager/log_manager.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index ba16db52f..b31ae6112 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1761,7 +1761,6 @@ static bool fnames_conf_init( case 's': /** record list of log file ids for later use */ - if(do_syslog) shmem_id_str = optarg; break; case 'h': @@ -1793,12 +1792,14 @@ static bool fnames_conf_init( strdup(get_logpath_default()) : fn->fn_logpath; /** Set identity string for syslog if it is not set in config.*/ + if(do_syslog) + { syslog_ident_str = (syslog_ident_str == NULL ? (argv == NULL ? strdup(program_invocation_short_name) : strdup(*argv)) : syslog_ident_str); - + } /* ss_dfprintf(stderr, "\n\n\tCommand line : "); for (i=0; i Date: Sun, 21 Jun 2015 12:51:54 +0300 Subject: [PATCH 174/273] Fix to MXS-212: https://mariadb.atlassian.net/browse/MXS-212 The listener DCB is now properly closed instead of just being removed from the poll set. --- server/core/dcb.c | 8 +++++++- server/core/poll.c | 3 ++- server/core/service.c | 19 +++++++++---------- server/modules/protocol/mysql_client.c | 2 +- server/modules/protocol/mysql_common.c | 5 ++++- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 7f3651953..fc9329524 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1949,13 +1949,15 @@ dcb_close(DCB *dcb) } ss_dassert(dcb->state == DCB_STATE_POLLING || + dcb->state == DCB_STATE_LISTENING || dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE); /*< * Stop dcb's listening and modify state accordingly. */ - if (dcb->state == DCB_STATE_POLLING) + if (dcb->state == DCB_STATE_POLLING || + dcb->state == DCB_STATE_LISTENING) { rc = poll_remove_dcb(dcb); @@ -2428,6 +2430,10 @@ static bool dcb_set_state_nomutex( case DCB_STATE_POLLING: /*< ok to try but state can't change */ succp = true; break; + case DCB_STATE_LISTENING: + dcb->state = new_state; + succp = true; + break; default: ss_dassert(old_state != NULL); break; diff --git a/server/core/poll.c b/server/core/poll.c index 377310cb0..9a1a5565d 100644 --- a/server/core/poll.c +++ b/server/core/poll.c @@ -330,7 +330,8 @@ poll_remove_dcb(DCB *dcb) CHK_DCB(dcb); /*< It is possible that dcb has already been removed from the set */ - if (dcb->state != DCB_STATE_POLLING) + if (dcb->state != DCB_STATE_POLLING && + dcb->state != DCB_STATE_LISTENING) { if (dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE) diff --git a/server/core/service.c b/server/core/service.c index b0959e6c3..c11c0bddc 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -534,11 +534,13 @@ int listeners = 0; port = service->ports; while (port) { - poll_remove_dcb(port->listener); - port->listener->session->state = SESSION_STATE_LISTENER_STOPPED; + if(port->listener) + { + dcb_close(port->listener); + port->listener = NULL; listeners++; - - port = port->next; + } + port = port->next; } service->state = SERVICE_STATE_STOPPED; @@ -562,13 +564,10 @@ int listeners = 0; port = service->ports; while (port) { - if (poll_add_dcb(port->listener) == 0) { - port->listener->session->state = SESSION_STATE_LISTENER; - listeners++; - } - port = port->next; + listeners += serviceStartPort(service,port); + port = port->next; } - + service->state = SERVICE_STATE_STARTED; return listeners; } diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index c3e463139..21a11dc42 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1771,7 +1771,7 @@ gw_client_close(DCB *dcb) dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE) { - if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(protocol); + if (!DCB_IS_CLONE(dcb) && protocol) CHK_PROTOCOL(protocol); } #endif LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 83e2912e5..eb044c1fb 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -128,7 +128,10 @@ void mysql_protocol_done ( MySQLProtocol* p; server_command_t* scmd; server_command_t* scmd2; - + + if(dcb->protocol == NULL) + return; + p = (MySQLProtocol *)dcb->protocol; spinlock_acquire(&p->protocol_lock); From c3aa5beeb45abc921bdc49a751e20269bea394ac Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 21 Jun 2015 19:32:19 +0300 Subject: [PATCH 175/273] Added missing initialization from MM monitor. --- server/modules/monitor/mmmon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index c0d54595d..545a77ca2 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -126,6 +126,7 @@ startMonitor(void *arg,void* opt) handle->shutdown = 0; handle->id = MONITOR_DEFAULT_ID; handle->master = NULL; + handle->script = NULL; memset(handle->events,false,sizeof(handle->events)); spinlock_init(&handle->lock); } From 8c900e73deab80691e004ee3319981adf173dd4e Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 22 Jun 2015 10:46:00 +0200 Subject: [PATCH 176/273] removed extra blr_file_add_magic removed extra blr_file_add_magic --- server/modules/include/blr.h | 1 + server/modules/routing/binlog/blr_file.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 62cda59a3..72171b253 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -34,6 +34,7 @@ #include #include +#include #define BINLOG_FNAMELEN 16 #define BLR_PROTOCOL "MySQLBackend" diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index ca92d6d06..68f47b1c3 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -210,9 +210,8 @@ int fd; close(router->binlog_fd); spinlock_acquire(&router->binlog_lock); strncpy(router->binlog_name, file,BINLOG_FNAMELEN); - blr_file_add_magic(router, fd); - spinlock_release(&router->binlog_lock); router->binlog_fd = fd; + spinlock_release(&router->binlog_lock); return 1; } @@ -254,12 +253,13 @@ int fd; LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "%s: binlog file %s has an invalid length %d.", router->service->name, path, router->binlog_position))); - close(fd); + close(fd); + spinlock_release(&router->binlog_lock); return; } } - spinlock_release(&router->binlog_lock); router->binlog_fd = fd; + spinlock_release(&router->binlog_lock); } /** From 15e6d6f9fcb53eb0d95bb3a4e6e082815e72f2ec Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 22 Jun 2015 18:24:39 +0200 Subject: [PATCH 177/273] fix for missing crc check in blr_slave_fake_rotate() fix for missing crc check in blr_slave_fake_rotate() --- server/modules/routing/binlog/blr_slave.c | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 4de69b8d7..ee7d52099 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -1693,6 +1693,9 @@ uint32_t chksum; binlognamelen = strlen(slave->binlogfile); len = 19 + 8 + 4 + binlognamelen; + /* no slave crc, remove 4 bytes */ + if (slave->nocrc) + len -= 4; // Build a fake rotate event resp = gwbuf_alloc(len + 5); @@ -1711,17 +1714,19 @@ uint32_t chksum; memcpy(ptr, slave->binlogfile, binlognamelen); ptr += binlognamelen; - /* - * 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); + } slave->dcb->func.write(slave->dcb, resp); return 1; From c22c6ea46aaaad1cfba993ccd19a41cbea4abe6e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 09:15:06 +0300 Subject: [PATCH 178/273] ServiceStop only removed DCBs from the polling system This removes the need to establish new DCBs for each of the listeners while still blocking new session creation for a service which is shut down. The client will not receive an error and the connection will be accepted when the service is restarted. --- server/core/service.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/server/core/service.c b/server/core/service.c index c11c0bddc..cfce442b8 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -534,12 +534,8 @@ int listeners = 0; port = service->ports; while (port) { - if(port->listener) - { - dcb_close(port->listener); - port->listener = NULL; + if(poll_remove_dcb(port->listener) == 0) listeners++; - } port = port->next; } service->state = SERVICE_STATE_STOPPED; @@ -564,7 +560,8 @@ int listeners = 0; port = service->ports; while (port) { - listeners += serviceStartPort(service,port); + if(poll_add_dcb(port->listener) == 0) + listeners++; port = port->next; } service->state = SERVICE_STATE_STARTED; From dc43a7d9da855015b1013980d1227c76c845e015 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 11:25:59 +0300 Subject: [PATCH 179/273] Removed unnecessary code from dcb_close and dcb_set_state_nomutex. --- server/core/dcb.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index fc9329524..7f3651953 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1949,15 +1949,13 @@ dcb_close(DCB *dcb) } ss_dassert(dcb->state == DCB_STATE_POLLING || - dcb->state == DCB_STATE_LISTENING || dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE); /*< * Stop dcb's listening and modify state accordingly. */ - if (dcb->state == DCB_STATE_POLLING || - dcb->state == DCB_STATE_LISTENING) + if (dcb->state == DCB_STATE_POLLING) { rc = poll_remove_dcb(dcb); @@ -2430,10 +2428,6 @@ static bool dcb_set_state_nomutex( case DCB_STATE_POLLING: /*< ok to try but state can't change */ succp = true; break; - case DCB_STATE_LISTENING: - dcb->state = new_state; - succp = true; - break; default: ss_dassert(old_state != NULL); break; From 3de7798fac7ffc74d36d50316e1449b3ab333bb2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 11:49:27 +0300 Subject: [PATCH 180/273] Added missing session state changes. --- server/core/service.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/core/service.c b/server/core/service.c index cfce442b8..bce7328cc 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -535,7 +535,10 @@ int listeners = 0; while (port) { if(poll_remove_dcb(port->listener) == 0) + { + port->listener->session->state = SESSION_STATE_LISTENER_STOPPED; listeners++; + } port = port->next; } service->state = SERVICE_STATE_STOPPED; @@ -561,7 +564,10 @@ int listeners = 0; while (port) { if(poll_add_dcb(port->listener) == 0) + { + port->listener->session->state = SESSION_STATE_LISTENER; listeners++; + } port = port->next; } service->state = SERVICE_STATE_STARTED; From 3d7b050f6ff3df7cc939903a8db202e938168be3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 11:53:29 +0300 Subject: [PATCH 181/273] Removed unnecessary NULL checks in mysql_client and mysql_common --- server/modules/protocol/mysql_client.c | 2 +- server/modules/protocol/mysql_common.c | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 21a11dc42..c3e463139 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1771,7 +1771,7 @@ gw_client_close(DCB *dcb) dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE) { - if (!DCB_IS_CLONE(dcb) && protocol) CHK_PROTOCOL(protocol); + if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(protocol); } #endif LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index eb044c1fb..83e2912e5 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -128,10 +128,7 @@ void mysql_protocol_done ( MySQLProtocol* p; server_command_t* scmd; server_command_t* scmd2; - - if(dcb->protocol == NULL) - return; - + p = (MySQLProtocol *)dcb->protocol; spinlock_acquire(&p->protocol_lock); From abf39303d7590e908a54de7d3309183ab5efa5d7 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 16:20:26 +0300 Subject: [PATCH 182/273] Fixed the wrong value being returned form dcb_read_SSL. --- server/core/dcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 7f3651953..9e94150a8 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1239,7 +1239,7 @@ int dcb_read_SSL( } /*< while (true) */ return_n: - return n; + return nread; } /** * General purpose routine to write to a DCB From 039cbff1811315606bdec20dee421d9521830fe1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 16:45:10 +0300 Subject: [PATCH 183/273] Added missing null checks. --- server/core/service.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/core/service.c b/server/core/service.c index bce7328cc..7966d33ec 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -534,7 +534,7 @@ int listeners = 0; port = service->ports; while (port) { - if(poll_remove_dcb(port->listener) == 0) + if(port->listener && poll_remove_dcb(port->listener) == 0) { port->listener->session->state = SESSION_STATE_LISTENER_STOPPED; listeners++; @@ -563,7 +563,7 @@ int listeners = 0; port = service->ports; while (port) { - if(poll_add_dcb(port->listener) == 0) + if(port->listener && poll_add_dcb(port->listener) == 0) { port->listener->session->state = SESSION_STATE_LISTENER; listeners++; From c42d3d9f7ac086b5e012d9c2ee64f3b024c9ed17 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 17:05:31 +0300 Subject: [PATCH 184/273] Added missing NULL checks. --- server/core/dcb.c | 3 +++ server/core/service.c | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 7f3651953..07bb1dc93 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -2423,6 +2423,9 @@ static bool dcb_set_state_nomutex( case DCB_STATE_NOPOLLING: switch (new_state) { + /** Stopped services which are restarting will go from + * DCB_STATE_NOPOLLING to DCB_STATE_LISTENING.*/ + case DCB_STATE_LISTENING: case DCB_STATE_ZOMBIE: /*< fall through */ dcb->state = new_state; case DCB_STATE_POLLING: /*< ok to try but state can't change */ diff --git a/server/core/service.c b/server/core/service.c index 7966d33ec..24d51542d 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -534,10 +534,14 @@ int listeners = 0; port = service->ports; while (port) { - if(port->listener && poll_remove_dcb(port->listener) == 0) + if(port->listener && + port->listener->session->state == SESSION_STATE_LISTENER) { - port->listener->session->state = SESSION_STATE_LISTENER_STOPPED; - listeners++; + if(poll_remove_dcb(port->listener) == 0) + { + port->listener->session->state = SESSION_STATE_LISTENER_STOPPED; + listeners++; + } } port = port->next; } @@ -563,11 +567,15 @@ int listeners = 0; port = service->ports; while (port) { - if(port->listener && poll_add_dcb(port->listener) == 0) + if(port->listener && + port->listener->session->state == SESSION_STATE_LISTENER_STOPPED) + { + if(poll_add_dcb(port->listener) == 0) { port->listener->session->state = SESSION_STATE_LISTENER; listeners++; } + } port = port->next; } service->state = SERVICE_STATE_STARTED; From 77c085fbd3be191f2b4679a628d89fb272814b02 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 19:51:19 +0300 Subject: [PATCH 185/273] Improved routing hint documentation. --- Documentation/Reference/Hint-Syntax.md | 83 +++++++++++++++++++++----- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/Documentation/Reference/Hint-Syntax.md b/Documentation/Reference/Hint-Syntax.md index 264d264f6..b84bb85fd 100644 --- a/Documentation/Reference/Hint-Syntax.md +++ b/Documentation/Reference/Hint-Syntax.md @@ -1,42 +1,97 @@ # Hint Syntax -Use either ’-- ’ (notice the whitespace) or ’#’ after the semicolon or ’/* .. */’ before -the semicolon. +## Enabling routing hints -The MySQL manual doesn’t specify if comment blocks, i.e. ’/* .. */’, should contain a w -hitespace character before or after the tags. +To enable routing hints for a service, the hintfilter module needs to be configured and the filter needs to be applied to the service. -All hints must start with the ’maxscale tag’: - -- maxscale - -The hints right now have two types, ones that route to a server and others that contain +Here is an example service which has the hint filter configured and applied. + +``` +[Read Service] +type=service +router=readconnroute +router_options=master +servers=server1 +user=maxuser +passwd=maxpwd +filter=Hint + +[Hint] +type=filter +module=hintfilter + +``` + +## Comments and comment types + +The client connection will need to have comments enabled. For example the `mysql` command line client has comments disabled by default. + +For comment types, use either `-- ` (notice the whitespace) or `#` after the semicolon or `/* .. */` before the semicolon. All comment types work with routing hints. + +The MySQL manual doesn`t specify if comment blocks, i.e. `/* .. */`, should contain a w +hitespace character before or after the tags, so adding whitespace at both the start and the end is advised. + +## Hint body + +All hints must start with the `maxscale` tag. + +``` +-- maxscale +``` + +The hints have two types, ones that route to a server and others that contain name-value pairs. -Routing queries to a server: +###Routing destination hints + +These hints will instruct the router to route a query to a certain type of a server. +``` -- maxscale route to [master | slave | server ] +``` -The name of the server is the same as in maxscale.cnf +A `master` value in a routing hint will route the query to a master server. This can be used to direct read queries to a master server for a up-to-date result with no replication lag. A `slave` value will route the query to a slave server. A `server` value will route the query to a named server. The value of needs to be the same as the server section name in maxscale.cnf. -Creating a name-value pair: +### Name-value hints + +These control the behavior and affect the routing decisions made by the router. + +``` -- maxscale = +``` -Currently the only accepted parameter is -’max_slave_replication_lag’ +Currently the only accepted parameter is `max_slave_replication_lag`. This will route the query to a server with lower replication lag then what is defined in the hint value. + +## Hint stack Hints can be either single-use hints, which makes them affect only one query, or named hints, which can be pushed on and off a stack of active hints. Defining named hints: + +``` -- maxscale prepare +``` Pushing a hint onto the stack: + +``` -- maxscale begin +``` Popping the topmost hint off the stack: + +``` -- maxscale end +``` You can define and activate a hint in a single command using the following: + +``` -- maxscale begin +``` You can also push anonymous hints onto the stack which are only used as long as they are on the stack: --- maxscale begin \ No newline at end of file + +``` +-- maxscale begin +``` From 29492426c3e48b30cb0273d0b20ea9315035751b Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 20:06:02 +0300 Subject: [PATCH 186/273] Updated documentation contents. --- Documentation/Documentation-Contents.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 99f8ba38e..fc51d83ec 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -5,7 +5,7 @@ ## About MaxScale - [About MaxScale](About/About-MaxScale.md) - - [Release Notes 1.1](Release-Notes/MaxScale-1.1-Release-Notes.md) + - [MaxScale 1.2.0 Release Notes](Release-Notes/MaxScale-1.2.0-Release-Notes.md) - [Changelog](Changelog.md) - [Limitations](About/Limitations.md) - [COPYRIGHT](About/COPYRIGHT.md) @@ -28,6 +28,7 @@ - [MaxScale HA with Lsyncd](Reference/MaxScale-HA-with-lsyncd.md) - [How Errors are Handled in MaxScale](Reference/How-errors-are-handled-in-MaxScale.md) - [Debug and Diagnostic Support](Reference/Debug-And-Diagnostic-Support.md) + - [Routing Hints](Reference/Hint-Syntax.md) ## Tutorials @@ -70,10 +71,6 @@ Here are detailed documents about the filters MaxScale offers. They contain conf - [RabbitMQ Consumer Client](filters/RabbitMQ-Consumer-Client.md) -## Routers - - - [Simple Schema Sharding Router](routers/schemarouter/SchemaRouter.md) - ## Design Documents - [Core Objects Design (in development)](http://mariadb-corporation.github.io/MaxScale/Design-Documents/core-objects-html-docs) @@ -89,4 +86,7 @@ Here are detailed documents about the filters MaxScale offers. They contain conf - [MaxScale 1.0 Release Notes](Release-Notes/MaxScale-1.0-Release-Notes.md) - [MaxScale 1.0.1 Release Notes](Release-Notes/MaxScale-1.0.1-Release-Notes.md) - [MaxScale 1.0.3 Release Notes](Release-Notes/MaxScale-1.0.3-Release-Notes.md) + - [MaxScale 1.1.0 Release Notes](Release-Notes/MaxScale-1.1-Release-Notes.md) + - [MaxScale 1.1.1 Release Notes](Release-Notes/MaxScale-1.1.1-Release-Notes.md) + - [MaxScale 1.2.0 Release Notes](Release-Notes/MaxScale-1.2.0-Release-Notes.md) From f44a3cf758ded11f85bb6fd2ab803e76061e2ac7 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 23 Jun 2015 20:10:43 +0300 Subject: [PATCH 187/273] Updated 1.2 release notes. --- Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index f85382b3c..a84f94c27 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -24,3 +24,8 @@ A quick list of changes in installation directories and file names: * Data directory is `/var/lib/maxscale`. This is the default location for MaxScale-specific data. * PID file can be found at `/var/run/maxscale` +### Client side SSL encryption +MaxScale now supports SSL/TLS encrypted connections to MaxScale. + +### Monitor scripts +State changes in backend servers can now trigger the execution of a custom script. With this you can easily customize MaxScale's behavior. From 13fb88ea076c80da79182ec2ee5d9123ae6cd075 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Jun 2015 11:29:43 +0300 Subject: [PATCH 188/273] Added optional code for older OpenSSL library versions. --- CMakeLists.txt | 14 +++++++++++++- server/core/gateway.c | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b040f0df..c28b2c657 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,19 +31,31 @@ include(cmake/CheckPlatform.cmake) check_deps() check_dirs() +find_package(OpenSSL) find_package(Valgrind) find_package(MySQLClient) find_package(MySQL) find_package(Pandoc) find_package(TCMalloc) find_package(Jemalloc) +find_package(CURL) # You can find the variables set by this in the FindCURL.cmake file # which is a default module in CMake. -find_package(CURL) + if(NOT CURL_FOUND) message(FATAL_ERROR "Failed to locate dependency: libcurl") endif() +if(NOT OPENSSL_FOUND) + message(FATAL_ERROR "Failed to locate dependency: OpenSSL") +else() + if(OPENSSL_VERSION VERSION_LESS 1 AND NOT FORCE_OPENSSL100) + add_definitions("-DOPENSSL_0_9") + else() + add_definitions("-DOPENSSL_1_0") + endif() +endif() + set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/${MAXSCALE_LIBDIR}) # Make sure the release notes for this release are present if it is a stable one diff --git a/server/core/gateway.c b/server/core/gateway.c index f206ca30e..5997a9eeb 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -263,6 +263,15 @@ static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, i free(n); } +/** + * The thread ID callback function for OpenSSL dynamic locks. + * @param id Id to modify + */ +static void maxscale_ssl_id(CRYPTO_THREADID* id) +{ + CRYPTO_THREADID_set_numeric(id,pthread_self()); +} + /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. @@ -1459,8 +1468,11 @@ int main(int argc, char **argv) CRYPTO_set_dynlock_create_callback(ssl_create_dynlock); CRYPTO_set_dynlock_destroy_callback(ssl_free_dynlock); CRYPTO_set_dynlock_lock_callback(ssl_lock_dynlock); +#ifdef OPENSSL_1_0 + CRYPTO_THREADID_set_callback(maxscale_ssl_id); +#else CRYPTO_set_id_callback(pthread_self); - +#endif /* register exit function for embedded MySQL library */ l = atexit(libmysqld_done); From 0f199d924f937c81f057285b07ca2871d7df5aa9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Jun 2015 12:56:22 +0300 Subject: [PATCH 189/273] Removed unnecessary call to SSL_get_error from dcb_accept_SSL. --- server/core/dcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 4bd51d9a2..3597d0d3a 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -3010,7 +3010,7 @@ int dcb_accept_SSL(DCB* dcb) do { ssl_rval = SSL_accept(dcb->ssl); - errnum = SSL_get_error(dcb->ssl,ssl_rval); + LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept %d, error %d", ssl_rval,errnum))); switch(ssl_rval) From 067a62b24032e17dc1bae46bfba9eb0f95471496 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Jun 2015 14:34:26 +0300 Subject: [PATCH 190/273] Added more error logging to dcb_write_SSL. --- server/core/dcb.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 3597d0d3a..e63a73ffc 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1645,7 +1645,7 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) { char errbuf[140]; ERR_error_string(ssl_errno,errbuf); - skygw_log_write(LE,"%s",errbuf); + skygw_log_write(LD,"%s",errbuf); } } break; @@ -1665,6 +1665,19 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) STRDCBSTATE(dcb->state), dcb->fd, ssl_errno))); + if(ssl_errno == SSL_ERROR_SSL) + { + while((ssl_errno = ERR_get_error()) != 0) + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%s",errbuf); + } + } + if(ssl_errno == SSL_ERROR_SYSCALL) + { + skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + } } } break; From 484781a463913f242bba6d370f3b43d734132007 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Jun 2015 14:46:46 +0300 Subject: [PATCH 191/273] More error logging for SSL connections. --- server/core/dcb.c | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index e63a73ffc..4e2ee4800 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1659,7 +1659,7 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Write to dcb %p in " - "state %s fd %d failed due " + "state %s fd %d failed due to " "SSL error %d", dcb, STRDCBSTATE(dcb->state), @@ -1881,28 +1881,47 @@ dcb_drain_writeq_SSL(DCB *dcb) while (dcb->writeq != NULL) { len = GWBUF_LENGTH(dcb->writeq); - w = gw_write_SSL(dcb->ssl, GWBUF_DATA(dcb->writeq), len); + w = gw_write_SSL(dcb->ssl, GWBUF_DATA(dcb->writeq), len); if (w < 0) { - int ssl_errno = ERR_get_error(); + int ssl_errno = SSL_get_error(dcb->ssl,w); - if(ssl_errno == SSL_ERROR_WANT_WRITE || - ssl_errno == SSL_ERROR_WANT_ACCEPT || - ssl_errno == SSL_ERROR_WANT_READ) + if(ssl_errno == SSL_ERROR_WANT_WRITE || ssl_errno == SSL_ERROR_WANT_READ) { break; } + skygw_log_write_flush(LOGFILE_ERROR, + "Error : Write to dcb failed due to " + "SSL error %d:", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno); + switch(ssl_errno) + { + case SSL_ERROR_SSL: + case SSL_ERROR_SYSCALL: + while((ssl_errno = ERR_get_error()) != 0) + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%s",errbuf); + } + if(errno != 0) + skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + break; + case SSL_ERROR_ZERO_RETURN: + skygw_log_write(LE,"Socket is closed."); + break; - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write to dcb %p " - "in state %s fd %d failed: %s", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ERR_error_string(ssl_errno,NULL)); + default: + skygw_log_write(LE,"Unexpected error."); + break; + } break; + + } /* * Pull the number of bytes we have written from From 80d130ef0c6f9d65407519920d79dd6e5bbcaf14 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Jun 2015 15:05:04 +0300 Subject: [PATCH 192/273] Fixed dcb_write_SSL being called multiple times on failure. --- server/core/dcb.c | 182 +++++++++++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 81 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 4e2ee4800..9142f7ee9 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1594,108 +1594,128 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) } #endif /* FAKE_CODE */ qlen = GWBUF_LENGTH(queue); - - w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), qlen); - dcb->stats.n_writes++; - - if (w < 0) + do { - int ssl_errno = SSL_get_error(dcb->ssl,w); + w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), qlen); + dcb->stats.n_writes++; - if (LOG_IS_ENABLED(LOGFILE_DEBUG)) + if (w <= 0) { - switch(ssl_errno) + int ssl_errno = SSL_get_error(dcb->ssl,w); + + if (LOG_IS_ENABLED(LOGFILE_DEBUG)) { - case SSL_ERROR_WANT_READ: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error SSL_ERROR_WANT_READ", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); - break; - case SSL_ERROR_WANT_WRITE: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error SSL_ERROR_WANT_WRITE", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); - break; - default: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error %d", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd,ssl_errno))); - if(ssl_errno == SSL_ERROR_SSL || - ssl_errno == SSL_ERROR_SYSCALL) + switch(ssl_errno) { - while((ssl_errno = ERR_get_error()) != 0) + case SSL_ERROR_WANT_READ: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_READ", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + break; + case SSL_ERROR_WANT_WRITE: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_WRITE", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + break; + default: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error %d", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd,ssl_errno))); + if(ssl_errno == SSL_ERROR_SSL || + ssl_errno == SSL_ERROR_SYSCALL) { - char errbuf[140]; - ERR_error_string(ssl_errno,errbuf); - skygw_log_write(LD,"%s",errbuf); + while((ssl_errno = ERR_get_error()) != 0) + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LD,"%s",errbuf); + } } - } - break; - } - } - - if (LOG_IS_ENABLED(LOGFILE_ERROR)) - { - if (ssl_errno != 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write to dcb %p in " - "state %s fd %d failed due to " - "SSL error %d", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ssl_errno))); - if(ssl_errno == SSL_ERROR_SSL) - { - while((ssl_errno = ERR_get_error()) != 0) - { - char errbuf[140]; - ERR_error_string(ssl_errno,errbuf); - skygw_log_write(LE,"%s",errbuf); - } - } - if(ssl_errno == SSL_ERROR_SYSCALL) - { - skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + break; } } + + if (LOG_IS_ENABLED(LOGFILE_ERROR) && ssl_errno != SSL_ERROR_WANT_WRITE) + { + if (ssl_errno == -1) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Write to dcb %p in " + "state %s fd %d failed due to " + "SSL error %d", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno))); + if(ssl_errno == SSL_ERROR_SSL) + { + do + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%s",errbuf); + }while((ssl_errno = ERR_get_error()) != 0); + } + if(ssl_errno == SSL_ERROR_SYSCALL) + { + skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + } + } + else if(w == 0) + { + do + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%s",errbuf); + }while((ssl_errno = ERR_get_error()) != 0); + } + } + + if(ssl_errno != SSL_ERROR_WANT_WRITE) + break; } - break; - } + }while(w <= 0); /* * Pull the number of bytes we have written from * queue with have. */ - queue = gwbuf_consume(queue, w); - LOGIF(LD, (skygw_log_write( + if(w == -1) + { + while((queue = GWBUF_CONSUME_ALL(queue))); + } + else + { + queue = gwbuf_consume(queue, w); + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [dcb_write] Wrote %d Bytes to dcb %p in " - "state %s fd %d", + "state %s fd %d", pthread_self(), w, dcb, STRDCBSTATE(dcb->state), dcb->fd))); + } } /*< while (queue != NULL) */ /*< * What wasn't successfully written is stored to write queue From 047985fb91a21c94fc6446ba1e26de33ae8aa74c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Jun 2015 15:26:35 +0300 Subject: [PATCH 193/273] Fixed SSL thread locking functions not being used. --- server/core/gateway.c | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 5997a9eeb..1b653d2f7 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -208,6 +208,15 @@ static int set_user(); /** SSL multi-threading functions and structures */ +static SPINLOCK* ssl_locks; + +static void ssl_locking_function(int mode,int n,const char* file, int line) +{ + if(mode & CRYPTO_LOCK) + spinlock_acquire(&ssl_locks[n]); + else + spinlock_release(&ssl_locks[n]); +} /** * OpenSSL requires this struct to be defined in order to use dynamic locks */ @@ -1465,14 +1474,19 @@ int main(int argc, char **argv) SSL_library_init(); SSL_load_error_strings(); OPENSSL_add_all_algorithms_noconf(); - CRYPTO_set_dynlock_create_callback(ssl_create_dynlock); - CRYPTO_set_dynlock_destroy_callback(ssl_free_dynlock); - CRYPTO_set_dynlock_lock_callback(ssl_lock_dynlock); -#ifdef OPENSSL_1_0 - CRYPTO_THREADID_set_callback(maxscale_ssl_id); -#else - CRYPTO_set_id_callback(pthread_self); -#endif + + int numlocks = CRYPTO_num_locks(); + if((ssl_locks = malloc(sizeof(SPINLOCK)*(numlocks + 1))) == NULL) + { + char* logerr = "Memory allocation failed"; + print_log_n_stderr(true, true, logerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + + for(i = 0;i Date: Wed, 24 Jun 2015 16:56:27 +0300 Subject: [PATCH 194/273] Moved SSL spinlock initialization to be done after thread initialization. --- server/core/gateway.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 1b653d2f7..6513bdb1f 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1475,18 +1475,6 @@ int main(int argc, char **argv) SSL_load_error_strings(); OPENSSL_add_all_algorithms_noconf(); - int numlocks = CRYPTO_num_locks(); - if((ssl_locks = malloc(sizeof(SPINLOCK)*(numlocks + 1))) == NULL) - { - char* logerr = "Memory allocation failed"; - print_log_n_stderr(true, true, logerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - - for(i = 0;i Date: Wed, 24 Jun 2015 18:17:00 +0300 Subject: [PATCH 195/273] Updated readwritesplit documentation. --- Documentation/routers/ReadWriteSplit.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md index 6058e0390..577bc2e36 100644 --- a/Documentation/routers/ReadWriteSplit.md +++ b/Documentation/routers/ReadWriteSplit.md @@ -4,7 +4,9 @@ This document provides a short overview of the **readwritesplit** router module ## Overview -The **readwritesplit** router is designed to be used with a Master-Slave replication cluster. It automatically detects changes in the master server and will use the current master server of the cluster. With a Galera cluster, one can achieve a resilient setup and easy master failover by using one of the Galera nodes as a Write-Master node, where are write queries are routed, and spreading the read load over all the nodes. +The **readwritesplit** router is designed to increase the read-only processing capability of a cluster while maintaining consistency. This is achieved by splitting the query load into read and write queries. Read queries, which do not modify data, are spread across multiple nodes while all write queries will be sent to a single node. + +The router is designed to be used with a traditional Master-Slave replication cluster. It automatically detects changes in the master server and will use the current master server of the cluster. With a Galera cluster, one can achieve a resilient setup and easy master failover by using one of the Galera nodes as a Write-Master node, where all write queries are routed, and spreading the read load over all the nodes. ## Configuration From 7a8c30751587ff530e1ffff57e66530a2e235186 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Jun 2015 19:23:43 +0300 Subject: [PATCH 196/273] MXS-171: https://mariadb.atlassian.net/browse/MXS-171 Added option which allows the master server to be used for reads. --- Documentation/routers/ReadWriteSplit.md | 7 +++++++ server/modules/include/readwritesplit.h | 1 + server/modules/routing/readwritesplit/readwritesplit.c | 7 ++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md index 577bc2e36..e58367789 100644 --- a/Documentation/routers/ReadWriteSplit.md +++ b/Documentation/routers/ReadWriteSplit.md @@ -93,6 +93,13 @@ disable_sescmd_history=true disable_slave_recovery=true ``` +**`master_accept_reads`** allows the master server to be used for reads. This is a useful option to enable if you are using a small number of servers and wish to use the master for reads as well. + +``` +# Use the master for reads +master_accept_reads=true +``` + ## Limitations In Master-Slave replication cluster also read-only queries are routed to master too in the following situations: diff --git a/server/modules/include/readwritesplit.h b/server/modules/include/readwritesplit.h index 9aa55016f..c78cde5b4 100644 --- a/server/modules/include/readwritesplit.h +++ b/server/modules/include/readwritesplit.h @@ -252,6 +252,7 @@ typedef struct rwsplit_config_st { int rw_max_sescmd_history_size; bool disable_sescmd_hist; bool disable_slave_recovery; + bool master_reads; /*< Use master for reads */ } rwsplit_config_t; diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index b9beb80d0..7b4a808c3 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1244,7 +1244,8 @@ static bool get_dcb( SERVER_IS_SLAVE(b->backend_server) && (max_rlag == MAX_RLAG_UNDEFINED || (b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE && - b->backend_server->rlag <= max_rlag))) + b->backend_server->rlag <= max_rlag)) && + !rses->rses_config.master_reads) { /** found slave */ candidate_bref = &backend_ref[i]; @@ -4589,6 +4590,10 @@ static void rwsplit_process_router_options( { router->rwsplit_config.disable_slave_recovery = config_truth_value(value); } + else if(strcmp(options[i],"master_accept_reads") == 0) + { + router->rwsplit_config.master_reads = config_truth_value(value); + } } } /*< for */ } From e5d9abbdcbdb9a31bc18c1e5ea5fb64b3fe8c75f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 25 Jun 2015 06:01:33 +0300 Subject: [PATCH 197/273] Fixes to Coverity defects. --- server/core/dcb.c | 82 +++++++----------------------- server/core/secrets.c | 6 +++ server/core/service.c | 2 +- server/modules/monitor/galeramon.c | 3 ++ utils/skygw_utils.cc | 2 + 5 files changed, 29 insertions(+), 66 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 9142f7ee9..8f2f159d4 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -939,10 +939,10 @@ int dcb_read_n( goto return_n; } - if (b == 0 && nread == 0) + if (b == 0) { /** Handle closed client socket */ - if (dcb_isclient(dcb)) + if (nread == 0 && dcb_isclient(dcb)) { char c; int l_errno = 0; @@ -964,11 +964,6 @@ int dcb_read_n( n = 0; goto return_n; } - else if (b == 0) - { - n = 0; - goto return_n; - } dcb->last_read = hkheartbeat; @@ -1208,6 +1203,10 @@ int dcb_read_SSL( } gwbuf_rtrim(buffer,bufsize - n); + if(buffer == NULL) + { + goto return_n; + } #ifdef SS_DEBUG skygw_log_write(LD,"%lu SSL: Truncated buffer from %d to %d bytes. " "Read %d bytes, %d bytes waiting.\n",pthread_self(), @@ -1234,9 +1233,6 @@ int dcb_read_SSL( /*< Append read data to the gwbuf */ *head = gwbuf_append(*head, buffer); - rc = ioctl(dcb->fd, FIONREAD, &b); - pending = SSL_pending(dcb->ssl); - } /*< while (true) */ return_n: return nread; @@ -1639,16 +1635,6 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) dcb, STRDCBSTATE(dcb->state), dcb->fd,ssl_errno))); - if(ssl_errno == SSL_ERROR_SSL || - ssl_errno == SSL_ERROR_SYSCALL) - { - while((ssl_errno = ERR_get_error()) != 0) - { - char errbuf[140]; - ERR_error_string(ssl_errno,errbuf); - skygw_log_write(LD,"%s",errbuf); - } - } break; } } @@ -1666,19 +1652,19 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) STRDCBSTATE(dcb->state), dcb->fd, ssl_errno))); - if(ssl_errno == SSL_ERROR_SSL) + if(ssl_errno == SSL_ERROR_SSL || ssl_errno == SSL_ERROR_SYSCALL) { + if(ssl_errno == SSL_ERROR_SYSCALL) + { + skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + } do { char errbuf[140]; ERR_error_string(ssl_errno,errbuf); - skygw_log_write(LE,"%s",errbuf); + skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); }while((ssl_errno = ERR_get_error()) != 0); } - if(ssl_errno == SSL_ERROR_SYSCALL) - { - skygw_log_write(LE,"%d:%s",errno,strerror(errno)); - } } else if(w == 0) { @@ -1686,7 +1672,7 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) { char errbuf[140]; ERR_error_string(ssl_errno,errbuf); - skygw_log_write(LE,"%s",errbuf); + skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); }while((ssl_errno = ERR_get_error()) != 0); } } @@ -1695,16 +1681,14 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) break; } }while(w <= 0); - /* - * Pull the number of bytes we have written from - * queue with have. - */ - if(w == -1) + + if(w <= 0) { - while((queue = GWBUF_CONSUME_ALL(queue))); + break; } else { + /** Remove written bytes from the queue */ queue = gwbuf_consume(queue, w); LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, @@ -1733,38 +1717,6 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) } } /* if (dcb->writeq) */ - if (saved_errno != 0 && - queue != NULL && - saved_errno != EAGAIN && - saved_errno != EWOULDBLOCK) - { - bool dolog = true; - - /** - * Do not log if writing COM_QUIT to backend failed. - */ - if (GWBUF_IS_TYPE_MYSQL(queue)) - { - uint8_t* data = GWBUF_DATA(queue); - - if (data[4] == 0x01) - { - dolog = false; - } - } - if (dolog) - { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Writing to %s socket failed due %d, %s.", - pthread_self(), - dcb_isclient(dcb) ? "client" : "backend server", - saved_errno, - strerror(saved_errno)))); - } - spinlock_release(&dcb->writeqlock); - return 0; - } spinlock_release(&dcb->writeqlock); if (dcb->high_water && dcb->writeqlen > dcb->high_water && below_water) diff --git a/server/core/secrets.c b/server/core/secrets.c index 97cb49806..b0e0c5542 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -228,6 +228,12 @@ unsigned int randval; MAXKEYS key; char secret_file[PATH_MAX + 10]; +if(strlen(path) > PATH_MAX) +{ + skygw_log_write(LOGFILE_ERROR,"Error: Pathname too long."); + return 1; +} + sprintf(secret_file,"%s/.secrets",path); /* Open for writing | Create | Truncate the file for writing */ diff --git a/server/core/service.c b/server/core/service.c index 24d51542d..be05932ae 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -268,7 +268,7 @@ GWPROTOCOL *funcs; /* Save authentication data to file cache */ char *ptr, path[PATH_MAX + 1]; int mkdir_rval = 0; - strcpy(path, get_cachedir()); + strncpy(path, get_cachedir(),PATH_MAX); strncat(path, "/", 4096); strncat(path, service->name, PATH_MAX); if (access(path, R_OK) == -1) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 07d9fd848..06a6e6de8 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -155,7 +155,10 @@ startMonitor(void *arg,void* opt) else if(!strcmp(params->name,"script")) { if(handle->script) + { free(handle->script); + handle->script = NULL; + } if(access(params->value,X_OK) == 0) { diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 4e3f84dca..bce8c631a 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -396,6 +396,7 @@ mlist_cursor_t* mlist_cursor_init( if (c == NULL) { goto return_cursor; + simple_mutex_unlock(&list->mlist_mutex); } c->mlcursor_chk_top = CHK_NUM_MLIST_CURSOR; c->mlcursor_chk_tail = CHK_NUM_MLIST_CURSOR; @@ -581,6 +582,7 @@ bool mlist_cursor_move_to_first( simple_mutex_lock(&list->mlist_mutex, true); if (mc->mlcursor_list->mlist_deleted) { + simple_mutex_unlock(&list->mlist_mutex); return false; } /** Set position point to first node */ From 61bee570d17404ff84735068fff3ec664d59ca41 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 25 Jun 2015 11:56:27 +0300 Subject: [PATCH 198/273] Fixed build failures due to old OpenSSL libraries. --- server/core/gateway.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/core/gateway.c b/server/core/gateway.c index 6513bdb1f..c474083ee 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -272,6 +272,7 @@ static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, i free(n); } +#ifdef OPENSSL_1_0 /** * The thread ID callback function for OpenSSL dynamic locks. * @param id Id to modify @@ -280,6 +281,7 @@ static void maxscale_ssl_id(CRYPTO_THREADID* id) { CRYPTO_THREADID_set_numeric(id,pthread_self()); } +#endif /** * Handler for SIGHUP signal. Reload the configuration for the From 80709ce039c3b07ec7cbc5461648d071be55f277 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 25 Jun 2015 16:43:50 +0300 Subject: [PATCH 199/273] Fixed compile errors for older SSL libraries. --- server/core/service.c | 3 ++- server/include/service.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server/core/service.c b/server/core/service.c index be05932ae..9822ac881 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -1966,13 +1966,14 @@ int serviceInitSSL(SERVICE* service) case SERVICE_TLS10: service->method = (SSL_METHOD*)TLSv1_server_method(); break; +#ifdef OPENSSL_1_0 case SERVICE_TLS11: service->method = (SSL_METHOD*)TLSv1_1_server_method(); break; case SERVICE_TLS12: service->method = (SSL_METHOD*)TLSv1_2_server_method(); break; - +#endif /** Rest of these use the maximum available SSL/TLS methods */ case SERVICE_SSL_MAX: service->method = (SSL_METHOD*)SSLv23_server_method(); diff --git a/server/include/service.h b/server/include/service.h index 3337ebfc0..b64270b9e 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -117,8 +117,10 @@ typedef enum { enum{ SERVICE_SSLV3, SERVICE_TLS10, +#ifdef OPENSSL_1_0 SERVICE_TLS11, SERVICE_TLS12, +#endif SERVICE_SSL_MAX, SERVICE_TLS_MAX, SERVICE_SSL_TLS_MAX From cff01cad055a29a6116ea5d2de106a2aaa6eb38e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 25 Jun 2015 17:13:10 +0300 Subject: [PATCH 200/273] Fixed compile errors with old SSL library. --- server/core/service.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/core/service.c b/server/core/service.c index 9822ac881..3dee01c91 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -923,10 +923,12 @@ serviceSetSSLVersion(SERVICE *service, char* version) service->ssl_method_type = SERVICE_SSLV3; else if(strcasecmp(version,"TLSV10") == 0) service->ssl_method_type = SERVICE_TLS10; +#ifdef OPENSSL_1_0 else if(strcasecmp(version,"TLSV11") == 0) service->ssl_method_type = SERVICE_TLS11; else if(strcasecmp(version,"TLSV12") == 0) service->ssl_method_type = SERVICE_TLS12; +#endif else if(strcasecmp(version,"MAX") == 0) service->ssl_method_type = SERVICE_SSL_TLS_MAX; else return -1; From 22053cfafbd3c933617b49505d3d5d5130768e09 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 26 Jun 2015 10:36:27 +0300 Subject: [PATCH 201/273] Updated MaxScale version to 1.2.0. --- cmake/macros.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/macros.cmake b/cmake/macros.cmake index c287343fb..b78502cf9 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -9,13 +9,13 @@ macro(set_maxscale_version) # MaxScale version number set(MAXSCALE_VERSION_MAJOR "1") - set(MAXSCALE_VERSION_MINOR "1") - set(MAXSCALE_VERSION_PATCH "1") + set(MAXSCALE_VERSION_MINOR "2") + set(MAXSCALE_VERSION_PATCH "0") set(MAXSCALE_VERSION_NUMERIC "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}") set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}") # This should be incremented each time a package is rebuilt - set(MAXSCALE_BUILD_NUMBER 2) + set(MAXSCALE_BUILD_NUMBER 1) endmacro() macro(set_variables) From fc1615c48931b7aab5fd255fcdae5f7e55f73eed Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 26 Jun 2015 13:35:14 +0300 Subject: [PATCH 202/273] Fixed the test header configuration failing. --- server/test/maxscale_test.h.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/test/maxscale_test.h.in b/server/test/maxscale_test.h.in index d41ff181f..b5448295c 100644 --- a/server/test/maxscale_test.h.in +++ b/server/test/maxscale_test.h.in @@ -1,9 +1,9 @@ #ifndef MAXSCALE_TEST_H #define MAXSCALE_TEST_H -#define TEST_DIR "${CMAKE_BINARY_DIR}" -#define TEST_LOG_DIR "${CMAKE_BINARY_DIR}/log" -#define TEST_BIN_DIR "${CMAKE_BINARY_DIR}/bin" -#define TEST_MOD_DIR "${CMAKE_BINARY_DIR}/modules" -#define TEST_LIB_DIR "${CMAKE_BINARY_DIR}/lib" -#define TEST_ETC_DIR "${CMAKE_BINARY_DIR}/etc" +#define TEST_DIR "@CMAKE_BINARY_DIR@" +#define TEST_LOG_DIR "@CMAKE_BINARY_DIR@/log" +#define TEST_BIN_DIR "@CMAKE_BINARY_DIR@/bin" +#define TEST_MOD_DIR "@CMAKE_BINARY_DIR@/modules" +#define TEST_LIB_DIR "@CMAKE_BINARY_DIR@/lib" +#define TEST_ETC_DIR "@CMAKE_BINARY_DIR@/etc" #endif From 03503a8f9bfc1a6e2da8ce5062fc59f9b5409f81 Mon Sep 17 00:00:00 2001 From: counterpoint Date: Fri, 26 Jun 2015 17:31:40 +0100 Subject: [PATCH 203/273] Fix a number of relatively simple bugs shown by Coverity. --- log_manager/log_manager.cc | 4 +-- server/core/config.c | 2 +- .../modules/routing/maxinfo/maxinfo_parse.c | 4 --- .../routing/readwritesplit/readwritesplit.c | 27 ++++++++++--------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index b31ae6112..a87157be7 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1394,7 +1394,7 @@ int skygw_log_write_flush( /** * Write log string to buffer and add to file write list. */ - for(i = LOGFILE_FIRST;i<=LOGFILE_LAST;i <<=1) + for (i = LOGFILE_FIRST; irses_lock)); + myrses = *rses; + ss_dassert(SPINLOCK_IS_LOCKED(&myrses->rses_lock)); ses = backend_dcb->session; CHK_SESSION(ses); @@ -4782,7 +4785,7 @@ static bool handle_error_new_connection( /** * If bref == NULL it has been replaced already with another one. */ - if ((bref = get_bref_from_dcb(rses, backend_dcb)) == NULL) + if ((bref = get_bref_from_dcb(myrses, backend_dcb)) == NULL) { succp = true; goto return_succp; @@ -4825,25 +4828,25 @@ static bool handle_error_new_connection( (void *)bref); router_nservers = router_get_servercount(inst); - max_nslaves = rses_get_max_slavecount(rses, router_nservers); - max_slave_rlag = rses_get_max_replication_lag(rses); + max_nslaves = rses_get_max_slavecount(myrses, router_nservers); + max_slave_rlag = rses_get_max_replication_lag(myrses); /** * Try to get replacement slave or at least the minimum * number of slave connections for router session. */ if(inst->rwsplit_config.disable_slave_recovery) { - succp = have_enough_servers(&rses,1,router_nservers,inst) ? true : false; + succp = have_enough_servers(&myrses,1,router_nservers,inst) ? true : false; } else { succp = select_connect_backend_servers( - &rses->rses_master_ref, - rses->rses_backend_ref, + &myrses->rses_master_ref, + myrses->rses_backend_ref, router_nservers, max_nslaves, max_slave_rlag, - rses->rses_config.rw_slave_select_criteria, + myrses->rses_config.rw_slave_select_criteria, ses, inst); } From c9606e1071fb6f21413002d7fa5d7ff78e810b46 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 27 Jun 2015 08:28:37 +0300 Subject: [PATCH 204/273] Fixed build failures due to mismatching function prototypes. --- server/modules/routing/readwritesplit/readwritesplit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 45ce0ab5f..70554c544 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -287,7 +287,7 @@ static sescmd_cursor_t* backend_ref_get_sescmd_cursor (backend_ref_t* bref); static int router_handle_state_switch(DCB* dcb, DCB_REASON reason, void* data); static bool handle_error_new_connection( ROUTER_INSTANCE* inst, - ROUTER_CLIENT_SES* rses, + ROUTER_CLIENT_SES** rses, DCB* backend_dcb, GWBUF* errmsg); static void handle_error_reply_client( From 4759df9f87723b1002ccba1a95f3447394bf861d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 27 Jun 2015 09:08:20 +0300 Subject: [PATCH 205/273] Added proper wildcard database grant detection. --- server/core/dbusers.c | 35 ++++++++++++++++++++++++++ server/modules/protocol/mysql_common.c | 7 ------ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index f4e0ed080..a54ed73a7 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -1706,6 +1706,41 @@ static int uh_cmpfun( void* v1, void* v2) { return 0; } + if(hu2->resource && strlen(hu2->resource) && strchr(hu2->resource,'%') != NULL) + { + regex_t re; + char db[MYSQL_DATABASE_MAXLEN*2 +1]; + strcpy(db,hu2->resource); + int len = strlen(db); + char* ptr = strrchr(db,'%'); + + if(ptr == NULL) + { + return 1; + } + + while(ptr) + { + memmove(ptr+1,ptr,(len - (ptr - db)) + 1); + *ptr = '.'; + *(ptr + 1) = '*'; + len = strlen(db); + ptr = strrchr(db,'%'); + } + + if((regcomp(&re,db,REG_ICASE|REG_NOSUB))) + { + return 1; + } + + if(regexec(&re,hu1->resource,0,NULL,0) == 0) + { + regfree(&re); + return 0; + } + regfree(&re); + } + /* no matches, deny auth */ return 1; } diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 83e2912e5..9b138360a 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -1552,13 +1552,6 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, break; } - /** See if ANYDB == Y */ - if(key.resource) - { - key.resource = NULL; - continue; - } - if (!user_password) { /* * user@% not found. From 88940c0097d4f76f04e53472c59f8b86b5a481d3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 27 Jun 2015 09:47:06 +0300 Subject: [PATCH 206/273] Fixed wildcard grants not being added to the users table. --- server/core/dbusers.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index a54ed73a7..935644c18 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -974,18 +974,10 @@ getAllUsers(SERVICE *service, USERS *users) } } - if(havedb && wildcard_db_grant(dbnm)) + if(havedb && wildcard_db_grant(dbnm) && service->optimize_wildcard) { - if(service->optimize_wildcard) - { - rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); - skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); - } - else - { - /** Use ANYDB for wildcard grants */ - rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL); - } + rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); + skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); } else { @@ -1041,8 +1033,8 @@ getAllUsers(SERVICE *service, USERS *users) } else if(rc == -1) { /** Duplicate user*/ - LOGIF(LE,(skygw_log_write(LT|LE, - "Warning: Duplicate MySQL user found for service [%s]: %s@%s%s%s", + LOGIF(LT,(skygw_log_write(LT, + "Duplicate MySQL user found for service [%s]: %s@%s%s%s", service->name, row[0],row[1],havedb?" for database: ":"", havedb ?dbnm:""))); From 05991384a6f7ac9bef6d9d04ab165cb392dcfd41 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 27 Jun 2015 09:51:59 +0300 Subject: [PATCH 207/273] Added missing removal of systemd files to the postinstall script. --- etc/postrm.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etc/postrm.in b/etc/postrm.in index 65c0f7116..7488ebe46 100755 --- a/etc/postrm.in +++ b/etc/postrm.in @@ -3,7 +3,9 @@ if [ "$1" -eq 0 ] then rm -f /etc/init.d/maxscale rm -f /etc/ld.so.conf.d/maxscale.conf + rm -f /usr/lib/systemd/system/maxscale.service else + # Copy and rename config from old location if [ -f "/usr/local/mariadb-maxscale/etc/MaxScale.cnf" ] then cp "/usr/local/mariadb-maxscale/etc/MaxScale.cnf" "/etc/maxscale.cnf" From 113fb4c33bb6ee07c13821b8472b2e797e95ceff Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 28 Jun 2015 08:43:05 +0300 Subject: [PATCH 208/273] Fix to MXS-209: https://mariadb.atlassian.net/browse/MXS-209 Added missing checks for proper column count on query result. --- server/modules/monitor/galeramon.c | 15 +++++++++++ server/modules/monitor/mmmon.c | 35 +++++++++++++++++++++++--- server/modules/monitor/mysql_mon.c | 23 +++++++++++++++++ server/modules/monitor/ndbclustermon.c | 14 +++++++++++ 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 06a6e6de8..e1ddc1217 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -372,6 +372,13 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'wsrep_local_state'\""); + return; + } + while ((row = mysql_fetch_row(result))) { if (strcmp(row[1], "4") == 0) @@ -398,6 +405,14 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long local_index = -1; + + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'wsrep_local_index'\""); + return; + } + while ((row = mysql_fetch_row(result))) { local_index = strtol(row[1], NULL, 10); diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 545a77ca2..970fd0d31 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -366,7 +366,14 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long server_id = -1; - + + if(mysql_field_count(database->con) != 1) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Malformed result for 'SELECT @@server_id'."); + return; + } + while ((row = mysql_fetch_row(result))) { server_id = strtol(row[0], NULL, 10); @@ -392,7 +399,15 @@ char *server_string; { int i = 0; long master_id = -1; - + + if(mysql_field_count(database->con) < 42) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: SHOW ALL SLAVES STATUS " + "returned less than the expected amount of rows."); + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -431,7 +446,15 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long master_id = -1; - + + if(mysql_field_count(database->con) < 40) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: SHOW SLAVE STATUS " + "returned less than the expected amount of rows."); + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -463,6 +486,12 @@ char *server_string; if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Malformed result for \"SHOW GLOBAL VARIABLES LIKE 'read_only'\""); + return; + } while ((row = mysql_fetch_row(result))) { diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index e310e2ede..4c5f57ab6 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -408,6 +408,12 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long server_id = -1; + if(mysql_field_count(database->con) != 1) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Malformed result for 'SELECT @@server_id'."); + return; + } while ((row = mysql_fetch_row(result))) { server_id = strtol(row[0], NULL, 10); @@ -433,6 +439,15 @@ char *server_string; { int i = 0; long master_id = -1; + + if(mysql_field_count(database->con) < 42) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: SHOW ALL SLAVES STATUS " + "returned less than the expected amount of rows."); + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -471,6 +486,14 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long master_id = -1; + if(mysql_field_count(database->con) < 40) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: SHOW SLAVE STATUS " + "returned less than the expected amount of rows."); + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index 9561e275e..ed218d43e 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -323,6 +323,13 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'\""); + return; + } + while ((row = mysql_fetch_row(result))) { if (atoi(row[1]) > 0) @@ -335,6 +342,13 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\""); + return; + } + long cluster_node_id = -1; while ((row = mysql_fetch_row(result))) { From 5c7a30e9fe326dc11af5c525581b771692c867c2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 28 Jun 2015 10:43:06 +0300 Subject: [PATCH 209/273] Added more error logging. --- server/modules/monitor/galeramon.c | 13 +++++++++++-- server/modules/monitor/mmmon.c | 26 ++++++++++++++++++++------ server/modules/monitor/mysql_mon.c | 22 +++++++++++++++++----- server/modules/monitor/ndbclustermon.c | 6 ++++-- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index e1ddc1217..f520b44aa 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -375,7 +375,8 @@ char *server_string; if(mysql_field_count(database->con) < 2) { mysql_free_result(result); - skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'wsrep_local_state'\""); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_state'\". Expected 2 columns." + " MySQL Version: %s",version_str); return; } @@ -389,6 +390,13 @@ char *server_string; if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW VARIABLES LIKE 'wsrep_sst_method'\". Expected 2 columns." + " MySQL Version: %s",version_str); + return; + } while ((row = mysql_fetch_row(result))) { if (strncmp(row[1], "xtrabackup", 10) == 0) @@ -409,7 +417,8 @@ char *server_string; if(mysql_field_count(database->con) < 2) { mysql_free_result(result); - skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'wsrep_local_index'\""); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_index'\". Expected 2 columns." + " MySQL Version: %s",version_str); return; } diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 970fd0d31..e70b3684a 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -370,7 +370,8 @@ char *server_string; if(mysql_field_count(database->con) != 1) { mysql_free_result(result); - skygw_log_write(LE,"Error: Malformed result for 'SELECT @@server_id'."); + skygw_log_write(LE,"Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column." + " MySQL Version: %s",version_str); return; } @@ -403,8 +404,9 @@ char *server_string; if(mysql_field_count(database->con) < 42) { mysql_free_result(result); - skygw_log_write(LE,"Error: SHOW ALL SLAVES STATUS " - "returned less than the expected amount of rows."); + skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " + "returned less than the expected amount of columns. Expected 42 columns" + " MySQL Version: %s",version_str); return; } @@ -450,8 +452,19 @@ char *server_string; if(mysql_field_count(database->con) < 40) { mysql_free_result(result); - skygw_log_write(LE,"Error: SHOW SLAVE STATUS " - "returned less than the expected amount of rows."); + + if(server_version < 5*10000 + 5*100) + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + " for MySQL 5.1 does not have master_server_id, replication tree cannot be resolved." + " MySQL Version: %s",version_str); + } + else + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 40 columns." + " MySQL Version: %s",version_str); + } return; } @@ -489,7 +502,8 @@ char *server_string; if(mysql_field_count(database->con) < 2) { mysql_free_result(result); - skygw_log_write(LE,"Error: Malformed result for \"SHOW GLOBAL VARIABLES LIKE 'read_only'\""); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW GLOBAL VARIABLES LIKE 'read_only'\". Expected 2 columns." + " MySQL Version: %s",version_str); return; } diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 4c5f57ab6..d2fa7acbd 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -411,7 +411,8 @@ char *server_string; if(mysql_field_count(database->con) != 1) { mysql_free_result(result); - skygw_log_write(LE,"Error: Malformed result for 'SELECT @@server_id'."); + skygw_log_write(LE,"Error: Unexpected result for \"SELECT @@server_id\". Expected 1 columns." + " MySQL Version: %s",version_str); return; } while ((row = mysql_fetch_row(result))) @@ -443,8 +444,9 @@ char *server_string; if(mysql_field_count(database->con) < 42) { mysql_free_result(result); - skygw_log_write(LE,"Error: SHOW ALL SLAVES STATUS " - "returned less than the expected amount of rows."); + skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " + "returned less than the expected amount of columns. Expected 42 columns." + " MySQL Version: %s",version_str); return; } @@ -489,8 +491,18 @@ char *server_string; if(mysql_field_count(database->con) < 40) { mysql_free_result(result); - skygw_log_write(LE,"Error: SHOW SLAVE STATUS " - "returned less than the expected amount of rows."); + if(server_version < 5*10000 + 5*100) + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + " for MySQL 5.1 does not have master_server_id, replication tree cannot be resolved." + " MySQL Version: %s",version_str); + } + else + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 40 columns." + " MySQL Version: %s",version_str); + } return; } diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index ed218d43e..a5d0b2455 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -326,7 +326,8 @@ char *server_string; if(mysql_field_count(database->con) < 2) { mysql_free_result(result); - skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'\""); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'\". Expected 2 columns." + " MySQL Version: %s",version_str); return; } @@ -345,7 +346,8 @@ char *server_string; if(mysql_field_count(database->con) < 2) { mysql_free_result(result); - skygw_log_write(LE,"Error: Malformed result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\""); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\". Expected 2 columns." + " MySQL Version: %s",version_str); return; } From 0062d9d2b783b752ee18a89fda6241a2c31c4ef9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 29 Jun 2015 10:24:16 +0300 Subject: [PATCH 210/273] Version errors for SHOW SLAVE STATUS now only print once. --- server/core/monitor.c | 1 + server/include/monitor.h | 1 + server/modules/monitor/mmmon.c | 11 ++++++++--- server/modules/monitor/mysql_mon.c | 11 ++++++++--- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index 0f24e691d..f422b4f6b 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -192,6 +192,7 @@ monitorAddServer(MONITOR *mon, SERVER *server) db->con = NULL; db->next = NULL; db->mon_err_count = 0; + db->log_version_err = true; /** Server status is uninitialized */ db->mon_prev_status = -1; /* pending status is updated by get_replication_tree */ diff --git a/server/include/monitor.h b/server/include/monitor.h index 681f34bac..442efb27f 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -124,6 +124,7 @@ typedef enum typedef struct monitor_servers { SERVER *server; /**< The server being monitored */ MYSQL *con; /**< The MySQL connection */ + bool log_version_err; int mon_err_count; unsigned int mon_prev_status; unsigned int pending_status; /**< Pending Status flag bitmap */ diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index e70b3684a..40c2c11fb 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -455,9 +455,14 @@ char *server_string; if(server_version < 5*10000 + 5*100) { - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - " for MySQL 5.1 does not have master_server_id, replication tree cannot be resolved." - " MySQL Version: %s",version_str); + if(database->log_version_err) + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + " for versions less than 5.5 does not have master_server_id, " + "replication tree cannot be resolved for server %s." + " MySQL Version: %s",database->server->unique_name,version_str); + database->log_version_err = false; + } } else { diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index d2fa7acbd..2ba70a499 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -493,9 +493,14 @@ char *server_string; mysql_free_result(result); if(server_version < 5*10000 + 5*100) { - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - " for MySQL 5.1 does not have master_server_id, replication tree cannot be resolved." - " MySQL Version: %s",version_str); + if(database->log_version_err) + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + " for versions less than 5.5 does not have master_server_id, " + "replication tree cannot be resolved for server %s." + " MySQL Version: %s",database->server->unique_name,version_str); + database->log_version_err = false; + } } else { From 2c3c856ea1b781b0e61f21836bf4a4bd0434f336 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 29 Jun 2015 16:01:56 +0300 Subject: [PATCH 211/273] Added option for C99 builds. --- CMakeLists.txt | 8 +++++++- cmake/macros.cmake | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c28b2c657..62abd54a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,7 +114,13 @@ if(PROFILE) set(FLAGS "${FLAGS} -pg " CACHE STRING "Compilation flags" FORCE) endif() -set(CMAKE_C_FLAGS "${FLAGS}") +if(USE_C99) + message(STATUS "Using C99 standard") + set(CMAKE_C_FLAGS "-std=c99 -D_GNU_SOURCE=1 ${FLAGS}") +else() + set(CMAKE_C_FLAGS "${FLAGS}") +endif() + set(CMAKE_C_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG -DLOG_ASSERT") set(CMAKE_C_FLAGS_RELEASE "") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-ggdb") diff --git a/cmake/macros.cmake b/cmake/macros.cmake index b78502cf9..d7921f1c4 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -20,6 +20,9 @@ endmacro() macro(set_variables) + # Use C99 + set(USE_C99 FALSE CACHE BOOL "Use C99 standard") + # hostname or IP address of MaxScale's host set(TEST_HOST "127.0.0.1" CACHE STRING "hostname or IP address of MaxScale's host") From 6f343ff57b7c599d0825c4094dfc6afb05ce140f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 29 Jun 2015 19:17:12 +0300 Subject: [PATCH 212/273] Fix to MXS-227: https://mariadb.atlassian.net/browse/MXS-227 Fixed memory leak. --- server/modules/monitor/galeramon.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index f520b44aa..11f9c565a 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -280,7 +280,7 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) { GALERA_MONITOR* handle = (GALERA_MONITOR*)mon->handle; MYSQL_ROW row; -MYSQL_RES *result; +MYSQL_RES *result,*result2; int isjoined = 0; char *uname = mon->user; char *passwd = mon->password; @@ -388,11 +388,12 @@ char *server_string; /* Check if the node is a donor and is using xtrabackup, in this case it can stay alive */ else if (strcmp(row[1], "2") == 0 && handle->availableWhenDonor == 1) { if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0 - && (result = mysql_store_result(database->con)) != NULL) + && (result2 = mysql_store_result(database->con)) != NULL) { if(mysql_field_count(database->con) < 2) { mysql_free_result(result); + mysql_free_result(result2); skygw_log_write(LE,"Error: Unexpected result for \"SHOW VARIABLES LIKE 'wsrep_sst_method'\". Expected 2 columns." " MySQL Version: %s",version_str); return; @@ -402,6 +403,7 @@ char *server_string; if (strncmp(row[1], "xtrabackup", 10) == 0) isjoined = 1; } + mysql_free_result(result2); } } } From a2e281823a6f6796b85a458f3a25927745747c4c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 30 Jun 2015 14:58:36 +0300 Subject: [PATCH 213/273] Added NULL checks to readwritesplit. --- .../routing/readwritesplit/readwritesplit.c | 131 +++++++++++++----- 1 file changed, 99 insertions(+), 32 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 14f99c361..b5b4ddb36 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -226,7 +226,7 @@ static rses_property_t* mysql_sescmd_get_property( static rses_property_t* rses_property_init( rses_property_type_t prop_type); -static void rses_property_add( +static int rses_property_add( ROUTER_CLIENT_SES* rses, rses_property_t* prop); @@ -2943,6 +2943,11 @@ static void bref_clear_state( backend_ref_t* bref, bref_state_t state) { + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to bref_clear_state. (%s:%d)",__FILE__,__LINE__); + return; + } if (state != BREF_WAITING_RESULT) { bref->bref_state &= ~state; @@ -2972,6 +2977,11 @@ static void bref_set_state( backend_ref_t* bref, bref_state_t state) { + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to bref_set_state. (%s:%d)",__FILE__,__LINE__); + return; + } if (state != BREF_WAITING_RESULT) { bref->bref_state |= state; @@ -3535,7 +3545,8 @@ static rses_property_t* rses_property_init( prop = (rses_property_t*)calloc(1, sizeof(rses_property_t)); if (prop == NULL) { - goto return_prop; + skygw_log_write(LE,"Error: Malloc returned NULL. (%s:%d)",__FILE__,__LINE__); + return NULL; } prop->rses_prop_type = prop_type; #if defined(SS_DEBUG) @@ -3554,6 +3565,11 @@ return_prop: static void rses_property_done( rses_property_t* prop) { + if(prop == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to rses_property_done. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_RSES_PROP(prop); switch (prop->rses_prop_type) { @@ -3587,10 +3603,20 @@ static void rses_property_done( * * Router client session must be locked. */ -static void rses_property_add( +static int rses_property_add( ROUTER_CLIENT_SES* rses, rses_property_t* prop) { + if(rses == NULL) + { + skygw_log_write(LE,"Error: Router client session is NULL. (%s:%d)",__FILE__,__LINE__); + return -1; + } + if(prop == NULL) + { + skygw_log_write(LE,"Error: Router client session property is NULL. (%s:%d)",__FILE__,__LINE__); + return -1; + } rses_property_t* p; CHK_CLIENT_RSES(rses); @@ -3612,6 +3638,7 @@ static void rses_property_add( } p->rses_prop_next = prop; } + return 0; } /** @@ -3622,7 +3649,13 @@ static mysql_sescmd_t* rses_property_get_sescmd( rses_property_t* prop) { mysql_sescmd_t* sescmd; - + + if(prop == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to rses_property_get_sescmd. (%s:%d)",__FILE__,__LINE__); + return; + } + CHK_RSES_PROP(prop); ss_dassert(prop->rses_prop_rsession == NULL || SPINLOCK_IS_LOCKED(&prop->rses_prop_rsession->rses_lock)); @@ -3635,22 +3668,6 @@ static mysql_sescmd_t* rses_property_get_sescmd( } return sescmd; } - -/** -static void rses_begin_locked_property_action( - rses_property_t* prop) -{ - CHK_RSES_PROP(prop); - spinlock_acquire(&prop->rses_prop_lock); -} - -static void rses_end_locked_property_action( - rses_property_t* prop) -{ - CHK_RSES_PROP(prop); - spinlock_release(&prop->rses_prop_lock); -} -*/ /** * Create session command property. @@ -3683,6 +3700,11 @@ static mysql_sescmd_t* mysql_sescmd_init ( static void mysql_sescmd_done( mysql_sescmd_t* sescmd) { + if(sescmd == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to mysql_sescmd_done. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_RSES_PROP(sescmd->my_sescmd_prop); gwbuf_free(sescmd->my_sescmd_buf); memset(sescmd, 0, sizeof(mysql_sescmd_t)); @@ -3855,6 +3877,12 @@ static bool sescmd_cursor_is_active( sescmd_cursor_t* sescmd_cursor) { bool succp; + + if(sescmd_cursor == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_is_active. (%s:%d)",__FILE__,__LINE__); + return false; + } ss_dassert(SPINLOCK_IS_LOCKED(&sescmd_cursor->scmd_cur_rses->rses_lock)); succp = sescmd_cursor->scmd_cur_active; @@ -3880,6 +3908,11 @@ static GWBUF* sescmd_cursor_clone_querybuf( sescmd_cursor_t* scur) { GWBUF* buf; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_clone_querybuf. (%s:%d)",__FILE__,__LINE__); + return NULL; + } ss_dassert(scur->scmd_cur_cmd != NULL); buf = gwbuf_clone(scur->scmd_cur_cmd->my_sescmd_buf); @@ -3892,7 +3925,12 @@ static bool sescmd_cursor_history_empty( sescmd_cursor_t* scur) { bool succp; - + + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_history_empty. (%s:%d)",__FILE__,__LINE__); + return true; + } CHK_SESCMD_CUR(scur); if (scur->scmd_cur_rses->rses_properties[RSES_PROP_TYPE_SESCMD] == NULL) @@ -3912,6 +3950,11 @@ static void sescmd_cursor_reset( sescmd_cursor_t* scur) { ROUTER_CLIENT_SES* rses; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_reset. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_SESCMD_CUR(scur); CHK_CLIENT_RSES(scur->scmd_cur_rses); rses = scur->scmd_cur_rses; @@ -3928,6 +3971,11 @@ static bool execute_sescmd_history( { bool succp; sescmd_cursor_t* scur; + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_history. (%s:%d)",__FILE__,__LINE__); + return false; + } CHK_BACKEND_REF(bref); scur = &bref->bref_sescmd_cur; @@ -3964,6 +4012,11 @@ static bool execute_sescmd_in_backend( int rc = 0; sescmd_cursor_t* scur; + if(backend_ref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_in_backend. (%s:%d)",__FILE__,__LINE__); + return false; + } if (BREF_IS_CLOSED(backend_ref)) { succp = false; @@ -4084,6 +4137,12 @@ static bool sescmd_cursor_next( rses_property_t* prop_curr; rses_property_t* prop_next; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_next. (%s:%d)",__FILE__,__LINE__); + return false; + } + ss_dassert(scur != NULL); ss_dassert(*(scur->scmd_cur_ptr_property) != NULL); ss_dassert(SPINLOCK_IS_LOCKED( @@ -4410,11 +4469,21 @@ static bool route_session_write( * prevent it from being released before properties * are cleaned up as a part of router sessionclean-up. */ - prop = rses_property_init(RSES_PROP_TYPE_SESCMD); + if((prop = rses_property_init(RSES_PROP_TYPE_SESCMD)) == NULL) + { + skygw_log_write(LE,"Error: Router session property initialization failed"); + rses_end_locked_router_action(router_cli_ses); + return false; + } mysql_sescmd_init(prop, querybuf, packet_type, router_cli_ses); /** Add sescmd property to router client session */ - rses_property_add(router_cli_ses, prop); + if(rses_property_add(router_cli_ses, prop) != 0) + { + skygw_log_write(LE,"Error: Session property addition failed."); + rses_end_locked_router_action(router_cli_ses); + return false; + } for (i=0; irses_nbackends; i++) { @@ -4537,7 +4606,10 @@ static void rwsplit_process_router_options( int i; char* value; select_criteria_t c; - + + if(options == NULL) + return; + for (i = 0; options[i]; i++) { if ((value = strchr(options[i], '=')) == NULL) @@ -4625,7 +4697,7 @@ static void handleError ( SESSION* session; ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session; - + CHK_DCB(backend_dcb); /** Reset error handle flag from a given DCB */ @@ -5088,8 +5160,6 @@ static int router_handle_state_switch( { backend_ref_t* bref; int rc = 1; - ROUTER_CLIENT_SES* rses; - SESSION* ses; SERVER* srv; CHK_DCB(dcb); @@ -5110,11 +5180,8 @@ static int router_handle_state_switch( srv->name, srv->port, STRSRVSTATUS(srv)))); - ses = dcb->session; - CHK_SESSION(ses); - - rses = (ROUTER_CLIENT_SES *)dcb->session->router_session; - CHK_CLIENT_RSES(rses); + CHK_SESSION(dcb->session); + CHK_CLIENT_RSES(dcb->session->router_session); switch (reason) { case DCB_REASON_NOT_RESPONDING: From 4706b756c25c62b89803cc5bdf1bafa0521c1d57 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 30 Jun 2015 19:16:49 +0300 Subject: [PATCH 214/273] Changed the service resource hashing function into a proper string hashing function. --- server/core/dbusers.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 935644c18..6748a1437 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -1842,18 +1842,6 @@ char *mysql_format_user_entry(void *data) return mysql_user; } -/* - * The hash function we use for storing MySQL database names. - * - * @param key The key value - * @return The hash key - */ -int -resource_hash(char *key) -{ - return (*key + *(key + 1)); -} - /** * Remove the resources table * @@ -1877,7 +1865,7 @@ resource_alloc() { HASHTABLE *resources; - if ((resources = hashtable_alloc(10, resource_hash, strcmp)) == NULL) + if ((resources = hashtable_alloc(10, simple_str_hash, strcmp)) == NULL) { return NULL; } From 8a0b399896bc157668c356ec038fa741701822a1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 30 Jun 2015 18:50:26 +0300 Subject: [PATCH 215/273] Added code for MySQL 5.1 replication detection. --- server/modules/monitor/mysql_mon.c | 651 ++++++++++++++++++----------- server/modules/monitor/mysqlmon.h | 1 + 2 files changed, 400 insertions(+), 252 deletions(-) diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 2ba70a499..2fd66766e 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -156,6 +156,7 @@ startMonitor(void *arg, void* opt) handle->detectStaleMaster = 0; handle->master = NULL; handle->script = NULL; + handle->mysql51_replication = false; memset(handle->events,false,sizeof(handle->events)); spinlock_init(&handle->lock); } @@ -199,6 +200,10 @@ startMonitor(void *arg, void* opt) else have_events = true; } + else if(!strcmp(params->name,"mysql51_only")) + { + handle->mysql51_replication = config_truth_value(params->value); + } params = params->next; } if(script_error) @@ -281,7 +286,281 @@ static void diagnostics(DCB *dcb, void *arg) } dcb_printf(dcb, "\n"); } +/** + * Connect to a database + * @param mon Monitor + * @param database Monitored database + * @return true if connection was successful, false if there was an error + */ +static inline bool connect_to_db(MONITOR* mon,MONITOR_SERVERS *database) +{ + char *uname = mon->user; + char *passwd = mon->password; + char *dpwd = decryptPassword(passwd); + int connect_timeout = mon->connect_timeout; + int read_timeout = mon->read_timeout; + int write_timeout = mon->write_timeout; + if(database->con) + mysql_close(database->con); + database->con = mysql_init(NULL); + + mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); + mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); + mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); + + return (mysql_real_connect(database->con, + database->server->name, + uname, + dpwd, + NULL, + database->server->port, + NULL, + 0) != NULL); +} + +inline void monitor_mysql100_db(MONITOR_SERVERS* database) +{ + bool isslave = false; + MYSQL_RES* result; + MYSQL_ROW row; + + if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + int i = 0; + long master_id = -1; + + if(mysql_field_count(database->con) < 42) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " + "returned less than the expected amount of columns. Expected 42 columns." + " MySQL Version: %s",version_str); + return; + } + + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[12], "Yes", 3) == 0 + && strncmp(row[13], "Yes", 3) == 0) { + isslave += 1; + } + + /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building + * the replication tree, slaves ids will be added to master(s) and we will have at least the + * root master server. + * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' + */ + if (strncmp(row[12], "Yes", 3) == 0) { + /* get Master_Server_Id values */ + master_id = atol(row[41]); + if (master_id == 0) + master_id = -1; + } + + i++; + } + /* store master_id of current node */ + memcpy(&database->server->master_id, &master_id, sizeof(long)); + + mysql_free_result(result); + + /* If all configured slaves are running set this node as slave */ + if (isslave > 0 && isslave == i) + isslave = 1; + else + isslave = 0; + } + + /* Remove addition info */ + monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + monitor_clear_pending_status(database, SERVER_STALE_STATUS); + + /* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER + * will be assigned in the monitorMain() via get_replication_tree() routine + */ + + /* Set the Slave Role */ + if (isslave) + { + monitor_set_pending_status(database, SERVER_SLAVE); + /* Avoid any possible stale Master state */ + monitor_clear_pending_status(database, SERVER_MASTER); + } else { + /* Avoid any possible Master/Slave stale state */ + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); + } +} + +inline void monitor_mysql55_db(MONITOR_SERVERS* database) +{ + bool isslave = false; + MYSQL_RES* result; + MYSQL_ROW row; + + if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + long master_id = -1; + if(mysql_field_count(database->con) < 40) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 40 columns." + " MySQL Version: %s",version_str); + return; + } + + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[10], "Yes", 3) == 0 + && strncmp(row[11], "Yes", 3) == 0) { + isslave = 1; + } + + /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building + * the replication tree, slaves ids will be added to master(s) and we will have at least the + * root master server. + * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' + */ + if (strncmp(row[10], "Yes", 3) == 0) { + /* get Master_Server_Id values */ + master_id = atol(row[39]); + if (master_id == 0) + master_id = -1; + } + } + /* store master_id of current node */ + memcpy(&database->server->master_id, &master_id, sizeof(long)); + + mysql_free_result(result); + } + + /* Remove addition info */ + monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + monitor_clear_pending_status(database, SERVER_STALE_STATUS); + + /* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER + * will be assigned in the monitorMain() via get_replication_tree() routine + */ + + /* Set the Slave Role */ + if (isslave) + { + monitor_set_pending_status(database, SERVER_SLAVE); + /* Avoid any possible stale Master state */ + monitor_clear_pending_status(database, SERVER_MASTER); + } else { + /* Avoid any possible Master/Slave stale state */ + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); + } +} + +inline void monitor_mysql51_db(MONITOR_SERVERS* database) +{ + bool isslave = false; + MYSQL_RES* result; + MYSQL_ROW row; + + if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + if(mysql_field_count(database->con) < 38) + { + mysql_free_result(result); + + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 38 columns." + " MySQL Version: %s",version_str); + return; + } + + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[10], "Yes", 3) == 0 + && strncmp(row[11], "Yes", 3) == 0) { + isslave = 1; + } + } + mysql_free_result(result); + } + + /* Remove addition info */ + monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + monitor_clear_pending_status(database, SERVER_STALE_STATUS); + + /* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER + * will be assigned in the monitorMain() via get_replication_tree() routine + */ + + /* Set the Slave Role */ + if (isslave) + { + monitor_set_pending_status(database, SERVER_SLAVE); + /* Avoid any possible stale Master state */ + monitor_clear_pending_status(database, SERVER_MASTER); + } else { + /* Avoid any possible Master/Slave stale state */ + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); + } +} + +static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) +{ + MONITOR_SERVERS* database = mon->databases; + + while(database) + { + bool ismaster = false; + MYSQL_RES* result; + MYSQL_ROW row; + unsigned long *slaves; + int nslaves = 0; + if(database->con) + { + if (mysql_query(database->con, "SHOW SLAVE HOSTS") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + if(mysql_field_count(database->con) < 4) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: \"SHOW SLAVE HOSTS\" " + "returned less than the expected amount of columns. Expected 4 columns." + " MySQL Version: %s",version_str); + return; + } + + if(mysql_num_rows(result) > 0) + { + ismaster = true; + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + database->server->slaves[nslaves] = atol(row[0]); + nslaves++; + } + } + + mysql_free_result(result); + } + + + /* Set the Slave Role */ + if (ismaster) + { + monitor_set_pending_status(database, SERVER_MASTER); + } + } + database = database->next; + } +} /** * Monitor an individual server * @@ -292,271 +571,135 @@ static void monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) { MYSQL_MONITOR* handle = mon->handle; -MYSQL_ROW row; -MYSQL_RES *result; -int isslave = 0; -char *uname = mon->user; -char *passwd = mon->password; -unsigned long int server_version = 0; -char *server_string; + MYSQL_ROW row; + MYSQL_RES *result; + int isslave = 0; + char *uname = mon->user; + char *passwd = mon->password; + unsigned long int server_version = 0; + char *server_string; - if (database->server->monuser != NULL) + if (database->server->monuser != NULL) + { + uname = database->server->monuser; + passwd = database->server->monpw; + } + + if (uname == NULL) + return; + + /* Don't probe servers in maintenance mode */ + if (SERVER_IN_MAINT(database->server)) + return; + + /** Store previous status */ + database->mon_prev_status = database->server->status; + + if (database->con == NULL || mysql_ping(database->con) != 0) + { + if(connect_to_db(mon,database)) { - uname = database->server->monuser; - passwd = database->server->monpw; + server_clear_status(database->server, SERVER_AUTH_ERROR); + monitor_clear_pending_status(database, SERVER_AUTH_ERROR); } - - if (uname == NULL) - return; - - /* Don't probe servers in maintenance mode */ - if (SERVER_IN_MAINT(database->server)) - return; - - /** Store previous status */ - database->mon_prev_status = database->server->status; - - if (database->con == NULL || mysql_ping(database->con) != 0) + else { - char *dpwd = decryptPassword(passwd); - int connect_timeout = mon->connect_timeout; - int read_timeout = mon->read_timeout; - int write_timeout = mon->write_timeout; + /* The current server is not running + * + * Store server NOT running in server and monitor server pending struct + * + */ + if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) + { + server_set_status(database->server, SERVER_AUTH_ERROR); + monitor_set_pending_status(database, SERVER_AUTH_ERROR); + } + server_clear_status(database->server, SERVER_RUNNING); + monitor_clear_pending_status(database, SERVER_RUNNING); - if(database->con) - mysql_close(database->con); - database->con = mysql_init(NULL); + /* Also clear M/S state in both server and monitor server pending struct */ + server_clear_status(database->server, SERVER_SLAVE); + server_clear_status(database->server, SERVER_MASTER); + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); - mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); - mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); - mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); - - if (mysql_real_connect(database->con, - database->server->name, - uname, - dpwd, - NULL, - database->server->port, - NULL, - 0) == NULL) - { - free(dpwd); - - /* The current server is not running - * - * Store server NOT running in server and monitor server pending struct - * - */ - if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) - { - server_set_status(database->server, SERVER_AUTH_ERROR); - monitor_set_pending_status(database, SERVER_AUTH_ERROR); - } - server_clear_status(database->server, SERVER_RUNNING); - monitor_clear_pending_status(database, SERVER_RUNNING); + /* Clean addition status too */ + server_clear_status(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER); + server_clear_status(database->server, SERVER_STALE_STATUS); + monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + monitor_clear_pending_status(database, SERVER_STALE_STATUS); - /* Also clear M/S state in both server and monitor server pending struct */ - server_clear_status(database->server, SERVER_SLAVE); - server_clear_status(database->server, SERVER_MASTER); - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_clear_pending_status(database, SERVER_MASTER); + /* Log connect failure only once */ + if (mon_status_changed(database) && mon_print_fail_status(database)) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Monitor was unable to connect to " + "server %s:%d : \"%s\"", + database->server->name, + database->server->port, + mysql_error(database->con)))); + } - /* Clean addition status too */ - server_clear_status(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER); - server_clear_status(database->server, SERVER_STALE_STATUS); - monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); - monitor_clear_pending_status(database, SERVER_STALE_STATUS); - - /* Log connect failure only once */ - if (mon_status_changed(database) && mon_print_fail_status(database)) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Monitor was unable to connect to " - "server %s:%d : \"%s\"", - database->server->name, - database->server->port, - mysql_error(database->con)))); - } - - return; - } - else - { - server_clear_status(database->server, SERVER_AUTH_ERROR); - monitor_clear_pending_status(database, SERVER_AUTH_ERROR); - } - free(dpwd); + return; } - /* Store current status in both server and monitor server pending struct */ - server_set_status(database->server, SERVER_RUNNING); - monitor_set_pending_status(database, SERVER_RUNNING); + } + /* Store current status in both server and monitor server pending struct */ + server_set_status(database->server, SERVER_RUNNING); + monitor_set_pending_status(database, SERVER_RUNNING); - /* get server version from current server */ - server_version = mysql_get_server_version(database->con); + /* get server version from current server */ + server_version = mysql_get_server_version(database->con); - /* get server version string */ - server_string = (char *)mysql_get_server_info(database->con); - if (server_string) { - database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); - if (database->server->server_string) - strcpy(database->server->server_string, server_string); - } + /* get server version string */ + server_string = (char *)mysql_get_server_info(database->con); + if (server_string) { + database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); + if (database->server->server_string) + strcpy(database->server->server_string, server_string); + } + + /* get server_id form current node */ + if (mysql_query(database->con, "SELECT @@server_id") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + long server_id = -1; - /* get server_id form current node */ - if (mysql_query(database->con, "SELECT @@server_id") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - long server_id = -1; - if(mysql_field_count(database->con) != 1) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for \"SELECT @@server_id\". Expected 1 columns." - " MySQL Version: %s",version_str); - return; - } - while ((row = mysql_fetch_row(result))) - { - server_id = strtol(row[0], NULL, 10); - if ((errno == ERANGE && (server_id == LONG_MAX - || server_id == LONG_MIN)) || (errno != 0 && server_id == 0)) - { - server_id = -1; - } - database->server->node_id = server_id; - } - mysql_free_result(result); - } - - /* Check if the Slave_SQL_Running and Slave_IO_Running status is - * set to Yes - */ - - /* Check first for MariaDB 10.x.x and get status for multimaster replication */ - if (server_version >= 100000) { - - if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - int i = 0; - long master_id = -1; - - if(mysql_field_count(database->con) < 42) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " - "returned less than the expected amount of columns. Expected 42 columns." - " MySQL Version: %s",version_str); - return; - } - - while ((row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - if (strncmp(row[12], "Yes", 3) == 0 - && strncmp(row[13], "Yes", 3) == 0) { - isslave += 1; - } - - /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building - * the replication tree, slaves ids will be added to master(s) and we will have at least the - * root master server. - * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' - */ - if (strncmp(row[12], "Yes", 3) == 0) { - /* get Master_Server_Id values */ - master_id = atol(row[41]); - if (master_id == 0) - master_id = -1; - } - - i++; - } - /* store master_id of current node */ - memcpy(&database->server->master_id, &master_id, sizeof(long)); - - mysql_free_result(result); - - /* If all configured slaves are running set this node as slave */ - if (isslave > 0 && isslave == i) - isslave = 1; - else - isslave = 0; - } - } else { - if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - long master_id = -1; - if(mysql_field_count(database->con) < 40) - { - mysql_free_result(result); - if(server_version < 5*10000 + 5*100) - { - if(database->log_version_err) - { - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - " for versions less than 5.5 does not have master_server_id, " - "replication tree cannot be resolved for server %s." - " MySQL Version: %s",database->server->unique_name,version_str); - database->log_version_err = false; - } - } - else - { - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - "returned less than the expected amount of columns. Expected 40 columns." - " MySQL Version: %s",version_str); - } - return; - } - - while ((row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - if (strncmp(row[10], "Yes", 3) == 0 - && strncmp(row[11], "Yes", 3) == 0) { - isslave = 1; - } - - /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building - * the replication tree, slaves ids will be added to master(s) and we will have at least the - * root master server. - * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' - */ - if (strncmp(row[10], "Yes", 3) == 0) { - /* get Master_Server_Id values */ - master_id = atol(row[39]); - if (master_id == 0) - master_id = -1; - } - } - /* store master_id of current node */ - memcpy(&database->server->master_id, &master_id, sizeof(long)); - - mysql_free_result(result); - } - } - - /* Remove addition info */ - monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); - monitor_clear_pending_status(database, SERVER_STALE_STATUS); - - /* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER - * will be assigned in the monitorMain() via get_replication_tree() routine - */ - - /* Set the Slave Role */ - if (isslave) + if(mysql_field_count(database->con) != 1) { - monitor_set_pending_status(database, SERVER_SLAVE); - /* Avoid any possible stale Master state */ - monitor_clear_pending_status(database, SERVER_MASTER); - } else { - /* Avoid any possible Master/Slave stale state */ - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_clear_pending_status(database, SERVER_MASTER); + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column." + " MySQL Version: %s",version_str); + return; } + + while ((row = mysql_fetch_row(result))) + { + server_id = strtol(row[0], NULL, 10); + if ((errno == ERANGE && (server_id == LONG_MAX + || server_id == LONG_MIN)) || (errno != 0 && server_id == 0)) + { + server_id = -1; + } + database->server->node_id = server_id; + } + mysql_free_result(result); + } + + /* Check first for MariaDB 10.x.x and get status for multi-master replication */ + if (server_version >= 100000) + { + monitor_mysql100_db(database); + } + else if(server_version >= 5*10000 + 5*100) + { + monitor_mysql55_db(database); + } + else + { + monitor_mysql51_db(database); + } + } /** @@ -723,7 +866,11 @@ detect_stale_master = handle->detectStaleMaster; } } else { /* Compute the replication tree */ + if(handle->mysql51_replication) + root_master = build_mysql51_replication_tree(mon); + else root_master = get_replication_tree(mon, num_servers); + } /* Update server status from monitor pending status on that server*/ @@ -1302,4 +1449,4 @@ bool isMySQLEvent(monitor_event_t event) return true; } return false; -} \ No newline at end of file +} diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 0510a13cf..227b693c9 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -67,6 +67,7 @@ typedef struct { int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ + bool mysql51_replication; /**< Use MySQL 5.1 replication */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ char* script; /*< Script to call when state changes occur on servers */ bool events[MAX_MONITOR_EVENT]; /*< enabled events */ From e350f19e6f07b0f3912d10a5937d8133cd2b94b9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 30 Jun 2015 14:58:36 +0300 Subject: [PATCH 216/273] Added NULL checks to readwritesplit. --- .../routing/readwritesplit/readwritesplit.c | 131 +++++++++++++----- 1 file changed, 99 insertions(+), 32 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 14f99c361..b5b4ddb36 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -226,7 +226,7 @@ static rses_property_t* mysql_sescmd_get_property( static rses_property_t* rses_property_init( rses_property_type_t prop_type); -static void rses_property_add( +static int rses_property_add( ROUTER_CLIENT_SES* rses, rses_property_t* prop); @@ -2943,6 +2943,11 @@ static void bref_clear_state( backend_ref_t* bref, bref_state_t state) { + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to bref_clear_state. (%s:%d)",__FILE__,__LINE__); + return; + } if (state != BREF_WAITING_RESULT) { bref->bref_state &= ~state; @@ -2972,6 +2977,11 @@ static void bref_set_state( backend_ref_t* bref, bref_state_t state) { + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to bref_set_state. (%s:%d)",__FILE__,__LINE__); + return; + } if (state != BREF_WAITING_RESULT) { bref->bref_state |= state; @@ -3535,7 +3545,8 @@ static rses_property_t* rses_property_init( prop = (rses_property_t*)calloc(1, sizeof(rses_property_t)); if (prop == NULL) { - goto return_prop; + skygw_log_write(LE,"Error: Malloc returned NULL. (%s:%d)",__FILE__,__LINE__); + return NULL; } prop->rses_prop_type = prop_type; #if defined(SS_DEBUG) @@ -3554,6 +3565,11 @@ return_prop: static void rses_property_done( rses_property_t* prop) { + if(prop == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to rses_property_done. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_RSES_PROP(prop); switch (prop->rses_prop_type) { @@ -3587,10 +3603,20 @@ static void rses_property_done( * * Router client session must be locked. */ -static void rses_property_add( +static int rses_property_add( ROUTER_CLIENT_SES* rses, rses_property_t* prop) { + if(rses == NULL) + { + skygw_log_write(LE,"Error: Router client session is NULL. (%s:%d)",__FILE__,__LINE__); + return -1; + } + if(prop == NULL) + { + skygw_log_write(LE,"Error: Router client session property is NULL. (%s:%d)",__FILE__,__LINE__); + return -1; + } rses_property_t* p; CHK_CLIENT_RSES(rses); @@ -3612,6 +3638,7 @@ static void rses_property_add( } p->rses_prop_next = prop; } + return 0; } /** @@ -3622,7 +3649,13 @@ static mysql_sescmd_t* rses_property_get_sescmd( rses_property_t* prop) { mysql_sescmd_t* sescmd; - + + if(prop == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to rses_property_get_sescmd. (%s:%d)",__FILE__,__LINE__); + return; + } + CHK_RSES_PROP(prop); ss_dassert(prop->rses_prop_rsession == NULL || SPINLOCK_IS_LOCKED(&prop->rses_prop_rsession->rses_lock)); @@ -3635,22 +3668,6 @@ static mysql_sescmd_t* rses_property_get_sescmd( } return sescmd; } - -/** -static void rses_begin_locked_property_action( - rses_property_t* prop) -{ - CHK_RSES_PROP(prop); - spinlock_acquire(&prop->rses_prop_lock); -} - -static void rses_end_locked_property_action( - rses_property_t* prop) -{ - CHK_RSES_PROP(prop); - spinlock_release(&prop->rses_prop_lock); -} -*/ /** * Create session command property. @@ -3683,6 +3700,11 @@ static mysql_sescmd_t* mysql_sescmd_init ( static void mysql_sescmd_done( mysql_sescmd_t* sescmd) { + if(sescmd == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to mysql_sescmd_done. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_RSES_PROP(sescmd->my_sescmd_prop); gwbuf_free(sescmd->my_sescmd_buf); memset(sescmd, 0, sizeof(mysql_sescmd_t)); @@ -3855,6 +3877,12 @@ static bool sescmd_cursor_is_active( sescmd_cursor_t* sescmd_cursor) { bool succp; + + if(sescmd_cursor == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_is_active. (%s:%d)",__FILE__,__LINE__); + return false; + } ss_dassert(SPINLOCK_IS_LOCKED(&sescmd_cursor->scmd_cur_rses->rses_lock)); succp = sescmd_cursor->scmd_cur_active; @@ -3880,6 +3908,11 @@ static GWBUF* sescmd_cursor_clone_querybuf( sescmd_cursor_t* scur) { GWBUF* buf; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_clone_querybuf. (%s:%d)",__FILE__,__LINE__); + return NULL; + } ss_dassert(scur->scmd_cur_cmd != NULL); buf = gwbuf_clone(scur->scmd_cur_cmd->my_sescmd_buf); @@ -3892,7 +3925,12 @@ static bool sescmd_cursor_history_empty( sescmd_cursor_t* scur) { bool succp; - + + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_history_empty. (%s:%d)",__FILE__,__LINE__); + return true; + } CHK_SESCMD_CUR(scur); if (scur->scmd_cur_rses->rses_properties[RSES_PROP_TYPE_SESCMD] == NULL) @@ -3912,6 +3950,11 @@ static void sescmd_cursor_reset( sescmd_cursor_t* scur) { ROUTER_CLIENT_SES* rses; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_reset. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_SESCMD_CUR(scur); CHK_CLIENT_RSES(scur->scmd_cur_rses); rses = scur->scmd_cur_rses; @@ -3928,6 +3971,11 @@ static bool execute_sescmd_history( { bool succp; sescmd_cursor_t* scur; + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_history. (%s:%d)",__FILE__,__LINE__); + return false; + } CHK_BACKEND_REF(bref); scur = &bref->bref_sescmd_cur; @@ -3964,6 +4012,11 @@ static bool execute_sescmd_in_backend( int rc = 0; sescmd_cursor_t* scur; + if(backend_ref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_in_backend. (%s:%d)",__FILE__,__LINE__); + return false; + } if (BREF_IS_CLOSED(backend_ref)) { succp = false; @@ -4084,6 +4137,12 @@ static bool sescmd_cursor_next( rses_property_t* prop_curr; rses_property_t* prop_next; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_next. (%s:%d)",__FILE__,__LINE__); + return false; + } + ss_dassert(scur != NULL); ss_dassert(*(scur->scmd_cur_ptr_property) != NULL); ss_dassert(SPINLOCK_IS_LOCKED( @@ -4410,11 +4469,21 @@ static bool route_session_write( * prevent it from being released before properties * are cleaned up as a part of router sessionclean-up. */ - prop = rses_property_init(RSES_PROP_TYPE_SESCMD); + if((prop = rses_property_init(RSES_PROP_TYPE_SESCMD)) == NULL) + { + skygw_log_write(LE,"Error: Router session property initialization failed"); + rses_end_locked_router_action(router_cli_ses); + return false; + } mysql_sescmd_init(prop, querybuf, packet_type, router_cli_ses); /** Add sescmd property to router client session */ - rses_property_add(router_cli_ses, prop); + if(rses_property_add(router_cli_ses, prop) != 0) + { + skygw_log_write(LE,"Error: Session property addition failed."); + rses_end_locked_router_action(router_cli_ses); + return false; + } for (i=0; irses_nbackends; i++) { @@ -4537,7 +4606,10 @@ static void rwsplit_process_router_options( int i; char* value; select_criteria_t c; - + + if(options == NULL) + return; + for (i = 0; options[i]; i++) { if ((value = strchr(options[i], '=')) == NULL) @@ -4625,7 +4697,7 @@ static void handleError ( SESSION* session; ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session; - + CHK_DCB(backend_dcb); /** Reset error handle flag from a given DCB */ @@ -5088,8 +5160,6 @@ static int router_handle_state_switch( { backend_ref_t* bref; int rc = 1; - ROUTER_CLIENT_SES* rses; - SESSION* ses; SERVER* srv; CHK_DCB(dcb); @@ -5110,11 +5180,8 @@ static int router_handle_state_switch( srv->name, srv->port, STRSRVSTATUS(srv)))); - ses = dcb->session; - CHK_SESSION(ses); - - rses = (ROUTER_CLIENT_SES *)dcb->session->router_session; - CHK_CLIENT_RSES(rses); + CHK_SESSION(dcb->session); + CHK_CLIENT_RSES(dcb->session->router_session); switch (reason) { case DCB_REASON_NOT_RESPONDING: From a2e31d6846321c15a127f5cc9e372e9f1dc02759 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 30 Jun 2015 19:16:49 +0300 Subject: [PATCH 217/273] Changed the service resource hashing function into a proper string hashing function. --- server/core/dbusers.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 935644c18..6748a1437 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -1842,18 +1842,6 @@ char *mysql_format_user_entry(void *data) return mysql_user; } -/* - * The hash function we use for storing MySQL database names. - * - * @param key The key value - * @return The hash key - */ -int -resource_hash(char *key) -{ - return (*key + *(key + 1)); -} - /** * Remove the resources table * @@ -1877,7 +1865,7 @@ resource_alloc() { HASHTABLE *resources; - if ((resources = hashtable_alloc(10, resource_hash, strcmp)) == NULL) + if ((resources = hashtable_alloc(10, simple_str_hash, strcmp)) == NULL) { return NULL; } From 6c61dabfab706b654637e166aecc49966194fd50 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 1 Jul 2015 05:15:08 +0300 Subject: [PATCH 218/273] Fix to a Coverity defect. --- server/modules/routing/readwritesplit/readwritesplit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index b5b4ddb36..c4008e65e 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -3653,7 +3653,7 @@ static mysql_sescmd_t* rses_property_get_sescmd( if(prop == NULL) { skygw_log_write(LE,"Error: NULL parameter passed to rses_property_get_sescmd. (%s:%d)",__FILE__,__LINE__); - return; + return NULL; } CHK_RSES_PROP(prop); From cc24777a902f85966bd23ce73748a66f73ad02a2 Mon Sep 17 00:00:00 2001 From: counterpoint Date: Wed, 1 Jul 2015 09:46:01 +0100 Subject: [PATCH 219/273] Correct mkdir logic for default log directory. --- server/core/gateway.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index c474083ee..8f486067c 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1531,17 +1531,13 @@ int main(int argc, char **argv) /** Use default log directory /var/log/maxscale/ */ if(logdir == NULL) { - - if(access(default_logdir,F_OK) != 0) - { - if(mkdir(logdir,0555) != 0) - { - fprintf(stderr, - "Error: Cannot create log directory: %s\n", - default_logdir); - goto return_main; - } - } + if(mkdir(default_logdir,0777) != 0 && errno != EEXIST) + { + fprintf(stderr, + "Error: Cannot create log directory: %s\n", + default_logdir); + goto return_main; + } logdir = strdup(default_logdir); } @@ -1598,7 +1594,7 @@ int main(int argc, char **argv) /** * Set a data directory for the mysqld library, we use * a unique directory name to avoid clauses if multiple - * instances of the gateway are beign run on the same + * instances of the gateway are being run on the same * machine. */ From 53a4dd393d0ad4b77be9f1c50525abcd5f315e01 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 1 Jul 2015 05:15:08 +0300 Subject: [PATCH 220/273] Fix to a Coverity defect. --- server/modules/routing/readwritesplit/readwritesplit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index b5b4ddb36..c4008e65e 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -3653,7 +3653,7 @@ static mysql_sescmd_t* rses_property_get_sescmd( if(prop == NULL) { skygw_log_write(LE,"Error: NULL parameter passed to rses_property_get_sescmd. (%s:%d)",__FILE__,__LINE__); - return; + return NULL; } CHK_RSES_PROP(prop); From dc15fbb576ce19cca92f309f6c572238242353be Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 1 Jul 2015 16:46:32 +0300 Subject: [PATCH 221/273] Fixed build failure. --- server/modules/routing/readwritesplit/readwritesplit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index c4008e65e..b801d9aec 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -5161,7 +5161,8 @@ static int router_handle_state_switch( backend_ref_t* bref; int rc = 1; SERVER* srv; - + ROUTER_CLIENT_SES* rses; + SESSION* ses; CHK_DCB(dcb); bref = (backend_ref_t *)data; CHK_BACKEND_REF(bref); @@ -5180,8 +5181,10 @@ static int router_handle_state_switch( srv->name, srv->port, STRSRVSTATUS(srv)))); - CHK_SESSION(dcb->session); - CHK_CLIENT_RSES(dcb->session->router_session); + ses = dcb->session; + CHK_SESSION(ses); + rses = (ROUTER_CLIENT_SES *)dcb->session->router_session; + CHK_CLIENT_RSES(rses); switch (reason) { case DCB_REASON_NOT_RESPONDING: From 22f8e61321a9c5bad6c7c02aca8c1e1687666113 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 1 Jul 2015 16:46:32 +0300 Subject: [PATCH 222/273] Fixed build failure. --- server/modules/routing/readwritesplit/readwritesplit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index c4008e65e..b801d9aec 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -5161,7 +5161,8 @@ static int router_handle_state_switch( backend_ref_t* bref; int rc = 1; SERVER* srv; - + ROUTER_CLIENT_SES* rses; + SESSION* ses; CHK_DCB(dcb); bref = (backend_ref_t *)data; CHK_BACKEND_REF(bref); @@ -5180,8 +5181,10 @@ static int router_handle_state_switch( srv->name, srv->port, STRSRVSTATUS(srv)))); - CHK_SESSION(dcb->session); - CHK_CLIENT_RSES(dcb->session->router_session); + ses = dcb->session; + CHK_SESSION(ses); + rses = (ROUTER_CLIENT_SES *)dcb->session->router_session; + CHK_CLIENT_RSES(rses); switch (reason) { case DCB_REASON_NOT_RESPONDING: From 2b0f0b89abb6327f64050e8c59541f8bc5ba1858 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 1 Jul 2015 16:07:27 +0300 Subject: [PATCH 223/273] Added master server id to mysql 5.1 monitoring. --- server/modules/monitor/mysql_mon.c | 39 ++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 2fd66766e..07418253d 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -319,7 +319,7 @@ static inline bool connect_to_db(MONITOR* mon,MONITOR_SERVERS *database) 0) != NULL); } -inline void monitor_mysql100_db(MONITOR_SERVERS* database) +static inline void monitor_mysql100_db(MONITOR_SERVERS* database) { bool isslave = false; MYSQL_RES* result; @@ -395,7 +395,7 @@ inline void monitor_mysql100_db(MONITOR_SERVERS* database) } } -inline void monitor_mysql55_db(MONITOR_SERVERS* database) +static inline void monitor_mysql55_db(MONITOR_SERVERS* database) { bool isslave = false; MYSQL_RES* result; @@ -461,7 +461,7 @@ inline void monitor_mysql55_db(MONITOR_SERVERS* database) } } -inline void monitor_mysql51_db(MONITOR_SERVERS* database) +static inline void monitor_mysql51_db(MONITOR_SERVERS* database) { bool isslave = false; MYSQL_RES* result; @@ -515,13 +515,13 @@ inline void monitor_mysql51_db(MONITOR_SERVERS* database) static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) { MONITOR_SERVERS* database = mon->databases; + MONITOR_SERVERS* ptr; while(database) { bool ismaster = false; MYSQL_RES* result; MYSQL_ROW row; - unsigned long *slaves; int nslaves = 0; if(database->con) { @@ -540,12 +540,13 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) if(mysql_num_rows(result) > 0) { ismaster = true; - while ((row = mysql_fetch_row(result))) + while (nslaves < MONITOR_MAX_NUM_SLAVES && (row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ database->server->slaves[nslaves] = atol(row[0]); nslaves++; } + database->server->slaves[nslaves] = 0; } mysql_free_result(result); @@ -560,6 +561,32 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) } database = database->next; } + + database = mon->databases; + + /** */ + while(database) + { + ptr = mon->databases; + + while(ptr) + { + for(int i = 0;ptr->server->slaves[i];i++) + { + if(ptr->server->slaves[i] == database->server->node_id) + { + database->server->master_id = ptr->server->node_id; + break; + } + } + ptr = ptr->next; + } + if(database->server->master_id <= 0) + { + monitor_set_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + } + database = database->next; + } } /** * Monitor an individual server @@ -575,14 +602,12 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) MYSQL_RES *result; int isslave = 0; char *uname = mon->user; - char *passwd = mon->password; unsigned long int server_version = 0; char *server_string; if (database->server->monuser != NULL) { uname = database->server->monuser; - passwd = database->server->monpw; } if (uname == NULL) From 0621e33cb0ac00364d56cbea80e1ef3d24af748e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 1 Jul 2015 19:38:45 +0300 Subject: [PATCH 224/273] Updated documentation and set C99 mode as the default. --- Documentation/monitors/MySQL-Monitor.md | 8 ++++++++ cmake/macros.cmake | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index 7228c1b10..6c7e70500 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -98,6 +98,14 @@ A list of event names which cause the script to be executed. If this option is n events=master_down,slave_down ``` +### `mysql51_only` + +Enable support for MySQL 5.1 replication monitoring. This is needed if a MySQL server older than 5.5 is used as a slave in replication. + +``` +mysql51_only=true +``` + ## Script events Here is a table of all possible event types and their descriptions. diff --git a/cmake/macros.cmake b/cmake/macros.cmake index d7921f1c4..3c9d16cc0 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -21,7 +21,7 @@ endmacro() macro(set_variables) # Use C99 - set(USE_C99 FALSE CACHE BOOL "Use C99 standard") + set(USE_C99 TRUE CACHE BOOL "Use C99 standard") # hostname or IP address of MaxScale's host set(TEST_HOST "127.0.0.1" CACHE STRING "hostname or IP address of MaxScale's host") From c8173ec4f7a272e5e4a5bc0288b9767f56bd084f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 2 Jul 2015 10:00:38 +0300 Subject: [PATCH 225/273] Added missing return value. --- server/modules/monitor/mysql_mon.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 07418253d..b5bbed4dd 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -512,10 +512,18 @@ static inline void monitor_mysql51_db(MONITOR_SERVERS* database) } } +/** + * Build the replication tree for a MySQL 5.1 cluster + * + * This function queries each server with SHOW SLAVE HOSTS to determine which servers + * have slaves replicating from them. + * @param mon Monitor + * @return Lowest server ID master in the monitor + */ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) { MONITOR_SERVERS* database = mon->databases; - MONITOR_SERVERS* ptr; + MONITOR_SERVERS *ptr,*rval = NULL; while(database) { @@ -534,7 +542,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) skygw_log_write(LE,"Error: \"SHOW SLAVE HOSTS\" " "returned less than the expected amount of columns. Expected 4 columns." " MySQL Version: %s",version_str); - return; + return NULL; } if(mysql_num_rows(result) > 0) @@ -557,6 +565,8 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) if (ismaster) { monitor_set_pending_status(database, SERVER_MASTER); + if(rval == NULL || rval->server->node_id > database->server->node_id) + rval = database; } } database = database->next; @@ -587,6 +597,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) } database = database->next; } + return rval; } /** * Monitor an individual server From ea14839dbcdc31ddd572e13e78eb895d63c505fd Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 2 Jul 2015 14:55:43 +0300 Subject: [PATCH 226/273] Updated release notes with fixed bugs. --- .../MaxScale-1.2.0-Release-Notes.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index a84f94c27..49206f8ed 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -29,3 +29,27 @@ MaxScale now supports SSL/TLS encrypted connections to MaxScale. ### Monitor scripts State changes in backend servers can now trigger the execution of a custom script. With this you can easily customize MaxScale's behavior. + +## Bug fixes + +Here is a list of bugs fixed since the release of MaxScale 1.1.1. +|Key|Summary| +|[MXS-24](https://mariadb.atlassian.net/browse/MXS-24)|bugzillaId-604: Module load path documentation issues ...| +|[MXS-40](https://mariadb.atlassian.net/browse/MXS-40)|Display logged in users| +|[MXS-113](https://mariadb.atlassian.net/browse/MXS-113)|MaxScale seems to fail if built against MariaDB 10.0 libraries| +|[MXS-116](https://mariadb.atlassian.net/browse/MXS-116)|Do not run maxscale as root.| +|[MXS-125](https://mariadb.atlassian.net/browse/MXS-125)|inconsistency in maxkeys/maxpassword output and parameters| +|[MXS-136](https://mariadb.atlassian.net/browse/MXS-136)|Check for MaxScale replication heartbeat table existence before creating| +|[MXS-137](https://mariadb.atlassian.net/browse/MXS-137)|cannot get sql for queries with length >= 0x80| +|[MXS-139](https://mariadb.atlassian.net/browse/MXS-139)|Schemarouter authentication for wildcard grants fails without optimize_wildcard| +|[MXS-140](https://mariadb.atlassian.net/browse/MXS-140)|strip_db_esc does not work without auth_all_servers| +|[MXS-162](https://mariadb.atlassian.net/browse/MXS-162)|Fix Incorrect info in Configuration Guide| +|[MXS-163](https://mariadb.atlassian.net/browse/MXS-163)|Reload config leaks memory| +|[MXS-165](https://mariadb.atlassian.net/browse/MXS-165)|Concurrency issue while incrementing sessions in qlafilter| +|[MXS-166](https://mariadb.atlassian.net/browse/MXS-166)|Memory leak when creating a new event| +|[MXS-171](https://mariadb.atlassian.net/browse/MXS-171)|Allow reads on master for readwritesplit| +|[MXS-176](https://mariadb.atlassian.net/browse/MXS-176)|Missing dependencies in documentation| +|[MXS-181](https://mariadb.atlassian.net/browse/MXS-181)|Poor performance on TCP connection due to Nagle's algoritm| +|[MXS-212](https://mariadb.atlassian.net/browse/MXS-212)|Stopped services accept connections| +|[MXS-225](https://mariadb.atlassian.net/browse/MXS-225)|RPM Debug build packages have no debugging symbols| +|[MXS-227](https://mariadb.atlassian.net/browse/MXS-227)|Memory leak in Galera Monitor| From 3817b6fca8e6df12c72617c35cb669b97dc1d084 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 2 Jul 2015 15:01:16 +0300 Subject: [PATCH 227/273] Fixed broken table. --- Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index 49206f8ed..f98c2191f 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -34,6 +34,7 @@ State changes in backend servers can now trigger the execution of a custom scrip Here is a list of bugs fixed since the release of MaxScale 1.1.1. |Key|Summary| +------------ |[MXS-24](https://mariadb.atlassian.net/browse/MXS-24)|bugzillaId-604: Module load path documentation issues ...| |[MXS-40](https://mariadb.atlassian.net/browse/MXS-40)|Display logged in users| |[MXS-113](https://mariadb.atlassian.net/browse/MXS-113)|MaxScale seems to fail if built against MariaDB 10.0 libraries| From fed2cd9a3a3df2a8a3aa79df9af9f5965047d83c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 2 Jul 2015 15:02:19 +0300 Subject: [PATCH 228/273] More broken tables. --- Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index f98c2191f..636d46ad8 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -34,7 +34,7 @@ State changes in backend servers can now trigger the execution of a custom scrip Here is a list of bugs fixed since the release of MaxScale 1.1.1. |Key|Summary| ------------- +|---|--------| |[MXS-24](https://mariadb.atlassian.net/browse/MXS-24)|bugzillaId-604: Module load path documentation issues ...| |[MXS-40](https://mariadb.atlassian.net/browse/MXS-40)|Display logged in users| |[MXS-113](https://mariadb.atlassian.net/browse/MXS-113)|MaxScale seems to fail if built against MariaDB 10.0 libraries| From e09e5381ec9dc91080ff34828e717c23b8f50018 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 2 Jul 2015 15:05:03 +0300 Subject: [PATCH 229/273] Fixed table. --- .../MaxScale-1.2.0-Release-Notes.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index 636d46ad8..caf7e28c6 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -33,24 +33,25 @@ State changes in backend servers can now trigger the execution of a custom scrip ## Bug fixes Here is a list of bugs fixed since the release of MaxScale 1.1.1. -|Key|Summary| -|---|--------| -|[MXS-24](https://mariadb.atlassian.net/browse/MXS-24)|bugzillaId-604: Module load path documentation issues ...| -|[MXS-40](https://mariadb.atlassian.net/browse/MXS-40)|Display logged in users| -|[MXS-113](https://mariadb.atlassian.net/browse/MXS-113)|MaxScale seems to fail if built against MariaDB 10.0 libraries| -|[MXS-116](https://mariadb.atlassian.net/browse/MXS-116)|Do not run maxscale as root.| -|[MXS-125](https://mariadb.atlassian.net/browse/MXS-125)|inconsistency in maxkeys/maxpassword output and parameters| -|[MXS-136](https://mariadb.atlassian.net/browse/MXS-136)|Check for MaxScale replication heartbeat table existence before creating| -|[MXS-137](https://mariadb.atlassian.net/browse/MXS-137)|cannot get sql for queries with length >= 0x80| -|[MXS-139](https://mariadb.atlassian.net/browse/MXS-139)|Schemarouter authentication for wildcard grants fails without optimize_wildcard| -|[MXS-140](https://mariadb.atlassian.net/browse/MXS-140)|strip_db_esc does not work without auth_all_servers| -|[MXS-162](https://mariadb.atlassian.net/browse/MXS-162)|Fix Incorrect info in Configuration Guide| -|[MXS-163](https://mariadb.atlassian.net/browse/MXS-163)|Reload config leaks memory| -|[MXS-165](https://mariadb.atlassian.net/browse/MXS-165)|Concurrency issue while incrementing sessions in qlafilter| -|[MXS-166](https://mariadb.atlassian.net/browse/MXS-166)|Memory leak when creating a new event| -|[MXS-171](https://mariadb.atlassian.net/browse/MXS-171)|Allow reads on master for readwritesplit| -|[MXS-176](https://mariadb.atlassian.net/browse/MXS-176)|Missing dependencies in documentation| -|[MXS-181](https://mariadb.atlassian.net/browse/MXS-181)|Poor performance on TCP connection due to Nagle's algoritm| -|[MXS-212](https://mariadb.atlassian.net/browse/MXS-212)|Stopped services accept connections| -|[MXS-225](https://mariadb.atlassian.net/browse/MXS-225)|RPM Debug build packages have no debugging symbols| -|[MXS-227](https://mariadb.atlassian.net/browse/MXS-227)|Memory leak in Galera Monitor| + +| Key | Summary | +| --- | -------- | +| [MXS-24](https://mariadb.atlassian.net/browse/MXS-24) | bugzillaId-604: Module load path documentation issues ... | +| [MXS-40](https://mariadb.atlassian.net/browse/MXS-40) | Display logged in users | +| [MXS-113](https://mariadb.atlassian.net/browse/MXS-113) | MaxScale seems to fail if built against MariaDB 10.0 libraries | +| [MXS-116](https://mariadb.atlassian.net/browse/MXS-116) | Do not run maxscale as root. | +| [MXS-125](https://mariadb.atlassian.net/browse/MXS-125) | inconsistency in maxkeys/maxpassword output and parameters | +| [MXS-136](https://mariadb.atlassian.net/browse/MXS-136) | Check for MaxScale replication heartbeat table existence before creating | +| [MXS-137](https://mariadb.atlassian.net/browse/MXS-137) | cannot get sql for queries with length >= 0x80 | +| [MXS-139](https://mariadb.atlassian.net/browse/MXS-139) | Schemarouter authentication for wildcard grants fails without optimize_wildcard | +| [MXS-140](https://mariadb.atlassian.net/browse/MXS-140) | strip_db_esc does not work without auth_all_servers | +| [MXS-162](https://mariadb.atlassian.net/browse/MXS-162) | Fix Incorrect info in Configuration Guide | +| [MXS-163](https://mariadb.atlassian.net/browse/MXS-163) | Reload config leaks memory | +| [MXS-165](https://mariadb.atlassian.net/browse/MXS-165) | Concurrency issue while incrementing sessions in qlafilter | +| [MXS-166](https://mariadb.atlassian.net/browse/MXS-166) | Memory leak when creating a new event | +| [MXS-171](https://mariadb.atlassian.net/browse/MXS-171) | Allow reads on master for readwritesplit | +| [MXS-176](https://mariadb.atlassian.net/browse/MXS-176) | Missing dependencies in documentation | +| [MXS-181](https://mariadb.atlassian.net/browse/MXS-181) | Poor performance on TCP connection due to Nagle's algoritm | +| [MXS-212](https://mariadb.atlassian.net/browse/MXS-212) | Stopped services accept connections | +| [MXS-225](https://mariadb.atlassian.net/browse/MXS-225) | RPM Debug build packages have no debugging symbols | +| [MXS-227](https://mariadb.atlassian.net/browse/MXS-227) | Memory leak in Galera Monitor | From 1eb907ad38ef0e41265f90c61498df3e996f05f7 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 3 Jul 2015 17:08:32 +0300 Subject: [PATCH 230/273] Fixed common sharding functions referring to schemarouter. --- server/modules/routing/schemarouter/sharding_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/schemarouter/sharding_common.c b/server/modules/routing/schemarouter/sharding_common.c index d69b12d02..cf6bf2c12 100644 --- a/server/modules/routing/schemarouter/sharding_common.c +++ b/server/modules/routing/schemarouter/sharding_common.c @@ -46,7 +46,7 @@ bool extract_database(GWBUF* buf, char* str) tok = strtok_r(query," ;",&saved); if(tok == NULL || strcasecmp(tok,"use") != 0) { - skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet."); + skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet."); succp = false; goto retblock; } @@ -54,7 +54,7 @@ bool extract_database(GWBUF* buf, char* str) tok = strtok_r(NULL," ;",&saved); if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet."); + skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet."); succp = false; goto retblock; } From 7e704d2312bda792183e3a124eb3e78fb9a0a4bd Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 4 Jul 2015 07:13:14 +0300 Subject: [PATCH 231/273] Fixed module load failure referring to lib/maxscale/modules instead of lib/maxscale. --- server/core/service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/service.c b/server/core/service.c index 3dee01c91..4e36708f8 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -125,7 +125,7 @@ SERVICE *service; "Error : Unable to load %s module \"%s\".\n\t\t\t" " Ensure that lib%s.so exists in one of the " "following directories :\n\t\t\t " - "- %s/modules\n%s%s", + "- %s\n%s%s", MODULE_ROUTER, router, router, From 4f421cb69a89ba2f9e3f9bdd65897f8b7ec08abd Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 4 Jul 2015 07:30:23 +0300 Subject: [PATCH 232/273] Added missing --piddir option to testall maxscale invokation. --- cmake/testall.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/testall.cmake b/cmake/testall.cmake index 65c27fa5a..839029312 100644 --- a/cmake/testall.cmake +++ b/cmake/testall.cmake @@ -1,4 +1,4 @@ -execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -f ${CMAKE_BINARY_DIR}/maxscale.cnf --logdir=${CMAKE_BINARY_DIR}/ --datadir=${CMAKE_BINARY_DIR}/ --cachedir=${CMAKE_BINARY_DIR}/ &> ${CMAKE_BINARY_DIR}/maxscale.output" +execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -f ${CMAKE_BINARY_DIR}/maxscale.cnf --logdir=${CMAKE_BINARY_DIR}/ --datadir=${CMAKE_BINARY_DIR}/ --cachedir=${CMAKE_BINARY_DIR}/ --piddir=${CMAKE_BINARY_DIR}/ &> ${CMAKE_BINARY_DIR}/maxscale.output" OUTPUT_VARIABLE MAXSCALE_OUT) execute_process(COMMAND make test RESULT_VARIABLE RVAL) execute_process(COMMAND killall maxscale) From 03b5a634f44f78d494ec7e4ee1ffb3c5fdaf3359 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 4 Jul 2015 09:04:53 +0300 Subject: [PATCH 233/273] Fix to MXS-244: https://mariadb.atlassian.net/browse/MXS-244 Fixed buffers being only partially consumed if they contained more than one packet when a replu to a prepared statement is received. --- server/modules/routing/readwritesplit/readwritesplit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index b801d9aec..3c329f646 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -3787,7 +3787,7 @@ static GWBUF* sescmd_cursor_process_replies( dcb_close(bref->bref_dcb); *reconnect = true; if(replybuf) - gwbuf_consume(replybuf,gwbuf_length(replybuf)); + while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf)))); } } /** This is a response from the master and it is the "right" one. @@ -3830,7 +3830,7 @@ static GWBUF* sescmd_cursor_process_replies( skygw_log_write(LOGFILE_DEBUG,"Slave '%s' responded faster to a session command.", bref->bref_backend->backend_server->unique_name); if(replybuf) - gwbuf_free(replybuf); + while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf)))); return NULL; } From 5b75443744ef0a9b105efd8e40c017fd1ddeee44 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 4 Jul 2015 07:13:14 +0300 Subject: [PATCH 234/273] Fixed module load failure referring to lib/maxscale/modules instead of lib/maxscale. --- server/core/service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/service.c b/server/core/service.c index 3dee01c91..4e36708f8 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -125,7 +125,7 @@ SERVICE *service; "Error : Unable to load %s module \"%s\".\n\t\t\t" " Ensure that lib%s.so exists in one of the " "following directories :\n\t\t\t " - "- %s/modules\n%s%s", + "- %s\n%s%s", MODULE_ROUTER, router, router, From 309b8bba9789de0036a48848ba4c347c717e44ca Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 4 Jul 2015 07:30:23 +0300 Subject: [PATCH 235/273] Added missing --piddir option to testall maxscale invokation. --- cmake/testall.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/testall.cmake b/cmake/testall.cmake index 65c27fa5a..839029312 100644 --- a/cmake/testall.cmake +++ b/cmake/testall.cmake @@ -1,4 +1,4 @@ -execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -f ${CMAKE_BINARY_DIR}/maxscale.cnf --logdir=${CMAKE_BINARY_DIR}/ --datadir=${CMAKE_BINARY_DIR}/ --cachedir=${CMAKE_BINARY_DIR}/ &> ${CMAKE_BINARY_DIR}/maxscale.output" +execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -f ${CMAKE_BINARY_DIR}/maxscale.cnf --logdir=${CMAKE_BINARY_DIR}/ --datadir=${CMAKE_BINARY_DIR}/ --cachedir=${CMAKE_BINARY_DIR}/ --piddir=${CMAKE_BINARY_DIR}/ &> ${CMAKE_BINARY_DIR}/maxscale.output" OUTPUT_VARIABLE MAXSCALE_OUT) execute_process(COMMAND make test RESULT_VARIABLE RVAL) execute_process(COMMAND killall maxscale) From dc646bfbf16bf21978fedec789278379aca9f7c5 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 4 Jul 2015 09:04:53 +0300 Subject: [PATCH 236/273] Fix to MXS-244: https://mariadb.atlassian.net/browse/MXS-244 Fixed buffers being only partially consumed if they contained more than one packet when a replu to a prepared statement is received. --- server/modules/routing/readwritesplit/readwritesplit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index b801d9aec..3c329f646 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -3787,7 +3787,7 @@ static GWBUF* sescmd_cursor_process_replies( dcb_close(bref->bref_dcb); *reconnect = true; if(replybuf) - gwbuf_consume(replybuf,gwbuf_length(replybuf)); + while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf)))); } } /** This is a response from the master and it is the "right" one. @@ -3830,7 +3830,7 @@ static GWBUF* sescmd_cursor_process_replies( skygw_log_write(LOGFILE_DEBUG,"Slave '%s' responded faster to a session command.", bref->bref_backend->backend_server->unique_name); if(replybuf) - gwbuf_free(replybuf); + while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf)))); return NULL; } From 5b95e2b32e30b9b27155f1b68243f9265e023d3b Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 6 Jul 2015 10:38:03 +0300 Subject: [PATCH 237/273] Updated 1.2 release notes. --- .../MaxScale-1.2.0-Release-Notes.md | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index caf7e28c6..de4b7a461 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -27,8 +27,8 @@ A quick list of changes in installation directories and file names: ### Client side SSL encryption MaxScale now supports SSL/TLS encrypted connections to MaxScale. -### Monitor scripts -State changes in backend servers can now trigger the execution of a custom script. With this you can easily customize MaxScale's behavior. +### Launchable scripts +Now you can configure MaxScale monitor module to automatically launch a script when it detects change in the state of a backend server. The script can be any customer script defined by you to take diagnostic or reporting action. With this you can easily customize MaxScale's behavior. ## Bug fixes @@ -40,18 +40,58 @@ Here is a list of bugs fixed since the release of MaxScale 1.1.1. | [MXS-40](https://mariadb.atlassian.net/browse/MXS-40) | Display logged in users | | [MXS-113](https://mariadb.atlassian.net/browse/MXS-113) | MaxScale seems to fail if built against MariaDB 10.0 libraries | | [MXS-116](https://mariadb.atlassian.net/browse/MXS-116) | Do not run maxscale as root. | +| [MXS-117](https://mariadb.atlassian.net/browse/MXS-117) | Allow configuration of the log file directory | | [MXS-125](https://mariadb.atlassian.net/browse/MXS-125) | inconsistency in maxkeys/maxpassword output and parameters | +| [MXS-128](https://mariadb.atlassian.net/browse/MXS-128) | cyclic dependency utils -> log_manager -> utils | | [MXS-136](https://mariadb.atlassian.net/browse/MXS-136) | Check for MaxScale replication heartbeat table existence before creating | | [MXS-137](https://mariadb.atlassian.net/browse/MXS-137) | cannot get sql for queries with length >= 0x80 | | [MXS-139](https://mariadb.atlassian.net/browse/MXS-139) | Schemarouter authentication for wildcard grants fails without optimize_wildcard | | [MXS-140](https://mariadb.atlassian.net/browse/MXS-140) | strip_db_esc does not work without auth_all_servers | -| [MXS-162](https://mariadb.atlassian.net/browse/MXS-162) | Fix Incorrect info in Configuration Guide | -| [MXS-163](https://mariadb.atlassian.net/browse/MXS-163) | Reload config leaks memory | +| [MXS-162](https://mariadb.atlassian.net/browse/MXS-162) | Fix Incorrect info in Configuration Guide | | [MXS-165](https://mariadb.atlassian.net/browse/MXS-165) | Concurrency issue while incrementing sessions in qlafilter | | [MXS-166](https://mariadb.atlassian.net/browse/MXS-166) | Memory leak when creating a new event | | [MXS-171](https://mariadb.atlassian.net/browse/MXS-171) | Allow reads on master for readwritesplit | | [MXS-176](https://mariadb.atlassian.net/browse/MXS-176) | Missing dependencies in documentation | +| [MXS-180](https://mariadb.atlassian.net/browse/MXS-180) | MariaDB10 binlog router compatibilty | | [MXS-181](https://mariadb.atlassian.net/browse/MXS-181) | Poor performance on TCP connection due to Nagle's algoritm | +| [MXS-182](https://mariadb.atlassian.net/browse/MXS-182) | SHOW SLAVE STATUS and maxadmin "show services" for binlog router needs updated when used with MariaDB 10 Master | | [MXS-212](https://mariadb.atlassian.net/browse/MXS-212) | Stopped services accept connections | | [MXS-225](https://mariadb.atlassian.net/browse/MXS-225) | RPM Debug build packages have no debugging symbols | | [MXS-227](https://mariadb.atlassian.net/browse/MXS-227) | Memory leak in Galera Monitor | +| [MXS-244](https://mariadb.atlassian.net/browse/MXS-244) | Memory leak when using prepared statements without arguments | + +## Known Issues and Limitations + +There are a number bugs and known limitations within this version of MaxScale, the most serious of this are listed below. + +* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries. + +* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale. + +* LONGBLOB are currently not supported. + +* Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser. + +* The Database Firewall filter does not support multi-statements. Using them will result in an error being sent to the client. + +## Packaging + +Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide + +* CentOS/RedHat 5 + +* CentOS/RedHat 6 + +* CentOS/RedHat 7 + +* Debian 6 + +* Debian 7 + +* Ubuntu 12.04 LTS + +* Ubuntu 14.04 LTS + +* SuSE Linux Enterprise 11 + +* SuSE Linux Enterprise 12 From 1af73b706148a64a470f79be297791b2586dbdd3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 3 Jul 2015 17:08:32 +0300 Subject: [PATCH 238/273] Fixed common sharding functions referring to schemarouter. --- server/modules/routing/schemarouter/sharding_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/schemarouter/sharding_common.c b/server/modules/routing/schemarouter/sharding_common.c index d69b12d02..cf6bf2c12 100644 --- a/server/modules/routing/schemarouter/sharding_common.c +++ b/server/modules/routing/schemarouter/sharding_common.c @@ -46,7 +46,7 @@ bool extract_database(GWBUF* buf, char* str) tok = strtok_r(query," ;",&saved); if(tok == NULL || strcasecmp(tok,"use") != 0) { - skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet."); + skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet."); succp = false; goto retblock; } @@ -54,7 +54,7 @@ bool extract_database(GWBUF* buf, char* str) tok = strtok_r(NULL," ;",&saved); if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet."); + skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet."); succp = false; goto retblock; } From 5bdfa42aa7c961097933bc9961a81f61a39def5e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 06:31:52 +0300 Subject: [PATCH 239/273] Fixed Building from Source document. --- .../Building-MaxScale-from-Source-Code.md | 32 ++++--------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md index 95236ed7e..2d8f0661e 100644 --- a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md +++ b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md @@ -87,16 +87,13 @@ You will also need some version specific packages. #### Earlier versions of Ubuntu or Debian -For these, you will need to obtain the MariaDB embedded library. It has to be manually extracted from the tarball. But first ascertain what version of glibc is installed. Run the command: +For these, you will need to obtain the MariaDB embedded library. It has to be manually extracted from the tarballs at the MariaDB site. But first ascertain what version of glibc is installed. Run the command: ``` dpkg -l | grep libc6 ``` -which will show the version number. If the version is less than 2.14 you should obtain the library from: -[https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-x86_64/mariadb-5.5.41-linux-x86_64.tar.gz](https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-x86_64/mariadb-5.5.41-linux-x86_64.tar.gz). -Otherwise, from: -[https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-glibc_214-x86_64/mariadb-5.5.41-linux-glibc_214-x86_64.tar.gz](https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-glibc_214-x86_64/mariadb-5.5.41-linux-glibc_214-x86_64.tar.gz) +which will show the version number. For versions older than 2.14 you should obtain the library which supports GLIBC versions older than 2.14 and for newer versions, the library which supports newer GLIBC versions should be used. The suggested location for extracting the tarball is `/usr` so the operation can be done by the following commands: @@ -107,22 +104,6 @@ The suggested location for extracting the tarball is `/usr` so the operation can where /path/to/mariadb.library.tar.gz is replaced by the actual path and name of the downloaded tarball. -## OpenSUSE - -At the time this guide was written, the MariaDB development packages for OpenSUSE were broken and the build failed. - -The packages required are: - -``` -gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc_s1 perl -make libtool libopenssl-devel libaio libaio-devel -libedit-devel librabbitmq-devel - MariaDB-devel MariaDB-client MariaDB-server -``` - -If zypper ask which MariaDB client should be installed `MariaDB-client` or `mariadb-client` - please select `MariaDB-client`. This is the package provided by the MariaDB repository. - # Obtaining the MaxScale Source Code Now clone the GitHub project to your machine either via the web interface, your favorite graphical interface or the git command line @@ -152,16 +133,15 @@ wipe the build directory clean without the danger of deleting important files wh something goes wrong. Building 'out-of-source' also allows you to have multiple configurations of MaxScale at the same time. -The default values that CMake uses can be found in the 'macros.cmake' file. -If you wish to change these, edit the 'macros.cmake' file or define the -variables manually at configuration time. +The default values that MaxScale uses for CMake can be found in the 'macros.cmake' file under the `cmake` folder. +If you wish to change these, edit the 'macros.cmake' file or define the variables manually at configuration time. To display all CMake variables with their descriptions: ``` cmake .. -LH ``` -This is a useful command if you have your libraries installed in non-standard locations. +This is a useful command if you have your libraries installed in non-standard locations and need to provide them manually. When you are ready to run cmake, provide the following command: @@ -241,7 +221,7 @@ $ make install This will result in an installation being created which is identical to that which would be achieved by installing the binary package. -By default, MaxScale installs to `/usr/local/mariadb-maxscale` and places init.d scripts and ldconfig files into their folders. Change the `CMAKE_INSTALL_PREFIX` variable to your desired installation directory and set `WITH_SCRIPTS=N` to prevent the init.d script and ldconfig file installation. +When building from source, MaxScale installs to `/usr/local/` and places init.d scripts and ldconfig files into their folders. Change the `CMAKE_INSTALL_PREFIX` variable to your desired installation directory and set `WITH_SCRIPTS=N` to prevent the init.d script and ldconfig file installation. Other useful targets for Make are `documentation`, which generates the Doxygen documentation, and `uninstall` which uninstall MaxScale binaries after an install. From 9d5d6da865d11a5daf391ad0562f0a7472bb2259 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 06:33:49 +0300 Subject: [PATCH 240/273] Removed Fedora references from the source building documentation. --- .../Building-MaxScale-from-Source-Code.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md index 2d8f0661e..96147c0f1 100644 --- a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md +++ b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md @@ -22,9 +22,9 @@ After following the instructions on that site you should have a working MariaDB The full list of dependencies for the most common distributions is provided in this section. If your system is not listed here, MaxScale building isn't guaranteed to be compatible but might still be successful. -## RHEL, CentOS and Fedora +## RHEL and CentOS -You will need to install all of the following packages for all versions of RHEL, CentOS and Fedora. +You will need to install all of the following packages for all versions of RHEL and CentOS. ``` gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc perl make libtool @@ -39,7 +39,7 @@ rpm-build There are also some version specific packages you need to install. -#### RHEL 6, 7, CentOS 6, 7, Fedora: +#### RHEL 6, 7, CentOS 6, 7: ``` libedit-devel @@ -51,17 +51,11 @@ libedit-devel mariadb-devel mariadb-embedded-devel ``` -#### RHEL 5, 6, CentOS 5, 6, Fedora 19, 20 +#### RHEL 5, 6, CentOS 5, 6 ``` MariaDB-devel MariaDB-server ``` -#### Fedora 19, 20 - -``` -systemtap-sdt-devel -``` - ## Ubuntu and Debian These packages are required on all versions of Ubuntu and Debian. From c2e51f2e983df1bbd47f9253c7106a2041545a8a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 08:16:29 +0300 Subject: [PATCH 241/273] Fix to MXS-223: https://mariadb.atlassian.net/browse/MXS-223 Added a temporary buffer to prevent losing the pointer to the cloned buffer when dcb->func.write fails. --- .../routing/readwritesplit/readwritesplit.c | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 3c329f646..be2fb68df 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -4011,7 +4011,7 @@ static bool execute_sescmd_in_backend( bool succp; int rc = 0; sescmd_cursor_t* scur; - + GWBUF* buf; if(backend_ref == NULL) { skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_in_backend. (%s:%d)",__FILE__,__LINE__); @@ -4048,27 +4048,9 @@ static bool execute_sescmd_in_backend( /** Cursor is left active when function returns. */ sescmd_cursor_set_active(scur, true); } -#if defined(SS_DEBUG) - LOGIF(LT, tracelog_routed_query(scur->scmd_cur_rses, - "execute_sescmd_in_backend", - backend_ref, - sescmd_cursor_clone_querybuf(scur))); - { - GWBUF* tmpbuf = sescmd_cursor_clone_querybuf(scur); - uint8_t* ptr = GWBUF_DATA(tmpbuf); - unsigned char cmd = MYSQL_GET_COMMAND(ptr); - - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [execute_sescmd_in_backend] Just before write, fd " - "%d : cmd %s.", - pthread_self(), - dcb->fd, - STRPACKETTYPE(cmd)))); - gwbuf_free(tmpbuf); - } -#endif /*< SS_DEBUG */ + buf = sescmd_cursor_clone_querybuf(scur); + switch (scur->scmd_cur_cmd->my_sescmd_packet_type) { case MYSQL_COM_CHANGE_USER: /** This makes it possible to handle replies correctly */ @@ -4077,7 +4059,7 @@ static bool execute_sescmd_in_backend( dcb, NULL, dcb->session, - sescmd_cursor_clone_querybuf(scur)); + buf); break; case MYSQL_COM_INIT_DB: @@ -4103,10 +4085,11 @@ static bool execute_sescmd_in_backend( * Mark session command buffer, it triggers writing * MySQL command to protocol */ + gwbuf_set_type(scur->scmd_cur_cmd->my_sescmd_buf, GWBUF_TYPE_SESCMD); rc = dcb->func.write( dcb, - sescmd_cursor_clone_querybuf(scur)); + buf); break; } @@ -4116,6 +4099,7 @@ static bool execute_sescmd_in_backend( } else { + while((buf = GWBUF_CONSUME_ALL(buf)) != NULL); succp = false; } return_succp: From fb09cbf1000ccb90062c2c316d4d9fc43d11dc42 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 13:15:23 +0300 Subject: [PATCH 242/273] Fixed queries being routed when the session is already closing. --- server/core/buffer.c | 1 + server/modules/protocol/mysql_backend.c | 11 +- server/modules/protocol/mysql_client.c | 140 ++++++++++-------- server/modules/routing/readconnroute.c | 12 +- .../routing/readwritesplit/readwritesplit.c | 2 +- 5 files changed, 89 insertions(+), 77 deletions(-) diff --git a/server/core/buffer.c b/server/core/buffer.c index 4c788cdd2..c3adacd34 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -197,6 +197,7 @@ GWBUF *rval; rval->gwbuf_info = buf->gwbuf_info; rval->gwbuf_bufobj = buf->gwbuf_bufobj; rval->tail = rval; + rval->next = NULL; CHK_GWBUF(rval); return rval; } diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 477ca4cf0..3a5e8f52b 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -1080,7 +1080,7 @@ gw_backend_hangup(DCB *dcb) len = sizeof(error); if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) { - if (error != 0) + if (error != 0 && ses_state != SESSION_STATE_STOPPING) { strerror_r(error, buf, 100); LOGIF(LE, (skygw_log_write_flush( @@ -1094,9 +1094,12 @@ gw_backend_hangup(DCB *dcb) goto retblock; } #if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend hangup error handling."))); + if(ses_state != SESSION_STATE_STOPPING) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Backend hangup error handling."))); + } #endif router->handleError(router_instance, diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index c3e463139..bab5971a8 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1092,7 +1092,8 @@ int gw_read_client_event( case MYSQL_IDLE: { uint8_t* payload = NULL; - + session_state_t ses_state; + session = dcb->session; ss_dassert(session!= NULL); @@ -1100,93 +1101,106 @@ int gw_read_client_event( { CHK_SESSION(session); } + spinlock_acquire(&session->ses_lock); + ses_state = session->state; + spinlock_release(&session->ses_lock); /* Now, we are assuming in the first buffer there is * the information form mysql command */ payload = GWBUF_DATA(read_buffer); - /** Route COM_QUIT to backend */ - if (MYSQL_IS_COM_QUIT(payload)) - { + if(ses_state == SESSION_STATE_ROUTER_READY) + { + /** Route COM_QUIT to backend */ + if (MYSQL_IS_COM_QUIT(payload)) + { /** - * Sends COM_QUIT packets since buffer is already - * created. A BREF_CLOSED flag is set so dcb_close won't - * send redundant COM_QUIT. - */ + * Sends COM_QUIT packets since buffer is already + * created. A BREF_CLOSED flag is set so dcb_close won't + * send redundant COM_QUIT. + */ SESSION_ROUTE_QUERY(session, read_buffer); /** - * Close router session which causes closing of backends. - */ + * Close router session which causes closing of backends. + */ dcb_close(dcb); - } - else - { + } + else + { /** Reset error handler when routing of the new query begins */ router->handleError(NULL, NULL, NULL, dcb, ERRACT_RESET, NULL); if (stmt_input) { - /** - * Feed each statement completely and separately - * to router. - */ - rc = route_by_statement(session, &read_buffer); - - if (read_buffer != NULL) - { - /** add incomplete mysql packet to read queue */ - dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); - } + /** + * Feed each statement completely and separately + * to router. + */ + rc = route_by_statement(session, &read_buffer); + + if (read_buffer != NULL) + { + /** add incomplete mysql packet to read queue */ + dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); + } } else { - /** Feed whole packet to router */ - rc = SESSION_ROUTE_QUERY(session, read_buffer); + /** Feed whole packet to router */ + rc = SESSION_ROUTE_QUERY(session, read_buffer); } - + /** Routing succeed */ if (rc) { - rc = 0; /**< here '0' means success */ + rc = 0; /**< here '0' means success */ } else { - bool succp; - GWBUF* errbuf; - /** - * Create error to be sent to client if session - * can't be continued. - */ - errbuf = mysql_create_custom_error( - 1, - 0, - "Routing failed. Session is closed."); - /** - * Ensure that there are enough backends - * available. - */ - router->handleError( - router_instance, - session->router_session, - errbuf, - dcb, - ERRACT_NEW_CONNECTION, - &succp); - gwbuf_free(errbuf); - /** - * If there are not enough backends close - * session - */ - if (!succp) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Routing the query failed. " - "Session will be closed."))); + bool succp; + GWBUF* errbuf; + /** + * Create error to be sent to client if session + * can't be continued. + */ + errbuf = mysql_create_custom_error( + 1, + 0, + "Routing failed. Session is closed."); + /** + * Ensure that there are enough backends + * available. + */ + router->handleError( + router_instance, + session->router_session, + errbuf, + dcb, + ERRACT_NEW_CONNECTION, + &succp); + gwbuf_free(errbuf); + /** + * If there are not enough backends close + * session + */ + if (!succp) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Routing the query failed. " + "Session will be closed."))); - dcb_close(dcb); - } + dcb_close(dcb); + } } - } + } + } + else + { + skygw_log_write_flush(LT,"Session received a query in state %s", + STRSESSIONSTATE(ses_state)); + while((read_buffer = GWBUF_CONSUME_ALL(read_buffer)) != NULL); + goto return_rc; + } goto return_rc; } /* MYSQL_IDLE */ break; diff --git a/server/modules/routing/readconnroute.c b/server/modules/routing/readconnroute.c index b4b080c16..48c78b2e2 100644 --- a/server/modules/routing/readconnroute.c +++ b/server/modules/routing/readconnroute.c @@ -723,16 +723,10 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue) SERVER_IS_DOWN(router_cli_ses->backend->server)) { LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, + LOGFILE_TRACE|LOGFILE_ERROR, "Error : Failed to route MySQL command %d to backend " - "server.", - mysql_command))); - skygw_log_write( - LOGFILE_ERROR, - "Error : Failed to route MySQL command %d to backend " - "server %s.", - mysql_command, - router_cli_ses->backend->server->unique_name); + "server.%s", + mysql_command,rses_is_closed ? " Session is closed." : ""))); rc = 0; goto return_rc; diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index be2fb68df..b35a674c4 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -3915,7 +3915,7 @@ static GWBUF* sescmd_cursor_clone_querybuf( } ss_dassert(scur->scmd_cur_cmd != NULL); - buf = gwbuf_clone(scur->scmd_cur_cmd->my_sescmd_buf); + buf = gwbuf_clone_all(scur->scmd_cur_cmd->my_sescmd_buf); CHK_GWBUF(buf); return buf; From f7e165612f6d20d4b6f790c33b76432ccce28b05 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 14:40:13 +0300 Subject: [PATCH 243/273] Added Upgrading to 1.2 document. --- Documentation/Upgrading-To-MaxScale-1.2.0.md | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/Upgrading-To-MaxScale-1.2.0.md diff --git a/Documentation/Upgrading-To-MaxScale-1.2.0.md b/Documentation/Upgrading-To-MaxScale-1.2.0.md new file mode 100644 index 000000000..a48750076 --- /dev/null +++ b/Documentation/Upgrading-To-MaxScale-1.2.0.md @@ -0,0 +1,24 @@ +# Upgrading MaxScale from 1.1 to 1.2 + +This document describes upgrading MaxScale from version 1.1.1 to 1.2 and the major differences in the new version compared to the old version. The major changes can be found in the `Changelog.txt` file in the installation directory and the official release notes in the `ReleaseNotes.txt` file. + +## Installation + +Before starting the upgrade, we recommend you back up your configuration, log and binary log files in `/usr/local/mariadb-maxscale/`. + +Upgrading MaxScale will copy the `MaxScale.cnf` file in `/usr/local/mariadb-maxscale/etc/` to `/etc/` and renamed to `maxscale.cnf`. Binary log files are not automatically copied and should be manually moved from `/usr/local/mariadb-maxscale` to `/var/lib/maxscale/`. + +## File location changes + +MaxScale 1.2 follows the [FHS-standard](http://www.pathname.com/fhs/) and installs to `/usr/` and `/var/` subfolders. Here are the major changes and file locations. + +* Configuration files are located in `/etc/` and use lowercase letters: `/etc/maxscale.cnf` +* Binary files are in `/usr/bin/` +* Libraries and modules are in `/usr/lib64/maxscale/`. If you are using custom modules, please make sure they are in this directory before starting MaxScale. +* Log files are in the `var/log/maxscale/` folder +* MaxScale's PID file is located in `/var/run/maxscale/maxscale.pid` +* Data files and other persistent files are in `/var/lib/maxscale/` + +## Running MaxScale without root permissions + +MaxScale can run as a non-root user with the 1.2 version. RPM and DEB packages install the `maxscale` user and `maxscale` group which are used by the init scripts and systemd configuration files. If you are installing from a binary tarball, you can run the `postinst` script included in it to manually create these groups. From 309e6881688e20a79d0ed054c461f6b30e7530c6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 15:03:23 +0300 Subject: [PATCH 244/273] Added link to 1.2 upgrade document in Documentation-Contents.md --- Documentation/Documentation-Contents.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index fc51d83ec..f19797d55 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -19,7 +19,8 @@ ## Upgrading MaxScale -- [Upgrading MaxScale to 1.1.0](Upgrading-To-MaxScale-1.1.0.md) +- [Upgrading MaxScale from 1.1.1 to 1.2.0](Upgrading-To-MaxScale-1.2.0.md) +- [Upgrading MaxScale from 1.0.5 to 1.1.0](Upgrading-To-MaxScale-1.1.0.md) ## Reference From 082a78ac3ded5947f2d0af6ca1a6ad7d04099eb4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 16:02:29 +0300 Subject: [PATCH 245/273] Updated release notes. --- .../MaxScale-1.2.0-Release-Notes.md | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index de4b7a461..c1252a932 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -34,31 +34,30 @@ Now you can configure MaxScale monitor module to automatically launch a script w Here is a list of bugs fixed since the release of MaxScale 1.1.1. -| Key | Summary | -| --- | -------- | -| [MXS-24](https://mariadb.atlassian.net/browse/MXS-24) | bugzillaId-604: Module load path documentation issues ... | -| [MXS-40](https://mariadb.atlassian.net/browse/MXS-40) | Display logged in users | -| [MXS-113](https://mariadb.atlassian.net/browse/MXS-113) | MaxScale seems to fail if built against MariaDB 10.0 libraries | -| [MXS-116](https://mariadb.atlassian.net/browse/MXS-116) | Do not run maxscale as root. | -| [MXS-117](https://mariadb.atlassian.net/browse/MXS-117) | Allow configuration of the log file directory | -| [MXS-125](https://mariadb.atlassian.net/browse/MXS-125) | inconsistency in maxkeys/maxpassword output and parameters | -| [MXS-128](https://mariadb.atlassian.net/browse/MXS-128) | cyclic dependency utils -> log_manager -> utils | -| [MXS-136](https://mariadb.atlassian.net/browse/MXS-136) | Check for MaxScale replication heartbeat table existence before creating | -| [MXS-137](https://mariadb.atlassian.net/browse/MXS-137) | cannot get sql for queries with length >= 0x80 | -| [MXS-139](https://mariadb.atlassian.net/browse/MXS-139) | Schemarouter authentication for wildcard grants fails without optimize_wildcard | -| [MXS-140](https://mariadb.atlassian.net/browse/MXS-140) | strip_db_esc does not work without auth_all_servers | -| [MXS-162](https://mariadb.atlassian.net/browse/MXS-162) | Fix Incorrect info in Configuration Guide | -| [MXS-165](https://mariadb.atlassian.net/browse/MXS-165) | Concurrency issue while incrementing sessions in qlafilter | -| [MXS-166](https://mariadb.atlassian.net/browse/MXS-166) | Memory leak when creating a new event | -| [MXS-171](https://mariadb.atlassian.net/browse/MXS-171) | Allow reads on master for readwritesplit | -| [MXS-176](https://mariadb.atlassian.net/browse/MXS-176) | Missing dependencies in documentation | -| [MXS-180](https://mariadb.atlassian.net/browse/MXS-180) | MariaDB10 binlog router compatibilty | -| [MXS-181](https://mariadb.atlassian.net/browse/MXS-181) | Poor performance on TCP connection due to Nagle's algoritm | -| [MXS-182](https://mariadb.atlassian.net/browse/MXS-182) | SHOW SLAVE STATUS and maxadmin "show services" for binlog router needs updated when used with MariaDB 10 Master | -| [MXS-212](https://mariadb.atlassian.net/browse/MXS-212) | Stopped services accept connections | -| [MXS-225](https://mariadb.atlassian.net/browse/MXS-225) | RPM Debug build packages have no debugging symbols | -| [MXS-227](https://mariadb.atlassian.net/browse/MXS-227) | Memory leak in Galera Monitor | -| [MXS-244](https://mariadb.atlassian.net/browse/MXS-244) | Memory leak when using prepared statements without arguments | + * [MXS-24](https://mariadb.atlassian.net/browse/MXS-24): bugzillaId-604: Module load path documentation issues ... + * [MXS-40](https://mariadb.atlassian.net/browse/MXS-40): Display logged in users + * [MXS-113](https://mariadb.atlassian.net/browse/MXS-113): MaxScale seems to fail if built against MariaDB 10.0 libraries + * [MXS-116](https://mariadb.atlassian.net/browse/MXS-116): Do not run maxscale as root. + * [MXS-117](https://mariadb.atlassian.net/browse/MXS-117): Allow configuration of the log file directory + * [MXS-125](https://mariadb.atlassian.net/browse/MXS-125): inconsistency in maxkeys/maxpassword output and parameters + * [MXS-128](https://mariadb.atlassian.net/browse/MXS-128): cyclic dependency utils -> log_manager -> utils + * [MXS-136](https://mariadb.atlassian.net/browse/MXS-136): Check for MaxScale replication heartbeat table existence before creating + * [MXS-137](https://mariadb.atlassian.net/browse/MXS-137): cannot get sql for queries with length >= 0x80 + * [MXS-139](https://mariadb.atlassian.net/browse/MXS-139): Schemarouter authentication for wildcard grants fails without optimize_wildcard + * [MXS-140](https://mariadb.atlassian.net/browse/MXS-140): strip_db_esc does not work without auth_all_servers + * [MXS-162](https://mariadb.atlassian.net/browse/MXS-162): Fix Incorrect info in Configuration Guide + * [MXS-165](https://mariadb.atlassian.net/browse/MXS-165): Concurrency issue while incrementing sessions in qlafilter + * [MXS-166](https://mariadb.atlassian.net/browse/MXS-166): Memory leak when creating a new event + * [MXS-171](https://mariadb.atlassian.net/browse/MXS-171): Allow reads on master for readwritesplit + * [MXS-176](https://mariadb.atlassian.net/browse/MXS-176): Missing dependencies in documentation + * [MXS-179](https://mariadb.atlassian.net/browse/MXS-179): Keep configuration changes in synch across MaxScale Mate Nodes + * [MXS-180](https://mariadb.atlassian.net/browse/MXS-180): MariaDB10 binlog router compatibilty + * [MXS-181](https://mariadb.atlassian.net/browse/MXS-181): Poor performance on TCP connection due to Nagle's algoritm + * [MXS-182](https://mariadb.atlassian.net/browse/MXS-182): SHOW SLAVE STATUS and maxadmin "show services" for binlog router needs updated when used with MariaDB 10 Master + * [MXS-212](https://mariadb.atlassian.net/browse/MXS-212): Stopped services accept connections + * [MXS-225](https://mariadb.atlassian.net/browse/MXS-225): RPM Debug build packages have no debugging symbols + * [MXS-227](https://mariadb.atlassian.net/browse/MXS-227): Memory leak in Galera Monitor + * [MXS-244](https://mariadb.atlassian.net/browse/MXS-244): Memory leak when using prepared statements without arguments ## Known Issues and Limitations From 72844c9e138ebd6c92785df1ca027f8a21e7af1d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 16:22:26 +0300 Subject: [PATCH 246/273] Added lsyncd guide to release notes. --- Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index c1252a932..43560ddff 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -30,6 +30,9 @@ MaxScale now supports SSL/TLS encrypted connections to MaxScale. ### Launchable scripts Now you can configure MaxScale monitor module to automatically launch a script when it detects change in the state of a backend server. The script can be any customer script defined by you to take diagnostic or reporting action. With this you can easily customize MaxScale's behavior. +### Lsyncd configuration guide +A new tutorial has beed added which helps you keep MaxScale's configuration files in sync across multiple hosts. This allows for easier HA setups with MaxScale and guarantees up-to-date configuration files on all nodes. The tutorial can be found [here](../Reference/MaxScale-HA-with-lsyncd.md). + ## Bug fixes Here is a list of bugs fixed since the release of MaxScale 1.1.1. From 058f49eb88aa25031ff6b76f898f8865fa5f8e41 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 7 Jul 2015 20:14:57 +0300 Subject: [PATCH 247/273] Added error message about MySQL versions lower than 5.5 and monitors without the 'mysql51_replication' enabled. --- Documentation/monitors/MySQL-Monitor.md | 4 ++-- server/core/config.c | 1 + server/modules/monitor/mysql_mon.c | 26 ++++++++++++++++++++----- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/monitors/MySQL-Monitor.md index 6c7e70500..e53b7c336 100644 --- a/Documentation/monitors/MySQL-Monitor.md +++ b/Documentation/monitors/MySQL-Monitor.md @@ -98,12 +98,12 @@ A list of event names which cause the script to be executed. If this option is n events=master_down,slave_down ``` -### `mysql51_only` +### `mysql51_replication` Enable support for MySQL 5.1 replication monitoring. This is needed if a MySQL server older than 5.5 is used as a slave in replication. ``` -mysql51_only=true +mysql51_replication=true ``` ## Script events diff --git a/server/core/config.c b/server/core/config.c index 58dd42b7a..e6034445d 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -2039,6 +2039,7 @@ static char *monitor_params[] = "passwd", "script", "events", + "mysql51_replication", "monitor_interval", "detect_replication_lag", "detect_stale_master", diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index b5bbed4dd..8cc78340f 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -81,6 +81,7 @@ static void set_master_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *); static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *); static int add_slave_to_master(long *, int, long); bool isMySQLEvent(monitor_event_t event); +static bool report_version_err = true; static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, @@ -200,7 +201,7 @@ startMonitor(void *arg, void* opt) else have_events = true; } - else if(!strcmp(params->name,"mysql51_only")) + else if(!strcmp(params->name,"mysql51_replication")) { handle->mysql51_replication = config_truth_value(params->value); } @@ -539,7 +540,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) if(mysql_field_count(database->con) < 4) { mysql_free_result(result); - skygw_log_write(LE,"Error: \"SHOW SLAVE HOSTS\" " + skygw_log_write_flush(LE,"Error: \"SHOW SLAVE HOSTS\" " "returned less than the expected amount of columns. Expected 4 columns." " MySQL Version: %s",version_str); return NULL; @@ -553,6 +554,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) /* get Slave_IO_Running and Slave_SQL_Running values*/ database->server->slaves[nslaves] = atol(row[0]); nslaves++; + LOGIF(LD,(skygw_log_write_flush(LD,"Found slave at %s:%d",row[1],row[2]))); } database->server->slaves[nslaves] = 0; } @@ -564,6 +566,10 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) /* Set the Slave Role */ if (ismaster) { + LOGIF(LD,(skygw_log_write(LD,"Master server found at %s:%d with %d slaves", + database->server->name, + database->server->port, + nslaves))); monitor_set_pending_status(database, SERVER_MASTER); if(rval == NULL || rval->server->node_id > database->server->node_id) rval = database; @@ -574,7 +580,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) database = mon->databases; - /** */ + /** Set master server IDs */ while(database) { ptr = mon->databases; @@ -591,7 +597,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) } ptr = ptr->next; } - if(database->server->master_id <= 0) + if(database->server->master_id <= 0 && SERVER_IS_SLAVE(database->server)) { monitor_set_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); } @@ -733,7 +739,17 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) } else { - monitor_mysql51_db(database); + if(handle->mysql51_replication) + { + monitor_mysql51_db(database); + } + else if(report_version_err) + { + report_version_err = false; + skygw_log_write(LE,"Error: MySQL version is lower than 5.5 and 'mysql51_replication' option is not enabled," + " replication tree cannot be resolved. To enable MySQL 5.1 replication detection, " + "add 'mysql51_replication=true' to the monitor section."); + } } } From b73aedd0ca9e36474ac6407aeff6fc4ef27b7cb0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 8 Jul 2015 10:05:15 +0300 Subject: [PATCH 248/273] Fixed debug log not working. --- log_manager/log_manager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index a87157be7..79c2ace30 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1457,7 +1457,7 @@ int skygw_log_write( * Write log string to buffer and add to file write list. */ - for (i = LOGFILE_FIRST; i Date: Wed, 8 Jul 2015 12:42:48 +0300 Subject: [PATCH 249/273] Added reference to hint document to rwsplit docs. --- Documentation/routers/ReadWriteSplit.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md index e58367789..7fb6d5cf0 100644 --- a/Documentation/routers/ReadWriteSplit.md +++ b/Documentation/routers/ReadWriteSplit.md @@ -100,6 +100,10 @@ disable_slave_recovery=true master_accept_reads=true ``` +### Routing hints + +The readwritesplit router supports routing hints. For a detailed guide on hint syntax and functionality, please see [this](../Reference/Hint-Syntax.md) document. + ## Limitations In Master-Slave replication cluster also read-only queries are routed to master too in the following situations: From af2ce0f4f1484cc1367ff86473cbd9824da17baf Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 8 Jul 2015 12:45:48 +0300 Subject: [PATCH 250/273] Fix to MXS-252: https://mariadb.atlassian.net/browse/MXS-252 Added missing buffer length parameter. --- server/modules/routing/schemarouter/shardrouter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/routing/schemarouter/shardrouter.c b/server/modules/routing/schemarouter/shardrouter.c index 94ab35974..c4148a237 100644 --- a/server/modules/routing/schemarouter/shardrouter.c +++ b/server/modules/routing/schemarouter/shardrouter.c @@ -1701,7 +1701,7 @@ routeQuery(ROUTER* instance, querybuf))) { extract_database(querybuf,db); - snprintf(errbuf,"Unknown database: %s",db); + snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db); create_error_reply(errbuf,router_cli_ses->replydcb); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, From b0e98bc420e97d71b95e2cafa4ed34f31bc2dff3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 9 Jul 2015 14:41:23 +0300 Subject: [PATCH 251/273] Added template cnf file to /etc and renamed folders. --- CMakeLists.txt | 3 ++ .../Database-Firewall-Filter.md | 0 .../Query-Log-All-Filter.md | 0 .../RabbitMQ-Consumer-Client.md | 0 .../{filters => Filters}/RabbitMQ-Filter.md | 0 .../{filters => Filters}/Regex-Filter.md | 0 .../{filters => Filters}/Tee-Filter.md | 0 .../{filters => Filters}/Top-N-Filter.md | 0 .../{monitors => Monitors}/Galera-Monitor.md | 0 .../{monitors => Monitors}/MM-Monitor.md | 0 .../{monitors => Monitors}/MySQL-Monitor.md | 0 .../NDB-Cluster-Monitor.md | 0 .../{routers => Routers}/ReadConnRoute.md | 0 .../{routers => Routers}/ReadWriteSplit.md | 0 .../{routers => Routers}/SchemaRouter.md | 0 cmake/macros.cmake | 3 ++ server/maxscale_template.cnf | 41 ++++++++++++------- 17 files changed, 33 insertions(+), 14 deletions(-) rename Documentation/{filters => Filters}/Database-Firewall-Filter.md (100%) rename Documentation/{filters => Filters}/Query-Log-All-Filter.md (100%) rename Documentation/{filters => Filters}/RabbitMQ-Consumer-Client.md (100%) rename Documentation/{filters => Filters}/RabbitMQ-Filter.md (100%) rename Documentation/{filters => Filters}/Regex-Filter.md (100%) rename Documentation/{filters => Filters}/Tee-Filter.md (100%) rename Documentation/{filters => Filters}/Top-N-Filter.md (100%) rename Documentation/{monitors => Monitors}/Galera-Monitor.md (100%) rename Documentation/{monitors => Monitors}/MM-Monitor.md (100%) rename Documentation/{monitors => Monitors}/MySQL-Monitor.md (100%) rename Documentation/{monitors => Monitors}/NDB-Cluster-Monitor.md (100%) rename Documentation/{routers => Routers}/ReadConnRoute.md (100%) rename Documentation/{routers => Routers}/ReadWriteSplit.md (100%) rename Documentation/{routers => Routers}/SchemaRouter.md (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62abd54a7..ef21fa1d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,9 @@ install(FILES ${CMAKE_BINARY_DIR}/Changelog.txt DESTINATION ${MAXSCALE_SHAREDIR} install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale110.txt DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES server/maxscale_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) +if(WITH_MAXSCALE_CNF) + install(FILES server/maxscale_template.cnf DESTINATION ${MAXSCALE_CONFDIR} RENAME maxscale.cnf) +endif() install(FILES server/maxscale_binlogserver_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_VARDIR}/lib/maxscale PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/Documentation/filters/Database-Firewall-Filter.md b/Documentation/Filters/Database-Firewall-Filter.md similarity index 100% rename from Documentation/filters/Database-Firewall-Filter.md rename to Documentation/Filters/Database-Firewall-Filter.md diff --git a/Documentation/filters/Query-Log-All-Filter.md b/Documentation/Filters/Query-Log-All-Filter.md similarity index 100% rename from Documentation/filters/Query-Log-All-Filter.md rename to Documentation/Filters/Query-Log-All-Filter.md diff --git a/Documentation/filters/RabbitMQ-Consumer-Client.md b/Documentation/Filters/RabbitMQ-Consumer-Client.md similarity index 100% rename from Documentation/filters/RabbitMQ-Consumer-Client.md rename to Documentation/Filters/RabbitMQ-Consumer-Client.md diff --git a/Documentation/filters/RabbitMQ-Filter.md b/Documentation/Filters/RabbitMQ-Filter.md similarity index 100% rename from Documentation/filters/RabbitMQ-Filter.md rename to Documentation/Filters/RabbitMQ-Filter.md diff --git a/Documentation/filters/Regex-Filter.md b/Documentation/Filters/Regex-Filter.md similarity index 100% rename from Documentation/filters/Regex-Filter.md rename to Documentation/Filters/Regex-Filter.md diff --git a/Documentation/filters/Tee-Filter.md b/Documentation/Filters/Tee-Filter.md similarity index 100% rename from Documentation/filters/Tee-Filter.md rename to Documentation/Filters/Tee-Filter.md diff --git a/Documentation/filters/Top-N-Filter.md b/Documentation/Filters/Top-N-Filter.md similarity index 100% rename from Documentation/filters/Top-N-Filter.md rename to Documentation/Filters/Top-N-Filter.md diff --git a/Documentation/monitors/Galera-Monitor.md b/Documentation/Monitors/Galera-Monitor.md similarity index 100% rename from Documentation/monitors/Galera-Monitor.md rename to Documentation/Monitors/Galera-Monitor.md diff --git a/Documentation/monitors/MM-Monitor.md b/Documentation/Monitors/MM-Monitor.md similarity index 100% rename from Documentation/monitors/MM-Monitor.md rename to Documentation/Monitors/MM-Monitor.md diff --git a/Documentation/monitors/MySQL-Monitor.md b/Documentation/Monitors/MySQL-Monitor.md similarity index 100% rename from Documentation/monitors/MySQL-Monitor.md rename to Documentation/Monitors/MySQL-Monitor.md diff --git a/Documentation/monitors/NDB-Cluster-Monitor.md b/Documentation/Monitors/NDB-Cluster-Monitor.md similarity index 100% rename from Documentation/monitors/NDB-Cluster-Monitor.md rename to Documentation/Monitors/NDB-Cluster-Monitor.md diff --git a/Documentation/routers/ReadConnRoute.md b/Documentation/Routers/ReadConnRoute.md similarity index 100% rename from Documentation/routers/ReadConnRoute.md rename to Documentation/Routers/ReadConnRoute.md diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/Routers/ReadWriteSplit.md similarity index 100% rename from Documentation/routers/ReadWriteSplit.md rename to Documentation/Routers/ReadWriteSplit.md diff --git a/Documentation/routers/SchemaRouter.md b/Documentation/Routers/SchemaRouter.md similarity index 100% rename from Documentation/routers/SchemaRouter.md rename to Documentation/Routers/SchemaRouter.md diff --git a/cmake/macros.cmake b/cmake/macros.cmake index d7921f1c4..49f4096dc 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -23,6 +23,9 @@ macro(set_variables) # Use C99 set(USE_C99 FALSE CACHE BOOL "Use C99 standard") + # Install the template maxscale.cnf file + set(WITH_MAXSCALE_CNF TRUE CACHE BOOL "Install the template maxscale.cnf file") + # hostname or IP address of MaxScale's host set(TEST_HOST "127.0.0.1" CACHE STRING "hostname or IP address of MaxScale's host") diff --git a/server/maxscale_template.cnf b/server/maxscale_template.cnf index feec5695d..0928a750d 100644 --- a/server/maxscale_template.cnf +++ b/server/maxscale_template.cnf @@ -128,6 +128,10 @@ module=regexfilter match=fetch replace=select +[hint] +type=filter +module=hintfilter + ## A series of service definition # @@ -218,11 +222,21 @@ router=readwritesplit servers=server1,server2,server3 user=myuser passwd=mypwd -#use_sql_variables_in= -#max_slave_connections=100% +max_slave_connections=100% +#use_sql_variables_in=master #max_slave_replication_lag=21 -#router_options=slave_selection_criteria= -#filters=fetch|qla +#filters=hint|fetch|qla +#router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS + +# Uncomment this to disable the saving of session modifying comments. Some scripting +# languages use connection pooling and will use the same session. MaxScale sees them +# as the same session and stores them for the slave recovery process. +#router_options=disable_sescmd_history=true,disable_slave_recovery=true + +# This will allow the master server to be used for read queries. By default +# MaxScale will only use the master for write queries. +#router_options=master_accept_reads=true + [Debug Interface] type=service @@ -265,27 +279,26 @@ service=Read Connection Router protocol=MySQLClient address=192.168.100.102 port=4008 -#socket=/tmp/readconn.sock +socket=/var/lib/maxscale/readconn.sock [RW Split Listener] type=listener service=RW Split Router protocol=MySQLClient port=4006 -#socket=/tmp/rwsplit.sock +#socket=/var/lib/maxscale/rwsplit.sock [Debug Listener] type=listener service=Debug Interface protocol=telnetd -#address=127.0.0.1 +address=127.0.0.1 port=4442 [CLI Listener] type=listener service=CLI protocol=maxscaled -#address=localhost port=6603 ## Definition of the servers @@ -314,18 +327,18 @@ port=6603 [server1] type=server -address=192.168.100.101 -port=3000 +address=127.0.0.1 +port=3306 protocol=MySQLBackend [server2] type=server -address=192.168.100.102 -port=3000 +address=127.0.0.1 +port=3306 protocol=MySQLBackend [server3] type=server -address=192.168.100.103 -port=3000 +address=127.0.0.1 +port=3306 protocol=MySQLBackend From 8630e4284c4291e0e6ae4ea028162a86823e7112 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 9 Jul 2015 22:28:24 +0300 Subject: [PATCH 252/273] Fixed broken links. --- Documentation/Documentation-Contents.md | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index f19797d55..20c6e079d 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -47,30 +47,30 @@ ## Routers - - [Read Write Split](routers/ReadWriteSplit.md) - - [Read Connnection Router](routers/ReadConnRoute.md) - - [Schemarouter](routers/SchemaRouter.md) + - [Read Write Split](Routers/ReadWriteSplit.md) + - [Read Connnection Router](Routers/ReadConnRoute.md) + - [Schemarouter](Routers/SchemaRouter.md) ## Filters Here are detailed documents about the filters MaxScale offers. They contain configuration guides and example use cases. Before reading these,you should have read the filter tutorial so that you know how they work and how to configure them. - - [Query Log All](filters/Query-Log-All-Filter.md) - - [Regex Filter](filters/Regex-Filter.md) - - [Tee Filter](filters/Tee-Filter.md) - - [Top N Filter](filters/Top-N-Filter.md) - - [Database Firewall Filter](filters/Database-Firewall-Filter.md) - - [RabbitMQ Filter](filters/RabbitMQ-Filter.md) + - [Query Log All](Filters/Query-Log-All-Filter.md) + - [Regex Filter](Filters/Regex-Filter.md) + - [Tee Filter](Filters/Tee-Filter.md) + - [Top N Filter](Filters/Top-N-Filter.md) + - [Database Firewall Filter](Filters/Database-Firewall-Filter.md) + - [RabbitMQ Filter](Filters/RabbitMQ-Filter.md) ## Monitors - - [MySQL Monitor](monitors/MySQL-Monitor.md) - - [Galera Monitor](monitors/Galera-Monitor.md) - - [Multi-Master Monitor](monitors/MM-Monitor.md) - - [MySQL Cluster Monitor](monitors/NDB-Cluster-Monitor.md) + - [MySQL Monitor](Monitors/MySQL-Monitor.md) + - [Galera Monitor](Monitors/Galera-Monitor.md) + - [Multi-Master Monitor](Monitors/MM-Monitor.md) + - [MySQL Cluster Monitor](Monitors/NDB-Cluster-Monitor.md) ## Utilities - - [RabbitMQ Consumer Client](filters/RabbitMQ-Consumer-Client.md) + - [RabbitMQ Consumer Client](Filters/RabbitMQ-Consumer-Client.md) ## Design Documents From 40d022b667ae90417258d50270cd62570b65fe72 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 9 Jul 2015 23:26:19 +0300 Subject: [PATCH 253/273] Added link to simple sharding tutorial. --- Documentation/Documentation-Contents.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 20c6e079d..0a892001d 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -44,6 +44,7 @@ - [Replication Proxy with the Binlog Router Tutorial](Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md) - [RabbitMQ Setup and MaxScale Integration Tutorial](Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md) - [Nagios Plugins for MaxScale Tutorial](Tutorials/Nagios-Plugins.md) + - [Simple Sharding Tutorial](Tutorials/Simple-Sharding-Tutorial.md) ## Routers From 7585adb2289610a639f88beb0e48104678830058 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 9 Jul 2015 23:52:31 +0300 Subject: [PATCH 254/273] Fixed links. --- Documentation/Documentation-Contents.md | 2 +- Documentation/Routers/SchemaRouter.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 0a892001d..a2f453a7b 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -44,7 +44,7 @@ - [Replication Proxy with the Binlog Router Tutorial](Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md) - [RabbitMQ Setup and MaxScale Integration Tutorial](Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md) - [Nagios Plugins for MaxScale Tutorial](Tutorials/Nagios-Plugins.md) - - [Simple Sharding Tutorial](Tutorials/Simple-Sharding-Tutorial.md) + - [Simple Schema Sharding Tutorial](Tutorials/Simple-Sharding-Tutorial.md) ## Routers diff --git a/Documentation/Routers/SchemaRouter.md b/Documentation/Routers/SchemaRouter.md index d1de66b2b..55c3ad706 100644 --- a/Documentation/Routers/SchemaRouter.md +++ b/Documentation/Routers/SchemaRouter.md @@ -64,4 +64,4 @@ The schemarouter router currently has some limitations due to the nature of the ## Examples -[Here](../../Tutorials/Simple-Sharding-Tutorial.md) is a small tutorial on how to set up a sharded database. +[Here](../Tutorials/Simple-Sharding-Tutorial.md) is a small tutorial on how to set up a sharded database. From 1ca1a25e235e37553531323954a0956ca15f0d7a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 10 Jul 2015 13:01:56 +0300 Subject: [PATCH 255/273] Updated the getting started document. --- .../Getting-Started-With-MaxScale.md | 16 +++++++--------- .../Getting-Started/images/getting_started.png | Bin 0 -> 73575 bytes .../Getting-Started/images/getting_started2.png | Bin 0 -> 74584 bytes 3 files changed, 7 insertions(+), 9 deletions(-) create mode 100644 Documentation/Getting-Started/images/getting_started.png create mode 100644 Documentation/Getting-Started/images/getting_started2.png diff --git a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md index 344ac500d..0193fc08f 100644 --- a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md +++ b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md @@ -8,21 +8,19 @@ In this introduction to MaxScale the aim is to take the reader from the point of The simplest way to install MaxScale is to use one of the binary packages that are available for download from the MariaDB website. -* Simply go to [www.mariadb.com](http://www.mariadb.com) +* Simply go to [https://mariadb.com/resources/downloads](https://mariadb.com/resources/downloads) -* Select the Downloads option from the Resources menu +* Find and Click on the button "Download MariaDB MaxScale Binaries" inder the MaxScale section -* Find and click on the button "Download MariaDB MaxScale Binaries" +* Sign in to MariaDB.com -* Find the section on that page entitled MariaDB MaxScale +* Follow the instructions at the top of the page. -* Select your operating system from the drop down box +![image alt text](images/getting_started.png) -* Instructions that are specific for your operating system will then appear +If you want to install only MaxScale, futher down you will find the product specific download pages. Click on the MariaDB MaxScale link and follow the distriution specific instructions. -![image alt text](images/image_1.png) - -* Follow these instructions to install MaxScale on your machine +![image alt text](images/getting_started2.png) Upon successful completion of the installation process you have a version of MaxScale that is missing only a configuration file before it can be started. diff --git a/Documentation/Getting-Started/images/getting_started.png b/Documentation/Getting-Started/images/getting_started.png new file mode 100644 index 0000000000000000000000000000000000000000..d90c3a2dd70b37cffcf959a880143a00d93cf6c0 GIT binary patch literal 73575 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 RA!CrsJzf1=);T3K0RTFAWOD!j literal 0 HcmV?d00001 diff --git a/Documentation/Getting-Started/images/getting_started2.png b/Documentation/Getting-Started/images/getting_started2.png new file mode 100644 index 0000000000000000000000000000000000000000..bac4926a5136e056258e87c5c55f3eb72f52948d GIT binary patch literal 74584 zcmeAS@N?(olHy`uVBq!ia0y~yV0p;E!1S4eje&t7{I#$n0|Ns~v6E*A2L}g74M$1` z0|NtRfk$L90|Va?5N4dJ%_q&kpuphi;uumf=gr;n8j+*F4}3iT)tjGF!0G*h5Y479 zYG;`^G@5Q~__IMV$mV>fQT(Z@rygT3T8@ z?5X@X>(1MEXWra-6Z`JunKfz)9zWJ{;!tc6027rj?wvT0yVmCl_r-_FSEiJgx~P_^ zoH`l*J9kZg$&~WYQ!V@UXe&S!J9Tuue3-ZI&b?DU0RgqB|+di$Ev|>rfQO|$^ zuIpDj7FX_=YbmYsGIz87HF1W5?aY;9Y)knlrRrhO1=o<|`_Nr$7s$L)W zwSPh`WQB;BELinmQxo$B?Su=>Cpn^eclX0RICajdRTep70aJPyy;m)nG%a(L)uq7D ztuBAJE}E$+2{C)_r4B$KK-YDVe$5`Io*B<1?SD) z*ohszy#BoF#n{`vY7nRG*!A*@+nIfJ61{3$s%)#885x!zZhTadwEgyms7nQwZ=c2l z2MVr`{C~%J%7le-xxVvL@$H6Df^$r4V^7C!Bzvg8#kTCf5_u}$l7WVt0%%b zhs);2Cv%%{I7+gq=2Zqbw3wl7!{KCm~(zxz8!Y{T*j`*1eLO;4EjSJd(C`Plg=eZuD& zh6@dD+*ZslY8=va9u?l~zuJ86{M>d(-dp6u+8%cN$?W6bCnt|7zZgAP*_ z;+>dly62obh~DD@(I$)7t{ z!@RcExq6zPn;G)vaaI4k6DzOYuae9El@)YNDkpr$KUdGY{{wtPkGBc;9e|x#k^`w3KnTb`5b!oe{upRYJx#XPh*}({JZXDvl%l(9T<;RyR)I&aRNp^z zn!?oE*6?6LjQ{rhpkDEJdF|^hvomMB3ayd6veM4>_LWC>f9}2^DL6ZS8{uU1_#=iuvZ4ox-k?=m#cRxU6>R)xeORExhT>n_D zq<3MFqeQ#E>*XbWrao+Y({~?US@6Zj9=jB(cWYgE(Z2ESWy)f-~(4|M`9&fsR z;r9>Wkj08`fAc);STA|{r7Y{T+sg`lMUK|;E?E-U+2yTxH}Cj@>%qD=`Y$Nhpa1tV zj9vV3kjLg*cb-arxw1yA<>rjg`$s~w0}D^?2oa56+!%GW$w2B(b0y#0u4Qo%9O=nI z1-nF|OjbM$)t~PD@WM-pSFNFnT6&VJ?>5G*UtG0e|8hBYf47TS6%xOd&I?Pua9^9{ z825Z)WdDyRB}u};1-nGPSm{1o=*aSGrp;3sTVe0FuMWB#RZT9oFqG!GvPP_9{Y{PR zL!yCCxwW6UvR+ugI_Km{9;r)nuOH(-^fKf^TFiu*2hDSjZMh=b?$>m0PGQE!wu6#i ztggMcVLGs(&b%U5`qFY;sopl<-TjFMFN);fY`U~LS6A#z&8!oq@k0C48PmlKYxme z_WvB46AkWZMW5#UKD@L3dl}E0OM5Pqb$a`x-g@3`+dtcEMd%B|r@EE5m&g6r&z*Df z-u-1C?=2Aw;ryce{*`FPM|IK1r@L>aB)s}@`^L5hsd{UEPWa&~FTC-MZ(Y^<#3=K% zv1w1kmg(m&&VPC2S*5FXiy-+zoW^~AeQx31D!IxFqj+`pC6 z@3z_MP7L1tZ>eK<-@NC?_it9X{;_}WZidPI_WgT)D(g8Gwt}1hjlw8f%Y?1x1?tFX_ZS(6{{~hC>!A`U9 zcsRM)%GEt)XL4R;{BJI6$=s!V#Tc|ZsnHw|4!UJf9{CPy*W#^eCa9L z(o^>^_)0@ToBdZa{{8=CxA)HX+oiX`=a+k${K_1y=N=ZO#X&o*n^s+1q0umD&bbST zwli)2#xZP(x{zfmX`Wng`K5`vJXNw=vqK)9yjc75{+HhpmLI!|)@A)|F)5?ue6}z485xouAevwSIeaxOru)a!YmmuUWy*A9HZ?KI*G8-%#?6 z#m(+BYxZ_|?WsjtUiB+Iir9*L-E0>gWaN2st?B)}stJkywPAjytM|lo9qM`hZqfh6 z@`CdtHZHfSHT){KFKSEQ&i%Ph&Na&ZNP1-(Q+`{URWA5mz08V?>pz(f@7h#YuENjr z`q-4n{qYw!X-7qK1d5%p+xb7iW>dPdKf}Abth;T_&+qm8YV+>iE;Z$9vqzhcvU8pJ zoi%6DtsQYM+5WCIW3RP}cYkto&BW%vd0BmjBlkRTeD=J#|KD6^w#RW>?2mse^XA&9&My$OUv=7v z-g$2ux6T$WPLeP4=9=gF+^8@3P1qzEwxHxkoF7b+&0c*plynH)cqIPGk6o=dzn$NG z=Spz0Tzq*!eL0lHT0I>J~lMYL9JFwiPvfXd(UYJCE(L^GSEb{ZqHP z_kH7Ead*(#i zU(6S9F>C9*h3lny*<4veuNJ&;F)1|s7+EPk*>CyN{kiY-kFA|`b$Oxg_q7LBhZ|nn zIrq2zQ|;|CAGummtDd|&xMa!A0;|}bieKDLyL}#Pyk5uPu=t`uTO&3IA6KwXLL-&XT|+z ztLC(AUCmTb^E3L&N^xV(nXd;g3BKd;Ic)O}^w`B}rHD>eOZ#*x{( zW6nSS9A0c^#kl%x1dwc6i9jW<6bzj-f%(3BWzW4X~ z?LD_F(*C-KaC3gqS{>z~VEEcz$>hfV6S<)gHv{T&uWyY_zGbrh@Zor&WpXJNux3;2P&)g&}_!!6Dx%+*Ve(yc| z^6Ey(+0XZSocYy%!|H!(Ue476!R?Rs9c#V%+iUa9xiKA{53Y+^znoI|SG+7P;D@Ei zx46Iy*|Xz!$F8l73k|(DcXy9|+|K0({&KCVKl>{5#;42P^J-UzaViv^Ru5S_X{*|$ zY4hV2rG3?MIC*|++E#^qaa$ToR?j`U`rDKzC3{=gevALw7j~g?wl7EIZvzq zX;PY%;yXV#X1kTO_`X9k>+dIj(Vg27XRe$qxv;7KbmiZ4yMLcAt8=aw^ZB&Rf9JZy zij`OHFSs74zNYJXacOo-g3&8ZZeFFdzwSTM))>A^HWS*L$9A=N&-wJ}d0UOsFIuh3 z54rp~rtbNw>#r@geu|Czxj(uy&RqHW&#x~oOwqO9>jDbyogYMhm1xyG3#t>j+PY}R zo%?IO4Owqg|6%&AwAkk5#m}88EB;-{x;uA;amap}s9(#8GqXn;$Y&=(rt^dodbzRo`LQ&drqetgu zj%9A2RP|D3`8M5!H+%ou{0`?_efC}N|3Xc>iCeaWoO>U8c3r&vhn)OtEA7?I|9e&? z@%Xh*{akftiF15k{*S3sW&~TjeYxc5T=k;M?_=(lZb-M^d+^B0TyKxPC1TSLAN%=t zHRsyM{XW0i7loahSP)(=pr>rN>}tv!k7HW`k3MzTxTN{8>+NNwKMH<1ulZf6RnEJI zar&YKP3jEKPkMz0ihhszes*_X)8W2vMo#MmmoMtH5#~K`Vc$E=mD|5(b%(8f`s1aB zlAr7I$~zA}$_FpMKDlMf?sn1tYmdz=nt1>Juj8Bl{yJ7#Ec-aw?BlsPOLd~_8%kF1 zJ#dj(^`yPMc#^QKeK_~o--lIxPq`5+{jBK!iP@eWUQf@SuX}h`UtDzOnl7jH*8|t5 zX$1d&Z)WzUi{VZ^<7d;^6$u&PZZ>}{O{@Hv9-K6+{rJ`JQstN1G4;Q-RhC6mynJ|Y z-ky2Y2@ISk#cuJx^XJiu)47wJ-ddOSwY!(7tK8Us z;P1Sh>+K6WGfSktiutnd_qnn(WuCmbz=_?x;kmYFHtfsY`$^*3cHPz6%~yTbr`-{C6||h<^RO|3Fjf?Vk^JuGy;D{aZ>r?7jBcj0?f$HZ8yN z-XD7Z|Lr%M`kjB5Z}{HT^yGS8c=mjAsV{#%o#%TrIatzuRrtQ&q9Ome4LKQ1PHBR@!ehTRYIHZHtyZcR`#S(^Zt}CTNuAj;%(i#UbKvt z_18~>hx6+0n5XK29CE0>tZLoCt}i!@stDzO_z6>pSnu zLu>obE_!+)+_KVcT4w*AFCHM{>e9qiJSAGejJpMPjP@A)U|@a30X7tEIo4P9COnS1BC_kTT=Xa0Jb zl=Z!`uYX_s$ulR*r~h#Y6%F}bU7Wd$!N2Z@wtvlsZuP(Wx7VdyIr4pFeQmYb|IgXi z{ynL+(TcI!+5ccw;thi)u9acd3m=wtT9+!EFM51lCh_*3mk~*R7nZnI=T#RN_3!zo z`}cjVTyli*qsh9iTIR*oms&a2zdb3gyfSL_zUPI9W;&NhShQ*NS`{dIK74Z2;tk*O z^lFdZT}xuEq7$r_9-Z^C&Mtj#!xHY}d-}Y)tUMDkBJO=X_qZ_Q?5-tl^S_)j*L^0U zX!@#R>SUqfTc2-CoY!{p`1+h*?y9m}<)v&7ZItgBS8P+Tu0U z;QyW%CXwQ;msg7X+@a<8?~dgT?S7v3UB_mA*Jlv*{8lQJbiMw_Bz?6BQ?edD48NQG ze&wpJ{p&mZpRHB;l%sR5=i&2m-Zv+nb6*pfKf^0&Q|tQr+Xr&@Zr&Gj_u6J%`TrmE z3p-cN>Fs)ccl|Wc)p6hN+?u)c^*3SR`8`*kJ(zvpyy&yD@u$1?8W-Acw20)Csjn21 zj@OBB-d}iB{#jfM|DHd*cdbpH9{IHW=D*J($L`e$o8K?l`+R-V%y%*u8sCfME7@uG zZYh!8EWYkxUypuj<<^h=_4%nT|M}u7dtTh1SLt2Uzd>isS}rZ=Q|sSaZCiEzp|H4@ z>iS3XoD=_E&$wXx_w)V^3)yDXyXWS_Ul)qgow0Jhp4z9JHP3fGYr~B6qe>uZ(=Av`_BIOvfOk@RL+^W9ewNGN>y(w&AZxQxj#?A(sb_mXX`)d zJc++i7=Eihn$NhX;rHI(>l$T$=-*C16TkYqk;3D>tMi|~j@V=M{(a-j`RvQX_Bj7v zr(5x+cg^lLv66LFd;9+X-6XgA>_qMJe&zd5cKNbzw4U>4Pfhj0`GKr~BCM`~Ms6BY zzuGxx+^5_*|#rOLe~Gu zomRr*w)y$Th4uU0ll>Pa-+y?=zSL~H%?~3^zQyUgPaYSaeKaV++H%dk+r`(o6>N3g zZ7*Fm{K-=Bbh7dNH~qb8@s}=N+VjcR-}`XJr;l34pKcJJ;h(v5W@Wfp`2AT0tGdFP zt9!SHaJLuY zem8gTtL)r0CE~%i(`W7VOAl>w+y6AY?%UV=FIBRjX^pSy?FI|7z3ruVmURtlD?An#~Hmh#U4pIE%)ARge=jM%9R!%gH*_#$&u#r=9-f?$-SJBmRyjkIA zO&_gR&_5G#8;;p{G#-R>{4T%-8-HKMEqxcZ#O%u@cxyj5q;0kEL&r} zUi7=Ep66cg;AdO!Z%z(Y>)AVV_x!&?v+vH#-hcY7y=&&{Lr-$Huikn--OGEs;lHo) zKkskZ_@(xR^ZL?&g&%&czf;pOU0(8xy8U1G^L%G35|6Fl^Z5)1L*Cn~v&{F!Ji57k z$Maj8lY{N*pUT<&s#9&=wS7_WYqpy2$6cG4Ya>lQ7vAcx(Y>F{{o6+KP3>BZ>lY?Y zj$CYA6xVDy_4?~OTO1|+)v0ZJ)60_ZeBtF6C06UQCEl4@ss5g|P&{e!ib&fVpLdw7 zlU7{kYTbX-U`24P+xN^@Th(HgU96o|^=-G1Q`4Ug?~?y^GV`Z+Wo(-%-1yJ#i26?Z z)92idivQf9{r1UD?=8F6=|!z=>(iAs`1^Uk&Fz0No7+?4cjZsJuspc9&Gu{Myh@QT zKLc*;d}h9BUG#6YJs+P0%(}L|asAC1&+S~N{*=9CY_xJq(20rbxvQ(BzI`!K5~^E% znmNB9?{UrfvgMaM{Z_Mv9)0)8&hFWbu&A$l`$gmPmYja@aL(;dyVKupne%ksjtLH5 z6XgHY`pkR&_GPAv?A>!mh1p*eoewdq|C}`O6zhhSQZ*q*Ki_EV`!Yqv|EJ#W-pSF^ z3yNL~)ikYIWIE$z<@3ohe-8g;4_d9tDjjvE>-6e*Keq?F3FsaFc~ew3ZLRidxm9el zRV}mk&-wYJB4>N#hQ*&(wadzW>tRT!d-^PV|7qXN=VR9RtedAWXH}Z7`POB^GD3&` ze&f*)KcAka_vrO~vqu{uJMYi>E?u_l z%)*7OnwkH0uF(3McV<`f-D9!GxowZUEGc|)#C2Z9TX&^@mFx|_+rO{<;J9PaChI-> z=gj^j5jx{-aqg2*y^gpjkT@U z^|?|t)skg*99@0q@{hLf`RCmyyB8k*@RDIkwCt6)-)q+C-@ciepWJ@==z8sPqdoQ2 zX?6Q2J^XuX@t-dTZ`|h0_k_X-~C^^YTzsIdNb6?3AR9f=}K%*91vz z-NgEk+uAQMvmN(UKCX-V_k(}hy4&rm4jtX9D*tkg7FXA*lsmsB1bH{c{IoK5bBfH#I=1WW zySHVlTT`rABw|1lu>Wtx9andq>#{hIGyke^e7t?oBfTfl?>=zmZ>;JN`Gw#q=Lp;Sq9mugJBRD_EFnd|B9M zcy{)y-F&s=?}=UW-cI|WVe;?cjY)osE@yadi|F+CTON3JLd|W?4>I$AMo5QrUwM)I zV+QYyH-05oUnhEYe0;;6u<@3GCcDr3W zbTeKz#&y?Guh(prZG7==E!@x#X3Hva!Vlndr>TCn57%E)pL-m}{R z%2JD8#mIckO52)y*y^f9t=+lLKaz5U6dfbCm*?NTQhjUc)n^CJtG^asp7u4nBG?M(|1=lRxa1WQf6HF}`l}PSw_QJOr~38uQ%}FmKXhxC#`;{p@a*~LUp|)qzclQt z)}6xQ>h*ygtyk|qEZLYC&T_tA`b68@cVDf|-&vje%+Htd_+3ZL!C#N&WUt+z^~SbJ ztf6~jr_I-EHFZ_*1=a3V8yDBzEU;Y8E%Iky*rn_h3gN#>zZptP-rR5d^_Q*xgI@oN zf7joAmO1=u+t;X74}RrvFTcJ%b}F}FIu z`P){mnE5Ye@AQ}cJenVUYq+eg&)GEp{>I6ME_3>hWn3`+c(3k=`m(BP-n!d*zV4jT zb1`n|R3;f4!LyCRf%CqUtZDTY%eb>mU7T;t_c!x}il_ZD|MK)lYf8}8#ych%B6`aY zYws`qZGIv1Kx~Z7j+FN>}fhb4obubUm?h@hbED0nqN3Bi9hYC; zPgzrQ-4*zZDf*vE+RYq$`O3>N@|< z&*yg>UU^Z}{rk0tI&-aCXC=PVXX2igmwNkOjobIs-PWtrY}vS=Iv`?`5Cy}jpKa3^b64iwq;hc z+S|&H=NMgB;b*$c*6zq_?-%^_d1*(^zFiq;%CY(6fxXUoNpH)~S8to&@3)}xaKU-m z=b#bE{+>6D``2?e#~q(=&%EGK%(b^yPM^y5=X?5cvWQIX{nUpN12MPucA%c8a)M@-W8R+zsGv);`&_gwM* zL$_9K*mQdJTf6uTv%05GiuiLo#+MVW*B!OJel@z^V268lR%__Nj0=+| zyoodST->-S^v~XR7b1mN_pYD%MC#N!r5Zg@=X7rS`HEY*!9`hCx4+F#a;g9RYiHM@ zPk)-3IoFH%1T^q}=YM%9K6}%(pj~%ftEESC1WHC;{v3BbH{C_Y|4h{JO>38Tcy352 z*P63`%BlOa{f!<@E}hfkwJa_|A>a0j<$Z^ky2|J3`?ne_um2`)zBTj6$JG4G+bUz1 z)qY!^etXOL|4;TUjJ@&yw@I0t%~MmUZ3`a9{eG06=DTn9-~Im26OL4#pCrj}zV3&% zb$#X1h*JHoQtSRe)eHTl)zYF~z6q>h#b5nrm-Y*|hb( z?fGMOC!UGiGVko?xKrP6G--7GUblEf*2`rFE^ik5{AG>3?aSui|Hs!pR$F}h+{RLF z+u!oB#{J83t&RW9m6xo!zj@A$PwINryQ=SQ==q~J$ENAx6K9_zeN3l&tv2=k^?r6? zPj31#8}sJnX@5>J2mg0}{!iIr<4gWU{ckmbc1Eq`JDT|X#;c!SnsL^X;8Yo1IasvGp`z;ho1f-(NWOzR{P7nmx14 z)X&!>E0qd<;Ci^qZy^ej<%nX~O*E&IAf6Q@}NQ7O#NcUgVU9A@e#FNv+C>qMeUtl7`*@MuK=A>g$y4(Gw&GHiLv+3 z*T3}dsbb~r<*re}dh7r8u-VGnm8&;bZ*d6>t^1j-9&8``=4W`rLAkpZMI{CMnOj7iWZQw_1KKKIzhL?w{X!*)ytMi}=e0%k6Ci zl^rQY8*2Y+s_p%6EV%q`b_LJt>wV{!^&HIFdb!|1_mEG3Q7nEMUWf7}aCTDZ@ zvvJ+i7bhRBbq$YU+uL`NpZC$ZM%k62wi2$Pmc@qiT(!$T9W?)6u}|b>Nz}cHB8hVT zcO4fqK&udKzCT<)@887x*&naW*tf?pQ6ktr_RN|0;Z^g_@2qzOO))*aS~e3jvee)I z%6IXutDb&aH~c-IY4;aXeTxNK;WFL7=g ztN!Jp6Cvkf&dlNqxl_DW=*8(T>TLy|eXj@Wsx(}F{`kkk^=FT$E;anQ&@qJb%i^^4 zo|W^W9XHJ{uX~@Zy0dMQeYl{V__ez4Ht~8fv!9=M`ta}V&sS#b)ERIQ+y2A0~ z$4{4e|ISoTS^p@gV{gT?O=-_XT)*@AcGwhm-uRL7PX5}|iGQsx{D1f--zjWX*Du)x z26k!6!tY)l?_Dcha6EYaRHw728drUK`oLlp%a4FvlX<=MLql1+uX^@w`@3^pt@*aU zU#ePHT%YR0ZueXFwEd3#cU}k1%5?Iw>eu7doq8}()bl9cp_P*6i5{Dcs*eTt$X#HY zeYer^{lC>qXBpZVCf7vITi@#)c;R)4&F6dP@7!ChYin(G^kvDy>#r>e{@7$@hFNXT zJ@YzVFY)d=;Ww5gOta5U{Q67Wxz2H;{5{Fz6YR`Ce4n+qr>@Sg@%Qf;y`Ua*+n3z# zn;8jbw=K5$eNcaeR?XjEwI%K2yOA3FIs?fcCIUKXh6b?(h;|A36GN%G<^(o=rb%5Jn2 zQ9FP0%~oAaJ+akGUzoFNuiQRg?%WDT*^i%13m^QboL5u)PxSZW+Dz7mdZ1-Ii_>?0 zKe}^H|30t%-&bhG^K>ok+Aql~6>^1Zx}5cNkH61ycb4AieiLoh{vz30+hy;%6ss3M z-$#7ASNk-s|B{bz(Au8(FyE$vR3+sHPiN|NRj=D9R&eXGWMOpvvwdb_v+s6>F|#%I z?NLtAdUtWfyWQJ0R{ic<-svf3)n7R~v{*gR+(u;g;m(T-(*Jys-?6UH*N4rPUFqEC zf5qh&Q!b=T3c9rZ%1p>gKt-3CoNIza<^NxbY`3&EZGF<>y>wEzYgm-y4ll>hpGgTk zp?}V{zK`}}v-{0k=4~`FdRjr%^{!9r{B}zGImEA1Tb}dKI?h!H79CRE5~ZsazyDCV-hr(`4b6MfC1X#ExH^V<{eL%o=~TV?_xrd4`#B?f0;H8u9$!`7ATp-1JL#CyrHDGO*y<=V4DwbbCwm0TK>&-Gu^AKmM{o2qb zEI#>1*+Z>qYj2-_`J&>xRZ0|Z)mlFLc;$tT|3bJq7gS{^m^v|Ee8qbsR?GfO{7L5W z?{2FvPRJ{MkZktp-S*WxN}ef6l(yV*`dRMHGWYhC=liUGZ76)qxMD|R9VE52Xly#N zLxL$r@dq2r)E7UlEOEIsY5k>_QyzU<*|2I6Z0&H+zhbk~E<4#DGM#0!tN8r?#r2hv zjQiMpDz9iFdyQ_A}y!+4Rz<5SY zPU05t^5BEVb5c$p++6eTZIDTwT8P4<6PoWTO4t&&I^Eg6`Mcp_Ev}4IkvWhRzK>o_ zpCr+5YAgHyKtj*6MZFdqqSr1_Q-8GKWRdN4lcP^1r%u_aw|mK?X_Zl)@B5kX_sg+QwvSpY-^Eqg=4u*cvLfS4 z)rFTAB|~J)q|15Vx}8=tWwaK+*Gar3jSW8)X7FbC)@d{>njkp;w6e#t zU5}T|Tt9Wus+JJ$r_G+qj1haDDfsWV?BDUiJHaf)$KqOw?1GDFJ`Q22y3m~hE=!*j z-SYI_HaYB`s9-p&YiNK->!*Fu%a&XZTeV`7T8!ky#PEkpXL4`69iy@=%A!_LE;>f& zN5;V%mR>g3(7TMAl-Q1&G#p#_;fu3#N_PRL z9qcPBuC+KrSA1(LTgg6O{K&Fi@!H)JR^?N-)TLd?X};2BW2BobGJDmEjGEe+KhMrT zGQXR7F4N3x`4s*GoY3Sapyc1RXvg&8QxXeRRxSxwn!0=OG}E=`UN3wkxisudjZy#Q ztbiE4N5ZlX@^}6D1=n?5A~f{w!HH*XW=`^oI<_*X^Sa_13#5GyAsSMP?akUn7yAVZ zJ~EyZ$Gr0H$_0kMrmxr^R$>*rR(ko=a#d&scH#*9*z)5wXTakCQP#bF+^c2@8cYni zbjg46r8g?eZrzwIbMBvn?bKXNLrG3({0lgB*!DC+gEs#@+e#q_)_lh$<&iwP48AglQ~6kAU8 zyG`C05&o3Bql0VBq;KY&hu|Jns-J42pmb#FGTrxF@J$&`9Dz4I1^f{F4kI-r&vPiA zn!)lA?mfj8jpSL(?0hF`+gjnSY0+@5+I%zSUMWIEU{TQJ-@krME0;ymy5hqs&dNZwnR|{#o&G zo6y|rNBYAnY`=XsytKrRV|#hN*M|K&k9^hjwDo#jda};1YVNdE2ls8sikB;_EFr_BD#<74B?1EPf%=RP~&-amQk&m15G_ z<@3|-*|S&FRJ02{|9SMiwWR%;+Rpy{F*Bd1Y_#(5*eFzJ*0;0%?Zd|C+X-e~;-cWd zbK+1ua@Bv?;%SVofq|hrreu7qveUfUc6i}~pev{KoS9jE%zANqp<;USBz^7mH(Iuw zUgw|r!Bl!hsBS~t+gF#jC)b9(w+X!$Dg-mFMME_Ftij@v*^}+B$v*tB(tCxL$!h6S zvU_Je&Nh1x6te7G+AMC|K!gbB4X>UPwl)RBI3F*Z7T!FRd+Wm2+EwlC_^Bw z_5L-P#0$Q?+&22l7a8TAlZva{z#htUX34VKGyi?Hij%$0TraQenQ6AHe($t!xi=?! ze$JM(ULab(#@Z`qQR@W-d60RZt3r#X#s^q4wQdVsb@W`*f4-tWF-&!7%+9;aKcA}i zcQNrcbDc0FX=&C5{lnF!%M0I5Fv<&sXR0aFxrAFz@4Of3ciK$;`1zbCt<}30T+RBC zf829gPFF(8{MQ#VO>eyMI~VWuaOv+|g-^byimkPCI{rGVO78zw#Y4g^zpIm39P89T z;l(z0n(1!;%|*94{ST-wPoG~q?c7{z3E?kanGYD3~-t~Kw!@~+yL63h}- zt$fDKmT&o?g_AeVGpaeucFSz#!DoM;@A!GFZQkQ;ayI_E(;oh0d$4JHaKrkEX{FAR zclWzaynXUr!N%7aB9Gr^_)pj`IX7%__S#LX+WZ$Dw^i9(QQ>h7jNJd6$9}EFfhQYd zxow_L+7=r3rAoKZ^QEiR`nIjB3VvKs-L+-a*@cs*UwFAi#;&xp@A}mz7dj)PuB>qq ztrvb7#vaW#@A<*ow}f7#C*5AitrJz;GX4GQX{-MIQLa!&%T|ie$x-d zODpa96u;${0YCrF#;B`IH-!a{J}PyeY4U4X()P*lLTMeJXqwe4ci~^pf6c$!C~R1g z9n5YouWYBreE0U1#}`g|&wH!qy3~L7uNl2nQD$rWvUbh2liehzu9D7L^3BcV@=n*> zKeatGda8D9UwL|7$BUZRX0^Xcrs>6=dUkg9OuJgE`u~5;XPf8ieW)pk;$15hntprX zXNEU6RTHQ6u3CIGWi9*l&uzPM?$nDd_hxhmP5j*W`OBV#aT9O7+}d7knC76(RO9QK zr@s5L>wjr!A)yzO-`^^k>~`Pf`0K2sE3XZtzkbOIpa0^=BHu_|&*-PFp?jCV5i5yX zE8SlBB1MPcxANkb7i{MIooL7)V|7ZB@83SRZ?gSrnpeGkr#%#E5Qb%at({!PLhB4P#jG7~ zzGIMjkgj5S_ubb1{p+iv(w}X)bS86N)!b=s6z7)AZqAHf|JCZ+g7fxrFP}d@zWKb} z=_e;A|NMO3ex^z0r036@?p)bqw)y`J*Y9RuZ*2I^^Ehw!!R+d*3h}S5a-=<<*j!tHjOcRe#Q~ z$@sNSPg}1Q^iwr=^&F-1suxslOJ%ao4WBT-bJkU+h<&P)tgbS1d@&B(9mViJr&nc9 zRztYK`W0N+#}C~)aX#ljR`G-Bf*gJ4&4h#%Oir}#{JXfvN-m7n|xlz`nxAxVOoqOvl3?450 zSM&9G-XW*ID`RyRxICY-QswQE>zSRtTPl_vy)~Ds^l;M$W9bf0kH_=ZPGtEZReblY zss8lKVS28rW{SO6t^GlzQu8ho%}5aV6FG4sD=l(}A{yyluchid>);t*Yu)NJl{*K{_5R2` zD;W1b%~pzcOGZHNLfcuQbqR88C0R%AODk98-%HCWQ{Q@+N%Gp_%6-50|Nr;9@?VUI zu2h(M%5l$i*%N;A&6!!Xef4p3?{7v+TAoji^}O}+-Rx+C?1yF-4!q|7o)mFDt!96Y z{K}VQuUc4((&xYaX!x?S{$AR>+CBM=l~b3jHOe<+umNNy!OuSaB*@< zA*|OY;M8&I#j2ToQIb1Frgm)p_-ChL)9qcRntvGJiG-stbXV50#bIvxHQ)6A?Q_d} zd1=P8ub<|xgY|r!I270A8Oeqsnt>fgYL^4yts79gQ>X=AlC%gc>LIPsil+JH>D^3+ zqWk4+i)^>&-Q88RqEkT0dZ!u8zL-8P(U#M%z4Mmk-QD%^T6Dgs<@%SGmw&!r|NrUk z*nKwD+AH1~XKsyRVrJ&#;84)f=@HY7y09u#`_|U%_PF>j>q4e%+GITY?2n&616POX zN||OI@io8e(#|K_6`JPxTKVs}=%gg2=-VQW9F`(}FlY0<`ngG~GW{mP!>xBU9HO)r|mR!za}+w$&Kv1XO#28y&EIpQKDENof*O~<Fzv#^!Z)|K_usGoKUc~$>%$RYe-Y~i;W3xq-F(wSdl;bS0vzVIKV!@=eg4%|HS3w>spbh>tn#cni8KR?gc#l>ajv_jo4yZ_wCF`mL%Ylq$`S*HGKXqDuS#(EyDo@*#_Bo6WyYKoH6&Wq}o166h-d>&P zZ9Ow)%!oODc9v=Ph7AV0N?(UP+GM2J<&>4BrS3Q9!TbIH(~fqD#@&w96W{;$o3#F( z4^9(LYpu)Pd0#hro6dQ`oViw|Ngo~@G|9a+<-&yk9yaEeFJH#&C}@0pd;8{shfYF5 zLVNlw%iqbIo~E08VuE7$s$bVzo}Qj=T>Q*ON38q$=dZ7>KK}CZa%xaoOpMI^y5G6$ zo;No)@BDtR`sIa%%nu(vw2VUT^qI4VD{M;C02j_{>{9#MKk$WPvW^bmL?e&6s~X7 z4qvxN=<2>@e?A_Uk7Kl08+Q7|#l=5gtzLg>&6%gCrzdZX+B@&__wVW7-rVewFl_qu z^>wn*%n#qcFZZ2o<}=@Ju46OX(^KhZXPKskq4?5b^G%<}$9gU6{_J?ZlULg8!n)Yq zHT^d?BsO1voOX7WpuBwi6Qi|Z+S2BEDW9L6b#ZeOl9uj1n&i0ty0CBPjuGxP2L|G&BW z^Ntm({_#JM7v;RO$))(<(&ML|uGDE~5xwK8{q&$}`B{^TXSgPBxo{@tf=G<9=ye|- z-op=0Zf$YDm6TKk;26LU;q4jV7@)_olf}-!3pPofBiJI_}RVU{on5tryt9I zK9AXIPaS`ZzjIDu(0CW&2+=$V>~t$9~`Rm)z#S*6%}XLRBj6Gw@f&| z@aD!w=hbiC-Q68#>*o5V{Pp$q$II{6E)NY`>Lt1+YAcui{y&>8Cd=E^M7)|kOU-|t zj&=U@7^RT#W;Wgr58LIFzP-7bacK$XoVjx^F7XsL$-Z{x$v%dHudhP?ys!W7TvKC{ zcW+N;PY+MX${^RQ?r(2zFZQ2rrzXy%dHVO`gU##~MNc^TWUbqd9C5j`yPThum9^s6 z%jFlhWCoYKzb9*3^~K@rEYr1RXQt^!Kf3I1@47l{?V7OVbMDvwk6oX0Wkq1dogEtw z*0%A>_tpOXmN_+cU(L-|8wJ5&YWPzt&gs*qx9i33%20J)w0QByhwbuGcihb-7X_^p z5EM+j_EAY$d1KL2ubWH!3JVQCeg0hd`&;gxf4|?~*pSE!N}&~%l`nI%^Yhn-FPpdF z|AT|gpcLOCaH)FQx$UWIE4)`bxARHZ*V!~RH*Y<4_|PGPv@;S{R)wCdf=*Sp4~VJ>H`9)szsmmIH@cxi@BBR8%`)5)4 zNrj1tso?cB-M!y~f`cc{n$@*qhegiKO|0{4ze&c##CVn3SX*27NSSgaAM2Tz%bi^f z3g2zH(JATa%G%o8H*ellleeGleIYV=iw-#40$l^w$-C=nXgIX<$!<9n6dYXm@DS_G zx0f$n`f@LLWk{MYo4k}%m;SyVhu#E+R#sMW@bFxbp32JLZ}YJwF0!u9?%ln;-3t~d ztYw+AbdBVCQyw`R34hzKQ^F!09UJ3{R&9CmY(e(LMXjOzfr7tdw`N`aa8x}0MK6!H zpWm@rrr9a4g`5-=6c{2SBcq=B&N4ZeKEIZYVMXljZTIS)sfkbBRrHig&39Ic0ps%3 z8#V~cHqSrSBWe6au2<*u@5dJwI?u2wU6sS+7akQQW%u)m@EnW6LysPB&AQ5U@ZdoS z^SnFCo(C8Y+}T;YvFvSB&h2fy_J6+!*Zut(K6$YW$cv}u)qL`dZuM{hfsl7oUaQ0; zCMw2Ny;QZZu*jGy9kn*7JgJ7Aoqf-rPp37sw2Z3eoSv?q{PopUv7-_)GHbq{n0Lp% z<@4vy8?&$LJ$d>xvP8!BTHkdk85x-eFJG<1dberPrJG*z3jYXjs4Rm)GlyIl*!^6%98xr$^tJ7rfl>Xz_X5 z^o#rYFWuW){q@u=^ZYori2~rrp4wY9zh!mY-d*=%V%jDt?pnMquJWnqjoY_p|6%|5 z{eJ!W?Y)~f8;j{iwRCiF$k^3L>@I)bHh=#7dyA(YJ$jUboBOiXU(?`h7x zz3t)j_&Udk2ni-;=G5oUe*b(vzwq0eNQTtv`ZaNTqw-mnba!)yt%+#t=-_B+Z&x-j z5IArDf6q0uojZ5l*i*Ur=yBcmLPA0*?vmeLU0uzyNKpv{w4|R_`B|8Thlk5pm-$rH zK0PJ6WXY0@vxZY<&+fMW|0g(mf1j-NtkC+fFtLAM*VikmtFu>DR-QO}_UIhT;w_T| zzrMcSK7G3Qo40QT1q2pst&fSBb1i$P<>o8OHEHMOblTPa@_2f6b$Gaq;-0OKw~FmN z?(|eneyZ|zg;ve;_Wx~|n3%q}t%>w@eO;dQ_;~;EmzS4seb&Ca_P1Hsx){#-`g#!& zk%W7DDmC=e)M#D zyjwe;?1c@9&XeC3yuW9AGcbsekuj>D_4?{?{;#jDHhV2?+P{DQy+;hY%ikZ{|NrlM zquusGLPB4D9qQKK*KxF4JiE8=?S+NRpqBf`$HyQ4`uaNP=f2wC29=*uuB-?=yy)@t z@acN7hnCN;TV-yv#dEUSi@UqaC0$)X8O`O(i_i|0nB{(RH{{$jTDwWvy)R?!B?)2S z#ozn)_|3JN8TvmwzBaW?r)YBj?AaMX>t94f^=ri~eKM)j($X@_-fwl-T7#4m0wwS7 z-Cb6k5PGF9Jw3hs=H~R|KR-T3Ex!Ei{r&!RvAdP@^!UQp$6dYk{AT+6*7@`2=hSXa zKkw$?z_4i1BA;V3r%Y*CvP4D3rXpZf`GE$;7q_>sKbVpk5;Eo93qyW>{=JnyudE7P z-By0TR(#{ejVmHIt7TnT!RYSpE+Qhrpt1Y+;~yU%SEaUz^;SIURR3_!`u&5``unGx z>gwte5EgEJcXxMm$)8UedU|}D)6ZX%YUP~rr@t%n^0JfX=6gayVTacB{>7_oKc6wq zxVma;*vxk)WbRh(jM|>pn?A4dnAY+74-Xi*xVgWo)rX%xd)9QdT(-)CGl^>}rcUzS zWK;9w!mHz!_5WTGU({)inzU1qTbW>j!sZ?=283jWL@IZ&jg83`g^yUy&9Q9$ z@}*?*?`6xECx3f$bB0~*u6s7c)AeF6-H!WwG0@xg)o$?Z%y6(MK^O@0bdAa}N zGc%1hy|1pW)}A+gwPQ2ehi~87wr@9YnB*QHEG%63?vCXg+iJ1r=jIw)Fcsb0l$vpU zo$M^LT&<}mbzeMq@??pIT5M`zeM)NT)Lnf~o}_5I1eHyiJlU}LnNQ5lBG>nOUtL{& z`22bP_51%-NxHa!(`U#xts{?DFkIMES-kh6)yIz?85Wp3?!L3_m`?OIo`VMun*C?r zy?F8B&{vfqAGM~=n6V-}Y<=9`pzGP`>Fnz2>RU1|vn_V(Jv2=>`oo*e=d*H!rKPpc zI;Z)H?!ABW=FNkrPoIA9dl$E1#G#uvCHGc;KgKR!)37!Bx{{I-lYIT3jc<~7y1Ke@ z@bX^Onk+pxEk8fM&?dFC{>js)Q+M%2+iLyOICA93f#b*B%^0t*2vn~5`}Mld0Z1Gz z4NII<(C|+~`Kwopy5F1&TH&8O5@KRx4jnpl=GL`y=hlS?cSa{qxwg0`e5L)rAI|I7 z#O^K&v-i8ZtJE;{l*ryse}8|E{u|+N^j-PNl`BErsDt01pPQTg^@#u5TU#H#ecP)W zz3oI?eW_5eZ=Rl&w{S*kWqris!WUh$T5fO8mrpx0L(y7lL-h8%8#@Y>Z*9p8W}66h znc!4;(PxDr3sg>S%)M>)^6T5%+Y4V^(cC*x+-s7eprBw?s%h4h0}~XT7X+<*@a9dA zRq3k*RbO9;UCqyZYI<*X`TJ9Td)5c7%qiMa|Nr;>s??(5?nr0Xx8+BV9-U#F-uLG5 zysYc%Vh=w$+HIJAPR6x#_Tsd&vp#(Nder*;p2eZ-cK^zlYgPKi?eNm2OEslJSAMKRX#FlOEG6?we<0ZR%je_kqA4qDr8M;Cx=JmC;7Z*4( zudP3le`||pw)9R>-QJ0_wO@DZ?{hfbC!4x`fA#luH%o6jRrZ#Des&gABWYDXo0*C})cA%>1mG89rXJ;g1b{4UQ zua7%=b#-{*(^H~4QCm22Z*McTW=l^`pL}~u#ziLc{CiilynTGOXmEy4`|f^uneW6& zlNNFiKg!;p8@9*o+ZYVA)I@H9|Qdt$cI&JIY3@;Cl3)-8H{V8q>*-}^j zdxTU)(XuL)Zl#lgdKWQJk#mu>O8%XIzb+pP`VR`aung^exg`@6e89{1Z%m^ZIa z*1AlhC-B_W)!|=Hm8`#fT7UnV?PnJ{xA$DDet3xW%ZrQ5`TKsVvGL3GTwLsa`26|x zKJWX*<7)!fzgfTkUr>y{>%<8Y8jg00zn;21<04b`^>wLCeIW02c(}KEAKJNlw{&fM zrjbjY-Kr%^RBq-bCntl-?3>@$Y&2SAc`A4hzr3AGMux_-b8}aJk9&Nq_v10?{3*XA ziZ*TD3~Ckc{q%fZ^*OcU%vxdlL5=c7uH9cwE%%$-)y6BGbZ19l#{GSJuYt;|8Mf8i zuASI0&$jy2RY6hF*0ZzCojDYx`bIogu-T6VCztuWsW@7F9^3&FC z-?2kN+C0ytu+VVRrcDLEzvag4F5~U%>r1VwKk;?qgb4ySk0>z6SeNn4n>SCwszgK0 zcUH@JyWc$7*VY8q#6-Wmw6y!4{MI{nVro7f6|Y(x|N8p+{sjvZRh%wv zKW}epV!Bz?Cu_az#g1uOuPcg+iavb5U$1OpBJ%iH@8MHZwZFRY-n@M~`Q4qJOQRkJ zO%`B!`SRtCnx97B-rQ{F6jnQN?*Fyz`SGh|{QX>CmbbRH{&>`_KVk0NwW04`zU;iW zx0+caQsK$x^Y+)JCzgGG7kg%|wK%_=4Ff|(b@k(w%jaFPe)jC_?2qTH-z%7yh=}XO zIE00Xb#-?KhJ>_Ke}DJz%F5sydn%3H`sH+gSFFe@+7`Ij?ZvIFt8=Q?L~rN2zAn}| zFi;TG^}1jGUv}TVeO20*nGPIiWIp}nU&hT%OYg~aWM*boyx;r%!)g8f3bwYguCA^g zzP_#*85;ZkeDZ$s{Q2W&XJ>nOcpSL6*j@gmlh)FWrLV)5EL-+!s{d>=-mkB(>oOg_Z-SrfQ$H6Ib16_o_lSaBY<7kKez&Ipr<4MQA+Q zdOglLAz{J24y|GvzrT701_AFcK5*-mN_>2*x2kyZT$@TEkOw`lJUra~c<=Xn%mGGG zIlsQV3`$zJWzU{H51v0?9$xqTUF@FE=d3;a{kN~Ux3Bj1q5Jpmr@iHxaovAi%+97x zVRb2=caoQvcnas-+QQk_*VpxJ^R>TAs`ZYY+n#=Yo`#mzrjv!8U=PcMi{`1wB>a${ zJMB;MmD{(YFD?$2um59cV{7~M-@=%fm>&E8e}vWj=Y_~L3q7^Gr#`<%$kEZ!O}Jp& zg=N0dXJ(n6Uc@p-=;_}3vQ{M?`i$XQvqI0zGW9MiYzU95WEEEPxuE_3-|zQ=va-Aj zRCZeC-?O>0Ci3!|2;P^Mm-8?4o2&IZZg17s4G9OCuB;4pFWkFs-MSfOxl#?A_S=Ix z7oZ8R4V#0vJ@WEd=jsB0fZr^^qcKf|U+wa${ zHorbY*V4iw;meDQ5gU_Q-y~1I7IyFV-ASt6UrxF8%k@r=t2+6>PT8$zLab%U!$Yl? zVzS=7dv{|?rtq(CZ?i8tN$W;$b8&EBNIN$tv*iEuxGK)uiu*%wzsdZZ-zyo zl9+CkitVvTxp)0>d#e;RG&p=`o0(?+{`U5E^4nWmLCN@Pk^-pzrL^di`^|#hqC@xY z$*o(ruA`@C%Beed?&!>GUXyxy8YrJADJgxpoxlI#`~Cmj%F4`SWMx64N;`J#I&}MX zbT;4Ghlkr2`^~lb@pO9pglW^*tgWq0a&BD6ICMDbl+nFap{pOARG)t&*x$AlH1xFf zdR+7B)vM1=PADrYTM@ci?A7)4{ps^-#q{*_UfkJfoOOL&?&Zf>TI>I=tNZ(FN5R9U zdrS6~Gb_H|EiZh1O?Rb@wYBv8`hS+!)R@ga$16pa-j8j+V z6o4yy5YfMw>!*blIAsY63I@6!Qh_8hK|#TZy@s6bAT=PIlI(K`rb_Up#AJjCX2(Xg zKtpC?@6DylmrpO{0fiVC_ixp5;t0Iv2pOIc6cn^Q_X82!qai%B6IfdKHAWF}@yEw{ zr9J%pj~{O5KW!xg$!1DQTIuuZer1~E-V!M*OPzOWQ-r8ui^gdNz1UqX?ELbJ>i)77 zl$7Y44gl9l9UUDj=4k5Z99bJ}o*2K^YF_QPo16HKC#C+g$Z~UIlQPRW0xkaq1qEMj z+`irY?=Rj7^XA1}TR6pQruxH%Nyk)R7EgNo;zh*6<%Zhgo)$2%MaLzFYnBB?!VRxO zH;q$enV|VFaOstA_w}chay&hkJiCRNVX9Z?HAPq!4AuJh@nfp2)6OHu10fB9j*gC; z9z<9vNhb?I+TR@=9Vd>>Xi z*^+rxMp61t;PSHGO?;5PPe;d!GX|XQU8!kLl|KG4eYrm}veLt%-dF4Llpp_}uG7ms zr4a@7$D&QgRb*y94M?%6>+Es&iBXyq^zKC7<=wlv_U>M7P=C^FZwjmu2uwYuA|qL< zVI;>rEyp?Nlki$RSbK3w!yp&+C?`?hsV{P%~-@BDln(dPw z2^oI6%djQ$>K{QjKX3u+;u7-DfYZIoXmb%rDkb%)LeldZ&CY2vr*`jjRJ`CVO9BgxnfMrP?YqEJy?=iHnKdyM=0#1P|DWfdK4ntdwzac7PnthJ_SG=C z%z&Xl1Qrb!|0fIeF-%$0w97KZ{+iBb(;W=WRu`9L7S!`I{P-VOaQ5T9D^ClGGn;45 z@7a>M2WG7(=i~yFwJo02=6Q>WK5`+xq2?~bh_#6scDK9_Q z&|1QP#~obn^sa!d1>lxb1Q{6_X$c*;5fl`hc|avbbz9Nfeh;sm%@cS}$vgiww2^98 zdMvoHxNfQM0Z;-qQIaanx;{Z{1*CPTq%`TUN{s5%DMjqjPJ9UyrnASy-qN^NUwneG z#v-%fX7PbmHy35ir+hW)d;v8&TOWVyw2=79p60JDuf6`4PjutskDUx&LPd|=zHGd? zF>1xJ6VtA*jAhP^*VxH+u6OQ*Z>?z`-Yjd{+$frNcIEY*8-?dcUu~bg%9QQy%uE9$eocNF;ks?I#O+QevcQK5~U;PJ=8f>MILI~^4Z-v2jj`>C^K$J$9j?;cdt zC{1H;Ke+e*agN@OZADl5=XG~`N49^6(DwcP>6Spk?YaYYYU?*`@piR8zp3l~jl$E> zv!`vZ(Asr;rLFw(V$J>=`%Y~<%kjWthu6!16dO=hdObNXBk7>V@)hPbON~cRtuf$jP)>ZOuM#cIj7T%V%#gl`n1!HJ0hOULHEH z;Gxx|yk(x9+aG`ItVlkh!*XtB!nMVlUR4=yE}B^I&uUWMy@Ll=RHURmJ(Pd0>dMMk z=6QER%q-OxJmJn3y?7ziQtM|9pC**Ho<)!OQugwq~`O z->U!*kE{w=skB>{!RF%;;T7TQrYo03WwlmF7+IHRdd0T(Gr@qaf z^>^`>+yKe_kJk$3M}FPw{_|$Q&0oj1WM2NT|NmeA)oZe@uCge6)DpUFy7lr?SN3Ju z|NmpW@5dwVn?K6l-RaEV|5uE`An%R^kDSeoH^mB@#MB;Y&0I3YdWNC3#5%oPgR&*5 z{r<;}sA|=T>j2(kmn+Q^z#Yb`0eZto`0UUzCXgav}(^aq1j(%zkxs`1YF{pRU-yg`1ljG)esV`T6A2({w?-9{~vo4r^=cy}4I!-``g&ZC(EE z$Z`4lp0~HRpT72g#kulpYa%o5?lJ|G`j?AzB*LmZ{jiQMy%0aJw zef?ytv}@0*Gr1{mi`SpYT^V-EY(>ufkGpxcZ$=FE-tB# zow~M`Q9(*B>v{vTajT@&x`s|C$(zj^cK!Tb09>GLYtlZg4QknDYPUh@w@&S9+SJi+0=sB-AS)JwF%m&$m^F2HL_FKNz%`$#| z%g%CrXJ=2GKD`|@ zl4rvY>YrI#CqFvU=~izO{kZ1y=g)>|XCz!)Tr#c-3JNwpJlw8qZ!dqmPxf$!pz?(c ziOv=l7NAkw72)gUs=mHzy|~!j`?pQa4}sO;>)qbp+xzh0!-dz?yl$;ov4W#pT;FZJ zUG0UX-r_7QEDKhwIFS&Y5jVT|`MHC~kF&?_Dsh~y7i&=X$R%b^#l;8vo_%XgJlqEA zA4-_zNchdQO5Lz;(IO=lHnv6i_xFAH@ZrGo`SpE&{?x3r>*?uz`1;j!=iaE46qR4!--9OGl8*IAo;Y{T>~+PnIOe5q1813; zU7cQX`^22{uE8HOj7s$-tmZA;m-S|I&WQ<%1?A=Letvz|swE{QLF}rkU12+8{;vpI zEA{WkV}2fKGoHM>JPDf$gL88%mESJjvOm+y*Z1g!h0aT_e}8xPaQ6DWZJ-I0ROfa+ z*Q~5n7tIW=<;_~Oc(GyGn}|$#)%i2Ml0(1qd*2EPPyO9z-hSz~gu(w}$Gc|J?ydIP zH{JVpZNSZ6pn-y{tE*CXCF02K^FzuF;@}h^vxvXtJb&j0AtCl$9ywi`fk?W^ku$G(O@$~hfEzfMdy>>P~eSK&{ zO{@gNgM<*9wx2pJ)&Kh+8t1l}NcoprC-b=-J)D28YHdW#vb7Uz*O@*`C=TB0Sa8Ap z((9d$iegtkYsmlq`sK@&-%R&*7OPi%eZ{KS5@MEr&*sbf`|`iPzdwF&Z}rDltJgm| zt-qfo;FIdy>C@XcY%o|8vr}mQ-*2<;752Q%_xbwjs$u1)lt171|2N&?TUcnAcXwCo z-st{_>}lg+;>4OG|HT$(;P9fBxm={-6~Qpjj#z8JQ1aOBlWd-&8@+8o-rZd{b{4CDdw-u_U0prr&W?*O zK3)Y)fN#H7#of*)duU(n?+a_A%|Y{x5566HRQ0G+eZt(iy=$YlCp|pW`s3^M_>0@~ z<8|Wp+(@Xsy)DU!OjI{`mR4ee&Tp-W%KV+!QoZ4P`f-LS;g>+-Q>zb55L z&-}bILrczg**jL})g3>O`1Tx#}z?KySB{T`1|UW z(p{ym#jdT31+BQTu&`hV_%?H;$+b&cR?a!~O*X#^l+OR{RJaaG=d*G?{#?G==xgtq zH9DXH#wUilcjNq0v*H+z$b-5Hmk&2Lm$8@>HlE4Mh$wa{DlXD+LI zAE2ssDk3Ik&WW?9xBr^IW57zbsc?l)5`G>F6wZ@6hufZ?Ih4bXUXm_aC|G z3bVG$>w<9HE2}`0eETS!=Wd4$27gTlfP=q{5$g(3NyL5rS(N@`{w>~>m9>- z4*Nx0pV<*45+~exI_wD;a&a|zwZ*9?UT3Va0 zvRrfB_tOV*gKpoR@6-PD>66^apK*IC0(oRC0_NCOhkbc_ zJ6y)LDx^wZRn^sJrjhH89XmQ4o7p-yY%utEJAZ%YojWmWt=joywes%m$^0;5S<;1` z@&^YRl}t@TtL9FesK~}Ald(bXY;E=Tce+W*$!Gfx&)K0Dw?|{|xpU{lWUb3gcKnad z->a$@yGv#7zM7w#ew?rWw>UIDDQVG{U&nf-qu$T9Dqs4MBXO7Sna;@RX*2KqO?6HeM-}prD{7*VE6>+j`43E93X&<^JC5*T?Qw`}Os8`nP|})-|fW4ZH;{bFVyK zbm=@p@b{)WAAP=tI?qpa@0Szxp00Ow)$3+{I{`yOL$>70wY5J#rM@f$6~kYzF5R+U zH+q{-&G)*HU~ z;$Ha2IM{Q|uKB{d3^g+^pZX!2|Dyk0nb*7IVmt9Cx7=RtUG=1T=a&2IQ`hE~yuTOw z=gVdPM`z9Ni>%-OZx;tYzxj@}s<&US%6XfuwT(~Ks^ih4q?e_EffEZ#OHEhXDNgx+ zF63AB^TH!B{Wj+~!#Q*KZuYZ!I~klf%`ESybyp_Mu<-eXWue8czgM{TZOy&PlN+DW zccCL=^Ygag{-)x*H!a)rP4pvm6mEae*ma>JBU8>~K}wymd1&^Q+^cW?hyS%{ztE8( z)9-&|LnkBK^RVFHeg$dsMf|t+1qkimDReIW_=Zl#ODpWnL!~Rqvh{Tv7WAoVKILok z<(VM&cZ*v5e8x*BK<2s@-%y>iU+-=9>7Ng-tO{S>cW-aCvW^bV`#qoe4jn#xac_0` zjoY`gbEnUn*C(nS_TWgT@Q(Wbc3oXvTmJdWF>J}XdFesnnVa_~JbIKAvO3InwNLT6 zu04BfmMmMAbbg*~#I6#}KY#z`*6llQ`(4KF-;d-TS!*$2H6MrNesh<6mom+230)ob z@Z4N$(E32of`_@*ted}Q?_yha%lX}|sKvcgzvLeH z@qE$BiYF4g#h>)0uUaqlOQ*QF*f}F(#g^ExFtMPZpbxjQ*UyyA?iSNsvj6&uK?2MOwGZ_v|b_q`9;I?9Z49+0skDuUdAg?%&MCMqhict&LvnHC5}! z!*=-z6DKw(W zv5uduYHHmj&!zo~nEtCge061&rfMswhi9O5??n?M=kxX6{e8cF@i+u|o$Q+1_DtQ| zqCVE^z}1zp%+}X_W*=Xr6}jNR^>l`sKhu;S{wbYYJQp-b@@>mR33I2LN@6o3cLp)7 z^Ls1BF~`_Zf8x`dzO3={89&V4EXTh1k#1ee6^ZA^{4#E@iafb9&Y))9thWnhu3_=I zRrGDk#ISzfa}A8lAMRDZPrR}s@W$5c>2Ds}&#(Kn@{2|5*lWe;;;w z`L7MMlr_pet<=)WYLRQVn1pSW$-S!Ax|8jN)|Bou&%4tweY*I%*xhQ?GV1E;UtV41 z-dpvRi9yDqfPq_F@5nq`RnI)t|XXKmj5?xV^1n^!$33RSUGn>cfYEipokw9UY&1RokT-v$W2ZGcoM>bV_?i#Yd%c^K84DnwTmo zDiZ$v`3agQeR_KO$5YztJ#Nps%vN-LrDHSOkC)5m3rI_^o>~*~YvxLouwb2+##7%~ z-;+Tu0atFBU;k{CPOWqC)@XNk_nL1v(0nR<&j2Wo2$@Y0D12ow_CR@v+{*&(C}( z8%hK%cH=F1drNfv{(n}WE>i67vPf0EVhd|)WmQ$zs@liLcug`dxnxV9J=uHt(xnRv z9GPF1W}d$*ker*Ol~!F{{p8ud;`i};tFAuz2r78yEuTDD*vQCe$@N{IH)`x%_bg|9 z<=sh>CvROh+w!;cZnkM#+SlHW&`bt z4-b1X3vMjl_vpAgucu8t@65=Z1)(!qF8lslA-~(lYiDwSAk*3JS8jWI?wDd-bvwIn z)@<9{B_~;C?_9k57;pHzhSO(GUG2JiMR`4gfJJ)KS>aet@h9${l8faoE-!JteM!Qi zKw;nSch)>oCLZ7Zg8D$;@7KG>#mOZlC2c5t>}FwUIrFb_z{!7397~ojS2i*dNqk>braTG&gABUsROTr_Y}^rk|JVkup8C z$n5i{PlwjU?k@QKE%(W@XODKj-)C&0<$Qhj`+eP@icejg-PP3<+*R0;8Qgq(`ZoO+ zukP+P=aaMPc=IMFB_$={`nuQ`cXw~UW+rWxbKvdm?GxwC>)Wuw0JNG^>v>+sN;5;z z;%+fr0V%1jKYwas_SebA*ZEBg{^d)_ojsMp(q=gq zs_tJY&AqUIQ7?X%mlviixyK5QA$tAZ> zh(OZc3BzmQzO~a#^X^EC+mmzif|F$aUXH=xxr!OQ)ohM9zd%OPK{+gE0s`X(DMwd-x(%jZp(szWE6 z+WqgTmCsnkQ>$!cB^A9r&owPg?b4-70wN+U-|toXXG@>`7PM=x;nan-zLJJXEZ5e@ z_fMH30*c7PZM+h8H9JDjdG>+2zMZy*em+kr+_dkKx5fAEGxk@^uDE*Fy`=VT_?v$( zJ5HQ9v+-AVcd)!X z-+02p@NIX(%Uha}K8J;7O`akqBGPfNnZ0|_BBh((larHMw{PG6;>O0s683d_c2s>` z6|ui=?~6M-H{aVf?cMsH%Zs0%OL}u-qmqt}k4?>wfc0@_?PaaYM4H)ng`~~%uFU#z zRqXlgjmhqYNk=#~7Cv@cyNh4mPKS+OPUq97PcDgx3qh*^UtC{5|HXxc%>|{UqWA9I zOFBEtRLR~x{?_w{hua^`&fh0!TlGa?u64QCWHn!*@bz(9SDfwc>T*d)Sn%TF;`V~_ za&cib9|3W_7=zWiLPA1Etl#fZwkm(8v#aE#(!J{UwrfxBFP*wg|Am%@hQQ}%XNC7x zeHEHFZ=RBdhR6F|GIli)UtVAL-?4LN=gZ5>vwa0}Ufr2`EZJ}NjIN*O?f;87xA6$( z<>e(^Sm5~RNT=|k^z-vBnH3#3zbb!b=P^A!J+||j+S=YVpH8YDI(*m}WR#1WTbJKl zD^J6jyVkMu%Za>u_YR~>ZTtDtr-k3Wd#7Y%6tw5(v)PNhr|TU8?L{aQ0w?7u-zu}W zY+Si-$!)h~+ssA#@9aDFj&I?%}Mv^1#m#fyyQ!pNd`UcYD0 zdGaJ>VbIDYX1hyXP6}8VqGV}#bB)=A?$ZI09v&SPzu#_mjfJZckkw6-fMGV_wKjne(qg)I&gUz zZ;k17_ltG9A;}C3)!WZccUz&l^iE^-|9-|PJxrJ9NT0h={c2YkOZ_s_Lpd|fJArx` z5B|w<%rQRr=Ek)3+=o9sFxav3R)YJ7bcTRU8O`UN_Rc8H%==#cCv30P?QL0Ydv8CV zEqnUlii#gT(F;YpXD$3}cKpr)ft*`)2NqRL4tnSD`FUhj&7$M2-QC?cC+CKzZO^~o zHh;eSw!FJpRmrO+oO%=oYPW7kKF;^&!(o20v+iyol2gBa7u(OD)BV=_scfmxqJu7m$~IrKIaj4$mq)^XJHq$C|sY^mi~_@Jd_x zypp@O;l zud)+7|DNleD_MDtj$bojNz3(1Ri?2srdER5J&W%}&Q%)=}) zfvkt#Gv7}7EfnhGb7-pbt0mDscPx(_nW_?SYfDz&wzabo-<;T2P@0*$wesGX&|5zr zJP~quH8tq!=l#6z-YZ&*&rvH)(l(B+o@ZJ5*VFv#^KNbJnKvy@Z8P6g(6n??&^wRC zH*9uq-M)hF`7ytO;H?Kg{&2bZ^G8RAiEh-E6>FRq9Pik@+j?HrD@~tyHj;ODm1@5( z=r< zQNc4y{TQ~^I@)Jym`k0zp<%HgrSvnn`U_78d@ngYe1+lLuOq@G4&Q1-Q zzMk9Iipy{JJ(IQ9U$IQyC>maRQuXtigT7bYq-Jh^S^YUo^lD7t)hH+BMTG{dR8Cst zxyX0Rm!CazYHC>6)~{Uapc8xj+G-C^c?6x3-D6&A>9$Xrfgxh^8S%jC>4|Sn*!8E> znX6v;e4(@;b8SWZ*T>y$&$2Gdd`!y{k@@g{@fnj>q1m@r%`^$Wd1ryZFYxe%+qZfCVexRdc`CyFQ7wY_R{_>(7DOb zGM__J8_z2KerQ^vX0*9TaMM9~Md?4eyH;gpzU9-GT$p-th0RwEtz>SQ7it|nsy!y+ z))#!Yu%4UQV6`<{!B|`Tuo|00jfv^QKczggB@!kF7Gynk`!ey|&GcJbIk)uemGq3Y z#qBPK&6v8vSnc7TQczC5vp`_S_x}-I8P`_EG9PT**X6!Jb&m zVM%FL_6+sUCE|7kd2d=|U;i=8NpkR1I+w0^CnWd5&9#5+SFbuYf3i^AtBb{LLMcl@ zB@s*ALuUE?PeBQCr=udr-+U9B-K!ovyCmhQS-vE~Xmb%r(T`s|29{R&GtN70PR_W! zN|I^+2gB^v@Zhi8a zo{RjCUpyN;nf#2W?Y;dRG>k3N)8&5X=WEr)@0+%6hYe(}ICyDA@b0!L6^GBv*tyeJptX?!$|q`L)eHqX2Fif^Y+DPs3DzZ|eU%=hJuXUEn)`E#YeJZoLUt!<&kJnx<_ERQ)G zVRHWHlx_MZ;d3;M!`83-rV0tONx_muw$l=X&K?L3NdT?I z5VU?iz0?AlZ9*7r-tCn0E=**T_VU`<%<8>uY9!M-lhwItX-|{>P0D5HIsY88|H8$^ zWtkyoy0dW_`{AogeP^mXm=ydDw0hvOw3A(=d2*S-ER%KlZ#Q(D(iIn+r=M%C}Vv$p=e`1ppt>Fw2fG;^z`KQG?gTKf7LXk?ykf3VKdC9=G^*{6SR(wX|) z?wiJ~o3oy;m(H)ui@ub0x-P-@siohyawG9@_pOc4QK0N;Kd0SXJf~gh@mZB9?` z7|)&U;_vMjZ~gnW`sRsq`bYLl+3QZ7yRU}(f7Tz9EUR108lHP%J$JTGk1}&PFim66 zjZcz4`rqp1N^el*GL_x^aOpJOrB?m(_cqSi<79LDN9J0KlGm4em>HyOV!g9(YioX= zFgbihXxQb8aoTa&l|McxF7un)_3G;C$Cv%>k8*s(>WudgxqY`=bSvHQoL&*v}B3jO@g{`9WW*8*~KeJg{PKU%$hpHpF>VN!DP z#*~vnbL?twJ(AbFzCPZ3ui!+^Ywz6OOz#)F9X<2R8J{ir>uX$px%XcGdT~X@+n9@w zRBx})_`6`y5zl`4zLw366W_BXEC4M4@mbH2#cki_u57F=eojqJ{p^`jN8f#@ciWZ2 zY1uF}yMBsL0mFk=m)JU=ub-M#xAWAIw?~(CSg!utSoh}*@7@#%hBY@Tc^IP?Z4rt! zOwW7pySd(TJ@Yn~hgqj;8B{&DKb-e=$@jRdn*xVzZ$JFDch028b9Od1=mutd2+;+_ zg-qY>7E$Z|ELme4D?!HSMFqb;>8^ZpeaGn^rhfHP?j4)|sKDZ#(YYAEssCbp_pM)I zd1hPsdAS?6Zztc}lv?rg>GX#W9x$-8vv0}1&UbpcKB)J-E&o2>f|M;YmM>R-^ytxp zw{LmP&CMB9IagpzYX6he{PE$TGbo4bt^VHD-rj!i_pGk2t_>9*lfJyX44y`IaA4@_ z>f$*T9~099S^#}%sdv=VlP6D}IDg)~u+Z?*<;xG>z3ZDZM`mrVk&)4ZcklW@oAIp6 zc$D3G91gefK78hesoPg#pZwjM|7zAe>vBFj zJG&Q`mU8bZc?nw0|LD=tZnk8j(D}>${Hdv^s8BF668ih=t21a{VC}DxCr_WA%+oi& z6|h;y+FJVZGGFKAesdqZecL-@hD6D`JCa9_9!=Yu@Aqcn#ECEN@3-Go_O?sXI8DLY zI@&C3>EGKoHuB!>U-r&C_ ze?Fg|IDdZr2ELuyi5g|e*Y91*j=XhVvG})FR+d&wOw5Go)7izv#c$SSu2}cyPYtLL z6ciMUy6x`blJMih!xQJvUtfLx#`D&N7cUomfBO6V{`^n7eW&l7ui3V4^CY{BR~5US zuilauJim0=KIxvX@=L$_*VWm9dO)??%HG@%WM=0(^6~L;L1AI#t)zh#inm*@gSHB<%z1HPp@*;U z)N`&U%MUEJU@v}U>~=C=EcyP!U4IYU%FSMFaW?T?{`5=7+@>~YBo`JIDw*rwe^vkJ z(IdH^J5y4gb-bLvUifd@nVKiZubCwMVFFDe7TTB{d{L%-?T<#(HfhygyfZTk<}Z0- z0+#ylVR8QXu4DgaGe7!%`TEfxth<+PI(@Rd^}jj$k#EfEN53(vFa307cVyC^rn^Z$ z`|cmnSJ(4=dUGf1qs0q23SV9F{djpI%f^JyiEDrT-EXN-XIbdEVK;xU?gp82^pos<&(vKTGv(f2Svs35`k+zru^z$Yeshg}{F`6@PqU3rR;xwe z(ZOc+LwE1S{`md7JNx>&&P9uq6k7z8-TOrT|9xK%>b#yfcTP;&EJxt+@&4(rI5;^4 zt;^quWM*b6>FfKqw6%3@Og`@Qesze}%l8Kl9!$EstMt*ozrU5#)!o-bZ}(dhx%pT^ zjn-5z8H)mkGqcV8SKDZ4c)Yp2-G5c}uP-l^tgNE4XWLeP>+rY#8^WP@U3#J=o0dT35FZ|E{!f64sw(J=kc~x8y*3s+bh;Tac`k^W|H3!`DWsg0_mhySrOGC@839{rdHbvXfS=pM86}oL!BERoNR2 zyB`mjLHh(J&Yr#c`nP3ChNXIKw}NsnFY`^hx2F;uxXQ}TI&pgC|CsjvtJpO(j6e_D%uLd1R0UCC?Qdaxxiy&wp z)Q+;ZvvR7XtoPsDUEZzgJuTwOv|a0ckLJDF^?K8{{1!#$HjnS8|NZ^lI(znPi-HFY z0!|&TudmPkyUwUIe)jL^6*~3LCAu$P$rn<+|Iq2iy45;3*1pij z%uz*kxn|$`KG<>atgom;`9Ym3u7$ zN~*YL_-l1C^_@XON}D!s-nz}(!$ZKf`rDLqckkZ4`{3WMDJl;`w5IN;`MK$fmAbn7 z>sc=^?OnFF`n%Z1jT_UBr>(Nv`9nxpc} z^-ufOSJ!#nF1WD3G3xsFH#awTt?OL5Z`~68l_6S%A0N5CEG;YB1{(0Zxp9V-c=AS0 z#UqeK2EU%y+r{pyiG01!XxsjkH_q<-wsh|_FZ*_#pLT{{ZiU2(>&J;Ov-63hpPwgM z`}>=yd%xV&weBaMw_4Vlf4!U5btk=Rhraw(zFwv^ z)hk>6?l+T?x{Vt*f=iL3M~|{SHoi7(*LvN~SYy9nuiw&%H#en%#^;5dI1Kae**tvx zTKn1g`T9rxZ!CZQ(eTcH`2+DMpG|TWQtCKTyS-GkL#Cx`v1)1CBX@z^RZG`h3gg#Z zwdLF{RWHx(pzyoyTNb)5TDPEY$J1%nv(H-Yx34iin`dUdd-j^rJ@fk8+i%W1U-Rtm z&ok-#`Np3GSspH*Unew8CsJwJ)GGZ~pQ2Y7-Bt*lzv|PYE6=K3Zoe;ZG>Ew$RyHT7 zWaaur8~19=`ySK3@Wadkp>?tQ+b>_d{^9-C-xU(mo6hK1u8ZBTo~K`#H*0mbu-)&B zmM6tG&bh=N&aYM~RCzS#lI}d+SyzSg8Se_z1!T@%-7WlT#}$G9Yz=*KXHQ?5J5yCW z-R0~1SL+P>CUr`$x)r&&&zm=9cO8HKqe+Etf7x!y2=ib0=EXx>!@u2IZ){W-V(vA# z{QC2<$J(WZHowH;^6zsTUz9BPD>M5|=bmLD&%R7$uekmHw6`Wma!|UyRF(c^W03GnRye%{2~xclhQ-vF(q8 z0xw?_Tb?ZU;On0+1Ea*{PIHZUd3bHrKQuRO_Ta=Xl~xVf4@CwIqb#7 z?#bup*@D(t>gfEl)}J?T-i>{=yMI_|YjYPCg7y^KGAt;2dkZv1IbqVICT?*(1vND` z(7Z%alG3xYv)lV*t(h*Y+yCY1!-tH<#>Qu67&@=aZ4268S6g`V)~%?Q)0kHlTa~^F z@tJMLtEZ=zaetrfuF}_SpgDV+psy=0Z%A}5ym|5@r>5pFTV-QQOUb^zzDLeU^E$v!iUSREZ5CCu~YSR7tub*nBe!}-;2->GKvHD{gM zye>Il`uV!p*Xv$9?_GC4`PQELkl%a(|MrBw^)HpPsb~mY9hUg=(o(PUkFP$j-lDWI zyKD2NdgY4h>g2PtOeGAHSQaf>WRQEyL?>p407Jo}Bb+TQEgh-auWSG6{5cosKKs33 z?7qXiSGP}+HBmO6K5H_AtX`I@{gPr}Okec>=){gudE3>nFxMJRe-YuY336#YLvjw`&X)4v5>g$sg*`zxF6F#z?J==fcjv z`oibK?`|l0ttr#pbu_y-Yno@@+`dYNXl-tlrK!rAy3)L{s~$#_togBL`_gEC#RJ<} zlahotsDF}Ts)>G3l*4qW%su(!6uTtbb60aNo?R@sO=e%qtiEj?KH9(kEMFS!|1kIe zwGAti8ZR62{|nDonzQ=mgNl+hDMIUJyDKmCl;K%g_xD#vXQ$?OOOuToH>TEcO!v!U zJaAmTp2ytWd`;wLHa|bV2`5utTv<7JN9=7+E`|fAPI0mG$v8~a3N6WCy|BN&e$V%N z)u1TYmV3KR)q9#ouDPkH>*r@@K~t72EG!An&&{p)eAfK5ip1PGb1v+yE(fiOWGHxX zLGjFNbN=0BZqrcc;2+aRawAJ?AU7}~$|>~G)c-%~P!KE965=e%0XJ|%bly4r|< zfPiOzUIfk8OODObS!umbZ==Un*RCt}|CVN0%`*dO8r97`b-CUo2cURrL6?OYw{IXqk_P2ZNnO%GQ zH*el-n0-y>%q&yxZQD-$|G4V>_YBV3WPShC|85zVm+|J^+taBXzD_}3pMT%JeKU;H z`PRkl?YeOzVoT;_H=XFn_pj3*miJlRs_B1W^bI*l8-Dzjlq}@eyYqhQQ))#h~WYjZEu1eLq%^!%R2aZaD9 zdi;z2wS}&mtGt8_-rTifKOOeJjrU>uhGhRGms)sLy@V#Iye;i_`|(vUS?>ky=_oGxzD=)(u0_{C#%kf-<_gX#_{i& zd0|>e^6R_1&8O+d_kDeRefm!)Ar{8jX1RxE=kHs&v9DWPA5_ma&z&pF@ZitS&tA7< zV`C@IoXM%Jt$ng0CMHHeUVeRv=oGV8S68!#hll_9@wmUBtSs!8{C%O?UtbPJ=kH}r zPfrIg3I6|YcgkMdsxJr1@7JpDt9#sQu5~9~)vZUOvGVh?PEK24VPVkBA!tWBX!;&> zFypN)nNe3mZ!S5f`Qb@D-|mu^Ob<>nZ>|3JCh}c>aPF(%(%08?3&UEC7AD}R*QZo9JcvN~I+pP%1}6DJ&crTo{W?Js=XcJ6DY^|htPCW)*BmB~l1UKK4Y zEKFMT|6nuwis0paex(_!qOTXKU%l5=$M^Dg?IQm*7cXA4sQ#t{I_zwwae5MHO~Cc_ z^KWee70lk#-<#Fi1$$jC<#|-%kDOO~}gwjaq^_c;UKRb8m|!CMITFUpMzsEaTL#`|8B%p05bL_+Buy z?bYp5eVdJJPj8&~v9K(})XzQM*}eZ+g~aq8k~4h{N;ob63d z&< z7eBnQyOhuGO}tL!?ANE)IIg_yzI$svsAPP!X2rR_Z3X}C=Pt9{$?6rpz%qO@sNj86 z7Nd3Uh|PiJj&%(4?$k-iT#!4yc;1=7;bONbq)#=dQxO*mU}DY9lu;CGaJu=Wxlfw3LZFYy&W49b77$~d(GFY;U7MJ zY@B?#zVqKw@9CgpXFK})-RtV?KnFyAe-{f{=Cg0#|GQlOzTM70eD2)3JO8vaG!8sC z*u1st`tjrJwZFd|-H>?Lp#ERYlP6CWL~YFiow^|)B-9kQx9Z^S+tHcxKdn0u{PfM6 zo*OqJ_O7p)Xtn&{!Gkw;m1-9i6$JzYG$bGITlv?$dE3{O!ctOQOO~iCS-u>!dgjHg zt*dVpeSLlX__w#W7Y1k){Qp-AT8I16ed+e?=AePJGiQA6?5!4`ZJr-?D?V%YvK!w2 zwwvnc@OXN9o;Y>tk_0^OsJWKPcplX+&AIw($+_6dx0jdl@7=o>bS@=m0gs)X+`@$m zt*$OV9Wh_ANz5c1iRdJ@Z+gDzCZoJea z_V@z5Y(`oTF<(8Y=W|@26zg)4}Y5Kj%Es4p`x%j`#h;fXMt@6KQ zVf^~b`Rg3LQT+nf^6frcNm9BLR#&$VwA2Z7q-xXj>C+!PeX7bMZx{39=kxhTBj0$f zsGg7&5;En&`gnWL=9@z|HYT@Pm%rn9eQmAqp1pfn9V?7JUpqBbyEX6bF3x-R?m2mQ zbQC;4C%d&&Ohkm^e*OQwpnVPpj~{mjH3$nHAL}hj*58{S7dOu!Lwt~*sYFX&q&IOXX zbN8<4x9Pj=m%6&SalPO7dmU&q(L(R(dLTz!a^8D7d;PN9c7A!Wnwpv;o^o4OF8^Y+ zvbgH&tI)(RFD^E1-@g6A&f;`X`4(JtVy)K7UssM6{rOS2(0{&N)AHrZL8q`Onwy8` zu6cfbzBm&zGpoP9f8v7!jfZY-PH(*)Umt5DuBoYc2(-q&qN<9kv9a;c&(F`New%)4 zo#wYYcVa-k?&#1} z?iage*SEy5uxTGIy32F!+qVxK5*-~J2Tz~&wrLBg4N8ukzES1s_aIPnWolOa^QTWa zH>aN$+g1Kv@5_se&JjBb5_kSybxcikx7}mEReMGM%B(u^z4O)UPp?j$JuBMAE3MYj z+N$}s`p=KT#0v`?o$~V5Mf9(mFMeyEKI_6i@6AGg)hwG5xBRulm8TWEuIZXSTVfZ# zDg3$@mM+M%I~P#os#of?_0i_rv1xy(YwzF1!}W9ENpFTPQ6*2TRTnNf&O%!`0I~57>5j zgQOCV&+`b`y7}0j_4g~Y3I!)_I&{csj$N&kcG#K&Eu6wyZ{;=L-r9Qj$;ru})6PDI zg;j0qkv8X(HqUE`(L4V5c>l-O>-THjeWty2u1BCg}821 zi(&FHo5>q5cDIHfnwnhk!JMIC!9&~AHr8)X_ALE=^!ZW70|yx5c{o0po+!R=a`Mci z?v2ytWq@1^Y7;8{Q83#4B;(>5Ik6hsK4EPE$%)ICtINpB8diPDU}0kmtNQ!!;X}}( z79Aa(f;TrddaeJT8ay-n(A}NI?4O^XKR#7EJRvoeH9tS!XRcLfO~s}wWlFh)Iu|de zy?Ae?RJCi~^!F3fEzhM}Zd?8mlp3Tb7d!=xO2~)(Z&Mb0K4GS+{^q@|o2xQzuFj-?RVkzixf~eZoxD`d8t*GB+jrtCYRzYyrvEi0d7TxOz_NU+mqB zdskbh8$)Nlm8#ebIF~O?|7dn@=Y89muYM%PaDfIu=5v16QRRZ4pWwOU`7Qg2**)+h z7z8sLFN?$G_XQ=94~$_$J}{;c`HYzaq%&q9TCyrCDkQ91ycVz2)eo}X8agxAtn9z! zryted-Z=Kl**7D|CK1z*YQGA+P-ejMrQ$6cXw&) zmgl!N->DasO$VJ)wEccvx3Icjh?mXNuc0r$?m8B8(ag^Ky=!u!ynvvfpsj7%F}{Y5 z7pcYn|J5e9JLord^=$^tO=e$TH)+wuzIBG-TY78sH}QkQdY)Pwr@M>W_xJn%Kbl*9 z&++-Wxfgd9r|ZzYYqE7nhgwTa~_Qd3kxcvblNqo&5_IFmQ|M zIGmqn3(^7Fw5*|_aiOX@FHbM++#F7OdwUiZmIvqU|Ld5CwK_G**;Xl7SxGs!^R-T& zF5bo`+vPo7FSB$HNL*2oQBF>7hDoN7kTB>tOM$}sSFVI?$-ghRa^=cLyA++<6buam z*Zltde!u(rxV;ZvzwSP7|6hiyb&*fb-(O#wmoHDB`9oP*Sx`hoe*&*Pn|n= z?8?gEhfklj*8TnU@XSo(58uAIB_=MswNFb=?^u_p_JjBD`Qzi`Yd#!gulVqwG06Vq zEwPowE4^Ob@w&P^GiTa6`<4GzzSwbbneNh+eTULyHp12d7_a=Nr)y{^C?_X(;_TVh z_V)H0$6jAwudJfNQdd{^;?h#@nV&UF_nqFoc2mmUT{BE&H}5gavbMYOR)6xz59jUw zC%m|@@WYocM;16X3kVA{JA9sT<;wb3x7NPhaZyce<#~7C|XHT0OOCJKNH{G*~!4*?d`20!u96%cK(G67g`iNV7PPV&W5zJQicC{ zrOgf;Jje)Iu5hrK{o#`*EgLo%tli$;+PX07>Z%(%i`B2KiEN&0T|P~E_0pxPn>KI$ zc-h}R@!pavMhXy&W=U47EIw}NK9vY7np{iUpH zYc%`zzj%>RSo!VE%@?<~>w}h7|NZ@aaqjJHpyTz9{9STxyGW^Fw$-v-pLX@|m;RnT zV@AjG7e78e1|{)*f4@aDJni?){k_#D=)JbDVdm$bE3)T<4%3*($mu2~E312=Saj+? zbt|h|H++?qbbtQ-%^esR_%yG)yj&c#*JNMahX;)xu0`i}u4m_wP*71*>soJYWYogQ z%$BjY_Q8Qh&{+aMew~`C-L!l6?qd~s_x5P+18u>xDt)!&>&KASaeFE@Zpgf><|x3D z`0dTjLrXj-w^$ZG<7iUgu>bpI@`jX?LL1*aIN018y*;mUeR6X0)L)?az8zIxvu0Ls zb8)qNzh57(nRhi}$*rsFtzY^BD+u8~Db@b%Dy0Q|~6_1ID zxv;Ocy5#jW-nG%&!?ciwKQ$k{cJG&a*lT_-Kr25pbLH1>M~=8ySXp(gmo~{zkSu;_ zWM*atnpFN(v1-rWy&KccO7%z>G6e<(f(#bZi{W4ZouupJ#J$jl@(_U!`8)g9_y7}T=@7{)pl)5%gB}Um&_CY_TeFD=lhi1vGcdRxV1g# zdDtqu++%5+xxbbxJ$9R47&Xi6?^@5QDywOFv0a8tDO_4T5oqo?nj8(QiUwk3Y6q*D722(;Jn^o&_r_igi&l9cqKBO@OkXk=ayw6yEiuch0>MMN~N>Q6qY zl6)=1?N!C9TU#=lXPf6gIyF`M#Oc$`>(}c)J2SI+b@+NEGc&Q=TU!KAPxHGq<@ljP zi_Tc3r>DQUw^uqaFfil(zS^C?v$CcuaCrS)_cAia;b!qNFFYPTd0f6;=iaLqPoA*M z^l9^(Yt?ys>ACHSeJj1Le_YrXVjXgP$-2K$At6h?M9nT#7M;B8y!*<>pv-ac^5OJD zf)|Ys-nu0PItSqWy}h8Vg*msk`DXrJ8@>I=(F) z6BYOED}3B$=X>E-#i}!B&w>_qT(}VMQESGw+}lbzIy`T0ZEfDUb7#ag$hgU)*X;aq z3;gC6@Ab^q4{t7TfT$K6vW zyb9;$=05SX=;PK`zt=B%%y!|=PmR(VB_$=Ls;U_+%1Vp5`S^}qTN^!b^5o{GCMM8H zp?P_EUa3yb&J(9hY5DS{sVIi{fWIhYlTj@Z^b# z+TJDSwwr|{PqSKz4f5TKD(YjeWJ&B3!KR?(gq^dV0FH>NRbvJchIL z`-4Dr+taft-k^d)^stJIpyb^vD}zD1@xQ#eDSUT#`SE{$e;ZbR%UNm!I(HVdQ10H| z>c=|eo{>9Dd{-AtxnBV?N)$bNg;RzEaG&D3YfDYPxaeckK zj;?NCM8t~l>Lq>Vm7h{rSXnh~cb__an)~R{qe(*RVt1Em=;`TAZjrXFG7%CMzPP`> zo`sF=(4j+4EA^LDR8=V%7zhL}^J#QicwuEM=-Ab(tHVKdg64WY?Qm}6aSRI+1GQt` z-Q5ko>%lnPuNApu^{xHwetp|ZE5Fd=k*>$jfetbOZH4`MJ^uKv($}E*5FRNLj%Id# zHig=KTNg@gzG4`9`n8qamA&Gns#n)vUhaP!bmG9)>~PTR%;9$a_TJv!ihEkHkX+wm zrvOTbZ)b4Knpf~gWR7(?pKg5R)=RJVR;jxEIj$pUd*8;!2DF^;jX_+MrKRPDZQI1= zn@^oIscE9Jd(!`Zf8F}t+&u74u{NaS&WFzvJ32b%xt{sD;<)z1jt&oPke`qBN-Iu3 z{qe`g zmWPl~>FDU-IdkR)qr|11#m|v?F&DdMw`Bi3wfY!eBTOL&L%ut#bEITsL3)^P5HBkW z0R{e^MT?X~#6=~oc@~M!Tj|3#(&5oSE}C?5vfYtKs@|s4 zH0O3`X=yomc^QF*Z+8}_dB6K@)78~=$o~JI#i2LX@BhbTTm9vTczg|~qkw?aECJ_~rgQVgLWg!^7q!^Wn!A#%ersPX z+U?GK<7R}SwtM61@}LcAbpP@(MV|_tMiObbhp`cs*62Hta?C9ztBdXEh2Iwzv`67_M!;WlcQu z@x}Sl)-DrQc5=$RUA16(y6~NDfAcbn6czoqQw!?q?3pYk&2-YFNuWjRCxe$Q$h}<_bzHGDJ15{m#>qO3^N)}B2QGH&E!uwk*s(>C zo6{b>UgkUd(Y3YFRr39iLPqQQ(;F^5FK$M?-6r;R&W5KMJomojMWsb=vytB~v$rRj zKPWLdwXbe&v_@|D6Y-?-C;NBQXbZBo9?iJE$BgaQ)|Q!PL|l(DrLFBguwmZ%#N!`V zdz?OX^bmiw=P@k~hqOK>!;PL9VgL6Zcg?h#rnPbt|74?$zx%T*X3S}Q^eAb^l@(q8 zVrMPR(`9Ek&??RO>#`HZt-j{gt($a#WRSU~x8P>$@<+{2${P?o74>xSsaA2D5{s+r@r47w2B+}B- zK=%f{um1jMcIEv=RbO9OY^b*v)BPhCy)DoA$NBvkU)OA#^!(7xn>QD3>*khu^Y-GF zOyinwH=id-`WCL3q_sQY-*yR;3WI0*Klof-U2p9B8-8Z4HT&$t^=u3}QCkGK#cJBs zd}k{uJ+cVXiSN6x;cL|K#1-k6pFV${eDKdt3CpaIovE8PZAwT_SKqfTYO9u!u`#a@ zKV-z}<-0;Ry*V;#ujUo6OY5Fevh!B%Z@<{~*~Ote?=Nz(wz}Ssu5`})eQ)yh#QfyW ze)nI8V$!-g>fb#$F*D-Jw=AZaD>*-X;x7DG#dM56i@*A|*3)C*pJFVswTnYGZ*N;W z)iP%8Tf;pej{+`l4Dpg}zw}cSIbR|NOnup9`urMz2m5zuzvuzp4D! z&c%J3ZT0T-9Dis3@933>_oA+@&5XZv&%4@Mi-Dsl;GvZW7pog9>%y?Lw+xJpwaf3$ zum97yulCoWg9jO}UArb>l3~y%Z?ASOI3l9s`?3A)R;8~%s~qyby=>mG!(whyr%ff7 zd!L;1>1n#45rU+Li;h;9tFVNw`tyCEf~b3DP^$CcYm39z-`lWhQ&Vp*ZyR5&^!Imn zg|}NJCne*vBh;A|oSbn3Zx_mA~tev#pZ)Q{mzgQpv>3eAHgQ z>CKy*HL<(J6crUg9gkx@lFg~7rzzUrHQ2slkH%j&7nhKz!i~S`uX8ss1Pcpqy}j<9 zZ)5rDstwzW+V`#%t*SH)yH)oxP04ESBevUGVjH#?I6pW0TKLax&ko!D8#rvO%$&Ba zx;A0vDtopMPi7Wy+r4P4+WO{W{oW|$6o=|BA2YeUi)UUteo1xR%ENmv?sJumm~k>8 zHL-2oJKl*iW+~@w-@5I<(&)|;C(gB28;dVhnH6$();HC+d$)uiy1Fu$9ki45(vqCA z$ndS%*QdoypEd-E}O7nfPNx3}>g*gV;1 zmTk3}-TyzuFPol5Y|D|%y1J^>viKQj3`&0`u+cQU3#qj(q2hvk+>@_ zd-jqgrxSyZE;_UAxMb0lf-Ql&kxE{&2|M zxWA$mt8~t!N?hA4Y9_m*+0-ubmr|@DJ(2}>Pl)>)HmKmyUyLbDf!yzaKP5AtEaM@c7o0_IT^ex>7IG9 zVfDF~h{(U+8y9)Y1f?$bo!fQbfP>F$E8cH>e4y6H`?&wHvCkeoYIdS&$Rinn0wpP=g-?gx6Wk7PS=YC zE!KL>0&1dVU0ovfPIbxRxVM|cePVXcZVL0wT6OpQvMyGUO4g)LdZxzVnY)|kiZ0{( z9OiXprPgyL>#c9r=o+rLt^MQYh18yRx5dQL{>O4IKf7^@?(Vd7lRvkvx+iQ8PY6EB z#M*lFN6J&d!s^oBS`v@`OEEFd+5L@UHebx4HHCGE^5WI)!)*^Q9Bhu*oyHp!6f|M- z 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 literal 0 HcmV?d00001 From a27289ca48bda6aedb6c86e2c5937ae13a9c23b1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 10 Jul 2015 13:23:08 +0300 Subject: [PATCH 256/273] Fixed typo. --- Documentation/Getting-Started/Getting-Started-With-MaxScale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md index 0193fc08f..a965482ac 100644 --- a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md +++ b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md @@ -18,7 +18,7 @@ The simplest way to install MaxScale is to use one of the binary packages that a ![image alt text](images/getting_started.png) -If you want to install only MaxScale, futher down you will find the product specific download pages. Click on the MariaDB MaxScale link and follow the distriution specific instructions. +If you want to install only MaxScale, futher down you will find the product specific download pages. Click on the MariaDB MaxScale link and follow the distribution specific instructions. ![image alt text](images/getting_started2.png) From af6e010ba19302b36a0661c5ebc3c5eafe21685c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 10 Jul 2015 13:51:32 +0300 Subject: [PATCH 257/273] Added missing namedserverfilter documentation. --- Documentation/Filters/Named-Server-Filter.md | 92 ++++++++++++++++++++ server/modules/filter/namedserverfilter.c | 2 +- 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 Documentation/Filters/Named-Server-Filter.md diff --git a/Documentation/Filters/Named-Server-Filter.md b/Documentation/Filters/Named-Server-Filter.md new file mode 100644 index 000000000..3e41092de --- /dev/null +++ b/Documentation/Filters/Named-Server-Filter.md @@ -0,0 +1,92 @@ +Named Server Filter + +# Overview + +The **namedserverfilter** is a filter module for MaxScale which is able to route queries to servers based on regular expression matches. + +# Configuration + +The configuration block for the Named Server filter requires the minimal filter options in it’s section within the maxscale.cnf file, stored in /etc/maxscale.cnf. + +``` +[NamedServerFilter] +type=filter +module=namedserverfilter +match=some string +server=server2 + +[MyService] +type=service +router=readwritesplit +servers=server1,server2 +user=myuser +passwd=mypasswd +filters=NamedServerFilter +``` + +## Filter Options + +The named server filter accepts the options ignorecase or case. These define if the pattern text should take the case of the string it is matching against into consideration or not. + +## Filter Parameters + +The named server filter requires two mandatory parameters to be defined. + +### `match` + +A parameter that can be used to match text in the SQL statement which should be replaced. + +``` +match=TYPE[ ]*= +``` + +If the filter option ignorecase is used all regular expressions are evaluated with the option to ignore the case of the text, therefore a match option of select will match both type, TYPE and any form of the word with upper or lowercase characters. + +### `server` + +This is the server where matching queries will be router. The server should be in use by the service which uses this filter. + +``` +server=server2 +``` + +### `source` + +The optional source parameter defines an address that is used to match against the address from which the client connection to MaxScale originates. Only sessions that originate from this address will have the match and replacement applied to them. + +``` +source=127.0.0.1 +``` + +### `user` + +The optional user parameter defines a user name that is used to match against the user from which the client connection to MaxScale originates. Only sessions that are connected using this username will have the match and replacement applied to them. + +``` +user=john +``` + +## Examples + +### Example 1 - Route queries targeting a specific table to a server + +This will route all queries matching the regular expression ` *from *users` to the server named *server2*. The filter will ignore character case in queries. + +A query like `SELECT * FROM users` would be routed to server2 where as a query like `SELECT * FROM accounts` would be routed according to the normal rules of the router. + +``` +[NamedServerFilter] +type=filter +module=namedserverfilter +match= *from *users +options=ignorecase +server=server2 + +[MyService] +type=service +router=readwritesplit +servers=server1,server2 +user=myuser +passwd=mypasswd +filters=NamedServerFilter +``` diff --git a/server/modules/filter/namedserverfilter.c b/server/modules/filter/namedserverfilter.c index b7a5b91c1..822b5eaf2 100644 --- a/server/modules/filter/namedserverfilter.c +++ b/server/modules/filter/namedserverfilter.c @@ -35,7 +35,7 @@ extern __thread log_info_t tls_log_info; * that routes to a named server if a regular expression match is found. * @verbatim * - * A simple regular expression query rewrite filter. + * A simple regular expression based query routing filter. * Two parameters should be defined in the filter configuration * match= * server= From 8fb2cf7064a3159fd4ef9b1ed7b88c17650b546c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 10 Jul 2015 13:52:42 +0300 Subject: [PATCH 258/273] Added missing link --- Documentation/Documentation-Contents.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index a2f453a7b..fd13f3bf1 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -62,6 +62,7 @@ Here are detailed documents about the filters MaxScale offers. They contain conf - [Top N Filter](Filters/Top-N-Filter.md) - [Database Firewall Filter](Filters/Database-Firewall-Filter.md) - [RabbitMQ Filter](Filters/RabbitMQ-Filter.md) + - [Named Server Filter](Filters/Named-Server-Filter.md) ## Monitors - [MySQL Monitor](Monitors/MySQL-Monitor.md) From d97011c23eca0c3082e554a70b7a9be7fc56d1fa Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 10 Jul 2015 14:18:20 +0300 Subject: [PATCH 259/273] Moved password encryption from configuration guide to getting started. --- .../Getting-Started/Configuration-Guide.md | 31 ------------- .../Getting-Started-With-MaxScale.md | 46 ++++++++++++++++++- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index c2f815d09..df49a03eb 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -1390,37 +1390,6 @@ count=10 In addition parameters may be added to define patterns to match against to either include or exclude particular SQL statements to be duplicated. You may also define that the filter is only active for connections from a particular source or when a particular user is connected. -## Encrypting Passwords - -Passwords stored in the maxscale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. The default location MaxScale stores the keys is `/var/lib/maxscale`. - -``` - # Usage: maxkeys [PATH] -maxkeys /var/lib/maxscale/ -``` - -Changing the encryption key for MaxScale will invalidate any currently encrypted keys stored in the maxscale.cnf file. - -### Creating Encrypted Passwords - -Encrypted passwords are created by executing the maxpasswd command with the location of the .secrets file and the password you require to encrypt as an argument. - -``` -# Usage: maxpasswd PATH PASSWORD -maxpasswd /var/lib/maxscale/ MaxScalePw001 -61DD955512C39A4A8BC4BB1E5F116705 -``` - -The output of the maxpasswd command is a hexadecimal string, this should be inserted into the maxscale.cnf file in place of the ordinary, plain text, password. MaxScale will determine this as an encrypted password and automatically decrypt it before sending it the database server. - -``` -[Split Service] -type=service -router=readwritesplit -servers=server1,server2,server3,server4 -user=maxscale -password=61DD955512C39A4A8BC4BB1E5F116705 -``` ## Reloading Configuration diff --git a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md index a965482ac..56393415a 100644 --- a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md +++ b/Documentation/Getting-Started/Getting-Started-With-MaxScale.md @@ -22,7 +22,19 @@ If you want to install only MaxScale, futher down you will find the product spec ![image alt text](images/getting_started2.png) -Upon successful completion of the installation process you have a version of MaxScale that is missing only a configuration file before it can be started. +After you have installed MaxScale, you can start it. + +``` +systemctl start maxscale.service +``` + +If your system does not support systemd you can start MaxScale using the installed init.d script. + +``` +service maxscale start +``` + +An example configuration file is installed into the `/etc/` folder. This file should be changed according to your needs. ## Building MaxScale From Source Code @@ -52,6 +64,38 @@ It is also possible to use the Read/Write Splitter with Galera. Although it is n As well as the four major configuration choices outlined above there are also other configurations sub-options that may be mixed with those to provide a variety of different configuration and functionality. The MaxScale filter concept allows the basic configurations to be built upon in a large variety of ways. A separate filter tutorial is available that discusses the concept and gives some examples of ways to use filters. +## Encrypting Passwords + +Passwords stored in the maxscale.cnf file may optionally be encrypted for added security. This is done by creation of an encryption key on installation of MaxScale. Encryption keys may be created manually by executing the maxkeys utility with the argument of the filename to store the key. The default location MaxScale stores the keys is `/var/lib/maxscale`. + +``` + # Usage: maxkeys [PATH] +maxkeys /var/lib/maxscale/ +``` + +Changing the encryption key for MaxScale will invalidate any currently encrypted keys stored in the maxscale.cnf file. + +### Creating Encrypted Passwords + +Encrypted passwords are created by executing the maxpasswd command with the location of the .secrets file and the password you require to encrypt as an argument. + +``` +# Usage: maxpasswd PATH PASSWORD +maxpasswd /var/lib/maxscale/ MaxScalePw001 +61DD955512C39A4A8BC4BB1E5F116705 +``` + +The output of the maxpasswd command is a hexadecimal string, this should be inserted into the maxscale.cnf file in place of the ordinary, plain text, password. MaxScale will determine this as an encrypted password and automatically decrypt it before sending it the database server. + +``` +[Split Service] +type=service +router=readwritesplit +servers=server1,server2,server3,server4 +user=maxscale +password=61DD955512C39A4A8BC4BB1E5F116705 +``` + ## Running MaxScale MaxScale consists of a core executable and a number of modules that implement From f095ec353c806090690320c7e6093b63504c6bb4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 10 Jul 2015 21:31:33 +0300 Subject: [PATCH 260/273] Fixed config file being installed into /usr/etc instead of /etc. --- cmake/install_layout.cmake | 5 ++--- server/core/adminusers.c | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index e25bd0a8d..a14ef05c5 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -5,8 +5,7 @@ set(MAXSCALE_LIBDIR ${CMAKE_INSTALL_LIBDIR}/maxscale CACHE PATH "Library install set(MAXSCALE_BINDIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Executable installation path") set(MAXSCALE_SHAREDIR ${CMAKE_INSTALL_DATADIR}/maxscale CACHE PATH "Share file installation path, includes licence and readme files") set(MAXSCALE_DOCDIR ${CMAKE_INSTALL_DOCDIR}/maxscale CACHE PATH "Documentation installation path, text versions only") -set(MAXSCALE_CONFDIR ${CMAKE_INSTALL_SYSCONFDIR} CACHE PATH "Configuration file installation path, this is not usually needed") -# This is the only hard-coded absolute path +# These are the only hard-coded absolute paths set(MAXSCALE_VARDIR /var CACHE PATH "Data file path (usually /var/)") - +set(MAXSCALE_CONFDIR /etc CACHE PATH "Configuration file installation path (/etc/)") diff --git a/server/core/adminusers.c b/server/core/adminusers.c index 53734e9cc..4b3f9af21 100644 --- a/server/core/adminusers.c +++ b/server/core/adminusers.c @@ -19,7 +19,9 @@ #include #include #include -#define _XOPEN_SOURCE +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 700 +#endif #include #include #include From a6b35af9f6f8af372062c1922b00bdbf0c82e10d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 10 Jul 2015 22:23:10 +0300 Subject: [PATCH 261/273] Fixed release note titles. --- Documentation/Release-Notes/MaxScale-0.5-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-0.6-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-0.7-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-1.0-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-1.0.3-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-1.0.4-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-1.0.5-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-1.1-Release-Notes.md | 2 +- Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md | 4 ++-- Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Documentation/Release-Notes/MaxScale-0.5-Release-Notes.md b/Documentation/Release-Notes/MaxScale-0.5-Release-Notes.md index 3ae56ce9e..5f09d7bfd 100644 --- a/Documentation/Release-Notes/MaxScale-0.5-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-0.5-Release-Notes.md @@ -1,4 +1,4 @@ -MaxScale Release Notes +# MariaDB MaxScale 0.5 Alpha Release Notes 0.5 Alpha diff --git a/Documentation/Release-Notes/MaxScale-0.6-Release-Notes.md b/Documentation/Release-Notes/MaxScale-0.6-Release-Notes.md index ff7033e4d..4b2c445c3 100644 --- a/Documentation/Release-Notes/MaxScale-0.6-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-0.6-Release-Notes.md @@ -1,4 +1,4 @@ -MaxScale Release Notes +# MariaDB MaxScale 0.6 Alpha Release Notes 0.6 Alpha diff --git a/Documentation/Release-Notes/MaxScale-0.7-Release-Notes.md b/Documentation/Release-Notes/MaxScale-0.7-Release-Notes.md index bc43ee00d..21d9320ec 100644 --- a/Documentation/Release-Notes/MaxScale-0.7-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-0.7-Release-Notes.md @@ -1,4 +1,4 @@ -MaxScale Release Notes +# MariaDB MaxScale 0.7 Alpha Release Notes 0.7 Alpha diff --git a/Documentation/Release-Notes/MaxScale-1.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.0-Release-Notes.md index a15757e0f..4ce52f6c3 100644 --- a/Documentation/Release-Notes/MaxScale-1.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.0-Release-Notes.md @@ -1,4 +1,4 @@ -MaxScale Release Notes +# MariaDB MaxScale 1.0 Beta Release Notes 1.0 Beta diff --git a/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md index a82083ac9..5586696a4 100644 --- a/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.0.1-Release-Notes.md @@ -1,4 +1,4 @@ -MaxScale Release Notes +# MariaDB MaxScale 1.0.1 Beta Release Notes 1.0.1 Beta diff --git a/Documentation/Release-Notes/MaxScale-1.0.3-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.0.3-Release-Notes.md index 67c5e3ece..db8329366 100644 --- a/Documentation/Release-Notes/MaxScale-1.0.3-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.0.3-Release-Notes.md @@ -1,4 +1,4 @@ -MaxScale Release Notes +# MariaDB MaxScale 1.0.3 Release Notes 1.0.3 GA diff --git a/Documentation/Release-Notes/MaxScale-1.0.4-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.0.4-Release-Notes.md index db341e893..9103c830a 100644 --- a/Documentation/Release-Notes/MaxScale-1.0.4-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.0.4-Release-Notes.md @@ -1,4 +1,4 @@ -# MaxScale Release Notes +# MariaDB MaxScale 1.0.4 Release Notes 1.0.4 GA diff --git a/Documentation/Release-Notes/MaxScale-1.0.5-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.0.5-Release-Notes.md index 4b65fd92f..e979ff6c1 100644 --- a/Documentation/Release-Notes/MaxScale-1.0.5-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.0.5-Release-Notes.md @@ -1,4 +1,4 @@ -MaxScale Release Notes 1.0.5 GA +# MariaDB MaxScale 1.0.5 Release Notes This document details the changes in version 1.0.5 since the release of the 1.0.4 GA of the MaxScale product. diff --git a/Documentation/Release-Notes/MaxScale-1.1-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.1-Release-Notes.md index c3ed66b73..e196335b9 100644 --- a/Documentation/Release-Notes/MaxScale-1.1-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.1-Release-Notes.md @@ -1,4 +1,4 @@ -# MaxScale Release Notes +# MariaDB MaxScale 1.1 Release Notes ## 1.1 GA diff --git a/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md index 6c1631d4c..689323543 100644 --- a/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md @@ -1,6 +1,6 @@ -# MaxScale Release Notes +# MariaDB MaxScale 1.1.1 Release Notes -## 1.1 GA +## 1.1.1 GA This document details the changes in version 1.1.1 since the release of the 1.1 GA Release of the MaxScale product. diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index 43560ddff..4ffdcba1f 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -1,4 +1,4 @@ -# MaxScale Release Notes +# MariaDB MaxScale 1.2 Release Notes ## 1.2 GA From ed5ee2d58659ca840ce07ab46fa9e2e082addb75 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 12 Jul 2015 22:59:19 +0300 Subject: [PATCH 262/273] Updated documents. --- Documentation/Documentation-Contents.md | 2 +- ...axScale.md => MariaDB-MaxScale-Installation-Guide.md} | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) rename Documentation/Getting-Started/{Getting-Started-With-MaxScale.md => MariaDB-MaxScale-Installation-Guide.md} (95%) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index fd13f3bf1..a7a92d999 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -13,7 +13,7 @@ ## Getting Started - - [Getting Started with MaxScale](Getting-Started/Getting-Started-With-MaxScale.md) + - [MariaDB MaxScale Installation Guide](Getting-Started/MariaDB-MaxScale-Installation-Guide.md) - [Building MaxScale from Source Code](Getting-Started/Building-MaxScale-from-Source-Code.md) - [Configuration Guide](Getting-Started/Configuration-Guide.md) diff --git a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md b/Documentation/Getting-Started/MariaDB-MaxScale-Installation-Guide.md similarity index 95% rename from Documentation/Getting-Started/Getting-Started-With-MaxScale.md rename to Documentation/Getting-Started/MariaDB-MaxScale-Installation-Guide.md index 56393415a..5bf239df8 100644 --- a/Documentation/Getting-Started/Getting-Started-With-MaxScale.md +++ b/Documentation/Getting-Started/MariaDB-MaxScale-Installation-Guide.md @@ -8,9 +8,7 @@ In this introduction to MaxScale the aim is to take the reader from the point of The simplest way to install MaxScale is to use one of the binary packages that are available for download from the MariaDB website. -* Simply go to [https://mariadb.com/resources/downloads](https://mariadb.com/resources/downloads) - -* Find and Click on the button "Download MariaDB MaxScale Binaries" inder the MaxScale section +* Simply go to [http://www.mariadb.com/my_portal/download](http://www.mariadb.com/my_portal/download) * Sign in to MariaDB.com @@ -44,6 +42,8 @@ Alternatively you may download the MaxScale source and build your own binaries. The first step in configuring your MaxScale is to determine what it is you want to achieve with your MaxScale and what environment it will run in. The later is probably the easiest starting point for choosing which configuration route you wish to take. There are two distinct database environments which the first GA release of MaxScale supports; MySQL Master/Slave Replication clusters and Galera Cluster. +For more details, refer to the [Configuration Guide](Configuration-Guide.md). + ### Master/Slave Replication Clusters There are two major configuration options available to use MaxScale with a MySQL Replication cluster; connection routing with separate read and write connections, or read/write splitting with a single connection. A separate tutorial is available for each of these configurations that describes how to build the configuration file for MaxScale that will work with your environment. @@ -111,5 +111,4 @@ Configuration is read by default from the file /etc/maxscale.cnf. An example fil ## Administration Of MaxScale -There are various administration tasks that may be done with MaxScale, a client command, maxadmin, is available that will interact with a running MaxScale and allow the status of MaxScale to be monitored and give some control of the MaxScale functionality. There is a separate reference guide for the maxadmin utility and also a short administration tutorial that covers the common administration tasks that need to be done with MaxScale. - +There are various administration tasks that may be done with MaxScale, a client command, maxadmin, is available that will interact with a running MaxScale and allow the status of MaxScale to be monitored and give some control of the MaxScale functionality. There is [a separate reference guide](../Reference/MaxAdmin.md) for the maxadmin utility and also [a short administration tutorial](../Tutorials/Administration-Tutorial.md) that covers the common administration tasks that need to be done with MaxScale. From ff3c9b94cb8481d8e06fde5b04e74c39890614f1 Mon Sep 17 00:00:00 2001 From: Timofey Turenko Date: Mon, 13 Jul 2015 09:11:38 +0300 Subject: [PATCH 263/273] Add architecture and distro name to package name --- CMakeLists.txt | 3 ++- cmake/package_rpm.cmake | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef21fa1d3..a43e527bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,6 +219,7 @@ endif() # Only do packaging if configured if(PACKAGE) + execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE CPACK_PACKAGE_ARCHITECTURE) # Install the files copied by the postinst script into the share folder install(FILES ${CMAKE_BINARY_DIR}/maxscale DESTINATION ${MAXSCALE_SHAREDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) @@ -239,7 +240,7 @@ if(PACKAGE) set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${MAXSCALE_VERSION_PATCH}") set(CPACK_PACKAGE_CONTACT "MariaDB Corporation Ab") - set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}-${CPACK_PACKAGE_ARCHITECTURE}-${DISTRIB_SUFFIX}") set(CPACK_PACKAGE_NAME "maxscale") set(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab") set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/etc/DESCRIPTION) diff --git a/cmake/package_rpm.cmake b/cmake/package_rpm.cmake index 0f65bcdd2..05e09c57f 100644 --- a/cmake/package_rpm.cmake +++ b/cmake/package_rpm.cmake @@ -5,7 +5,7 @@ set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${MAXSCALE_VERSION_PATCH}") set(CPACK_PACKAGE_CONTACT "MariaDB Corporation Ab") -set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}") +set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}-${CPACK_PACKAGE_ARCHITECTURE}-${DISTRIB_SUFFIX}") set(CPACK_PACKAGE_NAME "maxscale") set(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab") set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/etc/DESCRIPTION) From 05fad5685303716f5fcef522cb1749c93462dfe0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 13 Jul 2015 12:50:48 +0300 Subject: [PATCH 264/273] Fixed DEB packages not being build if both rpmbuild and dpkg-buildpackage are found. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef21fa1d3..b5f601768 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,7 +253,8 @@ if(PACKAGE) if(NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) ) include(cmake/package_rpm.cmake) message(STATUS "Generating RPM packages") - elseif(NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" ) ) + endif() + if(NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" ) ) include(cmake/package_deb.cmake) message(STATUS "Generating DEB packages for ${DEB_ARCHITECTURE}") endif() From 9728ec6169a397fe78c7d53a84825e3247ea0d7a Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 12 Jun 2015 19:05:48 +0200 Subject: [PATCH 265/273] Addition of new mariadb10 events to diagnostics() Addition of new mariadb10 events to diagnostics() --- server/modules/include/blr.h | 135 ++++++++++++--------- server/modules/routing/binlog/blr.c | 39 +++++- server/modules/routing/binlog/blr_master.c | 6 +- 3 files changed, 116 insertions(+), 64 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 6f6d61fb6..5c1270633 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -26,7 +26,8 @@ * * Date Who Description * 02/04/14 Mark Riddoch Initial implementation - * 11/05/15 Massimilaino Pinto Added mariadb10_compat to master and slave structs + * 11/05/15 Massimiliano Pinto Added mariadb10_compat to master and slave structs + * 12/06/15 Massimiliano Pinto Added mariadb10 new events * * @endverbatim */ @@ -45,7 +46,77 @@ #define BINLOG_EVENT_HDR_LEN 19 -/* How often to call the binlog status function (seconds) */ +/** + * 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 MAX_EVENT_TYPE 0x23 + +/* New MariaDB event numbers start from 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 MAX_EVENT_TYPE_MARIADB10 0xa3 + +/* Maximum event type so far */ +#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 + +/** + * How often to call the binlog status function (seconds) + */ #define BLR_STATS_FREQ 60 #define BLR_NSTATS_MINUTES 30 @@ -212,7 +283,7 @@ typedef struct { 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[0x24]; /*< Per event counters */ + uint64_t events[MAX_EVENT_TYPE_END + 1]; /*< Per event counters */ uint64_t lastsample; int minno; int minavgs[BLR_NSTATS_MINUTES]; @@ -327,7 +398,7 @@ static char *blrm_states[] = { "Unconnected", "Connecting", "Authenticated", "Ti "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 @@mx_allowed_packet", "Register slave", "Binlog Dump", "Set MariaDB slave capability" }; + "select @@max_allowed_packet", "Register slave", "Binlog Dump", "Set MariaDB slave capability" }; #define BLRS_CREATED 0x0000 #define BLRS_UNREGISTERED 0x0001 @@ -361,62 +432,6 @@ static char *blrs_states[] = { "Created", "Unregistered", "Registered", #define COM_REGISTER_SLAVE 0x15 #define COM_BINLOG_DUMP 0x12 -/** - * 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 MAX_EVENT_TYPE 0x23 -#define MAX_EVENT_TYPE_MARIADB10 0xa3 - -/** - * 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 - /** * Macros to extract common fields */ diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 8dce5ea58..7eac9e739 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -36,6 +36,7 @@ * 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() * * @endverbatim @@ -682,6 +683,15 @@ static char *event_names[] = { "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" +}; + /** * Display an entry from the spinlock statistics data * @@ -798,11 +808,28 @@ struct tm tm; buf); dcb_printf(dcb, "\t (%d seconds ago)\n", time(0) - router_inst->stats.lastReply); - dcb_printf(dcb, "\tLast event from master: 0x%x, %s", + + if (!router_inst->mariadb10_compat) { + dcb_printf(dcb, "\tLast event from master: 0x%x, %s", router_inst->lastEventReceived, (router_inst->lastEventReceived >= 0 && - router_inst->lastEventReceived < 0x24) ? + 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", + router_inst->lastEventReceived, (ptr != NULL) ? ptr : "unknown"); + } + if (router_inst->lastEventTimestamp) { localtime_r(&router_inst->lastEventTimestamp, &tm); @@ -815,11 +842,17 @@ struct tm tm; if (router_inst->reconnect_pending) dcb_printf(dcb, "\tRouter pending reconnect to master\n"); dcb_printf(dcb, "\tEvents received:\n"); - for (i = 0; i < 0x24; i++) + 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 SPINLOCK_PROFILE dcb_printf(dcb, "\tSpinlock statistics (instlock):\n"); spinlock_stats(&instlock, spin_reporter, dcb); diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 3a0519108..86bee0b6b 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -917,6 +917,7 @@ static REP_HEADER phdr; phdr = hdr; if (hdr.ok == 0) { + int event_limit; /* * First check that the checksum we calculate matches the * checksum in the packet we received. @@ -957,8 +958,11 @@ static REP_HEADER phdr; #ifdef SHOW_EVENTS printf("blr: event type 0x%02x, flags 0x%04x, event size %d", hdr.event_type, hdr.flags, hdr.event_size); #endif - if (hdr.event_type >= 0 && hdr.event_type < 0x24) + 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 == FORMAT_DESCRIPTION_EVENT && hdr.next_pos == 0) { // Fake format description message From 38b51665430d5501e3e0df791b60e1a88fc879bc Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 13 Jul 2015 13:03:15 +0300 Subject: [PATCH 266/273] fixed double definition of CMake variables. --- CMakeLists.txt | 2 +- cmake/package_rpm.cmake | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5f601768..ee0d8523c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,7 +270,7 @@ add_custom_target(buildtests ) add_custom_target(testall - COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N -DWITH_MAXSCALE_CNF=N -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR} COMMAND make install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake/testall.cmake COMMENT "Running full test suite..." VERBATIM) diff --git a/cmake/package_rpm.cmake b/cmake/package_rpm.cmake index 0f65bcdd2..7f11587a7 100644 --- a/cmake/package_rpm.cmake +++ b/cmake/package_rpm.cmake @@ -1,15 +1,5 @@ # RPM specific CPack configuration parameters set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale") -set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}") -set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}") -set(CPACK_PACKAGE_VERSION_PATCH "${MAXSCALE_VERSION_PATCH}") -set(CPACK_PACKAGE_CONTACT "MariaDB Corporation Ab") -set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}") -set(CPACK_PACKAGE_NAME "maxscale") -set(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab") -set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/etc/DESCRIPTION) -set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") set(CPACK_RPM_PACKAGE_RELEASE ${MAXSCALE_BUILD_NUMBER}) set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/postinst) set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_BINARY_DIR}/postrm) From 584ad321ff2e0b85bdb13b34803ff08cebad1fde Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 13 Jul 2015 13:22:19 +0300 Subject: [PATCH 267/273] Fixed possible crash. --- server/core/dbusers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 6748a1437..57594f63d 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -210,7 +210,7 @@ HASHTABLE *oldresources; oldusers = service->users; /* digest compare */ - if (memcmp(oldusers->cksum, newusers->cksum, SHA_DIGEST_LENGTH) == 0) { + if (oldusers != NULL && memcmp(oldusers->cksum, newusers->cksum, SHA_DIGEST_LENGTH) == 0) { /* same data, nothing to do */ LOGIF(LD, (skygw_log_write_flush( LOGFILE_DEBUG, @@ -234,7 +234,7 @@ HASHTABLE *oldresources; spinlock_release(&service->spin); - if (i) { + if (i && oldusers) { /* free the old table */ users_free(oldusers); } From d799a2d6765e2fa699241bfd73b80be6fc841611 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 13 Jul 2015 19:22:06 +0300 Subject: [PATCH 268/273] Fixed title. --- .../Getting-Started/MariaDB-MaxScale-Installation-Guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Getting-Started/MariaDB-MaxScale-Installation-Guide.md b/Documentation/Getting-Started/MariaDB-MaxScale-Installation-Guide.md index 5bf239df8..31b713121 100644 --- a/Documentation/Getting-Started/MariaDB-MaxScale-Installation-Guide.md +++ b/Documentation/Getting-Started/MariaDB-MaxScale-Installation-Guide.md @@ -1,4 +1,4 @@ -# Getting Started With MariaDB MaxScale +# MariaDB MaxScale Installation Guide ## First Steps With MaxScale From a1801237a8135cc277eb0748e6dbcf2c58d3e2bf Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 13 Jul 2015 19:35:38 +0300 Subject: [PATCH 269/273] Fixed error messages being printed for existing directories. --- server/core/service.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/server/core/service.c b/server/core/service.c index 4e36708f8..84e6f9b13 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -278,10 +278,13 @@ GWPROTOCOL *funcs; if(mkdir_rval) { - skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", - path, - errno, - strerror(errno)); + if(errno != EEXIST) + { + skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", + path, + errno, + strerror(errno)); + } mkdir_rval = 0; } @@ -293,10 +296,13 @@ GWPROTOCOL *funcs; if(mkdir_rval) { - skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", - path, - errno, - strerror(errno)); + if(errno != EEXIST) + { + skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", + path, + errno, + strerror(errno)); + } mkdir_rval = 0; } strncat(path, "/dbusers", PATH_MAX); From b592e226e2c56b4b80215cdc37459e5e79beeb21 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 14 Jul 2015 12:05:56 +0300 Subject: [PATCH 270/273] converted inline functions to normal functions. --- server/modules/routing/binlog/blr_master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 86bee0b6b..89b6f8c75 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -77,7 +77,7 @@ static int blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *pkt, REP_HEADER * void blr_distribute_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr); static void *CreateMySQLAuthData(char *username, char *password, char *database); void blr_extract_header(uint8_t *pkt, REP_HEADER *hdr); -inline uint32_t extract_field(uint8_t *src, int bits); +static uint32_t extract_field(uint8_t *src, int bits); static void blr_log_packet(logfile_id_t file, char *msg, uint8_t *ptr, int len); static void blr_master_close(ROUTER_INSTANCE *); static char *blr_extract_column(GWBUF *buf, int col); @@ -1166,7 +1166,7 @@ blr_extract_header(register uint8_t *ptr, register REP_HEADER *hdr) * @param src The raw packet source * @param bits The number of bits to extract (multiple of 8) */ -inline uint32_t +static uint32_t extract_field(register uint8_t *src, int bits) { register uint32_t rval = 0, shift = 0; From 40f4d8ee8f88f3bca6fa2b7f6c9da0644f52ba87 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 14 Jul 2015 12:10:41 +0300 Subject: [PATCH 271/273] Revert "converted inline functions to normal functions." This reverts commit b592e226e2c56b4b80215cdc37459e5e79beeb21. --- server/modules/routing/binlog/blr_master.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 89b6f8c75..86bee0b6b 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -77,7 +77,7 @@ static int blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *pkt, REP_HEADER * void blr_distribute_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr); static void *CreateMySQLAuthData(char *username, char *password, char *database); void blr_extract_header(uint8_t *pkt, REP_HEADER *hdr); -static uint32_t extract_field(uint8_t *src, int bits); +inline uint32_t extract_field(uint8_t *src, int bits); static void blr_log_packet(logfile_id_t file, char *msg, uint8_t *ptr, int len); static void blr_master_close(ROUTER_INSTANCE *); static char *blr_extract_column(GWBUF *buf, int col); @@ -1166,7 +1166,7 @@ blr_extract_header(register uint8_t *ptr, register REP_HEADER *hdr) * @param src The raw packet source * @param bits The number of bits to extract (multiple of 8) */ -static uint32_t +inline uint32_t extract_field(register uint8_t *src, int bits) { register uint32_t rval = 0, shift = 0; From 96edaca90c0d5c36999599929962b1aec2ac5bb5 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 14 Jul 2015 12:14:01 +0300 Subject: [PATCH 272/273] Disabled C99. --- cmake/macros.cmake | 2 +- server/modules/monitor/mysql_mon.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/macros.cmake b/cmake/macros.cmake index 848b3290f..49f4096dc 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -21,7 +21,7 @@ endmacro() macro(set_variables) # Use C99 - set(USE_C99 TRUE CACHE BOOL "Use C99 standard") + set(USE_C99 FALSE CACHE BOOL "Use C99 standard") # Install the template maxscale.cnf file set(WITH_MAXSCALE_CNF TRUE CACHE BOOL "Install the template maxscale.cnf file") diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 8cc78340f..a37526bb5 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -525,7 +525,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) { MONITOR_SERVERS* database = mon->databases; MONITOR_SERVERS *ptr,*rval = NULL; - + int i; while(database) { bool ismaster = false; @@ -587,7 +587,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) while(ptr) { - for(int i = 0;ptr->server->slaves[i];i++) + for(i = 0;ptr->server->slaves[i];i++) { if(ptr->server->slaves[i] == database->server->node_id) { From 124ad82de491adf1f6d5d675d737f83d2b64be6c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 15 Jul 2015 11:02:59 +0300 Subject: [PATCH 273/273] Small fix to readwritesplit session command handling. --- server/modules/routing/readwritesplit/readwritesplit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index b35a674c4..d6dba8e01 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -4049,12 +4049,11 @@ static bool execute_sescmd_in_backend( sescmd_cursor_set_active(scur, true); } - buf = sescmd_cursor_clone_querybuf(scur); - switch (scur->scmd_cur_cmd->my_sescmd_packet_type) { case MYSQL_COM_CHANGE_USER: /** This makes it possible to handle replies correctly */ gwbuf_set_type(scur->scmd_cur_cmd->my_sescmd_buf, GWBUF_TYPE_SESCMD); + buf = sescmd_cursor_clone_querybuf(scur); rc = dcb->func.auth( dcb, NULL, @@ -4087,6 +4086,7 @@ static bool execute_sescmd_in_backend( */ gwbuf_set_type(scur->scmd_cur_cmd->my_sescmd_buf, GWBUF_TYPE_SESCMD); + buf = sescmd_cursor_clone_querybuf(scur); rc = dcb->func.write( dcb, buf);