diff --git a/Documentation/Tutorials/MaxScale-Information-Schema.md b/Documentation/Tutorials/MaxScale-Information-Schema.md index 66bb3ab5b..fcbcd3937 100644 --- a/Documentation/Tutorials/MaxScale-Information-Schema.md +++ b/Documentation/Tutorials/MaxScale-Information-Schema.md @@ -67,6 +67,8 @@ Uptime: 72 Threads: 1 Sessions: 11 The SQL command used to interact with maxinfo is the show command, a variety of show commands are available and will be described in the following sections. +Maxinfo also supports the `FLUSH LOGS`, `SET SERVER ` and `CLEAR SERVER ` commands. These behave the same as their MaxAdmin counterpart. + ## Show variables The show variables command will display a set of name and value pairs for a number of MaxScale system variables. diff --git a/server/core/server.c b/server/core/server.c index 919b5bcbe..7ade3b499 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -907,3 +907,33 @@ server_update_port(SERVER *server, unsigned short port) spinlock_release(&server_spin); } +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 + */ +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; +} diff --git a/server/include/server.h b/server/include/server.h index b3d43c98f..08d2a76ff 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -208,4 +208,5 @@ extern DCB *server_get_persistent(SERVER *, char *, const char *); extern void server_update_address(SERVER *, char *); extern void server_update_port(SERVER *, unsigned short); extern RESULTSET *serverGetList(); +extern unsigned int server_map_status(char *str); #endif diff --git a/server/modules/include/maxinfo.h b/server/modules/include/maxinfo.h index 040433161..f845c3a3d 100644 --- a/server/modules/include/maxinfo.h +++ b/server/modules/include/maxinfo.h @@ -73,7 +73,10 @@ typedef enum MAXOP_LITERAL, MAXOP_PREDICATE, MAXOP_LIKE, - MAXOP_EQUAL + MAXOP_EQUAL, + MAXOP_FLUSH, + MAXOP_SET, + MAXOP_CLEAR } MAXINFO_OPERATOR; /** @@ -109,6 +112,9 @@ typedef struct maxinfo_tree { #define LT_FROM 7 #define LT_STAR 8 #define LT_VARIABLE 9 +#define LT_FLUSH 10 +#define LT_SET 11 +#define LT_CLEAR 12 /** diff --git a/server/modules/routing/maxinfo/maxinfo_exec.c b/server/modules/routing/maxinfo/maxinfo_exec.c index d51f81d0d..3b8e1a2d7 100644 --- a/server/modules/routing/maxinfo/maxinfo_exec.c +++ b/server/modules/routing/maxinfo/maxinfo_exec.c @@ -54,7 +54,10 @@ static void exec_select(DCB *dcb, MAXINFO_TREE *tree); static void exec_show_variables(DCB *dcb, MAXINFO_TREE *filter); static void exec_show_status(DCB *dcb, MAXINFO_TREE *filter); 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); +void maxinfo_send_ok(DCB *dcb); /** * Execute a parse tree and return the result set or runtime error * @@ -72,6 +75,17 @@ maxinfo_execute(DCB *dcb, MAXINFO_TREE *tree) case MAXOP_SELECT: exec_select(dcb, tree); break; + + case MAXOP_FLUSH: + exec_flush(dcb, tree); + break; + case MAXOP_SET: + exec_set(dcb, tree); + break; + case MAXOP_CLEAR: + exec_clear(dcb, tree); + break; + case MAXOP_TABLE: case MAXOP_COLUMNS: case MAXOP_LITERAL: @@ -274,6 +288,221 @@ char errmsg[120]; LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, errmsg))); } +/** + * Flush all logs to disk and rotate them. + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +void exec_flush_logs(DCB *dcb, MAXINFO_TREE *tree) +{ + skygw_log_rotate(LE); + skygw_log_rotate(LM); + skygw_log_rotate(LT); + skygw_log_rotate(LD); + maxinfo_send_ok(dcb); +} + +/** + * The table of flush commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} flush_commands[] = { + { "logs", exec_flush_logs}, + { NULL, NULL} +}; + +/** + * Execute a flush command parse tree and return the result set or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_flush(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; flush_commands[i].name; i++) + { + if (strcasecmp(flush_commands[i].name, tree->value) == 0) + { + (*flush_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported flush command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + +/** + * Set the server status. + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_set_server(DCB *dcb, MAXINFO_TREE *tree) +{ + SERVER* server = server_find_by_unique_name(tree->value); + char errmsg[120]; + + if (server) + { + int status = server_map_status(tree->right->value); + if (status != 0) + { + server_set_status(server, status); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->right->value) > 80) // Prevent buffer overrun + { + tree->right->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->right->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + 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); + } +} + +/** + * The table of set commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} set_commands[] = { + { "server", exec_set_server}, + { NULL, NULL} +}; + +/** + * Execute a set command parse tree and return the result set or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_set(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; set_commands[i].name; i++) + { + if (strcasecmp(set_commands[i].name, tree->value) == 0) + { + (*set_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported set command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + +/** + * Clear the server status. + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_clear_server(DCB *dcb, MAXINFO_TREE *tree) +{ + SERVER* server = server_find_by_unique_name(tree->value); + char errmsg[120]; + + if (server) + { + int status = server_map_status(tree->right->value); + if (status != 0) + { + server_clear_status(server, status); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->right->value) > 80) // Prevent buffer overrun + { + tree->right->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->right->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + 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); + } +} + +/** + * The table of clear commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} clear_commands[] = { + { "server", exec_clear_server}, + { NULL, NULL} +}; + +/** + * Execute a clear command parse tree and return the result set or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_clear(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; clear_commands[i].name; i++) + { + if (strcasecmp(clear_commands[i].name, tree->value) == 0) + { + (*clear_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported clear command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + /** * Return the current MaxScale version * @@ -764,3 +993,25 @@ extern char *strcasestr(); return rval; } } + +/** + * Send an OK packet to the client. + * @param dcb The DCB that connects to the client + */ +void maxinfo_send_ok(DCB *dcb) +{ + static const char ok_packet[] ={ + 0x07, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00 + }; + + GWBUF* buffer = gwbuf_alloc(sizeof(ok_packet)); + + if (buffer) + { + memcpy(buffer->start, ok_packet, sizeof(ok_packet)); + dcb->func.write(dcb, buffer); + } +} \ No newline at end of file diff --git a/server/modules/routing/maxinfo/maxinfo_parse.c b/server/modules/routing/maxinfo/maxinfo_parse.c index bdef7ef26..fe5719d3b 100644 --- a/server/modules/routing/maxinfo/maxinfo_parse.c +++ b/server/modules/routing/maxinfo/maxinfo_parse.c @@ -50,7 +50,8 @@ static void free_tree(MAXINFO_TREE *); static char *fetch_token(char *, int *, char **); static MAXINFO_TREE *parse_column_list(char **sql); static MAXINFO_TREE *parse_table_name(char **sql); - +MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr, + PARSE_ERROR *parse_error); /** * Parse a SQL subset for the maxinfo plugin and return a parse tree @@ -111,6 +112,23 @@ MAXINFO_TREE *col, *table; table = parse_table_name(&ptr); return make_tree_node(MAXOP_SELECT, NULL, col, table); #endif + case LT_FLUSH: + free(text); // not needed + ptr = fetch_token(ptr, &token, &text); + return make_tree_node(MAXOP_FLUSH, text, NULL, NULL); + + case LT_SET: + free(text); // not needed + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_SET, text, NULL, NULL); + return maxinfo_parse_literals(tree, 2, ptr, parse_error); + + case LT_CLEAR: + free(text); // not needed + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_CLEAR, text, NULL, NULL); + return maxinfo_parse_literals(tree, 2, ptr, parse_error); + break; default: *parse_error = PARSE_SYNTAX_ERROR; return NULL; @@ -242,6 +260,9 @@ static struct { { "=", LT_EQUAL }, { ",", LT_COMMA }, { "*", LT_STAR }, + { "flush", LT_FLUSH }, + { "set", LT_SET }, + { "clear", LT_CLEAR }, { NULL, 0 } }; @@ -322,3 +343,36 @@ int i; *token = LT_STRING; return s2; } + +/** + * Parse the remaining arguments as literals. + * @param tree Previous head of the parse tree + * @param min_args Minimum required number of arguments + * @param ptr Pointer to client command + * @param parse_error Pointer to parsing error to fill + * @return Parsed tree or NULL if parsing failed + */ +MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr, + PARSE_ERROR *parse_error) +{ + int token; + MAXINFO_TREE* node = tree; + char *text; + for(int i = 0; i < min_args; i++) + { + if((ptr = fetch_token(ptr, &token, &text)) == NULL || + (node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL) + { + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + if(ptr) + { + free(text); + } + return NULL; + } + node = node->right; + } + + return tree; +}