diff --git a/include/maxscale/cpp.hh b/include/maxscale/cpp.hh new file mode 100644 index 000000000..47348b798 --- /dev/null +++ b/include/maxscale/cpp.hh @@ -0,0 +1,65 @@ +#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/bsl. + * + * Change Date: 2019-07-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. + */ + +/** + * @file cpp.hh + * + * Common stuff for C++ code. + */ + +#if !defined(__cplusplus) +#error This file is only to be included by C++ code. +#endif + +#include +#include +#include +#include + +/** + * All classes of MaxScale are defined in the namespace @c maxscale. + * + * Third party plugins should not place any definitions inside the namespace + * to avoid any name clashes in the future. An exception are template + * specializations. + */ +namespace maxscale +{ +} + +/** + * Shorthand for the @c maxscale namespace. + */ +namespace mxs = maxscale; + +/** + * If a statement is placed inside this macro, then no exceptions will + * escape. Typical use-case is for preventing exceptions to escape across + * a C-API. + * + * @code{.cpp} + * + * void* cAPI() + * { + * void* rv = NULL; + * MXS_EXCEPTION_GUARD(rv = new Something); + * return rv; + * } + * @endcode + */ +#define MXS_EXCEPTION_GUARD(statement)\ + do { try { statement; }\ + catch (const std::bad_alloc&) { MXS_OOM(); }\ + catch (const std::exception& x) { MXS_ERROR("Caught standard exception: %s", x.what()); }\ + catch (...) { MXS_ERROR("Caught unknown exception."); } } while (false) diff --git a/include/maxscale/filter.hh b/include/maxscale/filter.hh new file mode 100644 index 000000000..5c2e935cb --- /dev/null +++ b/include/maxscale/filter.hh @@ -0,0 +1,321 @@ +#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/bsl. + * + * Change Date: 2019-07-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 +#include +#include + +namespace maxscale +{ + +/** + * @class FilterSession filter.hh + * + * FilterSession is a base class for filter sessions. A concrete filter session + * class should be derived from this class and override all relevant functions. + * + * Note that even though this class is intended to be derived from, no functions + * are virtual. That is by design, as the class will be used in a context where + * the concrete class is known. That is, there is no need for the virtual mechanism. + */ +class FilterSession +{ +public: + /** + * @class Downstream + * + * An instance of this class represents a component following a filter. + */ + class Downstream + { + public: + Downstream() + { + m_data.instance = NULL; + m_data.session = NULL; + m_data.routeQuery = NULL; + } + + Downstream(const DOWNSTREAM& down) + : m_data(down) + {} + + /** + * Function for sending a packet from the client to the next component + * in the routing chain towards the backend. + * + * @param pPacket A packet to be delivered towards the backend. + * + * @return Whatever the following component returns. + */ + int routeQuery(GWBUF* pPacket) + { + return m_data.routeQuery(m_data.instance, m_data.session, pPacket); + } + + DOWNSTREAM m_data; + }; + + class Upstream + { + public: + /** + * @class Upstream + * + * An instance of this class represents a component preceeding a filter. + */ + Upstream() + { + m_data.instance = NULL; + m_data.session = NULL; + m_data.clientReply = NULL; + } + + Upstream(const UPSTREAM& up) + : m_data(up) + {} + + /** + * Function for sending a packet from the backend to the next component + * in the routing chain towards the client. + * + * @param pPacket A packet to be delivered towards the backend. + * + * @return Whatever the following component returns. + */ + int clientReply(GWBUF* pPacket) + { + return m_data.clientReply(m_data.instance, m_data.session, pPacket); + } + + UPSTREAM m_data; + }; + + /** + * The FilterSession instance will be deleted when a client session + * has terminated. Will be called only after @c close() has been called. + */ + ~FilterSession(); + + /** + * Called when a client session has been closed. + */ + void close(); + + /** + * Called for setting the component following this filter session. + * + * @param down The component following this filter. + */ + void setDownstream(const Downstream& down); + + /** + * Called for setting the component preceeding this filter session. + * + * @param up The component preceeding this filter. + */ + void setUpstream(const Upstream& up); + + /** + * Called when a packet being is routed to the backend. The filter should + * forward the packet to the downstream component. + * + * @param pPacket A client packet. + */ + int routeQuery(GWBUF* pPacket); + + /** + * Called when a packet is routed to the client. The filter should + * forward the packet to the upstream component. + * + * @param pPacket A client packet. + */ + int clientReply(GWBUF* pPacket); + + /** + * Called for obtaining diagnostics about the filter session. + * + * @param pDcb The dcb where the diagnostics should be written. + */ + void diagnostics(DCB *pDcb); + +protected: + FilterSession(SESSION* pSession); + +protected: + SESSION* m_pSession; /*< The SESSION this filter session is associated with. */ + Downstream m_down; /*< The downstream component. */ + Upstream m_up; /*< The upstream component. */ +}; + + +/** + * @class Filter filter.hh + * + * An instantiation of the Filter template is used for creating a filter. + * Filter is an example of the "Curiously recurring template pattern" + * https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern + * that is used for compile time polymorfism. + * + * The typical way for using the template is as follows: + * + * @code + * class MyFilterSession : public maxscale::FilterSession + * { + * // Override the relevant functions. + * }; + * + * class MyFilter : public maxscale::Filter + * { + * public: + * static MyFilter* create(const char* zName, char** pzOptions, FILTER_PARAMETER** ppParams); + * + * MyFilterSession* newSession(SESSION* pSession); + * + * static uint64_t getCapabilities(); + * }; + * @endcode + * + * The concrete filter class must implement the methods @c create, @c newSession and + * @c getCapabilities, with the prototypes as shown above. + * + * The plugin function @c GetModuleObject is then implemented as follows: + * + * @code + * extern "C" FILTER_OBJECT *GetModuleObject() + * { + * return &MyFilter::s_object; + * }; + * @endcode + */ +template +class Filter +{ +public: + static FILTER* createInstance(const char* zName, char** pzOptions, FILTER_PARAMETER** ppParams) + { + FilterType* pFilter = NULL; + + MXS_EXCEPTION_GUARD(pFilter = FilterType::create(zName, pzOptions, ppParams)); + + return reinterpret_cast(pFilter); + } + + static void* newSession(FILTER* pInstance, SESSION* pSession) + { + FilterType* pFilter = reinterpret_cast(pInstance); + void* pFilterSession; + + MXS_EXCEPTION_GUARD(pFilterSession = pFilter->newSession(pSession)); + + return pFilterSession; + } + + static void closeSession(FILTER*, void* pData) + { + FilterSessionType* pFilterSession = reinterpret_cast(pData); + + MXS_EXCEPTION_GUARD(pFilterSession->close()); + } + + static void freeSession(FILTER*, void* pData) + { + FilterSessionType* pFilterSession = reinterpret_cast(pData); + + MXS_EXCEPTION_GUARD(delete pFilterSession); + } + + static void setDownstream(FILTER*, void* pData, DOWNSTREAM* pDownstream) + { + FilterSessionType* pFilterSession = reinterpret_cast(pData); + + typename FilterSessionType::Downstream down(*pDownstream); + + MXS_EXCEPTION_GUARD(pFilterSession->setDownstream(down)); + } + + static void setUpstream(FILTER* pInstance, void* pData, UPSTREAM* pUpstream) + { + FilterSessionType* pFilterSession = reinterpret_cast(pData); + + typename FilterSessionType::Upstream up(*pUpstream); + + MXS_EXCEPTION_GUARD(pFilterSession->setUpstream(up)); + } + + static int routeQuery(FILTER* pInstance, void* pData, GWBUF* pPacket) + { + FilterSessionType* pFilterSession = reinterpret_cast(pData); + + int rv = 0; + MXS_EXCEPTION_GUARD(rv = pFilterSession->routeQuery(pPacket)); + + return rv; + } + + static int clientReply(FILTER* pInstance, void* pData, GWBUF* pPacket) + { + FilterSessionType* pFilterSession = reinterpret_cast(pData); + + int rv = 0; + MXS_EXCEPTION_GUARD(rv = pFilterSession->clientReply(pPacket)); + + return rv; + } + + static void diagnostics(FILTER* pInstance, void* pData, DCB* pDcb) + { + FilterSessionType* pFilterSession = reinterpret_cast(pData); + + MXS_EXCEPTION_GUARD(pFilterSession->diagnostics(pDcb)); + } + + static uint64_t getCapabilities(void) + { + uint64_t rv = 0; + + MXS_EXCEPTION_GUARD(rv = FilterType::getCapabilities()); + + return rv; + } + + static void destroyInstance(FILTER* pInstance) + { + FilterType* pFilter = reinterpret_cast(pInstance); + + MXS_EXCEPTION_GUARD(delete pFilter); + } + + static FILTER_OBJECT s_object; +}; + + +template +FILTER_OBJECT Filter::s_object = +{ + &Filter::createInstance, + &Filter::newSession, + &Filter::closeSession, + &Filter::freeSession, + &Filter::setDownstream, + &Filter::setUpstream, + &Filter::routeQuery, + &Filter::clientReply, + &Filter::diagnostics, + &Filter::getCapabilities, + &Filter::destroyInstance, +}; + + +} diff --git a/include/maxscale/spinlock.hh b/include/maxscale/spinlock.hh new file mode 100644 index 000000000..102b76cf4 --- /dev/null +++ b/include/maxscale/spinlock.hh @@ -0,0 +1,122 @@ +#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/bsl. + * + * Change Date: 2019-07-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 +#include +#include + +namespace maxscale +{ + +class SpinLockGuard; + +/** + * @class SpinLock spinlock.hh + * + * The class SpinLock is a simple class that wraps a regular SPINLOCK + * and provides equivalent functionality. + */ +class SpinLock +{ +public: + friend class SpinLockGuard; + + /** + * Creates a spinlock. + */ + SpinLock() + { + spinlock_init(&m_lock); + } + + ~SpinLock() + { + } + + /** + * Acquires the spinlock. + */ + void acquire() + { + spinlock_acquire(&m_lock); + } + + /** + * Releases the spinlock. + */ + void release() + { + spinlock_release(&m_lock); + } + +private: + SpinLock(const SpinLock&) /* = delete */; + SpinLock& operator = (const SpinLock&) /* = delete */; + +private: + SPINLOCK m_lock; +}; + +/** + * @class SpinLockGuard spinlock.hh + * + * The class SpinLockGuard is a spinlock wrapper that provides a RAII-style + * mechanism for owning a spinlock for the duration of a scoped block. When + * a SpinLockGuard object is created, it attempts to take ownership of the + * spinlock it is given. When control leaves the scope in which the + * SpinLockGuard object was created, the SpinLockGuard is destructed and the + * mutex is released. + */ +class SpinLockGuard +{ +public: + /** + * Creates the guard and locks the provided lock. + * + * @param lock The spinlock to lock. + */ + SpinLockGuard(SPINLOCK& lock) + : m_lock(lock) + { + spinlock_acquire(&m_lock); + } + + /** + * Creates the guard and locks the provided lock. + * + * @param lock The spinlock to lock. + */ + SpinLockGuard(SpinLock& lock) + : m_lock(lock.m_lock) + { + spinlock_acquire(&m_lock); + } + + /** + * Destroys the guard and unlocks the lock that was provided when + * the guard was created. + */ + ~SpinLockGuard() + { + spinlock_release(&m_lock); + } + +private: + SpinLockGuard(const SpinLockGuard&) /* = delete */; + SpinLockGuard& operator = (const SpinLockGuard&) /* = delete */; + + SPINLOCK& m_lock; +}; + +} diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 9414df47e..dadaeb5d9 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c config_runtime.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c modulecmd.c ) +add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c config_runtime.c dcb.c filter.c filter.cc externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c modulecmd.c ) target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl pthread crypt dl crypto inih z rt m stdc++) diff --git a/server/core/filter.cc b/server/core/filter.cc new file mode 100644 index 000000000..604540e3e --- /dev/null +++ b/server/core/filter.cc @@ -0,0 +1,61 @@ +/* + * 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/bsl. + * + * Change Date: 2019-07-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 + +namespace maxscale +{ + +// +// FilterSession +// + +FilterSession::FilterSession(SESSION* pSession) + : m_pSession(pSession) +{ +} + +FilterSession::~FilterSession() +{ +} + +void FilterSession::close() +{ +} + +void FilterSession::setDownstream(const Downstream& down) +{ + m_down = down; +} + +void FilterSession::setUpstream(const Upstream& up) +{ + m_up = up; +} + +int FilterSession::routeQuery(GWBUF* pPacket) +{ + return m_down.routeQuery(pPacket); +} + +int FilterSession::clientReply(GWBUF* pPacket) +{ + return m_up.clientReply(pPacket); +} + +void FilterSession::diagnostics(DCB* pDcb) +{ +} + +} +