Files
MaxScale/server/modules/routing/maxinfo/maxinfo_exec.cc
Esa Korhonen 1c647f3753 MXS-2220 Move most remaining functions inside class
Most of the ones still remaining outside are special cases.
Also, removed locking from status manipulation functions as it
has not been required for quite some time.
2019-01-08 15:15:34 +02:00

1106 lines
27 KiB
C++

/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2022-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
/**
* @file maxinfo_parse.c - Parse the limited set of SQL that the MaxScale
* information schema can use
*
* @verbatim
* Revision History
*
* Date Who Description
* 17/02/15 Mark Riddoch Initial implementation
*
* @endverbatim
*/
#include "maxinfo.hh"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <maxscale/alloc.h>
#include <maxbase/atomic.h>
#include <maxscale/config.hh>
#include <maxscale/dcb.hh>
#include <maxscale/maxscale.h>
#include <maxscale/modinfo.h>
#include <maxscale/modutil.hh>
#include <maxscale/resultset.hh>
#include <maxscale/router.hh>
#include <maxscale/server.hh>
#include <maxscale/service.hh>
#include <maxscale/version.h>
#include "../../../core/internal/maxscale.hh"
#include "../../../core/internal/modules.hh"
#include "../../../core/internal/monitor.hh"
#include "../../../core/internal/poll.hh"
#include "../../../core/internal/session.hh"
#include "../../../core/internal/server.hh"
#include "../../../core/internal/service.hh"
static void exec_show(DCB* dcb, MAXINFO_TREE* tree);
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(const char* pattern, const 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
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
void maxinfo_execute(DCB* dcb, MAXINFO_TREE* tree)
{
switch (tree->op)
{
case MAXOP_SHOW:
exec_show(dcb, tree);
break;
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_SHUTDOWN:
exec_shutdown(dcb, tree);
break;
case MAXOP_RESTART:
exec_restart(dcb, tree);
break;
case MAXOP_TABLE:
case MAXOP_COLUMNS:
case MAXOP_LITERAL:
case MAXOP_PREDICATE:
case MAXOP_LIKE:
case MAXOP_EQUAL:
default:
maxinfo_send_error(dcb, 0, "Unexpected operator in parse tree");
}
}
/**
* Fetch the list of services and stream as a result set
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_services(DCB* dcb, MAXINFO_TREE* tree)
{
serviceGetList()->write(dcb);
}
/**
* Fetch the list of listeners and stream as a result set
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_listeners(DCB* dcb, MAXINFO_TREE* tree)
{
serviceGetListenerList()->write(dcb);
}
/**
* Fetch the list of sessions and stream as a result set
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_sessions(DCB* dcb, MAXINFO_TREE* tree)
{
sessionGetList()->write(dcb);
}
/**
* Fetch the list of client sessions and stream as a result set
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_clients(DCB* dcb, MAXINFO_TREE* tree)
{
sessionGetList()->write(dcb);
}
/**
* Fetch the list of servers and stream as a result set
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_servers(DCB* dcb, MAXINFO_TREE* tree)
{
serverGetList()->write(dcb);
}
/**
* Fetch the list of modules and stream as a result set
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_modules(DCB* dcb, MAXINFO_TREE* tree)
{
moduleGetList()->write(dcb);
}
/**
* Fetch the list of monitors and stream as a result set
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_monitors(DCB* dcb, MAXINFO_TREE* tree)
{
monitor_get_list()->write(dcb);
}
/**
* Fetch the event times data
*
* @param dcb DCB to which to stream result set
* @param tree Potential like clause (currently unused)
*/
static void exec_show_eventTimes(DCB* dcb, MAXINFO_TREE* tree)
{
eventTimesGetList()->write(dcb);
}
/**
* The table of show commands that are supported
*/
static struct
{
const char* name;
void (* func)(DCB*, MAXINFO_TREE*);
} show_commands[] =
{
{"variables", exec_show_variables },
{"status", exec_show_status },
{"services", exec_show_services },
{"listeners", exec_show_listeners },
{"sessions", exec_show_sessions },
{"clients", exec_show_clients },
{"servers", exec_show_servers },
{"modules", exec_show_modules },
{"monitors", exec_show_monitors },
{"eventTimes", exec_show_eventTimes},
{NULL, NULL }
};
/**
* Execute a show 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_show(DCB* dcb, MAXINFO_TREE* tree)
{
int i;
char errmsg[120];
for (i = 0; show_commands[i].name; i++)
{
if (strcasecmp(show_commands[i].name, tree->value) == 0)
{
(*show_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported show command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
MXS_NOTICE("%s", 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)
{
mxs_log_rotate();
maxinfo_send_ok(dcb);
}
/**
* The table of flush commands that are supported
*/
static struct
{
const 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];
sprintf(errmsg, "Unsupported flush command '%s'", tree->value);
if (!tree)
{
maxinfo_send_error(dcb, 0, errmsg);
MXS_ERROR("%s", errmsg);
return;
}
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;
}
maxinfo_send_error(dcb, 0, errmsg);
MXS_ERROR("%s", errmsg);
}
/**
* Set the server status.
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_set_server(DCB* dcb, MAXINFO_TREE* tree)
{
auto server = Server::find_by_unique_name(tree->value);
char errmsg[120];
if (server)
{
int status = SERVER::status_from_string(tree->right->value);
if (status != 0)
{
std::string errmsgs;
if (mxs::server_set_status(server, status, &errmsgs))
{
maxinfo_send_ok(dcb);
}
else
{
maxinfo_send_error(dcb, 0, errmsgs.c_str());
}
}
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
{
const 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);
MXS_ERROR("%s", errmsg);
}
/**
* Clear the server status.
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_clear_server(DCB* dcb, MAXINFO_TREE* tree)
{
auto server = Server::find_by_unique_name(tree->value);
char errmsg[120];
if (server)
{
int status = SERVER::status_from_string(tree->right->value);
if (status != 0)
{
std::string errmsgs;
if (mxs::server_clear_status(server, status, &errmsgs))
{
maxinfo_send_ok(dcb);
}
else
{
maxinfo_send_error(dcb, 0, errmsgs.c_str());
}
}
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
{
const 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);
MXS_ERROR("%s", errmsg);
}
/**
* MaxScale shutdown
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_shutdown_maxscale(DCB* dcb, MAXINFO_TREE* tree)
{
maxscale_shutdown();
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)
{
MXS_MONITOR* monitor = monitor_find(tree->value);
if (monitor)
{
monitor_stop(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
{
const 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);
MXS_ERROR("%s", 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)
{
MXS_MONITOR* monitor = monitor_find(tree->value);
if (monitor)
{
monitor_start(monitor, monitor->parameters);
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)
{
serviceStart(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
{
const 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);
MXS_ERROR("%s", errmsg);
}
/**
* Return the current MaxScale version
*
* @return The version string for MaxScale
*/
static char* getVersion()
{
return const_cast<char*>(MAXSCALE_VERSION);
}
static const char* versionComment = "MariaDB MaxScale";
/**
* Return the current MaxScale version
*
* @return The version string for MaxScale
*/
static char* getVersionComment()
{
return const_cast<char*>(versionComment);
}
/**
* Return the current MaxScale Home Directory
*
* @return The version string for MaxScale
*/
static char* getMaxScaleHome()
{
return getenv("MAXSCALE_HOME");
}
/* The various methods to fetch the variables */
#define VT_STRING 1
#define VT_INT 2
typedef void* (* STATSFUNC)();
/**
* Variables that may be sent in a show variables
*/
static struct
{
const char* name;
int type;
STATSFUNC func;
} variables[] =
{
{"version", VT_STRING, (STATSFUNC)getVersion },
{"version_comment", VT_STRING, (STATSFUNC)getVersionComment },
{"basedir", VT_STRING, (STATSFUNC)getMaxScaleHome },
{"MAXSCALE_VERSION", VT_STRING, (STATSFUNC)getVersion },
{"MAXSCALE_THREADS", VT_INT, (STATSFUNC)config_threadcount },
{"MAXSCALE_NBPOLLS", VT_INT, (STATSFUNC)config_nbpolls },
{"MAXSCALE_POLLSLEEP", VT_INT, (STATSFUNC)config_pollsleep },
{"MAXSCALE_UPTIME", VT_INT, (STATSFUNC)maxscale_uptime },
{"MAXSCALE_SESSIONS", VT_INT, (STATSFUNC)serviceSessionCountAll},
{NULL, 0, NULL }
};
std::string value_to_string(int type, STATSFUNC func)
{
std::string value;
if (type == VT_STRING)
{
if (char* str = (char*)func())
{
value = str;
}
}
else
{
value = std::to_string((int64_t)func());
}
return value;
}
/**
* Callback function to populate rows of the show variable
* command
*
* @param data The context point
* @return The next row or NULL if end of rows
*/
static void variable_row(std::unique_ptr<ResultSet>& set, const char* like)
{
for (int i = 0; variables[i].name; i++)
{
if (!like || !maxinfo_pattern_match(like, variables[i].name))
{
set->add_row({variables[i].name, value_to_string(variables[i].type, variables[i].func)});
}
}
}
/**
* Execute a show variables command applying an optional filter
*
* @param dcb The DCB connected to the client
* @param filter A potential like clause or NULL
*/
static void exec_show_variables(DCB* dcb, MAXINFO_TREE* filter)
{
std::unique_ptr<ResultSet> set = ResultSet::create({"Variable_name", "Value"});
variable_row(set, filter ? filter->value : NULL);
set->write(dcb);
}
/**
* Return the show variables output a a result set
*
* @return Variables as a result set
*/
std::unique_ptr<ResultSet> maxinfo_variables()
{
std::unique_ptr<ResultSet> set = ResultSet::create({"Variable_name", "Value"});
variable_row(set, NULL);
return set;
}
/**
* Interface to dcb_count_by_usage for all dcbs
*/
static int maxinfo_all_dcbs()
{
return dcb_count_by_usage(DCB_USAGE_ALL);
}
/**
* Interface to dcb_count_by_usage for client dcbs
*/
static int maxinfo_client_dcbs()
{
return dcb_count_by_usage(DCB_USAGE_CLIENT);
}
/**
* Interface to dcb_count_by_usage for listener dcbs
*/
static int maxinfo_listener_dcbs()
{
return dcb_count_by_usage(DCB_USAGE_LISTENER);
}
/**
* Interface to dcb_count_by_usage for backend dcbs
*/
static int maxinfo_backend_dcbs()
{
return dcb_count_by_usage(DCB_USAGE_BACKEND);
}
/**
* Interface to dcb_count_by_usage for internal dcbs
*/
static int maxinfo_internal_dcbs()
{
return dcb_count_by_usage(DCB_USAGE_INTERNAL);
}
/**
* Interface to poll stats for reads
*/
static int64_t maxinfo_read_events()
{
return poll_get_stat(POLL_STAT_READ);
}
/**
* Interface to poll stats for writes
*/
static int64_t maxinfo_write_events()
{
return poll_get_stat(POLL_STAT_WRITE);
}
/**
* Interface to poll stats for errors
*/
static int64_t maxinfo_error_events()
{
return poll_get_stat(POLL_STAT_ERROR);
}
/**
* Interface to poll stats for hangup
*/
static int64_t maxinfo_hangup_events()
{
return poll_get_stat(POLL_STAT_HANGUP);
}
/**
* Interface to poll stats for accepts
*/
static int64_t maxinfo_accept_events()
{
return poll_get_stat(POLL_STAT_ACCEPT);
}
/**
* Interface to poll stats for event queue length
*/
static int64_t maxinfo_avg_event_queue_length()
{
return poll_get_stat(POLL_STAT_EVQ_AVG);
}
/**
* Interface to poll stats for max event queue length
*/
static int64_t maxinfo_max_event_queue_length()
{
return poll_get_stat(POLL_STAT_EVQ_MAX);
}
/**
* Interface to poll stats for max queue time
*/
static int64_t maxinfo_max_event_queue_time()
{
return poll_get_stat(POLL_STAT_MAX_QTIME);
}
/**
* Interface to poll stats for max event execution time
*/
static int64_t maxinfo_max_event_exec_time()
{
return poll_get_stat(POLL_STAT_MAX_EXECTIME);
}
/**
* Variables that may be sent in a show status
*/
static struct
{
const char* name;
int type;
STATSFUNC func;
} status[] =
{
{"Uptime", VT_INT, (STATSFUNC)maxscale_uptime },
{"Uptime_since_flush_status", VT_INT, (STATSFUNC)maxscale_uptime },
{"Threads_created", VT_INT, (STATSFUNC)config_threadcount },
{"Threads_running", VT_INT, (STATSFUNC)config_threadcount },
{"Threadpool_threads", VT_INT, (STATSFUNC)config_threadcount },
{"Threads_connected", VT_INT, (STATSFUNC)serviceSessionCountAll },
{"Connections", VT_INT, (STATSFUNC)maxinfo_all_dcbs },
{"Client_connections", VT_INT, (STATSFUNC)maxinfo_client_dcbs },
{"Backend_connections", VT_INT, (STATSFUNC)maxinfo_backend_dcbs },
{"Listeners", VT_INT, (STATSFUNC)maxinfo_listener_dcbs },
{"Internal_descriptors", VT_INT, (STATSFUNC)maxinfo_internal_dcbs },
{"Read_events", VT_INT, (STATSFUNC)maxinfo_read_events },
{"Write_events", VT_INT, (STATSFUNC)maxinfo_write_events },
{"Hangup_events", VT_INT, (STATSFUNC)maxinfo_hangup_events },
{"Error_events", VT_INT, (STATSFUNC)maxinfo_error_events },
{"Accept_events", VT_INT, (STATSFUNC)maxinfo_accept_events },
{"Avg_event_queue_length", VT_INT, (STATSFUNC)maxinfo_avg_event_queue_length},
{"Max_event_queue_length", VT_INT, (STATSFUNC)maxinfo_max_event_queue_length},
{"Max_event_queue_time", VT_INT, (STATSFUNC)maxinfo_max_event_queue_time },
{"Max_event_execution_time", VT_INT, (STATSFUNC)maxinfo_max_event_exec_time },
{NULL, 0, NULL }
};
/**
* Callback function to populate rows of the show variable
* command
*
* @param data The context point
* @return The next row or NULL if end of rows
*/
static void status_row(std::unique_ptr<ResultSet>& set, const char* like)
{
for (int i = 0; status[i].name; i++)
{
if (!like || !maxinfo_pattern_match(like, status[i].name))
{
set->add_row({status[i].name, value_to_string(status[i].type, status[i].func)});
}
}
}
/**
* Execute a show status command applying an optional filter
*
* @param dcb The DCB connected to the client
* @param filter A potential like clause or NULL
*/
static void exec_show_status(DCB* dcb, MAXINFO_TREE* filter)
{
std::unique_ptr<ResultSet> set = ResultSet::create({"Variable_name", "Value"});
status_row(set, filter ? filter->value : NULL);
set->write(dcb);
}
/**
* Return the show status data as a result set
*
* @return The show status data as a result set
*/
std::unique_ptr<ResultSet> maxinfo_status()
{
std::unique_ptr<ResultSet> set = ResultSet::create({"Variable_name", "Value"});
status_row(set, NULL);
return set;
}
/**
* Execute a select 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_select(DCB* dcb, MAXINFO_TREE* tree)
{
maxinfo_send_error(dcb, 0, "Select not yet implemented");
}
/**
* Perform a "like" pattern match. Only works for leading and trailing %
*
* @param pattern Pattern to match
* @param str String to match against pattern
* @return Zero on match
*/
static int maxinfo_pattern_match(const char* pattern, const char* str)
{
int anchor = 0, len, trailing;
const char* fixed;
if (*pattern != '%')
{
fixed = pattern;
anchor = 1;
}
else
{
fixed = &pattern[1];
}
len = strlen(fixed);
if (fixed[len - 1] == '%')
{
trailing = 1;
}
else
{
trailing = 0;
}
if (anchor == 1 && trailing == 0) // No wildcard
{
return strcasecmp(pattern, str);
}
else if (anchor == 1)
{
return strncasecmp(str, pattern, len - trailing);
}
else
{
char* portion = static_cast<char*>(MXS_MALLOC(len + 1));
MXS_ABORT_IF_NULL(portion);
int rval;
strncpy(portion, fixed, len - trailing);
portion[len - trailing] = 0;
rval = (strcasestr(str, portion) != NULL ? 0 : 1);
MXS_FREE(portion);
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);
}
}