 09a445f1b7
			
		
	
	09a445f1b7
	
	
	
		
			
			The `monitoruser` and `monitorpw` parameters were mislabeled as `monuser` and `monpw`. To allow backwards compatibility, the `monuser` and `monpw` still work as aliases for the correct commands.
		
			
				
	
	
		
			663 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			663 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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: 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 "maxscale/config_runtime.h"
 | |
| 
 | |
| #include <strings.h>
 | |
| 
 | |
| #include <maxscale/atomic.h>
 | |
| #include <maxscale/paths.h>
 | |
| #include <maxscale/spinlock.h>
 | |
| 
 | |
| #include "maxscale/config.h"
 | |
| #include "maxscale/monitor.h"
 | |
| #include "maxscale/modules.h"
 | |
| #include "maxscale/service.h"
 | |
| 
 | |
| static SPINLOCK crt_lock = SPINLOCK_INIT;
 | |
| 
 | |
| bool runtime_link_server(SERVER *server, const char *target)
 | |
| {
 | |
|     spinlock_acquire(&crt_lock);
 | |
| 
 | |
|     bool rval = false;
 | |
|     SERVICE *service = service_find(target);
 | |
|     MXS_MONITOR *monitor = service ? NULL : monitor_find(target);
 | |
| 
 | |
|     if (service)
 | |
|     {
 | |
|         if (serviceAddBackend(service, server))
 | |
|         {
 | |
|             service_serialize_servers(service);
 | |
|             rval = true;
 | |
|         }
 | |
|     }
 | |
|     else if (monitor)
 | |
|     {
 | |
|         if (monitorAddServer(monitor, server))
 | |
|         {
 | |
|             monitor_serialize_servers(monitor);
 | |
|             rval = true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (rval)
 | |
|     {
 | |
|         const char *type = service ? "service" : "monitor";
 | |
|         MXS_NOTICE("Added server '%s' to %s '%s'", server->unique_name, type, target);
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| bool runtime_unlink_server(SERVER *server, const char *target)
 | |
| {
 | |
|     spinlock_acquire(&crt_lock);
 | |
| 
 | |
|     bool rval = false;
 | |
|     SERVICE *service = service_find(target);
 | |
|     MXS_MONITOR *monitor = service ? NULL : monitor_find(target);
 | |
| 
 | |
|     if (service || monitor)
 | |
|     {
 | |
|         rval = true;
 | |
| 
 | |
|         if (service)
 | |
|         {
 | |
|             serviceRemoveBackend(service, server);
 | |
|             service_serialize_servers(service);
 | |
|         }
 | |
|         else if (monitor)
 | |
|         {
 | |
|             monitorRemoveServer(monitor, server);
 | |
|             monitor_serialize_servers(monitor);
 | |
|         }
 | |
| 
 | |
|         const char *type = service ? "service" : "monitor";
 | |
|         MXS_NOTICE("Removed server '%s' from %s '%s'", server->unique_name, type, target);
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool runtime_create_server(const char *name, const char *address, const char *port,
 | |
|                            const char *protocol, const char *authenticator,
 | |
|                            const char *authenticator_options)
 | |
| {
 | |
|     spinlock_acquire(&crt_lock);
 | |
|     bool rval = false;
 | |
| 
 | |
|     if (server_find_by_unique_name(name) == NULL)
 | |
|     {
 | |
|         // TODO: Get default values from the protocol module
 | |
|         if (port == NULL)
 | |
|         {
 | |
|             port = "3306";
 | |
|         }
 | |
|         if (protocol == NULL)
 | |
|         {
 | |
|             protocol = "MySQLBackend";
 | |
|         }
 | |
|         if (authenticator == NULL && (authenticator = get_default_authenticator(protocol)) == NULL)
 | |
|         {
 | |
|             MXS_ERROR("No authenticator defined for server '%s' and no default "
 | |
|                       "authenticator for protocol '%s'.", name, protocol);
 | |
|             spinlock_release(&crt_lock);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /** First check if this service has been created before */
 | |
|         SERVER *server = server_find_destroyed(name, protocol, authenticator,
 | |
|                                                authenticator_options);
 | |
| 
 | |
|         if (server)
 | |
|         {
 | |
|             /** Found old server, replace network details with new ones and
 | |
|              * reactivate it */
 | |
|             snprintf(server->name, sizeof(server->name), "%s", address);
 | |
|             server->port = atoi(port);
 | |
|             server->is_active = true;
 | |
|             rval = true;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /**
 | |
|              * server_alloc will add the server to the global list of
 | |
|              * servers so we don't need to manually add it.
 | |
|              */
 | |
|             server = server_alloc(name, address, atoi(port), protocol,
 | |
|                                   authenticator, authenticator_options);
 | |
|         }
 | |
| 
 | |
|         if (server && server_serialize(server))
 | |
|         {
 | |
|             /** Mark that the server was created after startup */
 | |
|             server->created_online = true;
 | |
| 
 | |
|             MXS_NOTICE("Created server '%s' at %s:%u", server->unique_name,
 | |
|                        server->name, server->port);
 | |
|             rval = true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| bool runtime_destroy_server(SERVER *server)
 | |
| {
 | |
|     spinlock_acquire(&crt_lock);
 | |
|     bool rval = false;
 | |
| 
 | |
|     if (service_server_in_use(server) || monitor_server_in_use(server))
 | |
|     {
 | |
|         MXS_ERROR("Cannot destroy server '%s' as it is used by at least one "
 | |
|                   "service or monitor", server->unique_name);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         char filename[PATH_MAX];
 | |
|         snprintf(filename, sizeof(filename), "%s/%s.cnf", get_config_persistdir(),
 | |
|                  server->unique_name);
 | |
| 
 | |
|         if (unlink(filename) == -1)
 | |
|         {
 | |
|             if (errno != ENOENT)
 | |
|             {
 | |
|                 char err[MXS_STRERROR_BUFLEN];
 | |
|                 MXS_ERROR("Failed to remove persisted server configuration '%s': %d, %s",
 | |
|                           filename, errno, strerror_r(errno, err, sizeof(err)));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 rval = true;
 | |
|                 MXS_WARNING("Server '%s' was not created at runtime. Remove the "
 | |
|                             "server manually from the correct configuration file.",
 | |
|                             server->unique_name);
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             rval = true;
 | |
|         }
 | |
| 
 | |
|         if (rval)
 | |
|         {
 | |
|             MXS_NOTICE("Destroyed server '%s' at %s:%u", server->unique_name,
 | |
|                        server->name, server->port);
 | |
|             server->is_active = false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| static SSL_LISTENER* create_ssl(const char *name, const char *key, const char *cert,
 | |
|                                 const char *ca, const char *version, const char *depth)
 | |
| {
 | |
|     SSL_LISTENER *rval = NULL;
 | |
|     CONFIG_CONTEXT *obj = config_context_create(name);
 | |
| 
 | |
|     if (obj)
 | |
|     {
 | |
|         if (config_add_param(obj, "ssl", "required") &&
 | |
|             config_add_param(obj, "ssl_key", key) &&
 | |
|             config_add_param(obj, "ssl_cert", cert) &&
 | |
|             config_add_param(obj, "ssl_ca_cert", ca) &&
 | |
|             (!version || config_add_param(obj, "ssl_version", version)) &&
 | |
|             (!depth || config_add_param(obj, "ssl_cert_verify_depth", depth)))
 | |
|         {
 | |
|             int err = 0;
 | |
|             SSL_LISTENER *ssl = make_ssl_structure(obj, true, &err);
 | |
| 
 | |
|             if (err == 0 && ssl && listener_init_SSL(ssl) == 0)
 | |
|             {
 | |
|                 rval = ssl;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         config_context_free(obj);
 | |
|     }
 | |
| 
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| bool runtime_enable_server_ssl(SERVER *server, const char *key, const char *cert,
 | |
|                                const char *ca, const char *version, const char *depth)
 | |
| {
 | |
|     bool rval = false;
 | |
| 
 | |
|     if (key && cert && ca)
 | |
|     {
 | |
|         spinlock_acquire(&crt_lock);
 | |
|         SSL_LISTENER *ssl = create_ssl(server->unique_name, key, cert, ca, version, depth);
 | |
| 
 | |
|         if (ssl)
 | |
|         {
 | |
|             /** TODO: Properly discard old SSL configurations.This could cause the
 | |
|              * loss of a pointer if two update operations are done at the same time.*/
 | |
|             ssl->next = server->server_ssl;
 | |
| 
 | |
|             /** Sync to prevent reads on partially initialized server_ssl */
 | |
|             atomic_synchronize();
 | |
|             server->server_ssl = ssl;
 | |
| 
 | |
|             if (server_serialize(server))
 | |
|             {
 | |
|                 MXS_NOTICE("Enabled SSL for server '%s'", server->unique_name);
 | |
|                 rval = true;
 | |
|             }
 | |
|         }
 | |
|         spinlock_release(&crt_lock);
 | |
|     }
 | |
| 
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| bool runtime_alter_server(SERVER *server, char *key, char *value)
 | |
| {
 | |
|     spinlock_acquire(&crt_lock);
 | |
|     bool valid = true;
 | |
| 
 | |
|     if (strcmp(key, "address") == 0)
 | |
|     {
 | |
|         server_update_address(server, value);
 | |
|     }
 | |
|     else if (strcmp(key, "port") == 0)
 | |
|     {
 | |
|         server_update_port(server, atoi(value));
 | |
|     }
 | |
|     else if (strcmp(key, "monuser") == 0 || strcmp(key, "monitoruser") == 0)
 | |
|     {
 | |
|         if (strcmp(key, "monuser") == 0)
 | |
|         {
 | |
|             MXS_WARNING("Use of `monuser` is deprecated, use `monitoruser` instead.");
 | |
|         }
 | |
| 
 | |
|         server_update_credentials(server, value, server->monpw);
 | |
|     }
 | |
|     else if (strcmp(key, "monpw") == 0 || strcmp(key, "monitorpw") == 0)
 | |
|     {
 | |
|         if (strcmp(key, "monpw") == 0)
 | |
|         {
 | |
|             MXS_WARNING("Use of `monpw` is deprecated, use `monitorpw` instead.");
 | |
|         }
 | |
| 
 | |
|         server_update_credentials(server, server->monuser, value);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         if (!server_remove_parameter(server, key) && !value[0])
 | |
|         {
 | |
|             valid = false;
 | |
|         }
 | |
|         else if (value[0])
 | |
|         {
 | |
|             server_add_parameter(server, key, value);
 | |
| 
 | |
|             /**
 | |
|              * It's likely that this parameter is used as a weighting parameter.
 | |
|              * We need to update the weights of services that use this.
 | |
|              */
 | |
|             service_update_weights();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (valid)
 | |
|     {
 | |
|         if (server->created_online)
 | |
|         {
 | |
|             server_serialize(server);
 | |
|         }
 | |
|         MXS_NOTICE("Updated server '%s': %s=%s", server->unique_name, key, value);
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return valid;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Convert a string value to a positive integer
 | |
|  *
 | |
|  * If the value is not a positive integer, an error is printed to @c dcb.
 | |
|  *
 | |
|  * @param value String value
 | |
|  * @return 0 on error, otherwise a positive integer
 | |
|  */
 | |
| static long get_positive_int(const char *value)
 | |
| {
 | |
|     char *endptr;
 | |
|     long ival = strtol(value, &endptr, 10);
 | |
| 
 | |
|     if (*endptr == '\0' && ival > 0)
 | |
|     {
 | |
|         return ival;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Add default parameters to a monitor
 | |
|  *
 | |
|  * @param monitor Monitor to modify
 | |
|  */
 | |
| static void add_monitor_defaults(MXS_MONITOR *monitor)
 | |
| {
 | |
|     /** Inject the default module parameters in case we only deleted
 | |
|      * a parameter */
 | |
|     CONFIG_CONTEXT ctx = {.object = ""};
 | |
|     const MXS_MODULE *mod = get_module(monitor->module_name, MODULE_MONITOR);
 | |
| 
 | |
|     if (mod)
 | |
|     {
 | |
|         config_add_defaults(&ctx, mod->parameters);
 | |
|         monitorAddParameters(monitor, ctx.parameters);
 | |
|         config_parameter_free(ctx.parameters);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         MXS_ERROR("Failed to load module '%s'. See previous error messages for more details.",
 | |
|                   monitor->module_name);
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool runtime_alter_monitor(MXS_MONITOR *monitor, char *key, char *value)
 | |
| {
 | |
|     spinlock_acquire(&crt_lock);
 | |
|     bool valid = false;
 | |
| 
 | |
|     if (strcmp(key, "user") == 0)
 | |
|     {
 | |
|         valid = true;
 | |
|         monitorAddUser(monitor, value, monitor->password);
 | |
|     }
 | |
|     else if (strcmp(key, "password") == 0)
 | |
|     {
 | |
|         valid = true;
 | |
|         monitorAddUser(monitor, monitor->user, value);
 | |
|     }
 | |
|     else if (strcmp(key, "monitor_interval") == 0)
 | |
|     {
 | |
|         long ival = get_positive_int(value);
 | |
|         if (ival)
 | |
|         {
 | |
|             valid = true;
 | |
|             monitorSetInterval(monitor, ival);
 | |
|         }
 | |
|     }
 | |
|     else if (strcmp(key, "backend_connect_timeout") == 0)
 | |
|     {
 | |
|         long ival = get_positive_int(value);
 | |
|         if (ival)
 | |
|         {
 | |
|             valid = true;
 | |
|             monitorSetNetworkTimeout(monitor, MONITOR_CONNECT_TIMEOUT, ival);
 | |
|         }
 | |
|     }
 | |
|     else if (strcmp(key, "backend_write_timeout") == 0)
 | |
|     {
 | |
|         long ival = get_positive_int(value);
 | |
|         if (ival)
 | |
|         {
 | |
|             valid = true;
 | |
|             monitorSetNetworkTimeout(monitor, MONITOR_WRITE_TIMEOUT, ival);
 | |
|         }
 | |
|     }
 | |
|     else if (strcmp(key, "backend_read_timeout") == 0)
 | |
|     {
 | |
|         long ival = get_positive_int(value);
 | |
|         if (ival)
 | |
|         {
 | |
|             valid = true;
 | |
|             monitorSetNetworkTimeout(monitor, MONITOR_READ_TIMEOUT, ival);
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /** We're modifying module specific parameters and we need to stop the monitor */
 | |
|         monitorStop(monitor);
 | |
| 
 | |
|         if (monitorRemoveParameter(monitor, key) || value[0])
 | |
|         {
 | |
|             /** Either we're removing an existing parameter or adding a new one */
 | |
|             valid = true;
 | |
| 
 | |
|             if (value[0])
 | |
|             {
 | |
|                 MXS_CONFIG_PARAMETER p = {.name = key, .value = value};
 | |
|                 monitorAddParameters(monitor, &p);
 | |
|             }
 | |
| 
 | |
|             add_monitor_defaults(monitor);
 | |
|         }
 | |
| 
 | |
|         monitorStart(monitor, monitor->parameters);
 | |
|     }
 | |
| 
 | |
|     if (valid)
 | |
|     {
 | |
|         if (monitor->created_online)
 | |
|         {
 | |
|             monitor_serialize(monitor);
 | |
|         }
 | |
| 
 | |
|         MXS_NOTICE("Updated monitor '%s': %s=%s", monitor->name, key, value);
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return valid;
 | |
| }
 | |
| 
 | |
| bool runtime_create_listener(SERVICE *service, const char *name, const char *addr,
 | |
|                              const char *port, const char *proto, const char *auth,
 | |
|                              const char *auth_opt, const char *ssl_key,
 | |
|                              const char *ssl_cert, const char *ssl_ca,
 | |
|                              const char *ssl_version, const char *ssl_depth)
 | |
| {
 | |
| 
 | |
|     if (addr == NULL || strcasecmp(addr, "default") == 0)
 | |
|     {
 | |
|         addr = "::";
 | |
|     }
 | |
|     if (port == NULL || strcasecmp(port, "default") == 0)
 | |
|     {
 | |
|         port = "3306";
 | |
|     }
 | |
|     if (proto == NULL || strcasecmp(proto, "default") == 0)
 | |
|     {
 | |
|         proto = "MySQLClient";
 | |
|     }
 | |
| 
 | |
|     if (auth && strcasecmp(auth, "default") == 0)
 | |
|     {
 | |
|         /** Set auth to NULL so the protocol default authenticator is used */
 | |
|         auth = NULL;
 | |
|     }
 | |
| 
 | |
|     if (auth_opt && strcasecmp(auth_opt, "default") == 0)
 | |
|     {
 | |
|         /** Don't pass options to the authenticator */
 | |
|         auth_opt = NULL;
 | |
|     }
 | |
| 
 | |
|     unsigned short u_port = atoi(port);
 | |
| 
 | |
|     spinlock_acquire(&crt_lock);
 | |
| 
 | |
|     SSL_LISTENER *ssl = NULL;
 | |
|     bool rval = false;
 | |
| 
 | |
|     if (!serviceHasListener(service, proto, addr, u_port))
 | |
|     {
 | |
|         rval = true;
 | |
| 
 | |
|         if (ssl_key && ssl_cert && ssl_ca)
 | |
|         {
 | |
|             ssl = create_ssl(name, ssl_key, ssl_cert, ssl_ca, ssl_version, ssl_depth);
 | |
| 
 | |
|             if (ssl == NULL)
 | |
|             {
 | |
|                 MXS_ERROR("SSL initialization for listener '%s' failed.", name);
 | |
|                 rval = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (rval)
 | |
|         {
 | |
|             const char *print_addr = addr ? addr : "::";
 | |
|             SERV_LISTENER *listener = serviceCreateListener(service, name, proto, addr,
 | |
|                                                             u_port, auth, auth_opt, ssl);
 | |
| 
 | |
|             if (listener && listener_serialize(listener) && serviceLaunchListener(service, listener))
 | |
|             {
 | |
|                 MXS_NOTICE("Created %slistener '%s' at %s:%s for service '%s'",
 | |
|                            ssl ? "TLS encrypted " : "",
 | |
|                            name, print_addr, port, service->name);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 MXS_ERROR("Failed to start listener '%s' at %s:%s.", name, print_addr, port);
 | |
|                 rval = false;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| bool runtime_destroy_listener(SERVICE *service, const char *name)
 | |
| {
 | |
|     bool rval = false;
 | |
|     char filename[PATH_MAX];
 | |
|     snprintf(filename, sizeof(filename), "%s/%s.cnf", get_config_persistdir(), name);
 | |
| 
 | |
|     spinlock_acquire(&crt_lock);
 | |
| 
 | |
|     if (unlink(filename) == -1)
 | |
|     {
 | |
|         if (errno != ENOENT)
 | |
|         {
 | |
|             char err[MXS_STRERROR_BUFLEN];
 | |
|             MXS_ERROR("Failed to remove persisted listener configuration '%s': %d, %s",
 | |
|                       filename, errno, strerror_r(errno, err, sizeof(err)));
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             rval = false;
 | |
|             MXS_WARNING("Listener '%s' was not created at runtime. Remove the "
 | |
|                         "listener manually from the correct configuration file.",
 | |
|                         name);
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         rval = true;
 | |
|     }
 | |
| 
 | |
|     if (rval)
 | |
|     {
 | |
|         rval = serviceStopListener(service, name);
 | |
| 
 | |
|         if (rval)
 | |
|         {
 | |
|             MXS_NOTICE("Destroyed listener '%s' for service '%s'. The listener "
 | |
|                        "will be removed after the next restart of MaxScale.",
 | |
|                        name, service->name);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             MXS_ERROR("Failed to destroy listener '%s' for service '%s'", name, service->name);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| bool runtime_create_monitor(const char *name, const char *module)
 | |
| {
 | |
|     spinlock_acquire(&crt_lock);
 | |
|     bool rval = false;
 | |
| 
 | |
|     if (monitor_find(name) == NULL)
 | |
|     {
 | |
|         MXS_MONITOR *monitor = monitor_alloc((char*)name, (char*)module);
 | |
| 
 | |
|         if (monitor)
 | |
|         {
 | |
|             /** Mark that this monitor was created after MaxScale was started */
 | |
|             monitor->created_online = true;
 | |
|             add_monitor_defaults(monitor);
 | |
| 
 | |
|             if (monitor_serialize(monitor))
 | |
|             {
 | |
|                 MXS_NOTICE("Created monitor '%s'", name);
 | |
|                 rval = true;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| bool runtime_destroy_monitor(MXS_MONITOR *monitor)
 | |
| {
 | |
|     bool rval = false;
 | |
|     char filename[PATH_MAX];
 | |
|     snprintf(filename, sizeof(filename), "%s/%s.cnf", get_config_persistdir(), monitor->name);
 | |
| 
 | |
|     spinlock_acquire(&crt_lock);
 | |
| 
 | |
|     if (unlink(filename) == -1)
 | |
|     {
 | |
|         if (errno != ENOENT)
 | |
|         {
 | |
|             char err[MXS_STRERROR_BUFLEN];
 | |
|             MXS_ERROR("Failed to remove persisted monitor configuration '%s': %d, %s",
 | |
|                       filename, errno, strerror_r(errno, err, sizeof(err)));
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             rval = false;
 | |
|             MXS_WARNING("Monitor '%s' was not created at runtime. Remove the "
 | |
|                         "monitor manually from the correct configuration file.",
 | |
|                         monitor->name);
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         rval = true;
 | |
|     }
 | |
| 
 | |
|     if (rval)
 | |
|     {
 | |
|         monitorStop(monitor);
 | |
| 
 | |
|         while (monitor->databases)
 | |
|         {
 | |
|             monitorRemoveServer(monitor, monitor->databases->server);
 | |
|         }
 | |
|         MXS_NOTICE("Destroyed monitor '%s'. The monitor will be removed "
 | |
|                    "after the next restart of MaxScale.", monitor->name);
 | |
|     }
 | |
| 
 | |
|     spinlock_release(&crt_lock);
 | |
|     return rval;
 | |
| }
 |