MXS-2196: Move listener functionality into member functions

Moved most of the listener related processing inside the SERV_LISTENER (to
be renamed into Listener) class.
This commit is contained in:
Markus Mäkelä 2018-11-29 17:32:25 +02:00
parent 67626d6c32
commit 8c847ec10d
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
2 changed files with 383 additions and 150 deletions

View File

@ -14,6 +14,7 @@
#include <maxscale/ccdefs.hh>
#include <atomic>
#include <string>
#include <memory>
#include <vector>
@ -34,25 +35,152 @@ class SERVICE;
class SERV_LISTENER
{
public:
/**
* Creates a new listener that points to a service
*
* @param service Service where the listener points to
* @param name Name of the listener
* @param address The address where the listener listens
* @param port The port on which the listener listens
* @param protocol The protocol module to use
* @param authenticator The authenticator module to use
* @param auth_opts Options for the authenticator
* @param auth_instance The authenticator instance
* @param ssl The SSL configuration
*/
SERV_LISTENER(SERVICE* service, const std::string& name, const std::string& address, uint16_t port,
const std::string& protocol, const std::string& authenticator,
const std::string& auth_opts, void* auth_instance, SSL_LISTENER* ssl);
~SERV_LISTENER();
public:
std::string name; /**< Name of the listener */
std::string protocol; /**< Protocol module to load */
uint16_t port; /**< Port to listen on */
std::string address; /**< Address to listen with */
std::string authenticator; /**< Name of authenticator */
std::string auth_options; /**< Authenticator options */
void* auth_instance; /**< Authenticator instance */
SSL_LISTENER* ssl; /**< Structure of SSL data or NULL */
struct dcb* listener; /**< The DCB for the listener */
struct users* users; /**< The user data for this listener */
SERVICE* service; /**< The service which used by this listener */
int active; /**< True if the port has not been deleted */
SERV_LISTENER* next; /**< Next service protocol */
/**
* Start listening on the configured port
*
* @return True if the listener was able to start listening
*/
bool listen();
/**
* Stop the listener
*
* @return True if the listener was successfully stopped
*/
bool stop();
/**
* Start a stopped listener
*
* @return True if the listener was successfully started
*/
bool start();
/**
* Closes a listener
*
* This closes the network socket the listener listens on to allow immediate reuse of it. The listener
* instance can remain if there are open sessions for it.
*/
void close();
/**
* Listener name
*/
const char* name() const;
/**
* Network address the listener listens on
*/
const char* address() const;
/**
* Network port the listener listens on
*/
uint16_t port() const;
/**
* Service the listener points to
*/
SERVICE* service() const;
/**
* The authenticator module name
*/
const char* authenticator() const;
/**
* The protocol module name
*/
const char* protocol() const;
/**
* The authenticator instance
*/
void* auth_instance() const;
/**
* The state of the listener
*/
const char* state() const;
/**
* Whether the listener is active
*/
bool is_active() const;
/**
* Deactivate the listener
*
* TODO: Remove and deactivate via removal from global listener list
*/
void deactivate();
/**
* The SSL_LISTENER object
*/
SSL_LISTENER* ssl() const;
/**
* Convert to JSON
*
* @return JSON representation of the object
*/
json_t* to_json() const;
/**
* Load users for a listener
*
* @return The result from the authenticator module
*/
int load_users();
/**
* Print the users into a DCB
*
* Note: not const due to authenticator API
*
* @param dcb DCB to write into
*/
void print_users(DCB* dcb);
// Functions that are temporarily public
bool create_listener_config(const char* filename);
struct users* users() const;
void set_users(struct users* u);
private:
std::string m_name; /**< Name of the listener */
std::string m_protocol; /**< Protocol module to load */
uint16_t m_port; /**< Port to listen on */
std::string m_address; /**< Address to listen with */
std::string m_authenticator; /**< Name of authenticator */
std::string m_auth_options; /**< Authenticator options */
void* m_auth_instance; /**< Authenticator instance */
SSL_LISTENER* m_ssl; /**< Structure of SSL data or NULL */
struct dcb* m_listener; /**< The DCB for the listener */
struct users* m_users; /**< The user data for this listener */
SERVICE* m_service; /**< The service which used by this listener */
std::atomic<bool> m_active; /**< True if the port has not been deleted */
};
using SListener = std::shared_ptr<SERV_LISTENER>;
@ -69,15 +197,6 @@ using SListener = std::shared_ptr<SERV_LISTENER>;
*/
bool listener_serialize(const SListener& listener);
/**
* @brief Convert listener to JSON
*
* @param listener Listener to convert
*
* @return Converted listener
*/
json_t* listener_to_json(const SListener& listener);
/**
* Create a new listener
*
@ -110,34 +229,6 @@ SListener listener_alloc(SERVICE* service,
*/
void listener_free(const SListener& listener);
/**
* Destroy a listener
*
* This deactivates the listener and closes the network port it listens on. Once destroyed, the listener
* can no longer be used.
*
* @param listener Listener to destroy
*/
void listener_destroy(const SListener& listener);
/**
* Stop a listener
*
* @param listener Listener to stop
*
* @return True if listener was successfully stopped
*/
bool listener_stop(const SListener& listener);
/**
* Start a stopped listener
*
* @param listener Listener to start
*
* @return True if listener was successfully started
*/
bool listener_start(const SListener& listener);
/**
* Find a listener
*
@ -183,29 +274,3 @@ bool SSL_LISTENER_init(SSL_LISTENER* ssl);
* @param ssl SSL_LISTENER to free
*/
void SSL_LISTENER_free(SSL_LISTENER* ssl);
/**
* @brief Check if listener is active
*
* @param listener Listener to check
*
* @return True if listener is active
*/
bool listener_is_active(const SListener& listener);
/**
* @brief Modify listener active state
*
* @param listener Listener to modify
* @param active True to activate, false to disable
*/
void listener_set_active(const SListener& listener, bool active);
/**
* Get listener state as a string
*
* @param listener Listener to inspect
*
* @return State of the listener as a string
*/
const char* listener_state_to_string(const SListener& listener);

View File

@ -22,6 +22,7 @@
#include <list>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <maxscale/paths.h>
@ -33,6 +34,8 @@
#include <maxscale/service.hh>
#include <maxscale/poll.h>
#include "internal/modules.hh"
static std::list<SListener> all_listeners;
static std::mutex listener_lock;
@ -43,35 +46,34 @@ static RSA* tmp_rsa_callback(SSL* s, int is_export, int keylength);
SERV_LISTENER::SERV_LISTENER(SERVICE* service, const std::string& name, const std::string& address,
uint16_t port, const std::string& protocol, const std::string& authenticator,
const std::string& auth_opts, void* auth_instance, SSL_LISTENER* ssl)
: name(name)
, protocol(protocol)
, port(port)
, address(address)
, authenticator(authenticator)
, auth_options(auth_opts)
, auth_instance(auth_instance)
, ssl(ssl)
, listener(nullptr)
, users(nullptr)
, service(service)
, active(1)
, next(nullptr)
: m_name(name)
, m_protocol(protocol)
, m_port(port)
, m_address(address)
, m_authenticator(authenticator)
, m_auth_options(auth_opts)
, m_auth_instance(auth_instance)
, m_ssl(ssl)
, m_listener(nullptr)
, m_users(nullptr)
, m_service(service)
, m_active(1)
{
}
SERV_LISTENER::~SERV_LISTENER()
{
if (users)
if (m_users)
{
users_free(users);
users_free(m_users);
}
if (listener)
if (m_listener)
{
dcb_close(listener);
dcb_close(m_listener);
}
SSL_LISTENER_free(ssl);
SSL_LISTENER_free(m_ssl);
}
SListener listener_alloc(SERVICE* service,
@ -120,41 +122,41 @@ void listener_free(const SListener& listener)
all_listeners.remove(listener);
}
void listener_destroy(const SListener& listener)
void SERV_LISTENER::close()
{
listener_set_active(listener, false);
listener_stop(listener);
deactivate();
stop();
// TODO: This is not pretty but it works, revise when listeners are refactored. This is
// thread-safe as the listener is freed on the same thread that closes the socket.
close(listener->listener->fd);
listener->listener->fd = -1;
::close(m_listener->fd);
m_listener->fd = -1;
}
bool listener_stop(const SListener& listener)
bool SERV_LISTENER::stop()
{
bool rval = false;
mxb_assert(listener->listener);
mxb_assert(m_listener);
if (listener->listener->session->state == SESSION_STATE_LISTENER
&& poll_remove_dcb(listener->listener) == 0)
if (m_listener->session->state == SESSION_STATE_LISTENER
&& poll_remove_dcb(m_listener) == 0)
{
listener->listener->session->state = SESSION_STATE_LISTENER_STOPPED;
m_listener->session->state = SESSION_STATE_LISTENER_STOPPED;
rval = true;
}
return rval;
}
bool listener_start(const SListener& listener)
bool SERV_LISTENER::start()
{
bool rval = true;
mxb_assert(listener->listener);
mxb_assert(m_listener);
if (listener->listener->session->state == SESSION_STATE_LISTENER_STOPPED
&& poll_add_dcb(listener->listener) == 0)
if (m_listener->session->state == SESSION_STATE_LISTENER_STOPPED
&& poll_add_dcb(m_listener) == 0)
{
listener->listener->session->state = SESSION_STATE_LISTENER;
m_listener->session->state = SESSION_STATE_LISTENER;
rval = true;
}
@ -168,7 +170,7 @@ SListener listener_find(const std::string& name)
for (const auto& a : all_listeners)
{
if (listener_is_active(a) && a->name == name)
if (a->is_active() && a->name() == name)
{
rval = a;
break;
@ -185,7 +187,7 @@ std::vector<SListener> listener_find_by_service(const SERVICE* service)
for (const auto& a : all_listeners)
{
if (listener_is_active(a) && a->service == service)
if (a->is_active() && a->service() == service)
{
rval.push_back(a);
}
@ -490,7 +492,7 @@ static RSA* tmp_rsa_callback(SSL* s, int is_export, int keylength)
* @param filename Filename where configuration is written
* @return True on success, false on error
*/
static bool create_listener_config(const SListener& listener, const char* filename)
bool SERV_LISTENER::create_listener_config(const char* filename)
{
int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
@ -498,32 +500,32 @@ static bool create_listener_config(const SListener& listener, const char* filena
{
MXS_ERROR("Failed to open file '%s' when serializing listener '%s': %d, %s",
filename,
listener->name.c_str(),
m_name.c_str(),
errno,
mxs_strerror(errno));
return false;
}
// TODO: Check for return values on all of the dprintf calls
dprintf(file, "[%s]\n", listener->name.c_str());
dprintf(file, "[%s]\n", m_name.c_str());
dprintf(file, "type=listener\n");
dprintf(file, "protocol=%s\n", listener->protocol.c_str());
dprintf(file, "service=%s\n", listener->service->name);
dprintf(file, "address=%s\n", listener->address.c_str());
dprintf(file, "port=%u\n", listener->port);
dprintf(file, "authenticator=%s\n", listener->authenticator.c_str());
dprintf(file, "protocol=%s\n", m_protocol.c_str());
dprintf(file, "service=%s\n", m_service->name);
dprintf(file, "address=%s\n", m_address.c_str());
dprintf(file, "port=%u\n", m_port);
dprintf(file, "authenticator=%s\n", m_authenticator.c_str());
if (!listener->auth_options.empty())
if (!m_auth_options.empty())
{
dprintf(file, "authenticator_options=%s\n", listener->auth_options.c_str());
dprintf(file, "authenticator_options=%s\n", m_auth_options.c_str());
}
if (listener->ssl)
if (m_ssl)
{
write_ssl_config(file, listener->ssl);
write_ssl_config(file, m_ssl);
}
close(file);
::close(file);
return true;
}
@ -536,7 +538,7 @@ bool listener_serialize(const SListener& listener)
sizeof(filename),
"%s/%s.cnf.tmp",
get_config_persistdir(),
listener->name.c_str());
listener->name());
if (unlink(filename) == -1 && errno != ENOENT)
{
@ -545,7 +547,7 @@ bool listener_serialize(const SListener& listener)
errno,
mxs_strerror(errno));
}
else if (create_listener_config(listener, filename))
else if (listener->create_listener_config(filename))
{
char final_filename[PATH_MAX];
strcpy(final_filename, filename);
@ -570,35 +572,35 @@ bool listener_serialize(const SListener& listener)
return rval;
}
json_t* listener_to_json(const SListener& listener)
json_t* SERV_LISTENER::to_json() const
{
json_t* param = json_object();
json_object_set_new(param, "address", json_string(listener->address.c_str()));
json_object_set_new(param, "port", json_integer(listener->port));
json_object_set_new(param, "protocol", json_string(listener->protocol.c_str()));
json_object_set_new(param, "authenticator", json_string(listener->authenticator.c_str()));
json_object_set_new(param, "auth_options", json_string(listener->auth_options.c_str()));
json_object_set_new(param, "address", json_string(m_address.c_str()));
json_object_set_new(param, "port", json_integer(m_port));
json_object_set_new(param, "protocol", json_string(m_protocol.c_str()));
json_object_set_new(param, "authenticator", json_string(m_authenticator.c_str()));
json_object_set_new(param, "auth_options", json_string(m_auth_options.c_str()));
if (listener->ssl)
if (m_ssl)
{
json_t* ssl = json_object();
const char* ssl_method = ssl_method_type_to_string(listener->ssl->ssl_method_type);
const char* ssl_method = ssl_method_type_to_string(m_ssl->ssl_method_type);
json_object_set_new(ssl, "ssl_version", json_string(ssl_method));
json_object_set_new(ssl, "ssl_cert", json_string(listener->ssl->ssl_cert));
json_object_set_new(ssl, "ssl_ca_cert", json_string(listener->ssl->ssl_ca_cert));
json_object_set_new(ssl, "ssl_key", json_string(listener->ssl->ssl_key));
json_object_set_new(ssl, "ssl_cert", json_string(m_ssl->ssl_cert));
json_object_set_new(ssl, "ssl_ca_cert", json_string(m_ssl->ssl_ca_cert));
json_object_set_new(ssl, "ssl_key", json_string(m_ssl->ssl_key));
json_object_set_new(param, "ssl", ssl);
}
json_t* attr = json_object();
json_object_set_new(attr, CN_STATE, json_string(listener_state_to_string(listener)));
json_object_set_new(attr, CN_STATE, json_string(state()));
json_object_set_new(attr, CN_PARAMETERS, param);
if (listener->listener->authfunc.diagnostic_json)
if (m_listener->authfunc.diagnostic_json)
{
json_t* diag = listener->listener->authfunc.diagnostic_json(listener.get());
json_t* diag = m_listener->authfunc.diagnostic_json(this);
if (diag)
{
@ -607,30 +609,68 @@ json_t* listener_to_json(const SListener& listener)
}
json_t* rval = json_object();
json_object_set_new(rval, CN_ID, json_string(listener->name.c_str()));
json_object_set_new(rval, CN_ID, json_string(m_name.c_str()));
json_object_set_new(rval, CN_TYPE, json_string(CN_LISTENERS));
json_object_set_new(rval, CN_ATTRIBUTES, attr);
return rval;
}
void listener_set_active(const SListener& listener, bool active)
void SERV_LISTENER::deactivate()
{
atomic_store_int32(&listener->active, active ? 1 : 0);
m_active = false;
}
bool listener_is_active(const SListener& listener)
bool SERV_LISTENER::is_active() const
{
return atomic_load_int32(&listener->active);
return m_active;
}
const char* listener_state_to_string(const SListener& listener)
const char* SERV_LISTENER::name() const
{
mxb_assert(listener);
return m_name.c_str();
}
if (listener->listener && listener->listener->session)
const char* SERV_LISTENER::address() const
{
return m_address.c_str();
}
uint16_t SERV_LISTENER::port() const
{
return m_port;
}
SERVICE* SERV_LISTENER::service() const
{
return m_service;
}
const char* SERV_LISTENER::authenticator() const
{
return m_authenticator.c_str();
}
const char* SERV_LISTENER::protocol() const
{
return m_protocol.c_str();
}
void* SERV_LISTENER::auth_instance() const
{
return m_auth_instance;
}
SSL_LISTENER* SERV_LISTENER::ssl() const
{
return m_ssl;
}
const char* SERV_LISTENER::state() const
{
if (m_listener && m_listener->session)
{
switch (listener->listener->session->state)
switch (m_listener->session->state)
{
case SESSION_STATE_LISTENER_STOPPED:
return "Stopped";
@ -648,3 +688,131 @@ const char* listener_state_to_string(const SListener& listener)
return "Failed";
}
}
void SERV_LISTENER::print_users(DCB* dcb)
{
if (m_listener && m_listener->authfunc.diagnostic)
{
dcb_printf(dcb, "User names (%s): ", name());
m_listener->authfunc.diagnostic(dcb, this);
dcb_printf(dcb, "\n");
}
}
int SERV_LISTENER::load_users()
{
int rval = MXS_AUTH_LOADUSERS_OK;
if (m_listener && m_listener->authfunc.loadusers)
{
rval = m_listener->authfunc.loadusers(this);
}
return rval;
}
struct users* SERV_LISTENER::users() const
{
return m_users;
}
void SERV_LISTENER::set_users(struct users* u)
{
m_users = u;
}
bool SERV_LISTENER::listen()
{
m_listener = dcb_alloc(DCB_ROLE_SERVICE_LISTENER, this, m_service);
if (!m_listener)
{
MXS_ERROR("Failed to create listener for service %s.", m_service->name);
return false;
}
MXS_PROTOCOL* funcs = (MXS_PROTOCOL*)load_module(protocol(), MODULE_PROTOCOL);
if (!funcs)
{
MXS_ERROR("Unable to load protocol module %s. Listener for service %s not started.",
protocol(), m_service->name);
return false;
}
memcpy(&(m_listener->func), funcs, sizeof(MXS_PROTOCOL));
if (m_authenticator.empty())
{
m_authenticator = m_listener->func.auth_default();
}
MXS_AUTHENTICATOR* authfuncs = (MXS_AUTHENTICATOR*)load_module(authenticator(), MODULE_AUTHENTICATOR);
if (!authfuncs)
{
MXS_ERROR("Failed to load authenticator module '%s' for listener '%s'", authenticator(), name());
return false;
}
// Add protocol and authenticator capabilities from the listener
const MXS_MODULE* proto_mod = get_module(protocol(), MODULE_PROTOCOL);
const MXS_MODULE* auth_mod = get_module(authenticator(), MODULE_AUTHENTICATOR);
mxb_assert(proto_mod && auth_mod);
// Note: This isn't good: we modify the service from a listener and the service itself should do this.
m_service->capabilities |= proto_mod->module_capabilities | auth_mod->module_capabilities;
memcpy(&m_listener->authfunc, authfuncs, sizeof(MXS_AUTHENTICATOR));
/**
* Normally, we'd allocate the DCB specific authentication data. As the
* listeners aren't normal DCBs, we can skip that.
*/
std::stringstream ss;
ss << m_address << "|" << m_port;
auto config_bind = ss.str();
/** Load the authentication users before before starting the listener */
if (m_listener->authfunc.loadusers)
{
switch (m_listener->authfunc.loadusers(this))
{
case MXS_AUTH_LOADUSERS_FATAL:
MXS_ERROR("[%s] Fatal error when loading users for listener '%s', "
"service is not started.", m_service->name, name());
return 0;
case MXS_AUTH_LOADUSERS_ERROR:
MXS_WARNING("[%s] Failed to load users for listener '%s', authentication"
" might not work.", m_service->name, name());
break;
default:
break;
}
}
if (m_listener->func.listen(m_listener, &config_bind[0]))
{
m_listener->session = session_alloc(m_service, m_listener);
if (m_listener->session != NULL)
{
m_listener->session->state = SESSION_STATE_LISTENER;
}
else
{
MXS_ERROR("[%s] Failed to create listener session.", m_service->name);
}
}
else
{
MXS_ERROR("[%s] Failed to listen on %s", m_service->name, config_bind.c_str());
}
return true;
}