diff --git a/CMakeLists.txt b/CMakeLists.txt index e21a14d5e..ab5509402 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 2.6) - +message(STATUS "CMake version: ${CMAKE_VERSION}") include(macros.cmake) enable_testing() @@ -24,9 +24,13 @@ configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_SOURCE_ set(CMAKE_C_FLAGS "-Wall -fPIC") set(CMAKE_CXX_FLAGS "-Wall -fPIC") -if(BUILD_TYPE MATCHES Debug) +if(BUILD_TYPE STREQUAL Debug) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -pthread -pipe -DSS_DEBUG -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -pthread -pipe -DSS_DEBUG -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4") + message(STATUS "Generating debugging symbols and enabling debugging code") +elseif(BUILD_TYPE STREQUAL DebugSymbols) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -pthread -pipe -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -pthread -pipe -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4") message(STATUS "Generating debugging symbols") elseif(BUILD_TYPE MATCHES Optimized) if(NOT (DEFINED OLEVEL)) diff --git a/README b/README index eeb103c1d..a939021d0 100644 --- a/README +++ b/README @@ -205,7 +205,10 @@ or define the variables manually at configuration time. All the variables that control the CMake build process: INSTALL_DIR= Installation directory -BUILD_TYPE=[None|Debug|Release] Type of the build, defaults to Release (optimized) + +BUILD_TYPE= Type of the build. One of None, Debug, DebugSymbols, Optimized. (default None) + DebugSymbols enables debugging symbols, Debug enables debugging symbols and code, Optimized builds an optimized version. + INSTALL_SYSTEM_FILES=[Y|N] Install startup scripts and ld configuration files EMBEDDED_LIB= Path to the embedded library location (libmysqld.a for static and libmysqld.so for dynamic) MYSQL_DIR= Path to MySQL headers diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index f690661b6..59e108920 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -2073,7 +2073,7 @@ static bool logfile_init( char* c; pid_t pid = getpid(); int len = strlen(shm_pathname_prefix)+ - get_decimal_len((size_t)pid); + get_decimal_len((size_t)pid) + 1; c = (char *)calloc(len, sizeof(char)); diff --git a/query_classifier/test/canonical_tests/CMakeLists.txt b/query_classifier/test/canonical_tests/CMakeLists.txt index 4777fad8d..26bc64602 100644 --- a/query_classifier/test/canonical_tests/CMakeLists.txt +++ b/query_classifier/test/canonical_tests/CMakeLists.txt @@ -1,6 +1,11 @@ -file(COPY ${ERRMSG} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND") message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=") +else() + if(${CMAKE_VERSION} VERSION_LESS 2.8) + execute_process(COMMAND cp ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR}) + else() + file(COPY ${ERRMSG} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + endif() endif() add_executable(canonizer canonizer.c) target_link_libraries(canonizer pthread query_classifier z dl ssl aio crypt crypto rt m ${EMBEDDED_LIB} fullcore stdc++) diff --git a/server/core/buffer.c b/server/core/buffer.c index bb40a8d40..46315cf69 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include static buffer_object_t* gwbuf_remove_buffer_object( GWBUF* buf, diff --git a/server/core/poll.c b/server/core/poll.c index 37784176e..e1a3f80ec 100644 --- a/server/core/poll.c +++ b/server/core/poll.c @@ -257,7 +257,7 @@ poll_add_dcb(DCB *dcb) dcb, STRDCBSTATE(dcb->state)))); } - ss_dassert(rc == 0); /*< trap in debug */ + ss_info_dassert(rc == 0, "Unable to add poll"); /*< trap in debug */ } else { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, diff --git a/server/core/server.c b/server/core/server.c index 2afe3bd44..167ae4f23 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -136,7 +136,7 @@ SERVER *ptr; /** * Set a unique name for the server * - * @param server The server to ste the name on + * @param server The server to set the name on * @param name The unique name for the server */ void diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 914fe277e..14a80cd36 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -1,13 +1,37 @@ add_executable(test_hash testhash.c) +add_executable(test_hint testhint.c) add_executable(test_spinlock testspinlock.c) add_executable(test_filter testfilter.c) +add_executable(test_buffer testbuffer.c) +add_executable(test_dcb testdcb.c) +add_executable(test_modutil testmodutil.c) +add_executable(test_poll testpoll.c) +add_executable(test_service testservice.c) +add_executable(test_server testserver.c) +add_executable(test_users testusers.c) add_executable(test_adminusers testadminusers.c) target_link_libraries(test_hash fullcore) +target_link_libraries(test_hint fullcore) target_link_libraries(test_spinlock fullcore) target_link_libraries(test_filter fullcore) +target_link_libraries(test_buffer fullcore) +target_link_libraries(test_dcb fullcore) +target_link_libraries(test_modutil fullcore) +target_link_libraries(test_poll fullcore) +target_link_libraries(test_service fullcore) +target_link_libraries(test_server fullcore) +target_link_libraries(test_users fullcore) target_link_libraries(test_adminusers fullcore) add_test(TestHash test_hash) +add_test(TestHint test_hint) add_test(TestSpinlock test_spinlock) add_test(TestFilter test_filter) +add_test(TestBuffer test_buffer) +add_test(TestDCB test_dcb) +add_test(TestModutil test_modutil) +add_test(TestPoll test_poll) +add_test(TestService test_service) +add_test(TestServer test_server) +add_test(TestUsers test_users) add_test(TestAdminUsers test_adminusers) diff --git a/server/core/test/testbuffer.c b/server/core/test/testbuffer.c new file mode 100644 index 000000000..87ebf858b --- /dev/null +++ b/server/core/test/testbuffer.c @@ -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 +#include +#include + +#include + +/** + * 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); +} + + diff --git a/server/core/test/testdcb.c b/server/core/test/testdcb.c new file mode 100644 index 000000000..5f8e18601 --- /dev/null +++ b/server/core/test/testdcb.c @@ -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 +#include +#include + +#include + +/** + * 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); +} + + + diff --git a/server/core/test/testhint.c b/server/core/test/testhint.c new file mode 100644 index 000000000..3d2241642 --- /dev/null +++ b/server/core/test/testhint.c @@ -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 +#include +#include + +#include + +/** + * 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); +} + diff --git a/server/core/test/testmodutil.c b/server/core/test/testmodutil.c new file mode 100644 index 000000000..e83facdc5 --- /dev/null +++ b/server/core/test/testmodutil.c @@ -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 +#include +#include + +#include +#include + +/** + * 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); +} + + diff --git a/server/core/test/testpoll.c b/server/core/test/testpoll.c new file mode 100644 index 000000000..aee799bb4 --- /dev/null +++ b/server/core/test/testpoll.c @@ -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 +#include +#include + +#include +#include + +/** + * 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); +} + diff --git a/server/core/test/testserver.c b/server/core/test/testserver.c new file mode 100644 index 000000000..b06e53269 --- /dev/null +++ b/server/core/test/testserver.c @@ -0,0 +1,89 @@ +/* + * 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 +#include +#include + +#include + +/** + * 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); +} diff --git a/server/core/test/testservice.c b/server/core/test/testservice.c new file mode 100644 index 000000000..2b12c8062 --- /dev/null +++ b/server/core/test/testservice.c @@ -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 +#include +#include + +#include + +/** + * 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); +} diff --git a/server/core/test/testusers.c b/server/core/test/testusers.c new file mode 100644 index 000000000..1f32c5d97 --- /dev/null +++ b/server/core/test/testusers.c @@ -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 +#include +#include + +#include + +/** + * 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 != servers, "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); +} + diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index f3ef18e02..049455686 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -880,6 +880,10 @@ static int gw_create_backend_connection( goto return_fd; } + /** Copy client flags to backend protocol */ + protocol->client_capabilities = + ((MySQLProtocol *)(backend_dcb->session->client->protocol))->client_capabilities; + /*< if succeed, fd > 0, -1 otherwise */ rv = gw_do_connect_to_backend(server->name, server->port, &fd); /*< Assign protocol with backend_dcb */ diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 9372314d8..06f83f3e1 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -68,6 +68,8 @@ int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* int MySQLSendHandshake(DCB* dcb); static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue); static int route_by_statement(SESSION *, GWBUF **); +static char* create_auth_fail_str(GWBUF* readbuf, char* hostaddr, char* sha1); +static char* get_username_from_auth(char* ptr, uint8_t* data); /* * The "module object" for the mysqld client protocol module. @@ -372,9 +374,9 @@ MySQLSendHandshake(DCB* dcb) * The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data * client_capabilitiesa are copied into the dcb->protocol * - * @param dcb Descriptor Control Block of the client - * @param queue The GWBUF with data from client - * @return 0 for Authentication ok, !=0 for failed autht + * @param dcb Descriptor Control Block of the client + * @param queue The GWBUF with data from client + * @return 0 If succeed, otherwise non-zero value * */ @@ -432,12 +434,10 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { &protocol->client_capabilities); */ - /* now get the user */ - strncpy(username, (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23), MYSQL_USER_MAXLEN); - - - /* the empty username field is not allowed */ - if (!strlen(username)) { + username = get_username_from_auth(username, client_auth_packet); + + if (username == NULL) + { return 1; } @@ -448,9 +448,9 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { if (connect_with_db) { database = client_data->db; - strncpy(database, - (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + - 1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN); + strncpy(database, + (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + + 1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN); } /* allocate memory for token only if auth_token_len > 0 */ @@ -489,10 +489,80 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { { dcb->user = strdup(client_data->user); } - + return auth_ret; } +/** + * Read username from MySQL authentication packet. + * + * @param ptr address where to write the result or NULL if memory + * is allocated here. + * @param data Address of MySQL packet. + * + * @return Pointer to a copy of the username. NULL if memory allocation + * failed or if username was empty. + */ +static char* get_username_from_auth( + char* ptr, + uint8_t* data) +{ + char* first_letter; + char* rval; + + first_letter = (char *)(data + 4 + 4 + 4 + 1 + 23); + + if (first_letter == '\0') + { + rval = NULL; + goto retblock; + } + + if (ptr == NULL) + { + if ((rval = (char *)malloc(MYSQL_USER_MAXLEN+1)) == NULL) + { + goto retblock; + } + } + else + { + rval = ptr; + } + snprintf(rval, MYSQL_USER_MAXLEN+1, "%s", first_letter); + +retblock: + + return rval; +} + + +static char* create_auth_fail_str( + GWBUF* readbuf, + char* hostaddr, + char* sha1) +{ + char* errstr; + char* uname; + const char* ferrstr = "Access denied for user '%s'@'%s' (using password: %s)"; + + if ( (uname = get_username_from_auth(NULL, (uint8_t *)GWBUF_DATA(readbuf))) == NULL) + { + errstr = NULL; + goto retblock; + } + /** -4 comes from 2X'%s' minus terminating char */ + errstr = (char *)malloc(strlen(uname)+strlen(ferrstr)+strlen(hostaddr)+strlen("YES")-6+1); + + if (errstr != NULL) + { + sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES")); + } + +retblock: + return errstr; +} + /** * Write function for client DCB: writes data from MaxScale to Client * @@ -593,77 +663,83 @@ int gw_read_client_event( case MYSQL_AUTH_SENT: { - int auth_val = -1; - + int auth_val; + auth_val = gw_mysql_do_authentication(dcb, read_buffer); - read_buffer = gwbuf_consume(read_buffer, nbytes_read); - ss_dassert(read_buffer == NULL || GWBUF_EMPTY(read_buffer)); - - if (auth_val == 0) - { - SESSION *session = NULL; - protocol->protocol_auth_state = MYSQL_AUTH_RECV; - /** - * Create session, and a router session for it. - * If successful, there will be backend connection(s) - * after this point. - */ - session = session_alloc(dcb->service, dcb); - - if (session != NULL) - { - CHK_SESSION(session); - ss_dassert(session->state != SESSION_STATE_ALLOC); + + if (auth_val == 0) + { + SESSION *session; + + protocol->protocol_auth_state = MYSQL_AUTH_RECV; + /** + * Create session, and a router session for it. + * If successful, there will be backend connection(s) + * after this point. + */ + session = session_alloc(dcb->service, dcb); + + if (session != NULL) + { + CHK_SESSION(session); + ss_dassert(session->state != SESSION_STATE_ALLOC); + + protocol->protocol_auth_state = MYSQL_IDLE; + /** + * Send an AUTH_OK packet to the client, + * packet sequence is # 2 + */ + mysql_send_ok(dcb, 2, 0, NULL); + } + else + { + protocol->protocol_auth_state = MYSQL_AUTH_FAILED; + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_read_client_event] session " + "creation failed. fd %d, " + "state = MYSQL_AUTH_FAILED.", + protocol->owner_dcb->fd, + pthread_self()))); + + /** Send ERR 1045 to client */ + mysql_send_auth_error( + dcb, + 2, + 0, + "failed to create new session"); + + dcb_close(dcb); + } + } + else + { + char* fail_str; + + protocol->protocol_auth_state = MYSQL_AUTH_FAILED; + fail_str = create_auth_fail_str(read_buffer, + dcb->remote, + (char*)((MYSQL_session *)dcb->data)->client_sha1); + + /** Send error 1045 to client */ + mysql_send_auth_error( + dcb, + 2, + 0, + fail_str); - protocol->protocol_auth_state = MYSQL_IDLE; - /** - * Send an AUTH_OK packet to the client, - * packet sequence is # 2 - */ - mysql_send_ok(dcb, 2, 0, NULL); - } - else - { - protocol->protocol_auth_state = MYSQL_AUTH_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] session " - "creation failed. fd %d, " - "state = MYSQL_AUTH_FAILED.", - protocol->owner_dcb->fd, - pthread_self()))); - - /** Send ERR 1045 to client */ - mysql_send_auth_error( - dcb, - 2, - 0, - "failed to create new session"); - - dcb_close(dcb); - } - } - else - { - protocol->protocol_auth_state = MYSQL_AUTH_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] after " - "gw_mysql_do_authentication, fd %d, " - "state = MYSQL_AUTH_FAILED.", - protocol->owner_dcb->fd, - pthread_self()))); - - /** Send ERR 1045 to client */ - mysql_send_auth_error( - dcb, - 2, - 0, - "Access denied"); - - dcb_close(dcb); - } - } + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [gw_read_client_event] after " + "gw_mysql_do_authentication, fd %d, " + "state = MYSQL_AUTH_FAILED.", + protocol->owner_dcb->fd, + pthread_self()))); + free(fail_str); + dcb_close(dcb); + } + read_buffer = gwbuf_consume(read_buffer, nbytes_read); + } break; case MYSQL_IDLE: diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 892824c19..614f2b194 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -538,9 +538,9 @@ int gw_receive_backend_auth( * @return 0 on success, 1 on failure */ int gw_send_authentication_to_backend( - char *dbname, - char *user, - uint8_t *passwd, + char *dbname, + char *user, + uint8_t *passwd, MySQLProtocol *conn) { int compress = 0; @@ -572,11 +572,10 @@ int gw_send_authentication_to_backend( 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; #ifdef DEBUG_MYSQL_CONN fprintf(stderr, ">>>> Backend Connection with compression\n"); @@ -1362,8 +1361,10 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "%lu [MySQL Client Auth], user [%s@%s] not found, please try with 'localhost_match_wildcard_host=1' in service definition", - pthread_self(), + "Error : user %s@%s not found, try set " + "'localhost_match_wildcard_host=1' in " + "service definition of the configuration " + "file.", key.user, dcb->remote))); diff --git a/server/modules/routing/readwritesplit/test/CMakeLists.txt b/server/modules/routing/readwritesplit/test/CMakeLists.txt index b2012e789..1cd40be63 100644 --- a/server/modules/routing/readwritesplit/test/CMakeLists.txt +++ b/server/modules/routing/readwritesplit/test/CMakeLists.txt @@ -1,3 +1,2 @@ add_test(NAME ReadWriteSplitTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit.sh testrwsplit.log ${TEST_HOST} ${TEST_PORT_RW} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR}) -set_tests_properties(ReadWriteSplitTest PROPERTIES DEPENDS RunExecutable) add_subdirectory(test_hints) \ No newline at end of file diff --git a/server/test/CMakeLists.txt b/server/test/CMakeLists.txt index 22e4c1b4d..47eb22199 100644 --- a/server/test/CMakeLists.txt +++ b/server/test/CMakeLists.txt @@ -1,7 +1,3 @@ -file(COPY MaxScale_test.cnf DESTINATION ${CMAKE_BINARY_DIR}/etc) -file(RENAME ${CMAKE_BINARY_DIR}/etc/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf) -#add_test(NAME RunExecutable COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/startmaxscale.sh "${CMAKE_BINARY_DIR}/bin/" "-c ${CMAKE_BINARY_DIR}") -#set_tests_properties(RunExecutable PROPERTIES TIMEOUT 2) - -#add_test(NAME KillExecutable COMMAND killall -KILL maxscale) -#set_tests_properties(KillExecutable PROPERTIES DEPENDS StackHintTest ) #this needs to be the last test that requires a running maxscale +if(BUILD_TESTS) + install(FILES MaxScale_test.cnf DESTINATION etc RENAME MaxScale.cnf) +endif() diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 7e8407ba7..3699791c3 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -1918,7 +1918,7 @@ char* replace_literal( } rc = regcomp(&re, search_re, REG_EXTENDED|REG_ICASE); - ss_dassert(rc == 0); + ss_info_dassert(rc == 0, "Regex check"); if (rc != 0) {