diff --git a/include/maxscale/config_runtime.h b/include/maxscale/config_runtime.h index 06ae263a8..4601ec633 100644 --- a/include/maxscale/config_runtime.h +++ b/include/maxscale/config_runtime.h @@ -157,3 +157,23 @@ bool runtime_create_listener(SERVICE *service, const char *name, const char *add * @return True if the listener was successfully destroyed */ bool runtime_destroy_listener(SERVICE *service, const char *name); + +/** + * @brief Create a new monitor + * + * @param name Name of the monitor + * @param module Monitor module + * @return True if new monitor was created and persisted + */ +bool runtime_create_monitor(const char *name, const char *module); + +/** + * @brief Destroy a monitor + * + * Monitors are not removed from the runtime configuration but they are stopped. + * Destroyed monitor are removed after a restart. + * + * @param monitor Monitor to destroy + * @return True if monitor was destroyed + */ +bool runtime_destroy_monitor(MONITOR *monitor); diff --git a/include/maxscale/monitor.h b/include/maxscale/monitor.h index 5c458ce46..70dcd7b63 100644 --- a/include/maxscale/monitor.h +++ b/include/maxscale/monitor.h @@ -196,6 +196,7 @@ struct monitor * two times the option value. */ MONITOR_OBJECT *module; /**< The "monitor object" */ + char *module_name; /**< Name of the monitor module */ void *handle; /**< Handle returned from startMonitor */ size_t interval; /**< The monitor interval */ struct monitor *next; /**< Next monitor in the linked list */ @@ -243,7 +244,7 @@ void mon_log_state_change(MONITOR_SERVERS *ptr); void mon_hangup_failed_servers(MONITOR *monitor); /** - * @brief Serialize a monitor to a file + * @brief Serialize the servers of a monitor to a file * * This partially converts @c monitor into an INI format file. Only the servers * of the monitor are serialized. This allows the monitor to keep monitoring @@ -258,6 +259,16 @@ void mon_hangup_failed_servers(MONITOR *monitor); */ bool monitor_serialize_servers(const MONITOR *monitor); +/** + * @brief Serialize a monitor to a file + * + * This converts the static configuration of the monitor into an INI format file. + * + * @param monitor Monitor to serialize + * @return True if serialization was successful + */ +bool monitor_serialize(const MONITOR *monitor); + /** * Check if a monitor uses @c servers * @param server Server that is queried diff --git a/server/core/config_runtime.c b/server/core/config_runtime.c index 7e2619646..a5f5dcb8f 100644 --- a/server/core/config_runtime.c +++ b/server/core/config_runtime.c @@ -280,6 +280,11 @@ bool runtime_alter_server(SERVER *server, char *key, char *value) valid = false; } + if (valid) + { + server_serialize(server); + } + spinlock_release(&crt_lock); return valid; } @@ -357,6 +362,11 @@ bool runtime_alter_monitor(MONITOR *monitor, char *key, char *value) } } + if (valid) + { + monitor_serialize(monitor); + } + spinlock_release(&crt_lock); return valid; } @@ -480,3 +490,58 @@ bool runtime_destroy_listener(SERVICE *service, const char *name) spinlock_release(&crt_lock); return rval; } + +bool runtime_create_monitor(const char *name, const char *module) +{ + spinlock_acquire(&crt_lock); + bool rval = false; + MONITOR *monitor = monitor_alloc((char*)name, (char*)module); + + if (monitor && monitor_serialize(monitor)) + { + rval = true; + } + + spinlock_release(&crt_lock); + return rval; +} + +bool runtime_destroy_monitor(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); + MXS_NOTICE("Destroyed monitor '%s'. The monitor will be removed " + "after the next restart of MaxScale.", monitor->name); + } + + spinlock_release(&crt_lock); + return rval; +} diff --git a/server/core/monitor.c b/server/core/monitor.c index 6a22ee70a..9542b912b 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -73,13 +73,15 @@ MONITOR * monitor_alloc(char *name, char *module) { name = MXS_STRDUP(name); + char *my_module = MXS_STRDUP(module); MONITOR *mon = (MONITOR *)MXS_MALLOC(sizeof(MONITOR)); - if (!name || !mon) + if (!name || !mon || !my_module) { MXS_FREE(name); MXS_FREE(mon); + MXS_FREE(my_module); return NULL; } @@ -92,6 +94,7 @@ monitor_alloc(char *name, char *module) } mon->state = MONITOR_STATE_ALLOC; mon->name = name; + mon->module_name = module; mon->handle = NULL; mon->databases = NULL; *mon->password = '\0'; @@ -144,6 +147,7 @@ monitor_free(MONITOR *mon) free_config_parameter(mon->parameters); monitor_server_free_all(mon->databases); MXS_FREE(mon->name); + MXS_FREE(mon->module_name); MXS_FREE(mon); } @@ -1261,7 +1265,7 @@ bool monitor_server_in_use(const SERVER *server) * @param filename Filename where configuration is written * @return True on success, false on error */ -static bool create_monitor_config(const MONITOR *monitor, const char *filename) +static bool create_monitor_server_config(const MONITOR *monitor, const char *filename) { int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); @@ -1300,7 +1304,75 @@ static bool create_monitor_config(const MONITOR *monitor, const char *filename) return true; } +static bool create_monitor_config(const MONITOR *monitor, 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 monitor '%s': %d, %s", + filename, monitor->name, errno, strerror_r(errno, errbuf, sizeof(errbuf))); + return false; + } + + /** + * Only additional parameters are added to the configuration. This prevents + * duplication or addition of parameters that don't support it. + * + * TODO: Check for return values on all of the dprintf calls + */ + dprintf(file, "[%s]\n", monitor->name); + dprintf(file, "module=%s\n", monitor->module_name); + dprintf(file, "user=%s\n", monitor->user); + dprintf(file, "password=%s\n", monitor->password); + dprintf(file, "monitor_interval=%lu\n", monitor->interval); + dprintf(file, "backend_connect_timeout=%d\n", monitor->connect_timeout); + dprintf(file, "backend_write_timeout=%d\n", monitor->write_timeout); + dprintf(file, "backend_read_timeout=%d\n", monitor->read_timeout); + close(file); + + return true; +} + bool monitor_serialize_servers(const MONITOR *monitor) +{ + bool rval = false; + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "%s/%s.cnf.tmp", get_config_persistdir(), + monitor->name); + + if (unlink(filename) == -1 && errno != ENOENT) + { + char err[MXS_STRERROR_BUFLEN]; + MXS_ERROR("Failed to remove temporary monitor configuration at '%s': %d, %s", + filename, errno, strerror_r(errno, err, sizeof(err))); + } + else if (create_monitor_server_config(monitor, 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 monitor configuration at '%s': %d, %s", + filename, errno, strerror_r(errno, err, sizeof(err))); + } + } + + return rval; +} + +bool monitor_serialize(const MONITOR *monitor) { bool rval = false; char filename[PATH_MAX];