diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b040f0df..62abd54a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,19 +31,31 @@ include(cmake/CheckPlatform.cmake) check_deps() check_dirs() +find_package(OpenSSL) find_package(Valgrind) find_package(MySQLClient) find_package(MySQL) find_package(Pandoc) find_package(TCMalloc) find_package(Jemalloc) +find_package(CURL) # You can find the variables set by this in the FindCURL.cmake file # which is a default module in CMake. -find_package(CURL) + if(NOT CURL_FOUND) message(FATAL_ERROR "Failed to locate dependency: libcurl") endif() +if(NOT OPENSSL_FOUND) + message(FATAL_ERROR "Failed to locate dependency: OpenSSL") +else() + if(OPENSSL_VERSION VERSION_LESS 1 AND NOT FORCE_OPENSSL100) + add_definitions("-DOPENSSL_0_9") + else() + add_definitions("-DOPENSSL_1_0") + endif() +endif() + set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/${MAXSCALE_LIBDIR}) # Make sure the release notes for this release are present if it is a stable one @@ -102,7 +114,13 @@ if(PROFILE) set(FLAGS "${FLAGS} -pg " CACHE STRING "Compilation flags" FORCE) endif() -set(CMAKE_C_FLAGS "${FLAGS}") +if(USE_C99) + message(STATUS "Using C99 standard") + set(CMAKE_C_FLAGS "-std=c99 -D_GNU_SOURCE=1 ${FLAGS}") +else() + set(CMAKE_C_FLAGS "${FLAGS}") +endif() + set(CMAKE_C_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG -DLOG_ASSERT") set(CMAKE_C_FLAGS_RELEASE "") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-ggdb") diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 99f8ba38e..f19797d55 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -5,7 +5,7 @@ ## About MaxScale - [About MaxScale](About/About-MaxScale.md) - - [Release Notes 1.1](Release-Notes/MaxScale-1.1-Release-Notes.md) + - [MaxScale 1.2.0 Release Notes](Release-Notes/MaxScale-1.2.0-Release-Notes.md) - [Changelog](Changelog.md) - [Limitations](About/Limitations.md) - [COPYRIGHT](About/COPYRIGHT.md) @@ -19,7 +19,8 @@ ## Upgrading MaxScale -- [Upgrading MaxScale to 1.1.0](Upgrading-To-MaxScale-1.1.0.md) +- [Upgrading MaxScale from 1.1.1 to 1.2.0](Upgrading-To-MaxScale-1.2.0.md) +- [Upgrading MaxScale from 1.0.5 to 1.1.0](Upgrading-To-MaxScale-1.1.0.md) ## Reference @@ -28,6 +29,7 @@ - [MaxScale HA with Lsyncd](Reference/MaxScale-HA-with-lsyncd.md) - [How Errors are Handled in MaxScale](Reference/How-errors-are-handled-in-MaxScale.md) - [Debug and Diagnostic Support](Reference/Debug-And-Diagnostic-Support.md) + - [Routing Hints](Reference/Hint-Syntax.md) ## Tutorials @@ -70,10 +72,6 @@ Here are detailed documents about the filters MaxScale offers. They contain conf - [RabbitMQ Consumer Client](filters/RabbitMQ-Consumer-Client.md) -## Routers - - - [Simple Schema Sharding Router](routers/schemarouter/SchemaRouter.md) - ## Design Documents - [Core Objects Design (in development)](http://mariadb-corporation.github.io/MaxScale/Design-Documents/core-objects-html-docs) @@ -89,4 +87,7 @@ Here are detailed documents about the filters MaxScale offers. They contain conf - [MaxScale 1.0 Release Notes](Release-Notes/MaxScale-1.0-Release-Notes.md) - [MaxScale 1.0.1 Release Notes](Release-Notes/MaxScale-1.0.1-Release-Notes.md) - [MaxScale 1.0.3 Release Notes](Release-Notes/MaxScale-1.0.3-Release-Notes.md) + - [MaxScale 1.1.0 Release Notes](Release-Notes/MaxScale-1.1-Release-Notes.md) + - [MaxScale 1.1.1 Release Notes](Release-Notes/MaxScale-1.1.1-Release-Notes.md) + - [MaxScale 1.2.0 Release Notes](Release-Notes/MaxScale-1.2.0-Release-Notes.md) diff --git a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md index 95236ed7e..96147c0f1 100644 --- a/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md +++ b/Documentation/Getting-Started/Building-MaxScale-from-Source-Code.md @@ -22,9 +22,9 @@ After following the instructions on that site you should have a working MariaDB The full list of dependencies for the most common distributions is provided in this section. If your system is not listed here, MaxScale building isn't guaranteed to be compatible but might still be successful. -## RHEL, CentOS and Fedora +## RHEL and CentOS -You will need to install all of the following packages for all versions of RHEL, CentOS and Fedora. +You will need to install all of the following packages for all versions of RHEL and CentOS. ``` gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc perl make libtool @@ -39,7 +39,7 @@ rpm-build There are also some version specific packages you need to install. -#### RHEL 6, 7, CentOS 6, 7, Fedora: +#### RHEL 6, 7, CentOS 6, 7: ``` libedit-devel @@ -51,17 +51,11 @@ libedit-devel mariadb-devel mariadb-embedded-devel ``` -#### RHEL 5, 6, CentOS 5, 6, Fedora 19, 20 +#### RHEL 5, 6, CentOS 5, 6 ``` MariaDB-devel MariaDB-server ``` -#### Fedora 19, 20 - -``` -systemtap-sdt-devel -``` - ## Ubuntu and Debian These packages are required on all versions of Ubuntu and Debian. @@ -87,16 +81,13 @@ You will also need some version specific packages. #### Earlier versions of Ubuntu or Debian -For these, you will need to obtain the MariaDB embedded library. It has to be manually extracted from the tarball. But first ascertain what version of glibc is installed. Run the command: +For these, you will need to obtain the MariaDB embedded library. It has to be manually extracted from the tarballs at the MariaDB site. But first ascertain what version of glibc is installed. Run the command: ``` dpkg -l | grep libc6 ``` -which will show the version number. If the version is less than 2.14 you should obtain the library from: -[https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-x86_64/mariadb-5.5.41-linux-x86_64.tar.gz](https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-x86_64/mariadb-5.5.41-linux-x86_64.tar.gz). -Otherwise, from: -[https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-glibc_214-x86_64/mariadb-5.5.41-linux-glibc_214-x86_64.tar.gz](https://downloads.mariadb.org/interstitial/mariadb-5.5.41/bintar-linux-glibc_214-x86_64/mariadb-5.5.41-linux-glibc_214-x86_64.tar.gz) +which will show the version number. For versions older than 2.14 you should obtain the library which supports GLIBC versions older than 2.14 and for newer versions, the library which supports newer GLIBC versions should be used. The suggested location for extracting the tarball is `/usr` so the operation can be done by the following commands: @@ -107,22 +98,6 @@ The suggested location for extracting the tarball is `/usr` so the operation can where /path/to/mariadb.library.tar.gz is replaced by the actual path and name of the downloaded tarball. -## OpenSUSE - -At the time this guide was written, the MariaDB development packages for OpenSUSE were broken and the build failed. - -The packages required are: - -``` -gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc_s1 perl -make libtool libopenssl-devel libaio libaio-devel -libedit-devel librabbitmq-devel - MariaDB-devel MariaDB-client MariaDB-server -``` - -If zypper ask which MariaDB client should be installed `MariaDB-client` or `mariadb-client` - please select `MariaDB-client`. This is the package provided by the MariaDB repository. - # Obtaining the MaxScale Source Code Now clone the GitHub project to your machine either via the web interface, your favorite graphical interface or the git command line @@ -152,16 +127,15 @@ wipe the build directory clean without the danger of deleting important files wh something goes wrong. Building 'out-of-source' also allows you to have multiple configurations of MaxScale at the same time. -The default values that CMake uses can be found in the 'macros.cmake' file. -If you wish to change these, edit the 'macros.cmake' file or define the -variables manually at configuration time. +The default values that MaxScale uses for CMake can be found in the 'macros.cmake' file under the `cmake` folder. +If you wish to change these, edit the 'macros.cmake' file or define the variables manually at configuration time. To display all CMake variables with their descriptions: ``` cmake .. -LH ``` -This is a useful command if you have your libraries installed in non-standard locations. +This is a useful command if you have your libraries installed in non-standard locations and need to provide them manually. When you are ready to run cmake, provide the following command: @@ -241,7 +215,7 @@ $ make install This will result in an installation being created which is identical to that which would be achieved by installing the binary package. -By default, MaxScale installs to `/usr/local/mariadb-maxscale` and places init.d scripts and ldconfig files into their folders. Change the `CMAKE_INSTALL_PREFIX` variable to your desired installation directory and set `WITH_SCRIPTS=N` to prevent the init.d script and ldconfig file installation. +When building from source, MaxScale installs to `/usr/local/` and places init.d scripts and ldconfig files into their folders. Change the `CMAKE_INSTALL_PREFIX` variable to your desired installation directory and set `WITH_SCRIPTS=N` to prevent the init.d script and ldconfig file installation. Other useful targets for Make are `documentation`, which generates the Doxygen documentation, and `uninstall` which uninstall MaxScale binaries after an install. diff --git a/Documentation/Reference/Hint-Syntax.md b/Documentation/Reference/Hint-Syntax.md index 264d264f6..b84bb85fd 100644 --- a/Documentation/Reference/Hint-Syntax.md +++ b/Documentation/Reference/Hint-Syntax.md @@ -1,42 +1,97 @@ # Hint Syntax -Use either ’-- ’ (notice the whitespace) or ’#’ after the semicolon or ’/* .. */’ before -the semicolon. +## Enabling routing hints -The MySQL manual doesn’t specify if comment blocks, i.e. ’/* .. */’, should contain a w -hitespace character before or after the tags. +To enable routing hints for a service, the hintfilter module needs to be configured and the filter needs to be applied to the service. -All hints must start with the ’maxscale tag’: - -- maxscale - -The hints right now have two types, ones that route to a server and others that contain +Here is an example service which has the hint filter configured and applied. + +``` +[Read Service] +type=service +router=readconnroute +router_options=master +servers=server1 +user=maxuser +passwd=maxpwd +filter=Hint + +[Hint] +type=filter +module=hintfilter + +``` + +## Comments and comment types + +The client connection will need to have comments enabled. For example the `mysql` command line client has comments disabled by default. + +For comment types, use either `-- ` (notice the whitespace) or `#` after the semicolon or `/* .. */` before the semicolon. All comment types work with routing hints. + +The MySQL manual doesn`t specify if comment blocks, i.e. `/* .. */`, should contain a w +hitespace character before or after the tags, so adding whitespace at both the start and the end is advised. + +## Hint body + +All hints must start with the `maxscale` tag. + +``` +-- maxscale +``` + +The hints have two types, ones that route to a server and others that contain name-value pairs. -Routing queries to a server: +###Routing destination hints + +These hints will instruct the router to route a query to a certain type of a server. +``` -- maxscale route to [master | slave | server ] +``` -The name of the server is the same as in maxscale.cnf +A `master` value in a routing hint will route the query to a master server. This can be used to direct read queries to a master server for a up-to-date result with no replication lag. A `slave` value will route the query to a slave server. A `server` value will route the query to a named server. The value of needs to be the same as the server section name in maxscale.cnf. -Creating a name-value pair: +### Name-value hints + +These control the behavior and affect the routing decisions made by the router. + +``` -- maxscale = +``` -Currently the only accepted parameter is -’max_slave_replication_lag’ +Currently the only accepted parameter is `max_slave_replication_lag`. This will route the query to a server with lower replication lag then what is defined in the hint value. + +## Hint stack Hints can be either single-use hints, which makes them affect only one query, or named hints, which can be pushed on and off a stack of active hints. Defining named hints: + +``` -- maxscale prepare +``` Pushing a hint onto the stack: + +``` -- maxscale begin +``` Popping the topmost hint off the stack: + +``` -- maxscale end +``` You can define and activate a hint in a single command using the following: + +``` -- maxscale begin +``` You can also push anonymous hints onto the stack which are only used as long as they are on the stack: --- maxscale begin \ No newline at end of file + +``` +-- maxscale begin +``` diff --git a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md index f85382b3c..43560ddff 100644 --- a/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.2.0-Release-Notes.md @@ -24,3 +24,76 @@ A quick list of changes in installation directories and file names: * Data directory is `/var/lib/maxscale`. This is the default location for MaxScale-specific data. * PID file can be found at `/var/run/maxscale` +### Client side SSL encryption +MaxScale now supports SSL/TLS encrypted connections to MaxScale. + +### Launchable scripts +Now you can configure MaxScale monitor module to automatically launch a script when it detects change in the state of a backend server. The script can be any customer script defined by you to take diagnostic or reporting action. With this you can easily customize MaxScale's behavior. + +### Lsyncd configuration guide +A new tutorial has beed added which helps you keep MaxScale's configuration files in sync across multiple hosts. This allows for easier HA setups with MaxScale and guarantees up-to-date configuration files on all nodes. The tutorial can be found [here](../Reference/MaxScale-HA-with-lsyncd.md). + +## Bug fixes + +Here is a list of bugs fixed since the release of MaxScale 1.1.1. + + * [MXS-24](https://mariadb.atlassian.net/browse/MXS-24): bugzillaId-604: Module load path documentation issues ... + * [MXS-40](https://mariadb.atlassian.net/browse/MXS-40): Display logged in users + * [MXS-113](https://mariadb.atlassian.net/browse/MXS-113): MaxScale seems to fail if built against MariaDB 10.0 libraries + * [MXS-116](https://mariadb.atlassian.net/browse/MXS-116): Do not run maxscale as root. + * [MXS-117](https://mariadb.atlassian.net/browse/MXS-117): Allow configuration of the log file directory + * [MXS-125](https://mariadb.atlassian.net/browse/MXS-125): inconsistency in maxkeys/maxpassword output and parameters + * [MXS-128](https://mariadb.atlassian.net/browse/MXS-128): cyclic dependency utils -> log_manager -> utils + * [MXS-136](https://mariadb.atlassian.net/browse/MXS-136): Check for MaxScale replication heartbeat table existence before creating + * [MXS-137](https://mariadb.atlassian.net/browse/MXS-137): cannot get sql for queries with length >= 0x80 + * [MXS-139](https://mariadb.atlassian.net/browse/MXS-139): Schemarouter authentication for wildcard grants fails without optimize_wildcard + * [MXS-140](https://mariadb.atlassian.net/browse/MXS-140): strip_db_esc does not work without auth_all_servers + * [MXS-162](https://mariadb.atlassian.net/browse/MXS-162): Fix Incorrect info in Configuration Guide + * [MXS-165](https://mariadb.atlassian.net/browse/MXS-165): Concurrency issue while incrementing sessions in qlafilter + * [MXS-166](https://mariadb.atlassian.net/browse/MXS-166): Memory leak when creating a new event + * [MXS-171](https://mariadb.atlassian.net/browse/MXS-171): Allow reads on master for readwritesplit + * [MXS-176](https://mariadb.atlassian.net/browse/MXS-176): Missing dependencies in documentation + * [MXS-179](https://mariadb.atlassian.net/browse/MXS-179): Keep configuration changes in synch across MaxScale Mate Nodes + * [MXS-180](https://mariadb.atlassian.net/browse/MXS-180): MariaDB10 binlog router compatibilty + * [MXS-181](https://mariadb.atlassian.net/browse/MXS-181): Poor performance on TCP connection due to Nagle's algoritm + * [MXS-182](https://mariadb.atlassian.net/browse/MXS-182): SHOW SLAVE STATUS and maxadmin "show services" for binlog router needs updated when used with MariaDB 10 Master + * [MXS-212](https://mariadb.atlassian.net/browse/MXS-212): Stopped services accept connections + * [MXS-225](https://mariadb.atlassian.net/browse/MXS-225): RPM Debug build packages have no debugging symbols + * [MXS-227](https://mariadb.atlassian.net/browse/MXS-227): Memory leak in Galera Monitor + * [MXS-244](https://mariadb.atlassian.net/browse/MXS-244): Memory leak when using prepared statements without arguments + +## Known Issues and Limitations + +There are a number bugs and known limitations within this version of MaxScale, the most serious of this are listed below. + +* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries. + +* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale. + +* LONGBLOB are currently not supported. + +* Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser. + +* The Database Firewall filter does not support multi-statements. Using them will result in an error being sent to the client. + +## Packaging + +Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide + +* CentOS/RedHat 5 + +* CentOS/RedHat 6 + +* CentOS/RedHat 7 + +* Debian 6 + +* Debian 7 + +* Ubuntu 12.04 LTS + +* Ubuntu 14.04 LTS + +* SuSE Linux Enterprise 11 + +* SuSE Linux Enterprise 12 diff --git a/Documentation/Upgrading-To-MaxScale-1.2.0.md b/Documentation/Upgrading-To-MaxScale-1.2.0.md new file mode 100644 index 000000000..a48750076 --- /dev/null +++ b/Documentation/Upgrading-To-MaxScale-1.2.0.md @@ -0,0 +1,24 @@ +# Upgrading MaxScale from 1.1 to 1.2 + +This document describes upgrading MaxScale from version 1.1.1 to 1.2 and the major differences in the new version compared to the old version. The major changes can be found in the `Changelog.txt` file in the installation directory and the official release notes in the `ReleaseNotes.txt` file. + +## Installation + +Before starting the upgrade, we recommend you back up your configuration, log and binary log files in `/usr/local/mariadb-maxscale/`. + +Upgrading MaxScale will copy the `MaxScale.cnf` file in `/usr/local/mariadb-maxscale/etc/` to `/etc/` and renamed to `maxscale.cnf`. Binary log files are not automatically copied and should be manually moved from `/usr/local/mariadb-maxscale` to `/var/lib/maxscale/`. + +## File location changes + +MaxScale 1.2 follows the [FHS-standard](http://www.pathname.com/fhs/) and installs to `/usr/` and `/var/` subfolders. Here are the major changes and file locations. + +* Configuration files are located in `/etc/` and use lowercase letters: `/etc/maxscale.cnf` +* Binary files are in `/usr/bin/` +* Libraries and modules are in `/usr/lib64/maxscale/`. If you are using custom modules, please make sure they are in this directory before starting MaxScale. +* Log files are in the `var/log/maxscale/` folder +* MaxScale's PID file is located in `/var/run/maxscale/maxscale.pid` +* Data files and other persistent files are in `/var/lib/maxscale/` + +## Running MaxScale without root permissions + +MaxScale can run as a non-root user with the 1.2 version. RPM and DEB packages install the `maxscale` user and `maxscale` group which are used by the init scripts and systemd configuration files. If you are installing from a binary tarball, you can run the `postinst` script included in it to manually create these groups. diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md index 6058e0390..e58367789 100644 --- a/Documentation/routers/ReadWriteSplit.md +++ b/Documentation/routers/ReadWriteSplit.md @@ -4,7 +4,9 @@ This document provides a short overview of the **readwritesplit** router module ## Overview -The **readwritesplit** router is designed to be used with a Master-Slave replication cluster. It automatically detects changes in the master server and will use the current master server of the cluster. With a Galera cluster, one can achieve a resilient setup and easy master failover by using one of the Galera nodes as a Write-Master node, where are write queries are routed, and spreading the read load over all the nodes. +The **readwritesplit** router is designed to increase the read-only processing capability of a cluster while maintaining consistency. This is achieved by splitting the query load into read and write queries. Read queries, which do not modify data, are spread across multiple nodes while all write queries will be sent to a single node. + +The router is designed to be used with a traditional Master-Slave replication cluster. It automatically detects changes in the master server and will use the current master server of the cluster. With a Galera cluster, one can achieve a resilient setup and easy master failover by using one of the Galera nodes as a Write-Master node, where all write queries are routed, and spreading the read load over all the nodes. ## Configuration @@ -91,6 +93,13 @@ disable_sescmd_history=true disable_slave_recovery=true ``` +**`master_accept_reads`** allows the master server to be used for reads. This is a useful option to enable if you are using a small number of servers and wish to use the master for reads as well. + +``` +# Use the master for reads +master_accept_reads=true +``` + ## Limitations In Master-Slave replication cluster also read-only queries are routed to master too in the following situations: diff --git a/cmake/macros.cmake b/cmake/macros.cmake index c287343fb..d7921f1c4 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -9,17 +9,20 @@ macro(set_maxscale_version) # MaxScale version number set(MAXSCALE_VERSION_MAJOR "1") - set(MAXSCALE_VERSION_MINOR "1") - set(MAXSCALE_VERSION_PATCH "1") + set(MAXSCALE_VERSION_MINOR "2") + set(MAXSCALE_VERSION_PATCH "0") set(MAXSCALE_VERSION_NUMERIC "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}") set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}") # This should be incremented each time a package is rebuilt - set(MAXSCALE_BUILD_NUMBER 2) + set(MAXSCALE_BUILD_NUMBER 1) endmacro() macro(set_variables) + # Use C99 + set(USE_C99 FALSE CACHE BOOL "Use C99 standard") + # hostname or IP address of MaxScale's host set(TEST_HOST "127.0.0.1" CACHE STRING "hostname or IP address of MaxScale's host") diff --git a/cmake/testall.cmake b/cmake/testall.cmake index 65c27fa5a..839029312 100644 --- a/cmake/testall.cmake +++ b/cmake/testall.cmake @@ -1,4 +1,4 @@ -execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -f ${CMAKE_BINARY_DIR}/maxscale.cnf --logdir=${CMAKE_BINARY_DIR}/ --datadir=${CMAKE_BINARY_DIR}/ --cachedir=${CMAKE_BINARY_DIR}/ &> ${CMAKE_BINARY_DIR}/maxscale.output" +execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -f ${CMAKE_BINARY_DIR}/maxscale.cnf --logdir=${CMAKE_BINARY_DIR}/ --datadir=${CMAKE_BINARY_DIR}/ --cachedir=${CMAKE_BINARY_DIR}/ --piddir=${CMAKE_BINARY_DIR}/ &> ${CMAKE_BINARY_DIR}/maxscale.output" OUTPUT_VARIABLE MAXSCALE_OUT) execute_process(COMMAND make test RESULT_VARIABLE RVAL) execute_process(COMMAND killall maxscale) diff --git a/etc/postrm.in b/etc/postrm.in index 65c0f7116..7488ebe46 100755 --- a/etc/postrm.in +++ b/etc/postrm.in @@ -3,7 +3,9 @@ if [ "$1" -eq 0 ] then rm -f /etc/init.d/maxscale rm -f /etc/ld.so.conf.d/maxscale.conf + rm -f /usr/lib/systemd/system/maxscale.service else + # Copy and rename config from old location if [ -f "/usr/local/mariadb-maxscale/etc/MaxScale.cnf" ] then cp "/usr/local/mariadb-maxscale/etc/MaxScale.cnf" "/etc/maxscale.cnf" diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 5eda099ad..912882227 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1395,7 +1395,7 @@ int skygw_log_write_flush( /** * Write log string to buffer and add to file write list. */ - for(i = LOGFILE_FIRST;i<=LOGFILE_LAST;i <<=1) + for (i = LOGFILE_FIRST; i #include #include -#define _XOPEN_SOURCE #include #include #include diff --git a/server/core/config.c b/server/core/config.c index f62591e47..a5adca7f6 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -311,7 +311,7 @@ if((monitorhash = hashtable_alloc(5,simple_str_hash,strcmp)) == NULL) return 0; } -hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); +hashtable_memory_fns(monitorhash,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,NULL); /** * Process the data and create the services and servers defined * in the data. @@ -429,7 +429,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); /** Add the 5.5.5- string to the start of the version string if * the version string starts with "10.". - * This mimics MariaDB 10.0 behavior which adds 5.5.5- for backwards compatibility. */ + * This mimics MariaDB 10.0 replication which adds 5.5.5- for backwards compatibility. */ if(strncmp(version_string,"10.",3) == 0) { ((SERVICE *)(obj->element))->version_string = malloc((strlen(version_string) + @@ -940,7 +940,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL); /* if id is not set, do it now */ if (gateway.id == 0) { setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address); - gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + port != NULL ? atoi(port) : 0 + getpid()); + gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + (port != NULL ? atoi(port) : 0 + getpid())); } if (service && socket && protocol) { diff --git a/server/core/dbusers.c b/server/core/dbusers.c index f4e0ed080..6748a1437 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -974,18 +974,10 @@ getAllUsers(SERVICE *service, USERS *users) } } - if(havedb && wildcard_db_grant(dbnm)) + if(havedb && wildcard_db_grant(dbnm) && service->optimize_wildcard) { - if(service->optimize_wildcard) - { - rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); - skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); - } - else - { - /** Use ANYDB for wildcard grants */ - rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL); - } + rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); + skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); } else { @@ -1041,8 +1033,8 @@ getAllUsers(SERVICE *service, USERS *users) } else if(rc == -1) { /** Duplicate user*/ - LOGIF(LE,(skygw_log_write(LT|LE, - "Warning: Duplicate MySQL user found for service [%s]: %s@%s%s%s", + LOGIF(LT,(skygw_log_write(LT, + "Duplicate MySQL user found for service [%s]: %s@%s%s%s", service->name, row[0],row[1],havedb?" for database: ":"", havedb ?dbnm:""))); @@ -1706,6 +1698,41 @@ static int uh_cmpfun( void* v1, void* v2) { return 0; } + if(hu2->resource && strlen(hu2->resource) && strchr(hu2->resource,'%') != NULL) + { + regex_t re; + char db[MYSQL_DATABASE_MAXLEN*2 +1]; + strcpy(db,hu2->resource); + int len = strlen(db); + char* ptr = strrchr(db,'%'); + + if(ptr == NULL) + { + return 1; + } + + while(ptr) + { + memmove(ptr+1,ptr,(len - (ptr - db)) + 1); + *ptr = '.'; + *(ptr + 1) = '*'; + len = strlen(db); + ptr = strrchr(db,'%'); + } + + if((regcomp(&re,db,REG_ICASE|REG_NOSUB))) + { + return 1; + } + + if(regexec(&re,hu1->resource,0,NULL,0) == 0) + { + regfree(&re); + return 0; + } + regfree(&re); + } + /* no matches, deny auth */ return 1; } @@ -1815,18 +1842,6 @@ char *mysql_format_user_entry(void *data) return mysql_user; } -/* - * The hash function we use for storing MySQL database names. - * - * @param key The key value - * @return The hash key - */ -int -resource_hash(char *key) -{ - return (*key + *(key + 1)); -} - /** * Remove the resources table * @@ -1850,7 +1865,7 @@ resource_alloc() { HASHTABLE *resources; - if ((resources = hashtable_alloc(10, resource_hash, strcmp)) == NULL) + if ((resources = hashtable_alloc(10, simple_str_hash, strcmp)) == NULL) { return NULL; } diff --git a/server/core/dcb.c b/server/core/dcb.c index fe0ca7639..2a3d78ea1 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -49,8 +49,11 @@ * backend * 07/05/2014 Mark Riddoch Addition of callback mechanism * 20/06/2014 Mark Riddoch Addition of dcb_clone - * 29/05/2015 Markus Makela Addition of dcb_write_SSL + * 29/05/2015 Markus Makela Addition of dcb_write_SSL * 11/06/2015 Martin Brampton Persistent connnections and tidy up + * 07/07/2015 Martin Brampton Merged add to zombieslist into dcb_close, + * fixes for various error situations, + * remove dcb_set_state etc, simplifications. * * @endverbatim */ @@ -59,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -86,10 +90,6 @@ static SPINLOCK dcbspin = SPINLOCK_INIT; static SPINLOCK zombiespin = SPINLOCK_INIT; static void dcb_final_free(DCB *dcb); -static bool dcb_set_state_nolock( - DCB *dcb, - const dcb_state_t new_state, - dcb_state_t *old_state); static void dcb_call_callback(DCB *dcb, DCB_REASON reason); static DCB * dcb_get_next (DCB *dcb); static int dcb_null_write(DCB *dcb, GWBUF *buf); @@ -237,52 +237,6 @@ dcb_free(DCB *dcb) dcb_final_free(dcb); } -/** - * Add the DCB to the end of zombies list. - * - * Adding to list occurs once per DCB. This is ensured by changing the - * state of DCB to DCB_STATE_ZOMBIE after addition. Prior insertion, DCB state - * is checked and operation proceeds only if state differs from DCB_STATE_ZOMBIE. - * @param dcb The DCB to add to the zombie list - * @return none - */ -void -dcb_add_to_zombieslist(DCB *dcb) -{ - bool succp = false; - dcb_state_t prev_state = DCB_STATE_UNDEFINED; - - CHK_DCB(dcb); - - /*< - * Protect zombies list access. - */ - spinlock_acquire(&zombiespin); - /*< - * If dcb is already added to zombies list, return. - */ - if (dcb->state != DCB_STATE_NOPOLLING) { - ss_dassert(dcb->state != DCB_STATE_POLLING && - dcb->state != DCB_STATE_LISTENING); - spinlock_release(&zombiespin); - return; - } - - /*< - * Add closing dcb to the top of the list. - */ - dcb->memdata.next = zombies; - zombies = dcb; - /*< - * Set state which indicates that it has been added to zombies - * list. - */ - succp = dcb_set_state(dcb, DCB_STATE_ZOMBIE, &prev_state); - ss_info_dassert(succp, "Failed to set DCB_STATE_ZOMBIE"); - - spinlock_release(&zombiespin); -} - /* * Clone a DCB for internal use, mostly used for specialist filters * to create dummy clients based on real clients. @@ -599,8 +553,7 @@ bool succp = false; &tls_log_info.li_sesid, &tls_log_info.li_enabled_logs))); - succp = dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL); - ss_dassert(succp); + dcb->state = DCB_STATE_DISCONNECTED; nextdcb = dcb->memdata.next; dcb_final_free(dcb); dcb = nextdcb; @@ -615,7 +568,7 @@ bool succp = false; * Connect to a server * * This routine will create a server connection - * If succesful the new dcb will be put in + * If successful the new dcb will be put in * epoll set by dcb->func.connect * * @param server The server to connect to @@ -626,11 +579,11 @@ bool succp = false; DCB * dcb_connect(SERVER *server, SESSION *session, const char *protocol) { -DCB *dcb; -GWPROTOCOL *funcs; -int fd; -int rc; -char *user; + DCB *dcb; + GWPROTOCOL *funcs; + int fd; + int rc; + char *user; user = session_getUser(session); if (user && strlen(user)) @@ -677,7 +630,7 @@ char *user; if ((funcs = (GWPROTOCOL *)load_module(protocol, MODULE_PROTOCOL)) == NULL) { - dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL); + dcb->state = DCB_STATE_DISCONNECTED; dcb_final_free(dcb); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -716,7 +669,7 @@ char *user; dcb, session->client, session->client->fd))); - dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL); + dcb->state = DCB_STATE_DISCONNECTED; dcb_final_free(dcb); return NULL; } else { @@ -758,10 +711,10 @@ char *user; */ rc = poll_add_dcb(dcb); - if (rc == DCBFD_CLOSED) { - dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL); - dcb_final_free(dcb); - return NULL; + if (rc) { + dcb->state = DCB_STATE_DISCONNECTED; + dcb_final_free(dcb); + return NULL; } /** * The dcb will be addded into poll set by dcb->func.connect @@ -973,10 +926,10 @@ int dcb_read_n( goto return_n; } - if (b == 0 && nread == 0) + if (b == 0) { /** Handle closed client socket */ - if (dcb_isclient(dcb)) + if (nread == 0 && dcb_isclient(dcb)) { char c; int l_errno = 0; @@ -998,11 +951,6 @@ int dcb_read_n( n = 0; goto return_n; } - else if (b == 0) - { - n = 0; - goto return_n; - } dcb->last_read = hkheartbeat; @@ -1242,6 +1190,10 @@ int dcb_read_SSL( } gwbuf_rtrim(buffer,bufsize - n); + if(buffer == NULL) + { + goto return_n; + } #ifdef SS_DEBUG skygw_log_write(LD,"%lu SSL: Truncated buffer from %d to %d bytes. " "Read %d bytes, %d bytes waiting.\n",pthread_self(), @@ -1268,12 +1220,9 @@ int dcb_read_SSL( /*< Append read data to the gwbuf */ *head = gwbuf_append(*head, buffer); - rc = ioctl(dcb->fd, FIONREAD, &b); - pending = SSL_pending(dcb->ssl); - } /*< while (true) */ return_n: - return n; + return nread; } /** * General purpose routine to write to a DCB @@ -1628,95 +1577,116 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) } #endif /* FAKE_CODE */ qlen = GWBUF_LENGTH(queue); - - w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), qlen); - dcb->stats.n_writes++; - - if (w < 0) + do { - int ssl_errno = SSL_get_error(dcb->ssl,w); + w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), qlen); + dcb->stats.n_writes++; - if (LOG_IS_ENABLED(LOGFILE_DEBUG)) + if (w <= 0) { - switch(ssl_errno) + int ssl_errno = SSL_get_error(dcb->ssl,w); + + if (LOG_IS_ENABLED(LOGFILE_DEBUG)) { - case SSL_ERROR_WANT_READ: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error SSL_ERROR_WANT_READ", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); - break; - case SSL_ERROR_WANT_WRITE: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error SSL_ERROR_WANT_WRITE", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); - break; - default: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error %d", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd,ssl_errno))); - if(ssl_errno == SSL_ERROR_SSL || - ssl_errno == SSL_ERROR_SYSCALL) + switch(ssl_errno) { - while((ssl_errno = ERR_get_error()) != 0) + case SSL_ERROR_WANT_READ: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_READ", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + break; + case SSL_ERROR_WANT_WRITE: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_WRITE", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd))); + break; + default: + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error %d", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd,ssl_errno))); + break; + } + } + + if (LOG_IS_ENABLED(LOGFILE_ERROR) && ssl_errno != SSL_ERROR_WANT_WRITE) + { + if (ssl_errno == -1) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Write to dcb %p in " + "state %s fd %d failed due to " + "SSL error %d", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno))); + if(ssl_errno == SSL_ERROR_SSL || ssl_errno == SSL_ERROR_SYSCALL) { - char errbuf[140]; - ERR_error_string(ssl_errno,errbuf); - skygw_log_write(LE,"%s",errbuf); + if(ssl_errno == SSL_ERROR_SYSCALL) + { + skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + } + do + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); + }while((ssl_errno = ERR_get_error()) != 0); } } + else if(w == 0) + { + do + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); + }while((ssl_errno = ERR_get_error()) != 0); + } + } + + if(ssl_errno != SSL_ERROR_WANT_WRITE) break; - } - } - - if (LOG_IS_ENABLED(LOGFILE_ERROR)) - { - if (ssl_errno != 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write to dcb %p in " - "state %s fd %d failed due " - "SSL error %d", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ssl_errno))); - } } + }while(w <= 0); + + if(w <= 0) + { break; } - /* - * Pull the number of bytes we have written from - * queue with have. - */ - queue = gwbuf_consume(queue, w); - LOGIF(LD, (skygw_log_write( + else + { + /** Remove written bytes from the queue */ + queue = gwbuf_consume(queue, w); + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [dcb_write] Wrote %d Bytes to dcb %p in " - "state %s fd %d", + "state %s fd %d", pthread_self(), w, dcb, STRDCBSTATE(dcb->state), dcb->fd))); + } } /*< while (queue != NULL) */ /*< * What wasn't successfully written is stored to write queue @@ -1734,38 +1704,6 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) } } /* if (dcb->writeq) */ - if (saved_errno != 0 && - queue != NULL && - saved_errno != EAGAIN && - saved_errno != EWOULDBLOCK) - { - bool dolog = true; - - /** - * Do not log if writing COM_QUIT to backend failed. - */ - if (GWBUF_IS_TYPE_MYSQL(queue)) - { - uint8_t* data = GWBUF_DATA(queue); - - if (data[4] == 0x01) - { - dolog = false; - } - } - if (dolog) - { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Writing to %s socket failed due %d, %s.", - pthread_self(), - dcb_isclient(dcb) ? "client" : "backend server", - saved_errno, - strerror(saved_errno)))); - } - spinlock_release(&dcb->writeqlock); - return 0; - } spinlock_release(&dcb->writeqlock); if (dcb->high_water && dcb->writeqlen > dcb->high_water && below_water) @@ -1901,28 +1839,47 @@ dcb_drain_writeq_SSL(DCB *dcb) while (dcb->writeq != NULL) { len = GWBUF_LENGTH(dcb->writeq); - w = gw_write_SSL(dcb->ssl, GWBUF_DATA(dcb->writeq), len); + w = gw_write_SSL(dcb->ssl, GWBUF_DATA(dcb->writeq), len); if (w < 0) { - int ssl_errno = ERR_get_error(); + int ssl_errno = SSL_get_error(dcb->ssl,w); - if(ssl_errno == SSL_ERROR_WANT_WRITE || - ssl_errno == SSL_ERROR_WANT_ACCEPT || - ssl_errno == SSL_ERROR_WANT_READ) + if(ssl_errno == SSL_ERROR_WANT_WRITE || ssl_errno == SSL_ERROR_WANT_READ) { break; } + skygw_log_write_flush(LOGFILE_ERROR, + "Error : Write to dcb failed due to " + "SSL error %d:", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno); + switch(ssl_errno) + { + case SSL_ERROR_SSL: + case SSL_ERROR_SYSCALL: + while((ssl_errno = ERR_get_error()) != 0) + { + char errbuf[140]; + ERR_error_string(ssl_errno,errbuf); + skygw_log_write(LE,"%s",errbuf); + } + if(errno != 0) + skygw_log_write(LE,"%d:%s",errno,strerror(errno)); + break; + case SSL_ERROR_ZERO_RETURN: + skygw_log_write(LE,"Socket is closed."); + break; - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write to dcb %p " - "in state %s fd %d failed: %s", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ERR_error_string(ssl_errno,NULL)); + default: + skygw_log_write(LE,"Unexpected error."); + break; + } break; + + } /* * Pull the number of bytes we have written from @@ -1962,62 +1919,100 @@ dcb_drain_writeq_SSL(DCB *dcb) void dcb_close(DCB *dcb) { - CHK_DCB(dcb); + CHK_DCB(dcb); - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [dcb_close]", - pthread_self()))); - - /** - * dcb_close may be called for freshly created dcb, in which case - * it only needs to be freed. - */ - if (dcb->state == DCB_STATE_ALLOC) + LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, + "%lu [dcb_close] DCB %p in state %s", + pthread_self(), + dcb, + dcb ? STRDCBSTATE(dcb->state) : "Invalid DCB"))); + + if (DCB_STATE_ZOMBIE == dcb->state) + { + return; + } + + if (DCB_STATE_UNDEFINED == dcb->state + || DCB_STATE_DISCONNECTED == dcb->state) + { + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "%lu [dcb_close] Error : Removing DCB %p but was in state %s " + "which is not legal for a call to dcb_close. ", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); + raise(SIGABRT); + } + + /** + * dcb_close may be called for freshly created dcb, in which case + * it only needs to be freed. + */ + if (dcb->state == DCB_STATE_ALLOC && dcb->fd != DCBFD_CLOSED) + { + dcb_final_free(dcb); + return; + } + + /*< + * Stop dcb's listening and modify state accordingly. + */ + if (dcb->state == DCB_STATE_POLLING || dcb->state == DCB_STATE_LISTENING) + { + if (dcb->state == DCB_STATE_LISTENING) { - dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL); - dcb_final_free(dcb); - return; + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "%lu [dcb_close] Error : Removing DCB %p but was in state %s " + "which is not expected for a call to dcb_close, although it" + "should be processed correctly. ", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); } - - ss_dassert(dcb->state == DCB_STATE_POLLING || - dcb->state == DCB_STATE_NOPOLLING || - dcb->state == DCB_STATE_ZOMBIE); - - /*< - * Stop dcb's listening and modify state accordingly. - */ - if (dcb->state == DCB_STATE_POLLING) + if ((dcb->state == DCB_STATE_POLLING && !dcb_maybe_add_persistent(dcb)) + || (dcb->state == DCB_STATE_LISTENING)) { - if (!dcb_maybe_add_persistent(dcb)) + poll_remove_dcb(dcb); + /* + * Return will always be 0 or function will have crashed, so we + * threw away return value. + */ + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [dcb_close] Removed dcb %p in state %s from " + "poll set.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); + /** + * close protocol and router session + */ + if (dcb->func.close != NULL) { - if (poll_remove_dcb(dcb)) - { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : Removing DCB fd == %d in state %s from " - "poll set failed.", - dcb->fd, - STRDCBSTATE(dcb->state)))); - } - else - { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_close] Removed dcb %p in state %s from " - "poll set.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - - dcb_close_finish(dcb); - ss_dassert(dcb->state == DCB_STATE_NOPOLLING || - dcb->state == DCB_STATE_ZOMBIE); - } - ss_dassert(dcb->state == DCB_STATE_NOPOLLING || - dcb->state == DCB_STATE_ZOMBIE || - dcb->state == DCB_STATE_POLLING); + dcb->func.close(dcb); } + /** Call possible callback for this DCB in case of close */ + dcb_call_callback(dcb, DCB_REASON_CLOSE); } + } + + spinlock_acquire(&zombiespin); + if (dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ALLOC) + { + /*< + * Add closing dcb to the top of the list. + */ + dcb->memdata.next = zombies; + zombies = dcb; + /*< + * Set state which indicates that it has been added to zombies + * list. + */ + dcb->state = DCB_STATE_ZOMBIE; + } + spinlock_release(&zombiespin); } /** @@ -2097,7 +2092,7 @@ dcb_close_finish(DCB *dcb) /** Call possible callback for this DCB in case of close */ dcb_call_callback(dcb, DCB_REASON_CLOSE); /** Must be DCB_STATE_NOPOLLING when this function is called */ - dcb_add_to_zombieslist(dcb); + dcb_close(dcb); } /** @@ -2291,7 +2286,7 @@ DCB *dcb; } /** - * Diagnotic routine to print client DCB data in a tabular form. + * Diagnostic routine to print client DCB data in a tabular form. * * @param pdcb DCB to print results to */ @@ -2417,7 +2412,8 @@ dprintDCB(DCB *pdcb, DCB *dcb) * */ const char * -gw_dcb_state2string (int state) { +gw_dcb_state2string (int state) +{ switch(state) { case DCB_STATE_ALLOC: return "DCB Allocated"; @@ -2515,155 +2511,6 @@ void dcb_hashtable_stats( dcb_printf(dcb, "\tLongest chain length: %d\n", longest); } - -bool -dcb_set_state( - DCB *dcb, - const dcb_state_t new_state, - dcb_state_t *old_state_ptr) -{ - bool succp; - dcb_state_t old_state_buffer; - - CHK_DCB(dcb); - - spinlock_acquire(&dcb->dcb_initlock); - succp = dcb_set_state_nolock(dcb, new_state, &old_state_buffer); - spinlock_release(&dcb->dcb_initlock); - - if (old_state_ptr != NULL) - { - *old_state_ptr = old_state_buffer; - } - return succp; -} - -static bool -dcb_set_state_nolock( - DCB *dcb, - const dcb_state_t new_state, - dcb_state_t *old_state_ptr) -{ - bool succp = false; - bool old_state_supplied = true; - dcb_state_t state_buffer; - - CHK_DCB(dcb); - - if (NULL == old_state_ptr) - { - old_state_supplied = false; - old_state_ptr = &state_buffer; - } - *old_state_ptr = dcb->state; - - switch (*old_state_ptr) { - case DCB_STATE_UNDEFINED: - dcb->state = new_state; - succp = true; - break; - - case DCB_STATE_ALLOC: - switch (new_state) { - /** fall through, for client requests */ - case DCB_STATE_POLLING: - /** fall through, for connect listeners */ - case DCB_STATE_LISTENING: - /** for failed connections */ - case DCB_STATE_DISCONNECTED: - dcb->state = new_state; - succp = true; - default: ; - } - break; - - case DCB_STATE_POLLING: - switch(new_state) { - case DCB_STATE_NOPOLLING: - dcb->state = new_state; - succp = true; - default: ; - } - break; - - case DCB_STATE_LISTENING: - switch(new_state) { - case DCB_STATE_NOPOLLING: - dcb->state = new_state; - succp = true; - default: ; - } - break; - - case DCB_STATE_NOPOLLING: - switch (new_state) { - case DCB_STATE_ZOMBIE: /*< fall through */ - dcb->state = new_state; - case DCB_STATE_POLLING: /*< ok to try but state can't change */ - succp = true; - default: ; - } - break; - - case DCB_STATE_ZOMBIE: - switch (new_state) { - case DCB_STATE_DISCONNECTED: /*< fall through */ - dcb->state = new_state; - case DCB_STATE_POLLING: /*< ok to try but state can't change */ - succp = true; - default: ; - } - break; - - case DCB_STATE_DISCONNECTED: - switch (new_state) { - case DCB_STATE_FREED: - dcb->state = new_state; - succp = true; - default: ; - } - break; - - case DCB_STATE_FREED: - break; - - default: - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unknown dcb state %s for " - "dcb %p", - STRDCBSTATE(dcb->state), - dcb))); - ss_dassert(false); - break; - } /*< switch (dcb->state) */ - - if (succp) { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [dcb_set_state_nolock] dcb %p fd %d %s -> %s", - pthread_self(), - dcb, - dcb->fd, - STRDCBSTATE(*old_state_ptr), - STRDCBSTATE(dcb->state)))); - } - else - { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_set_state_nolock] Failed " - "to change state of DCB %p. " - "Old state %s > new state %s.", - pthread_self(), - dcb, - (old_state_ptr == NULL ? "NULL" : STRDCBSTATE(*old_state_ptr)), - STRDCBSTATE(new_state)))); - ss_dassert(old_state_supplied); - } - return succp; -} - /** * Write data to a socket through an SSL structure. The SSL structure is linked to a DCB's socket * and all communication is encrypted and done via the SSL structure. @@ -3236,7 +3083,7 @@ int dcb_accept_SSL(DCB* dcb) do { ssl_rval = SSL_accept(dcb->ssl); - errnum = SSL_get_error(dcb->ssl,ssl_rval); + LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept %d, error %d", ssl_rval,errnum))); switch(ssl_rval) diff --git a/server/core/gateway.c b/server/core/gateway.c index f206ca30e..8f486067c 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -208,6 +208,15 @@ static int set_user(); /** SSL multi-threading functions and structures */ +static SPINLOCK* ssl_locks; + +static void ssl_locking_function(int mode,int n,const char* file, int line) +{ + if(mode & CRYPTO_LOCK) + spinlock_acquire(&ssl_locks[n]); + else + spinlock_release(&ssl_locks[n]); +} /** * OpenSSL requires this struct to be defined in order to use dynamic locks */ @@ -263,6 +272,17 @@ static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, i free(n); } +#ifdef OPENSSL_1_0 +/** + * The thread ID callback function for OpenSSL dynamic locks. + * @param id Id to modify + */ +static void maxscale_ssl_id(CRYPTO_THREADID* id) +{ + CRYPTO_THREADID_set_numeric(id,pthread_self()); +} +#endif + /** * Handler for SIGHUP signal. Reload the configuration for the * gateway. @@ -1456,10 +1476,6 @@ int main(int argc, char **argv) SSL_library_init(); SSL_load_error_strings(); OPENSSL_add_all_algorithms_noconf(); - CRYPTO_set_dynlock_create_callback(ssl_create_dynlock); - CRYPTO_set_dynlock_destroy_callback(ssl_free_dynlock); - CRYPTO_set_dynlock_lock_callback(ssl_lock_dynlock); - CRYPTO_set_id_callback(pthread_self); /* register exit function for embedded MySQL library */ l = atexit(libmysqld_done); @@ -1515,17 +1531,13 @@ int main(int argc, char **argv) /** Use default log directory /var/log/maxscale/ */ if(logdir == NULL) { - - if(access(default_logdir,F_OK) != 0) - { - if(mkdir(logdir,0555) != 0) - { - fprintf(stderr, - "Error: Cannot create log directory: %s\n", - default_logdir); - goto return_main; - } - } + if(mkdir(default_logdir,0777) != 0 && errno != EEXIST) + { + fprintf(stderr, + "Error: Cannot create log directory: %s\n", + default_logdir); + goto return_main; + } logdir = strdup(default_logdir); } @@ -1582,7 +1594,7 @@ int main(int argc, char **argv) /** * Set a data directory for the mysqld library, we use * a unique directory name to avoid clauses if multiple - * instances of the gateway are beign run on the same + * instances of the gateway are being run on the same * machine. */ @@ -1793,6 +1805,26 @@ int main(int argc, char **argv) LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "MaxScale started with %d server threads.", config_threadcount()))); + int numlocks = CRYPTO_num_locks(); + if((ssl_locks = malloc(sizeof(SPINLOCK)*(numlocks + 1))) == NULL) + { + char* logerr = "Memory allocation failed"; + print_log_n_stderr(true, true, logerr, logerr, eno); + rc = MAXSCALE_INTERNALERROR; + goto return_main; + } + + for(i = 0;icon = NULL; db->next = NULL; db->mon_err_count = 0; + db->log_version_err = true; /** Server status is uninitialized */ db->mon_prev_status = -1; /* pending status is updated by get_replication_tree */ diff --git a/server/core/poll.c b/server/core/poll.c index 377310cb0..4123e0294 100644 --- a/server/core/poll.c +++ b/server/core/poll.c @@ -15,10 +15,12 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ + #include #include #include #include +#include #include #include #include @@ -71,6 +73,7 @@ int max_poll_sleep; * in the loop after the epoll_wait. This allows for better * thread utilisaiton and fairer scheduling of the event * processing. + * 07/07/15 Martin Brampton Simplified add and remove DCB, improve error handling. * * @endverbatim */ @@ -186,6 +189,11 @@ static struct { */ static void poll_loadav(void *); +/** + * Function to analyse error return from epoll_ctl + */ +static int poll_resolve_error(DCB *, int, bool); + /** * Initialise the polling system we are using for the gateway. * @@ -247,7 +255,7 @@ int poll_add_dcb(DCB *dcb) { int rc = -1; - dcb_state_t old_state = DCB_STATE_UNDEFINED; + dcb_state_t old_state = dcb->state; dcb_state_t new_state; struct epoll_event ev; @@ -263,58 +271,67 @@ poll_add_dcb(DCB *dcb) /*< * Choose new state according to the role of dcb. */ + spinlock_acquire(&dcb->dcb_initlock); if (dcb->dcb_role == DCB_ROLE_REQUEST_HANDLER) { new_state = DCB_STATE_POLLING; } else { ss_dassert(dcb->dcb_role == DCB_ROLE_SERVICE_LISTENER); new_state = DCB_STATE_LISTENING; } - /*< - * If dcb is in unexpected state, state change fails indicating that dcb - * is not polling anymore. + /* + * Check DCB current state seems sensible */ - if (dcb_set_state(dcb, new_state, &old_state)) { - rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dcb->fd, &ev); - - if (rc != 0) { - int eno = errno; - errno = 0; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Adding dcb %p in state %s " - "to poll set failed. epoll_ctl failed due " - "%d, %s.", - dcb, - STRDCBSTATE(dcb->state), - eno, - strerror(eno)))); - } else { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_add_dcb] Added dcb %p in state %s to " - "poll set.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - } - ss_info_dassert(rc == 0, "Unable to add poll"); /*< trap in debug */ - } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to set new state for dcb %p " - "in state %s. Adding to poll set failed.", - dcb, - STRDCBSTATE(dcb->state)))); + if (DCB_STATE_DISCONNECTED == dcb->state + || DCB_STATE_ZOMBIE == dcb->state + || DCB_STATE_UNDEFINED == dcb->state) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [poll_add_dcb] Error : existing state of dcb %p " + "is %s, but this should be impossible, crashing.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); + raise(SIGABRT); } - + if (DCB_STATE_POLLING == dcb->state + || DCB_STATE_LISTENING == dcb->state) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [poll_add_dcb] Error : existing state of dcb %p " + "is %s, but this is probably an error, not crashing.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); + } + dcb->state = new_state; + spinlock_release(&dcb->dcb_initlock); + /* + * The only possible failure that will not cause a crash is + * running out of system resources. + */ + rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dcb->fd, &ev); + if (rc) + { + rc = poll_resolve_error(dcb, errno, true); + } + if (0 == rc) + { + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "%lu [poll_add_dcb] Added dcb %p in state %s to poll set.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); + } + else dcb->state = old_state; return rc; } /** * Remove a descriptor from the set of descriptors within the * polling environment. - * The state change command may fail because concurrent threads may call - * dcb_set_state simultaneously and the conflict is prevented in dcb_set_state. * * @param dcb The descriptor to remove * @return -1 on error or 0 on success @@ -322,61 +339,121 @@ poll_add_dcb(DCB *dcb) int poll_remove_dcb(DCB *dcb) { - struct epoll_event ev; int rc = -1; - dcb_state_t old_state = DCB_STATE_UNDEFINED; - dcb_state_t new_state = DCB_STATE_NOPOLLING; - + struct epoll_event ev; CHK_DCB(dcb); + spinlock_acquire(&dcb->dcb_initlock); /*< It is possible that dcb has already been removed from the set */ - if (dcb->state != DCB_STATE_POLLING) - { - if (dcb->state == DCB_STATE_NOPOLLING || - dcb->state == DCB_STATE_ZOMBIE) - { - rc = 0; - } - goto return_rc; + if (dcb->state == DCB_STATE_NOPOLLING || + dcb->state == DCB_STATE_ZOMBIE) + { + spinlock_release(&dcb->dcb_initlock); + return 0; + } + if (DCB_STATE_POLLING != dcb->state + && DCB_STATE_LISTENING != dcb->state) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [poll_remove_dcb] Error : existing state of dcb %p " + "is %s, but this is probably an error, not crashing.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)))); } /*< * Set state to NOPOLLING and remove dcb from poll set. */ - if (dcb_set_state(dcb, new_state, &old_state)) - { - /** - * Only positive fds can be removed from epoll set. - */ - if (dcb->fd > 0) - { - rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev); - - if (rc != 0) { - int eno = errno; - errno = 0; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : epoll_ctl failed due %d, %s.", - eno, - strerror(eno)))); - } - ss_dassert(rc == 0); /*< trap in debug */ - } - } - /*< - * This call was redundant, but the end result is correct. + dcb->state = DCB_STATE_NOPOLLING; + /** + * Only positive fds can be removed from epoll set. + * But this test was removed by Martin as it is hard to + * see that there should ever be a situation where + * fd isn't positive and the DCB is also in the poll list. + */ + /* if (dcb->fd > 0) { */ + spinlock_release(&dcb->dcb_initlock); + rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev); + /** + * The poll_resolve_error function will always + * return 0 or crash. So if it returns non-zero result, + * things have gone wrong and we crash. */ - else if (old_state == new_state) - { - rc = 0; - goto return_rc; - } - + if (rc) rc = poll_resolve_error(dcb, errno, false); + if (rc) raise(SIGABRT); /*< Set bit for each maxscale thread */ - bitmask_copy(&dcb->memdata.bitmask, poll_bitmask()); - rc = 0; -return_rc: + bitmask_copy(&dcb->memdata.bitmask, poll_bitmask()); return rc; + /* } */ +} + +/** + * Check error returns from epoll_ctl. Most result in a crash since they + * are "impossible". Adding when already present is assumed non-fatal. + * Likewise, removing when not present is assumed non-fatal. + * It is assumed that callers to poll routines can handle the failure + * that results from hitting system limit, although an error is written + * here to record the problem. + * + * @param errornum The errno set by epoll_ctl + * @param adding True for adding to poll list, false for removing + * @return -1 on error or 0 for possibly revised return code + */ +static int +poll_resolve_error(DCB *dcb, int errornum, bool adding) +{ + if (adding) + { + if (EEXIST == errornum) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [poll_resolve_error] Error : epoll_ctl could not add, " + "already exists for DCB %p.", + pthread_self(), + dcb))); + // Assume another thread added and no serious harm done + return 0; + } + if (ENOSPC == errornum) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [poll_resolve_error] The limit imposed by " + "/proc/sys/fs/epoll/max_user_watches was " + "encountered while trying to register (EPOLL_CTL_ADD) a new " + "file descriptor on an epoll instance for dcb %p.", + pthread_self(), + dcb))); + /* Failure - assume handled by callers */ + return -1; + } + } + else + { + /* Must be removing */ + if (ENOENT == errornum) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [poll_resolve_error] Error : epoll_ctl could not remove, " + "not found, for dcb %p.", + pthread_self(), + dcb))); + // Assume another thread removed and no serious harm done + return 0; + } + } + /* Common checks for add or remove - crash MaxScale */ + if (EBADF == errornum) raise(SIGABRT); + if (EINVAL == errornum) raise(SIGABRT); + if (ENOMEM == errornum) raise(SIGABRT); + if (EPERM == errornum) raise(SIGABRT); + /* Undocumented error number */ + raise(SIGABRT); + /* The following statement should never be reached, but avoids compiler warning */ + return -1; } #define BLOCKINGPOLL 0 /*< Set BLOCKING POLL to 1 if using a single thread and to make @@ -1604,7 +1681,7 @@ RESULT_ROW *row; } /** - * Return a resultset that has the current set of services in it + * Return a result set that has the current set of services in it * * @return A Result set */ diff --git a/server/core/secrets.c b/server/core/secrets.c index 97cb49806..b0e0c5542 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -228,6 +228,12 @@ unsigned int randval; MAXKEYS key; char secret_file[PATH_MAX + 10]; +if(strlen(path) > PATH_MAX) +{ + skygw_log_write(LOGFILE_ERROR,"Error: Pathname too long."); + return 1; +} + sprintf(secret_file,"%s/.secrets",path); /* Open for writing | Create | Truncate the file for writing */ diff --git a/server/core/service.c b/server/core/service.c index 3de32aa25..9c4a4d681 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -126,7 +126,7 @@ SERVICE *service; "Error : Unable to load %s module \"%s\".\n\t\t\t" " Ensure that lib%s.so exists in one of the " "following directories :\n\t\t\t " - "- %s/modules\n%s%s", + "- %s\n%s%s", MODULE_ROUTER, router, router, @@ -269,7 +269,7 @@ GWPROTOCOL *funcs; /* Save authentication data to file cache */ char *ptr, path[PATH_MAX + 1]; int mkdir_rval = 0; - strcpy(path, get_cachedir()); + strncpy(path, get_cachedir(),PATH_MAX); strncat(path, "/", 4096); strncat(path, service->name, PATH_MAX); if (access(path, R_OK) == -1) @@ -535,11 +535,16 @@ int listeners = 0; port = service->ports; while (port) { - poll_remove_dcb(port->listener); - port->listener->session->state = SESSION_STATE_LISTENER_STOPPED; - listeners++; - - port = port->next; + if(port->listener && + port->listener->session->state == SESSION_STATE_LISTENER) + { + if(poll_remove_dcb(port->listener) == 0) + { + port->listener->session->state = SESSION_STATE_LISTENER_STOPPED; + listeners++; + } + } + port = port->next; } service->state = SERVICE_STATE_STOPPED; @@ -563,13 +568,18 @@ int listeners = 0; port = service->ports; while (port) { - if (poll_add_dcb(port->listener) == 0) { - port->listener->session->state = SESSION_STATE_LISTENER; - listeners++; - } - port = port->next; + if(port->listener && + port->listener->session->state == SESSION_STATE_LISTENER_STOPPED) + { + if(poll_add_dcb(port->listener) == 0) + { + port->listener->session->state = SESSION_STATE_LISTENER; + listeners++; + } + } + port = port->next; } - + service->state = SERVICE_STATE_STARTED; return listeners; } @@ -914,10 +924,12 @@ serviceSetSSLVersion(SERVICE *service, char* version) service->ssl_method_type = SERVICE_SSLV3; else if(strcasecmp(version,"TLSV10") == 0) service->ssl_method_type = SERVICE_TLS10; +#ifdef OPENSSL_1_0 else if(strcasecmp(version,"TLSV11") == 0) service->ssl_method_type = SERVICE_TLS11; else if(strcasecmp(version,"TLSV12") == 0) service->ssl_method_type = SERVICE_TLS12; +#endif else if(strcasecmp(version,"MAX") == 0) service->ssl_method_type = SERVICE_SSL_TLS_MAX; else return -1; @@ -1957,13 +1969,14 @@ int serviceInitSSL(SERVICE* service) case SERVICE_TLS10: service->method = (SSL_METHOD*)TLSv1_server_method(); break; +#ifdef OPENSSL_1_0 case SERVICE_TLS11: service->method = (SSL_METHOD*)TLSv1_1_server_method(); break; case SERVICE_TLS12: service->method = (SSL_METHOD*)TLSv1_2_server_method(); break; - +#endif /** Rest of these use the maximum available SSL/TLS methods */ case SERVICE_SSL_MAX: service->method = (SSL_METHOD*)SSLv23_server_method(); diff --git a/server/core/test/testdcb.c b/server/core/test/testdcb.c index 2703763f0..13462fc87 100644 --- a/server/core/test/testdcb.c +++ b/server/core/test/testdcb.c @@ -64,7 +64,7 @@ int buflen; 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); + dcb_close(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); diff --git a/server/include/dcb.h b/server/include/dcb.h index fb0bc33c6..057f092a6 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -331,7 +331,6 @@ const char *gw_dcb_state2string(int); /* DCB state to string */ void dcb_printf(DCB *, const char *, ...); /* DCB version of printf */ int dcb_isclient(DCB *); /* the DCB is the client of the session */ void dcb_hashtable_stats(DCB *, void *); /**< Print statisitics */ -void dcb_add_to_zombieslist(DCB* dcb); int dcb_add_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON, void *), void *); int dcb_remove_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON, void *), @@ -340,7 +339,6 @@ int dcb_isvalid(DCB *); /* Check the DCB is in the linked list */ int dcb_count_by_usage(DCB_USAGE); /* Return counts of DCBs */ int dcb_persistent_clean_count(DCB *, bool); /* Clean persistent and return count */ -bool dcb_set_state(DCB* dcb, dcb_state_t new_state, dcb_state_t* old_state); void dcb_call_foreach (struct server* server, DCB_REASON reason); size_t dcb_get_session_id(DCB* dcb); bool dcb_get_ses_log_info(DCB* dcb, size_t* sesid, int* enabled_logs); diff --git a/server/include/monitor.h b/server/include/monitor.h index 681f34bac..442efb27f 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -124,6 +124,7 @@ typedef enum typedef struct monitor_servers { SERVER *server; /**< The server being monitored */ MYSQL *con; /**< The MySQL connection */ + bool log_version_err; int mon_err_count; unsigned int mon_prev_status; unsigned int pending_status; /**< Pending Status flag bitmap */ diff --git a/server/include/service.h b/server/include/service.h index 3337ebfc0..b64270b9e 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -117,8 +117,10 @@ typedef enum { enum{ SERVICE_SSLV3, SERVICE_TLS10, +#ifdef OPENSSL_1_0 SERVICE_TLS11, SERVICE_TLS12, +#endif SERVICE_SSL_MAX, SERVICE_TLS_MAX, SERVICE_SSL_TLS_MAX diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 72171b253..6f6d61fb6 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -24,8 +24,9 @@ * @verbatim * Revision History * - * Date Who Description - * 02/04/14 Mark Riddoch Initial implementation + * Date Who Description + * 02/04/14 Mark Riddoch Initial implementation + * 11/05/15 Massimilaino Pinto Added mariadb10_compat to master and slave structs * * @endverbatim */ @@ -176,6 +177,7 @@ typedef struct router_slave { uint32_t lastEventTimestamp;/*< Last event timestamp sent */ SPINLOCK catch_lock; /*< Event catchup lock */ unsigned int cstate; /*< Catch up state */ + bool mariadb10_compat;/*< MariaDB 10.0 compatibility */ SPINLOCK rses_lock; /*< Protects rses_deleted */ pthread_t pthread; struct router_instance @@ -234,6 +236,7 @@ typedef struct { GWBUF *selectvercom; /*< select @@version_comment */ GWBUF *selecthostname;/*< select @@hostname */ GWBUF *map; /*< select @@max_allowed_packet */ + GWBUF *mariadb10; /*< set @mariadb_slave_capability */ uint8_t *fde_event; /*< Format Description Event */ int fde_len; /*< Length of fde_event */ } MASTER_RESPONSES; @@ -242,54 +245,54 @@ typedef struct { * The per instance data for the router. */ typedef struct router_instance { - SERVICE *service; /*< Pointer to the service using this router */ - ROUTER_SLAVE *slaves; /*< Link list of all the slave connections */ - SPINLOCK lock; /*< Spinlock for the instance data */ - char *uuid; /*< UUID for the router to use w/master */ - int masterid; /*< Server ID of the master */ - int serverid; /*< Server ID to use with master */ - int initbinlog; /*< Initial binlog file number */ - char *user; /*< User name to use with master */ - char *password; /*< Password to use with master */ - char *fileroot; /*< Root of binlog filename */ - bool master_chksum;/*< Does the master provide checksums */ - char *master_uuid; /*< UUID of the master */ - DCB *master; /*< DCB for master connection */ - DCB *client; /*< DCB for dummy client */ - SESSION *session; /*< Fake session for master connection */ - unsigned int master_state; /*< State of the master FSM */ - uint8_t lastEventReceived; - uint32_t lastEventTimestamp; /*< Timestamp from last event */ - GWBUF *residual; /*< Any residual binlog event */ - MASTER_RESPONSES saved_master; /*< Saved master responses */ - char *binlogdir; /*< The directory with the binlog files */ - SPINLOCK binlog_lock; /*< Lock to control update of the binlog position */ - char binlog_name[BINLOG_FNAMELEN+1]; + SERVICE *service; /*< Pointer to the service using this router */ + ROUTER_SLAVE *slaves; /*< Link list of all the slave connections */ + SPINLOCK lock; /*< Spinlock for the instance data */ + char *uuid; /*< UUID for the router to use w/master */ + int masterid; /*< Server ID of the master */ + int serverid; /*< Server ID to use with master */ + int initbinlog; /*< Initial binlog file number */ + char *user; /*< User name to use with master */ + char *password; /*< Password to use with master */ + char *fileroot; /*< Root of binlog filename */ + bool master_chksum; /*< Does the master provide checksums */ + bool mariadb10_compat; /*< MariaDB 10.0 compatibility */ + char *master_uuid; /*< UUID of the master */ + DCB *master; /*< DCB for master connection */ + DCB *client; /*< DCB for dummy client */ + SESSION *session; /*< Fake session for master connection */ + unsigned int master_state; /*< State of the master FSM */ + uint8_t lastEventReceived; + uint32_t lastEventTimestamp; /*< Timestamp from last event */ + GWBUF *residual; /*< Any residual binlog event */ + MASTER_RESPONSES saved_master; /*< Saved master responses */ + char *binlogdir; /*< The directory with the binlog files */ + SPINLOCK binlog_lock; /*< Lock to control update of the binlog position */ + char binlog_name[BINLOG_FNAMELEN+1]; /*< Name of the current binlog file */ - uint64_t binlog_position; + uint64_t binlog_position; /*< Current binlog position */ - int binlog_fd; /*< File descriptor of the binlog + int binlog_fd; /*< File descriptor of the binlog * file being written */ - uint64_t last_written; /*< Position of last event written */ - char prevbinlog[BINLOG_FNAMELEN+1]; - int rotating; /*< Rotation in progress flag */ - BLFILE *files; /*< Files used by the slaves */ - SPINLOCK fileslock; /*< Lock for the files queue above */ - unsigned int low_water; /*< Low water mark for client DCB */ - unsigned int high_water; /*< High water mark for client DCB */ - unsigned int short_burst; /*< Short burst for slave catchup */ - unsigned int long_burst; /*< Long burst for slave catchup */ - unsigned long burst_size; /*< Maximum size of burst to send */ - unsigned long heartbeat; /*< Configured heartbeat value */ - ROUTER_STATS stats; /*< Statistics for this router */ - int active_logs; - int reconnect_pending; - int retry_backoff; - time_t connect_time; - int handling_threads; - struct router_instance - *next; + uint64_t last_written; /*< Position of last event written */ + char prevbinlog[BINLOG_FNAMELEN+1]; + int rotating; /*< Rotation in progress flag */ + BLFILE *files; /*< Files used by the slaves */ + SPINLOCK fileslock; /*< Lock for the files queue above */ + unsigned int low_water; /*< Low water mark for client DCB */ + unsigned int high_water; /*< High water mark for client DCB */ + unsigned int short_burst; /*< Short burst for slave catchup */ + unsigned int long_burst; /*< Long burst for slave catchup */ + unsigned long burst_size; /*< Maximum size of burst to send */ + unsigned long heartbeat; /*< Configured heartbeat value */ + ROUTER_STATS stats; /*< Statistics for this router */ + int active_logs; + int reconnect_pending; + int retry_backoff; + time_t connect_time; + int handling_threads; + struct router_instance *next; } ROUTER_INSTANCE; /** @@ -315,15 +318,16 @@ typedef struct router_instance { #define BLRM_MAP 0x0011 #define BLRM_REGISTER 0x0012 #define BLRM_BINLOGDUMP 0x0013 +#define BLRM_MARIADB10 0x0014 -#define BLRM_MAXSTATE 0x0013 +#define BLRM_MAXSTATE 0x0014 static char *blrm_states[] = { "Unconnected", "Connecting", "Authenticated", "Timestamp retrieval", "Server ID retrieval", "HeartBeat Period setup", "binlog checksum config", "binlog checksum rerieval", "GTID Mode retrieval", "Master UUID retrieval", "Set Slave UUID", "Set Names latin1", "Set Names utf8", "select 1", "select version()", "select @@version_comment", "select @@hostname", - "select @@mx_allowed_packet", "Register slave", "Binlog Dump" }; + "select @@mx_allowed_packet", "Register slave", "Binlog Dump", "Set MariaDB slave capability" }; #define BLRS_CREATED 0x0000 #define BLRS_UNREGISTERED 0x0001 @@ -397,6 +401,7 @@ static char *blrs_states[] = { "Created", "Unregistered", "Registered", #define PREVIOUS_GTIDS_EVENT 0x23 #define MAX_EVENT_TYPE 0x23 +#define MAX_EVENT_TYPE_MARIADB10 0xa3 /** * Binlog event flags diff --git a/server/modules/include/readwritesplit.h b/server/modules/include/readwritesplit.h index 9aa55016f..c78cde5b4 100644 --- a/server/modules/include/readwritesplit.h +++ b/server/modules/include/readwritesplit.h @@ -252,6 +252,7 @@ typedef struct rwsplit_config_st { int rw_max_sescmd_history_size; bool disable_sescmd_hist; bool disable_slave_recovery; + bool master_reads; /*< Use master for reads */ } rwsplit_config_t; diff --git a/server/modules/include/shardrouter.h b/server/modules/include/shardrouter.h index dfd83d520..63572671e 100644 --- a/server/modules/include/shardrouter.h +++ b/server/modules/include/shardrouter.h @@ -245,4 +245,6 @@ typedef struct router_instance { #define BACKEND_TYPE(b) (SERVER_IS_MASTER((b)->backend_server) ? BE_MASTER : \ (SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : BE_UNDEFINED)); +bool subsvc_is_valid(SUBSERVICE*); + #endif /*< _SHARDROUTER_H */ diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 07d9fd848..11f9c565a 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -155,7 +155,10 @@ startMonitor(void *arg,void* opt) else if(!strcmp(params->name,"script")) { if(handle->script) + { free(handle->script); + handle->script = NULL; + } if(access(params->value,X_OK) == 0) { @@ -277,7 +280,7 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) { GALERA_MONITOR* handle = (GALERA_MONITOR*)mon->handle; MYSQL_ROW row; -MYSQL_RES *result; +MYSQL_RES *result,*result2; int isjoined = 0; char *uname = mon->user; char *passwd = mon->password; @@ -369,6 +372,14 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_state'\". Expected 2 columns." + " MySQL Version: %s",version_str); + return; + } + while ((row = mysql_fetch_row(result))) { if (strcmp(row[1], "4") == 0) @@ -377,13 +388,22 @@ char *server_string; /* Check if the node is a donor and is using xtrabackup, in this case it can stay alive */ else if (strcmp(row[1], "2") == 0 && handle->availableWhenDonor == 1) { if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0 - && (result = mysql_store_result(database->con)) != NULL) + && (result2 = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + mysql_free_result(result2); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW VARIABLES LIKE 'wsrep_sst_method'\". Expected 2 columns." + " MySQL Version: %s",version_str); + return; + } while ((row = mysql_fetch_row(result))) { if (strncmp(row[1], "xtrabackup", 10) == 0) isjoined = 1; } + mysql_free_result(result2); } } } @@ -395,6 +415,15 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long local_index = -1; + + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_index'\". Expected 2 columns." + " MySQL Version: %s",version_str); + return; + } + while ((row = mysql_fetch_row(result))) { local_index = strtol(row[1], NULL, 10); diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 545a77ca2..40c2c11fb 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -366,7 +366,15 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long server_id = -1; - + + if(mysql_field_count(database->con) != 1) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column." + " MySQL Version: %s",version_str); + return; + } + while ((row = mysql_fetch_row(result))) { server_id = strtol(row[0], NULL, 10); @@ -392,7 +400,16 @@ char *server_string; { int i = 0; long master_id = -1; - + + if(mysql_field_count(database->con) < 42) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " + "returned less than the expected amount of columns. Expected 42 columns" + " MySQL Version: %s",version_str); + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -431,7 +448,31 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long master_id = -1; - + + if(mysql_field_count(database->con) < 40) + { + mysql_free_result(result); + + if(server_version < 5*10000 + 5*100) + { + if(database->log_version_err) + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + " for versions less than 5.5 does not have master_server_id, " + "replication tree cannot be resolved for server %s." + " MySQL Version: %s",database->server->unique_name,version_str); + database->log_version_err = false; + } + } + else + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 40 columns." + " MySQL Version: %s",version_str); + } + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -463,6 +504,13 @@ char *server_string; if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW GLOBAL VARIABLES LIKE 'read_only'\". Expected 2 columns." + " MySQL Version: %s",version_str); + return; + } while ((row = mysql_fetch_row(result))) { diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index e310e2ede..2ba70a499 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -408,6 +408,13 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long server_id = -1; + if(mysql_field_count(database->con) != 1) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for \"SELECT @@server_id\". Expected 1 columns." + " MySQL Version: %s",version_str); + return; + } while ((row = mysql_fetch_row(result))) { server_id = strtol(row[0], NULL, 10); @@ -433,6 +440,16 @@ char *server_string; { int i = 0; long master_id = -1; + + if(mysql_field_count(database->con) < 42) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " + "returned less than the expected amount of columns. Expected 42 columns." + " MySQL Version: %s",version_str); + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ @@ -471,6 +488,29 @@ char *server_string; && (result = mysql_store_result(database->con)) != NULL) { long master_id = -1; + if(mysql_field_count(database->con) < 40) + { + mysql_free_result(result); + if(server_version < 5*10000 + 5*100) + { + if(database->log_version_err) + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + " for versions less than 5.5 does not have master_server_id, " + "replication tree cannot be resolved for server %s." + " MySQL Version: %s",database->server->unique_name,version_str); + database->log_version_err = false; + } + } + else + { + skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 40 columns." + " MySQL Version: %s",version_str); + } + return; + } + while ((row = mysql_fetch_row(result))) { /* get Slave_IO_Running and Slave_SQL_Running values*/ diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index 9561e275e..a5d0b2455 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -323,6 +323,14 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'\". Expected 2 columns." + " MySQL Version: %s",version_str); + return; + } + while ((row = mysql_fetch_row(result))) { if (atoi(row[1]) > 0) @@ -335,6 +343,14 @@ char *server_string; if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0 && (result = mysql_store_result(database->con)) != NULL) { + if(mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\". Expected 2 columns." + " MySQL Version: %s",version_str); + return; + } + long cluster_node_id = -1; while ((row = mysql_fetch_row(result))) { diff --git a/server/modules/protocol/maxscaled.c b/server/modules/protocol/maxscaled.c index 67d51f9bd..159718df0 100644 --- a/server/modules/protocol/maxscaled.c +++ b/server/modules/protocol/maxscaled.c @@ -58,6 +58,7 @@ extern __thread log_info_t tls_log_info; * Revision History * Date Who Description * 13/06/2014 Mark Riddoch Initial implementation + * 07/07/15 Martin Brampton Correct failure handling * * @endverbatim */ @@ -270,9 +271,7 @@ int n_connect = 0; { atomic_add(&dcb->stats.n_accepts, 1); client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); - if (client_dcb == NULL) - { close(so); return n_connect; @@ -283,7 +282,8 @@ int n_connect = 0; if ((maxscaled_pr = (MAXSCALED *)malloc(sizeof(MAXSCALED))) == NULL) { client_dcb->protocol = NULL; - dcb_add_to_zombieslist(client_dcb); + close(so); + dcb_close(client_dcb); return n_connect; } maxscaled_pr->username = NULL; @@ -293,9 +293,9 @@ int n_connect = 0; client_dcb->session = session_alloc(dcb->session->service, client_dcb); - if (poll_add_dcb(client_dcb) == -1) + if (poll_add_dcb(client_dcb)) { - dcb_add_to_zombieslist(dcb); + dcb_close(dcb); return n_connect; } n_connect++; diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 4d9b91ef4..08705f33c 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1452,8 +1452,8 @@ int gw_MySQLListener( // add listening socket to poll structure if (poll_add_dcb(listen_dcb) == -1) { fprintf(stderr, - "\n* Failed to start polling the socket due error " - "%i, %s.\n\n", + "\n* MaxScale encountered system limit while " + "attempting to register on an epoll instance.\n\n", errno, strerror(errno)); return 0; @@ -1688,7 +1688,8 @@ int gw_MySQLAccept(DCB *listener) client_dcb, 1, 0, - "MaxScale internal error."); + "MaxScale encountered system limit while " + "attempting to register on an epoll instance."); /** close client_dcb */ dcb_close(client_dcb); diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 66cf733ac..60c6273b6 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -36,6 +36,7 @@ * 03/10/2014 Massimiliano Pinto Added netmask for wildcard in IPv4 hosts. * 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support * 10/11/2014 Massimiliano Pinto Charset at connect is passed to backend during authentication + * 07/07/15 Martin Brampton Fix problem recognising null password * */ @@ -1550,13 +1551,6 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, break; } - /** See if ANYDB == Y */ - if(key.resource) - { - key.resource = NULL; - continue; - } - if (!user_password) { /* * user@% not found. diff --git a/server/modules/protocol/telnetd.c b/server/modules/protocol/telnetd.c index ab5c95b47..006fabe99 100644 --- a/server/modules/protocol/telnetd.c +++ b/server/modules/protocol/telnetd.c @@ -67,6 +67,7 @@ extern __thread log_info_t tls_log_info; * Date Who Description * 17/06/2013 Mark Riddoch Initial version * 17/07/2013 Mark Riddoch Addition of login phase + * 07/07/2015 Martin Brampton Call unified dcb_close on error * * @endverbatim */ @@ -315,13 +316,13 @@ int n_connect = 0; if (telnetd_pr == NULL) { - dcb_add_to_zombieslist(client_dcb); + dcb_close(client_dcb); return n_connect; } - if (poll_add_dcb(client_dcb) == -1) + if (poll_add_dcb(client_dcb)) { - dcb_add_to_zombieslist(dcb); + dcb_close(dcb); return n_connect; } n_connect++; diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 2ba89689f..8dce5ea58 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -35,6 +35,8 @@ * 02/04/2014 Mark Riddoch Initial implementation * 17/02/2015 Massimiliano Pinto Addition of slave port and username in diagnostics * 18/02/2015 Massimiliano Pinto Addition of dcb_close in closeSession + * 07/05/2015 Massimiliano Pinto Addition of MariaDB 10 compatibility support + * * @endverbatim */ @@ -195,6 +197,7 @@ unsigned char *defuuid; inst->retry_backoff = 1; inst->binlogdir = NULL; inst->heartbeat = 300; // Default is every 5 minutes + inst->mariadb10_compat = false; inst->user = strdup(service->credentials.name); inst->password = strdup(service->credentials.authdata); @@ -282,6 +285,10 @@ unsigned char *defuuid; { inst->masterid = atoi(value); } + else if (strcmp(options[i], "mariadb10-compatibility") == 0) + { + inst->mariadb10_compat = config_truth_value(value); + } else if (strcmp(options[i], "filestem") == 0) { inst->fileroot = strdup(value); @@ -388,6 +395,7 @@ unsigned char *defuuid; inst->saved_master.selectvercom = blr_cache_read_response(inst, "selectvercom"); inst->saved_master.selecthostname = blr_cache_read_response(inst, "selecthostname"); inst->saved_master.map = blr_cache_read_response(inst, "map"); + inst->saved_master.mariadb10 = blr_cache_read_response(inst, "mariadb10"); /* * Initialise the binlog file and position @@ -490,6 +498,7 @@ ROUTER_SLAVE *slave; strcpy(slave->binlogfile, "unassigned"); slave->connect_time = time(0); slave->lastEventTimestamp = 0; + slave->mariadb10_compat = false; /** * Add this session to the list of active sessions. diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 68f47b1c3..3196a8cf4 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -23,8 +23,9 @@ * @verbatim * Revision History * - * Date Who Description + * Date Who Description * 14/04/2014 Mark Riddoch Initial implementation + * 07/05/2015 Massimiliano Pinto Added MAX_EVENT_TYPE_MARIADB10 * * @endverbatim */ @@ -439,15 +440,26 @@ struct stat statb; hdr->next_pos = EXTRACT32(&hdbuf[13]); hdr->flags = EXTRACT16(&hdbuf[17]); - if (hdr->event_type > MAX_EVENT_TYPE) - { - LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, - "Invalid event type 0x%x. " + if (router->mariadb10_compat) { + if (hdr->event_type > MAX_EVENT_TYPE_MARIADB10) { + LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, + "Invalid MariaDB 10 event type 0x%x. " "Binlog file is %s, position %d", hdr->event_type, file->binlogname, pos))); - return NULL; - } + return NULL; + } + } else { + if (hdr->event_type > MAX_EVENT_TYPE) { + LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, + "Invalid event type 0x%x. " + "Binlog file is %s, position %d", + hdr->event_type, + file->binlogname, pos))); + + return NULL; + } + } if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT) { diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 2b164c349..3a0519108 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -31,8 +31,9 @@ * @verbatim * Revision History * - * Date Who Description + * Date Who Description * 02/04/2014 Mark Riddoch Initial implementation + * 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility * * @endverbatim */ @@ -448,11 +449,27 @@ char query[128]; GWBUF_CONSUME_ALL(router->saved_master.chksum2); router->saved_master.chksum2 = buf; blr_cache_response(router, "chksum2", buf); - buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); - router->master_state = BLRM_GTIDMODE; + + if (router->mariadb10_compat) { + buf = blr_make_query("SET @mariadb_slave_capability=4"); + router->master_state = BLRM_MARIADB10; + } else { + buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE"); + router->master_state = BLRM_GTIDMODE; + } router->master->func.write(router->master, buf); break; } + case BLRM_MARIADB10: + // Response to the SET @mariadb_slave_capability=4, should be stored + if (router->saved_master.mariadb10) + GWBUF_CONSUME_ALL(router->saved_master.mariadb10); + router->saved_master.mariadb10 = buf; + blr_cache_response(router, "mariadb10", buf); + buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_UUID'"); + router->master_state = BLRM_MUUID; + router->master->func.write(router->master, buf); + break; case BLRM_GTIDMODE: // Response to the GTID_MODE, should be stored if (router->saved_master.gtid_mode) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index ee7d52099..476a9d821 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -36,9 +36,12 @@ * 18/02/2015 Massimiliano Pinto Addition of DISCONNECT ALL and DISCONNECT SERVER server_id * 18/03/2015 Markus Makela Better detection of CRC32 | NONE checksum * 19/03/2015 Massimiliano Pinto Addition of basic MariaDB 10 compatibility support + * 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility + * 11/05/2015 Massimiliano Pinto Only MariaDB 10 Slaves can register to binlog router with a MariaDB 10 Master * * @endverbatim */ + #include #include #include @@ -123,7 +126,28 @@ blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) return blr_slave_query(router, slave, queue); break; case COM_REGISTER_SLAVE: - return blr_slave_register(router, slave, queue); + /* + * If Master is MariaDB10 don't allow registration from + * MariaDB/Mysql 5 Slaves + */ + + if (router->mariadb10_compat && !slave->mariadb10_compat) { + slave->state = BLRS_ERRORED; + blr_send_custom_error(slave->dcb, 1, 0, + "MariaDB 10 Slave is required for Slave registration"); + + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "%s: Slave %s: a MariaDB 10 Slave is required for Slave registration", + router->service->name, + slave->dcb->remote))); + + dcb_close(slave->dcb); + return 1; + } else { + /* Master and Slave version OK: continue with slave registration */ + return blr_slave_register(router, slave, queue); + } break; case COM_BINLOG_DUMP: return blr_slave_binlog_dump(router, slave, queue); @@ -366,10 +390,17 @@ int query_len; free(query_text); return blr_slave_replay(router, slave, router->saved_master.heartbeat); } - else if (strcasecmp(word, "@mariadb_slave_capability") == 0) + else if (strcasecmp(word, "@mariadb_slave_capability") == 0) { - free(query_text); - return blr_slave_send_ok(router, slave); + /* mariadb10 compatibility is set for the slave */ + slave->mariadb10_compat=true; + + free(query_text); + if (router->mariadb10_compat) { + return blr_slave_replay(router, slave, router->saved_master.mariadb10); + } else { + return blr_slave_send_ok(router, slave); + } } else if (strcasecmp(word, "@master_binlog_checksum") == 0) { @@ -442,7 +473,7 @@ int query_len; query_text = strndup(qtext, query_len); LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, "Unexpected query from slave server %s", query_text))); + LOGFILE_ERROR, "Unexpected query from slave %s: %s", slave->dcb->remote, query_text))); free(query_text); blr_slave_send_error(router, slave, "Unexpected SQL query received from slave."); return 1; diff --git a/server/modules/routing/maxinfo/maxinfo_parse.c b/server/modules/routing/maxinfo/maxinfo_parse.c index 9eb445a22..bdef7ef26 100644 --- a/server/modules/routing/maxinfo/maxinfo_parse.c +++ b/server/modules/routing/maxinfo/maxinfo_parse.c @@ -113,14 +113,10 @@ MAXINFO_TREE *col, *table; #endif default: *parse_error = PARSE_SYNTAX_ERROR; - if (tree) - free_tree(tree); return NULL; } } *parse_error = PARSE_SYNTAX_ERROR; - if (tree) - free_tree(tree); return NULL; } diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 2fb73b632..3c7f38246 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -226,7 +226,7 @@ static rses_property_t* mysql_sescmd_get_property( static rses_property_t* rses_property_init( rses_property_type_t prop_type); -static void rses_property_add( +static int rses_property_add( ROUTER_CLIENT_SES* rses, rses_property_t* prop); @@ -287,7 +287,7 @@ static sescmd_cursor_t* backend_ref_get_sescmd_cursor (backend_ref_t* bref); static int router_handle_state_switch(DCB* dcb, DCB_REASON reason, void* data); static bool handle_error_new_connection( ROUTER_INSTANCE* inst, - ROUTER_CLIENT_SES* rses, + ROUTER_CLIENT_SES** rses, DCB* backend_dcb, GWBUF* errmsg); static void handle_error_reply_client( @@ -1243,7 +1243,8 @@ static bool get_dcb( SERVER_IS_SLAVE(b->backend_server) && (max_rlag == MAX_RLAG_UNDEFINED || (b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE && - b->backend_server->rlag <= max_rlag))) + b->backend_server->rlag <= max_rlag)) && + !rses->rses_config.master_reads) { /** found slave */ candidate_bref = &backend_ref[i]; @@ -2941,6 +2942,11 @@ static void bref_clear_state( backend_ref_t* bref, bref_state_t state) { + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to bref_clear_state. (%s:%d)",__FILE__,__LINE__); + return; + } if (state != BREF_WAITING_RESULT) { bref->bref_state &= ~state; @@ -2970,6 +2976,11 @@ static void bref_set_state( backend_ref_t* bref, bref_state_t state) { + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to bref_set_state. (%s:%d)",__FILE__,__LINE__); + return; + } if (state != BREF_WAITING_RESULT) { bref->bref_state |= state; @@ -3533,7 +3544,8 @@ static rses_property_t* rses_property_init( prop = (rses_property_t*)calloc(1, sizeof(rses_property_t)); if (prop == NULL) { - goto return_prop; + skygw_log_write(LE,"Error: Malloc returned NULL. (%s:%d)",__FILE__,__LINE__); + return NULL; } prop->rses_prop_type = prop_type; #if defined(SS_DEBUG) @@ -3541,7 +3553,6 @@ static rses_property_t* rses_property_init( prop->rses_prop_chk_tail = CHK_NUM_ROUTER_PROPERTY; #endif -return_prop: CHK_RSES_PROP(prop); return prop; } @@ -3552,6 +3563,11 @@ return_prop: static void rses_property_done( rses_property_t* prop) { + if(prop == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to rses_property_done. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_RSES_PROP(prop); switch (prop->rses_prop_type) { @@ -3585,10 +3601,20 @@ static void rses_property_done( * * Router client session must be locked. */ -static void rses_property_add( +static int rses_property_add( ROUTER_CLIENT_SES* rses, rses_property_t* prop) { + if(rses == NULL) + { + skygw_log_write(LE,"Error: Router client session is NULL. (%s:%d)",__FILE__,__LINE__); + return -1; + } + if(prop == NULL) + { + skygw_log_write(LE,"Error: Router client session property is NULL. (%s:%d)",__FILE__,__LINE__); + return -1; + } rses_property_t* p; CHK_CLIENT_RSES(rses); @@ -3610,6 +3636,7 @@ static void rses_property_add( } p->rses_prop_next = prop; } + return 0; } /** @@ -3620,7 +3647,13 @@ static mysql_sescmd_t* rses_property_get_sescmd( rses_property_t* prop) { mysql_sescmd_t* sescmd; - + + if(prop == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to rses_property_get_sescmd. (%s:%d)",__FILE__,__LINE__); + return NULL; + } + CHK_RSES_PROP(prop); ss_dassert(prop->rses_prop_rsession == NULL || SPINLOCK_IS_LOCKED(&prop->rses_prop_rsession->rses_lock)); @@ -3633,22 +3666,6 @@ static mysql_sescmd_t* rses_property_get_sescmd( } return sescmd; } - -/** -static void rses_begin_locked_property_action( - rses_property_t* prop) -{ - CHK_RSES_PROP(prop); - spinlock_acquire(&prop->rses_prop_lock); -} - -static void rses_end_locked_property_action( - rses_property_t* prop) -{ - CHK_RSES_PROP(prop); - spinlock_release(&prop->rses_prop_lock); -} -*/ /** * Create session command property. @@ -3681,6 +3698,11 @@ static mysql_sescmd_t* mysql_sescmd_init ( static void mysql_sescmd_done( mysql_sescmd_t* sescmd) { + if(sescmd == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to mysql_sescmd_done. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_RSES_PROP(sescmd->my_sescmd_prop); gwbuf_free(sescmd->my_sescmd_buf); memset(sescmd, 0, sizeof(mysql_sescmd_t)); @@ -3763,7 +3785,7 @@ static GWBUF* sescmd_cursor_process_replies( dcb_close(bref->bref_dcb); *reconnect = true; if(replybuf) - gwbuf_consume(replybuf,gwbuf_length(replybuf)); + while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf)))); } } /** This is a response from the master and it is the "right" one. @@ -3806,7 +3828,7 @@ static GWBUF* sescmd_cursor_process_replies( skygw_log_write(LOGFILE_DEBUG,"Slave '%s' responded faster to a session command.", bref->bref_backend->backend_server->unique_name); if(replybuf) - gwbuf_free(replybuf); + while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf)))); return NULL; } @@ -3853,6 +3875,12 @@ static bool sescmd_cursor_is_active( sescmd_cursor_t* sescmd_cursor) { bool succp; + + if(sescmd_cursor == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_is_active. (%s:%d)",__FILE__,__LINE__); + return false; + } ss_dassert(SPINLOCK_IS_LOCKED(&sescmd_cursor->scmd_cur_rses->rses_lock)); succp = sescmd_cursor->scmd_cur_active; @@ -3878,6 +3906,11 @@ static GWBUF* sescmd_cursor_clone_querybuf( sescmd_cursor_t* scur) { GWBUF* buf; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_clone_querybuf. (%s:%d)",__FILE__,__LINE__); + return NULL; + } ss_dassert(scur->scmd_cur_cmd != NULL); buf = gwbuf_clone(scur->scmd_cur_cmd->my_sescmd_buf); @@ -3890,7 +3923,12 @@ static bool sescmd_cursor_history_empty( sescmd_cursor_t* scur) { bool succp; - + + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_history_empty. (%s:%d)",__FILE__,__LINE__); + return true; + } CHK_SESCMD_CUR(scur); if (scur->scmd_cur_rses->rses_properties[RSES_PROP_TYPE_SESCMD] == NULL) @@ -3910,6 +3948,11 @@ static void sescmd_cursor_reset( sescmd_cursor_t* scur) { ROUTER_CLIENT_SES* rses; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_reset. (%s:%d)",__FILE__,__LINE__); + return; + } CHK_SESCMD_CUR(scur); CHK_CLIENT_RSES(scur->scmd_cur_rses); rses = scur->scmd_cur_rses; @@ -3926,6 +3969,11 @@ static bool execute_sescmd_history( { bool succp; sescmd_cursor_t* scur; + if(bref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_history. (%s:%d)",__FILE__,__LINE__); + return false; + } CHK_BACKEND_REF(bref); scur = &bref->bref_sescmd_cur; @@ -3961,7 +4009,12 @@ static bool execute_sescmd_in_backend( bool succp; int rc = 0; sescmd_cursor_t* scur; - + GWBUF* buf; + if(backend_ref == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_in_backend. (%s:%d)",__FILE__,__LINE__); + return false; + } if (BREF_IS_CLOSED(backend_ref)) { succp = false; @@ -3993,27 +4046,9 @@ static bool execute_sescmd_in_backend( /** Cursor is left active when function returns. */ sescmd_cursor_set_active(scur, true); } -#if defined(SS_DEBUG) - LOGIF(LT, tracelog_routed_query(scur->scmd_cur_rses, - "execute_sescmd_in_backend", - backend_ref, - sescmd_cursor_clone_querybuf(scur))); - { - GWBUF* tmpbuf = sescmd_cursor_clone_querybuf(scur); - uint8_t* ptr = GWBUF_DATA(tmpbuf); - unsigned char cmd = MYSQL_GET_COMMAND(ptr); - - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [execute_sescmd_in_backend] Just before write, fd " - "%d : cmd %s.", - pthread_self(), - dcb->fd, - STRPACKETTYPE(cmd)))); - gwbuf_free(tmpbuf); - } -#endif /*< SS_DEBUG */ + buf = sescmd_cursor_clone_querybuf(scur); + switch (scur->scmd_cur_cmd->my_sescmd_packet_type) { case MYSQL_COM_CHANGE_USER: /** This makes it possible to handle replies correctly */ @@ -4022,7 +4057,7 @@ static bool execute_sescmd_in_backend( dcb, NULL, dcb->session, - sescmd_cursor_clone_querybuf(scur)); + buf); break; case MYSQL_COM_INIT_DB: @@ -4048,10 +4083,11 @@ static bool execute_sescmd_in_backend( * Mark session command buffer, it triggers writing * MySQL command to protocol */ + gwbuf_set_type(scur->scmd_cur_cmd->my_sescmd_buf, GWBUF_TYPE_SESCMD); rc = dcb->func.write( dcb, - sescmd_cursor_clone_querybuf(scur)); + buf); break; } @@ -4061,6 +4097,7 @@ static bool execute_sescmd_in_backend( } else { + while((buf = GWBUF_CONSUME_ALL(buf)) != NULL); succp = false; } return_succp: @@ -4082,6 +4119,12 @@ static bool sescmd_cursor_next( rses_property_t* prop_curr; rses_property_t* prop_next; + if(scur == NULL) + { + skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_next. (%s:%d)",__FILE__,__LINE__); + return false; + } + ss_dassert(scur != NULL); ss_dassert(*(scur->scmd_cur_ptr_property) != NULL); ss_dassert(SPINLOCK_IS_LOCKED( @@ -4408,11 +4451,21 @@ static bool route_session_write( * prevent it from being released before properties * are cleaned up as a part of router sessionclean-up. */ - prop = rses_property_init(RSES_PROP_TYPE_SESCMD); + if((prop = rses_property_init(RSES_PROP_TYPE_SESCMD)) == NULL) + { + skygw_log_write(LE,"Error: Router session property initialization failed"); + rses_end_locked_router_action(router_cli_ses); + return false; + } mysql_sescmd_init(prop, querybuf, packet_type, router_cli_ses); /** Add sescmd property to router client session */ - rses_property_add(router_cli_ses, prop); + if(rses_property_add(router_cli_ses, prop) != 0) + { + skygw_log_write(LE,"Error: Session property addition failed."); + rses_end_locked_router_action(router_cli_ses); + return false; + } for (i=0; irses_nbackends; i++) { @@ -4535,7 +4588,10 @@ static void rwsplit_process_router_options( int i; char* value; select_criteria_t c; - + + if(options == NULL) + return; + for (i = 0; options[i]; i++) { if ((value = strchr(options[i], '=')) == NULL) @@ -4588,6 +4644,10 @@ static void rwsplit_process_router_options( { router->rwsplit_config.disable_slave_recovery = config_truth_value(value); } + else if(strcmp(options[i],"master_accept_reads") == 0) + { + router->rwsplit_config.master_reads = config_truth_value(value); + } } } /*< for */ } @@ -4619,7 +4679,7 @@ static void handleError ( SESSION* session; ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session; - + CHK_DCB(backend_dcb); /** Reset error handle flag from a given DCB */ @@ -4686,14 +4746,15 @@ static void handleError ( { /** * This is called in hope of getting replacement for - * failed slave(s). + * failed slave(s). This call may free rses. */ *succp = handle_error_new_connection(inst, - rses, + &rses, backend_dcb, errmsgbuf); } - rses_end_locked_router_action(rses); + /* Free the lock if rses still exists */ + if (rses) rses_end_locked_router_action(rses); break; } @@ -4762,10 +4823,11 @@ static void handle_error_reply_client( */ static bool handle_error_new_connection( ROUTER_INSTANCE* inst, - ROUTER_CLIENT_SES* rses, + ROUTER_CLIENT_SES** rses, DCB* backend_dcb, GWBUF* errmsg) { + ROUTER_CLIENT_SES* myrses; SESSION* ses; int router_nservers; int max_nslaves; @@ -4773,7 +4835,8 @@ static bool handle_error_new_connection( backend_ref_t* bref; bool succp; - ss_dassert(SPINLOCK_IS_LOCKED(&rses->rses_lock)); + myrses = *rses; + ss_dassert(SPINLOCK_IS_LOCKED(&myrses->rses_lock)); ses = backend_dcb->session; CHK_SESSION(ses); @@ -4781,7 +4844,7 @@ static bool handle_error_new_connection( /** * If bref == NULL it has been replaced already with another one. */ - if ((bref = get_bref_from_dcb(rses, backend_dcb)) == NULL) + if ((bref = get_bref_from_dcb(myrses, backend_dcb)) == NULL) { succp = true; goto return_succp; @@ -4824,25 +4887,25 @@ static bool handle_error_new_connection( (void *)bref); router_nservers = router_get_servercount(inst); - max_nslaves = rses_get_max_slavecount(rses, router_nservers); - max_slave_rlag = rses_get_max_replication_lag(rses); + max_nslaves = rses_get_max_slavecount(myrses, router_nservers); + max_slave_rlag = rses_get_max_replication_lag(myrses); /** * Try to get replacement slave or at least the minimum * number of slave connections for router session. */ if(inst->rwsplit_config.disable_slave_recovery) { - succp = have_enough_servers(&rses,1,router_nservers,inst) ? true : false; + succp = have_enough_servers(&myrses,1,router_nservers,inst) ? true : false; } else { succp = select_connect_backend_servers( - &rses->rses_master_ref, - rses->rses_backend_ref, + &myrses->rses_master_ref, + myrses->rses_backend_ref, router_nservers, max_nslaves, max_slave_rlag, - rses->rses_config.rw_slave_select_criteria, + myrses->rses_config.rw_slave_select_criteria, ses, inst); } @@ -5079,10 +5142,9 @@ static int router_handle_state_switch( { backend_ref_t* bref; int rc = 1; - ROUTER_CLIENT_SES* rses; - SESSION* ses; SERVER* srv; - + ROUTER_CLIENT_SES* rses; + SESSION* ses; CHK_DCB(dcb); bref = (backend_ref_t *)data; CHK_BACKEND_REF(bref); @@ -5103,8 +5165,7 @@ static int router_handle_state_switch( STRSRVSTATUS(srv)))); ses = dcb->session; CHK_SESSION(ses); - - rses = (ROUTER_CLIENT_SES *)dcb->session->router_session; + rses = (ROUTER_CLIENT_SES *)dcb->session->router_session; CHK_CLIENT_RSES(rses); switch (reason) { diff --git a/server/modules/routing/schemarouter/sharding_common.c b/server/modules/routing/schemarouter/sharding_common.c index d69b12d02..cf6bf2c12 100644 --- a/server/modules/routing/schemarouter/sharding_common.c +++ b/server/modules/routing/schemarouter/sharding_common.c @@ -46,7 +46,7 @@ bool extract_database(GWBUF* buf, char* str) tok = strtok_r(query," ;",&saved); if(tok == NULL || strcasecmp(tok,"use") != 0) { - skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet."); + skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet."); succp = false; goto retblock; } @@ -54,7 +54,7 @@ bool extract_database(GWBUF* buf, char* str) tok = strtok_r(NULL," ;",&saved); if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet."); + skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet."); succp = false; goto retblock; } diff --git a/server/modules/routing/schemarouter/shardrouter.c b/server/modules/routing/schemarouter/shardrouter.c index 94ab35974..a0b2a4a43 100644 --- a/server/modules/routing/schemarouter/shardrouter.c +++ b/server/modules/routing/schemarouter/shardrouter.c @@ -319,7 +319,7 @@ parse_mapping_response(ROUTER_CLIENT_SES* rses, char* target, GWBUF* buf) if(PTR_IS_RESULTSET(((unsigned char*)buf->start)) && modutil_count_signal_packets(buf,0,0,&more) == 2) { - ptr = (char*)buf->start; + ptr = (unsigned char*)buf->start; if(ptr[5] != 1) { diff --git a/server/test/maxscale_test.h.in b/server/test/maxscale_test.h.in index d41ff181f..b5448295c 100644 --- a/server/test/maxscale_test.h.in +++ b/server/test/maxscale_test.h.in @@ -1,9 +1,9 @@ #ifndef MAXSCALE_TEST_H #define MAXSCALE_TEST_H -#define TEST_DIR "${CMAKE_BINARY_DIR}" -#define TEST_LOG_DIR "${CMAKE_BINARY_DIR}/log" -#define TEST_BIN_DIR "${CMAKE_BINARY_DIR}/bin" -#define TEST_MOD_DIR "${CMAKE_BINARY_DIR}/modules" -#define TEST_LIB_DIR "${CMAKE_BINARY_DIR}/lib" -#define TEST_ETC_DIR "${CMAKE_BINARY_DIR}/etc" +#define TEST_DIR "@CMAKE_BINARY_DIR@" +#define TEST_LOG_DIR "@CMAKE_BINARY_DIR@/log" +#define TEST_BIN_DIR "@CMAKE_BINARY_DIR@/bin" +#define TEST_MOD_DIR "@CMAKE_BINARY_DIR@/modules" +#define TEST_LIB_DIR "@CMAKE_BINARY_DIR@/lib" +#define TEST_ETC_DIR "@CMAKE_BINARY_DIR@/etc" #endif diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index d1d4413e9..a5a97c1eb 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -380,6 +380,7 @@ mlist_cursor_t* mlist_cursor_init( if (c == NULL) { goto return_cursor; + simple_mutex_unlock(&list->mlist_mutex); } c->mlcursor_chk_top = CHK_NUM_MLIST_CURSOR; c->mlcursor_chk_tail = CHK_NUM_MLIST_CURSOR; @@ -565,6 +566,7 @@ bool mlist_cursor_move_to_first( simple_mutex_lock(&list->mlist_mutex, true); if (mc->mlcursor_list->mlist_deleted) { + simple_mutex_unlock(&list->mlist_mutex); return false; } /** Set position point to first node */