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 */ unsigned short port; /**< Port to listen on */
char *address; /**< Address to listen with */ char *address; /**< Address to listen with */
char *authenticator; /**< Name of authenticator */ char *authenticator; /**< Name of authenticator */
char *auth_options; /**< Authenticator options */
void *auth_instance; /**< Authenticator instance created in GWAUTHENTICATOR::initialize() */ void *auth_instance; /**< Authenticator instance created in GWAUTHENTICATOR::initialize() */
SSL_LISTENER *ssl; /**< Structure of SSL data or NULL */ SSL_LISTENER *ssl; /**< Structure of SSL data or NULL */
struct dcb *listener; /**< The DCB for the listener */ struct dcb *listener; /**< The DCB for the listener */
@ -59,6 +60,18 @@ typedef struct servlistener
struct servlistener *next; /**< Next service protocol */ struct servlistener *next; /**< Next service protocol */
} SERV_LISTENER; } 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, SERV_LISTENER* listener_alloc(struct service* service, const char* name, const char *protocol,
const char *address, unsigned short port, const char *authenticator, const char *address, unsigned short port, const char *authenticator,
const char* auth_options, SSL_LISTENER *ssl); const char* auth_options, SSL_LISTENER *ssl);

View File

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

View File

@ -3121,7 +3121,7 @@ int create_new_listener(CONFIG_CONTEXT *obj)
} }
else else
{ {
serviceAddProtocol(service, obj->object, protocol, socket, 0, serviceCreateListener(service, obj->object, protocol, socket, 0,
authenticator, authenticator_options, ssl_info); authenticator, authenticator_options, ssl_info);
} }
} }
@ -3138,7 +3138,7 @@ int create_new_listener(CONFIG_CONTEXT *obj)
} }
else else
{ {
serviceAddProtocol(service, obj->object, protocol, address, atoi(port), serviceCreateListener(service, obj->object, protocol, address, atoi(port),
authenticator, authenticator_options, ssl_info); 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) if (rval)
{ {
const char *print_addr = addr ? addr : "0.0.0.0"; 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) && if (listener && listener_serialize(listener) && serviceListen(service, listener))
serviceListen(service, u_port))
{ {
MXS_NOTICE("Listener '%s' at %s:%s for service '%s' created", MXS_NOTICE("Listener '%s' at %s:%s for service '%s' created",
name, print_addr, port, service->name); name, print_addr, port, service->name);

View File

@ -30,13 +30,16 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <fcntl.h>
#include <maxscale/listener.h> #include <maxscale/listener.h>
#include <maxscale/gwdirs.h>
#include <maxscale/gw_ssl.h> #include <maxscale/gw_ssl.h>
#include <maxscale/gw_protocol.h> #include <maxscale/gw_protocol.h>
#include <maxscale/log_manager.h> #include <maxscale/log_manager.h>
#include <maxscale/alloc.h> #include <maxscale/alloc.h>
#include <maxscale/users.h> #include <maxscale/users.h>
#include <maxscale/modules.h> #include <maxscale/modules.h>
#include <maxscale/service.h>
static RSA *rsa_512 = NULL; static RSA *rsa_512 = NULL;
static RSA *rsa_1024 = 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; char *my_authenticator = NULL;
if (authenticator) if (authenticator)
@ -116,6 +127,7 @@ listener_alloc(struct service* service, const char* name, const char *protocol,
proto->address = my_address; proto->address = my_address;
proto->port = port; proto->port = port;
proto->authenticator = my_authenticator; proto->authenticator = my_authenticator;
proto->auth_options = my_auth_options;
proto->ssl = ssl; proto->ssl = ssl;
proto->users = NULL; proto->users = NULL;
proto->resources = NULL; proto->resources = NULL;
@ -146,6 +158,8 @@ void listener_free(SERV_LISTENER* listener)
MXS_FREE(listener->address); MXS_FREE(listener->address);
MXS_FREE(listener->authenticator); MXS_FREE(listener->authenticator);
MXS_FREE(listener->auth_options);
MXS_FREE(listener->name);
MXS_FREE(listener->protocol); MXS_FREE(listener->protocol);
MXS_FREE(listener); MXS_FREE(listener);
} }
@ -376,3 +390,132 @@ tmp_rsa_callback(SSL *s, int is_export, int keylength)
} }
return(rsa_tmp); 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 service The service to start the listener for
* @param port The port number * @param port The port number
*/ */
bool serviceListen(SERVICE *service, unsigned short port) bool serviceListen(SERVICE *service, SERV_LISTENER *port)
{ {
bool rval = false; return serviceStartPort(service, port);
for (SERV_LISTENER *ptr = service->ports; ptr; ptr = ptr->next)
{
if (ptr->port == port)
{
if (serviceStartPort(service, ptr))
{
rval = true;
}
break;
}
}
return rval;
} }
/** /**
@ -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 service The service
* @param protocol The name of the protocol module * @param protocol The name of the protocol module
@ -686,13 +673,13 @@ service_free(SERVICE *service)
* @param port The port to listen on * @param port The port to listen on
* @param authenticator Name of the authenticator to be used * @param authenticator Name of the authenticator to be used
* @param ssl SSL configuration * @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 *address, unsigned short port, const char *authenticator,
const char *options, SSL_LISTENER *ssl) const char *options, SSL_LISTENER *ssl)
{ {
bool rval = false;
SERV_LISTENER *proto = listener_alloc(service, name, protocol, address, SERV_LISTENER *proto = listener_alloc(service, name, protocol, address,
port, authenticator, options, ssl); port, authenticator, options, ssl);
@ -702,10 +689,9 @@ bool serviceAddProtocol(SERVICE *service, const char *name, const char *protocol
proto->next = service->ports; proto->next = service->ports;
service->ports = proto; service->ports = proto;
spinlock_release(&service->spin); spinlock_release(&service->spin);
rval = true;
} }
return rval; return proto;
} }
/** /**

View File

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