diff --git a/Makefile b/Makefile index 67eb0d159..07a385b61 100644 --- a/Makefile +++ b/Makefile @@ -39,21 +39,25 @@ all: (cd log_manager; make) (cd query_classifier; make) (cd server; make) + (cd client; make) clean: (cd log_manager; make clean) (cd query_classifier; make clean) (cd server; make clean) + (cd client; make clean) depend: (cd log_manager; make depend) (cd query_classifier; make depend) (cd server; make depend) + (cd client; make depend) install: (cd server; make DEST=$(DEST) install) (cd log_manager; make DEST=$(DEST) install) (cd query_classifier; make DEST=$(DEST) install) + (cd client; make DEST=$(DEST) install) cleantests: $(MAKE) -C test cleantests diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 000000000..4c1982e5d --- /dev/null +++ b/client/Makefile @@ -0,0 +1,71 @@ +# This file is distributed as part of MaxScale. 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 2014 +# +# Revision History +# Date Who Description +# 13/06/14 Mark Riddoch Initial implementation of MaxScale +# client program + +CC=cc + +CFLAGS=-c -Wall -g + +SRCS= maxadmin.c + +HDRS= + +OBJ=$(SRCS:.c=.o) + +LIBS=-ledit + +all: maxadmin + +cleantests: + $(MAKE) -C test cleantests + +buildtests: + $(MAKE) -C test buildtests + +runtests: + $(MAKE) -C test runtests + +testall: + $(MAKE) -C test testall + +maxadmin: $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + + +.c.o: + $(CC) $(CFLAGS) $< -o $@ + + +clean: + rm -f $(OBJ) maxadmin + - rm *.so + +tags: + ctags $(SRCS) $(HDRS) + +depend: + @rm -f depend.mk + cc -M $(CFLAGS) $(SRCS) > depend.mk + +install: maxadmin + @mkdir -p $(DEST)/bin + install -D maxadmin $(DEST)/bin + +include depend.mk diff --git a/client/depend.mk b/client/depend.mk new file mode 100644 index 000000000..e69de29bb diff --git a/client/maxadmin.c b/client/maxadmin.c new file mode 100644 index 000000000..00a75a872 --- /dev/null +++ b/client/maxadmin.c @@ -0,0 +1,420 @@ +/* + * This file is distributed as part of MaxScale. 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 2014 + */ + +/** + * @file maxadmin.c - The MaxScale administration client + * + * @verbatim + * Revision History + * + * Date Who Description + * 13/06/14 Mark Riddoch Initial implementation + * 15/06/14 Mark Riddoch Addition of source command + * + * @endverbatim + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int connectMaxScale(char *hostname, char *port); +static int setipaddress(struct in_addr *a, char *p); +static int authMaxScale(int so, char *user, char *password); +static int sendCommand(int so, char *cmd); +static void DoSource(int so, char *cmd); + +static char * +prompt(EditLine *el __attribute__((__unused__))) +{ + static char prompt[] = "MaxScale> "; + + return prompt; +} + +int +main(int argc, char **argv) +{ +EditLine *el = NULL; +int i, num, rv, fatal = 0; +char *buf; +Tokenizer *tok; +History *hist; +HistEvent ev; +const LineInfo *li; +char *hostname = "localhost"; +char *port = "6603"; +char *user = "admin"; +char *passwd = NULL; +int so, cmdlen; +char *cmd; + + cmd = malloc(1); + *cmd = 0; + cmdlen = 1; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'u': /* User */ + if (argv[i][2]) + user = &(argv[i][2]); + else if (i + 1 < argc) + user = argv[++i]; + else + { + fprintf(stderr, "Missing username" + "in -u option.\n"); + fatal = 1; + } + break; + case 'p': /* Password */ + if (argv[i][2]) + passwd = &(argv[i][2]); + else if (i + 1 < argc) + passwd = argv[++i]; + else + { + fprintf(stderr, "Missing password " + "in -p option.\n"); + fatal = 1; + } + break; + case 'h': /* hostname */ + if (argv[i][2]) + hostname = &(argv[i][2]); + else if (i + 1 < argc) + hostname = argv[++i]; + else + { + fprintf(stderr, "Missing hostname value " + "in -h option.\n"); + fatal = 1; + } + break; + case 'P': /* Port */ + if (argv[i][2]) + port = &(argv[i][2]); + else if (i + 1 < argc) + port = argv[++i]; + else + { + fprintf(stderr, "Missing Port value " + "in -P option.\n"); + fatal = 1; + } + break; + } + } + else + { + cmdlen += strlen(argv[i]) + 1; + cmd = realloc(cmd, cmdlen); + strcat(cmd, argv[i]); + strcat(cmd, " "); + } + } + + if (fatal) + exit(1); + + if (passwd == NULL) + { + struct termios tty_attr; + tcflag_t c_lflag; + + if (tcgetattr(STDIN_FILENO, &tty_attr) < 0) + return -1; + + c_lflag = tty_attr.c_lflag; + tty_attr.c_lflag &= ~ICANON; + tty_attr.c_lflag &= ~ECHO; + + if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0) + return -1; + + printf("Password: "); + passwd = malloc(80); + fgets(passwd, 80, stdin); + + tty_attr.c_lflag = c_lflag; + if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0) + return -1; + i = strlen(passwd); + if (i > 1) + passwd[i - 1] = '\0'; + printf("\n"); + } + + if ((so = connectMaxScale(hostname, port)) == -1) + exit(1); + if (!authMaxScale(so, user, passwd)) + { + fprintf(stderr, "Failed to connect to MaxScale. " + "Incorrect username or password.\n"); + exit(1); + } + + if (cmdlen > 1) + { + cmd[cmdlen - 2] = '\0'; + sendCommand(so, cmd); + exit(0); + } + + (void) setlocale(LC_CTYPE, ""); + + hist = history_init(); /* Init the builtin history */ + /* Remember 100 events */ + history(hist, &ev, H_SETSIZE, 100); + + tok = tok_init(NULL); /* Initialize the tokenizer */ + + /* Initialize editline */ + el = el_init(*argv, stdin, stdout, stderr); + + el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */ + el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */ + el_set(el, EL_PROMPT, prompt);/* Set the prompt function */ + + /* Tell editline to use this history interface */ + el_set(el, EL_HIST, history, hist); + + /* + * Bind j, k in vi command mode to previous and next line, instead + * of previous and next history. + */ + el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL); + el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL); + + /* + * Source the user's defaults file. + */ + el_source(el, NULL); + + while ((buf = el_gets(el, &num)) != NULL && num != 0) + { + /* Strip trailing \n\r */ + for (i = num - 1; buf[i] == '\r' || buf[i] == '\n'; i--) + buf[i] = 0; + + li = el_line(el); + history(hist, &ev, H_ENTER, buf); + + if (!strcasecmp(buf, "quit")) + { + break; + } + else if (!strcasecmp(buf, "history")) + { + for (rv = history(hist, &ev, H_LAST); rv != -1; + rv = history(hist, &ev, H_PREV)) + fprintf(stdout, "%4d %s\n", + ev.num, ev.str); + } + else if (!strncasecmp(buf, "source", 6)) + { + DoSource(so, buf); + } + else if (*buf) + { + sendCommand(so, buf); + } + } + + el_end(el); + tok_end(tok); + history_end(hist); + close(so); + return 0; +} + +static int +connectMaxScale(char *hostname, char *port) +{ +struct sockaddr_in addr; +int so; + + if ((so = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + fprintf(stderr, "Unable to create socket: %s\n", + strerror(errno)); + return -1; + } + memset(&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + setipaddress(&addr.sin_addr, hostname); + addr.sin_port = htons(atoi(port)); + if (connect(so, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + fprintf(stderr, "Unable to connect to MaxScale at %s, %s: %s\n", + hostname, port, strerror(errno)); + return -1; + } + + return so; +} + + +/* + * Set IP address in socket structure in_addr + * + * @param a Pointer to a struct in_addr into which the address is written + * @param p The hostname to lookup + * @return 1 on success, 0 on failure + */ +static int +setipaddress(struct in_addr *a, char *p) +{ +#ifdef __USE_POSIX + struct addrinfo *ai = NULL, hint; + int rc; + struct sockaddr_in * res_addr; + memset(&hint, 0, sizeof (hint)); + + hint.ai_socktype = SOCK_STREAM; + hint.ai_flags = AI_CANONNAME; + hint.ai_family = AF_INET; + + if ((rc = getaddrinfo(p, NULL, &hint, &ai)) != 0) { + return 0; + } + + /* take the first one */ + if (ai != NULL) { + res_addr = (struct sockaddr_in *)(ai->ai_addr); + memcpy(a, &res_addr->sin_addr, sizeof(struct in_addr)); + + freeaddrinfo(ai); + + return 1; + } +#else + struct hostent *h; + + spinlock_acquire(&tmplock); + h = gethostbyname(p); + spinlock_release(&tmplock); + + if (h == NULL) { + if ((a->s_addr = inet_addr(p)) == -1) { + return 0; + } + } else { + /* take the first one */ + memcpy(a, h->h_addr, h->h_length); + + return 1; + } +#endif + return 0; +} + +static int +authMaxScale(int so, char *user, char *password) +{ +char buf[20]; + + read(so, buf, 4); + write(so, user, strlen(user)); + read(so, buf, 8); + write(so, password, strlen(password)); + read(so, buf, 6); + + return strncmp(buf, "FAILED", 6); +} + +static int +sendCommand(int so, char *cmd) +{ +char buf[80]; +int i; + + write(so, cmd, strlen(cmd)); + while (1) + { + if ((i = read(so, buf, 80)) == -1) + return 0; + if (i > 1 && buf[i-1] == 'K' && buf[i-2] == 'O') + { + write(1, buf, i - 2); + return 1; + } + write(1, buf, i); + } + return 1; +} + +static void +DoSource(int so, char *buf) +{ +char *ptr, *pe; +char line[132]; +FILE *fp; + + /* Find the filename */ + ptr = &buf[strlen("source")]; + while (*ptr && isspace(*ptr)) + ptr++; + + if ((fp = fopen(ptr, "r")) == NULL) + { + fprintf(stderr, "Unable to open command file '%s'.\n", + ptr); + return; + } + + while ((ptr = fgets(line, 132, fp)) != NULL) + { + /* Strip tailing newlines */ + pe = &ptr[strlen(ptr)-1]; + while (pe >= ptr && (*pe == '\r' || *pe == '\n')) + { + *pe = '\0'; + pe--; + } + + if (*ptr != '#') /* Comment */ + { + if (! sendCommand(so, ptr)) + { + break; + } + } + } + fclose(fp); + return; +} diff --git a/server/core/dcb.c b/server/core/dcb.c index fd96361c4..0b5870754 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1117,12 +1117,18 @@ printDCB(DCB *dcb) if (dcb->writeq) printf("\tQueued write data: %d\n",gwbuf_length(dcb->writeq)); printf("\tStatistics:\n"); - printf("\t\tNo. of Reads: %d\n", dcb->stats.n_reads); - printf("\t\tNo. of Writes: %d\n", dcb->stats.n_writes); - printf("\t\tNo. of Buffered Writes: %d\n", dcb->stats.n_buffered); - printf("\t\tNo. of Accepts: %d\n", dcb->stats.n_accepts); - printf("\t\tNo. of High Water Events: %d\n", dcb->stats.n_high_water); - printf("\t\tNo. of Low Water Events: %d\n", dcb->stats.n_low_water); + printf("\t\tNo. of Reads: %d\n", + dcb->stats.n_reads); + printf("\t\tNo. of Writes: %d\n", + dcb->stats.n_writes); + printf("\t\tNo. of Buffered Writes: %d\n", + dcb->stats.n_buffered); + printf("\t\tNo. of Accepts: %d\n", + dcb->stats.n_accepts); + printf("\t\tNo. of High Water Events: %d\n", + dcb->stats.n_high_water); + printf("\t\tNo. of Low Water Events: %d\n", + dcb->stats.n_low_water); } /** @@ -1157,13 +1163,17 @@ DCB *dcb; while (dcb) { dcb_printf(pdcb, "DCB: %p\n", (void *)dcb); - dcb_printf(pdcb, "\tDCB state: %s\n", gw_dcb_state2string(dcb->state)); + dcb_printf(pdcb, "\tDCB state: %s\n", + gw_dcb_state2string(dcb->state)); if (dcb->session && dcb->session->service) - dcb_printf(pdcb, "\tService: %s\n", dcb->session->service->name); + dcb_printf(pdcb, "\tService: %s\n", + dcb->session->service->name); if (dcb->remote) - dcb_printf(pdcb, "\tConnected to: %s\n", dcb->remote); + dcb_printf(pdcb, "\tConnected to: %s\n", + dcb->remote); if (dcb->writeq) - dcb_printf(pdcb, "\tQueued write data: %d\n", gwbuf_length(dcb->writeq)); + 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); @@ -1176,6 +1186,36 @@ DCB *dcb; spinlock_release(&dcbspin); } +/** + * Diagnotic routine to print DCB data in a tabular form. + * + * @param pdcb DCB to print results to + */ +void +dListDCBs(DCB *pdcb) +{ +DCB *dcb; + + spinlock_acquire(&dcbspin); + dcb = allDCBs; + dcb_printf(pdcb, "Descriptor Control Blocks\n"); + dcb_printf(pdcb, "------------+----------------------------+----------------------+----------\n"); + dcb_printf(pdcb, " %-10s | %-26s | %-20s | %s\n", + "DCB", "State", "Service", "Remote"); + dcb_printf(pdcb, "------------+----------------------------+----------------------+----------\n"); + while (dcb) + { + dcb_printf(pdcb, " %10p | %-26s | %-20s | %s\n", + dcb, gw_dcb_state2string(dcb->state), + (dcb->session->service ? + dcb->session->service->name : ""), + (dcb->remote ? dcb->remote : "")); + dcb = dcb->next; + } + dcb_printf(pdcb, "------------+----------------------------+----------------------+----------\n\n"); + spinlock_release(&dcbspin); +} + /** * Diagnostic to print a DCB to another DCB * @@ -1189,16 +1229,22 @@ dprintDCB(DCB *pdcb, DCB *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, "\tOwning Session: %d\n", dcb->session); + dcb_printf(pdcb, "\tOwning Session: %p\n", dcb->session); if (dcb->writeq) 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); - dcb_printf(pdcb, "\t\tNo. of High Water Events: %d\n", dcb->stats.n_high_water); - dcb_printf(pdcb, "\t\tNo. of Low Water Events: %d\n", dcb->stats.n_low_water); + 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); + dcb_printf(pdcb, "\t\tNo. of High Water Events: %d\n", + dcb->stats.n_high_water); + dcb_printf(pdcb, "\t\tNo. of Low Water Events: %d\n", + dcb->stats.n_low_water); } /** @@ -1766,4 +1812,4 @@ void dcb_call_foreach ( } return; } - \ No newline at end of file + diff --git a/server/core/filter.c b/server/core/filter.c index a3db72e3b..87049461a 100644 --- a/server/core/filter.c +++ b/server/core/filter.c @@ -207,9 +207,11 @@ int i; ptr = allFilters; if (ptr) { + dcb_printf(dcb, "Filters\n"); + dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n"); dcb_printf(dcb, "%-18s | %-15s | Options\n", "Filter", "Module"); - dcb_printf(dcb, "-------------------------------------------------------------------------------\n"); + dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n"); } while (ptr) { @@ -220,6 +222,8 @@ int i; dcb_printf(dcb, "\n"); ptr = ptr->next; } + if (allFilters) + dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n\n"); spinlock_release(&filter_spin); } @@ -307,7 +311,7 @@ DOWNSTREAM *me; return NULL; } me->instance = filter->filter; - me->routeQuery = filter->obj->routeQuery; + me->routeQuery = (void *)(filter->obj->routeQuery); me->session = filter->obj->newSession(me->instance, session); filter->obj->setDownstream(me->instance, me->session, downstream); diff --git a/server/core/gateway.c b/server/core/gateway.c index 2bd592fe7..25c01e9b2 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -52,6 +52,7 @@ #include #include +#include #include #include #include diff --git a/server/core/gw_utils.c b/server/core/gw_utils.c index 1ace170e5..2662ab41d 100644 --- a/server/core/gw_utils.c +++ b/server/core/gw_utils.c @@ -130,6 +130,7 @@ setipaddress(struct in_addr *a, char *p) { return 1; } #endif + return 0; } /** diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 3ace0f91f..50efc5c89 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -359,8 +359,10 @@ dprintAllModules(DCB *dcb) { MODULES *ptr = registered; + dcb_printf(dcb, "Modules.\n"); + dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n"); dcb_printf(dcb, "%-15s | %-11s | Version | API | Status\n", "Module Name", "Module Type"); - dcb_printf(dcb, "--------------------------------------------------------------------------\n"); + dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n"); while (ptr) { dcb_printf(dcb, "%-15s | %-11s | %-7s ", ptr->module, ptr->type, ptr->version); @@ -380,4 +382,5 @@ MODULES *ptr = registered; dcb_printf(dcb, "\n"); ptr = ptr->next; } + dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n\n"); } diff --git a/server/core/server.c b/server/core/server.c index 42a60caea..c1bec6189 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -312,20 +312,24 @@ char *stat; ptr = allServers; if (ptr) { - dcb_printf(dcb, "%-18s | %-15s | Port | %-18s | Connections\n", + dcb_printf(dcb, "Servers.\n"); + dcb_printf(dcb, "-------------------+-----------------+-------+----------------------+------------\n"); + dcb_printf(dcb, "%-18s | %-15s | Port | %-20s | Connections\n", "Server", "Address", "Status"); - dcb_printf(dcb, "-------------------------------------------------------------------------------\n"); + dcb_printf(dcb, "-------------------+-----------------+-------+----------------------+------------\n"); } while (ptr) { stat = server_status(ptr); - dcb_printf(dcb, "%-18s | %-15s | %5d | %-18s | %4d\n", + dcb_printf(dcb, "%-18s | %-15s | %5d | %-20s | %4d\n", ptr->unique_name, ptr->name, ptr->port, stat, ptr->stats.n_current); free(stat); ptr = ptr->next; } + if (allServers) + dcb_printf(dcb, "-------------------+-----------------+-------+----------------------+------------\n\n"); spinlock_release(&server_spin); } diff --git a/server/core/service.c b/server/core/service.c index b07454b72..d3db48390 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -650,12 +650,23 @@ FILTER_DEF **flist; char *ptr, *brkt; int n = 0; - flist = (FILTER_DEF *)malloc(sizeof(FILTER_DEF *)); + if ((flist = (FILTER_DEF **)malloc(sizeof(FILTER_DEF *))) == NULL) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Out of memory adding filters to service.\n"))); + return; + } ptr = strtok_r(filters, "|", &brkt); while (ptr) { n++; - flist = (FILTER_DEF *)realloc(flist, (n + 1) * sizeof(FILTER_DEF *)); + if ((flist = (FILTER_DEF **)realloc(flist, + (n + 1) * sizeof(FILTER_DEF *))) == NULL) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Out of memory adding filters to service.\n"))); + return; + } if ((flist[n-1] = filter_find(trim(ptr))) == NULL) { LOGIF(LE, (skygw_log_write_flush( @@ -826,9 +837,11 @@ SERVICE *ptr; ptr = allServices; if (ptr) { + dcb_printf(dcb, "Services.\n"); + dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n"); dcb_printf(dcb, "%-25s | %-20s | #Users | Total Sessions\n", "Service Name", "Router Module"); - dcb_printf(dcb, "--------------------------------------------------------------------------\n"); + dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n"); } while (ptr) { @@ -837,6 +850,8 @@ SERVICE *ptr; ptr->stats.n_current, ptr->stats.n_sessions); ptr = ptr->next; } + if (allServices) + dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n\n"); spinlock_release(&service_spin); } @@ -855,9 +870,11 @@ SERV_PROTOCOL *lptr; ptr = allServices; if (ptr) { + dcb_printf(dcb, "Listeners.\n"); + dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+--------\n"); dcb_printf(dcb, "%-20s | %-18s | %-15s | Port | State\n", "Service Name", "Protocol Module", "Address"); - dcb_printf(dcb, "---------------------------------------------------------------------------\n"); + dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+--------\n"); } while (ptr) { @@ -866,7 +883,7 @@ SERV_PROTOCOL *lptr; { dcb_printf(dcb, "%-20s | %-18s | %-15s | %5d | %s\n", ptr->name, lptr->protocol, - (lptr != NULL) ? lptr->address : "*", + (lptr && lptr->address) ? lptr->address : "*", lptr->port, (lptr->listener->session->state == SESSION_STATE_LISTENER_STOPPED) ? "Stopped" : "Running" ); @@ -875,6 +892,8 @@ SERV_PROTOCOL *lptr; } ptr = ptr->next; } + if (allServices) + dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+--------\n\n"); spinlock_release(&service_spin); } diff --git a/server/core/session.c b/server/core/session.c index d02498fd4..49eda37e3 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -165,7 +165,7 @@ session_alloc(SERVICE *service, DCB *client_dcb) session->head.instance = service->router_instance; session->head.session = session->router_session; - session->head.routeQuery = service->router->routeQuery; + session->head.routeQuery = (void *)(service->router->routeQuery); if (service->n_filters > 0) { @@ -537,17 +537,23 @@ SESSION *ptr; ptr = allSessions; if (ptr) { - dcb_printf(dcb, "Session | Client | State\n"); - dcb_printf(dcb, "------------------------------------------\n"); + dcb_printf(dcb, "Sessions.\n"); + dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n"); + dcb_printf(dcb, "Session | Client | Service | State\n"); + dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n"); } while (ptr) { - dcb_printf(dcb, "%-16p | %-15s | %s\n", ptr, + dcb_printf(dcb, "%-16p | %-15s | %-14s | %s\n", ptr, ((ptr->client && ptr->client->remote) ? ptr->client->remote : ""), + (ptr->service && ptr->service->name ? ptr->service->name + : ""), session_state(ptr->state)); ptr = ptr->next; } + if (allSessions) + dcb_printf(dcb, "-----------------+-----------------+----------------+--------------------------\n\n"); spinlock_release(&session_spin); } @@ -668,4 +674,4 @@ bool session_route_query ( return_succp: return succp; } - \ No newline at end of file + diff --git a/server/include/dcb.h b/server/include/dcb.h index 405fb16f1..7b119c220 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -213,7 +213,7 @@ typedef struct dcb { struct session *session; /**< The owning session */ GWPROTOCOL func; /**< The functions for this descriptor */ - unsigned int writeqlen; /**< Current number of byes in the write queue */ + int writeqlen; /**< Current number of byes in the write queue */ SPINLOCK writeqlock; /**< Write Queue spinlock */ GWBUF *writeq; /**< Write Data Queue */ SPINLOCK delayqlock; /**< Delay Backend Write Queue spinlock */ @@ -278,6 +278,7 @@ void printAllDCBs(); /* Debug to print all DCB in the system */ void printDCB(DCB *); /* Debug print routine */ void dprintAllDCBs(DCB *); /* Debug to print all DCB in the system */ void dprintDCB(DCB *, DCB *); /* Debug to print a DCB in the system */ +void dListDCBs(DCB *); /* List all DCBs in the system */ const char *gw_dcb_state2string(int); /* DCB state to string */ void dcb_printf(DCB *, const char *, ...); /* DCB version of printf */ int dcb_isclient(DCB *); /* the DCB is the client of the session */ diff --git a/server/include/service.h b/server/include/service.h index 4df43613d..1c6fd4822 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -155,6 +155,7 @@ extern int serviceStop(SERVICE *); extern int serviceRestart(SERVICE *); extern int serviceSetUser(SERVICE *, char *, char *); extern int serviceGetUser(SERVICE *, char **, char **); +extern void serviceSetFilters(SERVICE *, char *); extern int serviceEnableRootUser(SERVICE *, int ); extern void service_update(SERVICE *, char *, char *, char *); extern int service_refresh_users(SERVICE *); diff --git a/server/modules/include/maxscaled.h b/server/modules/include/maxscaled.h new file mode 100644 index 000000000..3733772d8 --- /dev/null +++ b/server/modules/include/maxscaled.h @@ -0,0 +1,46 @@ +#ifndef _MAXSCALED_H +#define _MAXSCALED_H +/* + * This file is distributed as part of MaxScale. 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 2014 + */ + +/** + * @file maxscaled.h The maxscaled protocol module header file + * + * @verbatim + * Revision History + * + * Date Who Description + * 13/06/14 Mark Riddoch Initial implementation + * + * @endverbatim + */ +#include + +/** + * The telnetd specific protocol structure to put in the DCB. + */ +typedef struct maxscaled { + int state; /**< The connection state */ + char *username; /**< The login name of the user */ +} MAXSCALED; + +#define MAXSCALED_STATE_LOGIN 1 /**< Issued login prompt */ +#define MAXSCALED_STATE_PASSWD 2 /**< Issued password prompt */ +#define MAXSCALED_STATE_DATA 3 /**< User logged in */ + +#endif diff --git a/server/modules/protocol/Makefile b/server/modules/protocol/Makefile index 1d98f76db..789cd9b78 100644 --- a/server/modules/protocol/Makefile +++ b/server/modules/protocol/Makefile @@ -22,6 +22,7 @@ # headers so that liblog_manager.so can # be linked in. # 09/07/2013 Massimiliano Pinto Added the HTTPD protocol module +# 13/06/2014 Mark Riddoch Added thr MaxScale protocol module # include ../../../build_gateway.inc @@ -45,10 +46,12 @@ TELNETDSRCS=telnetd.c TELNETDOBJ=$(TELNETDSRCS:.c=.o) HTTPDSRCS=httpd.c HTTPDOBJ=$(HTTPDSRCS:.c=.o) +MAXSCALEDSRCS=maxscaled.c +MAXSCALEDOBJ=$(MAXSCALEDSRCS:.c=.o) SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS) $(TELNETDSRCS) $(HTTPDSRCS) OBJ=$(SRCS:.c=.o) LIBS=$(UTILSPATH)/skygw_utils.o -MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so +MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so libmaxscaled.so all: $(MODULES) @@ -64,6 +67,9 @@ libtelnetd.so: $(TELNETDOBJ) libHTTPD.so: $(HTTPDOBJ) $(CC) $(LDFLAGS) $(HTTPDOBJ) $(LIBS) -o $@ +libmaxscaled.so: $(MAXSCALEDOBJ) + $(CC) $(LDFLAGS) $(MAXSCALEDOBJ) $(LIBS) -lcrypt -o $@ + .c.o: $(CC) $(CFLAGS) $< -o $@ diff --git a/server/modules/protocol/maxscaled.c b/server/modules/protocol/maxscaled.c new file mode 100644 index 000000000..f26a3d17a --- /dev/null +++ b/server/modules/protocol/maxscaled.c @@ -0,0 +1,383 @@ +/* + * This file is distributed as part of MaxScale. 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 2014 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_INFO info = { + MODULE_API_PROTOCOL, + MODULE_ALPHA_RELEASE, + GWPROTOCOL_VERSION, + "A maxscale protocol for the administration interface" +}; + +extern int lm_enabled_logfiles_bitmask; + +/** + * @file maxscaled.c - MaxScale administration protocol + * + * + * @verbatim + * Revision History + * Date Who Description + * 13/06/2014 Mark Riddoch Initial implementation + * + * @endverbatim + */ + +static char *version_str = "V1.0.0"; + +static int maxscaled_read_event(DCB* dcb); +static int maxscaled_write_event(DCB *dcb); +static int maxscaled_write(DCB *dcb, GWBUF *queue); +static int maxscaled_error(DCB *dcb); +static int maxscaled_hangup(DCB *dcb); +static int maxscaled_accept(DCB *dcb); +static int maxscaled_close(DCB *dcb); +static int maxscaled_listen(DCB *dcb, char *config); + +/** + * The "module object" for the maxscaled protocol module. + */ +static GWPROTOCOL MyObject = { + maxscaled_read_event, /**< Read - EPOLLIN handler */ + maxscaled_write, /**< Write - data from gateway */ + maxscaled_write_event, /**< WriteReady - EPOLLOUT handler */ + maxscaled_error, /**< Error - EPOLLERR handler */ + maxscaled_hangup, /**< HangUp - EPOLLHUP handler */ + maxscaled_accept, /**< Accept */ + NULL, /**< Connect */ + maxscaled_close, /**< Close */ + maxscaled_listen, /**< Create a listener */ + NULL, /**< Authentication */ + NULL /**< Session */ + }; + +static void maxscaled_command(DCB *, unsigned char *cmd); + +/** + * Implementation of the mandatory version entry point + * + * @return version string of the module + */ +char * +version() +{ + return version_str; +} + +/** + * The module initialisation routine, called when the module + * is first loaded. + */ +void +ModuleInit() +{ + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Initialise MaxScaled Protocol module.\n"))); +} + +/** + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +GWPROTOCOL * +GetModuleObject() +{ + return &MyObject; +} + +/** + * Read event for EPOLLIN on the maxscaled protocol module. + * + * @param dcb The descriptor control block + * @return + */ +static int +maxscaled_read_event(DCB* dcb) +{ +int n; +GWBUF *head = NULL; +SESSION *session = dcb->session; +MAXSCALED *maxscaled = (MAXSCALED *)dcb->protocol; +char *password; + + if ((n = dcb_read(dcb, &head)) != -1) + { + if (head) + { + unsigned char *ptr = GWBUF_DATA(head); + ptr = GWBUF_DATA(head); + if (GWBUF_LENGTH(head)) + { + switch (maxscaled->state) + { + case MAXSCALED_STATE_LOGIN: + maxscaled->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head)); + maxscaled->state = MAXSCALED_STATE_PASSWD; + dcb_printf(dcb, "PASSWORD"); + gwbuf_consume(head, GWBUF_LENGTH(head)); + break; + case MAXSCALED_STATE_PASSWD: + password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head)); + if (admin_verify(maxscaled->username, password)) + { + dcb_printf(dcb, "OK----"); + maxscaled->state = MAXSCALED_STATE_DATA; + } + else + { + dcb_printf(dcb, "FAILED"); + maxscaled->state = MAXSCALED_STATE_LOGIN; + free(maxscaled->username); + } + gwbuf_consume(head, GWBUF_LENGTH(head)); + free(password); + break; + case MAXSCALED_STATE_DATA: + SESSION_ROUTE_QUERY(session, head); + dcb_printf(dcb, "OK"); + break; + } + } + else + { + // Force the free of the buffer header + gwbuf_consume(head, 0); + } + } + } + return n; +} + +/** + * EPOLLOUT handler for the maxscaled protocol module. + * + * @param dcb The descriptor control block + * @return + */ +static int +maxscaled_write_event(DCB *dcb) +{ + return dcb_drain_writeq(dcb); +} + +/** + * Write routine for the maxscaled protocol module. + * + * Writes the content of the buffer queue to the socket + * observing the non-blocking principles of MaxScale. + * + * @param dcb Descriptor Control Block for the socket + * @param queue Linked list of buffes to write + */ +static int +maxscaled_write(DCB *dcb, GWBUF *queue) +{ + int rc; + rc = dcb_write(dcb, queue); + return rc; +} + +/** + * Handler for the EPOLLERR event. + * + * @param dcb The descriptor control block + */ +static int +maxscaled_error(DCB *dcb) +{ + return 0; +} + +/** + * Handler for the EPOLLHUP event. + * + * @param dcb The descriptor control block + */ +static int +maxscaled_hangup(DCB *dcb) +{ + return 0; +} + +/** + * Handler for the EPOLLIN event when the DCB refers to the listening + * socket for the protocol. + * + * @param dcb The descriptor control block + * @return The number of new connections created + */ +static int +maxscaled_accept(DCB *dcb) +{ +int n_connect = 0; + + while (1) + { + int so; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr); + DCB *client_dcb; + MAXSCALED *maxscaled_pr = NULL; + + so = accept(dcb->fd, (struct sockaddr *)&addr, &addrlen); + + if (so == -1) + return n_connect; + else + { + atomic_add(&dcb->stats.n_accepts, 1); + client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); + + if (client_dcb == NULL) + + { + return n_connect; + } + client_dcb->fd = so; + client_dcb->remote = strdup(inet_ntoa(addr.sin_addr)); + memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); + client_dcb->session = + session_alloc(dcb->session->service, client_dcb); + maxscaled_pr = (MAXSCALED *)malloc(sizeof(MAXSCALED)); + client_dcb->protocol = (void *)maxscaled_pr; + + if (maxscaled_pr == NULL) + { + dcb_add_to_zombieslist(client_dcb); + return n_connect; + } + + if (poll_add_dcb(client_dcb) == -1) + { + dcb_add_to_zombieslist(dcb); + return n_connect; + } + n_connect++; + maxscaled_pr->state = MAXSCALED_STATE_LOGIN; + maxscaled_pr->username = NULL; + dcb_printf(client_dcb, "USER"); + } + } + return n_connect; +} + +/** + * The close handler for the descriptor. Called by the gateway to + * explicitly close a connection. + * + * @param dcb The descriptor control block + */ + +static int +maxscaled_close(DCB *dcb) +{ +MAXSCALED *maxscaled = dcb->protocol; + + if (maxscaled && maxscaled->username) + free(maxscaled->username); + + dcb_close(dcb); + return 0; +} + +/** + * Maxscale daemon listener entry point + * + * @param listener The Listener DCB + * @param config Configuration (ip:port) + */ +static int +maxscaled_listen(DCB *listener, char *config) +{ +struct sockaddr_in addr; +int one = 1; +int rc; + + memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL)); + + if (!parse_bindconfig(config, 6033, &addr)) + return 0; + + + if ((listener->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + return 0; + } + + // socket options + setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); + // set NONBLOCKING mode + setnonblocking(listener->fd); + // bind address and port + if (bind(listener->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + return 0; + } + + rc = listen(listener->fd, SOMAXCONN); + + if (rc == 0) { + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "Listening maxscale connections at %s\n", + config))); + } else { + int eno = errno; + errno = 0; + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "Failed to start listening for maxscale admin connections " + "due error %d, %s\n\n", + eno, + strerror(eno)))); + return 0; + } + + + if (poll_add_dcb(listener) == -1) + { + return 0; + } + return 1; +} diff --git a/server/modules/routing/Makefile b/server/modules/routing/Makefile index 1e50f3455..8019513c7 100644 --- a/server/modules/routing/Makefile +++ b/server/modules/routing/Makefile @@ -42,10 +42,12 @@ READCONSRCS=readconnroute.c READCONOBJ=$(READCONSRCS:.c=.o) DEBUGCLISRCS=debugcli.c debugcmd.c DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o) -SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) +CLISRCS=cli.c debugcmd.c +CLIOBJ=$(CLISRCS:.c=.o) +SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) cli.c OBJ=$(SRCS:.c=.o) LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager -MODULES= libdebugcli.so libreadconnroute.so libtestroute.so +MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so all: $(MODULES) @@ -59,6 +61,9 @@ libreadconnroute.so: $(READCONOBJ) libdebugcli.so: $(DEBUGCLIOBJ) $(CC) $(LDFLAGS) $(DEBUGCLIOBJ) $(LIBS) -o $@ +libcli.so: $(CLIOBJ) + $(CC) $(LDFLAGS) $(CLIOBJ) $(LIBS) -o $@ + libreadwritesplit.so: # (cd readwritesplit; touch depend.mk ; make; cp $@ ..) diff --git a/server/modules/routing/cli.c b/server/modules/routing/cli.c new file mode 100644 index 000000000..9abfa5377 --- /dev/null +++ b/server/modules/routing/cli.c @@ -0,0 +1,297 @@ +/* + * This file is distributed as part of MaxScale. 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 2014 + */ + +/** + * @file cli.c - A "routing module" that in fact merely gives access + * to a command line interface + * + * @verbatim + * Revision History + * + * Date Who Description + * 18/06/13 Mark Riddoch Initial implementation + * 13/06/14 Mark Riddoch Creted from the debugcli + * + * @endverbatim + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_INFO info = { + MODULE_API_ROUTER, + MODULE_ALPHA_RELEASE, + ROUTER_VERSION, + "The admin user interface" +}; + +extern int lm_enabled_logfiles_bitmask; + +static char *version_str = "V1.0.0"; + +/* The router entry points */ +static ROUTER *createInstance(SERVICE *service, char **options); +static void *newSession(ROUTER *instance, SESSION *session); +static void closeSession(ROUTER *instance, void *router_session); +static void freeSession(ROUTER *instance, void *router_session); +static int execute(ROUTER *instance, void *router_session, GWBUF *queue); +static void diagnostics(ROUTER *instance, DCB *dcb); +static uint8_t getCapabilities (ROUTER* inst, void* router_session); + +/** The module object definition */ +static ROUTER_OBJECT MyObject = { + createInstance, + newSession, + closeSession, + freeSession, + execute, + diagnostics, + NULL, + NULL, + getCapabilities +}; + +extern int execute_cmd(CLI_SESSION *cli); + +static SPINLOCK instlock; +static CLI_INSTANCE *instances; + +/** + * Implementation of the mandatory version entry point + * + * @return version string of the module + */ +char * +version() +{ + return version_str; +} + +/** + * The module initialisation routine, called when the module + * is first loaded. + */ +void +ModuleInit() +{ + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "Initialise CLI router module %s.\n", + version_str))); + spinlock_init(&instlock); + instances = NULL; +} + +/** + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +ROUTER_OBJECT * +GetModuleObject() +{ + return &MyObject; +} + +/** + * Create an instance of the router for a particular service + * within the gateway. + * + * @param service The service this router is being create for + * @param options Any array of options for the query router + * + * @return The instance data for this new instance + */ +static ROUTER * +createInstance(SERVICE *service, char **options) +{ +CLI_INSTANCE *inst; +int i; + + if ((inst = malloc(sizeof(CLI_INSTANCE))) == NULL) + return NULL; + + inst->service = service; + spinlock_init(&inst->lock); + inst->sessions = NULL; + inst->mode = CLIM_USER; + + if (options) + { + for (i = 0; options[i]; i++) + { + { + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "Unknown option for CLI '%s'\n", + options[i]))); + } + } + } + + /* + * We have completed the creation of the instance data, so now + * insert this router instance into the linked list of routers + * that have been created with this module. + */ + spinlock_acquire(&instlock); + inst->next = instances; + instances = inst; + spinlock_release(&instlock); + + return (ROUTER *)inst; +} + +/** + * Associate a new session with this instance of the router. + * + * @param instance The router instance data + * @param session The session itself + * @return Session specific data for this session + */ +static void * +newSession(ROUTER *instance, SESSION *session) +{ +CLI_INSTANCE *inst = (CLI_INSTANCE *)instance; +CLI_SESSION *client; + + if ((client = (CLI_SESSION *)malloc(sizeof(CLI_SESSION))) == NULL) + { + return NULL; + } + client->session = session; + + memset(client->cmdbuf, 0, 80); + + spinlock_acquire(&inst->lock); + client->next = inst->sessions; + inst->sessions = client; + spinlock_release(&inst->lock); + + session->state = SESSION_STATE_READY; + client->mode = inst->mode; + + return (void *)client; +} + +/** + * Close a session with the router, this is the mechanism + * by which a router may cleanup data structure etc. + * + * @param instance The router instance data + * @param router_session The session being closed + */ +static void +closeSession(ROUTER *instance, void *router_session) +{ +CLI_INSTANCE *inst = (CLI_INSTANCE *)instance; +CLI_SESSION *session = (CLI_SESSION *)router_session; + + + spinlock_acquire(&inst->lock); + if (inst->sessions == session) + inst->sessions = session->next; + else + { + CLI_SESSION *ptr = inst->sessions; + while (ptr && ptr->next != session) + ptr = ptr->next; + if (ptr) + ptr->next = session->next; + } + spinlock_release(&inst->lock); + /** + * Router session is freed in session.c:session_close, when session who + * owns it, is freed. + */ +} + +/** + * Free a debugcli session + * + * @param router_instance The router session + * @param router_client_session The router session as returned from newSession + */ +static void freeSession( + ROUTER* router_instance, + void* router_client_session) +{ + free(router_client_session); + return; +} + +/** + * We have data from the client, we must route it to the backend. + * This is simply a case of sending it to the connection that was + * chosen when we started the client session. + * + * @param instance The router instance + * @param router_session The router session returned from the newSession call + * @param queue The queue of data buffers to route + * @return The number of bytes sent + */ +static int +execute(ROUTER *instance, void *router_session, GWBUF *queue) +{ +CLI_SESSION *session = (CLI_SESSION *)router_session; + + /* Extract the characters */ + while (queue) + { + strncat(session->cmdbuf, GWBUF_DATA(queue), GWBUF_LENGTH(queue)); + queue = gwbuf_consume(queue, GWBUF_LENGTH(queue)); + } + + execute_cmd(session); + return 1; +} + +/** + * Display router diagnostics + * + * @param instance Instance of the router + * @param dcb DCB to send diagnostics to + */ +static void +diagnostics(ROUTER *instance, DCB *dcb) +{ + return; /* Nothing to do currently */ +} + +static uint8_t getCapabilities( + ROUTER* inst, + void* router_session) +{ + return 0; +} diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index eb0fc2832..fb638d541 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -116,7 +116,7 @@ struct subcommand showoptions[] = { "Show the poll statistics", "Show the poll statistics", {0, 0, 0} }, - { "filter", 0, dprintFilter, + { "filter", 1, dprintFilter, "Show details of a filter, called with a filter name", "Show details of a filter, called with the address of a filter", {ARG_TYPE_FILTER, 0, 0} }, @@ -168,6 +168,10 @@ struct subcommand showoptions[] = { * The subcommands of the list command */ struct subcommand listoptions[] = { + { "dcbs", 0, dListDCBs, + "List all the DCBs active within MaxScale", + "List all the DCBs active within MaxScale", + {0, 0, 0} }, { "filters", 0, dListFilters, "List all the filters defined within MaxScale", "List all the filters defined within MaxScale", @@ -597,6 +601,8 @@ char *ptr, *lptr; if (args[0] == NULL || *args[0] == 0) return 1; + for (i = 0; args[i] && *args[i]; i++) + ; argc = i - 2; /* The number of extra arguments to commands */