Merge branch 'develop' into blr

Conflicts:
	server/core/dcb.c
	server/modules/include/blr.h
	server/modules/routing/binlog/STATUS
	server/modules/routing/binlog/blr.c
	server/modules/routing/binlog/blr_file.c
	server/modules/routing/binlog/blr_master.c
	server/modules/routing/binlog/blr_slave.c
This commit is contained in:
Mark Riddoch
2014-06-08 00:50:36 +01:00
69 changed files with 5108 additions and 900 deletions

View File

@ -33,6 +33,7 @@
# 29/06/13 Vilho Raatikka Reverted Query classifier changes because
# gateway needs mysql client lib, not qc.
# 24/07/13 Mark Ridoch Addition of encryption routines
# 30/05/14 Mark Ridoch Filter API added
include ../../build_gateway.inc
@ -41,7 +42,7 @@ UTILSPATH := $(ROOT_PATH)/utils
CC=cc
CFLAGS=-c -I/usr/include -I../include -I../inih \
CFLAGS=-c -I/usr/include -I../include -I../modules/include -I../inih \
$(MYSQL_HEADERS) \
-I$(LOGPATH) -I$(UTILSPATH) \
-Wall -g
@ -56,14 +57,15 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \
SRCS= atomic.c buffer.c spinlock.c gateway.c \
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c \
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \
monitor.c adminusers.c secrets.c
monitor.c adminusers.c secrets.c filter.c modutil.c
HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
../include/gw.h ../include/mysql_protocol.h \
../include/gw.h ../modules/include/mysql_client_server_protocol.h \
../include/session.h ../include/spinlock.h ../include/thread.h \
../include/modules.h ../include/poll.h ../include/config.h \
../include/users.h ../include/hashtable.h ../include/gwbitmask.h \
../include/adminusers.h ../include/version.h ../include/maxscale.h
../include/adminusers.h ../include/version.h ../include/maxscale.h \
../include/filter.h modutil.h
OBJ=$(SRCS:.c=.o)

View File

@ -288,7 +288,11 @@ unsigned int
gwbuf_length(GWBUF *head)
{
int rval = 0;
CHK_GWBUF(head);
if (head)
{
CHK_GWBUF(head);
}
while (head)
{
rval += GWBUF_LENGTH(head);

View File

@ -31,6 +31,9 @@
* 11/03/14 Massimiliano Pinto Added Unix socket support
* 11/05/14 Massimiliano Pinto Added version_string support to service
* 19/05/14 Mark Riddoch Added unique names from section headers
* 29/05/14 Mark Riddoch Addition of filter definition
* 23/05/14 Massimiliano Pinto Added automatic set of maxscale-id: first listening ipv4_raw + port + pid
* 28/05/14 Massimiliano Pinto Added detect_replication_lag parameter
*
* @endverbatim
*/
@ -56,10 +59,11 @@ static char *config_get_value(CONFIG_PARAMETER *, const char *);
static int handle_global_item(const char *, const char *);
static void global_defaults();
static void check_config_objects(CONFIG_CONTEXT *context);
static int config_truth_value(char *str);
static char *config_file = NULL;
static GATEWAY_CONF gateway;
char *version_string = NULL;
char *version_string = NULL;
/**
* Config item handler for the ini file reader
@ -130,7 +134,6 @@ int rval;
if (ptr) {
*ptr = '\0';
}
}
mysql_close(conn);
}
@ -166,7 +169,6 @@ int rval;
if (!config_file)
return 0;
if (gateway.version_string)
free(gateway.version_string);
@ -219,6 +221,8 @@ int error_count = 0;
"router");
if (router)
{
char* max_slave_conn_str;
obj->element = service_alloc(obj->object, router);
char *user =
config_get_value(obj->parameters, "user");
@ -229,16 +233,9 @@ int error_count = 0;
char *version_string = config_get_value(obj->parameters, "version_string");
if (version_string) {
((SERVICE *)(obj->element))->version_string = strdup(version_string);
} else {
if (gateway.version_string)
((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string);
}
if (obj->element == NULL) /*< if module load failed */
{
LOGIF(LE, (skygw_log_write_flush(
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Reading configuration "
"for router service '%s' failed. "
@ -248,9 +245,20 @@ int error_count = 0;
obj = obj->next;
continue; /*< process next obj */
}
if (version_string) {
((SERVICE *)(obj->element))->version_string = strdup(version_string);
} else {
if (gateway.version_string)
((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string);
}
max_slave_conn_str =
config_get_value(obj->parameters,
"max_slave_connections");
if (enable_root_user)
serviceEnableRootUser(obj->element, atoi(enable_root_user));
serviceEnableRootUser(obj->element, config_truth_value(enable_root_user));
if (!auth)
auth = config_get_value(obj->parameters, "auth");
@ -268,6 +276,35 @@ int error_count = 0;
"corresponding password.",
obj->object)));
}
if (max_slave_conn_str != NULL)
{
CONFIG_PARAMETER* param;
bool succp;
param = config_get_param(obj->parameters,
"max_slave_connections");
succp = service_set_slave_conn_limit(
obj->element,
param,
max_slave_conn_str,
COUNT_ATMOST);
if (!succp)
{
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"* Warning : invalid value type "
"for parameter \'%s.%s = %s\'\n\tExpected "
"type is either <int> for slave connection "
"count or\n\t<int>%% for specifying the "
"maximum percentage of available the "
"slaves that will be connected.",
((SERVICE*)obj->element)->name,
param->name,
param->value)));
}
}
}
else
{
@ -326,6 +363,52 @@ int error_count = 0;
obj->object)));
}
}
else if (!strcmp(type, "filter"))
{
char *module = config_get_value(obj->parameters,
"module");
char *options = config_get_value(obj->parameters,
"options");
if (module)
{
obj->element = filter_alloc(obj->object, module);
}
else
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Filter '%s' has no module "
"defined defined to load.",
obj->object)));
error_count++;
}
if (obj->element && options)
{
char *s = strtok(options, ",");
while (s)
{
filterAddOption(obj->element, s);
s = strtok(NULL, ",");
}
}
if (obj->element)
{
CONFIG_PARAMETER *params = obj->parameters;
while (params)
{
if (strcmp(params->name, "module")
&& strcmp(params->name,
"options"))
{
filterAddParameter(obj->element,
params->name,
params->value);
}
params = params->next;
}
}
}
obj = obj->next;
}
@ -343,7 +426,8 @@ int error_count = 0;
{
char *servers;
char *roptions;
char *filters = config_get_value(obj->parameters,
"filters");
servers = config_get_value(obj->parameters, "servers");
roptions = config_get_value(obj->parameters,
"router_options");
@ -385,6 +469,10 @@ int error_count = 0;
s = strtok(NULL, ",");
}
}
if (filters)
{
serviceSetFilters(obj->element, filters);
}
}
else if (!strcmp(type, "listener"))
{
@ -393,12 +481,19 @@ int error_count = 0;
char *port;
char *protocol;
char *socket;
struct sockaddr_in serv_addr;
service = config_get_value(obj->parameters, "service");
port = config_get_value(obj->parameters, "port");
address = config_get_value(obj->parameters, "address");
protocol = config_get_value(obj->parameters, "protocol");
socket = config_get_value(obj->parameters, "socket");
/* if id is not set, do it now */
if (gateway.id == 0) {
setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address);
gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + port + getpid());
}
if (service && socket && protocol) {
CONFIG_CONTEXT *ptr = context;
@ -461,18 +556,46 @@ int error_count = 0;
char *servers;
char *user;
char *passwd;
unsigned long interval = 0;
int replication_heartbeat = 0;
module = config_get_value(obj->parameters, "module");
servers = config_get_value(obj->parameters, "servers");
user = config_get_value(obj->parameters, "user");
passwd = config_get_value(obj->parameters, "passwd");
if (config_get_value(obj->parameters, "monitor_interval")) {
interval = strtoul(config_get_value(obj->parameters, "monitor_interval"), NULL, 10);
}
if (config_get_value(obj->parameters, "detect_replication_lag")) {
replication_heartbeat = atoi(config_get_value(obj->parameters, "detect_replication_lag"));
}
if (module)
{
obj->element = monitor_alloc(obj->object, module);
if (servers && obj->element)
{
char *s = strtok(servers, ",");
char *s;
/* if id is not set, compute it now with pid only */
if (gateway.id == 0) {
gateway.id = getpid();
}
/* add the maxscale-id to monitor data */
monitorSetId(obj->element, gateway.id);
/* set monitor interval */
if (interval > 0)
monitorSetInterval(obj->element, interval);
/* set replication heartbeat */
if(replication_heartbeat == 1)
monitorSetReplicationHeartbeat(obj->element, replication_heartbeat);
/* get the servers to monitor */
s = strtok(servers, ",");
while (s)
{
CONFIG_CONTEXT *obj1 = context;
@ -517,7 +640,8 @@ int error_count = 0;
error_count++;
}
}
else if (strcmp(type, "server") != 0)
else if (strcmp(type, "server") != 0
&& strcmp(type, "filter") != 0)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -562,6 +686,89 @@ config_get_value(CONFIG_PARAMETER *params, const char *name)
return NULL;
}
CONFIG_PARAMETER* config_get_param(
CONFIG_PARAMETER* params,
const char* name)
{
while (params)
{
if (!strcmp(params->name, name))
return params;
params = params->next;
}
return NULL;
}
config_param_type_t config_get_paramtype(
CONFIG_PARAMETER* param)
{
return param->qfd_param_type;
}
int config_get_valint(
CONFIG_PARAMETER* param,
const char* name, /*< if NULL examine current param only */
config_param_type_t ptype)
{
int val = -1; /*< -1 indicates failure */
while (param)
{
if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN))
{
switch (ptype) {
case COUNT_TYPE:
val = param->qfd.valcount;
goto return_val;
case PERCENT_TYPE:
val = param->qfd.valpercent;
goto return_val;
case BOOL_TYPE:
val = param->qfd.valbool;
goto return_val;
default:
goto return_val;
}
}
else if (name == NULL)
{
goto return_val;
}
param = param->next;
}
return_val:
return val;
}
CONFIG_PARAMETER* config_clone_param(
CONFIG_PARAMETER* param)
{
CONFIG_PARAMETER* p2;
p2 = (CONFIG_PARAMETER*) malloc(sizeof(CONFIG_PARAMETER));
if (p2 == NULL)
{
goto return_p2;
}
memcpy(p2, param, sizeof(CONFIG_PARAMETER));
p2->name = strndup(param->name, MAX_PARAM_LEN);
p2->value = strndup(param->value, MAX_PARAM_LEN);
if (param->qfd_param_type == STRING_TYPE)
{
p2->qfd.valstr = strndup(param->qfd.valstr, MAX_PARAM_LEN);
}
return_p2:
return p2;
}
/**
* Free a config tree
*
@ -650,6 +857,7 @@ global_defaults()
gateway.version_string = strdup(version_string);
else
gateway.version_string = NULL;
gateway.id=0;
}
/**
@ -692,6 +900,7 @@ SERVER *server;
char *user;
char *auth;
char *enable_root_user;
char* max_slave_conn_str;
char *version_string;
enable_root_user = config_get_value(obj->parameters, "enable_root_user");
@ -704,8 +913,9 @@ SERVER *server;
version_string = config_get_value(obj->parameters, "version_string");
if (version_string) {
if (service->version_string)
if (service->version_string) {
free(service->version_string);
}
service->version_string = strdup(version_string);
}
@ -715,6 +925,42 @@ SERVER *server;
auth);
if (enable_root_user)
serviceEnableRootUser(service, atoi(enable_root_user));
max_slave_conn_str =
config_get_value(
obj->parameters,
"max_slave_connections");
if (max_slave_conn_str != NULL)
{
CONFIG_PARAMETER* param;
bool succp;
param = config_get_param(obj->parameters,
"max_slave_connections");
succp = service_set_slave_conn_limit(
service,
param,
max_slave_conn_str,
COUNT_ATMOST);
if (!succp)
{
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"* Warning : invalid value type "
"for parameter \'%s.%s = %s\'\n\tExpected "
"type is either <int> for slave connection "
"count or\n\t<int>%% for specifying the "
"maximum percentage of available the "
"slaves that will be connected.",
((SERVICE*)obj->element)->name,
param->name,
param->value)));
}
}
}
obj->element = service;
@ -725,7 +971,9 @@ SERVER *server;
char *auth;
char *enable_root_user;
enable_root_user = config_get_value(obj->parameters, "enable_root_user");
enable_root_user =
config_get_value(obj->parameters,
"enable_root_user");
user = config_get_value(obj->parameters,
"user");
@ -822,10 +1070,12 @@ SERVER *server;
{
char *servers;
char *roptions;
char *filters;
servers = config_get_value(obj->parameters, "servers");
roptions = config_get_value(obj->parameters,
"router_options");
filters = config_get_value(obj->parameters, "filters");
if (servers && obj->element)
{
char *s = strtok(servers, ",");
@ -859,6 +1109,8 @@ SERVER *server;
s = strtok(NULL, ",");
}
}
if (filters)
serviceSetFilters(obj->element, filters);
}
else if (!strcmp(type, "listener"))
{
@ -919,7 +1171,8 @@ SERVER *server;
}
}
else if (strcmp(type, "server") != 0 &&
strcmp(type, "monitor") != 0)
strcmp(type, "monitor") != 0 &&
strcmp(type, "filter") != 0)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -941,7 +1194,9 @@ static char *service_params[] =
"user",
"passwd",
"enable_root_user",
"max_slave_connections",
"version_string",
"filters",
NULL
};
@ -974,6 +1229,8 @@ static char *monitor_params[] =
"servers",
"user",
"passwd",
"monitor_interval",
"detect_replication_lag",
NULL
};
/**
@ -1031,3 +1288,68 @@ int i;
obj = obj->next;
}
}
/**
* Set qualified parameter value to CONFIG_PARAMETER struct.
*/
bool config_set_qualified_param(
CONFIG_PARAMETER* param,
void* val,
config_param_type_t type)
{
bool succp;
switch (type) {
case STRING_TYPE:
param->qfd.valstr = strndup((const char *)val, MAX_PARAM_LEN);
succp = true;
break;
case COUNT_TYPE:
param->qfd.valcount = *(int *)val;
succp = true;
break;
case PERCENT_TYPE:
param->qfd.valpercent = *(int *)val;
succp = true;
break;
case BOOL_TYPE:
param->qfd.valbool = *(bool *)val;
succp = true;
break;
default:
succp = false;
break;
}
if (succp)
{
param->qfd_param_type = type;
}
return succp;
}
/**
* Used for boolean settings where values may be 1, yes or true
* to enable a setting or -, no, false to disable a setting.
*
* @param str String to convert to a boolean
* @return Truth value
*/
static int
config_truth_value(char *str)
{
if (strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0)
{
return 1;
}
if (strcasecmp(str, "flase") == 0 || strcasecmp(str, "off") == 0)
{
return 0;
}
return atoi(str);
}

View File

@ -42,6 +42,7 @@
#include <skygw_utils.h>
#include <log_manager.h>
#include <secrets.h>
#include <mysql_client_server_protocol.h>
#define USERS_QUERY_NO_ROOT " AND user NOT IN ('root')"
#define LOAD_MYSQL_USERS_QUERY "SELECT user, host, password, concat(user,host,password) AS userdata FROM mysql.user WHERE user IS NOT NULL AND user <> ''"
@ -219,14 +220,14 @@ getUsers(SERVICE *service, struct users *users)
*/
server = service->databases;
dpwd = decryptPassword(service_passwd);
while (server != NULL && mysql_real_connect(con,
while (server != NULL && (mysql_real_connect(con,
server->name,
service_user,
dpwd,
NULL,
server->port,
NULL,
0) == NULL)
0) == NULL))
{
server = server->nextdb;
}

View File

@ -68,6 +68,7 @@
#include <atomic.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <hashtable.h>
extern int lm_enabled_logfiles_bitmask;
@ -311,11 +312,16 @@ DCB_CALLBACK *cb;
if (dcb->remote)
free(dcb->remote);
/* Consume dcb->delayq buffer */
/* Clear write and read buffers */
if (dcb->delayq) {
GWBUF *queue = dcb->delayq;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
}
if (dcb->dcb_readqueue)
{
GWBUF* queue = dcb->dcb_readqueue;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
}
spinlock_acquire(&dcb->cb_lock);
while ((cb = dcb->callbacks) != NULL)
@ -325,6 +331,11 @@ DCB_CALLBACK *cb;
}
spinlock_release(&dcb->cb_lock);
if (dcb->dcb_readqueue)
{
GWBUF* queue = dcb->dcb_readqueue;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
}
bitmask_free(&dcb->memdata.bitmask);
simple_mutex_done(&dcb->dcb_read_lock);
simple_mutex_done(&dcb->dcb_write_lock);
@ -707,11 +718,20 @@ int below_water;
below_water = (dcb->high_water && dcb->writeqlen < dcb->high_water) ? 1 : 0;
ss_dassert(queue != NULL);
/**
* SESSION_STATE_STOPPING means that one of the backends is closing
* the router session. Some backends may have not completed
* authentication yet and thus they have no information about router
* being closed. Session state is changed to SESSION_STATE_STOPPING
* before router's closeSession is called and that tells that DCB may
* still be writable.
*/
if (queue == NULL ||
(dcb->state != DCB_STATE_ALLOC &&
dcb->state != DCB_STATE_POLLING &&
dcb->state != DCB_STATE_LISTENING &&
dcb->state != DCB_STATE_NOPOLLING))
dcb->state != DCB_STATE_NOPOLLING &&
dcb->session->state != SESSION_STATE_STOPPING))
{
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
@ -722,6 +742,7 @@ int below_water;
dcb,
STRDCBSTATE(dcb->state),
dcb->fd)));
ss_dassert(false);
return 0;
}
@ -738,7 +759,10 @@ int below_water;
* the routine that drains the queue data, so we should
* not have a race condition on the event.
*/
qlen = gwbuf_length(queue);
if (queue)
qlen = gwbuf_length(queue);
else
qlen = 0;
atomic_add(&dcb->writeqlen, qlen);
dcb->writeq = gwbuf_append(dcb->writeq, queue);
dcb->stats.n_buffered++;
@ -852,7 +876,14 @@ int below_water;
* for suspended write.
*/
dcb->writeq = queue;
qlen = gwbuf_length(queue);
if (queue)
{
qlen = gwbuf_length(queue);
}
else
{
qlen = 0;
}
atomic_add(&dcb->writeqlen, qlen);
if (queue != NULL)
@ -1055,14 +1086,21 @@ printDCB(DCB *dcb)
printf("\tDCB state: %s\n", gw_dcb_state2string(dcb->state));
if (dcb->remote)
printf("\tConnected to: %s\n", dcb->remote);
printf("\tQueued write data: %d\n", gwbuf_length(dcb->writeq));
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);
}
/**
@ -1097,13 +1135,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);
@ -1116,6 +1158,32 @@ 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, " %-14s | %-26s | %-20s | %s\n",
"DCB", "State", "Service", "Remote");
dcb_printf(pdcb, "-----------------------------------------------------------------------------\n");
while (dcb)
{
dcb_printf(pdcb, " %-14p | %-26s | %-20s | %s\n",
dcb, gw_dcb_state2string(dcb->state),
(dcb->session->service ? dcb->session->service->name : ""),
(dcb->remote ? dcb->remote : ""));
dcb = dcb->next;
}
spinlock_release(&dcbspin);
}
/**
* Diagnostic to print a DCB to another DCB
*
@ -1129,16 +1197,24 @@ 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, "\tQueued write data: %d\n", gwbuf_length(dcb->writeq));
dcb_printf(pdcb, "\tDelayed write data: %d\n", gwbuf_length(dcb->delayq));
dcb_printf(pdcb, "\tOwning Session: %p\n", dcb->session);
if (dcb->writeq)
dcb_printf(pdcb, "\tQueued write data: %d\n", gwbuf_length(dcb->writeq));
if (dcb->delayq)
dcb_printf(pdcb, "\tDelayed write data: %d\n", gwbuf_length(dcb->delayq));
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);
}
/**
@ -1533,7 +1609,7 @@ int rval = 1;
/**
* Remove a callback from the callback list for the DCB
*
* Searches down the linked list to find he callback with a matching reason, function
* Searches down the linked list to find the callback with a matching reason, function
* and userdata.
*
* @param dcb The DCB to add the callback to

316
server/core/filter.c Normal file
View File

@ -0,0 +1,316 @@
/*
* This file is distributed as part of MaxScale from SkySQL. 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 filter.c - A representation of a filter within MaxScale.
*
* @verbatim
* Revision History
*
* Date Who Description
* 29/05/14 Mark Riddoch Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <filter.h>
#include <session.h>
#include <modules.h>
#include <spinlock.h>
#include <skygw_utils.h>
#include <log_manager.h>
extern int lm_enabled_logfiles_bitmask;
static SPINLOCK filter_spin = SPINLOCK_INIT;
static FILTER_DEF *allFilters = NULL;
/**
* Allocate a new filter within MaxScale
*
*
* @param name The filter name
* @param module The module to load
*
* @return The newly created filter or NULL if an error occured
*/
FILTER_DEF *
filter_alloc(char *name, char *module)
{
FILTER_DEF *filter;
if ((filter = (FILTER_DEF *)malloc(sizeof(FILTER_DEF))) == NULL)
return NULL;
filter->name = strdup(name);
filter->module = strdup(module);
filter->filter = NULL;
filter->options = NULL;
filter->obj = NULL;
filter->parameters = NULL;
spinlock_init(&filter->spin);
spinlock_acquire(&filter_spin);
filter->next = allFilters;
allFilters = filter;
spinlock_release(&filter_spin);
return filter;
}
/**
* Deallocate the specified filter
*
* @param server The service to deallocate
* @return Returns true if the server was freed
*/
void
filter_free(FILTER_DEF *filter)
{
FILTER_DEF *ptr;
/* First of all remove from the linked list */
spinlock_acquire(&filter_spin);
if (allFilters == filter)
{
allFilters = filter->next;
}
else
{
ptr = allFilters;
while (ptr && ptr->next != filter)
{
ptr = ptr->next;
}
if (ptr)
ptr->next = filter->next;
}
spinlock_release(&filter_spin);
/* Clean up session and free the memory */
free(filter->name);
free(filter->module);
free(filter);
}
/**
* Find an existing filter using the unique section name in
* configuration file
*
* @param name The filter name
* @return The server or NULL if not found
*/
FILTER_DEF *
filter_find(char *name)
{
FILTER_DEF *filter;
spinlock_acquire(&filter_spin);
filter = allFilters;
while (filter)
{
if (strcmp(filter->name, name) == 0)
break;
filter = filter->next;
}
spinlock_release(&filter_spin);
return filter;
}
/**
* Print all filters to a DCB
*
* Designed to be called within a debugger session in order
* to display all active filters within MaxScale
*/
void
dprintAllFilters(DCB *dcb)
{
FILTER_DEF *ptr;
int i;
spinlock_acquire(&filter_spin);
ptr = allFilters;
while (ptr)
{
dcb_printf(dcb, "Filter %p (%s)\n", ptr, ptr->name);
dcb_printf(dcb, "\tModule: %s\n", ptr->module);
if (ptr->options)
{
dcb_printf(dcb, "\tOptions: ");
for (i = 0; ptr->options && ptr->options[i]; i++)
dcb_printf(dcb, "%s ", ptr->options[i]);
dcb_printf(dcb, "\n");
}
if (ptr->obj && ptr->filter)
ptr->obj->diagnostics(ptr->filter, NULL, dcb);
else
dcb_printf(dcb, "\tModule not loaded.\n");
ptr = ptr->next;
}
spinlock_release(&filter_spin);
}
/**
* Print filter details to a DCB
*
* Designed to be called within a debug CLI in order
* to display all active filters in MaxScale
*/
void
dprintFilter(DCB *dcb, FILTER_DEF *filter)
{
int i;
dcb_printf(dcb, "Filter %p (%s)\n", filter, filter->name);
dcb_printf(dcb, "\tModule: %s\n", filter->module);
if (filter->options)
{
dcb_printf(dcb, "\tOptions: ");
for (i = 0; filter->options && filter->options[i]; i++)
dcb_printf(dcb, "%s ", filter->options[i]);
dcb_printf(dcb, "\n");
}
if (filter->obj && filter->filter)
filter->obj->diagnostics(filter->filter, NULL, dcb);
}
/**
* List all filters in a tabular form to a DCB
*
*/
void
dListFilters(DCB *dcb)
{
FILTER_DEF *ptr;
int i;
spinlock_acquire(&filter_spin);
ptr = allFilters;
if (ptr)
{
dcb_printf(dcb, "%-18s | %-15s | Options\n",
"Filter", "Module");
dcb_printf(dcb, "-------------------------------------------------------------------------------\n");
}
while (ptr)
{
dcb_printf(dcb, "%-18s | %-15s | ",
ptr->name, ptr->module);
for (i = 0; ptr->options && ptr->options[i]; i++)
dcb_printf(dcb, "%s ", ptr->options[i]);
dcb_printf(dcb, "\n");
ptr = ptr->next;
}
spinlock_release(&filter_spin);
}
/**
* Add a router option to a service
*
* @param service The service to add the router option to
* @param option The option string
*/
void
filterAddOption(FILTER_DEF *filter, char *option)
{
int i;
spinlock_acquire(&filter->spin);
if (filter->options == NULL)
{
filter->options = (char **)calloc(2, sizeof(char *));
filter->options[0] = strdup(option);
filter->options[1] = NULL;
}
else
{
for (i = 0; filter->options[i]; i++)
;
filter->options = (char **)realloc(filter->options,
(i + 2) * sizeof(char *));
filter->options[i] = strdup(option);
filter->options[i+1] = NULL;
}
spinlock_release(&filter->spin);
}
/**
* Add a router parameter to a service
*
* @param service The service to add the router option to
* @param name The parameter name
* @param value The parameter value
*/
void
filterAddParameter(FILTER_DEF *filter, char *name, char *value)
{
int i;
spinlock_acquire(&filter->spin);
if (filter->parameters == NULL)
{
filter->parameters = (FILTER_PARAMETER **)calloc(2, sizeof(FILTER_PARAMETER *));
i = 0;
}
else
{
for (i = 0; filter->parameters[i]; i++)
;
filter->parameters = (FILTER_PARAMETER **)realloc(filter->parameters,
(i + 2) * sizeof(FILTER_PARAMETER *));
}
filter->parameters[i] = (FILTER_PARAMETER *)calloc(1, sizeof(FILTER_PARAMETER));
filter->parameters[i]->name = strdup(name);
filter->parameters[i]->value = strdup(value);
filter->parameters[i+1] = NULL;
spinlock_release(&filter->spin);
}
DOWNSTREAM *
filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream)
{
DOWNSTREAM *me;
if (filter->obj == NULL)
{
/* Filter not yet loaded */
if ((filter->obj = load_module(filter->module,
MODULE_FILTER)) == NULL)
{
return NULL;
}
}
if (filter->filter == NULL)
filter->filter = (filter->obj->createInstance)(filter->options,
filter->parameters);
if ((me = (DOWNSTREAM *)calloc(1, sizeof(DOWNSTREAM))) == NULL)
{
return NULL;
}
me->instance = filter->filter;
me->routeQuery = filter->obj->routeQuery;
me->session = filter->obj->newSession(me->instance, session);
filter->obj->setDownstream(me->instance, me->session, downstream);
return me;
}

View File

@ -136,7 +136,7 @@ static void usage(void);
static char* get_expanded_pathname(
char** abs_path,
char* input_path,
char* fname);
const char* fname);
static void print_log_n_stderr(
bool do_log,
bool do_stderr,
@ -725,7 +725,7 @@ static bool file_is_writable(
static char* get_expanded_pathname(
char** output_path,
char* relative_path,
char* fname)
const char* fname)
{
char* cnf_file_buf = NULL;
char* expanded_path;
@ -1228,7 +1228,7 @@ int main(int argc, char **argv)
datadir)));
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE,
"Configuration file : %s",
"Configuration file : %s",
cnf_file_path)));
/*< Update the server options */

View File

@ -28,6 +28,7 @@
* 14/06/13 Mark Riddoch Updated to add call to ModuleInit if one is
* defined in the loaded module.
* Also updated to call fixed GetModuleObject
* 02/06/14 Mark Riddoch Addition of module info
*
* @endverbatim
*/
@ -38,6 +39,7 @@
#include <string.h>
#include <dlfcn.h>
#include <modules.h>
#include <modinfo.h>
#include <skygw_utils.h>
#include <log_manager.h>
@ -47,10 +49,11 @@ static MODULES *registered = NULL;
static MODULES *find_module(const char *module);
static void register_module(const char *module,
const char *type,
void *dlhandle,
char *version,
void *modobj);
const char *type,
void *dlhandle,
char *version,
void *modobj,
MODULE_INFO *info);
static void unregister_module(const char *module);
char* get_maxscale_home(void)
@ -76,12 +79,13 @@ char* get_maxscale_home(void)
void *
load_module(const char *module, const char *type)
{
char *home, *version;
char fname[MAXPATHLEN];
void *dlhandle, *sym;
char *(*ver)();
void *(*ep)(), *modobj;
MODULES *mod;
char *home, *version;
char fname[MAXPATHLEN];
void *dlhandle, *sym;
char *(*ver)();
void *(*ep)(), *modobj;
MODULES *mod;
MODULE_INFO *mod_info = NULL;
if ((mod = find_module(module)) == NULL)
{
@ -106,6 +110,7 @@ MODULES *mod;
return NULL;
}
}
if ((dlhandle = dlopen(fname, RTLD_NOW|RTLD_LOCAL)) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
@ -140,6 +145,57 @@ MODULES *mod;
ModuleInit();
}
if ((sym = dlsym(dlhandle, "info")) != NULL)
{
int fatal = 0;
mod_info = sym;
if (strcmp(type, MODULE_PROTOCOL) == 0
&& mod_info->modapi != MODULE_API_PROTOCOL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the protocol API.\n",
module)));
fatal = 1;
}
if (strcmp(type, MODULE_ROUTER) == 0
&& mod_info->modapi != MODULE_API_ROUTER)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the router API.\n",
module)));
fatal = 1;
}
if (strcmp(type, MODULE_MONITOR) == 0
&& mod_info->modapi != MODULE_API_MONITOR)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the monitor API.\n",
module)));
fatal = 1;
}
if (strcmp(type, MODULE_FILTER) == 0
&& mod_info->modapi != MODULE_API_FILTER)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Module '%s' does not implement "
"the filter API.\n",
module)));
fatal = 1;
}
if (fatal)
{
dlclose(dlhandle);
return NULL;
}
}
if ((sym = dlsym(dlhandle, "GetModuleObject")) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
@ -156,10 +212,11 @@ MODULES *mod;
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE,
"Loaded module %s: %s.",
"Loaded module %s: %s from %s",
module,
version)));
register_module(module, type, dlhandle, version, modobj);
version,
fname)));
register_module(module, type, dlhandle, version, modobj, mod_info);
}
else
{
@ -225,7 +282,7 @@ MODULES *mod = registered;
* @param modobj The module object
*/
static void
register_module(const char *module, const char *type, void *dlhandle, char *version, void *modobj)
register_module(const char *module, const char *type, void *dlhandle, char *version, void *modobj, MODULE_INFO *mod_info)
{
MODULES *mod;
@ -237,6 +294,7 @@ MODULES *mod;
mod->version = strdup(version);
mod->modobj = modobj;
mod->next = registered;
mod->info = mod_info;
registered = mod;
}
@ -301,11 +359,25 @@ dprintAllModules(DCB *dcb)
{
MODULES *ptr = registered;
dcb_printf(dcb, "%-15s | %-11s | Version\n", "Module Name", "Module Type");
dcb_printf(dcb, "-----------------------------------------------------\n");
dcb_printf(dcb, "%-15s | %-11s | Version | API | Status\n", "Module Name", "Module Type");
dcb_printf(dcb, "--------------------------------------------------------------------------\n");
while (ptr)
{
dcb_printf(dcb, "%-15s | %-11s | %s\n", ptr->module, ptr->type, ptr->version);
dcb_printf(dcb, "%-15s | %-11s | %-7s ", ptr->module, ptr->type, ptr->version);
if (ptr->info)
dcb_printf(dcb, "| %d.%d.%d | %s",
ptr->info->api_version.major,
ptr->info->api_version.minor,
ptr->info->api_version.patch,
ptr->info->status == MODULE_IN_DEVELOPMENT
? "In Development"
: (ptr->info->status == MODULE_ALPHA_RELEASE
? "Alpha"
: (ptr->info->status == MODULE_BETA_RELEASE
? "Beta"
: (ptr->info->status == MODULE_GA
? "GA" : "Unknown"))));
dcb_printf(dcb, "\n");
ptr = ptr->next;
}
}

123
server/core/modutil.c Normal file
View File

@ -0,0 +1,123 @@
/*
* This file is distributed as part of MaxScale from SkySQL. 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 modutil.c - Implementation of useful routines for modules
*
* @verbatim
* Revision History
*
* Date Who Description
* 04/06/14 Mark Riddoch Initial implementation
*
* @endverbatim
*/
#include <buffer.h>
#include <string.h>
/**
* Check if a GWBUF structure is a MySQL COM_QUERY packet
*
* @param buf Buffer to check
* @return True if GWBUF is a COM_QUERY packet
*/
int
modutil_is_SQL(GWBUF *buf)
{
unsigned char *ptr;
if (GWBUF_LENGTH(buf) < 5)
return 0;
ptr = GWBUF_DATA(buf);
return ptr[4] == 0x03; // COM_QUERY
}
/**
* Extract the SQL portion of a COM_QUERY packet
*
* NB This sets *sql to point into the packet and does not
* allocate any new storage. The string pointed to by *sql is
* not NULL terminated.
*
* This routine is very simplistic and does not deal with SQL text
* that spans multiple buffers.
*
* @param buf The packet buffer
* @param sql Pointer that is set to point at the SQL data
* @param length Length of the SQL data
* @return True if the packet is a COM_QUERY packet
*/
int
modutil_extract_SQL(GWBUF *buf, char **sql, int *length)
{
char *ptr;
if (!modutil_is_SQL(buf))
return 0;
ptr = GWBUF_DATA(buf);
*length = *ptr++;
*length += (*ptr++ << 8);
*length += (*ptr++ << 8);
ptr += 2; // Skip sequence id and COM_QUERY byte
*length = *length - 1;
*sql = ptr;
return 1;
}
GWBUF *
modutil_replace_SQL(GWBUF *orig, char *sql)
{
char *ptr;
int length, newlength;
GWBUF *addition;
if (!modutil_is_SQL(orig))
return NULL;
ptr = GWBUF_DATA(orig);
length = *ptr++;
length += (*ptr++ << 8);
length += (*ptr++ << 8);
ptr += 2; // Skip sequence id and COM_QUERY byte
newlength = strlen(sql);
if (length - 1 == newlength)
{
/* New SQL is the same length as old */
memcpy(ptr, sql, newlength);
}
else if (length - 1 > newlength)
{
/* New SQL is shorter */
memcpy(ptr, sql, newlength);
GWBUF_RTRIM(orig, (length - 1) - newlength);
}
else
{
memcpy(ptr, sql, length - 1);
addition = gwbuf_alloc(newlength - (length - 1));
memcpy(GWBUF_DATA(addition), &sql[length - 1], newlength - (length - 1));
ptr = GWBUF_DATA(orig);
*ptr++ = (newlength + 1) & 0xff;
*ptr++ = ((newlength + 1) >> 8) & 0xff;
*ptr++ = ((newlength + 1) >> 16) & 0xff;
orig->next = addition;
}
return orig;
}

View File

@ -22,8 +22,10 @@
* @verbatim
* Revision History
*
* Date Who Description
* 08/07/13 Mark Riddoch Initial implementation
* Date Who Description
* 08/07/13 Mark Riddoch Initial implementation
* 23/05/14 Massimiliano Pinto Addition of monitor_interval parameter
* and monitor id
*
* @endverbatim
*/
@ -220,3 +222,47 @@ MONITOR *ptr;
spinlock_release(&monLock);
return ptr;
}
/**
* Set the id of the monitor.
*
* @param mon The monitor instance
* @param id The id for the monitor
*/
void
monitorSetId(MONITOR *mon, unsigned long id)
{
if (mon->module->defaultId != NULL) {
mon->module->defaultId(mon->handle, id);
}
}
/**
* Set the monitor sampling interval.
*
* @param mon The monitor instance
* @param interval The sampling interval in milliseconds
*/
void
monitorSetInterval (MONITOR *mon, unsigned long interval)
{
if (mon->module->setInterval != NULL) {
mon->module->setInterval(mon->handle, interval);
}
}
/**
* Enable Replication Heartbeat support in monitor.
*
* @param mon The monitor instance
* @param interval The sampling interval in milliseconds
*/
void
monitorSetReplicationHeartbeat(MONITOR *mon, int replication_heartbeat)
{
if (mon->module->replicationHeartbeat != NULL) {
mon->module->replicationHeartbeat(mon->handle, replication_heartbeat);
}
}

View File

@ -27,6 +27,7 @@
#include <gwbitmask.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <gw.h>
extern int lm_enabled_logfiles_bitmask;

View File

@ -21,6 +21,7 @@
#include <skygw_utils.h>
#include <log_manager.h>
#include <ctype.h>
#include <mysql_client_server_protocol.h>
extern int lm_enabled_logfiles_bitmask;
/**

View File

@ -22,8 +22,12 @@
* @verbatim
* Revision History
*
* Date Who Description
* 18/06/13 Mark Riddoch Initial implementation
* Date Who Description
* 18/06/13 Mark Riddoch Initial implementation
* 17/05/14 Mark Riddoch Addition of unique_name
* 20/05/14 Massimiliano Pinto Addition of server_string
* 21/05/14 Massimiliano Pinto Addition of node_id
* 28/05/14 Massimiliano Pinto Addition of rlagd and node_ts fields
*
* @endverbatim
*/
@ -68,6 +72,10 @@ SERVER *server;
server->monuser = NULL;
server->monpw = NULL;
server->unique_name = NULL;
server->server_string = NULL;
server->node_id = -1;
server->rlag = -1;
server->node_ts = 0;
spinlock_acquire(&server_spin);
server->next = allServers;
@ -112,6 +120,8 @@ SERVER *ptr;
free(server->protocol);
if (server->unique_name)
free(server->unique_name);
if (server->server_string)
free(server->server_string);
free(server);
return 1;
}
@ -237,8 +247,19 @@ char *stat;
free(stat);
dcb_printf(dcb, "\tProtocol: %s\n", ptr->protocol);
dcb_printf(dcb, "\tPort: %d\n", ptr->port);
if (ptr->server_string)
dcb_printf(dcb, "\tServer Version:\t\t%s\n", ptr->server_string);
dcb_printf(dcb, "\tNode Id: %d\n", ptr->node_id);
if (SERVER_IS_SLAVE(ptr)) {
if (ptr->rlag >= 0) {
dcb_printf(dcb, "\tSlave delay:\t\t%d\n", ptr->rlag);
}
}
if (ptr->node_ts > 0) {
dcb_printf(dcb, "\tLast Repl Heartbeat:\t%lu\n", ptr->node_ts);
}
dcb_printf(dcb, "\tNumber of connections: %d\n", ptr->stats.n_connections);
dcb_printf(dcb, "\tCurrent no. of connections: %d\n", ptr->stats.n_current);
dcb_printf(dcb, "\tCurrent no. of conns: %d\n", ptr->stats.n_current);
ptr = ptr->next;
}
spinlock_release(&server_spin);
@ -262,8 +283,50 @@ char *stat;
free(stat);
dcb_printf(dcb, "\tProtocol: %s\n", server->protocol);
dcb_printf(dcb, "\tPort: %d\n", server->port);
if (server->server_string)
dcb_printf(dcb, "\tServer Version:\t\t%s\n", server->server_string);
dcb_printf(dcb, "\tNode Id: %d\n", server->node_id);
if (SERVER_IS_SLAVE(server)) {
if (server->rlag >= 0) {
dcb_printf(dcb, "\tSlave delay:\t\t%d\n", server->rlag);
}
}
if (server->node_ts > 0) {
dcb_printf(dcb, "\tLast Repl Heartbeat:\t%lu\n", server->node_ts);
}
dcb_printf(dcb, "\tNumber of connections: %d\n", server->stats.n_connections);
dcb_printf(dcb, "\tCurrent No. of connections: %d\n", server->stats.n_current);
dcb_printf(dcb, "\tCurrent no. of conns: %d\n", server->stats.n_current);
}
/**
* List all servers in a tabular form to a DCB
*
*/
void
dListServers(DCB *dcb)
{
SERVER *ptr;
char *stat;
spinlock_acquire(&server_spin);
ptr = allServers;
if (ptr)
{
dcb_printf(dcb, "%-18s | %-15s | Port | %-18s | Connections\n",
"Server", "Address", "Status");
dcb_printf(dcb, "-------------------------------------------------------------------------------\n");
}
while (ptr)
{
stat = server_status(ptr);
dcb_printf(dcb, "%-18s | %-15s | %5d | %-18s | %4d\n",
ptr->unique_name, ptr->name,
ptr->port, stat,
ptr->stats.n_current);
free(stat);
ptr = ptr->next;
}
spinlock_release(&server_spin);
}
/**
@ -281,6 +344,8 @@ char *status = NULL;
if ((status = (char *)malloc(200)) == NULL)
return NULL;
status[0] = 0;
if (server->status & SERVER_MAINT)
strcat(status, "Maintenance, ");
if (server->status & SERVER_MASTER)
strcat(status, "Master, ");
if (server->status & SERVER_SLAVE)

View File

@ -30,6 +30,7 @@
* 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services)
* 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL
* 23/05/14 Mark Riddoch Addition of service validation call
* 29/05/14 Mark Riddoch Filter API implementation
*
* @endverbatim
*/
@ -46,6 +47,7 @@
#include <modules.h>
#include <dcb.h>
#include <users.h>
#include <filter.h>
#include <dbusers.h>
#include <poll.h>
#include <skygw_utils.h>
@ -56,6 +58,11 @@ extern int lm_enabled_logfiles_bitmask;
static SPINLOCK service_spin = SPINLOCK_INIT;
static SERVICE *allServices = NULL;
static void service_add_qualified_param(
SERVICE* svc,
CONFIG_PARAMETER* param);
/**
* Allocate a new service for the gateway to support
*
@ -103,6 +110,10 @@ SERVICE *service;
service->enable_root = 0;
service->routerOptions = NULL;
service->databases = NULL;
service->svc_config_param = NULL;
service->svc_config_version = 0;
service->filters = NULL;
service->n_filters = 0;
spinlock_init(&service->spin);
spinlock_init(&service->users_table_spin);
memset(&service->rate_limit, 0, sizeof(SERVICE_REFRESH_RATE));
@ -531,10 +542,13 @@ serviceClearRouterOptions(SERVICE *service)
int i;
spinlock_acquire(&service->spin);
for (i = 0; service->routerOptions[i]; i++)
free(service->routerOptions[i]);
free(service->routerOptions);
service->routerOptions = NULL;
if (service->routerOptions != NULL)
{
for (i = 0; service->routerOptions[i]; i++)
free(service->routerOptions[i]);
free(service->routerOptions);
service->routerOptions = NULL;
}
spinlock_release(&service->spin);
}
/**
@ -601,6 +615,63 @@ serviceEnableRootUser(SERVICE *service, int action)
return 1;
}
/**
* Trim whitespace from the from an rear of a string
*
* @param str String to trim
* @return Trimmed string, chanesg are done in situ
*/
static char *
trim(char *str)
{
char *ptr;
while (isspace(*str))
str++;
/* Point to last character of the string */
ptr = str + strlen(str) - 1;
while (ptr > str && isspace(*ptr))
*ptr-- = 0;
return str;
}
/**
* Set the filters used by the service
*
* @param service The service itself
* @param filters ASCII string of filters to use
*/
void
serviceSetFilters(SERVICE *service, char *filters)
{
FILTER_DEF **flist;
char *ptr, *brkt;
int n = 0;
flist = (FILTER_DEF *)malloc(sizeof(FILTER_DEF *));
ptr = strtok_r(filters, "|", &brkt);
while (ptr)
{
n++;
flist = (FILTER_DEF *)realloc(flist, (n + 1) * sizeof(FILTER_DEF *));
if ((flist[n-1] = filter_find(trim(ptr))) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Unable to find filter '%s' for service '%s'\n",
trim(ptr), service->name
)));
}
flist[n] = NULL;
ptr = strtok_r(NULL, "|", &brkt);
}
service->filters = flist;
service->n_filters = n;
}
/**
* Return a named service
*
@ -631,6 +702,7 @@ void
printService(SERVICE *service)
{
SERVER *ptr = service->databases;
int i;
printf("Service %p\n", service);
printf("\tService: %s\n", service->name);
@ -642,6 +714,16 @@ SERVER *ptr = service->databases;
printf("\t\t%s:%d Protocol: %s\n", ptr->name, ptr->port, ptr->protocol);
ptr = ptr->nextdb;
}
if (service->n_filters)
{
printf("\tFilter chain: ");
for (i = 0; i < service->n_filters; i++)
{
printf("%s %s ", service->filters[i]->name,
i + 1 < service->n_filters ? "|" : "");
}
printf("\n");
}
printf("\tUsers data: %p\n", service->users);
printf("\tTotal connections: %d\n", service->stats.n_sessions);
printf("\tCurrently connected: %d\n", service->stats.n_current);
@ -695,9 +777,10 @@ SERVICE *ptr;
* @param dcb DCB to print data to
* @param service The service to print
*/
dprintService(DCB *dcb, SERVICE *service)
void dprintService(DCB *dcb, SERVICE *service)
{
SERVER *server = service->databases;
int i;
dcb_printf(dcb, "Service %p\n", service);
dcb_printf(dcb, "\tService: %s\n", service->name);
@ -707,6 +790,16 @@ SERVER *server = service->databases;
service->router->diagnostics(service->router_instance, dcb);
dcb_printf(dcb, "\tStarted: %s",
asctime(localtime(&service->stats.started)));
if (service->n_filters)
{
dcb_printf(dcb, "\tFilter chain: ");
for (i = 0; i < service->n_filters; i++)
{
dcb_printf(dcb, "%s %s ", service->filters[i]->name,
i + 1 < service->n_filters ? "|" : "");
}
dcb_printf(dcb, "\n");
}
dcb_printf(dcb, "\tBackend databases\n");
while (server)
{
@ -719,6 +812,72 @@ SERVER *server = service->databases;
dcb_printf(dcb, "\tCurrently connected: %d\n", service->stats.n_current);
}
/**
* List the defined services in a tabular format.
*
* @param dcb DCB to print the service list to.
*/
void
dListServices(DCB *dcb)
{
SERVICE *ptr;
spinlock_acquire(&service_spin);
ptr = allServices;
if (ptr)
{
dcb_printf(dcb, "%-25s | %-20s | #Users | Total Sessions\n",
"Service Name", "Router Module");
dcb_printf(dcb, "--------------------------------------------------------------------------\n");
}
while (ptr)
{
dcb_printf(dcb, "%-25s | %-20s | %6d | %5d\n",
ptr->name, ptr->routerModule,
ptr->stats.n_current, ptr->stats.n_sessions);
ptr = ptr->next;
}
spinlock_release(&service_spin);
}
/**
* List the defined listeners in a tabular format.
*
* @param dcb DCB to print the service list to.
*/
void
dListListeners(DCB *dcb)
{
SERVICE *ptr;
SERV_PROTOCOL *lptr;
spinlock_acquire(&service_spin);
ptr = allServices;
if (ptr)
{
dcb_printf(dcb, "%-20s | %-18s | %-15s | Port | State\n",
"Service Name", "Protocol Module", "Address");
dcb_printf(dcb, "---------------------------------------------------------------------------\n");
}
while (ptr)
{
lptr = ptr->ports;
while (lptr)
{
dcb_printf(dcb, "%-20s | %-18s | %-15s | %5d | %s\n",
ptr->name, lptr->protocol,
(lptr != NULL) ? lptr->address : "*",
lptr->port,
(lptr->listener->session->state == SESSION_STATE_LISTENER_STOPPED) ? "Stopped" : "Running"
);
lptr = lptr->next;
}
ptr = ptr->next;
}
spinlock_release(&service_spin);
}
/**
* Update the definition of a service
*
@ -810,3 +969,111 @@ int service_refresh_users(SERVICE *service) {
else
return 1;
}
bool service_set_slave_conn_limit (
SERVICE* service,
CONFIG_PARAMETER* param,
char* valstr,
count_spec_t count_spec)
{
char* p;
int valint;
bool percent = false;
bool succp;
/**
* Find out whether the value is numeric and ends with '%' or '\0'
*/
p = valstr;
while(isdigit(*p)) p++;
errno = 0;
if (p == valstr || (*p != '%' && *p != '\0'))
{
succp = false;
}
else if (*p == '%')
{
if (*(p+1) == '\0')
{
*p = '\0';
valint = (int) strtol(valstr, (char **)NULL, 10);
if (valint == 0 && errno != 0)
{
succp = false;
}
else
{
succp = true;
config_set_qualified_param(param, (void *)&valint, PERCENT_TYPE);
}
}
else
{
succp = false;
}
}
else if (*p == '\0')
{
valint = (int) strtol(valstr, (char **)NULL, 10);
if (valint == 0 && errno != 0)
{
succp = false;
}
else
{
succp = true;
config_set_qualified_param(param, (void *)&valint, COUNT_TYPE);
}
}
if (succp)
{
service_add_qualified_param(service, param); /*< add param to svc */
}
return succp;
}
/**
* Add qualified config parameter to SERVICE struct.
*/
static void service_add_qualified_param(
SERVICE* svc,
CONFIG_PARAMETER* param)
{
CONFIG_PARAMETER** p;
spinlock_acquire(&svc->spin);
p = &svc->svc_config_param;
if ((*p) != NULL)
{
do
{
/** If duplicate is found, latter remains */
if (strncasecmp(param->name,
(*p)->name,
strlen(param->name)) == 0)
{
*p = config_clone_param(param);
break;
}
}
while ((*p)->next != NULL);
(*p)->next = config_clone_param(param);
}
else
{
(*p) = config_clone_param(param);
}
/** Increment service's configuration version */
atomic_add(&svc->svc_config_version, 1);
(*p)->next = NULL;
spinlock_release(&svc->spin);
}

View File

@ -25,6 +25,7 @@
* Date Who Description
* 17/06/13 Mark Riddoch Initial implementation
* 02/09/13 Massimiliano Pinto Added session refcounter
* 29/05/14 Mark Riddoch Addition of filter mechanism
*
* @endverbatim
*/
@ -47,6 +48,9 @@ extern int lm_enabled_logfiles_bitmask;
static SPINLOCK session_spin = SPINLOCK_INIT;
static SESSION *allSessions = NULL;
static int session_setup_filters(SESSION *session);
/**
* Allocate a new session for a new client of the specified service.
*
@ -90,6 +94,7 @@ session_alloc(SERVICE *service, DCB *client_dcb)
spinlock_acquire(&session->ses_lock);
session->service = service;
session->client = client_dcb;
session->n_filters = 0;
memset(&session->stats, 0, sizeof(SESSION_STATS));
session->stats.connect = time(0);
session->state = SESSION_STATE_ALLOC;
@ -129,6 +134,10 @@ session_alloc(SERVICE *service, DCB *client_dcb)
session);
if (session->router_session == NULL) {
/**
* Inform other threads that session is closing.
*/
session->state = SESSION_STATE_STOPPING;
/*<
* Decrease refcount, set dcb's session pointer NULL
* and set session pointer to NULL.
@ -138,12 +147,50 @@ session_alloc(SERVICE *service, DCB *client_dcb)
session = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed to create router "
"client session. Freeing allocated resources.")));
"Error : Failed to create %s session.",
service->name)));
goto return_session;
}
/*
* Pending filter chain being setup set the head of the chain to
* be the router. As filters are inserted the current head will
* be pushed to the filter and the head updated.
*
* NB This dictates that filters are created starting at the end
* of the chain nearest the router working back to the client
* protocol end of the chain.
*/
session->head.instance = service->router_instance;
session->head.session = session->router_session;
session->head.routeQuery = service->router->routeQuery;
if (service->n_filters > 0)
{
if (!session_setup_filters(session))
{
/**
* Inform other threads that session is closing.
*/
session->state = SESSION_STATE_STOPPING;
/*<
* Decrease refcount, set dcb's session pointer NULL
* and set session pointer to NULL.
*/
session_free(session);
client_dcb->session = NULL;
session = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed to create %s session.",
service->name)));
goto return_session;
}
}
}
spinlock_acquire(&session_spin);
session->state = SESSION_STATE_ROUTER_READY;
session->next = allSessions;
@ -223,6 +270,7 @@ bool session_free(
bool succp = false;
SESSION *ptr;
int nlink;
int i;
CHK_SESSION(session);
@ -258,10 +306,23 @@ bool session_free(
/* Free router_session and session */
if (session->router_session) {
session->service->router->closeSession(
session->service->router_instance,
session->router_session);
session->service->router->freeSession(
session->service->router_instance,
session->router_session);
}
if (session->n_filters)
{
for (i = 0; i < session->n_filters; i++)
{
session->filters[i].filter->obj->freeSession(
session->filters[i].instance,
session->filters[i].session);
}
free(session->filters);
}
free(session);
succp = true;
@ -394,6 +455,7 @@ int norouter = 0;
if (norouter)
printf("%d Sessions have no router session\n", norouter);
}
/**
* Print all sessions to a DCB
*
@ -435,6 +497,8 @@ SESSION *ptr;
void
dprintSession(DCB *dcb, SESSION *ptr)
{
int i;
dcb_printf(dcb, "Session %p\n", ptr);
dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state));
dcb_printf(dcb, "\tService: %s (%p)\n", ptr->service->name, ptr->service);
@ -442,6 +506,49 @@ dprintSession(DCB *dcb, SESSION *ptr)
if (ptr->client && ptr->client->remote)
dcb_printf(dcb, "\tClient Address: %s\n", ptr->client->remote);
dcb_printf(dcb, "\tConnected: %s", asctime(localtime(&ptr->stats.connect)));
if (ptr->n_filters)
{
for (i = 0; i < ptr->n_filters; i++)
{
dcb_printf(dcb, "\tFilter: %s\n",
ptr->filters[i].filter->name);
ptr->filters[i].filter->obj->diagnostics(
ptr->filters[i].instance,
ptr->filters[i].session,
dcb);
}
}
}
/**
* List all sessions in tabular form to a DCB
*
* Designed to be called within a debugger session in order
* to display all active sessions within the gateway
*
* @param dcb The DCB to print to
*/
void
dListSessions(DCB *dcb)
{
SESSION *ptr;
spinlock_acquire(&session_spin);
ptr = allSessions;
if (ptr)
{
dcb_printf(dcb, "Session | Client | State\n");
dcb_printf(dcb, "------------------------------------------\n");
}
while (ptr)
{
dcb_printf(dcb, "%-16p | %-15s | %s\n", ptr,
((ptr->client && ptr->client->remote)
? ptr->client->remote : ""),
session_state(ptr->state));
ptr = ptr->next;
}
spinlock_release(&session_spin);
}
/**
@ -459,6 +566,8 @@ session_state(int state)
return "Session Allocated";
case SESSION_STATE_READY:
return "Session Ready";
case SESSION_STATE_ROUTER_READY:
return "Session ready for routing";
case SESSION_STATE_LISTENER:
return "Listener Session";
case SESSION_STATE_LISTENER_STOPPED:
@ -467,3 +576,69 @@ session_state(int state)
return "Invalid State";
}
}
SESSION* get_session_by_router_ses(
void* rses)
{
SESSION* ses = allSessions;
while (ses->router_session != rses && ses->next != NULL)
ses = ses->next;
if (ses->router_session != rses)
{
ses = NULL;
}
return ses;
}
/**
* Create the filter chain for this session.
*
* Filters must be setup in reverse order, starting with the last
* filter in the chain and working back towards the client connection
* Each filter is passed the current session head of the filter chain
* this head becomes the destination for the filter. The newly created
* filter becomes the new head of the filter chain.
*
* @param session The session that requires the chain
* @return 0 if filter creation fails
*/
static int
session_setup_filters(SESSION *session)
{
SERVICE *service = session->service;
DOWNSTREAM *head;
int i;
if ((session->filters = calloc(service->n_filters,
sizeof(SESSION_FILTER))) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Insufficient memory to allocate session filter "
"tracking.\n")));
return 0;
}
session->n_filters = service->n_filters;
for (i = service->n_filters - 1; i >= 0; i--)
{
if ((head = filterApply(service->filters[i], session,
&session->head)) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Failed to create filter '%s' for service '%s'.\n",
service->filters[i]->name,
service->name)));
return 0;
}
session->filters[i].filter = service->filters[i];
session->filters[i].session = head->session;
session->filters[i].instance = head->instance;
session->head = *head;
}
return 1;
}