From 9ab532696030a4eb24a7662571caa038f085d3f5 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 31 Oct 2015 19:07:19 +0200 Subject: [PATCH] Fix to MXS-438: https://mariadb.atlassian.net/browse/MXS-438 Maxinfo now supports the shutdown command which shuts down a service, monitor or MaxScale itself and the restart command which restarts a stopped monitor or service. --- server/core/server.c | 5 + server/include/server.h | 1 + server/modules/include/maxinfo.h | 6 +- server/modules/routing/debugcmd.c | 34 --- server/modules/routing/maxinfo/maxinfo_exec.c | 238 ++++++++++++++++++ .../modules/routing/maxinfo/maxinfo_parse.c | 75 ++++-- 6 files changed, 310 insertions(+), 49 deletions(-) diff --git a/server/core/server.c b/server/core/server.c index 7ade3b499..11633594c 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -82,6 +82,7 @@ SERVER *server; server->rlag = -2; server->master_id = -1; server->depth = -1; + spinlock_init(&server->lock); server->persistent = NULL; server->persistmax = 0; spinlock_init(&server->persistlock); @@ -663,6 +664,7 @@ char *status = NULL; void server_set_status(SERVER *server, int bit) { + spinlock_acquire(&server->lock); server->status |= bit; /** clear error logged flag before the next failure */ @@ -670,6 +672,7 @@ server_set_status(SERVER *server, int bit) { server->master_err_is_logged = false; } + spinlock_release(&server->lock); } /** @@ -681,7 +684,9 @@ server_set_status(SERVER *server, int bit) void server_clear_status(SERVER *server, int bit) { + spinlock_acquire(&server->lock); server->status &= ~bit; + spinlock_release(&server->lock); } /** diff --git a/server/include/server.h b/server/include/server.h index 08d2a76ff..030e034ba 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -81,6 +81,7 @@ typedef struct server { #if defined(SS_DEBUG) skygw_chk_t server_chk_top; #endif + SPINLOCK lock; /**< Common access lock */ char *unique_name; /**< Unique name for the server */ char *name; /**< Server name/IP address*/ unsigned short port; /**< Port to listen on */ diff --git a/server/modules/include/maxinfo.h b/server/modules/include/maxinfo.h index f845c3a3d..5f7cb6493 100644 --- a/server/modules/include/maxinfo.h +++ b/server/modules/include/maxinfo.h @@ -76,7 +76,9 @@ typedef enum MAXOP_EQUAL, MAXOP_FLUSH, MAXOP_SET, - MAXOP_CLEAR + MAXOP_CLEAR, + MAXOP_SHUTDOWN, + MAXOP_RESTART } MAXINFO_OPERATOR; /** @@ -115,6 +117,8 @@ typedef struct maxinfo_tree { #define LT_FLUSH 10 #define LT_SET 11 #define LT_CLEAR 12 +#define LT_SHUTDOWN 13 +#define LT_RESTART 14 /** diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index 73411dc83..414692e76 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -1092,40 +1092,6 @@ restart_service(DCB *dcb, SERVICE *service) serviceRestart(service); } -static struct { - char *str; - unsigned int bit; -} ServerBits[] = { - { "running", SERVER_RUNNING }, - { "master", SERVER_MASTER }, - { "slave", SERVER_SLAVE }, - { "synced", SERVER_JOINED }, - { "ndb", SERVER_NDB }, - { "maintenance", SERVER_MAINT }, - { "maint", SERVER_MAINT }, - { NULL, 0 } -}; -/** - * Map the server status bit - * - * @param str String representation - * @return bit value or 0 on error - */ -static unsigned int -server_map_status(char *str) -{ - int i; - - for (i = 0; ServerBits[i].str; i++) - { - if (!strcasecmp(str, ServerBits[i].str)) - { - return ServerBits[i].bit; - } - } - return 0; -} - /** * Set the status bit of a server * diff --git a/server/modules/routing/maxinfo/maxinfo_exec.c b/server/modules/routing/maxinfo/maxinfo_exec.c index 3b8e1a2d7..3c2b0849d 100644 --- a/server/modules/routing/maxinfo/maxinfo_exec.c +++ b/server/modules/routing/maxinfo/maxinfo_exec.c @@ -57,6 +57,8 @@ static int maxinfo_pattern_match(char *pattern, char *str); static void exec_flush(DCB *dcb, MAXINFO_TREE *tree); static void exec_set(DCB *dcb, MAXINFO_TREE *tree); static void exec_clear(DCB *dcb, MAXINFO_TREE *tree); +static void exec_shutdown(DCB *dcb, MAXINFO_TREE *tree); +static void exec_restart(DCB *dcb, MAXINFO_TREE *tree); void maxinfo_send_ok(DCB *dcb); /** * Execute a parse tree and return the result set or runtime error @@ -85,6 +87,12 @@ maxinfo_execute(DCB *dcb, MAXINFO_TREE *tree) case MAXOP_CLEAR: exec_clear(dcb, tree); break; + case MAXOP_SHUTDOWN: + exec_shutdown(dcb, tree); + break; + case MAXOP_RESTART: + exec_restart(dcb, tree); + break; case MAXOP_TABLE: case MAXOP_COLUMNS: @@ -503,6 +511,236 @@ exec_clear(DCB *dcb, MAXINFO_TREE *tree) skygw_log_write(LE, errmsg); } +extern void shutdown_server(); + +/** + * MaxScale shutdown + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_shutdown_maxscale(DCB *dcb, MAXINFO_TREE *tree) +{ + shutdown_server(); + maxinfo_send_ok(dcb); +} + +/** + * Stop a monitor + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_shutdown_monitor(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + MONITOR* monitor = monitor_find(tree->value); + if (monitor) + { + monitorStop(monitor); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'SHUTDOWN MONITOR'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * Stop a service + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_shutdown_service(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + SERVICE* service = service_find(tree->value); + if (service) + { + serviceStop(service); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'SHUTDOWN SERVICE'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * The table of shutdown commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} shutdown_commands[] = { + { "maxscale", exec_shutdown_maxscale}, + { "monitor", exec_shutdown_monitor}, + { "service", exec_shutdown_service}, + { NULL, NULL} +}; + +/** + * Execute a shutdown command parse tree and return OK or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_shutdown(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; shutdown_commands[i].name; i++) + { + if (strcasecmp(shutdown_commands[i].name, tree->value) == 0) + { + (*shutdown_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported shutdown command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + +/** + * Restart a monitor + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_restart_monitor(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + MONITOR* monitor = monitor_find(tree->value); + if (monitor) + { + monitorStart(monitor, NULL); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'RESTART MONITOR'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * Restart a service + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_restart_service(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + SERVICE* service = service_find(tree->value); + if (service) + { + serviceRestart(service); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'RESTART SERVICE'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * The table of restart commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} restart_commands[] = { + { "monitor", exec_restart_monitor}, + { "service", exec_restart_service}, + { NULL, NULL} +}; + +/** + * Execute a restart command parse tree and return OK or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_restart(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; restart_commands[i].name; i++) + { + if (strcasecmp(restart_commands[i].name, tree->value) == 0) + { + (*restart_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported restart command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + /** * Return the current MaxScale version * diff --git a/server/modules/routing/maxinfo/maxinfo_parse.c b/server/modules/routing/maxinfo/maxinfo_parse.c index fe5719d3b..e63d6b2cf 100644 --- a/server/modules/routing/maxinfo/maxinfo_parse.c +++ b/server/modules/routing/maxinfo/maxinfo_parse.c @@ -117,6 +117,50 @@ MAXINFO_TREE *col, *table; ptr = fetch_token(ptr, &token, &text); return make_tree_node(MAXOP_FLUSH, text, NULL, NULL); + case LT_SHUTDOWN: + free(text); + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_SHUTDOWN, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) == NULL) + { + /** Possibly SHUTDOWN MAXSCALE */ + return tree; + } + tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) != NULL) + { + /** Unknown token after SHUTDOWN MONITOR|SERVICE */ + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + return NULL; + } + return tree; + + case LT_RESTART: + free(text); + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_RESTART, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) == NULL) + { + /** Missing token for RESTART MONITOR|SERVICE */ + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + return NULL; + } + tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) != NULL) + { + /** Unknown token after RESTART MONITOR|SERVICE */ + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + return NULL; + } + return tree; + case LT_SET: free(text); // not needed ptr = fetch_token(ptr, &token, &text); @@ -249,21 +293,24 @@ free_tree(MAXINFO_TREE *tree) /** * The set of keywords known to the tokeniser */ -static struct { - char *text; - int token; +static struct +{ + char *text; + int token; } keywords[] = { - { "show", LT_SHOW }, - { "select", LT_SELECT }, - { "from", LT_FROM }, - { "like", LT_LIKE }, - { "=", LT_EQUAL }, - { ",", LT_COMMA }, - { "*", LT_STAR }, - { "flush", LT_FLUSH }, - { "set", LT_SET }, - { "clear", LT_CLEAR }, - { NULL, 0 } + { "show", LT_SHOW}, + { "select", LT_SELECT}, + { "from", LT_FROM}, + { "like", LT_LIKE}, + { "=", LT_EQUAL}, + { ",", LT_COMMA}, + { "*", LT_STAR}, + { "flush", LT_FLUSH}, + { "set", LT_SET}, + { "clear", LT_CLEAR}, + { "shutdown", LT_SHUTDOWN}, + { "restart", LT_RESTART}, + { NULL, 0} }; /**