From 63e68269066bb30a6fc5fad00149dcad6e66271c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 26 Feb 2015 11:16:50 +0200 Subject: [PATCH 01/20] Added find_package calls for libcurl and linked the maxscale executable against it. --- CMakeLists.txt | 8 ++++++++ server/core/CMakeLists.txt | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f5634e45..426c48c08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,13 @@ find_package(MySQLClient) find_package(MySQL) find_package(Pandoc) +# You can find the variables set by this in the FindCURL.cmake file +# which is a default module in CMake. +find_package(CURL) +if(NOT CURL_FOUND) + message(FATAL_ERROR "Failed to locate dependency: libcurl") +endif() + set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/modules) # Make sure the release notes for this release are present if it is a stable one @@ -109,6 +116,7 @@ include_directories(server/include) include_directories(server/inih) include_directories(server/modules/include) include_directories(${CMAKE_BINARY_DIR}/server/include) +include_directories(${CURL_INCLUDE_DIRS}) add_subdirectory(utils) add_subdirectory(log_manager) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index b60999901..5a4ab13b8 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -8,7 +8,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.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) -target_link_libraries(maxscale ${EMBEDDED_LIB} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++) +target_link_libraries(maxscale ${EMBEDDED_LIB} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++ ) install(TARGETS maxscale DESTINATION bin) add_executable(maxkeys maxkeys.c secrets.c utils.c) From 193fe22ef0f32091063c1a960968f3983a5874bc Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 26 Feb 2015 17:47:01 +0100 Subject: [PATCH 02/20] Addition of module_feedback_send Addition of module_feedback_send. Every 30 second data is sent to 127.0.0.1 Missing routines for configuration parameters and blocking the send after successful completion --- server/core/load_utils.c | 160 +++++++++++++++++++++++++++++++++++++-- server/core/service.c | 5 ++ server/include/config.h | 12 +++ server/include/modules.h | 15 ++-- 4 files changed, 180 insertions(+), 12 deletions(-) diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 3fe975f5c..1fd6b1ab9 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -23,12 +23,13 @@ * @verbatim * Revision History * - * Date Who Description - * 13/06/13 Mark Riddoch Initial implementation - * 14/06/13 Mark Riddoch Updated to add call to ModuleInit if one is - * defined in the loaded module. - * Also updated to call fixed GetModuleObject - * 02/06/14 Mark Riddoch Addition of module info + * Date Who Description + * 13/06/13 Mark Riddoch Initial implementation + * 14/06/13 Mark Riddoch Updated to add call to ModuleInit if one is + * defined in the loaded module. + * Also updated to call fixed GetModuleObject + * 02/06/14 Mark Riddoch Addition of module info + * 26/02/15 Massimiliano Pinto Addition of module_feedback_send * * @endverbatim */ @@ -42,6 +43,8 @@ #include #include #include +#include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -59,6 +62,31 @@ static void register_module(const char *module, MODULE_INFO *info); static void unregister_module(const char *module); +struct MemoryStruct { + char *data; + size_t size; +}; + +static size_t +WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + mem->data = realloc(mem->data, mem->size + realsize + 1); + if(mem->data == NULL) { + /* out of memory! */ + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + + memcpy(&(mem->data[mem->size]), contents, realsize); + mem->size += realsize; + mem->data[mem->size] = 0; + + return realsize; +} + char* get_maxscale_home(void) { char* home = getenv("MAXSCALE_HOME"); @@ -408,3 +436,123 @@ MODULES *ptr = registered; } dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n\n"); } + +/** + * Send loaded modules info to notification service + * + * @param data The configuration details of notification service + */ +void +module_feedback_send(void* data) +{ + MODULES *ptr = registered; + CURL *curl; + CURLcode res; + struct curl_httppost *formpost=NULL; + struct curl_httppost *lastptr=NULL; + GWBUF *buffer = NULL; + char *data_ptr = NULL; + int n_mod=0; + struct MemoryStruct chunk; + + chunk.data = malloc(1); /* will be grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ + + /* count loaded modules */ + while (ptr) + { + ptr = ptr->next; + n_mod++; + } + ptr = registered; + + buffer = gwbuf_alloc(n_mod * 256); + data_ptr = GWBUF_DATA(buffer); + + while (ptr) + { + /* current maxscale setup */ + sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", "xxxfcBRIvkRlxyGdoJL0bWy+TmY"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "FEEDBACK_USER_INFO\t%s\n", "0467009f-xxxx-yyyy-zzzz-b6b2ec9c6cf4"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "VERSION\t%s\n", MAXSCALE_VERSION); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "NOW\t%lu\nPRODUCT\t%s\n", time(NULL), "maxscale"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "Uname_sysname\t%s\n", "linux"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "Uname_distribution\t%s\n", "centos"); + data_ptr+=strlen(data_ptr); + + /* modules data */ + sprintf(data_ptr, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version); + data_ptr+=strlen(data_ptr); + + if (ptr->info) { + sprintf(data_ptr, "module_%s_api\t%d.%d.%d\n", + ptr->module, + ptr->info->api_version.major, + ptr->info->api_version.minor, + ptr->info->api_version.patch); + + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "module_%s_releasestatus\t%s\n", + ptr->module, + ptr->info->status == MODULE_IN_DEVELOPMENT + ? "In Development" + : (ptr->info->status == MODULE_ALPHA_RELEASE + ? "Alpha" + : (ptr->info->status == MODULE_BETA_RELEASE + ? "Beta" + : (ptr->info->status == MODULE_GA + ? "GA" + : (ptr->info->status == MODULE_EXPERIMENTAL + ? "Experimental" : "Unknown"))))); + data_ptr+=strlen(data_ptr); + } + ptr = ptr->next; + } + + /* curl API call for data send via HTTP POST using a "file" type input */ + curl = curl_easy_init(); + + if(curl) { + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "data", + CURLFORM_BUFFER, "report.txt", + CURLFORM_BUFFERPTR, (char *)GWBUF_DATA(buffer), + CURLFORM_BUFFERLENGTH, strlen((char *)GWBUF_DATA(buffer)), + CURLFORM_CONTENTTYPE, "text/plain", + CURLFORM_END); + + curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1/post.php"); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); + + /* send all received data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + + /* some servers don't like requests that are made without a user-agent field, so we provide one */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + + /* Check for errors */ + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + else + fprintf(stderr, "Reply from remote server is\n[%s]\n", chunk.data); + } + + if(chunk.data) + free(chunk.data); + + gwbuf_free(buffer); + curl_easy_cleanup(curl); + curl_formfree(formpost); +} diff --git a/server/core/service.c b/server/core/service.c index a9b34d2bc..e766263b0 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -474,6 +474,11 @@ serviceStartAll() SERVICE *ptr; int n = 0,i; + /** Add the notification service feedback task, if enabled */ + //if (config_feedback_enable() ) { + hktask_add("send_feedback", module_feedback_send, NULL, 30); + //} + ptr = allServices; while (ptr && !ptr->svc_do_shutdown) { diff --git a/server/include/config.h b/server/include/config.h index 93bd095c1..c27184468 100644 --- a/server/include/config.h +++ b/server/include/config.h @@ -30,6 +30,7 @@ * 07/05/14 Massimiliano Pinto Added version_string to global configuration * 23/05/14 Massimiliano Pinto Added id to global configuration * 17/10/14 Mark Riddoch Added poll tuning configuration parameters + * 26/02/15 Massimiliano Pinto Added notification service configuration parameters * * @endverbatim */ @@ -99,6 +100,17 @@ typedef struct { unsigned int pollsleep; /**< Wait time in blocking polls */ } GATEWAY_CONF; +/** + * The configuration and usage information data for feeback service + */ +typedef struct { + int feedback_enable; /**< Enable/Disable Notification feedback */ + char *feedback_url; /**< URL to which the data is sent */ + char *feedback_user_info; /**< User info included in the feedback data sent */ + char *feedback_server_uid; /**< Installation identifier included in the feedback data sent */ + int feedback_send_timeout; /**< An attempt to send the data times out and fails after this many seconds */ +} FEEDBACK_CONF; + extern int config_load(char *); extern int config_reload(); extern int config_threadcount(); diff --git a/server/include/modules.h b/server/include/modules.h index adda2b255..674241075 100644 --- a/server/include/modules.h +++ b/server/include/modules.h @@ -28,12 +28,14 @@ * @verbatim * Revision History * - * Date Who Description - * 13/06/13 Mark Riddoch Initial implementation - * 08/07/13 Mark Riddoch Addition of monitor modules - * 29/05/14 Mark Riddoch Addition of filter modules - * 01/10/14 Mark Riddoch Addition of call to unload all modules on - * shutdown + * Date Who Description + * 13/06/13 Mark Riddoch Initial implementation + * 08/07/13 Mark Riddoch Addition of monitor modules + * 29/05/14 Mark Riddoch Addition of filter modules + * 01/10/14 Mark Riddoch Addition of call to unload all modules on + * shutdown + * 26/02/15 Massimiliano Pinto Addition of module_feedback_send + * * @endverbatim */ @@ -64,5 +66,6 @@ extern void unload_all_modules(); extern void printModules(); extern void dprintAllModules(DCB *); char* get_maxscale_home(void); +extern void module_feedback_send(void*); #endif From 64c0b7b0415ff34fd7849759313a150721d2b126 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 2 Mar 2015 17:15:31 +0100 Subject: [PATCH 03/20] Notification service support Notification service support --- server/core/config.c | 66 ++++++++++- server/core/load_utils.c | 215 ++++++++++++++++++++++++---------- server/core/service.c | 9 +- server/include/config.h | 28 ++--- server/include/notification.h | 14 +++ 5 files changed, 241 insertions(+), 91 deletions(-) create mode 100644 server/include/notification.h diff --git a/server/core/config.c b/server/core/config.c index fcf851137..4e7ccae66 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -58,6 +58,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -70,12 +71,16 @@ static int process_config_update(CONFIG_CONTEXT *); static void free_config_context(CONFIG_CONTEXT *); static char *config_get_value(CONFIG_PARAMETER *, const char *); static int handle_global_item(const char *, const char *); +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 config_truth_value(char *str); static int internalService(char *router); static char *config_file = NULL; static GATEWAY_CONF gateway; +static FEEDBACK_CONF feedback; char *version_string = NULL; @@ -121,6 +126,12 @@ CONFIG_PARAMETER *param, *p1; { return handle_global_item(name, value); } + + if (strcasecmp(section, "feedback") == 0) + { + return handle_feedback_item(name, value); + } + /* * If we already have some parameters for the object * add the parameters to that object. If not create @@ -192,6 +203,7 @@ int rval; } global_defaults(); + feedback_defaults(); config.object = ""; config.next = NULL; @@ -337,11 +349,12 @@ int error_count = 0; serviceEnableRootUser( obj->element, config_truth_value(enable_root_user)); + if (connection_timeout) serviceSetTimeout( obj->element, atoi(connection_timeout)); - + if (weightby) serviceWeightBy(obj->element, weightby); @@ -1186,6 +1199,16 @@ config_pollsleep() return gateway.pollsleep; } +/** + * Return the feedback config data pointer + * + * @return The feedback config data pointer + */ +FEEDBACK_CONF * +notification_get_config_feedback() +{ + return &feedback; +} static struct { char *logname; @@ -1221,8 +1244,8 @@ int i; } else if (strcmp(name, "ms_timestamp") == 0) { - skygw_set_highp(atoi(value)); - } + skygw_set_highp(atoi(value)); + } else { for (i = 0; lognames[i].logname; i++) @@ -1239,6 +1262,28 @@ int i; return 1; } +/** + * Configuration handler for items in the feedback [feedback] section + * + * @param name The item name + * @param value The item value + * @return 0 on error + */ +static int +handle_feedback_item(const char *name, const char *value) +{ +int i; + if (strcmp(name, "feedback_enable") == 0) + { + feedback.feedback_enable = atoi(value); + } + else if (strcmp(name, "feedback_url") == 0) + { + feedback.feedback_enable = atoi(value); + } + return 1; +} + /** * Set the defaults for the global configuration options */ @@ -1255,6 +1300,16 @@ global_defaults() gateway.id=0; } +/** + * Set the defaults for the feedback configuration options + */ +static void +feedback_defaults() +{ + feedback.feedback_enable = 0; + feedback.feedback_last_action = 0; +} + /** * Process a configuration context update and turn it into the set of object * we need. @@ -1302,8 +1357,9 @@ SERVER *server; char *allow_localhost_match_wildcard_host; enable_root_user = config_get_value(obj->parameters, "enable_root_user"); + connection_timeout = config_get_value(obj->parameters, "connection_timeout"); - + user = config_get_value(obj->parameters, "user"); auth = config_get_value(obj->parameters, @@ -1439,11 +1495,9 @@ SERVER *server; enable_root_user = config_get_value(obj->parameters, "enable_root_user"); - connection_timeout = config_get_value(obj->parameters, "connection_timeout"); - allow_localhost_match_wildcard_host = config_get_value(obj->parameters, "localhost_match_wildcard_host"); diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 1fd6b1ab9..57564a6fa 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -26,10 +26,10 @@ * Date Who Description * 13/06/13 Mark Riddoch Initial implementation * 14/06/13 Mark Riddoch Updated to add call to ModuleInit if one is - * defined in the loaded module. + * defined in the loaded module. * Also updated to call fixed GetModuleObject * 02/06/14 Mark Riddoch Addition of module info - * 26/02/15 Massimiliano Pinto Addition of module_feedback_send + * 26/02/15 Massimiliano Pinto Addition of module_feedback_send * * @endverbatim */ @@ -44,6 +44,7 @@ #include #include #include +#include #include /** Defined in log_manager.cc */ @@ -72,18 +73,18 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; - + mem->data = realloc(mem->data, mem->size + realsize + 1); if(mem->data == NULL) { - /* out of memory! */ + /* out of memory! */ printf("not enough memory (realloc returned NULL)\n"); return 0; } - + memcpy(&(mem->data[mem->size]), contents, realsize); mem->size += realsize; mem->data[mem->size] = 0; - + return realsize; } @@ -443,24 +444,70 @@ MODULES *ptr = registered; * @param data The configuration details of notification service */ void -module_feedback_send(void* data) -{ +module_feedback_send(void* data) { MODULES *ptr = registered; CURL *curl; CURLcode res; struct curl_httppost *formpost=NULL; struct curl_httppost *lastptr=NULL; GWBUF *buffer = NULL; - char *data_ptr = NULL; - int n_mod=0; + void *data_ptr=NULL; + long http_code = 0; struct MemoryStruct chunk; + int last_action = 0; + time_t now; + struct tm *now_tm; + int hour; + int n_mod=0; + + now = time(NULL); + now_tm = localtime(&now); + hour = now_tm->tm_hour; + + FEEDBACK_CONF *feedback_config = (FEEDBACK_CONF *) data; + + /** + * Task runs nightly, from 2 AM to 4 AM + * + * If it's done in that time interval, it will be skipped + */ + + if (hour > 4 || hour < 2) { + /* It's not the rigt time, mark it as to be done and return */ + feedback_config->feedback_last_action = 0; + + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "module_feedback_create : skip task because of time interval: hour is [%d]", + hour))); + + return; + } + + /* Time to run the task: if a previous run was succesfull skip next runs */ + if (feedback_config->feedback_last_action == 1) { + /* task was done before, return */ + + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "module_feedback_create : skip task because of previous succesful run: hour is [%d], last_action [%d]", + hour, feedback_config->feedback_last_action))); + + return; + } + + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "module_feedback_create : task runs: hour is [%d], last_action [%d]", + hour, feedback_config->feedback_last_action))); + + + chunk.data = malloc(1); /* will be grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ - chunk.data = malloc(1); /* will be grown as needed by the realloc above */ - chunk.size = 0; /* no data at this point */ - /* count loaded modules */ while (ptr) - { + { ptr = ptr->next; n_mod++; } @@ -468,56 +515,65 @@ module_feedback_send(void* data) buffer = gwbuf_alloc(n_mod * 256); data_ptr = GWBUF_DATA(buffer); + + sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", "xxxfcBRIvkRlxyGdoJL0bWy+TmY"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "FEEDBACK_USER_INFO\t%s\n", "0467009f-xxxx-yyyy-zzzz-b6b2ec9c6cf4"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "VERSION\t%s\n", MAXSCALE_VERSION); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "Uname_sysname\t%s\n", "linux"); + data_ptr+=strlen(data_ptr); + sprintf(data_ptr, "Uname_distribution\t%s\n", "centos"); + data_ptr+=strlen(data_ptr); - while (ptr) - { - /* current maxscale setup */ - sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", "xxxfcBRIvkRlxyGdoJL0bWy+TmY"); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "FEEDBACK_USER_INFO\t%s\n", "0467009f-xxxx-yyyy-zzzz-b6b2ec9c6cf4"); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "VERSION\t%s\n", MAXSCALE_VERSION); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "NOW\t%lu\nPRODUCT\t%s\n", time(NULL), "maxscale"); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "Uname_sysname\t%s\n", "linux"); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "Uname_distribution\t%s\n", "centos"); + while (ptr) + { + sprintf(data_ptr, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version); data_ptr+=strlen(data_ptr); - /* modules data */ - sprintf(data_ptr, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version); - data_ptr+=strlen(data_ptr); - - if (ptr->info) { - sprintf(data_ptr, "module_%s_api\t%d.%d.%d\n", - ptr->module, - ptr->info->api_version.major, - ptr->info->api_version.minor, - ptr->info->api_version.patch); + if (ptr->info) { + sprintf(data_ptr, "module_%s_api\t%d.%d.%d\n", + ptr->module, + ptr->info->api_version.major, + ptr->info->api_version.minor, + ptr->info->api_version.patch); data_ptr+=strlen(data_ptr); sprintf(data_ptr, "module_%s_releasestatus\t%s\n", ptr->module, - ptr->info->status == MODULE_IN_DEVELOPMENT - ? "In Development" - : (ptr->info->status == MODULE_ALPHA_RELEASE - ? "Alpha" - : (ptr->info->status == MODULE_BETA_RELEASE - ? "Beta" - : (ptr->info->status == MODULE_GA - ? "GA" - : (ptr->info->status == MODULE_EXPERIMENTAL - ? "Experimental" : "Unknown"))))); - data_ptr+=strlen(data_ptr); + ptr->info->status == MODULE_IN_DEVELOPMENT + ? "In Development" + : (ptr->info->status == MODULE_ALPHA_RELEASE + ? "Alpha" + : (ptr->info->status == MODULE_BETA_RELEASE + ? "Beta" + : (ptr->info->status == MODULE_GA + ? "GA" + : (ptr->info->status == MODULE_EXPERIMENTAL + ? "Experimental" : "Unknown"))))); + data_ptr+=strlen(data_ptr); } - ptr = ptr->next; - } + ptr = ptr->next; + } + + + /* Initializing curl library for data send via HTTP */ + curl_global_init(CURL_GLOBAL_DEFAULT); - /* curl API call for data send via HTTP POST using a "file" type input */ curl = curl_easy_init(); if(curl) { + char error_message[CURL_ERROR_SIZE]=""; + + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_message); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); + + /* curl API call for data send via HTTP POST using a "file" type input */ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "data", @@ -527,32 +583,69 @@ module_feedback_send(void* data) CURLFORM_CONTENTTYPE, "text/plain", CURLFORM_END); + curl_easy_setopt(curl, CURLOPT_HEADER, 1); + + /* some servers don't like requests that are made without a user-agent field, so we provide one */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1/post.php"); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); - /* send all received data to this function */ +#ifdef SKIP_PEER_VERIFICATION + /* + * This makes the connection A LOT LESS SECURE. + */ +// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); +#endif + +#ifdef SKIP_HOSTNAME_VERIFICATION + /* + * this will make the connection less secure. + */ + // curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); +#endif + + /* send all data to this function */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - /* we pass our 'chunk' struct to the callback function */ + /* we pass our 'chunk' struct to the callback function */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); - - /* some servers don't like requests that are made without a user-agent field, so we provide one */ - curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - + /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(res != CURLE_OK) { + last_action = 0; + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - else - fprintf(stderr, "Reply from remote server is\n[%s]\n", chunk.data); + fprintf(stderr, "curl error_message: [%s]\n", error_message); + } else { + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + + fprintf(stderr, "Reply from remote server is\n[%s]. Code [%lu]\n", chunk.data, http_code); + } + + if (http_code == 200) { + last_action = 1; + } + } else { + last_action = 0; } + + memcpy(&feedback_config->feedback_last_action, &last_action, sizeof(int)); + + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "module_feedback_create : task run result: hour is [%d], last_action [%d], http_code [%d]", + hour, feedback_config->feedback_last_action, http_code))); if(chunk.data) free(chunk.data); gwbuf_free(buffer); + curl_easy_cleanup(curl); curl_formfree(formpost); + curl_global_cleanup(); } diff --git a/server/core/service.c b/server/core/service.c index e766263b0..346d2d73e 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -58,6 +58,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -473,11 +474,11 @@ serviceStartAll() { SERVICE *ptr; int n = 0,i; +FEEDBACK_CONF *feedback = notification_get_config_feedback(); - /** Add the notification service feedback task, if enabled */ - //if (config_feedback_enable() ) { - hktask_add("send_feedback", module_feedback_send, NULL, 30); - //} + if (feedback->feedback_enable) { + hktask_add("send_feedback", module_feedback_create, feedback, 30); + } ptr = allServices; while (ptr && !ptr->svc_do_shutdown) diff --git a/server/include/config.h b/server/include/config.h index c27184468..7b7ac9e90 100644 --- a/server/include/config.h +++ b/server/include/config.h @@ -30,7 +30,6 @@ * 07/05/14 Massimiliano Pinto Added version_string to global configuration * 23/05/14 Massimiliano Pinto Added id to global configuration * 17/10/14 Mark Riddoch Added poll tuning configuration parameters - * 26/02/15 Massimiliano Pinto Added notification service configuration parameters * * @endverbatim */ @@ -100,26 +99,15 @@ typedef struct { unsigned int pollsleep; /**< Wait time in blocking polls */ } GATEWAY_CONF; -/** - * The configuration and usage information data for feeback service - */ -typedef struct { - int feedback_enable; /**< Enable/Disable Notification feedback */ - char *feedback_url; /**< URL to which the data is sent */ - char *feedback_user_info; /**< User info included in the feedback data sent */ - char *feedback_server_uid; /**< Installation identifier included in the feedback data sent */ - int feedback_send_timeout; /**< An attempt to send the data times out and fails after this many seconds */ -} FEEDBACK_CONF; +extern int config_load(char *); +extern int config_reload(); +extern int config_threadcount(); +extern unsigned int config_nbpolls(); +extern unsigned int config_pollsleep(); +CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name); +config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param); +CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param); -extern int config_load(char *); -extern int config_reload(); -extern int config_threadcount(); -extern unsigned int config_nbpolls(); -extern unsigned int config_pollsleep(); -CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name); -config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param); -CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param); -int config_truth_value(char *str); bool config_set_qualified_param( CONFIG_PARAMETER* param, void* val, diff --git a/server/include/notification.h b/server/include/notification.h new file mode 100644 index 000000000..9b6356548 --- /dev/null +++ b/server/include/notification.h @@ -0,0 +1,14 @@ +/** + * The configuration and usage information data for feeback service + */ + +typedef struct { + int feedback_enable; /**< Enable/Disable Notification feedback */ + char *feedback_url; /**< URL to which the data is sent */ + char *feedback_user_info; /**< User info included in the feedback data sent */ + int feedback_send_timeout; /**< An attempt to send the data times out and fails after this many seconds */ + int feedback_last_action; /**< Holds the feedback last send action status */ +} FEEDBACK_CONF; + + +extern FEEDBACK_CONF* notification_get_config_feedback(); From 74e303f601e8ab3fb0f062a7cd0d3f7ee0dddc0e Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 2 Mar 2015 17:36:00 +0100 Subject: [PATCH 04/20] Fixed routine name Fixed routine name --- server/core/service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/service.c b/server/core/service.c index 346d2d73e..6847b881e 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -477,7 +477,7 @@ int n = 0,i; FEEDBACK_CONF *feedback = notification_get_config_feedback(); if (feedback->feedback_enable) { - hktask_add("send_feedback", module_feedback_create, feedback, 30); + hktask_add("send_feedback", module_feedback_send, feedback, 300); } ptr = allServices; From efda82cbf334dea9201866415d6c802e94f8945a Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 2 Mar 2015 17:50:29 +0100 Subject: [PATCH 05/20] Header file update Header file update --- server/include/notification.h | 48 +++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/server/include/notification.h b/server/include/notification.h index 9b6356548..b802e934d 100644 --- a/server/include/notification.h +++ b/server/include/notification.h @@ -1,14 +1,52 @@ +#ifndef _NOTIFICATION_H +#define _NOTIFICATION_H +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +/** + * @file notification.h + * + * The configuration stuct for notification/feedback service + * + * @verbatim + * Revision History + * + * Date Who Description + * 02/03/15 Massimiliano Pinto Initial implementation + * + * @endverbatim + */ + /** * The configuration and usage information data for feeback service */ typedef struct { - int feedback_enable; /**< Enable/Disable Notification feedback */ - char *feedback_url; /**< URL to which the data is sent */ - char *feedback_user_info; /**< User info included in the feedback data sent */ - int feedback_send_timeout; /**< An attempt to send the data times out and fails after this many seconds */ - int feedback_last_action; /**< Holds the feedback last send action status */ + int feedback_enable; /**< Enable/Disable Notification feedback */ + char *feedback_url; /**< URL to which the data is sent */ + char *feedback_setup_info; /**< MaxScale setup identifier info included in the feedback data sent */ + char *feedback_user_info; /**< User info included in the feedback data sent */ + int feedback_timeout; /**< An attempt to write/read the data times out and fails after this many seconds */ + int feedback_connect_timeout; /**< An attempt to send the data times out and fails after this many seconds */ + int feedback_last_action; /**< Holds the feedback last send action status */ } FEEDBACK_CONF; extern FEEDBACK_CONF* notification_get_config_feedback(); + +#endif From 7427afeb65c10f96c799db7781497b8753e34ce0 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Tue, 3 Mar 2015 10:25:44 +0100 Subject: [PATCH 06/20] Added configuration parameters Added configuration parameters and feedback_setup_info creation --- server/core/config.c | 56 +++++++++++++++++++++++++++++++++++++++- server/core/load_utils.c | 49 +++++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 9 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 4e7ccae66..557e039e1 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include /** Defined in log_manager.cc */ @@ -1277,10 +1279,22 @@ int i; { feedback.feedback_enable = atoi(value); } + else if (strcmp(name, "feedback_user_info") == 0) + { + feedback.feedback_user_info = strdup(value); + } else if (strcmp(name, "feedback_url") == 0) { - feedback.feedback_enable = atoi(value); + feedback.feedback_url = strdup(value); } + if (strcmp(name, "feedback_timeout") == 0) + { + feedback.feedback_timeout = atoi(value); + } + if (strcmp(name, "feedback_connect_timeout") == 0) + { + feedback.feedback_connect_timeout = atoi(value); + } return 1; } @@ -1308,6 +1322,11 @@ feedback_defaults() { feedback.feedback_enable = 0; feedback.feedback_last_action = 0; + feedback.feedback_timeout = 30; + feedback.feedback_connect_timeout = 30; + feedback.feedback_url = NULL; + feedback.feedback_setup_info = strdup("10000-eee-AAA-444"); + feedback.feedback_user_info = NULL; } /** @@ -1928,3 +1947,38 @@ int i; } return 0; } + +int config_get_ifaddr(unsigned char *output) +{ + struct ifreq ifr; + struct ifconf ifc; + char buf[1024]; + int success = 0; + + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock == -1) { /* handle error*/ }; + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ } + + struct ifreq* it = ifc.ifc_req; + const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq)); + + for (; it != end; ++it) { + strcpy(ifr.ifr_name, it->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { + if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { + success = 1; + break; + } + } + } + else { /* handle error */ } + } + + if (success) memcpy(output, ifr.ifr_hwaddr.sa_data, 6); + + return success; +} diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 57564a6fa..4844abce1 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -46,6 +46,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -459,6 +460,10 @@ module_feedback_send(void* data) { struct tm *now_tm; int hour; int n_mod=0; + struct utsname uname_data; + unsigned char setup_if_address[6]=""; + unsigned char sha1_if_address[20]=""; + char hex_setup_info[40 + 1]=""; now = time(NULL); now_tm = localtime(&now); @@ -466,6 +471,15 @@ module_feedback_send(void* data) { FEEDBACK_CONF *feedback_config = (FEEDBACK_CONF *) data; + /* Configuration check */ + + if (feedback_config->feedback_url == NULL || feedback_config->feedback_user_info == NULL) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "module_feedback_create : skip task because feedback_url or feedback_user_info is NULL"))); + return; + } + /** * Task runs nightly, from 2 AM to 4 AM * @@ -502,6 +516,16 @@ module_feedback_send(void* data) { hour, feedback_config->feedback_last_action))); + /* get uname info */ + uname(&uname_data); + + /* get first available network interface info */ + if (config_get_ifaddr(setup_if_address)) { + gw_sha1_str(setup_if_address, 6, sha1_if_address); + gw_bin2hex(hex_setup_info, sha1_if_address, 20); + } + + /* allocate first memory chunck for httpd servr reply */ chunk.data = malloc(1); /* will be grown as needed by the realloc above */ chunk.size = 0; /* no data at this point */ @@ -515,18 +539,27 @@ module_feedback_send(void* data) { buffer = gwbuf_alloc(n_mod * 256); data_ptr = GWBUF_DATA(buffer); - - sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", "xxxfcBRIvkRlxyGdoJL0bWy+TmY"); + + if (strlen(hex_setup_info)) + sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); + else + sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", feedback_config->feedback_setup_info); + data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "FEEDBACK_USER_INFO\t%s\n", "0467009f-xxxx-yyyy-zzzz-b6b2ec9c6cf4"); + sprintf(data_ptr, "FEEDBACK_USER_INFO\t%s\n", feedback_config->feedback_user_info); data_ptr+=strlen(data_ptr); sprintf(data_ptr, "VERSION\t%s\n", MAXSCALE_VERSION); data_ptr+=strlen(data_ptr); sprintf(data_ptr, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale"); data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "Uname_sysname\t%s\n", "linux"); + + if (strlen(uname_data.sysname)) + sprintf(data_ptr, "Uname_sysname\t%s\n", uname_data.sysname); + else + sprintf(data_ptr, "Uname_sysname\t%s\n", ""); + data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "Uname_distribution\t%s\n", "centos"); + sprintf(data_ptr, "Uname_distribution\t%s\n", ""); data_ptr+=strlen(data_ptr); while (ptr) @@ -570,8 +603,8 @@ module_feedback_send(void* data) { curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_message); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, feedback_config->feedback_connect_timeout); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, feedback_config->feedback_timeout); /* curl API call for data send via HTTP POST using a "file" type input */ curl_formadd(&formpost, @@ -588,7 +621,7 @@ module_feedback_send(void* data) { /* some servers don't like requests that are made without a user-agent field, so we provide one */ curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1/post.php"); + curl_easy_setopt(curl, CURLOPT_URL, feedback_config->feedback_url); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); #ifdef SKIP_PEER_VERIFICATION From 0dd63ceaf137633e7fc09e375f89ec9123e74137 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 4 Mar 2015 18:48:38 +0200 Subject: [PATCH 07/20] Added release version string function to config.c --- server/core/CMakeLists.txt | 2 +- server/core/config.c | 145 ++++++++++++++++++++++++++++++++++++- server/include/config.h | 4 +- 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index c9ab03850..6a481d370 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,7 +1,7 @@ if(BUILD_TESTS) file(GLOB FULLCORE_SRC *.c) add_library(fullcore STATIC ${FULLCORE_SRC}) - target_link_libraries(fullcore log_manager utils pthread ${EMBEDDED_LIB} ssl aio rt crypt dl crypto inih z m stdc++) + target_link_libraries(fullcore ${CURL_LIBRARIES} log_manager utils pthread ${EMBEDDED_LIB} ssl aio rt crypt dl crypto inih z m stdc++) endif() add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c diff --git a/server/core/config.c b/server/core/config.c index 557e039e1..6959a13d1 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -58,9 +58,16 @@ #include #include #include +#include +#include +#include #include #include #include +#include +#include +#include + /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -79,7 +86,8 @@ static void feedback_defaults(); static void check_config_objects(CONFIG_CONTEXT *context); static int config_truth_value(char *str); static int internalService(char *router); - +int get_release_string(char* release); +int config_get_ifaddr(unsigned char *output); static char *config_file = NULL; static GATEWAY_CONF gateway; static FEEDBACK_CONF feedback; @@ -1312,6 +1320,21 @@ global_defaults() else gateway.version_string = NULL; gateway.id=0; + + if(!get_release_string(gateway.release_string)) + { + sprintf(gateway.release_string,"undefined"); + } + + unsigned char mac[6]; + if(config_get_ifaddr(mac)) + { + SHA1(mac,6,gateway.mac_sha1); + } + else + { + sprintf(gateway.mac_sha1,"MAC-undef"); + } } /** @@ -1982,3 +2005,123 @@ int config_get_ifaddr(unsigned char *output) return success; } + +/** + * + * @param release + * @return + */ +int get_release_string(char* release) +{ + const char *masks[]= { + "/etc/*-version", "/etc/*-release", + "/etc/*_version", "/etc/*_release" + }; + bool have_ubuf; + struct utsname ubuf; + bool have_distribution; + char distribution[256]; + int fd; + int i; + char *to; + + have_ubuf = (uname(&ubuf) != -1); + have_distribution= false; + if ((fd= open("/etc/lsb-release", O_RDONLY)) != -1) + { + /* LSB-compliant distribution! */ + size_t len= read(fd, (char*)distribution, sizeof(distribution)-1); + close(fd); + if (len != (size_t)-1) + { + distribution[len]= 0; // safety + char *found= strstr(distribution, "DISTRIB_DESCRIPTION="); + if (found) + { + have_distribution = true; + char *end = strstr(found, "\n"); + if (end == NULL) + end = distribution + len; + found += 20; + + if (*found == '"' && end[-1] == '"') + { + found++; + end--; + } + *end = 0; + + to = strcpy(distribution, "lsb: "); + memmove(to, found, end - found + 1); + + strncpy(release, to, 255); + return 1; + } + } + } + + /* if not an LSB-compliant distribution */ + for (i= 0; !have_distribution && i < 4; i++) + { + glob_t found; + char *new_to; + fprintf(stderr, "glob %d: [%s]\n", i, masks[i]); + + if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0) + { + int fd; + int k = 0; + int skipindex = 0; + int startindex = 0; + + fprintf(stderr, "Matched [%lu] paths\n", found.gl_pathc); + + for (k = 0; k< found.gl_pathc; k++) { + fprintf(stderr, "Possibly opening [%s]\n", found.gl_pathv[k]); + if (strcmp(found.gl_pathv[k], "/etc/lsb-release") == 0) { + fprintf(stderr, "Skipping [%s]\n", found.gl_pathv[k]); + skipindex = k; + } + } + + + if ( skipindex == 0) + startindex++; + + // skip ound.gl_pathv[0] if it's /etc/lsb-release + if ((fd= open(found.gl_pathv[startindex], O_RDONLY)) != -1) + { + /* + +5 and -8 below cut the file name part out of the + full pathname that corresponds to the mask as above. + */ + //char *to= strmov(distribution, found.gl_pathv[0] + 5) - 8; + new_to = strcpy(distribution, found.gl_pathv[0] + 5); + new_to += 8; + *new_to++ = ':'; + *new_to++ = ' '; + + size_t to_len= distribution + sizeof(distribution) - 1 - new_to; + size_t len= read(fd, (char*)new_to, to_len); + close(fd); + if (len != (size_t)-1) + { + new_to[len]= 0; // safety + char *end= strstr(new_to, "\n"); + if (end) + *end= 0; + + have_distribution= true; + fprintf(stderr, "Distribution [%i]=[%s]\n", i, new_to); + strncpy(release, new_to, 255); + } + } + } + globfree(&found); + } + + if (have_distribution) + return 1; + else + return 0; +} diff --git a/server/include/config.h b/server/include/config.h index b14fc7fa2..811260205 100644 --- a/server/include/config.h +++ b/server/include/config.h @@ -18,7 +18,7 @@ * Copyright MariaDB Corporation Ab 2013-2014 */ #include - +#include /** * @file config.h The configuration handling elements * @@ -94,6 +94,8 @@ typedef struct config_context { typedef struct { int n_threads; /**< Number of polling threads */ char *version_string; /**< The version string of embedded database library */ + char release_string[256]; /**< The release name string of the system */ + char mac_sha1[SHA_DIGEST_LENGTH]; /*< The SHA1 digest of an interface MAC address */ unsigned long id; /**< MaxScale ID */ unsigned int n_nbpoll; /**< Tune number of non-blocking polls */ unsigned int pollsleep; /**< Wait time in blocking polls */ From 698af9d3b7f061b9af26e58143676851f8e2b7d6 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 5 Mar 2015 13:20:22 +0100 Subject: [PATCH 08/20] Send modules list into to Notification server Send modules list into to Notification server TODO: remove fprintf in module_feedback_send and use log manager instead --- server/core/config.c | 340 +++++++++++++++++++--------------- server/core/load_utils.c | 44 ++--- server/core/service.c | 7 +- server/include/config.h | 19 +- server/include/notification.h | 19 +- 5 files changed, 234 insertions(+), 195 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 6959a13d1..0fa519a25 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -41,6 +41,7 @@ * 30/10/14 Massimiliano Pinto Added disable_master_failback parameter * 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write * 20/02/15 Markus Mäkelä Added connection_timeout parameter for services + * 05/03/15 Massimiliano Pinto Added notification_feedback support * * @endverbatim */ @@ -55,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -63,10 +65,12 @@ #include #include #include +#include #include #include #include #include +#include /** Defined in log_manager.cc */ @@ -84,10 +88,11 @@ 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 config_truth_value(char *str); -static int internalService(char *router); -int get_release_string(char* release); -int config_get_ifaddr(unsigned char *output); +int config_truth_value(char *str); +static int internalService(char *router); +int config_get_ifaddr(unsigned char *output); +int config_get_release_string(char* release); + static char *config_file = NULL; static GATEWAY_CONF gateway; static FEEDBACK_CONF feedback; @@ -746,7 +751,7 @@ int error_count = 0; /* if id is not set, do it now */ if (gateway.id == 0) { setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address); - gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + port + getpid()); + gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + atoi(port) + getpid()); } if (service && socket && protocol) { @@ -1215,7 +1220,7 @@ config_pollsleep() * @return The feedback config data pointer */ FEEDBACK_CONF * -notification_get_config_feedback() +config_get_feedback_data() { return &feedback; } @@ -1312,6 +1317,8 @@ int i; static void global_defaults() { + uint8_t mac_addr[6]=""; + struct utsname uname_data; gateway.n_threads = 1; gateway.n_nbpoll = DEFAULT_NBPOLLS; gateway.pollsleep = DEFAULT_POLLSLEEP; @@ -1321,20 +1328,23 @@ global_defaults() gateway.version_string = NULL; gateway.id=0; - if(!get_release_string(gateway.release_string)) - { + /* get release string */ + if(!config_get_release_string(gateway.release_string)) sprintf(gateway.release_string,"undefined"); + + /* get first mac_address in SHA1 */ + if(config_get_ifaddr(mac_addr)) { + gw_sha1_str(mac_addr, 6, gateway.mac_sha1); + } else { + memset(gateway.mac_sha1, '\0', sizeof(gateway.mac_sha1)); + memcpy(gateway.mac_sha1, "MAC-undef", 9); } - unsigned char mac[6]; - if(config_get_ifaddr(mac)) - { - SHA1(mac,6,gateway.mac_sha1); - } - else - { - sprintf(gateway.mac_sha1,"MAC-undef"); - } + /* get uname info */ + if (uname(&uname_data)) + strcpy(gateway.sysname, "undefined"); + else + strncpy(gateway.sysname, uname_data.sysname, _SYSNAME_STR_LENGTH); } /** @@ -1344,12 +1354,14 @@ static void feedback_defaults() { feedback.feedback_enable = 0; - feedback.feedback_last_action = 0; - feedback.feedback_timeout = 30; - feedback.feedback_connect_timeout = 30; - feedback.feedback_url = NULL; - feedback.feedback_setup_info = strdup("10000-eee-AAA-444"); feedback.feedback_user_info = NULL; + feedback.feedback_last_action = _NOTIFICATION_SEND_PENDING; + feedback.feedback_timeout = _NOTIFICATION_OPERATION_TIMEOUT; + feedback.feedback_connect_timeout = _NOTIFICATION_CONNECT_TIMEOUT; + feedback.feedback_url = NULL; + feedback.release_info = gateway.release_string; + feedback.sysname = gateway.sysname; + feedback.mac_sha1 = gateway.mac_sha1; } /** @@ -1970,158 +1982,190 @@ int i; } return 0; } - -int config_get_ifaddr(unsigned char *output) +/** + * Get the MAC address of first network interface + * + * and fill the provided allocated buffer with SHA1 encoding + * @param output Allocated 6 bytes buffer + * @return 1 on success, 0 on failure + * + */ +int +config_get_ifaddr(unsigned char *output) { - struct ifreq ifr; - struct ifconf ifc; - char buf[1024]; - int success = 0; + struct ifreq ifr; + struct ifconf ifc; + char buf[1024]; + struct ifreq* it; + struct ifreq* end; + int success = 0; - int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sock == -1) { /* handle error*/ }; + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock == -1) { + return 0; + }; - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = buf; - if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ } + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { + return 0; + } - struct ifreq* it = ifc.ifc_req; - const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq)); + it = ifc.ifc_req; + end = it + (ifc.ifc_len / sizeof(struct ifreq)); - for (; it != end; ++it) { - strcpy(ifr.ifr_name, it->ifr_name); - if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { - if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback - if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { - success = 1; - break; - } - } - } - else { /* handle error */ } - } + for (; it != end; ++it) { + strcpy(ifr.ifr_name, it->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { + if (! (ifr.ifr_flags & IFF_LOOPBACK)) { /* don't count loopback */ + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { + success = 1; + break; + } + } + } else { + return 0; + } + } - if (success) memcpy(output, ifr.ifr_hwaddr.sa_data, 6); + if (success) + memcpy(output, ifr.ifr_hwaddr.sa_data, 6); - return success; + return success; } /** + * Get the linux distribution info + * + * @param release The allocated buffer where + * the found distribution is copied into. + * @return 1 on success, 0 on failure * - * @param release - * @return */ -int get_release_string(char* release) +int +config_get_release_string(char* release) { - const char *masks[]= { - "/etc/*-version", "/etc/*-release", - "/etc/*_version", "/etc/*_release" - }; - bool have_ubuf; - struct utsname ubuf; - bool have_distribution; - char distribution[256]; - int fd; - int i; - char *to; + const char *masks[]= { + "/etc/*-version", "/etc/*-release", + "/etc/*_version", "/etc/*_release" + }; - have_ubuf = (uname(&ubuf) != -1); - have_distribution= false; - if ((fd= open("/etc/lsb-release", O_RDONLY)) != -1) - { - /* LSB-compliant distribution! */ - size_t len= read(fd, (char*)distribution, sizeof(distribution)-1); - close(fd); - if (len != (size_t)-1) + bool have_distribution; + char distribution[_RELEASE_STR_LENGTH]=""; + int fd; + int i; + char *to; + + have_distribution= false; + + /* get data from lsb-release first */ + if ((fd= open("/etc/lsb-release", O_RDONLY)) != -1) { - distribution[len]= 0; // safety - char *found= strstr(distribution, "DISTRIB_DESCRIPTION="); - if (found) - { - have_distribution = true; - char *end = strstr(found, "\n"); - if (end == NULL) - end = distribution + len; - found += 20; - - if (*found == '"' && end[-1] == '"') + /* LSB-compliant distribution! */ + size_t len= read(fd, (char*)distribution, sizeof(distribution)-1); + close(fd); + if (len != (size_t)-1) { - found++; - end--; + distribution[len]= 0; + char *found= strstr(distribution, "DISTRIB_DESCRIPTION="); + if (found) + { + have_distribution = true; + char *end = strstr(found, "\n"); + if (end == NULL) + end = distribution + len; + found += 20; + + if (*found == '"' && end[-1] == '"') + { + found++; + end--; + } + *end = 0; + + to = strcpy(distribution, "lsb: "); + memmove(to, found, end - found + 1); + + strncpy(release, to, _RELEASE_STR_LENGTH); + + return 1; + } } - *end = 0; - - to = strcpy(distribution, "lsb: "); - memmove(to, found, end - found + 1); - - strncpy(release, to, 255); - return 1; - } } - } - /* if not an LSB-compliant distribution */ - for (i= 0; !have_distribution && i < 4; i++) - { - glob_t found; - char *new_to; - fprintf(stderr, "glob %d: [%s]\n", i, masks[i]); + /* if not an LSB-compliant distribution */ + for (i= 0; !have_distribution && i < 4; i++) + { + glob_t found; + char *new_to; - if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0) - { - int fd; - int k = 0; - int skipindex = 0; - int startindex = 0; + if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0) + { + int fd; + int k = 0; + int skipindex = 0; + int startindex = 0; - fprintf(stderr, "Matched [%lu] paths\n", found.gl_pathc); + for (k = 0; k< found.gl_pathc; k++) { + if (strcmp(found.gl_pathv[k], "/etc/lsb-release") == 0) { + skipindex = k; + } + } - for (k = 0; k< found.gl_pathc; k++) { - fprintf(stderr, "Possibly opening [%s]\n", found.gl_pathv[k]); - if (strcmp(found.gl_pathv[k], "/etc/lsb-release") == 0) { - fprintf(stderr, "Skipping [%s]\n", found.gl_pathv[k]); - skipindex = k; - } - } + if ( skipindex == 0) + startindex++; + if ((fd= open(found.gl_pathv[startindex], O_RDONLY)) != -1) + { + /* + +5 and -8 below cut the file name part out of the + full pathname that corresponds to the mask as above. + */ + new_to = strcpy(distribution, found.gl_pathv[0] + 5); + new_to += 8; + *new_to++ = ':'; + *new_to++ = ' '; - if ( skipindex == 0) - startindex++; + size_t to_len= distribution + sizeof(distribution) - 1 - new_to; + size_t len= read(fd, (char*)new_to, to_len); - // skip ound.gl_pathv[0] if it's /etc/lsb-release - if ((fd= open(found.gl_pathv[startindex], O_RDONLY)) != -1) - { - /* - +5 and -8 below cut the file name part out of the - full pathname that corresponds to the mask as above. - */ - //char *to= strmov(distribution, found.gl_pathv[0] + 5) - 8; - new_to = strcpy(distribution, found.gl_pathv[0] + 5); - new_to += 8; - *new_to++ = ':'; - *new_to++ = ' '; + close(fd); - size_t to_len= distribution + sizeof(distribution) - 1 - new_to; - size_t len= read(fd, (char*)new_to, to_len); - close(fd); - if (len != (size_t)-1) - { - new_to[len]= 0; // safety - char *end= strstr(new_to, "\n"); - if (end) - *end= 0; + if (len != (size_t)-1) + { + new_to[len]= 0; + char *end= strstr(new_to, "\n"); + if (end) + *end= 0; - have_distribution= true; - fprintf(stderr, "Distribution [%i]=[%s]\n", i, new_to); - strncpy(release, new_to, 255); - } - } - } - globfree(&found); - } + have_distribution= true; + strncpy(release, new_to, _RELEASE_STR_LENGTH); + } + } + } + globfree(&found); + } - if (have_distribution) - return 1; - else - return 0; + if (have_distribution) + return 1; + else + return 0; +} + +/** + * Add the 'send_feedback' task to the task list + */ +void +config_enable_feedback_task(void) { + FEEDBACK_CONF *cfg = config_get_feedback_data(); + if (cfg->feedback_enable) + hktask_add("send_feedback", module_feedback_send, cfg, 30); +} + +/** + * Remove the 'send_feedback' task + */ +void +config_disable_feedback_task(void) { + hktask_remove("send_feedback"); } diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 69c2d52b0..d0e1dd11d 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -47,6 +47,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -537,10 +538,7 @@ module_feedback_send(void* data) { struct tm *now_tm; int hour; int n_mod=0; - struct utsname uname_data; - unsigned char setup_if_address[6]=""; - unsigned char sha1_if_address[20]=""; - char hex_setup_info[40 + 1]=""; + char hex_setup_info[2 * SHA_DIGEST_LENGTH + 1]=""; now = time(NULL); now_tm = localtime(&now); @@ -565,7 +563,7 @@ module_feedback_send(void* data) { if (hour > 4 || hour < 2) { /* It's not the rigt time, mark it as to be done and return */ - feedback_config->feedback_last_action = 0; + feedback_config->feedback_last_action = _NOTIFICATION_SEND_PENDING; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -576,7 +574,7 @@ module_feedback_send(void* data) { } /* Time to run the task: if a previous run was succesfull skip next runs */ - if (feedback_config->feedback_last_action == 1) { + if (feedback_config->feedback_last_action == _NOTIFICATION_SEND_OK) { /* task was done before, return */ LOGIF(LE, (skygw_log_write_flush( @@ -593,14 +591,9 @@ module_feedback_send(void* data) { hour, feedback_config->feedback_last_action))); - /* get uname info */ - uname(&uname_data); + /* encode MAC-sha1 to HEX */ + gw_bin2hex(hex_setup_info, feedback_config->mac_sha1, SHA_DIGEST_LENGTH); - /* get first available network interface info */ - if (config_get_ifaddr(setup_if_address)) { - gw_sha1_str(setup_if_address, 6, sha1_if_address); - gw_bin2hex(hex_setup_info, sha1_if_address, 20); - } /* allocate first memory chunck for httpd servr reply */ chunk.data = malloc(1); /* will be grown as needed by the realloc above */ @@ -617,11 +610,7 @@ module_feedback_send(void* data) { buffer = gwbuf_alloc(n_mod * 256); data_ptr = GWBUF_DATA(buffer); - if (strlen(hex_setup_info)) - sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); - else - sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", feedback_config->feedback_setup_info); - + sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); data_ptr+=strlen(data_ptr); sprintf(data_ptr, "FEEDBACK_USER_INFO\t%s\n", feedback_config->feedback_user_info); data_ptr+=strlen(data_ptr); @@ -629,14 +618,9 @@ module_feedback_send(void* data) { data_ptr+=strlen(data_ptr); sprintf(data_ptr, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale"); data_ptr+=strlen(data_ptr); - - if (strlen(uname_data.sysname)) - sprintf(data_ptr, "Uname_sysname\t%s\n", uname_data.sysname); - else - sprintf(data_ptr, "Uname_sysname\t%s\n", ""); - + sprintf(data_ptr, "Uname_sysname\t%s\n", feedback_config->sysname); data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "Uname_distribution\t%s\n", ""); + sprintf(data_ptr, "Uname_distribution\t%s\n", feedback_config->release_info); data_ptr+=strlen(data_ptr); while (ptr) @@ -704,14 +688,14 @@ module_feedback_send(void* data) { /* * This makes the connection A LOT LESS SECURE. */ -// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); #endif #ifdef SKIP_HOSTNAME_VERIFICATION /* * this will make the connection less secure. */ - // curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); #endif /* send all data to this function */ @@ -725,7 +709,7 @@ module_feedback_send(void* data) { /* Check for errors */ if(res != CURLE_OK) { - last_action = 0; + last_action = _NOTIFICATION_SEND_ERROR; fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); fprintf(stderr, "curl error_message: [%s]\n", error_message); @@ -736,10 +720,10 @@ module_feedback_send(void* data) { } if (http_code == 200) { - last_action = 1; + last_action = _NOTIFICATION_SEND_OK; } } else { - last_action = 0; + last_action = _NOTIFICATION_SEND_ERROR; } memcpy(&feedback_config->feedback_last_action, &last_action, sizeof(int)); diff --git a/server/core/service.c b/server/core/service.c index 4e59a7f62..246243a93 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -35,6 +35,7 @@ * 13/10/14 Massimiliano Pinto Added hashtable for resources (i.e database names for MySQL services) * 06/02/15 Mark Riddoch Added caching of authentication data * 18/02/15 Mark Riddoch Added result set management + * 03/03/15 Massimiliano Pinto Added config_enable_feedback_task() call in serviceStartAll * * @endverbatim */ @@ -60,7 +61,6 @@ #include #include #include -#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -476,11 +476,8 @@ serviceStartAll() { SERVICE *ptr; int n = 0,i; -FEEDBACK_CONF *feedback = notification_get_config_feedback(); - if (feedback->feedback_enable) { - hktask_add("send_feedback", module_feedback_send, feedback, 300); - } + config_enable_feedback_task(); ptr = allServices; while (ptr && !ptr->svc_do_shutdown) diff --git a/server/include/config.h b/server/include/config.h index 811260205..3648c97a0 100644 --- a/server/include/config.h +++ b/server/include/config.h @@ -30,12 +30,15 @@ * 07/05/14 Massimiliano Pinto Added version_string to global configuration * 23/05/14 Massimiliano Pinto Added id to global configuration * 17/10/14 Mark Riddoch Added poll tuning configuration parameters + * 05/03/15 Massimiliano Pinto Added sysname, release, sha1_mac to gateway struct * * @endverbatim */ #define DEFAULT_NBPOLLS 3 /**< Default number of non block polls before we block */ #define DEFAULT_POLLSLEEP 1000 /**< Default poll wait time (milliseconds) */ +#define _SYSNAME_STR_LENGTH 256 /**< sysname len */ +#define _RELEASE_STR_LENGTH 256 /**< release len */ /** * Maximum length for configuration parameter value. */ @@ -92,11 +95,12 @@ typedef struct config_context { * The gateway global configuration data */ typedef struct { - int n_threads; /**< Number of polling threads */ - char *version_string; /**< The version string of embedded database library */ - char release_string[256]; /**< The release name string of the system */ - char mac_sha1[SHA_DIGEST_LENGTH]; /*< The SHA1 digest of an interface MAC address */ - unsigned long id; /**< MaxScale ID */ + 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 */ + char sysname[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */ + uint8_t mac_sha1[SHA_DIGEST_LENGTH]; /*< The SHA1 digest of an interface MAC address */ + unsigned long id; /**< MaxScale ID */ unsigned int n_nbpoll; /**< Tune number of non-blocking polls */ unsigned int pollsleep; /**< Wait time in blocking polls */ } GATEWAY_CONF; @@ -109,7 +113,7 @@ extern unsigned int config_pollsleep(); CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name); config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param); CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param); - +extern int config_truth_value(char *); bool config_set_qualified_param( CONFIG_PARAMETER* param, void* val, @@ -133,4 +137,7 @@ bool config_get_valtarget( CONFIG_PARAMETER* param, const char* name, /*< if NULL examine current param only */ config_param_type_t ptype); + +void config_enable_feedback_task(void); +void config_disable_feedback_task(void); #endif diff --git a/server/include/notification.h b/server/include/notification.h index b802e934d..ca91684b3 100644 --- a/server/include/notification.h +++ b/server/include/notification.h @@ -1,5 +1,5 @@ -#ifndef _NOTIFICATION_H -#define _NOTIFICATION_H +#ifndef _NOTIFICATION_SERVICE_H +#define _NOTIFICATION_SERVICE_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 @@ -32,6 +32,12 @@ * @endverbatim */ +#define _NOTIFICATION_CONNECT_TIMEOUT 30 +#define _NOTIFICATION_OPERATION_TIMEOUT 30 +#define _NOTIFICATION_SEND_PENDING 0 +#define _NOTIFICATION_SEND_OK 1 +#define _NOTIFICATION_SEND_ERROR 2 + /** * The configuration and usage information data for feeback service */ @@ -39,14 +45,15 @@ typedef struct { int feedback_enable; /**< Enable/Disable Notification feedback */ char *feedback_url; /**< URL to which the data is sent */ - char *feedback_setup_info; /**< MaxScale setup identifier info included in the feedback data sent */ char *feedback_user_info; /**< User info included in the feedback data sent */ int feedback_timeout; /**< An attempt to write/read the data times out and fails after this many seconds */ int feedback_connect_timeout; /**< An attempt to send the data times out and fails after this many seconds */ int feedback_last_action; /**< Holds the feedback last send action status */ + char *release_info; /**< Operating system Release name */ + char *sysname; /**< Operating system name */ + uint8_t *mac_sha1; /**< First available MAC address*/ } FEEDBACK_CONF; - -extern FEEDBACK_CONF* notification_get_config_feedback(); - +extern char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len); +extern void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out); #endif From e952db0ee9430c78f8f68628cdd2c7554de4a03a Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 5 Mar 2015 18:58:07 +0100 Subject: [PATCH 09/20] Notification server integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notification server integration: added proper logging to errors Added “enable/disable feedback” via maxadmin/telnet --- server/core/config.c | 33 ++++++++- server/core/load_utils.c | 118 +++++++++++++++++++++--------- server/modules/routing/debugcmd.c | 44 ++++++++++- 3 files changed, 156 insertions(+), 39 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 0fa519a25..50caee767 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -2158,8 +2158,37 @@ config_get_release_string(char* release) void config_enable_feedback_task(void) { FEEDBACK_CONF *cfg = config_get_feedback_data(); - if (cfg->feedback_enable) - hktask_add("send_feedback", module_feedback_send, cfg, 30); + int url_set = 0; + int user_info_set = 0; + int enable_set = cfg->feedback_enable; + + url_set = cfg->feedback_url != NULL && strlen(cfg->feedback_url); + user_info_set = cfg->feedback_user_info != NULL && strlen(cfg->feedback_user_info); + + if (enable_set && url_set && user_info_set) { + /* Add the task to the tasl list */ + if (hktask_add("send_feedback", module_feedback_send, cfg, 30)) { + + LOGIF(LM, (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Notification service feedback task started: URL=%s, User-Info=%s, Frequency %u seconds", + cfg->feedback_url, + cfg->feedback_user_info, + 30))); + } + } else { + if (enable_set) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Notification service feedback cannot start: feedback_enable=1 but" + " some required parameters are not set: %s%s%s", + url_set == 0 ? "feedback_url is not set" : "", (user_info_set == 0 && url_set == 0) ? ", " : "", user_info_set == 0 ? "feedback_user_info is not set" : ""))); + } else { + LOGIF(LT, (skygw_log_write_flush( + LOGFILE_TRACE, + "Notification service feedback is not enabled"))); + } + } } /** diff --git a/server/core/load_utils.c b/server/core/load_utils.c index d0e1dd11d..fc9fd03c0 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -70,24 +70,36 @@ struct MemoryStruct { size_t size; }; +/** + * Callback write routine for curl library, getting remote server reply + * + * @param contents New data to add + * @param size Data size + * @param nmemb Elements in the buffer + * @param userp Pointer to the buffer + * @return 0 on failure, memory size on success + * + */ static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { - size_t realsize = size * nmemb; - struct MemoryStruct *mem = (struct MemoryStruct *)userp; + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; - mem->data = realloc(mem->data, mem->size + realsize + 1); - if(mem->data == NULL) { - /* out of memory! */ - printf("not enough memory (realloc returned NULL)\n"); - return 0; - } + mem->data = realloc(mem->data, mem->size + realsize + 1); + if(mem->data == NULL) { + /* out of memory! */ + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error in module_feedback_send(), not enough memory for realloc"))); + return 0; + } - memcpy(&(mem->data[mem->size]), contents, realsize); - mem->size += realsize; - mem->data[mem->size] = 0; + memcpy(&(mem->data[mem->size]), contents, realsize); + mem->size += realsize; + mem->data[mem->size] = 0; - return realsize; + return realsize; } char* get_maxscale_home(void) @@ -525,7 +537,7 @@ int *data; void module_feedback_send(void* data) { MODULES *ptr = registered; - CURL *curl; + CURL *curl = NULL; CURLcode res; struct curl_httppost *formpost=NULL; struct curl_httppost *lastptr=NULL; @@ -533,7 +545,7 @@ module_feedback_send(void* data) { void *data_ptr=NULL; long http_code = 0; struct MemoryStruct chunk; - int last_action = 0; + int last_action = _NOTIFICATION_SEND_PENDING; time_t now; struct tm *now_tm; int hour; @@ -548,10 +560,17 @@ module_feedback_send(void* data) { /* Configuration check */ - if (feedback_config->feedback_url == NULL || feedback_config->feedback_user_info == NULL) { + if (feedback_config->feedback_enable == 0 || feedback_config->feedback_url == NULL || feedback_config->feedback_user_info == NULL) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "module_feedback_create : skip task because feedback_url or feedback_user_info is NULL"))); + "Error in module_feedback_send(): some mandatory parameters are not set" + " feedback_enable=%u, feedback_url=%s, feedback_user_info=%s", + feedback_config->feedback_enable, + feedback_config->feedback_url == NULL ? "NULL" : feedback_config->feedback_url, + feedback_config->feedback_user_info == NULL ? "NULL" : feedback_config->feedback_user_info))); + + feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR; + return; } @@ -565,9 +584,10 @@ module_feedback_send(void* data) { /* It's not the rigt time, mark it as to be done and return */ feedback_config->feedback_last_action = _NOTIFICATION_SEND_PENDING; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "module_feedback_create : skip task because of time interval: hour is [%d]", + LOGIF(LT, (skygw_log_write_flush( + LOGFILE_TRACE, + "module_feedback_send(): execution skipped, current hour [%d]" + " is not within the proper interval (from 2 AM to 4 AM)", hour))); return; @@ -577,17 +597,17 @@ module_feedback_send(void* data) { if (feedback_config->feedback_last_action == _NOTIFICATION_SEND_OK) { /* task was done before, return */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "module_feedback_create : skip task because of previous succesful run: hour is [%d], last_action [%d]", + LOGIF(LT, (skygw_log_write_flush( + LOGFILE_TRACE, + "module_feedback_send() : execution skipped because of previous succesful run: hour is [%d], last_action [%d]", hour, feedback_config->feedback_last_action))); return; } - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "module_feedback_create : task runs: hour is [%d], last_action [%d]", + LOGIF(LT, (skygw_log_write_flush( + LOGFILE_TRACE, + "module_feedback_send(): task now runs: hour is [%d], last_action [%d]", hour, feedback_config->feedback_last_action))); @@ -608,6 +628,17 @@ module_feedback_send(void* data) { ptr = registered; buffer = gwbuf_alloc(n_mod * 256); + if (buffer == NULL) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error in module_feedback_send(): gwbuf_alloc() failed to allocate %d bytes", + n_mod * 256))); + + feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR; + + return; + } + data_ptr = GWBUF_DATA(buffer); sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); @@ -711,26 +742,38 @@ module_feedback_send(void* data) { if(res != CURLE_OK) { last_action = _NOTIFICATION_SEND_ERROR; - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - fprintf(stderr, "curl error_message: [%s]\n", error_message); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: module_feedback_send(), curl call for [%s] failed due: %s, %s", + feedback_config->feedback_url, + curl_easy_strerror(res), + error_message))); } else { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - - fprintf(stderr, "Reply from remote server is\n[%s]. Code [%lu]\n", chunk.data, http_code); } if (http_code == 200) { last_action = _NOTIFICATION_SEND_OK; + } else { + last_action = _NOTIFICATION_SEND_ERROR; + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error in module_feedback_send(), Bad HTTP Code from remote server: %lu", + http_code))); } } else { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error in module_feedback_send(), curl object not initialized"))); last_action = _NOTIFICATION_SEND_ERROR; } - - memcpy(&feedback_config->feedback_last_action, &last_action, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "module_feedback_create : task run result: hour is [%d], last_action [%d], http_code [%d]", + /* update last action in the config struct */ + feedback_config->feedback_last_action = last_action; + + LOGIF(LT, (skygw_log_write_flush( + LOGFILE_TRACE, + "module_feedback_send(): task run result: hour is [%d], last_action [%d], HTTP code [%d]", hour, feedback_config->feedback_last_action, http_code))); if(chunk.data) @@ -738,8 +781,11 @@ module_feedback_send(void* data) { gwbuf_free(buffer); - curl_easy_cleanup(curl); - curl_formfree(formpost); + if (curl) { + curl_easy_cleanup(curl); + curl_formfree(formpost); + } + curl_global_cleanup(); } diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index 821e38f23..8285fabc5 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -36,12 +36,13 @@ * Date Who Description * 20/06/13 Mark Riddoch Initial implementation * 17/07/13 Mark Riddoch Additional commands - * 09/08/2013 Massimiliano Pinto Added enable/disable commands (now only for log) + * 09/08/13 Massimiliano Pinto Added enable/disable commands (now only for log) * 20/05/14 Mark Riddoch Added ability to give server and service names rather * than simply addresses * 23/05/14 Mark Riddoch Added support for developer and user modes * 29/05/14 Mark Riddoch Add Filter support * 16/10/14 Mark Riddoch Add show eventq + * 05/03/15 Massimiliano Pinto Added enable/disable feedback * * @endverbatim */ @@ -365,6 +366,8 @@ static void enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor); static void disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor); static void enable_service_root(DCB *dcb, SERVICE *service); static void disable_service_root(DCB *dcb, SERVICE *service); +static void enable_feedback_action(); +static void disable_feedback_action(); /** * * The subcommands of the enable command @@ -406,6 +409,14 @@ struct subcommand enableoptions[] = { "Enable root access to a service, pass a service name to enable root access", {ARG_TYPE_SERVICE, 0, 0} }, + { + "feedback", + 0, + enable_feedback_action, + "Enable MaxScale modules list sending via http to notification service", + "Enable MaxScale modules list sending via http to notification service", + {0, 0, 0} + }, { NULL, 0, @@ -458,6 +469,14 @@ struct subcommand disableoptions[] = { "Disable root access to a service", {ARG_TYPE_SERVICE, 0, 0} }, + { + "feedback", + 0, + disable_feedback_action, + "Disable MaxScale modules list sending via http to notification service", + "Disable MaxScale modules list sending via http to notification service", + {0, 0, 0} + }, { NULL, 0, @@ -1381,6 +1400,29 @@ set_nbpoll(DCB *dcb, int nb) poll_set_nonblocking_polls(nb); } +/** + * Re-enable sendig MaxScale module list via http + * Proper [feedback] section in MaxSclale.cnf + * is required. + */ +static void +enable_feedback_action(void) +{ + config_enable_feedback_task(); + return; +} + +/** + * Disable sendig MaxScale module list via http + */ + +static void +disable_feedback_action(void) +{ + config_disable_feedback_task(); + return; +} + #if defined(FAKE_CODE) static void fail_backendfd(void) { From 5bbd9a66fc9f6ffa6ef8775c250c8c12f4295236 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 6 Mar 2015 11:15:06 +0100 Subject: [PATCH 10/20] Added show feedbackreport Added show feedbackreport --- server/core/load_utils.c | 197 ++++++++++++++++++++---------- server/include/modules.h | 1 + server/modules/routing/debugcmd.c | 4 + 3 files changed, 137 insertions(+), 65 deletions(-) diff --git a/server/core/load_utils.c b/server/core/load_utils.c index fc9fd03c0..9388c2f51 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -64,6 +64,7 @@ static void register_module(const char *module, void *modobj, MODULE_INFO *info); static void unregister_module(const char *module); +int module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *cfg); struct MemoryStruct { char *data; @@ -452,6 +453,29 @@ MODULES *ptr = registered; dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n\n"); } +/** + * Print Modules to a DCB + * + * Diagnostic routine to display all the loaded modules + */ +void +moduleShowFeedbackReport(DCB *dcb) +{ +GWBUF *buffer; +MODULES *modules_list = registered; +FEEDBACK_CONF *feedback_config = config_get_feedback_data(); + + if (!module_create_feedback_report(&buffer, modules_list, feedback_config)) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory"))); + + return; + } + dcb_printf(dcb, (char *)GWBUF_DATA(buffer)); + gwbuf_free(buffer); +} + /** * Provide a row to the result set that defines the set of modules * @@ -536,7 +560,7 @@ int *data; */ void module_feedback_send(void* data) { - MODULES *ptr = registered; + MODULES *modules_list = registered; CURL *curl = NULL; CURLcode res; struct curl_httppost *formpost=NULL; @@ -599,7 +623,7 @@ module_feedback_send(void* data) { LOGIF(LT, (skygw_log_write_flush( LOGFILE_TRACE, - "module_feedback_send() : execution skipped because of previous succesful run: hour is [%d], last_action [%d]", + "module_feedback_send(): execution skipped because of previous succesful run: hour is [%d], last_action [%d]", hour, feedback_config->feedback_last_action))); return; @@ -611,85 +635,27 @@ module_feedback_send(void* data) { hour, feedback_config->feedback_last_action))); - /* encode MAC-sha1 to HEX */ - gw_bin2hex(hex_setup_info, feedback_config->mac_sha1, SHA_DIGEST_LENGTH); - - /* allocate first memory chunck for httpd servr reply */ chunk.data = malloc(1); /* will be grown as needed by the realloc above */ chunk.size = 0; /* no data at this point */ - /* count loaded modules */ - while (ptr) - { - ptr = ptr->next; - n_mod++; - } - ptr = registered; - - buffer = gwbuf_alloc(n_mod * 256); - if (buffer == NULL) { + if (!module_create_feedback_report(&buffer, modules_list, feedback_config)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error in module_feedback_send(): gwbuf_alloc() failed to allocate %d bytes", - n_mod * 256))); + "Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory"))); feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR; return; } - data_ptr = GWBUF_DATA(buffer); - - sprintf(data_ptr, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "FEEDBACK_USER_INFO\t%s\n", feedback_config->feedback_user_info); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "VERSION\t%s\n", MAXSCALE_VERSION); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale"); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "Uname_sysname\t%s\n", feedback_config->sysname); - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "Uname_distribution\t%s\n", feedback_config->release_info); - data_ptr+=strlen(data_ptr); - - while (ptr) - { - sprintf(data_ptr, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version); - data_ptr+=strlen(data_ptr); - - if (ptr->info) { - sprintf(data_ptr, "module_%s_api\t%d.%d.%d\n", - ptr->module, - ptr->info->api_version.major, - ptr->info->api_version.minor, - ptr->info->api_version.patch); - - data_ptr+=strlen(data_ptr); - sprintf(data_ptr, "module_%s_releasestatus\t%s\n", - ptr->module, - ptr->info->status == MODULE_IN_DEVELOPMENT - ? "In Development" - : (ptr->info->status == MODULE_ALPHA_RELEASE - ? "Alpha" - : (ptr->info->status == MODULE_BETA_RELEASE - ? "Beta" - : (ptr->info->status == MODULE_GA - ? "GA" - : (ptr->info->status == MODULE_EXPERIMENTAL - ? "Experimental" : "Unknown"))))); - data_ptr+=strlen(data_ptr); - } - ptr = ptr->next; - } /* Initializing curl library for data send via HTTP */ curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); - if(curl) { + if (curl) { char error_message[CURL_ERROR_SIZE]=""; curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_message); @@ -776,10 +742,11 @@ module_feedback_send(void* data) { "module_feedback_send(): task run result: hour is [%d], last_action [%d], HTTP code [%d]", hour, feedback_config->feedback_last_action, http_code))); - if(chunk.data) + if (chunk.data) free(chunk.data); - gwbuf_free(buffer); + if (buffer) + gwbuf_free(buffer); if (curl) { curl_easy_cleanup(curl); @@ -789,3 +756,103 @@ module_feedback_send(void* data) { curl_global_cleanup(); } +/** + * Create the feedback report as string. + * I t could be sent to notification service + * or just printed via maxadmin/telnet + * + * @param buffe The pointr for GWBUF allocation, to be freed by the caller + * @param modules The mouleds list + * @param cfg The feedback configuration + * @return 0 on failure, 1 on success + * + */ + +int +module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *cfg) { + + MODULES *ptr = modules; + int n_mod = 0; + char *data_ptr=NULL; + char hex_setup_info[2 * SHA_DIGEST_LENGTH + 1]=""; + time_t now; + struct tm *now_tm; + + now = time(NULL); + + /* count loaded modules */ + while (ptr) + { + ptr = ptr->next; + n_mod++; + } + + /* module lists pointer is set back to the head */ + ptr = modules; + + /** + * allocate gwbuf for data to send + * + * each module gives 4 rows + * product and release rows add 6 rows + * row is 256 bytes long + */ + + *buffer = gwbuf_alloc(((n_mod * 4) + 6) * 256); + + if (buffer == NULL) { + return 0; + } + + /* encode MAC-sha1 to HEX */ + gw_bin2hex(hex_setup_info, cfg->mac_sha1, SHA_DIGEST_LENGTH); + + + data_ptr = (char *)GWBUF_DATA(*buffer); + + snprintf(data_ptr, 255, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); + data_ptr+=strlen(data_ptr); + snprintf(data_ptr, 255, "FEEDBACK_USER_INFO\t%s\n", cfg->feedback_user_info); + data_ptr+=strlen(data_ptr); + snprintf(data_ptr, 255, "VERSION\t%s\n", MAXSCALE_VERSION); + data_ptr+=strlen(data_ptr); + snprintf(data_ptr, 255 * 2, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale"); + data_ptr+=strlen(data_ptr); + snprintf(data_ptr, 255, "Uname_sysname\t%s\n", cfg->sysname); + data_ptr+=strlen(data_ptr); + snprintf(data_ptr, 255, "Uname_distribution\t%s\n", cfg->release_info); + data_ptr+=strlen(data_ptr); + + while (ptr) + { + snprintf(data_ptr, 255 * 3, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version); + data_ptr+=strlen(data_ptr); + + if (ptr->info) { + snprintf(data_ptr, 255, "module_%s_api\t%d.%d.%d\n", + ptr->module, + ptr->info->api_version.major, + ptr->info->api_version.minor, + ptr->info->api_version.patch); + + data_ptr+=strlen(data_ptr); + snprintf(data_ptr, 255, "module_%s_releasestatus\t%s\n", + ptr->module, + ptr->info->status == MODULE_IN_DEVELOPMENT + ? "In Development" + : (ptr->info->status == MODULE_ALPHA_RELEASE + ? "Alpha" + : (ptr->info->status == MODULE_BETA_RELEASE + ? "Beta" + : (ptr->info->status == MODULE_GA + ? "GA" + : (ptr->info->status == MODULE_EXPERIMENTAL + ? "Experimental" : "Unknown"))))); + data_ptr+=strlen(data_ptr); + } + ptr = ptr->next; + } + + return 1; +} + diff --git a/server/include/modules.h b/server/include/modules.h index 2637e6a29..51e10b29d 100644 --- a/server/include/modules.h +++ b/server/include/modules.h @@ -70,5 +70,6 @@ extern void dprintAllModules(DCB *); extern RESULTSET *moduleGetList(); extern char *get_maxscale_home(void); extern void module_feedback_send(void*); +extern void moduleShowFeedbackReport(DCB *dcb); #endif diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index 8285fabc5..253647786 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -130,6 +130,10 @@ struct subcommand showoptions[] = { "Show the event statistics", "Show the event statistics", {0, 0, 0} }, + { "feedbackreport", 0, moduleShowFeedbackReport, + "Show the report of MaxScale loaded modules, suitable for Notification Service", + "Show the report of MaxScale loaded modules, suitable for Notification Service", + {0, 0, 0} }, { "filter", 1, dprintFilter, "Show details of a filter, called with a filter name", "Show details of a filter, called with the address of a filter", From 6a64457255c7858b2d25d6eaa0bf4d9beb2c3fb0 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 09:21:59 +0100 Subject: [PATCH 11/20] Fix compiler warning Fix compiler warning --- server/core/config.c | 1 + server/include/notification.h | 1 + 2 files changed, 2 insertions(+) diff --git a/server/core/config.c b/server/core/config.c index 50caee767..c7c0158e7 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -92,6 +92,7 @@ int config_truth_value(char *str); static int internalService(char *router); int config_get_ifaddr(unsigned char *output); int config_get_release_string(char* release); +FEEDBACK_CONF * config_get_feedback_data(); static char *config_file = NULL; static GATEWAY_CONF gateway; diff --git a/server/include/notification.h b/server/include/notification.h index ca91684b3..a2f813596 100644 --- a/server/include/notification.h +++ b/server/include/notification.h @@ -56,4 +56,5 @@ typedef struct { extern char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len); extern void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out); +extern FEEDBACK_CONF * config_get_feedback_data(); #endif From e1a6b1de08b3a0b9ef8e385d977a732c1fe849c7 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 09:59:43 +0100 Subject: [PATCH 12/20] Added _NOTIFICATION_REPORT_ROW_LEN Added _NOTIFICATION_REPORT_ROW_LEN for gwbuf_alloc based on know row numbers --- server/core/load_utils.c | 26 ++++++++++++++------------ server/include/notification.h | 1 + 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 9388c2f51..b8c9c5fb5 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -777,6 +777,7 @@ module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *c char hex_setup_info[2 * SHA_DIGEST_LENGTH + 1]=""; time_t now; struct tm *now_tm; + int report_max_bytes=0; now = time(NULL); @@ -794,11 +795,12 @@ module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *c * allocate gwbuf for data to send * * each module gives 4 rows - * product and release rows add 6 rows - * row is 256 bytes long + * product and release rows add 7 rows + * row is _NOTIFICATION_REPORT_ROW_LEN bytes long */ - *buffer = gwbuf_alloc(((n_mod * 4) + 6) * 256); + report_max_bytes = ((n_mod * 4) + 7) * (_NOTIFICATION_REPORT_ROW_LEN + 1); + *buffer = gwbuf_alloc(report_max_bytes); if (buffer == NULL) { return 0; @@ -810,33 +812,33 @@ module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *c data_ptr = (char *)GWBUF_DATA(*buffer); - snprintf(data_ptr, 255, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info); data_ptr+=strlen(data_ptr); - snprintf(data_ptr, 255, "FEEDBACK_USER_INFO\t%s\n", cfg->feedback_user_info); + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "FEEDBACK_USER_INFO\t%s\n", cfg->feedback_user_info == NULL ? "not_set" : cfg->feedback_user_info); data_ptr+=strlen(data_ptr); - snprintf(data_ptr, 255, "VERSION\t%s\n", MAXSCALE_VERSION); + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "VERSION\t%s\n", MAXSCALE_VERSION); data_ptr+=strlen(data_ptr); - snprintf(data_ptr, 255 * 2, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale"); + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN * 2, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale"); data_ptr+=strlen(data_ptr); - snprintf(data_ptr, 255, "Uname_sysname\t%s\n", cfg->sysname); + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "Uname_sysname\t%s\n", cfg->sysname); data_ptr+=strlen(data_ptr); - snprintf(data_ptr, 255, "Uname_distribution\t%s\n", cfg->release_info); + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "Uname_distribution\t%s\n", cfg->release_info); data_ptr+=strlen(data_ptr); while (ptr) { - snprintf(data_ptr, 255 * 3, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version); + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN * 2, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version); data_ptr+=strlen(data_ptr); if (ptr->info) { - snprintf(data_ptr, 255, "module_%s_api\t%d.%d.%d\n", + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "module_%s_api\t%d.%d.%d\n", ptr->module, ptr->info->api_version.major, ptr->info->api_version.minor, ptr->info->api_version.patch); data_ptr+=strlen(data_ptr); - snprintf(data_ptr, 255, "module_%s_releasestatus\t%s\n", + snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "module_%s_releasestatus\t%s\n", ptr->module, ptr->info->status == MODULE_IN_DEVELOPMENT ? "In Development" diff --git a/server/include/notification.h b/server/include/notification.h index a2f813596..349edd707 100644 --- a/server/include/notification.h +++ b/server/include/notification.h @@ -37,6 +37,7 @@ #define _NOTIFICATION_SEND_PENDING 0 #define _NOTIFICATION_SEND_OK 1 #define _NOTIFICATION_SEND_ERROR 2 +#define _NOTIFICATION_REPORT_ROW_LEN 255 /** * The configuration and usage information data for feeback service From af388a26b68fac2411208a339abae238b4ed8f41 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 11:05:48 +0100 Subject: [PATCH 13/20] Feedback report frequency set to 1800 seconds Feedback report frequency set to 1800 seconds --- 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 c7c0158e7..a3ce0992a 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -2168,14 +2168,14 @@ config_enable_feedback_task(void) { if (enable_set && url_set && user_info_set) { /* Add the task to the tasl list */ - if (hktask_add("send_feedback", module_feedback_send, cfg, 30)) { + if (hktask_add("send_feedback", module_feedback_send, cfg, 1800)) { LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, "Notification service feedback task started: URL=%s, User-Info=%s, Frequency %u seconds", cfg->feedback_url, cfg->feedback_user_info, - 30))); + 1800))); } } else { if (enable_set) { From 02e742f29e231f82bf11a3d286b06a4dfa598479 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 9 Mar 2015 14:17:40 +0200 Subject: [PATCH 14/20] Added unit test for feedback service. --- server/core/config.c | 2 +- server/core/test/CMakeLists.txt | 6 ++- server/core/test/testfeedback.c | 86 +++++++++++++++++++++++++++++++++ server/include/notification.h | 2 + server/test/MaxScale_test.cnf | 7 +++ 5 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 server/core/test/testfeedback.c diff --git a/server/core/config.c b/server/core/config.c index c7c0158e7..d9534aeff 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1291,7 +1291,7 @@ handle_feedback_item(const char *name, const char *value) int i; if (strcmp(name, "feedback_enable") == 0) { - feedback.feedback_enable = atoi(value); + feedback.feedback_enable = config_truth_value(value); } else if (strcmp(name, "feedback_user_info") == 0) { diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 2a0977088..0cff62772 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(test_server testserver.c) add_executable(test_users testusers.c) add_executable(test_adminusers testadminusers.c) add_executable(testmemlog testmemlog.c) +add_executable(testfeedback testfeedback.c) target_link_libraries(test_mysql_users MySQLClient fullcore) target_link_libraries(test_hash fullcore) target_link_libraries(test_hint fullcore) @@ -26,6 +27,7 @@ target_link_libraries(test_server fullcore) target_link_libraries(test_users fullcore) target_link_libraries(test_adminusers fullcore) target_link_libraries(testmemlog fullcore) +target_link_libraries(testfeedback fullcore) add_test(testMySQLUsers test_mysql_users) add_test(TestHash test_hash) add_test(TestHint test_hint) @@ -40,6 +42,7 @@ add_test(TestServer test_server) add_test(TestUsers test_users) add_test(TestAdminUsers test_adminusers) add_test(TestMemlog testmemlog) +add_test(TestFeedback testfeedback) set_tests_properties(testMySQLUsers TestHash TestHint @@ -53,4 +56,5 @@ set_tests_properties(testMySQLUsers TestServer TestUsers TestAdminUsers - TestMemlog PROPERTIES ENVIRONMENT MAXSCALE_HOME=${CMAKE_BINARY_DIR}/) + TestMemlog + TestFeedback PROPERTIES ENVIRONMENT MAXSCALE_HOME=${CMAKE_BINARY_DIR}/) diff --git a/server/core/test/testfeedback.c b/server/core/test/testfeedback.c new file mode 100644 index 000000000..cb363e39e --- /dev/null +++ b/server/core/test/testfeedback.c @@ -0,0 +1,86 @@ +/* + * This file is distributed as part of MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * 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 2014 + */ + +/** + * + * @verbatim + * Revision History + * + * Date Who Description + * 09-03-2015 Markus Mäkelä Initial implementation + * + * @endverbatim + */ + +#define FAILTEST(s) printf("TEST FAILED: " s "\n");return 1; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + FEEDBACK_CONF* fc; + char* home; + char* cnf; + GWBUF* buf; + regex_t re; + + hkinit(); + home = getenv("MAXSCALE_HOME"); + + if(home == NULL) + { + FAILTEST("MAXSCALE_HOME was not defined."); + } + printf("Home: %s\n",home); + + cnf = malloc(strlen(home) + strlen("/etc/MaxScale.cnf") + 1); + strcpy(cnf,home); + strcat(cnf,"/etc/MaxScale.cnf"); + + printf("Config: %s\n",cnf); + + config_load(cnf); + + if((fc = config_get_feedback_data()) == NULL || + fc->feedback_user_info == NULL) + { + FAILTEST("Configuration was NULL."); + } + + regcomp(&re,fc->feedback_user_info,0); + + config_enable_feedback_task(); + module_create_feedback_report(&buf,NULL,fc); + printf("%s",(char*)buf->start); + + if(regexec(&re,(char*)buf->start,0,NULL,0)) + { + FAILTEST("Regex match of 'user_info' failed."); + } + + config_disable_feedback_task(); + return 0; +} diff --git a/server/include/notification.h b/server/include/notification.h index 349edd707..6a7f07bac 100644 --- a/server/include/notification.h +++ b/server/include/notification.h @@ -39,6 +39,8 @@ #define _NOTIFICATION_SEND_ERROR 2 #define _NOTIFICATION_REPORT_ROW_LEN 255 +#include + /** * The configuration and usage information data for feeback service */ diff --git a/server/test/MaxScale_test.cnf b/server/test/MaxScale_test.cnf index aeaea2d69..f6887c3e0 100644 --- a/server/test/MaxScale_test.cnf +++ b/server/test/MaxScale_test.cnf @@ -1,6 +1,13 @@ [maxscale] threads=4 +[feedback] +feedback_enable=true +feedback_user_info=user_info +feedback_url=http://127.0.0.1:8080/load.php +feedback_timeout=60 +feedback_connect_timeout=60 + [MySQL Monitor] type=monitor module=mysqlmon From 2ef1e9ab04cd89cd7e080eff647c9485b99e5924 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 16:16:53 +0100 Subject: [PATCH 15/20] Notification Service Document Notification Service Document --- .../Tutorials/Notification-Service.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/Tutorials/Notification-Service.md diff --git a/Documentation/Tutorials/Notification-Service.md b/Documentation/Tutorials/Notification-Service.md new file mode 100644 index 000000000..ec9b0a3da --- /dev/null +++ b/Documentation/Tutorials/Notification-Service.md @@ -0,0 +1,58 @@ +# MaxScale Notification Service and Feedback Support + +Massimiliano Pinto + +Last Updated: 10th March 2015 + +## Contents + +## Document History + + + + + + + + + + + + +
DateChangeWho
10th March 2015Initial versionMassimiliano Pinto
+ + +## Overview + +The purpose of Notification Service in MaxScale is for a customer registered for the service to receive update notices, security bulletins, fixes and workarounds that are tailored to the database server configuration. + +## MaxScale Setup + +MaxScale may collect the installed plugins and send the informations nightly, between 2:00 AM and 4:59 AM. + +It tries to send data and if there is any failure (timeout, server is down, etc), the next retry is in 1800 seconds (30 minutes) + +This feature is not enabled by default: MaxScale must be configured in [feedback] section: + + +[feedback] +feedback_enable=1 +feedback_url=xxxxxx +feedback_user_info=yyyyy + + +## Manual Operation + +If it’s not possible to send data due to firewall or security settings the report could be generated manually (feedback_user_info is required) via MaxAdmin + + +MaxScale>show feedback report + + +Report could be saved to report.txt file: + + +# maxadmin -uxxx -pyyy show feedbackreport > ./report.txt + +# curl -F data=@./report.txt https://mariadb.org/feedback_plugin/post + From 990df106551075a226b80ee7130db5f17e20b1d1 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 16:18:25 +0100 Subject: [PATCH 16/20] Notification Service Document update Notification Service Document update --- Documentation/Tutorials/Notification-Service.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/Tutorials/Notification-Service.md b/Documentation/Tutorials/Notification-Service.md index ec9b0a3da..fa3159170 100644 --- a/Documentation/Tutorials/Notification-Service.md +++ b/Documentation/Tutorials/Notification-Service.md @@ -35,10 +35,10 @@ It tries to send data and if there is any failure (timeout, server is down, etc) This feature is not enabled by default: MaxScale must be configured in [feedback] section: -[feedback] -feedback_enable=1 -feedback_url=xxxxxx -feedback_user_info=yyyyy + [feedback] + feedback_enable=1 + feedback_url=xxxxxx + feedback_user_info=yyyyy ## Manual Operation @@ -52,7 +52,7 @@ MaxScale>show feedback report Report could be saved to report.txt file: -# maxadmin -uxxx -pyyy show feedbackreport > ./report.txt +maxadmin -uxxx -pyyy show feedbackreport > ./report.txt -# curl -F data=@./report.txt https://mariadb.org/feedback_plugin/post +curl -F data=@./report.txt https://mariadb.org/feedback_plugin/post From 784eb663beb9d16a9bfea8072bc036240febee4d Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 16:35:39 +0100 Subject: [PATCH 17/20] Notification Service Documentation Notification Service Documentation --- .../Tutorials/Notification-Service.md | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Documentation/Tutorials/Notification-Service.md b/Documentation/Tutorials/Notification-Service.md index fa3159170..4cfc89cd2 100644 --- a/Documentation/Tutorials/Notification-Service.md +++ b/Documentation/Tutorials/Notification-Service.md @@ -37,8 +37,26 @@ This feature is not enabled by default: MaxScale must be configured in [feedback [feedback] feedback_enable=1 - feedback_url=xxxxxx - feedback_user_info=yyyyy + feedback_url=https://mariadb.org/feedback_plugin/post + feedback_user_info=x-y-z-w + +The activation code that will be provided by MariaDB corp upon request by the customer and it shlud be put in feedback_user_info. + +Example: +feedback_user_info=0467009f-b04d-45b1-a77b-b6b2ec9c6cf4 + + +MaxScale generates the feedback report containing following information: + + -The activation code used to enable feedback + - MaxScale Version + - An identifier of the MaxScale installation, i.e. the HEX encoding of SHA1 digest of the first network interface MAC address + - Operating System (i.e Linux) + - Operating Suystem Distribution (i.e. CentOS release 6.5 (Final)) + - All the modules in use in MaxScale and their API and version + - MaxScale server UNIX_TIME at generation time + +MaxScale shall send the generated feedback report to a feedback server specified in feedback_url ## Manual Operation @@ -56,3 +74,21 @@ maxadmin -uxxx -pyyy show feedbackreport > ./report.txt curl -F data=@./report.txt https://mariadb.org/feedback_plugin/post + +Report Example: + + FEEDBACK_SERVER_UID 6B5C44AEA73137D049B02E6D1C7629EF431A350F + FEEDBACK_USER_INFO 0467009f-b04d-45b1-a77b-b6b2ec9c6cf4 + VERSION 1.0.6-unstable + NOW 1425914890 + PRODUCT maxscale + Uname_sysname Linux + Uname_distribution CentOS release 6.5 (Final) + module_maxscaled_type Protocol + module_maxscaled_version V1.0.0 + module_maxscaled_api 1.0.0 + module_maxscaled_releasestatus GA + module_telnetd_type Protocol + module_telnetd_version V1.0.1 + module_telnetd_api 1.0.0 + module_telnetd_releasestatus GA From 581f65918a5d54a1806bbaccda2044e9466543af Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 17:17:47 +0100 Subject: [PATCH 18/20] Getting the Server Reply code and content Getting the Server Reply code and content --- server/core/load_utils.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/server/core/load_utils.c b/server/core/load_utils.c index b8c9c5fb5..d8a735ecd 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -676,7 +676,9 @@ module_feedback_send(void* data) { curl_easy_setopt(curl, CURLOPT_HEADER, 1); /* some servers don't like requests that are made without a user-agent field, so we provide one */ - curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "MaxScale-agent/http-1.0"); + /* Force HTTP/1.0 */ + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt(curl, CURLOPT_URL, feedback_config->feedback_url); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); @@ -718,8 +720,13 @@ module_feedback_send(void* data) { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); } - if (http_code == 200) { - last_action = _NOTIFICATION_SEND_OK; + if (http_code == 302) { + char *from = strstr(chunk.data, "

ok

"); + if (from) { + last_action = _NOTIFICATION_SEND_OK; + } else { + last_action = _NOTIFICATION_SEND_ERROR; + } } else { last_action = _NOTIFICATION_SEND_ERROR; LOGIF(LE, (skygw_log_write_flush( From 773ad1f8a2766b045e542482f7cb4f20613dbbef Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 9 Mar 2015 18:29:03 +0200 Subject: [PATCH 19/20] Removed useless test parts. --- server/core/test/testfeedback.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/core/test/testfeedback.c b/server/core/test/testfeedback.c index cb363e39e..9e84b5cb2 100644 --- a/server/core/test/testfeedback.c +++ b/server/core/test/testfeedback.c @@ -72,7 +72,6 @@ int main(int argc, char** argv) regcomp(&re,fc->feedback_user_info,0); - config_enable_feedback_task(); module_create_feedback_report(&buf,NULL,fc); printf("%s",(char*)buf->start); @@ -81,6 +80,5 @@ int main(int argc, char** argv) FAILTEST("Regex match of 'user_info' failed."); } - config_disable_feedback_task(); return 0; } From 59245b50dd9520599898f00b3a5217184d55450b Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Mon, 9 Mar 2015 17:49:15 +0100 Subject: [PATCH 20/20] Fixed compiler warning Fixed compiler warning --- server/core/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index 181391dd6..c4f2bfbb4 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1291,7 +1291,7 @@ handle_feedback_item(const char *name, const char *value) int i; if (strcmp(name, "feedback_enable") == 0) { - feedback.feedback_enable = config_truth_value(value); + feedback.feedback_enable = config_truth_value((char *)value); } else if (strcmp(name, "feedback_user_info") == 0) {