Johan Wikman cf0aeed516 MXS-2014 Rename log_manager.h to log.h
There's nothing resembling a manager anymore.
2018-08-17 10:59:37 +03:00

1139 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.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <maxscale/alloc.h>
#include <maxbase/atomic.h>
#include <maxscale/config.h>
#include <maxscale/dcb.h>
#include <maxscale/log.h>
#include <maxscale/maxscale.h>
#include <maxscale/modinfo.h>
#include <maxscale/modutil.h>
#include <maxscale/resultset.hh>
#include <maxscale/router.h>
#include <maxscale/server.hh>
#include <maxscale/service.h>
#include <maxscale/spinlock.h>
#include <maxscale/version.h>
#include "../../../core/internal/maxscale.h"
#include "../../../core/internal/modules.hh"
#include "../../../core/internal/monitor.h"
#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)
{
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)
{
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)
{
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)
{
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);
}
}