From 5adae0ff3b120c5cd2009a874e45ceef4eb2a7ff Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 23 Aug 2015 20:46:16 +0300 Subject: [PATCH 01/74] Added missing return value. --- 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 06932e206..417abdfe7 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1684,7 +1684,7 @@ static skygw_query_type_t is_read_tmp_table( { skygw_log_write(LE,"[%s] Error: Master server reference is NULL.", __FUNCTION__); - return; + return type; } rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES]; From 799844e24389e593fa7f3c7603e035227dc5798b Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 24 Aug 2015 10:52:26 +0200 Subject: [PATCH 02/74] Fixed MXS-111 https://mariadb.atlassian.net/browse/MXS-111 Fixed MXS-111 https://mariadb.atlassian.net/browse/MXS-111 --- server/modules/routing/binlog/blr_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 351f30336..0a80a4d54 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -950,7 +950,7 @@ int n_bufs = -1, pn_bufs = -1; return; } } - router->stats.n_binlogs++; + router->lastEventReceived = hdr.event_type; router->lastEventTimestamp = hdr.timestamp; From 489980f4e5129622e63247dccbb89a27d6726aac Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 24 Aug 2015 11:49:11 +0300 Subject: [PATCH 03/74] Moved unpack_rpm.sh from root to script directory. --- unpack_rpm.sh => develop | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename unpack_rpm.sh => develop (100%) diff --git a/unpack_rpm.sh b/develop similarity index 100% rename from unpack_rpm.sh rename to develop From 082c3b6f9c5035c9796589df4bb8bc8e148e4dfc Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 24 Aug 2015 13:05:07 +0300 Subject: [PATCH 04/74] Utility for mapping binary+address to source-file + line. --- script/stacktrace | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100755 script/stacktrace diff --git a/script/stacktrace b/script/stacktrace new file mode 100755 index 000000000..328233017 --- /dev/null +++ b/script/stacktrace @@ -0,0 +1,87 @@ +#!/bin/bash + +# How we recognize whether a line in the file is from the stack-trace. +STACK_LINE=".*\(.*\) \[.*\]" +# How we match the date + time part of a line. +DATE_TIME="[0-9]\+-[0-9]\+-[0-9]\+ [0-9]\+:[0-9]\+:[0-9]\+" + +function print_usage { + echo "usage: stacktrace [-p prefix] [stacktrace.txt]" + echo + echo "-p frefix: The path prefix (e.g. /usr/local/mariadb-maxscale/) " + echo " to remove when searching for files." + echo + echo "stacktrace.txt: A file containing a stack-trace." + exit 1 +} + +function print_usage_and_exit { + print_usage + exit 1 +} + +function parse_stack_trace { + local prefix=$1 + local file=$2 + + cat $file | egrep "$STACK_LINE" | sed "s/$DATE_TIME//" | \ + while read line + do + local path=${line%%(*} + local entry="("${line##*(} + + path=${path#$prefix} + + if [ -e "${path}" ] + then + file ${path} | fgrep -q executable + let rc=$? + local address; + + if [ $rc -eq 0 ] + then + address=${entry#*\[} + address=${address%\]*} + else + address=${entry#*\+} + address=${address%\)*} + fi + + addr2line -e ${path} ${address} + else + echo ${line} + fi + done +} + +function main { + local prefix + local key + + while [[ $# > 1 ]] + do + key="$1" + + case $key in + -h) + print_usage + exit + ;; + -p|--prefix) + prefix="$2"; + shift + ;; + + *) + echo "error: Unknown parameter $key" + print_usage_and_exit + esac + shift + done + + local file=$1 + + parse_stack_trace "$prefix" "$file" +} + +main $* From 1731a90fad383c0ba50bc916e464e64908098e48 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Tue, 25 Aug 2015 11:37:17 +0200 Subject: [PATCH 05/74] Fix for lastEventTimestamp localtime Fix for lastEventTimestamp localtime computation --- server/modules/routing/binlog/blr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 630665628..ca853b1e6 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -854,7 +854,8 @@ struct tm tm; if (router_inst->lastEventTimestamp) { - localtime_r((const time_t*)&router_inst->lastEventTimestamp, &tm); + time_t last_event = (time_t)router_inst->lastEventTimestamp; + localtime_r(&last_event, &tm); asctime_r(&tm, buf); dcb_printf(dcb, "\tLast binlog event timestamp: %ld (%s)\n", router_inst->lastEventTimestamp, buf); @@ -982,7 +983,8 @@ struct tm tm; if (session->lastEventTimestamp && router_inst->lastEventTimestamp) { - localtime_r((const time_t*)&session->lastEventTimestamp, &tm); + time_t session_last_event = (time_t)session->lastEventTimestamp; + localtime_r(&session_last_event, &tm); asctime_r(&tm, buf); dcb_printf(dcb, "\t\tLast binlog event timestamp %u, %s", session->lastEventTimestamp, buf); dcb_printf(dcb, "\t\tSeconds behind master %u\n", router_inst->lastEventTimestamp - session->lastEventTimestamp); From b1d6096fa8e10d435ca9caeaedfc35a14f0d1466 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 19 Jul 2015 10:56:40 +0300 Subject: [PATCH 06/74] Added a check for running MaxScale processes. --- server/core/gateway.c | 91 ++++++++++++++++++++++++++++++++++++++- server/include/maxscale.h | 2 +- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 777e35ddb..f25d2207f 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -207,7 +207,7 @@ static bool resolve_maxscale_conf_fname( static char* check_dir_access(char* dirname,bool,bool); static int set_user(); - +bool pid_file_exists(); /** SSL multi-threading functions and structures */ static SPINLOCK* ssl_locks; @@ -1816,6 +1816,13 @@ int main(int argc, char **argv) "MaxScale is running in process %i", getpid()))); + /** Check if a MaxScale process is already running */ + if(pid_file_exists()) + { + rc = MAXSCALE_ALREADYRUNNING; + goto return_main; + } + /* Write process pid into MaxScale pidfile */ write_pid_file(); @@ -2000,6 +2007,88 @@ static void unlink_pidfile(void) } } +/** + * Check if a PID file is already written and a MaxScale process is running. + * @return True if the file exists and MaxScale should exit + */ +bool pid_file_exists() +{ + char pathbuf[PATH_MAX+1]; + char pidbuf[1024]; + pid_t pid; + int fd = -1; + int b; + + snprintf(pathbuf, PATH_MAX, "%s/maxscale.pid",piddir?piddir:default_piddir); + + if(access(pathbuf,F_OK) != 0) + return false; + + if(access(pathbuf,R_OK) == 0) + { + fd = open(pathbuf, O_RDONLY); + if(fd == -1) + { + close(fd); + char* logerr = "Error: Failed to open PID file."; + print_log_n_stderr(true, true, logerr, logerr, errno); + return true; + } + + if((b = read(fd,pidbuf,1024)) == -1) + { + close(fd); + char* logerr = "Error: Failed to read from PID file."; + print_log_n_stderr(true, true, logerr, logerr, errno); + return true; + } + close(fd); + + if(b == 0 ) + { + /** Empty file */ + char* logerr = "Error: PID file was empty."; + print_log_n_stderr(true, true, logerr, logerr, errno); + return true; + } + + pidbuf[b < 1024? b:1023] = '\0'; + pid = strtol(pidbuf,NULL,0); + + if(pid == 0 ) + { + /** Bad PID */ + char* logerr = "Error: PID file contents not valid."; + print_log_n_stderr(true, true, logerr, logerr, errno); + return true; + } + + if(kill(pid,0) == -1) + { + if(errno == ESRCH) + { + /** no such process, old PID file */ + return false; + } + } + else + { + char* logerr = "Error: MaxScale is already running. Process id: %d"; + char logbuf[1024]; + sprintf(logbuf,logerr,pid); + print_log_n_stderr(true, true, logbuf, logbuf, 0); + return true; + } + } + else + { + char* logerr = "Error: Cannot open PID file, no read permissions."; + print_log_n_stderr(true, true, logerr, logerr, 0); + return true; + } + return true; +} + /** * Write process pid into pidfile anc close it * Parameters: diff --git a/server/include/maxscale.h b/server/include/maxscale.h index 521c42dd9..b178c45a8 100644 --- a/server/include/maxscale.h +++ b/server/include/maxscale.h @@ -37,7 +37,7 @@ #define MAXSCALE_BADCONFIG 1 /* Configuration fiel error */ #define MAXSCALE_NOLIBRARY 2 /* No embedded library found */ #define MAXSCALE_NOSERVICES 3 /* No servics are running */ -#define MAXSCALE_HOMELESS 4 /* No MaxScale Home */ +#define MAXSCALE_ALREADYRUNNING 4 /* MaxScale is already runing */ #define MAXSCALE_BADARG 5 /* Bad command line argument */ #define MAXSCALE_INTERNALERROR 6 /* Internal error, see error log */ #endif From 1dd22a4d2c8b3638811849e744f4535499d2f146 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 20 Jul 2015 09:35:46 +0300 Subject: [PATCH 07/74] Cleaned up code and error messages. --- server/core/gateway.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index f25d2207f..6eacef4cc 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1819,6 +1819,9 @@ int main(int argc, char **argv) /** Check if a MaxScale process is already running */ if(pid_file_exists()) { + /** There is a process with the PID of the maxscale.pid file running. + * Assuming that this is an already running MaxScale process, we + * should exit with an error code. */ rc = MAXSCALE_ALREADYRUNNING; goto return_main; } @@ -2008,16 +2011,18 @@ static void unlink_pidfile(void) } /** - * Check if a PID file is already written and a MaxScale process is running. - * @return True if the file exists and MaxScale should exit + * Check if the maxscale.pid file exists and has a valid PID in it. If one has already been + * written and a MaxScale process is running, this instance of MaxScale should shut down. + * @return True if the conditions for starting MaxScale are not met and false if + * no PID file was found or there is no process running with the PID of the maxscale.pid + * file. If false is returned, this process should continue normally. */ bool pid_file_exists() { char pathbuf[PATH_MAX+1]; char pidbuf[1024]; pid_t pid; - int fd = -1; - int b; + int fd,b; snprintf(pathbuf, PATH_MAX, "%s/maxscale.pid",piddir?piddir:default_piddir); @@ -2026,11 +2031,9 @@ bool pid_file_exists() if(access(pathbuf,R_OK) == 0) { - fd = open(pathbuf, O_RDONLY); - if(fd == -1) + if((fd = open(pathbuf, O_RDONLY)) == -1) { - close(fd); - char* logerr = "Error: Failed to open PID file."; + char* logerr = "Failed to open PID file."; print_log_n_stderr(true, true, logerr, logerr, errno); return true; } @@ -2038,7 +2041,7 @@ bool pid_file_exists() if((b = read(fd,pidbuf,1024)) == -1) { close(fd); - char* logerr = "Error: Failed to read from PID file."; + char* logerr = "Failed to read from PID file."; print_log_n_stderr(true, true, logerr, logerr, errno); return true; } @@ -2047,19 +2050,19 @@ bool pid_file_exists() if(b == 0 ) { /** Empty file */ - char* logerr = "Error: PID file was empty."; - print_log_n_stderr(true, true, logerr, logerr, errno); + char* logerr = "PID file was empty."; + print_log_n_stderr(true, true, logerr, logerr, 0); return true; } pidbuf[b < 1024? b:1023] = '\0'; pid = strtol(pidbuf,NULL,0); - if(pid == 0 ) + if(pid < 1) { /** Bad PID */ - char* logerr = "Error: PID file contents not valid."; - print_log_n_stderr(true, true, logerr, logerr, errno); + char* logerr = "PID file contents not valid."; + print_log_n_stderr(true, true, logerr, logerr, 0); return true; } @@ -2070,10 +2073,17 @@ bool pid_file_exists() /** no such process, old PID file */ return false; } + else + { + char* logerr = "Failed to send signal to process %d"; + char logbuf[1024]; + sprintf(logbuf,logerr,pid); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + } } else { - char* logerr = "Error: MaxScale is already running. Process id: %d"; + char* logerr = "MaxScale is already running. Process id: %d"; char logbuf[1024]; sprintf(logbuf,logerr,pid); print_log_n_stderr(true, true, logbuf, logbuf, 0); @@ -2082,7 +2092,7 @@ bool pid_file_exists() } else { - char* logerr = "Error: Cannot open PID file, no read permissions."; + char* logerr = "Cannot open PID file, no read permissions."; print_log_n_stderr(true, true, logerr, logerr, 0); return true; } From 0a33174803c43050977d29b28db39c08bc134d77 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 24 Aug 2015 15:01:34 +0300 Subject: [PATCH 08/74] Added PID file locks and cleaned up code. --- server/core/gateway.c | 153 ++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 49 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 6eacef4cc..3e9303bbd 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -86,6 +86,9 @@ #include #include #include +#include + +#define STRING_BUFFER_SIZE 1024 /** for procname */ #if !defined(_GNU_SOURCE) @@ -139,7 +142,7 @@ 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 int pidfd = -1; /** * exit flag for log flusher. @@ -181,6 +184,7 @@ static void log_flush_shutdown(void); static void log_flush_cb(void* arg); static int write_pid_file(); /* write MaxScale pidfile */ static void unlink_pidfile(void); /* remove pidfile */ +static void unlock_pidfile(); static void libmysqld_done(void); static bool file_write_header(FILE* outfile); static bool file_write_footer(FILE* outfile); @@ -1827,7 +1831,12 @@ int main(int argc, char **argv) } /* Write process pid into MaxScale pidfile */ - write_pid_file(); + if(write_pid_file() != 0) + { + skygw_log_write(LE,"Error: Failed to write PID file. Exiting."); + rc = MAXSCALE_ALREADYRUNNING; + goto return_main; + } /* Init MaxScale poll system */ poll_init(); @@ -1932,6 +1941,7 @@ int main(int argc, char **argv) unload_all_modules(); /* Remove Pidfile */ + unlock_pidfile(); unlink_pidfile(); return_main: @@ -1993,6 +2003,19 @@ static void log_flush_cb( "Finished MaxScale log flusher."))); } +static void unlock_pidfile() +{ + if(pidfd != -1) + if(flock(pidfd,LOCK_UN|LOCK_NB) != 0) + { + char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; + char* logerr = "Failed to unlock PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pidfile); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + } + close(pidfd); +} + /** * Unlink pid file, called at program exit */ @@ -2020,49 +2043,67 @@ static void unlink_pidfile(void) bool pid_file_exists() { char pathbuf[PATH_MAX+1]; - char pidbuf[1024]; + char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; + char pidbuf[STRING_BUFFER_SIZE]; pid_t pid; - int fd,b; snprintf(pathbuf, PATH_MAX, "%s/maxscale.pid",piddir?piddir:default_piddir); + pathbuf[PATH_MAX] = '\0'; if(access(pathbuf,F_OK) != 0) return false; if(access(pathbuf,R_OK) == 0) { + int fd, b; + if((fd = open(pathbuf, O_RDONLY)) == -1) { - char* logerr = "Failed to open PID file."; - print_log_n_stderr(true, true, logerr, logerr, errno); + char* logerr = "Failed to open PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); + print_log_n_stderr(true, true, logbuf, logbuf, errno); return true; } + if(flock(fd,LOCK_EX|LOCK_NB) != 0) + { + close(fd); + char* logerr = "Failed to lock PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pidfile); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + return true; + } - if((b = read(fd,pidbuf,1024)) == -1) + pidfd = fd; + b = read(fd,pidbuf,sizeof(pidbuf)); + + if(b == -1) { - close(fd); - char* logerr = "Failed to read from PID file."; - print_log_n_stderr(true, true, logerr, logerr, errno); + char* logerr = "Failed to read from PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + unlock_pidfile(); return true; } - close(fd); - - if(b == 0 ) + else if(b == 0) { /** Empty file */ - char* logerr = "PID file was empty."; - print_log_n_stderr(true, true, logerr, logerr, 0); + char* logerr = "PID file read from '%s'. File was empty."; + snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + unlock_pidfile(); return true; } - pidbuf[b < 1024? b:1023] = '\0'; + pidbuf[b < sizeof(pidbuf) ? b : sizeof(pidbuf) - 1] = '\0'; pid = strtol(pidbuf,NULL,0); if(pid < 1) { /** Bad PID */ - char* logerr = "PID file contents not valid."; - print_log_n_stderr(true, true, logerr, logerr, 0); + char* logerr = "PID file read from '%s'. File contents not valid."; + snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + unlock_pidfile(); return true; } @@ -2075,25 +2116,26 @@ bool pid_file_exists() } else { - char* logerr = "Failed to send signal to process %d"; - char logbuf[1024]; - sprintf(logbuf,logerr,pid); + char* logerr = "Failed to check the existence of process %d"; + snprintf(logbuf,sizeof(logbuf),logerr,pid); print_log_n_stderr(true, true, logbuf, logbuf, errno); + unlock_pidfile(); } } else { char* logerr = "MaxScale is already running. Process id: %d"; - char logbuf[1024]; - sprintf(logbuf,logerr,pid); + snprintf(logbuf,sizeof(logbuf),logerr,pid); print_log_n_stderr(true, true, logbuf, logbuf, 0); + unlock_pidfile(); return true; } } else { - char* logerr = "Cannot open PID file, no read permissions."; - print_log_n_stderr(true, true, logerr, logerr, 0); + char* logerr = "Cannot open PID file '%s', no read permissions."; + snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); + print_log_n_stderr(true, true, logbuf, logbuf, errno); return true; } return true; @@ -2108,33 +2150,46 @@ bool pid_file_exists() */ static int write_pid_file() { + char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; + char pidstr[STRING_BUFFER_SIZE]; - int fd = -1; + if(pidfd == -1) + { + int fd = -1; + snprintf(pidfile, PATH_MAX, "%s/maxscale.pid",get_piddir()); + fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); + if (fd == -1 && errno != ENOENT) { + char* logerr = "Failed to open PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pidfile); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + return -1; + } - snprintf(pidfile, PATH_MAX, "%s/maxscale.pid",get_piddir()); - - fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); - if (fd == -1) { - fprintf(stderr, "MaxScale failed to open pidFile %s: error %d, %s\n", pidfile, errno, strerror(errno)); - return 1; - } else { - char pidstr[50]=""; - /* truncate pidfile content */ - if (ftruncate(fd, 0) == -1) { - fprintf(stderr, "MaxScale failed to truncate pidfile %s: error %d, %s\n", pidfile, errno, strerror(errno)); - } - - snprintf(pidstr, sizeof(pidstr)-1, "%d", getpid()); - - if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) { - fprintf(stderr, "MaxScale failed to write into pidfile %s: error %d, %s\n", pidfile, errno, strerror(errno)); - /* close file and return */ - close(fd); - return 1; - } - - /* close file */ + if(flock(fd,LOCK_EX|LOCK_NB) != 0) + { + char* logerr = "Failed to lock PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pidfile); + print_log_n_stderr(true, true, logbuf, logbuf, errno); close(fd); + return -1; + } + pidfd = fd; + } + + /* truncate pidfile content */ + if (ftruncate(pidfd, 0) == -1) { + fprintf(stderr, "MaxScale failed to truncate pidfile %s: error %d, %s\n", pidfile, errno, strerror(errno)); + unlock_pidfile(); + return -1; + } + + snprintf(pidstr, sizeof(pidstr)-1, "%d", getpid()); + + if (pwrite(pidfd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) { + fprintf(stderr, "MaxScale failed to write into pidfile %s: error %d, %s\n", pidfile, errno, strerror(errno)); + /* close file and return */ + unlock_pidfile(); + return -1; } /* success */ From 063c8f904ad5d07b5a58458bde7a7c653ebe1466 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 24 Aug 2015 16:04:08 +0300 Subject: [PATCH 09/74] Fixed wrong file open mode. --- 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 3e9303bbd..c5107b3bd 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -2057,7 +2057,7 @@ bool pid_file_exists() { int fd, b; - if((fd = open(pathbuf, O_RDONLY)) == -1) + if((fd = open(pathbuf, O_RDWR)) == -1) { char* logerr = "Failed to open PID file '%s'."; snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); From 372403760c4adeff5614e795cab879e173c361e8 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 24 Aug 2015 16:13:06 +0300 Subject: [PATCH 10/74] Cleaned up code. --- server/core/gateway.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index c5107b3bd..86dc9e051 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -2064,7 +2064,7 @@ bool pid_file_exists() print_log_n_stderr(true, true, logbuf, logbuf, errno); return true; } - if(flock(fd,LOCK_EX|LOCK_NB) != 0) + if(flock(fd,LOCK_EX|LOCK_NB)) { close(fd); char* logerr = "Failed to lock PID file '%s'."; @@ -2158,14 +2158,14 @@ static int write_pid_file() { int fd = -1; snprintf(pidfile, PATH_MAX, "%s/maxscale.pid",get_piddir()); fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); - if (fd == -1 && errno != ENOENT) { + if (fd == -1) { char* logerr = "Failed to open PID file '%s'."; snprintf(logbuf,sizeof(logbuf),logerr,pidfile); print_log_n_stderr(true, true, logbuf, logbuf, errno); return -1; } - if(flock(fd,LOCK_EX|LOCK_NB) != 0) + if(flock(fd,LOCK_EX|LOCK_NB)) { char* logerr = "Failed to lock PID file '%s'."; snprintf(logbuf,sizeof(logbuf),logerr,pidfile); @@ -2177,7 +2177,7 @@ static int write_pid_file() { } /* truncate pidfile content */ - if (ftruncate(pidfd, 0) == -1) { + if (ftruncate(pidfd, 0)) { fprintf(stderr, "MaxScale failed to truncate pidfile %s: error %d, %s\n", pidfile, errno, strerror(errno)); unlock_pidfile(); return -1; From f58e7af94dea02522e29d71ad1c13f16c45e16c0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 25 Aug 2015 06:56:05 +0300 Subject: [PATCH 11/74] Added a define for the PID file desciptor initial value. --- server/core/gateway.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 86dc9e051..df47961a7 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -89,6 +89,7 @@ #include #define STRING_BUFFER_SIZE 1024 +#define PIDFD_CLOSED -2 /** for procname */ #if !defined(_GNU_SOURCE) @@ -142,7 +143,7 @@ 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 int pidfd = -1; +static int pidfd = PIDFD_CLOSED; /** * exit flag for log flusher. @@ -2005,7 +2006,7 @@ static void log_flush_cb( static void unlock_pidfile() { - if(pidfd != -1) + if(pidfd != PIDFD_CLOSED) if(flock(pidfd,LOCK_UN|LOCK_NB) != 0) { char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; @@ -2153,7 +2154,7 @@ static int write_pid_file() { char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; char pidstr[STRING_BUFFER_SIZE]; - if(pidfd == -1) + if(pidfd == PIDFD_CLOSED) { int fd = -1; snprintf(pidfile, PATH_MAX, "%s/maxscale.pid",get_piddir()); From 1658e3d704d5e21785f9a93a68f7d2ec3b66b9a2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 25 Aug 2015 11:43:55 +0300 Subject: [PATCH 12/74] Added more verbose error messages and fixed bugs. --- server/core/gateway.c | 80 +++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index df47961a7..2eceea4b2 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -89,7 +89,7 @@ #include #define STRING_BUFFER_SIZE 1024 -#define PIDFD_CLOSED -2 +#define PIDFD_CLOSED -1 /** for procname */ #if !defined(_GNU_SOURCE) @@ -1834,7 +1834,6 @@ int main(int argc, char **argv) /* Write process pid into MaxScale pidfile */ if(write_pid_file() != 0) { - skygw_log_write(LE,"Error: Failed to write PID file. Exiting."); rc = MAXSCALE_ALREADYRUNNING; goto return_main; } @@ -2007,6 +2006,7 @@ static void log_flush_cb( static void unlock_pidfile() { if(pidfd != PIDFD_CLOSED) + { if(flock(pidfd,LOCK_UN|LOCK_NB) != 0) { char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; @@ -2014,7 +2014,8 @@ static void unlock_pidfile() snprintf(logbuf,sizeof(logbuf),logerr,pidfile); print_log_n_stderr(true, true, logbuf, logbuf, errno); } - close(pidfd); + close(pidfd); + } } /** @@ -2047,8 +2048,9 @@ bool pid_file_exists() char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; char pidbuf[STRING_BUFFER_SIZE]; pid_t pid; + bool lock_failed = false; - snprintf(pathbuf, PATH_MAX, "%s/maxscale.pid",piddir?piddir:default_piddir); + snprintf(pathbuf, PATH_MAX, "%s/maxscale.pid",get_piddir()); pathbuf[PATH_MAX] = '\0'; if(access(pathbuf,F_OK) != 0) @@ -2067,11 +2069,15 @@ bool pid_file_exists() } if(flock(fd,LOCK_EX|LOCK_NB)) { - close(fd); - char* logerr = "Failed to lock PID file '%s'."; - snprintf(logbuf,sizeof(logbuf),logerr,pidfile); - print_log_n_stderr(true, true, logbuf, logbuf, errno); - return true; + if(errno != EWOULDBLOCK) + { + char* logerr = "Failed to lock PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + close(fd); + return true; + } + lock_failed = true; } pidfd = fd; @@ -2088,7 +2094,9 @@ bool pid_file_exists() else if(b == 0) { /** Empty file */ - char* logerr = "PID file read from '%s'. File was empty."; + char* logerr = "PID file read from '%s'. File was empty.\n" + "If the file is the correct PID file and no other MaxScale processes " + "are running, please remove it manually and start MaxScale again."; snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); print_log_n_stderr(true, true, logbuf, logbuf, errno); unlock_pidfile(); @@ -2101,7 +2109,9 @@ bool pid_file_exists() if(pid < 1) { /** Bad PID */ - char* logerr = "PID file read from '%s'. File contents not valid."; + char* logerr = "PID file read from '%s'. File contents not valid.\n" + "If the file is the correct PID file and no other MaxScale processes " + "are running, please remove it manually and start MaxScale again."; snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); print_log_n_stderr(true, true, logbuf, logbuf, errno); unlock_pidfile(); @@ -2113,31 +2123,39 @@ bool pid_file_exists() if(errno == ESRCH) { /** no such process, old PID file */ - return false; + if(lock_failed) + { + char* logerr = "Locking the PID file '%s' failed. Read PID from file and no process found with PID %d. " + "Confirm that no other process holds the lock on the PID file."; + snprintf(logbuf,sizeof(logbuf),logerr,pathbuf,pid); + print_log_n_stderr(true, true, logbuf, logbuf, 0); + close(fd); + } + return lock_failed; } else { - char* logerr = "Failed to check the existence of process %d"; - snprintf(logbuf,sizeof(logbuf),logerr,pid); + char* logerr = "Failed to check the existence of process %d read from file '%s'"; + snprintf(logbuf,sizeof(logbuf),logerr,pid,pathbuf); print_log_n_stderr(true, true, logbuf, logbuf, errno); unlock_pidfile(); } } else { - char* logerr = "MaxScale is already running. Process id: %d"; + char* logerr = "MaxScale is already running. Process id: %d. " + "Use another location for the PID file to run multiple instances of MaxScale on the same machine."; snprintf(logbuf,sizeof(logbuf),logerr,pid); print_log_n_stderr(true, true, logbuf, logbuf, 0); unlock_pidfile(); - return true; } } else { - char* logerr = "Cannot open PID file '%s', no read permissions."; + char* logerr = "Cannot open PID file '%s', no read permissions. " + "Please confirm that the user running MaxScale has read permissions on the file."; snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); print_log_n_stderr(true, true, logbuf, logbuf, errno); - return true; } return true; } @@ -2154,11 +2172,13 @@ static int write_pid_file() { char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; char pidstr[STRING_BUFFER_SIZE]; + snprintf(pidfile, PATH_MAX, "%s/maxscale.pid",get_piddir()); + if(pidfd == PIDFD_CLOSED) { int fd = -1; - snprintf(pidfile, PATH_MAX, "%s/maxscale.pid",get_piddir()); - fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); + + fd = open(pidfile, O_WRONLY | O_CREAT, 0777); if (fd == -1) { char* logerr = "Failed to open PID file '%s'."; snprintf(logbuf,sizeof(logbuf),logerr,pidfile); @@ -2168,8 +2188,15 @@ static int write_pid_file() { if(flock(fd,LOCK_EX|LOCK_NB)) { - char* logerr = "Failed to lock PID file '%s'."; - snprintf(logbuf,sizeof(logbuf),logerr,pidfile); + if(errno == EWOULDBLOCK) + { + snprintf(logbuf,sizeof(logbuf),"Failed to lock PID file '%s', another process is holding a lock on it. " + "Please confirm that no other MaxScale process is using the same PID file location.",pidfile); + } + else + { + snprintf(logbuf,sizeof(logbuf),"Failed to lock PID file '%s'.",pidfile); + } print_log_n_stderr(true, true, logbuf, logbuf, errno); close(fd); return -1; @@ -2179,7 +2206,9 @@ static int write_pid_file() { /* truncate pidfile content */ if (ftruncate(pidfd, 0)) { - fprintf(stderr, "MaxScale failed to truncate pidfile %s: error %d, %s\n", pidfile, errno, strerror(errno)); + char* logerr = "MaxScale failed to truncate PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pidfile); + print_log_n_stderr(true, true, logbuf, logbuf, errno); unlock_pidfile(); return -1; } @@ -2187,8 +2216,9 @@ static int write_pid_file() { snprintf(pidstr, sizeof(pidstr)-1, "%d", getpid()); if (pwrite(pidfd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) { - fprintf(stderr, "MaxScale failed to write into pidfile %s: error %d, %s\n", pidfile, errno, strerror(errno)); - /* close file and return */ + char* logerr = "MaxScale failed to write into PID file '%s'."; + snprintf(logbuf,sizeof(logbuf),logerr,pidfile); + print_log_n_stderr(true, true, logbuf, logbuf, errno); unlock_pidfile(); return -1; } From 79c77d3f01b1abc0f1f9049db7fb6a5c08fe888c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 25 Aug 2015 15:23:27 +0300 Subject: [PATCH 13/74] Fixed compiler warnings. --- server/core/test/testfeedback.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/core/test/testfeedback.c b/server/core/test/testfeedback.c index 3b281537e..0c42d5cfb 100644 --- a/server/core/test/testfeedback.c +++ b/server/core/test/testfeedback.c @@ -63,6 +63,9 @@ static char* server_groups[] = { NULL }; +int config_load(char *); +int module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *cfg); +int do_http_post(GWBUF *buffer, void *cfg); int main(int argc, char** argv) { From 6ed3f0eee47bbdfd4b97a3d01a66e98a4b50dff8 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 25 Aug 2015 16:35:25 +0300 Subject: [PATCH 14/74] Updated documentation. --- Documentation/About/Limitations.md | 6 +++--- Documentation/Routers/ReadWriteSplit.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/About/Limitations.md b/Documentation/About/Limitations.md index 4183bcb61..4677640a9 100644 --- a/Documentation/About/Limitations.md +++ b/Documentation/About/Limitations.md @@ -75,12 +75,12 @@ There is a possibility for misbehavior; if `USE mytable` was executed in one of The above-mentioned behavior can be partially controller with the `use_sql_variables_in` configuration parameter. ``` -use_sql_variables_in=[master|all] (master) +use_sql_variables_in=[master|all] (default: all) ``` -Server-side session variables are called as SQL variables. If "master" or no value is set, SQL variables are read and written in master only. Autocommit values and prepared statements are routed to all nodes always. +Server-side session variables are called as SQL variables. If "master" is set, SQL variables are read and written in master only. Autocommit values and prepared statements are routed to all nodes always. -**NOTE**: If variable is written as a part of write query, it is treated like write query and not routed to all servers. For example, `INSERT INTO test.t1 VALUES (@myvar:= 7)` will be routed to the master and an error in the error log will be written. +**NOTE**: If variable is written as a part of write query, it is treated like write query and not routed to all servers. For example, `INSERT INTO test.t1 VALUES (@myvar:= 7)` will not be routed and an error in the error log will be written. Add the `use_sql_variables_in=master` to the service definition to allow these queries. #### Examples of session command limitations diff --git a/Documentation/Routers/ReadWriteSplit.md b/Documentation/Routers/ReadWriteSplit.md index 7fb6d5cf0..f75a5873b 100644 --- a/Documentation/Routers/ReadWriteSplit.md +++ b/Documentation/Routers/ReadWriteSplit.md @@ -65,7 +65,7 @@ where ** is one of the following: **`use_sql_variables_in`** specifies where should queries, which read session variable, be routed. The syntax for `use_sql_variable_in` is: - use_sql_variables_in=[master|all] + use_sql_variables_in=[master|all] (default: all) When value all is used, queries reading session variables can be routed to any available slave (depending on selection criteria). Note, that queries modifying session variables are routed to all backend servers by default, excluding write queries with embedded session variable modifications, such as: From 1c9b0665eee5e6a35283839d444a1587bbc05484 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Tue, 25 Aug 2015 16:08:29 +0200 Subject: [PATCH 15/74] Use dcb_close() instead of dcb_free() Use dcb_close() instead of dcb_free() --- server/core/test/test_mysql_users.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/core/test/test_mysql_users.c b/server/core/test/test_mysql_users.c index ccf52dbf3..ae776f571 100644 --- a/server/core/test/test_mysql_users.c +++ b/server/core/test/test_mysql_users.c @@ -67,7 +67,7 @@ int set_and_get_single_mysql_users_ipv4(char *username, unsigned long ipv4, char } if ((service = (SERVICE *)calloc(1, sizeof(SERVICE))) == NULL) { fprintf(stderr, "service_alloc() failed\n"); - dcb_free(dcb); + dcb_close(dcb); return 1; } @@ -97,7 +97,7 @@ int set_and_get_single_mysql_users_ipv4(char *username, unsigned long ipv4, char fprintf(stderr, "Failed adding %s@%s(%lu)\n", username, ret_ip, fix_ipv4); users_free(mysql_users); free(service); - dcb_free(dcb); + dcb_close(dcb); return 1; } @@ -114,7 +114,7 @@ int set_and_get_single_mysql_users_ipv4(char *username, unsigned long ipv4, char users_free(mysql_users); free(service); - dcb_free(dcb); + dcb_close(dcb); if (!fetch_data) return 1; @@ -198,7 +198,7 @@ int set_and_get_mysql_users_wildcards(char *username, char *hostname, char *pass } if ((service = (SERVICE *)calloc(1, sizeof(SERVICE))) == NULL) { fprintf(stderr, "service_alloc() failed\n"); - dcb_free(dcb); + dcb_close(dcb); return ret; } @@ -208,7 +208,7 @@ int set_and_get_mysql_users_wildcards(char *username, char *hostname, char *pass if(!setipaddress(&client_addr.sin_addr, from)) { fprintf(stderr, "setipaddress failed for host [%s]\n", from); free(service); - dcb_free(dcb); + dcb_close(dcb); return ret; } } @@ -216,7 +216,7 @@ int set_and_get_mysql_users_wildcards(char *username, char *hostname, char *pass if ((data = (MYSQL_session *) calloc(1, sizeof(MYSQL_session))) == NULL) { fprintf(stderr, "MYSQL_session alloc failed\n"); free(service); - dcb_free(dcb); + dcb_close(dcb); return ret; } @@ -235,7 +235,7 @@ int set_and_get_mysql_users_wildcards(char *username, char *hostname, char *pass else strncpy(data->db, "",MYSQL_DATABASE_MAXLEN); - /* freed by dcb_free(dcb) */ + /* freed by dcb_close(dcb) */ dcb->data = data; // the routine returns 1 on success @@ -264,7 +264,7 @@ int set_and_get_mysql_users_wildcards(char *username, char *hostname, char *pass users_free(mysql_users); free(service); - dcb_free(dcb); + dcb_close(dcb); return ret; } From ab86b67bda4bfe80ba71c5fc7b2fe9da75573d25 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 25 Aug 2015 21:23:55 +0300 Subject: [PATCH 16/74] Fixed missing include. --- server/core/test/testfeedback.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/core/test/testfeedback.c b/server/core/test/testfeedback.c index 0c42d5cfb..2947b0975 100644 --- a/server/core/test/testfeedback.c +++ b/server/core/test/testfeedback.c @@ -40,6 +40,7 @@ #include #include #include +#include static char* server_options[] = { "MariaDB Corporation MaxScale", From b3be72022c65d174590f8f9f1148a1a625b43c9a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 25 Aug 2015 21:53:54 +0300 Subject: [PATCH 17/74] Fixed use_sql_variables_in=master not working. --- server/modules/routing/readwritesplit/readwritesplit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 417abdfe7..ca13420a2 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1441,7 +1441,8 @@ static route_target_t get_route_target ( { target = TARGET_SLAVE; } - else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_MASTER_READ) || + + if (QUERY_IS_TYPE(qtype, QUERY_TYPE_MASTER_READ) || QUERY_IS_TYPE(qtype, QUERY_TYPE_EXEC_STMT) || /** Configured not to allow reading variables from slaves */ (use_sql_variables_in == TYPE_MASTER && From 3e863863dd94e9cd03592d4a792db51327635c73 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 26 Aug 2015 09:35:10 +0300 Subject: [PATCH 18/74] Fixes to Coverity defects. --- server/core/externcmd.c | 2 +- server/core/service.c | 10 +++++----- server/modules/monitor/monitor_common.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/server/core/externcmd.c b/server/core/externcmd.c index 93579faef..3099a767a 100644 --- a/server/core/externcmd.c +++ b/server/core/externcmd.c @@ -109,7 +109,7 @@ EXTERNCMD* externcmd_allocate(char* argstr) if(access(cmd->parameters[0],X_OK) != 0) { skygw_log_write(LE, - "Error: Cannot execute file: %s", + "Error: Cannot execute file '%s'. Missing execution permissions.", cmd->parameters[0]); externcmd_free(cmd); return NULL; diff --git a/server/core/service.c b/server/core/service.c index 224a602b3..012fe5d13 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -242,11 +242,11 @@ GWPROTOCOL *funcs; { /* Try loading authentication data from file cache */ - char *ptr, path[4097]; - strcpy(path, get_cachedir()); - strncat(path, "/", 4096); - strncat(path, service->name, 4096); - strncat(path, "/.cache/dbusers", 4096); + char *ptr, path[PATH_MAX+1]; + strncpy(path, get_cachedir(),sizeof(path)-1); + strncat(path, "/", sizeof(path)-1); + strncat(path, service->name, sizeof(path)-1); + strncat(path, "/.cache/dbusers", sizeof(path)-1); loaded = dbusers_load(service->users, path); if (loaded != -1) { diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index 19980c5dd..cbf1def75 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -239,7 +239,7 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) } first = false; sprintf(arr,"%s:%d",ptr->server->name,ptr->server->port); - strcat(str,arr); + strncat(str,arr,len); ptr = ptr->next; slen = strlen(str); } @@ -306,10 +306,10 @@ void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script) ptr->server->name, ptr->server->port); - mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX + 1); + mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX); if((cmd = externcmd_allocate(argstr)) == NULL) { - skygw_log_write(LE,"Failed to execute script: %s",script); + skygw_log_write(LE,"Failed to initialize script: %s",script); return; } From ec3a4644e47237d27ad4b1d68814885893804e58 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 26 Aug 2015 11:44:28 +0300 Subject: [PATCH 19/74] Erroneously renamed unpack_rpm.sh now renamed correctly. --- develop => script/unpack_rpm.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename develop => script/unpack_rpm.sh (100%) diff --git a/develop b/script/unpack_rpm.sh similarity index 100% rename from develop rename to script/unpack_rpm.sh From 2f9ae48f6a125337392735dafbb95b30f6db6984 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 26 Aug 2015 12:49:00 +0300 Subject: [PATCH 20/74] Fixed debug logs not working when logging and flushing. --- 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 6baf668ec..4f8897b12 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1395,7 +1395,7 @@ int skygw_log_write_flush( /** * Write log string to buffer and add to file write list. */ - for (i = LOGFILE_FIRST; i Date: Wed, 26 Aug 2015 16:32:17 +0300 Subject: [PATCH 21/74] Added a NULL check to the log manager. --- 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 4f8897b12..516fd8ecb 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -2981,7 +2981,7 @@ static void* thr_filewriter_fun( != 0); node = node->mlnode_next; vn2 = bb_list->mlist_versno; - } while (vn1 != vn2); + } while (vn1 != vn2 && node); } /* while (node != NULL) */ From 70a7a5f2f62706db1b020eaebc77f07fa115a16f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 09:16:00 +0300 Subject: [PATCH 22/74] Fix to MXS-328: https://mariadb.atlassian.net/browse/MXS-328 Removed gwbuf_free when the write fails. --- server/modules/routing/readwritesplit/readwritesplit.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index ca13420a2..b8ad34719 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -2586,8 +2586,8 @@ static bool route_single_stmt( rses_end_locked_router_action(rses); goto retblock; } - GWBUF* wbuf = gwbuf_clone(querybuf); - if ((ret = target_dcb->func.write(target_dcb, wbuf)) == 1) + + if ((ret = target_dcb->func.write(target_dcb, gwbuf_clone(querybuf))) == 1) { backend_ref_t* bref; @@ -2601,7 +2601,6 @@ static bool route_single_stmt( } else { - gwbuf_free(wbuf); LOGIF((LE|LT), (skygw_log_write_flush( LOGFILE_ERROR, "Error : Routing query failed."))); From 94eb1b4eb1d66425eca73ea74ad7477e71010fef Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 6 Aug 2015 11:45:55 +0300 Subject: [PATCH 23/74] Added service and monitor permission checks. --- server/core/config.c | 1 + server/core/dbusers.c | 104 +++++++++++++++++++++++++++++++++++++++ server/core/monitor.c | 58 ++++++++++++++++++++++ server/core/service.c | 15 +++++- server/include/dbusers.h | 1 + server/include/monitor.h | 1 + server/include/service.h | 1 + 7 files changed, 180 insertions(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index dee089a49..41bd25540 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1107,6 +1107,7 @@ process_config_context(CONFIG_CONTEXT *context) monitorAddUser(obj->element, user, passwd); + valid_monitor_permissions(obj->element); } else if (obj->element && user) { diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 57594f63d..a7abe4872 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2315,3 +2315,107 @@ int add_wildcard_users(USERS *users, char* name, char* host, char* password, cha return rval; } + +/** + * Check if the service user has all required permissions to operate properly. + * this checks for SELECT permissions on mysql.user and mysql.db tables and for + * SHOW DATABASES permissions. If permissions are not adequate, an error message + * is logged. + * @param service Service to inspect + */ +void valid_service_permissions(SERVICE* service) +{ + MYSQL* mysql; + MYSQL_RES* res; + char *user,*password,*dpasswd; + SERVER_REF* server; + int conn_timeout = 1; + + if(service_is_internal(service)) + return; + + if(service->dbref == NULL) + { + skygw_log_write(LE,"[%s] Error: Service is missing the servers parameter.",service->name); + return; + } + + server = service->dbref; + + if (serviceGetUser(service, &user, &password) == 0) + { + skygw_log_write(LE,"[%s] Error: Service %s is missing the user credentials for authentication.", + __FUNCTION__,service->name); + return; + } + + dpasswd = decryptPassword(password); + + if((mysql = mysql_init(NULL)) == NULL) + { + skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); + free(dpasswd); + return; + } + + mysql_options(mysql,MYSQL_OPT_USE_REMOTE_CONNECTION,NULL); + mysql_options(mysql,MYSQL_OPT_CONNECT_TIMEOUT,&conn_timeout); + /** Connect to the first server. This assumes all servers have identical + * user permissions. */ + + if(mysql_real_connect(mysql,server->server->name,user,dpasswd,NULL,server->server->port,NULL,0) == NULL) + { + skygw_log_write(LE,"[%s] Error: Failed to connect to server %s(%s:%d) when" + " checking authentication user credentials and permissions.", + service->name, + server->server->unique_name, + server->server->name, + server->server->port); + mysql_close(mysql); + free(dpasswd); + return; + } + + if(mysql_query(mysql,"select * from mysql.user limit 1") != 0) + { + skygw_log_write(LE,"[%s] Error: Failed to query from mysql.user table. MySQL error message: %s",service->name,mysql_error(mysql)); + mysql_close(mysql); + free(dpasswd); + return; + } + + mysql_free_result(mysql_use_result(mysql)); + + if(mysql_query(mysql,"select * from mysql.db limit 1") != 0) + { + skygw_log_write(LM|LE,"The user '%s' for service '%s' does not have" + " SELECT permissions on the mysql.db table. MaxScale will not use the database in authentication. MySQL error message: %s", + user,service->name,mysql_error(mysql)); + mysql_close(mysql); + free(dpasswd); + return; + } + else + { + mysql_free_result(mysql_use_result(mysql)); + } + + if(mysql_query(mysql,LOAD_MYSQL_DATABASE_NAMES) != 0) + { + skygw_log_write(LE,"[%s] Error: Failed to query for SHOW DATABASES permissions. MySQL error message: %s.",service->name,mysql_error(mysql)); + } + else + { + res = mysql_use_result(mysql); + if(mysql_num_rows(res) == 0) + { + skygw_log_write(LM|LE,"The user '%s' for service '%s' does not have" + " SHOW DATABASES permissions. MaxScale will not use the database in authentication.", + user,service->name); + } + mysql_free_result(res); + } + + mysql_close(mysql); + free(dpasswd); +} \ No newline at end of file diff --git a/server/core/monitor.c b/server/core/monitor.c index f422b4f6b..debbd67a9 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -40,6 +40,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -49,6 +50,8 @@ extern __thread log_info_t tls_log_info; static MONITOR *allMonitors = NULL; static SPINLOCK monLock = SPINLOCK_INIT; +void valid_monitor_permissions(MONITOR* monitor); + /** * Allocate a new monitor, load the associated module for the monitor * and start execution on the monitor. @@ -448,3 +451,58 @@ int *data; return set; } + +/** + * Check if the monitor user has all required permissions to operate properly. + * this checks for ... + * @param service Monitor to inspect + */ +void valid_monitor_permissions(MONITOR* monitor) +{ + MYSQL* mysql; + MYSQL_RES* res; + char *user,*dpasswd; + SERVER* server; + int conn_timeout = 1; + + user = monitor->user; + dpasswd = decryptPassword(monitor->password); + + if((mysql = mysql_init(NULL)) == NULL) + { + skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); + free(dpasswd); + return; + } + + server = monitor->databases->server; + mysql_options(mysql,MYSQL_OPT_USE_REMOTE_CONNECTION,NULL); + mysql_options(mysql,MYSQL_OPT_CONNECT_TIMEOUT,&conn_timeout); + + /** Connect to the first server. This assumes all servers have identical + * user permissions. */ + if(mysql_real_connect(mysql,server->name,user,dpasswd,NULL,server->port,NULL,0) == NULL) + { + skygw_log_write(LE,"[%s] Error: Failed to connect to server %s(%s:%d) when" + " checking monitor user credentials and permissions.", + monitor->name, + server->unique_name, + server->name, + server->port); + mysql_close(mysql); + free(dpasswd); + return; + } + + if(mysql_query(mysql,"show slave status") != 0) + { + skygw_log_write(LE,"[%s] Error: Monitor failed to query for slave status. MySQL error message: %s",monitor->name,mysql_error(mysql)); + mysql_close(mysql); + free(dpasswd); + return; + } + + mysql_free_result(mysql_use_result(mysql)); + mysql_close(mysql); + free(dpasswd); +} \ No newline at end of file diff --git a/server/core/service.c b/server/core/service.c index 012fe5d13..4257b74f2 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -438,6 +438,8 @@ if(service->ssl_mode != SSL_DISABLED) return 0; } + valid_service_permissions(service); + port = service->ports; while (!service->svc_do_shutdown && port) { @@ -456,7 +458,7 @@ if(service->ssl_mode != SSL_DISABLED) hktask_add("connection_timeout",session_close_timeouts,NULL,5); } - return listeners; + return listeners; } /** @@ -2042,3 +2044,14 @@ int serviceInitSSL(SERVICE* service) } return 0; } + +/** + * Check if the service is an internal service. Internal services are special + * services which do not require the servers parameter. + * @param service Service to check + * @return True if this service is used only internally + */ +bool service_is_internal(SERVICE* service) +{ + return strcmp(service->routerModule,"cli") == 0 || strcmp(service->routerModule,"debugcli") == 0; +} \ No newline at end of file diff --git a/server/include/dbusers.h b/server/include/dbusers.h index f0367a227..0d78cd548 100644 --- a/server/include/dbusers.h +++ b/server/include/dbusers.h @@ -68,4 +68,5 @@ extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key); extern int replace_mysql_users(SERVICE *service); extern int dbusers_save(USERS *, char *); extern int dbusers_load(USERS *, char *); +void valid_service_permissions(SERVICE* service); #endif diff --git a/server/include/monitor.h b/server/include/monitor.h index 442efb27f..e3a46cffc 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -169,4 +169,5 @@ extern void monitorList(DCB *); extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetNetworkTimeout(MONITOR *, int, int); extern RESULTSET *monitorGetList(); +void valid_monitor_permissions(MONITOR* monitor); #endif diff --git a/server/include/service.h b/server/include/service.h index b64270b9e..2deac6984 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -246,4 +246,5 @@ void service_shutdown(); extern int serviceSessionCountAll(); extern RESULTSET *serviceGetList(); extern RESULTSET *serviceGetListenerList(); +bool service_is_internal(SERVICE* service); #endif From 3ae177f4e88d446f898c19c6bd92832049c7a8b4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 16:58:39 +0300 Subject: [PATCH 24/74] Added documentation about refresh_databases and refresh_interval options. --- Documentation/Routers/SchemaRouter.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/Routers/SchemaRouter.md b/Documentation/Routers/SchemaRouter.md index 02d6f8e50..34da8506c 100644 --- a/Documentation/Routers/SchemaRouter.md +++ b/Documentation/Routers/SchemaRouter.md @@ -45,11 +45,11 @@ This would in effect allow the user 'john' to only see the database 'shard' on t The schemarouter supports the following router options: |option |parameter |description| -|-------------------|-----------|-----------| -|max_sescmd_history |integer |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consumption of the session.| -|disable_sescmd_history|true, false|Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.| -|refresh_databases|true, false|Enable database map refreshing mid-session. These are triggered by a failure to change the database i.e. `USE ...``queries.| -|refresh_interval|float|The minimum interval between database map refreshes in seconds.| +--------------------------------------------- +|max_sescmd_hitory | |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consupmtion of the session.| +|disable_sescmd_history||Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.| +|refresh_databases|true, false|Enable database map refreshing mid-session. These are triggered by a failure to change the database i.e. `USE ...``queries. +|refresh_interval||The minimum interval between database map refreshes in seconds. ## Limitations The schemarouter router currently has some limitations due to the nature of the sharding implementation and the way the session variables are detected and routed. Here is a list of the current limitations. From b7eee3ed1cbdff174a58f0a042c25831f4dee1e6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 16:59:44 +0300 Subject: [PATCH 25/74] Fixed markdown table. --- Documentation/Routers/SchemaRouter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Routers/SchemaRouter.md b/Documentation/Routers/SchemaRouter.md index 34da8506c..4f3cc4966 100644 --- a/Documentation/Routers/SchemaRouter.md +++ b/Documentation/Routers/SchemaRouter.md @@ -45,7 +45,7 @@ This would in effect allow the user 'john' to only see the database 'shard' on t The schemarouter supports the following router options: |option |parameter |description| ---------------------------------------------- +|-------------------|-----------|-----------| |max_sescmd_hitory | |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consupmtion of the session.| |disable_sescmd_history||Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.| |refresh_databases|true, false|Enable database map refreshing mid-session. These are triggered by a failure to change the database i.e. `USE ...``queries. From d99b7a5ef9f0d36e9605d74a2a6d5fce74d44bdc Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 17:01:02 +0300 Subject: [PATCH 26/74] Fixed documentation. --- Documentation/Routers/SchemaRouter.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/Routers/SchemaRouter.md b/Documentation/Routers/SchemaRouter.md index 4f3cc4966..951eed6b5 100644 --- a/Documentation/Routers/SchemaRouter.md +++ b/Documentation/Routers/SchemaRouter.md @@ -46,10 +46,10 @@ The schemarouter supports the following router options: |option |parameter |description| |-------------------|-----------|-----------| -|max_sescmd_hitory | |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consupmtion of the session.| -|disable_sescmd_history||Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.| -|refresh_databases|true, false|Enable database map refreshing mid-session. These are triggered by a failure to change the database i.e. `USE ...``queries. -|refresh_interval||The minimum interval between database map refreshes in seconds. +|max_sescmd_hitory |integer |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consupmtion of the session.| +|disable_sescmd_history|true, false|Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.| +|refresh_databases|true, false|Enable database map refreshing mid-session. These are triggered by a failure to change the database i.e. `USE ...``queries.| +|refresh_interval|float|The minimum interval between database map refreshes in seconds.| ## Limitations The schemarouter router currently has some limitations due to the nature of the sharding implementation and the way the session variables are detected and routed. Here is a list of the current limitations. From 501ea809a839f4610e3442c3ada32741a28c0e0f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 11 Jun 2015 17:02:07 +0300 Subject: [PATCH 27/74] Fixed typos in documentation. --- Documentation/Routers/SchemaRouter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Routers/SchemaRouter.md b/Documentation/Routers/SchemaRouter.md index 951eed6b5..02d6f8e50 100644 --- a/Documentation/Routers/SchemaRouter.md +++ b/Documentation/Routers/SchemaRouter.md @@ -46,7 +46,7 @@ The schemarouter supports the following router options: |option |parameter |description| |-------------------|-----------|-----------| -|max_sescmd_hitory |integer |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consupmtion of the session.| +|max_sescmd_history |integer |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consumption of the session.| |disable_sescmd_history|true, false|Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.| |refresh_databases|true, false|Enable database map refreshing mid-session. These are triggered by a failure to change the database i.e. `USE ...``queries.| |refresh_interval|float|The minimum interval between database map refreshes in seconds.| From 4da9045da4daf57369230e3b6cc5deab556e0c55 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 09:32:35 +0300 Subject: [PATCH 28/74] Removed redundant function. --- server/core/config.c | 4 ++-- server/core/dbusers.c | 2 +- server/core/service.c | 13 +------------ server/include/maxconfig.h | 1 + server/include/service.h | 1 - 5 files changed, 5 insertions(+), 16 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 41bd25540..9330d1665 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -92,7 +92,7 @@ static void global_defaults(); static void feedback_defaults(); static void check_config_objects(CONFIG_CONTEXT *context); int config_truth_value(char *str); -static int internalService(char *router); +int internalService(char *router); int config_get_ifaddr(unsigned char *output); int config_get_release_string(char* release); FEEDBACK_CONF * config_get_feedback_data(); @@ -2230,7 +2230,7 @@ static char *InternalRouters[] = { * @param router The router name * @return Non-zero if the router is in the InternalRouters table */ -static int +int internalService(char *router) { int i; diff --git a/server/core/dbusers.c b/server/core/dbusers.c index a7abe4872..eefa095e8 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2331,7 +2331,7 @@ void valid_service_permissions(SERVICE* service) SERVER_REF* server; int conn_timeout = 1; - if(service_is_internal(service)) + if(internalService(service->routerModule)) return; if(service->dbref == NULL) diff --git a/server/core/service.c b/server/core/service.c index 4257b74f2..7ba249c44 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -458,7 +458,7 @@ if(service->ssl_mode != SSL_DISABLED) hktask_add("connection_timeout",session_close_timeouts,NULL,5); } - return listeners; + return listeners; } /** @@ -2044,14 +2044,3 @@ int serviceInitSSL(SERVICE* service) } return 0; } - -/** - * Check if the service is an internal service. Internal services are special - * services which do not require the servers parameter. - * @param service Service to check - * @return True if this service is used only internally - */ -bool service_is_internal(SERVICE* service) -{ - return strcmp(service->routerModule,"cli") == 0 || strcmp(service->routerModule,"debugcli") == 0; -} \ No newline at end of file diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index a3c93f2f1..6cfa1cb1a 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -146,4 +146,5 @@ void config_enable_feedback_task(void); void config_disable_feedback_task(void); unsigned long config_get_gateway_id(void); GATEWAY_CONF* config_get_global_options(); +int internalService(char *router); #endif diff --git a/server/include/service.h b/server/include/service.h index 2deac6984..b64270b9e 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -246,5 +246,4 @@ void service_shutdown(); extern int serviceSessionCountAll(); extern RESULTSET *serviceGetList(); extern RESULTSET *serviceGetListenerList(); -bool service_is_internal(SERVICE* service); #endif From b232c49742e3d5d6d1ff8700ee3eb28efe3df745 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 09:37:50 +0300 Subject: [PATCH 29/74] Fixed function documentation. --- server/core/monitor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/monitor.c b/server/core/monitor.c index debbd67a9..762317e08 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -454,7 +454,7 @@ int *data; /** * Check if the monitor user has all required permissions to operate properly. - * this checks for ... + * this checks for REPLICATION CLIENT permissions * @param service Monitor to inspect */ void valid_monitor_permissions(MONITOR* monitor) From cb2b4655103c98f1f4b4f80e6a9bdccf94f051f6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 10:44:03 +0300 Subject: [PATCH 30/74] Changed queries to more closely match actual queries. --- server/core/dbusers.c | 53 ++++++++++++++++++++----------------------- server/core/monitor.c | 12 +++++++++- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index eefa095e8..c4b41259f 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2376,46 +2376,43 @@ void valid_service_permissions(SERVICE* service) return; } - if(mysql_query(mysql,"select * from mysql.user limit 1") != 0) + if(mysql_query(mysql,"SELECT user, host, password,Select_priv FROM mysql.user limit 1") != 0) { - skygw_log_write(LE,"[%s] Error: Failed to query from mysql.user table. MySQL error message: %s",service->name,mysql_error(mysql)); - mysql_close(mysql); + if(mysql_errno(mysql) == ER_TABLEACCESS_DENIED_ERROR) + { + skygw_log_write(LE,"[%s] Error: User '%s' is missing SELECT privileges on mysql.user table. MySQL error message: %s", + service->name,user,mysql_error(mysql)); + } + else + { + skygw_log_write(LE,"[%s] Error: Failed to query from mysql.user table. MySQL error message: %s", + service->name,mysql_error(mysql)); + } + mysql_close(mysql); free(dpasswd); return; } mysql_free_result(mysql_use_result(mysql)); - if(mysql_query(mysql,"select * from mysql.db limit 1") != 0) + if(mysql_query(mysql,"SELECT user, host, db FROM mysql.db limit 1") != 0) { - skygw_log_write(LM|LE,"The user '%s' for service '%s' does not have" - " SELECT permissions on the mysql.db table. MaxScale will not use the database in authentication. MySQL error message: %s", - user,service->name,mysql_error(mysql)); - mysql_close(mysql); + if(mysql_errno(mysql) == ER_TABLEACCESS_DENIED_ERROR) + { + skygw_log_write(LE,"[%s] Error: User '%s' is missing SELECT privileges on mysql.db table. MySQL error message: %s", + service->name,user,mysql_error(mysql)); + } + else + { + skygw_log_write(LE,"[%s] Error: Failed to query from mysql.user table. MySQL error message: %s", + service->name,mysql_error(mysql)); + } + mysql_close(mysql); free(dpasswd); return; } - else - { - mysql_free_result(mysql_use_result(mysql)); - } - - if(mysql_query(mysql,LOAD_MYSQL_DATABASE_NAMES) != 0) - { - skygw_log_write(LE,"[%s] Error: Failed to query for SHOW DATABASES permissions. MySQL error message: %s.",service->name,mysql_error(mysql)); - } - else - { - res = mysql_use_result(mysql); - if(mysql_num_rows(res) == 0) - { - skygw_log_write(LM|LE,"The user '%s' for service '%s' does not have" - " SHOW DATABASES permissions. MaxScale will not use the database in authentication.", - user,service->name); - } - mysql_free_result(res); - } + mysql_free_result(mysql_use_result(mysql)); mysql_close(mysql); free(dpasswd); } \ No newline at end of file diff --git a/server/core/monitor.c b/server/core/monitor.c index 762317e08..ae1dd250b 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -41,6 +41,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -496,7 +497,16 @@ void valid_monitor_permissions(MONITOR* monitor) if(mysql_query(mysql,"show slave status") != 0) { - skygw_log_write(LE,"[%s] Error: Monitor failed to query for slave status. MySQL error message: %s",monitor->name,mysql_error(mysql)); + if(mysql_errno(mysql) == ER_SPECIFIC_ACCESS_DENIED_ERROR) + { + skygw_log_write(LE,"[%s] Error: User '%s' is missing REPLICATION CLIENT privileges. MySQL error message: %s", + monitor->name,mysql_error(mysql)); + } + else + { + skygw_log_write(LE,"[%s] Error: Monitor failed to query for slave status. MySQL error message: %s", + monitor->name,mysql_error(mysql)); + } mysql_close(mysql); free(dpasswd); return; From 525daf827a38ee2a4729e6425e201b53650476cb Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 16:00:32 +0300 Subject: [PATCH 31/74] Service are not started if user permissions are inadequate. Also cleaned up code and error messages. --- server/core/config.c | 8 ++--- server/core/dbusers.c | 66 +++++++++++++++++++++++++------------- server/core/monitor.c | 34 ++++++++++++-------- server/core/service.c | 12 +++++-- server/include/dbusers.h | 2 +- server/include/maxconfig.h | 2 +- server/include/monitor.h | 2 +- 7 files changed, 81 insertions(+), 45 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 9330d1665..f86f97949 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -92,7 +92,7 @@ static void global_defaults(); static void feedback_defaults(); static void check_config_objects(CONFIG_CONTEXT *context); int config_truth_value(char *str); -int internalService(char *router); +int isInternalService(char *router); int config_get_ifaddr(unsigned char *output); int config_get_release_string(char* release); FEEDBACK_CONF * config_get_feedback_data(); @@ -898,7 +898,7 @@ process_config_context(CONFIG_CONTEXT *context) s = strtok_r(NULL, ",", &lasts); } } - else if (servers == NULL && internalService(router) == 0) + else if (servers == NULL && isInternalService(router) == 0) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -1107,7 +1107,7 @@ process_config_context(CONFIG_CONTEXT *context) monitorAddUser(obj->element, user, passwd); - valid_monitor_permissions(obj->element); + check_monitor_permissions(obj->element); } else if (obj->element && user) { @@ -2231,7 +2231,7 @@ static char *InternalRouters[] = { * @return Non-zero if the router is in the InternalRouters table */ int -internalService(char *router) +isInternalService(char *router) { int i; diff --git a/server/core/dbusers.c b/server/core/dbusers.c index c4b41259f..724381313 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2323,30 +2323,32 @@ int add_wildcard_users(USERS *users, char* name, char* host, char* password, cha * is logged. * @param service Service to inspect */ -void valid_service_permissions(SERVICE* service) +bool check_service_permissions(SERVICE* service) { MYSQL* mysql; MYSQL_RES* res; char *user,*password,*dpasswd; SERVER_REF* server; int conn_timeout = 1; + bool rval = true; - if(internalService(service->routerModule)) - return; + if(isInternalService(service->routerModule)) + return true; if(service->dbref == NULL) { - skygw_log_write(LE,"[%s] Error: Service is missing the servers parameter.",service->name); - return; + skygw_log_write(LE,"%s: Error: Service is missing the servers parameter.",service->name); + return false; } server = service->dbref; if (serviceGetUser(service, &user, &password) == 0) { - skygw_log_write(LE,"[%s] Error: Service %s is missing the user credentials for authentication.", - __FUNCTION__,service->name); - return; + skygw_log_write(LE, + "%s: Error: Service is missing the user credentials for authentication.", + service->name); + return false; } dpasswd = decryptPassword(password); @@ -2355,7 +2357,7 @@ void valid_service_permissions(SERVICE* service) { skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); free(dpasswd); - return; + return false; } mysql_options(mysql,MYSQL_OPT_USE_REMOTE_CONNECTION,NULL); @@ -2365,7 +2367,7 @@ void valid_service_permissions(SERVICE* service) if(mysql_real_connect(mysql,server->server->name,user,dpasswd,NULL,server->server->port,NULL,0) == NULL) { - skygw_log_write(LE,"[%s] Error: Failed to connect to server %s(%s:%d) when" + skygw_log_write(LE,"%s: Error: Failed to connect to server %s(%s:%d) when" " checking authentication user credentials and permissions.", service->name, server->server->unique_name, @@ -2373,46 +2375,64 @@ void valid_service_permissions(SERVICE* service) server->server->port); mysql_close(mysql); free(dpasswd); - return; + return false; } if(mysql_query(mysql,"SELECT user, host, password,Select_priv FROM mysql.user limit 1") != 0) { if(mysql_errno(mysql) == ER_TABLEACCESS_DENIED_ERROR) { - skygw_log_write(LE,"[%s] Error: User '%s' is missing SELECT privileges on mysql.user table. MySQL error message: %s", + skygw_log_write(LE,"%s: Error: User '%s' is missing SELECT privileges" + " on mysql.user table. MySQL error message: %s", service->name,user,mysql_error(mysql)); + rval = false; } else { - skygw_log_write(LE,"[%s] Error: Failed to query from mysql.user table. MySQL error message: %s", + skygw_log_write(LE,"%s: Error: Failed to query from mysql.user table." + " MySQL error message: %s", service->name,mysql_error(mysql)); } - mysql_close(mysql); - free(dpasswd); - return; } - mysql_free_result(mysql_use_result(mysql)); + if((res = mysql_use_result(mysql)) == NULL) + { + skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for" + " permissions to the mysql.user table: %s", + service->name,mysql_error(mysql)); + mysql_close(mysql); + free(dpasswd); + return rval; + } + + mysql_free_result(res); if(mysql_query(mysql,"SELECT user, host, db FROM mysql.db limit 1") != 0) { if(mysql_errno(mysql) == ER_TABLEACCESS_DENIED_ERROR) { - skygw_log_write(LE,"[%s] Error: User '%s' is missing SELECT privileges on mysql.db table. MySQL error message: %s", + skygw_log_write(LE,"%s: Error: User '%s' is missing SELECT privileges on mysql.db table. MySQL error message: %s", service->name,user,mysql_error(mysql)); + rval = false; } else { - skygw_log_write(LE,"[%s] Error: Failed to query from mysql.user table. MySQL error message: %s", + skygw_log_write(LE,"%s: Error: Failed to query from mysql.db table. MySQL error message: %s", service->name,mysql_error(mysql)); } - mysql_close(mysql); - free(dpasswd); - return; } - mysql_free_result(mysql_use_result(mysql)); + if((res = mysql_use_result(mysql)) == NULL) + { + skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for permissions to the mysql.db table: %s", + service->name,mysql_error(mysql)); + } + else + { + mysql_free_result(res); + } + mysql_close(mysql); free(dpasswd); + return rval; } \ No newline at end of file diff --git a/server/core/monitor.c b/server/core/monitor.c index ae1dd250b..f0a004086 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -51,8 +51,6 @@ extern __thread log_info_t tls_log_info; static MONITOR *allMonitors = NULL; static SPINLOCK monLock = SPINLOCK_INIT; -void valid_monitor_permissions(MONITOR* monitor); - /** * Allocate a new monitor, load the associated module for the monitor * and start execution on the monitor. @@ -458,13 +456,14 @@ int *data; * this checks for REPLICATION CLIENT permissions * @param service Monitor to inspect */ -void valid_monitor_permissions(MONITOR* monitor) +bool check_monitor_permissions(MONITOR* monitor) { MYSQL* mysql; MYSQL_RES* res; char *user,*dpasswd; SERVER* server; int conn_timeout = 1; + bool rval = true; user = monitor->user; dpasswd = decryptPassword(monitor->password); @@ -473,7 +472,7 @@ void valid_monitor_permissions(MONITOR* monitor) { skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); free(dpasswd); - return; + return false; } server = monitor->databases->server; @@ -484,7 +483,7 @@ void valid_monitor_permissions(MONITOR* monitor) * user permissions. */ if(mysql_real_connect(mysql,server->name,user,dpasswd,NULL,server->port,NULL,0) == NULL) { - skygw_log_write(LE,"[%s] Error: Failed to connect to server %s(%s:%d) when" + skygw_log_write(LE,"%s: Error: Failed to connect to server %s(%s:%d) when" " checking monitor user credentials and permissions.", monitor->name, server->unique_name, @@ -492,27 +491,36 @@ void valid_monitor_permissions(MONITOR* monitor) server->port); mysql_close(mysql); free(dpasswd); - return; + return true; } if(mysql_query(mysql,"show slave status") != 0) { if(mysql_errno(mysql) == ER_SPECIFIC_ACCESS_DENIED_ERROR) { - skygw_log_write(LE,"[%s] Error: User '%s' is missing REPLICATION CLIENT privileges. MySQL error message: %s", + skygw_log_write(LE,"%s: Error: User '%s' is missing REPLICATION CLIENT privileges. MySQL error message: %s", monitor->name,mysql_error(mysql)); + rval = false; } else { - skygw_log_write(LE,"[%s] Error: Monitor failed to query for slave status. MySQL error message: %s", + skygw_log_write(LE,"%s: Error: Monitor failed to query for slave status. MySQL error message: %s", monitor->name,mysql_error(mysql)); } - mysql_close(mysql); - free(dpasswd); - return; } - - mysql_free_result(mysql_use_result(mysql)); + else + { + if((res = mysql_use_result(mysql)) == NULL) + { + skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for REPLICATION CLIENT permissions: %s", + monitor->name,mysql_error(mysql)); + free(dpasswd); + mysql_close(mysql); + return rval; + } + mysql_free_result(res); + } mysql_close(mysql); free(dpasswd); + return rval; } \ No newline at end of file diff --git a/server/core/service.c b/server/core/service.c index 7ba249c44..b5a0066e5 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -417,6 +417,16 @@ serviceStart(SERVICE *service) SERV_PROTOCOL *port; int listeners = 0; + +if(!check_service_permissions(service)) +{ + skygw_log_write_flush(LE, + "%s: Inadequate user permissions for service. Service not started.", + service->name); + service->state = SERVICE_STATE_FAILED; + return 0; +} + if(service->ssl_mode != SSL_DISABLED) { if(serviceInitSSL(service) != 0) @@ -438,8 +448,6 @@ if(service->ssl_mode != SSL_DISABLED) return 0; } - valid_service_permissions(service); - port = service->ports; while (!service->svc_do_shutdown && port) { diff --git a/server/include/dbusers.h b/server/include/dbusers.h index 0d78cd548..4f2d2c787 100644 --- a/server/include/dbusers.h +++ b/server/include/dbusers.h @@ -68,5 +68,5 @@ extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key); extern int replace_mysql_users(SERVICE *service); extern int dbusers_save(USERS *, char *); extern int dbusers_load(USERS *, char *); -void valid_service_permissions(SERVICE* service); +bool check_service_permissions(SERVICE* service); #endif diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index 6cfa1cb1a..58d9a7ac5 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -146,5 +146,5 @@ void config_enable_feedback_task(void); void config_disable_feedback_task(void); unsigned long config_get_gateway_id(void); GATEWAY_CONF* config_get_global_options(); -int internalService(char *router); +int isInternalService(char *router); #endif diff --git a/server/include/monitor.h b/server/include/monitor.h index e3a46cffc..1841112ca 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -169,5 +169,5 @@ extern void monitorList(DCB *); extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetNetworkTimeout(MONITOR *, int, int); extern RESULTSET *monitorGetList(); -void valid_monitor_permissions(MONITOR* monitor); +bool check_monitor_permissions(MONITOR* monitor); #endif From 296bdc5df65e753c96666507cee1f3bcff1b89d3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 16:12:46 +0300 Subject: [PATCH 32/74] Fixed errors and added comments. --- server/core/dbusers.c | 40 ++++++++++++++++++++++------------------ server/core/monitor.c | 4 +++- server/core/service.c | 2 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 724381313..e8f38ab13 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2322,6 +2322,8 @@ int add_wildcard_users(USERS *users, char* name, char* host, char* password, cha * SHOW DATABASES permissions. If permissions are not adequate, an error message * is logged. * @param service Service to inspect + * @return True if service permissions are correct. False if one or more permissions + * are missing or if an error occurred. */ bool check_service_permissions(SERVICE* service) { @@ -2394,19 +2396,20 @@ bool check_service_permissions(SERVICE* service) service->name,mysql_error(mysql)); } } - - if((res = mysql_use_result(mysql)) == NULL) + else { - skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for" - " permissions to the mysql.user table: %s", + if((res = mysql_use_result(mysql)) == NULL) + { + skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for" + " permissions to the mysql.user table: %s", service->name,mysql_error(mysql)); - mysql_close(mysql); - free(dpasswd); - return rval; + mysql_close(mysql); + free(dpasswd); + return rval; + } + + mysql_free_result(res); } - - mysql_free_result(res); - if(mysql_query(mysql,"SELECT user, host, db FROM mysql.db limit 1") != 0) { if(mysql_errno(mysql) == ER_TABLEACCESS_DENIED_ERROR) @@ -2421,17 +2424,18 @@ bool check_service_permissions(SERVICE* service) service->name,mysql_error(mysql)); } } - - if((res = mysql_use_result(mysql)) == NULL) - { - skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for permissions to the mysql.db table: %s", - service->name,mysql_error(mysql)); - } else { - mysql_free_result(res); + if((res = mysql_use_result(mysql)) == NULL) + { + skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for permissions to the mysql.db table: %s", + service->name,mysql_error(mysql)); + } + else + { + mysql_free_result(res); + } } - mysql_close(mysql); free(dpasswd); return rval; diff --git a/server/core/monitor.c b/server/core/monitor.c index f0a004086..52d4c5f28 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -455,6 +455,8 @@ int *data; * Check if the monitor user has all required permissions to operate properly. * this checks for REPLICATION CLIENT permissions * @param service Monitor to inspect + * @return False if an error with monitor permissions was detected or if an + * error occurred. True if permissions are correct. */ bool check_monitor_permissions(MONITOR* monitor) { @@ -499,7 +501,7 @@ bool check_monitor_permissions(MONITOR* monitor) if(mysql_errno(mysql) == ER_SPECIFIC_ACCESS_DENIED_ERROR) { skygw_log_write(LE,"%s: Error: User '%s' is missing REPLICATION CLIENT privileges. MySQL error message: %s", - monitor->name,mysql_error(mysql)); + monitor->name,user,mysql_error(mysql)); rval = false; } else diff --git a/server/core/service.c b/server/core/service.c index b5a0066e5..cc31fcfa1 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -421,7 +421,7 @@ int listeners = 0; if(!check_service_permissions(service)) { skygw_log_write_flush(LE, - "%s: Inadequate user permissions for service. Service not started.", + "%s: Error: Inadequate user permissions for service. Service not started.", service->name); service->state = SERVICE_STATE_FAILED; return 0; From c5214bea1b23849eb5a1146ee01b16c5f90309be Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 16:33:10 +0300 Subject: [PATCH 33/74] Cleaned up code and changed function structure. --- server/core/config.c | 14 ++++++-------- server/core/dbusers.c | 7 ++++--- server/core/monitor.c | 13 +++++++------ server/include/maxconfig.h | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index f86f97949..c0113d001 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -92,7 +92,7 @@ static void global_defaults(); static void feedback_defaults(); static void check_config_objects(CONFIG_CONTEXT *context); int config_truth_value(char *str); -int isInternalService(char *router); +bool isInternalService(char *router); int config_get_ifaddr(unsigned char *output); int config_get_release_string(char* release); FEEDBACK_CONF * config_get_feedback_data(); @@ -898,7 +898,7 @@ process_config_context(CONFIG_CONTEXT *context) s = strtok_r(NULL, ",", &lasts); } } - else if (servers == NULL && isInternalService(router) == 0) + else if (servers == NULL && !isInternalService(router)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -2230,18 +2230,16 @@ static char *InternalRouters[] = { * @param router The router name * @return Non-zero if the router is in the InternalRouters table */ -int +bool isInternalService(char *router) { -int i; - if (router) { - for (i = 0; InternalRouters[i]; i++) + for (int i = 0; InternalRouters[i]; i++) if (strcmp(router, InternalRouters[i]) == 0) - return 1; + return true; } - return 0; + return false; } /** * Get the MAC address of first network interface diff --git a/server/core/dbusers.c b/server/core/dbusers.c index e8f38ab13..8079994c1 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2387,7 +2387,6 @@ bool check_service_permissions(SERVICE* service) skygw_log_write(LE,"%s: Error: User '%s' is missing SELECT privileges" " on mysql.user table. MySQL error message: %s", service->name,user,mysql_error(mysql)); - rval = false; } else { @@ -2395,6 +2394,7 @@ bool check_service_permissions(SERVICE* service) " MySQL error message: %s", service->name,mysql_error(mysql)); } + rval = false; } else { @@ -2405,7 +2405,7 @@ bool check_service_permissions(SERVICE* service) service->name,mysql_error(mysql)); mysql_close(mysql); free(dpasswd); - return rval; + return false; } mysql_free_result(res); @@ -2416,13 +2416,13 @@ bool check_service_permissions(SERVICE* service) { skygw_log_write(LE,"%s: Error: User '%s' is missing SELECT privileges on mysql.db table. MySQL error message: %s", service->name,user,mysql_error(mysql)); - rval = false; } else { skygw_log_write(LE,"%s: Error: Failed to query from mysql.db table. MySQL error message: %s", service->name,mysql_error(mysql)); } + rval = false; } else { @@ -2430,6 +2430,7 @@ bool check_service_permissions(SERVICE* service) { skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for permissions to the mysql.db table: %s", service->name,mysql_error(mysql)); + rval = false; } else { diff --git a/server/core/monitor.c b/server/core/monitor.c index 52d4c5f28..ef782ed47 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -493,7 +493,7 @@ bool check_monitor_permissions(MONITOR* monitor) server->port); mysql_close(mysql); free(dpasswd); - return true; + return false; } if(mysql_query(mysql,"show slave status") != 0) @@ -502,13 +502,13 @@ bool check_monitor_permissions(MONITOR* monitor) { skygw_log_write(LE,"%s: Error: User '%s' is missing REPLICATION CLIENT privileges. MySQL error message: %s", monitor->name,user,mysql_error(mysql)); - rval = false; } else { skygw_log_write(LE,"%s: Error: Monitor failed to query for slave status. MySQL error message: %s", monitor->name,mysql_error(mysql)); } + rval = false; } else { @@ -516,11 +516,12 @@ bool check_monitor_permissions(MONITOR* monitor) { skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for REPLICATION CLIENT permissions: %s", monitor->name,mysql_error(mysql)); - free(dpasswd); - mysql_close(mysql); - return rval; + rval = false; + } + else + { + mysql_free_result(res); } - mysql_free_result(res); } mysql_close(mysql); free(dpasswd); diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index 58d9a7ac5..5052d50c9 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -146,5 +146,5 @@ void config_enable_feedback_task(void); void config_disable_feedback_task(void); unsigned long config_get_gateway_id(void); GATEWAY_CONF* config_get_global_options(); -int isInternalService(char *router); +bool isInternalService(char *router); #endif From 3a901bfea7c6e45fd34158bcd92b5abadce9def2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 28 Aug 2015 12:47:38 +0300 Subject: [PATCH 34/74] Added automatic configuration of localhost_match_wildcard_host. --- server/core/dbusers.c | 35 +++++++++++++++++++++++++++++++---- server/core/service.c | 1 + server/include/service.h | 6 ++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 8079994c1..f79193226 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -82,7 +82,7 @@ #define MYSQL_USERS_COUNT "SELECT COUNT(1) AS nusers FROM mysql.user" #define MYSQL_USERS_WITH_DB_ORDER " ORDER BY host DESC" -#define LOAD_MYSQL_USERS_WITH_DB_QUERY "SELECT user.user AS user,user.host AS host,user.password AS password,concat(user.user,user.host,user.password,user.Select_priv,IFNULL(db,'')) AS userdata, user.Select_priv AS anydb,db.db AS db FROM mysql.user LEFT JOIN mysql.db ON user.user=db.user AND user.host=db.host WHERE user.user IS NOT NULL AND user.user <> ''" MYSQL_USERS_WITH_DB_ORDER +#define LOAD_MYSQL_USERS_WITH_DB_QUERY "SELECT user.user AS user,user.host AS host,user.password AS password,concat(user.user,user.host,user.password,user.Select_priv,IFNULL(db,'')) AS userdata, user.Select_priv AS anydb,db.db AS db FROM mysql.user LEFT JOIN mysql.db ON user.user=db.user AND user.host=db.host WHERE user.user IS NOT NULL" MYSQL_USERS_WITH_DB_ORDER #define MYSQL_USERS_WITH_DB_COUNT "SELECT COUNT(1) AS nusers_db FROM (" LOAD_MYSQL_USERS_WITH_DB_QUERY ") AS tbl_count" @@ -584,7 +584,8 @@ getAllUsers(SERVICE *service, USERS *users) MYSQL_DATABASE_MAXLEN; int dbnames = 0; int db_grants = 0; - + bool anon_user = false; + if (serviceGetUser(service, &service_user, &service_passwd) == 0) { ss_dassert(service_passwd == NULL || service_user == NULL); @@ -928,7 +929,16 @@ getAllUsers(SERVICE *service, USERS *users) int rc = 0; char *password = NULL; - + + /** If the username is empty, the backend server still has anonymous + * user in it. This will mean that localhost addresses do not match + * the wildcard host '%' */ + if(strlen(row[0]) == 0) + { + anon_user = true; + continue; + } + if (row[2] != NULL) { /* detect mysql_old_password (pre 4.1 protocol) */ if (strlen(row[2]) == 16) { @@ -1077,7 +1087,10 @@ getAllUsers(SERVICE *service, USERS *users) SHA1((const unsigned char *) final_data, strlen(final_data), hash); memcpy(users->cksum, hash, SHA_DIGEST_LENGTH); - + + /** Set the parameter if it is not configured by the user */ + if(service->localhost_match_wildcard_host == SERVICE_PARAM_UNINIT) + service->localhost_match_wildcard_host = anon_user ? 0 : 1; cleanup: free(dpwd); @@ -1119,6 +1132,7 @@ getUsers(SERVICE *service, USERS *users) int dbnames = 0; int db_grants = 0; char dbnm[MYSQL_DATABASE_MAXLEN+1]; + bool anon_user = false; if (serviceGetUser(service, &service_user, &service_passwd) == 0) { @@ -1438,6 +1452,15 @@ getUsers(SERVICE *service, USERS *users) int rc = 0; char *password = NULL; + /** If the username is empty, the backend server still has anonymous + * user in it. This will mean that localhost addresses do not match + * the wildcard host '%' */ + if(strlen(row[0]) == 0) + { + anon_user = true; + continue; + } + if (row[2] != NULL) { /* detect mysql_old_password (pre 4.1 protocol) */ if (strlen(row[2]) == 16) { @@ -1568,6 +1591,10 @@ getUsers(SERVICE *service, USERS *users) memcpy(users->cksum, hash, SHA_DIGEST_LENGTH); + /** Set the parameter if it is not configured by the user */ + if(service->localhost_match_wildcard_host == SERVICE_PARAM_UNINIT) + service->localhost_match_wildcard_host = anon_user ? 0 : 1; + free(users_data); mysql_free_result(result); mysql_close(con); diff --git a/server/core/service.c b/server/core/service.c index cc31fcfa1..a281db608 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -140,6 +140,7 @@ SERVICE *service; service->routerModule = strdup(router); service->users_from_all = false; service->resources = NULL; + service->localhost_match_wildcard_host = SERVICE_PARAM_UNINIT; service->ssl_mode = SSL_DISABLED; service->ssl_init_done = false; service->ssl_ca_cert = NULL; diff --git a/server/include/service.h b/server/include/service.h index b64270b9e..293856e85 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -128,6 +128,12 @@ enum{ #define DEFAULT_SSL_CERT_VERIFY_DEPTH 100 /*< The default certificate verification depth */ +/** + * Parameters that are automatically detected but can also be configured by the + * user are initially set to this value. + */ +#define SERVICE_PARAM_UNINIT -1 + /** * Defines a service within the gateway. * From 00a3d7eb56e078ace5f129a0ee1a955b639e0284 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 28 Aug 2015 17:33:05 +0300 Subject: [PATCH 35/74] Made service permission checks less strict. --- server/core/dbusers.c | 18 ++++++++++-------- server/core/service.c | 8 ++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index f79193226..63055934f 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2397,14 +2397,18 @@ bool check_service_permissions(SERVICE* service) if(mysql_real_connect(mysql,server->server->name,user,dpasswd,NULL,server->server->port,NULL,0) == NULL) { skygw_log_write(LE,"%s: Error: Failed to connect to server %s(%s:%d) when" - " checking authentication user credentials and permissions.", + " checking authentication user credentials and permissions: %d %s", service->name, server->server->unique_name, server->server->name, - server->server->port); + server->server->port, + mysql_errno(mysql), + mysql_error(mysql)); mysql_close(mysql); free(dpasswd); - return false; + + /** We don't know enough about user permissions */ + return true; } if(mysql_query(mysql,"SELECT user, host, password,Select_priv FROM mysql.user limit 1") != 0) @@ -2414,6 +2418,7 @@ bool check_service_permissions(SERVICE* service) skygw_log_write(LE,"%s: Error: User '%s' is missing SELECT privileges" " on mysql.user table. MySQL error message: %s", service->name,user,mysql_error(mysql)); + rval = false; } else { @@ -2421,7 +2426,6 @@ bool check_service_permissions(SERVICE* service) " MySQL error message: %s", service->name,mysql_error(mysql)); } - rval = false; } else { @@ -2432,9 +2436,8 @@ bool check_service_permissions(SERVICE* service) service->name,mysql_error(mysql)); mysql_close(mysql); free(dpasswd); - return false; + return true; } - mysql_free_result(res); } if(mysql_query(mysql,"SELECT user, host, db FROM mysql.db limit 1") != 0) @@ -2443,13 +2446,13 @@ bool check_service_permissions(SERVICE* service) { skygw_log_write(LE,"%s: Error: User '%s' is missing SELECT privileges on mysql.db table. MySQL error message: %s", service->name,user,mysql_error(mysql)); + rval = false; } else { skygw_log_write(LE,"%s: Error: Failed to query from mysql.db table. MySQL error message: %s", service->name,mysql_error(mysql)); } - rval = false; } else { @@ -2457,7 +2460,6 @@ bool check_service_permissions(SERVICE* service) { skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for permissions to the mysql.db table: %s", service->name,mysql_error(mysql)); - rval = false; } else { diff --git a/server/core/service.c b/server/core/service.c index a281db608..bc534f6b7 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -235,11 +235,11 @@ GWPROTOCOL *funcs; { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error : Unable to load users from %s:%d for " - "service %s.", + "Error : Unable to load users for " + "service %s listening at %s:%d.", + service->name, (port->address == NULL ? "0.0.0.0" : port->address), - port->port, - service->name))); + port->port))); { /* Try loading authentication data from file cache */ From 45227c8875b6184f13796ac5cc5d0f0e025dd3a1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 28 Aug 2015 17:39:17 +0300 Subject: [PATCH 36/74] Changed service permission checks to fail when user doesn't have access rights. --- server/core/config.c | 2 +- server/core/dbusers.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index c0113d001..acf133a92 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -898,7 +898,7 @@ process_config_context(CONFIG_CONTEXT *context) s = strtok_r(NULL, ",", &lasts); } } - else if (servers == NULL && !isInternalService(router)) + else if (servers == NULL && !isInternalService(router) && strcmp(router,"binlogrouter")) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 63055934f..d28aecaeb 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2396,19 +2396,21 @@ bool check_service_permissions(SERVICE* service) if(mysql_real_connect(mysql,server->server->name,user,dpasswd,NULL,server->server->port,NULL,0) == NULL) { + int my_errno = mysql_errno(mysql); + skygw_log_write(LE,"%s: Error: Failed to connect to server %s(%s:%d) when" " checking authentication user credentials and permissions: %d %s", service->name, server->server->unique_name, server->server->name, server->server->port, - mysql_errno(mysql), + my_errno, mysql_error(mysql)); mysql_close(mysql); free(dpasswd); /** We don't know enough about user permissions */ - return true; + return my_errno != ER_ACCESS_DENIED_ERROR; } if(mysql_query(mysql,"SELECT user, host, password,Select_priv FROM mysql.user limit 1") != 0) From b6f5108d2987bcf8274c2b5adb5c75d0351a0246 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Thu, 27 Aug 2015 12:51:25 +0300 Subject: [PATCH 37/74] Fix for MXS-335 Response handling as aborted if there is not enough data. --- server/modules/protocol/mysql_backend.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index de178fe60..661c57ca5 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -551,6 +551,18 @@ static int gw_read_backend_event(DCB *dcb) { rc = 0; goto return_rc; } + + if (!read_buffer) { + LOGIF(LM, (skygw_log_write_flush( + LOGFILE_MESSAGE, + "%lu [gw_read_backend_event] " + "Read buffer unexpectedly null, even though response " + "not marked as complete. User: %s", + pthread_self(), + current_session->user))); + rc = 0; + goto return_rc; + } } /** * Check that session is operable, and that client DCB is @@ -1562,9 +1574,10 @@ static GWBUF* process_response_data ( * enough data to read the packet length. */ init_response_status(readbuf, srvcmd, &npackets_left, &nbytes_left); - initial_packets = npackets_left; - initial_bytes = nbytes_left; } + + initial_packets = npackets_left; + initial_bytes = nbytes_left; } /** Only session commands with responses should be processed */ ss_dassert(npackets_left > 0); From ccfc6fe7802d37770276662e13e42e9fa9297ac0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 21 Aug 2015 19:10:05 +0300 Subject: [PATCH 38/74] Fix to MXS-317: https://mariadb.atlassian.net/browse/MXS-317 Log, lib, cache and run directories are created on start if they do not exist. The ownership of the directories is changed to maxscale and the permissions are set to 0755. --- etc/init.d/maxscale.in | 29 +++++++++++++++++++++++++++++ etc/ubuntu/init.d/maxscale.in | 30 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/etc/init.d/maxscale.in b/etc/init.d/maxscale.in index 38203dc11..2776d81e8 100755 --- a/etc/init.d/maxscale.in +++ b/etc/init.d/maxscale.in @@ -58,6 +58,35 @@ start() { CHECK_RET=$? [ $CHECK_RET -eq 0 ] && echo -n " found $my_check" && success && CHECK_RET=0 + if [ ! -d @MAXSCALE_VARDIR@/log/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/log/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/cache/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/cache/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/lib/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/lib/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/run/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/run/maxscale + fi + + 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 + daemon --pidfile $MAXSCALE_PIDFILE @CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale >& /dev/null RETVAL=$? diff --git a/etc/ubuntu/init.d/maxscale.in b/etc/ubuntu/init.d/maxscale.in index d17cea4ca..c9ee71bd3 100755 --- a/etc/ubuntu/init.d/maxscale.in +++ b/etc/ubuntu/init.d/maxscale.in @@ -56,6 +56,36 @@ servicename=maxscale RETVAL=0 start() { + + if [ ! -d @MAXSCALE_VARDIR@/log/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/log/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/cache/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/cache/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/lib/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/lib/maxscale + fi + + if [ ! -d @MAXSCALE_VARDIR@/run/maxscale ] + then + mkdir -p @MAXSCALE_VARDIR@/run/maxscale + fi + + 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 + log_daemon_msg "Starting MaxScale" start_daemon -p "$MAXSCALE_PIDFILE" "$DAEMON" "$DAEMON_OPTS" 2> /dev/null > /dev/null From c564b40c546d29364479075da73c0bff4963c6c2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 31 Aug 2015 10:23:51 +0300 Subject: [PATCH 39/74] Fixed gw_send_authentication_to_backend expecting a negative return value when dcb_write fails. --- server/modules/protocol/mysql_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 73865e36a..109299854 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -731,8 +731,8 @@ int gw_send_authentication_to_backend( rv = dcb_write(dcb, buffer); - if (rv < 0) { - return rv; + if (rv == 0) { + return 1; } else { return 0; } From 6601dfaaa045eae23ba75547dcf56f6427e9cac4 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 31 Aug 2015 13:00:59 +0300 Subject: [PATCH 40/74] Removed all trailing whitespace. --- log_manager/log_manager.cc | 518 ++++++++++++++++++------------------- log_manager/log_manager.h | 10 +- 2 files changed, 261 insertions(+), 267 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 516fd8ecb..5f708c5b8 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -46,7 +46,7 @@ extern char *program_invocation_name; extern char *program_invocation_short_name; -#if defined(SS_DEBUG) +#if defined(SS_DEBUG) static int write_index; static int block_start_index; static int prevval; @@ -64,13 +64,13 @@ static int use_stdout = 0; int lm_enabled_logfiles_bitmask = 0; /** - * Thread-specific struct variable for storing current session id and currently - * enabled log files for the session. + * Thread-specific struct variable for storing current session id and currently + * enabled log files for the session. */ __thread log_info_t tls_log_info = {0, 0}; /** - * Global counter for each log file type. It indicates for how many sessions + * Global counter for each log file type. It indicates for how many sessions * each log type is currently enabled. */ ssize_t log_ses_count[LOGFILE_LAST] = {0}; @@ -197,7 +197,7 @@ struct fnames_conf_st { char* fn_err_prefix; char* fn_err_suffix; char* fn_logpath; -#if defined(SS_DEBUG) +#if defined(SS_DEBUG) skygw_chk_t fn_chk_tail; #endif }; @@ -304,7 +304,7 @@ bool thr_flushall_check(); const char* get_suffix_default(void) { - return ".log"; + return ".log"; } const char* get_debug_prefix_default(void) @@ -362,7 +362,7 @@ static bool logmanager_init_nomutex( bool succp = false; lm = (logmanager_t *)calloc(1, sizeof(logmanager_t)); - + if (lm == NULL) { err = 1; @@ -378,14 +378,14 @@ static bool logmanager_init_nomutex( #endif lm->lm_clientmes = skygw_message_init(); lm->lm_logmes = skygw_message_init(); - - if (lm->lm_clientmes == NULL || + + if (lm->lm_clientmes == NULL || lm->lm_logmes == NULL) { err = 1; goto return_succp; } - + lm->lm_enabled_logfiles |= LOGFILE_ERROR; lm->lm_enabled_logfiles |= LOGFILE_MESSAGE; #if defined(SS_DEBUG) @@ -404,34 +404,34 @@ static bool logmanager_init_nomutex( } /** Initialize configuration including log file naming info */ - if (!fnames_conf_init(fn, argc, argv)) + if (!fnames_conf_init(fn, argc, argv)) { err = 1; goto return_succp; } /** Initialize logfiles */ - if(!logfiles_init(lm)) + if(!logfiles_init(lm)) { err = 1; goto return_succp; } - + /** * Set global variable */ lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles; - - /** + + /** * Initialize filewriter data and open the log file - * for each log file type. + * for each log file type. */ - if (!filewriter_init(lm, fw, lm->lm_clientmes, lm->lm_logmes)) + if (!filewriter_init(lm, fw, lm->lm_clientmes, lm->lm_logmes)) { err = 1; goto return_succp; } - + /** Initialize and start filewriter thread */ fw->fwr_thread = skygw_thread_init("filewriter thr", thr_filewriter_fun, @@ -443,7 +443,7 @@ static bool logmanager_init_nomutex( goto return_succp; } - if ((err = skygw_thread_start(fw->fwr_thread)) != 0) + if ((err = skygw_thread_start(fw->fwr_thread)) != 0) { goto return_succp; } @@ -452,9 +452,9 @@ static bool logmanager_init_nomutex( succp = true; lm->lm_enabled = true; - + return_succp: - if (err != 0) + if (err != 0) { /** This releases memory of all created objects */ logmanager_done_nomutex(); @@ -465,7 +465,7 @@ return_succp: -/** +/** * Initializes log managing routines in MariaDB Corporation MaxScale. * * Parameters: @@ -481,25 +481,25 @@ bool skygw_logmanager_init( char* argv[]) { bool succp = false; - + acquire_lock(&lmlock); if (lm != NULL) { succp = true; goto return_succp; } - + succp = logmanager_init_nomutex(argc, argv); - + return_succp: release_lock(&lmlock); - + return succp; } /** - * Release resources of log manager. - * + * Release resources of log manager. + * * Lock must have been acquired before calling * this function. */ @@ -517,16 +517,16 @@ static void logmanager_done_nomutex(void) skygw_thread_set_exitflag(fwr->fwr_thread, fwr->fwr_logmes, fwr->fwr_clientmes); - + /** Free thread memory */ skygw_thread_done(fwr->fwr_thread); } - + /** Free filewriter memory. */ filewriter_done(fwr); - + for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) { - lf = logmanager_get_logfile(lm, (logfile_id_t)i); + lf = logmanager_get_logfile(lm, (logfile_id_t)i); /** Release logfile memory */ logfile_done(lf); } @@ -546,15 +546,15 @@ static void logmanager_done_nomutex(void) } -/** - * This function is provided for atexit() system function. +/** + * This function is provided for atexit() system function. */ void skygw_logmanager_exit(void) { skygw_logmanager_done(); } -/** +/** * End execution of log manager * * Stops file writing thread, releases filewriter, and logfiles. @@ -571,7 +571,7 @@ void skygw_logmanager_done(void) CHK_LOGMANAGER(lm); /** Mark logmanager unavailable */ lm->lm_enabled = false; - + /** Wait until all users have left or someone shuts down * logmanager between lock release and acquire. */ @@ -608,26 +608,26 @@ static logfile_t* logmanager_get_logfile( } -/** +/** * Finds write position from block buffer for log string and writes there. - * + * * Parameters: * * @param id logfile object identifier - * @param flush indicates whether log string must be written to disk + * @param flush indicates whether log string must be written to disk * immediately - * @param use_valist does write involve formatting of the string and use of + * @param use_valist does write involve formatting of the string and use of * valist argument - * @param spread_down if true, log string is spread to all logs having - * larger id. - * @param rotate if set, closes currently open log file and opens a + * @param spread_down if true, log string is spread to all logs having + * larger id + * @param rotate if set, closes currently open log file and opens a * new one * @param str_len length of formatted string - * @param str string to be written to log + * @param str string to be written to log * @param valist variable-length argument list for formatting the string * * @return 0 if succeed, -1 otherwise - * + * */ static int logmanager_write_log( logfile_id_t id, @@ -648,7 +648,7 @@ static int logmanager_write_log( int i; CHK_LOGMANAGER(lm); - + if (id < LOGFILE_FIRST || id > LOGFILE_LAST) { const char* errstr = "Invalid logfile id argument."; /** @@ -668,7 +668,7 @@ static int logmanager_write_log( STRLOGID(LOGFILE_ERROR)); } err = -1; - ss_dassert(false); + ss_dassert(false); goto return_err; } lf = &lm->lm_logfile[id]; @@ -681,28 +681,28 @@ static int logmanager_write_log( { if (flush) { - logfile_flush(lf); /*< wakes up file writer */ + logfile_flush(lf); /*< wakes up file writer */ } else if (rotate) { - logfile_rotate(lf); /*< wakes up file writer */ + logfile_rotate(lf); /*< wakes up file writer */ } } else { /** Length of string that will be written, limited by bufsize */ - size_t safe_str_len; + size_t safe_str_len; /** Length of session id */ size_t sesid_str_len; size_t cmplen = 0; - /** + /** * 2 braces, 2 spaces and terminating char - * If session id is stored to tls_log_info structure, allocate + * If session id is stored to tls_log_info structure, allocate * room for session id too. */ if (id == LOGFILE_TRACE && tls_log_info.li_sesid != 0) { - sesid_str_len = 5*sizeof(char)+get_decimal_len(tls_log_info.li_sesid); + sesid_str_len = 5*sizeof(char)+get_decimal_len(tls_log_info.li_sesid); } else { @@ -713,7 +713,7 @@ static int logmanager_write_log( else timestamp_len = get_timestamp_len(); cmplen = sesid_str_len > 0 ? sesid_str_len - sizeof(char) : 0; - + /** Find out how much can be safely written with current block size */ if (timestamp_len-sizeof(char)+cmplen+str_len > lf->lf_buf_size) { @@ -787,9 +787,9 @@ static int logmanager_write_log( /** * Write session id */ - snprintf(wp+timestamp_len, - sesid_str_len, - "[%lu] ", + snprintf(wp+timestamp_len, + sesid_str_len, + "[%lu] ", tls_log_info.li_sesid); sesid_str_len -= 1; /*< don't calculate terminating char anymore */ } @@ -798,14 +798,14 @@ static int logmanager_write_log( * of the timestamp string. */ if (use_valist) { - vsnprintf(wp+timestamp_len+sesid_str_len, - safe_str_len-timestamp_len-sesid_str_len, - str, + vsnprintf(wp+timestamp_len+sesid_str_len, + safe_str_len-timestamp_len-sesid_str_len, + str, valist); } else { snprintf(wp+timestamp_len+sesid_str_len, - safe_str_len-timestamp_len-sesid_str_len, - "%s", + safe_str_len-timestamp_len-sesid_str_len, + "%s", str); } @@ -816,17 +816,17 @@ static int logmanager_write_log( case LOGFILE_ERROR: syslog(LOG_ERR, "%s", wp+timestamp_len); break; - + case LOGFILE_MESSAGE: syslog(LOG_NOTICE, "%s", wp+timestamp_len); break; - + default: break; } } /** remove double line feed */ - if (wp[safe_str_len-2] == '\n') + if (wp[safe_str_len-2] == '\n') { wp[safe_str_len-2]=' '; } @@ -861,7 +861,7 @@ static int logmanager_write_log( { /** pointer to write buffer of larger-id log */ char* wp_c; - + /**< Check if particular log is enabled */ if (!(lm->lm_enabled_logfiles & i)) { @@ -882,21 +882,21 @@ static int logmanager_write_log( * other logs' block buffers. */ snprintf(wp_c, timestamp_len+str_len, "%s", wp); - + /** remove double line feed */ if (wp_c[timestamp_len-1+str_len-2] == '\n') { wp_c[timestamp_len-1+str_len-2]=' '; } wp_c[timestamp_len-1+str_len-1]='\n'; - + /** lock-free unregistration, includes flush if * bb_state == BB_FULL */ blockbuf_unregister(bb_c); } } /* if (spread_down) */ } /* if (str == NULL) */ - + return_err: return err; } @@ -904,7 +904,7 @@ return_err: /** * Register writer to a block buffer. When reference counter is non-zero the * flusher thread doesn't write the block to disk. - * + * * @param bb block buffer */ static void blockbuf_register( @@ -918,14 +918,14 @@ static void blockbuf_register( /** * Unregister writer from block buffer. If the buffer got filled up and there * are no other registered writers anymore, notify the flusher thread. - * + * * @param bb block buffer */ static void blockbuf_unregister( blockbuf_t* bb) { logfile_t* lf; - + CHK_BLOCKBUF(bb); ss_dassert(bb->bb_refcount >= 1); lf = &lm->lm_logfile[bb->bb_fileid]; @@ -940,8 +940,8 @@ static void blockbuf_unregister( } -/** - * @node (write brief function description here) +/** + * @node (write brief function description here) * * Parameters: * @param id - @@ -950,9 +950,9 @@ static void blockbuf_unregister( * @param str_len - * * - * @return + * @return + * * - * * @details List mutex now protects both the list and the contents of it. * TODO : It should be so that adding and removing nodes of the list is protected * by the list mutex. Buffer modifications should be protected by buffer @@ -972,7 +972,6 @@ static char* blockbuf_get_writepos( blockbuf_t* bb; ss_debug(bool succp;) - CHK_LOGMANAGER(lm); lf = &lm->lm_logfile[id]; CHK_LOGFILE(lf); @@ -984,24 +983,23 @@ static char* blockbuf_get_writepos( if (bb_list->mlist_nodecount > 0) { /** - * At least block buffer exists on the list. + * At least block buffer exists on the list. */ node = bb_list->mlist_first; - - + /** Loop over blockbuf list to find write position */ while (true) { CHK_MLIST_NODE(node); /** Unlock list */ simple_mutex_unlock(&bb_list->mlist_mutex); - + bb = (blockbuf_t *)node->mlnode_data; CHK_BLOCKBUF(bb); /** Lock buffer */ simple_mutex_lock(&bb->bb_mutex, true); - + if (bb->bb_state == BB_FULL || bb->bb_buf_left < str_len) { /** * This block buffer is too full. @@ -1009,11 +1007,11 @@ static char* blockbuf_get_writepos( * flushing all buffers, and (eventually) frees buffer space. */ blockbuf_register(bb); - + bb->bb_state = BB_FULL; blockbuf_unregister(bb); - + /** Unlock buffer */ simple_mutex_unlock(&bb->bb_mutex); @@ -1047,7 +1045,7 @@ static char* blockbuf_get_writepos( */ bb_list->mlist_versno += 1; ss_dassert(bb_list->mlist_versno%2 == 1); - + ss_debug(succp =) mlist_add_data_nomutex(bb_list, bb); ss_dassert(succp); @@ -1069,7 +1067,6 @@ static char* blockbuf_get_writepos( node = bb_list->mlist_first; continue; } - }else if(bb->bb_state == BB_CLEARED){ @@ -1079,7 +1076,7 @@ static char* blockbuf_get_writepos( simple_mutex_unlock(&bb->bb_mutex); simple_mutex_lock(&bb_list->mlist_mutex, true); - + if(node == bb_list->mlist_first) { @@ -1112,9 +1109,6 @@ static char* blockbuf_get_writepos( continue; } - - - }else if (bb->bb_state == BB_READY){ /** * There is space for new log string. @@ -1147,11 +1141,11 @@ static char* blockbuf_get_writepos( */ bb_list->mlist_versno += 1; ss_dassert(bb_list->mlist_versno%2 == 0); - + /** Unlock list */ simple_mutex_unlock(&bb_list->mlist_mutex); } /* if (bb_list->mlist_nodecount > 0) */ - + ss_dassert(pos == NULL); ss_dassert(!(bb->bb_state == BB_FULL || bb->bb_buf_left < str_len)); ss_dassert(bb_list->mlist_nodecount <= bb_list->mlist_nodecount_max); @@ -1171,29 +1165,29 @@ static char* blockbuf_get_writepos( pos = &bb->bb_buf[bb->bb_buf_used]; bb->bb_buf_used += str_len; bb->bb_buf_left -= str_len; - + ss_dassert(pos >= &bb->bb_buf[0] && pos <= &bb->bb_buf[MAX_LOGSTRLEN-str_len]); - + /** read checkmark */ /** TODO: add buffer overflow checkmark chk_val = (int)bb->bb_buf[bb->bb_buf_used-count_len]; ss_dassert(chk_val == bb->bb_strcount); */ - + /** TODO : write next checkmark bb->bb_strcount += 1; memcpy(&bb->bb_buf[bb->bb_buf_used], &bb->bb_strcount, count_len); bb->bb_buf_used += count_len; bb->bb_buf_left -= count_len; */ - + /** * If flush flag is set, set buffer full. As a consequence, no-one * can write to it before it is flushed to disk. */ bb->bb_state = (flush == true ? BB_FULL : bb->bb_state); - + /** Unlock buffer */ simple_mutex_unlock(&bb->bb_mutex); return pos; @@ -1238,7 +1232,7 @@ int skygw_log_enable( { bool err = 0; - if (!logmanager_register(true)) + if (!logmanager_register(true)) { err = -1; goto return_err; @@ -1252,7 +1246,7 @@ int skygw_log_enable( */ lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles; } - + logmanager_unregister(); return_err: return err; @@ -1262,9 +1256,9 @@ int skygw_log_disable( logfile_id_t id) /*< no locking */ { int rc; - + rc = skygw_log_disable_raw(id, false); - + return rc; } @@ -1274,14 +1268,14 @@ static int skygw_log_disable_raw( { bool err = 0; - if (!logmanager_register(true)) + if (!logmanager_register(true)) { err = -1; goto return_err; } CHK_LOGMANAGER(lm); - if (emergency || logfile_set_enabled(id, false)) + if (emergency || logfile_set_enabled(id, false)) { lm->lm_enabled_logfiles &= ~id; /** @@ -1306,9 +1300,9 @@ static bool logfile_set_enabled( bool succp = false; int err = 0; logfile_t* lf; - + CHK_LOGMANAGER(lm); - + if (id < LOGFILE_FIRST || id > LOGFILE_LAST) { const char* errstr = "Invalid logfile id argument."; /** @@ -1327,7 +1321,7 @@ static bool logfile_set_enabled( "* Writing to logfile %s failed.\n", STRLOGID(LOGFILE_ERROR)); } - ss_dassert(false); + ss_dassert(false); goto return_succp; } lf = &lm->lm_logfile[id]; @@ -1339,7 +1333,7 @@ static bool logfile_set_enabled( } else { logstr = strdup("---\tLogging to file is disabled\t--"); } - + oldval = lf->lf_enabled; lf->lf_enabled = val; err = logmanager_write_log(id, @@ -1375,7 +1369,7 @@ int skygw_log_write_flush( va_list valist; size_t len; - if (!logmanager_register(true)) + if (!logmanager_register(true)) { err = -1; goto return_err; @@ -1431,8 +1425,8 @@ int skygw_log_write( int i,err = 0; va_list valist; size_t len; - - if (!logmanager_register(true)) + + if (!logmanager_register(true)) { err = -1; goto return_err; @@ -1490,7 +1484,7 @@ int skygw_log_flush( { int err = 0; va_list valist; /**< Dummy, must be present but it is not processed */ - + if (!logmanager_register(false)) { ss_dfprintf(stderr, "Can't register to logmanager, nothing to flush\n"); @@ -1511,7 +1505,7 @@ return_err: } /** - * Replace current logfile with new file with increased sequence number on + * Replace current logfile with new file with increased sequence number on * its name. */ int skygw_log_rotate( @@ -1520,8 +1514,8 @@ int skygw_log_rotate( int err = 0; logfile_t* lf; va_list valist; /**< Dummy, must be present but it is not processed */ - - if (!logmanager_register(false)) + + if (!logmanager_register(false)) { ss_dfprintf(stderr, "Can't register to logmanager, rotating failed\n"); @@ -1529,25 +1523,25 @@ int skygw_log_rotate( } CHK_LOGMANAGER(lm); lf = &lm->lm_logfile[id]; - + LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Log rotation is called for %s.", lf->lf_full_file_name))); - + err = logmanager_write_log(id, false, false, false, true, 0, NULL, valist); - - if (err != 0) + + if (err != 0) { LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "Log file rotation failed for file %s.", lf->lf_full_file_name))); - + fprintf(stderr, "skygw_log_rotate failed.\n"); goto return_unregister; } - + return_unregister: LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, @@ -1564,16 +1558,16 @@ return_err: -/** - * @node Register as a logging client to logmanager. +/** + * @node Register as a logging client to logmanager. * * Parameters: * @param lmgr - * * - * @return + * @return + * * - * * @details Link count modify is protected by mutex. * */ @@ -1608,7 +1602,7 @@ static bool logmanager_register( pthread_yield(); acquire_lock(&lmlock); } - + if (lm == NULL) { succp = logmanager_init_nomutex(0, NULL); } @@ -1617,22 +1611,22 @@ static bool logmanager_register( if (succp) { lm->lm_nlinks += 1; } - + return_succp: - release_lock(&lmlock); + release_lock(&lmlock); return succp; } -/** - * @node Unregister from logmanager. +/** + * @node Unregister from logmanager. * * Parameters: * @param lmgr - * * - * @return + * @return + * * - * * @details Link count modify is protected by mutex. * */ @@ -1647,7 +1641,7 @@ static void logmanager_unregister(void) } -/** +/** * @node Initialize log file naming parameters from call arguments * or from default functions in cases where arguments are not provided. * @@ -1663,7 +1657,7 @@ static void logmanager_unregister(void) * * @return pointer to object which is either in RUN or DONE state. * - * + * * @details Note that input parameter lenghts are checked here. * */ @@ -1731,7 +1725,7 @@ static bool fnames_conf_init( case 'g': fn->fn_err_prefix = strndup(optarg, MAX_PREFIXLEN); break; - + case 'i': fn->fn_err_suffix = strndup(optarg, MAX_SUFFIXLEN); break; @@ -1759,7 +1753,7 @@ static bool fnames_conf_init( */ syslog_ident_str = optarg; break; - + case 's': /** record list of log file ids for later use */ shmem_id_str = optarg; @@ -1797,7 +1791,7 @@ static bool fnames_conf_init( { syslog_ident_str = (syslog_ident_str == NULL ? - (argv == NULL ? strdup(program_invocation_short_name) : + (argv == NULL ? strdup(program_invocation_short_name) : strdup(*argv)) : syslog_ident_str); } @@ -1828,7 +1822,7 @@ static bool fnames_conf_init( succp = true; fn->fn_state = RUN; CHK_FNAMES_CONF(fn); - + return_conf_init: if (!succp) { fnames_conf_done(fn); @@ -1849,7 +1843,7 @@ static char* fname_conf_get_prefix( case LOGFILE_DEBUG: return strdup(fn->fn_debug_prefix); break; - + case LOGFILE_TRACE: return strdup(fn->fn_trace_prefix); break; @@ -1897,16 +1891,16 @@ static char* fname_conf_get_suffix( } -/** +/** * @node Calls logfile initializer for each logfile. - * + * * * Parameters: * @param lm Log manager pointer * - * @return succp true if succeed, otherwise false. + * @return succp true if succeed, otherwise false. + * * - * * @details If logfile is supposed to be located to shared memory * it is specified here. In the case of shared memory file, a soft * link is created to log directory. @@ -1965,7 +1959,7 @@ static bool logfiles_init( lm, store_shmem, write_syslog); - + if (!succp) { fprintf(stderr, "*\n* Error : Initializing log files failed.\n"); break; @@ -1987,9 +1981,9 @@ static void logfile_flush( } /** - * Set rotate flag for a log file and wake up the writer thread which then + * Set rotate flag for a log file and wake up the writer thread which then * performs the actual rotation task. - * + * * @param lf logfile pointer */ static void logfile_rotate( @@ -2005,14 +1999,14 @@ static void logfile_rotate( /** * Forms complete path name for logfile and tests that the file doesn't conflict * with any existing file and it is writable. - * + * * @param lf logfile pointer - * + * * @return true if succeed, false if failed - * - * @note Log file openings are not TOCTOU-safe. It is not likely that - * multiple copies of same files are opened in parallel but it is possible by - * using log manager in parallel with multiple processes and by configuring + * + * @note Log file openings are not TOCTOU-safe. It is not likely that + * multiple copies of same files are opened in parallel but it is possible by + * using log manager in parallel with multiple processes and by configuring * log manager to use same directories among those processes. */ static bool logfile_create( @@ -2032,16 +2026,16 @@ static bool logfile_create( spart[0].sp_next = &spart[1]; spart[1].sp_next = &spart[2]; spart[2].sp_next = NULL; - + spart[1].sp_string = lf->lf_name_prefix; spart[2].sp_string = lf->lf_name_suffix; - + store_shmem = lf->lf_store_shmem; - + do { namecreatefail = false; nameconflicts = false; - + spart[0].sp_string = lf->lf_filepath; /** * Create name for log file. Seqno is added between prefix & @@ -2049,8 +2043,8 @@ static bool logfile_create( */ lf->lf_full_file_name = form_full_file_name(spart, lf, 2); - - if (store_shmem) + + if (store_shmem) { spart[0].sp_string = lf->lf_linkpath; /** @@ -2068,7 +2062,7 @@ static bool logfile_create( namecreatefail = true; goto file_create_fail; } - + /** * If file exists but is different type, create fails and * new, increased sequence number is added to file name. @@ -2084,8 +2078,8 @@ static bool logfile_create( } else { - /** - * Opening the file failed for some other reason than + /** + * Opening the file failed for some other reason than * existing non-writable file. Shut down. */ if (!writable) @@ -2094,7 +2088,7 @@ static bool logfile_create( goto return_succp; } } - + if (store_shmem) { if (check_file_and_path(lf->lf_full_link_name, &writable, true)) @@ -2107,8 +2101,8 @@ static bool logfile_create( } else { - /** - * Opening the file failed for some other reason than + /** + * Opening the file failed for some other reason than * existing non-writable file. Shut down. */ if (!writable) @@ -2122,7 +2116,7 @@ file_create_fail: if (namecreatefail || nameconflicts) { lf->lf_name_seqno += 1; - + if (lf->lf_full_file_name != NULL) { free(lf->lf_full_file_name); @@ -2135,24 +2129,24 @@ file_create_fail: } } } while (namecreatefail || nameconflicts); - + succp = true; - + return_succp: return succp; } /** - * Opens a log file and writes header to the beginning of it. File name, FILE*, - * and file descriptor are stored to skygw_file_t struct which is stored in + * Opens a log file and writes header to the beginning of it. File name, FILE*, + * and file descriptor are stored to skygw_file_t struct which is stored in * filewriter strcuture passed as parameter. - * + * * @param fw filewriter pointer * @param lf logfile pointer - * - * @return true if succeed; the resulting skygw_file_t is written in filewriter, - * false if failed. - * + * + * @return true if succeed; the resulting skygw_file_t is written in filewriter, + * false if failed. + * */ static bool logfile_open_file( filewriter_t* fw, @@ -2182,10 +2176,10 @@ static bool logfile_open_file( lf->lf_full_file_name, NULL); } - - if (fw->fwr_file[lf->lf_id] == NULL) + + if (fw->fwr_file[lf->lf_id] == NULL) { - fprintf(stderr, + fprintf(stderr, "Error : opening logfile %s failed.\n", lf->lf_full_file_name); succp = false; @@ -2206,7 +2200,7 @@ static bool logfile_open_file( (void *)start_msg_str, strlen(start_msg_str), true); - + if (err != 0) { fprintf(stderr, @@ -2221,17 +2215,17 @@ static bool logfile_open_file( free(start_msg_str); } succp = true; - + return_succp: return succp; } -/** - * @node Combine all name parts from left to right. +/** + * @node Combine all name parts from left to right. * * Parameters: - * @param parts + * @param parts * * @param seqno specifies the the sequence number which will be added as a part * of full file name. seqno == -1 indicates that sequence number won't be used. @@ -2254,15 +2248,15 @@ static char* form_full_file_name( char* filename = NULL; char* seqnostr = NULL; strpart_t* p; - + if (lf->lf_name_seqno != -1) { int file_sn; int link_sn = 0; char* tmp = parts[0].sp_string; - + file_sn = find_last_seqno(parts, lf->lf_name_seqno, seqnoidx); - + if (lf->lf_linkpath != NULL) { tmp = parts[0].sp_string; @@ -2271,8 +2265,8 @@ static char* form_full_file_name( parts[0].sp_string = tmp; } lf->lf_name_seqno = MAX(file_sn, link_sn); - - seqno = lf->lf_name_seqno; + + seqno = lf->lf_name_seqno; s = UINTLEN(seqno); seqnostr = (char *)malloc((int)s+1); } @@ -2286,7 +2280,7 @@ static char* form_full_file_name( seqnoidx = -1; seqno = lf->lf_name_seqno; } - + if (parts == NULL || parts->sp_string == NULL) { goto return_filename; } @@ -2300,7 +2294,7 @@ static char* form_full_file_name( while (p->sp_string != NULL) { fnlen += strnlen(p->sp_string, NAME_MAX); - + if (p->sp_next == NULL) { break; @@ -2308,18 +2302,18 @@ static char* form_full_file_name( p = p->sp_next; } - if (fnlen > NAME_MAX) + if (fnlen > NAME_MAX) { fprintf(stderr, "Error : Too long file name= %d.\n", (int)fnlen); goto return_filename; } filename = (char*)calloc(1, fnlen); - + if (seqnostr != NULL) { snprintf(seqnostr, s+1, "%d", seqno); } - + for (i=0, p=parts; p->sp_string != NULL; i++, p=p->sp_next) { if (seqnostr != NULL && i == seqnoidx) @@ -2333,24 +2327,24 @@ static char* form_full_file_name( break; } } - + return_filename: if (seqnostr != NULL) free(seqnostr); return filename; } -/** +/** * @node Allocate new buffer where argument string with a slash is copied. * Original string buffer is freed. - * added. + * added. * * Parameters: * @param str - * * - * @return + * @return + * * - * * @details (write detailed description here) * */ @@ -2359,7 +2353,7 @@ static char* add_slash( { char* p = str; size_t plen = strlen(p); - + /** Add slash if missing */ if (p[plen-1] != '/') { @@ -2374,16 +2368,16 @@ static char* add_slash( /** * @node Check if the path and file exist in the local file system and if they do, * check if they are accessible and writable. - * + * * Parameters: * @param filename file to be checked * * @param writable flag indicating whether file was found writable or not * if writable is NULL, check is skipped. * - * @return true & writable if file exists and it is writable, - * true & not writable if file exists but it can't be written, - * false & writable if file doesn't exist but directory could be written, and + * @return true & writable if file exists and it is writable, + * true & not writable if file exists but it can't be written, + * false & writable if file doesn't exist but directory could be written, and * false & not writable if directory can't be written. * * @details Note, that a space character is written to the end of file. @@ -2396,11 +2390,11 @@ static bool check_file_and_path( bool do_log) { bool exists; - + if (filename == NULL) { exists = false; - + if (writable) { *writable = false; @@ -2479,10 +2473,10 @@ static bool file_is_symlink( } return succp; } - -/** + +/** * @node Initialize logfile structure. Form log file name, and optionally * link name. Create block buffer for logfile. * @@ -2492,7 +2486,7 @@ static bool file_is_symlink( * @param logmanager log manager pointer * @param store_shmem flag to indicate whether log is physically written to shmem * @param write_syslog flag to indicate whether log is also written to syslog - * + * * @return true if succeed, false otherwise */ static bool logfile_init( @@ -2528,16 +2522,16 @@ static bool logfile_init( * pointing to shm file is created and located to the file * directory. */ - if (store_shmem) + if (store_shmem) { char* c; pid_t pid = getpid(); int len = strlen(shm_pathname_prefix)+ + strlen("maxscale.") + get_decimal_len((size_t)pid) + 1; - + c = (char *)calloc(len, sizeof(char)); - + if (c == NULL) { succp = false; @@ -2545,7 +2539,7 @@ static bool logfile_init( } sprintf(c, "%smaxscale.%d", shm_pathname_prefix, pid); logfile->lf_filepath = c; - + if (mkdir(c, S_IRWXU | S_IRWXG) != 0 && errno != EEXIST) { @@ -2554,8 +2548,8 @@ static bool logfile_init( } logfile->lf_linkpath = strdup(fn->fn_logpath); logfile->lf_linkpath = add_slash(logfile->lf_linkpath); - } - else + } + else { logfile->lf_filepath = strdup(fn->fn_logpath); } @@ -2585,14 +2579,14 @@ static bool logfile_init( #if defined(SS_DEBUG) if (store_shmem && !use_stdout) { - fprintf(stderr, "%s\t: %s->%s\n", + fprintf(stderr, "%s\t: %s->%s\n", STRLOGNAME(logfile_id), logfile->lf_full_link_name, logfile->lf_full_file_name); } else if(!use_stdout) { - fprintf(stderr, "%s\t: %s\n", + fprintf(stderr, "%s\t: %s\n", STRLOGNAME(logfile_id), logfile->lf_full_file_name); } @@ -2600,18 +2594,18 @@ static bool logfile_init( succp = true; logfile->lf_state = RUN; CHK_LOGFILE(logfile); - + return_with_succp: - if (!succp) + if (!succp) { logfile_done(logfile); } ss_dassert(logfile->lf_state == RUN || logfile->lf_state == DONE); return succp; } - -/** - * @node Flush logfile and free memory allocated for it. + +/** + * @node Flush logfile and free memory allocated for it. * * Parameters: * @param lf - @@ -2619,7 +2613,7 @@ return_with_succp: * * @return void * - * + * * @details Operation is not protected. it is assumed that no one tries * to call logfile functions when logfile_done is called. * @@ -2664,8 +2658,8 @@ static void logfile_free_memory( if (lf->lf_full_file_name != NULL) free(lf->lf_full_file_name); } -/** - * @node Initialize filewriter data and open the log file for each log file type. +/** + * @node Initialize filewriter data and open the log file for each log file type. * * @param logmanager Log manager struct * @param fw File writer struct @@ -2685,7 +2679,7 @@ static bool filewriter_init( logfile_t* lf; logfile_id_t id; int i; - + CHK_LOGMANAGER(logmanager); fw->fwr_state = INIT; @@ -2702,20 +2696,20 @@ static bool filewriter_init( if (fw->fwr_logmes == NULL || fw->fwr_clientmes == NULL) { goto return_succp; } - - for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i <<= 1) + + for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i <<= 1) { id = (logfile_id_t)i; lf = logmanager_get_logfile(logmanager, id); if (!(succp = logfile_open_file(fw, lf))) { - fprintf(stderr, + fprintf(stderr, "Error : opening log file %s failed. Exiting " "MaxScale\n", lf->lf_full_file_name); goto return_succp; - } + } } /*< for */ fw->fwr_state = RUN; CHK_FILEWRITER(fw); @@ -2740,8 +2734,8 @@ static void filewriter_done( CHK_FILEWRITER(fw); case INIT: fw->fwr_logmes = NULL; - fw->fwr_clientmes = NULL; - for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) + fw->fwr_clientmes = NULL; + for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) { id = (logfile_id_t)i; if(use_stdout) @@ -2758,16 +2752,16 @@ static void filewriter_done( } -/** +/** * @node Writes block buffers of logfiles to physical log files on disk. * * Parameters: * @param data - thread context, skygw_thread_t * * - * @return + * @return + * * - * * @details Waits until receives wake-up message. Scans through block buffer * lists of each logfile object. * @@ -2775,7 +2769,7 @@ static void filewriter_done( * 1. bb_state == true, * 2. logfile object's lf_flushflag == true, or * 3. skygw_thread_must_exit returns true. - * + * * Log file is flushed (fsync'd) in cases #2 and #3. * * Concurrency control : block buffer is accessed by file writer (this) and @@ -2808,7 +2802,7 @@ static void* thr_filewriter_fun( filewriter_t* fwr; skygw_file_t* file; logfile_t* lf; - + mlist_t* bb_list; blockbuf_t* bb; mlist_node_t* node; @@ -2839,7 +2833,7 @@ static void* thr_filewriter_fun( if(skygw_thread_must_exit(thr)){ flushall_logfiles(true); } - + /** Process all logfiles which have buffered writes. */ for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i <<= 1) { @@ -2870,13 +2864,13 @@ static void* thr_filewriter_fun( if (rotate_logfile) { bool succp; - + lf->lf_name_seqno += 1; /*< new sequence number */ - + if (!(succp = logfile_create(lf))) { lf->lf_name_seqno -= 1; /*< restore */ - } + } else if ((succp = logfile_open_file(fwr, lf))) { if(use_stdout) @@ -2884,7 +2878,7 @@ static void* thr_filewriter_fun( else skygw_file_close(file, false); /*< close old file */ } - + if (!succp) { LOGIF(LE, (skygw_log_write( @@ -2907,11 +2901,11 @@ static void* thr_filewriter_fun( simple_mutex_unlock(&bb_list->mlist_mutex); #endif node = bb_list->mlist_first; - - while (node != NULL) + + while (node != NULL) { int err = 0; - + CHK_MLIST_NODE(node); bb = (blockbuf_t *)node->mlnode_data; CHK_BLOCKBUF(bb); @@ -2920,7 +2914,7 @@ static void* thr_filewriter_fun( simple_mutex_lock(&bb->bb_mutex, true); flush_blockbuf = bb->bb_state; - + if (bb->bb_buf_used != 0 && (flush_blockbuf == BB_FULL || flush_logfile || @@ -2930,7 +2924,7 @@ static void* thr_filewriter_fun( * buffer is at least half-full * -> write to disk */ - while(bb->bb_refcount > 0) + while(bb->bb_refcount > 0) { simple_mutex_unlock( &bb->bb_mutex); @@ -2942,7 +2936,7 @@ static void* thr_filewriter_fun( file, (void *)bb->bb_buf, bb->bb_buf_used, - (flush_logfile || + (flush_logfile || do_flushall)); if (err) { @@ -2974,7 +2968,7 @@ static void* thr_filewriter_fun( } /** Release lock to block buffer */ simple_mutex_unlock(&bb->bb_mutex); - + /** Consistent lock-free read on the list */ do { while ((vn1 = bb_list->mlist_versno)%2 @@ -2982,7 +2976,7 @@ static void* thr_filewriter_fun( node = node->mlnode_next; vn2 = bb_list->mlist_versno; } while (vn1 != vn2 && node); - + } /* while (node != NULL) */ /** @@ -3005,15 +2999,15 @@ static void* thr_filewriter_fun( goto retry_flush_on_exit; } }/* for */ - + if(flushall_done_flag){ flushall_done_flag = false; flushall_logfiles(false); skygw_message_send(fwr->fwr_clientmes); } - + } /* while (!skygw_thread_must_exit) */ - + ss_debug(skygw_thread_set_state(thr, THR_STOPPED)); /** Inform log manager that file writer thread has stopped. */ skygw_message_send(fwr->fwr_clientmes); @@ -3053,23 +3047,23 @@ static void fnames_conf_free_memory( } /** - * Find the file with biggest sequence number from given directory and return + * Find the file with biggest sequence number from given directory and return * the sequence number. - * + * * @param parts string parts of which the file name is composed of * @param seqno the sequence number to start with, if seqno is -1 just return - * - * @return the biggest sequence number used + * + * @return the biggest sequence number used */ static int find_last_seqno( - strpart_t* parts, + strpart_t* parts, int seqno, int seqnoidx) { strpart_t* p; char* snstr; int snstrlen; - + if (seqno == -1) { return seqno; @@ -3077,14 +3071,14 @@ static int find_last_seqno( snstrlen = UINTLEN(INT_MAX); snstr = (char *)calloc(1, snstrlen); p = parts; - + while (true) { int i; char filename[NAME_MAX] = {0}; /** Form name with next seqno */ snprintf(snstr, snstrlen, "%d", seqno+1); - + for (i=0, p=parts; p->sp_string != NULL; i++, p=p->sp_next) { if (snstr != NULL && i == seqnoidx) @@ -3092,13 +3086,13 @@ static int find_last_seqno( strncat(filename, snstr, NAME_MAX - 1); /*< add sequence number */ } strncat(filename, p->sp_string, NAME_MAX - 1); - + if (p->sp_next == NULL) { break; } } - + if (check_file_and_path(filename, NULL, false)) { seqno++; @@ -3116,7 +3110,7 @@ static int find_last_seqno( bool thr_flushall_check() { bool rval = false; - simple_mutex_lock(&lm->lm_mutex,true); + simple_mutex_lock(&lm->lm_mutex,true); rval = flushall_flag; if(rval && !flushall_started_flag && !flushall_done_flag){ flushall_started_flag = true; @@ -3127,7 +3121,7 @@ bool thr_flushall_check() void flushall_logfiles(bool flush) { - simple_mutex_lock(&lm->lm_mutex,true); + simple_mutex_lock(&lm->lm_mutex,true); flushall_flag = flush; simple_mutex_unlock(&lm->lm_mutex); } diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h index ed708682a..b80b5d6db 100644 --- a/log_manager/log_manager.h +++ b/log_manager/log_manager.h @@ -50,13 +50,13 @@ typedef struct log_info_st size_t li_sesid; int li_enabled_logs; } log_info_t; - + #define LE LOGFILE_ERROR #define LM LOGFILE_MESSAGE #define LT LOGFILE_TRACE #define LD LOGFILE_DEBUG -/** +/** * Check if specified log type is enabled in general or if it is enabled * for the current session. */ @@ -75,11 +75,11 @@ typedef struct log_info_st { \ cmd; \ } - + /** * Execute the given command if specified log is enabled in general or * if the log is enabled for the current session. - */ + */ #define LOGIF(id,cmd) if (LOG_IS_ENABLED(id)) \ { \ cmd; \ @@ -99,7 +99,7 @@ typedef struct log_info_st * RUN Struct is valid for run-time checking. * DONE means that possible memory allocations have been released. */ -typedef enum { UNINIT = 0, INIT, RUN, DONE } flat_obj_state_t; +typedef enum { UNINIT = 0, INIT, RUN, DONE } flat_obj_state_t; EXTERN_C_BLOCK_BEGIN From 28a6ea90c0c488775337c7cf64f6b17345a77ee2 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 27 Aug 2015 19:57:54 +0300 Subject: [PATCH 41/74] Added more details to log output when MaxScale receives a fatal signal. --- CMakeLists.txt | 13 +++++++++++++ server/core/gateway.c | 12 ++++++++---- server/include/version.h.in | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f66ec4b61..06a3b116c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ find_package(MySQL) find_package(Pandoc) find_package(TCMalloc) find_package(Jemalloc) +find_package(Git) find_package(CURL) # You can find the variables set by this in the FindCURL.cmake file # which is a default module in CMake. @@ -56,6 +57,18 @@ else() endif() endif() +if(GIT_FOUND) + message(STATUS "Found git ${GIT_VERSION_STRING}") + execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1 HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT) + string(REPLACE "\n" "" MAXSCALE_COMMIT ${GIT_COMMIT}) + message(STATUS "Commit ID: ${MAXSCALE_COMMIT}") +else() + message(WARNING "Could not find git, MaxScale commit ID will not be resolved. Will use 'source-build' for commit ID.") + set(MAXSCALE_COMMIT "source-build") +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 2eceea4b2..eef1ae643 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -388,12 +388,16 @@ sigfatal_handler (int i) _exit(1); } fatal_handling = 1; - - fprintf(stderr, "\n\nMaxScale received fatal signal %d\n", i); + GATEWAY_CONF* cnf = config_get_global_options(); + fprintf(stderr, "\n\nMaxScale "MAXSCALE_VERSION" received fatal signal %d\n", i); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Fatal: MaxScale received fatal signal %d. Attempting backtrace.", i))); + "Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i))); + + skygw_log_write_flush(LE,"Commit ID: "MAXSCALE_COMMIT" System name: %s " + "Release string: %s Embedded library version: %s", + cnf->sysname,cnf->release_string,cnf->version_string); { void *addrs[128]; @@ -1148,7 +1152,7 @@ int main(int argc, char **argv) case 'v': rc = EXIT_SUCCESS; - printf("%s\n",MAXSCALE_VERSION); + printf("MaxScale %s\n",MAXSCALE_VERSION); goto return_main; case 'l': diff --git a/server/include/version.h.in b/server/include/version.h.in index 5aced69a5..c15fa862d 100644 --- a/server/include/version.h.in +++ b/server/include/version.h.in @@ -1 +1,2 @@ #define MAXSCALE_VERSION "@MAXSCALE_VERSION@" +#define MAXSCALE_COMMIT "@MAXSCALE_COMMIT@" From 9fe479e679f119984e0f3611a5fae9d4e650732e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 30 Aug 2015 11:16:11 +0300 Subject: [PATCH 42/74] Cleaned up code. --- server/core/gateway.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index eef1ae643..063cf122c 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -397,7 +397,7 @@ sigfatal_handler (int i) skygw_log_write_flush(LE,"Commit ID: "MAXSCALE_COMMIT" System name: %s " "Release string: %s Embedded library version: %s", - cnf->sysname,cnf->release_string,cnf->version_string); + cnf->sysname, cnf->release_string, cnf->version_string); { void *addrs[128]; @@ -1152,7 +1152,7 @@ int main(int argc, char **argv) case 'v': rc = EXIT_SUCCESS; - printf("MaxScale %s\n",MAXSCALE_VERSION); + printf("MaxScale %s\n", MAXSCALE_VERSION); goto return_main; case 'l': From 72097fc30ecbe47f5e70d19c9bb19a30c280313d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 25 Aug 2015 23:38:49 +0300 Subject: [PATCH 43/74] Added support for multiline options for routers and filters. --- server/core/config.c | 23 +++++++++++++++++++++++ server/inih/CMakeLists.txt | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index acf133a92..00ba5f93f 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -174,12 +174,35 @@ CONFIG_PARAMETER *param, *p1; { if (!strcmp(p1->name, name)) { + if(strcmp(name,"router_options") == 0 || strcmp(name,"options")) + { + char* tmp; + + if((tmp = malloc(sizeof(char) * (strlen(p1->value) + strlen(value) + 2))) == NULL) + { + skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); + return 0; + } + strcpy(tmp,p1->value); + strcat(tmp,value); + free(p1->value); + p1->value = tmp; + if(p1->qfd_param_type == STRING_TYPE && p1->qfd.valstr != NULL) + { + free(p1->qfd.valstr); + p1->qfd.valstr = strdup(p1->value); + } + return 1; + } + else + { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Configuration object '%s' has multiple " "parameters names '%s'.", ptr->object, name))); return 0; + } } p1 = p1->next; } diff --git a/server/inih/CMakeLists.txt b/server/inih/CMakeLists.txt index 95a17866e..11c37320a 100644 --- a/server/inih/CMakeLists.txt +++ b/server/inih/CMakeLists.txt @@ -1,2 +1,2 @@ -add_definitions(-DINI_MAX_LINE=1024) +add_definitions(-DINI_MAX_LINE=1024 -DINI_ALLOW_MULTILINE) add_library(inih ini.c) From de643df06780d49639809f922e29e11937798dc1 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 28 Aug 2015 09:38:45 +0300 Subject: [PATCH 44/74] All configuration parameters now support multi-line style. --- server/core/config.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 00ba5f93f..b8569ed2c 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -174,35 +174,18 @@ CONFIG_PARAMETER *param, *p1; { if (!strcmp(p1->name, name)) { - if(strcmp(name,"router_options") == 0 || strcmp(name,"options")) - { - char* tmp; + char* tmp; - if((tmp = malloc(sizeof(char) * (strlen(p1->value) + strlen(value) + 2))) == NULL) - { - skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); - return 0; - } - strcpy(tmp,p1->value); - strcat(tmp,value); - free(p1->value); - p1->value = tmp; - if(p1->qfd_param_type == STRING_TYPE && p1->qfd.valstr != NULL) - { - free(p1->qfd.valstr); - p1->qfd.valstr = strdup(p1->value); - } - return 1; - } - else + if((tmp = malloc(sizeof(char) * (strlen(p1->value) + strlen(value) + 2))) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Configuration object '%s' has multiple " - "parameters names '%s'.", - ptr->object, name))); - return 0; + skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); + return 0; } + strcpy(tmp,p1->value); + strcat(tmp,value); + free(p1->value); + p1->value = tmp; + return 1; } p1 = p1->next; } From 50d1675c7b55d2ceea268ff44285e68238e22399 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 28 Aug 2015 09:48:38 +0300 Subject: [PATCH 45/74] Moved to realloc instead of always allocating new memory. --- server/core/config.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index b8569ed2c..4e334e15c 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -175,15 +175,14 @@ CONFIG_PARAMETER *param, *p1; if (!strcmp(p1->name, name)) { char* tmp; + int paramlen = strlen(p1->value) + strlen(value) + 2; - if((tmp = malloc(sizeof(char) * (strlen(p1->value) + strlen(value) + 2))) == NULL) + if((tmp = realloc(p1->value,sizeof(char) * (paramlen))) == NULL) { skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); return 0; } - strcpy(tmp,p1->value); strcat(tmp,value); - free(p1->value); p1->value = tmp; return 1; } From e0f95de60635ca9ac9bf25e8762af09541a9a22a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 28 Aug 2015 10:12:52 +0300 Subject: [PATCH 46/74] Added commas to multi-line parameters without one. --- server/core/config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/core/config.c b/server/core/config.c index 4e334e15c..28e0ac03c 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -182,6 +182,11 @@ CONFIG_PARAMETER *param, *p1; skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); return 0; } + /** This is a parameter with no comma or separate definition of + * an already existing parameter. Add a comma to it and concatenate + * the values. */ + if(p1->value[strlen(p1->value)-1] != ',') + strcat(tmp,","); strcat(tmp,value); p1->value = tmp; return 1; From 9d9b7bccfcad45f9efc9f64eef802c35a49e97ca Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 31 Aug 2015 11:33:10 +0300 Subject: [PATCH 47/74] Changed to PCRE for processing configuration file parameters. --- .../Getting-Started/Configuration-Guide.md | 13 +++- server/core/config.c | 70 +++++++++++++++++-- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 8077791ff..8e88eb9ef 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -33,7 +33,18 @@ The MaxScale configuration is read from a file which can be located in a number 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. +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. Parameters which expect a comma-separated list of values can be defined on multiple lines. The following is an example of a multi-line definition. + +``` +[MyService] +type=service +router=readconnroute +servers=server1, + server2, + server3 +``` + +The values of the parameter that are not on the first line need to have at least one whitespace character before them in order for them to be recognized as a part of the multi-line parameter. Please see the section about [Protocol Modules](#protocol-modules) for more details about MaxScale and the default directories where modules will be searched for. diff --git a/server/core/config.c b/server/core/config.c index 28e0ac03c..9ea23da25 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -73,7 +73,10 @@ #include #include #include +#include +/** According to the PCRE manual, this should be a multiple of 3 */ +#define MAXSCALE_PCRE_BUFSZ 24 /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -125,6 +128,57 @@ char *ptr; return str; } +/** + * Remove extra commas and whitespace from a string. This string is interpreted + * as a list of string values separated by commas. + * @param strptr String to clean + * @return pointer to a new string or NULL if an error occurred + */ +char* config_clean_string_list(char* str) +{ + char *tmp; + + if((tmp = malloc(sizeof(char)*(strlen(str) + 1))) != NULL) + { + char *ptr; + int match[MAXSCALE_PCRE_BUFSZ]; + pcre* re; + const char *re_err; + int err_offset,rval; + + + tmp[0] = '\0'; + + if((re = pcre_compile("\\s*+([^,]*[^,\\s])",0,&re_err,&err_offset,NULL)) == NULL) + { + skygw_log_write(LE,"[%s] Error: Regular expression compilation failed at %d: %s", + __FUNCTION__,err_offset,re_err); + free(tmp); + return NULL; + } + + ptr = str; + + while((rval = pcre_exec(re,NULL,ptr,strlen(ptr),0,0,(int*)&match,MAXSCALE_PCRE_BUFSZ)) > 1) + { + const char* substr; + + pcre_get_substring(ptr,(int*)&match,rval,1,&substr); + if(strlen(tmp) > 0) + strcat(tmp,","); + strcat(tmp,substr); + pcre_free_substring(substr); + ptr = &ptr[match[1]]; + } + pcre_free(re); + } + else + { + skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); + } + + return tmp; +} /** * Config item handler for the ini file reader * @@ -174,7 +228,7 @@ CONFIG_PARAMETER *param, *p1; { if (!strcmp(p1->name, name)) { - char* tmp; + char *tmp; int paramlen = strlen(p1->value) + strlen(value) + 2; if((tmp = realloc(p1->value,sizeof(char) * (paramlen))) == NULL) @@ -182,13 +236,15 @@ CONFIG_PARAMETER *param, *p1; skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); return 0; } - /** This is a parameter with no comma or separate definition of - * an already existing parameter. Add a comma to it and concatenate - * the values. */ - if(p1->value[strlen(p1->value)-1] != ',') - strcat(tmp,","); + strcat(tmp,value); - p1->value = tmp; + if((p1->value = config_clean_string_list(tmp)) == NULL) + { + p1->value = tmp; + skygw_log_write(LE,"[%s] Error: Cleaning configuration parameter failed.",__FUNCTION__); + return 0; + } + free(tmp); return 1; } p1 = p1->next; From 386fa78a3020f18b8b58d2aac3def8fd8b255c71 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 31 Aug 2015 11:39:20 +0300 Subject: [PATCH 48/74] Fix to MXS-342: https://mariadb.atlassian.net/browse/MXS-342 Added more error messaging when the parsing of the configuration file fails. --- server/core/config.c | 19 +++++++++++++++++-- server/core/gateway.c | 22 +++++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 9ea23da25..00fdef292 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -271,7 +271,7 @@ int config_load(char *file) { CONFIG_CONTEXT config; -int rval; +int rval, ini_rval; MYSQL *conn; conn = mysql_init(NULL); @@ -314,8 +314,23 @@ int rval; config.object = ""; config.next = NULL; - if (ini_parse(file, handler, &config) < 0) + if (( ini_rval = ini_parse(file, handler, &config)) != 0) + { + char errorbuffer[1024 + 1]; + + if(ini_rval > 0) + snprintf(errorbuffer, 1024, + "Error: Failed to parse configuration file. Error on line %d.", ini_rval); + else if(ini_rval == -1) + snprintf(errorbuffer, 1024, + "Error: Failed to parse configuration file. Failed to open file."); + else + snprintf(errorbuffer, 1024, + "Error: Failed to parse configuration file. Memory allocation failed."); + + skygw_log_write(LE, errorbuffer); return 0; + } config_file = file; diff --git a/server/core/gateway.c b/server/core/gateway.c index 063cf122c..2aecc296d 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1055,6 +1055,7 @@ int main(int argc, char **argv) int l; int i; int n; + int ini_rval; intptr_t thread_id; int n_threads; /*< number of epoll listener threads */ int n_services; @@ -1591,8 +1592,27 @@ int main(int argc, char **argv) goto return_main; } - if(ini_parse(cnf_file_path,cnf_preparser,NULL) != 0) + if((ini_rval = ini_parse(cnf_file_path,cnf_preparser,NULL)) != 0) { + char errorbuffer[STRING_BUFFER_SIZE + 1]; + + if(ini_rval > 0) + snprintf(errorbuffer, STRING_BUFFER_SIZE, + "Error: Failed to pre-parse configuration file. Error on line %d.", ini_rval); + else if(ini_rval == -1) + snprintf(errorbuffer, STRING_BUFFER_SIZE, + "Error: Failed to pre-parse configuration file. Failed to open file."); + else + snprintf(errorbuffer, STRING_BUFFER_SIZE, + "Error: Failed to pre-parse configuration file. Memory allocation failed."); + + skygw_log_write(LE, errorbuffer); + if(!daemon_mode) + { + strncat(errorbuffer,"\n",STRING_BUFFER_SIZE); + fprintf(stderr, errorbuffer); + } + rc = MAXSCALE_BADCONFIG; goto return_main; } From e839dafdd0a847d6498bcca446f844058f25f27d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 31 Aug 2015 11:48:09 +0300 Subject: [PATCH 49/74] Cleaned up code. --- server/core/gateway.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 2aecc296d..85662e167 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1592,7 +1592,7 @@ int main(int argc, char **argv) goto return_main; } - if((ini_rval = ini_parse(cnf_file_path,cnf_preparser,NULL)) != 0) + if((ini_rval = ini_parse(cnf_file_path, cnf_preparser,NULL)) != 0) { char errorbuffer[STRING_BUFFER_SIZE + 1]; @@ -1609,7 +1609,7 @@ int main(int argc, char **argv) skygw_log_write(LE, errorbuffer); if(!daemon_mode) { - strncat(errorbuffer,"\n",STRING_BUFFER_SIZE); + strncat(errorbuffer, "\n", STRING_BUFFER_SIZE); fprintf(stderr, errorbuffer); } From 77a49e8cbd2a44ff9070ab998ee61d17153f11c8 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 1 Sep 2015 11:59:37 +0300 Subject: [PATCH 50/74] Removed magic numbers. --- server/core/config.c | 8 ++++---- server/core/gateway.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 00fdef292..f5d63ee7b 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -318,14 +318,14 @@ int rval, ini_rval; { char errorbuffer[1024 + 1]; - if(ini_rval > 0) - snprintf(errorbuffer, 1024, + if (ini_rval > 0) + snprintf(errorbuffer, sizeof(errorbuffer), "Error: Failed to parse configuration file. Error on line %d.", ini_rval); else if(ini_rval == -1) - snprintf(errorbuffer, 1024, + snprintf(errorbuffer, sizeof(errorbuffer), "Error: Failed to parse configuration file. Failed to open file."); else - snprintf(errorbuffer, 1024, + snprintf(errorbuffer, sizeof(errorbuffer), "Error: Failed to parse configuration file. Memory allocation failed."); skygw_log_write(LE, errorbuffer); diff --git a/server/core/gateway.c b/server/core/gateway.c index 85662e167..99a654939 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1594,16 +1594,16 @@ int main(int argc, char **argv) if((ini_rval = ini_parse(cnf_file_path, cnf_preparser,NULL)) != 0) { - char errorbuffer[STRING_BUFFER_SIZE + 1]; + char errorbuffer[STRING_BUFFER_SIZE]; if(ini_rval > 0) - snprintf(errorbuffer, STRING_BUFFER_SIZE, + snprintf(errorbuffer, sizeof(errorbuffer), "Error: Failed to pre-parse configuration file. Error on line %d.", ini_rval); else if(ini_rval == -1) - snprintf(errorbuffer, STRING_BUFFER_SIZE, + snprintf(errorbuffer, sizeof(errorbuffer), "Error: Failed to pre-parse configuration file. Failed to open file."); else - snprintf(errorbuffer, STRING_BUFFER_SIZE, + snprintf(errorbuffer, sizeof(errorbuffer), "Error: Failed to pre-parse configuration file. Memory allocation failed."); skygw_log_write(LE, errorbuffer); From b66dfaa8ecd83d3bfb34a6a721bbbc0fc5ff9f5a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 1 Sep 2015 21:22:47 +0300 Subject: [PATCH 51/74] Fixed possible error with multi-line parameters. --- server/core/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index f5d63ee7b..03e55fd22 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -236,7 +236,7 @@ CONFIG_PARAMETER *param, *p1; skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); return 0; } - + strcat(tmp,","); strcat(tmp,value); if((p1->value = config_clean_string_list(tmp)) == NULL) { From e55d345ab49609e820bbaea8cd236b0ccda2852f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 2 Sep 2015 08:57:00 +0300 Subject: [PATCH 52/74] Added --version-full which prints version and commit ID. --- .../Tutorials/Administration-Tutorial.md | 1 + Documentation/maxscale.1 | 3 +++ server/core/gateway.c | 17 +++++++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index 48e90952c..364ca7e41 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -44,6 +44,7 @@ Switch|Long Option|Description `-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 +`-V`|`--version-full`|print version info and the commit ID the binary was built from `-?`|`--help`|show this help diff --git a/Documentation/maxscale.1 b/Documentation/maxscale.1 index 104eaa235..d24b3b47e 100644 --- a/Documentation/maxscale.1 +++ b/Documentation/maxscale.1 @@ -56,6 +56,9 @@ Log messages to MaxScale's own log files. .BR "-v, --version" Print version information and exit. .TP +.BR "-V, --version-full" +Print full version information including the Git commit the binary was built from and exit. +.TP .BR "-?, --help" Show the help information for MaxScale and exit. diff --git a/server/core/gateway.c b/server/core/gateway.c index 99a654939..2d85b20e2 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -160,6 +160,8 @@ static bool libmysqld_started = FALSE; */ static bool daemon_mode = true; +static const char* maxscale_commit = MAXSCALE_COMMIT; + const char *progname = NULL; static struct option long_options[] = { {"homedir", required_argument, 0, 'c'}, @@ -178,6 +180,7 @@ static struct option long_options[] = { {"user",required_argument,0,'U'}, {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, '?'}, + {"version-full", no_argument, 0, 'V'}, {0, 0, 0, 0} }; static int cnf_preparser(void* data, const char* section, const char* name, const char* value); @@ -395,9 +398,9 @@ sigfatal_handler (int i) LOGFILE_ERROR, "Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i))); - skygw_log_write_flush(LE,"Commit ID: "MAXSCALE_COMMIT" System name: %s " + skygw_log_write_flush(LE,"Commit ID: %s System name: %s " "Release string: %s Embedded library version: %s", - cnf->sysname, cnf->release_string, cnf->version_string); + maxscale_commit, cnf->sysname, cnf->release_string, cnf->version_string); { void *addrs[128]; @@ -1015,6 +1018,7 @@ static void usage(void) " -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" + " -V, --version-full print full version info and exit\n" " -?, --help show this help\n" , progname); } @@ -1118,7 +1122,7 @@ int main(int argc, char **argv) } } - while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:D:C:B:U:A:P:", + while ((opt = getopt_long(argc, argv, "dc:f:l:vVs:S:?L:D:C:B:U:A:P:", long_options, &option_index)) != -1) { bool succp = true; @@ -1154,7 +1158,12 @@ int main(int argc, char **argv) case 'v': rc = EXIT_SUCCESS; printf("MaxScale %s\n", MAXSCALE_VERSION); - goto return_main; + goto return_main; + + case 'V': + rc = EXIT_SUCCESS; + printf("MaxScale %s - %s\n", MAXSCALE_VERSION, maxscale_commit); + goto return_main; case 'l': if (strncasecmp(optarg, "file", PATH_MAX) == 0) From 81506c61eba5a439dffba6439e08d8077fd7fc5d Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Thu, 3 Sep 2015 09:44:56 +0300 Subject: [PATCH 53/74] MXS-343: Minor refactoring of logging. So that the functionality earlier in skygw_log_write[flush] need not be duplicated. To be used by new logging functions. --- log_manager/log_manager.cc | 170 ++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 78 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 5f708c5b8..4b0989076 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1359,59 +1359,104 @@ return_succp: return succp; } +/** + * Helper for skygw_log_write and friends. + * + * @param id The id of the log file. + * @param flush Whether the log should be flushed. + * @param len The length of the formatted string, as calculated by vsnprintf. + * @param str The printf format string. + * @param valist The arguments of /str/. + * + * @return 0 if the logging to at least one log succeeded. + */ + +static int log_write(logfile_id_t id, + bool flush, + size_t len, + const char* str, + va_list valist) +{ + int rv = 0; + + if (logmanager_register(true)) + { + CHK_LOGMANAGER(lm); + + int attempts = 0; + int successes = 0; + + /** + * Add one for line feed. + */ + len += sizeof(char); + + for (int i = LOGFILE_FIRST; i <= LOGFILE_LAST; i <<= 1) + { + /** + * If a particular log is enabled in general and it is enabled for + * the current session, log the stuff. + */ + if (LOG_IS_ENABLED(i) && ((i & id) != 0)) + { + ++attempts; + + const bool use_valist = true; + const bool spread_down = true; + const bool rotate = false; + + if (logmanager_write_log((logfile_id_t)i, flush, use_valist, spread_down, rotate, + len, str, valist) == 0) + { + ++successes; + } + } + } + + logmanager_unregister(); + + // Only if logging was attempted and nothing succeeded, it is considered a failure. + if ((attempts != 0) && (successes == 0)) + { + rv = -1; + } + } + else + { + rv = -1; + } + + return rv; +} int skygw_log_write_flush( logfile_id_t id, const char* str, ...) { - int i,err = 0; + int err = 0; va_list valist; - size_t len; - - if (!logmanager_register(true)) - { - err = -1; - goto return_err; - } - CHK_LOGMANAGER(lm); /** * Find out the length of log string (to be formatted str). */ va_start(valist, str); - len = sizeof(char) * vsnprintf(NULL, 0, str, valist); + size_t len = vsnprintf(NULL, 0, str, valist); va_end(valist); - /** - * Add one for line feed. - */ - len += sizeof(char); - /** - * Write log string to buffer and add to file write list. - */ - for (i = LOGFILE_FIRST; i<=LOGFILE_LAST ;i <<=1) - { - /** - * If particular log is disabled in general and it is not enabled for - * the current session, check the next log. - */ - if (!LOG_IS_ENABLED(i) || (i & id) == 0) - { - continue; - } + + if (len >= 0) { + const bool flush = true; va_start(valist, str); - err = logmanager_write_log((logfile_id_t)i, true, true, true, false, len, str, valist); + err = log_write(id, flush, len, str, valist); va_end(valist); - if (err != 0) { + if (err != 0) + { fprintf(stderr, "skygw_log_write_flush failed.\n"); - break; } } - logmanager_unregister(); -return_err: return err; } @@ -1422,60 +1467,29 @@ int skygw_log_write( const char* str, ...) { - int i,err = 0; - va_list valist; - size_t len; + int err = 0; + va_list valist; - if (!logmanager_register(true)) - { - err = -1; - goto return_err; - } - CHK_LOGMANAGER(lm); + /** + * Find out the length of log string (to be formatted str). + */ + va_start(valist, str); + size_t len = vsnprintf(NULL, 0, str, valist); + va_end(valist); - /** - * If particular log is disabled in general and it is not enabled for - * the current session, then unregister and return. - */ + if (len >= 0) { + const bool flush = false; - /** - * Find out the length of log string (to be formatted str). - */ va_start(valist, str); - len = vsnprintf(NULL, 0, str, valist); + err = log_write(id, flush, len, str, valist); va_end(valist); - /** - * Add one for line feed. - */ - len += 1; - /** - * Write log string to buffer and add to file write list. - */ - for (i = LOGFILE_FIRST; i<=LOGFILE_LAST; i <<=1) - { - /** - * If particular log is disabled in general and it is not enabled for - * the current session, check the next log. - */ - if (!LOG_IS_ENABLED(i) || (i & id) == 0) - { - continue; - } - - va_start(valist, str); - err = logmanager_write_log((logfile_id_t)i, false, true, true, false, len, str, valist); - va_end(valist); - - if (err != 0) { - fprintf(stderr, "skygw_log_write failed.\n"); - break; - } + if (err != 0) { + fprintf(stderr, "skygw_log_write failed.\n"); } + } - logmanager_unregister(); -return_err: - return err; + return err; } From 30cdda48c6e455d15bf654eefcaaf95d73845825 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 3 Sep 2015 19:28:59 +0300 Subject: [PATCH 54/74] The datadir path is now used as the location where the process specific data directories are created. --- server/core/gateway.c | 4 ++-- server/include/gwdirs.h.in | 2 +- server/test/maxscale_test.cnf | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 2d85b20e2..dc852e575 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1702,7 +1702,7 @@ int main(int argc, char **argv) * machine. */ - snprintf(datadir,PATH_MAX,"%s/data",get_datadir()); + snprintf(datadir,PATH_MAX, "%s", get_datadir()); datadir[PATH_MAX] = '\0'; if(mkdir(datadir, 0777) != 0){ @@ -1713,7 +1713,7 @@ int main(int argc, char **argv) } } - snprintf(datadir,PATH_MAX, "%s/data/data%d", get_datadir(), getpid()); + snprintf(datadir,PATH_MAX, "%s/data%d", get_datadir(), getpid()); if(mkdir(datadir, 0777) != 0){ diff --git a/server/include/gwdirs.h.in b/server/include/gwdirs.h.in index cc46b12fd..4d2e8266d 100644 --- a/server/include/gwdirs.h.in +++ b/server/include/gwdirs.h.in @@ -30,7 +30,7 @@ static const char* default_piddir = "@MAXSCALE_VARDIR@/run/maxscale"; /*< This s * the /var/run folder is an old standard and the newer 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_datadir = "@MAXSCALE_VARDIR@/lib/maxscale/data"; static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@"; static const char* default_cachedir = "@MAXSCALE_VARDIR@/cache/maxscale"; static const char* default_langdir = "@MAXSCALE_VARDIR@/lib/maxscale"; diff --git a/server/test/maxscale_test.cnf b/server/test/maxscale_test.cnf index fdc843d8d..54f0462ce 100644 --- a/server/test/maxscale_test.cnf +++ b/server/test/maxscale_test.cnf @@ -2,7 +2,7 @@ threads=4 libdir=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@ logdir=@CMAKE_INSTALL_PREFIX@/ -datadir=@CMAKE_INSTALL_PREFIX@/ +datadir=@CMAKE_INSTALL_PREFIX@/data/ cachedir=@CMAKE_INSTALL_PREFIX@/ language=@CMAKE_INSTALL_PREFIX@/lib/maxscale/ piddir=@CMAKE_INSTALL_PREFIX@/ From b44b59c9cd2b7e1793790b8b417eb5266162445a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 3 Sep 2015 19:38:56 +0300 Subject: [PATCH 55/74] Fixed Git use in CMake. --- CMakeLists.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06a3b116c..8a1f7e5e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,9 +61,17 @@ if(GIT_FOUND) message(STATUS "Found git ${GIT_VERSION_STRING}") execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1 HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE GIT_COMMIT) - string(REPLACE "\n" "" MAXSCALE_COMMIT ${GIT_COMMIT}) - message(STATUS "Commit ID: ${MAXSCALE_COMMIT}") + OUTPUT_VARIABLE GIT_COMMIT + ERROR_VARIABLE GIT_ERROR + RESULT_VARIABLE GIT_RVAL) + if(${GIT_RVAL} EQUAL 0) + string(REPLACE "\n" "" MAXSCALE_COMMIT ${GIT_COMMIT}) + message(STATUS "Commit ID: ${MAXSCALE_COMMIT}") + else() + message(STATUS "Git exited with non-zero value: ${GIT_ERROR}") + message(STATUS "Could not find repository in source folder, MaxScale commit ID will not be resolved. Will use 'source-build' for commit ID.") + set(MAXSCALE_COMMIT "source-build") + endif() else() message(WARNING "Could not find git, MaxScale commit ID will not be resolved. Will use 'source-build' for commit ID.") set(MAXSCALE_COMMIT "source-build") From 9a3510ab5699505e1817085c85ccaada6725f2ea Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 4 Sep 2015 19:30:14 +0300 Subject: [PATCH 56/74] Added maxinfo and binlogrouter to the list of "internal routers". --- server/core/config.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 03e55fd22..58f2f242d 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -2299,9 +2299,11 @@ config_percentage_value(char *str) } static char *InternalRouters[] = { - "debugcli", - "cli", - NULL + "debugcli", + "cli", + "maxinfo", + "binlogrouter", + NULL }; /** From a9fd7926ac598bab004b17f19db9cfba43fe7354 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 4 Sep 2015 19:34:18 +0300 Subject: [PATCH 57/74] Removed unnecessary strcmp. --- server/core/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index 58f2f242d..b5084252c 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -979,7 +979,7 @@ process_config_context(CONFIG_CONTEXT *context) s = strtok_r(NULL, ",", &lasts); } } - else if (servers == NULL && !isInternalService(router) && strcmp(router,"binlogrouter")) + else if (servers == NULL && !isInternalService(router)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, From 160bbb70ee3421fbe09195628a9b90414a0bf895 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 5 Sep 2015 15:32:32 +0300 Subject: [PATCH 58/74] MXS-251: strerror Replaces all calls to strerror with calls to strerror_r. The former is non-thread safe while the latter is. --- client/maxadmin.c | 13 ++- log_manager/log_manager.cc | 12 ++- log_manager/log_manager.h | 7 ++ query_classifier/test/testmain.c | 3 +- server/core/buffer.c | 16 ++-- server/core/dbusers.c | 12 +-- server/core/dcb.c | 44 ++++++---- server/core/externcmd.c | 3 +- server/core/filter.c | 3 +- server/core/gateway.c | 69 +++++++++------ server/core/gw_utils.c | 6 +- server/core/poll.c | 17 ++-- server/core/secrets.c | 36 +++++--- server/core/service.c | 6 +- server/core/session.c | 3 +- server/core/test/testpoll.c | 3 +- server/core/utils.c | 6 +- server/modules/filter/qlafilter.c | 9 +- server/modules/filter/test/harness_common.c | 3 +- server/modules/protocol/httpd.c | 8 +- server/modules/protocol/maxscaled.c | 3 +- server/modules/protocol/mysql_backend.c | 15 ++-- server/modules/protocol/mysql_client.c | 40 ++++++--- server/modules/protocol/mysql_common.c | 24 +++-- server/modules/protocol/telnetd.c | 6 +- server/modules/routing/binlog/blr.c | 6 +- server/modules/routing/debugcmd.c | 4 +- .../routing/schemarouter/schemarouter.c | 6 +- .../routing/schemarouter/shardrouter.c | 4 +- utils/skygw_utils.cc | 87 ++++++++++++------- utils/skygw_utils.h | 7 ++ 31 files changed, 314 insertions(+), 167 deletions(-) diff --git a/client/maxadmin.c b/client/maxadmin.c index 6262bd34b..dec941d2f 100644 --- a/client/maxadmin.c +++ b/client/maxadmin.c @@ -55,6 +55,13 @@ #include #endif +/* + * We need a common.h file that is included by every component. + */ +#if !defined(STRERROR_BUFLEN) +#define STRERROR_BUFLEN 512 +#endif + static int connectMaxScale(char *hostname, char *port); static int setipaddress(struct in_addr *a, char *p); static int authMaxScale(int so, char *user, char *password); @@ -329,8 +336,9 @@ int keepalive = 1; if ((so = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Unable to create socket: %s\n", - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); return -1; } memset(&addr, 0, sizeof addr); @@ -339,8 +347,9 @@ int keepalive = 1; addr.sin_port = htons(atoi(port)); if (connect(so, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Unable to connect to MaxScale at %s, %s: %s\n", - hostname, port, strerror(errno)); + hostname, port, strerror_r(errno, errbuf, sizeof(errbuf))); close(so); return -1; } diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 4b0989076..db2a1e566 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -2217,12 +2217,13 @@ static bool logfile_open_file( if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Error : writing to file %s failed due to %d, %s. " "Exiting MaxScale.\n", lf->lf_full_file_name, err, - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); succp = false; goto return_succp; } @@ -2433,20 +2434,22 @@ static bool check_file_and_path( if (do_log && file_is_symlink(filename)) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "*\n* Error : Can't access " "file pointed to by %s due " "to %s.\n", filename, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } else if (do_log) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "*\n* Error : Can't access %s due " "to %s.\n", filename, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } if(writable) @@ -2954,6 +2957,7 @@ static void* thr_filewriter_fun( do_flushall)); if (err) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Error : Write to %s log " ": %s failed due to %d, " @@ -2961,7 +2965,7 @@ static void* thr_filewriter_fun( STRLOGNAME((logfile_id_t)i), lf->lf_full_file_name, err, - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); /** Force log off */ skygw_log_disable_raw((logfile_id_t)i, true); } diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h index b80b5d6db..45410aedc 100644 --- a/log_manager/log_manager.h +++ b/log_manager/log_manager.h @@ -18,6 +18,13 @@ #if !defined(LOG_MANAGER_H) # define LOG_MANAGER_H +/* + * We need a common.h file that is included by every component. + */ +#if !defined(STRERROR_BUFLEN) +#define STRERROR_BUFLEN 512 +#endif + typedef struct filewriter_st filewriter_t; typedef struct logfile_st logfile_t; typedef struct fnames_conf_st fnames_conf_t; diff --git a/query_classifier/test/testmain.c b/query_classifier/test/testmain.c index 08a25d4fa..5e3afddf6 100644 --- a/query_classifier/test/testmain.c +++ b/query_classifier/test/testmain.c @@ -358,10 +358,11 @@ int main(int argc, char** argv) "set.\n"); ss_dassert(workingdir != NULL); } else if (access(workingdir, R_OK) != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Failed to access the working directory due %d, %s\n", errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); ss_dassert(false); } else { char** so = server_options; diff --git a/server/core/buffer.c b/server/core/buffer.c index c3adacd34..8de0f6851 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -113,10 +113,11 @@ SHARED_BUF *sbuf; retblock: if (rval == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); } return rval; } @@ -182,10 +183,11 @@ GWBUF *rval; if ((rval = (GWBUF *)calloc(1,sizeof(GWBUF))) == NULL) { ss_dassert(rval != NULL); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return NULL; } @@ -246,10 +248,11 @@ GWBUF *gwbuf_clone_portion( if ((clonebuf = (GWBUF *)malloc(sizeof(GWBUF))) == NULL) { ss_dassert(clonebuf != NULL); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return NULL; } atomic_add(&buf->sbuf->refcount, 1); @@ -500,10 +503,11 @@ void gwbuf_add_buffer_object( if (newb == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return; } newb->bo_id = id; @@ -590,11 +594,11 @@ BUF_PROPERTY *prop; if ((prop = malloc(sizeof(BUF_PROPERTY))) == NULL) { ss_dassert(prop != NULL); - + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return 0; } prop->name = strdup(name); diff --git a/server/core/dbusers.c b/server/core/dbusers.c index d28aecaeb..260c8be6c 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -907,12 +907,13 @@ getAllUsers(SERVICE *service, USERS *users) users_data = (char *)calloc(nusers, (users_data_row_len * sizeof(char)) + 1); if (users_data == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error : Memory allocation for user data failed due to " + "Error : Memory allocation for user data failed due to " "%d, %s.", - errno, - strerror(errno)))); + errno, + strerror_r(errno, errbuf, sizeof(errbuf))))); mysql_free_result(result); mysql_close(con); @@ -1416,12 +1417,13 @@ getUsers(SERVICE *service, USERS *users) users_data = (char *)calloc(nusers, (users_data_row_len * sizeof(char)) + 1); if (users_data == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation for user data failed due to " "%d, %s.", errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); mysql_free_result(result); mysql_close(con); @@ -2471,4 +2473,4 @@ bool check_service_permissions(SERVICE* service) mysql_close(mysql); free(dpasswd); return rval; -} \ No newline at end of file +} diff --git a/server/core/dcb.c b/server/core/dcb.c index 14fbdd3ff..56ae46128 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -545,6 +545,7 @@ dcb_process_victim_queue(DCB *listofdcb) { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "%lu [dcb_process_victim_queue] Error : Failed to close " @@ -553,7 +554,7 @@ dcb_process_victim_queue(DCB *listofdcb) dcb->fd, dcb, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } else { @@ -789,6 +790,7 @@ int dcb_read( if (-1 == ioctl(dcb->fd, FIONREAD, &bytesavailable)) { + char errbuf[STRERROR_BUFLEN]; /* */ LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -799,7 +801,7 @@ int dcb_read( STRDCBSTATE(dcb->state), dcb->fd, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); /* */ return -1; } @@ -839,6 +841,7 @@ int dcb_read( * This is a fatal error which should cause shutdown. * Todo shutdown if memory allocation fails. */ + char errbuf[STRERROR_BUFLEN]; /* */ LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -848,7 +851,7 @@ int dcb_read( dcb, dcb->fd, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); /* */ return -1; } @@ -859,6 +862,7 @@ int dcb_read( { if (errno != 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + char errbuf[STRERROR_BUFLEN]; /* */ LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -869,7 +873,7 @@ int dcb_read( STRDCBSTATE(dcb->state), dcb->fd, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); /* */ } gwbuf_free(buffer); @@ -935,6 +939,7 @@ int dcb_read_SSL( pending = SSL_pending(dcb->ssl); if (rc == -1) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : ioctl FIONREAD for dcb %p in " @@ -943,7 +948,7 @@ int dcb_read_SSL( STRDCBSTATE(dcb->state), dcb->fd, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); n = -1; goto return_n; } @@ -996,6 +1001,7 @@ int dcb_read_SSL( * This is a fatal error which should cause shutdown. * Todo shutdown if memory allocation fails. */ + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to allocate read buffer " @@ -1003,7 +1009,7 @@ int dcb_read_SSL( dcb, dcb->fd, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); n = -1; goto return_n; @@ -1038,6 +1044,7 @@ int dcb_read_SSL( } else { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Read failed, dcb %p in state " @@ -1046,7 +1053,7 @@ int dcb_read_SSL( STRDCBSTATE(dcb->state), dcb->fd, ssl_errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); if(ssl_errno == SSL_ERROR_SSL || ssl_errno == SSL_ERROR_SYSCALL) @@ -1321,6 +1328,7 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) { if (eno == EPIPE) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [dcb_write] Write to dcb " @@ -1331,7 +1339,7 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) STRDCBSTATE(dcb->state), dcb->fd, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } } @@ -1341,6 +1349,7 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) eno != EAGAIN && eno != EWOULDBLOCK) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Write to dcb %p in " @@ -1350,7 +1359,7 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) STRDCBSTATE(dcb->state), dcb->fd, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } @@ -1376,13 +1385,14 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) } if (dolog) { + char errbuf[STRERROR_BUFLEN]; 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", eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } } } @@ -1497,7 +1507,7 @@ static int dcb_write_SSL_error_report (DCB *dcb, int ret) { int ssl_errno; - char errbuf[256]; + char errbuf[STRERROR_BUFLEN]; ssl_errno = SSL_get_error(dcb->ssl,ret); if (LOG_IS_ENABLED(LOGFILE_DEBUG)) @@ -1557,9 +1567,7 @@ dcb_write_SSL_error_report (DCB *dcb, int ret) { if(ssl_errno == SSL_ERROR_SYSCALL) { - strerror_r(errno,errbuf,255); - errbuf[255] = '\0'; - skygw_log_write(LE,"%d:%s",errno,errbuf); + skygw_log_write(LE,"%d:%s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); } do { @@ -1630,6 +1638,7 @@ int above_water; { break; } + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Write to dcb %p " @@ -1638,7 +1647,7 @@ int above_water; STRDCBSTATE(dcb->state), dcb->fd, saved_errno, - strerror(saved_errno)))); + strerror_r(saved_errno, errbuf, sizeof(errbuf))))); break; } /* @@ -1734,7 +1743,10 @@ dcb_drain_writeq_SSL(DCB *dcb) skygw_log_write(LE,"%s",errbuf); } if(errno != 0) - skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + { + char errbuf[STRERROR_BUFLEN]; + skygw_log_write(LE,"%d:%s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); + } break; case SSL_ERROR_ZERO_RETURN: skygw_log_write(LE,"Socket is closed."); diff --git a/server/core/externcmd.c b/server/core/externcmd.c index 3099a767a..1163fab2f 100644 --- a/server/core/externcmd.c +++ b/server/core/externcmd.c @@ -147,8 +147,9 @@ int externcmd_execute(EXTERNCMD* cmd) if(pid < 0) { + char errbuf[STRERROR_BUFLEN]; skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s', fork failed: [%d] %s", - cmd->parameters[0],errno,strerror(errno)); + cmd->parameters[0],errno,strerror_r(errno, errbuf, sizeof(errbuf))); rval = -1; } else if(pid == 0) diff --git a/server/core/filter.c b/server/core/filter.c index 2cc18a42c..b8b1d3902 100644 --- a/server/core/filter.c +++ b/server/core/filter.c @@ -349,12 +349,13 @@ DOWNSTREAM *me; } if ((me = (DOWNSTREAM *)calloc(1, sizeof(DOWNSTREAM))) == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation for filter session failed " "due to %d,%s.", errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return NULL; } diff --git a/server/core/gateway.c b/server/core/gateway.c index dc852e575..dd13896e1 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -351,10 +351,9 @@ sigchld_handler (int i) if((child = wait(&exit_status)) == -1) { - char errbuf[512]; - strerror_r(errno,errbuf,511); - errbuf[511] = '\0'; - skygw_log_write_flush(LE,"Error: failed to wait child process: %d %s",errno,errbuf); + char errbuf[STRERROR_BUFLEN]; + skygw_log_write_flush(LE,"Error: failed to wait child process: %d %s", + errno,strerror_r(errno, errbuf, sizeof(errbuf))); } else { @@ -456,12 +455,13 @@ static int signal_set (int sig, void (*handler)(int)) { { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed call sigaction() in %s due to %d, %s.", program_invocation_short_name, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); rc = 1; } return rc; @@ -484,14 +484,14 @@ int ntfw_cb( { int eno = errno; errno = 0; - + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "Error : Failed to remove the data directory %s of " "MaxScale due to %d, %s.", datadir, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } return rc; } @@ -785,21 +785,23 @@ static void print_log_n_stderr( char* fpr_end = "\n*\n"; if (do_log) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "%s %s %s %s", log_err, logstr, eno == 0 ? " " : "Error :", - eno == 0 ? " " : strerror(eno)))); + eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf))))); } if (do_stderr) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "%s %s %s %s %s", fpr_err, fprstr, eno == 0 ? " " : "Error :", - eno == 0 ? " " : strerror(eno), + eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf)), fpr_end); } } @@ -813,6 +815,7 @@ static bool file_is_readable( { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; if (!daemon_mode) { @@ -820,7 +823,7 @@ static bool file_is_readable( "*\n* Warning : Failed to read the configuration " "file %s. %s.\n*\n", absolute_pathname, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); } LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -828,7 +831,7 @@ static bool file_is_readable( "to %d, %s.", absolute_pathname, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); LOGIF(LE,(skygw_log_sync_all())); succp = false; } @@ -844,6 +847,7 @@ static bool file_is_writable( { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; if (!daemon_mode) { @@ -852,7 +856,7 @@ static bool file_is_writable( "due %d, %s.\n*\n", absolute_pathname, eno, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); } LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -860,7 +864,7 @@ static bool file_is_writable( "to %d, %s.", absolute_pathname, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); succp = false; } return succp; @@ -907,13 +911,14 @@ static char* get_expanded_pathname( { int eno = errno; errno = 0; - + char errbuf[STRERROR_BUFLEN]; + fprintf(stderr, "*\n* Warning : Failed to read the " "directory %s. %s.\n*\n", relative_path, - strerror(eno)); - + strerror_r(eno, errbuf, sizeof(errbuf))); + LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Warning : Failed to read the " @@ -921,7 +926,7 @@ static char* get_expanded_pathname( "to %d, %s.", relative_path, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); free(expanded_path); *output_path = NULL; goto return_cnf_file_buf; @@ -942,11 +947,12 @@ static char* get_expanded_pathname( if (cnf_file_buf == NULL) { ss_dassert(cnf_file_buf != NULL); - + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror(errno)))); + "Error : Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))))); free(expanded_path); expanded_path = NULL; @@ -1707,8 +1713,10 @@ int main(int argc, char **argv) if(mkdir(datadir, 0777) != 0){ if(errno != EEXIST){ + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, - "Error: Cannot create data directory '%s': %d %s\n",datadir,errno,strerror(errno)); + "Error: Cannot create data directory '%s': %d %s\n", + datadir, errno, strerror_r(errno, errbuf, sizeof(errbuf))); goto return_main; } } @@ -1718,8 +1726,10 @@ int main(int argc, char **argv) if(mkdir(datadir, 0777) != 0){ if(errno != EEXIST){ + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, - "Error: Cannot create data directory '%s': %d %s\n",datadir,errno,strerror(errno)); + "Error: Cannot create data directory '%s': %d %s\n", + datadir, errno, strerror_r(errno, errbuf, sizeof(errbuf))); goto return_main; } } @@ -2059,11 +2069,12 @@ static void unlink_pidfile(void) if (strlen(pidfile)) { if (unlink(pidfile)) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "MaxScale failed to remove pidfile %s: error %d, %s\n", pidfile, errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } } } @@ -2426,32 +2437,36 @@ static int set_user(char* user) pwname = getpwnam(user); if(pwname == NULL) { + char errbuf[STRERROR_BUFLEN]; printf("Error: Failed to retrieve user information for '%s': %d %s\n", - user,errno,errno == 0 ? "User not found" : strerror(errno)); + user,errno,errno == 0 ? "User not found" : strerror_r(errno, errbuf, sizeof(errbuf))); return -1; } rval = setgid(pwname->pw_gid); if(rval != 0) { + char errbuf[STRERROR_BUFLEN]; printf("Error: Failed to change group to '%d': %d %s\n", - pwname->pw_gid,errno,strerror(errno)); + pwname->pw_gid, errno, strerror_r(errno, errbuf, sizeof(errbuf))); return rval; } rval = setuid(pwname->pw_uid); if(rval != 0) { + char errbuf[STRERROR_BUFLEN]; printf("Error: Failed to change user to '%s': %d %s\n", - pwname->pw_name,errno,strerror(errno)); + pwname->pw_name, errno, strerror_r(errno, errbuf, sizeof(errbuf))); return rval; } if(prctl(PR_GET_DUMPABLE) == 0) { if(prctl(PR_SET_DUMPABLE ,1) == -1) { + char errbuf[STRERROR_BUFLEN]; printf("Error: Failed to set dumpable flag on for the process '%s': %d %s\n", - pwname->pw_name,errno,strerror(errno)); + pwname->pw_name, errno, strerror_r(errno, errbuf, sizeof(errbuf))); return -1; } } diff --git a/server/core/gw_utils.c b/server/core/gw_utils.c index 5d4fb5ed2..ebf374f6c 100644 --- a/server/core/gw_utils.c +++ b/server/core/gw_utils.c @@ -147,7 +147,8 @@ void gw_daemonize(void) { pid = fork(); if (pid < 0) { - fprintf(stderr, "fork() error %s\n", strerror(errno)); + char errbuf[STRERROR_BUFLEN]; + fprintf(stderr, "fork() error %s\n", strerror_r(errno, errbuf, sizeof(errbuf))); exit(1); } @@ -157,7 +158,8 @@ void gw_daemonize(void) { } if (setsid() < 0) { - fprintf(stderr, "setsid() error %s\n", strerror(errno)); + char errbuf[STRERROR_BUFLEN]; + fprintf(stderr, "setsid() error %s\n", strerror_r(errno, errbuf, sizeof(errbuf))); exit(1); } } diff --git a/server/core/poll.c b/server/core/poll.c index 5e913f5f9..906682d22 100644 --- a/server/core/poll.c +++ b/server/core/poll.c @@ -909,6 +909,7 @@ unsigned long qtime; &tls_log_info.li_enabled_logs))); dcb->func.write_ready(dcb); } else { + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_waitevents] " @@ -916,7 +917,7 @@ unsigned long qtime; "dcb %p, fd %i", pthread_self(), eno, - strerror(eno), + strerror_r(eno, errbuf, sizeof(errbuf)), dcb, dcb->fd))); } @@ -963,6 +964,7 @@ unsigned long qtime; #if defined(FAKE_CODE) if (eno == 0) { eno = dcb_fake_write_errno[dcb->fd]; + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_waitevents] " @@ -970,18 +972,19 @@ unsigned long qtime; "%s", pthread_self(), eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } dcb_fake_write_errno[dcb->fd] = 0; #endif /* FAKE_CODE */ if (eno != 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_waitevents] " "EPOLLERR due %d, %s.", pthread_self(), eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } atomic_add(&pollStats.n_error, 1); /** Read session id to thread's local storage */ @@ -996,7 +999,7 @@ unsigned long qtime; { int eno = 0; eno = gw_getsockerrno(dcb->fd); - + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_waitevents] " @@ -1006,7 +1009,7 @@ unsigned long qtime; dcb, dcb->fd, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); atomic_add(&pollStats.n_hup, 1); spinlock_acquire(&dcb->dcb_initlock); if ((dcb->flags & DCBF_HUNG) == 0) @@ -1029,7 +1032,7 @@ unsigned long qtime; { int eno = 0; eno = gw_getsockerrno(dcb->fd); - + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [poll_waitevents] " @@ -1039,7 +1042,7 @@ unsigned long qtime; dcb, dcb->fd, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); atomic_add(&pollStats.n_hup, 1); spinlock_acquire(&dcb->dcb_initlock); if ((dcb->flags & DCBF_HUNG) == 0) diff --git a/server/core/secrets.c b/server/core/secrets.c index d26833e5c..fd4f42ac5 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -82,24 +82,26 @@ static int reported = 0; { if (!reported) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Encrypted password file %s can't be accessed " "(%s). Password encryption is not used.", secret_file, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); reported = 1; } } else { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : access for secrets file " "[%s] failed. Error %d, %s.", secret_file, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } return NULL; } @@ -109,13 +111,14 @@ static int reported = 0; { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed opening secret " "file [%s]. Error %d, %s.", secret_file, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); return NULL; } @@ -125,13 +128,14 @@ static int reported = 0; int eno = errno; errno = 0; close(fd); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : fstat for secret file %s " "failed. Error %d, %s.", secret_file, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); return NULL; } @@ -140,13 +144,14 @@ static int reported = 0; int eno = errno; errno = 0; close(fd); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Secrets file %s has " "incorrect size. Error %d, %s.", secret_file, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); return NULL; } if (secret_stats.st_mode != (S_IRUSR|S_IFREG)) @@ -182,6 +187,7 @@ static int reported = 0; errno = 0; close(fd); free(keys); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Read from secrets file " @@ -190,7 +196,7 @@ static int reported = 0; len, sizeof(MAXKEYS), eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); return NULL; } @@ -199,13 +205,14 @@ static int reported = 0; int eno = errno; errno = 0; free(keys); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed closing the " "secrets file %s. Error %d, %s.", secret_file, eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); return NULL; } ss_dassert(keys != NULL); @@ -240,24 +247,26 @@ if(strlen(path) > PATH_MAX) /* Open for writing | Create | Truncate the file for writing */ if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : failed opening secret " "file [%s]. Error %d, %s.", secret_file, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return 1; } /* Open for writing | Create | Truncate the file for writing */ if ((randfd = open("/dev/random", O_RDONLY)) < 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : failed opening /dev/random. Error %d, %s.", errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); close(fd); return 1; } @@ -280,13 +289,14 @@ if(strlen(path) > PATH_MAX) /* Write data */ if (write(fd, &key, sizeof(key)) < 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : failed writing into " "secret file [%s]. Error %d, %s.", secret_file, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); close(fd); return 1; } @@ -294,24 +304,26 @@ if(strlen(path) > PATH_MAX) /* close file */ if (close(fd) < 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : failed closing the " "secret file [%s]. Error %d, %s.", secret_file, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); } if( chmod(secret_file, S_IRUSR) < 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : failed to change the permissions of the" "secret file [%s]. Error %d, %s.", secret_file, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); } return 0; diff --git a/server/core/service.c b/server/core/service.c index bc534f6b7..51df84c03 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -282,10 +282,11 @@ GWPROTOCOL *funcs; { if(errno != EEXIST) { + char errbuf[STRERROR_BUFLEN]; skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", path, errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } mkdir_rval = 0; } @@ -300,10 +301,11 @@ GWPROTOCOL *funcs; { if(errno != EEXIST) { + char errbuf[STRERROR_BUFLEN]; skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", path, errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } mkdir_rval = 0; } diff --git a/server/core/session.c b/server/core/session.c index 06737b2fb..32c7148e5 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -81,12 +81,13 @@ session_alloc(SERVICE *service, DCB *client_dcb) if (session == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to allocate memory for " "session object due error %d, %s.", errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); if (client_dcb->data && !DCB_IS_CLONE(client_dcb)) { void *clientdata = client_dcb->data; diff --git a/server/core/test/testpoll.c b/server/core/test/testpoll.c index 7b9175b2c..b67ad208f 100644 --- a/server/core/test/testpoll.c +++ b/server/core/test/testpoll.c @@ -61,7 +61,8 @@ int result; dcb->fd = socket(AF_UNIX, SOCK_STREAM, 0); if(dcb->fd < 0){ - ss_dfprintf(stderr, "\nError on function call: socket() returned %d: %s\n",errno,strerror(errno)); + char errbuf[STRERROR_BUFLEN]; + ss_dfprintf(stderr, "\nError on function call: socket() returned %d: %s\n",errno,strerror_r(errno,errbuf,sizeof(errbuf))); return 1; } diff --git a/server/core/utils.c b/server/core/utils.c index a26c2b4e5..8b8f4d55f 100644 --- a/server/core/utils.c +++ b/server/core/utils.c @@ -68,22 +68,24 @@ int setnonblocking(int fd) { int fl; if ((fl = fcntl(fd, F_GETFL, 0)) == -1) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Can't GET fcntl for %i, errno = %d, %s.", fd, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return 1; } if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Can't SET fcntl for %i, errno = %d, %s", fd, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); return 1; } return 0; diff --git a/server/modules/filter/qlafilter.c b/server/modules/filter/qlafilter.c index 3c358cda1..e03dabf98 100644 --- a/server/modules/filter/qlafilter.c +++ b/server/modules/filter/qlafilter.c @@ -277,12 +277,13 @@ char *remote, *userName; (char *)malloc(strlen(my_instance->filebase) + 20)) == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "Error : Memory allocation for qla filter " "file name failed due to %d, %s.", errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); free(my_session); return NULL; } @@ -315,12 +316,13 @@ char *remote, *userName; if (my_session->fp == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "Error : Opening output file for qla " "fileter failed due to %d, %s", errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); free(my_session->filename); free(my_session); my_session = NULL; @@ -329,12 +331,13 @@ char *remote, *userName; } else { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "Error : Memory allocation for qla filter failed due to " "%d, %s.", errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); } return my_session; } diff --git a/server/modules/filter/test/harness_common.c b/server/modules/filter/test/harness_common.c index 2cfe49b42..e0326d040 100644 --- a/server/modules/filter/test/harness_common.c +++ b/server/modules/filter/test/harness_common.c @@ -131,7 +131,8 @@ int open_file(char* str, unsigned int write) mode = O_RDONLY; } if((fd = open(str,mode,S_IRWXU|S_IRGRP|S_IXGRP|S_IXOTH)) < 0){ - printf("Error %d: %s\n",errno,strerror(errno)); + char errbuf[STRERROR_BUFLEN]; + printf("Error %d: %s\n", errno, strerror_r(errno, errbuf, sizeof(errbuf))); } return fd; } diff --git a/server/modules/protocol/httpd.c b/server/modules/protocol/httpd.c index fa7418157..db4467418 100644 --- a/server/modules/protocol/httpd.c +++ b/server/modules/protocol/httpd.c @@ -427,7 +427,10 @@ int syseno = 0; sizeof(one)); if(syseno != 0){ - skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)); + char errbuf[STRERROR_BUFLEN]; + skygw_log_write_flush(LOGFILE_ERROR, + "Error: Failed to set socket options. Error %d: %s", + errno, strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } /* set NONBLOCKING mode */ @@ -446,10 +449,11 @@ int syseno = 0; } else { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* Failed to start listening http due error %d, %s\n\n", eno, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); return 0; } diff --git a/server/modules/protocol/maxscaled.c b/server/modules/protocol/maxscaled.c index 6b1b5e570..874aa8bbd 100644 --- a/server/modules/protocol/maxscaled.c +++ b/server/modules/protocol/maxscaled.c @@ -380,12 +380,13 @@ int rc; } else { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "Failed to start listening for maxscale admin connections " "due error %d, %s\n\n", eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); return 0; } diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 661c57ca5..4ecf5ffa1 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -840,7 +840,6 @@ static int gw_error_backend_event(DCB *dcb) if (dcb->state != DCB_STATE_POLLING) { int error, len; - char buf[100]; len = sizeof(error); @@ -848,12 +847,12 @@ static int gw_error_backend_event(DCB *dcb) { if (error != 0) { - strerror_r(error, buf, 100); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "DCB in state %s got error '%s'.", STRDCBSTATE(dcb->state), - buf))); + strerror_r(error, errbuf, sizeof(errbuf))))); } } return 1; @@ -883,18 +882,17 @@ static int gw_error_backend_event(DCB *dcb) 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); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error '%s' in session that is not ready for routing.", - buf))); + strerror_r(error, errbuf, sizeof(errbuf))))); } } gwbuf_free(errbuf); @@ -1109,19 +1107,18 @@ gw_backend_hangup(DCB *dcb) 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 && ses_state != SESSION_STATE_STOPPING) { - strerror_r(error, buf, 100); + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Hangup in session that is not ready for routing, " "Error reported is '%s'.", - buf))); + strerror_r(error, errbuf, sizeof(errbuf))))); } } gwbuf_free(errbuf); diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 70ddcac84..87b812c06 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1355,11 +1355,12 @@ int gw_MySQLListener( // UNIX socket create if ((l_so = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* Error: can't create UNIX socket due " "error %i, %s.\n\n\t", errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } memset(&local_addr, 0, sizeof(local_addr)); @@ -1376,11 +1377,12 @@ int gw_MySQLListener( } // TCP socket create if ((l_so = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* Error: can't create socket due " "error %i, %s.\n\n\t", errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } @@ -1392,13 +1394,15 @@ int gw_MySQLListener( // 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)))); + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); } if(is_tcp) { + char errbuf[STRERROR_BUFLEN]; 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)))); + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); } } // set NONBLOCKING mode @@ -1413,10 +1417,11 @@ int gw_MySQLListener( } if (bind(l_so, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* Bind failed due error %i, %s.\n", errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); fprintf(stderr, "* Can't bind to %s\n\n", config_bind); close(l_so); return 0; @@ -1424,21 +1429,23 @@ int gw_MySQLListener( /* set permission for all users */ if (chmod(config_bind, 0777) < 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* chmod failed for %s due error %i, %s.\n\n", config_bind, errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } break; case AF_INET: if (bind(l_so, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* Bind failed due error %i, %s.\n", errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); fprintf(stderr, "* Can't bind to %s\n\n", config_bind); close(l_so); return 0; @@ -1458,10 +1465,11 @@ int gw_MySQLListener( } else { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* Failed to start listening MySQL due error %d, %s\n\n", eno, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); close(l_so); return 0; } @@ -1554,22 +1562,24 @@ int gw_MySQLAccept(DCB *listener) * Exceeded system's (ENFILE) or processes * (EMFILE) max. number of files limit. */ + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_MySQLAccept] Error %d, %s. ", pthread_self(), eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); if (i == 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error %d, %s. " "Failed to accept new client " "connection.", eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); } i++; ts1.tv_nsec = 100*i*i*1000000; @@ -1586,18 +1596,19 @@ int gw_MySQLAccept(DCB *listener) /** * Other error. */ + char errbuf[STRERROR_BUFLEN]; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_MySQLAccept] Error %d, %s.", pthread_self(), eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to accept new client " "connection due to %d, %s.", eno, - strerror(eno)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); rc = 1; goto return_rc; } /* if (eno == ..) */ @@ -1618,15 +1629,16 @@ int gw_MySQLAccept(DCB *listener) #endif /* FAKE_CODE */ /* set nonblocking */ sendbuf = GW_CLIENT_SO_SNDBUF; + char errbuf[STRERROR_BUFLEN]; 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)))); + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); } 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)))); + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); } setnonblocking(c_sock); diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 109299854..b3dfc37be 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -90,13 +90,14 @@ MySQLProtocol* mysql_protocol_init( if (p == NULL) { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; 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)))); + strerror_r(eno, errbuf, sizeof(errbuf))))); goto return_p; } p->protocol_state = MYSQL_PROTOCOL_ALLOC; @@ -767,6 +768,7 @@ int gw_do_connect_to_backend( so = socket(AF_INET,SOCK_STREAM,0); if (so < 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error: Establishing connection to backend server " @@ -775,7 +777,7 @@ int gw_do_connect_to_backend( host, port, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); rv = -1; goto return_rv; } @@ -786,6 +788,7 @@ int gw_do_connect_to_backend( if(setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) != 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error: Failed to set socket options " @@ -794,7 +797,7 @@ int gw_do_connect_to_backend( host, port, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); rv = -1; /** Close socket */ goto close_so; @@ -803,6 +806,7 @@ int gw_do_connect_to_backend( if(setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) != 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error: Failed to set socket options " @@ -811,7 +815,7 @@ int gw_do_connect_to_backend( host, port, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); rv = -1; /** Close socket */ goto close_so; @@ -820,6 +824,7 @@ int gw_do_connect_to_backend( int one = 1; if(setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error: Failed to set socket options " @@ -828,7 +833,7 @@ int gw_do_connect_to_backend( host, port, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); rv = -1; /** Close socket */ goto close_so; @@ -846,6 +851,7 @@ int gw_do_connect_to_backend( } else { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error: Failed to connect backend server %s:%d, " @@ -853,7 +859,7 @@ int gw_do_connect_to_backend( host, port, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); /** Close socket */ goto close_so; } @@ -878,13 +884,14 @@ close_so: /*< Close newly created socket. */ if (close(so) != 0) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error: Failed to " "close socket %d due %d, %s.", so, errno, - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); } goto return_rv; } @@ -2240,10 +2247,11 @@ char *create_auth_fail_str( if (errstr == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", - strerror(errno)))); + strerror_r(errno, errbuf, sizeof(errbuf))))); goto retblock; } diff --git a/server/modules/protocol/telnetd.c b/server/modules/protocol/telnetd.c index 8274c91cf..f70f8c038 100644 --- a/server/modules/protocol/telnetd.c +++ b/server/modules/protocol/telnetd.c @@ -381,7 +381,8 @@ int syseno = 0; syseno = setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); if(syseno != 0){ - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno)))); + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); return 0; } // set NONBLOCKING mode @@ -399,10 +400,11 @@ int syseno = 0; } else { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\n* Failed to start listening telnet due error %d, %s\n\n", eno, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); return 0; } diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index ca853b1e6..350b19988 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -1084,7 +1084,7 @@ errorReply(ROUTER *instance, void *router_session, GWBUF *message, DCB *backend_ ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; int error; socklen_t len; -char msg[85], *errmsg; +char msg[STRERROR_BUFLEN + 1], *errmsg; if (action == ERRACT_RESET) { @@ -1107,8 +1107,8 @@ char msg[85], *errmsg; len = sizeof(error); if (router->master && getsockopt(router->master->fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error != 0) { - strerror_r(error, msg, 80); - strcat(msg, " "); + char errbuf[STRERROR_BUFLEN]; + sprintf(msg, "%s ", strerror_r(error, errbuf, sizeof(errbuf))); } else strcpy(msg, ""); diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index 058d16056..b88a67a19 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -1464,7 +1464,7 @@ static void fail_accept( { int failcount = MIN(atoi(arg2), 100); fail_accept_errno = atoi(arg1); - + char errbuf[STRERROR_BUFLEN]; switch(fail_accept_errno) { case EAGAIN: @@ -1486,7 +1486,7 @@ static void fail_accept( dcb_printf(dcb, "[%d, %s] is not valid errno for accept.\n", fail_accept_errno, - strerror(fail_accept_errno)); + strerror_r(fail_accept_errno, errbuf, sizeof(errbuf))); return ; } } diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index d0c6ad59c..f3ad795e4 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -609,9 +609,11 @@ char** tokenize_string(char* str) char** tmp = realloc(list,sizeof(char*)*(sz*2)); if(tmp == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : realloc returned NULL: %s.",strerror(errno)))); + LOGFILE_ERROR, + "Error : realloc returned NULL: %s.", + strerror_r(errno, errbuf, sizeof(errbuf))))); free(list); return NULL; } diff --git a/server/modules/routing/schemarouter/shardrouter.c b/server/modules/routing/schemarouter/shardrouter.c index 1122b0b5c..9efcd20e9 100644 --- a/server/modules/routing/schemarouter/shardrouter.c +++ b/server/modules/routing/schemarouter/shardrouter.c @@ -566,9 +566,11 @@ tokenize_string(char* str) char** tmp = realloc(list, sizeof(char*)*(sz * 2)); if(tmp == NULL) { + char errbuf[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error : realloc returned NULL: %s.", strerror(errno)))); + "Error : realloc returned NULL: %s.", + strerror_r(errno, errbuf, sizeof(errbuf))))); free(list); return NULL; } diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 26a8e8b88..235deed41 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -85,9 +85,10 @@ int skygw_rwlock_rdlock( rwlock->srw_rwlock_thr = pthread_self(); } else { rwlock->srw_rwlock_thr = 0; + char errbuf[STRERROR_BUFLEN]; ss_dfprintf(stderr, "* pthread_rwlock_rdlock : %s\n", - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); } return err; } @@ -101,9 +102,10 @@ int skygw_rwlock_wrlock( rwlock->srw_rwlock_thr = pthread_self(); } else { rwlock->srw_rwlock_thr = 0; + char errbuf[STRERROR_BUFLEN]; ss_dfprintf(stderr, "* pthread_rwlock_wrlock : %s\n", - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); } return err; } @@ -116,9 +118,10 @@ int skygw_rwlock_unlock( if (err == 0) { rwlock->srw_rwlock_thr = 0; } else { + char errbuf[STRERROR_BUFLEN]; ss_dfprintf(stderr, "* pthread_rwlock_unlock : %s\n", - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); } return err; } @@ -132,10 +135,11 @@ int skygw_rwlock_destroy( /** Lock */ if ((err = pthread_rwlock_wrlock(rwlock->srw_rwlock)) != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Error : pthread_rwlock_wrlock failed due to %d, %s.\n", err, - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); goto retblock; } /** Clean the struct */ @@ -145,10 +149,11 @@ int skygw_rwlock_destroy( /** Destroy */ if ((err = pthread_rwlock_destroy(rwlock->srw_rwlock)) != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Error : pthread_rwlock_destroy failed due to %d,%s\n", err, - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); } else { @@ -179,9 +184,10 @@ int skygw_rwlock_init( if (err != 0) { free(rwl); + char errbuf[STRERROR_BUFLEN]; ss_dfprintf(stderr, "* Creating pthread_rwlock failed : %s\n", - strerror(err)); + strerror_r(err, errbuf, sizeof(errbuf))); goto return_err; } *rwlock = rwl; @@ -1142,11 +1148,12 @@ int skygw_thread_start( ss_dassert(err == 0); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Starting file writer thread failed due error, " "%d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); goto return_err; } @@ -1322,12 +1329,13 @@ simple_mutex_t* simple_mutex_init( err = pthread_mutex_init(&sm->sm_mutex, NULL); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Initializing simple mutex %s failed due error " "%d, %s\n", name, err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); perror("simple_mutex : "); /** Write zeroes if flat, free otherwise. */ @@ -1361,12 +1369,13 @@ int simple_mutex_done( #if defined(NOT_USED) if (err != 0) { perror("simple_mutex : "); + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Destroying simple mutex %s failed due " "%d, %s\n", sm->sm_name, err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); goto return_err; } #endif @@ -1408,12 +1417,13 @@ int simple_mutex_lock( } if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Locking simple mutex %s failed due error, " "%d, %s\n", sm->sm_name, err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); perror("simple_mutex : "); } else { /** @@ -1438,12 +1448,13 @@ int simple_mutex_unlock( err = pthread_mutex_unlock(&sm->sm_mutex); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Unlocking simple mutex %s failed due error " "%d, %s\n", sm->sm_name, err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); perror("simple_mutex : "); } else { /** @@ -1472,11 +1483,12 @@ skygw_message_t* skygw_message_init(void) err = pthread_mutex_init(&(mes->mes_mutex), NULL); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Initializing pthread mutex failed due error " "%d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); free(mes); mes = NULL; goto return_mes; @@ -1484,11 +1496,12 @@ skygw_message_t* skygw_message_init(void) err = pthread_cond_init(&(mes->mes_cond), NULL); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Initializing pthread cond var failed, " "due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); pthread_mutex_destroy(&mes->mes_mutex); free(mes); mes = NULL; @@ -1514,20 +1527,22 @@ void skygw_message_done( err = pthread_cond_destroy(&(mes->mes_cond)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Destroying cond var failed due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } ss_dassert(err == 0); err = pthread_mutex_destroy(&(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Destroying pthread mutex failed, " "due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } ss_dassert(err == 0); free(mes); @@ -1543,11 +1558,12 @@ skygw_mes_rc_t skygw_message_send( err = pthread_mutex_lock(&(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Locking pthread mutex failed, " "due to error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); goto return_mes_rc; } mes->mes_sent = true; @@ -1559,21 +1575,23 @@ skygw_mes_rc_t skygw_message_send( } else { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Signaling pthread cond var failed, " "due to error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } err = pthread_mutex_unlock(&(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Unlocking pthread mutex failed, " "due to error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } return_mes_rc: @@ -1589,11 +1607,12 @@ void skygw_message_wait( err = pthread_mutex_lock(&(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Locking pthread mutex failed, " "due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } ss_dassert(err == 0); @@ -1601,22 +1620,24 @@ void skygw_message_wait( err = pthread_cond_wait(&(mes->mes_cond), &(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Locking pthread cond wait failed, " "due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } } mes->mes_sent = false; err = pthread_mutex_unlock(&(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Unlocking pthread mutex failed, " "due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } ss_dassert(err == 0); } @@ -1631,11 +1652,12 @@ void skygw_message_reset( err = pthread_mutex_lock(&(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Locking pthread mutex failed, " "due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); goto return_mes_rc; } ss_dassert(err == 0); @@ -1643,11 +1665,12 @@ void skygw_message_reset( err = pthread_mutex_unlock(&(mes->mes_mutex)); if (err != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Unlocking pthread mutex failed, " "due error %d, %s\n", err, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); goto return_mes_rc; } return_mes_rc: @@ -1895,11 +1918,12 @@ skygw_file_t* skygw_file_init( { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Opening file %s failed due %d, %s.\n", file->sf_fname, eno, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); free(file); file = NULL; goto return_file; @@ -1910,11 +1934,12 @@ skygw_file_t* skygw_file_init( { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "\nError : Writing header of log file %s failed due %d, %s.\n", file->sf_fname, eno, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); free(file); file = NULL; goto return_file; @@ -1936,13 +1961,14 @@ skygw_file_t* skygw_file_init( { int eno = errno; errno = 0; + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "failed to create symlink %s -> " "%s due %d, %s. Exiting.", fname, symlinkname, eno, - strerror(eno)); + strerror_r(eno, errbuf, sizeof(errbuf))); free(file); file = NULL; goto return_file; @@ -1985,11 +2011,12 @@ void skygw_file_close( if ((err = fclose(file->sf_file)) != 0) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "* Closing file %s failed due to %d, %s.\n", file->sf_fname, errno, - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); } else { @@ -2030,8 +2057,9 @@ char* replace_literal( if (search_re == NULL) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Regex memory allocation failed : %s\n", - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); newstr = haystack; goto retblock; } @@ -2042,8 +2070,9 @@ char* replace_literal( if (newstr == NULL) { + char errbuf[STRERROR_BUFLEN]; fprintf(stderr, "Regex memory allocation failed : %s\n", - strerror(errno)); + strerror_r(errno, errbuf, sizeof(errbuf))); free(search_re); free(newstr); newstr = haystack; diff --git a/utils/skygw_utils.h b/utils/skygw_utils.h index a6118bd48..1aca695d9 100644 --- a/utils/skygw_utils.h +++ b/utils/skygw_utils.h @@ -1,6 +1,13 @@ #if !defined(SKYGW_UTILS_H) #define SKYGW_UTILS_H +/* + * We need a common.h file that is included by every component. + */ +#if !defined(STRERROR_BUFLEN) +#define STRERROR_BUFLEN 512 +#endif + #define MLIST #ifndef MIN #define MIN(a,b) (a Date: Sun, 6 Sep 2015 06:51:33 +0300 Subject: [PATCH 59/74] Removed LOGIF macros which prevented implicit initialization of the log manager. --- server/core/gateway.c | 54 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index dd13896e1..b7a4d1ab6 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -323,9 +323,9 @@ static void sigusr1_handler (int i) static void sigterm_handler (int i) { extern void shutdown_server(); - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, - "MaxScale received signal SIGTERM. Exiting."))); + "MaxScale received signal SIGTERM. Exiting."); skygw_log_sync_all(); shutdown_server(); } @@ -335,9 +335,9 @@ sigint_handler (int i) { extern void shutdown_server(); - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, - "MaxScale received signal SIGINT. Shutting down."))); + "MaxScale received signal SIGINT. Shutting down."); skygw_log_sync_all(); shutdown_server(); fprintf(stderr, "\n\nShutting down MaxScale\n\n"); @@ -393,9 +393,9 @@ sigfatal_handler (int i) GATEWAY_CONF* cnf = config_get_global_options(); fprintf(stderr, "\n\nMaxScale "MAXSCALE_VERSION" received fatal signal %d\n", i); - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, - "Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i))); + "Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i); skygw_log_write_flush(LE,"Commit ID: %s System name: %s " "Release string: %s Embedded library version: %s", @@ -408,9 +408,9 @@ sigfatal_handler (int i) if (symbols) { for( n = 0; n < count; n++ ) { - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, - " %s\n", symbols[n]))); + " %s\n", symbols[n]); } free(symbols); } else { @@ -456,12 +456,12 @@ static int signal_set (int sig, void (*handler)(int)) { int eno = errno; errno = 0; char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed call sigaction() in %s due to %d, %s.", program_invocation_short_name, eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + strerror_r(eno, errbuf, sizeof(errbuf))); rc = 1; } return rc; @@ -485,13 +485,13 @@ int ntfw_cb( int eno = errno; errno = 0; char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write( + skygw_log_write( LOGFILE_ERROR, "Error : Failed to remove the data directory %s of " "MaxScale due to %d, %s.", datadir, eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + strerror_r(eno, errbuf, sizeof(errbuf))); } return rc; } @@ -786,13 +786,13 @@ static void print_log_n_stderr( if (do_log) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "%s %s %s %s", log_err, logstr, eno == 0 ? " " : "Error :", - eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf))))); + eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf))); } if (do_stderr) { char errbuf[STRERROR_BUFLEN]; @@ -825,14 +825,14 @@ static bool file_is_readable( absolute_pathname, strerror_r(eno, errbuf, sizeof(errbuf))); } - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "Warning : Failed to read the configuration file %s due " "to %d, %s.", absolute_pathname, eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - LOGIF(LE,(skygw_log_sync_all())); + strerror_r(eno, errbuf, sizeof(errbuf))); + skygw_log_sync_all(); succp = false; } return succp; @@ -858,13 +858,13 @@ static bool file_is_writable( eno, strerror_r(eno, errbuf, sizeof(errbuf))); } - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "Error : unable to open file %s for write due " "to %d, %s.", absolute_pathname, eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + strerror_r(eno, errbuf, sizeof(errbuf))); succp = false; } return succp; @@ -919,14 +919,14 @@ static char* get_expanded_pathname( relative_path, strerror_r(eno, errbuf, sizeof(errbuf))); - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "Warning : Failed to read the " "directory %s, due " "to %d, %s.", relative_path, eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + strerror_r(eno, errbuf, sizeof(errbuf))); free(expanded_path); *output_path = NULL; goto return_cnf_file_buf; @@ -949,10 +949,10 @@ static char* get_expanded_pathname( ss_dassert(cnf_file_buf != NULL); char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))))); + strerror_r(errno, errbuf, sizeof(errbuf))); free(expanded_path); expanded_path = NULL; @@ -1828,7 +1828,7 @@ int main(int argc, char **argv) } } } - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "Error : mysql_library_init failed. It is a " "mandatory component, required by router services and " @@ -1836,7 +1836,7 @@ int main(int argc, char **argv) mysql_errno(NULL), mysql_error(NULL), __FILE__, - __LINE__))); + __LINE__); rc = MAXSCALE_NOLIBRARY; goto return_main; } @@ -1847,11 +1847,11 @@ int main(int argc, char **argv) char* fprerr = "Failed to load MaxScale configuration " "file. Exiting. See the error log for details."; print_log_n_stderr(false, !daemon_mode, fprerr, fprerr, 0); - LOGIF(LE, (skygw_log_write_flush( + skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to load MaxScale configuration file %s. " "Exiting.", - cnf_file_path))); + cnf_file_path); rc = MAXSCALE_BADCONFIG; goto return_main; } From 8cd3971d45966dad7be61f9bcd90b4632a22674d Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 7 Sep 2015 11:18:43 +0300 Subject: [PATCH 60/74] Fixed segfault in log manager when writing to multiple logfiles at once. --- log_manager/log_manager.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index db2a1e566..3568bbd70 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1404,9 +1404,14 @@ static int log_write(logfile_id_t id, const bool use_valist = true; const bool spread_down = true; const bool rotate = false; + va_list vlist; + + /** Copy the value of valist to a local variable because + * logmanager_write_log modifies it. */ + memcpy(vlist, valist, sizeof(va_list)); if (logmanager_write_log((logfile_id_t)i, flush, use_valist, spread_down, rotate, - len, str, valist) == 0) + len, str, vlist) == 0) { ++successes; } From 07796734038918d31e2184307a751020b7c153da Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 4 Sep 2015 15:21:05 +0300 Subject: [PATCH 61/74] MXS-228: Include file name and line numbers This change does not log the file name and line numbers, but the function name. Together with the commit information that is logged in conjunction with a crash and that MaxScale can tell, when invoked, that is enough to be able to pinpoint the location where a logging was made. Furthermore, that is a lot less intrusive and less confusing for an end-user than filename + line. This is just a temporary workaround; the logging mechanism needs to get an overhaul: - Separate severity and logging target. - Take syslog severities into use. - Simplify what needs to be done by developer. - etc. --- log_manager/log_manager.cc | 70 ++++++++++++++++++++++++++------------ log_manager/log_manager.h | 14 ++++++-- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 3568bbd70..793e65651 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1362,20 +1362,23 @@ return_succp: /** * Helper for skygw_log_write and friends. * - * @param id The id of the log file. - * @param flush Whether the log should be flushed. - * @param len The length of the formatted string, as calculated by vsnprintf. - * @param str The printf format string. - * @param valist The arguments of /str/. + * @param id The id of the log file. + * @param file The name of the file where the logging was made. + * @param int The line where the logging was made. + * @param function The function where the logging was made. + * @param str String or printf format string. + * @param flush Whether the message should be flushed. * * @return 0 if the logging to at least one log succeeded. */ static int log_write(logfile_id_t id, - bool flush, + const char* file, + int line, + const char* function, size_t len, const char* str, - va_list valist) + bool flush) { int rv = 0; @@ -1383,6 +1386,17 @@ static int log_write(logfile_id_t id, { CHK_LOGMANAGER(lm); + const char format[] = "%s [%s]"; + len += sizeof(format); // A bit too much, but won't hurt. + assert(function); + len += strlen(function); + len += 1; // For the trailing NULL. + + char message[len]; + + len = snprintf(message, sizeof(message), format, str, function); + assert(len > 0); + int attempts = 0; int successes = 0; @@ -1401,17 +1415,13 @@ static int log_write(logfile_id_t id, { ++attempts; - const bool use_valist = true; + const bool use_valist = false; const bool spread_down = true; const bool rotate = false; - va_list vlist; - - /** Copy the value of valist to a local variable because - * logmanager_write_log modifies it. */ - memcpy(vlist, valist, sizeof(va_list)); + va_list valist; // Not used if (logmanager_write_log((logfile_id_t)i, flush, use_valist, spread_down, rotate, - len, str, vlist) == 0) + len, message, valist) == 0) { ++successes; } @@ -1434,8 +1444,11 @@ static int log_write(logfile_id_t id, return rv; } -int skygw_log_write_flush( +int skygw_log_write_context_flush( logfile_id_t id, + const char* file, + int line, + const char* function, const char* str, ...) { @@ -1446,15 +1459,20 @@ int skygw_log_write_flush( * Find out the length of log string (to be formatted str). */ va_start(valist, str); - size_t len = vsnprintf(NULL, 0, str, valist); + int len = vsnprintf(NULL, 0, str, valist); va_end(valist); if (len >= 0) { - const bool flush = true; + char message[len + 1]; va_start(valist, str); - err = log_write(id, flush, len, str, valist); + int len2 = vsnprintf(message, sizeof(message), str, valist); va_end(valist); + assert(len2 == len); + + const bool flush = true; + + err = log_write(id, file, line, function, len2, message, flush); if (err != 0) { @@ -1467,8 +1485,11 @@ int skygw_log_write_flush( -int skygw_log_write( +int skygw_log_write_context( logfile_id_t id, + const char* file, + int line, + const char* function, const char* str, ...) { @@ -1479,15 +1500,20 @@ int skygw_log_write( * Find out the length of log string (to be formatted str). */ va_start(valist, str); - size_t len = vsnprintf(NULL, 0, str, valist); + int len = vsnprintf(NULL, 0, str, valist); va_end(valist); if (len >= 0) { - const bool flush = false; + char message[len + 1]; va_start(valist, str); - err = log_write(id, flush, len, str, valist); + int len2 = vsnprintf(message, sizeof(message), str, valist); va_end(valist); + assert(len2 == len); + + const bool flush = false; + + err = log_write(id, file, line, function, len2, message, flush); if (err != 0) { fprintf(stderr, "skygw_log_write failed.\n"); diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h index 45410aedc..e2ec60650 100644 --- a/log_manager/log_manager.h +++ b/log_manager/log_manager.h @@ -118,11 +118,15 @@ void skygw_logmanager_exit(void); * free private write buffer list */ void skygw_log_done(void); -int skygw_log_write(logfile_id_t id, const char* format, ...); +int skygw_log_write_context(logfile_id_t id, + const char* file, int line, const char* function, + const char* format, ...); int skygw_log_flush(logfile_id_t id); void skygw_log_sync_all(void); int skygw_log_rotate(logfile_id_t id); -int skygw_log_write_flush(logfile_id_t id, const char* format, ...); +int skygw_log_write_context_flush(logfile_id_t id, + const char* file, int line, const char* function, + const char* format, ...); int skygw_log_enable(logfile_id_t id); int skygw_log_disable(logfile_id_t id); void skygw_log_sync_all(void); @@ -130,6 +134,12 @@ void skygw_set_highp(int); void logmanager_enable_syslog(int); void logmanager_enable_maxscalelog(int); +#define skygw_log_write(id, format, ...)\ + skygw_log_write_context(id, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) + +#define skygw_log_write_flush(id, format, ...)\ + skygw_log_write_context_flush(id, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) + EXTERN_C_BLOCK_END const char* get_trace_prefix_default(void); From 42fc2db65d3c0bbecc49a3af8d613ce9595b9717 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 7 Sep 2015 13:19:11 +0300 Subject: [PATCH 62/74] Fixed formatting. --- Documentation/Tutorials/Filter-Tutorial.md | 163 ++-- ...a-Cluster-Read-Write-Splitting-Tutorial.md | 247 +++--- .../Tutorials/MaxScale-Information-Schema.md | 746 +++++++++--------- .../Tutorials/MySQL-Cluster-Setup.md | 393 ++++----- ...plication-Read-Write-Splitting-Tutorial.md | 91 +-- Documentation/Tutorials/Nagios-Plugins.md | 40 +- .../Tutorials/Notification-Service.md | 52 +- ...RabbitMQ-Setup-And-MaxScale-Integration.md | 457 ++++++----- ...eplication-Proxy-Binlog-Router-Tutorial.md | 135 ++-- 9 files changed, 1160 insertions(+), 1164 deletions(-) diff --git a/Documentation/Tutorials/Filter-Tutorial.md b/Documentation/Tutorials/Filter-Tutorial.md index 15842caad..3bd90cbfd 100644 --- a/Documentation/Tutorials/Filter-Tutorial.md +++ b/Documentation/Tutorials/Filter-Tutorial.md @@ -47,23 +47,23 @@ The MaxScale 1.0 GA release contains an implementation of a tee filter that allo ## Filter Definition Filters are defined in the configuration file, MaxScale.ini, using a section for each filter instance. The content of the filter sections in the configuration file various from filter to filter, however there are always to entries present for every filter, the type and module. - - [MyFilter] - type=filter - module=xxxfilter - +``` +[MyFilter] +type=filter +module=xxxfilter +``` The type is used by the configuration manager within MaxScale to determine what this section is defining and the module is the name of the plugin that implements the filter. When a filter is used within a service in MaxScale the entry filters= is added to the service definition in the ini file section for the service. Multiple filters can be defined using a syntax akin to the Linux shell pipe syntax. - - [Split Service] - type=service - router=readwritesplit - servers=dbserver1,dbserver2,dbserver3,dbserver4 - user=massi - passwd=6628C50E07CCE1F0392EDEEB9D1203F3 - filters=hints | top10 - +``` +[Split Service] +type=service +router=readwritesplit +servers=dbserver1,dbserver2,dbserver3,dbserver4 +user=massi +passwd=6628C50E07CCE1F0392EDEEB9D1203F3 +filters=hints | top10 +``` The names used in the filters= parameter are the names of the filter definition sections in the ini file. The same filter definition can be used in multiple services and the same filter module can have multiple instances, each with its own section in the ini file. ## Filter Examples @@ -75,46 +75,45 @@ The filters that are bundled with the MaxScale 1.0 GA release are documented sep The top filter can be used to measure the execution time of every statement within a connection and log the details of the longest running statements. The first thing to do is to define a filter entry in the ini file for the top filter. In this case we will call it "top30". The type is filter and the module that implements the filter is called topfilter. - - [top30] - type=filter - module=topfilter - count=30 - filebase=/var/log/DBSessions/top30 - +``` +[top30] +type=filter +module=topfilter +count=30 +filebase=/var/log/DBSessions/top30 +``` In the definition above we have defined two filter specific parameters, the count of the number of statement to be logged and a filebase that is used to define where to log the information. This filename is a stem to which a session id is added for each new connection that uses the filter. The filter keeps track of every statement that is executed, monitors the time it takes for a response to come back and uses this as the measure of execution time for the statement. If the time is longer than the other statements that have been recorded, then this is added to the ordered list within the filter. Once 30 statements have been recorded those statements that have been recorded with the least time are discarded from the list. The result is that at any time the filter has a list of the 30 longest running statements in each session. It is possible to see what is in the current list by using the maxadmin tool to view the state of the filter by looking at the session data. First you need to find the session id for the session of interest, this can be done using commands such as list sessions. You can then use the show session command to see the details for a particular session. +``` +MaxScale> show session 0x736680 - MaxScale> show session 0x736680 +Session 0x736680 +State: Session ready for routing +Service: Split Service (0x719f60) +Client DCB: 0x7361a0 +Client Address: 127.0.0.1 +Connected: Thu Jun 26 10:10:44 2014 - Session 0x736680 - State: Session ready for routing - Service: Split Service (0x719f60) - Client DCB: 0x7361a0 - Client Address: 127.0.0.1 - Connected: Thu Jun 26 10:10:44 2014 +Filter: top30 +Report size 30 +Logging to file /var/log/DBSessions/top30.1. +Current Top 30: - Filter: top30 - Report size 30 - Logging to file /var/log/DBSessions/top30.1. - Current Top 30: +1 place: +Execution time: 23.826 seconds +SQL: select sum(salary), year(from_date) from salaries s, (select distinct year(from_date) as y1 from salaries) y where (makedate(y.y1, 1) between s.from_date and s.to_date) group by y.y1 ("1988-08-01? - 1 place: - Execution time: 23.826 seconds - SQL: select sum(salary), year(from_date) from salaries s, (select distinct year(from_date) as y1 from salaries) y where (makedate(y.y1, 1) between s.from_date and s.to_date) group by y.y1 ("1988-08-01? +2 place: +Execution time: 5.251 seconds +SQL: select d.dept_name as "Department", y.y1 as "Year", count(*) as "Count" from departments d, dept_emp de, (select distinct year(from_date) as y1 from dept_emp order by 1) y where d.dept_no = de.dept_no and (makedate(y.y1, 1) between de.from_date and de.to_date) group by y.y1, d.dept_name order by 1, 2 - 2 place: - Execution time: 5.251 seconds - SQL: select d.dept_name as "Department", y.y1 as "Year", count(*) as "Count" from departments d, dept_emp de, (select distinct year(from_date) as y1 from dept_emp order by 1) y where d.dept_no = de.dept_no and (makedate(y.y1, 1) between de.from_date and de.to_date) group by y.y1, d.dept_name order by 1, 2 - - 3 place: - Execution time: 2.903 seconds - SQL: select year(now()) - year(birth_date) as age, gender, avg(salary) as "Average Salary" from employees e, salaries s where e.emp_no = s.emp_no and ("1988-08-01" between from_date AND to_date) group by year(now()) - year(birth_date), gender order by 1,2 - - ... +3 place: +Execution time: 2.903 seconds +SQL: select year(now()) - year(birth_date) as age, gender, avg(salary) as "Average Salary" from employees e, salaries s where e.emp_no = s.emp_no and ("1988-08-01" between from_date AND to_date) group by year(now()) - year(birth_date), gender order by 1,2 +``` When the session ends a report will be written for the session into the logfile defined. That report will include the top 30 longest running statements, plus summary data for the session; @@ -133,46 +132,46 @@ When the session ends a report will be written for the session into the logfile ### Duplicate Data From Your Application Into Cassandra The scenario we are using in this example is one in which you have an online gaming application that is designed to work with a MariaDB/MySQL database. The database schema includes a high score table which you would like to have access to in a Cassandra cluster. The application is already using MaxScale to connect to a MariaDB Galera cluster, using a service names BubbleGame. The definition of that service is as follows - - [BubbleGame] - type=service - router=readwritesplit - servers=dbbubble1,dbbubble2,dbbubble3,dbbubble4,dbbubble5 - user=maxscale - passwd=6628C50E07CCE1F0392EDEEB9D1203F3 - +``` +[BubbleGame] +type=service +router=readwritesplit +servers=dbbubble1,dbbubble2,dbbubble3,dbbubble4,dbbubble5 +user=maxscale +passwd=6628C50E07CCE1F0392EDEEB9D1203F3 +``` The table you wish to store in Cassandra in called HighScore and will contain the same columns in both the MariaDB table and the Cassandra table. The first step is to install a MariaDB instance with the Cassandra storage engine to act as a bridge server between the relational database and Cassandra. In this bridge server add a table definition for the HighScore table with the engine type set to cassandra. Add this server into the MaxScale configuration and create a service that will connect to this server. - - [CassandraDB] - type=server - address=192.168.4.28 - port=3306 - protocol=MySQLBackend - [Cassandra] - type=service - router=readconnrouter - router_options=running - servers=CassandraDB - user=maxscale - passwd=6628C50E07CCE1F0392EDEEB9D1203F3 - +``` +[CassandraDB] +type=server +address=192.168.4.28 +port=3306 +protocol=MySQLBackend +[Cassandra] +type=service +router=readconnrouter +router_options=running +servers=CassandraDB +user=maxscale +passwd=6628C50E07CCE1F0392EDEEB9D1203F3 +``` Next add a filter definition for the tee filter that will duplication insert statements that are destined for the HighScore table to this new service. - - [HighScores] - type=filter - module=teefilter - match=insert.*HighScore.*values - service=Cassandra - +``` +[HighScores] +type=filter +module=teefilter +match=insert.*HighScore.*values +service=Cassandra +``` The above filter definition will cause all statements that match the regular expression inset.*HighScore.*values to be duplication and sent not just to the original destination, via the router but also to the service named Cassandra. The final step is to add the filter to the BubbleGame service to enable the use of the filter. - - [BubbleGame] - type=service - router=readwritesplit - servers=dbbubble1,dbbubble2,dbbubble3,dbbubble4,dbbubble5 - user=maxscale - passwd=6628C50E07CCE1F0392EDEEB9D1203F3 - filters=HighScores - +``` +[BubbleGame] +type=service +router=readwritesplit +servers=dbbubble1,dbbubble2,dbbubble3,dbbubble4,dbbubble5 +user=maxscale +passwd=6628C50E07CCE1F0392EDEEB9D1203F3 +filters=HighScores +``` diff --git a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md index 4da69f27e..18ca0f99c 100644 --- a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md @@ -65,158 +65,159 @@ If you wish to use two different usernames for the two different roles of monito 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. - - [maxscale] - threads=4 - +``` +[maxscale] +threads=4 +``` The first step is to create a service for our Read/Write Splitter. Create a section 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. - - [Splitter Service] - type=service - +``` +[Splitter Service] +type=service +``` The router for we need to use for this configuration is the readwritesplit 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. - - [Splitter Service] - type=service - router=readwritesplit - servers=dbserv1, dbserv2, dbserv3 - +``` +[Splitter Service] +type=service +router=readwritesplit +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 /var/lib/maxscale/.secrets - % - +``` +% maxkeys /var/lib/maxscale/.secrets +% +``` Once the keys have been created the maxpasswd command can be used to generate the encrypted password. - - % maxpasswd plainpassword - 96F99AA1315BDC3604B006F427DD9484 - % - +``` +% maxpasswd plainpassword +96F99AA1315BDC3604B006F427DD9484 +% +``` The username and password, either encrypted or plain text, are stored in the service section using the user and passwd parameters. - - [Splitter Service] - type=service - router=readwritesplit - servers=dbserv1, dbserv2, dbserv3 - user=maxscale - passwd=96F99AA1315BDC3604B006F427DD9484 - +``` +[Splitter Service] +type=service +router=readwritesplit +servers=dbserv1, dbserv2, dbserv3 +user=maxscale +passwd=96F99AA1315BDC3604B006F427DD9484 +``` This completes the definitions required by the service, however listening ports must be associated with the service in order to allow network connections. This is done by creating a series of listener sections. This section again is 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. A service may have multiple listeners. - - [Splitter Listener] - type=listener - service=Splitter Service - +``` +[Splitter Listener] +type=listener +service=Splitter 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. - - [Splitter Listener] - type=listener - service=Splitter Service - protocol=MySQLClient - port=3306 - socket=/tmp/ClusterMaster - +``` +[Splitter Listener] +type=listener +service=Splitter Service +protocol=MySQLClient +port=3306 +socket=/tmp/ClusterMaster +``` 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 module for all database connections in MySQLBackend. +``` +[dbserv1] +type=server +address=192.168.2.1 +port=3306 +protocol=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 +[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. - - [Galera Monitor] - type=monitor - module=galeramon - servers=dbserv1, dbserv2, dbserv3 - user=maxscale - passwd=96F99AA1315BDC3604B006F427DD9484 - +``` +[Galera Monitor] +type=monitor +module=galeramon +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. This monitor module will assign one node within the Galera Cluster as the current master and other nodes as slave. Only those nodes that are active members of the cluster are considered when making the choice of master node. Normally the master node will be the node with the lowest value of the status variable, WSREP_LOCAL_INDEX. When cluster membership changes a new master may be elected. In order to prevent changes of the node that is currently master, a parameter can be added to the monitor that will result in the current master remaining as master even if a node with a lower value of WSREP_LOCAL_INDEX joins the cluster. This parameter is called disable_master_failback. - - [Galera Monitor] - type=monitor - module=galeramon - diable_master_failback=1 - servers=dbserv1, dbserv2, dbserv3 - user=maxscale - passwd=96F99AA1315BDC3604B006F427DD9484 - +``` +[Galera Monitor] +type=monitor +module=galeramon +diable_master_failback=1 +servers=dbserv1, dbserv2, dbserv3 +user=maxscale +passwd=96F99AA1315BDC3604B006F427DD9484 +``` Using this option the master node will only change if there is a problem with the current master and never because other nodes have joined the cluster. 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] - type=service - router=cli - [CLI Listener] - type=listener - service=CLI - protocol=maxscaled - address=localhost - port=6603 - +[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. ## Starting MaxScale 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 /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 - % maxadmin -pmariadb list services - - Services. - --------------------------+----------------------+--------+--------------- - Service Name | Router Module | #Users | Total Sessions - --------------------------+----------------------+--------+--------------- - Splitter Service | readwritesplit | 1 | 1 - CLI | cli | 2 | 2 - --------------------------+----------------------+--------+--------------- - - % maxadmin -pmariadb list servers - Servers. - -------------------+-----------------+-------+-------------+-------------------- - Server | Address | Port | Connections | Status - -------------------+-----------------+-------+-------------+-------------------- - dbserv1 | 192.168.2.1 | 3306 | 0 | Running, Synced, Master - dbserv2 | 192.168.2.2 | 3306 | 0 | Running, Synced, Slave - dbserv3 | 192.168.2.3 | 3306 | 0 | Running, Synced, Slave - -------------------+-----------------+-------+-------------+-------------------- +Services. +--------------------------+----------------------+--------+--------------- +Service Name | Router Module | #Users | Total Sessions +--------------------------+----------------------+--------+--------------- +Splitter Service | readwritesplit | 1 | 1 +CLI | cli | 2 | 2 +--------------------------+----------------------+--------+--------------- +% maxadmin -pmariadb list servers +Servers. +-------------------+-----------------+-------+-------------+-------------------- +Server | Address | Port | Connections | Status +-------------------+-----------------+-------+-------------+-------------------- +dbserv1 | 192.168.2.1 | 3306 | 0 | Running, Synced, Master +dbserv2 | 192.168.2.2 | 3306 | 0 | Running, Synced, Slave +dbserv3 | 192.168.2.3 | 3306 | 0 | Running, Synced, Slave +-------------------+-----------------+-------+-------------+-------------------- +``` A Galera Cluster is a multi-master clustering technology, however the monitor is able to impose false notions of master and slave roles within a Galera Cluster in order to facilitate the use of Galera as if it were a standard MySQL Replication setup. This is merely an internal MaxScale convenience and has no impact on the behavior of the cluster but does allow the monitor to create these pseudo roles which are utilized by the Read/Write Splitter. +``` +% maxadmin -pmariadb list listeners - % maxadmin -pmariadb list listeners - - Listeners. - ---------------------+--------------------+-----------------+-------+-------- - Service Name | Protocol Module | Address | Port | State - ---------------------+--------------------+-----------------+-------+-------- - Splitter Service | MySQLClient | * | 3306 | Running - CLI | maxscaled | localhost | 6603 | Running - ---------------------+--------------------+-----------------+-------+-------- - % - +Listeners. +---------------------+--------------------+-----------------+-------+-------- +Service Name | Protocol Module | Address | Port | State +---------------------+--------------------+-----------------+-------+-------- +Splitter Service | MySQLClient | * | 3306 | Running +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". - diff --git a/Documentation/Tutorials/MaxScale-Information-Schema.md b/Documentation/Tutorials/MaxScale-Information-Schema.md index 370960287..66bb3ab5b 100644 --- a/Documentation/Tutorials/MaxScale-Information-Schema.md +++ b/Documentation/Tutorials/MaxScale-Information-Schema.md @@ -12,31 +12,31 @@ The specified user, with the password (plain or encrypted via maxpassword utilit 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 +[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 +[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 +[MaxInfo JSON Listener] +type=listener +service=MaxInfo +protocol=HTTPD +port=8003 ``` @@ -45,12 +45,12 @@ If both the MySQL and JSON responses are required then a single service can be c 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 +[MaxInfo Listener] +type=listener +service=MaxInfo +protocol=MySQLClient +address=localhost +port=9003 ``` # MySQL Interface to maxinfo @@ -58,11 +58,11 @@ As with any other listeners within MaxScale the listeners can be bound to a part 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 - % +% 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. @@ -72,64 +72,64 @@ 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 | - +--------------------+-------------------------+ - | version | 1.0.6-unstable | - | version_comment | MariaDB MaxScale | - | basedir | /home/mriddoch/skygate2 | - | MAXSCALE_VERSION | 1.0.6-unstable | - | MAXSCALE_THREADS | 1 | - | MAXSCALE_NBPOLLS | 3 | - | MAXSCALE_POLLSLEEP | 1000 | - | MAXSCALE_UPTIME | 223 | - | MAXSCALE_SESSIONS | 11 | - +--------------------+-------------------------+ - 9 rows in set (0.02 sec) - - mysql> +mysql> show variables; ++--------------------+-------------------------+ +| Variable_name | Value | ++--------------------+-------------------------+ +| version | 1.0.6-unstable | +| version_comment | MariaDB MaxScale | +| basedir | /home/mriddoch/skygate2 | +| MAXSCALE_VERSION | 1.0.6-unstable | +| MAXSCALE_THREADS | 1 | +| MAXSCALE_NBPOLLS | 3 | +| MAXSCALE_POLLSLEEP | 1000 | +| MAXSCALE_UPTIME | 223 | +| MAXSCALE_SESSIONS | 11 | ++--------------------+-------------------------+ +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 | - +---------------+----------------+ - | version | 1.0.6-unstable | - +---------------+----------------+ - 1 row in set (0.02 sec) - - mysql> show variables like 'version%'; - +-----------------+------------------+ - | Variable_name | Value | - +-----------------+------------------+ - | version | 1.0.6-unstable | - | version_comment | MariaDB MaxScale | - +-----------------+------------------+ - 2 rows in set (0.02 sec) - - mysql> show variables like '%comment'; - +-----------------+------------------+ - | Variable_name | Value | - +-----------------+------------------+ - | version_comment | MariaDB MaxScale | - +-----------------+------------------+ - 1 row in set (0.02 sec) - - mysql> show variables like '%ers%'; - +------------------+------------------+ - | Variable_name | Value | - +------------------+------------------+ - | version | 1.0.6-unstable | - | version_comment | MariaDB MaxScale | - | MAXSCALE_VERSION | 1.0.6-unstable | - +------------------+------------------+ - 3 rows in set (0.02 sec) - - mysql> +mysql> show variables like 'version'; ++---------------+----------------+ +| Variable_name | Value | ++---------------+----------------+ +| version | 1.0.6-unstable | ++---------------+----------------+ +1 row in set (0.02 sec) + +mysql> show variables like 'version%'; ++-----------------+------------------+ +| Variable_name | Value | ++-----------------+------------------+ +| version | 1.0.6-unstable | +| version_comment | MariaDB MaxScale | ++-----------------+------------------+ +2 rows in set (0.02 sec) + +mysql> show variables like '%comment'; ++-----------------+------------------+ +| Variable_name | Value | ++-----------------+------------------+ +| version_comment | MariaDB MaxScale | ++-----------------+------------------+ +1 row in set (0.02 sec) + +mysql> show variables like '%ers%'; ++------------------+------------------+ +| Variable_name | Value | ++------------------+------------------+ +| version | 1.0.6-unstable | +| version_comment | MariaDB MaxScale | +| MAXSCALE_VERSION | 1.0.6-unstable | ++------------------+------------------+ +3 rows in set (0.02 sec) + +mysql> ``` ## Show status @@ -137,36 +137,36 @@ The show variables command can also accept a limited like clause. This like clau 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 | - +---------------------------+-------+ - | Uptime | 156 | - | Uptime_since_flush_status | 156 | - | Threads_created | 1 | - | Threads_running | 1 | - | Threadpool_threads | 1 | - | Threads_connected | 11 | - | Connections | 11 | - | Client_connections | 2 | - | Backend_connections | 0 | - | Listeners | 9 | - | Zombie_connections | 0 | - | Internal_descriptors | 2 | - | Read_events | 22 | - | Write_events | 24 | - | Hangup_events | 0 | - | Error_events | 0 | - | Accept_events | 2 | - | Event_queue_length | 1 | - | Pending_events | 0 | - | Max_event_queue_length | 1 | - | Max_event_queue_time | 0 | - | Max_event_execution_time | 0 | - +---------------------------+-------+ - 22 rows in set (0.02 sec) - - mysql> +mysql> show status; ++---------------------------+-------+ +| Variable_name | Value | ++---------------------------+-------+ +| Uptime | 156 | +| Uptime_since_flush_status | 156 | +| Threads_created | 1 | +| Threads_running | 1 | +| Threadpool_threads | 1 | +| Threads_connected | 11 | +| Connections | 11 | +| Client_connections | 2 | +| Backend_connections | 0 | +| Listeners | 9 | +| Zombie_connections | 0 | +| Internal_descriptors | 2 | +| Read_events | 22 | +| Write_events | 24 | +| Hangup_events | 0 | +| Error_events | 0 | +| Accept_events | 2 | +| Event_queue_length | 1 | +| Pending_events | 0 | +| Max_event_queue_length | 1 | +| Max_event_queue_time | 0 | +| Max_event_execution_time | 0 | ++---------------------------+-------+ +22 rows in set (0.02 sec) + +mysql> ``` ## Show services @@ -174,22 +174,22 @@ The show status command displays a set of status counters, as with show variable 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 | - +----------------+----------------+--------------+----------------+ - | Test Service | readconnroute | 1 | 1 | - | Split Service | readwritesplit | 1 | 1 | - | Filter Service | readconnroute | 1 | 1 | - | Named Service | readwritesplit | 1 | 1 | - | QLA Service | readconnroute | 1 | 1 | - | Debug Service | debugcli | 1 | 1 | - | CLI | cli | 1 | 1 | - | MaxInfo | maxinfo | 4 | 4 | - +----------------+----------------+--------------+----------------+ - 8 rows in set (0.02 sec) - - mysql> +mysql> show services; ++----------------+----------------+--------------+----------------+ +| Service Name | Router Module | No. Sessions | Total Sessions | ++----------------+----------------+--------------+----------------+ +| Test Service | readconnroute | 1 | 1 | +| Split Service | readwritesplit | 1 | 1 | +| Filter Service | readconnroute | 1 | 1 | +| Named Service | readwritesplit | 1 | 1 | +| QLA Service | readconnroute | 1 | 1 | +| Debug Service | debugcli | 1 | 1 | +| CLI | cli | 1 | 1 | +| MaxInfo | maxinfo | 4 | 4 | ++----------------+----------------+--------------+----------------+ +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. @@ -199,23 +199,23 @@ 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 | - +----------------+-----------------+-----------+------+---------+ - | Test Service | MySQLClient | * | 4006 | Running | - | Split Service | MySQLClient | * | 4007 | Running | - | Filter Service | MySQLClient | * | 4008 | Running | - | Named Service | MySQLClient | * | 4010 | Running | - | QLA Service | MySQLClient | * | 4009 | Running | - | Debug Service | telnetd | localhost | 4242 | Running | - | CLI | maxscaled | localhost | 6603 | Running | - | MaxInfo | MySQLClient | * | 9003 | Running | - | MaxInfo | HTTPD | * | 8003 | Running | - +----------------+-----------------+-----------+------+---------+ - 9 rows in set (0.02 sec) - - mysql> +mysql> show listeners; ++----------------+-----------------+-----------+------+---------+ +| Service Name | Protocol Module | Address | Port | State | ++----------------+-----------------+-----------+------+---------+ +| Test Service | MySQLClient | * | 4006 | Running | +| Split Service | MySQLClient | * | 4007 | Running | +| Filter Service | MySQLClient | * | 4008 | Running | +| Named Service | MySQLClient | * | 4010 | Running | +| QLA Service | MySQLClient | * | 4009 | Running | +| Debug Service | telnetd | localhost | 4242 | Running | +| CLI | maxscaled | localhost | 6603 | Running | +| MaxInfo | MySQLClient | * | 9003 | Running | +| MaxInfo | HTTPD | * | 8003 | Running | ++----------------+-----------------+-----------+------+---------+ +9 rows in set (0.02 sec) + +mysql> ``` The show listeners command will ignore any like clause passed to it. @@ -225,25 +225,25 @@ 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 | - +-----------+---------------+----------------+---------------------------+ - | 0x1a92a60 | 127.0.0.1 | MaxInfo | Session ready for routing | - | 0x1a92100 | 80.240.130.35 | MaxInfo | Session ready for routing | - | 0x1a76a00 | | MaxInfo | Listener Session | - | 0x1a76020 | | MaxInfo | Listener Session | - | 0x1a75d40 | | CLI | Listener Session | - | 0x1a75220 | | Debug Service | Listener Session | - | 0x1a774b0 | | QLA Service | Listener Session | - | 0x1a78630 | | Named Service | Listener Session | - | 0x1a60270 | | Filter Service | Listener Session | - | 0x1a606f0 | | Split Service | Listener Session | - | 0x19b0380 | | Test Service | Listener Session | - +-----------+---------------+----------------+---------------------------+ - 11 rows in set (0.02 sec) - - mysql> +mysql> show sessions; ++-----------+---------------+----------------+---------------------------+ +| Session | Client | Service | State | ++-----------+---------------+----------------+---------------------------+ +| 0x1a92a60 | 127.0.0.1 | MaxInfo | Session ready for routing | +| 0x1a92100 | 80.240.130.35 | MaxInfo | Session ready for routing | +| 0x1a76a00 | | MaxInfo | Listener Session | +| 0x1a76020 | | MaxInfo | Listener Session | +| 0x1a75d40 | | CLI | Listener Session | +| 0x1a75220 | | Debug Service | Listener Session | +| 0x1a774b0 | | QLA Service | Listener Session | +| 0x1a78630 | | Named Service | Listener Session | +| 0x1a60270 | | Filter Service | Listener Session | +| 0x1a606f0 | | Split Service | Listener Session | +| 0x19b0380 | | Test Service | Listener Session | ++-----------+---------------+----------------+---------------------------+ +11 rows in set (0.02 sec) + +mysql> ``` ## Show clients @@ -251,16 +251,16 @@ The show sessions command returns information on every active session within Max 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 | - +-----------+---------------+---------+---------------------------+ - | 0x1a92a60 | 127.0.0.1 | MaxInfo | Session ready for routing | - | 0x1a92100 | 80.240.130.35 | MaxInfo | Session ready for routing | - +-----------+---------------+---------+---------------------------+ - 2 rows in set (0.02 sec) - - mysql> +mysql> show clients; ++-----------+---------------+---------+---------------------------+ +| Session | Client | Service | State | ++-----------+---------------+---------+---------------------------+ +| 0x1a92a60 | 127.0.0.1 | MaxInfo | Session ready for routing | +| 0x1a92100 | 80.240.130.35 | MaxInfo | Session ready for routing | ++-----------+---------------+---------+---------------------------+ +2 rows in set (0.02 sec) + +mysql> ``` ## Show servers @@ -268,18 +268,18 @@ The show clients command reports a row for every client application connected to 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 | - +---------+-----------+------+-------------+---------+ - | server1 | 127.0.0.1 | 3306 | 0 | Running | - | server2 | 127.0.0.1 | 3307 | 0 | Down | - | server3 | 127.0.0.1 | 3308 | 0 | Down | - | server4 | 127.0.0.1 | 3309 | 0 | Down | - +---------+-----------+------+-------------+---------+ - 4 rows in set (0.02 sec) - - mysql> +mysql> show servers; ++---------+-----------+------+-------------+---------+ +| Server | Address | Port | Connections | Status | ++---------+-----------+------+-------------+---------+ +| server1 | 127.0.0.1 | 3306 | 0 | Running | +| server2 | 127.0.0.1 | 3307 | 0 | Down | +| server3 | 127.0.0.1 | 3308 | 0 | Down | +| server4 | 127.0.0.1 | 3309 | 0 | Down | ++---------+-----------+------+-------------+---------+ +4 rows in set (0.02 sec) + +mysql> ``` ## Show modules @@ -287,24 +287,24 @@ The show servers command returns data for each backend server configured within 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 | - +----------------+-------------+---------+-------------+----------------+ - | HTTPD | Protocol | V1.0.1 | 1.0.0 | In Development | - | maxscaled | Protocol | V1.0.0 | 1.0.0 | GA | - | telnetd | Protocol | V1.0.1 | 1.0.0 | GA | - | MySQLClient | Protocol | V1.0.0 | 1.0.0 | GA | - | mysqlmon | Monitor | V1.4.0 | 1.0.0 | GA | - | readwritesplit | Router | V1.0.2 | 1.0.0 | GA | - | readconnroute | Router | V1.1.0 | 1.0.0 | GA | - | debugcli | Router | V1.1.1 | 1.0.0 | GA | - | cli | Router | V1.0.0 | 1.0.0 | GA | - | maxinfo | Router | V1.0.0 | 1.0.0 | Alpha | - +----------------+-------------+---------+-------------+----------------+ - 10 rows in set (0.02 sec) - - mysql> +mysql> show modules; ++----------------+-------------+---------+-------------+----------------+ +| Module Name | Module Type | Version | API Version | Status | ++----------------+-------------+---------+-------------+----------------+ +| HTTPD | Protocol | V1.0.1 | 1.0.0 | In Development | +| maxscaled | Protocol | V1.0.0 | 1.0.0 | GA | +| telnetd | Protocol | V1.0.1 | 1.0.0 | GA | +| MySQLClient | Protocol | V1.0.0 | 1.0.0 | GA | +| mysqlmon | Monitor | V1.4.0 | 1.0.0 | GA | +| readwritesplit | Router | V1.0.2 | 1.0.0 | GA | +| readconnroute | Router | V1.1.0 | 1.0.0 | GA | +| debugcli | Router | V1.1.1 | 1.0.0 | GA | +| cli | Router | V1.0.0 | 1.0.0 | GA | +| maxinfo | Router | V1.0.0 | 1.0.0 | Alpha | ++----------------+-------------+---------+-------------+----------------+ +10 rows in set (0.02 sec) + +mysql> ``` ## Show monitors @@ -312,15 +312,15 @@ The show modules command reports the information on the modules currently loaded The show monitors command reports each monitor configured within the system and the state of that monitor. ``` - mysql> show monitors; - +---------------+---------+ - | Monitor | Status | - +---------------+---------+ - | MySQL Monitor | Running | - +---------------+---------+ - 1 row in set (0.02 sec) - - mysql> +mysql> show monitors; ++---------------+---------+ +| Monitor | Status | ++---------------+---------+ +| MySQL Monitor | Running | ++---------------+---------+ +1 row in set (0.02 sec) + +mysql> ``` ## Show eventTimes @@ -328,44 +328,44 @@ The show monitors command reports each monitor configured within the system and 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 | - +---------------+-------------------+---------------------+ - | < 100ms | 460 | 456 | - | 100 - 200ms | 0 | 3 | - | 200 - 300ms | 0 | 0 | - | 300 - 400ms | 0 | 0 | - | 400 - 500ms | 0 | 0 | - | 500 - 600ms | 0 | 0 | - | 600 - 700ms | 0 | 0 | - | 700 - 800ms | 0 | 0 | - | 800 - 900ms | 0 | 0 | - | 900 - 1000ms | 0 | 0 | - | 1000 - 1100ms | 0 | 0 | - | 1100 - 1200ms | 0 | 0 | - | 1200 - 1300ms | 0 | 0 | - | 1300 - 1400ms | 0 | 0 | - | 1400 - 1500ms | 0 | 0 | - | 1500 - 1600ms | 0 | 0 | - | 1600 - 1700ms | 0 | 0 | - | 1700 - 1800ms | 0 | 0 | - | 1800 - 1900ms | 0 | 0 | - | 1900 - 2000ms | 0 | 0 | - | 2000 - 2100ms | 0 | 0 | - | 2100 - 2200ms | 0 | 0 | - | 2200 - 2300ms | 0 | 0 | - | 2300 - 2400ms | 0 | 0 | - | 2400 - 2500ms | 0 | 0 | - | 2500 - 2600ms | 0 | 0 | - | 2600 - 2700ms | 0 | 0 | - | 2700 - 2800ms | 0 | 0 | - | 2800 - 2900ms | 0 | 0 | - | > 3000ms | 0 | 0 | - +---------------+-------------------+---------------------+ - 30 rows in set (0.02 sec) - - mysql> +mysql> show eventTimes; ++---------------+-------------------+---------------------+ +| Duration | No. Events Queued | No. Events Executed | ++---------------+-------------------+---------------------+ +| < 100ms | 460 | 456 | +| 100 - 200ms | 0 | 3 | +| 200 - 300ms | 0 | 0 | +| 300 - 400ms | 0 | 0 | +| 400 - 500ms | 0 | 0 | +| 500 - 600ms | 0 | 0 | +| 600 - 700ms | 0 | 0 | +| 700 - 800ms | 0 | 0 | +| 800 - 900ms | 0 | 0 | +| 900 - 1000ms | 0 | 0 | +| 1000 - 1100ms | 0 | 0 | +| 1100 - 1200ms | 0 | 0 | +| 1200 - 1300ms | 0 | 0 | +| 1300 - 1400ms | 0 | 0 | +| 1400 - 1500ms | 0 | 0 | +| 1500 - 1600ms | 0 | 0 | +| 1600 - 1700ms | 0 | 0 | +| 1700 - 1800ms | 0 | 0 | +| 1800 - 1900ms | 0 | 0 | +| 1900 - 2000ms | 0 | 0 | +| 2000 - 2100ms | 0 | 0 | +| 2100 - 2200ms | 0 | 0 | +| 2200 - 2300ms | 0 | 0 | +| 2300 - 2400ms | 0 | 0 | +| 2400 - 2500ms | 0 | 0 | +| 2500 - 2600ms | 0 | 0 | +| 2600 - 2700ms | 0 | 0 | +| 2700 - 2800ms | 0 | 0 | +| 2800 - 2900ms | 0 | 0 | +| > 3000ms | 0 | 0 | ++---------------+-------------------+---------------------+ +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. @@ -379,17 +379,17 @@ 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"}, - { "Variable_name" : "basedir", "Value" : "/home/mriddoch/skygate2"}, - { "Variable_name" : "MAXSCALE_VERSION", "Value" : "1.0.6-unstable"}, - { "Variable_name" : "MAXSCALE_THREADS", "Value" : 1}, - { "Variable_name" : "MAXSCALE_NBPOLLS", "Value" : 3}, - { "Variable_name" : "MAXSCALE_POLLSLEEP", "Value" : 1000}, - { "Variable_name" : "MAXSCALE_UPTIME", "Value" : 3948}, - { "Variable_name" : "MAXSCALE_SESSIONS", "Value" : 12}] - $ +$ curl http://maxscale.mariadb.com:8003/variables +[ { "Variable_name" : "version", "Value" : "1.0.6-unstable"}, +{ "Variable_name" : "version_comment", "Value" : "MariaDB MaxScale"}, +{ "Variable_name" : "basedir", "Value" : "/home/mriddoch/skygate2"}, +{ "Variable_name" : "MAXSCALE_VERSION", "Value" : "1.0.6-unstable"}, +{ "Variable_name" : "MAXSCALE_THREADS", "Value" : 1}, +{ "Variable_name" : "MAXSCALE_NBPOLLS", "Value" : 3}, +{ "Variable_name" : "MAXSCALE_POLLSLEEP", "Value" : 1000}, +{ "Variable_name" : "MAXSCALE_UPTIME", "Value" : 3948}, +{ "Variable_name" : "MAXSCALE_SESSIONS", "Value" : 12}] +$ ``` ## Status @@ -397,47 +397,47 @@ The /variables URL will return the MaxScale variables, these variables can not b 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}, - { "Variable_name" : "Threads_created", "Value" : 1}, - { "Variable_name" : "Threads_running", "Value" : 1}, - { "Variable_name" : "Threadpool_threads", "Value" : 1}, - { "Variable_name" : "Threads_connected", "Value" : 12}, - { "Variable_name" : "Connections", "Value" : 12}, - { "Variable_name" : "Client_connections", "Value" : 3}, - { "Variable_name" : "Backend_connections", "Value" : 0}, - { "Variable_name" : "Listeners", "Value" : 9}, - { "Variable_name" : "Zombie_connections", "Value" : 0}, - { "Variable_name" : "Internal_descriptors", "Value" : 3}, - { "Variable_name" : "Read_events", "Value" : 469}, - { "Variable_name" : "Write_events", "Value" : 479}, - { "Variable_name" : "Hangup_events", "Value" : 12}, - { "Variable_name" : "Error_events", "Value" : 0}, - { "Variable_name" : "Accept_events", "Value" : 15}, - { "Variable_name" : "Event_queue_length", "Value" : 1}, - { "Variable_name" : "Pending_events", "Value" : 0}, - { "Variable_name" : "Max_event_queue_length", "Value" : 1}, - { "Variable_name" : "Max_event_queue_time", "Value" : 0}, - { "Variable_name" : "Max_event_execution_time", "Value" : 1}] - $ +$ curl http://maxscale.mariadb.com:8003/status +[ { "Variable_name" : "Uptime", "Value" : 3831}, +{ "Variable_name" : "Uptime_since_flush_status", "Value" : 3831}, +{ "Variable_name" : "Threads_created", "Value" : 1}, +{ "Variable_name" : "Threads_running", "Value" : 1}, +{ "Variable_name" : "Threadpool_threads", "Value" : 1}, +{ "Variable_name" : "Threads_connected", "Value" : 12}, +{ "Variable_name" : "Connections", "Value" : 12}, +{ "Variable_name" : "Client_connections", "Value" : 3}, +{ "Variable_name" : "Backend_connections", "Value" : 0}, +{ "Variable_name" : "Listeners", "Value" : 9}, +{ "Variable_name" : "Zombie_connections", "Value" : 0}, +{ "Variable_name" : "Internal_descriptors", "Value" : 3}, +{ "Variable_name" : "Read_events", "Value" : 469}, +{ "Variable_name" : "Write_events", "Value" : 479}, +{ "Variable_name" : "Hangup_events", "Value" : 12}, +{ "Variable_name" : "Error_events", "Value" : 0}, +{ "Variable_name" : "Accept_events", "Value" : 15}, +{ "Variable_name" : "Event_queue_length", "Value" : 1}, +{ "Variable_name" : "Pending_events", "Value" : 0}, +{ "Variable_name" : "Max_event_queue_length", "Value" : 1}, +{ "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}, - { "Service Name" : "Filter Service", "Router Module" : "readconnroute", "No. Sessions" : 1, "Total Sessions" : 1}, - { "Service Name" : "Named Service", "Router Module" : "readwritesplit", "No. Sessions" : 1, "Total Sessions" : 1}, - { "Service Name" : "QLA Service", "Router Module" : "readconnroute", "No. Sessions" : 1, "Total Sessions" : 1}, - { "Service Name" : "Debug Service", "Router Module" : "debugcli", "No. Sessions" : 1, "Total Sessions" : 1}, - { "Service Name" : "CLI", "Router Module" : "cli", "No. Sessions" : 1, "Total Sessions" : 1}, - { "Service Name" : "MaxInfo", "Router Module" : "maxinfo", "No. Sessions" : 5, "Total Sessions" : 20}] - $ +$ 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}, +{ "Service Name" : "Filter Service", "Router Module" : "readconnroute", "No. Sessions" : 1, "Total Sessions" : 1}, +{ "Service Name" : "Named Service", "Router Module" : "readwritesplit", "No. Sessions" : 1, "Total Sessions" : 1}, +{ "Service Name" : "QLA Service", "Router Module" : "readconnroute", "No. Sessions" : 1, "Total Sessions" : 1}, +{ "Service Name" : "Debug Service", "Router Module" : "debugcli", "No. Sessions" : 1, "Total Sessions" : 1}, +{ "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 @@ -445,17 +445,17 @@ The /services URI returns the data regarding the services defined within the con 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"}, - { "Service Name" : "Filter Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4008, "State" : "Running"}, - { "Service Name" : "Named Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4010, "State" : "Running"}, - { "Service Name" : "QLA Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4009, "State" : "Running"}, - { "Service Name" : "Debug Service", "Protocol Module" : "telnetd", "Address" : "localhost", "Port" : 4242, "State" : "Running"}, - { "Service Name" : "CLI", "Protocol Module" : "maxscaled", "Address" : "localhost", "Port" : 6603, "State" : "Running"}, - { "Service Name" : "MaxInfo", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 9003, "State" : "Running"}, - { "Service Name" : "MaxInfo", "Protocol Module" : "HTTPD", "Address" : "*", "Port" : 8003, "State" : "Running"}] - $ +$ 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"}, +{ "Service Name" : "Filter Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4008, "State" : "Running"}, +{ "Service Name" : "Named Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4010, "State" : "Running"}, +{ "Service Name" : "QLA Service", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 4009, "State" : "Running"}, +{ "Service Name" : "Debug Service", "Protocol Module" : "telnetd", "Address" : "localhost", "Port" : 4242, "State" : "Running"}, +{ "Service Name" : "CLI", "Protocol Module" : "maxscaled", "Address" : "localhost", "Port" : 6603, "State" : "Running"}, +{ "Service Name" : "MaxInfo", "Protocol Module" : "MySQLClient", "Address" : "*", "Port" : 9003, "State" : "Running"}, +{ "Service Name" : "MaxInfo", "Protocol Module" : "HTTPD", "Address" : "*", "Port" : 8003, "State" : "Running"}] +$ ``` ## Modules @@ -463,18 +463,18 @@ The /listeners URI will return a JSON array with one entry per listener, each en 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"}, - { "Module Name" : "telnetd", "Module Type" : "Protocol", "Version" : "V1.0.1", "API Version" : "1.0.0", "Status" : "GA"}, - { "Module Name" : "MySQLClient", "Module Type" : "Protocol", "Version" : "V1.0.0", "API Version" : "1.0.0", "Status" : "GA"}, - { "Module Name" : "mysqlmon", "Module Type" : "Monitor", "Version" : "V1.4.0", "API Version" : "1.0.0", "Status" : "GA"}, - { "Module Name" : "readwritesplit", "Module Type" : "Router", "Version" : "V1.0.2", "API Version" : "1.0.0", "Status" : "GA"}, - { "Module Name" : "readconnroute", "Module Type" : "Router", "Version" : "V1.1.0", "API Version" : "1.0.0", "Status" : "GA"}, - { "Module Name" : "debugcli", "Module Type" : "Router", "Version" : "V1.1.1", "API Version" : "1.0.0", "Status" : "GA"}, - { "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"}] - $ +$ 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"}, +{ "Module Name" : "telnetd", "Module Type" : "Protocol", "Version" : "V1.0.1", "API Version" : "1.0.0", "Status" : "GA"}, +{ "Module Name" : "MySQLClient", "Module Type" : "Protocol", "Version" : "V1.0.0", "API Version" : "1.0.0", "Status" : "GA"}, +{ "Module Name" : "mysqlmon", "Module Type" : "Monitor", "Version" : "V1.4.0", "API Version" : "1.0.0", "Status" : "GA"}, +{ "Module Name" : "readwritesplit", "Module Type" : "Router", "Version" : "V1.0.2", "API Version" : "1.0.0", "Status" : "GA"}, +{ "Module Name" : "readconnroute", "Module Type" : "Router", "Version" : "V1.1.0", "API Version" : "1.0.0", "Status" : "GA"}, +{ "Module Name" : "debugcli", "Module Type" : "Router", "Version" : "V1.1.1", "API Version" : "1.0.0", "Status" : "GA"}, +{ "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 @@ -482,31 +482,31 @@ The /modules URI returns data for each plugin that has been loaded into MaxScale 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"}, - { "Session" : "0x1a8ddd0", "Client" : , "Service" : "MaxInfo", "State" : "Listener Session"}, - { "Session" : "0x1a92da0", "Client" : , "Service" : "MaxInfo", "State" : "Listener Session"}, - { "Session" : "0x1a92ac0", "Client" : , "Service" : "CLI", "State" : "Listener Session"}, - { "Session" : "0x1a70e90", "Client" : , "Service" : "Debug Service", "State" : "Listener Session"}, - { "Session" : "0x1a758d0", "Client" : , "Service" : "QLA Service", "State" : "Listener Session"}, - { "Session" : "0x1a73a90", "Client" : , "Service" : "Named Service", "State" : "Listener Session"}, - { "Session" : "0x1a5c0b0", "Client" : , "Service" : "Filter Service", "State" : "Listener Session"}, - { "Session" : "0x1a5c530", "Client" : , "Service" : "Split Service", "State" : "Listener Session"}, - { "Session" : "0x19ac1c0", "Client" : , "Service" : "Test Service", "State" : "Listener Session"}] - $ +$ 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"}, +{ "Session" : "0x1a8ddd0", "Client" : , "Service" : "MaxInfo", "State" : "Listener Session"}, +{ "Session" : "0x1a92da0", "Client" : , "Service" : "MaxInfo", "State" : "Listener Session"}, +{ "Session" : "0x1a92ac0", "Client" : , "Service" : "CLI", "State" : "Listener Session"}, +{ "Session" : "0x1a70e90", "Client" : , "Service" : "Debug Service", "State" : "Listener Session"}, +{ "Session" : "0x1a758d0", "Client" : , "Service" : "QLA Service", "State" : "Listener Session"}, +{ "Session" : "0x1a73a90", "Client" : , "Service" : "Named Service", "State" : "Listener Session"}, +{ "Session" : "0x1a5c0b0", "Client" : , "Service" : "Filter Service", "State" : "Listener Session"}, +{ "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"}] - $ +$ 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 @@ -514,12 +514,12 @@ The /clients URI is a limited version of the /sessions, in this case it only ret 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"}] - $ +$ 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 @@ -527,35 +527,35 @@ The /servers URI is used to retrieve information for each of the servers defined 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}, - { "Duration" : " 200 - 300ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : " 300 - 400ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : " 400 - 500ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : " 500 - 600ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : " 600 - 700ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : " 700 - 800ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : " 800 - 900ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : " 900 - 1000ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1000 - 1100ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1100 - 1200ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1200 - 1300ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1300 - 1400ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1400 - 1500ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1500 - 1600ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1600 - 1700ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1700 - 1800ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1800 - 1900ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "1900 - 2000ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "2000 - 2100ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "2100 - 2200ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "2200 - 2300ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "2300 - 2400ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "2400 - 2500ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "2500 - 2600ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "Duration" : "2600 - 2700ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, - { "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}] +$ 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}, +{ "Duration" : " 200 - 300ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : " 300 - 400ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : " 400 - 500ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : " 500 - 600ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : " 600 - 700ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : " 700 - 800ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : " 800 - 900ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : " 900 - 1000ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1000 - 1100ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1100 - 1200ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1200 - 1300ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1300 - 1400ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1400 - 1500ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1500 - 1600ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1600 - 1700ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1700 - 1800ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1800 - 1900ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "1900 - 2000ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "2000 - 2100ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "2100 - 2200ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "2200 - 2300ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "2300 - 2400ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "2400 - 2500ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "2500 - 2600ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "Duration" : "2600 - 2700ms", "No. Events Queued" : 0, "No. Events Executed" : 0}, +{ "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-Cluster-Setup.md b/Documentation/Tutorials/MySQL-Cluster-Setup.md index a168fcfd4..08c4c2b73 100644 --- a/Documentation/Tutorials/MySQL-Cluster-Setup.md +++ b/Documentation/Tutorials/MySQL-Cluster-Setup.md @@ -46,67 +46,73 @@ MySQL 5.5.38 as SQL node2 Cluster configuration file is /var/lib/mysql-cluster/config.ini, copied on all servers - [ndbd default] - NoOfReplicas=2 - DataMemory=60M - IndexMemory=16M +``` +[ndbd default] +NoOfReplicas=2 +DataMemory=60M +IndexMemory=16M - [ndb_mgmd] - hostname=178.62.38.199 - id=21 - datadir=/var/lib/mysql-cluster - - [mysqld] - hostname=178.62.38.199 - - [mysqld] - hostname=162.243.90.81 +[ndb_mgmd] +hostname=178.62.38.199 +id=21 +datadir=/var/lib/mysql-cluster - [ndbd] - hostname=178.62.38.199 +[mysqld] +hostname=178.62.38.199 - [ndbd] - hostname=162.243.90.81 +[mysqld] +hostname=162.243.90.81 + +[ndbd] +hostname=178.62.38.199 + +[ndbd] +hostname=162.243.90.81 +``` Note, it’s possible to specify all node ids and datadir as well for each cluster component Example: - [ndbd] - hostname=162.243.90.81 - id=43 - datadir=/usr/local/mysql/data +``` +[ndbd] +hostname=162.243.90.81 +id=43 +datadir=/usr/local/mysql/data +``` and /etc/my.cnf, copied as well in all servers - [mysqld] - ndbcluster - ndb-connectstring=178.62.38.199 - innodb_buffer_pool_size=16M +``` +[mysqld] +ndbcluster +ndb-connectstring=178.62.38.199 +innodb_buffer_pool_size=16M - [mysql_cluster] - ndb-connectstring=178.62.38.199 +[mysql_cluster] +ndb-connectstring=178.62.38.199 +``` ## Startup of MySQL Cluster Each cluster node process must be started separately, and on the host where it resides. The management node should be started first, followed by the data nodes, and then finally by any SQL nodes: - On the management host, server1, issue the following command from the system shell to start the management node process: - - [root@server1 ~]# ndb_mgmd -f /var/lib/mysql-cluster/config.ini - +``` +[root@server1 ~]# ndb_mgmd -f /var/lib/mysql-cluster/config.ini +``` - On each of the data node hosts, run this command to start the ndbd process: +``` +[root@server1 ~]# ndbd —-initial -—initial-start - [root@server1 ~]# ndbd —-initial -—initial-start - - [root@server2 ~]# ndbd —-initial -—initial-start - +[root@server2 ~]# ndbd —-initial -—initial-start +``` - On each SQL node start the MySQL server process: +``` +[root@server1 ~]# /etc/init.d/mysql start - [root@server1 ~]# /etc/init.d/mysql start - - [root@server2 ~]# /etc/init.d/mysql start - +[root@server2 ~]# /etc/init.d/mysql start +``` ## Check the cluster status If all has gone well, and the cluster has been set up correctly, the cluster should now be operational. @@ -114,7 +120,7 @@ If all has gone well, and the cluster has been set up correctly, the cluster sho It’s possible to test this by invoking the ndb_mgm management node client. The output should look like that shown here, although you might see some slight differences in the output depending upon the exact version of MySQL that you are using: - +``` [root@server1 ~]# ndb_mgm -- NDB Cluster -- Management Client -- @@ -127,216 +133,217 @@ Cluster Configuration --------------------- - [ndbd(NDB)] 2 node(s) +[ndbd(NDB)] 2 node(s) - id=24 @178.62.38.199 (mysql-5.5.38 ndb-7.2.17, Nodegroup: 0, *) - - id=25 @162.243.90.81 (mysql-5.5.38 ndb-7.2.17, Nodegroup: 0) - - [ndb_mgmd(MGM)] 1 node(s) - - id=21 @178.62.38.199 (mysql-5.5.38 ndb-7.2.17) - - [mysqld(API)] 2 node(s) +id=24 @178.62.38.199 (mysql-5.5.38 ndb-7.2.17, Nodegroup: 0, *) - id=22 @178.62.38.199 (mysql-5.5.38 ndb-7.2.17) +id=25 @162.243.90.81 (mysql-5.5.38 ndb-7.2.17, Nodegroup: 0) - id=23 @162.243.90.81 (mysql-5.5.38 ndb-7.2.17) +[ndb_mgmd(MGM)] 1 node(s) + +id=21 @178.62.38.199 (mysql-5.5.38 ndb-7.2.17) + +[mysqld(API)] 2 node(s) + +id=22 @178.62.38.199 (mysql-5.5.38 ndb-7.2.17) + +id=23 @162.243.90.81 (mysql-5.5.38 ndb-7.2.17) ndb_mgm> - +``` The SQL node is referenced here as [mysqld(API)], which reflects the fact that the mysqld process is acting as a MySQL Cluster API node. ## Working with NDBCLUSTER engine in MySQL - - First create a table with NDBCLUSTER engine: - +- First create a table with NDBCLUSTER engine: +``` [root@server1 ~]# mysql - mysql> CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL ) ENGINE=NDBCLUSTER; +mysql> CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL ) ENGINE=NDBCLUSTER; - Query OK, 0 rows affected (3.28 sec) +Query OK, 0 rows affected (3.28 sec) - mysql> show create table t1; +mysql> show create table t1; - +------- +-------------------------------------------------------------------------------------------+ - | Table | Create Table | ++------- +-------------------------------------------------------------------------------------------+ +| Table | Create Table | - +-------+-------------------------------------------------------------------------------------------+ ++-------+-------------------------------------------------------------------------------------------+ - | t1 | CREATE TABLE `t1` ( - `a` int(11) DEFAULT NULL +| t1 | CREATE TABLE `t1` ( +`a` int(11) DEFAULT NULL - ) ENGINE=ndbcluster DEFAULT CHARSET=latin1 | +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 | - +-------+-------------------------------------------------------------------------------------------+ ++-------+-------------------------------------------------------------------------------------------+ - 1 row in set (0.01 sec) +1 row in set (0.01 sec) +``` +- Just add a row in the table: +``` +mysql> insert into test.t1 values(11); - - Just add a row in the table: +Query OK, 1 row affected (0.15 sec) +``` +- Select the current number of rows: +``` +mysql> select count(1) from t1; - mysql> insert into test.t1 values(11); - - Query OK, 1 row affected (0.15 sec) - - - Select the current number of rows: - - mysql> select count(1) from t1; - - +----------+ - | count(1) | - +----------+ - | 1 | - +----------+ - - 1 row in set (0.07 sec) ++----------+ +| count(1) | ++----------+ +| 1 | ++----------+ +1 row in set (0.07 sec) +``` - The same from the MySQL client pointing to SQL node on server2 - +``` [root@server2 ~]# mysql - mysql> select count(1) from test.t1; +mysql> select count(1) from test.t1; - +----------+ - | count(1) | - +----------+ - | 1 | - +----------+ - - 1 row in set (0.08 sec) ++----------+ +| count(1) | ++----------+ +| 1 | ++----------+ +1 row in set (0.08 sec) +``` ## Configuring MaxScale for connection load balancing of SQL nodes Add these sections in maxscale.cnf config file: +``` +[Cluster Service] +type=service +router=readconnroute +router_options=ndb +servers=server1,server2 +user=test +passwd=test +version_string=5.5.37-CLUSTER - [Cluster Service] - type=service - router=readconnroute - router_options=ndb - servers=server1,server2 - user=test - passwd=test - version_string=5.5.37-CLUSTER - - [Cluster Listener] - type=listener - service=Cluster Service - protocol=MySQLClient - port=4906 +[Cluster Listener] +type=listener +service=Cluster Service +protocol=MySQLClient +port=4906 - [NDB Cluster Monitor] - type=monitor - module=ndbclustermon - servers=server1,server2 - user=monitor - passwd=monitor - monitor_interval=8000 +[NDB Cluster Monitor] +type=monitor +module=ndbclustermon +servers=server1,server2 +user=monitor +passwd=monitor +monitor_interval=8000 - [server1] +[server1] - #SQL node1 - type=server - address=127.0.0.1 - port=3306 - protocol=MySQLBackend - - [server2] - #SQL node2 - type=server - address=162.243.90.81 - port=3306 - protocol=MySQLBackend +#SQL node1 +type=server +address=127.0.0.1 +port=3306 +protocol=MySQLBackend +[server2] +#SQL node2 +type=server +address=162.243.90.81 +port=3306 +protocol=MySQLBackend +``` Assuming MaxScale is installed in server1, start it +``` +[root@server1 ~]# cd /usr/bin - [root@server1 ~]# cd /usr/bin - - [root@server1 bin]# ./maxscale -c ../ - +[root@server1 bin]# ./maxscale -c ../ +``` Using the debug interface it’s possible to check the status of monitored servers +``` +MaxScale> show monitors - MaxScale> show monitors +Monitor: 0x387b880 - Monitor: 0x387b880 +Name: NDB Cluster Monitor +Monitor running +Sampling interval: 8000 milliseconds +Monitored servers: 127.0.0.1:3306, 162.243.90.81:3306 - Name: NDB Cluster Monitor - Monitor running - Sampling interval: 8000 milliseconds - Monitored servers: 127.0.0.1:3306, 162.243.90.81:3306 +MaxScale> show servers - MaxScale> show servers +Server 0x3873b40 (server1) - Server 0x3873b40 (server1) +Server: 127.0.0.1 +Status: NDB, Running +Protocol: MySQLBackend +Port: 3306 +Server Version: 5.5.38-ndb-7.2.17-cluster-gpl +Node Id: 22 +Master Id: -1 +Repl Depth: 0 +Number of connections: 0 +Current no. of conns: 0 +Current no. of operations: 0 - Server: 127.0.0.1 - Status: NDB, Running - Protocol: MySQLBackend - Port: 3306 - Server Version: 5.5.38-ndb-7.2.17-cluster-gpl - Node Id: 22 - Master Id: -1 - Repl Depth: 0 - Number of connections: 0 - Current no. of conns: 0 - Current no. of operations: 0 - - Server 0x3873a40 (server2) - - Server: 162.243.90.81 - Status: NDB, Running - Protocol: MySQLBackend - Port: 3306 - Server Version: 5.5.38-ndb-7.2.17-cluster-gpl - Node Id: 23 - Master Id: -1 - Repl Depth: 0 - Number of connections: 0 - Current no. of conns: 0 - Current no. of operations: 0 +Server 0x3873a40 (server2) +Server: 162.243.90.81 +Status: NDB, Running +Protocol: MySQLBackend +Port: 3306 +Server Version: 5.5.38-ndb-7.2.17-cluster-gpl +Node Id: 23 +Master Id: -1 +Repl Depth: 0 +Number of connections: 0 +Current no. of conns: 0 +Current no. of operations: 0 +``` It’s now possible to run basic tests with the read connection load balancing for the two configured SQL nodes (1) test MaxScale load balancing requesting the Ndb_cluster_node_id variable: +``` +[root@server1 ~]# mysql -h 127.0.0.1 -P 4906 -u test -ptest -e "SHOW STATUS LIKE 'Ndb_cluster_node_id'" - [root@server1 ~]# mysql -h 127.0.0.1 -P 4906 -u test -ptest -e "SHOW STATUS LIKE 'Ndb_cluster_node_id'" ++---------------------+-------+ +| Variable_name | Value | ++---------------------+-------+ +| Ndb_cluster_node_id | 23 | ++---------------------+-------+ - +---------------------+-------+ - | Variable_name | Value | - +---------------------+-------+ - | Ndb_cluster_node_id | 23 | - +---------------------+-------+ - - [root@server1 ~]# mysql -h 127.0.0.1 -P 4906 -u test -ptest -e "SHOW STATUS LIKE 'Ndb_cluster_node_id'" - - +---------------------+-------+ - | Variable_name | Value | - +---------------------+-------+ - | Ndb_cluster_node_id | 22 | - +---------------------+-------+ +[root@server1 ~]# mysql -h 127.0.0.1 -P 4906 -u test -ptest -e "SHOW STATUS LIKE 'Ndb_cluster_node_id'" ++---------------------+-------+ +| Variable_name | Value | ++---------------------+-------+ +| Ndb_cluster_node_id | 22 | ++---------------------+-------+ +``` The MaxScale connection load balancing is working. (2) test a select statement on an NBDBCLUSTER table, database test and table t1 created before: +``` +[root@server1 ~] mysql -h 127.0.0.1 -P 4906 -utest -ptest -e "SELECT COUNT(1) FROM test.t1" - [root@server1 ~] mysql -h 127.0.0.1 -P 4906 -utest -ptest -e "SELECT COUNT(1) FROM test.t1" - - +----------+ - | COUNT(1) | - +----------+ - | 1 | - +----------+ - ++----------+ +| COUNT(1) | ++----------+ +| 1 | ++----------+ +``` (3) test an insert statement - +``` mysql -h 127.0.0.1 -P 4906 -utest -ptest -e "INSERT INTO test.t1 VALUES (19)" - +``` (4) test again the select and check the number of rows - [root@server1 ~] mysql -h 127.0.0.1 -P 4906 -utest -ptest -e "SELECT COUNT(1) FROM test.t1" - - +----------+ - | COUNT(1) | - +----------+ - | 2 | - +----------+ +``` +[root@server1 ~] mysql -h 127.0.0.1 -P 4906 -utest -ptest -e "SELECT COUNT(1) FROM test.t1" ++----------+ +| COUNT(1) | ++----------+ +| 2 | ++----------+ +``` diff --git a/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md index 93ecdd931..577731b3e 100644 --- a/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md @@ -36,18 +36,23 @@ 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)** +``` 3. Grant select privileges on the mysql.user table. +``` 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)** @@ -56,8 +61,10 @@ 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)** @@ -66,6 +73,7 @@ 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 @@ -74,160 +82,139 @@ MaxScale configuration is held in an ini file that is located in the file maxsca 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 +``` + The first step is to create a service for our Read/Write Splitter. Create a section 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. +``` [Splitter Service] - type=service +``` The router for we need to use for this configuration is the readwritesplit 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. +``` [Splitter Service] - type=service - router=readwritesplit - 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 /var/lib/maxscale/.secrets +``` +maxkeys /var/lib/maxscale/.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. +``` [Splitter Service] - type=service - router=readwritesplit - servers=dbserv1, dbserv2, dbserv3 - user=maxscale - passwd=96F99AA1315BDC3604B006F427DD9484 +``` This completes the definitions required by the service, however listening ports must be associated with the service in order to allow network connections. This is done by creating a series of listener sections. This section again is 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. A service may have multiple listeners. +``` [Splitter Listener] - type=listener - service=Splitter 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. +``` [Splitter Listener] - type=listener - service=Splitter Service - protocol=MySQLClient - port=3306 - socket=/tmp/ClusterMaster +``` 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 behaviour 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 module 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. # Starting MaxScale 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 - +``` or - +``` % service maxscale start - +``` 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 Services. @@ -277,8 +264,8 @@ Splitter Service | MySQLClient | * | 3306 | Running 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". diff --git a/Documentation/Tutorials/Nagios-Plugins.md b/Documentation/Tutorials/Nagios-Plugins.md index 6c8cef9c2..1a90ff74e 100644 --- a/Documentation/Tutorials/Nagios-Plugins.md +++ b/Documentation/Tutorials/Nagios-Plugins.md @@ -45,17 +45,17 @@ 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: +``` +[AdminInterface] +type=service +router=cli - [AdminInterface] - type=service - router=cli - - [AdminListener] - type=listener - service=AdminInterface - protocol=maxscaled - port=6603 - +[AdminListener] +type=listener +service=AdminInterface +protocol=maxscaled +port=6603 +``` ## Prepare Nagios configuration files. Assuming Nagios installed on a separated server and the plugins are in /usr/lib64/nagios/plugins and configuration files are in /etc/nagios: @@ -66,8 +66,10 @@ Assuming Nagios installed on a separated server and the plugins are in /usr/lib6 and add (just after localhost.cfg or commnads.cfg) - cfg_file=/etc/nagios/objects/maxscale_commands.cfg - cfg_file=/etc/nagios/objects/server1.cfg +``` +cfg_file=/etc/nagios/objects/maxscale_commands.cfg +cfg_file=/etc/nagios/objects/server1.cfg +``` ### Please note: - modify server IP address in server1.cfg, pointing to MaxScale server @@ -80,6 +82,7 @@ and add (just after localhost.cfg or commnads.cfg) 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 +``` #Check MaxScale sessions, on the remote machine. define service{ use local-service @@ -88,6 +91,7 @@ In this example we are using the check_maxscale_resource as the check command check_command check_maxscale_resource!6603!admin!mariadb!sessions!/path_to/maxadmin notifications_enabled 0 } +``` ### Check new running monitors * Restart Nagios and check new monitors are running in HTTP Interface "Current Status -> Services" on Nagios Server @@ -143,15 +147,17 @@ In this example we are using the check_maxscale_resource as the check command # Output description: Example for 'services' +``` +#./check_maxscale_resources.pl -r resources - #./check_maxscale_resources.pl -r resources - - OK: 7 services found | services1=RW_Router;readwritesplit;1;1 services2=RW_Split;readwritesplit;1;1 services3=Test Service;readconnroute;1;1 services4=Master Service;readconnroute;2;2 services5=Debug Service;debugcli;1;1 services6=CLI;cli;2;145 services7=MaxInfo;maxinfo;2;2 - +OK: 7 services found | services1=RW_Router;readwritesplit;1;1 services2=RW_Split;readwritesplit;1;1 services3=Test Service;readconnroute;1;1 services4=Master Service;readconnroute;2;2 services5=Debug Service;debugcli;1;1 services6=CLI;cli;2;145 services7=MaxInfo;maxinfo;2;2 +``` Returns OK and the number of services Returns CRITICAL if no services are found The data after | char are so called performance data and may be collected by Nagios output format is: - servicex=Name;router_module;NumUsers;TotalSessions +``` +servicex=Name;router_module;NumUsers;TotalSessions +``` diff --git a/Documentation/Tutorials/Notification-Service.md b/Documentation/Tutorials/Notification-Service.md index 9cc35c03f..060b852c7 100644 --- a/Documentation/Tutorials/Notification-Service.md +++ b/Documentation/Tutorials/Notification-Service.md @@ -34,11 +34,12 @@ It tries to send data and if there is any failure (timeout, server is down, etc) This feature is not enabled by default: MaxScale must be configured in [feedback] section: - - [feedback] - feedback_enable=1 - feedback_url=https://enterprise.mariadb.com/feedback/post - feedback_user_info=x-y-z-w +``` +[feedback] +feedback_enable=1 +feedback_url=https://enterprise.mariadb.com/feedback/post +feedback_user_info=x-y-z-w +``` The activation code that will be provided by MariaDB corp upon request by the customer and it should be put in feedback_user_info. @@ -63,32 +64,33 @@ MaxScale shall send the generated feedback report to a feedback server specified If it’s not possible to send data due to firewall or security settings the report could be generated manually (feedback_user_info is required) via MaxAdmin - +``` MaxScale>show feedbackreport - +``` Report could be saved to report.txt file: - +``` maxadmin -uxxx -pyyy show feedbackreport > ./report.txt curl -F data=@./report.txt https://mariadb.org/feedback_plugin/post - +``` Report Example: - - FEEDBACK_SERVER_UID 6B5C44AEA73137D049B02E6D1C7629EF431A350F - FEEDBACK_USER_INFO 0467009f-b04d-45b1-a77b-b6b2ec9c6cf4 - VERSION 1.0.6-unstable - NOW 1425914890 - PRODUCT maxscale - Uname_sysname Linux - Uname_distribution CentOS release 6.5 (Final) - module_maxscaled_type Protocol - module_maxscaled_version V1.0.0 - module_maxscaled_api 1.0.0 - module_maxscaled_releasestatus GA - module_telnetd_type Protocol - module_telnetd_version V1.0.1 - module_telnetd_api 1.0.0 - module_telnetd_releasestatus GA +``` +FEEDBACK_SERVER_UID 6B5C44AEA73137D049B02E6D1C7629EF431A350F +FEEDBACK_USER_INFO 0467009f-b04d-45b1-a77b-b6b2ec9c6cf4 +VERSION 1.0.6-unstable +NOW 1425914890 +PRODUCT maxscale +Uname_sysname Linux +Uname_distribution CentOS release 6.5 (Final) +module_maxscaled_type Protocol +module_maxscaled_version V1.0.0 +module_maxscaled_api 1.0.0 +module_maxscaled_releasestatus GA +module_telnetd_type Protocol +module_telnetd_version V1.0.1 +module_telnetd_api 1.0.0 +module_telnetd_releasestatus GA +``` diff --git a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md index c66d8c1cb..5047731e8 100644 --- a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md +++ b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md @@ -10,31 +10,31 @@ The software install setup provides RPM and DEB packaging and traditional compil On Centos 6.5 using fedora / RHEL rpm get the rpm from [http://www.rabbitmq.com/](http://www.rabbitmq.com/ "RabbitMQ") - rabbitmq-server-3.3.4-1.noarch.rpm +rabbitmq-server-3.3.4-1.noarch.rpm Please note, before installing RabbitMQ, you must install Erlang. Example: - yum install erlang - Package erlang-R14B-04.3.el6.x86_64 already installed and latest version +yum install erlang +Package erlang-R14B-04.3.el6.x86_64 already installed and latest version ## Step 2 - Install and Start the Server Install the packages using your distribution's package manager and start the server: - yum install rabbitmq-server-3.3.4-1.noarch.rpm - systemctl start rabbitmq-server.service +yum install rabbitmq-server-3.3.4-1.noarch.rpm +systemctl start rabbitmq-server.service To configure your RabbitMQ server, please refer to the RabbitMQ website: [http://www.rabbitmq.com/](http://www.rabbitmq.com/ RabbitMQ website). rabbitmqctl is a command line tool for managing a RabbitMQ broker. It performs all actions by connecting to one of the broker's nodes. - - rabbitmqctl list_queues - rabbitmqctl list_queues | list_exchanges| cluster_status | list_bindings | list_connections | list_consumers | status - +``` +rabbitmqctl list_queues +rabbitmqctl list_queues | list_exchanges| cluster_status | list_bindings | list_connections | list_consumers | status +``` Example output: - +``` [root@maxscale-02 MaxScale]# rabbitmqctl status Status of node 'rabbit@maxscale-02' ... [{pid,12251}, @@ -57,13 +57,13 @@ Example output: Listing bindings ... x1 exchange q1 queue k1 [] ...done. - +``` Interaction with the server may require stop & reset at some point: - - rabbitmqctl stop_app - rabbitmqctl reset - rabbitmqctl start_app - +``` +rabbitmqctl stop_app +rabbitmqctl reset +rabbitmqctl start_app +``` ## Step 3 - Install and test the client libraries The selected library for MaxScale integration of RabbitMQ is: @@ -72,13 +72,13 @@ The selected library for MaxScale integration of RabbitMQ is: ### Manual software compilation To compile the RabbitMQ-C libraries manually: - - git clone https://github.com/alanxz/rabbitmq-c.git - cd rabbitmq-c - cmake -DCMAKE_INSTALL_PREFIX=/usr . - make - make install - +``` +git clone https://github.com/alanxz/rabbitmq-c.git +cd rabbitmq-c +cmake -DCMAKE_INSTALL_PREFIX=/usr . +make +make install +``` Please note, this will install the packages to /usr. If you do not wish to install them to this location, provide a different value for the CMAKE_INSTALL_PREFIX variable. @@ -87,19 +87,17 @@ Please note, this will install the packages to /usr. If you do not wish to insta Check how to configure your distribution for the EPEL repository: [https://fedoraproject.org/wiki/EPEL](https://fedoraproject.org/wiki/EPEL EPEL) Configure your repositories and install the software: - - yum install librabbitmq.x86_64 - +``` +yum install librabbitmq.x86_64 +``` you might also like to install: - - librabbitmq-tools.x86_64, librabbitmq-devel.x86_64 - +``` +librabbitmq-tools.x86_64, librabbitmq-devel.x86_64 +``` Please note you may also install the rabbitmq server from the EPEL repository: - - yum install rabbitmq-server - - - +``` +yum install rabbitmq-server +``` ### Basic tests with library @@ -108,150 +106,146 @@ The required library librabbitmq-c is now installed and we continue with basic o Please note, those example applications may not be included in the RPM library packages. #### Test 1 - create the exchange - - [root@maxscale-02 examples]# ./amqp_exchange_declare - Usage: amqp_exchange_declare host port exchange exchangetype - +``` +[root@maxscale-02 examples]# ./amqp_exchange_declare +Usage: amqp_exchange_declare host port exchange exchangetype +``` Declare the exchange: - - [root@maxscale-02 examples]# ./amqp_exchange_declare 127.0.0.1 5672 foo direct - +``` +[root@maxscale-02 examples]# ./amqp_exchange_declare 127.0.0.1 5672 foo direct +``` #### Test 2 - Listen to exchange with selected binding key - - [root@maxscale-02 examples]# ./amqp_listen - Usage: amqp_listen host port exchange bindingkey - +``` +[root@maxscale-02 examples]# ./amqp_listen +Usage: amqp_listen host port exchange bindingkey +``` Start the listener: - - [root@maxscale-02 examples]# ./amqp_listen 127.0.0.1 5672 foo k1 & - +``` +[root@maxscale-02 examples]# ./amqp_listen 127.0.0.1 5672 foo k1 & +``` #### Test 3 - Send a message … - - [root@maxscale-02 examples]# ./amqp_sendstring - Usage: amqp_sendstring host port exchange routingkey messagebody - - [root@maxscale-02 examples]# ./amqp_sendstring 127.0.0.1 5672 foo k1 “This is a new message” - +``` +[root@maxscale-02 examples]# ./amqp_sendstring +Usage: amqp_sendstring host port exchange routingkey messagebody +``` +[root@maxscale-02 examples]# ./amqp_sendstring 127.0.0.1 5672 foo k1 “This is a new message” +``` ... and watch the listener output - - Delivery 1, exchange foo routingkey k1 - Content-type: text/plain - +``` +Delivery 1, exchange foo routingkey k1 +Content-type: text/plain +``` ## Step 4 - MaxScale integration with librabbitmq-c A new filter (mqfilter.c) is implemented in order to send messages to the rabbitmq server and a message consumer (rabbitmq_consumer/consumer.c) program will get messages and store them into a MySQL/MariaDB database. A quick way to install MaxScale with the RabbitMQ filter is to go to the MaxScale source directory and run the following commands: - - mkdir build - cd build - cmake .. -DBUILD_RABBITMQ=Y - make - make install - +``` +mkdir build +cd build +cmake .. -DBUILD_RABBITMQ=Y +make +make install +``` To build the RabbitMQ filter CMake needs an additional parameter: - - -DBUILD_RABBITMQ=Y - +``` +-DBUILD_RABBITMQ=Y +``` If the librabbitmq-c library is manually compiled it may be necessary to manually pass the location of the libraries and header files to CMake. Libraries: - - -DRABBITMQ_LIBRARIES= - +``` +-DRABBITMQ_LIBRARIES= +``` Headers: - - -DRABBITMQ_HEADERS= - +``` +-DRABBITMQ_HEADERS= +``` Please note, Message Queue Consumer (consumer.c) also needs to be compiled with MySQL/MariaDB client libraries in addition to the RabbitMQ-c libraries. If you have your MySQL/MariaDB client libraries and headers in non-standard locations, you can pass them manually to CMake: Libraries: - - -DMYSQLCLIENT_LIBRARIES= - +``` +-DMYSQLCLIENT_LIBRARIES= +``` Headers: - - -DMYSQLCLIENT_HEADERS= - +``` +-DMYSQLCLIENT_HEADERS= +``` The message queue consumer must be also built as a separate task, it’s not built as part of MaxScale build system. To build it, run the following commands in the rabbitmq_consumer directory in the MaxScale source folder: - - mkdir build - cd build - cmake .. - make - +``` +mkdir build +cd build +cmake .. +make +``` To install it: - - make install - +``` +make install +``` To build packages: - - make package - +``` +make package +``` This generates RPM or DEB packages based on your system. These packages can then be installed on remote systems for easy access to the data generated by the consumer client. ## Step 5 - Configure new applications The new filter needs to be configured in maxscale.cnf. +``` +[Test Service] +type=service +router=readconnroute +router_options=slave +servers=server1,server2,server3,server5,server4 +user=massi +passwd=massi +filters=MQ - [Test Service] - type=service - router=readconnroute - router_options=slave - servers=server1,server2,server3,server5,server4 - user=massi - passwd=massi - filters=MQ - - [MQ] - type=filter - module=mqfilter - exchange=x1 - key=k1 - queue=q1 - hostname=127.0.0.1 - port=5672 - logging_trigger=all - - +[MQ] +type=filter +module=mqfilter +exchange=x1 +key=k1 +queue=q1 +hostname=127.0.0.1 +port=5672 +logging_trigger=all +``` Logging triggers define whether to log all or a subset of the incoming queries using these options: +``` +# log only some elements or all +logging_trigger=[all,source,schema,object] - # log only some elements or all - logging_trigger=[all,source,schema,object] +# Whether to log only SELECT, UPDATE, INSERT and DELETE queries or all possible queries +logging_log_all=true|false - # Whether to log only SELECT, UPDATE, INSERT and DELETE queries or all possible queries - logging_log_all=true|false +# Log only when any of the trigger parameters match or only if all parameters match +logging_strict=true|false +# specify objects +logging_object=mytable,another_table +# specify logged users +logging_source_user=testuser,testuser - # Log only when any of the trigger parameters match or only if all parameters match - logging_strict=true|false - - # specify objects - logging_object=mytable,another_table - - # specify logged users - logging_source_user=testuser,testuser - - - # specify source addresses - logging_source_host=127.0.0.1,192.168.10.14 - - # specify schemas - logging_schema=employees,orders,catalog +# specify source addresses +logging_source_host=127.0.0.1,192.168.10.14 +# specify schemas +logging_schema=employees,orders,catalog +``` Example: - - logging_trigger=object,schema,source - logging_strict=false - logging_log_all=false - logging_object=my1 - logging_schema=test - logging_source_user=maxtest - +``` +logging_trigger=object,schema,source +logging_strict=false +logging_log_all=false +logging_object=my1 +logging_schema=test +logging_source_user=maxtest +``` @@ -259,117 +253,118 @@ Example: The logging result of the example is: - - if user maxtest does something, it's logged - and all queries in test schema are logged - anything targeting my1 table is logged - SELECT NOW(), SELECT MD5(“xyz)” are not logged - +``` +if user maxtest does something, it's logged +and all queries in test schema are logged +anything targeting my1 table is logged +SELECT NOW(), SELECT MD5(“xyz)” are not logged +``` Please note that if we want to log only the user ‘maxtest’ accessing the schema ‘test’ with target ‘my1’ the option logging_strict must be set to TRUE and if we want to include those selects without schema name the option logging_log_all must be set to TRUE. The mqfilter logs into the MaxScale TRACE log information about the matched logging triggers and the message delivering: - - 2014 09/03 06:22:04 Trigger is TRG_SOURCE: user: testuser = testuser - 2014 09/03 06:22:04 Trigger is TRG_SCHEMA: test = test - 2014 09/03 06:22:04 Trigger is TRG_OBJECT: test.t1 = t1 - 2014 09/03 06:22:04 Routing message to: 127.0.0.1:5672 / as guest/guest, exchange: x1 key:k1 queue:q1 - +``` +2014 09/03 06:22:04 Trigger is TRG_SOURCE: user: testuser = testuser +2014 09/03 06:22:04 Trigger is TRG_SCHEMA: test = test +2014 09/03 06:22:04 Trigger is TRG_OBJECT: test.t1 = t1 +2014 09/03 06:22:04 Routing message to: 127.0.0.1:5672 / as guest/guest, exchange: x1 key:k1 queue:q1 +``` The consumer application needs to be configured as well: +``` + +#The options for the consumer are: +#hostname RabbitMQ hostname +#port RabbitMQ port +#vhost RabbitMQ virtual host +#user RabbitMQ username +#passwd RabbitMQ password - #The options for the consumer are: - #hostname RabbitMQ hostname - #port RabbitMQ port - #vhost RabbitMQ virtual host - #user RabbitMQ username - #passwd RabbitMQ password - - - #queue Name of the queue to use - #dbserver SQL server name - #dbport SQL server port - #dbname Name of the database to use - #dbuser SQL server username - #dbpasswd SQL server password - #logfile Message log filename - - [consumer] - hostname=127.0.0.1 - port=5672 - vhost=/ - user=guest - passwd=guest - queue=q1 - dbserver=127.0.0.1 - dbport=3308 - dbname=mqpairs - dbuser=xxx - dbpasswd=yyy +#queue Name of the queue to use +#dbserver SQL server name +#dbport SQL server port +#dbname Name of the database to use +#dbuser SQL server username +#dbpasswd SQL server password +#logfile Message log filename +[consumer] +hostname=127.0.0.1 +port=5672 +vhost=/ +user=guest +passwd=guest +queue=q1 +dbserver=127.0.0.1 +dbport=3308 +dbname=mqpairs +dbuser=xxx +dbpasswd=yyy +``` We may probably need to modify LD_LIBRARY_PATH before launching ‘consumer’: - - # export LD_LIBRARY_PATH=/packages/rabbitmq-c/rabbitmq-c/librabbitmq:/packages/mariadb_client-2.0.0-Linux/lib/mariadb:/usr/lib64 - +``` +# export LD_LIBRARY_PATH=/packages/rabbitmq-c/rabbitmq-c/librabbitmq:/packages/mariadb_client-2.0.0-Linux/lib/mariadb:/usr/lib64 +``` and finally we can launch it: - - # ./consumer - +``` +# ./consumer +``` If the consumer.cnf file is not in the same directory as the binary file is, you can provide the location of the folder that it is in by passing it the -c flag followed by the path: - # ./consumer -c path/to/file +# ./consumer -c path/to/file and start maxScale as well ## Step 6 - Test the filter and check collected data Assuming that MaxScale and the message consumer are successfully running let’s connect to the service with an active mqfilter: +``` +[root@maxscale-02 MaxScale]# mysql -h 127.0.0.1 -P 4506 -uxxx -pyyy +... +MariaDB [(none)]> select RAND(3), RAND(5); ++--------------------+---------------------+ +| RAND(3) | RAND(5) | ++--------------------+---------------------+ +| 0.9057697559760601 | 0.40613597483014313 | ++--------------------+---------------------+ +1 row in set (0.01 sec) + +… +MariaDB [(none)]> select RAND(3544), RAND(11); +``` + - [root@maxscale-02 MaxScale]# mysql -h 127.0.0.1 -P 4506 -uxxx -pyyy - ... - MariaDB [(none)]> select RAND(3), RAND(5); - +--------------------+---------------------+ - | RAND(3) | RAND(5) | - +--------------------+---------------------+ - | 0.9057697559760601 | 0.40613597483014313 | - +--------------------+---------------------+ - 1 row in set (0.01 sec) - - … - MariaDB [(none)]> select RAND(3544), RAND(11); - - - we can check the consumer output in the terminal where it was started: - - -------------------------------------------------------------- - Received: 1409671452|select @@version_comment limit ? - Received: 1409671452|Columns: 1 - ... - Received: 1409671477|select RAND(?), RAND(?) - Received: 1409671477|Columns: 2 - - We query now the database for the content collected so far: - - MariaDB [(none)]> use mqpairs; - Database changed +``` +-------------------------------------------------------------- +Received: 1409671452|select @@version_comment limit ? +Received: 1409671452|Columns: 1 +... +Received: 1409671477|select RAND(?), RAND(?) +Received: 1409671477|Columns: 2 + +We query now the database for the content collected so far: + +MariaDB [(none)]> use mqpairs; +Database changed - - MariaDB [mqpairs]> select * from pairs; - - +-------------------------------------+----------------------------------+------------+---------------------+---------------------+---------+ - | tag | query | reply | date_in | date_out | counter | - +-------------------------------------+----------------------------------+------------+---------------------+---------------------+---------+ - | 006c006d006e006f007000710072007374 | select @@version_comment limit ? | Columns: 1 | 2014-09-02 11:14:51 | 2014-09-02 11:26:38 | 3 | - | 00750076007700780079007a007b007c7d | SELECT DATABASE() | Columns: 1 | 2014-09-02 11:14:56 | 2014-09-02 11:27:06 | 3 | - | 007e007f00800081008200830084008586 | show databases | Columns: 1 | 2014-09-02 11:14:56 | 2014-09-02 11:27:06 | 3 | - | 008700880089008a008b008c008d008e8f | show tables | Columns: 1 | 2014-09-02 11:14:56 | 2014-09-02 11:27:06 | 3 | - | 0090009100920093009400950096009798 | select * from mqpairs.pairs | Columns: 6 | 2014-09-02 11:15:00 | 2014-09-02 11:27:00 | 12 | - | 00fc00fd00fe00ff0100010101020103104 | select NOW() | Columns: 1 | 2014-09-02 11:24:23 | 2014-09-02 11:24:23 | 1 | - | 01050106010701080109010a010b010c10d | select RAND(?), RAND(?) | Columns: 2 | 2014-09-02 11:24:37 | 2014-09-02 11:24:37 | 1 | - +-------------------------------------+----------------------------------+------------+---------------------+---------------------+---------+ - 7 rows in set (0.01 sec) - + +MariaDB [mqpairs]> select * from pairs; + ++-------------------------------------+----------------------------------+------------+---------------------+---------------------+---------+ +| tag | query | reply | date_in | date_out | counter | ++-------------------------------------+----------------------------------+------------+---------------------+---------------------+---------+ +| 006c006d006e006f007000710072007374 | select @@version_comment limit ? | Columns: 1 | 2014-09-02 11:14:51 | 2014-09-02 11:26:38 | 3 | +| 00750076007700780079007a007b007c7d | SELECT DATABASE() | Columns: 1 | 2014-09-02 11:14:56 | 2014-09-02 11:27:06 | 3 | +| 007e007f00800081008200830084008586 | show databases | Columns: 1 | 2014-09-02 11:14:56 | 2014-09-02 11:27:06 | 3 | +| 008700880089008a008b008c008d008e8f | show tables | Columns: 1 | 2014-09-02 11:14:56 | 2014-09-02 11:27:06 | 3 | +| 0090009100920093009400950096009798 | select * from mqpairs.pairs | Columns: 6 | 2014-09-02 11:15:00 | 2014-09-02 11:27:00 | 12 | +| 00fc00fd00fe00ff0100010101020103104 | select NOW() | Columns: 1 | 2014-09-02 11:24:23 | 2014-09-02 11:24:23 | 1 | +| 01050106010701080109010a010b010c10d | select RAND(?), RAND(?) | Columns: 2 | 2014-09-02 11:24:37 | 2014-09-02 11:24:37 | 1 | ++-------------------------------------+----------------------------------+------------+---------------------+---------------------+---------+ +7 rows in set (0.01 sec) +``` The filter send queries to the RabbitMQ server in the canonical format, i.e select RAND(?), RAND(?). The queries Message Queue Consumer application gets from the server are stored with a counter that quickly shows how many times that normalized query was received: - - | 01050106010701080109010a010b010c10d | select RAND(?), RAND(?) | Columns: 2 | 2014-09-02 11:24:37 | 2014-09-02 11:29:15 | 3 | +``` +| 01050106010701080109010a010b010c10d | select RAND(?), RAND(?) | Columns: 2 | 2014-09-02 11:24:37 | 2014-09-02 11:29:15 | 3 | +``` diff --git a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md index cc9b40061..3202b7c58 100644 --- a/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md +++ b/Documentation/Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md @@ -29,22 +29,22 @@ 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. +``` - - [Replication] - type=service - router=binlogrouter - +[Replication] +type=service +router=binlogrouter +``` Other standard service parameters need to be given in the configuration section that are used to retrieve the set of users from the backend (master) database, also a version string can be given such that the MaxScale instance will report this version string to the slave servers that connect to MaxScale. The master server entry must also be given. In the current implementation of the router only a single server can be given. - - [Replication] - type=service - router=binlogrouter - servers=masterdb - version_string=5.6.17-log - user=maxscale - passwd=Mhu87p2D - +``` +[Replication] +type=service +router=binlogrouter +servers=masterdb +version_string=5.6.17-log +user=maxscale +passwd=Mhu87p2D +``` The user and passwd entries in the above example are used in order for MaxScale to populate the credential information that is required to allow the slaves to connect to MaxScale. This user should be configured in exactly the same way a for any other MaxScale service, i.e. the user needs access to the mysql.user table and the mysql.db table as well as having the ability to perform a SHOW DATABASES command. The final configuration requirement is the router specific options. The binlog router requires a set of parameters to be passed, these are passed in the router_options parameter of the service definition as a comma separated list of name value pairs. @@ -62,10 +62,10 @@ As with uuid, MaxScale must have a unique server-id for the connection it makes This is the user name that MaxScale uses when it connects to the master. This user name must have the rights required for replication as with any other user that a slave uses for replication purposes. If the user parameter is not given in the router options then the same user as is used to retrieve the credential information will be used for the replication connection, i.e. the user in the service entry. The user that is used for replication, either defined using the user= option in the router options or using the username and password defined of the service must be granted replication privileges on the database server. - - MariaDB> CREATE USER 'repl'@'maxscalehost' IDENTIFIED by 'password'; - MariaDB> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'maxscalehost'; - +``` +MariaDB> CREATE USER 'repl'@'maxscalehost' IDENTIFIED by 'password'; +MariaDB> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'maxscalehost'; +``` ### password The password of the above user. If the password is not explicitly given then the password in the service entry will be used. For compatibility with other username and password definitions within the MaxScale configuration file it is also possible to use the parameter passwd=. @@ -95,45 +95,45 @@ This defines the value of the heartbeat interval in seconds for the connection t This parameter is used to define the maximum amount of data that will be sent to a slave by MaxScale when that slave is lagging behind the master. In this situation the slave is said to be in "catchup mode", this parameter is designed to both prevent flooding of that slave and also to prevent threads within MaxScale spending disproportionate amounts of time with slaves that are lagging behind the master. The burst size can be defined in Kb, Mb or Gb by adding the qualifier K, M or G to the number given. The default value of burstsize is 1Mb and will be used if burstsize is not given in the router options. A complete example of a service entry for a binlog router service would be as follows. - - [Replication] - type=service - router=binlogrouter - servers=masterdb - version_string=5.6.17-log - router_options=uuid=f12fcb7f-b97b-11e3-bc5e-0401152c4c22,server-id=3,user=repl,password=slavepass,master-id=1,filestem=mybin,heartbeat=30,binlogdir=/var/binlogs - user=maxscale - passwd=Mhu87p2D - +``` +[Replication] +type=service +router=binlogrouter +servers=masterdb +version_string=5.6.17-log +router_options=uuid=f12fcb7f-b97b-11e3-bc5e-0401152c4c22,server-id=3,user=repl,password=slavepass,master-id=1,filestem=mybin,heartbeat=30,binlogdir=/var/binlogs +user=maxscale +passwd=Mhu87p2D +``` The minimum set of router options that must be given in the configuration are are server-id and aster-id, default values may be used for all other options. ## Listener Section As per any service in MaxScale a listener section is required to define the address, port and protocol that is used to listen for incoming connections. In this case those incoming connections will originate from the slave servers. - - [Replication Listener] - type=listener - service=Replication - protocol=MySQLClient - port=5308 - +``` +[Replication Listener] +type=listener +service=Replication +protocol=MySQLClient +port=5308 +``` The protocol used by slaves for connection to MaxScale is the same MySQLClient protocol that is used for client applications to connect to databases, therefore the same MaxScale protocol module can be used. ## Master Server Section The master server is defined in a section within the MaxScale configuration file in the same way as any other server. The protocol that is used is the same backend protocol as is used in other configurations. - - [masterdb] - type=server - address=178.62.50.70 - port=3306 - protocol=MySQLBackend - +``` +[masterdb] +type=server +address=178.62.50.70 +port=3306 +protocol=MySQLBackend +``` # MaxScale replication diagnostics The binlog router module of MaxScale produces diagnostic output that can be viewed via the `maxadmin` client application. Running the maxadmin command and issuing a show service command will produce a considerable amount of output that will show both the master connection status and statistics and also a block for each of the slaves currently connected. - - -bash-4.1$ maxadmin show service Replication +``` +-bash-4.1$ maxadmin show service Replication Service 0x1567ef0 Service: Replication Router: binlogrouter (0x7f4ceb96a820) @@ -207,9 +207,8 @@ The binlog router module of MaxScale produces diagnostic output that can be view Users data: 0x156c030 Total connections: 2 Currently connected: 2 - -bash-4.1$ - - +-bash-4.1$ +``` # Binlog router compatibility @@ -224,13 +223,13 @@ Binlog Router currently does not work for MySQL 5.5 due to missing @@global.binl # Slave servers setup Examples of CHANGE MASTER TO command issued on a slave server that wants to gets replication events from MaxScale binlog router: +``` +CHANGE MASTER TO MASTER_HOST=‘$maxscale_IP’, MASTER_PORT=5308, MASTER_USER='repl', MASTER_PASSWORD=‘somepasswd’, +MASTER_LOG_FILE=‘mysql-bin.000001' - CHANGE MASTER TO MASTER_HOST=‘$maxscale_IP’, MASTER_PORT=5308, MASTER_USER='repl', MASTER_PASSWORD=‘somepasswd’, - MASTER_LOG_FILE=‘mysql-bin.000001' - - CHANGE MASTER TO MASTER_HOST=‘$maxscale_IP’, MASTER_PORT=5308, MASTER_USER='repl', MASTER_PASSWORD=‘somepasswd’, - MASTER_LOG_FILE=‘mysql-bin.000159', MASTER_LOG_POS=245 - +CHANGE MASTER TO MASTER_HOST=‘$maxscale_IP’, MASTER_PORT=5308, MASTER_USER='repl', MASTER_PASSWORD=‘somepasswd’, +MASTER_LOG_FILE=‘mysql-bin.000159', MASTER_LOG_POS=245 +``` The latter example specifies a MASTER_LOG_POS for the selected MASTER_LOG_FILE Note: @@ -242,31 +241,31 @@ Note: - Latest binlog file name and pos in MaxScale could be find via maxadmin output or from mysql client connected to MaxScale: Example: - - -bash-4.1$ mysql -h 127.0.0.1 -P 5308 -u$user -p$pass +``` +-bash-4.1$ mysql -h 127.0.0.1 -P 5308 -u$user -p$pass MySQL [(none)]> show master status\G *************************** 1. row *************************** File: mysql-bin.000181 Position: 2569 - +``` # Enabling MariaDB 10 compatibility MariaDB 10 has different slave registration phase so an option is required: - - router_options=...., mariadb10-compatibility=1 - +``` +router_options=...., mariadb10-compatibility=1 +``` version_string should be modified in order to present MariaDB 10 version when MaxScale sends server handshake packet. - - version_string=10.0.17-log - +``` +version_string=10.0.17-log +``` # New MariaDB events in Diagnostics With a MariaDB 10 setups new events are displayed when master server is MariaDB 10. - - MariaDB 10 Annotate Rows Event 0 - MariaDB 10 Binlog Checkpoint Event 0 - MariaDB 10 GTID Event 0 - MariaDB 10 GTID List Event 0 - +``` +MariaDB 10 Annotate Rows Event 0 +MariaDB 10 Binlog Checkpoint Event 0 +MariaDB 10 GTID Event 0 +MariaDB 10 GTID List Event 0 +``` From b8216aff4c0f55532ea4098981eb602b815676e4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 7 Sep 2015 13:48:27 +0300 Subject: [PATCH 63/74] More format fixes. --- ...RabbitMQ-Setup-And-MaxScale-Integration.md | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md index 5047731e8..7ffff76c2 100644 --- a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md +++ b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md @@ -29,11 +29,14 @@ systemctl start rabbitmq-server.service To configure your RabbitMQ server, please refer to the RabbitMQ website: [http://www.rabbitmq.com/](http://www.rabbitmq.com/ RabbitMQ website). rabbitmqctl is a command line tool for managing a RabbitMQ broker. It performs all actions by connecting to one of the broker's nodes. + ``` rabbitmqctl list_queues rabbitmqctl list_queues | list_exchanges| cluster_status | list_bindings | list_connections | list_consumers | status ``` + Example output: + ``` [root@maxscale-02 MaxScale]# rabbitmqctl status Status of node 'rabbit@maxscale-02' ... @@ -58,12 +61,15 @@ Example output: x1 exchange q1 queue k1 [] ...done. ``` + Interaction with the server may require stop & reset at some point: + ``` rabbitmqctl stop_app rabbitmqctl reset rabbitmqctl start_app ``` + ## Step 3 - Install and test the client libraries The selected library for MaxScale integration of RabbitMQ is: @@ -72,6 +78,7 @@ The selected library for MaxScale integration of RabbitMQ is: ### Manual software compilation To compile the RabbitMQ-C libraries manually: + ``` git clone https://github.com/alanxz/rabbitmq-c.git cd rabbitmq-c @@ -79,6 +86,7 @@ cmake -DCMAKE_INSTALL_PREFIX=/usr . make make install ``` + Please note, this will install the packages to /usr. If you do not wish to install them to this location, provide a different value for the CMAKE_INSTALL_PREFIX variable. @@ -87,14 +95,19 @@ Please note, this will install the packages to /usr. If you do not wish to insta Check how to configure your distribution for the EPEL repository: [https://fedoraproject.org/wiki/EPEL](https://fedoraproject.org/wiki/EPEL EPEL) Configure your repositories and install the software: + ``` yum install librabbitmq.x86_64 ``` + you might also like to install: + ``` librabbitmq-tools.x86_64, librabbitmq-devel.x86_64 ``` + Please note you may also install the rabbitmq server from the EPEL repository: + ``` yum install rabbitmq-server ``` @@ -106,31 +119,42 @@ The required library librabbitmq-c is now installed and we continue with basic o Please note, those example applications may not be included in the RPM library packages. #### Test 1 - create the exchange + ``` [root@maxscale-02 examples]# ./amqp_exchange_declare Usage: amqp_exchange_declare host port exchange exchangetype ``` + Declare the exchange: + ``` [root@maxscale-02 examples]# ./amqp_exchange_declare 127.0.0.1 5672 foo direct ``` + #### Test 2 - Listen to exchange with selected binding key + ``` [root@maxscale-02 examples]# ./amqp_listen Usage: amqp_listen host port exchange bindingkey ``` + Start the listener: + ``` [root@maxscale-02 examples]# ./amqp_listen 127.0.0.1 5672 foo k1 & ``` + #### Test 3 - Send a message … + ``` [root@maxscale-02 examples]# ./amqp_sendstring Usage: amqp_sendstring host port exchange routingkey messagebody -``` + [root@maxscale-02 examples]# ./amqp_sendstring 127.0.0.1 5672 foo k1 “This is a new message” ``` + ... and watch the listener output + ``` Delivery 1, exchange foo routingkey k1 Content-type: text/plain @@ -140,6 +164,7 @@ Content-type: text/plain A new filter (mqfilter.c) is implemented in order to send messages to the rabbitmq server and a message consumer (rabbitmq_consumer/consumer.c) program will get messages and store them into a MySQL/MariaDB database. A quick way to install MaxScale with the RabbitMQ filter is to go to the MaxScale source directory and run the following commands: + ``` mkdir build cd build @@ -147,17 +172,23 @@ cmake .. -DBUILD_RABBITMQ=Y make make install ``` + To build the RabbitMQ filter CMake needs an additional parameter: + ``` -DBUILD_RABBITMQ=Y ``` + If the librabbitmq-c library is manually compiled it may be necessary to manually pass the location of the libraries and header files to CMake. Libraries: + ``` -DRABBITMQ_LIBRARIES= ``` + Headers: + ``` -DRABBITMQ_HEADERS= ``` @@ -165,33 +196,44 @@ Headers: Please note, Message Queue Consumer (consumer.c) also needs to be compiled with MySQL/MariaDB client libraries in addition to the RabbitMQ-c libraries. If you have your MySQL/MariaDB client libraries and headers in non-standard locations, you can pass them manually to CMake: Libraries: + ``` -DMYSQLCLIENT_LIBRARIES= ``` + Headers: + ``` -DMYSQLCLIENT_HEADERS= ``` + The message queue consumer must be also built as a separate task, it’s not built as part of MaxScale build system. To build it, run the following commands in the rabbitmq_consumer directory in the MaxScale source folder: + ``` mkdir build cd build cmake .. make ``` + To install it: + ``` make install ``` + To build packages: + ``` make package ``` + This generates RPM or DEB packages based on your system. These packages can then be installed on remote systems for easy access to the data generated by the consumer client. ## Step 5 - Configure new applications The new filter needs to be configured in maxscale.cnf. + ``` [Test Service] type=service @@ -214,6 +256,7 @@ logging_trigger=all ``` Logging triggers define whether to log all or a subset of the incoming queries using these options: + ``` # log only some elements or all logging_trigger=[all,source,schema,object] @@ -238,6 +281,7 @@ logging_schema=employees,orders,catalog ``` Example: + ``` logging_trigger=object,schema,source logging_strict=false @@ -247,28 +291,29 @@ logging_schema=test logging_source_user=maxtest ``` - - - - - The logging result of the example is: + ``` if user maxtest does something, it's logged and all queries in test schema are logged anything targeting my1 table is logged SELECT NOW(), SELECT MD5(“xyz)” are not logged ``` + Please note that if we want to log only the user ‘maxtest’ accessing the schema ‘test’ with target ‘my1’ the option logging_strict must be set to TRUE and if we want to include those selects without schema name the option logging_log_all must be set to TRUE. The mqfilter logs into the MaxScale TRACE log information about the matched logging triggers and the message delivering: + ``` 2014 09/03 06:22:04 Trigger is TRG_SOURCE: user: testuser = testuser 2014 09/03 06:22:04 Trigger is TRG_SCHEMA: test = test 2014 09/03 06:22:04 Trigger is TRG_OBJECT: test.t1 = t1 2014 09/03 06:22:04 Routing message to: 127.0.0.1:5672 / as guest/guest, exchange: x1 key:k1 queue:q1 + ``` + The consumer application needs to be configured as well: + ``` #The options for the consumer are: @@ -300,22 +345,31 @@ dbname=mqpairs dbuser=xxx dbpasswd=yyy ``` + We may probably need to modify LD_LIBRARY_PATH before launching ‘consumer’: + ``` # export LD_LIBRARY_PATH=/packages/rabbitmq-c/rabbitmq-c/librabbitmq:/packages/mariadb_client-2.0.0-Linux/lib/mariadb:/usr/lib64 ``` + and finally we can launch it: + ``` # ./consumer ``` + If the consumer.cnf file is not in the same directory as the binary file is, you can provide the location of the folder that it is in by passing it the -c flag followed by the path: +``` # ./consumer -c path/to/file +``` and start maxScale as well ## Step 6 - Test the filter and check collected data + Assuming that MaxScale and the message consumer are successfully running let’s connect to the service with an active mqfilter: + ``` [root@maxscale-02 MaxScale]# mysql -h 127.0.0.1 -P 4506 -uxxx -pyyy ... @@ -326,13 +380,12 @@ MariaDB [(none)]> select RAND(3), RAND(5); | 0.9057697559760601 | 0.40613597483014313 | +--------------------+---------------------+ 1 row in set (0.01 sec) - … MariaDB [(none)]> select RAND(3544), RAND(11); ``` - we can check the consumer output in the terminal where it was started: + ``` -------------------------------------------------------------- Received: 1409671452|select @@version_comment limit ? @@ -362,9 +415,11 @@ MariaDB [mqpairs]> select * from pairs; | 01050106010701080109010a010b010c10d | select RAND(?), RAND(?) | Columns: 2 | 2014-09-02 11:24:37 | 2014-09-02 11:24:37 | 1 | +-------------------------------------+----------------------------------+------------+---------------------+---------------------+---------+ 7 rows in set (0.01 sec) -``` +``` + The filter send queries to the RabbitMQ server in the canonical format, i.e select RAND(?), RAND(?). The queries Message Queue Consumer application gets from the server are stored with a counter that quickly shows how many times that normalized query was received: + ``` | 01050106010701080109010a010b010c10d | select RAND(?), RAND(?) | Columns: 2 | 2014-09-02 11:24:37 | 2014-09-02 11:29:15 | 3 | ``` From e93f922e21cbf7196436ac8ff9013cd1db306cf4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 7 Sep 2015 14:03:30 +0300 Subject: [PATCH 64/74] Cleaned up Ubuntu init scripts. --- etc/ubuntu/init.d/maxscale.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/ubuntu/init.d/maxscale.in b/etc/ubuntu/init.d/maxscale.in index c9ee71bd3..95ed0bcd9 100755 --- a/etc/ubuntu/init.d/maxscale.in +++ b/etc/ubuntu/init.d/maxscale.in @@ -45,7 +45,7 @@ _RETVAL_STATUS_NOT_RUNNING=3 ################################# NAME=maxscale DAEMON=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale -DAEMON_OPTS=--user=maxscale +DAEMON_OPTS='--user=maxscale' # Source function library. . /lib/lsb/init-functions From ad0becae486cc332fa377d5a36ce4680238ce90b Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 7 Sep 2015 14:39:55 +0200 Subject: [PATCH 65/74] Copyright set to 2015 Copyright set to 2015 --- 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 b7a4d1ab6..32c11584f 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -13,7 +13,7 @@ * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright MariaDB Corporation Ab 2013-2014 + * Copyright MariaDB Corporation Ab 2013-2015 * */ From 26ad339c6b5b60b465848ebf50401f40f2ea8025 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 7 Sep 2015 14:25:19 +0300 Subject: [PATCH 66/74] Moved the section about maxadmin default user to the start of the document. --- Documentation/Reference/MaxAdmin.md | 123 ++++++++++++++-------------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/Documentation/Reference/MaxAdmin.md b/Documentation/Reference/MaxAdmin.md index d46d1f61e..2c801ae81 100644 --- a/Documentation/Reference/MaxAdmin.md +++ b/Documentation/Reference/MaxAdmin.md @@ -8,22 +8,22 @@ Mark Riddoch Last Updated: 24th June 2015 -[Overview](#overview) -[Running MaxAdmin](#running) -[Getting Help](#help) -[Working with Services](#services) -[Working with Servers](#servers) -[Working with Sessions](#sessions) -[Descriptor Control Blocks](#dcbs) -[Working with Filters](#filters) -[Working with Monitors](#monitors) -[Working With Administration Interface Users](#interface) -[MaxScale Status Commands](#statuscommands) -[Administration Commands](#admincommands) -[Configuring MaxScale to Accept MaxAdmin Connections](#connections) -[Tuning MaxScale](#tuning) +[Overview](#overview) +[Running MaxAdmin](#running) +[Working With Administration Interface Users](#interface) +[Getting Help](#help) +[Working with Services](#services) +[Working with Servers](#servers) +[Working with Sessions](#sessions) +[Descriptor Control Blocks](#dcbs) +[Working with Filters](#filters) +[Working with Monitors](#monitors) +[MaxScale Status Commands](#statuscommands) +[Administration Commands](#admincommands) +[Configuring MaxScale to Accept MaxAdmin Connections](#connections) +[Tuning MaxScale](#tuning) - + # Overview MaxAdmin is a simple client interface that can be used to interact with the MaxScale server, it allows the display of internal MaxScale statistics, status and control of MaxScale operations. @@ -36,12 +36,57 @@ MaxAdmin supports * Execution of command scripts - + # Running MaxAdmin The MaxAdmin client application may be run in two different modes, either as an interactive command shell for executing commands against MaxScale or by passing commands on the MaxAdmin command line itself. -## Command Line Switches + +# Working With Administration Interface Users + +A default installation of MaxScale allows connection to the administration interface using the username of `admin` and the password `mariadb`. This username and password stay in effect as long as no other users have been created for the administration interface. As soon as the first user is added the use of `admin/mariadb` as login credentials will be disabled. + + +## What Users Have Been Defined? + +In order to see the current users that have been defined for the administration interface use the command show users. + + MaxScale> show users + Administration interface users: + Users table data + Hashtable: 0x734470, size 52 + No. of entries: 5 + Average chain length: 0.1 + Longest chain length: 2 + User names: vilho, root, dba, massi, mark + MaxScale> + +Please note that if no users have been configured the default admin/mariadb user will not be shown. + + MaxScale> show users + Administration interface users: + No administration users have been defined. + MaxScale> + +## Add A New User + +To add a new administrative user to the MaxScale server use the command add user. This command is passed a user name and a password. + + MaxScale> add user maria dtbse243 + User maria has been successfully added. + MaxScale> + +## Delete A User + +To remove a user the command remove user is used, it must also be called with the username and password of the user. The password will be checked. + + MaxScale> remove user maria des + Failed to remove user maria. Authentication failed + MaxScale> remove user maria dtbse243 + User maria has been successfully removed. + MaxScale> + +# Command Line Switches The MaxAdmin command accepts a number of switches @@ -703,50 +748,6 @@ A monitor that has been shutdown may be restarted using the restart monitor comm Monitored servers: 127.0.0.1:3306, 127.0.0.1:3307, 127.0.0.1:3308, 127.0.0.1:3309 MaxScale> - -# Working With Administration Interface Users - -A default installation of MaxScale allows connection to the administration interface using the username of admin and the password mariadb. This username and password stay in effect as long as no other users have been created for the administration interface. As soon as the first user is added the use of admin/mariadb as login credentials will be disabled. - -## What Users Have Been Defined? - -In order to see the current users that have been defined for the administration interface use the command show users. - - MaxScale> show users - Administration interface users: - Users table data - Hashtable: 0x734470, size 52 - No. of entries: 5 - Average chain length: 0.1 - Longest chain length: 2 - User names: vilho, root, dba, massi, mark - MaxScale> - -Please note that if no users have been configured the default admin/mariadb user will not be shown. - - MaxScale> show users - Administration interface users: - No administration users have been defined. - MaxScale> - -## Add A New User - -To add a new administrative user to the MaxScale server use the command add user. This command is passed a user name and a password. - - MaxScale> add user maria dtbse243 - User maria has been successfully added. - MaxScale> - -## Delete A User - -To remove a user the command remove user is used, it must also be called with the username and password of the user. The password will be checked. - - MaxScale> remove user maria des - Failed to remove user maria. Authentication failed - MaxScale> remove user maria dtbse243 - User maria has been successfully removed. - MaxScale> - # MaxScale Status Commands From 6f481c5d62689ff1985abad068249f566fa77628 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Tue, 8 Sep 2015 09:45:59 +0200 Subject: [PATCH 67/74] Documentation Update Documentation Update --- .../MaxScale-HA-with-Corosync-Pacemaker.md | 315 ++++++------------ 1 file changed, 96 insertions(+), 219 deletions(-) diff --git a/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md b/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md index 7afaf5c0d..19764e881 100644 --- a/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md +++ b/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md @@ -1,32 +1,4 @@ -How to make MaxScale High Available - -Corosync/Pacemaker setup - -& MaxScale init script - -Massimiliano Pinto - -Last Updated: 4th August 2014 - -# Contents - -[Contents](#heading=h.myvf4p2ngdc5) - -[Overview](#heading=h.92d1rpk8nyx4) - -[Clustering Software installation](#heading=h.c1l0xy6aynl7) - -[MaxScale init script](#heading=h.cfb6xvv8fu1n) - -[Configure MaxScale for HA](#heading=h.qk4cgmtiugm0) - - [Use case: failed resource is restarted](#heading=h.3fszf28iz3m5) - - [Use case: failed resource migration on a node is started in another one](#heading=h.erqw535ttk7l) - -[Add a Virtual IP (VIP) to the cluster](#heading=h.vzslsgvxjyug) - -# Overview +# How to make MaxScale High Available The document shows an example of a Pacemaker / Corosync setup with MaxScale based on Linux Centos 6.5, using three virtual servers and unicast heartbeat mode with the following minimum requirements: @@ -44,19 +16,19 @@ On each node in the cluster do the following steps: (1) Add clustering repos to yum +``` # vi /etc/yum.repos.d/ha-clustering.repo +``` Add the following to the file +``` [haclustering] - name=HA Clustering - baseurl=http://download.opensuse.org/repositories/network:/ha-clustering:/Stable/CentOS_CentOS-6/ - enabled=1 - gpgcheck=0 +``` (2) Install the software @@ -64,45 +36,37 @@ gpgcheck=0 Package versions used -Package** pacemake**r-1.1.10-14.el6_5.3.x86_64 - -Package **corosync**-1.4.5-2.4.x86_64 - -Package **crmsh**-2.0+git46-1.1.x86_64 +``` +Package pacemaker-1.1.10-14.el6_5.3.x86_64 +Package corosync-1.4.5-2.4.x86_64 +Package crmsh-2.0+git46-1.1.x86_64 +``` (3) Assign hostname on each node - - -In this example the three names used for the nodes are: - - **node1,node,node3** - -# hostname **node1** +In this example the three names used for the nodes are: node1,node,node3 +``` +[root@server1 ~]# hostname node1 ... - -# hostname nodeN +[root@server2 ~]# hostname node2 +... +[root@server3 ~]# hostname node3 +``` (4) For each node add server names in /etc/hosts +``` [root@node3 ~]# vi /etc/hosts - 10.74.14.39 node1 - 10.228.103.72 node2 - 10.35.15.26 node3 current-node - -[root@node1 ~]# vi /etc/hosts - -10.74.14.39 node1 current-node - -10.228.103.72 node2 - -10.35.15.26 node3 - ... +[root@node1 ~]# vi /etc/hosts +10.74.14.39 node1 current-node +10.228.103.72 node2 +10.35.15.26 node3 +``` **Please note**: add **current-node** as an alias for the current node in each of the /etc/hosts files. @@ -110,97 +74,70 @@ In this example the three names used for the nodes are: On one of the nodes, say node2 run the corosync-keygen utility and follow +``` [root@node2 ~]# corosync-keygen Corosync Cluster Engine Authentication key generator. Gathering 1024 bits for key from /dev/random. Press keys on your keyboard to generate entropy. After completion the key will be found in /etc/corosync/authkey. +``` (6) Prepare the corosync configuration file Using node2 as an example: +``` [root@node2 ~]# vi /etc/corosync/corosync.conf +``` Add the following to the file: +``` # Please read the corosync.conf.5 manual page compatibility: whitetank totem { - version: 2 - secauth: off - interface { - member { - memberaddr: node1 - } - member { - memberaddr: node2 - } - member { - memberaddr: node3 - } - ringnumber: 0 - bindnetaddr: current-node - mcastport: 5405 - ttl: 1 - } - transport: udpu - } logging { - fileline: off - to_logfile: yes - to_syslog: yes - logfile: /var/log/cluster/corosync.log - debug: off - timestamp: on - logger_subsys { - subsys: AMF - debug: off - } - } # this will start Pacemaker processes service { - ver: 0 - name: pacemaker - } +``` **Please note **in this example: @@ -212,11 +149,11 @@ name: pacemaker (7) copy configuration files and auth key on each of the other nodes +``` [root@node2 ~]# scp /etc/corosync/* root@node1:/etc/corosync/ - -[root@node2 ~]# scp /etc/corosync/* root@nodeN:/etc/corosync/ - ... +[root@node2 ~]# scp /etc/corosync/* root@nodeN:/etc/corosync/ +``` (8) Corosync needs port *5*405 to be opened: @@ -224,39 +161,36 @@ name: pacemaker For a quick start just disable iptables on each nodes: +``` [root@node2 ~]# service iptables stop - -… - +... [root@nodeN ~]# service iptables stop +``` (9) Start Corosyn on each node: +``` [root@node2 ~] #/etc/init.d/corosync start - -… - +... [root@nodeN ~] #/etc/init.d/corosync start +``` and check the corosync daemon is successfully bound to port 5405: +``` [root@node2 ~] #netstat -na | grep 5405 - udp 0 0 10.228.103.72:5405 0.0.0.0:* +``` Check if other nodes are reachable with nc utility and option UDP (-u): +``` [root@node2 ~] #echo "check ..." | nc -u node1 5405 - [root@node2 ~] #echo "check ..." | nc -u node3 5405 - ... - [root@node1 ~] #echo "check ..." | nc -u node2 5405 - [root@node1 ~] #echo "check ..." | nc -u node3 5405 - -… +``` If the following message is displayed @@ -266,27 +200,25 @@ There is an issue with communication between the nodes, this is most likely to b (10) Check the cluster status, from any node +``` [root@node3 ~]# crm status +``` After a while this will be the output: +``` [root@node3 ~]# crm status - Last updated: Mon Jun 30 12:47:53 2014 - Last change: Mon Jun 30 12:47:39 2014 via crmd on node2 - Stack: classic openais (with plugin) - Current DC: node2 - partition with quorum - Version: 1.1.10-14.el6_5.3-368c726 3 Nodes configured, 3 expected votes - 0 Resources configured Online: [ node1 node2 node3 ] +``` For the basic setup disable the following properties: @@ -294,11 +226,12 @@ For the basic setup disable the following properties: - quorum policy +``` [root@node3 ~]# crm configure property 'stonith-enabled'='false' - [root@node3 ~]# crm configure property 'no-quorum-policy'='ignore' +``` -For more information see: +For additional information see: [http://www.clusterlabs.org/doc/crm_fencing.html](http://www.clusterlabs.org/doc/crm_fencing.html) @@ -308,29 +241,20 @@ The configuration is automatically updated on every node: Check it from another node, say node1 +``` [root@node1 ~]# crm configure show - node node1 - node node2 - node node3 - property cib-bootstrap-options: \ - dc-version=1.1.10-14.el6_5.3-368c726 \ - cluster-infrastructure="classic openais (with plugin)" \ - expected-quorum-votes=3 \ - stonith-enabled=false \ - no-quorum-policy=ignore \ - placement-strategy=balanced \ - default-resource-stickiness=infinity +``` The Corosync / Pacemaker cluster is ready to be configured to manage resources. @@ -338,126 +262,110 @@ The Corosync / Pacemaker cluster is ready to be configured to manage resources. The MaxScale /etc/init.d./maxscale script allows to start/stop/restart and monitor MaxScale process running in the system. -Edit it and modify the **MAXSCALE_BASEDIR** to match the installation directory you choose when you installed MaxScale. - -**Note**: - -It could be necessary to modify other variables, such as - -MAXSCALE_BIN, MAXSCALE_HOME, MAXSCALE_PIDFILE and LD_LIBRARY_PATH for a non standard setup. - +``` [root@node1 ~]# /etc/init.d/maxscale - Usage: /etc/init.d/maxscale {start|stop|status|restart|condrestart|reload} +``` - Start +``` [root@node1 ~]# /etc/init.d/maxscale start - Starting MaxScale: maxscale (pid 25892) is running... [ OK ] +``` - Start again +``` [root@node1 ~]# /etc/init.d/maxscale start - Starting MaxScale: found maxscale (pid 25892) is running.[ OK ] +``` - Stop +``` [root@node1 ~]# /etc/init.d/maxscale stop - Stopping MaxScale: [ OK ] +``` - Stop again +``` [root@node1 ~]# /etc/init.d/maxscale stop - Stopping MaxScale: [FAILED] +``` - Status (MaxScale not running) +``` [root@node1 ~]# /etc/init.d/maxscale status - MaxScale is stopped [FAILED] +``` The script exit code for "status" is 3 - Status (MaxScale is running) +``` [root@node1 ~]# /etc/init.d/maxscale status - Checking MaxScale status: MaxScale (pid 25953) is running.[ OK ] +``` The script exit code for "status" is 0 - - - Note: the MaxScale script is LSB compatible and returns the proper exit code for each action: -For more informations; +For additional informations; - [http://www.linux-ha.org/wiki/LSB_Resource_Agents](http://www.linux-ha.org/wiki/LSB_Resource_Agents) +[http://www.linux-ha.org/wiki/LSB_Resource_Agents](http://www.linux-ha.org/wiki/LSB_Resource_Agents) After checking MaxScale is well managed by the /etc/init.d/script is possible to configure the MaxScale HA via Pacemaker. # Configure MaxScale for HA with Pacemaker +``` [root@node2 ~]# crm configure primitive MaxScale lsb:maxscale \ - op monitor interval="10s” timeout=”15s” \ - op start interval="0” timeout=”15s” \ - op stop interval="0” timeout=”30s” +``` MaxScale resource will be started: +``` [root@node2 ~]# crm status - Last updated: Mon Jun 30 13:15:34 2014 - Last change: Mon Jun 30 13:15:28 2014 via cibadmin on node2 - Stack: classic openais (with plugin) - Current DC: node2 - partition with quorum - Version: 1.1.10-14.el6_5.3-368c726 3 Nodes configured, 3 expected votes - 1 Resources configured Online: [ node1 node2 node3 ] MaxScale (lsb:maxscale): Started node1 +``` -Basic use cases: +#Basic use cases: -# 1. Resource restarted after a failure: +## 1. Resource restarted after a failure: -MaxScale Pid is, $MAXSCALE_PIDFILE=$MAXSCALE_HOME/log/maxscale.pid - -In the example is 26114, kill the process immediately: +In the example MaxScale PID is 26114, kill the process immediately: +``` [root@node2 ~]# kill -9 26114 - +... [root@node2 ~]# crm status - Last updated: Mon Jun 30 13:16:11 2014 - Last change: Mon Jun 30 13:15:28 2014 via cibadmin on node2 - Stack: classic openais (with plugin) - Current DC: node2 - partition with quorum - Version: 1.1.10-14.el6_5.3-368c726 3 Nodes configured, 3 expected votes - 1 Resources configured Online: [ node1 node2 node3 ] @@ -465,37 +373,34 @@ Online: [ node1 node2 node3 ] Failed actions: MaxScale_monitor_15000 on node1 'not running' (7): call=19, status=complete, last-rc-change='Mon Jun 30 13:16:14 2014', queued=0ms, exec=0ms +``` **Note** the **MaxScale_monitor** failed action After a few seconds it will be started again: +``` [root@node2 ~]# crm status - Last updated: Mon Jun 30 13:21:12 2014 - Last change: Mon Jun 30 13:15:28 2014 via cibadmin on node1 - Stack: classic openais (with plugin) - Current DC: node2 - partition with quorum - Version: 1.1.10-14.el6_5.3-368c726 3 Nodes configured, 3 expected votes - 1 Resources configured Online: [ node1 node2 node3 ] MaxScale (lsb:maxscale): Started node1 +``` -# 2. The resource cannot be migrated to node1 for a failure: +## 2. The resource cannot be migrated to node1 for a failure: First, migrate the the resource to another node, say node3 +``` [root@node1 ~]# crm resource migrate MaxScale node3 - ... Online: [ node1 node2 node3 ] @@ -503,23 +408,19 @@ Online: [ node1 node2 node3 ] Failed actions: MaxScale_start_0 on node1 'not running' (7): call=76, status=complete, last-rc-change='Mon Jun 30 13:31:17 2014', queued=2015ms, exec=0ms +``` Note the **MaxScale_start** failed action on node1, and after a few seconds +``` [root@node3 ~]# crm status - Last updated: Mon Jun 30 13:35:00 2014 - Last change: Mon Jun 30 13:31:13 2014 via crm_resource on node3 - Stack: classic openais (with plugin) - Current DC: node2 - partition with quorum - Version: 1.1.10-14.el6_5.3-368c726 3 Nodes configured, 3 expected votes - 1 Resources configured Online: [ node1 node2 node3 ] @@ -529,6 +430,7 @@ Online: [ node1 node2 node3 ] Failed actions: MaxScale_start_0 on node1 'not running' (7): call=76, status=complete, last-rc-change='Mon Jun 30 13:31:17 2014', queued=2015ms, exec=0ms +``` Successfully, MaxScale has been started on a new node: node2. @@ -536,35 +438,30 @@ Successfully, MaxScale has been started on a new node: node2. With "crm resource cleanup MaxScale" is possible to cleanup the messages: +``` [root@node1 ~]# crm resource cleanup MaxScale - Cleaning up MaxScale on node1 - Cleaning up MaxScale on node2 - Cleaning up MaxScale on node3 +``` The cleaned status is visible from other nodes as well: +``` [root@node2 ~]# crm status - Last updated: Mon Jun 30 13:38:18 2014 - Last change: Mon Jun 30 13:38:17 2014 via crmd on node3 - Stack: classic openais (with plugin) - Current DC: node2 - partition with quorum - Version: 1.1.10-14.el6_5.3-368c726 3 Nodes configured, 3 expected votes - 1 Resources configured Online: [ node1 node2 node3 ] MaxScale (lsb:maxscale): Started node2 +``` # Add a Virtual IP (VIP) to the cluster @@ -576,74 +473,53 @@ Setup is very easy: assuming an addition IP address is available and can be added to one of the nodes, this i the new configuration to add: +``` [root@node2 ~]# crm configure primitive maxscale_vip ocf:heartbeat:IPaddr2 params ip=192.168.122.125 op monitor interval=10s - - +``` MaxScale process and the VIP must be run in the same node, so it’s mandatory to add to the configuration the group ‘maxscale_service’. +``` [root@node2 ~]# crm configure group maxscale_service maxscale_vip MaxScale +``` The final configuration is, from another node: +``` [root@node3 ~]# crm configure show - node node1 - node node2 - node node3 - primitive MaxScale lsb:maxscale \ - op monitor interval=15s timeout=10s \ - op start interval=0 timeout=15s \ - op stop interval=0 timeout=30s - primitive maxscale_vip IPaddr2 \ - params ip=192.168.122.125 \ - op monitor interval=10s - group maxscale_service maxscale_vip MaxScale \ - meta target-role=Started - property cib-bootstrap-options: \ - dc-version=1.1.10-14.el6_5.3-368c726 \ - cluster-infrastructure="classic openais (with plugin)" \ - expected-quorum-votes=3 \ - stonith-enabled=false \ - no-quorum-policy=ignore \ - placement-strategy=balanced \ - last-lrm-refresh=1404125486 +``` Check the resource status: +``` [root@node1 ~]# crm status - Last updated: Mon Jun 30 13:51:29 2014 - Last change: Mon Jun 30 13:51:27 2014 via crmd on node1 - Stack: classic openais (with plugin) - Current DC: node2 - partition with quorum - Version: 1.1.10-14.el6_5.3-368c726 3 Nodes configured, 3 expected votes - 2 Resources configured Online: [ node1 node2 node3 ] @@ -653,6 +529,7 @@ Online: [ node1 node2 node3 ] maxscale_vip (ocf::heartbeat:IPaddr2): Started node2 MaxScale (lsb:maxscale): Started node2 +``` With both resources on node2, now MaxScale service will be reachable via the configured VIP address 192.168.122.125 From ee19fbd41b53665a3351a3b7d3d490739c33455a Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Tue, 8 Sep 2015 09:48:34 +0200 Subject: [PATCH 68/74] Documentation Update Documentation Update --- .../MaxScale-HA-with-Corosync-Pacemaker.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md b/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md index 19764e881..af6a897d3 100644 --- a/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md +++ b/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md @@ -10,7 +10,7 @@ The document shows an example of a Pacemaker / Corosync setup with MaxScale base Please note the solution is a quick setup example that may not be suited for all production environments. -# Clustering Software installation +## Clustering Software installation On each node in the cluster do the following steps: @@ -32,7 +32,9 @@ gpgcheck=0 (2) Install the software +``` # yum install pacemaker corosync crmsh +``` Package versions used @@ -258,7 +260,7 @@ property cib-bootstrap-options: \ The Corosync / Pacemaker cluster is ready to be configured to manage resources. -# MaxScale init script /etc/init.d/maxscale +## MaxScale init script /etc/init.d/maxscale The MaxScale /etc/init.d./maxscale script allows to start/stop/restart and monitor MaxScale process running in the system. @@ -349,9 +351,9 @@ Online: [ node1 node2 node3 ] MaxScale (lsb:maxscale): Started node1 ``` -#Basic use cases: +##Basic use cases: -## 1. Resource restarted after a failure: +### 1. Resource restarted after a failure: In the example MaxScale PID is 26114, kill the process immediately: @@ -395,7 +397,7 @@ Online: [ node1 node2 node3 ] MaxScale (lsb:maxscale): Started node1 ``` -## 2. The resource cannot be migrated to node1 for a failure: +### 2. The resource cannot be migrated to node1 for a failure: First, migrate the the resource to another node, say node3 @@ -463,7 +465,7 @@ Online: [ node1 node2 node3 ] MaxScale (lsb:maxscale): Started node2 ``` -# Add a Virtual IP (VIP) to the cluster +## Add a Virtual IP (VIP) to the cluster It’s possible to add a virtual IP to the cluster: From 5350a85e2b36571e371b7549efeefd96134493ec Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 7 Sep 2015 18:51:01 +0300 Subject: [PATCH 69/74] Fix to MXS-356: https://mariadb.atlassian.net/browse/MXS-356 Added configurable timeouts for the embedded MySQL connections. --- .../Getting-Started/Configuration-Guide.md | 12 ++++++ server/core/config.c | 31 ++++++++++++++ server/core/dbusers.c | 40 +++++-------------- server/include/dbusers.h | 5 +++ server/include/maxconfig.h | 3 ++ 5 files changed, 61 insertions(+), 30 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 8e88eb9ef..065f4ce0c 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -66,6 +66,18 @@ threads=1 It should be noted that additional threads will be created to execute other internal services within MaxScale. This setting is used to configure the number of threads that will be used to manage the user connections. +#### `auth_connect_timeout` + +The connection timeout in seconds for the MySQL connections to the backend server when user authentication data is fetched. Increasing the value of this parameter will cause MaxScale to wait longer for a response from the backend server before aborting the connection process when creating a new connection is made. + +#### `auth_read_timeout` + +The read timeout in seconds for the MySQL connection to the backend database when user authentication data is fetched. Increasing the value of this parameter will cause MaxScale to wait longer for a response from the backend server when user data is being actively fetched. If you have a large number of database users and grants and the authentication is failing, it is a good idea. Slower networks will also benefit from an increased timeout value. + +#### `auth_write_timeout` + +The write timeout in seconds for the MySQL connection to the backend database when user authentication data is fetched. Currently MaxScale does not write or modify the data in the backend server. + #### `ms_timestamp` Enable or disable the high precision timestamps in logfiles. Enabling this adds millisecond precision to all logfile timestamps. diff --git a/server/core/config.c b/server/core/config.c index b5084252c..1a963d562 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -74,6 +74,7 @@ #include #include #include +#include /** According to the PCRE manual, this should be a multiple of 3 */ #define MAXSCALE_PCRE_BUFSZ 24 @@ -1542,6 +1543,33 @@ int i; { skygw_set_highp(config_truth_value((char*)value)); } + else if (strcmp(name, "auth_connect_timeout") == 0) + { + char* endptr; + int intval = strtol(value, &endptr, 0); + if(*endptr == '\0' && intval > 0) + gateway.auth_conn_timeout = intval; + else + skygw_log_write(LE, "Invalid timeout value for 'auth_connect_timeout': %s", value); + } + else if (strcmp(name, "auth_read_timeout") == 0) + { + char* endptr; + int intval = strtol(value, &endptr, 0); + if(*endptr == '\0' && intval > 0) + gateway.auth_read_timeout = intval; + else + skygw_log_write(LE, "Invalid timeout value for 'auth_read_timeout': %s", value); + } + else if (strcmp(name, "auth_write_timeout") == 0) + { + char* endptr; + int intval = strtol(value, &endptr, 0); + if(*endptr == '\0' && intval > 0) + gateway.auth_write_timeout = intval; + else + skygw_log_write(LE, "Invalid timeout value for 'auth_write_timeout': %s", value); + } else { for (i = 0; lognames[i].logname; i++) @@ -1607,6 +1635,9 @@ global_defaults() gateway.n_threads = 1; gateway.n_nbpoll = DEFAULT_NBPOLLS; gateway.pollsleep = DEFAULT_POLLSLEEP; + gateway.auth_conn_timeout = DEFAULT_AUTH_CONNECT_TIMEOUT; + gateway.auth_read_timeout = DEFAULT_AUTH_READ_TIMEOUT; + gateway.auth_write_timeout = DEFAULT_AUTH_WRITE_TIMEOUT; if (version_string != NULL) gateway.version_string = strdup(version_string); else diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 260c8be6c..701d59764 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -52,11 +52,6 @@ #include #include - -#define DEFAULT_CONNECT_TIMEOUT 3 -#define DEFAULT_READ_TIMEOUT 1 -#define DEFAULT_WRITE_TIMEOUT 2 - #define USERS_QUERY_NO_ROOT " AND user NOT IN ('root')" #if 0 @@ -121,11 +116,7 @@ int add_wildcard_users(USERS *users, char* db, HASHTABLE* hash); -static int gw_mysql_set_timeouts( - MYSQL* handle, - int read_timeout, - int write_timeout, - int connect_timeout); +static int gw_mysql_set_timeouts(MYSQL* handle); /** * Load the user/passwd form mysql.user table into the service users' hashtable @@ -630,10 +621,7 @@ getAllUsers(SERVICE *service, USERS *users) } /** Set read, write and connect timeout values */ - if (gw_mysql_set_timeouts(con, - DEFAULT_READ_TIMEOUT, - DEFAULT_WRITE_TIMEOUT, - DEFAULT_CONNECT_TIMEOUT)) + if (gw_mysql_set_timeouts(con)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -702,10 +690,7 @@ getAllUsers(SERVICE *service, USERS *users) } /** Set read, write and connect timeout values */ - if (gw_mysql_set_timeouts(con, - DEFAULT_READ_TIMEOUT, - DEFAULT_WRITE_TIMEOUT, - DEFAULT_CONNECT_TIMEOUT)) + if (gw_mysql_set_timeouts(con)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -1156,10 +1141,7 @@ getUsers(SERVICE *service, USERS *users) return -1; } /** Set read, write and connect timeout values */ - if (gw_mysql_set_timeouts(con, - DEFAULT_READ_TIMEOUT, - DEFAULT_WRITE_TIMEOUT, - DEFAULT_CONNECT_TIMEOUT)) + if (gw_mysql_set_timeouts(con)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -2026,17 +2008,15 @@ int useorig = 0; * * @return 0 if succeed, 1 if failed */ -static int gw_mysql_set_timeouts( - MYSQL* handle, - int read_timeout, - int write_timeout, - int connect_timeout) +static int gw_mysql_set_timeouts(MYSQL* handle) { int rc; + GATEWAY_CONF* cnf = config_get_global_options(); + if ((rc = mysql_options(handle, MYSQL_OPT_READ_TIMEOUT, - (void *)&read_timeout))) + (void *)&cnf->auth_read_timeout))) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -2047,7 +2027,7 @@ static int gw_mysql_set_timeouts( if ((rc = mysql_options(handle, MYSQL_OPT_CONNECT_TIMEOUT, - (void *)&connect_timeout))) + (void *)&cnf->auth_conn_timeout))) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -2058,7 +2038,7 @@ static int gw_mysql_set_timeouts( if ((rc = mysql_options(handle, MYSQL_OPT_WRITE_TIMEOUT, - (void *)&write_timeout))) + (void *)&cnf->auth_write_timeout))) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, diff --git a/server/include/dbusers.h b/server/include/dbusers.h index 4f2d2c787..ab7ff1638 100644 --- a/server/include/dbusers.h +++ b/server/include/dbusers.h @@ -42,6 +42,11 @@ #define USERS_REFRESH_TIME 30 /* Allowed time interval (in seconds) after last update*/ #define USERS_REFRESH_MAX_PER_TIME 4 /* Max number of load calls within the time interval */ +/** Default timeout values used by the connections which fetch user authentication data */ +#define DEFAULT_AUTH_CONNECT_TIMEOUT 3 +#define DEFAULT_AUTH_READ_TIMEOUT 1 +#define DEFAULT_AUTH_WRITE_TIMEOUT 2 + /* Max length of fields in the mysql.user table */ #define MYSQL_USER_MAXLEN 128 #define MYSQL_PASSWORD_LEN 41 diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index 5052d50c9..99816d820 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -106,6 +106,9 @@ typedef struct { unsigned int pollsleep; /**< Wait time in blocking polls */ int syslog; /*< Log to syslog */ int maxlog; /*< Log to MaxScale's own logs */ + unsigned int auth_conn_timeout; /*< Connection timeout for the user authentication */ + unsigned int auth_read_timeout; /*< Read timeout for the user authentication */ + unsigned int auth_write_timeout; /*< Write timeout for the user authentication */ } GATEWAY_CONF; extern int config_load(char *); From b84dbd8d3fcf7614c59b026108c755d62c3c69de Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 8 Sep 2015 15:02:42 +0300 Subject: [PATCH 70/74] MXS-357: Possibility to turn off log message augmentation. It's not always desireable to have the function name appended to every logged line. --- log_manager/log_manager.cc | 53 +++++++++++++++++++++++++++++++++----- log_manager/log_manager.h | 17 ++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 793e65651..c5c1f7bd6 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -103,6 +103,12 @@ static bool flushall_flag; static bool flushall_started_flag; static bool flushall_done_flag; +/** + * Default augmentation. + */ +static int default_log_augmentation = LOG_AUGMENT_WITH_FUNCTION; +static int log_augmentation = default_log_augmentation; + /** Writer thread structure */ struct filewriter_st { #if defined(SS_DEBUG) @@ -1359,6 +1365,16 @@ return_succp: return succp; } +void skygw_log_set_augmentation(int bits) +{ + log_augmentation = bits & LOG_AUGMENTATION_MASK; +} + +int skygw_log_get_augmentation() +{ + return log_augmentation; +} + /** * Helper for skygw_log_write and friends. * @@ -1386,16 +1402,41 @@ static int log_write(logfile_id_t id, { CHK_LOGMANAGER(lm); - const char format[] = "%s [%s]"; - len += sizeof(format); // A bit too much, but won't hurt. - assert(function); - len += strlen(function); + const char* format; + + if (log_augmentation == LOG_AUGMENT_WITH_FUNCTION) + { + static const char function_format[] = "%s [%s]"; + + format = function_format; + + len += sizeof(function_format); // A little bit more than needed, but won't hurt. + assert(function); + len += strlen(function); + } + else + { + static const char default_format[] = "%s"; + + format = default_format; + + len += sizeof(default_format); // A little bit more than needed, but won't hurt. + } + len += 1; // For the trailing NULL. char message[len]; - len = snprintf(message, sizeof(message), format, str, function); - assert(len > 0); + if (log_augmentation == LOG_AUGMENT_WITH_FUNCTION) + { + len = snprintf(message, sizeof(message), format, str, function); + } + else + { + len = snprintf(message, sizeof(message), format, str); + } + + assert(len >= 0); int attempts = 0; int successes = 0; diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h index e2ec60650..dfc83cb8b 100644 --- a/log_manager/log_manager.h +++ b/log_manager/log_manager.h @@ -108,6 +108,15 @@ typedef struct log_info_st */ typedef enum { UNINIT = 0, INIT, RUN, DONE } flat_obj_state_t; +/** + * LOG_AUGMENT_WITH_FUNCTION Each logged line is suffixed with [function-name]. + */ +typedef enum +{ + LOG_AUGMENT_WITH_FUNCTION = 1, + LOG_AUGMENTATION_MASK = (LOG_AUGMENT_WITH_FUNCTION) +} log_augmentation_t; + EXTERN_C_BLOCK_BEGIN bool skygw_logmanager_init(int argc, char* argv[]); @@ -140,6 +149,14 @@ void logmanager_enable_maxscalelog(int); #define skygw_log_write_flush(id, format, ...)\ skygw_log_write_context_flush(id, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) +/** + * What augmentation if any should a logged message be augmented with. + * + * Currently this is a global setting and affects all loggers. + */ +void skygw_log_set_augmentation(int bits); +int skygw_log_get_augmentation(); + EXTERN_C_BLOCK_END const char* get_trace_prefix_default(void); From 67f8520c385e854ebe74bb4e776fa57ecdb0d0b8 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 9 Sep 2015 13:00:10 +0300 Subject: [PATCH 71/74] Added missing include to sharding_common.h --- server/modules/include/sharding_common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/server/modules/include/sharding_common.h b/server/modules/include/sharding_common.h index a401d2e31..dd30a74ea 100644 --- a/server/modules/include/sharding_common.h +++ b/server/modules/include/sharding_common.h @@ -1,6 +1,7 @@ #ifndef _SHARDING_COMMON_HG #define _SHARDING_COMMON_HG +#include #include #include #include From 1bf8853046a67d6150499eeb82885ddd15edb41c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 9 Sep 2015 13:21:54 +0300 Subject: [PATCH 72/74] Fix to MXS-339: https://mariadb.atlassian.net/browse/MXS-339 Renamed maxscale.cnf to maxscale.cnf.template --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a1f7e5e3..4627a3e43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,7 +193,7 @@ install(FILES ${CMAKE_BINARY_DIR}/ReleaseNotes.txt DESTINATION ${MAXSCALE_SHARED install(FILES ${CMAKE_BINARY_DIR}/UpgradingToMaxScale120.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) + install(FILES server/maxscale_template.cnf DESTINATION ${MAXSCALE_CONFDIR} RENAME maxscale.cnf.template) endif() install(FILES server/maxscale_binlogserver_template.cnf DESTINATION ${MAXSCALE_SHAREDIR}) install(FILES ${ERRMSG} DESTINATION ${MAXSCALE_VARDIR}/lib/maxscale From dd45a8f41856b902a4766fe45c34e9757ad665fb Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 9 Sep 2015 16:23:07 +0300 Subject: [PATCH 73/74] Updated documentation. --- Documentation/Getting-Started/Configuration-Guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 065f4ce0c..01a8c1dba 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -68,11 +68,11 @@ It should be noted that additional threads will be created to execute other inte #### `auth_connect_timeout` -The connection timeout in seconds for the MySQL connections to the backend server when user authentication data is fetched. Increasing the value of this parameter will cause MaxScale to wait longer for a response from the backend server before aborting the connection process when creating a new connection is made. +The connection timeout in seconds for the MySQL connections to the backend server when user authentication data is fetched. Increasing the value of this parameter will cause MaxScale to wait longer for a response from the backend server before aborting the authentication process. #### `auth_read_timeout` -The read timeout in seconds for the MySQL connection to the backend database when user authentication data is fetched. Increasing the value of this parameter will cause MaxScale to wait longer for a response from the backend server when user data is being actively fetched. If you have a large number of database users and grants and the authentication is failing, it is a good idea. Slower networks will also benefit from an increased timeout value. +The read timeout in seconds for the MySQL connection to the backend database when user authentication data is fetched. Increasing the value of this parameter will cause MaxScale to wait longer for a response from the backend server when user data is being actively fetched. If the authentication is failing and you either have a large number of database users and grants or the connection to the backend servers is slow, it is a good idea to increase this value. #### `auth_write_timeout` From 9f01d0ec3f5a535b353dccf81d42b1885ddf1abd Mon Sep 17 00:00:00 2001 From: counterpoint Date: Thu, 10 Sep 2015 10:54:33 +0100 Subject: [PATCH 74/74] Fix type. --- server/modules/protocol/mysql_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 4ecf5ffa1..113e5dab0 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -32,7 +32,7 @@ * 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 + * 04/07/2013 Massimiliano Pinto Added asynchronous 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()