Fix to MXS-437: https://mariadb.atlassian.net/browse/MXS-437
Maxinfo now allows users to flush logs and change the server status through the MySQL interface.
This commit is contained in:
@ -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.
|
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 <name> <status>` and `CLEAR SERVER <name> <status>` commands. These behave the same as their MaxAdmin counterpart.
|
||||||
|
|
||||||
## Show variables
|
## Show variables
|
||||||
|
|
||||||
The show variables command will display a set of name and value pairs for a number of MaxScale system variables.
|
The show variables command will display a set of name and value pairs for a number of MaxScale system variables.
|
||||||
|
@ -907,3 +907,33 @@ server_update_port(SERVER *server, unsigned short port)
|
|||||||
spinlock_release(&server_spin);
|
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;
|
||||||
|
}
|
||||||
|
@ -208,4 +208,5 @@ extern DCB *server_get_persistent(SERVER *, char *, const char *);
|
|||||||
extern void server_update_address(SERVER *, char *);
|
extern void server_update_address(SERVER *, char *);
|
||||||
extern void server_update_port(SERVER *, unsigned short);
|
extern void server_update_port(SERVER *, unsigned short);
|
||||||
extern RESULTSET *serverGetList();
|
extern RESULTSET *serverGetList();
|
||||||
|
extern unsigned int server_map_status(char *str);
|
||||||
#endif
|
#endif
|
||||||
|
@ -73,7 +73,10 @@ typedef enum
|
|||||||
MAXOP_LITERAL,
|
MAXOP_LITERAL,
|
||||||
MAXOP_PREDICATE,
|
MAXOP_PREDICATE,
|
||||||
MAXOP_LIKE,
|
MAXOP_LIKE,
|
||||||
MAXOP_EQUAL
|
MAXOP_EQUAL,
|
||||||
|
MAXOP_FLUSH,
|
||||||
|
MAXOP_SET,
|
||||||
|
MAXOP_CLEAR
|
||||||
} MAXINFO_OPERATOR;
|
} MAXINFO_OPERATOR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,6 +112,9 @@ typedef struct maxinfo_tree {
|
|||||||
#define LT_FROM 7
|
#define LT_FROM 7
|
||||||
#define LT_STAR 8
|
#define LT_STAR 8
|
||||||
#define LT_VARIABLE 9
|
#define LT_VARIABLE 9
|
||||||
|
#define LT_FLUSH 10
|
||||||
|
#define LT_SET 11
|
||||||
|
#define LT_CLEAR 12
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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_variables(DCB *dcb, MAXINFO_TREE *filter);
|
||||||
static void exec_show_status(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 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
|
* 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:
|
case MAXOP_SELECT:
|
||||||
exec_select(dcb, tree);
|
exec_select(dcb, tree);
|
||||||
break;
|
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_TABLE:
|
||||||
case MAXOP_COLUMNS:
|
case MAXOP_COLUMNS:
|
||||||
case MAXOP_LITERAL:
|
case MAXOP_LITERAL:
|
||||||
@ -274,6 +288,221 @@ char errmsg[120];
|
|||||||
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, errmsg)));
|
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
|
* Return the current MaxScale version
|
||||||
*
|
*
|
||||||
@ -764,3 +993,25 @@ extern char *strcasestr();
|
|||||||
return rval;
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -50,7 +50,8 @@ static void free_tree(MAXINFO_TREE *);
|
|||||||
static char *fetch_token(char *, int *, char **);
|
static char *fetch_token(char *, int *, char **);
|
||||||
static MAXINFO_TREE *parse_column_list(char **sql);
|
static MAXINFO_TREE *parse_column_list(char **sql);
|
||||||
static MAXINFO_TREE *parse_table_name(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
|
* 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);
|
table = parse_table_name(&ptr);
|
||||||
return make_tree_node(MAXOP_SELECT, NULL, col, table);
|
return make_tree_node(MAXOP_SELECT, NULL, col, table);
|
||||||
#endif
|
#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:
|
default:
|
||||||
*parse_error = PARSE_SYNTAX_ERROR;
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -242,6 +260,9 @@ static struct {
|
|||||||
{ "=", LT_EQUAL },
|
{ "=", LT_EQUAL },
|
||||||
{ ",", LT_COMMA },
|
{ ",", LT_COMMA },
|
||||||
{ "*", LT_STAR },
|
{ "*", LT_STAR },
|
||||||
|
{ "flush", LT_FLUSH },
|
||||||
|
{ "set", LT_SET },
|
||||||
|
{ "clear", LT_CLEAR },
|
||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -322,3 +343,36 @@ int i;
|
|||||||
*token = LT_STRING;
|
*token = LT_STRING;
|
||||||
return s2;
|
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;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user