A number of updates to do with service startup

Telnet protocol support

Debug cli
This commit is contained in:
Mark Riddoch 2013-06-19 12:31:40 +02:00
parent 461dc31578
commit 53b6bc0a25
21 changed files with 580 additions and 51 deletions

31
README
View File

@ -22,4 +22,35 @@ function pointers. This structured is called the "module object".
The code that routes the queries to the database servers is also loaed
as external shared objects and are referred to as routing modules.
\section Building Building the Gateway
The gateway is designed to be built using make. Simply run the make command
from the top level and it will build all the components ofthe system.
Other make targets are available
install - Installs the binary and the modules in the location defined by the
make variable DEST
ctags - Build tags files for the vi editor
documentation - Build the doxygen documentation
depend - Update the dependencies used by the makefiles
\section Running Running the Gateway
The gateway consists of a core executable and a number of modules that implement
the different protocols and routing algorithms. These modules are built as
shared objects that are loaded on demand. In order for the gateway to find these
modules it will search using a predescribed search path. The rules are:
1. Look in the current directory for the module
2. Look in $GATEWAY_HOME/modules
3. Look in /usr/local/skysql/gateway/modules
*/

View File

@ -21,7 +21,7 @@
# 17/06/13 Mark Riddoch Addition of dependency generation
CC=cc
CFLAGS=-c -I/usr/include -I../include -Wall
CFLAGS=-c -I/usr/include -I../include -Wall -g
LDFLAGS=-rdynamic
SRCS= atomic.c buffer.c spinlock.c gateway.c gateway_mysql_protocol.c gw_utils.c \
utils.c dcb.c load_utils.c session.c service.c server.c

View File

@ -378,6 +378,38 @@ DCB *dcb;
spinlock_release(dcbspin);
}
/**
* Diagnostic to print all DCB allocated in the system
*
*/
void dprintAllDCBs(DCB *pdcb)
{
DCB *dcb;
if (dcbspin == NULL)
{
if ((dcbspin = malloc(sizeof(SPINLOCK))) == NULL)
return;
spinlock_init(dcbspin);
}
spinlock_acquire(dcbspin);
dcb = allDCBs;
while (dcb)
{
dcb_printf(pdcb, "DCB: 0x%p\n", (void *)dcb);
dcb_printf(pdcb, "\tDCB state: %s\n", gw_dcb_state2string(dcb->state));
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 = dcb->next;
}
spinlock_release(dcbspin);
}
/**
* Return a string representation of a DCB state.
*

View File

@ -285,7 +285,10 @@ load_utils.o: load_utils.c /usr/include/sys/param.h \
/usr/include/unistd.h /usr/include/bits/posix_opt.h \
/usr/include/bits/environments.h /usr/include/bits/confname.h \
/usr/include/getopt.h /usr/include/string.h /usr/include/xlocale.h \
/usr/include/dlfcn.h /usr/include/bits/dlfcn.h ../include/modules.h
/usr/include/dlfcn.h /usr/include/bits/dlfcn.h ../include/modules.h \
../include/dcb.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/buffer.h
session.o: session.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 \
@ -347,4 +350,4 @@ server.o: server.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/string.h /usr/include/xlocale.h ../include/session.h \
../include/server.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
/usr/include/bits/setjmp.h ../include/dcb.h ../include/buffer.h

View File

@ -176,7 +176,7 @@ struct epoll_event ev;
int nfds;
int n;
unsigned short port = 4406;
SERVICE *service;
SERVICE *service1, *service2;
SERVER *server1, *server2, *server3;
for (n = 0; n < argc; n++)
@ -185,6 +185,11 @@ SERVER *server1, *server2, *server3;
{
port = atoi(&argv[n][2]);
}
if (strcmp(argv[n], "-d") == 0)
{
// Debug mode
daemon_mode = 0;
}
}
/*
@ -193,16 +198,21 @@ SERVER *server1, *server2, *server3;
* will build a static configuration here
*/
if ((service = service_alloc("Test Service", "readconnroute")) == NULL)
if ((service1 = service_alloc("Test Service", "readconnroute")) == NULL)
exit(1);
serviceAddProtocol(service, "MySQLClient", port);
serviceAddProtocol(service1, "MySQLClient", port);
server1 = server_alloc("127.0.0.1", "MySQLBackend", 3306);
server2 = server_alloc("127.0.0.1", "MySQLBackend", 3307);
server3 = server_alloc("127.0.0.1", "MySQLBackend", 3308);
serviceAddBackend(service, server1);
serviceAddBackend(service, server2);
serviceAddBackend(service, server3);
serviceAddBackend(service1, server1);
serviceAddBackend(service1, server2);
serviceAddBackend(service1, server3);
if ((service2 = service_alloc("Debug Service", "debugcli")) == NULL)
exit(1);
serviceAddProtocol(service2, "telnetd", 4442);
fprintf(stderr, "(C) SkySQL Ab 2013\n");
@ -246,7 +256,8 @@ SERVER *server1, *server2, *server3;
/*
* Start the service that was created above
*/
serviceStart(service, epollfd);
serviceStart(service1, epollfd);
serviceStart(service2, epollfd);
fprintf(stderr, ">> GATEWAY epoll maxevents is %i\n", MAX_EVENTS);
@ -259,7 +270,7 @@ SERVER *server1, *server2, *server3;
5. bind
6. epoll add event
*/
MySQLListener(epollfd, port);
// MySQLListener(epollfd, port);
// event loop for all the descriptors added via epoll_ctl
while (1) {

View File

@ -246,3 +246,22 @@ MODULES *ptr = registered;
ptr = ptr->next;
}
}
/**
* Print Modules to a DCB
*
* Diagnostic routine to display all the loaded modules
*/
void
dprintAllModules(DCB *dcb)
{
MODULES *ptr = registered;
dcb_printf(dcb, "%-15s | %-10s | Version\n", "Module Name", "Module Type");
dcb_printf(dcb, "-----------------------------------------------------\n");
while (ptr)
{
dcb_printf(dcb, "%-15s | %-10s | %s\n", ptr->module, ptr->type, ptr->version);
ptr = ptr->next;
}
}

View File

@ -33,6 +33,7 @@
#include <session.h>
#include <server.h>
#include <spinlock.h>
#include <dcb.h>
static SPINLOCK server_spin = SPINLOCK_INIT;
static SERVER *allServers = NULL;
@ -139,3 +140,27 @@ SERVER *ptr;
}
spinlock_release(&server_spin);
}
/**
* Print all servers to a DCB
*
* Designed to be called within a debugger session in order
* to display all active servers within the gateway
*/
void
dprintAllServers(DCB *dcb)
{
SERVER *ptr;
spinlock_acquire(&server_spin);
ptr = allServers;
while (ptr)
{
dcb_printf(dcb, "Server %p\n", ptr);
dcb_printf(dcb, "\tServer: %s\n", ptr->name);
dcb_printf(dcb, "\tProtocol: %s\n", ptr->protocol);
dcb_printf(dcb, "\tPort: %d\n", ptr->port);
ptr = ptr->next;
}
spinlock_release(&server_spin);
}

View File

@ -36,6 +36,7 @@
#include <router.h>
#include <spinlock.h>
#include <modules.h>
#include <dcb.h>
static SPINLOCK service_spin = SPINLOCK_INIT;
static SERVICE *allServices = NULL;
@ -96,6 +97,8 @@ int listeners = 0;
char config_bind[40];
GWPROTOCOL *funcs;
service->router_instance = service->router->createInstance(service);
port = service->ports;
while (port)
{
@ -111,15 +114,16 @@ GWPROTOCOL *funcs;
memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
port->listener->session = NULL;
sprintf(config_bind, "0.0.0.0:%d", port->port);
if (port->listener->func.listen(efd, config_bind))
if (port->listener->func.listen(port->listener, efd, config_bind))
listeners++;
port->listener->session = session_alloc(service, port->listener);
port->listener->session->state = SESSION_STATE_LISTENER;
port = port->next;
}
port->listener->session = session_alloc(service, port->listener);
port->listener->session->state = SESSION_STATE_LISTENER;
if (listeners)
service->stats.started = time(0);
service->router_instance = service->router->createInstance(service);
return listeners;
}
@ -247,3 +251,39 @@ SERVICE *ptr;
}
spinlock_release(&service_spin);
}
/**
* Print all services to a DCB
*
* Designed to be called within a debugger session in order
* to display all active services within the gateway
*/
void
dprintAllServices(DCB *dcb)
{
SERVICE *ptr;
spinlock_acquire(&service_spin);
ptr = allServices;
while (ptr)
{
SERVER *server = ptr->databases;
dcb_printf(dcb, "Service %p\n", ptr);
dcb_printf(dcb, "\tService: %s\n", ptr->name);
dcb_printf(dcb, "\tRouter: %s (%p)\n", ptr->routerModule,
ptr->router);
dcb_printf(dcb, "\tStarted: %s",
asctime(localtime(&ptr->stats.started)));
dcb_printf(dcb, "\tBackend databases\n");
while (server)
{
dcb_printf(dcb, "\t\t%s:%d %s\n", server->name, server->port,
server->protocol);
server = server->next;
}
dcb_printf(dcb, "\tTotal connections: %d\n", ptr->stats.n_sessions);
dcb_printf(dcb, "\tCurrently connected: %d\n", ptr->stats.n_current);
ptr = ptr->next;
}
spinlock_release(&service_spin);
}

View File

@ -146,3 +146,27 @@ SESSION *ptr;
}
spinlock_release(&session_spin);
}
/**
* Print all sessions to a DCB
*
* Designed to be called within a debugger session in order
* to display all active sessions within the gateway
*/
void
dprintAllSessions(DCB *dcb)
{
SESSION *ptr;
spinlock_acquire(&session_spin);
ptr = allSessions;
while (ptr)
{
dcb_printf(dcb, "Session %p\n", ptr);
dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service);
dcb_printf(dcb, "\tClient DCB: %p\n", ptr->client);
dcb_printf(dcb, "\tConnected: %s", asctime(localtime(&ptr->stats.connect)));
ptr = ptr->next;
}
spinlock_release(&session_spin);
}

View File

@ -73,7 +73,7 @@ typedef struct gw_protocol {
int (*accept)(struct dcb *, int);
int (*connect)(struct server *, struct session *, int);
int (*close)(struct dcb *, int);
int (*listen)(int, char *);
int (*listen)(struct dcb *, int, char *);
} GWPROTOCOL;
/**
@ -133,6 +133,7 @@ extern int dcb_write(DCB *, GWBUF *); /* Generic write routine */
extern int dcb_drain_writeq(DCB *); /* Generic write routine */
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 const char *gw_dcb_state2string(int); /* DCB state to string */
extern void dcb_printf(DCB *, const char *, ...); /* DCB version of printf */

View File

@ -17,6 +17,7 @@
*
* Copyright SkySQL Ab 2013
*/
#include <dcb.h>
/**
* @file modules.h Utilities for loading modules
@ -51,4 +52,5 @@ typedef struct modules {
extern void *load_module(const char *module, const char *type);
extern void unload_module(const char *module);
extern void printModules();
extern void dprintAllModules(DCB *);
#endif

View File

@ -59,4 +59,5 @@ extern SERVER *server_alloc(char *, char *, unsigned short);
extern int server_free(SERVER *);
extern void printServer(SERVER *);
extern void printAllServers();
extern void dprintAllServers();
#endif

View File

@ -98,4 +98,5 @@ extern void serviceAddBackend(SERVICE *, SERVER *);
extern int serviceStart(SERVICE *, int);
extern void printService(SERVICE *);
extern void printAllServices();
extern void dprintAllServices(DCB *);
#endif

View File

@ -70,4 +70,5 @@ extern SESSION *session_alloc(struct service *, struct dcb *);
extern void session_free(SESSION *);
extern void printAllSessions();
extern void printSession(SESSION *);
extern void dprintAllSessions(struct dcb *);
#endif

View File

@ -0,0 +1,60 @@
#ifndef _DEBUGCLI_H
#define _DEBUGCLI_H
/*
* 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
*/
#include <service.h>
#include <session.h>
#include <spinlock.h>
/**
* @file debugcli.h The debug interface to the gateway
*
* @verbatim
* Revision History
*
* Date Who Description
* 18/06/13 Mark Riddoch Initial implementation
*
* @endverbatim
*/
struct cli_session;
/**
* The CLI_INSTANCE structure. There is one instane of the CLI "router" for
* each service that uses the CLI.
*/
typedef struct cli_instance {
SPINLOCK lock; /**< The instance spinlock */
SERVICE *service; /**< The debug cli service */
struct cli_session
*sessions; /**< Linked list of sessions within this instance */
struct cli_instance
*next; /**< The next pointer for the list of instances */
} CLI_INSTANCE;
/**
* The CLI_SESSION structure. As CLI_SESSION is created for each user that logs into
* the DEBUG CLI.
*/
typedef struct cli_session {
char cmdbuf[80]; /**< The command buffer used to build up user commands */
SESSION *session; /**< The gateway session */
struct cli_session
*next; /**< The next pointer for the list of sessions */
} CLI_SESSION;
#endif

View File

@ -20,16 +20,18 @@
# 17/06/2013 Massimiliano Pinto Added mysql_common top both libraries
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -g
LDFLAGS=-shared
MYSQLCLIENTSRCS=mysql_client.c mysql_common.c
MYSQLCLIENTOBJ=$(MYSQLCLIENTSRCS:.c=.o)
MYSQLBACKENDSRCS=mysql_backend.c mysql_common.c
MYSQLBACKENDOBJ=$(MYSQLBACKENDSRCS:.c=.o)
TELNETDSRCS=telnetd.c
TELNETDOBJ=$(TELNETDSRCS:.c=.o)
SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS)
OBJ=$(SRCS:.c=.o)
LIBS=
MODULES=libMySQLClient.so libMySQLBackend.so
MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so
all: $(MODULES)
@ -39,11 +41,14 @@ libMySQLClient.so: $(MYSQLCLIENTOBJ)
libMySQLBackend.so: $(MYSQLBACKENDOBJ)
$(CC) $(LDFLAGS) $(MYSQLBACKENDOBJ) $(LIBS) -o $@
libtelnetd.so: $(TELNETDOBJ)
$(CC) $(LDFLAGS) $(TELNETDOBJ) $(LIBS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f $(OBJ) libMySQLClient.so libMySQLBackend.so
rm -f $(OBJ) $(MODULES)
tags:
ctags $(SRCS) $(HDRS)

View File

@ -33,7 +33,7 @@
static char *version_str = "V1.0.0";
static int gw_MySQLAccept(DCB *listener, int efd);
static int gw_MySQLListener(int epfd, char *config_bind);
static int gw_MySQLListener(DCB *listener, int epfd, char *config_bind);
static int gw_read_client_event(DCB* dcb, int epfd);
static int gw_write_client_event(DCB *dcb, int epfd);
static int gw_MySQLWrite_client(DCB *dcb, GWBUF *queue);
@ -904,8 +904,7 @@ int gw_write_client_event(DCB *dcb, int epfd) {
///
// set listener for mysql protocol
///
int gw_MySQLListener(int epfd, char *config_bind) {
DCB *listener;
int gw_MySQLListener(DCB *listener, int epfd, char *config_bind) {
int l_so;
int fl;
struct sockaddr_in serv_addr;
@ -921,9 +920,6 @@ int gw_MySQLListener(int epfd, char *config_bind) {
// this gateway, as default, will bind on port 4404 for localhost only
(config_bind != NULL) ? (bind_address_and_port = config_bind) : (bind_address_and_port = "127.0.0.1:4406");
listener = (DCB *) calloc(1, sizeof(DCB));
listener->state = DCB_STATE_ALLOC;
listener->fd = -1;
memset(&serv_addr, 0, sizeof serv_addr);

View File

@ -16,6 +16,7 @@
* Copyright SkySQL Ab 2013
*/
#include <stdio.h>
#include <string.h>
#include <dcb.h>
#include <buffer.h>
#include <service.h>
@ -26,6 +27,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <router.h>
/**
* @file telnetd.c - telnet daemon protocol module
@ -56,7 +58,7 @@ static int telnetd_error(DCB *dcb, int event);
static int telnetd_hangup(DCB *dcb, int event);
static int telnetd_accept(DCB *dcb, int event);
static int telnetd_close(DCB *dcb, int event);
static int telnetd_listen(DCB *dcb, int event);
static int telnetd_listen(DCB *dcb, int efd, char *config);
/**
* The "module object" for the telnetd protocol module.
@ -122,7 +124,7 @@ int n;
GWBUF *head = NULL;
SESSION *session = dcb->session;
ROUTER_OBJECT *router = session->service->router;
ROUTER *router_instanc = session->service->router_instance;
ROUTER *router_instance = session->service->router_instance;
void *rsession = session->router_session;
if ((n = dcb_read(dcb, &head)) != -1)
@ -143,7 +145,7 @@ void *rsession = session->router_session;
static int
telnetd_write_event(DCB *dcb, int epfd)
{
int dcb_drain_writeq(dcb);
return dcb_drain_writeq(dcb);
}
/**
@ -188,10 +190,10 @@ telnetd_hangup(DCB *dcb, int event)
* socket for the protocol.
*
* @param dcb The descriptor control block
* @param event The epoll descriptor
* @param efd The epoll descriptor
*/
static int
telnetd_accept(DCB *dcb, int event)
telnetd_accept(DCB *dcb, int efd)
{
int n_connect = 0;
@ -210,14 +212,14 @@ int n_connect = 0;
atomic_add(&dcb->stats.n_accepts, 1);
client = alloc_dcb();
client->fd = so;
memcpy(&client->func, MyObject, sizeof(GWPROTOCOL));
client->session = session_alloc(listener->service, client);
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
client->session = session_alloc(dcb->session->service, client);
ee.events = EPOLLIN | EPOLLOUT | EPOLLET;
ee.data.ptr = client;
client->state = DCB_STATE_IDLE;
if (epoll_ctl(efd, EPOLL_CTL_ADD, c_sock, &ee) == -1)
if (epoll_ctl(efd, EPOLL_CTL_ADD, so, &ee) == -1)
{
return n_connect;
}
@ -249,20 +251,15 @@ telnetd_close(DCB *dcb, int event)
* @param config Configuration (ip:port)
*/
static int
telnetd_listen(int efd, char *config)
telnetd_listen(DCB *listener, int efd, char *config)
{
DCB *listener;
struct sockaddr_in addr;
char *port;
struct epoll_event ev;
int one;
int one = 1;
short pnum;
if ((listener = dcb_alloc()) == NULL)
{
return 0;
}
memcpy(&listener->func, MyObject, sizeof(GWPROTOCOL));
memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL));
port = strrchr(config, ':');
if (port)
@ -272,7 +269,9 @@ int one;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
pnum = atoi(port);
addr.sin_port = htons(pnum);
printf("telnetd listen on port %d from %s from %s\n", pnum, port, config);
if ((listener->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
@ -283,8 +282,8 @@ int one;
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 *) &serv_addr, sizeof(serv_addr)) < 0)
// bind address and port
if (bind(listener->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
return 0;
}
@ -294,7 +293,7 @@ int one;
ev.events = EPOLLIN;
ev.data.ptr = listener;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, l_so, &ev) == -1)
if (epoll_ctl(efd, EPOLL_CTL_ADD, listener->fd, &ev) == -1)
{
return 0;
}

View File

@ -19,16 +19,18 @@
# 13/06/13 Mark Riddoch Initial routing module development
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -Wall
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -Wall -g
LDFLAGS=-shared
TESTSRCS=testroute.c
TESTOBJ=$(TESTSRCS:.c=.o)
READCONSRCS=readconnroute.c
READCONOBJ=$(READCONSRCS:.c=.o)
DEBUGSRCS=debugcli.c
DEBUGCLIOBJ=$(DEBUGSRCS:.c=.o)
SRCS=$(TESTSRCS) $(READCONSRCS)
OBJ=$(SRCS:.c=.o)
LIBS=-lssl
MODULES=libtestroute.so libreadconnroute.so
MODULES=libtestroute.so libreadconnroute.so libdebugcli.so
all: $(MODULES)
@ -38,11 +40,14 @@ libtestroute.so: $(TESTOBJ)
libreadconnroute.so: $(READCONOBJ)
$(CC) $(LDFLAGS) $(READCONOBJ) $(LIBS) -o $@
libdebugcli.so: $(DEBUGCLIOBJ)
$(CC) $(LDFLAGS) $(DEBUGCLIOBJ) $(LIBS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f $(OBJ) libtestroute.so
rm -f $(OBJ) $(MODULES)
tags:
ctags $(SRCS) $(HDRS)

272
modules/routing/debugcli.c Normal file
View File

@ -0,0 +1,272 @@
/*
* 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 debugcli.c - A "routing module" that in fact merely gives
* access to debug commands within the gateway
*
* @verbatim
* Revision History
*
* Date Who Description
* 18/06/13 Mark Riddoch Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <service.h>
#include <session.h>
#include <router.h>
#include <modules.h>
#include <atomic.h>
#include <spinlock.h>
#include <dcb.h>
#include <debugcli.h>
static char *version_str = "V1.0.0";
/* The router entry points */
static ROUTER *createInstance(SERVICE *service);
static void *newSession(ROUTER *instance, SESSION *session);
static void closeSession(ROUTER *instance, void *router_session);
static int execute(ROUTER *instance, void *router_session, GWBUF *queue);
/** The module object definition */
static ROUTER_OBJECT MyObject = { createInstance, newSession, closeSession, execute };
static void 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()
{
fprintf(stderr, "Initial debug router module.\n");
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()
{
fprintf(stderr, "Returing debug router module object.\n");
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
*
* @return The instance data for this new instance
*/
static ROUTER *
createInstance(SERVICE *service)
{
CLI_INSTANCE *inst;
if ((inst = malloc(sizeof(CLI_INSTANCE))) == NULL)
return NULL;
inst->service = service;
spinlock_init(&inst->lock);
inst->sessions = NULL;
/*
* 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, 80, 0);
spinlock_acquire(&inst->lock);
client->next = inst->sessions;
inst->sessions = client;
spinlock_release(&inst->lock);
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;
/*
* Close the connection to the backend
*/
session->session->client->func.close(session->session->client, 0);
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);
/*
* We are no longer in the linked list, free
* all the memory and other resources associated
* to the client session.
*/
free(session);
}
/**
* 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 */
strncat(session->cmdbuf, GWBUF_DATA(queue), GWBUF_LENGTH(queue));
/* Echo back to the user */
dcb_write(session->session->client, queue);
if (strrchr(session->cmdbuf, '\n'))
{
execute_cmd(session);
dcb_printf(session->session->client, "Gateway> ");
}
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 },
{ NULL, NULL }
};
/**
* We have a complete line from the user, lookup the commands and execute them
*
* @param cli The CLI_SESSION
*/
static void
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
{
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, 80, 0);
}

View File

@ -130,7 +130,7 @@ int i, n;
for (server = service->databases, n = 0; server; server = server->nextdb)
n++;
inst->servers = (BACKEND **)calloc(n, sizeof(BACKEND *));
inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *));
if (!inst->servers)
{
free(inst);
@ -153,6 +153,7 @@ int i, n;
inst->servers[n]->count = 0;
n++;
}
inst->servers[n] = NULL;
/*
* We have completed the creation of the instance data, so now
@ -194,7 +195,7 @@ int i;
candidate = inst->servers[0];
for (i = 1; inst->servers[i]; i++);
{
if (inst->servers[i]->count < candidate->count)
if (inst->servers[i] && inst->servers[i]->count < candidate->count)
candidate = inst->servers[i];
}