diff --git a/include/maxscale/server.h b/include/maxscale/server.h index a67199fe2..b58377c5d 100644 --- a/include/maxscale/server.h +++ b/include/maxscale/server.h @@ -234,13 +234,12 @@ extern void server_update_ssl(SERVER *server, const char *key, const char *value * @brief Serialize a server to a file * * This converts @c server into an INI format file. This allows created servers - * to be persisted to disk. A new file is only created if the file pointed by - * @c filename does not exist at the time this function is called. + * to be persisted to disk. This will replace any existing files with the same + * name. * * @param server Server to serialize - * @param filename Path to a file where the server is persisted * @return False if the serialization of the server fails, true if it was successful */ -bool server_serialize(SERVER *server, const char *filename); +bool server_serialize(SERVER *server); MXS_END_DECLS diff --git a/server/core/server.c b/server/core/server.c index 2e5359eef..a2abb488d 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -47,6 +47,7 @@ #include #include #include +#include static SPINLOCK server_spin = SPINLOCK_INIT; static SERVER *allServers = NULL; @@ -1062,7 +1063,14 @@ bool server_set_version_string(SERVER* server, const char* string) return rval; } -bool server_serialize(SERVER *server, const char *filename) +/** + * Creates a server configuration at the location pointed by @c filename + * + * @param server Server to serialize into a configuration + * @param filename Filename where configuration is written + * @return True on success, false on error + */ +static bool create_server_config(SERVER *server, const char *filename) { int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); @@ -1162,6 +1170,43 @@ bool server_serialize(SERVER *server, const char *filename) return true; } +bool server_serialize(SERVER *server) +{ + bool rval = false; + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "%s/%s.cnf.tmp", get_config_persistdir(), + server->unique_name); + + if (unlink(filename) == -1 && errno != ENOENT) + { + char err[MXS_STRERROR_BUFLEN]; + MXS_ERROR("Failed to remove temporary server configuration at '%s': %d, %s", + filename, errno, strerror_r(errno, err, sizeof(err))); + } + else if (create_server_config(server, 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 server configuration at '%s': %d, %s", + filename, errno, strerror_r(errno, err, sizeof(err))); + } + } + + return rval; +} + bool server_is_ssl_parameter(const char *key) { // TODO: Implement this diff --git a/server/core/test/testserver.c b/server/core/test/testserver.c index 5a1f6f0ba..bb195885e 100644 --- a/server/core/test/testserver.c +++ b/server/core/test/testserver.c @@ -140,31 +140,37 @@ bool test_load_config(const char *input, SERVER *server) bool test_serialize() { char name[] = "serialized-server"; + char config_name[] = "serialized-server.cnf"; + char old_config_name[] = "serialized-server.cnf.old"; + char *persist_dir = MXS_STRDUP_A("./"); + set_config_persistdir(persist_dir); SERVER *server = server_alloc("127.0.0.1", "HTTPD", 9876, "NullAuthAllow", "fake=option"); TEST(server, "Server allocation failed"); server_set_unique_name(server, name); - /** Make sure the file doesn't exist */ - unlink("./server.cnf"); + /** Make sure the files don't exist */ + unlink(config_name); + unlink(old_config_name); /** Serialize server to disk */ - TEST(server_serialize(server, "./server.cnf"), "Failed to synchronize original server"); + TEST(server_serialize(server), "Failed to synchronize original server"); /** Load it again */ - TEST(test_load_config("./server.cnf", server), "Failed to load the serialized server"); + TEST(test_load_config(config_name, server), "Failed to load the serialized server"); /** We should have two identical servers */ SERVER *created = server_find_by_unique_name(name); TEST(created->next == server, "We should end up with two servers"); - /** Make sure the file doesn't exist */ - unlink("./server-created.cnf"); + rename(config_name, old_config_name); /** Serialize the loaded server to disk */ - TEST(server_serialize(created, "./server-created.cnf"), "Failed to synchronize the copied server"); + TEST(server_serialize(created), "Failed to synchronize the copied server"); /** Check that they serialize to identical files */ - TEST(system("diff ./server.cnf ./server-created.cnf") == 0, "The files are not identical"); + char cmd[1024]; + sprintf(cmd, "diff ./%s ./%s", config_name, old_config_name); + TEST(system(cmd) == 0, "The files are not identical"); return true; } diff --git a/server/modules/routing/debugcli/debugcmd.c b/server/modules/routing/debugcli/debugcmd.c index a59785d91..3c0fb17dc 100644 --- a/server/modules/routing/debugcli/debugcmd.c +++ b/server/modules/routing/debugcli/debugcmd.c @@ -987,6 +987,17 @@ struct subcommand flushoptions[] = /** This is used to prevent concurrent creation or removal of servers */ static SPINLOCK server_mod_lock = SPINLOCK_INIT; +/** + * Create a new server + * + * @param dcb Client DCB + * @param name Server name + * @param address Server network address + * @param port Server port + * @param protocol Protocol, NULL for default (MySQLBackend) + * @param authenticator Authenticator module, NULL for default (MySQLBackendAuth) + * @param authenticator_options Authenticator options, NULL for no options + */ static void createServer(DCB *dcb, char *name, char *address, char *port, char *protocol, char *authenticator, char *authenticator_options) { @@ -1005,8 +1016,18 @@ static void createServer(DCB *dcb, char *name, char *address, char *port, if (server) { server_set_unique_name(server, name); - // TODO: Serialize the server to disk - dcb_printf(dcb, "Created server '%s'\n", name); + + if (server_serialize(server)) + { + dcb_printf(dcb, "Created server '%s'\n", name); + } + else + { + dcb_printf(dcb, "WARNING: The server was added to the runtime " + "configuration but persisting the new server to disk " + "failed. This server will NOT be loaded when MaxScale " + "is restarted. See log file for more details on why it failed.\n"); + } } else {