From c795032358957d933ff7666dd33d258cb775ff60 Mon Sep 17 00:00:00 2001 From: Jason Lowenthal Date: Tue, 20 Oct 2015 10:48:11 -0500 Subject: [PATCH 01/28] Update Hint-Syntax.md Added "s" to filters in example [Read Service] as it was confusing trying to get hint routing working correctly. --- Documentation/Reference/Hint-Syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Reference/Hint-Syntax.md b/Documentation/Reference/Hint-Syntax.md index a037365e0..4f5d17f30 100644 --- a/Documentation/Reference/Hint-Syntax.md +++ b/Documentation/Reference/Hint-Syntax.md @@ -14,7 +14,7 @@ router_options=master servers=server1 user=maxuser passwd=maxpwd -filter=Hint +filters=Hint [Hint] type=filter From 8d84deecc580741d85097afaadd0c0ae2d762ebc Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 28 Oct 2015 00:04:45 +0200 Subject: [PATCH 02/28] Updated schemarouter to use PCRE2 and moved includes to the header file. --- server/modules/include/schemarouter.h | 25 ++++++++- .../routing/schemarouter/schemarouter.c | 56 +++++++++---------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/server/modules/include/schemarouter.h b/server/modules/include/schemarouter.h index 4f5698972..1607669d1 100644 --- a/server/modules/include/schemarouter.h +++ b/server/modules/include/schemarouter.h @@ -28,11 +28,31 @@ * * @endverbatim */ +#ifndef PCRE2_CODE_UNIT_WIDTH +#define PCRE2_CODE_UNIT_WIDTH 8 +#endif +#include +#include +#include +#include +#include +#include #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + /** * Bitmask values for the router session's initialization. These values are used * to prevent responses from internal commands being forwarded to the client. @@ -330,9 +350,10 @@ typedef struct router_instance { HASHTABLE* ignored_dbs; /*< List of databases to ignore when the * database mapping finds multiple servers * with the same database */ - pcre* ignore_regex; /*< Databases matching this regex will + pcre2_code* ignore_regex; /*< Databases matching this regex will * not cause the session to be terminated * if they are found on more than one server. */ + pcre2_match_data* ignore_match_data; } ROUTER_INSTANCE; diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 8f155892d..710c766bf 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -15,35 +15,14 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ -#include -#include -#include -#include -#include -#include -#include + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #define DEFAULT_REFRESH_INTERVAL 30.0 /** Size of the hashtable used to store ignored databases */ #define SCHEMAROUTER_HASHSIZE 100 -/** Size of the offset vector used for regex matching */ -#define SCHEMA_OVEC_SIZE 24 - MODULE_INFO info = { MODULE_API_ROUTER, MODULE_BETA_RELEASE, @@ -380,13 +359,11 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* } else { - const int ovec_count = SCHEMA_OVEC_SIZE; - int ovector[ovec_count]; - if (!(hashtable_fetch(rses->router->ignored_dbs, data) || (rses->router->ignore_regex && - pcre_exec(rses->router->ignore_regex, NULL, data, - strlen(data), 0, 0, ovector, ovec_count) >= 0))) + pcre2_match(rses->router->ignore_regex, (PCRE2_SPTR)data, + PCRE2_ZERO_TERMINATED, 0, 0, + rses->router->ignore_match_data, NULL) >= 0))) { duplicate_found = true; skygw_log_write(LE, "Error: Database '%s' found on servers '%s' and '%s' for user %s@%s.", @@ -801,19 +778,36 @@ createInstance(SERVICE *service, char **options) if((param = config_get_param(conf,"ignore_databases_regex"))) { - const char* errptr; - int erroffset; - pcre* re = pcre_compile(param->value, 0, &errptr, &erroffset, NULL); + int errcode; + PCRE2_SIZE erroffset; + pcre2_code* re = pcre2_compile((PCRE2_SPTR)param->value, PCRE2_ZERO_TERMINATED, 0, + &errcode, &erroffset, NULL); if(re == NULL) { + PCRE2_UCHAR errbuf[512]; + pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); skygw_log_write(LE, "Error: Regex compilation failed at %d for regex '%s': %s", - erroffset, param->value, errptr); + erroffset, param->value, errbuf); hashtable_free(router->ignored_dbs); free(router); return NULL; } + + pcre2_match_data* match_data = pcre2_match_data_create_from_pattern(re, NULL); + + if (match_data == NULL) + { + skygw_log_write(LE, "Error: PCRE2 match data creation failed. This" + " is most likely caused by a lack of available memory."); + pcre2_code_free(re); + hashtable_free(router->ignored_dbs); + free(router); + return NULL; + } + router->ignore_regex = re; + router->ignore_match_data = match_data; } if((param = config_get_param(conf,"ignore_databases"))) From c37e18e3d2353562cc2ab144cba5e8a2b63fa2db Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 28 Oct 2015 14:59:27 +0200 Subject: [PATCH 03/28] Moved included headers back to .c file --- server/modules/include/schemarouter.h | 17 ---------------- .../routing/schemarouter/schemarouter.c | 20 ++++++++++++++++++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/server/modules/include/schemarouter.h b/server/modules/include/schemarouter.h index 1607669d1..16fb8640b 100644 --- a/server/modules/include/schemarouter.h +++ b/server/modules/include/schemarouter.h @@ -32,27 +32,10 @@ #define PCRE2_CODE_UNIT_WIDTH 8 #endif -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include - /** * Bitmask values for the router session's initialization. These values are used * to prevent responses from internal commands being forwarded to the client. diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 710c766bf..22330c24d 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -15,8 +15,26 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ - +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define DEFAULT_REFRESH_INTERVAL 30.0 From 90ff0f1fdf6b22a4ab0b71d623d34e883406f837 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 28 Oct 2015 14:43:30 +0200 Subject: [PATCH 04/28] Augmentation moved. Augmentation moved to skygw_log_write_context. The severity prefix will be added there as well. If all is done on that level, the amount of memory needed can be figured out in one go. No need to allocate and copy the message several times. --- log_manager/log_manager.cc | 92 ++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 520cfa75e..4ed9ae3a7 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1468,42 +1468,6 @@ static int log_write(logfile_id_t id, { CHK_LOGMANAGER(lm); - 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]; - - 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; @@ -1529,7 +1493,7 @@ static int log_write(logfile_id_t id, LOG_USE_VALIST_NO, LOG_SPREAD_DOWN_YES, LOG_ROTATE_NO, - len, message, valist) == 0) + len, str, valist) == 0) { ++successes; } @@ -1567,23 +1531,63 @@ int skygw_log_write_context(logfile_id_t id, * Find out the length of log string (to be formatted str). */ va_start(valist, str); - int len = vsnprintf(NULL, 0, str, valist); + int message_len = vsnprintf(NULL, 0, str, valist); va_end(valist); - if (len >= 0) + if (message_len >= 0) { - if (len > MAX_LOGSTRLEN) + static const char FORMAT_FUNCTION[] = "(%s): "; + + int augmentation_len = 0; + + switch (log_augmentation) { - len = MAX_LOGSTRLEN; + case LOG_AUGMENT_WITH_FUNCTION: + augmentation_len = sizeof(FORMAT_FUNCTION) - 1; // Remove trailing 0 + augmentation_len -= 2; // Remove the %s + augmentation_len += strlen(function); + break; + + default: + break; } - char message[len + 1]; + int buffer_len = augmentation_len + message_len + 1; // Trailing NULL + + if (buffer_len > MAX_LOGSTRLEN) + { + message_len -= (buffer_len - MAX_LOGSTRLEN); + buffer_len = MAX_LOGSTRLEN; + + assert(augmentation_len + message_len + 1 == buffer_len); + } + + char buffer[buffer_len]; + char *message = buffer + augmentation_len; + + if (augmentation_len) + { + char *augmentation = buffer; + int len = 0; + + switch (log_augmentation) + { + case LOG_AUGMENT_WITH_FUNCTION: + len = sprintf(augmentation, FORMAT_FUNCTION, function); + break; + + default: + assert(!true); + } + + assert(len == augmentation_len); + } va_start(valist, str); - vsnprintf(message, sizeof(message), str, valist); + vsnprintf(message, message_len + 1, str, valist); va_end(valist); - err = log_write(id, file, line, function, len, message, flush); + err = log_write(id, file, line, function, buffer_len - 1, buffer, flush); if (err != 0) { From 22c8af4923f80deb70ede5141821bddcd8a67e78 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 28 Oct 2015 15:00:32 +0200 Subject: [PATCH 05/28] Removed obsolete parameters. Valist is handled before logmanager_write_log is called. So it is quite unnecessary to always having to pass a valist whether it is used or not (and not it is never used). --- log_manager/log_manager.cc | 61 ++++++++------------------------------ 1 file changed, 12 insertions(+), 49 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 4ed9ae3a7..621dccaae 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -294,12 +294,6 @@ enum log_spread_down LOG_SPREAD_DOWN_YES = 1 }; -enum log_use_valist -{ - LOG_USE_VALIST_NO = 0, - LOG_USE_VALIST_YES = 1 -}; - enum log_rotate { LOG_ROTATE_NO = 0, @@ -308,12 +302,10 @@ enum log_rotate static int logmanager_write_log(logfile_id_t id, enum log_flush flush, - enum log_use_valist use_valist, enum log_spread_down spread_down, enum log_rotate rotate, size_t len, - const char* str, - va_list valist); + const char* str); static blockbuf_t* blockbuf_init(logfile_id_t id); static void blockbuf_node_done(void* bb_data); @@ -647,27 +639,22 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) * @param id logfile object identifier * @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 - * 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 * new one * @param str_len length of formatted string * @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, enum log_flush flush, - enum log_use_valist use_valist, enum log_spread_down spread_down, enum log_rotate rotate, size_t str_len, - const char* str, - va_list valist) + const char* str) { logfile_t* lf; char* wp; @@ -687,12 +674,10 @@ static int logmanager_write_log(logfile_id_t id, */ err = logmanager_write_log(LOGFILE_ERROR, LOG_FLUSH_YES, - LOG_USE_VALIST_NO, LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, strlen(errstr) + 1, - errstr, - valist); + errstr); if (err != 0) { fprintf(stderr, @@ -837,20 +822,10 @@ static int logmanager_write_log(logfile_id_t id, * Write next string to overwrite terminating null character * of the timestamp string. */ - if (use_valist) - { - 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", - str); - } + snprintf(wp + timestamp_len + sesid_str_len, + safe_str_len-timestamp_len-sesid_str_len, + "%s", + str); /** Add an ellipsis to an overflowing message to signal truncation. */ if (overflow && safe_str_len > 4) @@ -1361,7 +1336,6 @@ return_err: static bool logfile_set_enabled(logfile_id_t id, bool val) { char* logstr; - va_list notused; bool oldval; bool succp = false; int err = 0; @@ -1377,12 +1351,10 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) */ err = logmanager_write_log(LOGFILE_ERROR, LOG_FLUSH_YES, - LOG_USE_VALIST_NO, LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, strlen(errstr) + 1, - errstr, - notused); + errstr); if (err != 0) { fprintf(stderr, @@ -1409,12 +1381,10 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) lf->lf_enabled = val; err = logmanager_write_log(id, LOG_FLUSH_YES, - LOG_USE_VALIST_NO, LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, strlen(logstr) + 1, - logstr, - notused); + logstr); free(logstr); } if (err != 0) @@ -1486,14 +1456,11 @@ static int log_write(logfile_id_t id, { ++attempts; - va_list valist; // Not used - if (logmanager_write_log((logfile_id_t)i, flush, - LOG_USE_VALIST_NO, LOG_SPREAD_DOWN_YES, LOG_ROTATE_NO, - len, str, valist) == 0) + len, str) == 0) { ++successes; } @@ -1602,7 +1569,6 @@ int skygw_log_write_context(logfile_id_t id, int skygw_log_flush(logfile_id_t id) { int err = 0; - va_list valist; /**< Dummy, must be present but it is not processed */ if (!logmanager_register(false)) { @@ -1613,10 +1579,9 @@ int skygw_log_flush(logfile_id_t id) CHK_LOGMANAGER(lm); err = logmanager_write_log(id, LOG_FLUSH_YES, - LOG_USE_VALIST_NO, LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, - 0, NULL, valist); + 0, NULL); if (err != 0) { @@ -1638,7 +1603,6 @@ int skygw_log_rotate(logfile_id_t id) { int err = 0; logfile_t* lf; - va_list valist; /**< Dummy, must be present but it is not processed */ if (!logmanager_register(false)) { @@ -1654,10 +1618,9 @@ int skygw_log_rotate(logfile_id_t id) err = logmanager_write_log(id, LOG_FLUSH_NO, - LOG_USE_VALIST_NO, LOG_SPREAD_DOWN_NO, LOG_ROTATE_YES, - 0, NULL, valist); + 0, NULL); if (err != 0) { From f964938aaf9a671c02f2274b618e81b4336f9ad3 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 28 Oct 2015 15:52:50 +0200 Subject: [PATCH 06/28] Minor cleanup. Parameter documentation clarified, same approach followed all through. --- log_manager/log_manager.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 621dccaae..f70d69c64 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -643,7 +643,7 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) * 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_len length of formatted string (including terminating NULL). * @param str string to be written to log * * @return 0 if succeed, -1 otherwise @@ -1418,7 +1418,8 @@ int skygw_log_get_augmentation() * @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 len Length of str, including terminating NULL. + * @param str String * @param flush Whether the message should be flushed. * * @return 0 if the logging to at least one log succeeded. @@ -1441,11 +1442,6 @@ static int log_write(logfile_id_t id, int attempts = 0; int successes = 0; - /** - * Add one for line feed. - */ - len += sizeof(char); - for (int i = LOGFILE_FIRST; i <= LOGFILE_LAST; i <<= 1) { /** @@ -1554,7 +1550,7 @@ int skygw_log_write_context(logfile_id_t id, vsnprintf(message, message_len + 1, str, valist); va_end(valist); - err = log_write(id, file, line, function, buffer_len - 1, buffer, flush); + err = log_write(id, file, line, function, buffer_len, buffer, flush); if (err != 0) { From bea4051ad5af511310c5769ff3677b22168cec9a Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 28 Oct 2015 16:13:42 +0200 Subject: [PATCH 07/28] Spread down feature removed. With only one file, the spreading down idea becomes nonsensical. Furthermore, it has never been enabled due to some problems. --- log_manager/log_manager.cc | 91 +++++--------------------------------- 1 file changed, 10 insertions(+), 81 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index f70d69c64..d751d4c9d 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -288,24 +288,17 @@ static void logmanager_unregister(void); static bool logmanager_init_nomutex(int argc, char* argv[]); static void logmanager_done_nomutex(void); -enum log_spread_down -{ - LOG_SPREAD_DOWN_NO = 0, - LOG_SPREAD_DOWN_YES = 1 -}; - enum log_rotate { LOG_ROTATE_NO = 0, LOG_ROTATE_YES = 1 }; -static int logmanager_write_log(logfile_id_t id, - enum log_flush flush, - enum log_spread_down spread_down, - enum log_rotate rotate, - size_t len, - const char* str); +static int logmanager_write_log(logfile_id_t id, + enum log_flush flush, + enum log_rotate rotate, + size_t len, + const char* str); static blockbuf_t* blockbuf_init(logfile_id_t id); static void blockbuf_node_done(void* bb_data); @@ -639,8 +632,6 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) * @param id logfile object identifier * @param flush indicates whether log string must be written to disk * immediately - * @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 (including terminating NULL). @@ -649,12 +640,11 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) * @return 0 if succeed, -1 otherwise * */ -static int logmanager_write_log(logfile_id_t id, - enum log_flush flush, - enum log_spread_down spread_down, - enum log_rotate rotate, - size_t str_len, - const char* str) +static int logmanager_write_log(logfile_id_t id, + enum log_flush flush, + enum log_rotate rotate, + size_t str_len, + const char* str) { logfile_t* lf; char* wp; @@ -674,7 +664,6 @@ static int logmanager_write_log(logfile_id_t id, */ err = logmanager_write_log(LOGFILE_ERROR, LOG_FLUSH_YES, - LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, strlen(errstr) + 1, errstr); @@ -864,61 +853,6 @@ static int logmanager_write_log(logfile_id_t id, { free(wp); } - /** - * disabled because cross-blockbuffer locking either causes deadlock - * or run out of memory blocks. - */ - if (spread_down && false) - { - /** - * Write to target log. If spread_down == true, then - * write also to all logs with greater logfile id. - * LOGFILE_ERROR = 1, - * LOGFILE_MESSAGE = 2, - * LOGFILE_TRACE = 4, - * LOGFILE_DEBUG = 8 - * - * So everything written to error log will appear in - * message, trace and debuglog. Messages will be - * written in trace and debug log. - */ - for (i = (id << 1); i <= LOGFILE_LAST; i <<= 1) - { - /** pointer to write buffer of larger-id log */ - char* wp_c; - - /**< Check if particular log is enabled */ - if (!(lm->lm_enabled_logfiles & i)) - { - continue; - } - /** - * Seek write position and register to block - * buffer. Then print formatted string to - * write position. - */ - wp_c = blockbuf_get_writepos(&bb_c, - (logfile_id_t)i, - timestamp_len - 1 + str_len, - flush); - /** - * Copy original string from block buffer to - * 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: @@ -1351,7 +1285,6 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) */ err = logmanager_write_log(LOGFILE_ERROR, LOG_FLUSH_YES, - LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, strlen(errstr) + 1, errstr); @@ -1381,7 +1314,6 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) lf->lf_enabled = val; err = logmanager_write_log(id, LOG_FLUSH_YES, - LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, strlen(logstr) + 1, logstr); @@ -1454,7 +1386,6 @@ static int log_write(logfile_id_t id, if (logmanager_write_log((logfile_id_t)i, flush, - LOG_SPREAD_DOWN_YES, LOG_ROTATE_NO, len, str) == 0) { @@ -1575,7 +1506,6 @@ int skygw_log_flush(logfile_id_t id) CHK_LOGMANAGER(lm); err = logmanager_write_log(id, LOG_FLUSH_YES, - LOG_SPREAD_DOWN_NO, LOG_ROTATE_NO, 0, NULL); @@ -1614,7 +1544,6 @@ int skygw_log_rotate(logfile_id_t id) err = logmanager_write_log(id, LOG_FLUSH_NO, - LOG_SPREAD_DOWN_NO, LOG_ROTATE_YES, 0, NULL); From 2b8fd85e3021768fc8152810cc438039ced970bd Mon Sep 17 00:00:00 2001 From: Will Fong Date: Thu, 29 Oct 2015 19:02:46 +0800 Subject: [PATCH 08/28] Multiple Edits Added *'s for consistency Added monitor_interval to prevent warning in error log Reworded default cnf location to make it a little easier to read Typo in log location --- .../MySQL-Replication-Connection-Routing-Tutorial.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md b/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md index 45de0a791..0140b7e00 100644 --- a/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md +++ b/Documentation/Tutorials/MySQL-Replication-Connection-Routing-Tutorial.md @@ -51,11 +51,11 @@ MariaDB [(none)]> grant SELECT on mysql.user to '*username*'@'*maxscalehost*'; 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'; +MariaDB [(none)]> GRANT SELECT ON mysql.db TO '*username*'@'maxscalehost'; **Query OK, 0 rows affected (0.00 sec)** -MariaDB [(none)]> GRANT SHOW DATABASES ON *.* TO 'username'@'maxscalehost'; +MariaDB [(none)]> GRANT SHOW DATABASES ON *.* TO '*username*'@'maxscalehost'; **Query OK, 0 rows affected (0.00 sec)** ``` @@ -76,7 +76,7 @@ If you wish to use two different usernames for the two different roles of monito ## Creating Your MaxScale Configuration -MaxScale configuration is held in an ini file that is located in the file maxscale.cnf in the directory /etc. This is not created as part of the installation process and must be manually created. A template file does exist in the `/usr/share/maxscale` folder that can be use as a basis for your configuration. +MaxScale reads its configuration from `/etc/maxscale.cnf`. This is not created as part of the installation process and must be manually created. A template file does exist in the `/usr/share/maxscale` folder that can be use as a basis for your configuration. A global, maxscale, section is included within every MaxScale configuration file; this is used to set the values of various MaxScale wide parameters, perhaps the most important of these is the number of threads that MaxScale will use to execute the code that forwards requests and handles responses for clients. @@ -220,6 +220,7 @@ module=mysqlmon servers=dbserv1, dbserv2, dbserv3 user=maxscale passwd=96F99AA1315BDC3604B006F427DD9484 +monitor_interval=10000 ``` As with the password definition in the server either plain text or encrypted passwords may be used. @@ -255,7 +256,7 @@ or service maxscale start ``` -Check the error log in /var/log/lomaxscale/ to see if any errors are detected in the configuration file and to confirm MaxScale has been started. Also the maxadmin command may be used to confirm that MaxScale is running and the services, listeners etc have been correctly configured. +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 From 8d6c583a1cd47b4966aed7afbe519d7b685d9a01 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 28 Oct 2015 20:15:52 +0200 Subject: [PATCH 09/28] Added a check for duplicate sections in the configuration file. --- server/core/config.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ utils/skygw_utils.cc | 58 +++++++++++++++++++++++++++++ utils/skygw_utils.h | 2 +- 3 files changed, 146 insertions(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index 5c73d6963..9d6195e73 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -75,6 +75,8 @@ #include #include #include +#define PCRE2_CODE_UNIT_WIDTH 8 +#include /** According to the PCRE manual, this should be a multiple of 3 */ #define MAXSCALE_PCRE_BUFSZ 24 @@ -101,6 +103,7 @@ int config_get_ifaddr(unsigned char *output); int config_get_release_string(char* release); FEEDBACK_CONF * config_get_feedback_data(); void config_add_param(CONFIG_CONTEXT*,char*,char*); +bool config_has_duplicate_sections(const char* config); static char *config_file = NULL; static GATEWAY_CONF gateway; static FEEDBACK_CONF feedback; @@ -274,6 +277,10 @@ config_load(char *file) CONFIG_CONTEXT config; int rval, ini_rval; + if (config_has_duplicate_sections(file)) + { + return 0; + } MYSQL *conn; conn = mysql_init(NULL); if (conn) { @@ -356,6 +363,11 @@ int rval; if (!config_file) return 0; + if (config_has_duplicate_sections(config_file)) + { + return 0; + } + if (gateway.version_string) free(gateway.version_string); @@ -2629,3 +2641,78 @@ GATEWAY_CONF* config_get_global_options() { return &gateway; } + +/** + * Check if sections are defined multiple times in the configuration file. + * @param config Path to the configuration file + * @return True if duplicate sections were found or an error occurred + */ +bool config_has_duplicate_sections(const char* config) +{ + bool rval = false; + const int table_size = 10; + int errcode; + PCRE2_SIZE erroffset; + HASHTABLE *hash = hashtable_alloc(table_size, simple_str_hash, strcmp); + pcre2_code *re = pcre2_compile((PCRE2_SPTR) "^\\s*\\[(.+)\\]\\s*$", PCRE2_ZERO_TERMINATED, + 0, &errcode, &erroffset, NULL); + pcre2_match_data *mdata; + int size = 1024; + char *buffer = malloc(size * sizeof(char)); + + if (buffer && hash && re && + (mdata = pcre2_match_data_create_from_pattern(re, NULL))) + { + hashtable_memory_fns(hash, (HASHMEMORYFN) strdup, NULL, + (HASHMEMORYFN) free, NULL); + FILE* file = fopen(config, "r"); + + if (file) + { + while (maxscale_getline(&buffer, &size, file) > 0) + { + if (pcre2_match(re, (PCRE2_SPTR) buffer, + PCRE2_ZERO_TERMINATED, 0, 0, + mdata, NULL) > 0) + { + /** + * Neither of the PCRE2 calls will fail since we know the pattern + * beforehand and we allocate enough memory from the stack + */ + PCRE2_SIZE len; + pcre2_substring_length_bynumber(mdata, 1, &len); + len += 1; /** one for the null terminator */ + PCRE2_UCHAR section[len]; + pcre2_substring_copy_bynumber(mdata, 1, section, &len); + + if (hashtable_add(hash, section, "") == 0) + { + skygw_log_write(LE, "Error: Duplicate section found: %s", + section); + rval = true; + } + } + } + fclose(file); + } + else + { + char errbuf[STRERROR_BUFLEN]; + skygw_log_write(LE, "Error: Failed to open file '%s': %s", config, + strerror_r(errno, errbuf, sizeof(errbuf))); + rval = true; + } + } + else + { + skygw_log_write(LE, "Error: Failed to allocate enough memory when checking" + " for duplicate sections in configuration file."); + rval = true; + } + + hashtable_free(hash); + pcre2_code_free(re); + pcre2_match_data_free(mdata); + free(buffer); + return rval; +} diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 95391039e..521d2ec74 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -2266,3 +2266,61 @@ int simple_str_hash(char* key) return hash; } + +/** + * Read from a FILE pointer until a newline character or the end of the file is found. + * The provided buffer will be reallocated if it is too small to store the whole + * line. The size after the reallocation will be stored in @c size. The read line + * will be stored in @c dest and it will always be null terminated. The newline + * character will not be copied into the buffer. + * @param dest Pointer to a buffer of at least @c size bytes + * @param size Size of the buffer + * @param file A valid file stream + * @return When a complete line was successfully read the function returns 1. If + * the end of the file was reached before any characters were read the return value + * will be 0. If the provided buffer could not be reallocated to store the complete + * line the original size will be retained, everything read up to this point + * will be stored in it as a null terminated string and -1 will be returned. + */ +int maxscale_getline(char** dest, int* size, FILE* file) +{ + char* destptr = *dest; + int offset = 0; + + if (feof(file)) + { + return 0; + } + + while (true) + { + if (*size <= offset) + { + char* tmp = (char*) realloc(destptr, *size * 2); + if (tmp) + { + destptr = tmp; + *size *= 2; + } + else + { + skygw_log_write(LE, "Error: Failed to reallocate memory from %d" + " bytes to %d bytes when reading from file.", + *size, *size * 2); + destptr[offset - 1] = '\0'; + *dest = destptr; + return -1; + } + } + + if ((destptr[offset] = fgetc(file)) == '\n' || feof(file)) + { + destptr[offset] = '\0'; + break; + } + offset++; + } + + *dest = destptr; + return 1; +} diff --git a/utils/skygw_utils.h b/utils/skygw_utils.h index 29d1d867e..bfa48b8cc 100644 --- a/utils/skygw_utils.h +++ b/utils/skygw_utils.h @@ -281,7 +281,7 @@ char* replace_quoted(const char* str); bool is_valid_posix_path(char* path); bool strip_escape_chars(char*); int simple_str_hash(char* key); - +int maxscale_getline(char** dest, int* size, FILE* file); EXTERN_C_BLOCK_END From cab5e58045923c48a2a144cb1d3c09b93fdd8567 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 29 Oct 2015 15:14:49 +0200 Subject: [PATCH 10/28] Fixed SSL initialization for services failing. --- server/core/service.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/server/core/service.c b/server/core/service.c index 7071273ce..617ee7926 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -471,7 +471,8 @@ serviceStart(SERVICE *service) if (check_service_permissions(service)) { - if (service->ssl_mode == SSL_DISABLED || (service->ssl_mode != SSL_DISABLED && serviceInitSSL(service) != 0)) + if (service->ssl_mode == SSL_DISABLED || + (service->ssl_mode != SSL_DISABLED && serviceInitSSL(service) == 0)) { if ((service->router_instance = service->router->createInstance( service,service->routerOptions))) @@ -1999,11 +2000,11 @@ int *data; } /** - * Initialize the servce's SSL context. This sets up the generated RSA + * Initialize the service's SSL context. This sets up the generated RSA * encryption keys, chooses the server encryption level and configures the server * certificate, private key and certificate authority file. - * @param service - * @return + * @param service Service to initialize + * @return 0 on success, -1 on error */ int serviceInitSSL(SERVICE* service) { @@ -2043,7 +2044,11 @@ int serviceInitSSL(SERVICE* service) break; } - service->ctx = SSL_CTX_new(service->method); + if((service->ctx = SSL_CTX_new(service->method)) == NULL) + { + skygw_log_write(LE, "Error: SSL context initialization failed."); + return -1; + } /** Enable all OpenSSL bug fixes */ SSL_CTX_set_options(service->ctx,SSL_OP_ALL); @@ -2053,13 +2058,19 @@ int serviceInitSSL(SERVICE* service) { rsa_512 = RSA_generate_key(512,RSA_F4,NULL,NULL); if (rsa_512 == NULL) - skygw_log_write(LE,"Error: 512-bit RSA key generation failed."); + { + skygw_log_write(LE,"Error: 512-bit RSA key generation failed."); + return -1; + } } if(rsa_1024 == NULL) { rsa_1024 = RSA_generate_key(1024,RSA_F4,NULL,NULL); if (rsa_1024 == NULL) + { skygw_log_write(LE,"Error: 1024-bit RSA key generation failed."); + return -1; + } } if(rsa_512 != NULL && rsa_1024 != NULL) From f819f164b57da16e8df88d1d69d1884c51c87cf3 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 29 Oct 2015 15:38:24 +0200 Subject: [PATCH 11/28] Cleaned up dcb_accept_SSL and added missing logging. --- server/core/dcb.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 6c836a6fd..f63ae6705 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -2865,7 +2865,8 @@ int dcb_create_SSL(DCB* dcb) */ int dcb_accept_SSL(DCB* dcb) { - int rval = 0,ssl_rval,errnum = 0,fd,b = 0,pending; + int rval = 0,ssl_rval,ssl_errnum = 0,fd,b = 0,pending; + int err_errnum; char errbuf[140]; fd = dcb->fd; @@ -2874,22 +2875,20 @@ int dcb_accept_SSL(DCB* dcb) ssl_rval = SSL_accept(dcb->ssl); LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept %d, error %d", - ssl_rval,errnum))); + ssl_rval,ssl_errnum))); switch(ssl_rval) { case 0: - errnum = SSL_get_error(dcb->ssl,ssl_rval); + ssl_errnum = SSL_get_error(dcb->ssl,ssl_rval); skygw_log_write(LE,"Error: SSL authentication failed (SSL error %d):", - dcb, - dcb->remote, - errnum); + ssl_errnum); - if(errnum == SSL_ERROR_SSL || - errnum == SSL_ERROR_SYSCALL) + if(ssl_errnum == SSL_ERROR_SSL || + ssl_errnum == SSL_ERROR_SYSCALL) { - while((errnum = ERR_get_error()) != 0) + while((err_errnum = ERR_get_error()) != 0) { - ERR_error_string_n(errnum,errbuf,140); + ERR_error_string_n(err_errnum,errbuf,140); skygw_log_write(LE,"%s",errbuf); } } @@ -2903,9 +2902,9 @@ int dcb_accept_SSL(DCB* dcb) case -1: - errnum = SSL_get_error(dcb->ssl,ssl_rval); + ssl_errnum = SSL_get_error(dcb->ssl,ssl_rval); - if(errnum == SSL_ERROR_WANT_READ || errnum == SSL_ERROR_WANT_WRITE) + if(ssl_errnum == SSL_ERROR_WANT_READ || ssl_errnum == SSL_ERROR_WANT_WRITE) { /** Not all of the data has been read. Go back to the poll queue and wait for more.*/ @@ -2921,17 +2920,22 @@ int dcb_accept_SSL(DCB* dcb) "Error: Fatal error in SSL_accept for %s: (SSL version: %s SSL error code: %d)", dcb->remote, SSL_get_version(dcb->ssl), - errnum); - if(errnum == SSL_ERROR_SSL || - errnum == SSL_ERROR_SYSCALL) + ssl_errnum); + if(ssl_errnum == SSL_ERROR_SSL || + ssl_errnum == SSL_ERROR_SYSCALL) { - while((errnum = ERR_get_error()) != 0) + while((err_errnum = ERR_get_error()) != 0) { - ERR_error_string_n(errnum,errbuf,140); + ERR_error_string_n(err_errnum,errbuf,140); skygw_log_write(LE, "%s", errbuf); } + if(errno) + { + skygw_log_write(LE, "Error: SSL authentication failed due to system" + " error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); + } } } break; From c29858d21560ee2e7c2dc3d11af9d0cd58c59b88 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 29 Oct 2015 15:41:51 +0200 Subject: [PATCH 12/28] Added a definition for the size of the SSL error message buffer and changed all buffers to use it. --- server/core/dcb.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index f63ae6705..570c2587e 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -80,6 +80,8 @@ #include #include +#define SSL_ERRBUF_LEN 140 + /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; @@ -1480,8 +1482,8 @@ dcb_write_SSL_error_report (DCB *dcb, int ret, int ssl_errno) } do { - char errbuf[140]; - ERR_error_string_n(ssl_errno,errbuf,140); + char errbuf[SSL_ERRBUF_LEN]; + ERR_error_string_n(ssl_errno,errbuf, sizeof(errbuf)); skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); } while((ssl_errno = ERR_get_error()) != 0); } @@ -1490,8 +1492,8 @@ dcb_write_SSL_error_report (DCB *dcb, int ret, int ssl_errno) { do { - char errbuf[140]; - ERR_error_string_n(ssl_errno,errbuf,140); + char errbuf[SSL_ERRBUF_LEN]; + ERR_error_string_n(ssl_errno,errbuf,sizeof(errbuf)); skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); } while((ssl_errno = ERR_get_error()) != 0); } @@ -1646,8 +1648,8 @@ dcb_drain_writeq_SSL(DCB *dcb) case SSL_ERROR_SYSCALL: while((ssl_errno = ERR_get_error()) != 0) { - char errbuf[140]; - ERR_error_string_n(ssl_errno,errbuf,140); + char errbuf[SSL_ERRBUF_LEN]; + ERR_error_string_n(ssl_errno,errbuf,sizeof(errbuf)); skygw_log_write(LE,"%s",errbuf); } if(errno != 0) @@ -2867,7 +2869,7 @@ int dcb_accept_SSL(DCB* dcb) { int rval = 0,ssl_rval,ssl_errnum = 0,fd,b = 0,pending; int err_errnum; - char errbuf[140]; + char errbuf[SSL_ERRBUF_LEN]; fd = dcb->fd; do @@ -2888,7 +2890,7 @@ int dcb_accept_SSL(DCB* dcb) { while((err_errnum = ERR_get_error()) != 0) { - ERR_error_string_n(err_errnum,errbuf,140); + ERR_error_string_n(err_errnum,errbuf,sizeof(errbuf)); skygw_log_write(LE,"%s",errbuf); } } @@ -2926,7 +2928,7 @@ int dcb_accept_SSL(DCB* dcb) { while((err_errnum = ERR_get_error()) != 0) { - ERR_error_string_n(err_errnum,errbuf,140); + ERR_error_string_n(err_errnum,errbuf,sizeof(errbuf)); skygw_log_write(LE, "%s", errbuf); @@ -2969,7 +2971,7 @@ int dcb_accept_SSL(DCB* dcb) int dcb_connect_SSL(DCB* dcb) { int rval,errnum; - char errbuf[140]; + char errbuf[SSL_ERRBUF_LEN]; rval = SSL_connect(dcb->ssl); switch(rval) @@ -3004,7 +3006,7 @@ int dcb_connect_SSL(DCB* dcb) else { rval = -1; - ERR_error_string_n(errnum,errbuf,140); + ERR_error_string_n(errnum,errbuf,sizeo(errbuf)); skygw_log_write_flush(LE, "Error: Fatal error in SSL_accept for %s@%s: (SSL error code: %d) %s", dcb->user, From 55076243264f789dd97904513d900e64f27929be Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 29 Oct 2015 18:19:05 +0200 Subject: [PATCH 13/28] Fixed typo'd sizeof. --- server/core/dcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 570c2587e..19d95a177 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -3006,7 +3006,7 @@ int dcb_connect_SSL(DCB* dcb) else { rval = -1; - ERR_error_string_n(errnum,errbuf,sizeo(errbuf)); + ERR_error_string_n(errnum,errbuf,sizeof(errbuf)); skygw_log_write_flush(LE, "Error: Fatal error in SSL_accept for %s@%s: (SSL error code: %d) %s", dcb->user, From 1f22e2854b5b27c6295775a1b03c936917dd73a8 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 30 Oct 2015 09:38:40 +0200 Subject: [PATCH 14/28] maxscale_getline moved to config.c As a general purpose function should be somewhere else, but we need to sort out what should be where first. --- server/core/config.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ utils/skygw_utils.cc | 58 ------------------------------------------ utils/skygw_utils.h | 1 - 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 9d6195e73..549b35ce4 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -97,6 +97,7 @@ static int handle_feedback_item(const char *, const char *); static void global_defaults(); static void feedback_defaults(); static void check_config_objects(CONFIG_CONTEXT *context); +static int maxscale_getline(char** dest, int* size, FILE* file); int config_truth_value(char *str); bool isInternalService(char *router); int config_get_ifaddr(unsigned char *output); @@ -2716,3 +2717,62 @@ bool config_has_duplicate_sections(const char* config) free(buffer); return rval; } + + +/** + * Read from a FILE pointer until a newline character or the end of the file is found. + * The provided buffer will be reallocated if it is too small to store the whole + * line. The size after the reallocation will be stored in @c size. The read line + * will be stored in @c dest and it will always be null terminated. The newline + * character will not be copied into the buffer. + * @param dest Pointer to a buffer of at least @c size bytes + * @param size Size of the buffer + * @param file A valid file stream + * @return When a complete line was successfully read the function returns 1. If + * the end of the file was reached before any characters were read the return value + * will be 0. If the provided buffer could not be reallocated to store the complete + * line the original size will be retained, everything read up to this point + * will be stored in it as a null terminated string and -1 will be returned. + */ +int maxscale_getline(char** dest, int* size, FILE* file) +{ + char* destptr = *dest; + int offset = 0; + + if (feof(file)) + { + return 0; + } + + while (true) + { + if (*size <= offset) + { + char* tmp = (char*) realloc(destptr, *size * 2); + if (tmp) + { + destptr = tmp; + *size *= 2; + } + else + { + skygw_log_write(LE, "Error: Failed to reallocate memory from %d" + " bytes to %d bytes when reading from file.", + *size, *size * 2); + destptr[offset - 1] = '\0'; + *dest = destptr; + return -1; + } + } + + if ((destptr[offset] = fgetc(file)) == '\n' || feof(file)) + { + destptr[offset] = '\0'; + break; + } + offset++; + } + + *dest = destptr; + return 1; +} diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 521d2ec74..95391039e 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -2266,61 +2266,3 @@ int simple_str_hash(char* key) return hash; } - -/** - * Read from a FILE pointer until a newline character or the end of the file is found. - * The provided buffer will be reallocated if it is too small to store the whole - * line. The size after the reallocation will be stored in @c size. The read line - * will be stored in @c dest and it will always be null terminated. The newline - * character will not be copied into the buffer. - * @param dest Pointer to a buffer of at least @c size bytes - * @param size Size of the buffer - * @param file A valid file stream - * @return When a complete line was successfully read the function returns 1. If - * the end of the file was reached before any characters were read the return value - * will be 0. If the provided buffer could not be reallocated to store the complete - * line the original size will be retained, everything read up to this point - * will be stored in it as a null terminated string and -1 will be returned. - */ -int maxscale_getline(char** dest, int* size, FILE* file) -{ - char* destptr = *dest; - int offset = 0; - - if (feof(file)) - { - return 0; - } - - while (true) - { - if (*size <= offset) - { - char* tmp = (char*) realloc(destptr, *size * 2); - if (tmp) - { - destptr = tmp; - *size *= 2; - } - else - { - skygw_log_write(LE, "Error: Failed to reallocate memory from %d" - " bytes to %d bytes when reading from file.", - *size, *size * 2); - destptr[offset - 1] = '\0'; - *dest = destptr; - return -1; - } - } - - if ((destptr[offset] = fgetc(file)) == '\n' || feof(file)) - { - destptr[offset] = '\0'; - break; - } - offset++; - } - - *dest = destptr; - return 1; -} diff --git a/utils/skygw_utils.h b/utils/skygw_utils.h index bfa48b8cc..083842291 100644 --- a/utils/skygw_utils.h +++ b/utils/skygw_utils.h @@ -281,7 +281,6 @@ char* replace_quoted(const char* str); bool is_valid_posix_path(char* path); bool strip_escape_chars(char*); int simple_str_hash(char* key); -int maxscale_getline(char** dest, int* size, FILE* file); EXTERN_C_BLOCK_END From b543f36a49c24f4c54a556fe48a3c0420f084148 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Thu, 29 Oct 2015 15:07:30 +0200 Subject: [PATCH 15/28] Indentation and whitespace fixes. There were many combinations of tab-width used so making the indentation look right in the editor wasn't really possible. The changes made: - All tabs replaced with spaces. - Indentation depth 4. - , followed by space. - Most binary operators (*, =, -) surrounded by one space. - No space following ( or before ). - Keywords follwed by 1 space. --- server/core/gateway.c | 3713 +++++++++++++++++++++-------------------- 1 file changed, 1860 insertions(+), 1853 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index fedf28f39..62f5afabc 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -3,18 +3,18 @@ * software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, * version 2. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. - * + * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * + * * Copyright MariaDB Corporation Ab 2013-2015 - * + * */ /** @@ -23,19 +23,19 @@ * @verbatim * Revision History * - * Date Who Description - * 23-05-2013 Massimiliano Pinto epoll loop test - * 12-06-2013 Mark Riddoch Add the -p option to set the - * listening port - * and bind addr is 0.0.0.0 - * 19/06/13 Mark Riddoch Extract the epoll functionality - * 21/06/13 Mark Riddoch Added initial config support + * Date Who Description + * 23-05-2013 Massimiliano Pinto epoll loop test + * 12-06-2013 Mark Riddoch Add the -p option to set the + * listening port + * and bind addr is 0.0.0.0 + * 19/06/13 Mark Riddoch Extract the epoll functionality + * 21/06/13 Mark Riddoch Added initial config support * 27/06/13 - * 28/06/13 Vilho Raatikka Added necessary headers, example functions and - * calls to log manager and to query classifier. - * Put example code behind SS_DEBUG macros. - * 05/02/14 Mark Riddoch Addition of version string - * 29/06/14 Massimiliano Pinto Addition of pidfile + * 28/06/13 Vilho Raatikka Added necessary headers, example functions and + * calls to log manager and to query classifier. + * Put example code behind SS_DEBUG macros. + * 05/02/14 Mark Riddoch Addition of version string + * 29/06/14 Massimiliano Pinto Addition of pidfile * 10/08/15 Markus Makela Added configurable directory locations * @endverbatim */ @@ -43,12 +43,12 @@ #define OPENSSL_THREAD_DEFINES #include - #include - #if defined(OPENSSL_THREADS) +#include +#if defined(OPENSSL_THREADS) #define HAVE_OPENSSL_THREADS 1 - #else +#else #define HAVE_OPENSSL_THREADS 0 - #endif +#endif #include #include @@ -96,7 +96,7 @@ # define _GNU_SOURCE #endif -time_t MaxScaleStarted; +time_t MaxScaleStarted; extern char *program_invocation_name; extern char *program_invocation_short_name; @@ -139,10 +139,10 @@ static char* server_groups[] = { }; /* The data directory we created for this gateway instance */ -static char datadir[PATH_MAX+1] = ""; -static bool datadir_defined = false; /*< If the datadir was already set */ +static char datadir[PATH_MAX + 1] = ""; +static bool datadir_defined = false; /*< If the datadir was already set */ /* The data directory we created for this gateway instance */ -static char pidfile[PATH_MAX+1] = ""; +static char pidfile[PATH_MAX + 1] = ""; static int pidfd = PIDFD_CLOSED; /** @@ -164,25 +164,25 @@ static const char* maxscale_commit = MAXSCALE_COMMIT; const char *progname = NULL; static struct option long_options[] = { - {"homedir", required_argument, 0, 'c'}, - {"config", required_argument, 0, 'f'}, - {"nodaemon", no_argument, 0, 'd'}, - {"log", required_argument, 0, 'l'}, - {"logdir", required_argument, 0, 'L'}, - {"datadir", required_argument, 0, 'D'}, - {"configdir",required_argument, 0, 'C'}, - {"piddir",required_argument, 0, 'P'}, - {"libdir",required_argument, 0, 'B'}, - {"cachedir",required_argument, 0, 'A'}, - {"language",required_argument, 0, 'N'}, - {"syslog", required_argument, 0, 's'}, - {"maxscalelog",required_argument,0,'S'}, - {"user",required_argument,0,'U'}, - {"version", no_argument, 0, 'v'}, - {"help", no_argument, 0, '?'}, - {"version-full", no_argument, 0, 'V'}, - {"log_augmentation", required_argument, 0, 'G'}, - {0, 0, 0, 0} + {"homedir", required_argument, 0, 'c'}, + {"config", required_argument, 0, 'f'}, + {"nodaemon", no_argument, 0, 'd'}, + {"log", required_argument, 0, 'l'}, + {"logdir", required_argument, 0, 'L'}, + {"datadir", required_argument, 0, 'D'}, + {"configdir", required_argument, 0, 'C'}, + {"piddir", required_argument, 0, 'P'}, + {"libdir", required_argument, 0, 'B'}, + {"cachedir", required_argument, 0, 'A'}, + {"language", required_argument, 0, 'N'}, + {"syslog", required_argument, 0, 's'}, + {"maxscalelog", required_argument, 0, 'S'}, + {"user", required_argument, 0, 'U'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, '?'}, + {"version-full", no_argument, 0, 'V'}, + {"log_augmentation", required_argument, 0, 'G'}, + {0, 0, 0, 0} }; static int cnf_preparser(void* data, const char* section, const char* name, const char* value); static void log_flush_shutdown(void); @@ -201,21 +201,21 @@ bool handle_path_arg(char** dest, char* path, char* arg, bool rd, bool wr); static void set_log_augmentation(const char* value); static void usage(void); static char* get_expanded_pathname( - char** abs_path, - char* input_path, - const char* fname); + char** abs_path, + char* input_path, + const char* fname); static void print_log_n_stderr( - bool do_log, - bool do_stderr, - char* logstr, - char* fprstr, - int eno); + bool do_log, + bool do_stderr, + char* logstr, + char* fprstr, + int eno); static bool resolve_maxscale_conf_fname( - char** cnf_full_path, - char* home_dir, - char* cnf_file_arg); + char** cnf_full_path, + char* home_dir, + char* cnf_file_arg); -static char* check_dir_access(char* dirname,bool,bool); +static char* check_dir_access(char* dirname, bool, bool); static int set_user(); bool pid_file_exists(); void write_child_exit_code(int fd, int code); @@ -223,12 +223,12 @@ void write_child_exit_code(int fd, int code); static SPINLOCK* ssl_locks; -static void ssl_locking_function(int mode,int n,const char* file, int line) +static void ssl_locking_function(int mode, int n, const char* file, int line) { - if(mode & CRYPTO_LOCK) - spinlock_acquire(&ssl_locks[n]); + if (mode & CRYPTO_LOCK) + spinlock_acquire(&ssl_locks[n]); else - spinlock_release(&ssl_locks[n]); + spinlock_release(&ssl_locks[n]); } /** * OpenSSL requires this struct to be defined in order to use dynamic locks @@ -248,9 +248,9 @@ struct CRYPTO_dynlock_value static struct CRYPTO_dynlock_value *ssl_create_dynlock(const char* file, int line) { struct CRYPTO_dynlock_value* lock = malloc(sizeof(struct CRYPTO_dynlock_value)); - if(lock) + if (lock) { - spinlock_init(&lock->lock); + spinlock_init(&lock->lock); } return lock; } @@ -262,15 +262,15 @@ static struct CRYPTO_dynlock_value *ssl_create_dynlock(const char* file, int lin * @param file File name * @param line Line number */ -static void ssl_lock_dynlock(int mode,struct CRYPTO_dynlock_value * n,const char* file, int line) +static void ssl_lock_dynlock(int mode, struct CRYPTO_dynlock_value * n, const char* file, int line) { - if(mode & CRYPTO_LOCK) + if (mode & CRYPTO_LOCK) { - spinlock_acquire(&n->lock); + spinlock_acquire(&n->lock); } else { - spinlock_release(&n->lock); + spinlock_release(&n->lock); } } @@ -280,7 +280,7 @@ static void ssl_lock_dynlock(int mode,struct CRYPTO_dynlock_value * n,const char * @param file File name * @param line Line number */ -static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, int line) +static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n, const char* file, int line) { free(n); } @@ -292,7 +292,7 @@ static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, i */ static void maxscale_ssl_id(CRYPTO_THREADID* id) { - CRYPTO_THREADID_set_numeric(id,pthread_self()); + CRYPTO_THREADID_set_numeric(id, pthread_self()); } #endif @@ -302,48 +302,48 @@ static void maxscale_ssl_id(CRYPTO_THREADID* id) */ static void sighup_handler (int i) { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Refreshing configuration following SIGHUP\n"))); - config_reload(); + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "Refreshing configuration following SIGHUP\n"))); + config_reload(); } /** - * Handler for SIGUSR1 signal. A SIGUSR1 signal will cause + * Handler for SIGUSR1 signal. A SIGUSR1 signal will cause * maxscale to rotate all log files. */ static void sigusr1_handler (int i) { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Log file flush following reception of SIGUSR1\n"))); - skygw_log_rotate(LOGFILE_ERROR); - skygw_log_rotate(LOGFILE_MESSAGE); - skygw_log_rotate(LOGFILE_TRACE); - skygw_log_rotate(LOGFILE_DEBUG); + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "Log file flush following reception of SIGUSR1\n"))); + skygw_log_rotate(LOGFILE_ERROR); + skygw_log_rotate(LOGFILE_MESSAGE); + skygw_log_rotate(LOGFILE_TRACE); + skygw_log_rotate(LOGFILE_DEBUG); } static void sigterm_handler (int i) { - extern void shutdown_server(); - - skygw_log_write_flush( - LOGFILE_ERROR, - "MaxScale received signal SIGTERM. Exiting."); - skygw_log_sync_all(); - shutdown_server(); + extern void shutdown_server(); + + skygw_log_write_flush( + LOGFILE_ERROR, + "MaxScale received signal SIGTERM. Exiting."); + skygw_log_sync_all(); + shutdown_server(); } static void sigint_handler (int i) { - extern void shutdown_server(); + extern void shutdown_server(); - skygw_log_write_flush( - LOGFILE_ERROR, - "MaxScale received signal SIGINT. Shutting down."); - skygw_log_sync_all(); - shutdown_server(); - fprintf(stderr, "\n\nShutting down MaxScale\n\n"); + skygw_log_write_flush( + LOGFILE_ERROR, + "MaxScale received signal SIGINT. Shutting down."); + skygw_log_sync_all(); + shutdown_server(); + fprintf(stderr, "\n\nShutting down MaxScale\n\n"); } static void @@ -352,87 +352,87 @@ sigchld_handler (int i) int exit_status = 0; pid_t child = -1; - if((child = wait(&exit_status)) == -1) + if ((child = wait(&exit_status)) == -1) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write_flush(LE,"Error: failed to wait child process: %d %s", - errno,strerror_r(errno, errbuf, sizeof(errbuf))); + skygw_log_write_flush(LE, "Error: failed to wait child process: %d %s", + errno, strerror_r(errno, errbuf, sizeof(errbuf))); } else { - if(WIFEXITED(exit_status)) - { - skygw_log_write_flush(WEXITSTATUS(exit_status) != 0 ? LE : LT, - "Child process %d exited with status %d", - child,WEXITSTATUS(exit_status)); - } - else if(WIFSIGNALED(exit_status)) - { - skygw_log_write_flush((LE|LT), - "Child process %d was stopped by signal %d.", - child,WTERMSIG(exit_status)); - } - else - { - skygw_log_write_flush((LE|LT), - "Child process %d did not exit normally. Exit status: %d", - child,exit_status); - } + if (WIFEXITED(exit_status)) + { + skygw_log_write_flush(WEXITSTATUS(exit_status) != 0 ? LE : LT, + "Child process %d exited with status %d", + child, WEXITSTATUS(exit_status)); + } + else if (WIFSIGNALED(exit_status)) + { + skygw_log_write_flush((LE|LT), + "Child process %d was stopped by signal %d.", + child, WTERMSIG(exit_status)); + } + else + { + skygw_log_write_flush((LE|LT), + "Child process %d did not exit normally. Exit status: %d", + child, exit_status); + } } } int fatal_handling = 0; -static int signal_set (int sig, void (*handler)(int)); +static int signal_set(int sig, void (*handler)(int)); static void -sigfatal_handler (int i) +sigfatal_handler(int i) { - if (fatal_handling) { - fprintf(stderr, "Fatal signal %d while backtracing\n", i); - _exit(1); - } - fatal_handling = 1; - GATEWAY_CONF* cnf = config_get_global_options(); - fprintf(stderr, "\n\nMaxScale "MAXSCALE_VERSION" received fatal signal %d\n", i); + if (fatal_handling) { + fprintf(stderr, "Fatal signal %d while backtracing\n", i); + _exit(1); + } + fatal_handling = 1; + GATEWAY_CONF* cnf = config_get_global_options(); + fprintf(stderr, "\n\nMaxScale "MAXSCALE_VERSION" received fatal signal %d\n", i); - skygw_log_write_flush( - LOGFILE_ERROR, - "Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i); + skygw_log_write_flush( + LOGFILE_ERROR, + "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", - maxscale_commit, cnf->sysname, cnf->release_string, cnf->version_string); + skygw_log_write_flush(LE, "Commit ID: %s System name: %s " + "Release string: %s Embedded library version: %s", + maxscale_commit, cnf->sysname, cnf->release_string, cnf->version_string); - { - void *addrs[128]; - int n, count = backtrace(addrs, 128); - char** symbols = backtrace_symbols( addrs, count ); + { + void *addrs[128]; + int n, count = backtrace(addrs, 128); + char** symbols = backtrace_symbols(addrs, count); - if (symbols) { - for( n = 0; n < count; n++ ) { - skygw_log_write_flush( - LOGFILE_ERROR, - " %s\n", symbols[n]); - } - free(symbols); - } else { - fprintf(stderr, "\nresolving symbols to error log failed, writing call trace to stderr:\n"); - backtrace_symbols_fd(addrs, count, fileno(stderr)); - } - } + if (symbols) { + for (n = 0; n < count; n++) { + skygw_log_write_flush( + LOGFILE_ERROR, + " %s\n", symbols[n]); + } + free(symbols); + } else { + fprintf(stderr, "\nresolving symbols to error log failed, writing call trace to stderr:\n"); + backtrace_symbols_fd(addrs, count, fileno(stderr)); + } + } - skygw_log_sync_all(); + skygw_log_sync_all(); - /* re-raise signal to enforce core dump */ - fprintf(stderr, "\n\nWriting core dump\n"); - signal_set(i, SIG_DFL); - raise(i); + /* re-raise signal to enforce core dump */ + fprintf(stderr, "\n\nWriting core dump\n"); + signal_set(i, SIG_DFL); + raise(i); } -/** +/** * @node Wraps sigaction calls * * Parameters: @@ -441,33 +441,33 @@ sigfatal_handler (int i) * * @return 0 in success, 1 otherwise * - * + * * @details (write detailed description here) * */ -static int signal_set (int sig, void (*handler)(int)) { - static struct sigaction sigact; - static int err; - int rc = 0; +static int signal_set(int sig, void (*handler)(int)) { + static struct sigaction sigact; + static int err; + int rc = 0; - memset(&sigact, 0, sizeof(struct sigaction)); - sigact.sa_handler = handler; - GW_NOINTR_CALL(err = sigaction(sig, &sigact, NULL)); + memset(&sigact, 0, sizeof(struct sigaction)); + sigact.sa_handler = handler; + GW_NOINTR_CALL(err = sigaction(sig, &sigact, NULL)); - if (err < 0) - { - int eno = errno; - errno = 0; - char errbuf[STRERROR_BUFLEN]; - 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))); - rc = 1; - } - return rc; + if (err < 0) + { + int eno = errno; + errno = 0; + char errbuf[STRERROR_BUFLEN]; + 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))); + rc = 1; + } + return rc; } @@ -476,294 +476,294 @@ static int signal_set (int sig, void (*handler)(int)) { * Cleanup the temporary data directory we created for the gateway */ int ntfw_cb( - const char* filename, - const struct stat* filestat, - int fileflags, - struct FTW* pfwt) + const char* filename, + const struct stat* filestat, + int fileflags, + struct FTW* pfwt) { - int rc = remove(filename); + int rc = remove(filename); - if (rc != 0) - { - int eno = errno; - errno = 0; - char errbuf[STRERROR_BUFLEN]; - 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))); - } - return rc; + if (rc != 0) + { + int eno = errno; + errno = 0; + char errbuf[STRERROR_BUFLEN]; + 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))); + } + return rc; } void datadir_cleanup() { - int depth = 1; - int flags = FTW_CHDIR|FTW_DEPTH|FTW_MOUNT; + int depth = 1; + int flags = FTW_CHDIR|FTW_DEPTH|FTW_MOUNT; - if (datadir[0] != 0 && access(datadir, F_OK) == 0) - { - nftw(datadir, ntfw_cb, depth, flags); - } + if (datadir[0] != 0 && access(datadir, F_OK) == 0) + { + nftw(datadir, ntfw_cb, depth, flags); + } } static void libmysqld_done(void) { - if (libmysqld_started) { - mysql_library_end(); - } + if (libmysqld_started) { + mysql_library_end(); + } } static void write_footer(void) { - file_write_footer(stdout); + file_write_footer(stdout); } static bool file_write_footer( - FILE* outfile) + FILE* outfile) { - bool succp = false; - size_t len1; - const char* header_buf1; + bool succp = false; + size_t len1; + const char* header_buf1; - header_buf1 = "------------------------------------------------------" - "\n\n"; - len1 = strlen(header_buf1); - fwrite((void*)header_buf1, len1, 1, outfile); + header_buf1 = "------------------------------------------------------" + "\n\n"; + len1 = strlen(header_buf1); + fwrite((void*)header_buf1, len1, 1, outfile); - succp = true; + succp = true; - return succp; + return succp; } static bool file_write_header( - FILE* outfile) + FILE* outfile) { - bool succp = false; - size_t len1; - size_t len2; - size_t len3; - const char* header_buf1; - char* header_buf2 = NULL; - const char* header_buf3; - time_t* t = NULL; - struct tm* tm = NULL; + bool succp = false; + size_t len1; + size_t len2; + size_t len3; + const char* header_buf1; + char* header_buf2 = NULL; + const char* header_buf3; + time_t* t = NULL; + struct tm* tm = NULL; #if defined(LAPTOP_TEST) - struct timespec ts1; - ts1.tv_sec = 0; - ts1.tv_nsec = DISKWRITE_LATENCY*1000000; + struct timespec ts1; + ts1.tv_sec = 0; + ts1.tv_nsec = DISKWRITE_LATENCY * 1000000; #endif #if !defined(SS_DEBUG) - return true; -#endif - - if ((t = (time_t *)malloc(sizeof(time_t))) == NULL) { - goto return_succp; - } - - if ((tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) { - goto return_succp; - } - - *t = time(NULL); - *tm = *localtime(t); - - header_buf1 = "\n\nMariaDB Corporation MaxScale " MAXSCALE_VERSION "\t"; - header_buf2 = strdup(asctime(tm)); - - if (header_buf2 == NULL) { - goto return_succp; - } - header_buf3 = "------------------------------------------------------\n"; - - len1 = strlen(header_buf1); - len2 = strlen(header_buf2); - len3 = strlen(header_buf3); -#if defined(LAPTOP_TEST) - nanosleep(&ts1, NULL); -#else - fwrite((void*)header_buf1, len1, 1, outfile); - fwrite((void*)header_buf2, len2, 1, outfile); - fwrite((void*)header_buf3, len3, 1, outfile); + return true; #endif - - succp = true; + + if ((t = (time_t *)malloc(sizeof(time_t))) == NULL) { + goto return_succp; + } + + if ((tm = (struct tm *)malloc(sizeof(struct tm))) == NULL) { + goto return_succp; + } + + *t = time(NULL); + *tm = *localtime(t); + + header_buf1 = "\n\nMariaDB Corporation MaxScale " MAXSCALE_VERSION "\t"; + header_buf2 = strdup(asctime(tm)); + + if (header_buf2 == NULL) { + goto return_succp; + } + header_buf3 = "------------------------------------------------------\n"; + + len1 = strlen(header_buf1); + len2 = strlen(header_buf2); + len3 = strlen(header_buf3); +#if defined(LAPTOP_TEST) + nanosleep(&ts1, NULL); +#else + fwrite((void*)header_buf1, len1, 1, outfile); + fwrite((void*)header_buf2, len2, 1, outfile); + fwrite((void*)header_buf3, len3, 1, outfile); +#endif + + succp = true; return_succp: - if (tm != NULL) { - free(tm); - } - if (t != NULL) { - free(t); - } - if (header_buf2 != NULL) { - free(header_buf2); - } - return succp; + if (tm != NULL) { + free(tm); + } + if (t != NULL) { + free(t); + } + if (header_buf2 != NULL) { + free(header_buf2); + } + return succp; } static bool resolve_maxscale_conf_fname( - char** cnf_full_path, - char* home_dir, - char* cnf_file_arg) + char** cnf_full_path, + char* home_dir, + char* cnf_file_arg) { - bool succp = false; - - if (cnf_file_arg != NULL) - { - char* home_etc_dir; - /*< - * 1. argument is valid full pathname - * '- /home/jdoe/MaxScale/myconf.cnf' - */ - if (file_is_readable(cnf_file_arg)) - { - *cnf_full_path = cnf_file_arg; - succp = true; - goto return_succp; - } - /*< - * 2. argument is file name only and file is located in home - * directory. - * '-f MaxScale.cnf' - */ - *cnf_full_path = get_expanded_pathname(NULL, - home_dir, - cnf_file_arg); + bool succp = false; - if (*cnf_full_path != NULL) - { - if (file_is_readable(*cnf_full_path)) - { - succp = true; - goto return_succp; - } - else - { - char* logstr = "Found config file but wasn't " - "able to read it."; - int eno = errno; - print_log_n_stderr(true, true, logstr, logstr, eno); - goto return_succp; - } - } - else - { - /** Allocate memory for use of realpath */ - *cnf_full_path = (char *)malloc(PATH_MAX+1); - } - /*< - * 3. argument is valid relative pathname - * '-f ../myconf.cnf' - */ - if (realpath(cnf_file_arg, *cnf_full_path) != NULL) - { - if (file_is_readable(*cnf_full_path)) - { - succp = true; - goto return_succp; - } - else - { - char* logstr = "Failed to open read access to " - "config file."; - int eno = errno; - print_log_n_stderr(true, true, logstr, logstr, eno); - goto return_succp; - } - } - else - { - char* logstr = "Failed to expand config file name to " - "complete path."; - int eno = errno; - errno = 0; - print_log_n_stderr(true, true, logstr, logstr, eno); - goto return_succp; - } + if (cnf_file_arg != NULL) + { + char* home_etc_dir; + /*< + * 1. argument is valid full pathname + * '- /home/jdoe/MaxScale/myconf.cnf' + */ + if (file_is_readable(cnf_file_arg)) + { + *cnf_full_path = cnf_file_arg; + succp = true; + goto return_succp; } - else /*< default config file name is used */ - { - *cnf_full_path = get_expanded_pathname(NULL, home_dir, default_cnf_fname); + /*< + * 2. argument is file name only and file is located in home + * directory. + * '-f MaxScale.cnf' + */ + *cnf_full_path = get_expanded_pathname(NULL, + home_dir, + cnf_file_arg); - if (*cnf_full_path != NULL) - { - if (file_is_readable(*cnf_full_path)) - { - succp = true; - goto return_succp; - } - else - { - char* logstr = "Found config file but wasn't " - "able to read it."; - int eno = errno; - print_log_n_stderr(true, true, logstr, logstr, eno); - goto return_succp; - } - } + if (*cnf_full_path != NULL) + { + if (file_is_readable(*cnf_full_path)) + { + succp = true; goto return_succp; + } + else + { + char* logstr = "Found config file but wasn't " + "able to read it."; + int eno = errno; + print_log_n_stderr(true, true, logstr, logstr, eno); + goto return_succp; + } } + else + { + /** Allocate memory for use of realpath */ + *cnf_full_path = (char *)malloc(PATH_MAX+1); + } + /*< + * 3. argument is valid relative pathname + * '-f ../myconf.cnf' + */ + if (realpath(cnf_file_arg, *cnf_full_path) != NULL) + { + if (file_is_readable(*cnf_full_path)) + { + succp = true; + goto return_succp; + } + else + { + char* logstr = "Failed to open read access to " + "config file."; + int eno = errno; + print_log_n_stderr(true, true, logstr, logstr, eno); + goto return_succp; + } + } + else + { + char* logstr = "Failed to expand config file name to " + "complete path."; + int eno = errno; + errno = 0; + print_log_n_stderr(true, true, logstr, logstr, eno); + goto return_succp; + } + } + else /*< default config file name is used */ + { + *cnf_full_path = get_expanded_pathname(NULL, home_dir, default_cnf_fname); + + if (*cnf_full_path != NULL) + { + if (file_is_readable(*cnf_full_path)) + { + succp = true; + goto return_succp; + } + else + { + char* logstr = "Found config file but wasn't " + "able to read it."; + int eno = errno; + print_log_n_stderr(true, true, logstr, logstr, eno); + goto return_succp; + } + } + goto return_succp; + } return_succp: - return succp; + return succp; } /** * Check read and write accessibility to a directory. - * @param dirname directory to be checked - * - * @return NULL if directory can be read and written, an error message if either - * read or write is not permitted. + * @param dirname directory to be checked + * + * @return NULL if directory can be read and written, an error message if either + * read or write is not permitted. */ static char* check_dir_access( - char* dirname, bool rd, bool wr) + char* dirname, bool rd, bool wr) { - char errbuf[PATH_MAX*2]; - char* errstr = NULL; - - if (dirname == NULL) - { - errstr = strdup("Directory argument is NULL"); - goto retblock; - } + char errbuf[PATH_MAX * 2]; + char* errstr = NULL; - if(access(dirname,F_OK) != 0) - { - snprintf(errbuf,PATH_MAX*2-1,"Can't access '%s'.",dirname); - errbuf[PATH_MAX*2-1] = '\0'; - errstr = strdup(errbuf); - goto retblock; - } + if (dirname == NULL) + { + errstr = strdup("Directory argument is NULL"); + goto retblock; + } - if (rd && !file_is_readable(dirname)) - { - snprintf(errbuf,PATH_MAX*2-1,"MaxScale doesn't have read permission " - "to '%s'.",dirname); - errbuf[PATH_MAX*2-1] = '\0'; - errstr = strdup(errbuf); - goto retblock; - } - - if (wr && !file_is_writable(dirname)) - { - snprintf(errbuf,PATH_MAX*2-1,"MaxScale doesn't have write permission " - "to '%s'.",dirname); - errbuf[PATH_MAX*2-1] = '\0'; - errstr = strdup(errbuf); - goto retblock; - } + if (access(dirname, F_OK) != 0) + { + snprintf(errbuf, PATH_MAX * 2 - 1, "Can't access '%s'.", dirname); + errbuf[PATH_MAX * 2 - 1] = '\0'; + errstr = strdup(errbuf); + goto retblock; + } + + if (rd && !file_is_readable(dirname)) + { + snprintf(errbuf, PATH_MAX * 2 - 1, "MaxScale doesn't have read permission " + "to '%s'.", dirname); + errbuf[PATH_MAX * 2 - 1] = '\0'; + errstr = strdup(errbuf); + goto retblock; + } + + if (wr && !file_is_writable(dirname)) + { + snprintf(errbuf, PATH_MAX * 2 - 1, "MaxScale doesn't have write permission " + "to '%s'.", dirname); + errbuf[PATH_MAX * 2 - 1] = '\0'; + errstr = strdup(errbuf); + goto retblock; + } retblock: - return errstr; + return errstr; } -/** +/** * @node Provides error printing for non-formatted error strings. * * @param do_log Specifies whether printing to log is enabled @@ -777,104 +777,104 @@ retblock: * @param eno Errno, if it is set, zero, otherwise */ static void print_log_n_stderr( - bool do_log, /*< is printing to log enabled */ - bool do_stderr,/*< is printing to stderr enabled */ - char* logstr, /*< string to be printed to log */ - char* fprstr, /*< string to be printed to stderr */ - int eno) /*< errno, if it is set, zero, otherwise */ + bool do_log, /*< is printing to log enabled */ + bool do_stderr,/*< is printing to stderr enabled */ + char* logstr, /*< string to be printed to log */ + char* fprstr, /*< string to be printed to stderr */ + int eno) /*< errno, if it is set, zero, otherwise */ { - char* log_err = "Error :"; - char* fpr_err = "*\n* Error :"; - char* fpr_end = "\n*\n"; - - if (do_log) { - char errbuf[STRERROR_BUFLEN]; - skygw_log_write_flush( - LOGFILE_ERROR, - "%s %s %s %s", - log_err, - logstr, - eno == 0 ? " " : "Error :", - 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_r(eno, errbuf, sizeof(errbuf)), - fpr_end); - } + char* log_err = "Error :"; + char* fpr_err = "*\n* Error :"; + char* fpr_end = "\n*\n"; + + if (do_log) { + char errbuf[STRERROR_BUFLEN]; + skygw_log_write_flush( + LOGFILE_ERROR, + "%s %s %s %s", + log_err, + logstr, + eno == 0 ? " " : "Error :", + 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_r(eno, errbuf, sizeof(errbuf)), + fpr_end); + } } static bool file_is_readable( - char* absolute_pathname) + char* absolute_pathname) { - bool succp = true; + bool succp = true; - if (access(absolute_pathname, R_OK) != 0) + if (access(absolute_pathname, R_OK) != 0) + { + int eno = errno; + errno = 0; + char errbuf[STRERROR_BUFLEN]; + + if (!daemon_mode) { - int eno = errno; - errno = 0; - char errbuf[STRERROR_BUFLEN]; - - if (!daemon_mode) - { - fprintf(stderr, - "*\n* Warning : Failed to read the configuration " - "file %s. %s.\n*\n", - absolute_pathname, - strerror_r(eno, errbuf, sizeof(errbuf))); - } - 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))); - skygw_log_sync_all(); - succp = false; + fprintf(stderr, + "*\n* Warning : Failed to read the configuration " + "file %s. %s.\n*\n", + absolute_pathname, + strerror_r(eno, errbuf, sizeof(errbuf))); } - return succp; + 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))); + skygw_log_sync_all(); + succp = false; + } + return succp; } static bool file_is_writable( - char* absolute_pathname) + char* absolute_pathname) { - bool succp = true; + bool succp = true; - if (access(absolute_pathname, W_OK) != 0) + if (access(absolute_pathname, W_OK) != 0) + { + int eno = errno; + errno = 0; + char errbuf[STRERROR_BUFLEN]; + + if (!daemon_mode) { - int eno = errno; - errno = 0; - char errbuf[STRERROR_BUFLEN]; - - if (!daemon_mode) - { - fprintf(stderr, - "*\n* Error : unable to open file %s for write " - "due %d, %s.\n*\n", - absolute_pathname, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))); - } - 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))); - succp = false; + fprintf(stderr, + "*\n* Error : unable to open file %s for write " + "due %d, %s.\n*\n", + absolute_pathname, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } - return succp; + 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))); + succp = false; + } + return succp; } -/** +/** * @node Expand path expression and if fname is provided, concatenate * it to path to for an absolute pathname. If fname is provided * its readability is tested. @@ -890,157 +890,157 @@ static bool file_is_writable( * @return expanded path and if fname was NULL, absolute pathname of it. * Both return value and *output_path are NULL in case of failure. * - * + * */ static char* get_expanded_pathname( - char** output_path, - char* relative_path, - const char* fname) + char** output_path, + char* relative_path, + const char* fname) { - char* cnf_file_buf = NULL; - char* expanded_path; - - if (relative_path == NULL) - { - goto return_cnf_file_buf; - } - - expanded_path = (char*)malloc(PATH_MAX); - + char* cnf_file_buf = NULL; + char* expanded_path; + + if (relative_path == NULL) + { + goto return_cnf_file_buf; + } + + expanded_path = (char*)malloc(PATH_MAX); + + /*< + * Expand possible relative pathname to absolute path + */ + if (realpath(relative_path, expanded_path) == NULL) + { + 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_r(eno, errbuf, sizeof(errbuf))); + + 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))); + free(expanded_path); + *output_path = NULL; + goto return_cnf_file_buf; + } + + if (fname != NULL) + { /*< - * Expand possible relative pathname to absolute path + * Concatenate an absolute filename and test its existence and + * readability. */ - if (realpath(relative_path, expanded_path) == NULL) + size_t pathlen = strnlen(expanded_path, PATH_MAX)+ + 1+ + strnlen(fname, PATH_MAX)+ + 1; + cnf_file_buf = (char*)malloc(pathlen); + + if (cnf_file_buf == NULL) { - int eno = errno; - errno = 0; - char errbuf[STRERROR_BUFLEN]; + ss_dassert(cnf_file_buf != NULL); + char errbuf[STRERROR_BUFLEN]; - fprintf(stderr, - "*\n* Warning : Failed to read the " - "directory %s. %s.\n*\n", - relative_path, - strerror_r(eno, errbuf, sizeof(errbuf))); + skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); - 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))); - free(expanded_path); - *output_path = NULL; - goto return_cnf_file_buf; + free(expanded_path); + expanded_path = NULL; + goto return_cnf_file_buf; } + snprintf(cnf_file_buf, pathlen, "%s/%s", expanded_path, fname); - if (fname != NULL) + if (!file_is_readable(cnf_file_buf)) { - /*< - * Concatenate an absolute filename and test its existence and - * readability. - */ - size_t pathlen = strnlen(expanded_path, PATH_MAX)+ - 1+ - strnlen(fname, PATH_MAX)+ - 1; - cnf_file_buf = (char*)malloc(pathlen); - - if (cnf_file_buf == NULL) - { - ss_dassert(cnf_file_buf != NULL); - char errbuf[STRERROR_BUFLEN]; - - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))); - - free(expanded_path); - expanded_path = NULL; - goto return_cnf_file_buf; - } - snprintf(cnf_file_buf, pathlen, "%s/%s", expanded_path, fname); - - if (!file_is_readable(cnf_file_buf)) - { - free(expanded_path); - free(cnf_file_buf); - expanded_path = NULL; - cnf_file_buf = NULL; - goto return_cnf_file_buf; - } + free(expanded_path); + free(cnf_file_buf); + expanded_path = NULL; + cnf_file_buf = NULL; + goto return_cnf_file_buf; } - else + } + else + { + /*< + * If only directory was provided, check that it is + * readable. + */ + if (!file_is_readable(expanded_path)) { - /*< - * If only directory was provided, check that it is - * readable. - */ - if (!file_is_readable(expanded_path)) - { - free(expanded_path); - expanded_path = NULL; - goto return_cnf_file_buf; - } - } - - if (output_path == NULL) - { - free(expanded_path); - } - else - { - *output_path = expanded_path; + free(expanded_path); + expanded_path = NULL; + goto return_cnf_file_buf; } + } + + if (output_path == NULL) + { + free(expanded_path); + } + else + { + *output_path = expanded_path; + } return_cnf_file_buf: - - return cnf_file_buf; + + return cnf_file_buf; } static void usage(void) { - fprintf(stderr, - "\nUsage : %s [OPTION]...\n\n" - " -d, --nodaemon enable running in terminal process (default:disabled)\n" - " -f, --config=FILE relative|absolute pathname of MaxScale configuration file\n" - " (default:/etc/maxscale.cnf)\n" - " -l, --log=[file|shm] log to file or shared memory (default: shm)\n" - " -L, --logdir=PATH path to log file directory\n" - " (default: /var/log/maxscale)\n" - " -A, --cachedir=PATH path to cache directory\n" - " (default: /var/cache/maxscale)\n" - " -B, --libdir=PATH path to module directory\n" - " (default: /usr/lib64/maxscale)\n" - " -C, --configdir=PATH path to configuration file directory\n" - " (default: /etc/)\n" - " -D, --datadir=PATH path to data directory, stored embedded mysql tables\n" - " (default: /var/cache/maxscale)\n" - " -N, --language=PATH apth to errmsg.sys file\n" - " (default: /var/lib/maxscale)\n" - " -P, --piddir=PATH path to PID file directory\n" - " (default: /var/run/maxscale)\n" - " -U, --user=USER run MaxScale as another user.\n" - " The user ID and group ID of this user are used to run MaxScale.\n" - " -s, --syslog=[yes|no] log messages to syslog (default:yes)\n" - " -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); + fprintf(stderr, + "\nUsage : %s [OPTION]...\n\n" + " -d, --nodaemon enable running in terminal process (default:disabled)\n" + " -f, --config=FILE relative|absolute pathname of MaxScale configuration file\n" + " (default:/etc/maxscale.cnf)\n" + " -l, --log=[file|shm] log to file or shared memory (default: shm)\n" + " -L, --logdir=PATH path to log file directory\n" + " (default: /var/log/maxscale)\n" + " -A, --cachedir=PATH path to cache directory\n" + " (default: /var/cache/maxscale)\n" + " -B, --libdir=PATH path to module directory\n" + " (default: /usr/lib64/maxscale)\n" + " -C, --configdir=PATH path to configuration file directory\n" + " (default: /etc/)\n" + " -D, --datadir=PATH path to data directory, stored embedded mysql tables\n" + " (default: /var/cache/maxscale)\n" + " -N, --language=PATH apth to errmsg.sys file\n" + " (default: /var/lib/maxscale)\n" + " -P, --piddir=PATH path to PID file directory\n" + " (default: /var/run/maxscale)\n" + " -U, --user=USER run MaxScale as another user.\n" + " The user ID and group ID of this user are used to run MaxScale.\n" + " -s, --syslog=[yes|no] log messages to syslog (default:yes)\n" + " -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); } -/** - * The main entry point into the gateway +/** + * The main entry point into the gateway * * @param argc The argument count * @param argv The array of arguments themselves * * @return 0 in success, 1 otherwise * - * + * * @details Logging and error printing: * --- * What is printed to the terminal is something that the user can understand, @@ -1056,7 +1056,7 @@ static void usage(void) * The configuration file is by default /etc/maxscale.cnf * The name of configuration file and its location can be specified by * command-line argument. - * + * * \ is resolved in the following order: * 1. from '-f \' command-line argument * 2. by using default value "maxscale.cnf" @@ -1064,995 +1064,996 @@ static void usage(void) */ int main(int argc, char **argv) { - int rc = MAXSCALE_SHUTDOWN; - int l; - int i; - int n; - int ini_rval; - intptr_t thread_id; - int n_threads; /*< number of epoll listener threads */ - int n_services; - int eno = 0; /*< local variable for errno */ - int opt; - int daemon_pipe[2]; - bool parent_process; - int child_status; - void** threads = NULL; /*< thread list */ - char mysql_home[PATH_MAX+1]; - char datadir_arg[10+PATH_MAX+1]; /*< '--datadir=' + PATH_MAX */ - char language_arg[11+PATH_MAX+1]; /*< '--language=' + PATH_MAX */ - char* cnf_file_path = NULL; /*< conf file, to be freed */ - char* cnf_file_arg = NULL; /*< conf filename from cmd-line arg */ - void* log_flush_thr = NULL; - char* tmp_path; - char* tmp_var; - int option_index; - int logtofile = 0; /* Use shared memory or file */ - int *syslog_enabled = &config_get_global_options()->syslog; /** Log to syslog */ - int *maxscalelog_enabled = &config_get_global_options()->maxlog; /** Log with MaxScale */ - ssize_t log_flush_timeout_ms = 0; - sigset_t sigset; - sigset_t sigpipe_mask; - sigset_t saved_mask; - void (*exitfunp[4])(void) = {skygw_logmanager_exit, - datadir_cleanup, - write_footer, - NULL}; + int rc = MAXSCALE_SHUTDOWN; + int l; + int i; + int n; + int ini_rval; + intptr_t thread_id; + int n_threads; /*< number of epoll listener threads */ + int n_services; + int eno = 0; /*< local variable for errno */ + int opt; + int daemon_pipe[2]; + bool parent_process; + int child_status; + void** threads = NULL; /*< thread list */ + char mysql_home[PATH_MAX+1]; + char datadir_arg[10+PATH_MAX+1]; /*< '--datadir=' + PATH_MAX */ + char language_arg[11+PATH_MAX+1]; /*< '--language=' + PATH_MAX */ + char* cnf_file_path = NULL; /*< conf file, to be freed */ + char* cnf_file_arg = NULL; /*< conf filename from cmd-line arg */ + void* log_flush_thr = NULL; + char* tmp_path; + char* tmp_var; + int option_index; + int logtofile = 0; /* Use shared memory or file */ + int *syslog_enabled = &config_get_global_options()->syslog; /** Log to syslog */ + int *maxscalelog_enabled = &config_get_global_options()->maxlog; /** Log with MaxScale */ + ssize_t log_flush_timeout_ms = 0; + sigset_t sigset; + sigset_t sigpipe_mask; + sigset_t saved_mask; + void (*exitfunp[4])(void) = {skygw_logmanager_exit, + datadir_cleanup, + write_footer, + NULL}; - *syslog_enabled = 0; - *maxscalelog_enabled = 1; + *syslog_enabled = 0; + *maxscalelog_enabled = 1; - sigemptyset(&sigpipe_mask); - sigaddset(&sigpipe_mask, SIGPIPE); - progname = *argv; - snprintf(datadir,PATH_MAX, "%s", default_datadir); - datadir[PATH_MAX] = '\0'; + sigemptyset(&sigpipe_mask); + sigaddset(&sigpipe_mask, SIGPIPE); + progname = *argv; + snprintf(datadir, PATH_MAX, "%s", default_datadir); + datadir[PATH_MAX] = '\0'; #if defined(FAKE_CODE) - memset(conn_open, 0, sizeof(bool)*10240); - memset(dcb_fake_write_errno, 0, sizeof(unsigned char)*10240); - memset(dcb_fake_write_ev, 0, sizeof(__int32_t)*10240); - fail_next_backend_fd = false; - fail_next_client_fd = false; - fail_next_accept = 0; - fail_accept_errno = 0; + memset(conn_open, 0, sizeof(bool) * 10240); + memset(dcb_fake_write_errno, 0, sizeof(unsigned char) * 10240); + memset(dcb_fake_write_ev, 0, sizeof(__int32_t) * 10240); + fail_next_backend_fd = false; + fail_next_client_fd = false; + fail_next_accept = 0; + fail_accept_errno = 0; #endif /* FAKE_CODE */ - file_write_header(stderr); - /*< - * Register functions which are called at exit except libmysqld-related, - * which must be registered later to avoid ordering issues. - */ - for (i=0; exitfunp[i] != NULL; i++) - { - l = atexit(*exitfunp); + file_write_header(stderr); + /*< + * Register functions which are called at exit except libmysqld-related, + * which must be registered later to avoid ordering issues. + */ + for (i = 0; exitfunp[i] != NULL; i++) + { + l = atexit(*exitfunp); - if (l != 0) - { - char* fprerr = "Failed to register exit functions for MaxScale"; - print_log_n_stderr(false, true, NULL, fprerr, 0); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } + if (l != 0) + { + char* fprerr = "Failed to register exit functions for MaxScale"; + print_log_n_stderr(false, true, NULL, fprerr, 0); + rc = MAXSCALE_INTERNALERROR; + goto return_main; } + } - while ((opt = getopt_long(argc, argv, "dc:f:l:vVs:S:?L:D:C:B:U:A:P:G:", - long_options, &option_index)) != -1) - { - bool succp = true; + while ((opt = getopt_long(argc, argv, "dc:f:l:vVs:S:?L:D:C:B:U:A:P:G:", + long_options, &option_index)) != -1) + { + bool succp = true; - switch (opt) { - case 'd': - /*< Debug mode, maxscale runs in this same process */ - daemon_mode = false; - break; + switch (opt) { + case 'd': + /*< Debug mode, maxscale runs in this same process */ + daemon_mode = false; + break; - case 'f': - /*< - * Simply copy the conf file argument. Expand or validate - * it when MaxScale home directory is resolved. - */ - if (optarg[0] != '-') - { - cnf_file_arg = strndup(optarg, PATH_MAX); - } - if (cnf_file_arg == NULL) - { - char* logerr = "Configuration file argument " - "identifier \'-f\' was specified but " - "the argument didn't specify\n a valid " - "configuration file or the argument " - "was missing."; - print_log_n_stderr(true, true, logerr, logerr, 0); - usage(); - succp = false; - } - break; - - case 'v': - rc = EXIT_SUCCESS; - printf("MaxScale %s\n", MAXSCALE_VERSION); - 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) - logtofile = 1; - else if (strncasecmp(optarg, "shm", PATH_MAX) == 0) - logtofile = 0; - else - { - char* logerr = "Configuration file argument " - "identifier \'-l\' was specified but " - "the argument didn't specify\n a valid " - "configuration file or the argument " - "was missing."; - print_log_n_stderr(true, true, logerr, logerr, 0); - usage(); - succp = false; - } - break; - case 'L': - - if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) - { - set_logdir(tmp_path); - } - else - { - succp = false; - } - break; - case 'N': - if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) - { - set_langdir(tmp_path); - } - else - { - succp = false; - } - break; - case 'P': - if(handle_path_arg(&tmp_path,optarg,NULL,true,true)) - { - set_piddir(tmp_path); - } - else - { - succp = false; - } - break; - case 'D': - snprintf(datadir,PATH_MAX,"%s",optarg); - datadir[PATH_MAX] = '\0'; - set_datadir(strdup(optarg)); - datadir_defined = true; - break; - case 'C': - if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) - { - set_configdir(tmp_path); - } - else - { - succp = false; - } - break; - case 'B': - if(handle_path_arg(&tmp_path,optarg,NULL,true,false)) - { - set_libdir(tmp_path); - } - else - { - succp = false; - } - break; - case 'A': - if(handle_path_arg(&tmp_path,optarg,NULL,true,true)) - { - set_cachedir(tmp_path); - } - else - { - succp = false; - } - break; - case 'S': + case 'f': + /*< + * Simply copy the conf file argument. Expand or validate + * it when MaxScale home directory is resolved. + */ + if (optarg[0] != '-') { - char* tok = strstr(optarg,"="); - if(tok) - { - tok++; - if(tok) - *maxscalelog_enabled = config_truth_value(tok); - } - else - { - *maxscalelog_enabled = config_truth_value(optarg); - } + cnf_file_arg = strndup(optarg, PATH_MAX); } - break; - case 's': + if (cnf_file_arg == NULL) { - char* tok = strstr(optarg,"="); - if(tok) - { - tok++; - if(tok) - *syslog_enabled = config_truth_value(tok); - } - else - { - *syslog_enabled = config_truth_value(optarg); - } + char* logerr = "Configuration file argument " + "identifier \'-f\' was specified but " + "the argument didn't specify\n a valid " + "configuration file or the argument " + "was missing."; + print_log_n_stderr(true, true, logerr, logerr, 0); + usage(); + succp = false; } - break; - case 'U': - if(set_user(optarg) != 0) - { - succp = false; - } - break; - case 'G': - set_log_augmentation(optarg); - break; - case '?': - usage(); - rc = EXIT_SUCCESS; - goto return_main; - - default: - usage(); - succp = false; - break; - } - - if (!succp) - { - rc = MAXSCALE_BADARG; - goto return_main; - } - } + break; - if (!daemon_mode) - { - fprintf(stderr, - "Info : MaxScale will be run in the terminal process.\n"); -#if defined(SS_DEBUG) - fprintf(stderr, - "\tSee " - "the log from the following log files : \n\n"); -#endif - } - else - { - if(pipe(daemon_pipe) == -1) - { - fprintf(stderr,"Error: Failed to create pipe for inter-process communication: %d %s",errno,strerror(errno)); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } + case 'v': + rc = EXIT_SUCCESS; + printf("MaxScale %s\n", MAXSCALE_VERSION); + goto return_main; - /*< - * Maxscale must be daemonized before opening files, initializing - * embedded MariaDB and in general, as early as possible. - */ - int r; - int eno = 0; - char* fprerr = "Failed to initialize set the signal " - "set for MaxScale. Exiting."; -#if defined(SS_DEBUG) - fprintf(stderr, - "Info : MaxScale will be run in a daemon process.\n\tSee " - "the log from the following log files : \n\n"); -#endif - r = sigfillset(&sigset); + case 'V': + rc = EXIT_SUCCESS; + printf("MaxScale %s - %s\n", MAXSCALE_VERSION, maxscale_commit); + goto return_main; - if (r != 0) - { - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, fprerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - r = sigdelset(&sigset, SIGHUP); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGHUP from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - r = sigdelset(&sigset, SIGUSR1); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGUSR1 from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - r = sigdelset(&sigset, SIGTERM); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGTERM from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - r = sigdelset(&sigset, SIGSEGV); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGSEGV from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - r = sigdelset(&sigset, SIGABRT); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGABRT from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - r = sigdelset(&sigset, SIGILL); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGILL from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - r = sigdelset(&sigset, SIGFPE); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGFPE from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } -#ifdef SIGBUS - r = sigdelset(&sigset, SIGBUS); - - if (r != 0) - { - char* logerr = "Failed to delete signal SIGBUS from the " - "signal set of MaxScale. Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } -#endif - r = sigprocmask(SIG_SETMASK, &sigset, NULL); - - if (r != 0) { - char* logerr = "Failed to set the signal set for MaxScale." - " Exiting."; - eno = errno; - errno = 0; - print_log_n_stderr(true, true, fprerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - - /** Daemonize the process and wait for the child process to notify - * the parent process of its exit status. */ - parent_process = gw_daemonize(); - - if(parent_process) - { - close(daemon_pipe[1]); - int nread = read(daemon_pipe[0],(void*)&child_status,sizeof(int)); - close(daemon_pipe[0]); - - if(nread == -1) - { - char* logerr = "Failed to read data from child process pipe."; - print_log_n_stderr(true, true, logerr, logerr, errno); - exit(MAXSCALE_INTERNALERROR); - } - else if(nread == 0) - { - /** Child process has exited or closed write pipe */ - char* logerr = "No data read from child process pipe."; - print_log_n_stderr(true, true, logerr, logerr, 0); - exit(MAXSCALE_INTERNALERROR); - } - - exit(child_status); - } - - /** This is the child process and we can close the read end of - * the pipe. */ - close(daemon_pipe[0]); - } - /*< - * Set signal handlers for SIGHUP, SIGTERM, SIGINT and critical signals like SIGSEGV. - */ - { - char* fprerr = "Failed to initialize signal handlers. Exiting."; - char* logerr = NULL; - l = signal_set(SIGHUP, sighup_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGHUP. Exiting."); - goto sigset_err; - } - l = signal_set(SIGUSR1, sigusr1_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGUSR1. Exiting."); - goto sigset_err; - } - l = signal_set(SIGTERM, sigterm_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGTERM. Exiting."); - goto sigset_err; - } - l = signal_set(SIGINT, sigint_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGINT. Exiting."); - goto sigset_err; - } - l = signal_set(SIGSEGV, sigfatal_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGSEGV. Exiting."); - goto sigset_err; - } - l = signal_set(SIGABRT, sigfatal_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGABRT. Exiting."); - goto sigset_err; - } - l = signal_set(SIGILL, sigfatal_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGILL. Exiting."); - goto sigset_err; - } - l = signal_set(SIGFPE, sigfatal_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGFPE. Exiting."); - goto sigset_err; - } - l = signal_set(SIGCHLD, sigchld_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGCHLD. Exiting."); - goto sigset_err; - } -#ifdef SIGBUS - l = signal_set(SIGBUS, sigfatal_handler); - - if (l != 0) - { - logerr = strdup("Failed to set signal handler for " - "SIGBUS. Exiting."); - goto sigset_err; - } -#endif - sigset_err: - if (l != 0) - { - eno = errno; - errno = 0; - print_log_n_stderr(true, !daemon_mode, logerr, fprerr, eno); - free(logerr); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - } - eno = pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &saved_mask); - - if (eno != 0) - { - char* logerr = "Failed to initialise signal mask for MaxScale. " - "Exiting."; - print_log_n_stderr(true, true, logerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - - /** OpenSSL initialization */ - if(!HAVE_OPENSSL_THREADS) - { - char* logerr = "OpenSSL library does not support multi-threading"; - print_log_n_stderr(true, true, logerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - SSL_library_init(); - SSL_load_error_strings(); - OPENSSL_add_all_algorithms_noconf(); - - int numlocks = CRYPTO_num_locks(); - if((ssl_locks = malloc(sizeof(SPINLOCK)*(numlocks + 1))) == NULL) - { - char* logerr = "Memory allocation failed"; - print_log_n_stderr(true, true, logerr, logerr, eno); - rc = MAXSCALE_INTERNALERROR; - goto return_main; - } - - for(i = 0;i 0) - snprintf(errorbuffer, sizeof(errorbuffer), - "Error: Failed to pre-parse configuration file. Error on line %d.", ini_rval); - else if(ini_rval == -1) - snprintf(errorbuffer, sizeof(errorbuffer), - "Error: Failed to pre-parse configuration file. Failed to open file."); + case 'l': + if (strncasecmp(optarg, "file", PATH_MAX) == 0) + logtofile = 1; + else if (strncasecmp(optarg, "shm", PATH_MAX) == 0) + logtofile = 0; else - snprintf(errorbuffer, sizeof(errorbuffer), - "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, "%s", errorbuffer); + char* logerr = "Configuration file argument " + "identifier \'-l\' was specified but " + "the argument didn't specify\n a valid " + "configuration file or the argument " + "was missing."; + print_log_n_stderr(true, true, logerr, logerr, 0); + usage(); + succp = false; } + break; + case 'L': - rc = MAXSCALE_BADCONFIG; - goto return_main; - } - - - /** Use the cache dir for the mysql folder of the embedded library */ - snprintf(mysql_home,PATH_MAX, "%s/mysql", get_cachedir()); - mysql_home[PATH_MAX] = '\0'; - setenv("MYSQL_HOME", mysql_home, 1); - - - /** - * Init Log Manager for MaxScale. - * The skygw_logmanager_init expects to take arguments as passed to main - * and proesses them with getopt, therefore we need to give it a dummy - * argv[0] - */ + if (handle_path_arg(&tmp_path, optarg, NULL, true, false)) + { + set_logdir(tmp_path); + } + else + { + succp = false; + } + break; + case 'N': + if (handle_path_arg(&tmp_path, optarg, NULL, true, false)) + { + set_langdir(tmp_path); + } + else + { + succp = false; + } + break; + case 'P': + if (handle_path_arg(&tmp_path, optarg, NULL, true, true)) + { + set_piddir(tmp_path); + } + else + { + succp = false; + } + break; + case 'D': + snprintf(datadir, PATH_MAX, "%s", optarg); + datadir[PATH_MAX] = '\0'; + set_datadir(strdup(optarg)); + datadir_defined = true; + break; + case 'C': + if (handle_path_arg(&tmp_path, optarg, NULL, true, false)) + { + set_configdir(tmp_path); + } + else + { + succp = false; + } + break; + case 'B': + if (handle_path_arg(&tmp_path, optarg, NULL, true, false)) + { + set_libdir(tmp_path); + } + else + { + succp = false; + } + break; + case 'A': + if (handle_path_arg(&tmp_path, optarg, NULL, true, true)) + { + set_cachedir(tmp_path); + } + else + { + succp = false; + } + break; + case 'S': { - char buf[1024]; - char *argv[8]; - bool succp; + char* tok = strstr(optarg, "="); + if (tok) + { + tok++; + if (tok) + *maxscalelog_enabled = config_truth_value(tok); + } + else + { + *maxscalelog_enabled = config_truth_value(optarg); + } + } + break; + case 's': + { + char* tok = strstr(optarg, "="); + if (tok) + { + tok++; + if (tok) + *syslog_enabled = config_truth_value(tok); + } + else + { + *syslog_enabled = config_truth_value(optarg); + } + } + break; + case 'U': + if (set_user(optarg) != 0) + { + succp = false; + } + break; + case 'G': + set_log_augmentation(optarg); + break; + case '?': + usage(); + rc = EXIT_SUCCESS; + goto return_main; - if(mkdir(get_logdir(),0777) != 0 && errno != EEXIST) - { - fprintf(stderr, - "Error: Cannot create log directory: %s\n", - default_logdir); - goto return_main; - } - - argv[0] = "MaxScale"; - argv[1] = "-j"; - argv[2] = get_logdir(); - - if(!(*syslog_enabled)) - { - printf("Syslog logging is disabled.\n"); - } - - if(!(*maxscalelog_enabled)) - { - printf("MaxScale logging is disabled.\n"); - } - logmanager_enable_syslog(*syslog_enabled); - logmanager_enable_maxscalelog(*maxscalelog_enabled); - - if (logtofile) - { - argv[3] = "-l"; /*< write to syslog */ - /** Logs that should be syslogged */ - argv[4] = "LOGFILE_MESSAGE,LOGFILE_ERROR" - "LOGFILE_DEBUG,LOGFILE_TRACE"; - argv[5] = NULL; - succp = skygw_logmanager_init(5, argv); - } - else - { - argv[3] = "-s"; /*< store to shared memory */ - argv[4] = "LOGFILE_DEBUG,LOGFILE_TRACE"; /*< to shm */ - argv[5] = "-l"; /*< write to syslog */ - argv[6] = "LOGFILE_MESSAGE,LOGFILE_ERROR"; /*< to syslog */ - argv[7] = NULL; - succp = skygw_logmanager_init(7, argv); - } - - if (!succp) - { - rc = MAXSCALE_BADCONFIG; - goto return_main; - } + default: + usage(); + succp = false; + break; } - - /* - * Set a data directory for the mysqld library, we use - * a unique directory name to avoid clauses if multiple - * instances of the gateway are being run on the same - * machine. - */ - - snprintf(datadir,PATH_MAX, "%s", get_datadir()); - datadir[PATH_MAX] = '\0'; - 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_r(errno, errbuf, sizeof(errbuf))); - goto return_main; - } - } - - snprintf(datadir,PATH_MAX, "%s/data%d", get_datadir(), getpid()); - - 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_r(errno, errbuf, sizeof(errbuf))); - goto return_main; - } - } - - if (!daemon_mode) + if (!succp) { - fprintf(stderr, - "Configuration file : %s\n" - "Log directory : %s\n" - "Data directory : %s\n" - "Module directory : %s\n" - "Service cache : %s\n\n", - cnf_file_path, - get_logdir(), - get_datadir(), - get_libdir(), - get_cachedir()); + rc = MAXSCALE_BADARG; + goto return_main; } + } - LOGIF(LM, - (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Configuration file: %s", - cnf_file_path))); - LOGIF(LM, - (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Log directory: %s/", - get_logdir()))); - LOGIF(LM, - (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Data directory: %s", - get_datadir()))); - LOGIF(LM, - (skygw_log_write_flush(LOGFILE_MESSAGE, - "Module directory: %s", - get_libdir()))); - LOGIF(LM, - (skygw_log_write_flush(LOGFILE_MESSAGE, - "Service cache: %s", - get_cachedir()))); - - /*< Update the server options */ - for (i = 0; server_options[i]; i++) + if (!daemon_mode) + { + fprintf(stderr, + "Info : MaxScale will be run in the terminal process.\n"); +#if defined(SS_DEBUG) + fprintf(stderr, + "\tSee " + "the log from the following log files : \n\n"); +#endif + } + else + { + if (pipe(daemon_pipe) == -1) { - if (!strcmp(server_options[i], "--datadir=")) - { - snprintf(datadir_arg, 10+PATH_MAX+1, "--datadir=%s", datadir); - server_options[i] = datadir_arg; - } - else if (!strcmp(server_options[i], "--language=")) - { - snprintf(language_arg, - 11+PATH_MAX+1, - "--language=%s", - get_langdir()); - server_options[i] = language_arg; - } - } - - if (mysql_library_init(num_elements, server_options, server_groups)) - { - if (!daemon_mode) - { - char* fprerr = "Failed to initialise the MySQL library. " - "Exiting."; - print_log_n_stderr(false, true, fprerr, fprerr, 0); - - if (mysql_errno(NULL) == 2000) - { - if (strncmp(mysql_error(NULL), - "Unknown MySQL error", - strlen("Unknown MySQL error")) != 0) - { - fprintf(stderr, - "*\n* Error : MySQL Error should " - "be \"Unknown MySQL error\" " - "instead of\n* %s\n* Hint " - ":\n* Ensure that you have " - "MySQL error messages file, errmsg.sys in " - "\n* %s/mysql\n* Ensure that Embedded " - "Server Library version matches " - "exactly with that of the errmsg.sys " - "file.\n*\n", - mysql_error(NULL), - get_langdir()); - } - else - { - fprintf(stderr, - "*\n* Error : MySQL Error %d, %s" - "\n*\n", - mysql_errno(NULL), - mysql_error(NULL)); - } - } - } - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : mysql_library_init failed. It is a " - "mandatory component, required by router services and " - "the MaxScale core. Error %d, %s, %s : %d. Exiting.", - mysql_errno(NULL), - mysql_error(NULL), - __FILE__, - __LINE__); - rc = MAXSCALE_NOLIBRARY; - goto return_main; - } - libmysqld_started = TRUE; - - if (!config_load(cnf_file_path)) - { - 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); - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to load MaxScale configuration file %s. " - "Exiting.", - cnf_file_path); - rc = MAXSCALE_BADCONFIG; - goto return_main; - } - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MariaDB Corporation MaxScale %s (C) MariaDB Corporation Ab 2013-2015", - MAXSCALE_VERSION))); - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MaxScale is running in process %i", - getpid()))); - - /** 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; - } - - /* Write process pid into MaxScale pidfile */ - if(write_pid_file() != 0) - { - rc = MAXSCALE_ALREADYRUNNING; + fprintf(stderr, "Error: Failed to create pipe for inter-process communication: %d %s", + errno, strerror(errno)); + rc = MAXSCALE_INTERNALERROR; goto return_main; } - /* Init MaxScale poll system */ - poll_init(); - - /** - * Init mysql thread context for main thread as well. Needed when users - * are queried from backends. - */ - mysql_thread_init(); - - /** Start the services that were created above */ - n_services = serviceStartAll(); - - if (n_services == 0) + /*< + * Maxscale must be daemonized before opening files, initializing + * embedded MariaDB and in general, as early as possible. + */ + int r; + int eno = 0; + char* fprerr = "Failed to initialize set the signal " + "set for MaxScale. Exiting."; +#if defined(SS_DEBUG) + fprintf(stderr, + "Info : MaxScale will be run in a daemon process.\n\tSee " + "the log from the following log files : \n\n"); +#endif + r = sigfillset(&sigset); + + if (r != 0) { - char* logerr = "Failed to start any MaxScale services. Exiting."; - print_log_n_stderr(true, !daemon_mode, logerr, logerr, 0); - rc = MAXSCALE_NOSERVICES; - goto return_main; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, fprerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; } - /*< - * Start periodic log flusher thread. - */ - log_flush_timeout_ms = 1000; - log_flush_thr = thread_start( - log_flush_cb, - (void *)&log_flush_timeout_ms); + r = sigdelset(&sigset, SIGHUP); - /* - * Start the housekeeper thread - */ - hkinit(); - - /*< - * Start the polling threads, note this is one less than is - * configured as the main thread will also poll. - */ - n_threads = config_threadcount(); - threads = (void **)calloc(n_threads, sizeof(void *)); - /*< - * Start server threads. - */ - for (thread_id = 0; thread_id < n_threads - 1; thread_id++) + if (r != 0) { - threads[thread_id] = thread_start(poll_waitevents, (void *)(thread_id + 1)); + char* logerr = "Failed to delete signal SIGHUP from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; } - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "MaxScale started with %d server threads.", - config_threadcount()))); - /** - * Successful start, notify the parent process that it can exit. - */ - ss_dassert(rc == MAXSCALE_SHUTDOWN); - if(daemon_mode) - write_child_exit_code(daemon_pipe[1], rc); + r = sigdelset(&sigset, SIGUSR1); - MaxScaleStarted = time(0); - /*< - * Serve clients. - */ - poll_waitevents((void *)0); - - /*< - * Wait server threads' completion. - */ - for (thread_id = 0; thread_id < n_threads - 1; thread_id++) + if (r != 0) { - thread_wait(threads[thread_id]); - } - /*< - * Wait the flush thread. - */ - thread_wait(log_flush_thr); + char* logerr = "Failed to delete signal SIGUSR1 from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + r = sigdelset(&sigset, SIGTERM); - /*< Stop all the monitors */ - monitorStopAll(); - - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MaxScale is shutting down."))); - /** Release mysql thread context*/ - mysql_thread_end(); - - datadir_cleanup(); - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MaxScale shutdown completed."))); + if (r != 0) + { + char* logerr = "Failed to delete signal SIGTERM from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + r = sigdelset(&sigset, SIGSEGV); + + if (r != 0) + { + char* logerr = "Failed to delete signal SIGSEGV from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + r = sigdelset(&sigset, SIGABRT); + + if (r != 0) + { + char* logerr = "Failed to delete signal SIGABRT from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + r = sigdelset(&sigset, SIGILL); + + if (r != 0) + { + char* logerr = "Failed to delete signal SIGILL from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + r = sigdelset(&sigset, SIGFPE); + + if (r != 0) + { + char* logerr = "Failed to delete signal SIGFPE from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } +#ifdef SIGBUS + r = sigdelset(&sigset, SIGBUS); + + if (r != 0) + { + char* logerr = "Failed to delete signal SIGBUS from the " + "signal set of MaxScale. Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } +#endif + r = sigprocmask(SIG_SETMASK, &sigset, NULL); + + if (r != 0) { + char* logerr = "Failed to set the signal set for MaxScale." + " Exiting."; + eno = errno; + errno = 0; + print_log_n_stderr(true, true, fprerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + + /** Daemonize the process and wait for the child process to notify + * the parent process of its exit status. */ + parent_process = gw_daemonize(); + + if (parent_process) + { + close(daemon_pipe[1]); + int nread = read(daemon_pipe[0], (void*)&child_status, sizeof(int)); + close(daemon_pipe[0]); + + if (nread == -1) + { + char* logerr = "Failed to read data from child process pipe."; + print_log_n_stderr(true, true, logerr, logerr, errno); + exit(MAXSCALE_INTERNALERROR); + } + else if (nread == 0) + { + /** Child process has exited or closed write pipe */ + char* logerr = "No data read from child process pipe."; + print_log_n_stderr(true, true, logerr, logerr, 0); + exit(MAXSCALE_INTERNALERROR); + } + + exit(child_status); + } + + /** This is the child process and we can close the read end of + * the pipe. */ + close(daemon_pipe[0]); + } + /*< + * Set signal handlers for SIGHUP, SIGTERM, SIGINT and critical signals like SIGSEGV. + */ + { + char* fprerr = "Failed to initialize signal handlers. Exiting."; + char* logerr = NULL; + l = signal_set(SIGHUP, sighup_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGHUP. Exiting."); + goto sigset_err; + } + l = signal_set(SIGUSR1, sigusr1_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGUSR1. Exiting."); + goto sigset_err; + } + l = signal_set(SIGTERM, sigterm_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGTERM. Exiting."); + goto sigset_err; + } + l = signal_set(SIGINT, sigint_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGINT. Exiting."); + goto sigset_err; + } + l = signal_set(SIGSEGV, sigfatal_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGSEGV. Exiting."); + goto sigset_err; + } + l = signal_set(SIGABRT, sigfatal_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGABRT. Exiting."); + goto sigset_err; + } + l = signal_set(SIGILL, sigfatal_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGILL. Exiting."); + goto sigset_err; + } + l = signal_set(SIGFPE, sigfatal_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGFPE. Exiting."); + goto sigset_err; + } + l = signal_set(SIGCHLD, sigchld_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGCHLD. Exiting."); + goto sigset_err; + } +#ifdef SIGBUS + l = signal_set(SIGBUS, sigfatal_handler); + + if (l != 0) + { + logerr = strdup("Failed to set signal handler for " + "SIGBUS. Exiting."); + goto sigset_err; + } +#endif + sigset_err: + if (l != 0) + { + eno = errno; + errno = 0; + print_log_n_stderr(true, !daemon_mode, logerr, fprerr, eno); + free(logerr); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + } + eno = pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &saved_mask); + + if (eno != 0) + { + char* logerr = "Failed to initialise signal mask for MaxScale. " + "Exiting."; + print_log_n_stderr(true, true, logerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + + /** OpenSSL initialization */ + if (!HAVE_OPENSSL_THREADS) + { + char* logerr = "OpenSSL library does not support multi-threading"; + print_log_n_stderr(true, true, logerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + SSL_library_init(); + SSL_load_error_strings(); + OPENSSL_add_all_algorithms_noconf(); + + int numlocks = CRYPTO_num_locks(); + if ((ssl_locks = malloc(sizeof(SPINLOCK) * (numlocks + 1))) == NULL) + { + char* logerr = "Memory allocation failed"; + print_log_n_stderr(true, true, logerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + + for (i = 0;i 0) + snprintf(errorbuffer, sizeof(errorbuffer), + "Error: Failed to pre-parse configuration file. Error on line %d.", ini_rval); + else if (ini_rval == -1) + snprintf(errorbuffer, sizeof(errorbuffer), + "Error: Failed to pre-parse configuration file. Failed to open file."); + else + snprintf(errorbuffer, sizeof(errorbuffer), + "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, "%s", errorbuffer); + } + + rc = MAXSCALE_BADCONFIG; + goto return_main; + } + + + /** Use the cache dir for the mysql folder of the embedded library */ + snprintf(mysql_home, PATH_MAX, "%s/mysql", get_cachedir()); + mysql_home[PATH_MAX] = '\0'; + setenv("MYSQL_HOME", mysql_home, 1); + + + /** + * Init Log Manager for MaxScale. + * The skygw_logmanager_init expects to take arguments as passed to main + * and proesses them with getopt, therefore we need to give it a dummy + * argv[0] + */ + { + char buf[1024]; + char *argv[8]; + bool succp; + + if (mkdir(get_logdir(), 0777) != 0 && errno != EEXIST) + { + fprintf(stderr, + "Error: Cannot create log directory: %s\n", + default_logdir); + goto return_main; + } + + argv[0] = "MaxScale"; + argv[1] = "-j"; + argv[2] = get_logdir(); + + if (!(*syslog_enabled)) + { + printf("Syslog logging is disabled.\n"); + } + + if (!(*maxscalelog_enabled)) + { + printf("MaxScale logging is disabled.\n"); + } + logmanager_enable_syslog(*syslog_enabled); + logmanager_enable_maxscalelog(*maxscalelog_enabled); + + if (logtofile) + { + argv[3] = "-l"; /*< write to syslog */ + /** Logs that should be syslogged */ + argv[4] = "LOGFILE_MESSAGE,LOGFILE_ERROR" + "LOGFILE_DEBUG,LOGFILE_TRACE"; + argv[5] = NULL; + succp = skygw_logmanager_init(5, argv); + } + else + { + argv[3] = "-s"; /*< store to shared memory */ + argv[4] = "LOGFILE_DEBUG,LOGFILE_TRACE"; /*< to shm */ + argv[5] = "-l"; /*< write to syslog */ + argv[6] = "LOGFILE_MESSAGE,LOGFILE_ERROR"; /*< to syslog */ + argv[7] = NULL; + succp = skygw_logmanager_init(7, argv); + } + + if (!succp) + { + rc = MAXSCALE_BADCONFIG; + goto return_main; + } + } + + + /* + * Set a data directory for the mysqld library, we use + * a unique directory name to avoid clauses if multiple + * instances of the gateway are being run on the same + * machine. + */ + + snprintf(datadir, PATH_MAX, "%s", get_datadir()); + datadir[PATH_MAX] = '\0'; + 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_r(errno, errbuf, sizeof(errbuf))); + goto return_main; + } + } + + snprintf(datadir, PATH_MAX, "%s/data%d", get_datadir(), getpid()); + + 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_r(errno, errbuf, sizeof(errbuf))); + goto return_main; + } + } + + if (!daemon_mode) + { + fprintf(stderr, + "Configuration file : %s\n" + "Log directory : %s\n" + "Data directory : %s\n" + "Module directory : %s\n" + "Service cache : %s\n\n", + cnf_file_path, + get_logdir(), + get_datadir(), + get_libdir(), + get_cachedir()); + } + + LOGIF(LM, + (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Configuration file: %s", + cnf_file_path))); + LOGIF(LM, + (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Log directory: %s/", + get_logdir()))); + LOGIF(LM, + (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Data directory: %s", + get_datadir()))); + LOGIF(LM, + (skygw_log_write_flush(LOGFILE_MESSAGE, + "Module directory: %s", + get_libdir()))); + LOGIF(LM, + (skygw_log_write_flush(LOGFILE_MESSAGE, + "Service cache: %s", + get_cachedir()))); + + /*< Update the server options */ + for (i = 0; server_options[i]; i++) + { + if (!strcmp(server_options[i], "--datadir=")) + { + snprintf(datadir_arg, 10+PATH_MAX+1, "--datadir=%s", datadir); + server_options[i] = datadir_arg; + } + else if (!strcmp(server_options[i], "--language=")) + { + snprintf(language_arg, + 11+PATH_MAX+1, + "--language=%s", + get_langdir()); + server_options[i] = language_arg; + } + } + + if (mysql_library_init(num_elements, server_options, server_groups)) + { + if (!daemon_mode) + { + char* fprerr = "Failed to initialise the MySQL library. " + "Exiting."; + print_log_n_stderr(false, true, fprerr, fprerr, 0); + + if (mysql_errno(NULL) == 2000) + { + if (strncmp(mysql_error(NULL), + "Unknown MySQL error", + strlen("Unknown MySQL error")) != 0) + { + fprintf(stderr, + "*\n* Error : MySQL Error should " + "be \"Unknown MySQL error\" " + "instead of\n* %s\n* Hint " + ":\n* Ensure that you have " + "MySQL error messages file, errmsg.sys in " + "\n* %s/mysql\n* Ensure that Embedded " + "Server Library version matches " + "exactly with that of the errmsg.sys " + "file.\n*\n", + mysql_error(NULL), + get_langdir()); + } + else + { + fprintf(stderr, + "*\n* Error : MySQL Error %d, %s" + "\n*\n", + mysql_errno(NULL), + mysql_error(NULL)); + } + } + } + skygw_log_write_flush( + LOGFILE_ERROR, + "Error : mysql_library_init failed. It is a " + "mandatory component, required by router services and " + "the MaxScale core. Error %d, %s, %s : %d. Exiting.", + mysql_errno(NULL), + mysql_error(NULL), + __FILE__, + __LINE__); + rc = MAXSCALE_NOLIBRARY; + goto return_main; + } + libmysqld_started = TRUE; + + if (!config_load(cnf_file_path)) + { + 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); + skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Failed to load MaxScale configuration file %s. " + "Exiting.", + cnf_file_path); + rc = MAXSCALE_BADCONFIG; + goto return_main; + } + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "MariaDB Corporation MaxScale %s (C) MariaDB Corporation Ab 2013-2015", + MAXSCALE_VERSION))); + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "MaxScale is running in process %i", + getpid()))); + + /** 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; + } + + /* Write process pid into MaxScale pidfile */ + if (write_pid_file() != 0) + { + rc = MAXSCALE_ALREADYRUNNING; + goto return_main; + } + + /* Init MaxScale poll system */ + poll_init(); + + /** + * Init mysql thread context for main thread as well. Needed when users + * are queried from backends. + */ + mysql_thread_init(); + + /** Start the services that were created above */ + n_services = serviceStartAll(); + + if (n_services == 0) + { + char* logerr = "Failed to start any MaxScale services. Exiting."; + print_log_n_stderr(true, !daemon_mode, logerr, logerr, 0); + rc = MAXSCALE_NOSERVICES; + goto return_main; + } + /*< + * Start periodic log flusher thread. + */ + log_flush_timeout_ms = 1000; + log_flush_thr = thread_start( + log_flush_cb, + (void *)&log_flush_timeout_ms); + + /* + * Start the housekeeper thread + */ + hkinit(); + + /*< + * Start the polling threads, note this is one less than is + * configured as the main thread will also poll. + */ + n_threads = config_threadcount(); + threads = (void **)calloc(n_threads, sizeof(void *)); + /*< + * Start server threads. + */ + for (thread_id = 0; thread_id < n_threads - 1; thread_id++) + { + threads[thread_id] = thread_start(poll_waitevents, (void *)(thread_id + 1)); + } + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "MaxScale started with %d server threads.", + config_threadcount()))); + /** + * Successful start, notify the parent process that it can exit. + */ + ss_dassert(rc == MAXSCALE_SHUTDOWN); + if (daemon_mode) + write_child_exit_code(daemon_pipe[1], rc); + + MaxScaleStarted = time(0); + /*< + * Serve clients. + */ + poll_waitevents((void *)0); + + /*< + * Wait server threads' completion. + */ + for (thread_id = 0; thread_id < n_threads - 1; thread_id++) + { + thread_wait(threads[thread_id]); + } + /*< + * Wait the flush thread. + */ + thread_wait(log_flush_thr); + + /*< Stop all the monitors */ + monitorStopAll(); + + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "MaxScale is shutting down."))); + /** Release mysql thread context*/ + mysql_thread_end(); + + datadir_cleanup(); + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "MaxScale shutdown completed."))); + + unload_all_modules(); + /* Remove Pidfile */ + unlock_pidfile(); + unlink_pidfile(); - unload_all_modules(); - /* Remove Pidfile */ - unlock_pidfile(); - unlink_pidfile(); - return_main: - if(daemon_mode && rc != MAXSCALE_SHUTDOWN) - { - /** Notify the parent process that an error has occurred */ - write_child_exit_code(daemon_pipe[1], rc); - } + if (daemon_mode && rc != MAXSCALE_SHUTDOWN) + { + /** Notify the parent process that an error has occurred */ + write_child_exit_code(daemon_pipe[1], rc); + } - if (threads) - free(threads); - if (cnf_file_path) - free(cnf_file_path); + if (threads) + free(threads); + if (cnf_file_path) + free(cnf_file_path); - return rc; + return rc; } /*< End of main */ /*< @@ -2061,81 +2062,81 @@ return_main: void shutdown_server() { - service_shutdown(); - poll_shutdown(); - hkshutdown(); - memlog_flush_all(); - log_flush_shutdown(); + service_shutdown(); + poll_shutdown(); + hkshutdown(); + memlog_flush_all(); + log_flush_shutdown(); } static void log_flush_shutdown(void) { - do_exit = TRUE; + do_exit = TRUE; } -/** +/** * Periodic log flusher to ensure that log buffers are * written to log even if block buffer used for temporarily * storing log contents are not full. * * @param arg - Flush frequency in milliseconds - * + * * */ static void log_flush_cb( - void* arg) + void* arg) { - ssize_t timeout_ms = *(ssize_t *)arg; - struct timespec ts1; + ssize_t timeout_ms = *(ssize_t *)arg; + struct timespec ts1; - ts1.tv_sec = timeout_ms/1000; - ts1.tv_nsec = (timeout_ms%1000)*1000000; - - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "Started MaxScale log flusher."))); - while (!do_exit) { - skygw_log_flush(LOGFILE_ERROR); - skygw_log_flush(LOGFILE_MESSAGE); - skygw_log_flush(LOGFILE_TRACE); - skygw_log_flush(LOGFILE_DEBUG); - nanosleep(&ts1, NULL); - } - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "Finished MaxScale log flusher."))); + ts1.tv_sec = timeout_ms/1000; + ts1.tv_nsec = (timeout_ms%1000) * 1000000; + + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "Started MaxScale log flusher."))); + while (!do_exit) { + skygw_log_flush(LOGFILE_ERROR); + skygw_log_flush(LOGFILE_MESSAGE); + skygw_log_flush(LOGFILE_TRACE); + skygw_log_flush(LOGFILE_DEBUG); + nanosleep(&ts1, NULL); + } + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "Finished MaxScale log flusher."))); } static void unlock_pidfile() { - if(pidfd != PIDFD_CLOSED) + if (pidfd != PIDFD_CLOSED) { - if(flock(pidfd,LOCK_UN|LOCK_NB) != 0) + 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); + snprintf(logbuf, sizeof(logbuf), logerr, pidfile); print_log_n_stderr(true, true, logbuf, logbuf, errno); } close(pidfd); } } -/** +/** * Unlink pid file, called at program exit */ 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_r(errno, errbuf, sizeof(errbuf))); - } - } + 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_r(errno, errbuf, sizeof(errbuf))); + } + } } /** @@ -2153,29 +2154,29 @@ bool pid_file_exists() pid_t pid; bool lock_failed = false; - snprintf(pathbuf, PATH_MAX, "%s/maxscale.pid",get_piddir()); + snprintf(pathbuf, PATH_MAX, "%s/maxscale.pid", get_piddir()); pathbuf[PATH_MAX] = '\0'; - if(access(pathbuf,F_OK) != 0) - return false; + if (access(pathbuf, F_OK) != 0) + return false; - if(access(pathbuf,R_OK) == 0) + if (access(pathbuf, R_OK) == 0) { int fd, b; - if((fd = open(pathbuf, O_RDWR)) == -1) - { - 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)) + if ((fd = open(pathbuf, O_RDWR)) == -1) { - if(errno != EWOULDBLOCK) + 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)) + { + if (errno != EWOULDBLOCK) { char* logerr = "Failed to lock PID file '%s'."; - snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); + snprintf(logbuf, sizeof(logbuf), logerr, pathbuf); print_log_n_stderr(true, true, logbuf, logbuf, errno); close(fd); return true; @@ -2184,86 +2185,90 @@ bool pid_file_exists() } pidfd = fd; - b = read(fd,pidbuf,sizeof(pidbuf)); + b = read(fd, pidbuf, sizeof(pidbuf)); - if(b == -1) - { - char* logerr = "Failed to read from PID file '%s'."; - snprintf(logbuf,sizeof(logbuf),logerr,pathbuf); - print_log_n_stderr(true, true, logbuf, logbuf, errno); + if (b == -1) + { + 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; - } - else if(b == 0) - { - /** Empty file */ - 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); + return true; + } + else if (b == 0) + { + /** Empty file */ + 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(); - return true; - } + return true; + } - pidbuf[b < sizeof(pidbuf) ? b : sizeof(pidbuf) - 1] = '\0'; - pid = strtol(pidbuf,NULL,0); + pidbuf[b < sizeof(pidbuf) ? b : sizeof(pidbuf) - 1] = '\0'; + pid = strtol(pidbuf, NULL, 0); - if(pid < 1) - { - /** Bad PID */ - 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); + if (pid < 1) + { + /** Bad PID */ + 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(); - return true; - } + return true; + } - if(kill(pid,0) == -1) - { - if(errno == ESRCH) - { - /** no such process, old PID file */ - if(lock_failed) + if (kill(pid, 0) == -1) + { + if (errno == ESRCH) + { + /** no such process, old PID file */ + 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); + 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 read from file '%s'"; - snprintf(logbuf,sizeof(logbuf),logerr,pid,pathbuf); - print_log_n_stderr(true, true, logbuf, logbuf, errno); + return lock_failed; + } + else + { + 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. " - "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); + } + } + else + { + 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(); - } + } } else { - 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); + 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; } -/** +/** * Write process pid into pidfile anc close it * Parameters: * @param home_dir The MaxScale home dir @@ -2272,103 +2277,105 @@ bool pid_file_exists() */ static int write_pid_file() { - char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; - char pidstr[STRING_BUFFER_SIZE]; + char logbuf[STRING_BUFFER_SIZE + PATH_MAX]; + char pidstr[STRING_BUFFER_SIZE]; - snprintf(pidfile, PATH_MAX, "%s/maxscale.pid",get_piddir()); + snprintf(pidfile, PATH_MAX, "%s/maxscale.pid", get_piddir()); - if(pidfd == PIDFD_CLOSED) + if (pidfd == PIDFD_CLOSED) + { + int fd = -1; + + 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); + print_log_n_stderr(true, true, logbuf, logbuf, errno); + return -1; + } + + if (flock(fd, LOCK_EX|LOCK_NB)) { - int fd = -1; - - 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); - print_log_n_stderr(true, true, logbuf, logbuf, errno); - return -1; - } - - if(flock(fd,LOCK_EX|LOCK_NB)) + if (errno == EWOULDBLOCK) { - 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; + 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); } - pidfd = fd; - } - - /* truncate pidfile content */ - if (ftruncate(pidfd, 0)) { - 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(); + close(fd); return -1; } + pidfd = fd; + } - snprintf(pidstr, sizeof(pidstr)-1, "%d", getpid()); + /* truncate pidfile content */ + if (ftruncate(pidfd, 0)) { + 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; + } - if (pwrite(pidfd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) { - 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; - } + snprintf(pidstr, sizeof(pidstr) - 1, "%d", getpid()); - /* success */ - return 0; + if (pwrite(pidfd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) { + 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; + } + + /* success */ + return 0; } int MaxScaleUptime() { - return time(0) - MaxScaleStarted; + return time(0) - MaxScaleStarted; } bool handle_path_arg(char** dest, char* path, char* arg, bool rd, bool wr) { - char pathbuffer[PATH_MAX+2]; - char* errstr; - bool rval = false; + char pathbuffer[PATH_MAX+2]; + char* errstr; + bool rval = false; - if(path == NULL && arg == NULL) - return rval; + if (path == NULL && arg == NULL) + return rval; - if(path) - { - snprintf(pathbuffer,PATH_MAX,"%s",path); - if(pathbuffer[strlen(path) - 1] != '/') - { - strcat(pathbuffer,"/"); - } - if(arg && strlen(pathbuffer) + strlen(arg) + 1 < PATH_MAX) - strcat(pathbuffer,arg); + if (path) + { + snprintf(pathbuffer, PATH_MAX, "%s", path); + if (pathbuffer[strlen(path) - 1] != '/') + { + strcat(pathbuffer, "/"); + } + if (arg && strlen(pathbuffer) + strlen(arg) + 1 < PATH_MAX) + strcat(pathbuffer, arg); - if((errstr = check_dir_access(pathbuffer,rd,wr)) == NULL) - { - *dest = strdup(pathbuffer); - rval = true; - } - else - { - print_log_n_stderr(true,true,errstr,errstr,0); - free(errstr); - errstr = NULL; - } - } + if ((errstr = check_dir_access(pathbuffer, rd, wr)) == NULL) + { + *dest = strdup(pathbuffer); + rval = true; + } + else + { + print_log_n_stderr(true, true, errstr, errstr, 0); + free(errstr); + errstr = NULL; + } + } - return rval; + return rval; } void set_log_augmentation(const char* value) @@ -2400,104 +2407,104 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons char *tmp; /** These are read from the configuration file. These will not override * command line parameters but will override default values. */ - if(strcasecmp(section,"maxscale") == 0) + if (strcasecmp(section, "maxscale") == 0) { - if(strcmp(name, "logdir") == 0) - { - if(strcmp(get_logdir(),default_logdir) == 0) - { - if(handle_path_arg(&tmp,(char*)value,NULL,true,true)) - { - set_logdir(tmp); - } - else - { - return 0; - } - } - } - else if(strcmp(name, "libdir") == 0) - { - if(strcmp(get_libdir(),default_libdir) == 0 ) - { - if(handle_path_arg(&tmp,(char*)value,NULL,true,false)) - { - set_libdir(tmp); - } - else - { - return 0; - } - } - } - else if(strcmp(name, "piddir") == 0) - { - if(strcmp(get_piddir(),default_piddir) == 0) - { - if(handle_path_arg(&tmp,(char*)value,NULL,true,true)) - { - set_piddir(tmp); - } - else - { - return 0; - } - } - } - else if(strcmp(name, "datadir") == 0) - { - if(!datadir_defined) - { - if(handle_path_arg(&tmp,(char*)value,NULL,true,false)) - { - snprintf(datadir,PATH_MAX,"%s",tmp); - datadir[PATH_MAX] = '\0'; - set_datadir(tmp); - datadir_defined = true; - } - else - { - return 0; - } - } - } - else if(strcmp(name, "cachedir") == 0) - { - if(strcmp(get_cachedir(),default_cachedir) == 0) - { - if(handle_path_arg((char**)&tmp,(char*)value,NULL,true,false)) - { - set_cachedir(tmp); - } - else - { - return 0; - } - } - } - else if(strcmp(name, "language") == 0) - { - if(strcmp(get_langdir(),default_langdir) == 0) - { - if(handle_path_arg((char**)&tmp,(char*)value,NULL,true,false)) - { - set_langdir(tmp); - } - else - { - return 0; - } - } - } - else if(strcmp(name, "syslog") == 0) - { - cnf->syslog = config_truth_value((char*)value); - } - else if(strcmp(name, "maxlog") == 0) - { - cnf->maxlog = config_truth_value((char*)value); - } - else if(strcmp(name, "log_augmentation") == 0) + if (strcmp(name, "logdir") == 0) + { + if (strcmp(get_logdir(), default_logdir) == 0) + { + if (handle_path_arg(&tmp, (char*)value, NULL, true, true)) + { + set_logdir(tmp); + } + else + { + return 0; + } + } + } + else if (strcmp(name, "libdir") == 0) + { + if (strcmp(get_libdir(), default_libdir) == 0) + { + if (handle_path_arg(&tmp, (char*)value, NULL, true, false)) + { + set_libdir(tmp); + } + else + { + return 0; + } + } + } + else if (strcmp(name, "piddir") == 0) + { + if (strcmp(get_piddir(), default_piddir) == 0) + { + if (handle_path_arg(&tmp, (char*)value, NULL, true, true)) + { + set_piddir(tmp); + } + else + { + return 0; + } + } + } + else if (strcmp(name, "datadir") == 0) + { + if (!datadir_defined) + { + if (handle_path_arg(&tmp, (char*)value, NULL, true, false)) + { + snprintf(datadir, PATH_MAX, "%s", tmp); + datadir[PATH_MAX] = '\0'; + set_datadir(tmp); + datadir_defined = true; + } + else + { + return 0; + } + } + } + else if (strcmp(name, "cachedir") == 0) + { + if (strcmp(get_cachedir(), default_cachedir) == 0) + { + if (handle_path_arg((char**)&tmp, (char*)value, NULL, true, false)) + { + set_cachedir(tmp); + } + else + { + return 0; + } + } + } + else if (strcmp(name, "language") == 0) + { + if (strcmp(get_langdir(), default_langdir) == 0) + { + if (handle_path_arg((char**)&tmp, (char*)value, NULL, true, false)) + { + set_langdir(tmp); + } + else + { + return 0; + } + } + } + else if (strcmp(name, "syslog") == 0) + { + cnf->syslog = config_truth_value((char*)value); + } + else if (strcmp(name, "maxlog") == 0) + { + cnf->maxlog = config_truth_value((char*)value); + } + else if (strcmp(name, "log_augmentation") == 0) { set_log_augmentation(value); } @@ -2513,45 +2520,45 @@ static int set_user(char* user) int rval; pwname = getpwnam(user); - if(pwname == NULL) + 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_r(errno, errbuf, sizeof(errbuf))); - return -1; + printf("Error: Failed to retrieve user information for '%s': %d %s\n", + user, errno, errno == 0 ? "User not found" : strerror_r(errno, errbuf, sizeof(errbuf))); + return -1; } - + rval = setgid(pwname->pw_gid); - if(rval != 0) + if (rval != 0) { char errbuf[STRERROR_BUFLEN]; - printf("Error: Failed to change group to '%d': %d %s\n", + printf("Error: Failed to change group to '%d': %d %s\n", pwname->pw_gid, errno, strerror_r(errno, errbuf, sizeof(errbuf))); - return rval; + return rval; } rval = setuid(pwname->pw_uid); - if(rval != 0) + if (rval != 0) { char errbuf[STRERROR_BUFLEN]; - printf("Error: Failed to change user to '%s': %d %s\n", + printf("Error: Failed to change user to '%s': %d %s\n", pwname->pw_name, errno, strerror_r(errno, errbuf, sizeof(errbuf))); - return rval; + return rval; } - if(prctl(PR_GET_DUMPABLE) == 0) + if (prctl(PR_GET_DUMPABLE) == 0) { - if(prctl(PR_SET_DUMPABLE ,1) == -1) - { + 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", + printf("Error: Failed to set dumpable flag on for the process '%s': %d %s\n", pwname->pw_name, errno, strerror_r(errno, errbuf, sizeof(errbuf))); - return -1; - } + return -1; + } } #ifdef SS_DEBUG else { - printf("Running MaxScale as: %s %d:%d\n",pwname->pw_name,pwname->pw_uid,pwname->pw_gid); + printf("Running MaxScale as: %s %d:%d\n", pwname->pw_name, pwname->pw_uid, pwname->pw_gid); } #endif From 7ac5176b460f5450934336152d1725119d26e0e9 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 30 Oct 2015 10:55:22 +0200 Subject: [PATCH 16/28] Everything logged to one file with severity prefix. Another step on the road of log manager modifications. - All messages are now logged to error.log. The other files are still created but not used anymore. - A severity prefix is added, to distinguish between messages logged to "different" files: LOGFILE_ERROR => "[Error]: " LOGFILE_MESSAGE => "[Notice]: " LOGFILE_TRACE => "[Info]: " LOGFILE_DEBUG => "[Debug] " That prefix is not written to syslog. - When maxscale is built in debug mode, trace and debug messages are no longer enabled by default. Next step is to remove the other files entirelly. --- log_manager/log_manager.cc | 100 ++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 17 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index d751d4c9d..9164c7730 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -297,6 +297,7 @@ enum log_rotate static int logmanager_write_log(logfile_id_t id, enum log_flush flush, enum log_rotate rotate, + size_t prefix_len, size_t len, const char* str); @@ -405,10 +406,7 @@ static bool logmanager_init_nomutex(int argc, char* argv[]) lm->lm_enabled_logfiles |= LOGFILE_ERROR; lm->lm_enabled_logfiles |= LOGFILE_MESSAGE; -#if defined(SS_DEBUG) - lm->lm_enabled_logfiles |= LOGFILE_TRACE; - lm->lm_enabled_logfiles |= LOGFILE_DEBUG; -#endif + fn = &lm->lm_fnames_conf; fw = &lm->lm_filewriter; fn->fn_state = UNINIT; @@ -634,6 +632,7 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) * immediately * @param rotate if set, closes currently open log file and opens a * new one + * @param prefix_len length of prefix to be stripped away when syslogging * @param str_len length of formatted string (including terminating NULL). * @param str string to be written to log * @@ -643,6 +642,7 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) static int logmanager_write_log(logfile_id_t id, enum log_flush flush, enum log_rotate rotate, + size_t prefix_len, size_t str_len, const char* str) { @@ -665,6 +665,7 @@ static int logmanager_write_log(logfile_id_t id, err = logmanager_write_log(LOGFILE_ERROR, LOG_FLUSH_YES, LOG_ROTATE_NO, + 0, strlen(errstr) + 1, errstr); if (err != 0) @@ -677,7 +678,8 @@ static int logmanager_write_log(logfile_id_t id, ss_dassert(false); goto return_err; } - lf = &lm->lm_logfile[id]; + // All messages are now logged to the error log file. + lf = &lm->lm_logfile[LOGFILE_ERROR]; CHK_LOGFILE(lf); /** @@ -767,7 +769,8 @@ static int logmanager_write_log(logfile_id_t id, /** Book space for log string from buffer */ if (do_maxscalelog) { - wp = blockbuf_get_writepos(&bb, id, safe_str_len, flush); + // All messages are now logged to the error log file. + wp = blockbuf_get_writepos(&bb, LOGFILE_ERROR, safe_str_len, flush); } else { @@ -824,14 +827,17 @@ static int logmanager_write_log(logfile_id_t id, /** write to syslog */ if (lf->lf_write_syslog) { + // Strip away the timestamp and the prefix (e.g. "[Error]: "). + const char *message = wp + timestamp_len + prefix_len; + switch (id) { case LOGFILE_ERROR: - syslog(LOG_ERR, "%s", wp + timestamp_len); + syslog(LOG_ERR, "%s", message); break; case LOGFILE_MESSAGE: - syslog(LOG_NOTICE, "%s", wp + timestamp_len); + syslog(LOG_NOTICE, "%s", message); break; default: @@ -1286,6 +1292,7 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) err = logmanager_write_log(LOGFILE_ERROR, LOG_FLUSH_YES, LOG_ROTATE_NO, + 0, strlen(errstr) + 1, errstr); if (err != 0) @@ -1315,6 +1322,7 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) err = logmanager_write_log(id, LOG_FLUSH_YES, LOG_ROTATE_NO, + 0, strlen(logstr) + 1, logstr); free(logstr); @@ -1350,6 +1358,7 @@ int skygw_log_get_augmentation() * @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 prefix_len The length of the text to be stripped away when syslogging. * @param len Length of str, including terminating NULL. * @param str String * @param flush Whether the message should be flushed. @@ -1361,6 +1370,7 @@ static int log_write(logfile_id_t id, const char* file, int line, const char* function, + size_t prefix_len, size_t len, const char* str, enum log_flush flush) @@ -1387,6 +1397,7 @@ static int log_write(logfile_id_t id, if (logmanager_write_log((logfile_id_t)i, flush, LOG_ROTATE_NO, + prefix_len, len, str) == 0) { ++successes; @@ -1410,6 +1421,55 @@ static int log_write(logfile_id_t id, return rv; } +typedef struct log_prefix +{ + const char* text; // The prefix, e.g. "[Error]: " + int len; // The length of the prefix without the trailing NULL. +} log_prefix_t; + +const char PREFIX_ERROR[] = "[Error] : "; +const char PREFIX_NOTICE[] = "[Notice]: "; +const char PREFIX_INFO[] = "[Info] : "; +const char PREFIX_DEBUG[] = "[Debug] : "; + +static log_prefix_t logfile_to_prefix(logfile_id_t id) +{ + log_prefix_t prefix; + + // The id can be a bitmask, hence we choose the most "severe" one. + if (id & LOGFILE_ERROR) + { + prefix.text = PREFIX_ERROR; + prefix.len = sizeof(PREFIX_ERROR); + } + else if (id & LOGFILE_MESSAGE) + { + prefix.text = PREFIX_NOTICE; + prefix.len = sizeof(PREFIX_NOTICE); + } + else if (id & LOGFILE_TRACE) + { + prefix.text = PREFIX_INFO; + prefix.len = sizeof(PREFIX_INFO); + } + else if (id & LOGFILE_DEBUG) + { + prefix.text = PREFIX_DEBUG; + prefix.len = sizeof(PREFIX_DEBUG); + } + else + { + assert(!true); + + prefix.text = PREFIX_ERROR; + prefix.len = sizeof(PREFIX_ERROR); + } + + --prefix.len; // Remove trailing NULL. + + return prefix; +} + int skygw_log_write_context(logfile_id_t id, enum log_flush flush, const char* file, @@ -1430,6 +1490,8 @@ int skygw_log_write_context(logfile_id_t id, if (message_len >= 0) { + log_prefix_t prefix = logfile_to_prefix(id); + static const char FORMAT_FUNCTION[] = "(%s): "; int augmentation_len = 0; @@ -1446,28 +1508,32 @@ int skygw_log_write_context(logfile_id_t id, break; } - int buffer_len = augmentation_len + message_len + 1; // Trailing NULL + int buffer_len = prefix.len + augmentation_len + message_len + 1; // Trailing NULL if (buffer_len > MAX_LOGSTRLEN) { message_len -= (buffer_len - MAX_LOGSTRLEN); buffer_len = MAX_LOGSTRLEN; - assert(augmentation_len + message_len + 1 == buffer_len); + assert(prefix.len + augmentation_len + message_len + 1 == buffer_len); } char buffer[buffer_len]; - char *message = buffer + augmentation_len; + + char *prefix_text = buffer; + char *augmentation_text = buffer + prefix.len; + char *message_text = buffer + prefix.len + augmentation_len; + + strcpy(prefix_text, prefix.text); if (augmentation_len) { - char *augmentation = buffer; int len = 0; switch (log_augmentation) { case LOG_AUGMENT_WITH_FUNCTION: - len = sprintf(augmentation, FORMAT_FUNCTION, function); + len = sprintf(augmentation_text, FORMAT_FUNCTION, function); break; default: @@ -1478,10 +1544,10 @@ int skygw_log_write_context(logfile_id_t id, } va_start(valist, str); - vsnprintf(message, message_len + 1, str, valist); + vsnprintf(message_text, message_len + 1, str, valist); va_end(valist); - err = log_write(id, file, line, function, buffer_len, buffer, flush); + err = log_write(id, file, line, function, prefix.len, buffer_len, buffer, flush); if (err != 0) { @@ -1507,7 +1573,7 @@ int skygw_log_flush(logfile_id_t id) err = logmanager_write_log(id, LOG_FLUSH_YES, LOG_ROTATE_NO, - 0, NULL); + 0, 0, NULL); if (err != 0) { @@ -1545,7 +1611,7 @@ int skygw_log_rotate(logfile_id_t id) err = logmanager_write_log(id, LOG_FLUSH_NO, LOG_ROTATE_YES, - 0, NULL); + 0, 0, NULL); if (err != 0) { From 217a0ae406f7e4017f833c32ec1f68abddebf1b5 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 30 Oct 2015 14:34:59 +0200 Subject: [PATCH 17/28] Making logmanager_write_log into less of a kitchen-sink. logmanager_write_log did three different things - logged a message, flushed a file and rotated a file - none of which were performed in one go. Hence there's no reason to do all those things in that function. --- log_manager/log_manager.cc | 124 +++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 67 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 9164c7730..d35c99db0 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -287,16 +287,10 @@ static bool logmanager_register(bool writep); static void logmanager_unregister(void); static bool logmanager_init_nomutex(int argc, char* argv[]); static void logmanager_done_nomutex(void); - -enum log_rotate -{ - LOG_ROTATE_NO = 0, - LOG_ROTATE_YES = 1 -}; +static bool logmanager_is_valid_id(logfile_id_t id); static int logmanager_write_log(logfile_id_t id, enum log_flush flush, - enum log_rotate rotate, size_t prefix_len, size_t len, const char* str); @@ -622,6 +616,42 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) } +/** + * Returns true if the id log file id is valid. + * + * NOTE: Log manager is assumed to exist. + * + * @param id The id of a log file. + * + */ + +static bool logmanager_is_valid_id(logfile_id_t id) +{ + bool rval = false; + + CHK_LOGMANAGER(lm); + + if ((id >= LOGFILE_FIRST) && (id <= LOGFILE_LAST)) + { + rval = true; + } + else + { + const char ERRSTR[] = "Invalid logfile id argument."; + + int err = logmanager_write_log(LOGFILE_ERROR, + LOG_FLUSH_YES, + 0, sizeof(ERRSTR), ERRSTR); + + if (err != 0) + { + fprintf(stderr, "Writing to logfile %s failed.\n", STRLOGID(LOGFILE_ERROR)); + } + } + + return rval; +} + /** * Finds write position from block buffer for log string and writes there. * @@ -641,7 +671,6 @@ static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) */ static int logmanager_write_log(logfile_id_t id, enum log_flush flush, - enum log_rotate rotate, size_t prefix_len, size_t str_len, const char* str) @@ -656,24 +685,8 @@ static int logmanager_write_log(logfile_id_t id, CHK_LOGMANAGER(lm); - if (id < LOGFILE_FIRST || id > LOGFILE_LAST) + if (!logmanager_is_valid_id(id)) { - const char* errstr = "Invalid logfile id argument."; - /** - * invalid id, since we don't have logfile yet. - */ - err = logmanager_write_log(LOGFILE_ERROR, - LOG_FLUSH_YES, - LOG_ROTATE_NO, - 0, - strlen(errstr) + 1, - errstr); - if (err != 0) - { - fprintf(stderr, - "Writing to logfile %s failed.\n", - STRLOGID(LOGFILE_ERROR)); - } err = -1; ss_dassert(false); goto return_err; @@ -683,7 +696,7 @@ static int logmanager_write_log(logfile_id_t id, CHK_LOGFILE(lf); /** - * When string pointer is NULL, operation is either flush or rotate. + * When string pointer is NULL, operation is flush. */ if (str == NULL) { @@ -691,10 +704,6 @@ static int logmanager_write_log(logfile_id_t id, { logfile_flush(lf); /*< wakes up file writer */ } - else if (rotate) - { - logfile_rotate(lf); /*< wakes up file writer */ - } } else { @@ -1291,7 +1300,6 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) */ err = logmanager_write_log(LOGFILE_ERROR, LOG_FLUSH_YES, - LOG_ROTATE_NO, 0, strlen(errstr) + 1, errstr); @@ -1321,7 +1329,6 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) lf->lf_enabled = val; err = logmanager_write_log(id, LOG_FLUSH_YES, - LOG_ROTATE_NO, 0, strlen(logstr) + 1, logstr); @@ -1396,7 +1403,6 @@ static int log_write(logfile_id_t id, if (logmanager_write_log((logfile_id_t)i, flush, - LOG_ROTATE_NO, prefix_len, len, str) == 0) { @@ -1572,7 +1578,6 @@ int skygw_log_flush(logfile_id_t id) CHK_LOGMANAGER(lm); err = logmanager_write_log(id, LOG_FLUSH_YES, - LOG_ROTATE_NO, 0, 0, NULL); if (err != 0) @@ -1593,45 +1598,30 @@ return_err: */ int skygw_log_rotate(logfile_id_t id) { - int err = 0; - logfile_t* lf; + int err = -1; - if (!logmanager_register(false)) + if (logmanager_register(false)) { - ss_dfprintf(stderr, "Can't register to logmanager, rotating failed\n"); - goto return_err; + CHK_LOGMANAGER(lm); + + if (logmanager_is_valid_id(id)) + { + logfile_t *lf = logmanager_get_logfile(lm, id); + CHK_LOGFILE(lf); + + MAXSCALE_NOTICE("Log rotation is called for %s.", lf->lf_full_file_name); + + logfile_rotate(lf); + err = 0; + } + + logmanager_unregister(); } - 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, - LOG_FLUSH_NO, - LOG_ROTATE_YES, - 0, 0, NULL); - - if (err != 0) + else { - 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; + ss_dfprintf(stderr, "Can't register to logmanager, rotating failed.\n"); } -return_unregister: - LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE, - "File %s use for log writing..", - lf->lf_full_file_name))); - - logmanager_unregister(); - -return_err: - return err; } From 3da1769d1211a3be07142ac33e7316248308d9c9 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 30 Oct 2015 15:26:10 +0200 Subject: [PATCH 18/28] skygw_log_flush no longer calls logmanager_write_log. --- log_manager/log_manager.cc | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index d35c99db0..189326796 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1567,28 +1567,28 @@ int skygw_log_write_context(logfile_id_t id, int skygw_log_flush(logfile_id_t id) { - int err = 0; + int err = -1; - if (!logmanager_register(false)) + if (logmanager_register(false)) { - ss_dfprintf(stderr, - "Can't register to logmanager, nothing to flush\n"); - goto return_err; + CHK_LOGMANAGER(lm); + + if (logmanager_is_valid_id(id)) + { + logfile_t *lf = logmanager_get_logfile(lm, id); + CHK_LOGFILE(lf); + + logfile_flush(lf); + err = 0; + } + + logmanager_unregister(); } - CHK_LOGMANAGER(lm); - err = logmanager_write_log(id, - LOG_FLUSH_YES, - 0, 0, NULL); - - if (err != 0) + else { - fprintf(stderr, "skygw_log_flush failed.\n"); - goto return_unregister; + ss_dfprintf(stderr, "Can't register to logmanager, flushing failed.\n"); } -return_unregister: - logmanager_unregister(); -return_err: return err; } From c1eb84b3779249b19f2df1117d99f97bd616ff58 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 30 Oct 2015 11:47:38 +0200 Subject: [PATCH 19/28] Added utility functions for regular expression matching with the PCRE2 library. --- server/core/CMakeLists.txt | 4 +- server/core/maxscale_pcre2.c | 139 ++++++++++++++++++++++++++++++++ server/include/maxscale_pcre2.h | 52 ++++++++++++ 3 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 server/core/maxscale_pcre2.c create mode 100644 server/include/maxscale_pcre2.h diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index f9fb47657..28dd5a873 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,5 +1,5 @@ if(BUILD_TESTS OR BUILD_TOOLS) - add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c externcmd.c) + add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c externcmd.c maxscale_pcre2.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) @@ -12,7 +12,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c monitor.c adminusers.c secrets.c filter.c modutil.c hint.c - housekeeper.c memlog.c resultset.c gwdirs.c externcmd.c) + housekeeper.c memlog.c resultset.c gwdirs.c externcmd.c maxscale_pcre2.c) if(WITH_JEMALLOC) target_link_libraries(maxscale ${JEMALLOC_LIBRARIES}) diff --git a/server/core/maxscale_pcre2.c b/server/core/maxscale_pcre2.c new file mode 100644 index 000000000..6d0539cbb --- /dev/null +++ b/server/core/maxscale_pcre2.c @@ -0,0 +1,139 @@ +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2015 + * + */ + +/** + * @file maxscale_pcre2.c - Utility functions for regular expression matching + * with the bundled PCRE2 library. + * + * @verbatim + * Revision History + * + * Date Who Description + * 30-10-2015 Markus Makela Initial implementation + * @endverbatim + */ + +#include + +/** + * Utility wrapper for PCRE2 library function call pcre2_substitute. + * + * This function replaces all occurences of a pattern with the provided replacement + * and places the end result into @c dest. This buffer must be allocated by the caller. + * If the size of @c dest is not large enough it will be reallocated to a larger size. + * The size of @c dest is stored in @c size if any reallocation takes place. + * + * @param re Compiled pattern to use + * @param subject Subject string + * @param replace Replacement string + * @param dest Destination buffer + * @param size Size of the desination buffer + * @return MXS_PCRE2_MATCH if replacements were made, MXS_PCRE2_NOMATCH if nothing + * was replaced or MXS_PCRE2_ERROR if memory reallocation failed + */ +mxs_pcre2_result_t mxs_pcre2_substitute(pcre2_code *re, const char *subject, const char *replace, + char** dest, size_t* size) +{ + int rc; + mxs_pcre2_result_t rval = MXS_PCRE2_ERROR; + pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL); + + if (mdata) + { + while ((rc = pcre2_substitute(re, (PCRE2_SPTR) subject, PCRE2_ZERO_TERMINATED, 0, + PCRE2_SUBSTITUTE_GLOBAL, mdata, NULL, + (PCRE2_SPTR) replace, PCRE2_ZERO_TERMINATED, + (PCRE2_UCHAR*) *dest, size)) == PCRE2_ERROR_NOMEMORY) + { + char *tmp = realloc(*dest, *size * 2); + if (tmp == NULL) + { + break; + } + *dest = tmp; + *size *= 2; + } + + if (rc > 0) + { + rval = MXS_PCRE2_MATCH; + } + else if (rc == 0) + { + rval = MXS_PCRE2_NOMATCH; + } + pcre2_match_data_free(mdata); + } + + return rval; +} + +/** + * Do a simple matching of a pattern to a string. + * + * This function compiles the given pattern and checks if the subject string matches + * it. + * @param pattern Pattern used for matching + * @param subject Subject string to match + * @param options PCRE2 compilation options + * @param error The PCRE2 error code is stored here if one is available + * @return MXS_PCRE2_MATCH if @c subject matches @c pattern, MXS_PCRE2_NOMATCH if + * they do not match and MXS_PCRE2_ERROR if an error occurred. If an error occurred + * within the PCRE2 library, @c error will contain the error code. Otherwise it is + * set to 0. + */ +mxs_pcre2_result_t mxs_pcre2_simple_match(const char* pattern, const char* subject, + int options, int *error) +{ + int err; + size_t erroff; + mxs_pcre2_result_t rval = MXS_PCRE2_ERROR; + pcre2_code *re = pcre2_compile((PCRE2_SPTR) pattern, PCRE2_ZERO_TERMINATED, + options, &err, &erroff, NULL); + if (re) + { + pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL); + if (mdata) + { + int rc = pcre2_match(re, (PCRE2_SPTR) subject, PCRE2_ZERO_TERMINATED, + 0, 0, mdata, NULL); + if (rc == PCRE2_ERROR_NOMATCH) + { + rval = MXS_PCRE2_NOMATCH; + } + else if (rc > 0) + { + /** Since we used the pattern to create the matching data, + * pcre2_match will never return 0 */ + rval = MXS_PCRE2_MATCH; + } + pcre2_match_data_free(mdata); + } + else + { + *error = 0; + } + pcre2_code_free(re); + } + else + { + *error = err; + } + return rval; +} diff --git a/server/include/maxscale_pcre2.h b/server/include/maxscale_pcre2.h new file mode 100644 index 000000000..b44dbc9a9 --- /dev/null +++ b/server/include/maxscale_pcre2.h @@ -0,0 +1,52 @@ +#ifndef _MAXSCALE_PCRE2_H +#define _MAXSCALE_PCRE2_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2015 + * + */ + +#ifndef PCRE2_CODE_UNIT_WIDTH +#define PCRE2_CODE_UNIT_WIDTH 8 +#endif + +#include + +/** + * @file maxscale_pcre2.h - Utility functions for regular expression matching + * with the bundled PCRE2 library. + * + * @verbatim + * Revision History + * + * Date Who Description + * 30-10-2015 Markus Makela Initial implementation + * @endverbatim + */ + +typedef enum +{ + MXS_PCRE2_MATCH, + MXS_PCRE2_NOMATCH, + MXS_PCRE2_ERROR +} mxs_pcre2_result_t; + +mxs_pcre2_result_t mxs_pcre2_substitute(pcre2_code *re, const char *subject, + const char *replace, char** dest, size_t* size); +mxs_pcre2_result_t mxs_pcre2_simple_match(const char* pattern, const char* subject, + int options, int* error); + +#endif From cd55f620510e96eda5c30e713f04683922767b28 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 27 Oct 2015 20:25:12 +0200 Subject: [PATCH 20/28] Fix to MXS-29: https://mariadb.atlassian.net/browse/MXS-29 If MAXSCALE_SCHEMA.REPLICATION_HEARTBEAT isn't replicated, a warning is logged. --- server/core/modutil.c | 129 ++++++++++++++++ server/include/modutil.h | 2 + server/modules/monitor/mysql_mon.c | 235 ++++++++++++++++++++++++++++- 3 files changed, 365 insertions(+), 1 deletion(-) diff --git a/server/core/modutil.c b/server/core/modutil.c index 860f93cc9..355bb71aa 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -38,6 +38,19 @@ extern int lm_enabled_logfiles_bitmask; extern size_t log_ses_count[]; extern __thread log_info_t tls_log_info; +/** These are used when converting MySQL wildcards to regular expressions */ +static SPINLOCK re_lock = SPINLOCK_INIT; +static bool pattern_init = false; +static pcre2_code *re_percent = NULL; +static pcre2_code *re_single = NULL; +static pcre2_code *re_escape = NULL; +static const PCRE2_SPTR pattern_percent = (PCRE2_SPTR) "%"; +static const PCRE2_SPTR pattern_single = (PCRE2_SPTR) "([^\\\\]|^)_"; +static const PCRE2_SPTR pattern_escape = (PCRE2_SPTR) "[.]"; +static const char* sub_percent = ".*"; +static const char* sub_single = "$1."; +static const char* sub_escape = "\\."; + static void modutil_reply_routing_error( DCB* backend_dcb, int error, @@ -842,3 +855,119 @@ int modutil_count_statements(GWBUF* buffer) return num; } + +/** + * Initialize the PCRE2 patterns used when converting MySQL wildcards to PCRE syntax. + */ +void init_pcre2_patterns() +{ + spinlock_acquire(&re_lock); + if (!pattern_init) + { + int err; + size_t erroff; + PCRE2_UCHAR errbuf[STRERROR_BUFLEN]; + + if ((re_percent = pcre2_compile(pattern_percent, PCRE2_ZERO_TERMINATED, + 0, &err, &erroff, NULL)) && + (re_single = pcre2_compile(pattern_single, PCRE2_ZERO_TERMINATED, + 0, &err, &erroff, NULL)) && + (re_escape = pcre2_compile(pattern_escape, PCRE2_ZERO_TERMINATED, + 0, &err, &erroff, NULL))) + { + pattern_init = true; + } + else + { + pcre2_get_error_message(err, errbuf, sizeof(errbuf)); + skygw_log_write(LE, "Error: Failed to compile PCRE2 pattern: %s", + errbuf); + } + + if (!pattern_init) + { + pcre2_code_free(re_percent); + pcre2_code_free(re_single); + pcre2_code_free(re_escape); + re_percent = NULL; + re_single = NULL; + re_escape = NULL; + } + } + spinlock_release(&re_lock); +} + +/** + * Check if @c string matches @c pattern according to the MySQL wildcard rules. + * The wildcard character @c '%%' is replaced with @c '.*' and @c '_' is replaced + * with @c '.'. All Regular expression special characters are escaped before + * matching is made. + * @param pattern Wildcard pattern + * @param string String to match + * @return MXS_PCRE2_MATCH if the pattern matches, MXS_PCRE2_NOMATCH if it does + * not match and MXS_PCRE2_ERROR if an error occurred + * @see maxscale_pcre2.h + */ +mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* string) +{ + if (!pattern_init) + { + init_pcre2_patterns(); + } + mxs_pcre2_result_t rval = MXS_PCRE2_ERROR; + bool err = false; + PCRE2_SIZE matchsize = strlen(string) + 1; + PCRE2_SIZE tempsize = matchsize; + char* matchstr = (char*) malloc(matchsize); + char* tempstr = (char*) malloc(tempsize); + + pcre2_match_data *mdata_percent = pcre2_match_data_create_from_pattern(re_percent, NULL); + pcre2_match_data *mdata_single = pcre2_match_data_create_from_pattern(re_single, NULL); + pcre2_match_data *mdata_escape = pcre2_match_data_create_from_pattern(re_escape, NULL); + + if (matchstr && tempstr && mdata_percent && mdata_single && mdata_escape) + { + if (mxs_pcre2_substitute(re_escape, pattern, sub_escape, + &matchstr, &matchsize) == MXS_PCRE2_ERROR || + mxs_pcre2_substitute(re_single, matchstr, sub_single, + &tempstr, &tempsize) == MXS_PCRE2_ERROR || + mxs_pcre2_substitute(re_percent, tempstr, sub_percent, + &matchstr, &matchsize) == MXS_PCRE2_ERROR) + { + err = true; + } + + if (!err) + { + int errcode; + rval = mxs_pcre2_simple_match(matchstr, string, PCRE2_CASELESS, &errcode); + if (rval == MXS_PCRE2_ERROR) + { + if(errcode != 0) + { + PCRE2_UCHAR errbuf[STRERROR_BUFLEN]; + pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); + skygw_log_write(LE, "Error: Failed to match pattern: %s", + errbuf); + } + err = true; + } + } + } + else + { + err = true; + } + + if (err) + { + skygw_log_write(LE, "Error: Fatal error when matching wildcard patterns."); + } + + pcre2_match_data_free(mdata_percent); + pcre2_match_data_free(mdata_single); + pcre2_match_data_free(mdata_escape); + free(matchstr); + free(tempstr); + return rval; +} diff --git a/server/include/modutil.h b/server/include/modutil.h index 4964b7b31..090776483 100644 --- a/server/include/modutil.h +++ b/server/include/modutil.h @@ -34,6 +34,7 @@ #include #include #include +#include #define PTR_IS_RESULTSET(b) (b[0] == 0x01 && b[1] == 0x0 && b[2] == 0x0 && b[3] == 0x01) #define PTR_IS_EOF(b) (b[0] == 0x05 && b[1] == 0x0 && b[2] == 0x0 && b[4] == 0xfe) @@ -68,4 +69,5 @@ GWBUF *modutil_create_mysql_err_msg( const char *msg); int modutil_count_signal_packets(GWBUF*,int,int,int*); +mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* string); #endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 23bdb23ad..4c0b6d4a2 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -51,8 +51,9 @@ * @endverbatim */ - #include +#include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -81,7 +82,10 @@ static void set_master_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *); static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *); static int add_slave_to_master(long *, int, long); bool isMySQLEvent(monitor_event_t event); +void check_maxscale_schema_replication(MONITOR *monitor); static bool report_version_err = true; +static const char* hb_table_name = "maxscale_schema.replication_heartbeat"; + static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, @@ -219,6 +223,7 @@ startMonitor(void *arg, void* opt) { memset(handle->events,true,sizeof(handle->events)); } + handle->tid = (THREAD)thread_start(monitorMain, monitor); return handle; } @@ -734,6 +739,7 @@ int num_servers=0; MONITOR_SERVERS *root_master = NULL; size_t nrounds = 0; int log_no_master = 1; +bool heartbeat_checked = false; spinlock_acquire(&mon->lock); handle = (MYSQL_MONITOR *)mon->handle; @@ -762,6 +768,13 @@ detect_stale_master = handle->detectStaleMaster; } /** Wait base interval */ thread_millisleep(MON_BASE_INTERVAL_MS); + + if (handle->replicationHeartbeat && !heartbeat_checked) + { + check_maxscale_schema_replication(mon); + heartbeat_checked = true; + } + /** * Calculate how far away the monitor interval is from its full * cycle and if monitor interval time further than the base @@ -1465,3 +1478,223 @@ bool isMySQLEvent(monitor_event_t event) } return false; } + +/** + * Check if replicate_ignore_table is defined and if maxscale_schema.replication_hearbeat + * table is in the list. + * @param database Server to check + * @return False if the table is not replicated or an error occurred when querying + * the server + */ +bool check_replicate_ignore_table(MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mysql_query(database->con, + "show variables like 'replicate_ignore_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0 && + strcasestr(row[1], hb_table_name)) + { + skygw_log_write(LE, "Warning: 'replicate_ignore_table' is " + "defined on server '%s' and '%s' was found in it. ", + database->server->unique_name, hb_table_name); + rval = false; + } + } + + mysql_free_result(result); + } + else + { + skygw_log_write(LE, "Error: Failed to query server %s for " + "'replicate_ignore_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + +/** + * Check if replicate_do_table is defined and if maxscale_schema.replication_hearbeat + * table is not in the list. + * @param database Server to check + * @return False if the table is not replicated or an error occurred when querying + * the server + */ +bool check_replicate_do_table(MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mysql_query(database->con, + "show variables like 'replicate_do_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0 && + strcasestr(row[1], hb_table_name) == NULL) + { + skygw_log_write(LE, "Warning: 'replicate_do_table' is " + "defined on server '%s' and '%s' was not found in it. ", + database->server->unique_name, hb_table_name); + rval = false; + } + } + mysql_free_result(result); + } + else + { + skygw_log_write(LE, "Error: Failed to query server %s for " + "'replicate_do_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + +/** + * Check if replicate_wild_do_table is defined and if it doesn't match + * maxscale_schema.replication_heartbeat. + * @param database Database server + * @return False if the table is not replicated or an error occurred when trying to + * query the server. + */ +bool check_replicate_wild_do_table(MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mysql_query(database->con, + "show variables like 'replicate_wild_do_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0) + { + mxs_pcre2_result_t rc = modutil_mysql_wildcard_match(row[1], hb_table_name); + if (rc == MXS_PCRE2_NOMATCH) + { + skygw_log_write(LE, "Warning: 'replicate_wild_do_table' is " + "defined on server '%s' and '%s' does not match it. ", + database->server->unique_name, + hb_table_name); + rval = false; + } + } + } + mysql_free_result(result); + } + else + { + skygw_log_write(LE, "Error: Failed to query server %s for " + "'replicate_wild_do_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + + +/** + * Check if replicate_wild_ignore_table is defined and if it matches + * maxscale_schema.replication_heartbeat. + * @param database Database server + * @return False if the table is not replicated or an error occurred when trying to + * query the server. + */ +bool check_replicate_wild_ignore_table(MONITOR_SERVERS* database) +{ + MYSQL_RES *result; + bool rval = true; + + if (mysql_query(database->con, + "show variables like 'replicate_wild_ignore_table'") == 0 && + (result = mysql_store_result(database->con)) && + mysql_num_fields(result) > 1) + { + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + if (strlen(row[1]) > 0) + { + mxs_pcre2_result_t rc = modutil_mysql_wildcard_match(row[1], hb_table_name); + if (rc == MXS_PCRE2_MATCH) + { + skygw_log_write(LE, "Warning: 'replicate_wild_ignore_table' is " + "defined on server '%s' and '%s' matches it. ", + database->server->unique_name, + hb_table_name); + rval = false; + } + } + } + mysql_free_result(result); + } + else + { + skygw_log_write(LE, "Error: Failed to query server %s for " + "'replicate_wild_do_table': %s", + database->server->unique_name, + mysql_error(database->con)); + rval = false; + } + return rval; +} + +/** + * Check if the maxscale_schema.replication_heartbeat table is replicated on all + * servers and log a warning if problems were found. + * @param monitor Monitor structure + */ +void check_maxscale_schema_replication(MONITOR *monitor) +{ + MONITOR_SERVERS* database = monitor->databases; + bool err = false; + + while (database) + { + connect_result_t rval = mon_connect_to_db(monitor, database); + if (rval == MONITOR_CONN_OK) + { + if (!check_replicate_ignore_table(database) || + !check_replicate_do_table(database) || + !check_replicate_wild_do_table(database) || + !check_replicate_wild_ignore_table(database)) + { + err = true; + } + } + else + { + mon_log_connect_error(database, rval); + } + database = database->next; + } + + if (err) + { + skygw_log_write(LE, "Warning: Problems were encountered when " + "checking if '%s' is replicated. Make sure that the table is " + "replicated to all slaves.", hb_table_name); + } +} From 3187f2c3f8547f0b8f7f2df6aacdb538c9ebb968 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 30 Oct 2015 17:23:31 +0200 Subject: [PATCH 21/28] Updated code based on review of 9a04984 --- server/core/modutil.c | 10 ++++------ server/modules/monitor/mysql_mon.c | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/server/core/modutil.c b/server/core/modutil.c index 355bb71aa..fdf4d4f73 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -859,7 +859,7 @@ int modutil_count_statements(GWBUF* buffer) /** * Initialize the PCRE2 patterns used when converting MySQL wildcards to PCRE syntax. */ -void init_pcre2_patterns() +void prepare_pcre2_patterns() { spinlock_acquire(&re_lock); if (!pattern_init) @@ -875,6 +875,7 @@ void init_pcre2_patterns() (re_escape = pcre2_compile(pattern_escape, PCRE2_ZERO_TERMINATED, 0, &err, &erroff, NULL))) { + assert(!pattern_init); pattern_init = true; } else @@ -910,10 +911,7 @@ void init_pcre2_patterns() */ mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* string) { - if (!pattern_init) - { - init_pcre2_patterns(); - } + prepare_pcre2_patterns(); mxs_pcre2_result_t rval = MXS_PCRE2_ERROR; bool err = false; PCRE2_SIZE matchsize = strlen(string) + 1; @@ -943,7 +941,7 @@ mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* rval = mxs_pcre2_simple_match(matchstr, string, PCRE2_CASELESS, &errcode); if (rval == MXS_PCRE2_ERROR) { - if(errcode != 0) + if (errcode != 0) { PCRE2_UCHAR errbuf[STRERROR_BUFLEN]; pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 4c0b6d4a2..fccd8b767 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -52,7 +52,6 @@ */ #include -#include #include /** Defined in log_manager.cc */ From 112e21d507659558e03bd34145dbdff531a11a9e Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 2 Nov 2015 10:05:25 +0200 Subject: [PATCH 22/28] MAXSCALE_ macros renames to MXS_ Markus boldly introduced mxs as an abbreviation for Maxscale. MXS_ is less wieldy than MAXSCALE_ for logging macros. --- log_manager/log_manager.cc | 2 +- log_manager/log_manager.h | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 189326796..efcbdde8a 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1609,7 +1609,7 @@ int skygw_log_rotate(logfile_id_t id) logfile_t *lf = logmanager_get_logfile(lm, id); CHK_LOGFILE(lf); - MAXSCALE_NOTICE("Log rotation is called for %s.", lf->lf_full_file_name); + MXS_NOTICE("Log rotation is called for %s.", lf->lf_full_file_name); logfile_rotate(lf); err = 0; diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h index 8956b3cf0..cba349695 100644 --- a/log_manager/log_manager.h +++ b/log_manager/log_manager.h @@ -185,13 +185,13 @@ const char* get_logpath_default(void); /** * Helper, not to be called directly. */ -#define MAXSCALE_MESSAGE_FLUSH(id, format, ...)\ +#define MXS_MESSAGE_FLUSH(id, format, ...)\ do { if (LOG_IS_ENABLED(id)) { skygw_log_write_flush(id, format, ##__VA_ARGS__); } } while (false) /** * Helper, not to be called directly. */ -#define MAXSCALE_MESSAGE(id, format, ...)\ +#define MXS_MESSAGE(id, format, ...)\ do { if (LOG_IS_ENABLED(id)) { skygw_log_write(id, format, ##__VA_ARGS__); } } while (false) /** @@ -200,10 +200,10 @@ const char* get_logpath_default(void); * @param format The printf format of the message. * @param ... Arguments, depending on the format. */ -#define MAXSCALE_ERROR(format, ...) MAXSCALE_MESSAGE_FLUSH(LOGFILE_ERROR, format, ##__VA_ARGS__) -#define MAXSCALE_WARNING(format, ...) MAXSCALE_MESSAGE(LOGFILE_ERROR, format, ##__VA_ARGS__) -#define MAXSCALE_NOTICE(format, ...) MAXSCALE_MESSAGE(LOGFILE_MESSAGE, format, ##__VA_ARGS__) -#define MAXSCALE_INFO(format, ...) MAXSCALE_MESSAGE(LOGFILE_TRACE, format, ##__VA_ARGS__) -#define MAXSCALE_DEBUG(format, ...) MAXSCALE_MESSAGE(LOGFILE_DEBUG, format, ##__VA_ARGS__) +#define MXS_ERROR(format, ...) MXS_MESSAGE_FLUSH(LOGFILE_ERROR, format, ##__VA_ARGS__) +#define MXS_WARNING(format, ...) MXS_MESSAGE(LOGFILE_ERROR, format, ##__VA_ARGS__) +#define MXS_NOTICE(format, ...) MXS_MESSAGE(LOGFILE_MESSAGE, format, ##__VA_ARGS__) +#define MXS_INFO(format, ...) MXS_MESSAGE(LOGFILE_TRACE, format, ##__VA_ARGS__) +#define MXS_DEBUG(format, ...) MXS_MESSAGE(LOGFILE_DEBUG, format, ##__VA_ARGS__) #endif /** LOG_MANAGER_H */ From 6810ed15ddd7ee4dd31be4e32a3b4c05a52d967e Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 31 Oct 2015 07:36:22 +0200 Subject: [PATCH 23/28] Fix to MXS-436: https://mariadb.atlassian.net/browse/MXS-436 Added autodetection of processor cores and used it as the default if an invalid value is used for threads. --- .../Getting-Started/Configuration-Guide.md | 6 ++++-- server/core/config.c | 13 ++++++++++++- server/core/gw_utils.c | 14 ++++++++++++++ server/include/gw.h | 1 + server/include/maxconfig.h | 2 ++ server/maxscale_template.cnf | 4 ++-- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index e48d44293..4eaf175e6 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -50,11 +50,13 @@ Please see the section about [Protocol Modules](#protocol-modules) for more deta ### Global Settings -The global settings, in a section named `[MaxScale]`, allow various parameters that affect MaxScale as a whole to be tuned. Currently the only setting that is supported is the number of threads to use to handle the network traffic. MaxScale will also accept the section name of `[gateway]` for global settings. This is for backward compatibility with versions prior to the naming of MaxScale. +The global settings, in a section named `[MaxScale]`, allow various parameters that affect MaxScale as a whole to be tuned. #### `threads` -To control the number of threads that poll for network traffic set the parameter threads to a number. It is recommended that you start with a single thread and add more as you find the performance is not satisfactory. MaxScale is implemented to be very thread efficient, so a small number of threads is usually adequate to support reasonably heavy workloads. Adding more threads may not improve performance and can consume resources needlessly. +This parameter controls the number of threads that poll for network traffic. MaxScale will autodetect the number of processors of the system unless number of threads is manually configured. It is recommended that you let MaxScale detect how many cores the system has and leave this parameter undefined. The number of used cores will be logged into the message logs and if you are not satisfied with the autodetected value, you can manually configure it. + +If you want to fine-tune the number of threads, start with a single thread and add more as you find the performance is not satisfactory. MaxScale is implemented to be very thread efficient, so a small number of threads is usually adequate to support reasonably heavy workloads. Adding more threads may not improve performance and can consume resources needlessly. Increasing the amount of worker threads beyond the number of processor cores is not recommended. ``` # Valid options are: diff --git a/server/core/config.c b/server/core/config.c index 549b35ce4..b1c713a6b 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -75,6 +75,7 @@ #include #include #include +#include #define PCRE2_CODE_UNIT_WIDTH 8 #include @@ -1494,6 +1495,15 @@ CONFIG_PARAMETER *p1, *p2; int config_threadcount() { + spinlock_acquire(&gateway.lock); + if(gateway.n_threads <= 0) + { + int nthr = get_processor_count(); + skygw_log_write(LE, "Warning: Invalid value for 'threads': %d. Using default " + "number of %d threads.", gateway.n_threads, nthr); + gateway.n_threads = nthr; + } + spinlock_release(&gateway.lock); return gateway.n_threads; } @@ -1657,7 +1667,8 @@ global_defaults() { uint8_t mac_addr[6]=""; struct utsname uname_data; - gateway.n_threads = 1; + spinlock_init(&gateway.lock); + gateway.n_threads = get_processor_count(); gateway.n_nbpoll = DEFAULT_NBPOLLS; gateway.pollsleep = DEFAULT_POLLSLEEP; gateway.auth_conn_timeout = DEFAULT_AUTH_CONNECT_TIMEOUT; diff --git a/server/core/gw_utils.c b/server/core/gw_utils.c index 8e671e3c2..593cb447f 100644 --- a/server/core/gw_utils.c +++ b/server/core/gw_utils.c @@ -226,3 +226,17 @@ struct hostent *hp; addr->sin_port = htons(pnum); return 1; } + +/** + * Return the number of processors available. + * @return Number of processors or 1 if the required definition of _SC_NPROCESSORS_CONF + * is not found + */ +long get_processor_count() +{ + long processors = 1; +#ifdef _SC_NPROCESSORS_CONF + processors = sysconf(_SC_NPROCESSORS_CONF); +#endif + return processors; +} \ No newline at end of file diff --git a/server/include/gw.h b/server/include/gw.h index 4f61d4512..f8294c97c 100644 --- a/server/include/gw.h +++ b/server/include/gw.h @@ -88,4 +88,5 @@ int gw_getsockerrno(int fd); int parse_bindconfig(char *, unsigned short, struct sockaddr_in *); int setipaddress(struct in_addr *, char *); char* get_libdir(); +long get_processor_count(); #endif diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index b1a1b1589..e5ae5989a 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -20,6 +20,7 @@ #include #include #include +#include /** * @file config.h The configuration handling elements * @@ -96,6 +97,7 @@ typedef struct config_context { * The gateway global configuration data */ typedef struct { + SPINLOCK lock; /*< Lock used when accessing the global configuration */ int n_threads; /**< Number of polling threads */ char *version_string; /**< The version string of embedded database library */ char release_string[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */ diff --git a/server/maxscale_template.cnf b/server/maxscale_template.cnf index e439ebe13..26e35d8a4 100644 --- a/server/maxscale_template.cnf +++ b/server/maxscale_template.cnf @@ -3,12 +3,12 @@ # Global parameters # -# Set the number of threads to a value equal to the number of CPU cores. +# Number of threads is autodetected, uncomment for manual configuration # Complete list of configuration options: # https://github.com/mariadb-corporation/MaxScale/blob/master/Documentation/Getting-Started/Configuration-Guide.md [maxscale] -threads=4 +#threads=8 # Server definitions # From 529e697eafc8e48a4550beac024352d2c3120880 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 2 Nov 2015 10:29:47 +0200 Subject: [PATCH 24/28] Updated code based on review of ac308dcb2c34e081f9814ad40c0961a217c86fc4 Removed unnecessary spinlock and added more checks. --- .../Getting-Started/Configuration-Guide.md | 4 +--- server/core/config.c | 21 +++++++++---------- server/core/gw_utils.c | 10 +++++++-- server/include/maxconfig.h | 1 - 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 4eaf175e6..a4af5529c 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -54,9 +54,7 @@ The global settings, in a section named `[MaxScale]`, allow various parameters t #### `threads` -This parameter controls the number of threads that poll for network traffic. MaxScale will autodetect the number of processors of the system unless number of threads is manually configured. It is recommended that you let MaxScale detect how many cores the system has and leave this parameter undefined. The number of used cores will be logged into the message logs and if you are not satisfied with the autodetected value, you can manually configure it. - -If you want to fine-tune the number of threads, start with a single thread and add more as you find the performance is not satisfactory. MaxScale is implemented to be very thread efficient, so a small number of threads is usually adequate to support reasonably heavy workloads. Adding more threads may not improve performance and can consume resources needlessly. Increasing the amount of worker threads beyond the number of processor cores is not recommended. +This parameter controls the number of worker threads that are handling the events coming from the kernel. MaxScale will auto-detect the number of processors of the system unless number of threads is manually configured. It is recommended that you let MaxScale detect how many cores the system has and leave this parameter undefined. The number of used cores will be logged into the message logs and if you are not satisfied with the auto-detected value, you can manually configure it. Increasing the amount of worker threads beyond the number of processor cores does not improve performance and can consume resources needlessly. ``` # Valid options are: diff --git a/server/core/config.c b/server/core/config.c index b1c713a6b..ae7181a41 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1495,15 +1495,6 @@ CONFIG_PARAMETER *p1, *p2; int config_threadcount() { - spinlock_acquire(&gateway.lock); - if(gateway.n_threads <= 0) - { - int nthr = get_processor_count(); - skygw_log_write(LE, "Warning: Invalid value for 'threads': %d. Using default " - "number of %d threads.", gateway.n_threads, nthr); - gateway.n_threads = nthr; - } - spinlock_release(&gateway.lock); return gateway.n_threads; } @@ -1564,7 +1555,16 @@ handle_global_item(const char *name, const char *value) int i; if (strcmp(name, "threads") == 0) { - gateway.n_threads = atoi(value); + int thrcount = atoi(value); + if (thrcount > 0) + { + gateway.n_threads = thrcount; + } + else + { + skygw_log_write(LE, "Warning: Invalid value for 'threads': %d. Using default " + "number of %d threads.", thrcount, gateway.n_threads); + } } else if (strcmp(name, "non_blocking_polls") == 0) { @@ -1667,7 +1667,6 @@ global_defaults() { uint8_t mac_addr[6]=""; struct utsname uname_data; - spinlock_init(&gateway.lock); gateway.n_threads = get_processor_count(); gateway.n_nbpoll = DEFAULT_NBPOLLS; gateway.pollsleep = DEFAULT_POLLSLEEP; diff --git a/server/core/gw_utils.c b/server/core/gw_utils.c index 593cb447f..043a4d645 100644 --- a/server/core/gw_utils.c +++ b/server/core/gw_utils.c @@ -235,8 +235,14 @@ struct hostent *hp; long get_processor_count() { long processors = 1; -#ifdef _SC_NPROCESSORS_CONF - processors = sysconf(_SC_NPROCESSORS_CONF); +#ifdef _SC_NPROCESSORS_ONLN + if ((processors = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) + { + skygw_log_write(LE, "Unable to establish the number of available cores. Defaulting to 4."); + processors = 4; + } +#else +#error _SC_NPROCESSORS_ONLN not available. #endif return processors; } \ No newline at end of file diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index e5ae5989a..f6fb52596 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -97,7 +97,6 @@ typedef struct config_context { * The gateway global configuration data */ typedef struct { - SPINLOCK lock; /*< Lock used when accessing the global configuration */ int n_threads; /**< Number of polling threads */ char *version_string; /**< The version string of embedded database library */ char release_string[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */ From 0d8faa6840b4393d653a7ab0a602d1e998499236 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 2 Nov 2015 11:45:03 +0200 Subject: [PATCH 25/28] Invalid thread argument now results in shutdown. This is done to avoid situations where the loaded configuration doesn't match the contents of maxscale.cnf. --- server/core/config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index ae7181a41..457e257af 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1562,8 +1562,8 @@ int i; } else { - skygw_log_write(LE, "Warning: Invalid value for 'threads': %d. Using default " - "number of %d threads.", thrcount, gateway.n_threads); + skygw_log_write(LE, "Warning: Invalid value for 'threads': %s.", value); + return 0; } } else if (strcmp(name, "non_blocking_polls") == 0) From 142c22c2a8dd2cbda637ee572148f12d1c98582c Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 2 Nov 2015 14:21:44 +0200 Subject: [PATCH 26/28] The enabling/disabling of log written to error.log A change on the path of removing all logs but error.log. --- log_manager/log_manager.cc | 99 ++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 52 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index efcbdde8a..ca1785674 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1284,68 +1284,63 @@ return_err: static bool logfile_set_enabled(logfile_id_t id, bool val) { - char* logstr; - bool oldval; - bool succp = false; - int err = 0; - logfile_t* lf; + bool rval = false; CHK_LOGMANAGER(lm); - if (id < LOGFILE_FIRST || id > LOGFILE_LAST) + if (logmanager_is_valid_id(id)) { - const char* errstr = "Invalid logfile id argument."; - /** - * invalid id, since we don't have logfile yet. - */ - err = logmanager_write_log(LOGFILE_ERROR, - LOG_FLUSH_YES, - 0, - strlen(errstr) + 1, - errstr); - if (err != 0) + if (use_stdout == 0) { - fprintf(stderr, - "* Writing to logfile %s failed.\n", - STRLOGID(LOGFILE_ERROR)); - } - ss_dassert(false); - goto return_succp; - } - lf = &lm->lm_logfile[id]; - CHK_LOGFILE(lf); - if (use_stdout == 0) - { - if (val) - { - logstr = strdup("---\tLogging to file is enabled\t--"); - } - else - { - logstr = strdup("---\tLogging to file is disabled\t--"); + logfile_t *lf = logmanager_get_logfile(lm, id); + lf->lf_enabled = val; + + const char *name; + + switch (id) + { + default: + case LOGFILE_ERROR: + name = "LOGFILE_ERROR"; + break; + + case LOGFILE_MESSAGE: + name = "LOGFILE_MESSAGE"; + break; + + case LOGFILE_TRACE: + name = "LOGFILE_TRACE"; + break; + + case LOGFILE_DEBUG: + name = "LOGFILE_DEBUG"; + break; + } + + const char FORMAT[] = "The logging of %s messages is %s."; + const char *action; + + if (val) + { + action = "enabled"; + } + else + { + action = "disabled"; + } + + MXS_NOTICE(FORMAT, name, action); } - oldval = lf->lf_enabled; - lf->lf_enabled = val; - err = logmanager_write_log(id, - LOG_FLUSH_YES, - 0, - strlen(logstr) + 1, - logstr); - free(logstr); + rval = true; } - if (err != 0) + else { - lf->lf_enabled = oldval; - fprintf(stderr, - "logfile_set_enabled failed. Writing notification to logfile %s " - "failed.\n ", - STRLOGID(id)); - goto return_succp; + MXS_ERROR("Invalid logfile id %d.", id); + ss_dassert(!true); } - succp = true; -return_succp: - return succp; + + return rval; } void skygw_log_set_augmentation(int bits) From 24ee9ca67562e39fec67401a699590b81bd294aa Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 2 Nov 2015 15:52:12 +0200 Subject: [PATCH 27/28] Removed redundant log messages Moved logging from functions that perform user authentication to the code that uses these functions. This way the messages are only logged once. --- server/modules/protocol/mysql_client.c | 17 ++++++++++++----- server/modules/protocol/mysql_common.c | 10 ---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index f99b3d599..3d25106b1 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -608,11 +608,18 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { dcb->user = strdup(client_data->user); } else - { - skygw_log_write(LOGFILE_ERROR, - "%s: login attempt for user '%s', authentication failed.", - dcb->service->name, username); - } + { + skygw_log_write(LM, "%s: login attempt for user '%s', authentication failed.", + dcb->service->name, username); + if (dcb->ipv4.sin_addr.s_addr == 0x0100007F && + !dcb->service->localhost_match_wildcard_host) + { + skygw_log_write_flush(LM, "If you have a wildcard grant that covers" + " this address, try adding " + "'localhost_match_wildcard_host=true' for " + "service '%s'. ", dcb->service->name); + } + } /* let's free the auth_token now */ if (auth_token) { diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 354820d4a..c2bec47da 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -1489,16 +1489,6 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, !dcb->service->localhost_match_wildcard_host) { /* Skip the wildcard check and return 1 */ - LOGIF(LE, - (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : user %s@%s not found, try set " - "'localhost_match_wildcard_host=1' in " - "service definition of the configuration " - "file.", - key.user, - dcb->remote))); - break; } From 9b4e8223b4bd22f364a8d3bbc86a9e018bd297d0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 2 Nov 2015 15:54:46 +0200 Subject: [PATCH 28/28] Removed double check of database name on login. Removed redundant check of the default database when a user with proper credentials is being authenticated. --- server/modules/protocol/mysql_client.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 3d25106b1..db580e69a 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -590,18 +590,11 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { sizeof(protocol->scramble), username, stage1_hash); - } - else - { - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "%s: login attempt for user %s, user not " - "found.", - dcb->service->name, username))); - } - } - /* Do again the database check */ - auth_ret = check_db_name_after_auth(dcb, database, auth_ret); + /* Do again the database check */ + auth_ret = check_db_name_after_auth(dcb, database, auth_ret); + } + } /* on succesful auth set user into dcb field */ if (auth_ret == 0) {