Merge branch 'develop' into MAX-11

This commit is contained in:
VilhoRaatikka
2014-06-16 19:53:19 +03:00
20 changed files with 1363 additions and 39 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -52,6 +52,7 @@
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
#include <mysql.h>
#include <monitor.h>
#include <version.h>

View File

@ -130,6 +130,7 @@ setipaddress(struct in_addr *a, char *p) {
return 1;
}
#endif
return 0;
}
/**

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 *);

View File

@ -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 <dcb.h>
/**
* 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

View File

@ -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 $@

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dcb.h>
#include <buffer.h>
#include <service.h>
#include <session.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <router.h>
#include <poll.h>
#include <atomic.h>
#include <gw.h>
#include <adminusers.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <modinfo.h>
#include <maxscaled.h>
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;
}

View File

@ -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 $@ ..)

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <service.h>
#include <session.h>
#include <router.h>
#include <modules.h>
#include <modinfo.h>
#include <atomic.h>
#include <spinlock.h>
#include <dcb.h>
#include <poll.h>
#include <debugcli.h>
#include <skygw_utils.h>
#include <log_manager.h>
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;
}

View File

@ -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 */