Use MXS_MODULE_PARAM_REGEX in QLA-filter
The QLA filter now uses the automatically compiled regex parameter type. Regex matching now prints errors. Some minor refactoring.
This commit is contained in:
@ -36,21 +36,21 @@
|
|||||||
|
|
||||||
#define MXS_MODULE_NAME "qlafilter"
|
#define MXS_MODULE_NAME "qlafilter"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <maxscale/alloc.h>
|
||||||
|
#include <maxscale/atomic.h>
|
||||||
#include <maxscale/filter.h>
|
#include <maxscale/filter.h>
|
||||||
|
#include <maxscale/log_manager.h>
|
||||||
#include <maxscale/modinfo.h>
|
#include <maxscale/modinfo.h>
|
||||||
#include <maxscale/modutil.h>
|
#include <maxscale/modutil.h>
|
||||||
#include <maxscale/utils.h>
|
#include <maxscale/pcre2.h>
|
||||||
#include <maxscale/log_manager.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <regex.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <maxscale/atomic.h>
|
|
||||||
#include <maxscale/alloc.h>
|
|
||||||
#include <maxscale/service.h>
|
#include <maxscale/service.h>
|
||||||
|
#include <maxscale/utils.h>
|
||||||
|
|
||||||
/** Date string buffer size */
|
/** Date string buffer size */
|
||||||
#define QLA_DATE_BUFFER_SIZE 20
|
#define QLA_DATE_BUFFER_SIZE 20
|
||||||
@ -101,16 +101,21 @@ typedef struct
|
|||||||
char *source; /* The source of the client connection to filter on */
|
char *source; /* The source of the client connection to filter on */
|
||||||
char *user_name; /* The user name to filter on */
|
char *user_name; /* The user name to filter on */
|
||||||
char *match; /* Optional text to match against */
|
char *match; /* Optional text to match against */
|
||||||
regex_t re; /* Compiled regex text */
|
pcre2_code* re_match; /* Compiled regex text */
|
||||||
char *nomatch; /* Optional text to match against for exclusion */
|
char *nomatch; /* Optional text to match against for exclusion */
|
||||||
regex_t nore; /* Compiled regex nomatch text */
|
pcre2_code* re_nomatch; /* Compiled regex nomatch text */
|
||||||
|
uint32_t ovec_size; /* PCRE2 match data ovector size */
|
||||||
uint32_t log_mode_flags; /* Log file mode settings */
|
uint32_t log_mode_flags; /* Log file mode settings */
|
||||||
uint32_t log_file_data_flags; /* What data is saved to the files */
|
uint32_t log_file_data_flags; /* What data is saved to the files */
|
||||||
FILE *unified_fp; /* Unified log file. The pointer needs to be shared here
|
FILE *unified_fp; /* Unified log file. The pointer needs to be shared here
|
||||||
* to avoid garbled printing. */
|
* to avoid garbled printing. */
|
||||||
bool flush_writes; /* Flush log file after every write? */
|
bool flush_writes; /* Flush log file after every write? */
|
||||||
bool append; /* Open files in append-mode? */
|
bool append; /* Open files in append-mode? */
|
||||||
bool write_warning_given; /* To make sure some warning are only given once */
|
|
||||||
|
/* Avoid repeatedly printing some errors/warnings. */
|
||||||
|
bool write_warning_given;
|
||||||
|
bool match_error_printed;
|
||||||
|
bool nomatch_error_printed;
|
||||||
} QLA_INSTANCE;
|
} QLA_INSTANCE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,21 +132,24 @@ typedef struct
|
|||||||
MXS_DOWNSTREAM down;
|
MXS_DOWNSTREAM down;
|
||||||
char *filename; /* The session-specific log file name */
|
char *filename; /* The session-specific log file name */
|
||||||
FILE *fp; /* The session-specific log file */
|
FILE *fp; /* The session-specific log file */
|
||||||
const char *remote;
|
const char *remote; /* Client address */
|
||||||
char *service; /* The service name this filter is attached to. Not owned. */
|
char *service; /* The service name this filter is attached to. Not owned. */
|
||||||
size_t ses_id; /* The session this filter serves */
|
size_t ses_id; /* The session this filter serves */
|
||||||
const char *user; /* The client */
|
const char *user; /* The client */
|
||||||
|
pcre2_match_data* match_data; /* Regex match data */
|
||||||
} QLA_SESSION;
|
} QLA_SESSION;
|
||||||
|
|
||||||
static FILE* open_log_file(uint32_t, QLA_INSTANCE *, const char *);
|
static FILE* open_log_file(uint32_t, QLA_INSTANCE *, const char *);
|
||||||
static int write_log_entry(uint32_t, FILE*, QLA_INSTANCE*, QLA_SESSION*, const char*,
|
static int write_log_entry(uint32_t, FILE*, QLA_INSTANCE*, QLA_SESSION*, const char*,
|
||||||
const char*, size_t);
|
const char*, size_t);
|
||||||
|
static bool regex_check(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||||
|
const char* ptr, int length);
|
||||||
|
|
||||||
static const MXS_ENUM_VALUE option_values[] =
|
static const MXS_ENUM_VALUE option_values[] =
|
||||||
{
|
{
|
||||||
{"ignorecase", REG_ICASE},
|
{"ignorecase", PCRE2_CASELESS},
|
||||||
{"case", 0},
|
{"case", 0},
|
||||||
{"extended", REG_EXTENDED},
|
{"extended", PCRE2_EXTENDED},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -204,11 +212,11 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
"match",
|
"match",
|
||||||
MXS_MODULE_PARAM_STRING
|
MXS_MODULE_PARAM_REGEX
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"exclude",
|
"exclude",
|
||||||
MXS_MODULE_PARAM_STRING
|
MXS_MODULE_PARAM_REGEX
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"user",
|
"user",
|
||||||
@ -279,40 +287,54 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
|||||||
|
|
||||||
if (my_instance)
|
if (my_instance)
|
||||||
{
|
{
|
||||||
|
my_instance->name = MXS_STRDUP_A(name);
|
||||||
my_instance->sessions = 0;
|
my_instance->sessions = 0;
|
||||||
|
my_instance->ovec_size = 0;
|
||||||
my_instance->unified_fp = NULL;
|
my_instance->unified_fp = NULL;
|
||||||
my_instance->write_warning_given = false;
|
my_instance->write_warning_given = false;
|
||||||
my_instance->name = MXS_STRDUP_A(name);
|
my_instance->match_error_printed = false;
|
||||||
my_instance->filebase = MXS_STRDUP_A(config_get_string(params, "filebase"));
|
my_instance->nomatch_error_printed = false;
|
||||||
my_instance->flush_writes = config_get_bool(params, "flush");
|
|
||||||
my_instance->append = config_get_bool(params, "append");
|
|
||||||
my_instance->match = config_copy_string(params, "match");
|
|
||||||
my_instance->nomatch = config_copy_string(params, "exclude");
|
|
||||||
my_instance->source = config_copy_string(params, "source");
|
my_instance->source = config_copy_string(params, "source");
|
||||||
my_instance->user_name = config_copy_string(params, "user");
|
my_instance->user_name = config_copy_string(params, "user");
|
||||||
|
|
||||||
|
my_instance->filebase = MXS_STRDUP_A(config_get_string(params, "filebase"));
|
||||||
|
my_instance->append = config_get_bool(params, "append");
|
||||||
|
my_instance->flush_writes = config_get_bool(params, "flush");
|
||||||
my_instance->log_file_data_flags = config_get_enum(params, "log_data", log_data_values);
|
my_instance->log_file_data_flags = config_get_enum(params, "log_data", log_data_values);
|
||||||
my_instance->log_mode_flags = config_get_enum(params, "log_type", log_type_values);
|
my_instance->log_mode_flags = config_get_enum(params, "log_type", log_type_values);
|
||||||
|
|
||||||
|
my_instance->match = config_copy_string(params, "match");
|
||||||
|
my_instance->nomatch = config_copy_string(params, "exclude");
|
||||||
|
my_instance->re_nomatch = NULL;
|
||||||
|
my_instance->re_match = NULL;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
int cflags = config_get_enum(params, "options", option_values);
|
int cflags = config_get_enum(params, "options", option_values);
|
||||||
|
if (my_instance->match)
|
||||||
if (my_instance->match && regcomp(&my_instance->re, my_instance->match, cflags))
|
{
|
||||||
|
my_instance->re_match =
|
||||||
|
config_get_compiled_regex(params, "match", cflags, &my_instance->ovec_size);
|
||||||
|
if (!my_instance->re_match)
|
||||||
{
|
{
|
||||||
MXS_ERROR("Invalid regular expression '%s' for the 'match' "
|
|
||||||
"parameter.", my_instance->match);
|
|
||||||
MXS_FREE(my_instance->match);
|
|
||||||
my_instance->match = NULL;
|
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (my_instance->nomatch && regcomp(&my_instance->nore, my_instance->nomatch, cflags))
|
if (my_instance->nomatch)
|
||||||
|
{
|
||||||
|
uint32_t ovec_size_temp = 0;
|
||||||
|
my_instance->re_nomatch =
|
||||||
|
config_get_compiled_regex(params, "exclude", cflags, &ovec_size_temp);
|
||||||
|
if (ovec_size_temp > my_instance->ovec_size)
|
||||||
|
{
|
||||||
|
my_instance->ovec_size = ovec_size_temp;
|
||||||
|
}
|
||||||
|
if (!my_instance->re_nomatch)
|
||||||
{
|
{
|
||||||
MXS_ERROR("Invalid regular expression '%s' for the 'nomatch'"
|
|
||||||
" parameter.", my_instance->nomatch);
|
|
||||||
MXS_FREE(my_instance->nomatch);
|
|
||||||
my_instance->nomatch = NULL;
|
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to open the unified log file
|
// Try to open the unified log file
|
||||||
if (!error && (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED))
|
if (!error && (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED))
|
||||||
@ -346,17 +368,11 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
|||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
if (my_instance->match)
|
MXS_FREE(my_instance->name);
|
||||||
{
|
|
||||||
MXS_FREE(my_instance->match);
|
MXS_FREE(my_instance->match);
|
||||||
regfree(&my_instance->re);
|
pcre2_code_free(my_instance->re_match);
|
||||||
}
|
|
||||||
|
|
||||||
if (my_instance->nomatch)
|
|
||||||
{
|
|
||||||
MXS_FREE(my_instance->nomatch);
|
MXS_FREE(my_instance->nomatch);
|
||||||
regfree(&my_instance->nore);
|
pcre2_code_free(my_instance->re_nomatch);
|
||||||
}
|
|
||||||
if (my_instance->unified_fp != NULL)
|
if (my_instance->unified_fp != NULL)
|
||||||
{
|
{
|
||||||
fclose(my_instance->unified_fp);
|
fclose(my_instance->unified_fp);
|
||||||
@ -389,8 +405,19 @@ newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
|||||||
|
|
||||||
if ((my_session = MXS_CALLOC(1, sizeof(QLA_SESSION))) != NULL)
|
if ((my_session = MXS_CALLOC(1, sizeof(QLA_SESSION))) != NULL)
|
||||||
{
|
{
|
||||||
if ((my_session->filename = (char *)MXS_MALLOC(strlen(my_instance->filebase) + 20)) == NULL)
|
my_session->fp = NULL;
|
||||||
|
my_session->match_data = NULL;
|
||||||
|
my_session->filename = (char *)MXS_MALLOC(strlen(my_instance->filebase) + 20);
|
||||||
|
const uint32_t ovec_size = my_instance->ovec_size;
|
||||||
|
if (ovec_size)
|
||||||
{
|
{
|
||||||
|
my_session->match_data = pcre2_match_data_create(ovec_size, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!my_session->filename || (ovec_size && !my_session->match_data))
|
||||||
|
{
|
||||||
|
MXS_FREE(my_session->filename);
|
||||||
|
pcre2_match_data_free(my_session->match_data);
|
||||||
MXS_FREE(my_session);
|
MXS_FREE(my_session);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -434,6 +461,7 @@ newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
|||||||
errno,
|
errno,
|
||||||
mxs_strerror(errno));
|
mxs_strerror(errno));
|
||||||
MXS_FREE(my_session->filename);
|
MXS_FREE(my_session->filename);
|
||||||
|
pcre2_match_data_free(my_session->match_data);
|
||||||
MXS_FREE(my_session);
|
MXS_FREE(my_session);
|
||||||
my_session = NULL;
|
my_session = NULL;
|
||||||
}
|
}
|
||||||
@ -473,6 +501,7 @@ freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
|||||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||||
|
|
||||||
MXS_FREE(my_session->filename);
|
MXS_FREE(my_session->filename);
|
||||||
|
pcre2_match_data_free(my_session->match_data);
|
||||||
MXS_FREE(session);
|
MXS_FREE(session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -513,14 +542,9 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
|||||||
struct tm t;
|
struct tm t;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
if (my_session->active)
|
if (my_session->active &&
|
||||||
{
|
modutil_extract_SQL(queue, &ptr, &length) &&
|
||||||
if (modutil_extract_SQL(queue, &ptr, &length))
|
regex_check(my_instance, my_session, ptr, length))
|
||||||
{
|
|
||||||
if ((my_instance->match == NULL ||
|
|
||||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
|
||||||
(my_instance->nomatch == NULL ||
|
|
||||||
regexec(&my_instance->nore, ptr, 0, NULL, 0) != 0))
|
|
||||||
{
|
{
|
||||||
char buffer[QLA_DATE_BUFFER_SIZE];
|
char buffer[QLA_DATE_BUFFER_SIZE];
|
||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
@ -564,8 +588,6 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
|||||||
my_instance->write_warning_given = true;
|
my_instance->write_warning_given = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Pass the query downstream */
|
/* Pass the query downstream */
|
||||||
return my_session->down.routeQuery(my_session->down.instance,
|
return my_session->down.routeQuery(my_session->down.instance,
|
||||||
my_session->down.session, queue);
|
my_session->down.session, queue);
|
||||||
@ -910,3 +932,46 @@ static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *ins
|
|||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool regex_check(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||||
|
const char* ptr, int length)
|
||||||
|
{
|
||||||
|
bool rval = true;
|
||||||
|
if (my_instance->re_match)
|
||||||
|
{
|
||||||
|
int result = pcre2_match(my_instance->re_match, (PCRE2_SPTR)ptr,
|
||||||
|
length, 0, 0, my_session->match_data, NULL);
|
||||||
|
if (result == PCRE2_ERROR_NOMATCH)
|
||||||
|
{
|
||||||
|
rval = false; // Didn't match the "match"-regex
|
||||||
|
}
|
||||||
|
else if (result < 0)
|
||||||
|
{
|
||||||
|
rval = false;
|
||||||
|
if (!my_instance->match_error_printed)
|
||||||
|
{
|
||||||
|
MXS_PCRE2_PRINT_ERROR(result);
|
||||||
|
my_instance->match_error_printed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rval && my_instance->re_nomatch)
|
||||||
|
{
|
||||||
|
int result = pcre2_match(my_instance->re_nomatch, (PCRE2_SPTR)ptr,
|
||||||
|
length, 0, 0, my_session->match_data, NULL);
|
||||||
|
if (result >= 0)
|
||||||
|
{
|
||||||
|
rval = false; // Matched the "exclude"-regex
|
||||||
|
}
|
||||||
|
else if (result != PCRE2_ERROR_NOMATCH)
|
||||||
|
{
|
||||||
|
rval = false;
|
||||||
|
if (!my_instance->nomatch_error_printed)
|
||||||
|
{
|
||||||
|
MXS_PCRE2_PRINT_ERROR(result);
|
||||||
|
my_instance->nomatch_error_printed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user