MXS-1267: Use filter template in tee
The tee filter now uses the MaxScale filter template.
This commit is contained in:
@ -183,7 +183,7 @@ protected:
|
|||||||
* class MyFilter : public maxscale::Filter<MyFilter, MyFilterSession>
|
* class MyFilter : public maxscale::Filter<MyFilter, MyFilterSession>
|
||||||
* {
|
* {
|
||||||
* public:
|
* public:
|
||||||
* static MyFilter* create(const char* zName, char** pzOptions, FILTER_PARAMETER** ppParams);
|
* static MyFilter* create(const char* zName, char** pzOptions, MXS_CONFIG_PARAMETER* ppParams);
|
||||||
*
|
*
|
||||||
* MyFilterSession* newSession(MXS_SESSION* pSession);
|
* MyFilterSession* newSession(MXS_SESSION* pSession);
|
||||||
*
|
*
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
add_library(tee SHARED tee.cc local_client.cc)
|
add_library(tee SHARED tee.cc teesession.cc local_client.cc)
|
||||||
target_link_libraries(tee maxscale-common MySQLCommon)
|
target_link_libraries(tee maxscale-common MySQLCommon)
|
||||||
set_target_properties(tee PROPERTIES VERSION "1.0.0")
|
set_target_properties(tee PROPERTIES VERSION "1.0.0")
|
||||||
install_module(tee core)
|
install_module(tee core)
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* Use of this software is governed by the Business Source License included
|
* Use of this software is governed by the Business Source License included
|
||||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
*
|
*
|
||||||
* Change Date: 2019-07-01
|
* Change Date: 2020-01-01
|
||||||
*
|
*
|
||||||
* On the date above, in accordance with the Business Source License, use
|
* On the date above, in accordance with the Business Source License, use
|
||||||
* of this software will be governed by version 2 or later of the General
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* Use of this software is governed by the Business Source License included
|
* Use of this software is governed by the Business Source License included
|
||||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
*
|
*
|
||||||
* Change Date: 2019-07-01
|
* Change Date: 2020-01-01
|
||||||
*
|
*
|
||||||
* On the date above, in accordance with the Business Source License, use
|
* On the date above, in accordance with the Business Source License, use
|
||||||
* of this software will be governed by version 2 or later of the General
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
@ -19,58 +19,42 @@
|
|||||||
|
|
||||||
#include <maxscale/cppdefs.hh>
|
#include <maxscale/cppdefs.hh>
|
||||||
|
|
||||||
#include <regex.h>
|
#include <maxscale/alloc.h>
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <maxscale/filter.h>
|
|
||||||
#include <maxscale/modinfo.h>
|
#include <maxscale/modinfo.h>
|
||||||
#include <maxscale/log_manager.h>
|
#include <maxscale/log_manager.h>
|
||||||
#include <maxscale/service.h>
|
|
||||||
#include <maxscale/alloc.h>
|
|
||||||
|
|
||||||
|
#include "tee.hh"
|
||||||
#include "local_client.hh"
|
#include "local_client.hh"
|
||||||
|
#include "teesession.hh"
|
||||||
/**
|
|
||||||
* The instance structure for the TEE filter - this holds the configuration
|
|
||||||
* information for the filter.
|
|
||||||
*/
|
|
||||||
struct Tee
|
|
||||||
{
|
|
||||||
SERVICE *service; /* The service to duplicate requests to */
|
|
||||||
char *source; /* The source of the client connection */
|
|
||||||
char *user; /* The user name to filter on */
|
|
||||||
char *match; /* Optional text to match against */
|
|
||||||
regex_t re; /* Compiled regex text */
|
|
||||||
char *nomatch; /* Optional text to match against for exclusion */
|
|
||||||
regex_t nore; /* Compiled regex nomatch text */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The session structure for this TEE filter.
|
|
||||||
* This stores the downstream filter information, such that the
|
|
||||||
* filter is able to pass the query on to the next filter (or router)
|
|
||||||
* in the chain.
|
|
||||||
*
|
|
||||||
* It also holds the file descriptor to which queries are written.
|
|
||||||
*/
|
|
||||||
struct TeeSession
|
|
||||||
{
|
|
||||||
MXS_DOWNSTREAM down; /**< The downstream filter */
|
|
||||||
MXS_UPSTREAM up; /**< The upstream filter */
|
|
||||||
bool passive; /**< Whether to clone queries */
|
|
||||||
LocalClient* client; /**< The client connection to the local service */
|
|
||||||
};
|
|
||||||
|
|
||||||
static const MXS_ENUM_VALUE option_values[] =
|
static const MXS_ENUM_VALUE option_values[] =
|
||||||
{
|
{
|
||||||
{"ignorecase", REG_ICASE},
|
{"ignorecase", REG_ICASE},
|
||||||
{"case", 0},
|
{"case", 0},
|
||||||
{"extended", REG_EXTENDED},
|
{"extended", REG_EXTENDED},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool recursive_tee_usage(std::set<std::string>& services, SERVICE* service);
|
Tee::Tee(SERVICE* service, const char* user, const char* remote,
|
||||||
|
const char* match, const char* nomatch, int cflags):
|
||||||
|
m_service(service),
|
||||||
|
m_user(user),
|
||||||
|
m_source(remote),
|
||||||
|
m_match(match),
|
||||||
|
m_nomatch(nomatch)
|
||||||
|
{
|
||||||
|
if (*match)
|
||||||
|
{
|
||||||
|
ss_debug(int rc = )regcomp(&m_re, match, cflags);
|
||||||
|
ss_dassert(rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*nomatch)
|
||||||
|
{
|
||||||
|
ss_debug(int rc = )regcomp(&m_nore, nomatch, cflags);
|
||||||
|
ss_dassert(rc == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance of the filter for a particular service
|
* Create an instance of the filter for a particular service
|
||||||
@ -82,193 +66,44 @@ bool recursive_tee_usage(std::set<std::string>& services, SERVICE* service);
|
|||||||
*
|
*
|
||||||
* @return The instance data for this new instance
|
* @return The instance data for this new instance
|
||||||
*/
|
*/
|
||||||
static MXS_FILTER *
|
Tee* Tee::create(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||||
createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
|
||||||
{
|
{
|
||||||
Tee *my_instance = new (std::nothrow) Tee;
|
Tee *my_instance = NULL;
|
||||||
|
|
||||||
if (my_instance)
|
SERVICE* service = config_get_service(params, "service");
|
||||||
|
const char* source = config_get_string(params, "source");
|
||||||
|
const char* user = config_get_string(params, "user");
|
||||||
|
const char* match = config_get_string(params, "match");
|
||||||
|
const char* nomatch = config_get_string(params, "exclude");
|
||||||
|
|
||||||
|
int cflags = config_get_enum(params, "options", option_values);
|
||||||
|
regex_t re;
|
||||||
|
regex_t nore;
|
||||||
|
|
||||||
|
if (*match && regcomp(&re, match, cflags) != 0)
|
||||||
{
|
{
|
||||||
my_instance->service = config_get_service(params, "service");
|
MXS_ERROR("Invalid regular expression '%s' for the match parameter.", match);
|
||||||
my_instance->source = config_copy_string(params, "source");
|
}
|
||||||
my_instance->user = config_copy_string(params, "user");
|
else if (*nomatch && regcomp(&nore, nomatch, cflags) != 0)
|
||||||
my_instance->match = config_copy_string(params, "match");
|
{
|
||||||
my_instance->nomatch = config_copy_string(params, "exclude");
|
MXS_ERROR("Invalid regular expression '%s' for the nomatch parameter.", nomatch);
|
||||||
|
|
||||||
int cflags = config_get_enum(params, "options", option_values);
|
if (*match)
|
||||||
|
|
||||||
if (my_instance->match && regcomp(&my_instance->re, my_instance->match, cflags))
|
|
||||||
{
|
{
|
||||||
MXS_ERROR("Invalid regular expression '%s' for the match parameter.",
|
regfree(&re);
|
||||||
my_instance->match);
|
|
||||||
MXS_FREE(my_instance->match);
|
|
||||||
MXS_FREE(my_instance->nomatch);
|
|
||||||
MXS_FREE(my_instance->source);
|
|
||||||
MXS_FREE(my_instance->user);
|
|
||||||
delete my_instance;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (my_instance->nomatch && regcomp(&my_instance->nore, my_instance->nomatch, cflags))
|
|
||||||
{
|
|
||||||
MXS_ERROR("Invalid regular expression '%s' for the nomatch paramter.",
|
|
||||||
my_instance->nomatch);
|
|
||||||
if (my_instance->match)
|
|
||||||
{
|
|
||||||
regfree(&my_instance->re);
|
|
||||||
MXS_FREE(my_instance->match);
|
|
||||||
}
|
|
||||||
MXS_FREE(my_instance->nomatch);
|
|
||||||
MXS_FREE(my_instance->source);
|
|
||||||
MXS_FREE(my_instance->user);
|
|
||||||
delete my_instance;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return (MXS_FILTER*) my_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a filter new session
|
|
||||||
*
|
|
||||||
* @param instance The filter instance data
|
|
||||||
* @param session The session itself
|
|
||||||
*
|
|
||||||
* @return Session specific data for this session
|
|
||||||
*/
|
|
||||||
static MXS_FILTER_SESSION* newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
|
||||||
{
|
|
||||||
std::set<std::string> services;
|
|
||||||
|
|
||||||
if (recursive_tee_usage(services, session->service))
|
|
||||||
{
|
{
|
||||||
MXS_ERROR("%s: Recursive use of tee filter in service.",
|
my_instance = new (std::nothrow) Tee(service, source, user, match, nomatch, cflags);
|
||||||
session->service->name);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TeeSession* my_session = new (std::nothrow) TeeSession;
|
return my_instance;
|
||||||
|
|
||||||
if (my_session)
|
|
||||||
{
|
|
||||||
Tee *my_instance = reinterpret_cast<Tee*>(instance);
|
|
||||||
const char* remote = session_get_remote(session);
|
|
||||||
const char* user = session_get_user(session);
|
|
||||||
|
|
||||||
if ((my_instance->source && remote && strcmp(remote, my_instance->source) != 0) ||
|
|
||||||
(my_instance->user && user && strcmp(user, my_instance->user) != 0))
|
|
||||||
{
|
|
||||||
my_session->passive = true;
|
|
||||||
my_session->client = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
my_session->client = LocalClient::create(session, my_instance->service);
|
|
||||||
my_session->passive = false;
|
|
||||||
|
|
||||||
if (my_session->client == NULL)
|
|
||||||
{
|
|
||||||
delete my_session;
|
|
||||||
my_session = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return reinterpret_cast<MXS_FILTER_SESSION*>(my_session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
TeeSession* Tee::newSession(MXS_SESSION* pSession)
|
||||||
* Close the filter session
|
|
||||||
*
|
|
||||||
* @param instance The filter instance data
|
|
||||||
* @param session The session being closed
|
|
||||||
*/
|
|
||||||
static void closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
|
||||||
{
|
{
|
||||||
}
|
return TeeSession::create(this, pSession);
|
||||||
|
|
||||||
/**
|
|
||||||
* Free the memory associated with the session
|
|
||||||
*
|
|
||||||
* @param instance The filter instance
|
|
||||||
* @param session The filter session
|
|
||||||
*/
|
|
||||||
static void freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
|
||||||
{
|
|
||||||
TeeSession *my_session = reinterpret_cast<TeeSession*>(session);
|
|
||||||
delete my_session->client;
|
|
||||||
delete my_session;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the downstream filter or router to which queries will be
|
|
||||||
* passed from this filter.
|
|
||||||
*
|
|
||||||
* @param instance The filter instance data
|
|
||||||
* @param session The filter session
|
|
||||||
* @param downstream The downstream filter or router.
|
|
||||||
*/
|
|
||||||
static void setDownstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_DOWNSTREAM *downstream)
|
|
||||||
{
|
|
||||||
TeeSession *my_session = reinterpret_cast<TeeSession*>(session);
|
|
||||||
my_session->down = *downstream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the downstream filter or router to which queries will be
|
|
||||||
* passed from this filter.
|
|
||||||
*
|
|
||||||
* @param instance The filter instance data
|
|
||||||
* @param session The filter session
|
|
||||||
* @param downstream The downstream filter or router.
|
|
||||||
*/
|
|
||||||
static void setUpstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_UPSTREAM *upstream)
|
|
||||||
{
|
|
||||||
TeeSession *my_session = reinterpret_cast<TeeSession*>(session);
|
|
||||||
my_session->up = *upstream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Route a query
|
|
||||||
*
|
|
||||||
* @param instance Filter instance
|
|
||||||
* @param session Filter session
|
|
||||||
* @param queue The query itself
|
|
||||||
*
|
|
||||||
* @retrn 1 on success, 0 on failure
|
|
||||||
*/
|
|
||||||
static int routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
|
||||||
{
|
|
||||||
TeeSession *my_session = reinterpret_cast<TeeSession*>(session);
|
|
||||||
|
|
||||||
int rval = my_session->down.routeQuery(my_session->down.instance,
|
|
||||||
my_session->down.session,
|
|
||||||
queue);
|
|
||||||
|
|
||||||
my_session->client->queue_query(queue);
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The clientReply entry point. This is passed the response buffer
|
|
||||||
* to which the filter should be applied. Once processed the
|
|
||||||
* query is passed to the upstream component
|
|
||||||
* (filter or router) in the filter chain.
|
|
||||||
*
|
|
||||||
* @param instance The filter instance data
|
|
||||||
* @param session The filter session
|
|
||||||
* @param reply The response data
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
clientReply(MXS_FILTER* instance, MXS_FILTER_SESSION *session, GWBUF *reply)
|
|
||||||
{
|
|
||||||
TeeSession *my_session = reinterpret_cast<TeeSession*>(session);
|
|
||||||
|
|
||||||
return my_session->up.clientReply(my_session->up.instance,
|
|
||||||
my_session->up.session,
|
|
||||||
reply);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -282,32 +117,29 @@ clientReply(MXS_FILTER* instance, MXS_FILTER_SESSION *session, GWBUF *reply)
|
|||||||
* @param fsession Filter session, may be NULL
|
* @param fsession Filter session, may be NULL
|
||||||
* @param dcb The DCB for diagnostic output
|
* @param dcb The DCB for diagnostic output
|
||||||
*/
|
*/
|
||||||
static void
|
void Tee::diagnostics(DCB *dcb)
|
||||||
diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb)
|
|
||||||
{
|
{
|
||||||
Tee *my_instance = reinterpret_cast<Tee*>(instance);
|
if (m_source.length())
|
||||||
|
|
||||||
if (my_instance->source)
|
|
||||||
{
|
{
|
||||||
dcb_printf(dcb, "\t\tLimit to connections from %s\n",
|
dcb_printf(dcb, "\t\tLimit to connections from %s\n",
|
||||||
my_instance->source);
|
m_source.c_str());
|
||||||
}
|
}
|
||||||
dcb_printf(dcb, "\t\tDuplicate statements to service %s\n",
|
dcb_printf(dcb, "\t\tDuplicate statements to service %s\n",
|
||||||
my_instance->service->name);
|
m_service->name);
|
||||||
if (my_instance->user)
|
if (m_user.length())
|
||||||
{
|
{
|
||||||
dcb_printf(dcb, "\t\tLimit to user %s\n",
|
dcb_printf(dcb, "\t\tLimit to user %s\n",
|
||||||
my_instance->user);
|
m_user.c_str());
|
||||||
}
|
}
|
||||||
if (my_instance->match)
|
if (m_match.length())
|
||||||
{
|
{
|
||||||
dcb_printf(dcb, "\t\tInclude queries that match %s\n",
|
dcb_printf(dcb, "\t\tInclude queries that match %s\n",
|
||||||
my_instance->match);
|
m_match.c_str());
|
||||||
}
|
}
|
||||||
if (my_instance->nomatch)
|
if (m_nomatch.c_str())
|
||||||
{
|
{
|
||||||
dcb_printf(dcb, "\t\tExclude queries that match %s\n",
|
dcb_printf(dcb, "\t\tExclude queries that match %s\n",
|
||||||
my_instance->nomatch);
|
m_nomatch.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,87 +153,35 @@ diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb)
|
|||||||
* @param instance The filter instance
|
* @param instance The filter instance
|
||||||
* @param fsession Filter session, may be NULL
|
* @param fsession Filter session, may be NULL
|
||||||
*/
|
*/
|
||||||
static json_t* diagnostic_json(const MXS_FILTER *instance, const MXS_FILTER_SESSION *fsession)
|
json_t* Tee::diagnostics_json() const
|
||||||
{
|
{
|
||||||
const Tee *my_instance = reinterpret_cast<const Tee*>(instance);
|
|
||||||
|
|
||||||
json_t* rval = json_object();
|
json_t* rval = json_object();
|
||||||
|
|
||||||
if (my_instance->source)
|
if (m_source.length())
|
||||||
{
|
{
|
||||||
json_object_set_new(rval, "source", json_string(my_instance->source));
|
json_object_set_new(rval, "source", json_string(m_source.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object_set_new(rval, "service", json_string(my_instance->service->name));
|
json_object_set_new(rval, "service", json_string(m_service->name));
|
||||||
|
|
||||||
if (my_instance->user)
|
if (m_user.length())
|
||||||
{
|
{
|
||||||
json_object_set_new(rval, "user", json_string(my_instance->user));
|
json_object_set_new(rval, "user", json_string(m_user.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (my_instance->match)
|
if (m_match.length())
|
||||||
{
|
{
|
||||||
json_object_set_new(rval, "match", json_string(my_instance->match));
|
json_object_set_new(rval, "match", json_string(m_match.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (my_instance->nomatch)
|
if (m_nomatch.length())
|
||||||
{
|
{
|
||||||
json_object_set_new(rval, "exclude", json_string(my_instance->nomatch));
|
json_object_set_new(rval, "exclude", json_string(m_nomatch.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Capability routine.
|
|
||||||
*
|
|
||||||
* @return The capabilities of the filter.
|
|
||||||
*/
|
|
||||||
static uint64_t getCapabilities(MXS_FILTER* instance)
|
|
||||||
{
|
|
||||||
return RCAP_TYPE_CONTIGUOUS_INPUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detect loops in the filter chain.
|
|
||||||
*/
|
|
||||||
bool recursive_tee_usage(std::set<std::string>& services, SERVICE* service)
|
|
||||||
{
|
|
||||||
if (!services.insert(service->name).second)
|
|
||||||
{
|
|
||||||
/** The service name was already in the set */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < service->n_filters; i++)
|
|
||||||
{
|
|
||||||
const char* module = filter_def_get_module_name(service->filters[i]);
|
|
||||||
|
|
||||||
if (strcmp(module, "tee") == 0)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Found a Tee filter, recurse down its path
|
|
||||||
* if the service name isn't already in the hashtable.
|
|
||||||
*/
|
|
||||||
Tee* inst = (Tee*)filter_def_get_instance(service->filters[i]);
|
|
||||||
|
|
||||||
if (inst == NULL)
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* This tee instance hasn't been initialized yet and full
|
|
||||||
* resolution of recursion cannot be done now.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
else if (recursive_tee_usage(services, inst->service))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MXS_BEGIN_DECLS
|
MXS_BEGIN_DECLS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -414,21 +194,6 @@ MXS_BEGIN_DECLS
|
|||||||
*/
|
*/
|
||||||
MXS_MODULE* MXS_CREATE_MODULE()
|
MXS_MODULE* MXS_CREATE_MODULE()
|
||||||
{
|
{
|
||||||
static MXS_FILTER_OBJECT MyObject =
|
|
||||||
{
|
|
||||||
createInstance,
|
|
||||||
newSession,
|
|
||||||
closeSession,
|
|
||||||
freeSession,
|
|
||||||
setDownstream,
|
|
||||||
setUpstream,
|
|
||||||
routeQuery,
|
|
||||||
clientReply,
|
|
||||||
diagnostic,
|
|
||||||
diagnostic_json,
|
|
||||||
getCapabilities,
|
|
||||||
NULL, // No destroyInstance
|
|
||||||
};
|
|
||||||
|
|
||||||
static MXS_MODULE info =
|
static MXS_MODULE info =
|
||||||
{
|
{
|
||||||
@ -436,9 +201,9 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
|||||||
MXS_MODULE_GA,
|
MXS_MODULE_GA,
|
||||||
MXS_FILTER_VERSION,
|
MXS_FILTER_VERSION,
|
||||||
"A tee piece in the filter plumbing",
|
"A tee piece in the filter plumbing",
|
||||||
"V1.0.0",
|
"V1.1.0",
|
||||||
RCAP_TYPE_CONTIGUOUS_INPUT,
|
RCAP_TYPE_CONTIGUOUS_INPUT,
|
||||||
&MyObject,
|
&Tee::s_object,
|
||||||
NULL, /* Process init. */
|
NULL, /* Process init. */
|
||||||
NULL, /* Process finish. */
|
NULL, /* Process finish. */
|
||||||
NULL, /* Thread init. */
|
NULL, /* Thread init. */
|
||||||
|
71
server/modules/filter/tee/tee.hh
Normal file
71
server/modules/filter/tee/tee.hh
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <maxscale/cppdefs.hh>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
#include <maxscale/filter.hh>
|
||||||
|
#include <maxscale/service.h>
|
||||||
|
|
||||||
|
#include "teesession.hh"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance structure for the TEE filter - this holds the configuration
|
||||||
|
* information for the filter.
|
||||||
|
*/
|
||||||
|
class Tee: public mxs::Filter<Tee, TeeSession>
|
||||||
|
{
|
||||||
|
Tee(const Tee&);
|
||||||
|
const Tee& operator=(const Tee&);
|
||||||
|
public:
|
||||||
|
|
||||||
|
static Tee* create(const char* zName, char** pzOptions, MXS_CONFIG_PARAMETER* ppParams);
|
||||||
|
TeeSession* newSession(MXS_SESSION* session);
|
||||||
|
void diagnostics(DCB* pDcb);
|
||||||
|
json_t* diagnostics_json() const;
|
||||||
|
|
||||||
|
uint64_t getCapabilities()
|
||||||
|
{
|
||||||
|
return RCAP_TYPE_CONTIGUOUS_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool user_matches(const char* user)const
|
||||||
|
{
|
||||||
|
return m_user.length() == 0 || strcmp(user, m_user.c_str()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remote_matches(const char* remote)const
|
||||||
|
{
|
||||||
|
return m_source.length() == 0 || strcmp(remote, m_source.c_str()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVICE* get_service() const
|
||||||
|
{
|
||||||
|
return m_service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Tee(SERVICE* service, const char* user, const char* remote,
|
||||||
|
const char* match, const char* nomatch, int cflags);
|
||||||
|
|
||||||
|
SERVICE* m_service;
|
||||||
|
std::string m_user; /* The user name to filter on */
|
||||||
|
std::string m_source; /* The source of the client connection */
|
||||||
|
std::string m_match; /* Optional text to match against */
|
||||||
|
std::string m_nomatch; /* Optional text to match against for exclusion */
|
||||||
|
regex_t m_re; /* Compiled regex text */
|
||||||
|
regex_t m_nore; /* Compiled regex nomatch text */
|
||||||
|
};
|
117
server/modules/filter/tee/teesession.cc
Normal file
117
server/modules/filter/tee/teesession.cc
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "teesession.hh"
|
||||||
|
#include "tee.hh"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect loops in the filter chain.
|
||||||
|
*/
|
||||||
|
bool recursive_tee_usage(std::set<std::string>& services, SERVICE* service)
|
||||||
|
{
|
||||||
|
if (!services.insert(service->name).second)
|
||||||
|
{
|
||||||
|
/** The service name was already in the set */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < service->n_filters; i++)
|
||||||
|
{
|
||||||
|
const char* module = filter_def_get_module_name(service->filters[i]);
|
||||||
|
|
||||||
|
if (strcmp(module, "tee") == 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Found a Tee filter, recurse down its path
|
||||||
|
* if the service name isn't already in the hashtable.
|
||||||
|
*/
|
||||||
|
Tee* inst = (Tee*)filter_def_get_instance(service->filters[i]);
|
||||||
|
|
||||||
|
if (inst == NULL)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This tee instance hasn't been initialized yet and full
|
||||||
|
* resolution of recursion cannot be done now.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else if (recursive_tee_usage(services, inst->get_service()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeeSession::TeeSession(MXS_SESSION* session, LocalClient* client):
|
||||||
|
mxs::FilterSession(session),
|
||||||
|
m_client(client)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TeeSession* TeeSession::create(Tee* my_instance, MXS_SESSION* session)
|
||||||
|
{
|
||||||
|
std::set<std::string> services;
|
||||||
|
|
||||||
|
if (recursive_tee_usage(services, my_instance->get_service()))
|
||||||
|
{
|
||||||
|
MXS_ERROR("%s: Recursive use of tee filter in service.",
|
||||||
|
session->service->name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalClient* client = NULL;
|
||||||
|
|
||||||
|
if (my_instance->user_matches(session_get_user(session)) &&
|
||||||
|
my_instance->remote_matches(session_get_remote(session)))
|
||||||
|
{
|
||||||
|
if ((client = LocalClient::create(session, my_instance->get_service())) == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new (std::nothrow) TeeSession(session, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
TeeSession::~TeeSession()
|
||||||
|
{
|
||||||
|
delete m_client;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TeeSession::close()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int TeeSession::routeQuery(GWBUF* queue)
|
||||||
|
{
|
||||||
|
if (m_client)
|
||||||
|
{
|
||||||
|
m_client->queue_query(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mxs::FilterSession::routeQuery(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TeeSession::diagnostics(DCB *pDcb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t* TeeSession::diagnostics_json() const
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
43
server/modules/filter/tee/teesession.hh
Normal file
43
server/modules/filter/tee/teesession.hh
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <maxscale/cppdefs.hh>
|
||||||
|
|
||||||
|
#include <maxscale/filter.hh>
|
||||||
|
|
||||||
|
#include "local_client.hh"
|
||||||
|
|
||||||
|
class Tee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Tee session
|
||||||
|
*/
|
||||||
|
class TeeSession: public mxs::FilterSession
|
||||||
|
{
|
||||||
|
TeeSession(const TeeSession&);
|
||||||
|
const TeeSession& operator=(const TeeSession&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~TeeSession();
|
||||||
|
static TeeSession* create(Tee* my_instance, MXS_SESSION* session);
|
||||||
|
|
||||||
|
void close();
|
||||||
|
int routeQuery(GWBUF* pPacket);
|
||||||
|
void diagnostics(DCB *pDcb);
|
||||||
|
json_t* diagnostics_json() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TeeSession(MXS_SESSION* session, LocalClient* client);
|
||||||
|
LocalClient* m_client; /**< The client connection to the local service */
|
||||||
|
};
|
Reference in New Issue
Block a user