First working filters implementaton.

Only downstream filters are supported currently, i.e. no result
set filtering can be done.

A crude QLA (Query Log All) filter is included as a test harness only
This commit is contained in:
Mark Riddoch
2014-05-30 16:45:39 +01:00
parent 7bca4e383f
commit 8d55be4b23
16 changed files with 1158 additions and 51 deletions

View File

@ -33,6 +33,7 @@ all:
(cd modules/routing/readwritesplit; touch depend.mk ;make) (cd modules/routing/readwritesplit; touch depend.mk ;make)
(cd modules/protocol; touch depend.mk ;make) (cd modules/protocol; touch depend.mk ;make)
(cd modules/monitor; touch depend.mk ;make) (cd modules/monitor; touch depend.mk ;make)
(cd modules/filter; touch depend.mk ;make)
cleantests: cleantests:
$(MAKE) -C test cleantests $(MAKE) -C test cleantests
@ -49,12 +50,14 @@ clean:
(cd modules/routing; touch depend.mk ; make clean) (cd modules/routing; touch depend.mk ; make clean)
(cd modules/protocol; touch depend.mk ; make clean) (cd modules/protocol; touch depend.mk ; make clean)
(cd modules/monitor; touch depend.mk ; make clean) (cd modules/monitor; touch depend.mk ; make clean)
(cd modules/filter; touch depend.mk ; make clean)
depend: depend:
(cd core; touch depend.mk ; make depend) (cd core; touch depend.mk ; make depend)
(cd modules/routing; touch depend.mk ; make depend) (cd modules/routing; touch depend.mk ; make depend)
(cd modules/protocol; touch depend.mk ; make depend) (cd modules/protocol; touch depend.mk ; make depend)
(cd modules/monitor; touch depend.mk ; make depend) (cd modules/monitor; touch depend.mk ; make depend)
(cd modules/filter; touch depend.mk ; make depend)
install: install:
@mkdir -p $(DEST) @mkdir -p $(DEST)
@ -71,3 +74,4 @@ install:
(cd modules/routing; make DEST=$(DEST) install) (cd modules/routing; make DEST=$(DEST) install)
(cd modules/protocol; make DEST=$(DEST) install) (cd modules/protocol; make DEST=$(DEST) install)
(cd modules/monitor; make DEST=$(DEST) install) (cd modules/monitor; make DEST=$(DEST) install)
(cd modules/filter; make DEST=$(DEST) install)

View File

@ -33,6 +33,7 @@
# 29/06/13 Vilho Raatikka Reverted Query classifier changes because # 29/06/13 Vilho Raatikka Reverted Query classifier changes because
# gateway needs mysql client lib, not qc. # gateway needs mysql client lib, not qc.
# 24/07/13 Mark Ridoch Addition of encryption routines # 24/07/13 Mark Ridoch Addition of encryption routines
# 30/05/14 Mark Ridoch Filter API added
include ../../build_gateway.inc include ../../build_gateway.inc
@ -56,14 +57,15 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \
SRCS= atomic.c buffer.c spinlock.c gateway.c \ SRCS= atomic.c buffer.c spinlock.c gateway.c \
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c \ gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c \
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \ poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \
monitor.c adminusers.c secrets.c monitor.c adminusers.c secrets.c filter.c
HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \ HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
../include/gw.h ../modules/include/mysql_client_server_protocol.h \ ../include/gw.h ../modules/include/mysql_client_server_protocol.h \
../include/session.h ../include/spinlock.h ../include/thread.h \ ../include/session.h ../include/spinlock.h ../include/thread.h \
../include/modules.h ../include/poll.h ../include/config.h \ ../include/modules.h ../include/poll.h ../include/config.h \
../include/users.h ../include/hashtable.h ../include/gwbitmask.h \ ../include/users.h ../include/hashtable.h ../include/gwbitmask.h \
../include/adminusers.h ../include/version.h ../include/maxscale.h ../include/adminusers.h ../include/version.h ../include/maxscale.h \
../include/filter.h
OBJ=$(SRCS:.c=.o) OBJ=$(SRCS:.c=.o)

View File

@ -31,6 +31,7 @@
* 11/03/14 Massimiliano Pinto Added Unix socket support * 11/03/14 Massimiliano Pinto Added Unix socket support
* 11/05/14 Massimiliano Pinto Added version_string support to service * 11/05/14 Massimiliano Pinto Added version_string support to service
* 19/05/14 Mark Riddoch Added unique names from section headers * 19/05/14 Mark Riddoch Added unique names from section headers
* 29/05/14 Mark Riddoch Addition of filter definition
* *
* @endverbatim * @endverbatim
*/ */
@ -215,6 +216,8 @@ int error_count = 0;
{ {
char *router = config_get_value(obj->parameters, char *router = config_get_value(obj->parameters,
"router"); "router");
char *filters = config_get_value(obj->parameters,
"filters");
if (router) if (router)
{ {
char* max_slave_conn_str; char* max_slave_conn_str;
@ -301,6 +304,9 @@ int error_count = 0;
param->value))); param->value)));
} }
} }
if (filters)
serviceSetFilters(obj->element,
filters);
} }
else else
{ {
@ -359,6 +365,36 @@ int error_count = 0;
obj->object))); obj->object)));
} }
} }
else if (!strcmp(type, "filter"))
{
char *module = config_get_value(obj->parameters,
"module");
char *options = config_get_value(obj->parameters,
"options");
if (module)
{
obj->element = filter_alloc(obj->object, module);
}
else
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Filter '%s' has no module "
"defined defined to load.",
obj->object)));
error_count++;
}
if (obj->element)
{
char *s = strtok(options, ",");
while (s)
{
filterAddOption(obj->element, s);
s = strtok(NULL, ",");
}
}
}
obj = obj->next; obj = obj->next;
} }
@ -550,7 +586,8 @@ int error_count = 0;
error_count++; error_count++;
} }
} }
else if (strcmp(type, "server") != 0) else if (strcmp(type, "server") != 0
&& strcmp(type, "filter") != 0)
{ {
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
@ -959,10 +996,12 @@ SERVER *server;
{ {
char *servers; char *servers;
char *roptions; char *roptions;
char *filters;
servers = config_get_value(obj->parameters, "servers"); servers = config_get_value(obj->parameters, "servers");
roptions = config_get_value(obj->parameters, roptions = config_get_value(obj->parameters,
"router_options"); "router_options");
filters = config_get_value(obj->parameters, "filters");
if (servers && obj->element) if (servers && obj->element)
{ {
char *s = strtok(servers, ","); char *s = strtok(servers, ",");
@ -996,6 +1035,8 @@ SERVER *server;
s = strtok(NULL, ","); s = strtok(NULL, ",");
} }
} }
if (filters)
serviceSetFilters(obj->element, filters);
} }
else if (!strcmp(type, "listener")) else if (!strcmp(type, "listener"))
{ {
@ -1080,6 +1121,7 @@ static char *service_params[] =
"enable_root_user", "enable_root_user",
"max_slave_connections", "max_slave_connections",
"version_string", "version_string",
"filters",
NULL NULL
}; };

279
server/core/filter.c Normal file
View File

@ -0,0 +1,279 @@
/*
* This file is distributed as part of MaxScale from SkySQL. 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 SkySQL Ab 2014
*/
/**
* @file filter.c - A representation of a filter within MaxScale.
*
* @verbatim
* Revision History
*
* Date Who Description
* 29/05/14 Mark Riddoch Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <filter.h>
#include <session.h>
#include <modules.h>
#include <spinlock.h>
#include <skygw_utils.h>
#include <log_manager.h>
extern int lm_enabled_logfiles_bitmask;
static SPINLOCK filter_spin = SPINLOCK_INIT;
static FILTER_DEF *allFilters = NULL;
/**
* Allocate a new filter within MaxScale
*
*
* @param name The filter name
* @param module The module to load
*
* @return The newly created filter or NULL if an error occured
*/
FILTER_DEF *
filter_alloc(char *name, char *module)
{
FILTER_DEF *filter;
if ((filter = (FILTER_DEF *)malloc(sizeof(FILTER_DEF))) == NULL)
return NULL;
filter->name = strdup(name);
filter->module = strdup(module);
spinlock_init(&filter->spin);
spinlock_acquire(&filter_spin);
filter->next = allFilters;
allFilters = filter;
spinlock_release(&filter_spin);
return filter;
}
/**
* Deallocate the specified filter
*
* @param server The service to deallocate
* @return Returns true if the server was freed
*/
void
filter_free(FILTER_DEF *filter)
{
FILTER_DEF *ptr;
/* First of all remove from the linked list */
spinlock_acquire(&filter_spin);
if (allFilters == filter)
{
allFilters = filter->next;
}
else
{
ptr = allFilters;
while (ptr && ptr->next != filter)
{
ptr = ptr->next;
}
if (ptr)
ptr->next = filter->next;
}
spinlock_release(&filter_spin);
/* Clean up session and free the memory */
free(filter->name);
free(filter->module);
free(filter);
}
/**
* Find an existing filter using the unique section name in
* configuration file
*
* @param name The filter name
* @return The server or NULL if not found
*/
FILTER_DEF *
filter_find(char *name)
{
FILTER_DEF *filter;
spinlock_acquire(&filter_spin);
filter = allFilters;
while (filter)
{
if (strcmp(filter->name, name) == 0)
break;
filter = filter->next;
}
spinlock_release(&filter_spin);
return filter;
}
/**
* Print all filters to a DCB
*
* Designed to be called within a debugger session in order
* to display all active filters within MaxScale
*/
void
dprintAllFilters(DCB *dcb)
{
FILTER_DEF *ptr;
int i;
spinlock_acquire(&filter_spin);
ptr = allFilters;
while (ptr)
{
dcb_printf(dcb, "Filter %p (%s)\n", ptr, ptr->name);
dcb_printf(dcb, "\tModule: %s\n", ptr->module);
if (ptr->options)
{
dcb_printf(dcb, "\tOptions: ");
for (i = 0; ptr->options && ptr->options[i]; i++)
dcb_printf(dcb, "%s ", ptr->options[i]);
dcb_printf(dcb, "\n");
}
if (ptr->obj && ptr->filter)
ptr->obj->diagnostics(ptr->filter, NULL, dcb);
ptr = ptr->next;
}
spinlock_release(&filter_spin);
}
/**
* Print filter details to a DCB
*
* Designed to be called within a debug CLI in order
* to display all active filters in MaxScale
*/
void
dprintFilter(DCB *dcb, FILTER_DEF *filter)
{
int i;
dcb_printf(dcb, "Filter %p (%s)\n", filter, filter->name);
dcb_printf(dcb, "\tModule: %s\n", filter->module);
if (filter->options)
{
dcb_printf(dcb, "\tOptions: ");
for (i = 0; filter->options && filter->options[i]; i++)
dcb_printf(dcb, "%s ", filter->options[i]);
dcb_printf(dcb, "\n");
}
if (filter->obj && filter->filter)
filter->obj->diagnostics(filter->filter, NULL, dcb);
}
/**
* List all filters in a tabular form to a DCB
*
*/
void
dListFilters(DCB *dcb)
{
FILTER_DEF *ptr;
int i;
spinlock_acquire(&filter_spin);
ptr = allFilters;
if (ptr)
{
dcb_printf(dcb, "%-18s | %-15s | Options\n",
"Filter", "Address", "Status");
dcb_printf(dcb, "-------------------------------------------------------------------------------\n");
}
while (ptr)
{
dcb_printf(dcb, "%-18s | %-15s | ",
ptr->name, ptr->module);
for (i = 0; ptr->options && ptr->options[i]; i++)
dcb_printf(dcb, "%s ", ptr->options[i]);
dcb_printf(dcb, "\n");
ptr = ptr->next;
}
spinlock_release(&filter_spin);
}
/**
* Add a router option to a service
*
* @param service The service to add the router option to
* @param option The option string
*/
void
filterAddOption(FILTER_DEF *filter, char *option)
{
int i;
spinlock_acquire(&filter->spin);
if (filter->options == NULL)
{
filter->options = (char **)calloc(2, sizeof(char *));
filter->options[0] = strdup(option);
filter->options[1] = NULL;
}
else
{
for (i = 0; filter->options[i]; i++)
;
filter->options = (char **)realloc(filter->options,
(i + 2) * sizeof(char *));
filter->options[i] = strdup(option);
filter->options[i+1] = NULL;
}
spinlock_release(&filter->spin);
}
DOWNSTREAM *
filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream)
{
DOWNSTREAM *me;
if (filter->obj == NULL)
{
/* Filter not yet loaded */
if ((filter->obj = load_module(filter->module,
MODULE_FILTER)) == NULL)
{
return NULL;
}
}
if (filter->filter == NULL)
filter->filter = (filter->obj->createInstance)(filter->options);
if ((me = (DOWNSTREAM *)calloc(1, sizeof(DOWNSTREAM))) == NULL)
{
return NULL;
}
me->instance = filter->filter;
me->routeQuery = filter->obj->routeQuery;
me->session = filter->obj->newSession(me->instance, session);
filter->obj->setDownstream(me->instance, me->session, downstream);
return me;
}

View File

@ -30,6 +30,7 @@
* 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services) * 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services)
* 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL * 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL
* 23/05/14 Mark Riddoch Addition of service validation call * 23/05/14 Mark Riddoch Addition of service validation call
* 29/05/14 Mark Riddoch Filter API implementation
* *
* @endverbatim * @endverbatim
*/ */
@ -46,6 +47,7 @@
#include <modules.h> #include <modules.h>
#include <dcb.h> #include <dcb.h>
#include <users.h> #include <users.h>
#include <filter.h>
#include <dbusers.h> #include <dbusers.h>
#include <poll.h> #include <poll.h>
#include <skygw_utils.h> #include <skygw_utils.h>
@ -110,6 +112,8 @@ SERVICE *service;
service->databases = NULL; service->databases = NULL;
service->svc_config_param = NULL; service->svc_config_param = NULL;
service->svc_config_version = 0; service->svc_config_version = 0;
service->filters = NULL;
service->n_filters = 0;
spinlock_init(&service->spin); spinlock_init(&service->spin);
spinlock_init(&service->users_table_spin); spinlock_init(&service->users_table_spin);
memset(&service->rate_limit, 0, sizeof(SERVICE_REFRESH_RATE)); memset(&service->rate_limit, 0, sizeof(SERVICE_REFRESH_RATE));
@ -608,6 +612,62 @@ serviceEnableRootUser(SERVICE *service, int action)
return 1; return 1;
} }
/**
* Trim whitespace from the from an rear of a string
*
* @param str String to trim
* @return Trimmed string, chanesg are done in situ
*/
static char *
trim(char *str)
{
char *ptr;
while (isspace(*str))
str++;
ptr = str + strlen(str);
while (ptr > str && isspace(*ptr))
*ptr-- = 0;
return str;
}
/**
* Set the filters used by the service
*
* @param service The service itself
* @param filters ASCII string of filters to use
*/
void
serviceSetFilters(SERVICE *service, char *filters)
{
FILTER_DEF **flist;
char *ptr, *brkt;
int n = 0;
flist = (FILTER_DEF *)malloc(sizeof(FILTER_DEF *));
ptr = strtok_r(filters, "|", &brkt);
while (ptr)
{
n++;
flist = (FILTER_DEF *)realloc(flist, (n + 1) * sizeof(FILTER_DEF *));
if ((flist[n-1] = filter_find(trim(ptr))) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Unable to find filter '%s' for service '%s'\n",
trim(ptr), service->name
)));
}
flist[n] = NULL;
ptr = strtok_r(NULL, "|", &brkt);
}
service->filters = flist;
service->n_filters = n;
}
/** /**
* Return a named service * Return a named service
* *
@ -638,6 +698,7 @@ void
printService(SERVICE *service) printService(SERVICE *service)
{ {
SERVER *ptr = service->databases; SERVER *ptr = service->databases;
int i;
printf("Service %p\n", service); printf("Service %p\n", service);
printf("\tService: %s\n", service->name); printf("\tService: %s\n", service->name);
@ -649,6 +710,16 @@ SERVER *ptr = service->databases;
printf("\t\t%s:%d Protocol: %s\n", ptr->name, ptr->port, ptr->protocol); printf("\t\t%s:%d Protocol: %s\n", ptr->name, ptr->port, ptr->protocol);
ptr = ptr->nextdb; ptr = ptr->nextdb;
} }
if (service->n_filters)
{
printf("\tFilter chain: ");
for (i = 0; i < service->n_filters; i++)
{
printf("%s %s ", service->filters[i]->name,
i + 1 < service->n_filters ? "|" : "");
}
printf("\n");
}
printf("\tUsers data: %p\n", service->users); printf("\tUsers data: %p\n", service->users);
printf("\tTotal connections: %d\n", service->stats.n_sessions); printf("\tTotal connections: %d\n", service->stats.n_sessions);
printf("\tCurrently connected: %d\n", service->stats.n_current); printf("\tCurrently connected: %d\n", service->stats.n_current);
@ -705,6 +776,7 @@ SERVICE *ptr;
void dprintService(DCB *dcb, SERVICE *service) void dprintService(DCB *dcb, SERVICE *service)
{ {
SERVER *server = service->databases; SERVER *server = service->databases;
int i;
dcb_printf(dcb, "Service %p\n", service); dcb_printf(dcb, "Service %p\n", service);
dcb_printf(dcb, "\tService: %s\n", service->name); dcb_printf(dcb, "\tService: %s\n", service->name);
@ -714,6 +786,16 @@ SERVER *server = service->databases;
service->router->diagnostics(service->router_instance, dcb); service->router->diagnostics(service->router_instance, dcb);
dcb_printf(dcb, "\tStarted: %s", dcb_printf(dcb, "\tStarted: %s",
asctime(localtime(&service->stats.started))); asctime(localtime(&service->stats.started)));
if (service->n_filters)
{
dcb_printf(dcb, "\tFilter chain: ");
for (i = 0; i < service->n_filters; i++)
{
dcb_printf(dcb, "%s %s ", service->filters[i]->name,
i + 1 < service->n_filters ? "|" : "");
}
dcb_printf(dcb, "\n");
}
dcb_printf(dcb, "\tBackend databases\n"); dcb_printf(dcb, "\tBackend databases\n");
while (server) while (server)
{ {

View File

@ -25,6 +25,7 @@
* Date Who Description * Date Who Description
* 17/06/13 Mark Riddoch Initial implementation * 17/06/13 Mark Riddoch Initial implementation
* 02/09/13 Massimiliano Pinto Added session refcounter * 02/09/13 Massimiliano Pinto Added session refcounter
* 29/05/14 Mark Riddoch Addition of filter mechanism
* *
* @endverbatim * @endverbatim
*/ */
@ -47,6 +48,9 @@ extern int lm_enabled_logfiles_bitmask;
static SPINLOCK session_spin = SPINLOCK_INIT; static SPINLOCK session_spin = SPINLOCK_INIT;
static SESSION *allSessions = NULL; static SESSION *allSessions = NULL;
static int session_setup_filters(SESSION *session);
/** /**
* Allocate a new session for a new client of the specified service. * Allocate a new session for a new client of the specified service.
* *
@ -132,7 +136,7 @@ session_alloc(SERVICE *service, DCB *client_dcb)
/** /**
* Inform other threads that session is closing. * Inform other threads that session is closing.
*/ */
session->state == SESSION_STATE_STOPPING; session->state = SESSION_STATE_STOPPING;
/*< /*<
* Decrease refcount, set dcb's session pointer NULL * Decrease refcount, set dcb's session pointer NULL
* and set session pointer to NULL. * and set session pointer to NULL.
@ -147,7 +151,45 @@ session_alloc(SERVICE *service, DCB *client_dcb)
goto return_session; goto return_session;
} }
/*
* Pending filter chain being setup set the head of the chain to
* be the router. As filters are inserted the current head will
* be pushed to the filter and the head updated.
*
* NB This dictates that filters are created starting at the end
* of the chain nearest the router working back to the client
* protocol end of the chain.
*/
session->head.instance = service->router_instance;
session->head.session = session->router_session;
session->head.routeQuery = service->router->routeQuery;
if (service->n_filters > 0)
{
if (!session_setup_filters(session))
{
/**
* Inform other threads that session is closing.
*/
session->state = SESSION_STATE_STOPPING;
/*<
* Decrease refcount, set dcb's session pointer NULL
* and set session pointer to NULL.
*/
session_free(session);
client_dcb->session = NULL;
session = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed to create %s session.",
service->name)));
goto return_session;
} }
}
}
spinlock_acquire(&session_spin); spinlock_acquire(&session_spin);
session->state = SESSION_STATE_ROUTER_READY; session->state = SESSION_STATE_ROUTER_READY;
session->next = allSessions; session->next = allSessions;
@ -440,6 +482,8 @@ SESSION *ptr;
void void
dprintSession(DCB *dcb, SESSION *ptr) dprintSession(DCB *dcb, SESSION *ptr)
{ {
DOWNSTREAM *dptr;
dcb_printf(dcb, "Session %p\n", ptr); dcb_printf(dcb, "Session %p\n", ptr);
dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state)); dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state));
dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service); dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service);
@ -520,3 +564,41 @@ SESSION* get_session_by_router_ses(
} }
return ses; return ses;
} }
/**
* Create the filter chain for this session.
*
* Filters must be setup in reverse order, starting with the last
* filter in the chain and working back towards the client connection
* Each filter is passed the current session head of the filter chain
* this head becomes the destination for the filter. The newly created
* filter becomes the new head of the filter chain.
*
* @param session The session that requires the chain
* @return 0 if filter creation fails
*/
static int
session_setup_filters(SESSION *session)
{
SERVICE *service = session->service;
DOWNSTREAM *head;
int i;
for (i = service->n_filters - 1; i >= 0; i--)
{
if ((head = filterApply(service->filters[i], session,
&session->head)) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Failed to create filter '%s' for service '%s'.\n",
service->filters[i]->name,
service->name)));
return 0;
}
session->head = *head;
}
return 1;
}

96
server/include/filter.h Normal file
View File

@ -0,0 +1,96 @@
#ifndef _FILTER_H
#define _FILTER_H
/*
* This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2014
*/
/**
* @file filter.h - The filter interface mechanisms
*
* Revision History
*
* Date Who Description
* 27/05/2014 Mark Riddoch Initial implementation
*
*/
#include <dcb.h>
#include <session.h>
#include <buffer.h>
#include <stdint.h>
/**
* The FILTER handle points to module specific data, so the best we can do
* is to make it a void * externally.
*/
typedef void *FILTER;
/**
* @verbatim
* The "module object" structure for a query router module
*
* The entry points are:
* createInstance Called by the service to create a new
* instance of the filter
* newSession Called to create a new user session
* within the filter
* closeSession Called when a session is closed
* freeSession Called when a session is freed
* setDownstream Sets the downstream component of the
* filter pipline
* routeQuery Called on each query that requires
* routing
* diagnostics Called to force the filter to print
* diagnostic output
*
* @endverbatim
*
* @see load_module
*/
typedef struct filter_object {
FILTER *(*createInstance)(char **options);
void *(*newSession)(FILTER *instance, SESSION *session);
void (*closeSession)(FILTER *instance, void *fsession);
void (*freeSession)(FILTER *instance, void *fsession);
void (*setDownstream)(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
int (*routeQuery)(FILTER *instance, void *fsession, GWBUF *queue);
void (*diagnostics)(FILTER *instance, void *fsession, DCB *dcb);
} FILTER_OBJECT;
/**
* The definition of a filter form the configuration file.
* This is basically the link between a plugin to load and the
* optons to pass to that plugin.
*/
typedef struct filter_def {
char *name; /*< The Filter name */
char *module; /*< The module to load */
char **options; /*< The options set for this filter */
FILTER filter;
FILTER_OBJECT *obj;
SPINLOCK spin;
struct filter_def
*next; /*< Next filter in the chain of all filters */
} FILTER_DEF;
FILTER_DEF *filter_alloc(char *, char *);
void filter_free(FILTER_DEF *);
FILTER_DEF *filter_find(char *);
void filterAddOption(FILTER_DEF *, char *);
DOWNSTREAM *filterApply(FILTER_DEF *, SESSION *, DOWNSTREAM *);
void dprintAllFilters(DCB *);
void dprintFilter(DCB *, FILTER_DEF *);
void dListFilters(DCB *);
#endif

View File

@ -30,6 +30,7 @@
* Date Who Description * Date Who Description
* 13/06/13 Mark Riddoch Initial implementation * 13/06/13 Mark Riddoch Initial implementation
* 08/07/13 Mark Riddoch Addition of monitor modules * 08/07/13 Mark Riddoch Addition of monitor modules
* 29/05/14 Mark Riddoch Addition of filter modules
* @endverbatim * @endverbatim
*/ */
@ -49,6 +50,7 @@ typedef struct modules {
#define MODULE_PROTOCOL "Protocol" /**< A protocol module type */ #define MODULE_PROTOCOL "Protocol" /**< A protocol module type */
#define MODULE_ROUTER "Router" /**< A router module type */ #define MODULE_ROUTER "Router" /**< A router module type */
#define MODULE_MONITOR "Monitor" /**< A database monitor module type */ #define MODULE_MONITOR "Monitor" /**< A database monitor module type */
#define MODULE_FILTER "Filter" /**< A filter module type */
extern void *load_module(const char *module, const char *type); extern void *load_module(const char *module, const char *type);

View File

@ -22,6 +22,7 @@
#include <spinlock.h> #include <spinlock.h>
#include <dcb.h> #include <dcb.h>
#include <server.h> #include <server.h>
#include <filter.h>
#include "config.h" #include "config.h"
/** /**
@ -39,7 +40,9 @@
* 23/06/13 Mark Riddoch Added service user and users * 23/06/13 Mark Riddoch Added service user and users
* 06/02/14 Massimiliano Pinto Added service flag for root user access * 06/02/14 Massimiliano Pinto Added service flag for root user access
* 25/02/14 Massimiliano Pinto Added service refresh limit feature * 25/02/14 Massimiliano Pinto Added service refresh limit feature
* 07/05/14 Massimiliano Pinto Added version_string field to service struct * 07/05/14 Massimiliano Pinto Added version_string field to service
* struct
* 29/05/14 Mark Riddoch Filter API mechanism
* *
* @endverbatim * @endverbatim
*/ */
@ -73,9 +76,10 @@ typedef struct {
} SERVICE_STATS; } SERVICE_STATS;
/** /**
* The service user structure holds the information that is needed for this service to * The service user structure holds the information that is needed
* allow the gateway to login to the backend database and extact information such as for this service to allow the gateway to login to the backend
* the user table or other database status or configuration data. database and extact information such as the user table or other
database status or configuration data.
*/ */
typedef struct { typedef struct {
char *name; /**< The user name to use to extract information */ char *name; /**< The user name to use to extract information */
@ -83,8 +87,8 @@ typedef struct {
} SERVICE_USER; } SERVICE_USER;
/** /**
* The service refresh rate hols the counter and last load timet for this service to * The service refresh rate holds the counter and last load time_t
* load users data from the backend database for this service to load users data from the backend database
*/ */
typedef struct { typedef struct {
int nloads; int nloads;
@ -105,24 +109,27 @@ typedef struct service {
* that this service will listen on. * that this service will listen on.
*/ */
char *routerModule; /**< Name of router module to use */ char *routerModule; /**< Name of router module to use */
char **routerOptions; /**< Router specific option strings */ char **routerOptions;/**< Router specific option strings */
struct router_object struct router_object
*router; /**< The router we are using */ *router; /**< The router we are using */
void *router_instance; void *router_instance;
/**< The router instance for this service */ /**< The router instance for this service */
char *version_string; /** version string for this service listeners */ char *version_string;/** version string for this service listeners */
struct server *databases; /**< The set of servers in the backend */ struct server *databases; /**< The set of servers in the backend */
SERVICE_USER credentials; /**< The cedentials of the service user */ SERVICE_USER credentials; /**< The cedentials of the service user */
SPINLOCK spin; /**< The service spinlock */ SPINLOCK spin; /**< The service spinlock */
SERVICE_STATS stats; /**< The service statistics */ SERVICE_STATS stats; /**< The service statistics */
struct users *users; /**< The user data for this service */ struct users *users; /**< The user data for this service */
int enable_root; /**< Allow root user access */ int enable_root; /**< Allow root user access */
CONFIG_PARAMETER* svc_config_param; /*< list of config params and values */ CONFIG_PARAMETER*
svc_config_param; /*< list of config params and values */
int svc_config_version; /*< Version number of configuration */ int svc_config_version; /*< Version number of configuration */
SPINLOCK SPINLOCK
users_table_spin; /**< The spinlock for users data refresh */ users_table_spin; /**< The spinlock for users data refresh */
SERVICE_REFRESH_RATE SERVICE_REFRESH_RATE
rate_limit; /**< The refresh rate limit for users table */ rate_limit; /**< The refresh rate limit for users table */
FILTER_DEF **filters; /**< Ordered list of filters */
int n_filters; /**< Number of filters */
struct service *next; /**< The next service in the linked list */ struct service *next; /**< The next service in the linked list */
} SERVICE; } SERVICE;

View File

@ -31,11 +31,14 @@
* 01-07-2013 Massimiliano Pinto Removed backends pointer * 01-07-2013 Massimiliano Pinto Removed backends pointer
* from struct session * from struct session
* 02-09-2013 Massimiliano Pinto Added session ref counter * 02-09-2013 Massimiliano Pinto Added session ref counter
* 29-05-2014 Mark Riddoch Support for filter mechanism
* added
* *
* @endverbatim * @endverbatim
*/ */
#include <time.h> #include <time.h>
#include <atomic.h> #include <atomic.h>
#include <buffer.h>
#include <spinlock.h> #include <spinlock.h>
#include <skygw_utils.h> #include <skygw_utils.h>
@ -59,6 +62,17 @@ typedef enum {
SESSION_STATE_FREE /*< for all sessions */ SESSION_STATE_FREE /*< for all sessions */
} session_state_t; } session_state_t;
/**
* The downstream element in the filter chain. This may refer to
* another filter or to a router.
*/
typedef struct {
void *instance;
void *session;
int (*routeQuery)(void *instance, void *router_session, GWBUF *queue);
} DOWNSTREAM;
/** /**
* The session status block * The session status block
* *
@ -77,6 +91,7 @@ typedef struct session {
void *router_session;/**< The router instance data */ void *router_session;/**< The router instance data */
SESSION_STATS stats; /**< Session statistics */ SESSION_STATS stats; /**< Session statistics */
struct service *service; /**< The service this session is using */ struct service *service; /**< The service this session is using */
DOWNSTREAM head; /**< Head of the filter chain */
struct session *next; /**< Linked list of all sessions */ struct session *next; /**< Linked list of all sessions */
int refcount; /**< Reference count on the session */ int refcount; /**< Reference count on the session */
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
@ -86,6 +101,15 @@ typedef struct session {
#define SESSION_PROTOCOL(x, type) DCB_PROTOCOL((x)->client, type) #define SESSION_PROTOCOL(x, type) DCB_PROTOCOL((x)->client, type)
/**
* A convenience macro that can be used by the protocol modules to route
* the incoming data to the first element in the pipeline of filters and
* routers.
*/
#define SESSION_ROUTE_QUERY(session, buf) \
((session)->head.routeQuery)((session)->head.instance, \
(session)->head.session, (buf))
SESSION *session_alloc(struct service *, struct dcb *); SESSION *session_alloc(struct service *, struct dcb *);
bool session_free(SESSION *); bool session_free(SESSION *);
int session_isvalid(SESSION *); int session_isvalid(SESSION *);

View File

@ -0,0 +1,83 @@
# This file is distributed as part of MaxScale form SkySQL. 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 SkySQL Ab 2014
#
# Revision History
# Date Who Description
# 29/05/14 Mark Riddoch Initial module development
include ../../../build_gateway.inc
LOGPATH := $(ROOT_PATH)/log_manager
UTILSPATH := $(ROOT_PATH)/utils
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
-I$(UTILSPATH) -Wall -g
include ../../../makefile.inc
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
TESTSRCS=testfilter.c
TESTOBJ=$(TESTSRCS:.c=.o)
QLASRCS=qlafilter.c
QLAOBJ=$(QLASRCS:.c=.o)
SRCS=$(TESTSRCS)
OBJ=$(SRCS:.c=.o)
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
MODULES= libtestfilter.so libqlafilter.so
all: $(MODULES)
libtestfilter.so: $(TESTOBJ)
$(CC) $(LDFLAGS) $(TESTOBJ) $(LIBS) -o $@
libqlafilter.so: $(QLAOBJ)
$(CC) $(LDFLAGS) $(QLAOBJ) $(LIBS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f $(OBJ) $(MODULES)
tags:
ctags $(SRCS) $(HDRS)
depend:
@rm -f depend.mk
cc -M $(CFLAGS) $(SRCS) > depend.mk
install: $(MODULES)
install -D $(MODULES) $(DEST)/MaxScale/modules
cleantests:
$(MAKE) -C test cleantests
buildtests:
$(MAKE) -C readwritesplit/test DEBUG=Y buildtests
$(MAKE) -C test DEBUG=Y buildtests
runtests:
$(MAKE) -C test runtests
$(MAKE) -C readwritesplit runtests
testall:
$(MAKE) -C test testall
include depend.mk

View File

@ -0,0 +1,211 @@
/*
* This file is distributed as part of MaxScale by SkySQL. 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 SkySQL Ab 2014
*/
#include <stdio.h>
#include <fcntl.h>
#include <filter.h>
static char *version_str = "V1.0.0";
static FILTER *createInstance(char **options);
static void *newSession(FILTER *instance, SESSION *session);
static void closeSession(FILTER *instance, void *session);
static void freeSession(FILTER *instance, void *session);
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
static FILTER_OBJECT MyObject = {
createInstance,
newSession,
closeSession,
freeSession,
setDownstream,
routeQuery,
diagnostic,
};
/**
* A dummy instance structure
*/
typedef struct {
int sessions;
char *filebase;
} QLA_INSTANCE;
/**
* A dummy session structure for this test filter
*/
typedef struct {
DOWNSTREAM down;
int mysession;
int fd;
} QLA_SESSION;
/**
* Implementation of the mandatory version entry point
*
* @return version string of the module
*/
char *
version()
{
return version_str;
}
/**
* The module initialisation routine, called when the module
* is first loaded.
*/
void
ModuleInit()
{
}
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
* "module object", this is a structure with the set of
* external entry points for this module.
*
* @return The module object
*/
FILTER_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Create an instance of the filter for a particular service
* within MaxScale.
*
* @param options The options for this filter
*
* @return The instance data for this new instance
*/
static FILTER *
createInstance(char **options)
{
QLA_INSTANCE *my_instance;
if ((my_instance = calloc(1, sizeof(QLA_INSTANCE))) != NULL)
{
if (options)
my_instance->filebase = strdup(options[0]);
else
my_instance->filebase = strdup("qla");
my_instance->sessions = 0;
}
return (FILTER *)my_instance;
}
/**
* Associate a new session with this instance of the router.
*
* @param instance The filter instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(FILTER *instance, SESSION *session)
{
QLA_INSTANCE *my_instance = (QLA_INSTANCE *)instance;
QLA_SESSION *my_session;
char filename[100];
if ((my_session = calloc(1, sizeof(QLA_SESSION))) != NULL)
{
sprintf(filename, "%s.%d", my_instance->filebase,
my_instance->sessions);
printf("Open file %s\n", filename);
my_instance->sessions++;
my_session->fd = open(filename, O_WRONLY|O_CREAT, 0666);
}
return my_session;
}
/**
* Close a session with the router, this is the mechanism
* by which a router may cleanup data structure etc.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
closeSession(FILTER *instance, void *session)
{
QLA_SESSION *my_session;
close(my_session->fd);
}
static void
freeSession(FILTER *instance, void *session)
{
return;
}
static void
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
{
QLA_SESSION *my_session = (QLA_SESSION *)session;
my_session->down = *downstream;
}
static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
QLA_SESSION *my_session = (QLA_SESSION *)session;
unsigned char *ptr;
unsigned int length;
ptr = GWBUF_DATA(queue);
length = *ptr++;
length += (*ptr++ << 8);
length += (*ptr++ << 8);
ptr++; // Skip sequence id
if (*ptr++ == 0x03) // COM_QUERY
{
write(my_session->fd, ptr, length - 1);
write(my_session->fd, "\n", 1);
}
return my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
}
/**
* Diagnostics routine
*
* If fsession is NULL then print diagnostics on the filter
* instance as a whole, otherwise print diagnostics for the
* particular session.
*
* @param instance The filter instance
* @param fsession Filter session, may be NULL
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
{
QLA_INSTANCE *my_instance = instance;
QLA_SESSION *my_session = fsession;
}

View File

@ -0,0 +1,190 @@
/*
* This file is distributed as part of MaxScale by SkySQL. 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 SkySQL Ab 2014
*/
#include <stdio.h>
#include <filter.h>
static char *version_str = "V1.0.0";
static FILTER *createInstance(char **options);
static void *newSession(FILTER *instance, SESSION *session);
static void closeSession(FILTER *instance, void *session);
static void freeSession(FILTER *instance, void *session);
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
static FILTER_OBJECT MyObject = {
createInstance,
newSession,
closeSession,
freeSession,
setDownstream,
routeQuery,
diagnostic,
};
/**
* A dummy instance structure
*/
typedef struct {
int sessions;
} TEST_INSTANCE;
/**
* A dummy session structure for this test filter
*/
typedef struct {
DOWNSTREAM down;
int count;
} TEST_SESSION;
/**
* Implementation of the mandatory version entry point
*
* @return version string of the module
*/
char *
version()
{
return version_str;
}
/**
* The module initialisation routine, called when the module
* is first loaded.
*/
void
ModuleInit()
{
}
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
* "module object", this is a structure with the set of
* external entry points for this module.
*
* @return The module object
*/
FILTER_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Create an instance of the filter for a particular service
* within MaxScale.
*
* @param options The options for this filter
*
* @return The instance data for this new instance
*/
static FILTER *
createInstance(char **options)
{
TEST_INSTANCE *my_instance;
if ((my_instance = calloc(1, sizeof(TEST_INSTANCE))) != NULL)
my_instance->sessions = 0;
return my_instance;
}
/**
* Associate a new session with this instance of the router.
*
* @param instance The filter instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(FILTER *instance, SESSION *session)
{
TEST_INSTANCE *my_instance = (TEST_INSTANCE *)instance;
TEST_SESSION *my_session;
if ((my_session = calloc(1, sizeof(TEST_SESSION))) != NULL)
{
my_instance->sessions++;
my_session->count = 0;
}
return my_session;
}
/**
* Close a session with the router, this is the mechanism
* by which a router may cleanup data structure etc.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
closeSession(FILTER *instance, void *session)
{
}
static void
freeSession(FILTER *instance, void *session)
{
return;
}
static void
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
{
TEST_SESSION *my_session = (TEST_SESSION *)session;
my_session->down = *downstream;
}
static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
TEST_SESSION *my_session = (TEST_SESSION *)session;
my_session->count++;
return my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
}
/**
* Diagnostics routine
*
* If fsession is NULL then print diagnostics on the filter
* instance as a whole, otherwise print diagnostics for the
* particular session.
*
* @param instance The filter instance
* @param fsession Filter session, may be NULL
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
{
TEST_INSTANCE *my_instance = instance;
TEST_SESSION *my_session = fsession;
if (my_session)
dcb_printf(dcb, "No. of queries routed by filter: %d\n",
my_session->count);
else
dcb_printf(dcb, "No. of sessions created: %d\n",
my_instance->sessions);
}

View File

@ -57,11 +57,7 @@ static int gw_client_hangup_event(DCB *dcb);
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message); int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
int MySQLSendHandshake(DCB* dcb); int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue); static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
static int route_by_statement( static int route_by_statement(SESSION *, GWBUF *);
ROUTER* router_instance,
ROUTER_OBJECT* router,
void* rsession,
GWBUF* read_buf);
/* /*
* The "module object" for the mysqld client protocol module. * The "module object" for the mysqld client protocol module.
@ -765,7 +761,7 @@ int gw_read_client_event(DCB* dcb) {
/** Route COM_QUIT to backend */ /** Route COM_QUIT to backend */
if (mysql_command == '\x01') { if (mysql_command == '\x01') {
router->routeQuery(router_instance, rsession, read_buffer); SESSION_ROUTE_QUERY(session, read_buffer);
LOGIF(LD, (skygw_log_write_flush( LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG, LOGFILE_DEBUG,
"%lu [gw_read_client_event] Routed COM_QUIT to " "%lu [gw_read_client_event] Routed COM_QUIT to "
@ -783,10 +779,7 @@ int gw_read_client_event(DCB* dcb) {
* Feed each statement completely and separately * Feed each statement completely and separately
* to router. * to router.
*/ */
rc = route_by_statement(router_instance, rc = route_by_statement(session, read_buffer);
router,
rsession,
read_buffer);
if (read_buffer != NULL) if (read_buffer != NULL)
{ {
/** add incomplete mysql packet to read queue */ /** add incomplete mysql packet to read queue */
@ -796,9 +789,7 @@ int gw_read_client_event(DCB* dcb) {
else else
{ {
/** Feed whole packet to router */ /** Feed whole packet to router */
rc = router->routeQuery(router_instance, rc = SESSION_ROUTE_QUERY(session, read_buffer);
rsession,
read_buffer);
} }
/** succeed */ /** succeed */
@ -1339,11 +1330,7 @@ gw_client_hangup_event(DCB *dcb)
* Return 1 in success. If the last packet is incomplete return success but * Return 1 in success. If the last packet is incomplete return success but
* leave incomplete packet to readbuf. * leave incomplete packet to readbuf.
*/ */
static int route_by_statement( static int route_by_statement(SESSION *session, GWBUF *readbuf)
ROUTER* router_instance,
ROUTER_OBJECT* router,
void* rsession,
GWBUF* readbuf)
{ {
int rc = -1; int rc = -1;
GWBUF* packetbuf; GWBUF* packetbuf;
@ -1355,7 +1342,7 @@ static int route_by_statement(
if (packetbuf != NULL) if (packetbuf != NULL)
{ {
CHK_GWBUF(packetbuf); CHK_GWBUF(packetbuf);
rc = router->routeQuery(router_instance, rsession, packetbuf); rc = SESSION_ROUTE_QUERY(session, packetbuf);
} }
else else
{ {

View File

@ -140,9 +140,6 @@ telnetd_read_event(DCB* dcb)
int n; int n;
GWBUF *head = NULL; GWBUF *head = NULL;
SESSION *session = dcb->session; SESSION *session = dcb->session;
ROUTER_OBJECT *router = session->service->router;
ROUTER *router_instance = session->service->router_instance;
void *rsession = session->router_session;
TELNETD *telnetd = (TELNETD *)dcb->protocol; TELNETD *telnetd = (TELNETD *)dcb->protocol;
char *password, *t; char *password, *t;
@ -196,7 +193,7 @@ char *password, *t;
free(password); free(password);
break; break;
case TELNETD_STATE_DATA: case TELNETD_STATE_DATA:
router->routeQuery(router_instance, rsession, head); SESSION_ROUTE_QUERY(session, head);
break; break;
} }
} }

View File

@ -40,6 +40,7 @@
* 20/05/14 Mark Riddoch Added ability to give server and service names rather * 20/05/14 Mark Riddoch Added ability to give server and service names rather
* than simply addresses * than simply addresses
* 23/05/14 Mark Riddoch Added support for developer and user modes * 23/05/14 Mark Riddoch Added support for developer and user modes
* 29/05/14 Mark Riddoch Add Filter support
* *
* @endverbatim * @endverbatim
*/ */
@ -50,6 +51,7 @@
#include <service.h> #include <service.h>
#include <session.h> #include <session.h>
#include <router.h> #include <router.h>
#include <filter.h>
#include <modules.h> #include <modules.h>
#include <atomic.h> #include <atomic.h>
#include <server.h> #include <server.h>
@ -77,6 +79,7 @@
#define ARG_TYPE_SESSION 6 #define ARG_TYPE_SESSION 6
#define ARG_TYPE_DCB 7 #define ARG_TYPE_DCB 7
#define ARG_TYPE_MONITOR 8 #define ARG_TYPE_MONITOR 8
#define ARG_TYPE_FILTER 9
/** /**
* The subcommand structure * The subcommand structure
@ -113,6 +116,14 @@ struct subcommand showoptions[] = {
"Show the poll statistics", "Show the poll statistics",
"Show the poll statistics", "Show the poll statistics",
{0, 0, 0} }, {0, 0, 0} },
{ "filter", 0, dprintFilter,
"Show details of a filter, called with a filter name",
"Show details of a filter, called with the address of a filter",
{ARG_TYPE_FILTER, 0, 0} },
{ "filters", 0, dprintAllFilters,
"Show all filters",
"Show all filters",
{0, 0, 0} },
{ "modules", 0, dprintAllModules, { "modules", 0, dprintAllModules,
"Show all currently loaded modules", "Show all currently loaded modules",
"Show all currently loaded modules", "Show all currently loaded modules",
@ -157,6 +168,10 @@ struct subcommand showoptions[] = {
* The subcommands of the list command * The subcommands of the list command
*/ */
struct subcommand listoptions[] = { struct subcommand listoptions[] = {
{ "filters", 0, dListFilters,
"List all the filters defined within MaxScale",
"List all the filters defined within MaxScale",
{0, 0, 0} },
{ "listeners", 0, dListListeners, { "listeners", 0, dListListeners,
"List all the listeners defined within MaxScale", "List all the listeners defined within MaxScale",
"List all the listeners defined within MaxScale", "List all the listeners defined within MaxScale",
@ -493,6 +508,10 @@ SERVICE *service;
if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
rval = (unsigned long)monitor_find(arg); rval = (unsigned long)monitor_find(arg);
return rval; return rval;
case ARG_TYPE_FILTER:
if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
rval = (unsigned long)filter_find(arg);
return rval;
} }
return 0; return 0;
} }