From 57e910ad3d8c8f8e8e23ad6b4f5c27ee1e8cb079 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Wed, 4 Jun 2014 18:34:24 +0100 Subject: [PATCH] Addition of the module utilities functions --- server/core/Makefile | 4 +- server/core/config.c | 18 ++++- server/core/filter.c | 39 +++++++++- server/core/modutil.c | 121 +++++++++++++++++++++++++++++ server/include/buffer.h | 2 + server/include/filter.h | 15 +++- server/include/modutil.h | 36 +++++++++ server/include/session.h | 11 +++ server/modules/filter/qlafilter.c | 23 +++--- server/modules/filter/testfilter.c | 8 +- 10 files changed, 252 insertions(+), 25 deletions(-) create mode 100644 server/core/modutil.c create mode 100644 server/include/modutil.h diff --git a/server/core/Makefile b/server/core/Makefile index b2722f94d..0a8de1a4a 100644 --- a/server/core/Makefile +++ b/server/core/Makefile @@ -57,7 +57,7 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \ 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 \ poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \ - monitor.c adminusers.c secrets.c filter.c + monitor.c adminusers.c secrets.c filter.c modutil.c HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \ ../include/gw.h ../modules/include/mysql_client_server_protocol.h \ @@ -65,7 +65,7 @@ HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \ ../include/modules.h ../include/poll.h ../include/config.h \ ../include/users.h ../include/hashtable.h ../include/gwbitmask.h \ ../include/adminusers.h ../include/version.h ../include/maxscale.h \ - ../include/filter.h + ../include/filter.h modutil.h OBJ=$(SRCS:.c=.o) diff --git a/server/core/config.c b/server/core/config.c index a5e7c6d25..c5a052a8e 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -388,7 +388,7 @@ int error_count = 0; obj->object))); error_count++; } - if (obj->element) + if (obj->element && options) { char *s = strtok(options, ","); while (s) @@ -397,6 +397,22 @@ int error_count = 0; s = strtok(NULL, ","); } } + if (obj->element) + { + CONFIG_PARAMETER *params = obj->parameters; + while (params) + { + if (strcmp(params->name, "module") + && strcmp(params->name, + "options")) + { + filterAddParameter(obj->element, + params->name, + params->value); + } + params = params->next; + } + } } obj = obj->next; } diff --git a/server/core/filter.c b/server/core/filter.c index bdc38a0f9..9336d6903 100644 --- a/server/core/filter.c +++ b/server/core/filter.c @@ -60,6 +60,8 @@ FILTER_DEF *filter; return NULL; filter->name = strdup(name); filter->module = strdup(module); + filter->options = NULL; + filter->parameters = NULL; spinlock_init(&filter->spin); @@ -217,8 +219,6 @@ int i; spinlock_release(&filter_spin); } - - /** * Add a router option to a service * @@ -249,6 +249,38 @@ int i; spinlock_release(&filter->spin); } +/** + * Add a router parameter to a service + * + * @param service The service to add the router option to + * @param name The parameter name + * @param value The parameter value + */ +void +filterAddParameter(FILTER_DEF *filter, char *name, char *value) +{ +int i; + + spinlock_acquire(&filter->spin); + if (filter->parameters == NULL) + { + filter->parameters = (FILTER_PARAMETER **)calloc(2, sizeof(FILTER_PARAMETER *)); + i = 0; + } + else + { + for (i = 0; filter->parameters[i]; i++) + ; + filter->parameters = (FILTER_PARAMETER **)realloc(filter->options, + (i + 2) * sizeof(FILTER_PARAMETER *)); + } + filter->parameters[i] = (FILTER_PARAMETER *)calloc(1, sizeof(FILTER_PARAMETER)); + filter->parameters[i]->name = strdup(name); + filter->parameters[i]->value = strdup(value); + filter->parameters[i+1] = NULL; + spinlock_release(&filter->spin); +} + DOWNSTREAM * filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream) { @@ -264,7 +296,8 @@ DOWNSTREAM *me; } } if (filter->filter == NULL) - filter->filter = (filter->obj->createInstance)(filter->options); + filter->filter = (filter->obj->createInstance)(filter->options, + filter->parameters); if ((me = (DOWNSTREAM *)calloc(1, sizeof(DOWNSTREAM))) == NULL) { return NULL; diff --git a/server/core/modutil.c b/server/core/modutil.c new file mode 100644 index 000000000..5e6b1a8d3 --- /dev/null +++ b/server/core/modutil.c @@ -0,0 +1,121 @@ +/* + * 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 modutil.c - Implementation of useful routines for modules + * + * @verbatim + * Revision History + * + * Date Who Description + * 04/06/14 Mark Riddoch Initial implementation + * + * @endverbatim + */ +#include + +/** + * Check if a GWBUF structure is a MySQL COM_QUERY packet + * + * @param buf Buffer to check + * @return True if GWBUF is a COM_QUERY packet + */ +int +modutil_is_SQL(GWBUF *buf) +{ +unsigned char *ptr; + + if (GWBUF_LENGTH(buf) < 5) + return 0; + ptr = GWBUF_DATA(buf); + return ptr[4] == 0x03; // COM_QUERY +} + +/** + * Extract the SQL portion of a COM_QUERY packet + * + * NB This sets *sql to point into the packet and does not + * allocate any new storage. The string pointed to by *sql is + * not NULL terminated. + * + * This routine is very simplistic and does not deal with SQL text + * that spans multiple buffers. + * + * @param buf The packet buffer + * @param sql Pointer that is set to point at the SQL data + * @param length Length of the SQL data + * @return True if the packet is a COM_QUERY packet + */ +int +modutil_extract_SQL(GWBUF *buf, char **sql, int *length) +{ +char *ptr; + + if (!modutil_is_SQL(buf)) + return 0; + ptr = GWBUF_DATA(buf); + *length = *ptr++; + *length += (*ptr++ << 8); + *length += (*ptr++ << 8); + ptr += 2; // Skip sequence id and COM_QUERY byte + *length = *length - 1; + *sql = ptr; +} + + +GWBUF * +modutil_replace_SQL(GWBUF *orig, char *sql) +{ +char *ptr; +int length, newlength; +GWBUF *addition; + + if (!modutil_is_SQL(orig)) + return 0; + ptr = GWBUF_DATA(orig); + length = *ptr++; + length += (*ptr++ << 8); + length += (*ptr++ << 8); + ptr += 2; // Skip sequence id and COM_QUERY byte + + newlength = strlen(sql); + if (length - 1 == newlength) + { + /* New SQL is the same length as old */ + memcpy(ptr, sql, newlength); + } + else if (length - 1 > newlength) + { + /* New SQL is shorter */ + memcpy(ptr, sql, newlength); + GWBUF_RTRIM(orig, (length - 1) - newlength); + } + else + { + memcpy(ptr, sql, length - 1); + addition = gwbuf_alloc(newlength - (length - 1)); + memcpy(GWBUF_DATA(addition), &sql[length - 1], newlength - (length - 1)); + ptr = GWBUF_DATA(orig); + *ptr++ = (newlength + 1) & 0xff; + *ptr++ = ((newlength + 1) >> 8) & 0xff; + *ptr++ = ((newlength + 1) >> 16) & 0xff; + orig->next = addition; + } + + return orig; +} diff --git a/server/include/buffer.h b/server/include/buffer.h index 53c81a4a6..9651031b2 100644 --- a/server/include/buffer.h +++ b/server/include/buffer.h @@ -93,6 +93,8 @@ typedef struct gwbuf { /*< Consume a number of bytes in the buffer */ #define GWBUF_CONSUME(b, bytes) (b)->start += bytes +#define GWBUF_RTRIM(b, bytes) (b)->end -= bytes + #define GWBUF_TYPE(b) (b)->gwbuf_type /*< * Function prototypes for the API to maniplate the buffers diff --git a/server/include/filter.h b/server/include/filter.h index e29ecf363..8ddcfb00d 100644 --- a/server/include/filter.h +++ b/server/include/filter.h @@ -37,6 +37,15 @@ * is to make it a void * externally. */ typedef void *FILTER; + +/** + * The structure used to pass name, value pairs to the filter instances + */ +typedef struct { + char *name; /**< Name of the parameter */ + char *value; /**< Value of the parameter */ +} FILTER_PARAMETER; + /** * @verbatim * The "module object" structure for a query router module @@ -60,7 +69,7 @@ typedef void *FILTER; * @see load_module */ typedef struct filter_object { - FILTER *(*createInstance)(char **options); + FILTER *(*createInstance)(char **options, FILTER_PARAMETER **); void *(*newSession)(FILTER *instance, SESSION *session); void (*closeSession)(FILTER *instance, void *fsession); void (*freeSession)(FILTER *instance, void *fsession); @@ -75,7 +84,6 @@ typedef struct filter_object { * file modinfo.h. */ #define FILTER_VERSION {1, 0, 0} - /** * The definition of a filter form the configuration file. * This is basically the link between a plugin to load and the @@ -85,6 +93,8 @@ typedef struct filter_def { char *name; /*< The Filter name */ char *module; /*< The module to load */ char **options; /*< The options set for this filter */ + FILTER_PARAMETER + **parameters; /*< The filter parameters */ FILTER filter; FILTER_OBJECT *obj; SPINLOCK spin; @@ -96,6 +106,7 @@ FILTER_DEF *filter_alloc(char *, char *); void filter_free(FILTER_DEF *); FILTER_DEF *filter_find(char *); void filterAddOption(FILTER_DEF *, char *); +void filterAddParameter(FILTER_DEF *, char *, char *); DOWNSTREAM *filterApply(FILTER_DEF *, SESSION *, DOWNSTREAM *); void dprintAllFilters(DCB *); void dprintFilter(DCB *, FILTER_DEF *); diff --git a/server/include/modutil.h b/server/include/modutil.h new file mode 100644 index 000000000..3dbe82132 --- /dev/null +++ b/server/include/modutil.h @@ -0,0 +1,36 @@ +#ifndef _MODUTIL_H +#define _MODUTIL_H +/* + * 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 modutil.h A set of useful routines for module writers + * + * @verbatim + * Revision History + * + * Date Who Description + * 04/06/14 Mark Riddoch Initial implementation + * + * @endverbatim + */ +#include + +extern int modutil_is_SQL(GWBUF *); +extern int modutil_extract_SQL(GWBUF *, char **, int *); +#endif diff --git a/server/include/session.h b/server/include/session.h index 6838dc1f1..f99982802 100644 --- a/server/include/session.h +++ b/server/include/session.h @@ -74,6 +74,17 @@ typedef struct { void *router_session, GWBUF *queue); } DOWNSTREAM; +/** + * The upstream element in the filter chain. This may refer to + * another filter or to the protocol implementation. + */ +typedef struct { + void *instance; + void *session; + int (*write)(void *, void *, GWBUF *); + int (*error)(void *); +} UPSTREAM; + /** * Structure used to track the filter instances and sessions of the filters * that are in use within a session. diff --git a/server/modules/filter/qlafilter.c b/server/modules/filter/qlafilter.c index 7af66fe8c..8cf7fb433 100644 --- a/server/modules/filter/qlafilter.c +++ b/server/modules/filter/qlafilter.c @@ -32,6 +32,7 @@ #include #include #include +#include #include MODULE_INFO info = { @@ -46,7 +47,7 @@ static char *version_str = "V1.0.0"; /* * The filter entry points */ -static FILTER *createInstance(char **options); +static FILTER *createInstance(char **options, FILTER_PARAMETER **); static void *newSession(FILTER *instance, SESSION *session); static void closeSession(FILTER *instance, void *session); static void freeSession(FILTER *instance, void *session); @@ -135,7 +136,7 @@ GetModuleObject() * @return The instance data for this new instance */ static FILTER * -createInstance(char **options) +createInstance(char **options, FILTER_PARAMETER **params) { QLA_INSTANCE *my_instance; @@ -178,7 +179,7 @@ QLA_SESSION *my_session; my_instance->sessions); my_instance->sessions++; my_session->fd = open(my_session->filename, - O_WRONLY|O_CREAT, 0666); + O_WRONLY|O_CREAT|O_TRUNC, 0666); } return my_session; @@ -235,7 +236,7 @@ QLA_SESSION *my_session = (QLA_SESSION *)session; /** * The routeQuery entry point. This is passed the query buffer * to which the filter should be applied. Once applied the - * query shoudl normally be passed to the downstream component + * query should normally be passed to the downstream component * (filter or router) in the filter chain. * * @param instance The filter instance data @@ -246,18 +247,12 @@ static int routeQuery(FILTER *instance, void *session, GWBUF *queue) { QLA_SESSION *my_session = (QLA_SESSION *)session; -unsigned char *ptr; -unsigned int length; +char *ptr; +int length; - /* Find the text of the query and write to the file */ - ptr = GWBUF_DATA(queue); - length = *ptr++; - length += (*ptr++ << 8); - length += (*ptr++ << 8); - ptr++; // Skip sequence id - if (*ptr++ == 0x03 && my_session->fd != -1) // COM_QUERY + if (modutil_extract_SQL(queue, &ptr, &length)) { - write(my_session->fd, ptr, length - 1); + write(my_session->fd, ptr, length); write(my_session->fd, "\n", 1); } diff --git a/server/modules/filter/testfilter.c b/server/modules/filter/testfilter.c index 8553ab42b..270dbd1cb 100644 --- a/server/modules/filter/testfilter.c +++ b/server/modules/filter/testfilter.c @@ -18,6 +18,7 @@ #include #include #include +#include /** * testfilter.c - a very simple test filter. @@ -38,7 +39,7 @@ MODULE_INFO info = { static char *version_str = "V1.0.0"; -static FILTER *createInstance(char **options); +static FILTER *createInstance(char **options, FILTER_PARAMETER **params); static void *newSession(FILTER *instance, SESSION *session); static void closeSession(FILTER *instance, void *session); static void freeSession(FILTER *instance, void *session); @@ -115,7 +116,7 @@ GetModuleObject() * @return The instance data for this new instance */ static FILTER * -createInstance(char **options) +createInstance(char **options, FILTER_PARAMETER **params) { TEST_INSTANCE *my_instance; @@ -201,7 +202,8 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) { TEST_SESSION *my_session = (TEST_SESSION *)session; - my_session->count++; + if (modutil_is_SQL(queue)) + my_session->count++; return my_session->down.routeQuery(my_session->down.instance, my_session->down.session, queue); }