Serialize created listeners to disk

The created listeners are now stored to disk like created servers
are. This allows them to be used even after a restart.

Currently, the listeners cannot be deleted and need to be manually
removed.
This commit is contained in:
Markus Makela 2016-11-24 11:53:40 +02:00
parent dd63253261
commit e31afa28e4
7 changed files with 176 additions and 33 deletions

View File

@ -49,6 +49,7 @@ typedef struct servlistener
unsigned short port; /**< Port to listen on */
char *address; /**< Address to listen with */
char *authenticator; /**< Name of authenticator */
char *auth_options; /**< Authenticator options */
void *auth_instance; /**< Authenticator instance created in GWAUTHENTICATOR::initialize() */
SSL_LISTENER *ssl; /**< Structure of SSL data or NULL */
struct dcb *listener; /**< The DCB for the listener */
@ -59,6 +60,18 @@ typedef struct servlistener
struct servlistener *next; /**< Next service protocol */
} SERV_LISTENER;
/**
* @brief Serialize a listener to a file
*
* This converts @c listener into an INI format file. This allows created listeners
* to be persisted to disk. This will replace any existing files with the same
* name.
*
* @param listener Listener to serialize
* @return True if the serialization of the listener was successful, false if it fails
*/
bool listener_serialize(const SERV_LISTENER *listener);
SERV_LISTENER* listener_alloc(struct service* service, const char* name, const char *protocol,
const char *address, unsigned short port, const char *authenticator,
const char* auth_options, SSL_LISTENER *ssl);

View File

@ -192,9 +192,9 @@ extern SERVICE *service_alloc(const char *, const char *);
extern int service_free(SERVICE *);
extern SERVICE *service_find(const char *);
extern int service_isvalid(SERVICE *);
extern bool serviceAddProtocol(SERVICE *service, const char *name, const char *protocol,
const char *address, unsigned short port, const char *authenticator,
const char *options, SSL_LISTENER *ssl);
extern SERV_LISTENER* serviceCreateListener(SERVICE *service, const char *name, const char *protocol,
const char *address, unsigned short port, const char *authenticator,
const char *options, SSL_LISTENER *ssl);
extern int serviceHasProtocol(SERVICE *service, const char *protocol,
const char* address, unsigned short port);
extern void serviceAddBackend(SERVICE *, SERVER *);
@ -204,7 +204,7 @@ extern void serviceAddRouterOption(SERVICE *, char *);
extern void serviceClearRouterOptions(SERVICE *);
extern int serviceStart(SERVICE *);
extern int serviceStartAll();
extern bool serviceListen(SERVICE *service, unsigned short port);
extern bool serviceListen(SERVICE *service, SERV_LISTENER *port);
extern int serviceStop(SERVICE *);
extern int serviceRestart(SERVICE *);
extern int serviceSetUser(SERVICE *, char *, char *);

View File

@ -3121,8 +3121,8 @@ int create_new_listener(CONFIG_CONTEXT *obj)
}
else
{
serviceAddProtocol(service, obj->object, protocol, socket, 0,
authenticator, authenticator_options, ssl_info);
serviceCreateListener(service, obj->object, protocol, socket, 0,
authenticator, authenticator_options, ssl_info);
}
}
@ -3138,8 +3138,8 @@ int create_new_listener(CONFIG_CONTEXT *obj)
}
else
{
serviceAddProtocol(service, obj->object, protocol, address, atoi(port),
authenticator, authenticator_options, ssl_info);
serviceCreateListener(service, obj->object, protocol, address, atoi(port),
authenticator, authenticator_options, ssl_info);
}
}

View File

@ -411,9 +411,10 @@ bool runtime_create_listener(SERVICE *service, const char *name, const char *add
if (rval)
{
const char *print_addr = addr ? addr : "0.0.0.0";
SERV_LISTENER *listener = serviceCreateListener(service, name, proto, addr,
u_port, auth, auth_opt, ssl);
if (serviceAddProtocol(service, name, proto, addr, u_port, auth, auth_opt, ssl) &&
serviceListen(service, u_port))
if (listener && listener_serialize(listener) && serviceListen(service, listener))
{
MXS_NOTICE("Listener '%s' at %s:%s for service '%s' created",
name, print_addr, port, service->name);

View File

@ -30,13 +30,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <maxscale/listener.h>
#include <maxscale/gwdirs.h>
#include <maxscale/gw_ssl.h>
#include <maxscale/gw_protocol.h>
#include <maxscale/log_manager.h>
#include <maxscale/alloc.h>
#include <maxscale/users.h>
#include <maxscale/modules.h>
#include <maxscale/service.h>
static RSA *rsa_512 = NULL;
static RSA *rsa_1024 = NULL;
@ -69,6 +72,14 @@ listener_alloc(struct service* service, const char* name, const char *protocol,
}
}
char *my_auth_options = NULL;
if (auth_options && (my_auth_options = MXS_STRDUP(auth_options)) == NULL)
{
MXS_FREE(my_address);
return NULL;
}
char *my_authenticator = NULL;
if (authenticator)
@ -116,6 +127,7 @@ listener_alloc(struct service* service, const char* name, const char *protocol,
proto->address = my_address;
proto->port = port;
proto->authenticator = my_authenticator;
proto->auth_options = my_auth_options;
proto->ssl = ssl;
proto->users = NULL;
proto->resources = NULL;
@ -146,6 +158,8 @@ void listener_free(SERV_LISTENER* listener)
MXS_FREE(listener->address);
MXS_FREE(listener->authenticator);
MXS_FREE(listener->auth_options);
MXS_FREE(listener->name);
MXS_FREE(listener->protocol);
MXS_FREE(listener);
}
@ -376,3 +390,132 @@ tmp_rsa_callback(SSL *s, int is_export, int keylength)
}
return(rsa_tmp);
}
/**
* Creates a listener configuration at the location pointed by @c filename
*
* @param listener Listener to serialize into a configuration
* @param filename Filename where configuration is written
* @return True on success, false on error
*/
static bool create_listener_config(const SERV_LISTENER *listener, const char *filename)
{
int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (file == -1)
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to open file '%s' when serializing listener '%s': %d, %s",
filename, listener->name, errno, strerror_r(errno, errbuf, sizeof(errbuf)));
return false;
}
// TODO: Check for return values on all of the dprintf calls
dprintf(file, "[%s]\n", listener->name);
dprintf(file, "type=listener\n");
dprintf(file, "protocol=%s\n", listener->protocol);
dprintf(file, "service=%s\n", listener->service->name);
dprintf(file, "address=%s\n", listener->address);
dprintf(file, "port=%u\n", listener->port);
dprintf(file, "authenticator=%s\n", listener->authenticator);
if (listener->auth_options)
{
dprintf(file, "authenticator_options=%s\n", listener->auth_options);
}
if (listener->ssl)
{
dprintf(file, "ssl=required\n");
if (listener->ssl->ssl_cert)
{
dprintf(file, "ssl_cert=%s\n", listener->ssl->ssl_cert);
}
if (listener->ssl->ssl_key)
{
dprintf(file, "ssl_key=%s\n", listener->ssl->ssl_key);
}
if (listener->ssl->ssl_ca_cert)
{
dprintf(file, "ssl_ca_cert=%s\n", listener->ssl->ssl_ca_cert);
}
if (listener->ssl->ssl_cert_verify_depth)
{
dprintf(file, "ssl_cert_verify_depth=%d\n", listener->ssl->ssl_cert_verify_depth);
}
const char *version = NULL;
switch (listener->ssl->ssl_method_type)
{
case SERVICE_TLS10:
version = "TLSV10";
break;
#ifdef OPENSSL_1_0
case SERVICE_TLS11:
version = "TLSV11";
break;
case SERVICE_TLS12:
version = "TLSV12";
break;
#endif
case SERVICE_SSL_TLS_MAX:
version = "MAX";
break;
default:
break;
}
if (version)
{
dprintf(file, "ssl_version=%s\n", version);
}
}
close(file);
return true;
}
bool listener_serialize(const SERV_LISTENER *listener)
{
bool rval = false;
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s/%s.cnf.tmp", get_config_persistdir(),
listener->name);
if (unlink(filename) == -1 && errno != ENOENT)
{
char err[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to remove temporary listener configuration at '%s': %d, %s",
filename, errno, strerror_r(errno, err, sizeof(err)));
}
else if (create_listener_config(listener, filename))
{
char final_filename[PATH_MAX];
strcpy(final_filename, filename);
char *dot = strrchr(final_filename, '.');
ss_dassert(dot);
*dot = '\0';
if (rename(filename, final_filename) == 0)
{
rval = true;
}
else
{
char err[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to rename temporary listener configuration at '%s': %d, %s",
filename, errno, strerror_r(errno, err, sizeof(err)));
}
}
return rval;
}

View File

@ -508,22 +508,9 @@ serviceStart(SERVICE *service)
* @param service The service to start the listener for
* @param port The port number
*/
bool serviceListen(SERVICE *service, unsigned short port)
bool serviceListen(SERVICE *service, SERV_LISTENER *port)
{
bool rval = false;
for (SERV_LISTENER *ptr = service->ports; ptr; ptr = ptr->next)
{
if (ptr->port == port)
{
if (serviceStartPort(service, ptr))
{
rval = true;
}
break;
}
}
return rval;
return serviceStartPort(service, port);
}
/**
@ -678,7 +665,7 @@ service_free(SERVICE *service)
}
/**
* Add a protocol/port pair to the service
* Create a listener for the service
*
* @param service The service
* @param protocol The name of the protocol module
@ -686,13 +673,13 @@ service_free(SERVICE *service)
* @param port The port to listen on
* @param authenticator Name of the authenticator to be used
* @param ssl SSL configuration
* @return TRUE if the protocol/port could be added
*
* @return Created listener or NULL on error
*/
bool serviceAddProtocol(SERVICE *service, const char *name, const char *protocol,
SERV_LISTENER* serviceCreateListener(SERVICE *service, const char *name, const char *protocol,
const char *address, unsigned short port, const char *authenticator,
const char *options, SSL_LISTENER *ssl)
{
bool rval = false;
SERV_LISTENER *proto = listener_alloc(service, name, protocol, address,
port, authenticator, options, ssl);
@ -702,10 +689,9 @@ bool serviceAddProtocol(SERVICE *service, const char *name, const char *protocol
proto->next = service->ports;
service->ports = proto;
spinlock_release(&service->spin);
rval = true;
}
return rval;
return proto;
}
/**

View File

@ -69,8 +69,8 @@ test1()
ss_info_dassert(0 == strcmp("MyService", service_get_name(service)), "Service must have given name");
ss_dfprintf(stderr, "\t..done\nAdding protocol testprotocol.");
set_libdir(MXS_STRDUP_A("../../modules/authenticator/MySQLAuth/"));
ss_info_dassert(0 != serviceAddProtocol(service, "TestProtocol", "testprotocol",
"localhost", 9876, "MySQLAuth", NULL, NULL),
ss_info_dassert(serviceCreateListener(service, "TestProtocol", "testprotocol",
"localhost", 9876, "MySQLAuth", NULL, NULL),
"Add Protocol should succeed");
ss_info_dassert(0 != serviceHasProtocol(service, "testprotocol", "localhost", 9876),
"Service should have new protocol as requested");