parse_kill_query() now also expects the "KILL"
This was originally removed, since it was checking the same word twice. However, the parsing is clearer with it and the cost is only paid when the KILL is detected, which is very rare. Also, fix some incorrect parsing.
This commit is contained in:
@ -48,10 +48,7 @@ typedef enum kill_type
|
|||||||
KT_QUERY
|
KT_QUERY
|
||||||
} kill_type_t;
|
} kill_type_t;
|
||||||
|
|
||||||
/* Limits on the length of the queries in which "KILL" is searched for. Reducing
|
const char WORD_KILL[] = "KILL";
|
||||||
* LONGEST_KILL will reduce overhead but also limit the range of accepted queries. */
|
|
||||||
const int SHORTEST_KILL = sizeof("KILL 1") - 1;
|
|
||||||
const int LONGEST_KILL = sizeof("KILL CONNECTION 12345678901234567890 ;");
|
|
||||||
|
|
||||||
static int process_init(void);
|
static int process_init(void);
|
||||||
static void process_finish(void);
|
static void process_finish(void);
|
||||||
@ -79,7 +76,7 @@ static void gw_process_one_new_client(DCB *client_dcb);
|
|||||||
static spec_com_res_t process_special_commands(DCB *client_dcb, GWBUF *read_buffer, int nbytes_read);
|
static spec_com_res_t process_special_commands(DCB *client_dcb, GWBUF *read_buffer, int nbytes_read);
|
||||||
static spec_com_res_t handle_query_kill(DCB* dcb, GWBUF* read_buffer, spec_com_res_t current,
|
static spec_com_res_t handle_query_kill(DCB* dcb, GWBUF* read_buffer, spec_com_res_t current,
|
||||||
bool is_complete, unsigned int packet_len);
|
bool is_complete, unsigned int packet_len);
|
||||||
static uint64_t parse_kill_query(char *query, kill_type_t *kt_out);
|
static bool parse_kill_query(char *query, uint64_t *thread_id_out, kill_type_t *kt_out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The module entry point routine. It is this routine that
|
* The module entry point routine. It is this routine that
|
||||||
@ -1589,6 +1586,10 @@ static spec_com_res_t process_special_commands(DCB *dcb, GWBUF *read_buffer, int
|
|||||||
}
|
}
|
||||||
else if (proto->current_command == MYSQL_COM_QUERY)
|
else if (proto->current_command == MYSQL_COM_QUERY)
|
||||||
{
|
{
|
||||||
|
/* Limits on the length of the queries in which "KILL" is searched for. Reducing
|
||||||
|
* LONGEST_KILL will reduce overhead but also limit the range of accepted queries. */
|
||||||
|
const int SHORTEST_KILL = sizeof("KILL 1") - 1;
|
||||||
|
const int LONGEST_KILL = sizeof("KILL CONNECTION 12345678901234567890 ;");
|
||||||
/* Is length within limits for a kill-type query? */
|
/* Is length within limits for a kill-type query? */
|
||||||
if (packet_len >= (MYSQL_HEADER_LEN + 1 + SHORTEST_KILL) &&
|
if (packet_len >= (MYSQL_HEADER_LEN + 1 + SHORTEST_KILL) &&
|
||||||
packet_len <= (MYSQL_HEADER_LEN + 1 + LONGEST_KILL))
|
packet_len <= (MYSQL_HEADER_LEN + 1 + LONGEST_KILL))
|
||||||
@ -1617,28 +1618,28 @@ spec_com_res_t handle_query_kill(DCB* dcb, GWBUF* read_buffer, spec_com_res_t cu
|
|||||||
spec_com_res_t rval = current;
|
spec_com_res_t rval = current;
|
||||||
/* First, we need to detect the text "KILL" (ignorecase) in the start
|
/* First, we need to detect the text "KILL" (ignorecase) in the start
|
||||||
* of the packet. Copy just enough characters. */
|
* of the packet. Copy just enough characters. */
|
||||||
const char KILL_BEGIN[] = "KILL";
|
const size_t KILL_BEGIN_LEN = sizeof(WORD_KILL) - 1;
|
||||||
const size_t KILL_BEGIN_LEN = sizeof(KILL_BEGIN) - 1;
|
|
||||||
char startbuf[KILL_BEGIN_LEN]; // Not 0-terminated, careful...
|
char startbuf[KILL_BEGIN_LEN]; // Not 0-terminated, careful...
|
||||||
size_t copied_len = gwbuf_copy_data(read_buffer, MYSQL_HEADER_LEN + 1,
|
size_t copied_len = gwbuf_copy_data(read_buffer, MYSQL_HEADER_LEN + 1,
|
||||||
KILL_BEGIN_LEN, (uint8_t*)startbuf);
|
KILL_BEGIN_LEN, (uint8_t*)startbuf);
|
||||||
if (is_complete)
|
if (is_complete)
|
||||||
{
|
{
|
||||||
if (strncasecmp(KILL_BEGIN, startbuf, KILL_BEGIN_LEN) == 0)
|
if (strncasecmp(WORD_KILL, startbuf, KILL_BEGIN_LEN) == 0)
|
||||||
{
|
{
|
||||||
/* Good chance that the query is a KILL-query. Copy the entire
|
/* Good chance that the query is a KILL-query. Copy the entire
|
||||||
* buffer (skip the "KILL ") and process. */
|
* buffer and process. */
|
||||||
size_t buffer_len = packet_len - (MYSQL_HEADER_LEN + 1) - KILL_BEGIN_LEN;
|
size_t buffer_len = packet_len - (MYSQL_HEADER_LEN + 1);
|
||||||
char querybuf[buffer_len + 1]; // 0-terminated
|
char querybuf[buffer_len + 1]; // 0-terminated
|
||||||
copied_len = gwbuf_copy_data(read_buffer,
|
copied_len = gwbuf_copy_data(read_buffer,
|
||||||
MYSQL_HEADER_LEN + 1 + KILL_BEGIN_LEN,
|
MYSQL_HEADER_LEN + 1,
|
||||||
buffer_len,
|
buffer_len,
|
||||||
(uint8_t*)querybuf);
|
(uint8_t*)querybuf);
|
||||||
querybuf[copied_len] = '\0';
|
querybuf[copied_len] = '\0';
|
||||||
kill_type_t kt = KT_CONNECTION;
|
kill_type_t kt = KT_CONNECTION;
|
||||||
uint64_t thread_id = parse_kill_query(querybuf, &kt);
|
uint64_t thread_id = 0;
|
||||||
|
bool parsed = parse_kill_query(querybuf, &thread_id, &kt);
|
||||||
|
|
||||||
if (thread_id)
|
if (parsed && (thread_id > 0)) // MaxScale session counter starts at 1
|
||||||
{
|
{
|
||||||
switch (kt)
|
switch (kt)
|
||||||
{
|
{
|
||||||
@ -1667,7 +1668,7 @@ spec_com_res_t handle_query_kill(DCB* dcb, GWBUF* read_buffer, spec_com_res_t cu
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Look at the start of the query and see if it might contain "KILL" */
|
/* Look at the start of the query and see if it might contain "KILL" */
|
||||||
if (strncasecmp(KILL_BEGIN, startbuf, copied_len) == 0)
|
if (strncasecmp(WORD_KILL, startbuf, copied_len) == 0)
|
||||||
{
|
{
|
||||||
rval = RES_MORE_DATA;
|
rval = RES_MORE_DATA;
|
||||||
}
|
}
|
||||||
@ -1676,14 +1677,15 @@ spec_com_res_t handle_query_kill(DCB* dcb, GWBUF* read_buffer, spec_com_res_t cu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse and process a "KILL [CONNECTION | QUERY] <process_id>" query. Will modify
|
* Parse a "KILL [CONNECTION | QUERY] <process_id>" query. Will modify
|
||||||
* the argument string even if not successful.
|
* the argument string even if unsuccessful.
|
||||||
*
|
*
|
||||||
* @param query The query string
|
* @param query Query string to parse
|
||||||
* @param kt_out The kill command type output
|
* @paran thread_id_out Thread id output
|
||||||
* @return Zero on error, a valid ID otherwise
|
* @param kt_out Kill command type output
|
||||||
|
* @return true on success, false on error
|
||||||
*/
|
*/
|
||||||
static uint64_t parse_kill_query(char *query, kill_type_t *kt_out)
|
static bool parse_kill_query(char *query, uint64_t *thread_id_out, kill_type_t *kt_out)
|
||||||
{
|
{
|
||||||
const char WORD_CONNECTION[] = "CONNECTION";
|
const char WORD_CONNECTION[] = "CONNECTION";
|
||||||
const char WORD_QUERY[] = "QUERY";
|
const char WORD_QUERY[] = "QUERY";
|
||||||
@ -1694,48 +1696,68 @@ static uint64_t parse_kill_query(char *query, kill_type_t *kt_out)
|
|||||||
|
|
||||||
enum kill_parse_state_t
|
enum kill_parse_state_t
|
||||||
{
|
{
|
||||||
|
KILL,
|
||||||
CONN_QUERY,
|
CONN_QUERY,
|
||||||
ID,
|
ID,
|
||||||
SEMICOLON,
|
SEMICOLON,
|
||||||
DONE
|
DONE
|
||||||
} state = CONN_QUERY;
|
} state = KILL;
|
||||||
|
|
||||||
char *saveptr = NULL;
|
char *saveptr = NULL;
|
||||||
char *token = strtok_r(query, DELIM, &saveptr);
|
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
while (token && !error && state != DONE)
|
char *token = strtok_r(query, DELIM, &saveptr);
|
||||||
|
|
||||||
|
while (token && !error)
|
||||||
{
|
{
|
||||||
bool get_next = false;
|
bool get_next = false;
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case CONN_QUERY:
|
case KILL:
|
||||||
|
if (strncasecmp(token, WORD_KILL, sizeof(WORD_KILL) - 1) == 0)
|
||||||
{
|
{
|
||||||
if (strncasecmp(token, WORD_QUERY, sizeof(WORD_QUERY) - 1) == 0)
|
state = CONN_QUERY;
|
||||||
{
|
get_next = true;
|
||||||
kill_type = KT_QUERY;
|
|
||||||
get_next = true;
|
|
||||||
}
|
|
||||||
else if (strncasecmp(token, WORD_CONNECTION, sizeof(WORD_CONNECTION) - 1) == 0)
|
|
||||||
{
|
|
||||||
get_next = true;
|
|
||||||
}
|
|
||||||
/* Move to next state regardless of comparison result. The current
|
|
||||||
* part is optional and the process id may already be in the token. */
|
|
||||||
state = ID;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONN_QUERY:
|
||||||
|
if (strncasecmp(token, WORD_QUERY, sizeof(WORD_QUERY) - 1) == 0)
|
||||||
|
{
|
||||||
|
kill_type = KT_QUERY;
|
||||||
|
get_next = true;
|
||||||
|
}
|
||||||
|
else if (strncasecmp(token, WORD_CONNECTION, sizeof(WORD_CONNECTION) - 1) == 0)
|
||||||
|
{
|
||||||
|
get_next = true;
|
||||||
|
}
|
||||||
|
/* Move to next state regardless of comparison result. The current
|
||||||
|
* part is optional and the process id may already be in the token. */
|
||||||
|
state = ID;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ID:
|
case ID:
|
||||||
{
|
{
|
||||||
char *endptr_id;
|
/* strtoull() accepts negative numbers, so check for '-' here */
|
||||||
|
if (*token == '-')
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char *endptr_id = NULL;
|
||||||
thread_id = strtoull(token, &endptr_id, 0);
|
thread_id = strtoull(token, &endptr_id, 0);
|
||||||
/* Zero is an error value, also MaxScale session id:s start at 1. */
|
if ((thread_id == ULLONG_MAX) && (errno == ERANGE))
|
||||||
if (thread_id == 0 || (thread_id == ULLONG_MAX && errno == ERANGE))
|
|
||||||
{
|
{
|
||||||
error = true;
|
error = true;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
}
|
}
|
||||||
|
else if (endptr_id == token)
|
||||||
|
{
|
||||||
|
error = true; // No digits were read
|
||||||
|
}
|
||||||
else if (*endptr_id == '\0') // Can be real end or written by strtok
|
else if (*endptr_id == '\0') // Can be real end or written by strtok
|
||||||
{
|
{
|
||||||
state = SEMICOLON; // In case we have space before ;
|
state = SEMICOLON; // In case we have space before ;
|
||||||
@ -1758,6 +1780,7 @@ static uint64_t parse_kill_query(char *query, kill_type_t *kt_out)
|
|||||||
if (strncmp(token, ";", 1) == 0)
|
if (strncmp(token, ";", 1) == 0)
|
||||||
{
|
{
|
||||||
state = DONE;
|
state = DONE;
|
||||||
|
get_next = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1767,11 +1790,10 @@ static uint64_t parse_kill_query(char *query, kill_type_t *kt_out)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
error = true;
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_next)
|
if (get_next)
|
||||||
{
|
{
|
||||||
token = strtok_r(NULL, DELIM, &saveptr);
|
token = strtok_r(NULL, DELIM, &saveptr);
|
||||||
@ -1780,11 +1802,12 @@ static uint64_t parse_kill_query(char *query, kill_type_t *kt_out)
|
|||||||
|
|
||||||
if (error || (state != DONE && state != SEMICOLON))
|
if (error || (state != DONE && state != SEMICOLON))
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
*thread_id_out = thread_id;
|
||||||
*kt_out = kill_type;
|
*kt_out = kill_type;
|
||||||
return thread_id;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user