Merge branch 'develop' into shm_log_fix
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 2.6)
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||||
include(macros.cmake)
|
include(macros.cmake)
|
||||||
|
|
||||||
enable_testing()
|
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_C_FLAGS "-Wall -fPIC")
|
||||||
set(CMAKE_CXX_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_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")
|
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")
|
message(STATUS "Generating debugging symbols")
|
||||||
elseif(BUILD_TYPE MATCHES Optimized)
|
elseif(BUILD_TYPE MATCHES Optimized)
|
||||||
if(NOT (DEFINED OLEVEL))
|
if(NOT (DEFINED OLEVEL))
|
||||||
|
|||||||
5
README
5
README
@ -205,7 +205,10 @@ or define the variables manually at configuration time.
|
|||||||
All the variables that control the CMake build process:
|
All the variables that control the CMake build process:
|
||||||
|
|
||||||
INSTALL_DIR=<path> Installation directory
|
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
|
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)
|
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
|
MYSQL_DIR=<path> Path to MySQL headers
|
||||||
|
|||||||
@ -2073,7 +2073,7 @@ static bool logfile_init(
|
|||||||
char* c;
|
char* c;
|
||||||
pid_t pid = getpid();
|
pid_t pid = getpid();
|
||||||
int len = strlen(shm_pathname_prefix)+
|
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));
|
c = (char *)calloc(len, sizeof(char));
|
||||||
|
|
||||||
|
|||||||
@ -1 +1,14 @@
|
|||||||
|
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_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)
|
||||||
@ -1,6 +1,11 @@
|
|||||||
file(COPY ${ERRMSG} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND")
|
if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND")
|
||||||
message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=<path>")
|
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()
|
endif()
|
||||||
add_executable(canonizer canonizer.c)
|
add_executable(canonizer canonizer.c)
|
||||||
target_link_libraries(canonizer pthread query_classifier z dl ssl aio crypt crypto rt m ${EMBEDDED_LIB} fullcore stdc++)
|
target_link_libraries(canonizer pthread query_classifier z dl ssl aio crypt crypto rt m ${EMBEDDED_LIB} fullcore stdc++)
|
||||||
|
|||||||
175
query_classifier/test/classify.c
Normal file
175
query_classifier/test/classify.c
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
13
query_classifier/test/expected.sql
Normal file
13
query_classifier/test/expected.sql
Normal 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
|
||||||
13
query_classifier/test/input.sql
Normal file
13
query_classifier/test/input.sql
Normal 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;
|
||||||
@ -42,6 +42,8 @@
|
|||||||
#include <buffer.h>
|
#include <buffer.h>
|
||||||
#include <atomic.h>
|
#include <atomic.h>
|
||||||
#include <skygw_debug.h>
|
#include <skygw_debug.h>
|
||||||
|
#include <spinlock.h>
|
||||||
|
#include <hint.h>
|
||||||
|
|
||||||
static buffer_object_t* gwbuf_remove_buffer_object(
|
static buffer_object_t* gwbuf_remove_buffer_object(
|
||||||
GWBUF* buf,
|
GWBUF* buf,
|
||||||
|
|||||||
@ -42,8 +42,10 @@
|
|||||||
#define _XOPEN_SOURCE 700
|
#define _XOPEN_SOURCE 700
|
||||||
#include <ftw.h>
|
#include <ftw.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
#include <gw.h>
|
#include <gw.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <service.h>
|
#include <service.h>
|
||||||
#include <server.h>
|
#include <server.h>
|
||||||
@ -226,7 +228,6 @@ sigfatal_handler (int i)
|
|||||||
|
|
||||||
{
|
{
|
||||||
void *addrs[128];
|
void *addrs[128];
|
||||||
char **strings= NULL;
|
|
||||||
int n, count = backtrace(addrs, 128);
|
int n, count = backtrace(addrs, 128);
|
||||||
char** symbols = backtrace_symbols( addrs, count );
|
char** symbols = backtrace_symbols( addrs, count );
|
||||||
|
|
||||||
@ -1088,9 +1089,9 @@ int main(int argc, char **argv)
|
|||||||
goto return_main;
|
goto return_main;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
if (strncasecmp(optarg, "file") == 0)
|
if (strncasecmp(optarg, "file", PATH_MAX) == 0)
|
||||||
logtofile = 1;
|
logtofile = 1;
|
||||||
else if (strncasecmp(optarg, "shm") == 0)
|
else if (strncasecmp(optarg, "shm", PATH_MAX) == 0)
|
||||||
logtofile = 0;
|
logtofile = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1669,6 +1670,8 @@ static void log_flush_cb(
|
|||||||
void* arg)
|
void* arg)
|
||||||
{
|
{
|
||||||
ssize_t timeout_ms = *(ssize_t *)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,
|
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
|
||||||
"Started MaxScale log flusher.")));
|
"Started MaxScale log flusher.")));
|
||||||
@ -1677,7 +1680,7 @@ static void log_flush_cb(
|
|||||||
skygw_log_flush(LOGFILE_MESSAGE);
|
skygw_log_flush(LOGFILE_MESSAGE);
|
||||||
skygw_log_flush(LOGFILE_TRACE);
|
skygw_log_flush(LOGFILE_TRACE);
|
||||||
skygw_log_flush(LOGFILE_DEBUG);
|
skygw_log_flush(LOGFILE_DEBUG);
|
||||||
usleep(timeout_ms*1000);
|
nanosleep(&ts1, &ts2);
|
||||||
}
|
}
|
||||||
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
|
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
|
||||||
"Finished MaxScale log flusher.")));
|
"Finished MaxScale log flusher.")));
|
||||||
|
|||||||
@ -257,7 +257,7 @@ poll_add_dcb(DCB *dcb)
|
|||||||
dcb,
|
dcb,
|
||||||
STRDCBSTATE(dcb->state))));
|
STRDCBSTATE(dcb->state))));
|
||||||
}
|
}
|
||||||
ss_dassert(rc == 0); /*< trap in debug */
|
ss_info_dassert(rc == 0, "Unable to add poll"); /*< trap in debug */
|
||||||
} else {
|
} else {
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
|
|||||||
@ -136,7 +136,7 @@ SERVER *ptr;
|
|||||||
/**
|
/**
|
||||||
* Set a unique name for the server
|
* 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
|
* @param name The unique name for the server
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
|||||||
@ -1,13 +1,37 @@
|
|||||||
add_executable(test_hash testhash.c)
|
add_executable(test_hash testhash.c)
|
||||||
|
add_executable(test_hint testhint.c)
|
||||||
add_executable(test_spinlock testspinlock.c)
|
add_executable(test_spinlock testspinlock.c)
|
||||||
add_executable(test_filter testfilter.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)
|
add_executable(test_adminusers testadminusers.c)
|
||||||
target_link_libraries(test_hash fullcore)
|
target_link_libraries(test_hash fullcore)
|
||||||
|
target_link_libraries(test_hint fullcore)
|
||||||
target_link_libraries(test_spinlock fullcore)
|
target_link_libraries(test_spinlock fullcore)
|
||||||
target_link_libraries(test_filter 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)
|
target_link_libraries(test_adminusers fullcore)
|
||||||
add_test(TestHash test_hash)
|
add_test(TestHash test_hash)
|
||||||
|
add_test(TestHint test_hint)
|
||||||
add_test(TestSpinlock test_spinlock)
|
add_test(TestSpinlock test_spinlock)
|
||||||
add_test(TestFilter test_filter)
|
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)
|
add_test(TestAdminUsers test_adminusers)
|
||||||
|
|
||||||
|
|||||||
158
server/core/test/testbuffer.c
Normal file
158
server/core/test/testbuffer.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
87
server/core/test/testdcb.c
Normal file
87
server/core/test/testdcb.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
69
server/core/test/testhint.c
Normal file
69
server/core/test/testhint.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
78
server/core/test/testmodutil.c
Normal file
78
server/core/test/testmodutil.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
77
server/core/test/testpoll.c
Normal file
77
server/core/test/testpoll.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
92
server/core/test/testserver.c
Normal file
92
server/core/test/testserver.c
Normal 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);
|
||||||
|
}
|
||||||
86
server/core/test/testservice.c
Normal file
86
server/core/test/testservice.c
Normal 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);
|
||||||
|
}
|
||||||
81
server/core/test/testusers.c
Normal file
81
server/core/test/testusers.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
@ -25,3 +25,7 @@ target_link_libraries(topfilter log_manager utils)
|
|||||||
install(TARGETS topfilter DESTINATION modules)
|
install(TARGETS topfilter DESTINATION modules)
|
||||||
|
|
||||||
add_subdirectory(hint)
|
add_subdirectory(hint)
|
||||||
|
|
||||||
|
if(BUILD_TESTS)
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
13
server/modules/filter/test/CMakeLists.txt
Normal file
13
server/modules/filter/test/CMakeLists.txt
Normal 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 $?")
|
||||||
80
server/modules/filter/test/Makefile
Executable file
80
server/modules/filter/test/Makefile
Executable 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
|
||||||
20
server/modules/filter/test/README
Normal file
20
server/modules/filter/test/README
Normal 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
|
||||||
2303
server/modules/filter/test/doxygen.conf
Normal file
2303
server/modules/filter/test/doxygen.conf
Normal file
File diff suppressed because it is too large
Load Diff
2
server/modules/filter/test/harness.cnf
Normal file
2
server/modules/filter/test/harness.cnf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
threads=2
|
||||||
|
sessions=4
|
||||||
362
server/modules/filter/test/harness.h
Normal file
362
server/modules/filter/test/harness.h
Normal 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
|
||||||
997
server/modules/filter/test/harness_common.c
Normal file
997
server/modules/filter/test/harness_common.c
Normal 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(¶mc);
|
||||||
|
|
||||||
|
}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;
|
||||||
|
}
|
||||||
403
server/modules/filter/test/harness_ui.c
Executable file
403
server/modules/filter/test/harness_ui.c
Executable 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
server/modules/filter/test/harness_util.c
Normal file
14
server/modules/filter/test/harness_util.c
Normal 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;
|
||||||
|
}
|
||||||
165
server/modules/filter/test/hint_testing.cnf
Executable file
165
server/modules/filter/test/hint_testing.cnf
Executable 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
|
||||||
48
server/modules/filter/test/hint_testing.expected
Executable file
48
server/modules/filter/test/hint_testing.expected
Executable 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 */;
|
||||||
48
server/modules/filter/test/hint_testing.input
Normal file
48
server/modules/filter/test/hint_testing.input
Normal 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 */;
|
||||||
9
server/modules/filter/test/hint_tests.sh
Executable file
9
server/modules/filter/test/hint_tests.sh
Executable 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
|
||||||
1
server/modules/filter/test/querysmall
Normal file
1
server/modules/filter/test/querysmall
Normal file
@ -0,0 +1 @@
|
|||||||
|
SELECT * FROM test_table;
|
||||||
@ -880,6 +880,10 @@ static int gw_create_backend_connection(
|
|||||||
goto return_fd;
|
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 */
|
/*< if succeed, fd > 0, -1 otherwise */
|
||||||
rv = gw_do_connect_to_backend(server->name, server->port, &fd);
|
rv = gw_do_connect_to_backend(server->name, server->port, &fd);
|
||||||
/*< Assign protocol with backend_dcb */
|
/*< Assign protocol with backend_dcb */
|
||||||
|
|||||||
@ -68,6 +68,8 @@ int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char*
|
|||||||
int MySQLSendHandshake(DCB* dcb);
|
int MySQLSendHandshake(DCB* dcb);
|
||||||
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
|
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
|
||||||
static int route_by_statement(SESSION *, GWBUF **);
|
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.
|
* 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
|
* The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data
|
||||||
* client_capabilitiesa are copied into the dcb->protocol
|
* client_capabilitiesa are copied into the dcb->protocol
|
||||||
*
|
*
|
||||||
* @param dcb Descriptor Control Block of the client
|
* @param dcb Descriptor Control Block of the client
|
||||||
* @param queue The GWBUF with data from client
|
* @param queue The GWBUF with data from client
|
||||||
* @return 0 for Authentication ok, !=0 for failed autht
|
* @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);
|
&protocol->client_capabilities);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* now get the user */
|
username = get_username_from_auth(username, client_auth_packet);
|
||||||
strncpy(username, (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23), MYSQL_USER_MAXLEN);
|
|
||||||
|
|
||||||
|
if (username == NULL)
|
||||||
/* the empty username field is not allowed */
|
{
|
||||||
if (!strlen(username)) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,9 +448,9 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
|||||||
|
|
||||||
if (connect_with_db) {
|
if (connect_with_db) {
|
||||||
database = client_data->db;
|
database = client_data->db;
|
||||||
strncpy(database,
|
strncpy(database,
|
||||||
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
|
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
|
||||||
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
|
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allocate memory for token only if auth_token_len > 0 */
|
/* allocate memory for token only if auth_token_len > 0 */
|
||||||
@ -493,6 +493,76 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
|||||||
return auth_ret;
|
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
|
* Write function for client DCB: writes data from MaxScale to Client
|
||||||
*
|
*
|
||||||
@ -593,77 +663,83 @@ int gw_read_client_event(
|
|||||||
|
|
||||||
case MYSQL_AUTH_SENT:
|
case MYSQL_AUTH_SENT:
|
||||||
{
|
{
|
||||||
int auth_val = -1;
|
int auth_val;
|
||||||
|
|
||||||
auth_val = gw_mysql_do_authentication(dcb, read_buffer);
|
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)
|
if (auth_val == 0)
|
||||||
{
|
{
|
||||||
SESSION *session = NULL;
|
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)
|
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
|
||||||
{
|
/**
|
||||||
CHK_SESSION(session);
|
* Create session, and a router session for it.
|
||||||
ss_dassert(session->state != SESSION_STATE_ALLOC);
|
* If successful, there will be backend connection(s)
|
||||||
|
* after this point.
|
||||||
|
*/
|
||||||
|
session = session_alloc(dcb->service, dcb);
|
||||||
|
|
||||||
protocol->protocol_auth_state = MYSQL_IDLE;
|
if (session != NULL)
|
||||||
/**
|
{
|
||||||
* Send an AUTH_OK packet to the client,
|
CHK_SESSION(session);
|
||||||
* packet sequence is # 2
|
ss_dassert(session->state != SESSION_STATE_ALLOC);
|
||||||
*/
|
|
||||||
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 */
|
protocol->protocol_auth_state = MYSQL_IDLE;
|
||||||
mysql_send_auth_error(
|
/**
|
||||||
dcb,
|
* Send an AUTH_OK packet to the client,
|
||||||
2,
|
* packet sequence is # 2
|
||||||
0,
|
*/
|
||||||
"failed to create new session");
|
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())));
|
||||||
|
|
||||||
dcb_close(dcb);
|
/** Send ERR 1045 to client */
|
||||||
}
|
mysql_send_auth_error(
|
||||||
}
|
dcb,
|
||||||
else
|
2,
|
||||||
{
|
0,
|
||||||
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
|
"failed to create new session");
|
||||||
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 */
|
dcb_close(dcb);
|
||||||
mysql_send_auth_error(
|
}
|
||||||
dcb,
|
}
|
||||||
2,
|
else
|
||||||
0,
|
{
|
||||||
"Access denied");
|
char* fail_str;
|
||||||
|
|
||||||
dcb_close(dcb);
|
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);
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
|
|
||||||
case MYSQL_IDLE:
|
case MYSQL_IDLE:
|
||||||
|
|||||||
@ -538,9 +538,9 @@ int gw_receive_backend_auth(
|
|||||||
* @return 0 on success, 1 on failure
|
* @return 0 on success, 1 on failure
|
||||||
*/
|
*/
|
||||||
int gw_send_authentication_to_backend(
|
int gw_send_authentication_to_backend(
|
||||||
char *dbname,
|
char *dbname,
|
||||||
char *user,
|
char *user,
|
||||||
uint8_t *passwd,
|
uint8_t *passwd,
|
||||||
MySQLProtocol *conn)
|
MySQLProtocol *conn)
|
||||||
{
|
{
|
||||||
int compress = 0;
|
int compress = 0;
|
||||||
@ -550,8 +550,8 @@ int gw_send_authentication_to_backend(
|
|||||||
long bytes;
|
long bytes;
|
||||||
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
|
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
|
||||||
uint8_t client_capabilities[4];
|
uint8_t client_capabilities[4];
|
||||||
uint32_t server_capabilities;
|
uint32_t server_capabilities = 0;
|
||||||
uint32_t final_capabilities;
|
uint32_t final_capabilities = 0;
|
||||||
char dbpass[MYSQL_USER_MAXLEN + 1]="";
|
char dbpass[MYSQL_USER_MAXLEN + 1]="";
|
||||||
GWBUF *buffer;
|
GWBUF *buffer;
|
||||||
DCB *dcb;
|
DCB *dcb;
|
||||||
@ -566,17 +566,12 @@ int gw_send_authentication_to_backend(
|
|||||||
curr_passwd = passwd;
|
curr_passwd = passwd;
|
||||||
|
|
||||||
dcb = conn->owner_dcb;
|
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;
|
/** Copy client's flags to backend */
|
||||||
final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;
|
final_capabilities |= conn->client_capabilities;;
|
||||||
|
|
||||||
if (compress) {
|
if (compress) {
|
||||||
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||||
#ifdef DEBUG_MYSQL_CONN
|
#ifdef DEBUG_MYSQL_CONN
|
||||||
fprintf(stderr, ">>>> Backend Connection with compression\n");
|
fprintf(stderr, ">>>> Backend Connection with compression\n");
|
||||||
@ -1031,19 +1026,24 @@ int mysql_send_custom_error (
|
|||||||
* @param passwd The SHA1(real_password): Note real_password is unknown
|
* @param passwd The SHA1(real_password): Note real_password is unknown
|
||||||
* @return 1 on success, 0 on failure
|
* @return 1 on success, 0 on failure
|
||||||
*/
|
*/
|
||||||
int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn) {
|
int gw_send_change_user_to_backend(
|
||||||
int compress = 0;
|
char *dbname,
|
||||||
int rv;
|
char *user,
|
||||||
uint8_t *payload = NULL;
|
uint8_t *passwd,
|
||||||
uint8_t *payload_start = NULL;
|
MySQLProtocol *conn)
|
||||||
long bytes;
|
{
|
||||||
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
|
int compress = 0;
|
||||||
uint8_t client_capabilities[4];
|
int rv;
|
||||||
uint32_t server_capabilities;
|
uint8_t *payload = NULL;
|
||||||
uint32_t final_capabilities;
|
uint8_t *payload_start = NULL;
|
||||||
char dbpass[MYSQL_USER_MAXLEN + 1]="";
|
long bytes;
|
||||||
GWBUF *buffer;
|
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
|
||||||
DCB *dcb;
|
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;
|
char *curr_db = NULL;
|
||||||
uint8_t *curr_passwd = NULL;
|
uint8_t *curr_passwd = NULL;
|
||||||
@ -1056,14 +1056,10 @@ int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, My
|
|||||||
|
|
||||||
dcb = conn->owner_dcb;
|
dcb = conn->owner_dcb;
|
||||||
|
|
||||||
// Zero the vars
|
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
|
||||||
memset(&server_capabilities, '\0', sizeof(server_capabilities));
|
|
||||||
memset(&final_capabilities, '\0', sizeof(final_capabilities));
|
|
||||||
|
|
||||||
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
|
/** Copy client's flags to backend */
|
||||||
|
final_capabilities |= conn->client_capabilities;;
|
||||||
final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41;
|
|
||||||
final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;
|
|
||||||
|
|
||||||
if (compress) {
|
if (compress) {
|
||||||
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||||
@ -1362,8 +1358,10 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
|
|||||||
LOGIF(LE,
|
LOGIF(LE,
|
||||||
(skygw_log_write_flush(
|
(skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
"%lu [MySQL Client Auth], user [%s@%s] not found, please try with 'localhost_match_wildcard_host=1' in service definition",
|
"Error : user %s@%s not found, try set "
|
||||||
pthread_self(),
|
"'localhost_match_wildcard_host=1' in "
|
||||||
|
"service definition of the configuration "
|
||||||
|
"file.",
|
||||||
key.user,
|
key.user,
|
||||||
dcb->remote)));
|
dcb->remote)));
|
||||||
|
|
||||||
|
|||||||
@ -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})
|
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)
|
add_subdirectory(test_hints)
|
||||||
@ -1,7 +1,3 @@
|
|||||||
file(COPY MaxScale_test.cnf DESTINATION ${CMAKE_BINARY_DIR}/etc)
|
if(BUILD_TESTS)
|
||||||
file(RENAME ${CMAKE_BINARY_DIR}/etc/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf)
|
install(FILES MaxScale_test.cnf DESTINATION etc RENAME MaxScale.cnf)
|
||||||
#add_test(NAME RunExecutable COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/startmaxscale.sh "${CMAKE_BINARY_DIR}/bin/" "-c ${CMAKE_BINARY_DIR}")
|
endif()
|
||||||
#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
|
|
||||||
|
|||||||
@ -1918,7 +1918,7 @@ char* replace_literal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
rc = regcomp(&re, search_re, REG_EXTENDED|REG_ICASE);
|
rc = regcomp(&re, search_re, REG_EXTENDED|REG_ICASE);
|
||||||
ss_dassert(rc == 0);
|
ss_info_dassert(rc == 0, "Regex check");
|
||||||
|
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user