develop branch merged

develop branch merged
This commit is contained in:
MassimilianoPinto 2014-10-10 15:30:56 +02:00
commit 18ae3e35ef
63 changed files with 6064 additions and 329 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.6)
message(STATUS "CMake version: ${CMAKE_VERSION}")
include(macros.cmake)
enable_testing()
@ -24,9 +24,13 @@ configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_SOURCE_
set(CMAKE_C_FLAGS "-Wall -fPIC")
set(CMAKE_CXX_FLAGS "-Wall -fPIC")
if(BUILD_TYPE MATCHES Debug)
if(BUILD_TYPE STREQUAL Debug)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -pthread -pipe -DSS_DEBUG -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -pthread -pipe -DSS_DEBUG -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4")
message(STATUS "Generating debugging symbols and enabling debugging code")
elseif(BUILD_TYPE STREQUAL DebugSymbols)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -pthread -pipe -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -pthread -pipe -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4")
message(STATUS "Generating debugging symbols")
elseif(BUILD_TYPE MATCHES Optimized)
if(NOT (DEFINED OLEVEL))

5
README
View File

@ -205,7 +205,10 @@ or define the variables manually at configuration time.
All the variables that control the CMake build process:
INSTALL_DIR=<path> Installation directory
BUILD_TYPE=[None|Debug|Release] Type of the build, defaults to Release (optimized)
BUILD_TYPE=<type> Type of the build. One of None, Debug, DebugSymbols, Optimized. (default None)
DebugSymbols enables debugging symbols, Debug enables debugging symbols and code, Optimized builds an optimized version.
INSTALL_SYSTEM_FILES=[Y|N] Install startup scripts and ld configuration files
EMBEDDED_LIB=<path> Path to the embedded library location (libmysqld.a for static and libmysqld.so for dynamic)
MYSQL_DIR=<path> Path to MySQL headers

View File

@ -2073,7 +2073,7 @@ static bool logfile_init(
char* c;
pid_t pid = getpid();
int len = strlen(shm_pathname_prefix)+
get_decimal_len((size_t)pid);
get_decimal_len((size_t)pid) + 1;
c = (char *)calloc(len, sizeof(char));

View File

@ -82,24 +82,29 @@ macro(check_dirs)
set(DEPS_OK TRUE CACHE BOOL "If all the dependencies were found.")
# Find the MySQL headers if they were not defined
if(DEFINED MYSQL_DIR)
if(DEBUG_OUTPUT)
message(STATUS "Searching for MySQL headers at: ${MYSQL_DIR}")
endif()
find_path(MYSQL_DIR_LOC mysql.h PATHS ${MYSQL_DIR} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH)
else()
find_path(MYSQL_DIR_LOC mysql.h PATH_SUFFIXES mysql mariadb)
endif()
find_path(MYSQL_DIR_LOC mysql.h PATH_SUFFIXES mysql mariadb)
if(DEBUG_OUTPUT)
message(STATUS "Search returned: ${MYSQL_DIR_LOC}")
endif()
if(${MYSQL_DIR_LOC} STREQUAL "MYSQL_DIR_LOC-NOTFOUND")
if(${MYSQL_DIR_LOC} MATCHES "NOTFOUND")
set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.")
message(FATAL_ERROR "Fatal Error: MySQL headers were not found.")
else()
message(STATUS "Using MySQL headers found at: ${MYSQL_DIR}")
set(MYSQL_DIR ${MYSQL_DIR_LOC} CACHE PATH "Path to MySQL headers" FORCE)
message(STATUS "Using MySQL headers found at: ${MYSQL_DIR}")
endif()
set(MYSQL_DIR_LOC "" INTERNAL)
unset(MYSQL_DIR_LOC)
# Find the errmsg.sys file if it was not defied
if( DEFINED ERRMSG )
@ -113,7 +118,7 @@ macro(check_dirs)
message(STATUS "Using errmsg.sys found at: ${ERRMSG_FILE}")
endif()
set(ERRMSG ${ERRMSG_FILE} CACHE FILEPATH "Path to the errmsg.sys file." FORCE)
set(ERRMSG_FILE "" INTERNAL)
unset(ERRMSG_FILE)
# Find the embedded mysql library
if(STATIC_EMBEDDED)
@ -133,7 +138,7 @@ macro(check_dirs)
endif()
set(EMBEDDED_LIB ${EMBEDDED_LIB_STATIC} CACHE FILEPATH "Path to libmysqld" FORCE)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES})
set(OLD_SUFFIXES "" INTERNAL)
unset(OLD_SUFFIXES)
else()
if (DEFINED EMBEDDED_LIB)
@ -150,8 +155,8 @@ macro(check_dirs)
set(EMBEDDED_LIB ${EMBEDDED_LIB_DYNAMIC} CACHE FILEPATH "Path to libmysqld" FORCE)
endif()
set(EMBEDDED_LIB_DYNAMIC "" INTERNAL)
set(EMBEDDED_LIB_STATIC "" INTERNAL)
unset(EMBEDDED_LIB_DYNAMIC)
unset(EMBEDDED_LIB_STATIC)
# Inform the user about the embedded library
if( (${EMBEDDED_LIB} STREQUAL "EMBEDDED_LIB_STATIC-NOTFOUND") OR (${EMBEDDED_LIB} STREQUAL "EMBEDDED_LIB_DYNAMIC-NOTFOUND"))
@ -176,17 +181,18 @@ macro(check_dirs)
else()
set(DEB_BASED FALSE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.")
endif()
set(DEB_FNC "" INTERNAL)
set(RPM_FNC "" INTERNAL)
unset(DEB_FNC)
unset(RPM_FNC)
#Check RabbitMQ headers and libraries
if(BUILD_RABBITMQ)
if(DEFINED RABBITMQ_LIB)
find_library(RMQ_LIB rabbitmq PATHS ${RABBITMQ_LIB} NO_DEFAULT_PATH)
else()
find_library(RMQ_LIB rabbitmq)
endif()
find_library(RMQ_LIB rabbitmq)
if(RMQ_LIB STREQUAL "RMQ_LIB-NOTFOUND")
if(RMQ_LIB MATCHES "NOTFOUND")
set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.")
message(FATAL_ERROR "Cannot find RabbitMQ libraries, please define the path to the libraries with -DRABBITMQ_LIB=<path>")
else()
@ -196,9 +202,10 @@ macro(check_dirs)
if(DEFINED RABBITMQ_HEADERS)
find_file(RMQ_HEADERS amqp.h PATHS ${RABBITMQ_HEADERS} NO_DEFAULT_PATH)
else()
find_file(RMQ_HEADERS amqp.h)
endif()
find_file(RMQ_HEADERS amqp.h)
if(RMQ_HEADERS STREQUAL "RMQ_HEADERS-NOTFOUND")
if(RMQ_HEADERS MATCHES "NOTFOUND")
set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.")
message(FATAL_ERROR "Cannot find RabbitMQ headers, please define the path to the headers with -DRABBITMQ_HEADERS=<path>")
else()

View File

@ -25,7 +25,7 @@ endif
# -O2 -g -pipe -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector --param=ssp-buffer-size=4 -fPIC
CFLAGS := $(CFLAGS) -Wall
LDLIBS := $(LDLIBS) -pthread
LDLIBS := $(LDLIBS) -pthread -lm
LDMYSQL := -lmysqld
CPP_LDLIBS := -lstdc++

View File

@ -18,6 +18,7 @@ Copyright MariaDB Corporation Ab
*/
/** getpid */
#include <my_config.h>
#include <unistd.h>
#include <mysql.h>
#include <skygw_utils.h>

View File

@ -1 +1,14 @@
add_subdirectory(canonical_tests)
if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND")
message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=<path>")
else()
if(${CMAKE_VERSION} VERSION_LESS 2.8)
execute_process(COMMAND cp ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR})
else()
file(COPY ${ERRMSG} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()
endif()
add_subdirectory(canonical_tests)
add_executable(classify classify.c)
target_link_libraries(classify query_classifier fullcore)
add_test(TestQueryClassifier classify ${CMAKE_CURRENT_SOURCE_DIR}/input.sql ${CMAKE_CURRENT_SOURCE_DIR}/expected.sql)

View File

@ -1,6 +1,11 @@
file(COPY ${ERRMSG} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND")
message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=<path>")
else()
if(${CMAKE_VERSION} VERSION_LESS 2.8)
execute_process(COMMAND cp ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR})
else()
file(COPY ${ERRMSG} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()
endif()
add_executable(canonizer canonizer.c)
target_link_libraries(canonizer pthread query_classifier z dl ssl aio crypt crypto rt m ${EMBEDDED_LIB} fullcore stdc++)

View File

@ -1,3 +1,4 @@
#include <my_config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

View File

@ -0,0 +1,176 @@
#include <my_config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <query_classifier.h>
#include <buffer.h>
#include <mysql.h>
#include <unistd.h>
static char* server_options[] = {
"SkySQL Gateway",
"--no-defaults",
"--datadir=.",
"--language=.",
"--skip-innodb",
"--default-storage-engine=myisam",
NULL
};
const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1;
static char* server_groups[] = {
"embedded",
"server",
"server",
NULL
};
int main(int argc, char** argv)
{
if(argc < 3){
fprintf(stderr,"Usage: classify <input> <expected output>");
return 1;
}
int rd = 0,buffsz = getpagesize(),strsz = 0,ex_val = 0;
char buffer[buffsz], *strbuff = (char*)calloc(buffsz,sizeof(char));
FILE *input,*expected;
if(mysql_library_init(num_elements, server_options, server_groups))
{
printf("Error: Cannot initialize Embedded Library.");
return 1;
}
input = fopen(argv[1],"rb");
expected = fopen(argv[2],"rb");
while((rd = fread(buffer,sizeof(char),buffsz,input))){
/**Fill the read buffer*/
if(strsz + rd >= buffsz){
char* tmp = (char*)calloc((buffsz*2),sizeof(char));
if(!tmp){
fprintf(stderr,"Error: Cannot allocate enough memory.");
return 1;
}
memcpy(tmp,strbuff,buffsz);
free(strbuff);
strbuff = tmp;
buffsz *= 2;
}
memcpy(strbuff+strsz,buffer,rd);
strsz += rd;
char *tok,*nlptr;
/**Remove newlines*/
while((nlptr = strpbrk(strbuff,"\n")) != NULL && (nlptr - strbuff) < strsz){
memmove(nlptr,nlptr+1,strsz - (nlptr + 1 - strbuff));
strsz -= 1;
}
/**Parse read buffer for full queries*/
while(strpbrk(strbuff,";") != NULL){
tok = strpbrk(strbuff,";");
unsigned int qlen = tok - strbuff + 1;
GWBUF* buff = gwbuf_alloc(qlen+6);
*((unsigned char*)(buff->start)) = qlen;
*((unsigned char*)(buff->start + 1)) = (qlen >> 8);
*((unsigned char*)(buff->start + 2)) = (qlen >> 16);
*((unsigned char*)(buff->start + 3)) = 0x00;
*((unsigned char*)(buff->start + 4)) = 0x03;
memcpy(buff->start+5, strbuff, qlen);
memmove(strbuff,tok + 1, strsz - qlen);
strsz -= qlen;
memset(strbuff + strsz,0,buffsz - strsz);
skygw_query_type_t type = query_classifier_get_type(buff);
char qtypestr[64];
char expbuff[256];
int expos = 0;
while((rd = fgetc(expected)) != '\n' && !feof(expected)){
expbuff[expos++] = rd;
}
expbuff[expos] = '\0';
if(type == QUERY_TYPE_UNKNOWN){
sprintf(qtypestr,"QUERY_TYPE_UNKNOWN");
}
if(type & QUERY_TYPE_LOCAL_READ){
sprintf(qtypestr,"QUERY_TYPE_LOCAL_READ");
}
if(type & QUERY_TYPE_READ){
sprintf(qtypestr,"QUERY_TYPE_READ");
}
if(type & QUERY_TYPE_WRITE){
sprintf(qtypestr,"QUERY_TYPE_WRITE");
}
if(type & QUERY_TYPE_MASTER_READ){
sprintf(qtypestr,"QUERY_TYPE_MASTER_READ");
}
if(type & QUERY_TYPE_SESSION_WRITE){
sprintf(qtypestr,"QUERY_TYPE_SESSION_WRITE");
}
if(type & QUERY_TYPE_USERVAR_READ){
sprintf(qtypestr,"QUERY_TYPE_USERVAR_READ");
}
if(type & QUERY_TYPE_SYSVAR_READ){
sprintf(qtypestr,"QUERY_TYPE_SYSVAR_READ");
}
if(type & QUERY_TYPE_GSYSVAR_READ){
sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_READ");
}
if(type & QUERY_TYPE_GSYSVAR_WRITE){
sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_WRITE");
}
if(type & QUERY_TYPE_BEGIN_TRX){
sprintf(qtypestr,"QUERY_TYPE_BEGIN_TRX");
}
if(type & QUERY_TYPE_ENABLE_AUTOCOMMIT){
sprintf(qtypestr,"QUERY_TYPE_ENABLE_AUTOCOMMIT");
}
if(type & QUERY_TYPE_DISABLE_AUTOCOMMIT){
sprintf(qtypestr,"QUERY_TYPE_DISABLE_AUTOCOMMIT");
}
if(type & QUERY_TYPE_ROLLBACK){
sprintf(qtypestr,"QUERY_TYPE_ROLLBACK");
}
if(type & QUERY_TYPE_COMMIT){
sprintf(qtypestr,"QUERY_TYPE_COMMIT");
}
if(type & QUERY_TYPE_PREPARE_NAMED_STMT){
sprintf(qtypestr,"QUERY_TYPE_PREPARE_NAMED_STMT");
}
if(type & QUERY_TYPE_PREPARE_STMT){
sprintf(qtypestr,"QUERY_TYPE_PREPARE_STMT");
}
if(type & QUERY_TYPE_EXEC_STMT){
sprintf(qtypestr,"QUERY_TYPE_EXEC_STMT");
}
if(type & QUERY_TYPE_CREATE_TMP_TABLE){
sprintf(qtypestr,"QUERY_TYPE_CREATE_TMP_TABLE");
}
if(type & QUERY_TYPE_READ_TMP_TABLE){
sprintf(qtypestr,"QUERY_TYPE_READ_TMP_TABLE");
}
if(strcmp(qtypestr,expbuff) != 0){
printf("Error in output: '%s' was expected but got '%s'",expbuff,qtypestr);
ex_val = 1;
}
gwbuf_free(buff);
}
}
fclose(input);
fclose(expected);
free(strbuff);
return ex_val;
}

View File

@ -0,0 +1,13 @@
QUERY_TYPE_READ
QUERY_TYPE_READ
QUERY_TYPE_WRITE
QUERY_TYPE_WRITE
QUERY_TYPE_CREATE_TMP_TABLE
QUERY_TYPE_GSYSVAR_WRITE
QUERY_TYPE_SYSVAR_READ
QUERY_TYPE_USERVAR_READ
QUERY_TYPE_COMMIT
QUERY_TYPE_DISABLE_AUTOCOMMIT
QUERY_TYPE_BEGIN_TRX
QUERY_TYPE_ROLLBACK
QUERY_TYPE_COMMIT

View File

@ -0,0 +1,13 @@
select sleep(2);
select * from tst where lname like '%e%' order by fname;
insert into tst values ("Jane","Doe"),("Daisy","Duck"),("Marie","Curie");
update tst set fname="Farmer", lname="McDonald" where lname="%Doe" and fname="John";
create temporary table tmp as select * from t1;
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
select @@server_id;
select @OLD_SQL_NOTES;
SET autocommit=1;
SET autocommit=0;
BEGIN;
ROLLBACK;
COMMIT;

View File

@ -47,7 +47,7 @@ CC=cc
CFLAGS=-c -I/usr/include -I../include -I../modules/include -I../inih \
$(MYSQL_HEADERS) \
-I$(LOGPATH) -I$(UTILSPATH) \
-Wall -g
-Wall -pedantic -g
LDFLAGS=-rdynamic -L$(LOGPATH) \
-Wl,-rpath,$(DEST)/lib \

View File

@ -1,5 +1,5 @@
/*
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
* 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.
@ -13,11 +13,11 @@
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright MariaDB Corporation Ab 2013-2014
* Copyright SkySQL Ab 2013
*/
/**
* @file buffer.h - The MaxScale buffer management functions
* @file buffer.h - The Gateway buffer management functions
*
* The buffer management is based on the principle of a linked list
* of variable size buffer, the intention beign to allow longer
@ -42,6 +42,8 @@
#include <buffer.h>
#include <atomic.h>
#include <skygw_debug.h>
#include <spinlock.h>
#include <hint.h>
static buffer_object_t* gwbuf_remove_buffer_object(
GWBUF* buf,
@ -65,20 +67,20 @@ gwbuf_alloc(unsigned int size)
GWBUF *rval;
SHARED_BUF *sbuf;
// Allocate the buffer header
/* Allocate the buffer header */
if ((rval = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
{
return NULL;
}
// Allocate the shared data buffer
/* Allocate the shared data buffer */
if ((sbuf = (SHARED_BUF *)malloc(sizeof(SHARED_BUF))) == NULL)
{
free(rval);
return NULL;
}
// Allocate the space for the actual data
/* Allocate the space for the actual data */
if ((sbuf->data = (unsigned char *)malloc(size)) == NULL)
{
free(rval);
@ -87,7 +89,7 @@ SHARED_BUF *sbuf;
}
spinlock_init(&rval->gwbuf_lock);
rval->start = sbuf->data;
rval->end = (void*)((uint8_t*)rval->start + size);
rval->end = (void *)((char *)rval->start+size);
sbuf->refcount = 1;
rval->sbuf = sbuf;
rval->next = NULL;
@ -197,8 +199,8 @@ GWBUF *gwbuf_clone_portion(
atomic_add(&buf->sbuf->refcount, 1);
clonebuf->sbuf = buf->sbuf;
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone info bits too */
clonebuf->start = (void *)((char*)buf->start)+start_offset;
clonebuf->end = (void *)((char *)clonebuf->start)+length;
clonebuf->start = (void *)((char*)buf->start+start_offset);
clonebuf->end = (void *)((char *)clonebuf->start+length);
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */
clonebuf->properties = NULL;
clonebuf->hint = NULL;
@ -277,8 +279,6 @@ return_clonebuf:
GWBUF *
gwbuf_append(GWBUF *head, GWBUF *tail)
{
GWBUF *ptr = head;
if (!head)
return tail;
CHK_GWBUF(head);
@ -311,7 +311,7 @@ GWBUF *rval = head;
CHK_GWBUF(head);
GWBUF_CONSUME(head, length);
CHK_GWBUF(head);
CHK_GWBUF(head);
if (GWBUF_EMPTY(head))
{
@ -370,11 +370,36 @@ gwbuf_trim(GWBUF *buf, unsigned int n_bytes)
gwbuf_consume(buf, GWBUF_LENGTH(buf));
return NULL;
}
buf->end -= n_bytes;
buf->end = (void *)((char *)buf->end - n_bytes);
return buf;
}
/**
* Trim bytes from the end of a GWBUF structure that may be the first
* in a list. If the buffer has n_bytes or less then it will be freed and
* the next buffer in the list will be returned, or if none, NULL.
*
* @param head The buffer to trim
* @param n_bytes The number of bytes to trim off
* @return The buffer chain or NULL if buffer chain now empty
*/
GWBUF *
gwbuf_rtrim(GWBUF *head, unsigned int n_bytes)
{
GWBUF *rval = head;
CHK_GWBUF(head);
GWBUF_RTRIM(head, n_bytes);
CHK_GWBUF(head);
if (GWBUF_EMPTY(head))
{
rval = head->next;
gwbuf_free(head);
}
return rval;
}
/**
* Set given type to all buffers on the list.
* *

View File

@ -41,6 +41,7 @@
*
* @endverbatim
*/
#include <my_config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@ -131,7 +132,7 @@ CONFIG_PARAMETER *param, *p1;
ptr->element = NULL;
cntxt->next = ptr;
}
/* Check to see if the paramter already exists for the section */
/* Check to see if the parameter already exists for the section */
p1 = ptr->parameters;
while (p1)
{

View File

@ -32,7 +32,7 @@
* 12/06/13 Mark Riddoch Initial implementation
* 21/06/13 Massimiliano Pinto free_dcb is used
* 25/06/13 Massimiliano Pinto Added checks to session and router_session
* 28/06/13 Mark Riddoch Changed the free mechanism ti
* 28/06/13 Mark Riddoch Changed the free mechanism to
* introduce a zombie state for the
* dcb
* 02/07/2013 Massimiliano Pinto Addition of delayqlock, delayq and
@ -73,7 +73,7 @@
extern int lm_enabled_logfiles_bitmask;
static DCB *allDCBs = NULL; /* Diagnotics need a list of DCBs */
static DCB *allDCBs = NULL; /* Diagnostics need a list of DCBs */
static DCB *zombies = NULL;
static SPINLOCK dcbspin = SPINLOCK_INIT;
static SPINLOCK zombiespin = SPINLOCK_INIT;
@ -88,6 +88,7 @@ static DCB* dcb_get_next (DCB* dcb);
static int dcb_null_write(DCB *dcb, GWBUF *buf);
static int dcb_null_close(DCB *dcb);
static int dcb_null_auth(DCB *dcb, SERVER *server, SESSION *session, GWBUF *buf);
static int dcb_isvalid_nolock(DCB *dcb);
/**
* Return the pointer to the lsit of zombie DCB's
@ -1893,20 +1894,40 @@ dcb_isvalid(DCB *dcb)
DCB *ptr;
int rval = 0;
if (dcb)
{
spinlock_acquire(&dcbspin);
rval = dcb_isvalid_nolock(dcb);
spinlock_release(&dcbspin);
}
return rval;
}
/**
* Check the passed DCB to ensure it is in the list of allDCBS.
* Requires that the DCB list is already locked before call.
*
* @param dcb The DCB to check
* @return 1 if the DCB is in the list, otherwise 0
*/
static int
dcb_isvalid_nolock(DCB *dcb)
{
DCB *ptr;
int rval = 0;
if (dcb)
{
ptr = allDCBs;
while (ptr)
while (ptr && ptr != dcb)
{
if (ptr == dcb)
{
rval = 1;
break;
}
ptr = ptr->next;
}
spinlock_release(&dcbspin);
return rval;
rval = (ptr == dcb);
}
return rval;
}
@ -1919,33 +1940,11 @@ int rval = 0;
static DCB *
dcb_get_next (DCB* dcb)
{
DCB* p;
spinlock_acquire(&dcbspin);
p = allDCBs;
if (dcb == NULL || p == NULL)
{
dcb = p;
}
else
{
while (p != NULL && dcb != p)
{
p = p->next;
}
if (p != NULL)
{
dcb = p->next;
}
else
{
dcb = NULL;
}
if (dcb) {
dcb = dcb_isvalid_nolock(dcb) ? dcb->next : NULL;
}
else dcb = allDCBs;
spinlock_release(&dcbspin);
return dcb;

View File

@ -40,10 +40,13 @@
* @endverbatim
*/
#define _XOPEN_SOURCE 700
#include <my_config.h>
#include <ftw.h>
#include <string.h>
#include <strings.h>
#include <gw.h>
#include <unistd.h>
#include <time.h>
#include <getopt.h>
#include <service.h>
#include <server.h>
@ -226,7 +229,6 @@ sigfatal_handler (int i)
{
void *addrs[128];
char **strings= NULL;
int n, count = backtrace(addrs, 128);
char** symbols = backtrace_symbols( addrs, count );
@ -1088,9 +1090,9 @@ int main(int argc, char **argv)
goto return_main;
case 'l':
if (strncasecmp(optarg, "file") == 0)
if (strncasecmp(optarg, "file", PATH_MAX) == 0)
logtofile = 1;
else if (strncasecmp(optarg, "shm") == 0)
else if (strncasecmp(optarg, "shm", PATH_MAX) == 0)
logtofile = 0;
else
{
@ -1669,6 +1671,8 @@ static void log_flush_cb(
void* arg)
{
ssize_t timeout_ms = *(ssize_t *)arg;
const struct timespec ts1 = {0, 1000000*timeout_ms};
struct timespec ts2;
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
"Started MaxScale log flusher.")));
@ -1677,7 +1681,7 @@ static void log_flush_cb(
skygw_log_flush(LOGFILE_MESSAGE);
skygw_log_flush(LOGFILE_TRACE);
skygw_log_flush(LOGFILE_DEBUG);
usleep(timeout_ms*1000);
nanosleep(&ts1, &ts2);
}
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
"Finished MaxScale log flusher.")));

View File

@ -34,6 +34,7 @@
* 25-09-2013 Massimiliano Pinto setipaddress uses getaddrinfo
* 06-02-2014 Mark Riddoch Added parse_bindconfig
* 10-02-2014 Massimiliano Pinto Added return code to setipaddress
* 02-09-2014 Martin Brampton Replace C++ comment with C comment
*
*@endverbatim
*/
@ -148,7 +149,7 @@ void gw_daemonize(void) {
}
if (pid != 0) {
// exit from main
/* exit from main */
exit(0);
}

View File

@ -15,6 +15,7 @@
*
* Copyright MariaDB Corporation Ab 2013-2014
*/
#include <my_config.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@ -257,7 +258,7 @@ poll_add_dcb(DCB *dcb)
dcb,
STRDCBSTATE(dcb->state))));
}
ss_dassert(rc == 0); /*< trap in debug */
ss_info_dassert(rc == 0, "Unable to add poll"); /*< trap in debug */
} else {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,

View File

@ -136,7 +136,7 @@ SERVER *ptr;
/**
* Set a unique name for the server
*
* @param server The server to ste the name on
* @param server The server to set the name on
* @param name The unique name for the server
*/
void

View File

@ -91,10 +91,10 @@ static void service_add_qualified_param(
* @param servname The service name
* @param router Name of the router module this service uses
*
* @return The newly created service or NULL if an error occured
* @return The newly created service or NULL if an error occurred
*/
SERVICE *
service_alloc(char *servname, char *router)
service_alloc(const char *servname, const char *router)
{
SERVICE *service;
@ -152,7 +152,7 @@ SERVICE *service;
/**
* Check to see if a service pointer is valid
*
* @param service The poitner to check
* @param service The pointer to check
* @return 1 if the service is in the list of all services
*/
int

View File

@ -30,6 +30,7 @@
#include <spinlock.h>
#include <atomic.h>
#include <time.h>
/**
* Initialise a spinlock.
@ -39,13 +40,13 @@
void
spinlock_init(SPINLOCK *lock)
{
lock->lock = 0;
lock->lock = 0;
#if SPINLOCK_PROFILE
lock->spins = 0;
lock->acquired = 0;
lock->waiting = 0;
lock->max_waiting = 0;
lock->contended = 0;
lock->spins = 0;
lock->acquired = 0;
lock->waiting = 0;
lock->max_waiting = 0;
lock->contended = 0;
#endif
}
@ -62,24 +63,30 @@ int spins = 0;
atomic_add(&(lock->waiting), 1);
#endif
while (atomic_add(&(lock->lock), 1) != 0)
{
atomic_add(&(lock->lock), -1);
#ifdef __GNUC__
while (__sync_lock_test_and_set(&(lock->lock), 1))
while (lock->lock) {
#else
while (atomic_add(&(lock->lock), 1) != 0)
{
atomic_add(&(lock->lock), -1);
#endif
#if SPINLOCK_PROFILE
atomic_add(&(lock->spins), 1);
spins++;
#endif
}
#if SPINLOCK_PROFILE
if (spins)
{
lock->contended++;
if (lock->maxspins < spins)
lock->maxspins = spins;
}
lock->acquired++;
lock->owner = THREAD_SHELF();
atomic_add(&(lock->waiting), -1);
if (spins)
{
lock->contended++;
if (lock->maxspins < spins)
lock->maxspins = spins;
}
lock->acquired++;
lock->owner = THREAD_SHELF();
atomic_add(&(lock->waiting), -1);
#endif
}
@ -92,16 +99,20 @@ int spins = 0;
int
spinlock_acquire_nowait(SPINLOCK *lock)
{
if (atomic_add(&(lock->lock), 1) != 0)
{
atomic_add(&(lock->lock), -1);
return FALSE;
}
#if SPINLOCK_PROFILE
lock->acquired++;
lock->owner = THREAD_SHELF();
#ifdef __GNUC__
if (__sync_lock_test_and_set(&(lock->lock), 1)) return FALSE;
#else
if (atomic_add(&(lock->lock), 1) != 0)
{
atomic_add(&(lock->lock), -1);
return FALSE;
}
#endif
return TRUE;
#if SPINLOCK_PROFILE
lock->acquired++;
lock->owner = THREAD_SHELF();
#endif
return TRUE;
}
/*
@ -112,11 +123,16 @@ spinlock_acquire_nowait(SPINLOCK *lock)
void
spinlock_release(SPINLOCK *lock)
{
#if SPINLOCK_PROFILE
if (lock->waiting > lock->max_waiting)
lock->max_waiting = lock->waiting;
#if SPINLOCK_PROFILE
if (lock->waiting > lock->max_waiting)
lock->max_waiting = lock->waiting;
#endif
#ifdef __GNUC__
__sync_synchronize(); /* Memory barrier. */
lock->lock = 0;
#else
atomic_add(&(lock->lock), -1);
#endif
atomic_add(&(lock->lock), -1);
}
/**

View File

@ -1,16 +1,40 @@
add_executable(test_mysql_users test_mysql_users.c)
add_executable(test_hash testhash.c)
add_executable(test_hint testhint.c)
add_executable(test_spinlock testspinlock.c)
add_executable(test_filter testfilter.c)
add_executable(test_buffer testbuffer.c)
add_executable(test_dcb testdcb.c)
add_executable(test_modutil testmodutil.c)
add_executable(test_poll testpoll.c)
add_executable(test_service testservice.c)
add_executable(test_server testserver.c)
add_executable(test_users testusers.c)
add_executable(test_adminusers testadminusers.c)
target_link_libraries(test_mysql_users fullcore MySQLClient)
target_link_libraries(test_hash fullcore)
target_link_libraries(test_hint fullcore)
target_link_libraries(test_spinlock fullcore)
target_link_libraries(test_filter fullcore)
target_link_libraries(test_buffer fullcore)
target_link_libraries(test_dcb fullcore)
target_link_libraries(test_modutil fullcore)
target_link_libraries(test_poll fullcore)
target_link_libraries(test_service fullcore)
target_link_libraries(test_server fullcore)
target_link_libraries(test_users fullcore)
target_link_libraries(test_adminusers fullcore)
add_test(testMySQLUsers test_mysql_users)
add_test(TestHash test_hash)
add_test(TestHint test_hint)
add_test(TestSpinlock test_spinlock)
add_test(TestFilter test_filter)
add_test(TestBuffer test_buffer)
add_test(TestDCB test_dcb)
add_test(TestModutil test_modutil)
add_test(TestPoll test_poll)
add_test(TestService test_service)
add_test(TestServer test_server)
add_test(TestUsers test_users)
add_test(TestAdminUsers test_adminusers)

View File

@ -18,10 +18,11 @@ LDFLAGS=-rdynamic -L$(LOGPATH) -L$(EMBEDDED_LIB) \
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) \
-Wl,-rpath,$(EMBEDDED_LIB)
LIBS= -lz -lm -lcrypt -lcrypto -ldl -laio -lrt -pthread -llog_manager \
-L../../inih/extra -linih -lssl -lstdc++ -lmysqld
LIBS= -L$(EMBEDDED_LIB) -lmysqld \
-lz -lm -lcrypt -lcrypto -ldl -laio -lrt -pthread -llog_manager \
-L../../inih/extra -linih -lssl -lstdc++
TESTS=testhash testspinlock testfilter testadminusers
TESTS=testhash testspinlock testbuffer testmodutil testpoll testservice testdcb testfilter testadminusers
cleantests:
- $(DEL) *.o
@ -47,6 +48,39 @@ testspinlock: testspinlock.c
-I$(ROOT_PATH)/utils \
testspinlock.c ../spinlock.o ../atomic.o ../thread.o -o testspinlock
testmodutil: testmodutil.c
$(CC) $(CFLAGS) \
-I$(ROOT_PATH)/server/include \
-I$(ROOT_PATH)/utils \
testmodutil.c ../modutil.o ../buffer.o ../atomic.o -o testmodutil
testbuffer: testbuffer.c
$(CC) $(CFLAGS) \
-I$(ROOT_PATH)/server/include \
-I$(ROOT_PATH)/utils \
testbuffer.c ../buffer.o ../atomic.o -o testbuffer
testpoll: testpoll.c
$(CC) $(CFLAGS) $(LDFLAGS) \
-I$(ROOT_PATH)/server/include \
-I$(ROOT_PATH)/utils \
-I$(ROOT_PATH)/log_manager \
testpoll.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testpoll
testservice: testservice.c
$(CC) $(CFLAGS) $(LDFLAGS) \
-I$(ROOT_PATH)/server/include \
-I$(ROOT_PATH)/utils \
-I$(ROOT_PATH)/log_manager \
testservice.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testservice
testdcb: testdcb.c
$(CC) $(CFLAGS) $(LDFLAGS) \
-I$(ROOT_PATH)/server/include \
-I$(ROOT_PATH)/utils \
-I$(ROOT_PATH)/log_manager \
testdcb.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testdcb
testfilter: testfilter.c libcore.a
$(CC) $(CFLAGS) $(LDFLAGS) \
-I$(ROOT_PATH)/server/include \

View File

@ -0,0 +1,158 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 29-08-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <buffer.h>
/**
* test1 Allocate a buffer and do lots of things
*
*/
static int
test1()
{
GWBUF *buffer, *extra, *clone, *partclone, *transform;
int size = 100;
int bite1 = 35;
int bite2 = 60;
int bite3 = 10;
int buflen;
/* Single buffer tests */
ss_dfprintf(stderr,
"testbuffer : creating buffer with data size %d bytes",
size);
buffer = gwbuf_alloc(size);
ss_dfprintf(stderr, "\t..done\nAllocated buffer of size %d.", size);
buflen = GWBUF_LENGTH(buffer);
ss_dfprintf(stderr, "\nBuffer length is now %d", buflen);
ss_info_dassert(size == buflen, "Incorrect buffer size");
ss_info_dassert(0 == GWBUF_EMPTY(buffer), "Buffer should not be empty");
ss_info_dassert(GWBUF_IS_TYPE_UNDEFINED(buffer), "Buffer type should be undefined");
ss_dfprintf(stderr, "\t..done\nSet a property for the buffer");
gwbuf_add_property(buffer, "name", "value");
ss_info_dassert(0 == strcmp("value", gwbuf_get_property(buffer, "name")), "Should now have correct property");
strcpy(GWBUF_DATA(buffer), "The quick brown fox jumps over the lazy dog");
ss_dfprintf(stderr, "\t..done\nLoad some data into the buffer");
ss_info_dassert('q' == GWBUF_DATA_CHAR(buffer, 4), "Fourth character of buffer must be 'q'");
ss_info_dassert(-1 == GWBUF_DATA_CHAR(buffer, 105), "Hundred and fifth character of buffer must return -1");
ss_info_dassert(0 == GWBUF_IS_SQL(buffer), "Must say buffer is not SQL, as it does not have marker");
strcpy(GWBUF_DATA(buffer), "1234\x03SELECT * FROM sometable");
ss_dfprintf(stderr, "\t..done\nLoad SQL data into the buffer");
ss_info_dassert(1 == GWBUF_IS_SQL(buffer), "Must say buffer is SQL, as it does have marker");
transform = gwbuf_clone_transform(buffer, GWBUF_TYPE_PLAINSQL);
ss_dfprintf(stderr, "\t..done\nAttempt to transform buffer to plain SQL - should fail");
ss_info_dassert(NULL == transform, "Buffer cannot be transformed to plain SQL");
gwbuf_set_type(buffer, GWBUF_TYPE_MYSQL);
ss_dfprintf(stderr, "\t..done\nChanged buffer type to MySQL");
ss_info_dassert(GWBUF_IS_TYPE_MYSQL(buffer), "Buffer type changed to MySQL");
transform = gwbuf_clone_transform(buffer, GWBUF_TYPE_PLAINSQL);
ss_dfprintf(stderr, "\t..done\nAttempt to transform buffer to plain SQL - should succeed");
ss_info_dassert((NULL != transform) && (GWBUF_IS_TYPE_PLAINSQL(transform)), "Transformed buffer is plain SQL");
clone = gwbuf_clone(buffer);
ss_dfprintf(stderr, "\t..done\nCloned buffer");
buflen = GWBUF_LENGTH(clone);
ss_dfprintf(stderr, "\nCloned buffer length is now %d", buflen);
ss_info_dassert(size == buflen, "Incorrect buffer size");
ss_info_dassert(0 == GWBUF_EMPTY(clone), "Cloned buffer should not be empty");
ss_dfprintf(stderr, "\t..done\n");
gwbuf_free(clone);
ss_dfprintf(stderr, "Freed cloned buffer");
ss_dfprintf(stderr, "\t..done\n");
partclone = gwbuf_clone_portion(buffer, 25, 50);
buflen = GWBUF_LENGTH(partclone);
ss_dfprintf(stderr, "Part cloned buffer length is now %d", buflen);
ss_info_dassert(50 == buflen, "Incorrect buffer size");
ss_info_dassert(0 == GWBUF_EMPTY(partclone), "Part cloned buffer should not be empty");
ss_dfprintf(stderr, "\t..done\n");
gwbuf_free(partclone);
ss_dfprintf(stderr, "Freed part cloned buffer");
ss_dfprintf(stderr, "\t..done\n");
buffer = gwbuf_consume(buffer, bite1);
ss_info_dassert(NULL != buffer, "Buffer should not be null");
buflen = GWBUF_LENGTH(buffer);
ss_dfprintf(stderr, "Consumed %d bytes, now have %d, should have %d", bite1, buflen, size-bite1);
ss_info_dassert((size - bite1) == buflen, "Incorrect buffer size");
ss_info_dassert(0 == GWBUF_EMPTY(buffer), "Buffer should not be empty");
ss_dfprintf(stderr, "\t..done\n");
buffer = gwbuf_consume(buffer, bite2);
ss_info_dassert(NULL != buffer, "Buffer should not be null");
buflen = GWBUF_LENGTH(buffer);
ss_dfprintf(stderr, "Consumed %d bytes, now have %d, should have %d", bite2, buflen, size-bite1-bite2);
ss_info_dassert((size-bite1-bite2) == buflen, "Incorrect buffer size");
ss_info_dassert(0 == GWBUF_EMPTY(buffer), "Buffer should not be empty");
ss_dfprintf(stderr, "\t..done\n");
buffer = gwbuf_consume(buffer, bite3);
ss_dfprintf(stderr, "Consumed %d bytes, should have null buffer", bite3);
ss_info_dassert(NULL == buffer, "Buffer should be null");
/* Buffer list tests */
size = 100000;
buffer = gwbuf_alloc(size);
ss_dfprintf(stderr, "\t..done\nAllocated buffer of size %d.", size);
buflen = GWBUF_LENGTH(buffer);
ss_dfprintf(stderr, "\nBuffer length is now %d", buflen);
ss_info_dassert(size == buflen, "Incorrect buffer size");
ss_info_dassert(0 == GWBUF_EMPTY(buffer), "Buffer should not be empty");
ss_info_dassert(GWBUF_IS_TYPE_UNDEFINED(buffer), "Buffer type should be undefined");
extra = gwbuf_alloc(size);
buflen = GWBUF_LENGTH(buffer);
ss_dfprintf(stderr, "\t..done\nAllocated extra buffer of size %d.", size);
ss_info_dassert(size == buflen, "Incorrect buffer size");
buffer = gwbuf_append(buffer, extra);
buflen = gwbuf_length(buffer);
ss_dfprintf(stderr, "\t..done\nAppended extra buffer to original buffer to create list of size %d", buflen);
ss_info_dassert((size*2) == gwbuf_length(buffer), "Incorrect size for set of buffers");
buffer = gwbuf_rtrim(buffer, 60000);
buflen = GWBUF_LENGTH(buffer);
ss_dfprintf(stderr, "\t..done\nTrimmed 60 bytes from buffer, now size is %d.", buflen);
ss_info_dassert((size-60000) == buflen, "Incorrect buffer size");
buffer = gwbuf_rtrim(buffer, 60000);
buflen = GWBUF_LENGTH(buffer);
ss_dfprintf(stderr, "\t..done\nTrimmed another 60 bytes from buffer, now size is %d.", buflen);
ss_info_dassert(100000 == buflen, "Incorrect buffer size");
ss_info_dassert(buffer == extra, "The buffer pointer should now point to the extra buffer");
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -0,0 +1,87 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 05-09-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dcb.h>
/**
* test1 Allocate a dcb and do lots of other things
*
*/
static int
test1()
{
DCB *dcb, *extra, *clone;
int size = 100;
int bite1 = 35;
int bite2 = 60;
int bite3 = 10;
int buflen;
/* Single buffer tests */
ss_dfprintf(stderr,
"testdcb : creating buffer with type DCB_ROLE_SERVICE_LISTENER");
dcb = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);
ss_info_dassert(dcb_isvalid(dcb), "New DCB must be valid");
ss_dfprintf(stderr, "\t..done\nAllocated dcb.");
clone = dcb_clone(dcb);
ss_dfprintf(stderr, "\t..done\nCloned dcb");
printAllDCBs();
ss_info_dassert(true, "Something is true");
ss_dfprintf(stderr, "\t..done\n");
dcb_free(dcb);
ss_dfprintf(stderr, "Freed original dcb");
ss_info_dassert(!dcb_isvalid(dcb), "Freed DCB must not be valid");
ss_dfprintf(stderr, "\t..done\nMake clone DCB a zombie");
clone->state = DCB_STATE_NOPOLLING;
dcb_add_to_zombieslist(clone);
ss_info_dassert(dcb_get_zombies() == clone, "Clone DCB must be start of zombie list now");
ss_dfprintf(stderr, "\t..done\nProcess the zombies list");
dcb_process_zombies(0);
ss_dfprintf(stderr, "\t..done\nCheck clone no longer valid");
ss_info_dassert(!dcb_isvalid(clone), "After zombie processing, clone DCB must not be valid");
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -0,0 +1,69 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 08-10-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hint.h>
/**
* test1 Allocate table of users and mess around with it
*
*/
static int
test1()
{
HINT *hint;
/* Hint tests */
ss_dfprintf(stderr,
"testhint : Add a parameter hint to a null list");
hint = hint_create_parameter(NULL, strdup("name"), "value");
ss_info_dassert(NULL != hint, "New hint list should not be null");
ss_info_dassert(0 == strcmp("value", hint->value), "Hint value should be correct");
ss_info_dassert(0 != hint_exists(hint, HINT_PARAMETER), "Hint of parameter type should exist");
ss_dfprintf(stderr, "\t..done\nFree hints.");
if (NULL != hint) hint_free(hint);
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -0,0 +1,78 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 17-09-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <modutil.h>
#include <buffer.h>
/**
* test1 Allocate a service and do lots of other things
*
*/
static int
test1()
{
GWBUF *buffer;
char *(sql[100]);
int result, length, residual;
/* Poll tests */
ss_dfprintf(stderr,
"testmodutil : Rudimentary tests.");
buffer = gwbuf_alloc(100);
ss_info_dassert(0 == modutil_is_SQL(buffer), "Default buffer should be diagnosed as not SQL");
/* There would ideally be some straightforward way to create a SQL buffer? */
ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer");
ss_info_dassert(0 == modutil_extract_SQL(buffer, sql, &length), "Default buffer should fail");
ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer different way?");
ss_info_dassert(0 == modutil_MySQL_Query(buffer, sql, &length, &residual), "Default buffer should fail");
ss_dfprintf(stderr, "\t..done\nReplace SQL in buffer");
ss_info_dassert(0 == modutil_replace_SQL(buffer, "select * from some_table;"), "Default buffer should fail");
ss_dfprintf(stderr, "\t..done\nTidy up.");
gwbuf_free(buffer);
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -0,0 +1,77 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 11-09-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <dcb.h>
/**
* test1 Allocate a service and do lots of other things
*
*/
static int
test1()
{
DCB *dcb;
int result;
/* Poll tests */
ss_dfprintf(stderr,
"testpoll : Initialise the polling system.");
poll_init();
ss_dfprintf(stderr, "\t..done\nAdd a DCB");
dcb = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);
dcb->fd = socket(AF_UNIX, SOCK_STREAM, 0);
poll_add_dcb(dcb);
poll_remove_dcb(dcb);
poll_add_dcb(dcb);
ss_dfprintf(stderr, "\t..done\nStart wait for events.");
sleep(10);
poll_shutdown();
ss_dfprintf(stderr, "\t..done\nTidy up.");
dcb_free(dcb);
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -0,0 +1,92 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 08-10-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <server.h>
/**
* test1 Allocate a server and do lots of other things
*
*/
static int
test1()
{
SERVER *server;
int result;
char *status;
/* Server tests */
ss_dfprintf(stderr,
"testserver : creating server called MyServer");
server = server_alloc("MyServer", "HTTPD", 9876);
//ss_info_dassert(NULL != service, "New server with valid protocol and port must not be null");
//ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation");
ss_dfprintf(stderr, "\t..done\nTest Parameter for Server.");
ss_info_dassert(NULL == serverGetParameter(server, "name"), "Parameter should be null when not set");
serverAddParameter(server, "name", "value");
ss_info_dassert(0 == strcmp("value", serverGetParameter(server, "name")), "Parameter should be returned correctly");
ss_dfprintf(stderr, "\t..done\nTesting Unique Name for Server.");
ss_info_dassert(NULL == server_find_by_unique_name("uniquename"), "Should not find non-existent unique name.");
server_set_unique_name(server, "uniquename");
ss_info_dassert(server == server_find_by_unique_name("uniquename"), "Should find by unique name.");
ss_dfprintf(stderr, "\t..done\nTesting Status Setting for Server.");
status = server_status(server);
ss_info_dassert(0 == strcmp("Down", status), "Status of Server should be Down prior to being set.");
if (NULL != status) free(status);
server_set_status(server, SERVER_MASTER);
status = server_status(server);
ss_info_dassert(0 == strcmp("Master, Down", status), "Should find correct status.");
server_clear_status(server, SERVER_MASTER);
ss_info_dassert(0 == strcmp("Down", status), "Status of Server should be Down after status cleared.");
if (NULL != status) free(status);
ss_dfprintf(stderr, "\t..done\nRun Prints for Server and all Servers.");
printServer(server);
printAllServers();
ss_dfprintf(stderr, "\t..done\nFreeing Server.");
ss_info_dassert(0 != server_free(server), "Free should succeed");
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -0,0 +1,86 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 08-09-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <service.h>
/**
* test1 Allocate a service and do lots of other things
*
*/
static int
test1()
{
SERVICE *service;
int result;
/* Service tests */
ss_dfprintf(stderr,
"testservice : creating service called MyService with router nonexistent");
service = service_alloc("MyService", "non-existent");
ss_info_dassert(NULL == service, "New service with invalid router should be null");
ss_info_dassert(0 == service_isvalid(service), "Service must not be valid after incorrect creation");
ss_dfprintf(stderr, "\t..done\nValid service creation, router testroute.");
service = service_alloc("MyService", "testroute");
ss_info_dassert(NULL != service, "New service with valid router must not be null");
ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation");
ss_info_dassert(0 == strcmp("MyService", service_get_name(service)), "Service must have given name");
ss_dfprintf(stderr, "\t..done\nAdding protocol HTTPD.");
ss_info_dassert(0 != serviceAddProtocol(service, "HTTPD", "localhost", 9876), "Add Protocol should succeed");
ss_info_dassert(0 != serviceHasProtocol(service, "HTTPD", 9876), "Service should have new protocol as requested");
serviceStartProtocol(service, "HTTPD", 9876);
ss_dfprintf(stderr, "\t..done\nStarting Service.");
result = serviceStart(service);
ss_info_dassert(0 != result, "Start should succeed");
result = serviceStop(service);
ss_info_dassert(0 != result, "Stop should succeed");
result = serviceStartAll();
ss_info_dassert(0 != result, "Start all should succeed");
ss_dfprintf(stderr, "\t..done\nStopping Service.");
ss_info_dassert(0 != serviceStop(service), "Stop should succeed");
ss_dfprintf(stderr, "\t..done\nFreeing Service.");
ss_info_dassert(0 != service_free(service), "Free should succeed");
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -105,12 +105,16 @@ test2()
{
SPINLOCK lck;
void *handle;
struct timespec sleeptime;
sleeptime.tv_sec = 10;
sleeptime.tv_nsec = 0;
acquire_time = 0;
spinlock_init(&lck);
spinlock_acquire(&lck);
handle = thread_start(test2_helper, (void *)&lck);
sleep(10);
nanosleep(&sleeptime, NULL);
spinlock_release(&lck);
thread_wait(handle);
@ -122,12 +126,118 @@ void *handle;
return 0;
}
main(int argc, char **argv)
/**
* test3 spinlock_acquire tests process bound threads
*
* Check that spinlock correctly blocks all other threads whilst the spinlock
* is held.
*
* Start multiple threads that obtain spinlock and run process bound
*/
#define THREADS 5
#define ITERATIONS 50000
#define PROCESS_LOOP 10000
#define SECONDS 15
#define NANOTIME 100000
static int times_run, failures;
static volatile int active;
static int threadrun[THREADS];
static int nowait[THREADS];
static SPINLOCK lck;
static void
test3_helper(void *data)
{
// SPINLOCK *lck = (SPINLOCK *)data;
int i;
int n = *(int *)data;
struct timespec sleeptime;
time_t rawtime;
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 1;
while (1) {
if (spinlock_acquire_nowait(&lck)) {
nowait[n]++;
}
else {
spinlock_acquire(&lck);
}
if (times_run++ > ITERATIONS) {
break;
}
threadrun[n]++;
/*
if (99 == (times_run % 100)) {
time ( &rawtime );
fprintf(stderr, "%s Done %d iterations of test, in thread %d.\n", asctime (localtime ( &rawtime )), times_run, n);
}
*/
if (0 != active) {
fprintf(stderr, "spinlock: test 3 failed with active non-zero after lock obtained.\n");
failures++;
}
else {
active = 1;
for (i=0; i<PROCESS_LOOP; i++);
}
active = 0;
spinlock_release(&lck);
for (i=0; i<(4*PROCESS_LOOP); i++);
// nanosleep(&sleeptime, NULL);
}
spinlock_release(&lck);
}
static int
test3()
{
// SPINLOCK lck;
void *handle[THREADS];
int i;
int tnum[THREADS];
time_t rawtime;
struct timespec sleeptime;
sleeptime.tv_sec = 20;
sleeptime.tv_nsec = NANOTIME;
times_run = 0;
active = 0;
failures = 0;
spinlock_init(&lck);
time ( &rawtime );
fprintf(stderr, "%s Starting %d threads.\n", asctime (localtime ( &rawtime )), THREADS);
for (i = 0; i<THREADS; i++) {
threadrun[i] = 0;
tnum[i] = i;
handle[i] = thread_start(test3_helper, &tnum[i]);
}
for (i = 0; i<THREADS; i++) {
fprintf(stderr, "spinlock_test 3 thread %d ran %d times, no wait %d times before waits.\n", i, threadrun[i], nowait[i]);
}
for (i = 0; i<THREADS; i++) {
time ( &rawtime );
fprintf(stderr, "%s spinlock_test 3 finished sleeps, about to wait for thread %d.\n", asctime (localtime ( &rawtime )), i);
thread_wait(handle[i]);
}
for (i = 0; i<THREADS; i++) {
fprintf(stderr, "spinlock_test 3 thread %d ran %d times, no wait %d times.\n", i, threadrun[i], nowait[i]);
}
time ( &rawtime );
fprintf(stderr, "%s spinlock_test 3 completed, %d failures.\n", asctime (localtime ( &rawtime )), failures);
return 0 == failures ? 0: 1;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
result += test2();
result += test3();
exit(result);
}

View File

@ -0,0 +1,81 @@
/*
* 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
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 08-10-2014 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <users.h>
/**
* test1 Allocate table of users and mess around with it
*
*/
static int
test1()
{
USERS *users;
int result, count;
/* Poll tests */
ss_dfprintf(stderr,
"testusers : Initialise the user table.");
users = users_alloc();
ss_info_dassert(NULL != users, "Allocating user table should not return NULL.")
ss_dfprintf(stderr, "\t..done\nAdd a user");
count = users_add(users, "username", "authorisation");
ss_info_dassert(1 == count, "Should add one user");
ss_info_dassert(strcmp("authorisation", users_fetch(users, "username")), "User authorisation should be correct");
ss_dfprintf(stderr, "\t..done\nPrint users");
usersPrint(users);
ss_dfprintf(stderr, "\t..done\nUpdate a user");
count = users_update(users, "username", "newauth");
ss_info_dassert(1 == count, "Should update just one user");
ss_info_dassert(strcmp("newauth", users_fetch(users, "username")), "User authorisation should be correctly updated");
ss_dfprintf(stderr, "\t..done\nDelete a user.");
count = users_delete(users, "username");
ss_info_dassert(1 == count, "Should delete just one user");
ss_dfprintf(stderr, "\t..done\nFree user table.");
users_free(users);
ss_dfprintf(stderr, "\t..done\n");
return 0;
}
int main(int argc, char **argv)
{
int result = 0;
result += test1();
exit(result);
}

View File

@ -29,6 +29,7 @@
* the gwbuff strategy
* 13-06-2013 Massimiliano Pinto MaxScale local authentication
* basics
* 02-09-2014 Martin Brampton Replaced C++ comments by C comments
*
* @endverbatim
*/
@ -44,19 +45,19 @@
extern int lm_enabled_logfiles_bitmask;
// used in the hex2bin function
/* used in the hex2bin function */
#define char_val(X) (X >= '0' && X <= '9' ? X-'0' :\
X >= 'A' && X <= 'Z' ? X-'A'+10 :\
X >= 'a' && X <= 'z' ? X-'a'+10 :\
'\177')
// used in the bin2hex function
/* used in the bin2hex function */
char hex_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char hex_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
//////////////////////////////////////////
//backend read event triggered by EPOLLIN
//////////////////////////////////////////
/*****************************************
* backend read event triggered by EPOLLIN
*****************************************/
int setnonblocking(int fd) {
@ -91,17 +92,17 @@ char *gw_strend(register const char *s) {
return (char*) (s-1);
}
///////////////////////////////
// generate a random char
//////////////////////////////
/*****************************************
* generate a random char
*****************************************/
static char gw_randomchar() {
return (char)((rand() % 78) + 30);
}
/////////////////////////////////
// generate a random string
// output must be pre allocated
/////////////////////////////////
/*****************************************
* generate a random string
* output must be pre allocated
*****************************************/
int gw_generate_random_str(char *output, int len) {
int i;
@ -116,10 +117,10 @@ int gw_generate_random_str(char *output, int len) {
return 0;
}
/////////////////////////////////
// hex string to binary data
// output must be pre allocated
/////////////////////////////////
/*****************************************
* hex string to binary data
* output must be pre allocated
*****************************************/
int gw_hex2bin(uint8_t *out, const char *in, unsigned int len) {
const char *in_end= in + len;
@ -140,10 +141,10 @@ int gw_hex2bin(uint8_t *out, const char *in, unsigned int len) {
return 0;
}
/////////////////////////////////
// binary data to hex string
// output must be pre allocated
/////////////////////////////////
/*****************************************
* binary data to hex string
* output must be pre allocated
*****************************************/
char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len) {
const uint8_t *in_end= in + len;
if (len == 0 || in == NULL) {
@ -159,12 +160,12 @@ char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len) {
return out;
}
///////////////////////////////////////////////////////
// fill a preallocated buffer with XOR(str1, str2)
// XOR between 2 equal len strings
// note that XOR(str1, XOR(str1 CONCAT str2)) == str2
// and that XOR(str1, str2) == XOR(str2, str1)
///////////////////////////////////////////////////////
/****************************************************
* fill a preallocated buffer with XOR(str1, str2)
* XOR between 2 equal len strings
* note that XOR(str1, XOR(str1 CONCAT str2)) == str2
* and that XOR(str1, str2) == XOR(str2, str1)
*****************************************************/
void gw_str_xor(uint8_t *output, const uint8_t *input1, const uint8_t *input2, unsigned int len) {
const uint8_t *input1_end = NULL;
input1_end = input1 + len;
@ -175,10 +176,10 @@ void gw_str_xor(uint8_t *output, const uint8_t *input1, const uint8_t *input2, u
*output = '\0';
}
/////////////////////////////////////////////////////////////
// fill a 20 bytes preallocated with SHA1 digest (160 bits)
// for one input on in_len bytes
/////////////////////////////////////////////////////////////
/**********************************************************
* fill a 20 bytes preallocated with SHA1 digest (160 bits)
* for one input on in_len bytes
**********************************************************/
void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out) {
unsigned char hash[SHA_DIGEST_LENGTH];
@ -186,10 +187,10 @@ void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out) {
memcpy(out, hash, SHA_DIGEST_LENGTH);
}
/////////////////////////////////////////////////////////////
// fill 20 bytes preallocated with SHA1 digest (160 bits)
// for two inputs, in_len and in2_len bytes
/////////////////////////////////////////////////////////////
/********************************************************
* fill 20 bytes preallocated with SHA1 digest (160 bits)
* for two inputs, in_len and in2_len bytes
********************************************************/
void gw_sha1_2_str(const uint8_t *in, int in_len, const uint8_t *in2, int in2_len, uint8_t *out) {
SHA_CTX context;
unsigned char hash[SHA_DIGEST_LENGTH];

View File

@ -40,6 +40,9 @@
* 16/07/2013 Massimiliano Pinto Added command type for the queue
* 10/07/2014 Mark Riddoch Addition of hints
* 15/07/2014 Mark Riddoch Added buffer properties
* 03/10/2014 Martin Brampton Pointer arithmetic standard conformity
* Add more buffer handling macros
* Add gwbuf_rtrim (handle chains)
*
* @endverbatim
*/
@ -147,19 +150,25 @@ typedef struct gwbuf {
/*<
* Macros to access the data in the buffers
*/
/*< First valid, uncomsumed byte in the buffer */
/*< First valid, unconsumed byte in the buffer */
#define GWBUF_DATA(b) ((b)->start)
/*< Number of bytes in the individual buffer */
#define GWBUF_LENGTH(b) ((unsigned int)(((uint8_t*)(b)->end) - ((uint8_t*)(b)->start)))
#define GWBUF_LENGTH(b) ((char *)(b)->end - (char *)(b)->start)
/*< Return the byte at offset byte from the start of the unconsumed portion of the buffer */
#define GWBUF_DATA_CHAR(b, byte) (GWBUF_LENGTH(b) < ((byte)+1) ? -1 : *(((char *)(b)->start)+4))
/*< Check that the data in a buffer has the SQL marker*/
#define GWBUF_IS_SQL(b) (0x03 == GWBUF_DATA_CHAR(b,4))
/*< True if all bytes in the buffer have been consumed */
#define GWBUF_EMPTY(b) ((b)->start == (b)->end)
#define GWBUF_EMPTY(b) ((char *)(b)->start >= (char *)(b)->end)
/*< Consume a number of bytes in the buffer */
#define GWBUF_CONSUME(b, bytes) (b)->start = (void*)((uint8_t*)(b)->start + (bytes))
#define GWBUF_CONSUME(b, bytes) ((b)->start = bytes > ((char *)(b)->end - (char *)(b)->start) ? (b)->end : (void *)((char *)(b)->start + (bytes)));
#define GWBUF_RTRIM(b, bytes) (b)->end = (void*)((uint8_t*)(b)->end - (bytes))
#define GWBUF_RTRIM(b, bytes) ((b)->end = bytes > ((char *)(b)->end - (char *)(b)->start) ? (b)->start : (void *)((char *)(b)->end - (bytes)));
#define GWBUF_TYPE(b) (b)->gwbuf_type
/*<
@ -171,6 +180,7 @@ extern GWBUF *gwbuf_clone(GWBUF *buf);
extern GWBUF *gwbuf_append(GWBUF *head, GWBUF *tail);
extern GWBUF *gwbuf_consume(GWBUF *head, unsigned int length);
extern GWBUF *gwbuf_trim(GWBUF *head, unsigned int length);
extern GWBUF *gwbuf_rtrim(GWBUF *head, unsigned int length);
extern unsigned int gwbuf_length(GWBUF *head);
extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len);
extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type);

View File

@ -142,7 +142,7 @@ typedef enum count_spec_t {COUNT_NONE=0, COUNT_ATLEAST, COUNT_EXACT, COUNT_ATMOS
#define SERVICE_STATE_ALLOC 1 /**< The service has been allocated */
#define SERVICE_STATE_STARTED 2 /**< The service has been started */
extern SERVICE *service_alloc(char *, char *);
extern SERVICE *service_alloc(const char *, const char *);
extern int service_free(SERVICE *);
extern SERVICE *service_find(char *);
extern int service_isvalid(SERVICE *);

View File

@ -24,4 +24,8 @@ add_library(topfilter SHARED topfilter.c)
target_link_libraries(topfilter log_manager utils)
install(TARGETS topfilter DESTINATION modules)
add_subdirectory(hint)
add_subdirectory(hint)
if(BUILD_TESTS)
add_subdirectory(test)
endif()

View File

@ -58,6 +58,7 @@
*@endverbatim
* See the individual struct documentations for logging trigger parameters
*/
#include <my_config.h>
#include <stdio.h>
#include <fcntl.h>
#include <filter.h>

View File

@ -0,0 +1,13 @@
aux_source_directory(${CMAKE_SOURCE_DIR}/server/core CORE_ALL)
foreach(VAR ${CORE_ALL})
if(NOT( (${VAR} MATCHES "max[a-z_]*.c") OR (${VAR} MATCHES "gateway.c")))
list(APPEND CORE ${VAR})
endif()
endforeach()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(harness_ui harness_ui.c harness_common.c)
add_executable(harness harness_util.c harness_common.c ${CORE})
target_link_libraries(harness_ui fullcore log_manager utils)
target_link_libraries(harness fullcore)
add_test(TestHintfilter /bin/sh -c "MAXSCALE_HOME=\"${CMAKE_BINARY_DIR}\" ${CMAKE_CURRENT_BINARY_DIR}/harness -i ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.input -o ${CMAKE_CURRENT_BINARY_DIR}/hint_testing.output -c ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.cnf -t 1 -s 1 && diff ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.expected ${CMAKE_CURRENT_BINARY_DIR}/hint_testing.output;exit $?")

View File

@ -0,0 +1,80 @@
# This file is distributed as part of MaxScale form 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
include ../../../../build_gateway.inc
LOGPATH := $(ROOT_PATH)/log_manager
UTILSPATH := $(ROOT_PATH)/utils
QCLASSPATH := $(ROOT_PATH)/query_classifier
COREPATH := $(ROOT_PATH)/server/core
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include \
-I$(LOGPATH) -I$(UTILSPATH) -I./ -I$(ROOT_PATH)/server/inih -I$(QCLASSPATH) \
$(MYSQL_HEADERS) -Wall -g
include ../../../../makefile.inc
LDFLAGS=-rdynamic -L$(LOGPATH) -L$(UTILSPATH) -L$(EMBEDDED_LIB) \
-Wl,-rpath,$(DEST)/lib \
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) \
-Wl,-rpath,$(EMBEDDED_LIB)
SRCS=harness_util.c harness_common.c
OBJ=$(SRCS:.c=.o)
COREOBJ=$(COREPATH)/load_utils.o $(COREPATH)/dcb.o $(COREPATH)/utils.o \
$(COREPATH)/gw_utils.o $(COREPATH)/buffer.o $(COREPATH)/poll.o \
$(COREPATH)/spinlock.o $(COREPATH)/gwbitmask.o $(COREPATH)/session.o \
$(COREPATH)/atomic.o $(COREPATH)/hashtable.o $(COREPATH)/filter.o $(COREPATH)/modutil.o $(ROOT_PATH)/server/inih/ini.o \
$(COREPATH)/hint.o $(COREPATH)/config.o $(COREPATH)/service.o $(COREPATH)/server.o $(COREPATH)/monitor.o $(COREPATH)/housekeeper.o $(COREPATH)/adminusers.o $(COREPATH)/dbusers.o $(COREPATH)/thread.o $(COREPATH)/users.o $(COREPATH)/secrets.o
LIBS= $(UTILSPATH)/skygw_utils.o -lssl -pthread -llog_manager -lmysqld -ldl -lcrypto -lcrypt -lm
MODULES := $(wildcard ../*.so)
all: build
build:$(OBJ)
$(CC) $(OBJ) $(COREOBJ) $(LDFLAGS) $(LIBS) -o harness
$(MAKE) -C ../
cp ../*.so ./
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f *.o
rm -f *.so
rm -f harness
cleantests:clean
rm *.output
buildtests:build
testall:
$(MAKE) cleantests
$(MAKE) buildtests
$(MAKE) runtests
runtests:
@echo ""
@echo "-------------------------------"
@echo "$(shell date)"
@echo "Test Filter harness"
@echo "-------------------------------"
@echo "Testing hints... "
@./hint_tests.sh
@echo ""
documentation:
doxygen doxygen.conf

View File

@ -0,0 +1,20 @@
Filter Test Harness
For a more detailed description of the filter harness, either generate the documentation or read the harness.h file.
Running the program without arguments enters the interactive mode. Type 'help' for a list of all commands.
The default values for threads and sessions are stored in the 'harness.cnf' file
Mandatory parameters for the command line mode are -c and -i.
Parameters for the command line:
-h Display this information
-c Path to the MaxScale configuration file to parse for filters
-i Name of the input file for buffers
-o Name of the output file for results
-q Suppress printing to stdout
-t Number of threads
-s Number of sessions
-d Routing delay

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
threads=2
sessions=4

View File

@ -0,0 +1,362 @@
#ifndef _FILTER_HARNESS_H
#define _FILTER_HARNESS_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
*/
/**
* @mainpage
* Test harness for independent testing of filters
*
* A test harness that feeds a GWBUF to a chain of filters and prints the results
* either into a file or to the standard output.
*
* The contents of the GWBUF and the filter parameters are either manually set through
* the command line or read from a file.
* @verbatim
* Options for the configuration file 'harness.cnf'':
*
* threads Number of threads to use when routing buffers
* sessions Number of sessions
*
* Options for the command line:
*
* -c Path to the MaxScale configuration file to parse for filters
* -i Name of the input file for buffers
* -o Name of the output file for results
* -q Suppress printing to stdout
* -s Number of sessions
* -t Number of threads
* -d Routing delay, in milliseconds
*
*
* Revision History
*
* Date Who Description
* 01/07/14 Markus Makela Initial implementation
*
* @endverbatim
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <filter.h>
#include <buffer.h>
#include <modules.h>
#include <modutil.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <atomic.h>
#include <ini.h>
#include <hint.h>
#include <modutil.h>
/**
* A single name-value pair and a link to the next item in the
* configuration.
*/
typedef struct CONFIG_ITEM_T
{
char* name;
char* value;
struct CONFIG_ITEM_T* next;
}CONFIG_ITEM;
/**
*A simplified version of a MaxScale configuration context used to load filters
* and their options.
*/
typedef struct CONFIG_T
{
char* section;
CONFIG_ITEM* item;
struct CONFIG_T* next;
}CONFIG;
/**
*A structure that holds all the necessary information to emulate a working
* filter environment.
*/
struct FILTERCHAIN_T
{
FILTER* filter; /**An instance of a particular filter*/
FILTER_OBJECT* instance; /**Dynamically loaded module*/
SESSION** session; /**A list of sessions*/
DOWNSTREAM** down; /** A list of next filters downstreams*/
UPSTREAM** up; /** A list of next filters upstreams*/
char* name; /**Module name*/
struct FILTERCHAIN_T* next;
};
typedef struct FILTERCHAIN_T FILTERCHAIN;
/**
* A container for all the filters, query buffers and user specified parameters
*/
typedef struct
{
int running;
int verbose; /**Whether to print to stdout*/
int infile; /**A file where the queries are loaded from*/
int error;
char* mod_dir; /**Module directory absolute path*/
char* infile_name;
int outfile; /**A file where the output of the filters is logged*/
char* outfile_name;
FILTERCHAIN* head; /**The head of the filter chain*/
FILTERCHAIN* tail; /**The tail of the filter chain*/
GWBUF** buffer; /**Buffers that are fed to the filter chain*/
int buffer_count;
int session_count;
DOWNSTREAM dummyrouter; /**Dummy downstream router for data extraction*/
UPSTREAM dummyclient; /**Dummy downstream router for data extraction*/
CONFIG* conf; /**Configurations loaded from a file*/
pthread_mutex_t work_mtx; /**Mutex for buffer routing*/
int buff_ind; /**Index of first unrouted buffer*/
int sess_ind;/**Index of first unused session*/
int last_ind; /**Index of last used session*/
pthread_t* thrpool;
int thrcount; /**Number of active threads*/
int rt_delay; /**Delay each thread waits after routing a query, in milliseconds*/
}HARNESS_INSTANCE;
static HARNESS_INSTANCE instance;
/**
*A list of available actions.
*/
typedef enum
{
UNDEFINED,
RUNFILTERS,
LOAD_FILTER,
DELETE_FILTER,
LOAD_CONFIG,
SET_INFILE,
SET_OUTFILE,
THR_COUNT,
SESS_COUNT,
OK,
QUIT
} operation_t;
typedef enum
{
PACKET_OK,
PACKET_ERROR,
PACKET_RESULT_SET
} packet_t;
typedef packet_t PACKET;
/**
* Initialize the static instance.
*/
int harness_init(int argc,char** argv);
/**
* Frees all the query buffers
*/
void free_buffers();
/**
* Frees all the loaded filters
*/
void free_filters();
/**
* Converts the passed string into an operation
*
* @param tk The string to parse
* @return The operation to perform or UNDEFINED, if parsing failed
*/
operation_t user_input(char* tk);
/**
*Prints a list of available commands.
*/
void print_help();
/**
* Prints the current status of loaded filters and queries, number of threads
* and sessions and possible output files.
*/
void print_status();
/**
*Opens a file for reading and/or writing with adequate permissions.
*
* @param str Path to file
* @param write Non-zero for write permissions, zero for read only.
* @return The assigned file descriptor or -1 in case an error occurred
*/
int open_file(char* str, unsigned int write);
/**
* Reads filter parameters from the command line as name-value pairs.
*
*@param paramc The number of parameters read is assigned to this variable
*@return The newly allocated list of parameters with the last one being NULL
*/
FILTER_PARAMETER** read_params(int* paramc);
/**
* Dummy endpoint for the queries of the filter chain
*
* Prints and logs the contents of the GWBUF after it has passed through all the filters.
* The packet is handled as a COM_QUERY packet and the packet header is not printed.
*/
int routeQuery(void* instance, void* session, GWBUF* queue);
/**
* Dummy endpoint for the replies of the filter chain
*
* Prints and logs the contents of the GWBUF after it has passed through all the filters.
* The packet is handled as a OK packet with no message and the packet header is not printed.
*/
int clientReply(void* ins, void* session, GWBUF* queue);
/**
*Manual input if a query string
*
* Reads a single query from the standard input and inserts it into a GWBUF.
*/
void manual_query();
/**
*Loads the file pointed by @{instance.infile}
* @return Zero if successful, non-zero if an error occurred
*/
int load_query();
/**
* Handler for the INI file parser that builds a linked list
* of all the sections and their name-value pairs.
* @param user Current configuration.
* @param section Name of the section.
* @param name Name of the item.
* @param value Value of the item.
* @return Non-zero on success, zero in case parsing is finished.
* @see load_config()
*/
int handler(void* user, const char* section, const char* name,const char* value);
/**
* Removes all non-filter modules from the configuration
*
* @param conf A pointer to a configuration struct
* @return The stripped version of the configuration
* @see load_config()
*/
CONFIG* process_config(CONFIG* conf);
/**
* Loads the filter module and link it to the filter chain
*
* The downstream is set to point to the current head of the filter chain
*
* @param str Name of the filter module
* @return Pointer to the newly initialized FILTER_CHAIN element or NULL in case module loading failed
* @see load_filter()
*/
FILTERCHAIN* load_filter_module(char* str);
/**
* Loads a new instance of a filter and starts a new session.
* This function assumes that the filter module is already loaded.
* Passing NULL as the CONFIG parameter causes the parameters to be
* read from the command line one at a time.
*
* @param fc The FILTERCHAIN where the new instance and session are created
* @param cnf A configuration read from a file
* @return 1 on success, 0 in case an error occurred
* @see load_filter_module()
*/
int load_filter(FILTERCHAIN* fc, CONFIG* cnf);
/**
* Reads a MaxScale configuration (or any INI file using MaxScale notation) file and loads only the filter modules in it.
*
* @param fname Configuration file name
* @return Non-zero on success, zero in case an error occurred.
*/
int load_config(char* fname);
/**
* Initializes the indexes used while routing buffers and prints the progress
* of the routing process.
*/
void route_buffers();
/**
* Worker function for threads.
* Routes a query buffer if there are unrouted buffers left.
*
* @param thr_num ID number of the thread
*/
void work_buffer(void* thr_num);
/**
* Generates a fake packet used to emulate a response from the backend.
*
* Current implementation only works with PACKET_OK and the packet has no message.
* The caller is responsible for freeing the allocated memory by calling gwbuf_free().
* @param pkt The packet type
* @return The newly generated packet or NULL if an error occurred
*/
GWBUF* gen_packet(PACKET pkt);
/**
* Process the command line parameters and the harness configuration file.
*
* Reads the contents of the 'harness.cnf' file and command line parameters
* and parses them. Options are interpreted accoding to the following table.
* If no command line arguments are given, interactive mode is used.
*
* By default if no input file is given or no configuration file or specific
* filters are given, but other options are, the program exits with 0.
*
* Options for the configuration file 'harness.cnf'':
*
* threads Number of threads to use when routing buffers
* sessions Number of sessions
*
* Options for the command line:
*
* -h Display this information
* -c Path to the MaxScale configuration file to parse for filters
* -i Name of the input file for buffers
* -o Name of the output file for results
* -q Suppress printing to stdout
* -t Number of threads
* -s Number of sessions
* -d Routing delay
*
* @param argc Number of arguments
* @param argv List of argument strings
* @return 1 if successful, 0 if no input file, configuration file or specific
* filters are given, but other options are, or if an error occurs.
*/
int process_opts(int argc, char** argv);
#endif

View File

@ -0,0 +1,997 @@
#include <harness.h>
int harness_init(int argc, char** argv){
int i = 0;
if(!(argc == 2 && strcmp(argv[1],"-h") == 0)){
skygw_logmanager_init(0,NULL);
}
if(!(instance.head = calloc(1,sizeof(FILTERCHAIN))))
{
printf("Error: Out of memory\n");
skygw_log_write(LOGFILE_ERROR,"Error: Out of memory\n");
return 1;
}
instance.running = 1;
instance.infile = -1;
instance.outfile = -1;
instance.buff_ind = -1;
instance.last_ind = -1;
instance.sess_ind = -1;
process_opts(argc,argv);
if(!(instance.thrpool = malloc(instance.thrcount * sizeof(pthread_t)))){
printf("Error: Out of memory\n");
skygw_log_write(LOGFILE_ERROR,"Error: Out of memory\n");
return 1;
}
/**Initialize worker threads*/
pthread_mutex_lock(&instance.work_mtx);
size_t thr_num = 1;
for(i = 0;i<instance.thrcount;i++){
pthread_create(&instance.thrpool[i],NULL,(void*)work_buffer,(void*)thr_num++);
}
return 0;
}
void free_filters()
{
int i;
if(instance.head){
while(instance.head->next){
FILTERCHAIN* tmph = instance.head;
instance.head = instance.head->next;
if(tmph->instance){
for(i = 0;i<instance.session_count;i++){
if(tmph->filter && tmph->session[i]){
tmph->instance->freeSession(tmph->filter,tmph->session[i]);
}
}
}
free(tmph->filter);
free(tmph->session);
free(tmph->down);
free(tmph->name);
free(tmph);
}
}
}
void free_buffers()
{
if(instance.buffer){
int i;
for(i = 0;i<instance.buffer_count;i++){
gwbuf_free(instance.buffer[i]);
}
free(instance.buffer);
instance.buffer = NULL;
instance.buffer_count = 0;
}
if(instance.infile >= 0){
close(instance.infile);
free(instance.infile_name);
instance.infile = -1;
}
}
int open_file(char* str, unsigned int write)
{
int mode;
if(write){
mode = O_RDWR|O_CREAT;
}else{
mode = O_RDONLY;
}
return open(str,mode,S_IRWXU|S_IRGRP|S_IXGRP|S_IXOTH);
}
FILTER_PARAMETER** read_params(int* paramc)
{
char buffer[256];
char* token;
char* names[64];
char* values[64];
int pc = 0, do_read = 1, val_len = 0;
int i;
memset(names,0,64);
memset(values,0,64);
printf("Enter filter parametes as <name>=<value>, enter \"done\" to stop.\n");
while(do_read){
memset(buffer,0,256);
printf(">");
fgets(buffer,255,stdin);
if(strcmp("done\n",buffer) == 0){
do_read = 0;
}else{
token = strtok(buffer,"=\n");
if(token!=NULL){
val_len = strcspn(token," \n\0");
if((names[pc] = calloc((val_len + 1),sizeof(char))) != NULL){
memcpy(names[pc],token,val_len);
}
}
token = strtok(NULL,"=\n");
if(token!=NULL){
val_len = strcspn(token," \n\0");
if((values[pc] = calloc((val_len + 1),sizeof(char))) != NULL){
memcpy(values[pc],token,val_len);
}
pc++;
}
}
if(pc >= 64){
do_read = 0;
}
}
FILTER_PARAMETER** params;
if((params = malloc(sizeof(FILTER_PARAMETER*)*(pc+1)))!=NULL){
for(i = 0;i<pc;i++){
params[i] = malloc(sizeof(FILTER_PARAMETER));
if(params[i]){
params[i]->name = strdup(names[i]);
params[i]->value = strdup(values[i]);
}
free(names[i]);
free(values[i]);
}
}
params[pc] = NULL;
*paramc = pc;
return params;
}
int routeQuery(void* ins, void* session, GWBUF* queue)
{
unsigned int buffsz = 0;
char *qstr;
buffsz = (char*)queue->end - ((char*)queue->start + 5);
if(queue->hint){
buffsz += 40;
if(queue->hint->data){
buffsz += strnlen(queue->hint->data,1024);
}
if(queue->hint->value){
buffsz += strnlen(queue->hint->value,1024);
}
}
qstr = calloc(buffsz + 1,sizeof(char));
if(qstr){
memcpy(qstr,queue->start + 5,buffsz);
if(queue->hint){
char *ptr = qstr + strlen(qstr);
switch(queue->hint->type){
case HINT_ROUTE_TO_MASTER:
sprintf(ptr,"|HINT_ROUTE_TO_MASTER");
break;
case HINT_ROUTE_TO_SLAVE:
sprintf(ptr,"|HINT_ROUTE_TO_SLAVE");
break;
case HINT_ROUTE_TO_NAMED_SERVER:
sprintf(ptr,"|HINT_ROUTE_TO_NAMED_SERVER");
break;
case HINT_ROUTE_TO_UPTODATE_SERVER:
sprintf(ptr,"|HINT_ROUTE_TO_UPTODATE_SERVER");
break;
case HINT_ROUTE_TO_ALL:
sprintf(ptr,"|HINT_ROUTE_TO_ALL");
break;
case HINT_PARAMETER:
sprintf(ptr,"|HINT_PARAMETER");
break;
default:
sprintf(ptr,"|HINT_UNDEFINED");
break;
}
ptr = qstr + strlen(qstr);
if(queue->hint->data){
sprintf(ptr,"|%s",(char*)queue->hint->data);
ptr = qstr + strlen(qstr);
}
if(queue->hint->value){
sprintf(ptr,"|%s",(char*)queue->hint->value);
ptr = qstr + strlen(qstr);
}
}
}else{
printf("Error: cannot allocate enough memory.\n");
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
return 0;
}
if(instance.verbose){
printf("Query endpoint: %s\n", qstr);
}
if(instance.outfile>=0){
write(instance.outfile,qstr,strlen(qstr));
write(instance.outfile,"\n",1);
}
free(qstr);
return 1;
}
int clientReply(void* ins, void* session, GWBUF* queue)
{
if(instance.verbose){
pthread_mutex_lock(&instance.work_mtx);
unsigned char* ptr = (unsigned char*)queue->start;
unsigned int i,pktsize = 4 + ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
printf("Reply endpoint: ");
for(i = 0;i<pktsize;i++){
printf("%.2x ",*ptr++);
}
printf("\n");
pthread_mutex_unlock(&instance.work_mtx);
}
if(instance.outfile>=0){
int qlen = queue->end - queue->start;
write(instance.outfile,"Reply: ",strlen("Reply: "));
write(instance.outfile,queue->start,qlen);
write(instance.outfile,"\n",1);
}
return 1;
}
int load_query()
{
char** query_list;
char* buff;
char rc;
int i, qcount = 0, qbuff_sz = 10, buff_sz = 2048;
int offset = 0;
unsigned int qlen = 0;
if((buff = calloc(buff_sz,sizeof(char))) == NULL ||
(query_list = calloc(qbuff_sz,sizeof(char*))) == NULL){
printf("Error: cannot allocate enough memory.\n");
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
return 1;
}
while(read(instance.infile,&rc,1)){
if(rc != '\n' && rc != '\0'){
if(offset >= buff_sz){
char* tmp = malloc(sizeof(char)*2*buff_sz);
if(tmp){
memcpy(tmp,buff,buff_sz);
free(buff);
buff = tmp;
buff_sz *= 2;
}else{
printf("Error: cannot allocate enough memory.\n");
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
free(buff);
return 1;
}
}
buff[offset++] = rc;
}else{
if(qcount >= qbuff_sz){
char** tmpcl = malloc(sizeof(char*) * (qcount * 2 + 1));
if(!tmpcl){
printf("Error: cannot allocate enough memory.\n");
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
return 1;
}
for(i = 0;i < qbuff_sz;i++){
tmpcl[i] = query_list[i];
}
free(query_list);
query_list = tmpcl;
qbuff_sz = qcount * 2 + 1;
}
query_list[qcount] = malloc(sizeof(char)*(offset + 1));
memcpy(query_list[qcount],buff,offset);
query_list[qcount][offset] = '\0';
offset = 0;
qcount++;
}
}
GWBUF** tmpbff = malloc(sizeof(GWBUF*)*(qcount + 1));
if(tmpbff){
for(i = 0;i<qcount;i++){
tmpbff[i] = gwbuf_alloc(strnlen(query_list[i],buff_sz) + 6);
gwbuf_set_type(tmpbff[i],GWBUF_TYPE_MYSQL);
memcpy(tmpbff[i]->sbuf->data + 5,query_list[i],strnlen(query_list[i],buff_sz));
qlen = strnlen(query_list[i],buff_sz);
tmpbff[i]->sbuf->data[0] = qlen;
tmpbff[i]->sbuf->data[1] = (qlen << 8);
tmpbff[i]->sbuf->data[2] = (qlen << 16);
tmpbff[i]->sbuf->data[3] = 0x00;
tmpbff[i]->sbuf->data[4] = 0x03;
}
tmpbff[qcount] = NULL;
instance.buffer = tmpbff;
}else{
printf("Error: cannot allocate enough memory for buffers.\n");
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory for buffers.\n");
free_buffers();
return 1;
}
if(qcount < 1){
return 1;
}
instance.buffer_count = qcount;
return 0;
}
int handler(void* user, const char* section, const char* name,
const char* value)
{
CONFIG* conf = instance.conf;
if(conf == NULL){/**No sections handled*/
if((conf = malloc(sizeof(CONFIG))) &&
(conf->item = malloc(sizeof(CONFIG_ITEM)))){
conf->section = strdup(section);
conf->item->name = strdup(name);
conf->item->value = strdup(value);
conf->item->next = NULL;
conf->next = NULL;
}
}else{
CONFIG* iter = instance.conf;
/**Finds the matching section*/
while(iter){
if(strcmp(iter->section,section) == 0){
CONFIG_ITEM* item = malloc(sizeof(CONFIG_ITEM));
if(item){
item->name = strdup(name);
item->value = strdup(value);
item->next = iter->item;
iter->item = item;
break;
}
}else{
iter = iter->next;
}
}
/**Section not found, creating a new one*/
if(iter == NULL){
CONFIG* nxt = malloc(sizeof(CONFIG));
if(nxt && (nxt->item = malloc(sizeof(CONFIG_ITEM)))){
nxt->section = strdup(section);
nxt->item->name = strdup(name);
nxt->item->value = strdup(value);
nxt->item->next = NULL;
nxt->next = conf;
conf = nxt;
}
}
}
instance.conf = conf;
return 1;
}
CONFIG* process_config(CONFIG* conf)
{
CONFIG* tmp;
CONFIG* tail = conf;
CONFIG* head = NULL;
CONFIG_ITEM* item;
while(tail){
item = tail->item;
while(item){
if(strcmp("type",item->name) == 0 &&
strcmp("filter",item->value) == 0){
tmp = tail->next;
tail->next = head;
head = tail;
tail = tmp;
break;
}else{
item = item->next;
}
}
if(item == NULL){
tail = tail->next;
}
}
return head;
}
int load_config( char* fname)
{
CONFIG* iter;
CONFIG_ITEM* item;
int config_ok = 1;
free_filters();
if(ini_parse(fname,handler,instance.conf) < 0){
printf("Error parsing configuration file!\n");
skygw_log_write(LOGFILE_ERROR,"Error parsing configuration file!\n");
config_ok = 0;
goto cleanup;
}
if(instance.verbose){
printf("Configuration loaded from %s\n\n",fname);
}
if(instance.conf == NULL){
printf("Nothing valid was read from the file.\n");
skygw_log_write(LOGFILE_MESSAGE,"Nothing valid was read from the file.\n");
config_ok = 0;
goto cleanup;
}
instance.conf = process_config(instance.conf);
if(instance.conf){
if(instance.verbose){
printf("Modules Loaded:\n");
}
iter = instance.conf;
}else{
printf("No filters found in the configuration file.\n");
skygw_log_write(LOGFILE_MESSAGE,"No filters found in the configuration file.\n");
config_ok = 0;
goto cleanup;
}
while(iter){
item = iter->item;
while(item){
if(!strcmp("module",item->name)){
if(instance.mod_dir){
char* modstr = malloc(sizeof(char)*(strlen(instance.mod_dir) + strlen(item->value) + 1));
strcpy(modstr,instance.mod_dir);
strcat(modstr,"/");
strcat(modstr,item->value);
instance.head = load_filter_module(modstr);
free(modstr);
}else{
instance.head = load_filter_module(item->value);
}
if(!instance.head || !load_filter(instance.head,instance.conf)){
printf("Error creating filter instance!\nModule: %s\n",item->value);
skygw_log_write(LOGFILE_ERROR,"Error creating filter instance!\nModule: %s\n",item->value);
config_ok = 0;
goto cleanup;
}else{
if(instance.verbose){
printf("\t%s\n",iter->section);
}
}
}
item = item->next;
}
iter = iter->next;
}
while(instance.conf){
item = instance.conf->item;
while(item){
item = instance.conf->item;
instance.conf->item = instance.conf->item->next;
free(item->name);
free(item->value);
free(item);
item = instance.conf->item;
}
instance.conf = instance.conf->next;
}
cleanup:
while(instance.conf){
iter = instance.conf;
instance.conf = instance.conf->next;
item = iter->item;
while(item){
free(item->name);
free(item->value);
free(item);
iter->item = iter->item->next;
item = iter->item;
}
free(iter);
}
instance.conf = NULL;
return config_ok;
}
int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
{
FILTER_PARAMETER** fparams;
int i, paramc = -1;
if(cnf == NULL){
fparams = read_params(&paramc);
}else{
CONFIG* iter = cnf;
CONFIG_ITEM* item;
while(iter){
paramc = -1;
item = iter->item;
while(item){
/**Matching configuration found*/
if(!strcmp(item->name,"module") && !strcmp(item->value,fc->name)){
paramc = 0;
item = iter->item;
while(item){
if(strcmp(item->name,"module") && strcmp(item->name,"type")){
paramc++;
}
item = item->next;
}
item = iter->item;
fparams = calloc((paramc + 1),sizeof(FILTER_PARAMETER*));
if(fparams){
int i = 0;
while(item){
if(strcmp(item->name,"module") != 0 &&
strcmp(item->name,"type") != 0){
fparams[i] = malloc(sizeof(FILTER_PARAMETER));
if(fparams[i]){
fparams[i]->name = strdup(item->name);
fparams[i]->value = strdup(item->value);
i++;
}
}
item = item->next;
}
}
}
if(paramc > -1){
break;
}else{
item = item->next;
}
}
if(paramc > -1){
break;
}else{
iter = iter->next;
}
}
}
int sess_err = 0;
if(fc && fc->instance){
fc->filter = (FILTER*)fc->instance->createInstance(NULL,fparams);
for(i = 0;i<instance.session_count;i++){
if((fc->session[i] = fc->instance->newSession(fc->filter, fc->session[i])) &&
(fc->down[i] = calloc(1,sizeof(DOWNSTREAM))) &&
(fc->up[i] = calloc(1,sizeof(UPSTREAM)))){
fc->up[i]->session = NULL;
fc->up[i]->instance = NULL;
fc->up[i]->clientReply = (void*)clientReply;
if(fc->instance->setUpstream && fc->instance->clientReply){
fc->instance->setUpstream(fc->filter, fc->session[i], fc->up[i]);
}else{
skygw_log_write(LOGFILE_MESSAGE,
"Warning: The filter %s does not support client relies.\n",fc->name);
}
if(fc->next && fc->next->next){
fc->down[i]->routeQuery = (void*)fc->next->instance->routeQuery;
fc->down[i]->session = fc->next->session[i];
fc->down[i]->instance = fc->next->filter;
fc->instance->setDownstream(fc->filter, fc->session[i], fc->down[i]);
fc->next->up[i]->clientReply = (void*)fc->instance->clientReply;
fc->next->up[i]->session = fc->session[i];
fc->next->up[i]->instance = fc->filter;
if(fc->instance->setUpstream && fc->instance->clientReply){
fc->next->instance->setUpstream(fc->next->filter,fc->next->session[i],fc->next->up[i]);
}
}else{ /**The dummy router is the next one*/
fc->down[i]->routeQuery = (void*)routeQuery;
fc->down[i]->session = NULL;
fc->down[i]->instance = NULL;
fc->instance->setDownstream(fc->filter, fc->session[i], fc->down[i]);
}
}
if(!fc->session[i] || !fc->down[i] || !fc->up[i]){
sess_err = 1;
break;
}
}
if(sess_err){
for(i = 0;i<instance.session_count;i++){
if(fc->filter && fc->session[i]){
fc->instance->freeSession(fc->filter, fc->session[i]);
}
free(fc->down[i]);
}
free(fc->session);
free(fc->down);
free(fc->name);
free(fc);
}
}
if(cnf){
int x;
for(x = 0;x<paramc;x++){
free(fparams[x]->name);
free(fparams[x]->value);
}
free(fparams);
}
return sess_err ? 0 : 1;
}
FILTERCHAIN* load_filter_module(char* str)
{
FILTERCHAIN* flt_ptr = NULL;
if((flt_ptr = calloc(1,sizeof(FILTERCHAIN))) != NULL &&
(flt_ptr->session = calloc(instance.session_count,sizeof(SESSION*))) != NULL &&
(flt_ptr->down = calloc(instance.session_count,sizeof(DOWNSTREAM*))) != NULL &&
(flt_ptr->up = calloc(instance.session_count,sizeof(UPSTREAM*))) != NULL){
flt_ptr->next = instance.head;
}
if((flt_ptr->instance = (FILTER_OBJECT*)load_module(str, MODULE_FILTER)) == NULL)
{
printf("Error: Module loading failed: %s\n",str);
skygw_log_write(LOGFILE_ERROR,"Error: Module loading failed: %s\n",str);
free(flt_ptr->down);
free(flt_ptr);
return NULL;
}
flt_ptr->name = strdup(str);
return flt_ptr;
}
void route_buffers()
{
if(instance.buffer_count > 0){
float tprg = 0.f, bprg = 0.f, trig = 0.f,
fin = instance.buffer_count*instance.session_count,
step = (fin/50.f)/fin;
FILTERCHAIN* fc = instance.head;
while(fc->next->next){
fc = fc->next;
}
instance.tail = fc;
instance.buff_ind = 0;
instance.sess_ind = 0;
instance.last_ind = 0;
printf("Routing queries...\n");
if(!instance.verbose){
printf("%s","|0%");
float f;
for(f = 0.f;f<1.f - step*7;f += step){
printf(" ");
}
printf("%s\n","100%|");
write(1,"|",1);
}
while(instance.buff_ind < instance.buffer_count){
pthread_mutex_unlock(&instance.work_mtx);
while(instance.last_ind < instance.session_count){
tprg = ((bprg + (float)instance.last_ind)/fin);
if(!instance.verbose){
if(tprg >= trig){
write(1,"-",1);
trig += step;
}
}
usleep(100);
}
pthread_mutex_lock(&instance.work_mtx);
instance.buff_ind++;
bprg += instance.last_ind;
instance.sess_ind = 0;
instance.last_ind = 0;
}
if(!instance.verbose){
write(1,"|\n",2);
}
printf("Queries routed.\n");
}
}
void work_buffer(void* thr_num)
{
unsigned int index = instance.session_count;
GWBUF* fake_ok = gen_packet(PACKET_OK);
while(instance.running){
pthread_mutex_lock(&instance.work_mtx);
pthread_mutex_unlock(&instance.work_mtx);
index = atomic_add(&instance.sess_ind,1);
if(instance.running &&
index < instance.session_count &&
instance.buff_ind < instance.buffer_count)
{
instance.head->instance->routeQuery(instance.head->filter,
instance.head->session[index],
instance.buffer[instance.buff_ind]);
if(instance.tail->instance->clientReply){
instance.tail->instance->clientReply(instance.tail->filter,
instance.tail->session[index],
fake_ok);
}
atomic_add(&instance.last_ind,1);
usleep(1000*instance.rt_delay);
}
}
gwbuf_free(fake_ok);
}
GWBUF* gen_packet(PACKET pkt)
{
unsigned int psize = 0;
GWBUF* buff = NULL;
unsigned char* ptr;
switch(pkt){
case PACKET_OK:
psize = 11;
break;
default:
break;
}
if(psize > 0){
buff = gwbuf_alloc(psize);
ptr = (unsigned char*)buff->start;
switch(pkt){
case PACKET_OK:
ptr[0] = 7; /**Packet size*/
ptr[1] = 0;
ptr[2] = 0;
ptr[3] = 1; /**sequence_id*/
ptr[4] = 0; /**OK header*/
ptr[5] = 0; /**affected_rows*/
ptr[6] = 0; /**last_insert_id*/
ptr[7] = 0; /**status_flags*/
ptr[8] = 0;
ptr[9] = 0; /**warnings*/
ptr[10] = 0;
break;
default:
break;
}
}
return buff;
}
int process_opts(int argc, char** argv)
{
int fd = open_file("harness.cnf",1), buffsize = 1024;
int rd,fsize;
char *buff = calloc(buffsize,sizeof(char)), *tok = NULL;
/**Parse 'harness.cnf' file*/
fsize = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
instance.thrcount = 1;
instance.session_count = 1;
read(fd,buff,fsize);
tok = strtok(buff,"=");
while(tok){
if(!strcmp(tok,"threads")){
tok = strtok(NULL,"\n\0");
instance.thrcount = strtol(tok,0,0);
}else if(!strcmp(tok,"sessions")){
tok = strtok(NULL,"\n\0");
instance.session_count = strtol(tok,0,0);
}
tok = strtok(NULL,"=");
}
free(buff);
instance.verbose = 1;
if(argc < 2){
return 1;
}
char* conf_name = NULL;
while((rd = getopt(argc,argv,"m:c:i:o:s:t:d:qh")) > 0){
switch(rd){
case 'o':
instance.outfile = open_file(optarg,1);
printf("Output is written to: %s\n",optarg);
break;
case 'i':
instance.infile = open_file(optarg,0);
printf("Input is read from: %s\n",optarg);
break;
case 'c':
conf_name = strdup(optarg);
break;
case 'q':
instance.verbose = 0;
break;
case 's':
instance.session_count = atoi(optarg);
printf("Sessions: %i ",instance.session_count);
break;
case 't':
instance.thrcount = atoi(optarg);
printf("Threads: %i ",instance.thrcount);
break;
case 'd':
instance.rt_delay = atoi(optarg);
printf("Routing delay: %i ",instance.rt_delay);
break;
case 'h':
printf(
"\nOptions for the configuration file 'harness.cnf'':\n\n"
"\tthreads\tNumber of threads to use when routing buffers\n"
"\tsessions\tNumber of sessions\n\n"
"Options for the command line:\n\n"
"\t-h\tDisplay this information\n"
"\t-c\tPath to the MaxScale configuration file to parse for filters\n"
"\t-i\tName of the input file for buffers\n"
"\t-o\tName of the output file for results\n"
"\t-q\tSuppress printing to stdout\n"
"\t-s\tNumber of sessions\n"
"\t-t\tNumber of threads\n"
"\t-d\tRouting delay\n");
break;
case 'm':
instance.mod_dir = strdup(optarg);
printf("Module directory: %s",optarg);
break;
default:
break;
}
}
printf("\n");
if(conf_name && load_config(conf_name)){
load_query();
}else{
instance.running = 0;
}
return 0;
}

View File

@ -0,0 +1,403 @@
#include <harness.h>
int main(int argc, char** argv){
int i;
char buffer[256];
char* tk;
FILTERCHAIN* tmp_chn;
FILTERCHAIN* del_chn;
if(harness_init(argc,argv)){
printf("Error: Initialization failed.\n");
skygw_log_write(LOGFILE_ERROR,"Error: Initialization failed.\n");
skygw_logmanager_done();
skygw_logmanager_exit();
return 1;
}
if(instance.verbose){
printf("\n\n\tFilter Test Harness\n\n");
}
while(instance.running){
printf("Harness> ");
memset(buffer,0,256);
fgets(buffer,256,stdin);
tk = strtok(buffer," \n");
switch(user_input(tk))
{
case RUNFILTERS:
if(instance.head->next == NULL){
printf("No filters loaded.\n");
break;
}
if(instance.buffer == NULL){
if(instance.infile<0){
manual_query();
}else{
load_query();
}
}
route_buffers();
break;
case LOAD_FILTER:
tk = strtok(NULL," \n");
tmp_chn = load_filter_module(tk);
if(!tmp_chn || !load_filter(tmp_chn,instance.conf)){
printf("Error creating filter instance.\n");
skygw_log_write(LOGFILE_ERROR,"Error: Error creating filter instance.\n");
}else{
instance.head = tmp_chn;
}
break;
case DELETE_FILTER:
tk = strtok(NULL," \n\0");
tmp_chn = instance.head;
del_chn = instance.head;
if(tk){
if(strcmp(instance.head->name,tk) == 0){
instance.head = instance.head->next;
}else{
while(del_chn->next){
if(strcmp(del_chn->name,tk) == 0){
tmp_chn->next = del_chn->next;
break;
}else{
tmp_chn = del_chn;
del_chn = del_chn->next;
}
}
}
if(del_chn && del_chn->next){
printf("Deleted %s.\n",del_chn->name);
if(del_chn->instance){
del_chn->instance->freeSession(del_chn->filter,del_chn->session);
}
free(del_chn->filter);
free(del_chn->down);
free(del_chn->name);
free(del_chn);
}else{
printf("No matching filter found.\n");
}
}
break;
case LOAD_CONFIG:
tk = strtok(NULL," \n\0");
if(!load_config(tk)){
free_filters();
}
break;
case SET_INFILE:
tk = strtok(NULL," \n\0");
if(instance.infile >= 0){
close(instance.infile);
free(instance.infile_name);
}
if(tk!= NULL){
free_buffers();
instance.infile = open_file(tk,0);
if(instance.infile >= 0){
load_query();
instance.infile_name = strdup(tk);
if(instance.verbose){
printf("Loaded %d queries from file '%s'\n",instance.buffer_count,instance.infile_name);
}
}
}else{
instance.infile = -1;
printf("Queries are read from: command line\n");
}
break;
case SET_OUTFILE:
tk = strtok(NULL," \n\0");
if(instance.outfile >= 0){
close(instance.outfile);
free(instance.outfile_name);
}
if(tk!= NULL){
instance.outfile = open_file(tk,1);
if(instance.outfile >= 0){
instance.outfile_name = strdup(tk);
printf("Output is logged to: %s\n",tk);
}
}else{
instance.outfile = -1;
printf("Output logging disabled.\n");
}
break;
case SESS_COUNT:
tk = strtok(NULL," \n\0");
free_buffers();
free_filters();
instance.session_count = atoi(tk);
printf("Sessions set to: %d\n", instance.session_count);
break;
case THR_COUNT:
instance.running = 0;
pthread_mutex_unlock(&instance.work_mtx);
for(i = 0;i<instance.thrcount;i++){
pthread_join(instance.thrpool[i],NULL);
}
pthread_mutex_lock(&instance.work_mtx);
instance.running = 1;
tk = strtok(NULL," \n\0");
instance.thrcount = atoi(tk);
void* t_thr_pool;
if(!(t_thr_pool = realloc(instance.thrpool,instance.thrcount * sizeof(pthread_t)))){
printf("Error: Out of memory\n");
skygw_log_write(LOGFILE_ERROR,"Error: Out of memory\n");
instance.running = 0;
break;
}
instance.thrpool = t_thr_pool;
int thr_num = 1;
for(i = 0;i<instance.thrcount;i++){
pthread_create(&instance.thrpool[i],
NULL,
(void*)work_buffer,
(void*)thr_num++);
}
printf("Threads set to: %d\n", instance.thrcount);
break;
case QUIT:
instance.running = 0;
pthread_mutex_unlock(&instance.work_mtx);
for(i = 0;i<instance.thrcount;i++){
pthread_join(instance.thrpool[i],NULL);
}
break;
case UNDEFINED:
printf("Command not found, enter \"help\" for a list of commands\n");
break;
default:
break;
}
}
if(instance.infile >= 0){
close(instance.infile);
}
if(instance.outfile >= 0){
close(instance.outfile);
}
free_buffers();
free_filters();
skygw_logmanager_done();
skygw_logmanager_exit();
free(instance.head);
return 0;
}
operation_t user_input(char* tk)
{
if(tk){
char cmpbuff[256];
int tklen = strcspn(tk," \n\0");
memset(cmpbuff,0,256);
if(tklen > 0 && tklen < 256){
strncpy(cmpbuff,tk,tklen);
strcat(cmpbuff,"\0");
if(strcmp(tk,"run")==0 || strcmp(tk,"r")==0){
return RUNFILTERS;
}else if(strcmp(cmpbuff,"add")==0){
return LOAD_FILTER;
}else if(strcmp(cmpbuff,"delete")==0){
return DELETE_FILTER;
}else if(strcmp(cmpbuff,"clear")==0){
tk = strtok(NULL," \n\0");
if(tk && !strcmp(tk,"queries")){
free_buffers();
printf("Queries cleared.\n");
}else if(tk && !strcmp(tk,"filters")){
printf("Filters cleared.\n");
free_filters();
}else{
printf("All cleared.\n");
free_buffers();
free_filters();
}
return OK;
}else if(strcmp(cmpbuff,"config")==0){
return LOAD_CONFIG;
}else if(strcmp(cmpbuff,"in")==0){
return SET_INFILE;
}else if(strcmp(cmpbuff,"out")==0){
return SET_OUTFILE;
}else if(strcmp(cmpbuff,"exit")==0 || strcmp(cmpbuff,"quit")==0 || strcmp(cmpbuff,"q")==0){
return QUIT;
}else if(strcmp(cmpbuff,"help")==0){
print_help();
return OK;
}else if(strcmp(cmpbuff,"status")==0){
print_status();
return OK;
}else if(strcmp(cmpbuff,"quiet")==0){
instance.verbose = 0;
return OK;
}else if(strcmp(cmpbuff,"verbose")==0){
instance.verbose = 1;
return OK;
}else if(strcmp(cmpbuff,"sessions")==0){
return SESS_COUNT;
}else if(strcmp(cmpbuff,"threads")==0){
return THR_COUNT;
}
}
}
return UNDEFINED;
}
void print_help()
{
printf("\nFilter Test Harness\n\n"
"List of commands:\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n "
"%-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n "
"%-32s%s\n %-32s%s\n"
,"help","Prints this help message."
,"run","Feeds the contents of the buffer to the filter chain."
,"add <filter name>","Loads a filter and appeds it to the end of the chain."
,"delete <filter name>","Deletes a filter."
,"status","Lists all loaded filters and queries"
,"clear","Clears the filter chain."
,"config <file name>","Loads filter configurations from a file."
,"in <file name>","Source file for the SQL statements."
,"out <file name>","Destination file for the SQL statements. Defaults to stdout if no parameters were passed."
,"threads <number>","Sets the amount of threads to use"
,"sessions <number>","How many sessions to create for each filter. This clears all loaded filters."
,"quiet","Print only error messages."
,"verbose","Print everything."
,"exit","Exit the program"
);
}
void manual_query()
{
char query[1024];
unsigned int qlen;
GWBUF** tmpbuf;
free_buffers();
printf("Enter query: ");
fgets(query,1024,stdin);
qlen = strnlen(query, 1024);
if((tmpbuf = malloc(sizeof(GWBUF*)))== NULL){
printf("Error: cannot allocate enough memory.\n");
skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n");
return;
}
instance.buffer = tmpbuf;
instance.buffer_count = 1;
instance.buffer[0] = gwbuf_alloc(qlen + 5);
gwbuf_set_type(instance.buffer[0],GWBUF_TYPE_MYSQL);
memcpy(instance.buffer[0]->sbuf->data + 5,query,qlen);
instance.buffer[0]->sbuf->data[0] = (qlen);
instance.buffer[0]->sbuf->data[1] = (qlen << 8);
instance.buffer[0]->sbuf->data[2] = (qlen << 16);
instance.buffer[0]->sbuf->data[3] = 0x00;
instance.buffer[0]->sbuf->data[4] = 0x03;
}
void print_status()
{
if(instance.head->filter){
printf("Filters currently loaded:\n\n");
FILTERCHAIN* hd = instance.head;
int i = 1;
while(hd->filter){
printf("%d: %s\n", i++, hd->name);
hd = hd->next;
}
}else{
printf("No filters loaded.\n");
}
printf("\n");
if(instance.buffer_count > 0){
printf("%d queries loaded.\n",instance.buffer_count);
}else{
printf("No queries loaded.\n");
}
printf("Using %d threads and %d sessions.\n",instance.thrcount,instance.session_count);
if(instance.infile_name){
printf("Input is read from %s.\n",instance.infile_name);
}
if(instance.outfile_name){
printf("Output is written to %s.\n",instance.outfile_name);
}
}

View File

@ -0,0 +1,14 @@
#include <harness.h>
int main(int argc,char** argv)
{
if(harness_init(argc,argv) || instance.error){
printf("Error: Initialization failed.\n");
skygw_log_write(LOGFILE_ERROR,"Error: Initialization failed.\n");
skygw_logmanager_done();
skygw_logmanager_exit();
return 1;
}
route_buffers();
return 0;
}

View File

@ -0,0 +1,165 @@
#
# Example MaxScale.cnf configuration file
#
#
#
# Number of server threads
# Valid options are:
# threads=<number of threads>
[maxscale]
threads=1
# Define a monitor that can be used to determine the state and role of
# the servers.
#
# Valid options are:
#
# module=<name of module to load>
# servers=<server name>,<server name>,...
# user =<user name - must have slave replication and
# slave client privileges>
# passwd=<password of the above user, plain text currently>
# monitor_interval=<sampling interval in milliseconds,
# default value is 10000>
[MySQL Monitor]
type=monitor
module=mysqlmon
servers=server1,server2,server3,server4
user=maxuser
passwd=maxpwd
# A series of service definition
#
# Valid options are:
#
# router=<name of router module>
# servers=<server name>,<server name>,...
# user=<User to fetch password inforamtion with>
# passwd=<Password of the user, plain text currently>
# enable_root_user=<0 or 1, default is 0>
# version_string=<specific string for server handshake,
# default is the MariaDB embedded library version>
#
# Valid router modules currently are:
# readwritesplit, readconnroute and debugcli
[RW Split Router]
type=service
router=readwritesplit
servers=server1,server2,server3,server4
max_slave_connections=90%
user=maxuser
passwd=maxpwd
#filters=MQ
[RW Split Hint Router]
type=service
router=readwritesplit
servers=server1,server2,server3,server4
max_slave_connections=90%
user=maxuser
passwd=maxpwd
filters=Hint
[Read Connection Router]
type=service
router=readconnroute
router_options=master
servers=server1
user=maxuser
passwd=maxpwd
[HTTPD Router]
type=service
router=testroute
servers=server1,server2,server3
[Debug Interface]
type=service
router=debugcli
[Hint]
type=filter
module=hintfilter
#[MQ]
#type=filter
#module=mqfilter
#exchange=x1
#key=k1
#queue=q1
#port=5673
# Listener definitions for the services
#
# Valid options are:
#
# service=<name of service defined elsewhere>
# protocol=<name of protocol module with which to listen>
# port=<Listening port>
# address=<Address to bind to>
# socket=<Listening socket>
[RW Split Listener]
type=listener
service=RW Split Router
protocol=MySQLClient
port=4006
[RW Split Hint Listener]
type=listener
service=RW Split Hint Router
protocol=MySQLClient
port=4009
[Read Connection Listener]
type=listener
service=Read Connection Router
protocol=MySQLClient
port=4008
#socket=/tmp/readconn.sock
[Debug Listener]
type=listener
service=Debug Interface
protocol=telnetd
port=4442
#address=127.0.0.1
[HTTPD Listener]
type=listener
service=HTTPD Router
protocol=HTTPD
port=6444
# Definition of the servers
[server1]
type=server
address=127.0.0.1
port=3000
protocol=MySQLBackend
[server2]
type=server
address=127.0.0.1
port=3001
protocol=MySQLBackend
[server3]
type=server
address=127.0.0.1
port=3002
protocol=MySQLBackend
[server4]
type=server
address=127.0.0.1
port=3003
protocol=MySQLBackend

View File

@ -0,0 +1,48 @@
select @@server_id; -- maxscale begin route to master|HINT_ROUTE_TO_MASTER
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; -- maxscale route to server server3|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; -- maxscale end
select @@server_id; -- maxscale named1 prepare route to master
select @@server_id; -- maxscale named1 begin|HINT_ROUTE_TO_MASTER
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; -- maxscale route to server server3|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; -- maxscale end
select @@server_id; -- maxscale shorthand1 begin route to server server2|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id;|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id; -- maxscale route to server server3|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id; -- maxscale end
select @@server_id; # maxscale begin route to master|HINT_ROUTE_TO_MASTER
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; # maxscale route to server server3|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; # maxscale end
select @@server_id; # maxscale named2 prepare route to master
select @@server_id; # maxscale named2 begin|HINT_ROUTE_TO_MASTER
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; # maxscale route to server server3|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id; # maxscale end
select @@server_id; # maxscale shorthand2 begin route to server server2|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id;|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id; # maxscale route to server server3|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id; # maxscale end
select @@server_id/* maxscale begin route to master */;|HINT_ROUTE_TO_MASTER
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id/* maxscale route to server server3 */;|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id/* maxscale end */;
select @@server_id/* maxscale named3 prepare route to master */;
select @@server_id/* maxscale named3 begin */;|HINT_ROUTE_TO_MASTER
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id/* maxscale route to server server3 */;|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_MASTER
select @@server_id/* maxscale end */;
select @@server_id/* maxscale shorthand3 begin route to server server2 */; |HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id;|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id/* maxscale route to server server3 */;|HINT_ROUTE_TO_NAMED_SERVER|server3
select @@server_id;|HINT_ROUTE_TO_NAMED_SERVER|server2
select @@server_id/* maxscale end */;

View File

@ -0,0 +1,48 @@
select @@server_id; -- maxscale begin route to master
select @@server_id;
select @@server_id; -- maxscale route to server server3
select @@server_id;
select @@server_id; -- maxscale end
select @@server_id; -- maxscale named1 prepare route to master
select @@server_id; -- maxscale named1 begin
select @@server_id;
select @@server_id; -- maxscale route to server server3
select @@server_id;
select @@server_id; -- maxscale end
select @@server_id; -- maxscale shorthand1 begin route to server server2
select @@server_id;
select @@server_id; -- maxscale route to server server3
select @@server_id;
select @@server_id; -- maxscale end
select @@server_id; # maxscale begin route to master
select @@server_id;
select @@server_id; # maxscale route to server server3
select @@server_id;
select @@server_id; # maxscale end
select @@server_id; # maxscale named2 prepare route to master
select @@server_id; # maxscale named2 begin
select @@server_id;
select @@server_id; # maxscale route to server server3
select @@server_id;
select @@server_id; # maxscale end
select @@server_id; # maxscale shorthand2 begin route to server server2
select @@server_id;
select @@server_id; # maxscale route to server server3
select @@server_id;
select @@server_id; # maxscale end
select @@server_id/* maxscale begin route to master */;
select @@server_id;
select @@server_id/* maxscale route to server server3 */;
select @@server_id;
select @@server_id/* maxscale end */;
select @@server_id/* maxscale named3 prepare route to master */;
select @@server_id/* maxscale named3 begin */;
select @@server_id;
select @@server_id/* maxscale route to server server3 */;
select @@server_id;
select @@server_id/* maxscale end */;
select @@server_id/* maxscale shorthand3 begin route to server server2 */;
select @@server_id;
select @@server_id/* maxscale route to server server3 */;
select @@server_id;
select @@server_id/* maxscale end */;

View File

@ -0,0 +1,9 @@
#! /bin/bash
./harness -q -i hint_testing.input -c hint_testing.cnf -o hint_testing.output -t 1 -s 1 -q &>/dev/null
diff hint_testing.expected hint_testing.output &>/dev/null
if [[ "$?" == "0" ]]
then
echo "PASSED"
else
echo "FAILED"
fi

View File

@ -0,0 +1 @@
SELECT * FROM test_table;

View File

@ -199,6 +199,7 @@ static int gw_read_backend_event(DCB *dcb) {
if (backend_protocol->protocol_auth_state == MYSQL_CONNECTED)
{
/** Read cached backend handshake */
if (gw_read_backend_handshake(backend_protocol) != 0)
{
backend_protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
@ -209,11 +210,13 @@ static int gw_read_backend_event(DCB *dcb) {
"state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
}
else
else
{
/* handshake decoded, send the auth credentials */
/**
* Decode password and send the auth credentials
* to backend.
*/
if (gw_send_authentication_to_backend(
current_session->db,
current_session->user,
@ -227,16 +230,16 @@ static int gw_read_backend_event(DCB *dcb) {
"gw_send_authentication_to_backend "
"fd %d, state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
}
else
backend_protocol->owner_dcb->fd)));
}
else
{
backend_protocol->protocol_auth_state = MYSQL_AUTH_RECV;
}
}
}
spinlock_release(&dcb->authlock);
}
} /*< backend_protocol->protocol_auth_state == MYSQL_CONNECTED */
/*
* Now:
* -- check the authentication reply from backend
@ -266,9 +269,10 @@ static int gw_read_backend_event(DCB *dcb) {
router_instance = session->service->router_instance;
rsession = session->router_session;
if (backend_protocol->protocol_auth_state == MYSQL_AUTH_RECV) {
/*<
* Read backed auth reply
if (backend_protocol->protocol_auth_state == MYSQL_AUTH_RECV)
{
/**
* Read backed's reply to authentication message
*/
receive_rc =
gw_receive_backend_auth(backend_protocol);
@ -283,7 +287,6 @@ static int gw_read_backend_event(DCB *dcb) {
"fd %d, state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -362,8 +365,8 @@ static int gw_read_backend_event(DCB *dcb) {
0,
"Authentication with backend failed. "
"Session will be closed.");
router->handleError(router_instance,
router->handleError(router_instance,
rsession,
errbuf,
dcb,
@ -371,7 +374,6 @@ static int gw_read_backend_event(DCB *dcb) {
&succp);
ss_dassert(!succp);
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_event] "
@ -412,7 +414,7 @@ static int gw_read_backend_event(DCB *dcb) {
}
}
} /* MYSQL_AUTH_RECV || MYSQL_AUTH_FAILED */
spinlock_release(&dcb->authlock);
} /* MYSQL_AUTH_RECV || MYSQL_AUTH_FAILED */
@ -878,6 +880,10 @@ static int gw_create_backend_connection(
goto return_fd;
}
/** Copy client flags to backend protocol */
protocol->client_capabilities =
((MySQLProtocol *)(backend_dcb->session->client->protocol))->client_capabilities;
/*< if succeed, fd > 0, -1 otherwise */
rv = gw_do_connect_to_backend(server->name, server->port, &fd);
/*< Assign protocol with backend_dcb */
@ -1048,6 +1054,10 @@ gw_backend_close(DCB *dcb)
mysql_protocol_done(dcb);
/**
* If session->state is set to STOPPING the client and the session must
* be closed too.
*/
if (session != NULL && session->state == SESSION_STATE_STOPPING)
{
client_dcb = session->client;

View File

@ -68,6 +68,8 @@ int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char*
int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
static int route_by_statement(SESSION *, GWBUF **);
static char* create_auth_fail_str(GWBUF* readbuf, char* hostaddr, char* sha1);
static char* get_username_from_auth(char* ptr, uint8_t* data);
/*
* The "module object" for the mysqld client protocol module.
@ -372,9 +374,9 @@ MySQLSendHandshake(DCB* dcb)
* The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data
* client_capabilitiesa are copied into the dcb->protocol
*
* @param dcb Descriptor Control Block of the client
* @param queue The GWBUF with data from client
* @return 0 for Authentication ok, !=0 for failed autht
* @param dcb Descriptor Control Block of the client
* @param queue The GWBUF with data from client
* @return 0 If succeed, otherwise non-zero value
*
*/
@ -432,12 +434,10 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
&protocol->client_capabilities);
*/
/* now get the user */
strncpy(username, (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23), MYSQL_USER_MAXLEN);
/* the empty username field is not allowed */
if (!strlen(username)) {
username = get_username_from_auth(username, client_auth_packet);
if (username == NULL)
{
return 1;
}
@ -448,9 +448,9 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
if (connect_with_db) {
database = client_data->db;
strncpy(database,
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
strncpy(database,
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
}
/* allocate memory for token only if auth_token_len > 0 */
@ -489,10 +489,80 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
{
dcb->user = strdup(client_data->user);
}
return auth_ret;
}
/**
* Read username from MySQL authentication packet.
*
* @param ptr address where to write the result or NULL if memory
* is allocated here.
* @param data Address of MySQL packet.
*
* @return Pointer to a copy of the username. NULL if memory allocation
* failed or if username was empty.
*/
static char* get_username_from_auth(
char* ptr,
uint8_t* data)
{
char* first_letter;
char* rval;
first_letter = (char *)(data + 4 + 4 + 4 + 1 + 23);
if (first_letter == '\0')
{
rval = NULL;
goto retblock;
}
if (ptr == NULL)
{
if ((rval = (char *)malloc(MYSQL_USER_MAXLEN+1)) == NULL)
{
goto retblock;
}
}
else
{
rval = ptr;
}
snprintf(rval, MYSQL_USER_MAXLEN+1, "%s", first_letter);
retblock:
return rval;
}
static char* create_auth_fail_str(
GWBUF* readbuf,
char* hostaddr,
char* sha1)
{
char* errstr;
char* uname;
const char* ferrstr = "Access denied for user '%s'@'%s' (using password: %s)";
if ( (uname = get_username_from_auth(NULL, (uint8_t *)GWBUF_DATA(readbuf))) == NULL)
{
errstr = NULL;
goto retblock;
}
/** -4 comes from 2X'%s' minus terminating char */
errstr = (char *)malloc(strlen(uname)+strlen(ferrstr)+strlen(hostaddr)+strlen("YES")-6+1);
if (errstr != NULL)
{
sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES"));
}
retblock:
return errstr;
}
/**
* Write function for client DCB: writes data from MaxScale to Client
*
@ -593,77 +663,83 @@ int gw_read_client_event(
case MYSQL_AUTH_SENT:
{
int auth_val = -1;
int auth_val;
auth_val = gw_mysql_do_authentication(dcb, read_buffer);
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
ss_dassert(read_buffer == NULL || GWBUF_EMPTY(read_buffer));
if (auth_val == 0)
{
SESSION *session = NULL;
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
/**
* Create session, and a router session for it.
* If successful, there will be backend connection(s)
* after this point.
*/
session = session_alloc(dcb->service, dcb);
if (session != NULL)
{
CHK_SESSION(session);
ss_dassert(session->state != SESSION_STATE_ALLOC);
if (auth_val == 0)
{
SESSION *session;
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
/**
* Create session, and a router session for it.
* If successful, there will be backend connection(s)
* after this point.
*/
session = session_alloc(dcb->service, dcb);
if (session != NULL)
{
CHK_SESSION(session);
ss_dassert(session->state != SESSION_STATE_ALLOC);
protocol->protocol_auth_state = MYSQL_IDLE;
/**
* Send an AUTH_OK packet to the client,
* packet sequence is # 2
*/
mysql_send_ok(dcb, 2, 0, NULL);
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] session "
"creation failed. fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"failed to create new session");
dcb_close(dcb);
}
}
else
{
char* fail_str;
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
fail_str = create_auth_fail_str(read_buffer,
dcb->remote,
(char*)((MYSQL_session *)dcb->data)->client_sha1);
/** Send error 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
fail_str);
protocol->protocol_auth_state = MYSQL_IDLE;
/**
* Send an AUTH_OK packet to the client,
* packet sequence is # 2
*/
mysql_send_ok(dcb, 2, 0, NULL);
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] session "
"creation failed. fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"failed to create new session");
dcb_close(dcb);
}
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] after "
"gw_mysql_do_authentication, fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"Authorization failed");
dcb_close(dcb);
}
}
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] after "
"gw_mysql_do_authentication, fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
free(fail_str);
dcb_close(dcb);
}
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
}
break;
case MYSQL_IDLE:
@ -801,9 +877,12 @@ int gw_read_client_event(
}
/** succeed */
if (rc) {
if (rc)
{
rc = 0; /**< here '0' means success */
} else {
}
else
{
GWBUF* errbuf;
bool succp;
@ -1360,20 +1439,12 @@ gw_client_close(DCB *dcb)
CHK_SESSION(session);
spinlock_acquire(&session->ses_lock);
if (session->state == SESSION_STATE_STOPPING)
if (session->state != SESSION_STATE_STOPPING)
{
/**
* Session is already getting closed so avoid
* redundant calls
*/
spinlock_release(&session->ses_lock);
return 1;
}
else
{
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
}
session->state = SESSION_STATE_STOPPING;
}
spinlock_release(&session->ses_lock);
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;

View File

@ -466,6 +466,7 @@ int gw_receive_backend_auth(
bufstr)));
free(bufstr);
free(err);
rc = -1;
}
else
@ -540,9 +541,9 @@ int gw_receive_backend_auth(
* @return 0 on success, 1 on failure
*/
int gw_send_authentication_to_backend(
char *dbname,
char *user,
uint8_t *passwd,
char *dbname,
char *user,
uint8_t *passwd,
MySQLProtocol *conn)
{
int compress = 0;
@ -552,8 +553,8 @@ int gw_send_authentication_to_backend(
long bytes;
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
uint8_t client_capabilities[4];
uint32_t server_capabilities;
uint32_t final_capabilities;
uint32_t server_capabilities = 0;
uint32_t final_capabilities = 0;
char dbpass[MYSQL_USER_MAXLEN + 1]="";
GWBUF *buffer;
DCB *dcb;
@ -568,17 +569,12 @@ int gw_send_authentication_to_backend(
curr_passwd = passwd;
dcb = conn->owner_dcb;
// Zero the vars
memset(&server_capabilities, '\0', sizeof(server_capabilities));
memset(&final_capabilities, '\0', sizeof(final_capabilities));
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41;
final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;
/** Copy client's flags to backend */
final_capabilities |= conn->client_capabilities;;
if (compress) {
if (compress) {
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
#ifdef DEBUG_MYSQL_CONN
fprintf(stderr, ">>>> Backend Connection with compression\n");
@ -1033,19 +1029,24 @@ int mysql_send_custom_error (
* @param passwd The SHA1(real_password): Note real_password is unknown
* @return 1 on success, 0 on failure
*/
int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn) {
int compress = 0;
int rv;
uint8_t *payload = NULL;
uint8_t *payload_start = NULL;
long bytes;
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
uint8_t client_capabilities[4];
uint32_t server_capabilities;
uint32_t final_capabilities;
char dbpass[MYSQL_USER_MAXLEN + 1]="";
GWBUF *buffer;
DCB *dcb;
int gw_send_change_user_to_backend(
char *dbname,
char *user,
uint8_t *passwd,
MySQLProtocol *conn)
{
int compress = 0;
int rv;
uint8_t *payload = NULL;
uint8_t *payload_start = NULL;
long bytes;
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
uint8_t client_capabilities[4];
uint32_t server_capabilities = 0;
uint32_t final_capabilities = 0;
char dbpass[MYSQL_USER_MAXLEN + 1]="";
GWBUF *buffer;
DCB *dcb;
char *curr_db = NULL;
uint8_t *curr_passwd = NULL;
@ -1058,14 +1059,10 @@ int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, My
dcb = conn->owner_dcb;
// Zero the vars
memset(&server_capabilities, '\0', sizeof(server_capabilities));
memset(&final_capabilities, '\0', sizeof(final_capabilities));
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41;
final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;
/** Copy client's flags to backend */
final_capabilities |= conn->client_capabilities;;
if (compress) {
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
@ -1366,8 +1363,10 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
LOGIF(LE,
(skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [MySQL Client Auth], user [%s@%s] not found, please try with 'localhost_match_wildcard_host=1' in service definition",
pthread_self(),
"Error : user %s@%s not found, try set "
"'localhost_match_wildcard_host=1' in "
"service definition of the configuration "
"file.",
key.user,
dcb->remote)));
@ -1506,7 +1505,7 @@ mysql_send_auth_error (
}
mysql_errno = 1045;
mysql_error_msg = "Access denied!";
mysql_state = "2800";
mysql_state = "28000";
field_count = 0xff;
gw_mysql_set_byte2(mysql_err, mysql_errno);

View File

@ -44,6 +44,7 @@
*
* @endverbatim
*/
#include <my_config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

View File

@ -825,7 +825,8 @@ handleError(
DCB *client = NULL;
SESSION *session = backend_dcb->session;
client = session->client;
/** false because connection is not available anymore */
*succp = false;
ss_dassert(client != NULL);
}

View File

@ -15,6 +15,7 @@
*
* Copyright MariaDB Corporation Ab 2013-2014
*/
#include <my_config.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
@ -266,7 +267,7 @@ static bool handle_error_new_connection(
ROUTER_CLIENT_SES* rses,
DCB* backend_dcb,
GWBUF* errmsg);
static bool handle_error_reply_client(SESSION* ses, GWBUF* errmsg);
static void handle_error_reply_client(SESSION* ses, GWBUF* errmsg);
static backend_ref_t* get_root_master_bref(ROUTER_CLIENT_SES* rses);
@ -1669,7 +1670,7 @@ static int routeQuery(
route_target_t route_target;
bool succp = false;
int rlag_max = MAX_RLAG_UNDEFINED;
backend_type_t btype; /*< target backend type */
backend_type_t btype; /*< target backend type */
CHK_CLIENT_RSES(router_cli_ses);
@ -1683,7 +1684,6 @@ static int routeQuery(
packet = GWBUF_DATA(querybuf);
packet_type = packet[4];
if (rses_is_closed)
{
/**
@ -4015,7 +4015,8 @@ static void handleError (
case ERRACT_REPLY_CLIENT:
{
*succp = handle_error_reply_client(session, errmsgbuf);
handle_error_reply_client(session, errmsgbuf);
*succp = false; /*< no new backend servers were made available */
break;
}
@ -4026,13 +4027,12 @@ static void handleError (
}
static bool handle_error_reply_client(
static void handle_error_reply_client(
SESSION* ses,
GWBUF* errmsg)
{
session_state_t sesstate;
DCB* client_dcb;
bool succp;
spinlock_acquire(&ses->ses_lock);
sesstate = ses->state;
@ -4048,10 +4048,7 @@ static bool handle_error_reply_client(
{
while ((errmsg=gwbuf_consume(errmsg, GWBUF_LENGTH(errmsg))) != NULL)
;
}
succp = false; /** false because new servers aren's selected. */
return succp;
}
}
/**

View File

@ -1,3 +1,2 @@
add_test(NAME ReadWriteSplitTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit.sh testrwsplit.log ${TEST_HOST} ${TEST_PORT_RW} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR})
set_tests_properties(ReadWriteSplitTest PROPERTIES DEPENDS RunExecutable)
add_subdirectory(test_hints)

View File

@ -1,7 +1,3 @@
file(COPY MaxScale_test.cnf DESTINATION ${CMAKE_BINARY_DIR}/etc)
file(RENAME ${CMAKE_BINARY_DIR}/etc/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf)
#add_test(NAME RunExecutable COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/startmaxscale.sh "${CMAKE_BINARY_DIR}/bin/" "-c ${CMAKE_BINARY_DIR}")
#set_tests_properties(RunExecutable PROPERTIES TIMEOUT 2)
#add_test(NAME KillExecutable COMMAND killall -KILL maxscale)
#set_tests_properties(KillExecutable PROPERTIES DEPENDS StackHintTest ) #this needs to be the last test that requires a running maxscale
if(BUILD_TESTS)
install(FILES MaxScale_test.cnf DESTINATION etc RENAME MaxScale.cnf)
endif()

View File

@ -43,4 +43,5 @@ TMASTER_ID :=
# Global test log where all log is gathered
# TEST_MAXSCALE_LOG := $(ROOT_PATH)/test/test_maxscale.log
#
TEST_MAXSCALE_LOG :=
TEST_MAXSCALE_LOG := /home/mbrampton/Dropbox/skygit/MaxScale/test/testserver.log

View File

@ -474,7 +474,7 @@ typedef enum skygw_chk_t {
}
#define CHK_GWBUF(b) { \
ss_info_dassert(((b)->start <= (b)->end), \
ss_info_dassert(((char *)(b)->start <= (char *)(b)->end), \
"gwbuf start has passed the endpoint"); \
}

View File

@ -1918,7 +1918,7 @@ char* replace_literal(
}
rc = regcomp(&re, search_re, REG_EXTENDED|REG_ICASE);
ss_dassert(rc == 0);
ss_info_dassert(rc == 0, "Regex check");
if (rc != 0)
{