Fix memory leaks in maxinfo (modified for 2.0 and develop-branch)
MXS-1009. This commit adds a gwbuf_free after maxinfo_execute() to free a buffer with an sql-query after it has been processed. Also, the parse tree in maxinfo_execute_query() is now freed. The tree_free- function was renamed to maxinfo_tree_free, since it is now globally available. This commit has additional changes (in relation to the 1.4.4 branch) to remove errors caused by differences in the html and sql-sides of MaxInfo.
This commit is contained in:
@ -132,6 +132,7 @@ typedef enum
|
|||||||
|
|
||||||
|
|
||||||
extern MAXINFO_TREE *maxinfo_parse(char *, PARSE_ERROR *);
|
extern MAXINFO_TREE *maxinfo_parse(char *, PARSE_ERROR *);
|
||||||
|
extern void maxinfo_free_tree(MAXINFO_TREE *);
|
||||||
extern void maxinfo_execute(DCB *, MAXINFO_TREE *);
|
extern void maxinfo_execute(DCB *, MAXINFO_TREE *);
|
||||||
extern void maxinfo_send_error(DCB *, int, char *);
|
extern void maxinfo_send_error(DCB *, int, char *);
|
||||||
extern void maxinfo_send_parse_error(DCB *, char *, PARSE_ERROR);
|
extern void maxinfo_send_parse_error(DCB *, char *, PARSE_ERROR);
|
||||||
|
@ -335,54 +335,60 @@ static void handleError(
|
|||||||
static int
|
static int
|
||||||
execute(ROUTER *rinstance, void *router_session, GWBUF *queue)
|
execute(ROUTER *rinstance, void *router_session, GWBUF *queue)
|
||||||
{
|
{
|
||||||
INFO_INSTANCE *instance = (INFO_INSTANCE *)rinstance;
|
INFO_INSTANCE *instance = (INFO_INSTANCE *)rinstance;
|
||||||
INFO_SESSION *session = (INFO_SESSION *)router_session;
|
INFO_SESSION *session = (INFO_SESSION *)router_session;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
int length, len, residual;
|
int length, len, residual;
|
||||||
char *sql;
|
char *sql;
|
||||||
|
|
||||||
if (GWBUF_TYPE(queue) == GWBUF_TYPE_HTTP)
|
if (GWBUF_TYPE(queue) == GWBUF_TYPE_HTTP)
|
||||||
{
|
{
|
||||||
return handle_url(instance, session, queue);
|
return handle_url(instance, session, queue);
|
||||||
}
|
}
|
||||||
if (session->queue)
|
if (session->queue)
|
||||||
{
|
{
|
||||||
queue = gwbuf_append(session->queue, queue);
|
queue = gwbuf_append(session->queue, queue);
|
||||||
session->queue = NULL;
|
session->queue = NULL;
|
||||||
queue = gwbuf_make_contiguous(queue);
|
queue = gwbuf_make_contiguous(queue);
|
||||||
}
|
}
|
||||||
data = (uint8_t *)GWBUF_DATA(queue);
|
data = (uint8_t *)GWBUF_DATA(queue);
|
||||||
length = data[0] + (data[1] << 8) + (data[2] << 16);
|
length = data[0] + (data[1] << 8) + (data[2] << 16);
|
||||||
if (length + 4 > GWBUF_LENGTH(queue))
|
if (length + 4 > GWBUF_LENGTH(queue))
|
||||||
{
|
{
|
||||||
// Incomplete packet, must be buffered
|
// Incomplete packet, must be buffered
|
||||||
session->queue = queue;
|
session->queue = queue;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have a complete request in a signle buffer
|
int rc = 1;
|
||||||
if (modutil_MySQL_Query(queue, &sql, &len, &residual))
|
// We have a complete request in a single buffer
|
||||||
{
|
if (modutil_MySQL_Query(queue, &sql, &len, &residual))
|
||||||
sql = strndup(sql, len);
|
{
|
||||||
int rc = maxinfo_execute_query(instance, session, sql);
|
sql = strndup(sql, len);
|
||||||
free(sql);
|
rc = maxinfo_execute_query(instance, session, sql);
|
||||||
return rc;
|
free(sql);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (MYSQL_COMMAND(queue))
|
switch (MYSQL_COMMAND(queue))
|
||||||
{
|
{
|
||||||
case COM_PING:
|
case COM_PING:
|
||||||
return maxinfo_ping(instance, session, queue);
|
rc = maxinfo_ping(instance, session, queue);
|
||||||
case COM_STATISTICS:
|
break;
|
||||||
return maxinfo_statistics(instance, session, queue);
|
case COM_STATISTICS:
|
||||||
default:
|
rc = maxinfo_statistics(instance, session, queue);
|
||||||
MXS_ERROR("maxinfo: Unexpected MySQL command 0x%x",
|
break;
|
||||||
MYSQL_COMMAND(queue));
|
case COM_QUIT:
|
||||||
}
|
break;
|
||||||
}
|
default:
|
||||||
|
MXS_ERROR("maxinfo: Unexpected MySQL command 0x%x",
|
||||||
return 1;
|
MYSQL_COMMAND(queue));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// MaxInfo doesn't route the data forward so it should be freed.
|
||||||
|
gwbuf_free(queue);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -601,51 +607,54 @@ uint8_t *ptr;
|
|||||||
static int
|
static int
|
||||||
maxinfo_execute_query(INFO_INSTANCE *instance, INFO_SESSION *session, char *sql)
|
maxinfo_execute_query(INFO_INSTANCE *instance, INFO_SESSION *session, char *sql)
|
||||||
{
|
{
|
||||||
MAXINFO_TREE *tree;
|
MAXINFO_TREE *tree;
|
||||||
PARSE_ERROR err;
|
PARSE_ERROR err;
|
||||||
|
|
||||||
MXS_INFO("maxinfo: SQL statement: '%s' for 0x%p.",
|
MXS_INFO("maxinfo: SQL statement: '%s' for 0x%p.",
|
||||||
sql, session->dcb);
|
sql, session->dcb);
|
||||||
if (strcmp(sql, "select @@version_comment limit 1") == 0)
|
if (strcmp(sql, "select @@version_comment limit 1") == 0)
|
||||||
{
|
{
|
||||||
respond_vercom(session->dcb);
|
respond_vercom(session->dcb);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* Below is a kludge for MonYog, if we see
|
/* Below is a kludge for MonYog, if we see
|
||||||
* select unix_timestamp... as starttime
|
* select unix_timestamp... as starttime
|
||||||
* just return the starttime of MaxScale
|
* just return the starttime of MaxScale
|
||||||
*/
|
*/
|
||||||
if (strncasecmp(sql, "select UNIX_TIMESTAMP",
|
if (strncasecmp(sql, "select UNIX_TIMESTAMP",
|
||||||
strlen("select UNIX_TIMESTAMP")) == 0
|
strlen("select UNIX_TIMESTAMP")) == 0
|
||||||
&& (strstr(sql, "as starttime") != NULL || strstr(sql, "AS starttime") != NULL))
|
&& (strstr(sql, "as starttime") != NULL || strstr(sql, "AS starttime") != NULL))
|
||||||
{
|
{
|
||||||
respond_starttime(session->dcb);
|
respond_starttime(session->dcb);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (strcasecmp(sql, "set names 'utf8'") == 0)
|
if (strcasecmp(sql, "set names 'utf8'") == 0)
|
||||||
{
|
{
|
||||||
return maxinfo_send_ok(session->dcb);
|
return maxinfo_send_ok(session->dcb);
|
||||||
}
|
}
|
||||||
if (strncasecmp(sql, "set session", 11) == 0)
|
if (strncasecmp(sql, "set session", 11) == 0)
|
||||||
{
|
{
|
||||||
return maxinfo_send_ok(session->dcb);
|
return maxinfo_send_ok(session->dcb);
|
||||||
}
|
}
|
||||||
if (strncasecmp(sql, "set autocommit", 14) == 0)
|
if (strncasecmp(sql, "set autocommit", 14) == 0)
|
||||||
{
|
{
|
||||||
return maxinfo_send_ok(session->dcb);
|
return maxinfo_send_ok(session->dcb);
|
||||||
}
|
}
|
||||||
if (strncasecmp(sql, "SELECT `ENGINES`.`SUPPORT`", 26) == 0)
|
if (strncasecmp(sql, "SELECT `ENGINES`.`SUPPORT`", 26) == 0)
|
||||||
{
|
{
|
||||||
return maxinfo_send_ok(session->dcb);
|
return maxinfo_send_ok(session->dcb);
|
||||||
}
|
}
|
||||||
if ((tree = maxinfo_parse(sql, &err)) == NULL)
|
if ((tree = maxinfo_parse(sql, &err)) == NULL)
|
||||||
{
|
{
|
||||||
maxinfo_send_parse_error(session->dcb, sql, err);
|
maxinfo_send_parse_error(session->dcb, sql, err);
|
||||||
MXS_NOTICE("Failed to parse SQL statement: '%s'.", sql);
|
MXS_NOTICE("Failed to parse SQL statement: '%s'.", sql);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
maxinfo_execute(session->dcb, tree);
|
{
|
||||||
return 1;
|
maxinfo_execute(session->dcb, tree);
|
||||||
|
maxinfo_free_tree(tree);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -881,22 +881,28 @@ variable_row(RESULTSET *result, void *data)
|
|||||||
static void
|
static void
|
||||||
exec_show_variables(DCB *dcb, MAXINFO_TREE *filter)
|
exec_show_variables(DCB *dcb, MAXINFO_TREE *filter)
|
||||||
{
|
{
|
||||||
RESULTSET *result;
|
RESULTSET *result;
|
||||||
VARCONTEXT context;
|
VARCONTEXT *context;
|
||||||
|
|
||||||
|
if ((context = malloc(sizeof(VARCONTEXT))) == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter)
|
if (filter)
|
||||||
{
|
{
|
||||||
context.like = filter->value;
|
context->like = filter->value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.like = NULL;
|
context->like = NULL;
|
||||||
}
|
}
|
||||||
context.index = 0;
|
context->index = 0;
|
||||||
|
|
||||||
if ((result = resultset_create(variable_row, &context)) == NULL)
|
if ((result = resultset_create(variable_row, context)) == NULL)
|
||||||
{
|
{
|
||||||
maxinfo_send_error(dcb, 0, "No resources available");
|
maxinfo_send_error(dcb, 0, "No resources available");
|
||||||
|
free(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
||||||
@ -1167,22 +1173,28 @@ status_row(RESULTSET *result, void *data)
|
|||||||
static void
|
static void
|
||||||
exec_show_status(DCB *dcb, MAXINFO_TREE *filter)
|
exec_show_status(DCB *dcb, MAXINFO_TREE *filter)
|
||||||
{
|
{
|
||||||
RESULTSET *result;
|
RESULTSET *result;
|
||||||
VARCONTEXT context;
|
VARCONTEXT *context;
|
||||||
|
|
||||||
|
if ((context = malloc(sizeof(VARCONTEXT))) == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter)
|
if (filter)
|
||||||
{
|
{
|
||||||
context.like = filter->value;
|
context->like = filter->value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.like = NULL;
|
context->like = NULL;
|
||||||
}
|
}
|
||||||
context.index = 0;
|
context->index = 0;
|
||||||
|
|
||||||
if ((result = resultset_create(status_row, &context)) == NULL)
|
if ((result = resultset_create(status_row, context)) == NULL)
|
||||||
{
|
{
|
||||||
maxinfo_send_error(dcb, 0, "No resources available");
|
maxinfo_send_error(dcb, 0, "No resources available");
|
||||||
|
free(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
#include <log_manager.h>
|
#include <log_manager.h>
|
||||||
|
|
||||||
static MAXINFO_TREE *make_tree_node(MAXINFO_OPERATOR, char *, MAXINFO_TREE *, MAXINFO_TREE *);
|
static MAXINFO_TREE *make_tree_node(MAXINFO_OPERATOR, char *, MAXINFO_TREE *, MAXINFO_TREE *);
|
||||||
static void free_tree(MAXINFO_TREE *);
|
void maxinfo_free_tree(MAXINFO_TREE *); // This function is needed by maxinfo.c
|
||||||
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);
|
||||||
@ -62,44 +62,46 @@ char *ptr, *text;
|
|||||||
MAXINFO_TREE *tree = NULL;
|
MAXINFO_TREE *tree = NULL;
|
||||||
MAXINFO_TREE *col, *table;
|
MAXINFO_TREE *col, *table;
|
||||||
|
|
||||||
*parse_error = PARSE_NOERROR;
|
*parse_error = PARSE_NOERROR;
|
||||||
while ((ptr = fetch_token(sql, &token, &text)) != NULL)
|
while ((ptr = fetch_token(sql, &token, &text)) != NULL)
|
||||||
{
|
{
|
||||||
switch (token)
|
switch (token)
|
||||||
{
|
{
|
||||||
case LT_SHOW:
|
case LT_SHOW:
|
||||||
free(text); // not needed
|
free(text); // not needed
|
||||||
ptr = fetch_token(ptr, &token, &text);
|
ptr = fetch_token(ptr, &token, &text);
|
||||||
if (ptr == NULL || token != LT_STRING)
|
if (ptr == NULL || token != LT_STRING)
|
||||||
{
|
{
|
||||||
// Expected show "name"
|
// Expected show "name"
|
||||||
*parse_error = PARSE_MALFORMED_SHOW;
|
*parse_error = PARSE_MALFORMED_SHOW;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
tree = make_tree_node(MAXOP_SHOW, text, NULL, NULL);
|
tree = make_tree_node(MAXOP_SHOW, text, NULL, NULL);
|
||||||
if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
|
if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
|
||||||
return tree;
|
{
|
||||||
else if (token == LT_LIKE)
|
return tree;
|
||||||
{
|
}
|
||||||
if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
|
else if (token == LT_LIKE)
|
||||||
{
|
{
|
||||||
tree->right = make_tree_node(MAXOP_LIKE,
|
if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
|
||||||
text, NULL, NULL);
|
{
|
||||||
return tree;
|
tree->right = make_tree_node(MAXOP_LIKE,
|
||||||
}
|
text, NULL, NULL);
|
||||||
else
|
return tree;
|
||||||
{
|
}
|
||||||
// Expected expression
|
else
|
||||||
*parse_error = PARSE_EXPECTED_LIKE;
|
{
|
||||||
free_tree(tree);
|
// Expected expression
|
||||||
return NULL;
|
*parse_error = PARSE_EXPECTED_LIKE;
|
||||||
}
|
maxinfo_free_tree(tree);
|
||||||
}
|
return NULL;
|
||||||
// Malformed show
|
}
|
||||||
free(text);
|
}
|
||||||
free_tree(tree);
|
// Malformed show
|
||||||
*parse_error = PARSE_MALFORMED_SHOW;
|
free(text);
|
||||||
return NULL;
|
maxinfo_free_tree(tree);
|
||||||
|
*parse_error = PARSE_MALFORMED_SHOW;
|
||||||
|
return NULL;
|
||||||
#if 0
|
#if 0
|
||||||
case LT_SELECT:
|
case LT_SELECT:
|
||||||
free(text); // not needed
|
free(text); // not needed
|
||||||
@ -128,7 +130,7 @@ MAXINFO_TREE *col, *table;
|
|||||||
{
|
{
|
||||||
/** Unknown token after SHUTDOWN MONITOR|SERVICE */
|
/** Unknown token after SHUTDOWN MONITOR|SERVICE */
|
||||||
*parse_error = PARSE_SYNTAX_ERROR;
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
free_tree(tree);
|
maxinfo_free_tree(tree);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return tree;
|
return tree;
|
||||||
@ -142,7 +144,7 @@ MAXINFO_TREE *col, *table;
|
|||||||
{
|
{
|
||||||
/** Missing token for RESTART MONITOR|SERVICE */
|
/** Missing token for RESTART MONITOR|SERVICE */
|
||||||
*parse_error = PARSE_SYNTAX_ERROR;
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
free_tree(tree);
|
maxinfo_free_tree(tree);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
|
tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
|
||||||
@ -152,7 +154,7 @@ MAXINFO_TREE *col, *table;
|
|||||||
/** Unknown token after RESTART MONITOR|SERVICE */
|
/** Unknown token after RESTART MONITOR|SERVICE */
|
||||||
*parse_error = PARSE_SYNTAX_ERROR;
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
free(text);
|
free(text);
|
||||||
free_tree(tree);
|
maxinfo_free_tree(tree);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return tree;
|
return tree;
|
||||||
@ -270,20 +272,26 @@ MAXINFO_TREE *node;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recusrsively free the storage associated with a parse tree
|
* Recursively free the storage associated with a parse tree
|
||||||
*
|
*
|
||||||
* @param tree The parse tree to free
|
* @param tree The parse tree to free
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
free_tree(MAXINFO_TREE *tree)
|
maxinfo_free_tree(MAXINFO_TREE *tree)
|
||||||
{
|
{
|
||||||
if (tree->left)
|
if (tree->left)
|
||||||
free_tree(tree->left);
|
{
|
||||||
if (tree->right)
|
maxinfo_free_tree(tree->left);
|
||||||
free_tree(tree->right);
|
}
|
||||||
if (tree->value)
|
if (tree->right)
|
||||||
free(tree->value);
|
{
|
||||||
free(tree);
|
maxinfo_free_tree(tree->right);
|
||||||
|
}
|
||||||
|
if (tree->value)
|
||||||
|
{
|
||||||
|
free(tree->value);
|
||||||
|
}
|
||||||
|
free(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -410,8 +418,8 @@ MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr
|
|||||||
(node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL)
|
(node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL)
|
||||||
{
|
{
|
||||||
*parse_error = PARSE_SYNTAX_ERROR;
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
free_tree(tree);
|
maxinfo_free_tree(tree);
|
||||||
if(ptr)
|
if (ptr)
|
||||||
{
|
{
|
||||||
free(text);
|
free(text);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user