Merge from develop
Merge from develop
This commit is contained in:
@ -7,7 +7,7 @@ if(BUILD_RABBITMQ)
|
||||
endif()
|
||||
|
||||
add_library(regexfilter SHARED regexfilter.c)
|
||||
target_link_libraries(regexfilter log_manager)
|
||||
target_link_libraries(regexfilter log_manager ${PCRE2_LIBRARIES})
|
||||
install(TARGETS regexfilter DESTINATION ${MAXSCALE_LIBDIR})
|
||||
|
||||
add_library(testfilter SHARED testfilter.c)
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2014
|
||||
*/
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#include <stdio.h>
|
||||
#include <filter.h>
|
||||
#include <modinfo.h>
|
||||
@ -22,7 +23,7 @@
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <string.h>
|
||||
#include <regex.h>
|
||||
#include <pcre2.h>
|
||||
#include <atomic.h>
|
||||
#include "maxconfig.h"
|
||||
|
||||
@ -65,7 +66,7 @@ static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstre
|
||||
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
|
||||
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
|
||||
|
||||
static char *regex_replace(char *sql, regex_t *re, char *replace);
|
||||
static char *regex_replace(const char *sql, pcre2_code *re, pcre2_match_data *study, const char *replace);
|
||||
|
||||
static FILTER_OBJECT MyObject = {
|
||||
createInstance,
|
||||
@ -82,25 +83,28 @@ static FILTER_OBJECT MyObject = {
|
||||
/**
|
||||
* Instance structure
|
||||
*/
|
||||
typedef struct {
|
||||
char *source; /* Source address to restrict matches */
|
||||
char *user; /* User name to restrict matches */
|
||||
char *match; /* Regular expression to match */
|
||||
char *replace; /* Replacement text */
|
||||
regex_t re; /* Compiled regex text */
|
||||
FILE* logfile;
|
||||
bool log_trace;
|
||||
typedef struct
|
||||
{
|
||||
char *source; /*< Source address to restrict matches */
|
||||
char *user; /*< User name to restrict matches */
|
||||
char *match; /*< Regular expression to match */
|
||||
char *replace; /*< Replacement text */
|
||||
pcre2_code *re; /*< Compiled regex text */
|
||||
pcre2_match_data *match_data; /*< Matching data used by the compiled regex */
|
||||
FILE* logfile; /*< Log file */
|
||||
bool log_trace; /*< Whether messages should be printed to tracelog */
|
||||
} REGEX_INSTANCE;
|
||||
|
||||
/**
|
||||
* The session structure for this regex filter
|
||||
*/
|
||||
typedef struct {
|
||||
DOWNSTREAM down; /* The downstream filter */
|
||||
SPINLOCK lock;
|
||||
int no_change; /* No. of unchanged requests */
|
||||
int replacements; /* No. of changed requests */
|
||||
int active; /* Is filter active */
|
||||
typedef struct
|
||||
{
|
||||
DOWNSTREAM down; /* The downstream filter */
|
||||
SPINLOCK lock;
|
||||
int no_change; /* No. of unchanged requests */
|
||||
int replacements; /* No. of changed requests */
|
||||
int active; /* Is filter active */
|
||||
} REGEX_SESSION;
|
||||
|
||||
void log_match(REGEX_INSTANCE* inst,char* re, char* old, char* new);
|
||||
@ -140,6 +144,32 @@ GetModuleObject()
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a regexfilter instance.
|
||||
* @param instance instance to free
|
||||
*/
|
||||
void free_instance(REGEX_INSTANCE *instance)
|
||||
{
|
||||
if (instance)
|
||||
{
|
||||
if (instance->re)
|
||||
{
|
||||
pcre2_code_free(instance->re);
|
||||
}
|
||||
|
||||
if (instance->match_data)
|
||||
{
|
||||
pcre2_match_data_free(instance->match_data);
|
||||
}
|
||||
|
||||
free(instance->match);
|
||||
free(instance->replace);
|
||||
free(instance->source);
|
||||
free(instance->user);
|
||||
free(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the filter for a particular service
|
||||
* within MaxScale.
|
||||
@ -152,9 +182,12 @@ GetModuleObject()
|
||||
static FILTER *
|
||||
createInstance(char **options, FILTER_PARAMETER **params)
|
||||
{
|
||||
REGEX_INSTANCE *my_instance;
|
||||
int i, cflags = REG_ICASE;
|
||||
char *logfile = NULL;
|
||||
REGEX_INSTANCE *my_instance;
|
||||
int i, errnumber, cflags = PCRE2_CASELESS;
|
||||
PCRE2_SIZE erroffset;
|
||||
char *logfile = NULL;
|
||||
const char *errmsg;
|
||||
|
||||
if ((my_instance = calloc(1, sizeof(REGEX_INSTANCE))) != NULL)
|
||||
{
|
||||
my_instance->match = NULL;
|
||||
@ -193,11 +226,11 @@ char *logfile = NULL;
|
||||
{
|
||||
if (!strcasecmp(options[i], "ignorecase"))
|
||||
{
|
||||
cflags |= REG_ICASE;
|
||||
cflags |= PCRE2_CASELESS;
|
||||
}
|
||||
else if (!strcasecmp(options[i], "case"))
|
||||
{
|
||||
cflags &= ~REG_ICASE;
|
||||
cflags &= ~PCRE2_CASELESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -207,45 +240,56 @@ char *logfile = NULL;
|
||||
options[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logfile != NULL)
|
||||
{
|
||||
if ((my_instance->logfile = fopen(logfile, "a")) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"regexfilter: Failed to open file '%s'.\n",
|
||||
logfile)));
|
||||
free_instance(my_instance);
|
||||
free(logfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(my_instance->logfile, "\nOpened regex filter log\n");
|
||||
fflush(my_instance->logfile);
|
||||
}
|
||||
free(logfile);
|
||||
|
||||
if (my_instance->match == NULL || my_instance->replace == NULL)
|
||||
{
|
||||
free(my_instance);
|
||||
free(logfile);
|
||||
free_instance(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (regcomp(&my_instance->re, my_instance->match, REG_ICASE))
|
||||
if ((my_instance->re = pcre2_compile((PCRE2_SPTR)my_instance->match,
|
||||
PCRE2_ZERO_TERMINATED,
|
||||
cflags,
|
||||
&errnumber,
|
||||
&erroffset,
|
||||
NULL)) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"regexfilter: Invalid regular expression '%s'.\n",
|
||||
my_instance->match)));
|
||||
free(my_instance->match);
|
||||
free(my_instance->replace);
|
||||
free(my_instance);
|
||||
free(logfile);
|
||||
return NULL;
|
||||
}
|
||||
char errbuffer[1024];
|
||||
pcre2_get_error_message(errnumber, (PCRE2_UCHAR*) & errbuffer, sizeof(errbuffer));
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Error: regexfilter: Compiling regular expression '%s' failed at %d: %s\n",
|
||||
my_instance->match, erroffset, errbuffer)));
|
||||
free_instance(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(logfile != NULL)
|
||||
{
|
||||
if((my_instance->logfile = fopen(logfile,"a")) == NULL)
|
||||
{
|
||||
if((my_instance->match_data = pcre2_match_data_create_from_pattern(
|
||||
my_instance->re, NULL)) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"regexfilter: Failed to open file '%s'.\n",
|
||||
logfile)));
|
||||
free(my_instance->match);
|
||||
free(my_instance->replace);
|
||||
free(my_instance);
|
||||
free(logfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(my_instance->logfile,"\nOpened regex filter log\n");
|
||||
fflush(my_instance->logfile);
|
||||
}
|
||||
free(logfile);
|
||||
"Error: regexfilter: Failure to create PCRE2 matching data. "
|
||||
"This is most likely caused by a lack of available memory.")));
|
||||
free_instance(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return (FILTER *)my_instance;
|
||||
}
|
||||
@ -351,8 +395,10 @@ char *sql, *newsql;
|
||||
}
|
||||
if ((sql = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
newsql = regex_replace(sql, &my_instance->re,
|
||||
my_instance->replace);
|
||||
newsql = regex_replace(sql,
|
||||
my_instance->re,
|
||||
my_instance->match_data,
|
||||
my_instance->replace);
|
||||
if (newsql)
|
||||
{
|
||||
queue = modutil_replace_SQL(queue, newsql);
|
||||
@ -415,76 +461,42 @@ REGEX_SESSION *my_session = (REGEX_SESSION *)fsession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a regular expression match and subsititution on the SQL
|
||||
* Perform a regular expression match and substitution on the SQL
|
||||
*
|
||||
* @param sql The original SQL text
|
||||
* @param re The compiled regular expression
|
||||
* @param match_data The PCRE2 matching data buffer
|
||||
* @param replace The replacement text
|
||||
* @return The replaced text or NULL if no replacement was done.
|
||||
*/
|
||||
static char *
|
||||
regex_replace(char *sql, regex_t *re, char *replace)
|
||||
regex_replace(const char *sql, pcre2_code *re, pcre2_match_data *match_data, const char *replace)
|
||||
{
|
||||
char *orig, *result, *ptr;
|
||||
int i, res_size, res_length, rep_length;
|
||||
int last_match, length;
|
||||
regmatch_t match[10];
|
||||
char *result = NULL;
|
||||
size_t result_size;
|
||||
|
||||
if (regexec(re, sql, 10, match, 0))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
length = strlen(sql);
|
||||
|
||||
res_size = 2 * length;
|
||||
result = (char *)malloc(res_size);
|
||||
res_length = 0;
|
||||
rep_length = strlen(replace);
|
||||
last_match = 0;
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
if (match[i].rm_so != -1)
|
||||
{
|
||||
ptr = &result[res_length];
|
||||
if (last_match < match[i].rm_so)
|
||||
{
|
||||
int to_copy = match[i].rm_so - last_match;
|
||||
if (last_match + to_copy > res_size)
|
||||
{
|
||||
res_size = last_match + to_copy + length;
|
||||
result = (char *)realloc(result, res_size);
|
||||
}
|
||||
memcpy(ptr, &sql[last_match], to_copy);
|
||||
res_length += to_copy;
|
||||
}
|
||||
last_match = match[i].rm_eo;
|
||||
if (res_length + rep_length > res_size)
|
||||
{
|
||||
res_size += rep_length;
|
||||
result = (char *)realloc(result, res_size);
|
||||
}
|
||||
ptr = &result[res_length];
|
||||
memcpy(ptr, replace, rep_length);
|
||||
res_length += rep_length;
|
||||
}
|
||||
}
|
||||
/** This should never fail with rc == 0 because we used pcre2_match_data_create_from_pattern() */
|
||||
if (pcre2_match(re, (PCRE2_SPTR)sql, PCRE2_ZERO_TERMINATED, 0, 0, match_data, NULL))
|
||||
{
|
||||
result_size = strlen(sql) + strlen(replace);
|
||||
result = malloc(result_size);
|
||||
|
||||
if (last_match < length)
|
||||
{
|
||||
int to_copy = length - last_match;
|
||||
if (last_match + to_copy > res_size)
|
||||
{
|
||||
res_size = last_match + to_copy + 1;
|
||||
result = (char *)realloc(result, res_size);
|
||||
}
|
||||
ptr = &result[res_length];
|
||||
memcpy(ptr, &sql[last_match], to_copy);
|
||||
res_length += to_copy;
|
||||
}
|
||||
result[res_length] = 0;
|
||||
|
||||
return result;
|
||||
while (result &&
|
||||
pcre2_substitute(re, (PCRE2_SPTR)sql, PCRE2_ZERO_TERMINATED, 0,
|
||||
PCRE2_SUBSTITUTE_GLOBAL, match_data, NULL,
|
||||
(PCRE2_SPTR)replace, PCRE2_ZERO_TERMINATED,
|
||||
(PCRE2_UCHAR*)result, (PCRE2_SIZE*)&result_size) == PCRE2_ERROR_NOMEMORY)
|
||||
{
|
||||
char *tmp;
|
||||
if ((tmp = realloc(result, (result_size *= 1.5))) == NULL)
|
||||
{
|
||||
free(result);
|
||||
result = NULL;
|
||||
}
|
||||
result = tmp;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -525,4 +537,4 @@ void log_nomatch(REGEX_INSTANCE* inst, char* re, char* old)
|
||||
{
|
||||
LOGIF(LT,(skygw_log_write(LT,"No match %s: [%s]",re,old)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,19 +285,9 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result,*result2;
|
||||
int isjoined = 0;
|
||||
char *uname = mon->user;
|
||||
char *passwd = mon->password;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
if (database->server->monuser != NULL)
|
||||
{
|
||||
uname = database->server->monuser;
|
||||
passwd = database->server->monpw;
|
||||
}
|
||||
if (uname == NULL)
|
||||
return;
|
||||
|
||||
/* Don't even probe server flagged as in maintenance */
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
@ -305,60 +295,35 @@ char *server_string;
|
||||
/** Store previous status */
|
||||
database->mon_prev_status = database->server->status;
|
||||
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int connect_timeout = mon->connect_timeout;
|
||||
int read_timeout = mon->read_timeout;
|
||||
int write_timeout = mon->write_timeout;
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
database->con = mysql_init(NULL);
|
||||
/* Also clear Joined, M/S and Stickiness bits */
|
||||
server_clear_status(database->server, SERVER_JOINED);
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
server_clear_status(database->server, SERVER_MASTER_STICKINESS);
|
||||
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
connect_result_t rval = mon_connect_to_db(mon, database);
|
||||
if (rval != MONITOR_CONN_OK)
|
||||
{
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
}
|
||||
|
||||
if (mysql_real_connect(database->con, database->server->name,
|
||||
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||
{
|
||||
free(dpwd);
|
||||
database->server->node_id = -1;
|
||||
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
if (mon_status_changed(database) && mon_print_fail_status(database))
|
||||
{
|
||||
mon_log_connect_error(database, rval);
|
||||
}
|
||||
|
||||
/* Also clear Joined, M/S and Stickiness bits */
|
||||
server_clear_status(database->server, SERVER_JOINED);
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
server_clear_status(database->server, SERVER_MASTER_STICKINESS);
|
||||
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
}
|
||||
|
||||
database->server->node_id = -1;
|
||||
|
||||
if (mon_status_changed(database) && mon_print_fail_status(database))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor was unable to connect to "
|
||||
"server %s:%d : \"%s\"",
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con))));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
}
|
||||
free(dpwd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we get this far then we have a working connection */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
|
||||
@ -260,95 +260,54 @@ char *sep;
|
||||
static void
|
||||
monitorDatabase(MONITOR* mon, MONITOR_SERVERS *database)
|
||||
{
|
||||
MM_MONITOR *handle = mon->handle;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int isslave = 0;
|
||||
int ismaster = 0;
|
||||
char *uname = mon->user;
|
||||
char *passwd = mon->password;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
if (database->server->monuser != NULL)
|
||||
{
|
||||
uname = database->server->monuser;
|
||||
passwd = database->server->monpw;
|
||||
}
|
||||
|
||||
if (uname == NULL)
|
||||
return;
|
||||
|
||||
/* Don't probe servers in maintenance mode */
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
/* Don't probe servers in maintenance mode */
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
|
||||
/** Store previous status */
|
||||
database->mon_prev_status = database->server->status;
|
||||
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int read_timeout = 1;
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
database->con = mysql_init(NULL);
|
||||
/** Store previous status */
|
||||
database->mon_prev_status = database->server->status;
|
||||
connect_result_t rval = mon_connect_to_db(mon, database);
|
||||
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con,
|
||||
database->server->name,
|
||||
uname,
|
||||
dpwd,
|
||||
NULL,
|
||||
database->server->port,
|
||||
NULL,
|
||||
0) == NULL)
|
||||
{
|
||||
free(dpwd);
|
||||
|
||||
if (mon_print_fail_status(database))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor was unable to connect to "
|
||||
"server %s:%d : \"%s\"",
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con))));
|
||||
}
|
||||
if (rval != MONITOR_CONN_OK)
|
||||
{
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_set_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
monitor_clear_pending_status(database, SERVER_RUNNING);
|
||||
|
||||
/* The current server is not running
|
||||
*
|
||||
* Store server NOT running in server and monitor server pending struct
|
||||
*
|
||||
*/
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_set_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
monitor_clear_pending_status(database, SERVER_RUNNING);
|
||||
/* Also clear M/S state in both server and monitor server pending struct */
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
|
||||
/* Also clear M/S state in both server and monitor server pending struct */
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
/* Clean addition status too */
|
||||
server_clear_status(database->server, SERVER_STALE_STATUS);
|
||||
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
|
||||
|
||||
/* Clean addition status too */
|
||||
server_clear_status(database->server, SERVER_STALE_STATUS);
|
||||
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
|
||||
if (mon_status_changed(database) && mon_print_fail_status(database))
|
||||
{
|
||||
mon_log_connect_error(database, rval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_clear_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_clear_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
free(dpwd);
|
||||
}
|
||||
/* Store current status in both server and monitor server pending struct */
|
||||
/* Store current status in both server and monitor server pending struct */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
monitor_set_pending_status(database, SERVER_RUNNING);
|
||||
|
||||
|
||||
@ -413,3 +413,92 @@ monitor_event_t mon_name_to_event(char* tok)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a database. This will always leave a valid database handle in the
|
||||
* database->con pointer. This allows the user to call MySQL C API functions to
|
||||
* find out the reason of the failure.
|
||||
* @param mon Monitor
|
||||
* @param database Monitored database
|
||||
* @return MONITOR_CONN_OK if the connection is OK else the reason for the failure
|
||||
*/
|
||||
connect_result_t mon_connect_to_db(MONITOR* mon, MONITOR_SERVERS *database)
|
||||
{
|
||||
connect_result_t rval = MONITOR_CONN_OK;
|
||||
|
||||
/** Return if the connection is OK */
|
||||
if (database->con && mysql_ping(database->con) == 0)
|
||||
{
|
||||
return rval;
|
||||
}
|
||||
|
||||
int connect_timeout = mon->connect_timeout;
|
||||
int read_timeout = mon->read_timeout;
|
||||
int write_timeout = mon->write_timeout;
|
||||
char *uname = database->server->monuser ? database->server->monuser : mon->user;
|
||||
char *passwd = database->server->monpw ? database->server->monpw : mon->password;
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
|
||||
if (database->con)
|
||||
{
|
||||
mysql_close(database->con);
|
||||
}
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *) &connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *) &read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *) &write_timeout);
|
||||
|
||||
time_t start = time(NULL);
|
||||
bool result = (mysql_real_connect(database->con,
|
||||
database->server->name,
|
||||
uname,
|
||||
dpwd,
|
||||
NULL,
|
||||
database->server->port,
|
||||
NULL,
|
||||
0) != NULL);
|
||||
time_t end = time(NULL);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
if ((int) difftime(end, start) >= connect_timeout)
|
||||
{
|
||||
rval = MONITOR_CONN_TIMEOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
rval = MONITOR_CONN_REFUSED;
|
||||
}
|
||||
}
|
||||
|
||||
free(dpwd);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error about the failure to connect to a backend server
|
||||
* and why it happened.
|
||||
* @param database Backend database
|
||||
* @param rval Return value of mon_connect_to_db
|
||||
*/
|
||||
void mon_log_connect_error(MONITOR_SERVERS* database, connect_result_t rval)
|
||||
{
|
||||
if (rval == MONITOR_CONN_TIMEOUT)
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Error : Monitor timed out when connecting to "
|
||||
"server %s:%d : \"%s\"",
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con));
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Error : Monitor was unable to connect to "
|
||||
"server %s:%d : \"%s\"",
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con));
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include <monitor.h>
|
||||
#include <log_manager.h>
|
||||
#include <externcmd.h>
|
||||
#include <secrets.h>
|
||||
/**
|
||||
* @file monitor_common.h - The generic monitor structures all monitors use
|
||||
*
|
||||
@ -63,6 +64,14 @@ typedef enum {
|
||||
NEW_NDB_EVENT,
|
||||
MAX_MONITOR_EVENT
|
||||
}monitor_event_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MONITOR_CONN_OK,
|
||||
MONITOR_CONN_REFUSED,
|
||||
MONITOR_CONN_TIMEOUT
|
||||
} connect_result_t;
|
||||
|
||||
void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len);
|
||||
monitor_event_t mon_get_event_type(MONITOR_SERVERS* node);
|
||||
char* mon_get_event_name(MONITOR_SERVERS* node);
|
||||
@ -72,4 +81,6 @@ bool mon_status_changed(MONITOR_SERVERS* mon_srv);
|
||||
bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
|
||||
void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script);
|
||||
int mon_parse_event_string(bool* events, size_t count,char* string);
|
||||
connect_result_t mon_connect_to_db(MONITOR* mon, MONITOR_SERVERS *database);
|
||||
void mon_log_connect_error(MONITOR_SERVERS* database, connect_result_t rval);
|
||||
#endif
|
||||
|
||||
@ -287,40 +287,6 @@ static void diagnostics(DCB *dcb, void *arg)
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
/**
|
||||
* Connect to a database
|
||||
* @param mon Monitor
|
||||
* @param database Monitored database
|
||||
* @return true if connection was successful, false if there was an error
|
||||
*/
|
||||
static inline bool connect_to_db(MONITOR* mon,MONITOR_SERVERS *database)
|
||||
{
|
||||
char *uname = mon->user;
|
||||
char *passwd = mon->password;
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int connect_timeout = mon->connect_timeout;
|
||||
int read_timeout = mon->read_timeout;
|
||||
int write_timeout = mon->write_timeout;
|
||||
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
|
||||
bool result = (mysql_real_connect(database->con,
|
||||
database->server->name,
|
||||
uname,
|
||||
dpwd,
|
||||
NULL,
|
||||
database->server->port,
|
||||
NULL,
|
||||
0) != NULL);
|
||||
free(dpwd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void monitor_mysql100_db(MONITOR_SERVERS* database)
|
||||
{
|
||||
@ -641,7 +607,8 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
|
||||
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
if(connect_to_db(mon,database))
|
||||
connect_result_t rval;
|
||||
if ((rval = mon_connect_to_db(mon, database)) == MONITOR_CONN_OK)
|
||||
{
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_clear_pending_status(database, SERVER_AUTH_ERROR);
|
||||
@ -675,15 +642,9 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
|
||||
|
||||
/* Log connect failure only once */
|
||||
if (mon_status_changed(database) && mon_print_fail_status(database))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor was unable to connect to "
|
||||
"server %s:%d : \"%s\"",
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con))));
|
||||
}
|
||||
{
|
||||
mon_log_connect_error(database, rval);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -248,66 +248,35 @@ char *sep;
|
||||
static void
|
||||
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd, MONITOR *mon)
|
||||
{
|
||||
MYSQL_MONITOR* handle = mon->handle;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int isjoined = 0;
|
||||
char *uname = defaultUser, *passwd = defaultPasswd;
|
||||
char *server_string;
|
||||
|
||||
if (database->server->monuser != NULL)
|
||||
{
|
||||
uname = database->server->monuser;
|
||||
passwd = database->server->monpw;
|
||||
}
|
||||
if (uname == NULL)
|
||||
return;
|
||||
/* Don't even probe server flagged as in maintenance */
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
|
||||
/* Don't even probe server flagged as in maintenance */
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
connect_result_t rval = mon_connect_to_db(mon, database);
|
||||
if (rval != MONITOR_CONN_OK)
|
||||
{
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int connect_timeout = mon->connect_timeout;
|
||||
int read_timeout = mon->read_timeout;
|
||||
int write_timeout = mon->write_timeout;
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
}
|
||||
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
database->con = mysql_init(NULL);
|
||||
database->server->node_id = -1;
|
||||
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con, database->server->name,
|
||||
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor was unable to connect to "
|
||||
"server %s:%d : \"%s\"",
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con))));
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
}
|
||||
database->server->node_id = -1;
|
||||
free(dpwd);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
}
|
||||
free(dpwd);
|
||||
}
|
||||
if (mon_status_changed(database) && mon_print_fail_status(database))
|
||||
{
|
||||
mon_log_connect_error(database, rval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
/* If we get this far then we have a working connection */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
extern __thread log_info_t tls_log_info;
|
||||
|
||||
static char *version_str = "V1.2.2";
|
||||
static char *version_str = "V2.0.0";
|
||||
|
||||
/* The router entry points */
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
@ -115,6 +115,8 @@ extern int blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debu
|
||||
void blr_master_close(ROUTER_INSTANCE *);
|
||||
char * blr_last_event_description(ROUTER_INSTANCE *router);
|
||||
extern int MaxScaleUptime();
|
||||
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
|
||||
char *blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event);
|
||||
|
||||
/** The module object definition */
|
||||
static ROUTER_OBJECT MyObject = {
|
||||
@ -2059,7 +2061,38 @@ char *event_desc = NULL;
|
||||
event_desc = event_names_mariadb10[(router->lastEventReceived - MARIADB_NEW_EVENTS_BEGIN)];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return event_desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event description
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param event The current event
|
||||
* @return The event description or NULL
|
||||
*/
|
||||
char *
|
||||
blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event) {
|
||||
char *event_desc = NULL;
|
||||
|
||||
if (!router->mariadb10_compat) {
|
||||
if (event >= 0 &&
|
||||
event <= MAX_EVENT_TYPE) {
|
||||
event_desc = event_names[event];
|
||||
}
|
||||
} else {
|
||||
if (event >= 0 &&
|
||||
event <= MAX_EVENT_TYPE) {
|
||||
event_desc = event_names[event];
|
||||
} else {
|
||||
/* Check MariaDB 10 new events */
|
||||
if (event >= MARIADB_NEW_EVENTS_BEGIN &&
|
||||
event <= MAX_EVENT_TYPE_MARIADB10) {
|
||||
event_desc = event_names_mariadb10[(event - MARIADB_NEW_EVENTS_BEGIN)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return event_desc;
|
||||
|
||||
@ -78,6 +78,15 @@ int blr_file_write_master_config(ROUTER_INSTANCE *router, char *error);
|
||||
extern uint32_t extract_field(uint8_t *src, int bits);
|
||||
static void blr_format_event_size(double *event_size, char *label);
|
||||
extern int MaxScaleUptime();
|
||||
extern char *blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event);
|
||||
|
||||
typedef struct binlog_event_desc {
|
||||
unsigned long long event_pos;
|
||||
uint8_t event_type;
|
||||
time_t event_time;
|
||||
} BINLOG_EVENT_DESC;
|
||||
|
||||
static void blr_print_binlog_details(ROUTER_INSTANCE *router, BINLOG_EVENT_DESC first_event_time, BINLOG_EVENT_DESC last_event_time);
|
||||
|
||||
/**
|
||||
* Initialise the binlog file for this instance. MaxScale will look
|
||||
@ -831,6 +840,14 @@ unsigned long event_bytes = 0;
|
||||
unsigned long max_bytes = 0;
|
||||
double average_events = 0;
|
||||
double average_bytes = 0;
|
||||
BINLOG_EVENT_DESC first_event;
|
||||
BINLOG_EVENT_DESC last_event;
|
||||
BINLOG_EVENT_DESC fde_event;
|
||||
int fde_seen = 0;
|
||||
|
||||
memset(&first_event, '\0', sizeof(first_event));
|
||||
memset(&last_event, '\0', sizeof(last_event));
|
||||
memset(&fde_event, '\0', sizeof(fde_event));
|
||||
|
||||
if (router->binlog_fd == -1) {
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
@ -862,6 +879,13 @@ double average_bytes = 0;
|
||||
if (n_transactions)
|
||||
average_bytes = (double)((double)total_bytes / (double)n_transactions) * (1.0);
|
||||
|
||||
/* Report Binlog First and Last event */
|
||||
if (first_event.event_type == 0)
|
||||
blr_print_binlog_details(router, fde_event, last_event);
|
||||
else
|
||||
blr_print_binlog_details(router, first_event, last_event);
|
||||
|
||||
/* Report Transaction Summary */
|
||||
if (n_transactions != 0) {
|
||||
char total_label[2]="";
|
||||
char average_label[2]="";
|
||||
@ -1147,6 +1171,14 @@ double average_bytes = 0;
|
||||
last_known_commit = pos;
|
||||
}
|
||||
|
||||
/* get firts event timestamp, after FDE */
|
||||
if (fde_seen) {
|
||||
first_event.event_time = (unsigned long)hdr.timestamp;
|
||||
first_event.event_type = hdr.event_type;
|
||||
first_event.event_pos = pos;
|
||||
fde_seen = 0;
|
||||
}
|
||||
|
||||
/* get event content */
|
||||
ptr = data+BINLOG_EVENT_HDR_LEN;
|
||||
|
||||
@ -1157,11 +1189,25 @@ double average_bytes = 0;
|
||||
int n_events;
|
||||
int check_alg;
|
||||
uint8_t *checksum;
|
||||
char buf_t[40];
|
||||
struct tm tm_t;
|
||||
|
||||
fde_seen = 1;
|
||||
fde_event.event_time = (unsigned long)hdr.timestamp;
|
||||
fde_event.event_type = hdr.event_type;
|
||||
fde_event.event_pos = pos;
|
||||
|
||||
localtime_r(&fde_event.event_time, &tm_t);
|
||||
asctime_r(&tm_t, buf_t);
|
||||
|
||||
if (buf_t[strlen(buf_t)-1] == '\n') {
|
||||
buf_t[strlen(buf_t)-1] = '\0';
|
||||
}
|
||||
|
||||
if(debug)
|
||||
LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG,
|
||||
"- Format Description event FDE @ %llu, size %lu",
|
||||
pos, (unsigned long)hdr.event_size)));
|
||||
"- Format Description event FDE @ %llu, size %lu, time %lu (%s)",
|
||||
pos, (unsigned long)hdr.event_size, fde_event.event_time, buf_t)));
|
||||
|
||||
event_header_length = ptr[2 + 50 + 4];
|
||||
event_header_ntypes = hdr.event_size - event_header_length - (2 + 50 + 4 + 1);
|
||||
@ -1208,6 +1254,12 @@ double average_bytes = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set last event time, pos and type */
|
||||
last_event.event_time = (unsigned long)hdr.timestamp;
|
||||
last_event.event_type = hdr.event_type;
|
||||
last_event.event_pos = pos;
|
||||
|
||||
/* Decode ROTATE EVENT */
|
||||
if(hdr.event_type == ROTATE_EVENT) {
|
||||
int len, slen;
|
||||
@ -1627,3 +1679,48 @@ char err_msg[STRERROR_BUFLEN];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Print Binlog Details
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param first_event First Event details
|
||||
* @param last_event First Event details
|
||||
*/
|
||||
|
||||
static void
|
||||
blr_print_binlog_details(ROUTER_INSTANCE *router, BINLOG_EVENT_DESC first_event, BINLOG_EVENT_DESC last_event)
|
||||
{
|
||||
char buf_t[40];
|
||||
struct tm tm_t;
|
||||
char *event_desc;
|
||||
|
||||
/* First Event */
|
||||
localtime_r(&first_event.event_time, &tm_t);
|
||||
asctime_r(&tm_t, buf_t);
|
||||
|
||||
if (buf_t[strlen(buf_t)-1] == '\n') {
|
||||
buf_t[strlen(buf_t)-1] = '\0';
|
||||
}
|
||||
|
||||
event_desc = blr_get_event_description(router, first_event.event_type);
|
||||
|
||||
LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
|
||||
"%lu @ %llu, %s, (%s), First EventTime",
|
||||
first_event.event_time, first_event.event_pos,
|
||||
event_desc != NULL ? event_desc : "unknown", buf_t)));
|
||||
|
||||
/* Last Event */
|
||||
localtime_r(&last_event.event_time, &tm_t);
|
||||
asctime_r(&tm_t, buf_t);
|
||||
|
||||
if (buf_t[strlen(buf_t)-1] == '\n') {
|
||||
buf_t[strlen(buf_t)-1] = '\0';
|
||||
}
|
||||
|
||||
event_desc = blr_get_event_description(router, last_event.event_type);
|
||||
|
||||
LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,
|
||||
"%lu @ %llu, %s, (%s), Last EventTime",
|
||||
last_event.event_time, last_event.event_pos,
|
||||
event_desc != NULL ? event_desc : "unknown", buf_t)));
|
||||
}
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ static struct option long_options[] = {
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
char *binlog_check_version = "1.0.0";
|
||||
char *binlog_check_version = "1.1.0";
|
||||
|
||||
int
|
||||
MaxScaleUptime()
|
||||
|
||||
Reference in New Issue
Block a user