From e55631e60f9cf7efd241a9c3ae4c5f890b6f644e Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 20 Jun 2013 13:21:37 +0200 Subject: [PATCH] Improved debug CLI command interpreter to allow for commands with arguments --- core/dcb.c | 21 ++++ include/dcb.h | 1 + modules/routing/Makefile | 2 +- modules/routing/debugcli.c | 57 +---------- modules/routing/debugcmd.c | 202 +++++++++++++++++++++++++++++++++++++ modules/routing/depend.mk | 44 ++++++++ 6 files changed, 270 insertions(+), 57 deletions(-) create mode 100644 modules/routing/debugcmd.c diff --git a/core/dcb.c b/core/dcb.c index aa130c471..f7e5f9b22 100644 --- a/core/dcb.c +++ b/core/dcb.c @@ -435,6 +435,27 @@ DCB *dcb; spinlock_release(dcbspin); } +/** + * Diagnostic to print a DCB + * + * @param dcb The DCB to print + * + */ +void +dprintDCB(DCB *pdcb, DCB *dcb) +{ + dcb_printf(pdcb, "DCB: %p\n", (void *)dcb); + dcb_printf(pdcb, "\tDCB state: %s\n", gw_dcb_state2string(dcb->state)); + if (dcb->remote) + dcb_printf(pdcb, "\tConnected to: %s\n", dcb->remote); + dcb_printf(pdcb, "\tQueued write data: %d\n", gwbuf_length(dcb->writeq)); + dcb_printf(pdcb, "\tStatistics:\n"); + dcb_printf(pdcb, "\t\tNo. of Reads: %d\n", dcb->stats.n_reads); + dcb_printf(pdcb, "\t\tNo. of Writes: %d\n", dcb->stats.n_writes); + dcb_printf(pdcb, "\t\tNo. of Buffered Writes: %d\n", dcb->stats.n_buffered); + dcb_printf(pdcb, "\t\tNo. of Accepts: %d\n", dcb->stats.n_accepts); +} + /** * Return a string representation of a DCB state. * diff --git a/include/dcb.h b/include/dcb.h index 6bae62aa2..01a50930b 100644 --- a/include/dcb.h +++ b/include/dcb.h @@ -136,6 +136,7 @@ extern void dcb_close(DCB *); /* Generic close functionality */ extern void printAllDCBs(); /* Debug to print all DCB in the system */ extern void printDCB(DCB *); /* Debug print routine */ extern void dprintAllDCBs(DCB *); /* Debug to print all DCB in the system */ +extern void dprintDCB(DCB *, DCB *); /* Debug to print a DCB in the system */ extern const char *gw_dcb_state2string(int); /* DCB state to string */ extern void dcb_printf(DCB *, const char *, ...); /* DCB version of printf */ diff --git a/modules/routing/Makefile b/modules/routing/Makefile index d3e25f496..b015d4794 100644 --- a/modules/routing/Makefile +++ b/modules/routing/Makefile @@ -25,7 +25,7 @@ TESTSRCS=testroute.c TESTOBJ=$(TESTSRCS:.c=.o) READCONSRCS=readconnroute.c READCONOBJ=$(READCONSRCS:.c=.o) -DEBUGCLISRCS=debugcli.c +DEBUGCLISRCS=debugcli.c debugcmd.c DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o) SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) OBJ=$(SRCS:.c=.o) diff --git a/modules/routing/debugcli.c b/modules/routing/debugcli.c index efca7f00b..3911992b8 100644 --- a/modules/routing/debugcli.c +++ b/modules/routing/debugcli.c @@ -52,7 +52,7 @@ static int execute(ROUTER *instance, void *router_session, GWBUF *queue); /** The module object definition */ static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, execute }; -static int execute_cmd(CLI_SESSION *cli); +extern int execute_cmd(CLI_SESSION *cli); static SPINLOCK instlock; static CLI_INSTANCE *instances; @@ -227,58 +227,3 @@ CLI_SESSION *session = (CLI_SESSION *)router_session; } return 1; } - -static struct { - char *cmd; - void (*fn)(DCB *); -} cmds[] = { - { "show sessions", dprintAllSessions }, - { "show services", dprintAllServices }, - { "show servers", dprintAllServers }, - { "show modules", dprintAllModules }, - { "show dcbs", dprintAllDCBs }, - { "show epoll", dprintPollStats }, - { NULL, NULL } -}; - -/** - * We have a complete line from the user, lookup the commands and execute them - * - * @param cli The CLI_SESSION - */ -static int -execute_cmd(CLI_SESSION *cli) -{ -int i, found = 0; - - if (!strncmp(cli->cmdbuf, "help", 4)) - { - dcb_printf(cli->session->client, "Available commands:\n"); - for (i = 0; cmds[i].cmd; i++) - { - dcb_printf(cli->session->client, " %s\n", cmds[i].cmd); - } - found = 1; - } - else if (!strncmp(cli->cmdbuf, "quit", 4)) - { - return 0; - } - else - { - for (i = 0; cmds[i].cmd; i++) - { - if (strncmp(cli->cmdbuf, cmds[i].cmd, strlen(cmds[i].cmd)) == 0) - { - cmds[i].fn(cli->session->client); - found = 1; - } - } - } - if (!found) - dcb_printf(cli->session->client, - "Command not known, type help for a list of available commands\n"); - memset(cli->cmdbuf, 0, 80); - - return 1; -} diff --git a/modules/routing/debugcmd.c b/modules/routing/debugcmd.c new file mode 100644 index 000000000..af52b4e4c --- /dev/null +++ b/modules/routing/debugcmd.c @@ -0,0 +1,202 @@ +/* + * This file is distributed as part of the SkySQL Gateway. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright SkySQL Ab 2013 + */ + +/** + * @file debugcmd.c - The debug CLI command line interpreter + * + * @verbatim + * Revision History + * + * Date Who Description + * 20/06/13 Mark Riddoch Initial implementation + * + * @endverbatim + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXARGS 5 + +/** + * The subcommand structure + * + * These are the options that may be passed to a command + */ +struct subcommand { + char *arg1; + int n_args; + void (*fn)(); + char *help; +}; + +struct subcommand showoptions[] = { + { "sessions", 0, dprintAllSessions, "Show all active sessions in the gateway" }, + { "services", 0, dprintAllServices, "Show all configured services in the gateway" }, + { "servers", 0, dprintAllServers, "Show all configured servers" }, + { "modules", 0, dprintAllModules, "Show all currently loaded modules" }, + { "dcbs", 0, dprintAllDCBs, "Show all descriptor control blocks (network connections)" }, + { "dcb", 1, dprintDCB, "Show a single descriptor control block e.g. show dcb 0x493340" }, + { "epoll", 0, dprintPollStats, "Show the poll statistics" }, + { NULL, 0, NULL, NULL } +}; + +/** + * The debug command table + */ +static struct { + char *cmd; + struct subcommand *options; +} cmds[] = { + { "show", showoptions }, + { NULL, NULL } +}; + + +static long +convert_arg(char *arg) +{ + return strtol(arg, NULL, 0); +} + +/** + * We have a complete line from the user, lookup the commands and execute them + * + * @param cli The CLI_SESSION + * @return Returns 0 if the interpreter should exit + */ +int +execute_cmd(CLI_SESSION *cli) +{ +DCB *dcb = cli->session->client; +int argc, i, j, found = 0; +char *args[MAXARGS]; +char *saveptr, *delim = " \t\r\n"; + + /* Tokenize the input string */ + args[0] = strtok_r(cli->cmdbuf, delim, &saveptr); + i = 0; + do { + i++; + args[i] = strtok_r(NULL, delim, &saveptr); + } while (args[i] != NULL && i < MAXARGS); + + if (args[0] == NULL) + return 1; + argc = i - 2; /* The number of extra arguments to commands */ + + + if (!strcasecmp(args[0], "help")) + { + if (args[1] == NULL) + { + found = 1; + dcb_printf(dcb, "Available commands:\n"); + for (i = 0; cmds[i].cmd; i++) + { + for (j = 0; cmds[i].options[j].arg1; j++) + { + dcb_printf(dcb, " %s %s\n", cmds[i].cmd, cmds[i].options[j].arg1); + } + } + } + else + { + for (i = 0; cmds[i].cmd; i++) + { + if (!strcasecmp(args[1], cmds[i].cmd)) + { + found = 1; + dcb_printf(dcb, "Available options to the %s command:\n", args[1]); + for (j = 0; cmds[i].options[j].arg1; j++) + { + dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1, + cmds[i].options[j].help); + } + } + } + if (found == 0) + { + dcb_printf(dcb, "No command %s to offer help with\n", args[1]); + } + } + found = 1; + } + else if (!strcasecmp(args[0], "quit")) + { + return 0; + } + else + { + for (i = 0; cmds[i].cmd; i++) + { + if (strcasecmp(args[0], cmds[i].cmd) == 0) + { + for (j = 0; cmds[i].options[j].arg1; j++) + { + if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0) + { + if (argc != cmds[i].options[j].n_args) + { + dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n", + cmds[i].cmd, cmds[i].options[j].arg1, + cmds[i].options[j].n_args); + + } + else + { + switch (cmds[i].options[j].n_args) + { + case 0: + cmds[i].options[j].fn(dcb); + break; + case 1: + cmds[i].options[j].fn(dcb, convert_arg(args[2])); + break; + case 2: + cmds[i].options[j].fn(dcb, convert_arg(args[2]), + convert_arg(args[3])); + break; + case 3: + cmds[i].options[j].fn(dcb, convert_arg(args[2]), + convert_arg(args[3]), + convert_arg(args[4])); + } + found = 1; + } + } + } + } + } + } + if (!found) + dcb_printf(dcb, + "Command not known, type help for a list of available commands\n"); + memset(cli->cmdbuf, 0, 80); + + return 1; +} diff --git a/modules/routing/depend.mk b/modules/routing/depend.mk index a93efe0dd..e90941377 100644 --- a/modules/routing/depend.mk +++ b/modules/routing/depend.mk @@ -36,3 +36,47 @@ readconnroute.o: readconnroute.c /usr/include/stdio.h \ /usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \ ../../include/server.h ../../include/router.h ../../include/session.h \ ../../include/atomic.h ../include/readconnection.h +debugcli.o: debugcli.c /usr/include/stdio.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ + /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \ + /usr/include/bits/types.h /usr/include/bits/typesizes.h \ + /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \ + /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ + /usr/include/stdlib.h /usr/include/bits/waitflags.h \ + /usr/include/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/bits/endian.h /usr/include/bits/byteswap.h \ + /usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h \ + /usr/include/bits/time.h /usr/include/sys/sysmacros.h \ + /usr/include/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h ../../include/service.h \ + ../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \ + /usr/include/sched.h /usr/include/bits/sched.h \ + /usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \ + ../../include/server.h ../../include/session.h ../../include/router.h \ + ../../include/modules.h ../../include/atomic.h ../../include/poll.h \ + ../include/debugcli.h +debugcmd.o: debugcmd.c /usr/include/stdio.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ + /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \ + /usr/include/bits/types.h /usr/include/bits/typesizes.h \ + /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \ + /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ + /usr/include/stdlib.h /usr/include/bits/waitflags.h \ + /usr/include/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/bits/endian.h /usr/include/bits/byteswap.h \ + /usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h \ + /usr/include/bits/time.h /usr/include/sys/sysmacros.h \ + /usr/include/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/string.h /usr/include/xlocale.h ../../include/service.h \ + ../../include/spinlock.h ../../include/thread.h /usr/include/pthread.h \ + /usr/include/sched.h /usr/include/bits/sched.h \ + /usr/include/bits/setjmp.h ../../include/dcb.h ../../include/buffer.h \ + ../../include/server.h ../../include/session.h ../../include/router.h \ + ../../include/modules.h ../../include/atomic.h ../../include/poll.h \ + ../include/debugcli.h