A number of updates to do with service startup
Telnet protocol support Debug cli
This commit is contained in:
parent
461dc31578
commit
53b6bc0a25
31
README
31
README
@ -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
|
||||
|
||||
|
||||
*/
|
||||
|
@ -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
|
||||
|
32
core/dcb.c
32
core/dcb.c
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
60
modules/include/debugcli.h
Normal file
60
modules/include/debugcli.h
Normal 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
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
272
modules/routing/debugcli.c
Normal 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);
|
||||
}
|
@ -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];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user