Merge branch 'develop' into binlog_server_mariadb_master_encryption
This commit is contained in:
commit
a9f7e4ac6f
@ -12,9 +12,9 @@ echo TRAVIS_BUILD_DIR: ${TRAVIS_BUILD_DIR}
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_TESTS=Y
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=Y -DBUILD_AVRO=N
|
||||
|
||||
make VERBOSE=1
|
||||
make
|
||||
make test
|
||||
sudo make install
|
||||
|
||||
|
@ -41,8 +41,10 @@ find_package(Git)
|
||||
find_package(CURL)
|
||||
find_package(RabbitMQ)
|
||||
find_package(LibUUID)
|
||||
find_package(Jansson)
|
||||
find_package(Avro)
|
||||
find_package(GSSAPI)
|
||||
find_package(SQLite)
|
||||
|
||||
# Find or build PCRE2
|
||||
# Read BuildPCRE2 for details about how to add pcre2 as a dependency to a target
|
||||
@ -177,7 +179,6 @@ set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG -DLOG_ASSERT")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-ggdb")
|
||||
|
||||
include_directories(avro)
|
||||
include_directories(include)
|
||||
include_directories(server/inih)
|
||||
include_directories(server/modules/include)
|
||||
@ -185,8 +186,10 @@ include_directories(${CMAKE_BINARY_DIR}/include)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
|
||||
if (BUILD_AVRO)
|
||||
include_directories(avro)
|
||||
add_subdirectory(avro)
|
||||
endif()
|
||||
|
||||
add_subdirectory(plugins)
|
||||
add_subdirectory(query_classifier)
|
||||
add_subdirectory(server)
|
||||
@ -222,6 +225,7 @@ endif()
|
||||
install_file(${CMAKE_SOURCE_DIR}/COPYRIGHT core)
|
||||
install_file(${CMAKE_SOURCE_DIR}/README core)
|
||||
install_file(${CMAKE_SOURCE_DIR}/LICENSE.TXT core)
|
||||
install_file(${CMAKE_SOURCE_DIR}/LICENSE-THIRDPARTY.TXT core)
|
||||
install_file(etc/lsyncd_example.conf core)
|
||||
install_manual(Documentation/maxscale.1 1 core)
|
||||
install_file(${CMAKE_SOURCE_DIR}/server/maxscale_binlogserver_template.cnf core)
|
||||
|
50
Documentation/Authenticators/MySQL-Authenticator.md
Normal file
50
Documentation/Authenticators/MySQL-Authenticator.md
Normal file
@ -0,0 +1,50 @@
|
||||
# MySQL Authenticator
|
||||
|
||||
The _MySQLAuth_ and _MySQLBackendAuth_ modules implement the client and
|
||||
backend authentication for the MySQL native password authentication. This
|
||||
is the default authentication plugin used by both MariaDB and MySQL.
|
||||
|
||||
These modules are the default authenticators for all MySQL connections and
|
||||
needs no further configuration to work.
|
||||
|
||||
## Authenticator options
|
||||
|
||||
The client authentication module, _MySQLAuth_, supports authenticator
|
||||
options. The `authenticator_options` parameter is supported by listeners
|
||||
and servers and expects a comma-separated list of key-value pairs. The
|
||||
following options contain examples on how to define it.
|
||||
|
||||
### `cache_dir`
|
||||
|
||||
The location where the user credential cache is stored. The default value
|
||||
for this is `<cache dir>/<service name>/<listener name>/cache/` where
|
||||
`<cache dir>` by default is `/var/cache`.
|
||||
|
||||
If _cache_dir_ is defined, the user cache file is stored in `<cache
|
||||
dir>/`. No additional directories are appended to the _cache_dir_ value.
|
||||
|
||||
Each listener has its own user cache where the user credential information
|
||||
queried from the backends is stored. This information is used to
|
||||
authenticate users if a connection to the backend servers can't be made.
|
||||
|
||||
```
|
||||
authenticator_options=cache_dir=/tmp
|
||||
```
|
||||
|
||||
### `inject_service_user`
|
||||
|
||||
Inject service credentials into the list of database users if loading of
|
||||
users fails. This option takes a boolean value and it is enabled by
|
||||
default.
|
||||
|
||||
When a connection to the backend database cannot be made, the service user
|
||||
can be injected into the list of allowed users. This allows administrative
|
||||
operations to be done via the SQL interface with modules that support it
|
||||
e.g. the Binlogrouter and Maxinfo modules.
|
||||
|
||||
If users are loaded successfully, the service user credentials are _not_
|
||||
injected into the list of users.
|
||||
|
||||
```
|
||||
authenticator_options=inject_service_user=false
|
||||
```
|
@ -106,16 +106,17 @@ An integer value, using which the level of debug logging made by the cache
|
||||
can be controlled. The value is actually a bitfield with different bits
|
||||
denoting different logging.
|
||||
|
||||
* `0` (`0b0000`) No logging is made.
|
||||
* `1` (`0b0001`) A matching rule is logged.
|
||||
* `2` (`0b0010`) A non-matching rule is logged.
|
||||
* `4` (`0b0100`) A decision to use data from the cache is logged.
|
||||
* `8` (`0b1000`) A decision not to use data from the cache is logged.
|
||||
* ` 0` (`0b00000`) No logging is made.
|
||||
* ` 1` (`0b00001`) A matching rule is logged.
|
||||
* ` 2` (`0b00010`) A non-matching rule is logged.
|
||||
* ` 4` (`0b00100`) A decision to use data from the cache is logged.
|
||||
* ` 8` (`0b01000`) A decision not to use data from the cache is logged.
|
||||
* '16' (`0b10000`) Higher level decisions are logged.
|
||||
|
||||
Default is `0`. To log everything, give `debug` a value of `15`.
|
||||
Default is `0`. To log everything, give `debug` a value of `31`.
|
||||
|
||||
```
|
||||
debug=2
|
||||
debug=31
|
||||
```
|
||||
|
||||
# Rules
|
||||
|
@ -41,16 +41,28 @@ connection failover| When a connection currently being used between MariaDB MaxS
|
||||
|
||||
## Configuration
|
||||
|
||||
The MariaDB MaxScale configuration is read from a file that MariaDB MaxScale will look for
|
||||
in a number of places.
|
||||
The MariaDB MaxScale configuration is read from a file that MariaDB MaxScale
|
||||
will look for in the following places:
|
||||
|
||||
1. Location given with the --configdir=<path> command line argument
|
||||
1. By default, the file `maxscale.cnf` in the directory `/etc`
|
||||
1. The location given with the `--configdir=<path>` command line argument.
|
||||
|
||||
2. MariaDB MaxScale will look for a configuration file called `maxscale.cnf` in the directory `/etc/maxscale.cnf`
|
||||
MariaDB MaxScale will further look for a directory with the same name as the
|
||||
configuration file, followed by `.d` (for instance `/etc/maxscale.cnf.d`) and
|
||||
recursively read all files, having a `.cnf` suffix, it finds in the directory
|
||||
hierarchy. All other files will be ignored.
|
||||
|
||||
An explicit path to a configuration file can be passed by using the `-f` option to MariaDB MaxScale.
|
||||
There are no restrictions on how different configuration sections are arranged,
|
||||
but the strong suggestion is to place global settings into the configuration
|
||||
file MariaDB MaxScale is invoked with, and then, if deemed necessary, create
|
||||
separate configuration files for _servers_, _filters_, etc.
|
||||
|
||||
The configuration file itself is based on the ".ini" file format and consists of various sections that are used to build the configuration; these sections define services, servers, listeners, monitors and global settings. Parameters, which expect a comma-separated list of values can be defined on multiple lines. The following is an example of a multi-line definition.
|
||||
The configuration file itself is based on the [.ini](https://en.wikipedia.org/wiki/INI_file)
|
||||
file format and consists of various sections that are used to build the
|
||||
configuration; these sections define services, servers, listeners, monitors and
|
||||
global settings. Parameters, which expect a comma-separated list of values can
|
||||
be defined on multiple lines. The following is an example of a multi-line
|
||||
definition.
|
||||
|
||||
```
|
||||
[MyService]
|
||||
@ -247,7 +259,11 @@ To disable these messages use the value 0 and to enable them use the value 1.
|
||||
|
||||
#### `log_debug`
|
||||
|
||||
Enable or disable the logging of messages whose syslog priority is *debug*. This kind of messages are intended for development purposes and are disabled by default.
|
||||
Enable or disable the logging of messages whose syslog priority is *debug*.
|
||||
This kind of messages are intended for development purposes and are disabled
|
||||
by default. Note that if MariaDB MaxScale has been built in release mode, then
|
||||
debug messages are excluded from the build and this setting will not have any
|
||||
effect.
|
||||
|
||||
```
|
||||
# Valid options are:
|
||||
|
@ -287,13 +287,22 @@ Then simply set this file to have execute permissions and it may be run like any
|
||||
|
||||
## The .maxadmin file
|
||||
|
||||
MaxAdmin supports a mechanism to set defaults for all the command line switches via a file in the home directory of the user. If a file named .maxadmin exists it will be read and parameters set according to the lies in this files. The parameter that can be set is: socket. An example of a .maxadmin file that will alter the default password and user name arguments would be
|
||||
MaxAdmin supports a mechanism to set defaults for the command line switches via a file in the home directory of the user. If a file named `.maxadmin` exists, it will be read and parameters set according to the entries in that file.
|
||||
|
||||
This mechanism can be used to provide defaults to the command line options. If a command line option is provided, it will still override the value in the `.maxadmin` file.
|
||||
|
||||
The parameters than can be set are:
|
||||
* `1.4`: _hostname_, _port_, _user_ and _passwd_
|
||||
* `2.0.0` and `2.0.1`: _socket_
|
||||
* `2.0.2` onwards: _socket_, _hostname_, _port_, _user_ and _passwd_ (and as synonym _password_)
|
||||
|
||||
An example of a `.maxadmin` file that will alter the default socket path is:
|
||||
|
||||
socket=/somepath/maxadmin.socket
|
||||
|
||||
This mechanism can be used to provide a means of passwords entry into maxadmin or to override any of the command line option defaults. If a command line option is given that will still override the value in the .maxadmin file.
|
||||
Note that if in `2.0.2` or later, a value for _socket_ as well as any of the internet socket related options, such as _hostname_, is provided in `.maxadmin`, then _socket_ takes precedense. In that case, provide at least one internet socket related option on the command line to force MaxAdmin to use an internet socket and thus the internet socket related options from `.maxadmin`.
|
||||
|
||||
The .maxadmin file may be made read only to protect any passwords written to that file.
|
||||
The `.maxadmin` file may be made read only to protect any passwords written to that file.
|
||||
|
||||
<a name="help"></a>
|
||||
# Getting Help
|
||||
|
@ -8,7 +8,19 @@ release 2.0.X.
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## Changes Features
|
||||
## Changed Features
|
||||
|
||||
### Configuration Files
|
||||
|
||||
From 2.1.0 onwards MariaDB MaxScale supports hierarchical configuration
|
||||
files. When invoked with a configuration file, e.g. `maxscale.cnf`, MariaDB
|
||||
MaxScale looks for a directory `maxscale.cnf.d` in the same directory as the
|
||||
configuration file, and reads all `.cnf` files it finds in that directory
|
||||
hierarchy. All other files will be ignored.
|
||||
|
||||
Please see the
|
||||
[Configuration Guide](../Getting-Started/Configuration-Guide.md#configuration)
|
||||
for details.
|
||||
|
||||
### Logging
|
||||
|
||||
@ -47,6 +59,11 @@ For more information about this configuration entry, please see
|
||||
|
||||
### User data cache
|
||||
|
||||
The user data cache stores the cached credentials that are used by some router
|
||||
modules. In 2.1.0, the authenticator modules are responsible for the persisting
|
||||
of the user data cache. Currently, only the MySQLAuth module implements user
|
||||
data caching.
|
||||
|
||||
The user data loaded from the backend databases is now stored on a per listener
|
||||
basis instead of a per service basis. In earlier versions, each service had its own
|
||||
cache directory in `/var/cache/maxscale`. This directory contains cached user
|
||||
|
@ -16,8 +16,21 @@ Binlogrouter is configured with a comma-separated list of key-value pairs. The f
|
||||
|
||||
### `binlogdir`
|
||||
|
||||
This parameter allows the location that MariaDB MaxScale uses to store binlog files to be set. If this parameter is not set to a directory name then MariaDB MaxScale will store the binlog files in the directory /var/cache/maxscale/<Service Name>.
|
||||
In the binlog dir there is also the 'cache' directory that contains data retrieved from the master during registration phase and the master.ini file which contains the configuration of current configured master.
|
||||
This parameter allows the location that MariaDB MaxScale uses to store binlog
|
||||
files to be set. If this parameter is not set to a directory name then MariaDB
|
||||
MaxScale will store the binlog files in the directory
|
||||
/var/cache/maxscale/<Service Name>. In the binlog dir there is also the 'cache'
|
||||
directory that contains data retrieved from the master during registration phase
|
||||
and the master.ini file which contains the configuration of current configured
|
||||
master.
|
||||
|
||||
From 2.1 onwards, the 'cache' directory is stored in the same location as other
|
||||
user credential caches. This means that with the default options, the user
|
||||
credential cache is stored in /var/cache/maxscale/<Service Name>/<Listener Name>/cache/.
|
||||
|
||||
Read the [MySQL Authenticator](../Authenticators/MySQL-Authenticator.md)
|
||||
documentation for instructions on how to define a custom location for the user
|
||||
cache.
|
||||
|
||||
### `uuid`
|
||||
|
||||
@ -52,6 +65,10 @@ This is the user name that MariaDB MaxScale uses when it connects to the master.
|
||||
|
||||
This user is the only one available for MySQL connection to MaxScale Binlog Server for administration when master connection is not done yet.
|
||||
|
||||
In MaxScale 2.1, the service user injection is done by the MySQLAuth
|
||||
authenticator module. Read the [MySQL Authenticator](../Authenticators/MySQL-Authenticator.md)
|
||||
documentation for more details.
|
||||
|
||||
The user that is used for replication, either defined using the user= option in the router options or using the username and password defined of the service must be granted replication privileges on the database server.
|
||||
|
||||
```
|
||||
|
86
LICENSE-THIRDPARTY.TXT
Normal file
86
LICENSE-THIRDPARTY.TXT
Normal file
@ -0,0 +1,86 @@
|
||||
The following software may be included by this product:
|
||||
|
||||
FindGSSAPI.cmake
|
||||
|
||||
Copyright (c) 2006, Pino Toscano, <toscano.pino@tiscali.it>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The "inih" library is distributed under the New BSD license:
|
||||
|
||||
Copyright (c) 2009, Brush Technology
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Brush Technology nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Release 10 of PCRE2 is distributed under the terms of the "BSD" licence.
|
||||
|
||||
THE "BSD" LICENCE
|
||||
-----------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Cambridge nor the names of any
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
@ -1,8 +1,10 @@
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_library(maxavro maxavro.c maxavro_schema.c maxavro_record.c maxavro_file.c)
|
||||
target_link_libraries(maxavro maxscale-common jansson)
|
||||
if (AVRO_FOUND AND JANSSON_FOUND)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_library(maxavro maxavro.c maxavro_schema.c maxavro_record.c maxavro_file.c)
|
||||
target_link_libraries(maxavro maxscale-common jansson)
|
||||
|
||||
add_executable(maxavrocheck maxavrocheck.c)
|
||||
target_link_libraries(maxavrocheck maxavro)
|
||||
install_executable(maxavrocheck core)
|
||||
add_subdirectory(test)
|
||||
add_executable(maxavrocheck maxavrocheck.c)
|
||||
target_link_libraries(maxavrocheck maxavro)
|
||||
install_executable(maxavrocheck core)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
@ -75,7 +75,9 @@ static void DoSource(int so, char *cmd);
|
||||
static void DoUsage(const char*);
|
||||
static int isquit(char *buf);
|
||||
static void PrintVersion(const char *progname);
|
||||
static void read_inifile(char **, int*);
|
||||
static void read_inifile(char **socket,
|
||||
char **hostname, char **port, char **user, char **passwd,
|
||||
int *editor);
|
||||
static bool getPassword(char *password, size_t length);
|
||||
|
||||
#ifdef HISTORY
|
||||
@ -116,10 +118,6 @@ static struct option long_options[] =
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const char* vi = "vi";
|
||||
const char* emacs = "emacs";
|
||||
|
||||
int i, num, rv;
|
||||
#ifdef HISTORY
|
||||
char *buf;
|
||||
EditLine *el = NULL;
|
||||
@ -133,39 +131,45 @@ main(int argc, char **argv)
|
||||
char *port = NULL;
|
||||
char *user = NULL;
|
||||
char *passwd = NULL;
|
||||
char *conn_socket = NULL;
|
||||
char *default_socket = MAXADMIN_DEFAULT_SOCKET;
|
||||
char *socket_path = NULL;
|
||||
int use_emacs = 0;
|
||||
int so;
|
||||
|
||||
read_inifile(&socket_path, &hostname, &port, &user, &passwd, &use_emacs);
|
||||
|
||||
bool use_inet_socket = false;
|
||||
bool use_unix_socket = false;
|
||||
|
||||
int option_index = 0;
|
||||
char c;
|
||||
|
||||
read_inifile(&conn_socket, &use_emacs);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h:p:P:u:S:v?e",
|
||||
long_options, &option_index)) >= 0)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
use_inet_socket = true;
|
||||
hostname = strdup(optarg);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
use_inet_socket = true;
|
||||
passwd = strdup(optarg);
|
||||
memset(optarg, '\0', strlen(optarg));
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
use_inet_socket = true;
|
||||
port = strdup(optarg);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
use_inet_socket = true;
|
||||
user = strdup(optarg);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
conn_socket = strdup(optarg);
|
||||
use_unix_socket = true;
|
||||
socket_path = strdup(optarg);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
@ -182,16 +186,20 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if ((hostname || port || user || passwd) && (conn_socket))
|
||||
if (use_inet_socket && use_unix_socket)
|
||||
{
|
||||
// Either socket or any parameters related to hostname/port.
|
||||
// Both unix socket path and at least of the internet socket
|
||||
// options have been provided.
|
||||
DoUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (hostname || port || user || passwd)
|
||||
if (use_inet_socket || (!socket_path && (hostname || port || user || passwd)))
|
||||
{
|
||||
assert(!conn_socket);
|
||||
// If any of the internet socket options have explicitly been provided, or
|
||||
// .maxadmin does not contain "socket" but does contain at least one of
|
||||
// the internet socket options, we use an internet socket. Note that if
|
||||
// -S is provided, then socket_path will be non-NULL.
|
||||
|
||||
if (!hostname)
|
||||
{
|
||||
@ -210,23 +218,29 @@ main(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!conn_socket)
|
||||
use_unix_socket = true;
|
||||
|
||||
if (!socket_path)
|
||||
{
|
||||
conn_socket = MAXADMIN_DEFAULT_SOCKET;
|
||||
socket_path = MAXADMIN_DEFAULT_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
assert(!((hostname || port) && conn_socket));
|
||||
int so;
|
||||
|
||||
if (conn_socket)
|
||||
if (use_unix_socket)
|
||||
{
|
||||
if ((so = connectUsingUnixSocket(conn_socket)) == -1)
|
||||
assert(socket_path);
|
||||
|
||||
if ((so = connectUsingUnixSocket(socket_path)) == -1)
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(hostname && user && port);
|
||||
|
||||
char password[MAX_PASSWORD_LEN];
|
||||
|
||||
if (passwd == NULL)
|
||||
@ -301,11 +315,11 @@ main(int argc, char **argv)
|
||||
|
||||
if (use_emacs)
|
||||
{
|
||||
el_set(el, EL_EDITOR, emacs); /** Editor is emacs */
|
||||
el_set(el, EL_EDITOR, "emacs"); /** Editor is emacs */
|
||||
}
|
||||
else
|
||||
{
|
||||
el_set(el, EL_EDITOR, vi); /* Default editor is vi */
|
||||
el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */
|
||||
}
|
||||
el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
|
||||
el_set(el, EL_PROMPT, prompt); /* Set the prompt function */
|
||||
@ -325,15 +339,16 @@ main(int argc, char **argv)
|
||||
*/
|
||||
el_source(el, NULL);
|
||||
|
||||
int num;
|
||||
while ((buf = (char *) el_gets(el, &num)) != NULL && num != 0)
|
||||
{
|
||||
#else
|
||||
while (printf("MaxScale> ") && fgets(buf, 1024, stdin) != NULL)
|
||||
{
|
||||
num = strlen(buf);
|
||||
int num = strlen(buf);
|
||||
#endif
|
||||
/* Strip trailing \n\r */
|
||||
for (i = num - 1; buf[i] == '\r' || buf[i] == '\n'; i--)
|
||||
for (int i = num - 1; buf[i] == '\r' || buf[i] == '\n'; i--)
|
||||
{
|
||||
buf[i] = 0;
|
||||
}
|
||||
@ -350,6 +365,7 @@ main(int argc, char **argv)
|
||||
else if (!strcasecmp(buf, "history"))
|
||||
{
|
||||
#ifdef HISTORY
|
||||
int rv;
|
||||
for (rv = history(hist, &ev, H_LAST); rv != -1;
|
||||
rv = history(hist, &ev, H_PREV))
|
||||
{
|
||||
@ -394,11 +410,11 @@ main(int argc, char **argv)
|
||||
/**
|
||||
* Connect to the MaxScale server
|
||||
*
|
||||
* @param conn_socket The UNIX socket to connect to
|
||||
* @param socket_path The UNIX socket to connect to
|
||||
* @return The connected socket or -1 on error
|
||||
*/
|
||||
static int
|
||||
connectUsingUnixSocket(const char *conn_socket)
|
||||
connectUsingUnixSocket(const char *socket_path)
|
||||
{
|
||||
int so = -1;
|
||||
|
||||
@ -408,7 +424,7 @@ connectUsingUnixSocket(const char *conn_socket)
|
||||
|
||||
memset(&local_addr, 0, sizeof local_addr);
|
||||
local_addr.sun_family = AF_UNIX;
|
||||
strncpy(local_addr.sun_path, conn_socket, sizeof(local_addr.sun_path) - 1);
|
||||
strncpy(local_addr.sun_path, socket_path, sizeof(local_addr.sun_path) - 1);
|
||||
|
||||
if (connect(so, (struct sockaddr *) &local_addr, sizeof(local_addr)) == 0)
|
||||
{
|
||||
@ -441,7 +457,7 @@ connectUsingUnixSocket(const char *conn_socket)
|
||||
{
|
||||
char errbuf[STRERROR_BUFLEN];
|
||||
fprintf(stderr, "Unable to connect to MaxScale at %s: %s\n",
|
||||
conn_socket, strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
socket_path, strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
close(so);
|
||||
so = -1;
|
||||
}
|
||||
@ -853,13 +869,16 @@ rtrim(char *str)
|
||||
* Read defaults for hostname, port, user and password from
|
||||
* the .maxadmin file in the users home directory.
|
||||
*
|
||||
* @param hostname Pointer the hostname to be updated
|
||||
* @param socket Pointer to the socket to be updated.
|
||||
* @param hostname Pointer to the hostname to be updated
|
||||
* @param port Pointer to the port to be updated
|
||||
* @param user Pointer to the user to be updated
|
||||
* @param passwd Pointer to the password to be updated
|
||||
*/
|
||||
static void
|
||||
read_inifile(char **conn_socket, int* editor)
|
||||
read_inifile(char **socket,
|
||||
char **hostname, char** port, char **user, char **passwd,
|
||||
int* editor)
|
||||
{
|
||||
char pathname[400];
|
||||
char *home, *brkt;
|
||||
@ -893,11 +912,26 @@ read_inifile(char **conn_socket, int* editor)
|
||||
{
|
||||
if (strcmp(name, "socket") == 0)
|
||||
{
|
||||
*conn_socket = strdup(value);
|
||||
*socket = strdup(value);
|
||||
}
|
||||
else if (strcmp(name, "hostname") == 0)
|
||||
{
|
||||
*hostname = strdup(value);
|
||||
}
|
||||
else if (strcmp(name, "port") == 0)
|
||||
{
|
||||
*port = strdup(value);
|
||||
}
|
||||
else if (strcmp(name, "user") == 0)
|
||||
{
|
||||
*user = strdup(value);
|
||||
}
|
||||
else if ((strcmp(name, "passwd") == 0) || (strcmp(name, "password") == 0))
|
||||
{
|
||||
*passwd = strdup(value);
|
||||
}
|
||||
else if (strcmp(name, "editor") == 0)
|
||||
{
|
||||
|
||||
if (strcmp(value, "vi") == 0)
|
||||
{
|
||||
*editor = 0;
|
||||
|
16
cmake/FindJansson.cmake
Normal file
16
cmake/FindJansson.cmake
Normal file
@ -0,0 +1,16 @@
|
||||
# This CMake file locates the Jansson libraries and headers
|
||||
#
|
||||
# The following variables are set:
|
||||
# JANSSON_FOUND - If the Jansson library was found
|
||||
# JANSSON_LIBRARIES - Path to the static library
|
||||
# JANSSON_INCLUDE_DIR - Path to Jansson headers
|
||||
|
||||
find_path(JANSSON_INCLUDE_DIR jansson.h)
|
||||
find_library(JANSSON_LIBRARIES NAMES libjansson.so libjansson.a)
|
||||
|
||||
if (JANSSON_INCLUDE_DIR AND JANSSON_LIBRARIES)
|
||||
message(STATUS "Found Jansson: ${JANSSON_LIBRARIES}")
|
||||
set(JANSSON_FOUND TRUE)
|
||||
else()
|
||||
message(STATUS "Could not find Jansson")
|
||||
endif()
|
23
cmake/FindSQLite.cmake
Normal file
23
cmake/FindSQLite.cmake
Normal file
@ -0,0 +1,23 @@
|
||||
# This CMake file locates the SQLite3 development libraries
|
||||
#
|
||||
# The following variables are set:
|
||||
# SQLITE_FOUND - If the SQLite library was found
|
||||
# SQLITE_LIBRARIES - Path to the static library
|
||||
# SQLITE_INCLUDE_DIR - Path to SQLite headers
|
||||
# SQLITE_VERSION - Library version
|
||||
|
||||
find_path(SQLITE_INCLUDE_DIR sqlite3.h)
|
||||
find_library(SQLITE_LIBRARIES NAMES libsqlite3.so)
|
||||
|
||||
if (SQLITE_INCLUDE_DIR AND SQLITE_LIBRARIES)
|
||||
|
||||
execute_process(COMMAND grep ".*#define.*SQLITE_VERSION " ${SQLITE_INCLUDE_DIR}/sqlite3.h
|
||||
COMMAND sed "s/.*\"\\(.*\\)\".*/\\1/"
|
||||
OUTPUT_VARIABLE SQLITE_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
message(STATUS "Found SQLite version ${SQLITE_VERSION}: ${SQLITE_LIBRARIES}")
|
||||
set(SQLITE_FOUND TRUE)
|
||||
else()
|
||||
message(STATUS "Could not find SQLite")
|
||||
endif()
|
@ -5,9 +5,18 @@ After=network.target
|
||||
[Service]
|
||||
Type=forking
|
||||
Restart=on-abort
|
||||
PIDFile=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid
|
||||
|
||||
# Make sure /var/run/maxscale exists
|
||||
PermissionsStartOnly=true
|
||||
ExecStartPre=/usr/bin/install -d @MAXSCALE_VARDIR@/run/maxscale -o maxscale -g maxscale
|
||||
ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale --user=maxscale
|
||||
|
||||
PIDFile=@MAXSCALE_VARDIR@/run/maxscale/maxscale.pid
|
||||
|
||||
# Use the default user and group
|
||||
User=maxscale
|
||||
Group=maxscale
|
||||
|
||||
ExecStart=@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@/maxscale
|
||||
TimeoutStartSec=120
|
||||
LimitNOFILE=65535
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_ADMINUSERS_H
|
||||
#define _MAXSCALE_ADMINUSERS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -69,5 +67,3 @@ extern bool admin_verify_inet_user(const char *uname, const char *passwor
|
||||
extern void dcb_PrintAdminUsers(DCB *dcb);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_ALLOC_H
|
||||
#define _MAXSCALE_ALLOC_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -79,5 +77,3 @@ char *mxs_strndup_a(const char *s, size_t n/*, const char *caller*/);
|
||||
#define MXS_ABORT_IF_FALSE(b) do { if (!b) { abort(); } } while (false)
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_ATOMIC_H
|
||||
#define _MAXSCALE_ATOMIC_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -35,5 +33,3 @@ int atomic_add(int *variable, int value);
|
||||
int atomic_add(int *variable, int value);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_BUFFER_H
|
||||
#define _MAXSCALE_BUFFER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -219,5 +217,3 @@ extern void dprintAllBuffers(void *pdcb);
|
||||
#endif
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_CDEFS_H
|
||||
#define _MAXSCALE_CDEFS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -35,6 +33,9 @@
|
||||
# define MXS_END_DECLS
|
||||
#endif
|
||||
|
||||
#define _XOPEN_SOURCE 700
|
||||
#define OPENSSL_THREAD_DEFINES
|
||||
|
||||
/**
|
||||
* Define intended for use with strerror.
|
||||
*
|
||||
@ -73,5 +74,3 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_CONFIG_H
|
||||
#define _MAXSCALE_CONFIG_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -31,10 +29,9 @@
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <limits.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <stdint.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <maxscale/spinlock.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
@ -132,31 +129,31 @@ typedef struct
|
||||
} GATEWAY_CONF;
|
||||
|
||||
|
||||
char* config_clean_string_list(char* str);
|
||||
CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param);
|
||||
char* config_clean_string_list(const char* str);
|
||||
CONFIG_PARAMETER* config_clone_param(const CONFIG_PARAMETER* param);
|
||||
void config_enable_feedback_task(void);
|
||||
void config_disable_feedback_task(void);
|
||||
unsigned long config_get_gateway_id(void);
|
||||
GATEWAY_CONF* config_get_global_options();
|
||||
CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name);
|
||||
config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param);
|
||||
bool config_get_valint(int* val,
|
||||
CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
config_param_type_t config_get_paramtype(const CONFIG_PARAMETER* param);
|
||||
bool config_get_valint(int* val,
|
||||
const CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
config_param_type_t ptype);
|
||||
bool config_get_valbool(bool* val,
|
||||
CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
bool config_get_valbool(bool* val,
|
||||
const CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
config_param_type_t ptype);
|
||||
bool config_get_valtarget(target_t* val,
|
||||
CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
bool config_get_valtarget(target_t* val,
|
||||
const CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
config_param_type_t ptype);
|
||||
bool config_load(char *);
|
||||
bool config_load(const char *);
|
||||
unsigned int config_nbpolls();
|
||||
double config_percentage_value(char *str);
|
||||
double config_percentage_value(const char *str);
|
||||
unsigned int config_pollsleep();
|
||||
int config_reload();
|
||||
bool config_reload();
|
||||
bool config_set_qualified_param(CONFIG_PARAMETER* param,
|
||||
void* val,
|
||||
config_param_type_t type);
|
||||
@ -166,5 +163,3 @@ void free_config_parameter(CONFIG_PARAMETER* p1);
|
||||
bool is_internal_service(const char *router);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_DCB_H
|
||||
#define _MAXSCALE_DCB_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -375,5 +373,3 @@ void dcb_append_readqueue(DCB *dcb, GWBUF *buffer);
|
||||
#define DCB_REPLIED(d) ((d)->flags & DCBF_REPLIED)
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* _DCB_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_DEBUG_H
|
||||
#define _MAXSCALE_DEBUG_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -568,5 +566,3 @@ static bool conn_open[10240];
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_EXTERN_CMD_HG
|
||||
#define _MAXSCALE_EXTERN_CMD_HG
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -42,5 +40,3 @@ bool externcmd_can_execute(const char* argstr);
|
||||
bool externcmd_matches(const EXTERNCMD* cmd, const char* match);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_FILTER_H
|
||||
#define _MAXSCALE_FILTER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -25,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <maxscale/routing.h>
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/buffer.h>
|
||||
@ -83,6 +82,7 @@ typedef struct filter_object
|
||||
int (*routeQuery)(FILTER *instance, void *fsession, GWBUF *queue);
|
||||
int (*clientReply)(FILTER *instance, void *fsession, GWBUF *queue);
|
||||
void (*diagnostics)(FILTER *instance, void *fsession, DCB *dcb);
|
||||
uint64_t (*getCapabilities)(void);
|
||||
} FILTER_OBJECT;
|
||||
|
||||
/**
|
||||
@ -90,7 +90,7 @@ typedef struct filter_object
|
||||
* is changed these values must be updated in line with the rules in the
|
||||
* file modinfo.h.
|
||||
*/
|
||||
#define FILTER_VERSION {2, 1, 0}
|
||||
#define FILTER_VERSION {2, 2, 0}
|
||||
/**
|
||||
* The definition of a filter from the configuration file.
|
||||
* This is basically the link between a plugin to load and the
|
||||
@ -121,6 +121,20 @@ void dprintAllFilters(DCB *);
|
||||
void dprintFilter(DCB *, FILTER_DEF *);
|
||||
void dListFilters(DCB *);
|
||||
|
||||
MXS_END_DECLS
|
||||
/**
|
||||
* Specifies capabilities specific for filters. Common capabilities
|
||||
* are defined by @c routing_capability_t.
|
||||
*
|
||||
* @see routing_capability_t
|
||||
*
|
||||
* @note The values of the capabilities here *must* be between 0x000100000000
|
||||
* and 0x800000000000, that is, bits 32 to 47.
|
||||
*/
|
||||
|
||||
#endif
|
||||
/*
|
||||
typedef enum filter_capability
|
||||
{
|
||||
} filter_capability_t;
|
||||
*/
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_GW_AUTHENTICATOR_H
|
||||
#define _MAXSCALE_GW_AUTHENTICATOR_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -72,7 +70,7 @@ struct servlistener;
|
||||
* destroy Destroy the unique DCB data returned by the `create`
|
||||
* entry point.
|
||||
*
|
||||
* loadUsers Load or update authenticator user data
|
||||
* loadusers Load or update authenticator user data
|
||||
* @endverbatim
|
||||
*
|
||||
* This forms the "module object" for authenticator modules within the gateway.
|
||||
@ -102,7 +100,8 @@ typedef struct gw_authenticator
|
||||
|
||||
/** Return values for the loadusers entry point */
|
||||
#define MXS_AUTH_LOADUSERS_OK 0 /**< Users loaded successfully */
|
||||
#define MXS_AUTH_LOADUSERS_ERROR 1 /**< Failed to load users */
|
||||
#define MXS_AUTH_LOADUSERS_ERROR 1 /**< Temporary error, service is started */
|
||||
#define MXS_AUTH_LOADUSERS_FATAL 2 /**< Fatal error, service is not started */
|
||||
|
||||
/**
|
||||
* Authentication states
|
||||
@ -136,9 +135,6 @@ typedef enum
|
||||
|
||||
|
||||
bool authenticator_init(void **instance, const char *authenticator, const char *options);
|
||||
char* get_default_authenticator(const char *protocol);
|
||||
const char* get_default_authenticator(const char *protocol);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* GW_AUTHENTICATOR_H */
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_GW_PROTOCOL_H
|
||||
#define _MAXSCALE_GW_PROTOCOL_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -86,6 +84,3 @@ typedef struct gw_protocol
|
||||
#define GWPROTOCOL_VERSION {1, 1, 0}
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* GW_PROTOCOL_H */
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_GW_SSL_H
|
||||
#define _MAXSCALE_GW_SSL_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -83,5 +81,3 @@ bool ssl_required_but_not_negotiated(struct dcb *dcb);
|
||||
const char* ssl_method_type_to_string(ssl_method_type_t method_type);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* _GW_SSL_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_GWBITMASK_H
|
||||
#define _MAXSCALE_GWBITMASK_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -60,5 +58,3 @@ extern void bitmask_copy(GWBITMASK *, GWBITMASK *);
|
||||
extern char *bitmask_render_readable(GWBITMASK *);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_GW_DIRS_HG
|
||||
#define _MAXSCALE_GW_DIRS_HG
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -79,5 +77,3 @@ char* get_langdir();
|
||||
char* get_execdir();
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_HASTABLE_H
|
||||
#define _MAXSCALE_HASTABLE_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -153,5 +151,3 @@ extern void* hashtable_item_strdup(const void *str);
|
||||
extern int hashtable_item_strhash(const void *str);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_HINT_H
|
||||
#define _MAXSCALE_HINT_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -68,5 +66,3 @@ extern HINT *hint_dup(HINT *);
|
||||
bool hint_exists(HINT **, HINT_TYPE);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_HK_HEARTBEAT_H
|
||||
#define _MAXSCALE_HK_HEARTBEAT_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -27,5 +24,3 @@ MXS_BEGIN_DECLS
|
||||
extern long hkheartbeat;
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_HOUSEKEEPER_H
|
||||
#define _MAXSCALE_HOUSEKEEPER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -61,5 +59,3 @@ extern void hkshutdown();
|
||||
extern void hkshow_tasks(DCB *pdcb);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_LIMITS_H
|
||||
#define _MAXSCALE_LIMITS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -74,5 +72,3 @@ MXS_BEGIN_DECLS
|
||||
#define MXS_MAX_THREADS 255
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_LISTENER_H
|
||||
#define _MAXSCALE_LISTENER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -70,5 +68,3 @@ void listener_set_certificates(SSL_LISTENER *ssl_listener, char* cert, char* key
|
||||
int listener_init_SSL(SSL_LISTENER *ssl_listener);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_LISTMANAGER_H
|
||||
#define _MAXSCALE_LISTMANAGER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -120,5 +118,3 @@ list_entry_t *list_remove_first(LIST_CONFIG *list_config);
|
||||
list_entry_t *list_remove_last(LIST_CONFIG *list_config);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* LISTMANAGER_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_LOG_MANAGER_H
|
||||
#define _MAXSCALE_LOG_MANAGER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -15,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
@ -111,6 +110,12 @@ void mxs_log_set_throttling(const MXS_LOG_THROTTLING* throttling);
|
||||
|
||||
void mxs_log_get_throttling(MXS_LOG_THROTTLING* throttling);
|
||||
|
||||
static inline bool mxs_log_priority_is_enabled(int priority)
|
||||
{
|
||||
assert((priority & ~LOG_PRIMASK) == 0);
|
||||
return MXS_LOG_PRIORITY_IS_ENABLED(priority);
|
||||
}
|
||||
|
||||
int mxs_log_message(int priority,
|
||||
const char* modname,
|
||||
const char* file, int line, const char* function,
|
||||
@ -126,7 +131,9 @@ int mxs_log_message(int priority,
|
||||
* MXS_ERROR, MXS_WARNING, etc. macros instead.
|
||||
*/
|
||||
#define MXS_LOG_MESSAGE(priority, format, ...)\
|
||||
mxs_log_message(priority, MXS_MODULE_NAME, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__)
|
||||
(mxs_log_priority_is_enabled(priority) ? \
|
||||
mxs_log_message(priority, MXS_MODULE_NAME, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__) :\
|
||||
0)
|
||||
|
||||
/**
|
||||
* Log an alert, error, warning, notice, info, or debug message.
|
||||
@ -147,7 +154,12 @@ int mxs_log_message(int priority,
|
||||
#define MXS_WARNING(format, ...) MXS_LOG_MESSAGE(LOG_WARNING, format, ##__VA_ARGS__)
|
||||
#define MXS_NOTICE(format, ...) MXS_LOG_MESSAGE(LOG_NOTICE, format, ##__VA_ARGS__)
|
||||
#define MXS_INFO(format, ...) MXS_LOG_MESSAGE(LOG_INFO, format, ##__VA_ARGS__)
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#define MXS_DEBUG(format, ...) MXS_LOG_MESSAGE(LOG_DEBUG, format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define MXS_DEBUG(format, ...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Log an out of memory error using custom message.
|
||||
@ -189,5 +201,3 @@ enum
|
||||
};
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /** LOG_MANAGER_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MAXADMIN_H
|
||||
#define _MAXSCALE_MAXADMIN_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -34,5 +32,3 @@ MXS_BEGIN_DECLS
|
||||
#define MAXADMIN_AUTH_PASSWORD_PROMPT_LEN 8
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MAXSCALE_H
|
||||
#define _MAXSCALE_MAXSCALE_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -47,5 +45,3 @@ time_t maxscale_started(void);
|
||||
int maxscale_uptime(void);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MEMLOG_H
|
||||
#define _MAXSCALE_MEMLOG_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -65,5 +63,3 @@ extern void memlog_flush_all();
|
||||
extern void memlog_flush(MEMLOG *);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MODINFO_H
|
||||
#define _MAXSCALE_MODINFO_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -90,5 +88,3 @@ typedef struct
|
||||
} MODULE_INFO;
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MODULES_H
|
||||
#define _MAXSCALE_MODULES_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -75,5 +73,3 @@ extern void module_feedback_send(void*);
|
||||
extern void moduleShowFeedbackReport(DCB *dcb);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MODUTIL_H
|
||||
#define _MAXSCALE_MODUTIL_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -76,5 +74,3 @@ bool is_mysql_sp_end(const char* start, int len);
|
||||
char* modutil_get_canonical(GWBUF* querybuf);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MONITOR_H
|
||||
#define _MAXSCALE_MONITOR_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -232,5 +230,3 @@ void mon_log_connect_error(MONITOR_SERVERS* database, connect_result_t rval);
|
||||
void mon_log_state_change(MONITOR_SERVERS *ptr);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MYSQL_BINLOG_H
|
||||
#define _MAXSCALE_MYSQL_BINLOG_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -96,5 +94,3 @@ uint64_t unpack_bit(uint8_t *ptr, uint8_t *null_mask, uint32_t col_count,
|
||||
void format_temporal_value(char *str, size_t size, uint8_t type, struct tm *tm);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* MYSQL_BINLOG_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_MYSQL_UTILS_H
|
||||
#define _MAXSCALE_MYSQL_UTILS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -34,5 +32,3 @@ char* lestr_consume(uint8_t** c, size_t *size);
|
||||
MYSQL *mxs_mysql_real_connect(MYSQL *mysql, SERVER *server, const char *user, const char *passwd);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_NOTIFICATION_H
|
||||
#define _MAXSCALE_NOTIFICATION_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -64,5 +62,3 @@ extern void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out);
|
||||
extern FEEDBACK_CONF * config_get_feedback_data();
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_PCRE2_H
|
||||
#define _MAXSCALE_PCRE2_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -29,14 +27,16 @@
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
#if defined(PCRE2_CODE_UNIT_WIDTH)
|
||||
#error PCRE2_CODE_UNIT_WIDTH already defined. Do not define, and include <maxscale/pcre2.h>.
|
||||
#else
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
|
||||
#include <pcre2.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MXS_PCRE2_MATCH,
|
||||
@ -50,5 +50,3 @@ mxs_pcre2_result_t mxs_pcre2_simple_match(const char* pattern, const char* subje
|
||||
int options, int* error);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_PLATFORM_H
|
||||
#define _MAXSCALE_PLATFORM_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -54,5 +52,3 @@ MXS_BEGIN_DECLS
|
||||
#endif // __cplusplus
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif // _PLATFORM_H
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_POLL_H
|
||||
#define _MAXSCALE_POLL_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -75,5 +73,3 @@ extern void poll_fake_write_event(DCB *dcb);
|
||||
extern void poll_fake_read_event(DCB *dcb);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,4 @@
|
||||
#ifndef _MAXSCALE_PROTOCOL_MYSQL_H
|
||||
#define _MAXSCALE_PROTOCOL_MYSQL_H
|
||||
#pragma once
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -59,7 +58,6 @@
|
||||
#include <maxscale/router.h>
|
||||
#include <maxscale/poll.h>
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/dbusers.h>
|
||||
#include <maxscale/version.h>
|
||||
#include <maxscale/housekeeper.h>
|
||||
#include <maxscale/utils.h>
|
||||
@ -93,6 +91,7 @@ MXS_BEGIN_DECLS
|
||||
#define GW_MYSQL_SCRAMBLE_SIZE 20
|
||||
#define GW_SCRAMBLE_LENGTH_323 8
|
||||
|
||||
/** Name of the default server side authentication plugin */
|
||||
#define DEFAULT_MYSQL_AUTH_PLUGIN "mysql_native_password"
|
||||
|
||||
/** All authentication responses are at least this many bytes long */
|
||||
@ -105,7 +104,12 @@ MXS_BEGIN_DECLS
|
||||
# define MYSQL_SCRAMBLE_LEN GW_MYSQL_SCRAMBLE_SIZE
|
||||
#endif
|
||||
|
||||
#define MYSQL_HOSTNAME_MAXLEN 60
|
||||
/* Max length of fields in the mysql.user table */
|
||||
#define MYSQL_USER_MAXLEN 128
|
||||
#define MYSQL_PASSWORD_LEN 41
|
||||
#define MYSQL_HOST_MAXLEN 60
|
||||
#define MYSQL_DATABASE_MAXLEN 128
|
||||
#define MYSQL_TABLE_MAXLEN 64
|
||||
|
||||
#define GW_NOINTR_CALL(A) do { errno = 0; A; } while (errno == EINTR)
|
||||
#define SMALL_CHUNK 1024
|
||||
@ -383,5 +387,3 @@ int mxs_mysql_send_ok(DCB *dcb, int sequence, int affected_rows, const char* mes
|
||||
bool mxs_mysql_is_ok_packet(GWBUF *buffer);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /** _MYSQL_PROTOCOL_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_QUERY_CLASSIFIER_HG
|
||||
#define _MAXSCALE_QUERY_CLASSIFIER_HG
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -19,7 +17,15 @@
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
#define QUERY_CLASSIFIER_VERSION {1, 1, 0}
|
||||
|
||||
/**
|
||||
* qc_query_type_t defines bits that provide information about a
|
||||
* particular statement.
|
||||
*
|
||||
* Note that more than one bit may be set for a single statement.
|
||||
*/
|
||||
typedef enum qc_query_type
|
||||
{
|
||||
QUERY_TYPE_UNKNOWN = 0x000000, /*< Initial value, can't be tested bitwisely */
|
||||
QUERY_TYPE_LOCAL_READ = 0x000001, /*< Read non-database data, execute in MaxScale:any */
|
||||
@ -49,7 +55,10 @@ typedef enum
|
||||
QUERY_TYPE_SHOW_TABLES = 0x400000 /*< Show list of tables */
|
||||
} qc_query_type_t;
|
||||
|
||||
typedef enum
|
||||
/**
|
||||
* qc_query_op_t defines the operations a particular statement can perform.
|
||||
*/
|
||||
typedef enum qc_query_op
|
||||
{
|
||||
QUERY_OP_UNDEFINED = 0,
|
||||
QUERY_OP_SELECT = (1 << 0),
|
||||
@ -66,6 +75,9 @@ typedef enum
|
||||
QUERY_OP_REVOKE = (1 << 11)
|
||||
} qc_query_op_t;
|
||||
|
||||
/**
|
||||
* qc_parse_result_t defines the possible outcomes when a statement is parsed.
|
||||
*/
|
||||
typedef enum qc_parse_result
|
||||
{
|
||||
QC_QUERY_INVALID = 0, /*< The query was not recognized or could not be parsed. */
|
||||
@ -74,39 +86,15 @@ typedef enum qc_parse_result
|
||||
QC_QUERY_PARSED = 3 /*< The query was fully parsed; completely classified. */
|
||||
} qc_parse_result_t;
|
||||
|
||||
#define QUERY_IS_TYPE(mask,type) ((mask & type) == type)
|
||||
|
||||
bool qc_init(const char* plugin_name, const char* plugin_args);
|
||||
void qc_end(void);
|
||||
|
||||
typedef struct query_classifier QUERY_CLASSIFIER;
|
||||
|
||||
QUERY_CLASSIFIER* qc_load(const char* plugin_name);
|
||||
void qc_unload(QUERY_CLASSIFIER* classifier);
|
||||
|
||||
bool qc_thread_init(void);
|
||||
void qc_thread_end(void);
|
||||
|
||||
qc_parse_result_t qc_parse(GWBUF* querybuf);
|
||||
|
||||
uint32_t qc_get_type(GWBUF* querybuf);
|
||||
qc_query_op_t qc_get_operation(GWBUF* querybuf);
|
||||
|
||||
char* qc_get_created_table_name(GWBUF* querybuf);
|
||||
bool qc_is_drop_table_query(GWBUF* querybuf);
|
||||
bool qc_is_real_query(GWBUF* querybuf);
|
||||
char** qc_get_table_names(GWBUF* querybuf, int* tblsize, bool fullnames);
|
||||
char* qc_get_canonical(GWBUF* querybuf);
|
||||
bool qc_query_has_clause(GWBUF* buf);
|
||||
char* qc_get_qtype_str(qc_query_type_t qtype);
|
||||
char* qc_get_affected_fields(GWBUF* buf);
|
||||
char** qc_get_database_names(GWBUF* querybuf, int* size);
|
||||
|
||||
const char* qc_op_to_string(qc_query_op_t op);
|
||||
const char* qc_type_to_string(qc_query_type_t type);
|
||||
char* qc_types_to_string(uint32_t types);
|
||||
|
||||
struct query_classifier
|
||||
/**
|
||||
* QUERY_CLASSIFIER defines the object a query classifier plugin must
|
||||
* implement and return.
|
||||
*
|
||||
* To a user of the query classifier functionality, it can in general
|
||||
* be ignored.
|
||||
*/
|
||||
typedef struct query_classifier
|
||||
{
|
||||
bool (*qc_init)(const char* args);
|
||||
void (*qc_end)(void);
|
||||
@ -114,23 +102,306 @@ struct query_classifier
|
||||
bool (*qc_thread_init)(void);
|
||||
void (*qc_thread_end)(void);
|
||||
|
||||
qc_parse_result_t (*qc_parse)(GWBUF* querybuf);
|
||||
qc_parse_result_t (*qc_parse)(GWBUF* stmt);
|
||||
|
||||
uint32_t (*qc_get_type)(GWBUF* querybuf);
|
||||
qc_query_op_t (*qc_get_operation)(GWBUF* querybuf);
|
||||
uint32_t (*qc_get_type)(GWBUF* stmt);
|
||||
qc_query_op_t (*qc_get_operation)(GWBUF* stmt);
|
||||
|
||||
char* (*qc_get_created_table_name)(GWBUF* querybuf);
|
||||
bool (*qc_is_drop_table_query)(GWBUF* querybuf);
|
||||
bool (*qc_is_real_query)(GWBUF* querybuf);
|
||||
char** (*qc_get_table_names)(GWBUF* querybuf, int* tblsize, bool fullnames);
|
||||
char* (*qc_get_canonical)(GWBUF* querybuf);
|
||||
bool (*qc_query_has_clause)(GWBUF* buf);
|
||||
char* (*qc_get_affected_fields)(GWBUF* buf);
|
||||
char** (*qc_get_database_names)(GWBUF* querybuf, int* size);
|
||||
};
|
||||
char* (*qc_get_created_table_name)(GWBUF* stmt);
|
||||
bool (*qc_is_drop_table_query)(GWBUF* stmt);
|
||||
bool (*qc_is_real_query)(GWBUF* stmt);
|
||||
char** (*qc_get_table_names)(GWBUF* stmt, int* tblsize, bool fullnames);
|
||||
char* (*qc_get_canonical)(GWBUF* stmt);
|
||||
bool (*qc_query_has_clause)(GWBUF* stmt);
|
||||
char* (*qc_get_affected_fields)(GWBUF* stmt);
|
||||
char** (*qc_get_database_names)(GWBUF* stmt, int* size);
|
||||
char* (*qc_get_prepare_name)(GWBUF* stmt);
|
||||
} QUERY_CLASSIFIER;
|
||||
|
||||
#define QUERY_CLASSIFIER_VERSION {1, 0, 0}
|
||||
/**
|
||||
* Loads and initializes the default query classifier.
|
||||
*
|
||||
* This must be called once during the execution of a process. The query
|
||||
* classifier functions can only be used if this function returns true.
|
||||
* MaxScale calls this function, so plugins should not do that.
|
||||
*
|
||||
* @param plugin_name The name of the plugin from which the query classifier
|
||||
* should be loaded.
|
||||
* @param plugin_args The arguments to be provided to the query classifier.
|
||||
*
|
||||
* @return True if the query classifier could be loaded and initialized,
|
||||
* false otherwise.
|
||||
*
|
||||
* @see qc_end qc_thread_init
|
||||
*/
|
||||
bool qc_init(const char* plugin_name, const char* plugin_args);
|
||||
|
||||
/**
|
||||
* Finalizes and unloads the query classifier.
|
||||
*
|
||||
* A successful call of qc_init() should before program exit be followed
|
||||
* by a call to this function. MaxScale calls this function, so plugins
|
||||
* should not do that.
|
||||
*
|
||||
* @see qc_init qc_thread_end
|
||||
*/
|
||||
void qc_end(void);
|
||||
|
||||
/**
|
||||
* Loads a particular query classifier.
|
||||
*
|
||||
* In general there is no need to use this function, but rely upon qc_init().
|
||||
* However, if there is a need to use multiple query classifiers concurrently
|
||||
* then this function provides the means for that. Note that after a query
|
||||
* classifier has been loaded, it must explicitly be initialized before it
|
||||
* can be used.
|
||||
*
|
||||
* @param plugin_name The name of the plugin from which the query classifier
|
||||
* should be loaded.
|
||||
*
|
||||
* @return A QUERY_CLASSIFIER object if successful, NULL otherwise.
|
||||
*
|
||||
* @see qc_unload
|
||||
*/
|
||||
QUERY_CLASSIFIER* qc_load(const char* plugin_name);
|
||||
|
||||
/**
|
||||
* Unloads an explicitly loaded query classifier.
|
||||
*
|
||||
* @see qc_load
|
||||
*/
|
||||
void qc_unload(QUERY_CLASSIFIER* classifier);
|
||||
|
||||
/**
|
||||
* Performs thread initialization needed by the query classifier.
|
||||
* Should be called in every thread, except the one where qc_init()
|
||||
* was called. MaxScale calls this function, so plugins should not
|
||||
* do that.
|
||||
*
|
||||
* @return True if the initialization succeeded, false otherwise.
|
||||
*
|
||||
* @see qc_thread_end
|
||||
*/
|
||||
bool qc_thread_init(void);
|
||||
|
||||
/**
|
||||
* Performs thread finalization needed by the query classifier.
|
||||
* A successful call to qc_thread_init() should at some point be followed
|
||||
* by a call to this function. MaxScale calls this function, so plugins
|
||||
* should not do that.
|
||||
*
|
||||
* @see qc_thread_init
|
||||
*/
|
||||
void qc_thread_end(void);
|
||||
|
||||
/**
|
||||
* Parses the statement in the provided buffer and returns a value specifying
|
||||
* to what extent the statement could be parsed.
|
||||
*
|
||||
* There is no need to call this function explicitly before calling any of
|
||||
* the other functions; e.g. qc_get_type(). When some particular property of
|
||||
* a statement is asked for, the statement will be parsed if it has not been
|
||||
* parsed yet. Also, if the statement in the provided buffer has been parsed
|
||||
* already then this function will only return the result of that parsing;
|
||||
* the statement will not be parsed again.
|
||||
*
|
||||
* @param stmt A buffer containing an COM_QUERY packet.
|
||||
*
|
||||
* @return To what extent the statement could be parsed.
|
||||
*/
|
||||
qc_parse_result_t qc_parse(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the fields the statement affects, as a string of names separated
|
||||
* by spaces. Note that the fields do not contain any table information.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
*
|
||||
* @return A string containing the fields or NULL if a memory allocation
|
||||
* failure occurs. The string must be freed by the caller.
|
||||
*/
|
||||
char* qc_get_affected_fields(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the statement, with literals replaced with question marks.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
*
|
||||
* @return A statement in its canonical form, or NULL if a memory
|
||||
* allocation fails. The string must be freed by the caller.
|
||||
*/
|
||||
char* qc_get_canonical(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the name of the created table.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
*
|
||||
* @return The name of the created table or NULL if the statement
|
||||
* does not create a table or a memory allocation failed.
|
||||
* The string must be freed by the caller.
|
||||
*/
|
||||
char* qc_get_created_table_name(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the databases accessed by the statement. Note that a
|
||||
* possible default database is not returned.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
* @param size Pointer to integer where the number of databases
|
||||
* is stored.
|
||||
*
|
||||
* @return Array of strings or NULL if a memory allocation fails.
|
||||
*
|
||||
* @note The returned array and the strings pointed to @b must be freed
|
||||
* by the caller.
|
||||
*/
|
||||
char** qc_get_database_names(GWBUF* stmt, int* size);
|
||||
|
||||
/**
|
||||
* Returns the operation of the statement.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
*
|
||||
* @return The operation of the statement.
|
||||
*/
|
||||
qc_query_op_t qc_get_operation(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the name of the prepared statement, if the statement
|
||||
* is a PREPARE or EXECUTE statement.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
*
|
||||
* @return The name of the prepared statement, if the statement
|
||||
* is a PREPARE or EXECUTE statement; otherwise NULL.
|
||||
*
|
||||
* @note The returned string @b must be freed by the caller.
|
||||
*
|
||||
* @note Even though a COM_STMT_PREPARE can be given to the query
|
||||
* classifier for parsing, this function will in that case
|
||||
* return NULL since the id of the statement is provided by
|
||||
* the server.
|
||||
*/
|
||||
char* qc_get_prepare_name(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the tables accessed by the statement.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
* @param tblsize Pointer to integer where the number of tables is stored.
|
||||
* @param fullnames If true, a table names will include the database name
|
||||
* as well (if explicitly referred to in the statement).
|
||||
*
|
||||
* @return Array of strings or NULL if a memory allocation fails.
|
||||
*
|
||||
* @note The returned array and the strings pointed to @b must be freed
|
||||
* by the caller.
|
||||
*/
|
||||
char** qc_get_table_names(GWBUF* stmt, int* size, bool fullnames);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a bitmask specifying the type(s) of the statement. The result
|
||||
* should be tested against specific qc_query_type_t values* using the
|
||||
* bitwise & operator, never using the == operator.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
*
|
||||
* @return A bitmask with the type(s) the query.
|
||||
*
|
||||
* @see qc_query_is_type
|
||||
*/
|
||||
uint32_t qc_get_type(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns whether the statement is a DROP TABLE statement.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY packet.
|
||||
*
|
||||
* @return True if the statement is a DROP TABLE statement, false otherwise.
|
||||
*
|
||||
* @todo This function is far too specific.
|
||||
*/
|
||||
bool qc_is_drop_table_query(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns whether the statement is a "real" statement. Statements that affect
|
||||
* the underlying database are considered real statements, while statements that
|
||||
* target specific rows or variable data are regarded as real statement. That is,
|
||||
* a real statement is SELECT, UPDATE, INSERT, DELETE or a variation thereof.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY.
|
||||
*
|
||||
* @return True if the statement is a real query, false otherwise.
|
||||
*
|
||||
* @todo Consider whether the function name should be changed or the function
|
||||
* removed altogether.
|
||||
*/
|
||||
bool qc_is_real_query(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the string representation of a query operation.
|
||||
*
|
||||
* @param op A query operation.
|
||||
*
|
||||
* @return The corresponding string.
|
||||
*
|
||||
* @note The returned string is statically allocated and must *not* be freed.
|
||||
*/
|
||||
const char* qc_op_to_string(qc_query_op_t op);
|
||||
|
||||
/**
|
||||
* Returns whether the typemask contains a particular type.
|
||||
*
|
||||
* @param typemask A bitmask of query types.
|
||||
* @param type A particular qc_query_type_t value.
|
||||
*
|
||||
* @return True, if the type is in the mask.
|
||||
*/
|
||||
static inline bool qc_query_is_type(uint32_t typemask, qc_query_type_t type)
|
||||
{
|
||||
return (typemask & type) == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the statement has a WHERE or a USING clause.
|
||||
*
|
||||
* @param stmt A buffer containing a COM_QUERY.
|
||||
*
|
||||
* @return True, if the statement has a WHERE or USING clause, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool qc_query_has_clause(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the string representation of a query type.
|
||||
*
|
||||
* @param type A query type (not a bitmask of several).
|
||||
*
|
||||
* @return The corresponding string.
|
||||
*
|
||||
* @note The returned string is statically allocated and must @b not be freed.
|
||||
*/
|
||||
const char* qc_type_to_string(qc_query_type_t type);
|
||||
|
||||
/**
|
||||
* Returns a string representation of a type bitmask.
|
||||
*
|
||||
* @param typemask A bit mask of query types.
|
||||
*
|
||||
* @return The corresponding string or NULL if the allocation fails.
|
||||
*
|
||||
* @note The returned string is dynamically allocated and @b must be freed.
|
||||
*/
|
||||
char* qc_typemask_to_string(uint32_t typemask);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Synonym for qc_query_is_type().
|
||||
*
|
||||
* @see qc_query_is_type
|
||||
*/
|
||||
#define QUERY_IS_TYPE(typemask, type) qc_query_is_type(typemask, type)
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_QUEUEMANAGER_H
|
||||
#define _MAXSCALE_QUEUEMANAGER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -72,5 +70,3 @@ mxs_queue_count(QUEUE_CONFIG *queue_config)
|
||||
}
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* QUEUEMANAGER_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_RANDOM_JKISS_H
|
||||
#define _MAXSCALE_RANDOM_JKISS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -28,5 +26,3 @@ MXS_BEGIN_DECLS
|
||||
extern unsigned int random_jkiss(void);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif /* RANDOM_H */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_RDTSC_H
|
||||
#define _MAXSCALE_RDTSC_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -60,5 +58,3 @@ static __inline__ CYCLES rdtsc(void)
|
||||
}
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_RESULTSET_H
|
||||
#define _MAXSCALE_RESULTSET_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -90,5 +88,3 @@ extern void resultset_stream_mysql(RESULTSET *, DCB *);
|
||||
extern void resultset_stream_json(RESULTSET *, DCB *);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_ROUTER_H
|
||||
#define _MAXSCALE_ROUTER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -30,6 +28,7 @@
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <maxscale/routing.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/buffer.h>
|
||||
@ -82,7 +81,7 @@ typedef struct router_object
|
||||
DCB* backend_dcb,
|
||||
error_action_t action,
|
||||
bool* succp);
|
||||
int (*getCapabilities)();
|
||||
uint64_t (*getCapabilities)(void);
|
||||
} ROUTER_OBJECT;
|
||||
|
||||
/**
|
||||
@ -90,21 +89,22 @@ typedef struct router_object
|
||||
* must update these versions numbers in accordance with the rules in
|
||||
* modinfo.h.
|
||||
*/
|
||||
#define ROUTER_VERSION { 1, 0, 0 }
|
||||
#define ROUTER_VERSION { 2, 0, 0 }
|
||||
|
||||
/**
|
||||
* Router capability type. Indicates what kind of input router accepts.
|
||||
* Specifies capabilities specific for routers. Common capabilities
|
||||
* are defined by @c routing_capability_t.
|
||||
*
|
||||
* @see routing_capability_t
|
||||
*
|
||||
* @note The values of the capabilities here *must* be between 0x00010000
|
||||
* and 0x80000000, that is, bits 16 to 31.
|
||||
*/
|
||||
typedef enum router_capability_t
|
||||
typedef enum router_capability
|
||||
{
|
||||
RCAP_TYPE_UNDEFINED = 0x00,
|
||||
RCAP_TYPE_STMT_INPUT = 0x01, /**< Statement per buffer */
|
||||
RCAP_TYPE_PACKET_INPUT = 0x02, /**< Data as it was read from DCB */
|
||||
RCAP_TYPE_NO_RSESSION = 0x04, /**< Router does not use router sessions */
|
||||
RCAP_TYPE_NO_USERS_INIT = 0x08 /**< Prevent the loading of authenticator
|
||||
users when the service is started */
|
||||
RCAP_TYPE_NO_RSESSION = 0x00010000, /**< Router does not use router sessions */
|
||||
RCAP_TYPE_NO_USERS_INIT = 0x00020000, /**< Prevent the loading of authenticator
|
||||
users when the service is started */
|
||||
} router_capability_t;
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
57
include/maxscale/routing.h
Normal file
57
include/maxscale/routing.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
|
||||
*
|
||||
* Change Date: 2019-07-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file routing.h - Common definitions and declarations for routers and filters.
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Routing capability type. Indicates what kind of input a router or
|
||||
* a filter accepts.
|
||||
*
|
||||
* @note The values of the capabilities here *must* be between 0x0000
|
||||
* and 0x8000, that is, bits 0 to 15.
|
||||
*/
|
||||
typedef enum routing_capability
|
||||
{
|
||||
/**< Statements are delivered one per buffer. */
|
||||
RCAP_TYPE_STMT_INPUT = 0x0001, /* 0b0000000000000001 */
|
||||
/**< Each delivered buffer is contiguous; implies RCAP_TYPE_STMT_INPUT. */
|
||||
RCAP_TYPE_CONTIGUOUS_INPUT = 0x0003, /* 0b0000000000000011 */
|
||||
/**< The transaction state and autocommit mode of the session are tracked;
|
||||
implies RCAP_TYPE_CONTIGUOUS_INPUT and RCAP_TYPE_STMT_INPUT. */
|
||||
RCAP_TYPE_TRANSACTION_TRACKING = 0x0007, /* 0b0000000000000111 */
|
||||
} routing_capability_t;
|
||||
|
||||
#define RCAP_TYPE_NONE 0
|
||||
|
||||
/**
|
||||
* Determines whether a particular capability type is required.
|
||||
*
|
||||
* @param capabilites The capability bits to be tested.
|
||||
* @param type A particular capability type or a bitmask of types.
|
||||
*
|
||||
* @return True, if @c type is present in @c capabilities.
|
||||
*/
|
||||
static inline bool rcap_type_required(uint64_t capabilities, uint64_t type)
|
||||
{
|
||||
return (capabilities & type) == type;
|
||||
}
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_SECRETS_H
|
||||
#define _MAXSCALE_SECRETS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -60,5 +58,3 @@ extern char *decryptPassword(const char *);
|
||||
extern char *encryptPassword(const char*, const char *);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_SERVER_H
|
||||
#define _MAXSCALE_SERVER_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -228,5 +226,3 @@ extern unsigned int server_map_status(char *str);
|
||||
extern bool server_set_version_string(SERVER* server, const char* string);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_SERVICE_H
|
||||
#define _MAXSCALE_SERVICE_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -114,6 +112,15 @@ typedef struct server_ref_t
|
||||
*/
|
||||
#define SERVICE_PARAM_UNINIT -1
|
||||
|
||||
/* Refresh rate limits for load users from database */
|
||||
#define USERS_REFRESH_TIME 30 /* Allowed time interval (in seconds) after last update*/
|
||||
#define USERS_REFRESH_MAX_PER_TIME 4 /* Max number of load calls within the time interval */
|
||||
|
||||
/** Default timeout values used by the connections which fetch user authentication data */
|
||||
#define DEFAULT_AUTH_CONNECT_TIMEOUT 3
|
||||
#define DEFAULT_AUTH_READ_TIMEOUT 1
|
||||
#define DEFAULT_AUTH_WRITE_TIMEOUT 2
|
||||
|
||||
/**
|
||||
* Defines a service within the gateway.
|
||||
*
|
||||
@ -158,6 +165,7 @@ typedef struct service
|
||||
struct service *next; /**< The next service in the linked list */
|
||||
bool retry_start; /*< If starting of the service should be retried later */
|
||||
bool log_auth_warnings; /*< Log authentication failures and warnings */
|
||||
uint64_t capabilities; /*< The capabilities of the service. */
|
||||
} SERVICE;
|
||||
|
||||
typedef enum count_spec_t
|
||||
@ -228,6 +236,17 @@ extern RESULTSET *serviceGetList();
|
||||
extern RESULTSET *serviceGetListenerList();
|
||||
extern bool service_all_services_have_listeners();
|
||||
|
||||
MXS_END_DECLS
|
||||
/**
|
||||
* Get the capabilities of the servive.
|
||||
*
|
||||
* The capabilities of a service are the union of the capabilities of
|
||||
* its router and all filters.
|
||||
*
|
||||
* @return The service capabilities.
|
||||
*/
|
||||
static inline uint64_t service_get_capabilities(const SERVICE *service)
|
||||
{
|
||||
return service->capabilities;
|
||||
}
|
||||
|
||||
#endif
|
||||
MXS_END_DECLS
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_SESSION_H
|
||||
#define _MAXSCALE_SESSION_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -73,6 +71,34 @@ typedef enum
|
||||
SESSION_STATE_DUMMY /*< dummy session for consistency */
|
||||
} session_state_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SESSION_TRX_INACTIVE_BIT = 1, /* 0b0001 */
|
||||
SESSION_TRX_ACTIVE_BIT = 2, /* 0b0010 */
|
||||
SESSION_TRX_READ_ONLY_BIT = 4, /* 0b0100 */
|
||||
SESSION_TRX_READ_WRITE_BIT = 8, /* 0b1000 */
|
||||
} session_trx_state_bit_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/*< There is no on-going transaction. */
|
||||
SESSION_TRX_INACTIVE = SESSION_TRX_INACTIVE_BIT,
|
||||
/*< A transaction is active. */
|
||||
SESSION_TRX_ACTIVE = SESSION_TRX_ACTIVE_BIT,
|
||||
/*< An explicit READ ONLY transaction is active. */
|
||||
SESSION_TRX_READ_ONLY = (SESSION_TRX_ACTIVE_BIT | SESSION_TRX_READ_ONLY_BIT),
|
||||
/*< An explicit READ WRITE transaction is active. */
|
||||
SESSION_TRX_READ_WRITE = (SESSION_TRX_ACTIVE_BIT | SESSION_TRX_READ_WRITE_BIT)
|
||||
} session_trx_state_t;
|
||||
|
||||
/**
|
||||
* Convert transaction state to string representation.
|
||||
*
|
||||
* @param state A transaction state.
|
||||
* @return String representation of the state.
|
||||
*/
|
||||
const char* session_trx_state_to_string(session_trx_state_t state);
|
||||
|
||||
/**
|
||||
* The downstream element in the filter chain. This may refer to
|
||||
* another filter or to a router.
|
||||
@ -150,6 +176,8 @@ typedef struct session
|
||||
UPSTREAM tail; /*< The tail of the filter chain */
|
||||
int refcount; /*< Reference count on the session */
|
||||
bool ses_is_child; /*< this is a child session */
|
||||
session_trx_state_t trx_state; /*< The current transaction state. */
|
||||
bool autocommit; /*< Whether autocommit is on. */
|
||||
skygw_chk_t ses_chk_tail;
|
||||
} SESSION;
|
||||
|
||||
@ -206,6 +234,122 @@ RESULTSET *sessionGetList(SESSIONLISTFILTER);
|
||||
void process_idle_sessions();
|
||||
void enable_session_timeouts();
|
||||
|
||||
MXS_END_DECLS
|
||||
/**
|
||||
* Get the transaction state of the session.
|
||||
*
|
||||
* Note that this tells only the state of @e explicitly started transactions.
|
||||
* That is, if @e autocommit is OFF, which means that there is always an
|
||||
* active transaction that is ended with an explicit COMMIT or ROLLBACK,
|
||||
* at which point a new transaction is started, this function will still
|
||||
* return SESSION_TRX_INACTIVE, unless a transaction has explicitly been
|
||||
* started with START TRANSACTION.
|
||||
*
|
||||
* Likewise, if @e autocommit is ON, which means that every statement is
|
||||
* executed in a transaction of its own, this will return false, unless a
|
||||
* transaction has explicitly been started with START TRANSACTION.
|
||||
*
|
||||
* @note The return value is valid only if either a router or a filter
|
||||
* has declared that it needs RCAP_TYPE_TRANSACTION_TRACKING.
|
||||
*
|
||||
* @param ses The SESSION object.
|
||||
* @return The transaction state.
|
||||
*/
|
||||
session_trx_state_t session_get_trx_state(const SESSION* ses);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Set the transaction state of the session.
|
||||
*
|
||||
* NOTE: Only the protocol object may call this.
|
||||
*
|
||||
* @param ses The SESSION object.
|
||||
* @param new_state The new transaction state.
|
||||
*
|
||||
* @return The previous transaction state.
|
||||
*/
|
||||
session_trx_state_t session_set_trx_state(SESSION* ses, session_trx_state_t new_state);
|
||||
|
||||
/**
|
||||
* Tells whether an explicit transaction is active.
|
||||
*
|
||||
* @see session_get_trx_state
|
||||
*
|
||||
* @note The return value is valid only if either a router or a filter
|
||||
* has declared that it needs RCAP_TYPE_TRANSACTION_TRACKING.
|
||||
*
|
||||
* @return True if a transaction is active, false otherwise.
|
||||
*/
|
||||
static inline bool session_trx_is_active(const SESSION* ses)
|
||||
{
|
||||
return ses->trx_state & SESSION_TRX_ACTIVE_BIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether an explicit READ ONLY transaction is active.
|
||||
*
|
||||
* @see session_get_trx_state
|
||||
*
|
||||
* @note The return value is valid only if either a router or a filter
|
||||
* has declared that it needs RCAP_TYPE_TRANSACTION_TRACKING.
|
||||
*
|
||||
* @return True if an explicit READ ONLY transaction is active,
|
||||
* false otherwise.
|
||||
*/
|
||||
static inline bool session_trx_is_read_only(const SESSION* ses)
|
||||
{
|
||||
return ses->trx_state == SESSION_TRX_READ_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether an explicit READ WRITE transaction is active.
|
||||
*
|
||||
* @see session_get_trx_state
|
||||
*
|
||||
* @note The return value is valid only if either a router or a filter
|
||||
* has declared that it needs RCAP_TYPE_TRANSACTION_TRACKING.
|
||||
*
|
||||
* @return True if an explicit READ WRITE transaction is active,
|
||||
* false otherwise.
|
||||
*/
|
||||
static inline bool session_trx_is_read_write(const SESSION* ses)
|
||||
{
|
||||
return ses->trx_state == SESSION_TRX_READ_WRITE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether autocommit is ON or not.
|
||||
*
|
||||
* Note that the returned value effectively only tells the last value
|
||||
* of the statement "set autocommit=...".
|
||||
*
|
||||
* That is, if the statement "set autocommit=1" has been executed, then
|
||||
* even if a transaction has been started, which implicitly will cause
|
||||
* autocommit to be set to 0 for the duration of the transaction, this
|
||||
* function will still return true.
|
||||
*
|
||||
* Note also that by default autocommit is ON.
|
||||
*
|
||||
* @see session_get_trx_state
|
||||
*
|
||||
* @return True if autocommit has been set ON, false otherwise.
|
||||
*/
|
||||
static inline bool session_is_autocommit(const SESSION* ses)
|
||||
{
|
||||
return ses->autocommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the autocommit state of the session.
|
||||
*
|
||||
* NOTE: Only the protocol object may call this.
|
||||
*
|
||||
* @param enable True if autocommit is enabled, false otherwise.
|
||||
* @return The previous state.
|
||||
*/
|
||||
static inline bool session_set_autocommit(SESSION* ses, bool autocommit)
|
||||
{
|
||||
bool prev_autocommit = ses->autocommit;
|
||||
ses->autocommit = autocommit;
|
||||
return prev_autocommit;
|
||||
}
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_SPINLOCK_H
|
||||
#define _MAXSCALE_SPINLOCK_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -78,5 +76,3 @@ extern void spinlock_release(const SPINLOCK *lock);
|
||||
extern void spinlock_stats(const SPINLOCK *lock, void (*reporter)(void *, char *, int), void *hdl);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_STATISTICS_HG
|
||||
#define _MAXSCALE_STATISTICS_HG
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -73,5 +71,3 @@ ts_stats_set(ts_stats_t stats, int value, int thread_id)
|
||||
}
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_THREAD_H
|
||||
#define _MAXSCALE_THREAD_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -39,5 +37,3 @@ extern void thread_wait(THREAD thd);
|
||||
extern void thread_millisleep(int ms);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_USERS_H
|
||||
#define _MAXSCALE_USERS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -76,5 +74,3 @@ extern void usersPrint(USERS *); /**< Print data about the user
|
||||
extern void dcb_usersPrint(DCB *, USERS *); /**< Print data about the users loaded */
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef _MAXSCALE_UTILS_H
|
||||
#define _MAXSCALE_UTILS_H
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
@ -72,12 +70,10 @@ char* replace_literal(char* haystack,
|
||||
const char* replacement);
|
||||
char* replace_quoted(const char** src, const size_t* srcsize, char** dest, size_t* destsize);
|
||||
|
||||
void clean_up_pathname(char *path);
|
||||
bool clean_up_pathname(char *path);
|
||||
|
||||
bool mxs_mkdir_all(const char *path, int mask);
|
||||
|
||||
long get_processor_count();
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -630,6 +630,14 @@ static uint32_t resolve_query_type(THD* thd)
|
||||
|
||||
case SQLCOM_BEGIN:
|
||||
type |= QUERY_TYPE_BEGIN_TRX;
|
||||
if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_READ_WRITE)
|
||||
{
|
||||
type |= QUERY_TYPE_WRITE;
|
||||
}
|
||||
else if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_READ_ONLY)
|
||||
{
|
||||
type |= QUERY_TYPE_READ;
|
||||
}
|
||||
goto return_qtype;
|
||||
break;
|
||||
|
||||
@ -1075,15 +1083,6 @@ static void* skygw_get_affected_tables(void* lexptr)
|
||||
return (void*) lex->current_select->table_list.first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the parsetree and lists all the affected tables and views in the query.
|
||||
* In the case of an error, the size of the table is set to zero and no memory
|
||||
* is allocated. The caller must free the allocated memory.
|
||||
*
|
||||
* @param querybuf GWBUF where the table names are extracted from
|
||||
* @param tblsize Pointer where the number of tables is written
|
||||
* @return Array of null-terminated strings with the table names
|
||||
*/
|
||||
char** qc_get_table_names(GWBUF* querybuf, int* tblsize, bool fullnames)
|
||||
{
|
||||
LEX* lex;
|
||||
@ -1185,11 +1184,6 @@ retblock:
|
||||
return tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract, allocate memory and copy the name of the created table.
|
||||
* @param querybuf Buffer to use.
|
||||
* @return A pointer to the name if a table was created, otherwise NULL
|
||||
*/
|
||||
char* qc_get_created_table_name(GWBUF* querybuf)
|
||||
{
|
||||
if (querybuf == NULL)
|
||||
@ -1218,16 +1212,6 @@ char* qc_get_created_table_name(GWBUF* querybuf)
|
||||
return table_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the query is a "real" query ie. SELECT,UPDATE,INSERT,DELETE or
|
||||
* any variation of these. Queries that affect the underlying database are not
|
||||
* considered as real queries and the queries that target specific row or
|
||||
* variable data are regarded as the real queries.
|
||||
*
|
||||
* @param GWBUF to analyze
|
||||
*
|
||||
* @return true if the query is a real query, otherwise false
|
||||
*/
|
||||
bool qc_is_real_query(GWBUF* querybuf)
|
||||
{
|
||||
bool succp;
|
||||
@ -1283,11 +1267,6 @@ retblock:
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the buffer contains a DROP TABLE... query.
|
||||
* @param querybuf Buffer to inspect
|
||||
* @return true if it contains the query otherwise false
|
||||
*/
|
||||
bool qc_is_drop_table_query(GWBUF* querybuf)
|
||||
{
|
||||
bool answer = false;
|
||||
@ -1514,11 +1493,6 @@ static void collect_affected_fields(collect_source_t source,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the fields that the query affects.
|
||||
* @param buf Buffer to parse
|
||||
* @return Pointer to newly allocated string or NULL if nothing was found
|
||||
*/
|
||||
char* qc_get_affected_fields(GWBUF* buf)
|
||||
{
|
||||
LEX* lex;
|
||||
@ -1765,17 +1739,6 @@ static void parsing_info_set_plain_str(void* ptr, char* str)
|
||||
pi->pi_query_plain_str = str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of strings of databases that this query uses.
|
||||
* If the database isn't defined in the query, it is assumed that this query
|
||||
* only targets the current database.
|
||||
* The value of @p size is set to the number of allocated strings. The caller is
|
||||
* responsible for freeing all the allocated memory.
|
||||
* @param querybuf GWBUF containing the query
|
||||
* @param size Size of the resulting array
|
||||
* @return A new array of strings containing the database names or NULL if no
|
||||
* databases were found.
|
||||
*/
|
||||
char** qc_get_database_names(GWBUF* querybuf, int* size)
|
||||
{
|
||||
LEX* lex;
|
||||
@ -1941,6 +1904,31 @@ qc_query_op_t qc_get_operation(GWBUF* querybuf)
|
||||
return operation;
|
||||
}
|
||||
|
||||
char* qc_get_prepare_name(GWBUF* stmt)
|
||||
{
|
||||
char* name = NULL;
|
||||
|
||||
if (stmt)
|
||||
{
|
||||
if (ensure_query_is_parsed(stmt))
|
||||
{
|
||||
LEX* lex = get_lex(stmt);
|
||||
|
||||
if (lex->sql_command == SQLCOM_PREPARE)
|
||||
{
|
||||
name = (char*)malloc(lex->prepared_stmt_name.length + 1);
|
||||
if (name)
|
||||
{
|
||||
memcpy(name, lex->prepared_stmt_name.str, lex->prepared_stmt_name.length);
|
||||
name[lex->prepared_stmt_name.length] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -2085,6 +2073,7 @@ static QUERY_CLASSIFIER qc =
|
||||
qc_query_has_clause,
|
||||
qc_get_affected_fields,
|
||||
qc_get_database_names,
|
||||
qc_get_prepare_name,
|
||||
};
|
||||
|
||||
/* @see function load_module in load_utils.c for explanation of the following
|
||||
|
@ -11,17 +11,18 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#define MXS_MODULE_NAME "qc_sqlite"
|
||||
#include <sqliteInt.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/modinfo.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <maxscale/platform.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/platform.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include "builtin_functions.h"
|
||||
|
||||
//#define QC_TRACE_ENABLED
|
||||
@ -77,6 +78,9 @@ typedef struct qc_sqlite_info
|
||||
size_t database_names_capacity; // The capacity of database_names.
|
||||
int keyword_1; // The first encountered keyword.
|
||||
int keyword_2; // The second encountered keyword.
|
||||
char* prepare_name; // The name of a prepared statement.
|
||||
size_t preparable_stmt_offset; // The start of the preparable statement.
|
||||
size_t preparable_stmt_length; // The length of the preparable statement.
|
||||
} QC_SQLITE_INFO;
|
||||
|
||||
typedef enum qc_log_level
|
||||
@ -326,6 +330,9 @@ static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info)
|
||||
info->database_names_capacity = 0;
|
||||
info->keyword_1 = 0; // Sqlite3 starts numbering tokens from 1, so 0 means
|
||||
info->keyword_2 = 0; // that we have not seen a keyword.
|
||||
info->prepare_name = NULL;
|
||||
info->preparable_stmt_offset = 0;
|
||||
info->preparable_stmt_length = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
@ -348,7 +355,7 @@ static void parse_query_string(const char* query, size_t len)
|
||||
if (qc_info_was_tokenized(this_thread.info->status))
|
||||
{
|
||||
format =
|
||||
"qc_sqlite: Statement was classified only based on keywords "
|
||||
"Statement was classified only based on keywords "
|
||||
"(Sqlite3 error: %s, %s): \"%.*s%s\"";
|
||||
}
|
||||
else
|
||||
@ -356,7 +363,7 @@ static void parse_query_string(const char* query, size_t len)
|
||||
if (qc_info_was_parsed(this_thread.info->status))
|
||||
{
|
||||
format =
|
||||
"qc_sqlite: Statement was only partially parsed "
|
||||
"Statement was only partially parsed "
|
||||
"(Sqlite3 error: %s, %s): \"%.*s%s\"";
|
||||
|
||||
// The status was set to QC_QUERY_PARSED, but sqlite3 returned an
|
||||
@ -366,7 +373,7 @@ static void parse_query_string(const char* query, size_t len)
|
||||
else
|
||||
{
|
||||
format =
|
||||
"qc_sqlite: Statement was neither parsed nor recognized from keywords "
|
||||
"Statement was neither parsed nor recognized from keywords "
|
||||
"(Sqlite3 error: %s, %s): \"%.*s%s\"";
|
||||
}
|
||||
}
|
||||
@ -406,7 +413,7 @@ static void parse_query_string(const char* query, size_t len)
|
||||
{
|
||||
// This suggests a callback from the parser into this module is not made.
|
||||
format =
|
||||
"qc_sqlite: Statement was classified only based on keywords, "
|
||||
"Statement was classified only based on keywords, "
|
||||
"even though the statement was parsed: \"%.*s%s\"";
|
||||
|
||||
MXS_WARNING(format, l, query, suffix);
|
||||
@ -416,7 +423,7 @@ static void parse_query_string(const char* query, size_t len)
|
||||
// This suggests there are keywords that should be recognized but are not,
|
||||
// a tentative classification cannot be (or is not) made using the keywords
|
||||
// seen and/or a callback from the parser into this module is not made.
|
||||
format = "qc_sqlite: Statement was parsed, but not classified: \"%.*s%s\"";
|
||||
format = "Statement was parsed, but not classified: \"%.*s%s\"";
|
||||
|
||||
MXS_ERROR(format, l, query, suffix);
|
||||
}
|
||||
@ -433,36 +440,85 @@ static bool parse_query(GWBUF* query)
|
||||
bool parsed = false;
|
||||
ss_dassert(!query_is_parsed(query));
|
||||
|
||||
QC_SQLITE_INFO* info = info_alloc();
|
||||
|
||||
if (info)
|
||||
if (GWBUF_IS_CONTIGUOUS(query))
|
||||
{
|
||||
this_thread.info = info;
|
||||
|
||||
// TODO: Somewhere it needs to be ensured that this buffer is contiguous.
|
||||
// TODO: Where is it checked that the GWBUF really contains a query?
|
||||
uint8_t* data = (uint8_t*) GWBUF_DATA(query);
|
||||
size_t len = MYSQL_GET_PACKET_LEN(data) - 1; // Subtract 1 for packet type byte.
|
||||
|
||||
const char* s = (const char*) &data[5]; // TODO: Are there symbolic constants somewhere?
|
||||
if ((GWBUF_LENGTH(query) >= MYSQL_HEADER_LEN + 1) &&
|
||||
(GWBUF_LENGTH(query) == MYSQL_HEADER_LEN + MYSQL_GET_PACKET_LEN(data)))
|
||||
{
|
||||
if (MYSQL_GET_COMMAND(data) == MYSQL_COM_QUERY)
|
||||
{
|
||||
QC_SQLITE_INFO* info = info_alloc();
|
||||
|
||||
this_thread.info->query = s;
|
||||
this_thread.info->query_len = len;
|
||||
parse_query_string(s, len);
|
||||
this_thread.info->query = NULL;
|
||||
this_thread.info->query_len = 0;
|
||||
if (info)
|
||||
{
|
||||
this_thread.info = info;
|
||||
|
||||
// TODO: Add return value to gwbuf_add_buffer_object.
|
||||
// Always added; also when it was not recognized. If it was not recognized now,
|
||||
// it won't be if we try a second time.
|
||||
gwbuf_add_buffer_object(query, GWBUF_PARSING_INFO, info, buffer_object_free);
|
||||
parsed = true;
|
||||
size_t len = MYSQL_GET_PACKET_LEN(data) - 1; // Subtract 1 for packet type byte.
|
||||
|
||||
this_thread.info = NULL;
|
||||
const char* s = (const char*) &data[MYSQL_HEADER_LEN + 1];
|
||||
|
||||
this_thread.info->query = s;
|
||||
this_thread.info->query_len = len;
|
||||
parse_query_string(s, len);
|
||||
this_thread.info->query = NULL;
|
||||
this_thread.info->query_len = 0;
|
||||
|
||||
if (info->types & QUERY_TYPE_PREPARE_NAMED_STMT)
|
||||
{
|
||||
QC_SQLITE_INFO* preparable_info = info_alloc();
|
||||
|
||||
if (preparable_info)
|
||||
{
|
||||
this_thread.info = preparable_info;
|
||||
|
||||
const char *preparable_s = s + info->preparable_stmt_offset;
|
||||
size_t preparable_len = info->preparable_stmt_length;
|
||||
|
||||
this_thread.info->query = preparable_s;
|
||||
this_thread.info->query_len = preparable_len;
|
||||
parse_query_string(preparable_s, preparable_len);
|
||||
this_thread.info->query = NULL;
|
||||
this_thread.info->query_len = 0;
|
||||
|
||||
// TODO: Perhaps the rest of the stuff should be
|
||||
// TODO: copied as well.
|
||||
info->operation = preparable_info->operation;
|
||||
|
||||
info_free(preparable_info);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add return value to gwbuf_add_buffer_object.
|
||||
// Always added; also when it was not recognized. If it was not recognized now,
|
||||
// it won't be if we try a second time.
|
||||
gwbuf_add_buffer_object(query, GWBUF_PARSING_INFO, info, buffer_object_free);
|
||||
parsed = true;
|
||||
|
||||
this_thread.info = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Could not allocate structure for containing parse data.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("The provided buffer does not contain a COM_QUERY, but a %s.",
|
||||
STRPACKETTYPE(MYSQL_GET_COMMAND(data)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Packet size %ld, provided buffer is %ld.",
|
||||
MYSQL_HEADER_LEN + MYSQL_GET_PACKET_LEN(data),
|
||||
GWBUF_LENGTH(query));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: Could not allocate structure for containing parse data.");
|
||||
MXS_ERROR("Provided buffer is not contiguous.");
|
||||
}
|
||||
|
||||
return parsed;
|
||||
@ -565,7 +621,7 @@ static void log_invalid_data(GWBUF* query, const char* message)
|
||||
length = GWBUF_LENGTH(query) - MYSQL_HEADER_LEN - 1;
|
||||
}
|
||||
|
||||
MXS_INFO("qc_sqlite: Parsing the query failed, %s: %*s", message, length, sql);
|
||||
MXS_INFO("Parsing the query failed, %s: %*s", message, length, sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -703,13 +759,13 @@ static void update_affected_fields(QC_SQLITE_INFO* info,
|
||||
}
|
||||
else if (zToken[0] != '?')
|
||||
{
|
||||
MXS_WARNING("qc_sqlite: %s reported as VARIABLE.", zToken);
|
||||
MXS_WARNING("%s reported as VARIABLE.", zToken);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MXS_DEBUG("qc_sqlite: Token %d not handled explicitly.", pExpr->op);
|
||||
MXS_DEBUG("Token %d not handled explicitly.", pExpr->op);
|
||||
// Fallthrough intended.
|
||||
case TK_BETWEEN:
|
||||
case TK_CASE:
|
||||
@ -968,7 +1024,7 @@ void mxs_sqlite3BeginTransaction(Parse* pParse, int type)
|
||||
ss_dassert(info);
|
||||
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->types = QUERY_TYPE_BEGIN_TRX;
|
||||
info->types = QUERY_TYPE_BEGIN_TRX | type;
|
||||
}
|
||||
|
||||
void mxs_sqlite3BeginTrigger(Parse *pParse, /* The parse context of the CREATE TRIGGER statement */
|
||||
@ -1532,6 +1588,13 @@ void maxscaleDeallocate(Parse* pParse, Token* pName)
|
||||
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->types = QUERY_TYPE_WRITE;
|
||||
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void maxscaleDo(Parse* pParse, ExprList* pEList)
|
||||
@ -1569,6 +1632,13 @@ void maxscaleExecute(Parse* pParse, Token* pName)
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->types = QUERY_TYPE_WRITE;
|
||||
info->is_real_query = true;
|
||||
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void maxscaleExplain(Parse* pParse, SrcList* pName)
|
||||
@ -1936,6 +2006,19 @@ void maxscalePrepare(Parse* pParse, Token* pName, Token* pStmt)
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->types = QUERY_TYPE_PREPARE_NAMED_STMT;
|
||||
info->is_real_query = true;
|
||||
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
}
|
||||
|
||||
// We store the position of the preparable statement inside the original
|
||||
// statement. That will allow us to later create a new GWBUF of the
|
||||
// parsable statment and parse that.
|
||||
info->preparable_stmt_offset = pParse->sLastToken.z - pParse->zTail + 1; // Ignore starting quote.
|
||||
info->preparable_stmt_length = pStmt->n - 2; // Remove starting and ending quotes.
|
||||
}
|
||||
|
||||
void maxscalePrivileges(Parse* pParse, int kind)
|
||||
@ -2383,18 +2466,18 @@ static bool qc_sqlite_init(const char* args)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("qc_sqlite: '%s' is not a number between %d and %d.",
|
||||
MXS_WARNING("'%s' is not a number between %d and %d.",
|
||||
value, QC_LOG_NOTHING, QC_LOG_NON_TOKENIZED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("qc_sqlite: '%s' is not a recognized argument.", key);
|
||||
MXS_WARNING("'%s' is not a recognized argument.", key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("qc_sqlite: '%s' is not a recognized argument string.", args);
|
||||
MXS_WARNING("'%s' is not a recognized argument string.", args);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2430,7 +2513,7 @@ static bool qc_sqlite_init(const char* args)
|
||||
ss_dassert(!true);
|
||||
}
|
||||
|
||||
MXS_NOTICE("qc_sqlite: %s", message);
|
||||
MXS_NOTICE("%s", message);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -2473,12 +2556,12 @@ static bool qc_sqlite_thread_init(void)
|
||||
{
|
||||
this_thread.initialized = true;
|
||||
|
||||
MXS_INFO("qc_sqlite: In-memory sqlite database successfully opened for thread %lu.",
|
||||
MXS_INFO("In-memory sqlite database successfully opened for thread %lu.",
|
||||
(unsigned long) pthread_self());
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: Failed to open in-memory sqlite database for thread %lu: %d, %s",
|
||||
MXS_ERROR("Failed to open in-memory sqlite database for thread %lu: %d, %s",
|
||||
(unsigned long) pthread_self(), rc, sqlite3_errstr(rc));
|
||||
}
|
||||
|
||||
@ -2496,7 +2579,7 @@ static void qc_sqlite_thread_end(void)
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
{
|
||||
MXS_WARNING("qc_sqlite: The closing of the thread specific sqlite database failed: %d, %s",
|
||||
MXS_WARNING("The closing of the thread specific sqlite database failed: %d, %s",
|
||||
rc, sqlite3_errstr(rc));
|
||||
}
|
||||
|
||||
@ -2537,7 +2620,7 @@ static uint32_t qc_sqlite_get_type(GWBUF* query)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return types;
|
||||
@ -2565,7 +2648,7 @@ static qc_query_op_t qc_sqlite_get_operation(GWBUF* query)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return op;
|
||||
@ -2597,7 +2680,7 @@ static char* qc_sqlite_get_created_table_name(GWBUF* query)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return created_table_name;
|
||||
@ -2625,7 +2708,7 @@ static bool qc_sqlite_is_drop_table_query(GWBUF* query)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return is_drop_table;
|
||||
@ -2653,7 +2736,7 @@ static bool qc_sqlite_is_real_query(GWBUF* query)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return is_real_query;
|
||||
@ -2697,7 +2780,7 @@ static char** qc_sqlite_get_table_names(GWBUF* query, int* tblsize, bool fullnam
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return table_names;
|
||||
@ -2709,7 +2792,7 @@ static char* qc_sqlite_get_canonical(GWBUF* query)
|
||||
ss_dassert(this_unit.initialized);
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
MXS_ERROR("qc_sqlite: qc_get_canonical not implemented yet.");
|
||||
MXS_ERROR("qc_get_canonical not implemented yet.");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -2736,7 +2819,7 @@ static bool qc_sqlite_query_has_clause(GWBUF* query)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return has_clause;
|
||||
@ -2764,7 +2847,7 @@ static char* qc_sqlite_get_affected_fields(GWBUF* query)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
if (!affected_fields)
|
||||
@ -2803,12 +2886,43 @@ static char** qc_sqlite_get_database_names(GWBUF* query, int* sizep)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qc_sqlite: The query could not be parsed. Response not valid.");
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return database_names;
|
||||
}
|
||||
|
||||
static char* qc_sqlite_get_prepare_name(GWBUF* query)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(this_unit.initialized);
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
char* name = NULL;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
|
||||
if (info)
|
||||
{
|
||||
if (qc_info_is_valid(info->status))
|
||||
{
|
||||
if (info->prepare_name)
|
||||
{
|
||||
name = MXS_STRDUP(info->prepare_name);
|
||||
}
|
||||
}
|
||||
else if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO))
|
||||
{
|
||||
log_invalid_data(query, "cannot report the name of a prepared statement");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("The query could not be parsed. Response not valid.");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPORTS
|
||||
*/
|
||||
@ -2832,6 +2946,7 @@ static QUERY_CLASSIFIER qc =
|
||||
qc_sqlite_query_has_clause,
|
||||
qc_sqlite_get_affected_fields,
|
||||
qc_sqlite_get_database_names,
|
||||
qc_sqlite_get_prepare_name,
|
||||
};
|
||||
|
||||
|
||||
|
@ -59,6 +59,13 @@
|
||||
%include {
|
||||
#include "sqliteInt.h"
|
||||
|
||||
// Copied from query_classifier.h
|
||||
enum
|
||||
{
|
||||
QUERY_TYPE_READ = 0x000002, /*< Read database data:any */
|
||||
QUERY_TYPE_WRITE = 0x000004, /*< Master data will be modified:master */
|
||||
};
|
||||
|
||||
// MaxScale naming convention:
|
||||
//
|
||||
// - A function that "overloads" a sqlite3 function has the same name
|
||||
@ -294,11 +301,13 @@ cmdx ::= cmd. { sqlite3FinishCoding(pParse); }
|
||||
//
|
||||
|
||||
%ifdef MAXSCALE
|
||||
cmd ::= BEGIN transtype(Y) trans_opt. {mxs_sqlite3BeginTransaction(pParse, Y);}
|
||||
id_opt ::= .
|
||||
id_opt ::= deferred_id.
|
||||
|
||||
cmd ::= BEGIN id_opt. {mxs_sqlite3BeginTransaction(pParse, 0);} // BEGIN [WORK]
|
||||
%endif
|
||||
%ifndef MAXSCALE
|
||||
cmd ::= BEGIN transtype(Y) trans_opt. {sqlite3BeginTransaction(pParse, Y);}
|
||||
%endif
|
||||
trans_opt ::= .
|
||||
trans_opt ::= TRANSACTION.
|
||||
trans_opt ::= TRANSACTION nm.
|
||||
@ -307,10 +316,11 @@ transtype(A) ::= . {A = TK_DEFERRED;}
|
||||
transtype(A) ::= DEFERRED(X). {A = @X;}
|
||||
transtype(A) ::= IMMEDIATE(X). {A = @X;}
|
||||
transtype(A) ::= EXCLUSIVE(X). {A = @X;}
|
||||
%endif
|
||||
%ifdef MAXSCALE
|
||||
cmd ::= COMMIT trans_opt. {mxs_sqlite3CommitTransaction(pParse);}
|
||||
cmd ::= END trans_opt. {mxs_sqlite3CommitTransaction(pParse);}
|
||||
cmd ::= ROLLBACK trans_opt. {mxs_sqlite3RollbackTransaction(pParse);}
|
||||
cmd ::= COMMIT id_opt. {mxs_sqlite3CommitTransaction(pParse);}
|
||||
cmd ::= END id_opt. {mxs_sqlite3CommitTransaction(pParse);}
|
||||
cmd ::= ROLLBACK id_opt. {mxs_sqlite3RollbackTransaction(pParse);}
|
||||
%endif
|
||||
%ifndef MAXSCALE
|
||||
cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);}
|
||||
@ -318,6 +328,7 @@ cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);}
|
||||
cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);}
|
||||
%endif
|
||||
|
||||
%ifndef MAXSCALE
|
||||
savepoint_opt ::= SAVEPOINT.
|
||||
savepoint_opt ::= .
|
||||
cmd ::= SAVEPOINT nm(X). {
|
||||
@ -329,6 +340,7 @@ cmd ::= RELEASE savepoint_opt nm(X). {
|
||||
cmd ::= ROLLBACK trans_opt TO savepoint_opt nm(X). {
|
||||
sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &X);
|
||||
}
|
||||
%endif
|
||||
|
||||
///////////////////// The CREATE TABLE statement ////////////////////////////
|
||||
//
|
||||
@ -2815,7 +2827,7 @@ execute_variables ::= VARIABLE.
|
||||
execute_variables ::= execute_variables COMMA VARIABLE.
|
||||
|
||||
execute_variables_opt ::= .
|
||||
execute_variables_opt ::= execute_variables.
|
||||
execute_variables_opt ::= USING execute_variables.
|
||||
|
||||
execute ::= EXECUTE nm(X) execute_variables_opt. {
|
||||
maxscaleExecute(pParse, &X);
|
||||
@ -3144,8 +3156,39 @@ show(A) ::= SHOW WARNINGS show_warnings_options. {
|
||||
//////////////////////// The START TRANSACTION statement ////////////////////////////////////
|
||||
//
|
||||
|
||||
cmd ::= START TRANSACTION. {
|
||||
mxs_sqlite3BeginTransaction(pParse, 0);
|
||||
%type start_transaction_characteristic {int}
|
||||
|
||||
start_transaction_characteristic(A) ::= READ WRITE. {
|
||||
A = QUERY_TYPE_WRITE;
|
||||
}
|
||||
|
||||
start_transaction_characteristic(A) ::= READ id. { // READ ONLY
|
||||
A = QUERY_TYPE_READ;
|
||||
}
|
||||
|
||||
start_transaction_characteristic(A) ::= WITH id id. { // WITH CONSISTENT SNAPSHOT
|
||||
A = 0;
|
||||
}
|
||||
|
||||
%type start_transaction_characteristics {int}
|
||||
|
||||
start_transaction_characteristics(A) ::= .
|
||||
{
|
||||
A = 0;
|
||||
}
|
||||
|
||||
start_transaction_characteristics(A) ::= start_transaction_characteristic(X).
|
||||
{
|
||||
A = X;
|
||||
}
|
||||
|
||||
start_transaction_characteristics(A) ::=
|
||||
start_transaction_characteristics(X) COMMA start_transaction_characteristic(Y). {
|
||||
A = X | Y;
|
||||
}
|
||||
|
||||
cmd ::= START TRANSACTION start_transaction_characteristics(X). {
|
||||
mxs_sqlite3BeginTransaction(pParse, X);
|
||||
}
|
||||
|
||||
//////////////////////// The TRUNCATE statement ////////////////////////////////////
|
||||
|
@ -117,17 +117,18 @@ ostream& operator << (ostream& out, qc_parse_result_t x)
|
||||
|
||||
GWBUF* create_gwbuf(const string& s)
|
||||
{
|
||||
size_t len = s.length() + 1;
|
||||
size_t gwbuf_len = len + MYSQL_HEADER_LEN + 1;
|
||||
size_t len = s.length();
|
||||
size_t payload_len = len + 1;
|
||||
size_t gwbuf_len = MYSQL_HEADER_LEN + payload_len;
|
||||
|
||||
GWBUF* gwbuf = gwbuf_alloc(gwbuf_len);
|
||||
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf))) = len;
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 1)) = (len >> 8);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 2)) = (len >> 16);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf))) = payload_len;
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 1)) = (payload_len >> 8);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 2)) = (payload_len >> 16);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 3)) = 0x00;
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 4)) = 0x03;
|
||||
memcpy((char*)GWBUF_DATA(gwbuf) + 5, s.c_str(), s.length() + 1);
|
||||
memcpy((char*)GWBUF_DATA(gwbuf) + 5, s.c_str(), len);
|
||||
|
||||
return gwbuf;
|
||||
}
|
||||
@ -353,7 +354,7 @@ bool compare_get_type(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||
|
||||
if (rv1 == rv2)
|
||||
{
|
||||
char* types = qc_types_to_string(rv1);
|
||||
char* types = qc_typemask_to_string(rv1);
|
||||
ss << "Ok : " << types;
|
||||
free(types);
|
||||
success = true;
|
||||
@ -384,8 +385,8 @@ bool compare_get_type(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||
rv2b &= ~(uint32_t)QUERY_TYPE_LOCAL_READ;
|
||||
}
|
||||
|
||||
char* types1 = qc_types_to_string(rv1);
|
||||
char* types2 = qc_types_to_string(rv2);
|
||||
char* types1 = qc_typemask_to_string(rv1);
|
||||
char* types2 = qc_typemask_to_string(rv2);
|
||||
|
||||
if (rv1b == rv2b)
|
||||
{
|
||||
@ -813,6 +814,36 @@ bool compare_get_database_names(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||
return success;
|
||||
}
|
||||
|
||||
bool compare_get_prepare_name(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||
QUERY_CLASSIFIER* pClassifier2, GWBUF* pCopy2)
|
||||
{
|
||||
bool success = false;
|
||||
const char HEADING[] = "qc_get_prepare_name : ";
|
||||
|
||||
char* rv1 = pClassifier1->qc_get_prepare_name(pCopy1);
|
||||
char* rv2 = pClassifier2->qc_get_prepare_name(pCopy2);
|
||||
|
||||
stringstream ss;
|
||||
ss << HEADING;
|
||||
|
||||
if ((!rv1 && !rv2) || (rv1 && rv2 && (strcmp(rv1, rv2) == 0)))
|
||||
{
|
||||
ss << "Ok : " << (rv1 ? rv1 : "NULL");
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "ERR: " << (rv1 ? rv1 : "NULL") << " != " << (rv2 ? rv2 : "NULL");
|
||||
}
|
||||
|
||||
report(success, ss.str());
|
||||
|
||||
free(rv1);
|
||||
free(rv2);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, const string& s)
|
||||
{
|
||||
GWBUF* pCopy1 = create_gwbuf(s);
|
||||
@ -831,6 +862,7 @@ bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, con
|
||||
errors += !compare_query_has_clause(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||
errors += !compare_get_affected_fields(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||
errors += !compare_get_database_names(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||
errors += !compare_get_prepare_name(pClassifier1, pCopy1, pClassifier2, pCopy2);
|
||||
|
||||
gwbuf_free(pCopy1);
|
||||
gwbuf_free(pCopy2);
|
||||
|
@ -54,4 +54,20 @@ SET autocommit=true;
|
||||
SET autocommit=FALSE;
|
||||
SET autocommit=Off;
|
||||
|
||||
LOAD DATA LOCAL INFILE '/tmp/data.csv' INTO TABLE test.t1;
|
||||
LOAD DATA LOCAL INFILE '/tmp/data.csv' INTO TABLE test.t1;
|
||||
|
||||
START TRANSACTION;
|
||||
START TRANSACTION READ ONLY;
|
||||
START TRANSACTION READ WRITE;
|
||||
START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;
|
||||
START TRANSACTION READ WRITE, WITH CONSISTENT SNAPSHOT;
|
||||
START TRANSACTION WITH CONSISTENT SNAPSHOT, READ ONLY;
|
||||
START TRANSACTION WITH CONSISTENT SNAPSHOT, READ WRITE;
|
||||
|
||||
BEGIN;
|
||||
BEGIN WORK;
|
||||
COMMIT;
|
||||
COMMIT WORK;
|
||||
ROLLBACK;
|
||||
ROLLBACK WORK;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c listmanager.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c)
|
||||
add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c listmanager.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c)
|
||||
|
||||
target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl pthread crypt dl crypto inih z rt m stdc++)
|
||||
|
||||
|
@ -89,14 +89,14 @@ bool authenticator_init(void** dest, const char *authenticator, const char *opti
|
||||
* @return The default authenticator for the protocol or NULL if the protocol
|
||||
* does not provide one
|
||||
*/
|
||||
char* get_default_authenticator(const char *protocol)
|
||||
const char* get_default_authenticator(const char *protocol)
|
||||
{
|
||||
char *rval = NULL;
|
||||
GWPROTOCOL *protofuncs = (GWPROTOCOL*)load_module(protocol, MODULE_PROTOCOL);
|
||||
|
||||
if (protofuncs && protofuncs->auth_default)
|
||||
{
|
||||
rval = MXS_STRDUP(protofuncs->auth_default());
|
||||
rval = protofuncs->auth_default();
|
||||
}
|
||||
|
||||
return rval;
|
||||
|
@ -44,43 +44,41 @@
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <my_config.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <ini.h>
|
||||
#include <maxscale/config.h>
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/monitor.h>
|
||||
#include <maxscale/modules.h>
|
||||
#include <maxscale/utils.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <mysql.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <ftw.h>
|
||||
#include <glob.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <maxscale/housekeeper.h>
|
||||
#include <maxscale/notification.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <maxscale/dbusers.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <ini.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/housekeeper.h>
|
||||
#include <maxscale/limits.h>
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#include <pcre2.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/modules.h>
|
||||
#include <maxscale/monitor.h>
|
||||
#include <maxscale/notification.h>
|
||||
#include <maxscale/pcre2.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/spinlock.h>
|
||||
#include <maxscale/utils.h>
|
||||
|
||||
typedef struct duplicate_context
|
||||
{
|
||||
HASHTABLE *hash;
|
||||
pcre2_code *re;
|
||||
pcre2_match_data *mdata;
|
||||
} DUPLICATE_CONTEXT;
|
||||
|
||||
static bool duplicate_context_init(DUPLICATE_CONTEXT* context);
|
||||
static void duplicate_context_finish(DUPLICATE_CONTEXT* context);
|
||||
|
||||
extern int setipaddress(struct in_addr *, char *);
|
||||
static bool process_config_context(CONFIG_CONTEXT *);
|
||||
static int process_config_update(CONFIG_CONTEXT *);
|
||||
static bool process_config_context(CONFIG_CONTEXT *);
|
||||
static bool process_config_update(CONFIG_CONTEXT *);
|
||||
static void free_config_context(CONFIG_CONTEXT *);
|
||||
static char *config_get_value(CONFIG_PARAMETER *, const char *);
|
||||
static char *config_get_password(CONFIG_PARAMETER *);
|
||||
@ -98,7 +96,7 @@ int config_get_ifaddr(unsigned char *output);
|
||||
static int config_get_release_string(char* release);
|
||||
FEEDBACK_CONF *config_get_feedback_data();
|
||||
void config_add_param(CONFIG_CONTEXT*, char*, char*);
|
||||
bool config_has_duplicate_sections(const char* config);
|
||||
bool config_has_duplicate_sections(const char* config, DUPLICATE_CONTEXT* context);
|
||||
int create_new_service(CONFIG_CONTEXT *obj);
|
||||
int create_new_server(CONFIG_CONTEXT *obj);
|
||||
int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE* monitorhash);
|
||||
@ -106,7 +104,7 @@ int create_new_listener(CONFIG_CONTEXT *obj, bool startnow);
|
||||
int create_new_filter(CONFIG_CONTEXT *obj);
|
||||
int configure_new_service(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj);
|
||||
|
||||
static char *config_file = NULL;
|
||||
static const char *config_file = NULL;
|
||||
static GATEWAY_CONF gateway;
|
||||
static FEEDBACK_CONF feedback;
|
||||
char *version_string = NULL;
|
||||
@ -210,13 +208,68 @@ static char *server_params[] =
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the context object used for tracking duplicate sections.
|
||||
*
|
||||
* @param context The context object to be initialized.
|
||||
*
|
||||
* @return True, if the object could be initialized.
|
||||
*/
|
||||
static bool duplicate_context_init(DUPLICATE_CONTEXT* context)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
const int table_size = 10;
|
||||
HASHTABLE *hash = hashtable_alloc(table_size, hashtable_item_strhash, hashtable_item_strcmp);
|
||||
int errcode;
|
||||
PCRE2_SIZE erroffset;
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR) "^\\s*\\[(.+)\\]\\s*$", PCRE2_ZERO_TERMINATED,
|
||||
0, &errcode, &erroffset, NULL);
|
||||
pcre2_match_data *mdata = NULL;
|
||||
|
||||
if (hash && re && (mdata = pcre2_match_data_create_from_pattern(re, NULL)))
|
||||
{
|
||||
hashtable_memory_fns(hash, hashtable_item_strdup, NULL, hashtable_item_free, NULL);
|
||||
|
||||
context->hash = hash;
|
||||
context->re = re;
|
||||
context->mdata = mdata;
|
||||
rv = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
hashtable_free(hash);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize the context object used for tracking duplicate sections.
|
||||
*
|
||||
* @param context The context object to be initialized.
|
||||
*/
|
||||
static void duplicate_context_finish(DUPLICATE_CONTEXT* context)
|
||||
{
|
||||
pcre2_match_data_free(context->mdata);
|
||||
pcre2_code_free(context->re);
|
||||
hashtable_free(context->hash);
|
||||
|
||||
context->mdata = NULL;
|
||||
context->re = NULL;
|
||||
context->hash = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove extra commas and whitespace from a string. This string is interpreted
|
||||
* as a list of string values separated by commas.
|
||||
* @param strptr String to clean
|
||||
* @return pointer to a new string or NULL if an error occurred
|
||||
*/
|
||||
char* config_clean_string_list(char* str)
|
||||
char* config_clean_string_list(const char* str)
|
||||
{
|
||||
size_t destsize = strlen(str) + 1;
|
||||
char *dest = MXS_MALLOC(destsize);
|
||||
@ -282,7 +335,7 @@ char* config_clean_string_list(char* str)
|
||||
* @return zero on error
|
||||
*/
|
||||
static int
|
||||
handler(void *userdata, const char *section, const char *name, const char *value)
|
||||
ini_handler(void *userdata, const char *section, const char *name, const char *value)
|
||||
{
|
||||
CONFIG_CONTEXT *cntxt = (CONFIG_CONTEXT *)userdata;
|
||||
CONFIG_CONTEXT *ptr = cntxt;
|
||||
@ -367,25 +420,211 @@ handler(void *userdata, const char *section, const char *name, const char *value
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the configuration file for the MaxScale
|
||||
* Load single configuration file.
|
||||
*
|
||||
* @param file The file to load.
|
||||
* @param dcontext The context object used when tracking duplicate sections.
|
||||
* @param ccontext The context object used when parsing.
|
||||
*
|
||||
* @return True if the file could be parsed, false otherwise.
|
||||
*/
|
||||
static bool config_load_single_file(const char* file,
|
||||
DUPLICATE_CONTEXT* dcontext,
|
||||
CONFIG_CONTEXT* ccontext)
|
||||
{
|
||||
int rval = -1;
|
||||
|
||||
// With multiple configuration files being loaded, we need to log the file
|
||||
// currently being loaded so that the context is clear in case of errors.
|
||||
MXS_NOTICE("Loading %s.", file);
|
||||
|
||||
if (!config_has_duplicate_sections(file, dcontext))
|
||||
{
|
||||
if ((rval = ini_parse(file, ini_handler, ccontext)) != 0)
|
||||
{
|
||||
char errorbuffer[1024 + 1];
|
||||
|
||||
if (rval > 0)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Failed to parse configuration file %s. Error on line %d.", file, rval);
|
||||
}
|
||||
else if (rval == -1)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Failed to parse configuration file %s. Could not open file.", file);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Failed to parse configuration file %s. Memory allocation failed.", file);
|
||||
}
|
||||
|
||||
MXS_ERROR("%s", errorbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
return rval == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current parsing contexts must be managed explicitly since the ftw callback
|
||||
* can not have user data.
|
||||
*/
|
||||
static CONFIG_CONTEXT *current_ccontext;
|
||||
static DUPLICATE_CONTEXT *current_dcontext;
|
||||
|
||||
/**
|
||||
* The nftw callback.
|
||||
*
|
||||
* @see man ftw
|
||||
*/
|
||||
int config_cb(const char* fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
if (typeflag == FTW_F) // We are only interested in files,
|
||||
{
|
||||
const char* filename = fpath + ftwbuf->base;
|
||||
const char* dot = strrchr(filename, '.');
|
||||
|
||||
if (dot) // that must have a suffix,
|
||||
{
|
||||
const char* suffix = dot + 1;
|
||||
|
||||
if (strcmp(suffix, "cnf") == 0) // that is ".cnf".
|
||||
{
|
||||
ss_dassert(current_dcontext);
|
||||
ss_dassert(current_ccontext);
|
||||
|
||||
if (!config_load_single_file(fpath, current_dcontext, current_ccontext))
|
||||
{
|
||||
rval = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all configuration files in a directory hierarchy.
|
||||
*
|
||||
* Only files with the suffix ".cnf" are considered to be configuration files.
|
||||
*
|
||||
* @param dir The directory.
|
||||
* @param dcontext The duplicate section context.
|
||||
* @param ccontext The configuration context.
|
||||
*
|
||||
* @return True, if all configuration files in the directory hierarchy could be loaded,
|
||||
* otherwise false.
|
||||
*/
|
||||
static bool config_load_dir(const char *dir, DUPLICATE_CONTEXT *dcontext, CONFIG_CONTEXT *ccontext)
|
||||
{
|
||||
// Since there is no way to pass userdata to the callback, we need to store
|
||||
// the current context into a static variable. Consequently, we need lock.
|
||||
// Should not matter since config_load() is called once at startup.
|
||||
static SPINLOCK lock = SPINLOCK_INIT;
|
||||
|
||||
int nopenfd = 5; // Maximum concurrently opened directory descriptors
|
||||
|
||||
spinlock_acquire(&lock);
|
||||
current_dcontext = dcontext;
|
||||
current_ccontext = ccontext;
|
||||
int rv = nftw(dir, config_cb, nopenfd, FTW_PHYS);
|
||||
current_ccontext = NULL;
|
||||
current_dcontext = NULL;
|
||||
spinlock_release(&lock);
|
||||
|
||||
return rv == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the specified configuration file for MaxScale
|
||||
*
|
||||
* This function will parse the configuration file, check for duplicate sections,
|
||||
* validate the module parameters and finally turn it into a set of objects.
|
||||
*
|
||||
* @param file The filename of the configuration file
|
||||
* @param filename The filename of the configuration file
|
||||
* @param process_config The function using which the successfully loaded
|
||||
* configuration should be processed.
|
||||
*
|
||||
* @return True on success, false on fatal error
|
||||
*/
|
||||
static bool
|
||||
config_load_and_process(const char* filename, bool (*process_config)(CONFIG_CONTEXT*))
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
DUPLICATE_CONTEXT dcontext;
|
||||
|
||||
if (duplicate_context_init(&dcontext))
|
||||
{
|
||||
CONFIG_CONTEXT ccontext = {.object = ""};
|
||||
|
||||
if (config_load_single_file(filename, &dcontext, &ccontext))
|
||||
{
|
||||
const char DIR_SUFFIX[] = ".d";
|
||||
|
||||
char dir[strlen(filename) + sizeof(DIR_SUFFIX)];
|
||||
strcpy(dir, filename);
|
||||
strcat(dir, DIR_SUFFIX);
|
||||
|
||||
rval = true;
|
||||
|
||||
struct stat st;
|
||||
if (stat(dir, &st) == -1)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
MXS_NOTICE("%s does not exist, not reading.", dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_WARNING("Could not access %s, not reading: %s",
|
||||
dir, strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (S_ISDIR(st.st_mode))
|
||||
{
|
||||
rval = config_load_dir(dir, &dcontext, &ccontext);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("%s exists, but it is not a directory. Ignoring.", dir);
|
||||
}
|
||||
}
|
||||
|
||||
if (rval)
|
||||
{
|
||||
if (check_config_objects(ccontext.next) && process_config(ccontext.next))
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_config_context(ccontext.next);
|
||||
|
||||
duplicate_context_finish(&dcontext);
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load the configuration file for the MaxScale
|
||||
*
|
||||
* @param filename The filename of the configuration file
|
||||
* @return True on success, false on fatal error
|
||||
*/
|
||||
bool
|
||||
config_load(char *file)
|
||||
config_load(const char *filename)
|
||||
{
|
||||
CONFIG_CONTEXT config = {.object = ""};
|
||||
int ini_rval;
|
||||
bool rval = false;
|
||||
|
||||
if (config_has_duplicate_sections(file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ss_dassert(!config_file);
|
||||
|
||||
/* Temporary - should use configuration values and test return value (bool) */
|
||||
dcb_pre_alloc(1000);
|
||||
@ -394,80 +633,44 @@ config_load(char *file)
|
||||
global_defaults();
|
||||
feedback_defaults();
|
||||
|
||||
if ((ini_rval = ini_parse(file, handler, &config)) != 0)
|
||||
bool rval = config_load_and_process(filename, process_config_context);
|
||||
|
||||
if (rval)
|
||||
{
|
||||
char errorbuffer[1024 + 1];
|
||||
|
||||
if (ini_rval > 0)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Error: Failed to parse configuration file. Error on line %d.", ini_rval);
|
||||
}
|
||||
else if (ini_rval == -1)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Error: Failed to parse configuration file. Failed to open file.");
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Error: Failed to parse configuration file. Memory allocation failed.");
|
||||
}
|
||||
|
||||
MXS_ERROR("%s", errorbuffer);
|
||||
return 0;
|
||||
config_file = filename;
|
||||
}
|
||||
|
||||
config_file = file;
|
||||
|
||||
if (check_config_objects(config.next) && process_config_context(config.next))
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
|
||||
free_config_context(config.next);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the configuration file for the MaxScale
|
||||
*
|
||||
* @return A zero return indicates a fatal error reading the configuration
|
||||
* @return True on success, false on fatal error.
|
||||
*/
|
||||
int
|
||||
bool
|
||||
config_reload()
|
||||
{
|
||||
CONFIG_CONTEXT config;
|
||||
int rval;
|
||||
bool rval = false;
|
||||
|
||||
if (!config_file)
|
||||
if (config_file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (gateway.version_string)
|
||||
{
|
||||
MXS_FREE(gateway.version_string);
|
||||
}
|
||||
|
||||
if (config_has_duplicate_sections(config_file))
|
||||
global_defaults();
|
||||
feedback_defaults();
|
||||
|
||||
rval = config_load_and_process(config_file, process_config_update);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
MXS_ERROR("config_reload() called without the configuration having "
|
||||
"been loaded first.");
|
||||
}
|
||||
|
||||
if (gateway.version_string)
|
||||
{
|
||||
MXS_FREE(gateway.version_string);
|
||||
}
|
||||
|
||||
global_defaults();
|
||||
|
||||
config.object = "";
|
||||
config.next = NULL;
|
||||
|
||||
if (ini_parse(config_file, handler, &config) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
rval = process_config_update(config.next);
|
||||
free_config_context(config.next);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -666,16 +869,16 @@ CONFIG_PARAMETER* config_get_param(
|
||||
}
|
||||
|
||||
config_param_type_t config_get_paramtype(
|
||||
CONFIG_PARAMETER* param)
|
||||
const CONFIG_PARAMETER* param)
|
||||
{
|
||||
return param->qfd_param_type;
|
||||
}
|
||||
|
||||
bool config_get_valint(
|
||||
int* val,
|
||||
CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
config_param_type_t ptype)
|
||||
int* val,
|
||||
const CONFIG_PARAMETER* param,
|
||||
const char* name, /*< if NULL examine current param only */
|
||||
config_param_type_t ptype)
|
||||
{
|
||||
bool succp = false;;
|
||||
|
||||
@ -709,10 +912,10 @@ return_succp:
|
||||
|
||||
|
||||
bool config_get_valbool(
|
||||
bool* val,
|
||||
CONFIG_PARAMETER* param,
|
||||
const char* name,
|
||||
config_param_type_t ptype)
|
||||
bool* val,
|
||||
const CONFIG_PARAMETER* param,
|
||||
const char* name,
|
||||
config_param_type_t ptype)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
@ -743,10 +946,10 @@ return_succp:
|
||||
|
||||
|
||||
bool config_get_valtarget(
|
||||
target_t* val,
|
||||
CONFIG_PARAMETER* param,
|
||||
const char* name,
|
||||
config_param_type_t ptype)
|
||||
target_t* val,
|
||||
const CONFIG_PARAMETER* param,
|
||||
const char* name,
|
||||
config_param_type_t ptype)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
@ -775,8 +978,7 @@ return_succp:
|
||||
return succp;
|
||||
}
|
||||
|
||||
CONFIG_PARAMETER* config_clone_param(
|
||||
CONFIG_PARAMETER* param)
|
||||
CONFIG_PARAMETER* config_clone_param(const CONFIG_PARAMETER* param)
|
||||
{
|
||||
CONFIG_PARAMETER* p2;
|
||||
|
||||
@ -787,12 +989,12 @@ CONFIG_PARAMETER* config_clone_param(
|
||||
goto return_p2;
|
||||
}
|
||||
memcpy(p2, param, sizeof(CONFIG_PARAMETER));
|
||||
p2->name = strndup(param->name, MAX_PARAM_LEN);
|
||||
p2->value = strndup(param->value, MAX_PARAM_LEN);
|
||||
p2->name = MXS_STRNDUP_A(param->name, MAX_PARAM_LEN);
|
||||
p2->value = MXS_STRNDUP_A(param->value, MAX_PARAM_LEN);
|
||||
|
||||
if (param->qfd_param_type == STRING_TYPE)
|
||||
{
|
||||
p2->qfd.valstr = strndup(param->qfd.valstr, MAX_PARAM_LEN);
|
||||
p2->qfd.valstr = MXS_STRNDUP_A(param->qfd.valstr, MAX_PARAM_LEN);
|
||||
}
|
||||
|
||||
return_p2:
|
||||
@ -1374,7 +1576,7 @@ feedback_defaults()
|
||||
*
|
||||
* @param context The configuration data
|
||||
*/
|
||||
static int
|
||||
static bool
|
||||
process_config_update(CONFIG_CONTEXT *context)
|
||||
{
|
||||
CONFIG_CONTEXT *obj;
|
||||
@ -1616,7 +1818,7 @@ process_config_update(CONFIG_CONTEXT *context)
|
||||
obj = obj->next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1694,7 +1896,7 @@ bool config_set_qualified_param(CONFIG_PARAMETER* param,
|
||||
switch (type)
|
||||
{
|
||||
case STRING_TYPE:
|
||||
param->qfd.valstr = strndup((const char *)val, MAX_PARAM_LEN);
|
||||
param->qfd.valstr = MXS_STRNDUP_A((const char *)val, MAX_PARAM_LEN);
|
||||
succp = true;
|
||||
break;
|
||||
|
||||
@ -1761,7 +1963,7 @@ config_truth_value(char *str)
|
||||
* @return String converted to a floating point percentage
|
||||
*/
|
||||
double
|
||||
config_percentage_value(char *str)
|
||||
config_percentage_value(const char *str)
|
||||
{
|
||||
double value = 0;
|
||||
|
||||
@ -2094,46 +2296,43 @@ GATEWAY_CONF* config_get_global_options()
|
||||
|
||||
/**
|
||||
* Check if sections are defined multiple times in the configuration file.
|
||||
* @param config Path to the configuration file
|
||||
*
|
||||
* @param filename Path to the configuration file
|
||||
* @param context The context object used for tracking the duplication
|
||||
* section information.
|
||||
*
|
||||
* @return True if duplicate sections were found or an error occurred
|
||||
*/
|
||||
bool config_has_duplicate_sections(const char* config)
|
||||
bool config_has_duplicate_sections(const char* filename, DUPLICATE_CONTEXT* context)
|
||||
{
|
||||
bool rval = false;
|
||||
const int table_size = 10;
|
||||
int errcode;
|
||||
PCRE2_SIZE erroffset;
|
||||
HASHTABLE *hash = hashtable_alloc(table_size, hashtable_item_strhash, hashtable_item_strcmp);
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR) "^\\s*\\[(.+)\\]\\s*$", PCRE2_ZERO_TERMINATED,
|
||||
0, &errcode, &erroffset, NULL);
|
||||
pcre2_match_data *mdata = NULL;
|
||||
|
||||
int size = 1024;
|
||||
char *buffer = MXS_MALLOC(size * sizeof(char));
|
||||
|
||||
if (buffer && hash && re && (mdata = pcre2_match_data_create_from_pattern(re, NULL)))
|
||||
if (buffer)
|
||||
{
|
||||
hashtable_memory_fns(hash, hashtable_item_strdup, NULL, hashtable_item_free, NULL);
|
||||
FILE* file = fopen(config, "r");
|
||||
FILE* file = fopen(filename, "r");
|
||||
|
||||
if (file)
|
||||
{
|
||||
while (maxscale_getline(&buffer, &size, file) > 0)
|
||||
{
|
||||
if (pcre2_match(re, (PCRE2_SPTR) buffer,
|
||||
if (pcre2_match(context->re, (PCRE2_SPTR) buffer,
|
||||
PCRE2_ZERO_TERMINATED, 0, 0,
|
||||
mdata, NULL) > 0)
|
||||
context->mdata, NULL) > 0)
|
||||
{
|
||||
/**
|
||||
* Neither of the PCRE2 calls will fail since we know the pattern
|
||||
* beforehand and we allocate enough memory from the stack
|
||||
*/
|
||||
PCRE2_SIZE len;
|
||||
pcre2_substring_length_bynumber(mdata, 1, &len);
|
||||
pcre2_substring_length_bynumber(context->mdata, 1, &len);
|
||||
len += 1; /** one for the null terminator */
|
||||
PCRE2_UCHAR section[len];
|
||||
pcre2_substring_copy_bynumber(mdata, 1, section, &len);
|
||||
pcre2_substring_copy_bynumber(context->mdata, 1, section, &len);
|
||||
|
||||
if (hashtable_add(hash, section, "") == 0)
|
||||
if (hashtable_add(context->hash, section, "") == 0)
|
||||
{
|
||||
MXS_ERROR("Duplicate section found: %s", section);
|
||||
rval = true;
|
||||
@ -2145,7 +2344,7 @@ bool config_has_duplicate_sections(const char* config)
|
||||
else
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_ERROR("Failed to open file '%s': %s", config,
|
||||
MXS_ERROR("Failed to open file '%s': %s", filename,
|
||||
strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
rval = true;
|
||||
}
|
||||
@ -2157,9 +2356,6 @@ bool config_has_duplicate_sections(const char* config)
|
||||
rval = true;
|
||||
}
|
||||
|
||||
hashtable_free(hash);
|
||||
pcre2_code_free(re);
|
||||
pcre2_match_data_free(mdata);
|
||||
MXS_FREE(buffer);
|
||||
return rval;
|
||||
}
|
||||
|
@ -2919,20 +2919,16 @@ dcb_create_SSL(DCB* dcb, SSL_LISTENER *ssl)
|
||||
*/
|
||||
int dcb_accept_SSL(DCB* dcb)
|
||||
{
|
||||
int ssl_rval;
|
||||
char *remote;
|
||||
char *user;
|
||||
|
||||
if ((NULL == dcb->listener || NULL == dcb->listener->ssl) ||
|
||||
(NULL == dcb->ssl && dcb_create_SSL(dcb, dcb->listener->ssl) != 0))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
remote = dcb->remote ? dcb->remote : "";
|
||||
user = dcb->user ? dcb->user : "";
|
||||
ss_debug(char *remote = dcb->remote ? dcb->remote : "");
|
||||
ss_debug(char *user = dcb->user ? dcb->user : "");
|
||||
|
||||
ssl_rval = SSL_accept(dcb->ssl);
|
||||
int ssl_rval = SSL_accept(dcb->ssl);
|
||||
|
||||
switch (SSL_get_error(dcb->ssl, ssl_rval))
|
||||
{
|
||||
|
@ -34,59 +34,45 @@
|
||||
* 19/01/16 Markus Makela Set cwd to log directory
|
||||
* @endverbatim
|
||||
*/
|
||||
#define _XOPEN_SOURCE 700
|
||||
#define OPENSSL_THREAD_DEFINES
|
||||
#include <my_config.h>
|
||||
|
||||
#include <openssl/opensslconf.h>
|
||||
#if defined(OPENSSL_THREADS)
|
||||
#define HAVE_OPENSSL_THREADS 1
|
||||
#else
|
||||
#define HAVE_OPENSSL_THREADS 0
|
||||
#endif
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <execinfo.h>
|
||||
#include <ftw.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <getopt.h>
|
||||
#include <pwd.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/modules.h>
|
||||
#include <maxscale/config.h>
|
||||
#include <maxscale/poll.h>
|
||||
#include <maxscale/housekeeper.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/thread.h>
|
||||
#include <maxscale/memlog.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <mysql.h>
|
||||
#include <maxscale/monitor.h>
|
||||
#include <maxscale/version.h>
|
||||
#include <maxscale/maxscale.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <maxscale/utils.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
#include <ini.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/file.h>
|
||||
#include <maxscale/statistics.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <openssl/opensslconf.h>
|
||||
#include <mysql.h>
|
||||
#include <ini.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/config.h>
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/gwdirs.h>
|
||||
#include <maxscale/housekeeper.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/maxscale.h>
|
||||
#include <maxscale/memlog.h>
|
||||
#include <maxscale/modules.h>
|
||||
#include <maxscale/monitor.h>
|
||||
#include <maxscale/poll.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/statistics.h>
|
||||
#include <maxscale/thread.h>
|
||||
#include <maxscale/utils.h>
|
||||
#include <maxscale/version.h>
|
||||
|
||||
#define STRING_BUFFER_SIZE 1024
|
||||
#define PIDFD_CLOSED -1
|
||||
@ -96,6 +82,12 @@
|
||||
# define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(OPENSSL_THREADS)
|
||||
#define HAVE_OPENSSL_THREADS 1
|
||||
#else
|
||||
#define HAVE_OPENSSL_THREADS 0
|
||||
#endif
|
||||
|
||||
extern char *program_invocation_name;
|
||||
extern char *program_invocation_short_name;
|
||||
|
||||
@ -192,6 +184,7 @@ static bool change_cwd();
|
||||
void shutdown_server();
|
||||
static void log_exit_status();
|
||||
static bool daemonize();
|
||||
static bool sniff_configuration(const char* filepath);
|
||||
|
||||
/** SSL multi-threading functions and structures */
|
||||
|
||||
@ -1704,32 +1697,12 @@ int main(int argc, char **argv)
|
||||
goto return_main;
|
||||
}
|
||||
|
||||
if ((ini_rval = ini_parse(cnf_file_path, cnf_preparser, NULL)) != 0)
|
||||
if (!sniff_configuration(cnf_file_path))
|
||||
{
|
||||
char errorbuffer[STRING_BUFFER_SIZE];
|
||||
|
||||
if (ini_rval > 0)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Error: Failed to pre-parse configuration file. Error on line %d.", ini_rval);
|
||||
}
|
||||
else if (ini_rval == -1)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Error: Failed to pre-parse configuration file. Failed to open file.");
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer),
|
||||
"Error: Failed to pre-parse configuration file. Memory allocation failed.");
|
||||
}
|
||||
|
||||
print_log_n_stderr(true, true, errorbuffer, errorbuffer, 0);
|
||||
rc = MAXSCALE_BADCONFIG;
|
||||
goto return_main;
|
||||
}
|
||||
|
||||
|
||||
/** Use the cache dir for the mysql folder of the embedded library */
|
||||
snprintf(mysql_home, PATH_MAX, "%s/mysql", get_cachedir());
|
||||
mysql_home[PATH_MAX] = '\0';
|
||||
@ -1750,16 +1723,6 @@ int main(int argc, char **argv)
|
||||
goto return_main;
|
||||
}
|
||||
|
||||
if (!(*syslog_enabled))
|
||||
{
|
||||
printf("Syslog logging is disabled.\n");
|
||||
}
|
||||
|
||||
if (!(*maxlog_enabled))
|
||||
{
|
||||
printf("MaxScale logging is disabled.\n");
|
||||
}
|
||||
|
||||
mxs_log_set_syslog_enabled(*syslog_enabled);
|
||||
mxs_log_set_maxlog_enabled(*maxlog_enabled);
|
||||
|
||||
@ -1826,6 +1789,11 @@ int main(int argc, char **argv)
|
||||
get_cachedir());
|
||||
}
|
||||
|
||||
if (!(*syslog_enabled) && !(*maxlog_enabled))
|
||||
{
|
||||
fprintf(stderr, "warning: Both MaxScale and Syslog logging disabled.\n");
|
||||
}
|
||||
|
||||
MXS_NOTICE("Configuration file: %s", cnf_file_path);
|
||||
MXS_NOTICE("Log directory: %s", get_logdir());
|
||||
MXS_NOTICE("Data directory: %s", get_datadir());
|
||||
@ -2700,3 +2668,46 @@ static bool daemonize(void)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sniffs the configuration file, primarily for various directory paths,
|
||||
* so that certain settings take effect immediately.
|
||||
*
|
||||
* @param filepath The path of the configuration file.
|
||||
*
|
||||
* @return True, if the sniffing succeeded, false otherwise.
|
||||
*/
|
||||
static bool sniff_configuration(const char* filepath)
|
||||
{
|
||||
int rv = ini_parse(filepath, cnf_preparser, NULL);
|
||||
|
||||
if (rv != 0)
|
||||
{
|
||||
const char FORMAT_SYNTAX[] =
|
||||
"Error: Failed to pre-parse configuration file %s. Error on line %d.";
|
||||
const char FORMAT_OPEN[] =
|
||||
"Error: Failed to pre-parse configuration file %s. Failed to open file.";
|
||||
const char FORMAT_MALLOC[] =
|
||||
"Error: Failed to pre-parse configuration file %s. Memory allocation failed.";
|
||||
|
||||
// We just use the largest one.
|
||||
char errorbuffer[sizeof(FORMAT_MALLOC) + strlen(filepath) + UINTLEN(abs(rv))];
|
||||
|
||||
if (rv > 0)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer), FORMAT_SYNTAX, filepath, rv);
|
||||
}
|
||||
else if (rv == -1)
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer), FORMAT_OPEN, filepath);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errorbuffer, sizeof(errorbuffer), FORMAT_MALLOC, filepath);
|
||||
}
|
||||
|
||||
print_log_n_stderr(true, true, errorbuffer, errorbuffer, 0);
|
||||
}
|
||||
|
||||
return rv == 0;
|
||||
}
|
||||
|
@ -71,7 +71,8 @@ listener_alloc(struct service* service, char* name, char *protocol, char *addres
|
||||
{
|
||||
authenticator = MXS_STRDUP(authenticator);
|
||||
}
|
||||
else if ((authenticator = get_default_authenticator(protocol)) == NULL)
|
||||
else if ((authenticator = (char*)get_default_authenticator(protocol)) == NULL ||
|
||||
(authenticator = MXS_STRDUP(authenticator)) == NULL)
|
||||
{
|
||||
MXS_ERROR("No authenticator defined for listener '%s' and could not get "
|
||||
"default authenticator for protocol '%s'.", name, protocol);
|
||||
@ -83,6 +84,9 @@ listener_alloc(struct service* service, char* name, char *protocol, char *addres
|
||||
{
|
||||
MXS_ERROR("Failed to initialize authenticator module '%s' for "
|
||||
"listener '%s'.", authenticator, name);
|
||||
MXS_FREE(address);
|
||||
MXS_FREE(authenticator);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
protocol = MXS_STRDUP(protocol);
|
||||
|
@ -2913,131 +2913,128 @@ int mxs_log_message(int priority,
|
||||
|
||||
if ((priority & ~LOG_PRIMASK) == 0) // Check that the priority is ok,
|
||||
{
|
||||
if (MXS_LOG_PRIORITY_IS_ENABLED(priority))
|
||||
{
|
||||
message_suppression_t status = MESSAGE_NOT_SUPPRESSED;
|
||||
message_suppression_t status = MESSAGE_NOT_SUPPRESSED;
|
||||
|
||||
// We only throttle errors and warnings. Info and debug messages
|
||||
// are never on during normal operation, so if they are enabled,
|
||||
// we are presumably debugging something. Notice messages are
|
||||
// assumed to be logged for a reason and always in a context where
|
||||
// flooding cannot be caused.
|
||||
if ((priority == LOG_ERR) || (priority == LOG_WARNING))
|
||||
// We only throttle errors and warnings. Info and debug messages
|
||||
// are never on during normal operation, so if they are enabled,
|
||||
// we are presumably debugging something. Notice messages are
|
||||
// assumed to be logged for a reason and always in a context where
|
||||
// flooding cannot be caused.
|
||||
if ((priority == LOG_ERR) || (priority == LOG_WARNING))
|
||||
{
|
||||
status = message_status(file, line);
|
||||
}
|
||||
|
||||
if (status != MESSAGE_STILL_SUPPRESSED)
|
||||
{
|
||||
va_list valist;
|
||||
|
||||
int modname_len = modname ? strlen(modname) + 3 : 0; // +3 due to "[...] "
|
||||
|
||||
static const char SUPPRESSION[] =
|
||||
" (subsequent similar messages suppressed for %lu milliseconds)";
|
||||
int suppression_len = 0;
|
||||
size_t suppress_ms = log_config.throttling.suppress_ms;
|
||||
|
||||
if (status == MESSAGE_SUPPRESSED)
|
||||
{
|
||||
status = message_status(file, line);
|
||||
suppression_len += sizeof(SUPPRESSION) - 1; // Remove trailing NULL
|
||||
suppression_len -= 3; // Remove the %lu
|
||||
suppression_len += UINTLEN(suppress_ms);
|
||||
}
|
||||
|
||||
if (status != MESSAGE_STILL_SUPPRESSED)
|
||||
/**
|
||||
* Find out the length of log string (to be formatted str).
|
||||
*/
|
||||
va_start(valist, format);
|
||||
int message_len = vsnprintf(NULL, 0, format, valist);
|
||||
va_end(valist);
|
||||
|
||||
if (message_len >= 0)
|
||||
{
|
||||
va_list valist;
|
||||
log_prefix_t prefix = priority_to_prefix(priority);
|
||||
|
||||
int modname_len = modname ? strlen(modname) + 3 : 0; // +3 due to "[...] "
|
||||
static const char FORMAT_FUNCTION[] = "(%s): ";
|
||||
|
||||
static const char SUPPRESSION[] =
|
||||
" (subsequent similar messages suppressed for %lu milliseconds)";
|
||||
int suppression_len = 0;
|
||||
size_t suppress_ms = log_config.throttling.suppress_ms;
|
||||
// Other thread might change log_config.augmentation.
|
||||
int augmentation = log_config.augmentation;
|
||||
int augmentation_len = 0;
|
||||
|
||||
if (status == MESSAGE_SUPPRESSED)
|
||||
switch (augmentation)
|
||||
{
|
||||
suppression_len += sizeof(SUPPRESSION) - 1; // Remove trailing NULL
|
||||
suppression_len -= 3; // Remove the %lu
|
||||
suppression_len += UINTLEN(suppress_ms);
|
||||
case MXS_LOG_AUGMENT_WITH_FUNCTION:
|
||||
augmentation_len = sizeof(FORMAT_FUNCTION) - 1; // Remove trailing 0
|
||||
augmentation_len -= 2; // Remove the %s
|
||||
augmentation_len += strlen(function);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out the length of log string (to be formatted str).
|
||||
*/
|
||||
va_start(valist, format);
|
||||
int message_len = vsnprintf(NULL, 0, format, valist);
|
||||
va_end(valist);
|
||||
int buffer_len = 0;
|
||||
buffer_len += prefix.len;
|
||||
buffer_len += modname_len;
|
||||
buffer_len += augmentation_len;
|
||||
buffer_len += message_len;
|
||||
buffer_len += suppression_len;
|
||||
buffer_len += 1; // Trailing NULL
|
||||
|
||||
if (message_len >= 0)
|
||||
if (buffer_len > MAX_LOGSTRLEN)
|
||||
{
|
||||
log_prefix_t prefix = priority_to_prefix(priority);
|
||||
message_len -= (buffer_len - MAX_LOGSTRLEN);
|
||||
buffer_len = MAX_LOGSTRLEN;
|
||||
|
||||
static const char FORMAT_FUNCTION[] = "(%s): ";
|
||||
ss_dassert(prefix.len + modname_len +
|
||||
augmentation_len + message_len + suppression_len + 1 == buffer_len);
|
||||
}
|
||||
|
||||
// Other thread might change log_config.augmentation.
|
||||
int augmentation = log_config.augmentation;
|
||||
int augmentation_len = 0;
|
||||
char buffer[buffer_len];
|
||||
|
||||
char *prefix_text = buffer;
|
||||
char *modname_text = prefix_text + prefix.len;
|
||||
char *augmentation_text = modname_text + modname_len;
|
||||
char *message_text = augmentation_text + augmentation_len;
|
||||
char *suppression_text = message_text + message_len;
|
||||
|
||||
strcpy(prefix_text, prefix.text);
|
||||
|
||||
if (modname_len)
|
||||
{
|
||||
strcpy(modname_text, "[");
|
||||
strcat(modname_text, modname);
|
||||
strcat(modname_text, "] ");
|
||||
}
|
||||
|
||||
if (augmentation_len)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
switch (augmentation)
|
||||
{
|
||||
case MXS_LOG_AUGMENT_WITH_FUNCTION:
|
||||
augmentation_len = sizeof(FORMAT_FUNCTION) - 1; // Remove trailing 0
|
||||
augmentation_len -= 2; // Remove the %s
|
||||
augmentation_len += strlen(function);
|
||||
len = sprintf(augmentation_text, FORMAT_FUNCTION, function);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
assert(!true);
|
||||
}
|
||||
|
||||
int buffer_len = 0;
|
||||
buffer_len += prefix.len;
|
||||
buffer_len += modname_len;
|
||||
buffer_len += augmentation_len;
|
||||
buffer_len += message_len;
|
||||
buffer_len += suppression_len;
|
||||
buffer_len += 1; // Trailing NULL
|
||||
|
||||
if (buffer_len > MAX_LOGSTRLEN)
|
||||
{
|
||||
message_len -= (buffer_len - MAX_LOGSTRLEN);
|
||||
buffer_len = MAX_LOGSTRLEN;
|
||||
|
||||
ss_dassert(prefix.len + modname_len +
|
||||
augmentation_len + message_len + suppression_len + 1 == buffer_len);
|
||||
}
|
||||
|
||||
char buffer[buffer_len];
|
||||
|
||||
char *prefix_text = buffer;
|
||||
char *modname_text = prefix_text + prefix.len;
|
||||
char *augmentation_text = modname_text + modname_len;
|
||||
char *message_text = augmentation_text + augmentation_len;
|
||||
char *suppression_text = message_text + message_len;
|
||||
|
||||
strcpy(prefix_text, prefix.text);
|
||||
|
||||
if (modname_len)
|
||||
{
|
||||
strcpy(modname_text, "[");
|
||||
strcat(modname_text, modname);
|
||||
strcat(modname_text, "] ");
|
||||
}
|
||||
|
||||
if (augmentation_len)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
switch (augmentation)
|
||||
{
|
||||
case MXS_LOG_AUGMENT_WITH_FUNCTION:
|
||||
len = sprintf(augmentation_text, FORMAT_FUNCTION, function);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(!true);
|
||||
}
|
||||
|
||||
assert(len == augmentation_len);
|
||||
}
|
||||
|
||||
va_start(valist, format);
|
||||
vsnprintf(message_text, message_len + 1, format, valist);
|
||||
va_end(valist);
|
||||
|
||||
if (suppression_len)
|
||||
{
|
||||
sprintf(suppression_text, SUPPRESSION, suppress_ms);
|
||||
}
|
||||
|
||||
enum log_flush flush = priority_to_flush(priority);
|
||||
|
||||
err = log_write(priority, file, line, function, prefix.len, buffer_len, buffer, flush);
|
||||
assert(len == augmentation_len);
|
||||
}
|
||||
|
||||
va_start(valist, format);
|
||||
vsnprintf(message_text, message_len + 1, format, valist);
|
||||
va_end(valist);
|
||||
|
||||
if (suppression_len)
|
||||
{
|
||||
sprintf(suppression_text, SUPPRESSION, suppress_ms);
|
||||
}
|
||||
|
||||
enum log_flush flush = priority_to_flush(priority);
|
||||
|
||||
err = log_write(priority, file, line, function, prefix.len, buffer_len, buffer, flush);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <string.h>
|
||||
#include <maxscale/debug.h>
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/dbusers.h>
|
||||
#include <strings.h>
|
||||
|
||||
/**
|
||||
|
@ -1044,9 +1044,8 @@ process_pollq(int thread_id)
|
||||
|
||||
if (ev & EPOLLHUP)
|
||||
{
|
||||
int eno = 0;
|
||||
eno = gw_getsockerrno(dcb->fd);
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
ss_debug(int eno = gw_getsockerrno(dcb->fd));
|
||||
ss_debug(char errbuf[MXS_STRERROR_BUFLEN]);
|
||||
MXS_DEBUG("%lu [poll_waitevents] "
|
||||
"EPOLLHUP on dcb %p, fd %d. "
|
||||
"Errno %d, %s.",
|
||||
@ -1080,9 +1079,8 @@ process_pollq(int thread_id)
|
||||
#ifdef EPOLLRDHUP
|
||||
if (ev & EPOLLRDHUP)
|
||||
{
|
||||
int eno = 0;
|
||||
eno = gw_getsockerrno(dcb->fd);
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
ss_debug(int eno = gw_getsockerrno(dcb->fd));
|
||||
ss_debug(char errbuf[MXS_STRERROR_BUFLEN]);
|
||||
MXS_DEBUG("%lu [poll_waitevents] "
|
||||
"EPOLLRDHUP on dcb %p, fd %d. "
|
||||
"Errno %d, %s.",
|
||||
|
@ -101,20 +101,6 @@ void qc_thread_end(void)
|
||||
return classifier->qc_thread_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the query in the provided buffer and returns a value specifying
|
||||
* to what extent the query could be parsed.
|
||||
*
|
||||
* There is no need to call this function explicitly before calling any of
|
||||
* the other functions; e.g. qc_get_type. When some particular property of
|
||||
* a query is asked for, the query will be parsed if it has not been parsed
|
||||
* yet. Also, if the query in the provided buffer has been parsed already
|
||||
* then this function will only return the result of that parsing; the query
|
||||
* will not be parsed again.
|
||||
*
|
||||
* @param query A GWBUF containing an SQL statement.
|
||||
* @result To what extent the query could be parsed.
|
||||
*/
|
||||
qc_parse_result_t qc_parse(GWBUF* query)
|
||||
{
|
||||
QC_TRACE();
|
||||
@ -123,15 +109,6 @@ qc_parse_result_t qc_parse(GWBUF* query)
|
||||
return classifier->qc_parse(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bitmask specifying the type(s) of the query.
|
||||
* The result should be tested against specific qc_query_type_t values
|
||||
* using the bitwise & operator, never using the == operator.
|
||||
*
|
||||
* @param query A buffer containing a query.
|
||||
*
|
||||
* @return A bitmask of type bits.
|
||||
*/
|
||||
uint32_t qc_get_type(GWBUF* query)
|
||||
{
|
||||
QC_TRACE();
|
||||
@ -212,56 +189,6 @@ bool qc_query_has_clause(GWBUF* query)
|
||||
return classifier->qc_query_has_clause(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a string of query type value.
|
||||
* Caller must free the memory of the resulting string.
|
||||
*
|
||||
* @param qtype Query type value, combination of values listed in
|
||||
* query_classifier.h
|
||||
*
|
||||
* @return string representing the query type value
|
||||
*/
|
||||
char* qc_get_qtype_str(qc_query_type_t qtype)
|
||||
{
|
||||
QC_TRACE();
|
||||
int t1 = (int) qtype;
|
||||
int t2 = 1;
|
||||
qc_query_type_t t = QUERY_TYPE_UNKNOWN;
|
||||
char* qtype_str = NULL;
|
||||
|
||||
/**
|
||||
* Test values (bits) and clear matching bits from t1 one by one until
|
||||
* t1 is completely cleared.
|
||||
*/
|
||||
while (t1 != 0)
|
||||
{
|
||||
if (t1 & t2)
|
||||
{
|
||||
t = (qc_query_type_t) t2;
|
||||
|
||||
if (qtype_str == NULL)
|
||||
{
|
||||
qtype_str = MXS_STRDUP_A(STRQTYPE(t));
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t len = strlen(STRQTYPE(t));
|
||||
/** reallocate space for delimiter, new string and termination */
|
||||
qtype_str = (char *) MXS_REALLOC(qtype_str, strlen(qtype_str) + 1 + len + 1);
|
||||
MXS_ABORT_IF_NULL(qtype_str);
|
||||
snprintf(qtype_str + strlen(qtype_str), 1 + len + 1, "|%s", STRQTYPE(t));
|
||||
}
|
||||
|
||||
/** Remove found value from t1 */
|
||||
t1 &= ~t2;
|
||||
}
|
||||
|
||||
t2 <<= 1;
|
||||
}
|
||||
|
||||
return qtype_str;
|
||||
}
|
||||
|
||||
char* qc_get_affected_fields(GWBUF* query)
|
||||
{
|
||||
QC_TRACE();
|
||||
@ -278,14 +205,14 @@ char** qc_get_database_names(GWBUF* query, int* sizep)
|
||||
return classifier->qc_get_database_names(query, sizep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of a query operation.
|
||||
*
|
||||
* @param op An operation.
|
||||
* @return The corresponding string.
|
||||
* NOTE: The returned string is statically allocated
|
||||
* and must *not* be freed.
|
||||
*/
|
||||
char* qc_get_prepare_name(GWBUF* query)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
return classifier->qc_get_prepare_name(query);
|
||||
}
|
||||
|
||||
const char* qc_op_to_string(qc_query_op_t op)
|
||||
{
|
||||
switch (op)
|
||||
@ -539,15 +466,6 @@ struct type_name_info type_to_type_name_info(qc_query_type_t type)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the string representation of a query type.
|
||||
*
|
||||
* @param type A specific type (not a bitmask of several).
|
||||
* @return The corresponding string.
|
||||
* NOTE: The returned string is statically allocated
|
||||
* and must *not* be freed.
|
||||
*/
|
||||
const char* qc_type_to_string(qc_query_type_t type)
|
||||
{
|
||||
return type_to_type_name_info(type).name;
|
||||
@ -587,15 +505,7 @@ static const qc_query_type_t QUERY_TYPES[] =
|
||||
static const int N_QUERY_TYPES = sizeof(QUERY_TYPES) / sizeof(QUERY_TYPES[0]);
|
||||
static const int QUERY_TYPE_MAX_LEN = 29; // strlen("QUERY_TYPE_PREPARE_NAMED_STMT");
|
||||
|
||||
/**
|
||||
* Returns the string representation of a bitmask of query types.
|
||||
*
|
||||
* @param type Bitmask of several qc_query_type_t values.
|
||||
* @return The corresponding string.
|
||||
* NOTE: The returned string is dynamically allocated
|
||||
* and *must* be freed by the caller.
|
||||
*/
|
||||
char* qc_types_to_string(uint32_t types)
|
||||
char* qc_typemask_to_string(uint32_t types)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
|
@ -69,7 +69,8 @@ server_alloc(char *servname, char *protocol, unsigned short port, char *authenti
|
||||
{
|
||||
authenticator = MXS_STRDUP(authenticator);
|
||||
}
|
||||
else if ((authenticator = get_default_authenticator(protocol)) == NULL)
|
||||
else if ((authenticator = (char*)get_default_authenticator(protocol)) == NULL ||
|
||||
(authenticator = MXS_STRDUP(authenticator)) == NULL)
|
||||
{
|
||||
MXS_ERROR("No authenticator defined for server at %s:%u and no default "
|
||||
"authenticator for protocol '%s'.", servname, port, protocol);
|
||||
|
@ -53,7 +53,6 @@
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/filter.h>
|
||||
#include <maxscale/dbusers.h>
|
||||
#include <maxscale/poll.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <sys/stat.h>
|
||||
@ -144,6 +143,7 @@ service_alloc(const char *servname, const char *router)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
service->capabilities = service->router->getCapabilities();
|
||||
service->client_count = 0;
|
||||
service->name = (char*)servname;
|
||||
service->routerModule = (char*)router;
|
||||
@ -210,6 +210,16 @@ service_isvalid(SERVICE *service)
|
||||
return rval;
|
||||
}
|
||||
|
||||
static inline void close_port(SERV_LISTENER *port)
|
||||
{
|
||||
port->service->state = SERVICE_STATE_FAILED;
|
||||
if (port->listener)
|
||||
{
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an individual port/protocol pair
|
||||
*
|
||||
@ -232,7 +242,9 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
|
||||
{
|
||||
/* Should never happen, this guarantees it can't */
|
||||
MXS_ERROR("Attempt to start port with null or incomplete service");
|
||||
goto retblock;
|
||||
close_port(port);
|
||||
ss_dassert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
port->listener = dcb_alloc(DCB_ROLE_SERVICE_LISTENER, port);
|
||||
@ -240,7 +252,8 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
|
||||
if (port->listener == NULL)
|
||||
{
|
||||
MXS_ERROR("Failed to create listener for service %s.", service->name);
|
||||
goto retblock;
|
||||
close_port(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
port->listener->service = service;
|
||||
@ -252,18 +265,15 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
|
||||
|
||||
if ((funcs = (GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) == NULL)
|
||||
{
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
MXS_ERROR("Unable to load protocol module %s. Listener "
|
||||
"for service %s not started.",
|
||||
port->protocol,
|
||||
service->name);
|
||||
goto retblock;
|
||||
MXS_ERROR("Unable to load protocol module %s. Listener for service %s not started.",
|
||||
port->protocol, service->name);
|
||||
close_port(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
|
||||
|
||||
const char *authenticator_name = "NullAuth";
|
||||
const char *authenticator_name = "NullAuthDeny";
|
||||
|
||||
if (port->authenticator)
|
||||
{
|
||||
@ -280,8 +290,7 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
|
||||
{
|
||||
MXS_ERROR("Failed to load authenticator module '%s' for listener '%s'",
|
||||
authenticator_name, port->name);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
close_port(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -302,12 +311,24 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
|
||||
}
|
||||
|
||||
/** Load the authentication users before before starting the listener */
|
||||
if (port->listener->authfunc.loadusers &&
|
||||
(service->router->getCapabilities() & RCAP_TYPE_NO_USERS_INIT) == 0 &&
|
||||
port->listener->authfunc.loadusers(port) != MXS_AUTH_LOADUSERS_OK)
|
||||
if (port->listener->authfunc.loadusers)
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to load users for listener '%s', authentication might not work.",
|
||||
service->name, port->name);
|
||||
switch (port->listener->authfunc.loadusers(port))
|
||||
{
|
||||
case MXS_AUTH_LOADUSERS_FATAL:
|
||||
MXS_ERROR("[%s] Fatal error when loading users for listener '%s', "
|
||||
"service is not started.", service->name, port->name);
|
||||
close_port(port);
|
||||
return 0;
|
||||
|
||||
case MXS_AUTH_LOADUSERS_ERROR:
|
||||
MXS_WARNING("[%s] Failed to load users for listener '%s', authentication"
|
||||
" might not work.", service->name, port->name);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -328,24 +349,16 @@ serviceStartPort(SERVICE *service, SERV_LISTENER *port)
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to create session to service %s.",
|
||||
service->name);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
MXS_ERROR("[%s] Failed to create listener session.", service->name);
|
||||
close_port(port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Unable to start to listen port %d for %s %s.",
|
||||
port->port,
|
||||
port->protocol,
|
||||
service->name);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
MXS_ERROR("[%s] Failed to listen on %s", service->name, config_bind);
|
||||
close_port(port);
|
||||
}
|
||||
|
||||
retblock:
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@ -370,7 +383,11 @@ int serviceStartAllPorts(SERVICE* service)
|
||||
port = port->next;
|
||||
}
|
||||
|
||||
if (listeners)
|
||||
if (service->state == SERVICE_STATE_FAILED)
|
||||
{
|
||||
listeners = 0;
|
||||
}
|
||||
else if (listeners)
|
||||
{
|
||||
service->state = SERVICE_STATE_STARTED;
|
||||
service->stats.started = time(0);
|
||||
@ -458,29 +475,20 @@ int
|
||||
serviceStart(SERVICE *service)
|
||||
{
|
||||
int listeners = 0;
|
||||
char **router_options = copy_string_array(service->routerOptions);
|
||||
|
||||
if (check_service_permissions(service))
|
||||
if ((service->router_instance = service->router->createInstance(service, router_options)))
|
||||
{
|
||||
char **router_options = copy_string_array(service->routerOptions);
|
||||
if ((service->router_instance = service->router->createInstance(
|
||||
service, router_options)))
|
||||
{
|
||||
listeners += serviceStartAllPorts(service);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("%s: Failed to create router instance for service. Service not started.",
|
||||
service->name);
|
||||
service->state = SERVICE_STATE_FAILED;
|
||||
}
|
||||
free_string_array(router_options);
|
||||
listeners = serviceStartAllPorts(service);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("%s: Inadequate user permissions for service. Service not started.",
|
||||
service->name);
|
||||
MXS_ERROR("%s: Failed to create router instance. Service not started.", service->name);
|
||||
service->state = SERVICE_STATE_FAILED;
|
||||
}
|
||||
|
||||
free_string_array(router_options);
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@ -1059,6 +1067,7 @@ serviceSetFilters(SERVICE *service, char *filters)
|
||||
char *ptr, *brkt;
|
||||
int n = 0;
|
||||
bool rval = true;
|
||||
uint64_t capabilities = 0;
|
||||
|
||||
if ((flist = (FILTER_DEF **) MXS_MALLOC(sizeof(FILTER_DEF *))) == NULL)
|
||||
{
|
||||
@ -1081,7 +1090,11 @@ serviceSetFilters(SERVICE *service, char *filters)
|
||||
|
||||
if ((flist[n - 1] = filter_find(filter_name)))
|
||||
{
|
||||
if (!filter_load(flist[n - 1]))
|
||||
if (filter_load(flist[n - 1]))
|
||||
{
|
||||
capabilities |= flist[n - 1]->obj->getCapabilities();
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to load filter '%s' for service '%s'.",
|
||||
filter_name, service->name);
|
||||
@ -1105,6 +1118,7 @@ serviceSetFilters(SERVICE *service, char *filters)
|
||||
{
|
||||
service->filters = flist;
|
||||
service->n_filters = n;
|
||||
service->capabilities |= capabilities;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1462,12 +1476,26 @@ int service_refresh_users(SERVICE *service)
|
||||
|
||||
for (SERV_LISTENER *port = service->ports; port; port = port->next)
|
||||
{
|
||||
if (port->listener->authfunc.loadusers &&
|
||||
port->listener->authfunc.loadusers(port) != MXS_AUTH_LOADUSERS_OK)
|
||||
/** Load the authentication users before before starting the listener */
|
||||
if (port->listener->authfunc.loadusers)
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to load users for listener '%s', authentication might not work.",
|
||||
service->name, port->name);
|
||||
ret = 1;
|
||||
switch (port->listener->authfunc.loadusers(port))
|
||||
{
|
||||
case MXS_AUTH_LOADUSERS_FATAL:
|
||||
MXS_ERROR("[%s] Fatal error when loading users for listener '%s',"
|
||||
" authentication will not work.", service->name, port->name);
|
||||
ret = 1;
|
||||
break;
|
||||
|
||||
case MXS_AUTH_LOADUSERS_ERROR:
|
||||
MXS_WARNING("[%s] Failed to load users for listener '%s', authentication"
|
||||
" might not work.", service->name, port->name);
|
||||
ret = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,6 +142,8 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
*/
|
||||
session->state = SESSION_STATE_READY;
|
||||
|
||||
session->trx_state = SESSION_TRX_INACTIVE;
|
||||
session->autocommit = true;
|
||||
/*
|
||||
* Only create a router session if we are not the listening
|
||||
* DCB or an internal DCB. Creating a router session may create a connection to a
|
||||
@ -1061,3 +1063,34 @@ sessionGetList(SESSIONLISTFILTER filter)
|
||||
}
|
||||
/*lint +e429 */
|
||||
|
||||
session_trx_state_t session_get_trx_state(const SESSION* ses)
|
||||
{
|
||||
return ses->trx_state;
|
||||
}
|
||||
|
||||
session_trx_state_t session_set_trx_state(SESSION* ses, session_trx_state_t new_state)
|
||||
{
|
||||
session_trx_state_t prev_state = ses->trx_state;
|
||||
|
||||
ses->trx_state = new_state;
|
||||
|
||||
return prev_state;
|
||||
}
|
||||
|
||||
const char* session_trx_state_to_string(session_trx_state_t state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SESSION_TRX_INACTIVE:
|
||||
return "SESSION_TRX_INACTIVE";
|
||||
case SESSION_TRX_ACTIVE:
|
||||
return "SESSION_TRX_ACTIVE";
|
||||
case SESSION_TRX_READ_ONLY:
|
||||
return "SESSION_TRX_READ_ONLY";
|
||||
case SESSION_TRX_READ_WRITE:
|
||||
return "SESSION_TRX_READ_WRITE";
|
||||
}
|
||||
|
||||
MXS_ERROR("Unknown session_trx_state_t value: %d", (int)state);
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
@ -24,9 +24,10 @@
|
||||
*/
|
||||
|
||||
#include <maxscale/statistics.h>
|
||||
#include <string.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/config.h>
|
||||
#include <string.h>
|
||||
#include <maxscale/debug.h>
|
||||
#include <maxscale/platform.h>
|
||||
|
||||
static int thread_count = 0;
|
||||
|
@ -9,7 +9,6 @@ add_executable(test_log testlog.c)
|
||||
add_executable(test_logorder testlogorder.c)
|
||||
add_executable(test_logthrottling testlogthrottling.cc)
|
||||
add_executable(test_modutil testmodutil.c)
|
||||
add_executable(test_mysql_users test_mysql_users.c)
|
||||
add_executable(test_poll testpoll.c)
|
||||
add_executable(test_queuemanager testqueuemanager.c)
|
||||
add_executable(test_server testserver.c)
|
||||
@ -30,7 +29,6 @@ target_link_libraries(test_log maxscale-common)
|
||||
target_link_libraries(test_logorder maxscale-common)
|
||||
target_link_libraries(test_logthrottling maxscale-common)
|
||||
target_link_libraries(test_modutil maxscale-common)
|
||||
target_link_libraries(test_mysql_users MySQLAuth MySQLCommon maxscale-common)
|
||||
target_link_libraries(test_poll maxscale-common)
|
||||
target_link_libraries(test_queuemanager maxscale-common)
|
||||
target_link_libraries(test_server maxscale-common)
|
||||
@ -53,7 +51,6 @@ add_test(TestLogThrottling test_logthrottling)
|
||||
add_test(TestMaxScalePCRE2 testmaxscalepcre2)
|
||||
add_test(TestMemlog testmemlog)
|
||||
add_test(TestModutil test_modutil)
|
||||
add_test(TestMySQLUsers test_mysql_users)
|
||||
add_test(NAME TestMaxPasswd COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/testmaxpasswd.sh)
|
||||
add_test(TestPoll test_poll)
|
||||
add_test(TestQueueManager test_queuemanager)
|
||||
|
@ -11,6 +11,10 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#ifndef SS_DEBUG
|
||||
#define SS_DEBUG
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/gwdirs.h>
|
||||
/**
|
||||
* test1 Allocate a server and do lots of other things
|
||||
*
|
||||
@ -48,9 +49,10 @@ test1()
|
||||
char *status;
|
||||
|
||||
/* Server tests */
|
||||
ss_dfprintf(stderr,
|
||||
"testserver : creating server called MyServer");
|
||||
server = server_alloc("MyServer", "HTTPD", 9876, "NullAuthAccept", NULL);
|
||||
ss_dfprintf(stderr, "testserver : creating server called MyServer");
|
||||
set_libdir(MXS_STRDUP_A("../../modules/authenticator/"));
|
||||
server = server_alloc("MyServer", "HTTPD", 9876, "NullAuthAllow", NULL);
|
||||
ss_info_dassert(server, "Allocating the server should not fail");
|
||||
mxs_log_flush_sync();
|
||||
|
||||
//ss_info_dassert(NULL != service, "New server with valid protocol and port must not be null");
|
||||
|
@ -51,45 +51,29 @@ test1()
|
||||
int result;
|
||||
int argc = 3;
|
||||
|
||||
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
|
||||
init_test_env(NULL);
|
||||
|
||||
/* Service tests */
|
||||
ss_dfprintf(stderr,
|
||||
"testservice : creating service called MyService with router nonexistent");
|
||||
service = service_alloc("MyService", "non-existent");
|
||||
mxs_log_flush_sync();
|
||||
ss_info_dassert(NULL == service, "New service with invalid router should be null");
|
||||
ss_info_dassert(0 == service_isvalid(service), "Service must not be valid after incorrect creation");
|
||||
ss_dfprintf(stderr, "\t..done\nValid service creation, router testroute.");
|
||||
set_libdir(MXS_STRDUP_A("../../modules/routing/"));
|
||||
service = service_alloc("MyService", "testroute");
|
||||
mxs_log_flush_sync();
|
||||
set_libdir(MXS_STRDUP_A("../../modules/routing/readconnroute/"));
|
||||
service = service_alloc("MyService", "readconnroute");
|
||||
|
||||
ss_info_dassert(NULL != service, "New service with valid router must not be null");
|
||||
ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation");
|
||||
ss_info_dassert(0 == strcmp("MyService", service_get_name(service)), "Service must have given name");
|
||||
ss_dfprintf(stderr, "\t..done\nAdding protocol testprotocol.");
|
||||
set_libdir(MXS_STRDUP_A("../../modules/authenticator/MySQLAuth/"));
|
||||
ss_info_dassert(0 != serviceAddProtocol(service, "TestProtocol", "testprotocol",
|
||||
"localhost", 9876, "MySQLClient", "MySQLAuth", NULL),
|
||||
"localhost", 9876, "MySQLAuth", NULL, NULL),
|
||||
"Add Protocol should succeed");
|
||||
ss_info_dassert(0 != serviceHasProtocol(service, "testprotocol", "localhost", 9876),
|
||||
"Service should have new protocol as requested");
|
||||
set_libdir(MXS_STRDUP_A("../../modules/protocol/"));
|
||||
serviceStartProtocol(service, "testprotocol", 9876);
|
||||
mxs_log_flush_sync();
|
||||
ss_dfprintf(stderr, "\t..done\nStarting Service.");
|
||||
result = serviceStart(service);
|
||||
mxs_log_flush_sync();
|
||||
ss_info_dassert(0 != result, "Start should succeed");
|
||||
serviceStop(service);
|
||||
mxs_log_flush_sync();
|
||||
ss_info_dassert(service->state == SERVICE_STATE_STOPPED, "Stop should succeed");
|
||||
result = serviceStartAll();
|
||||
mxs_log_flush_sync();
|
||||
ss_info_dassert(0 != result, "Start all should succeed");
|
||||
ss_dfprintf(stderr, "\t..done\nStopping Service.");
|
||||
serviceStop(service);
|
||||
ss_info_dassert(service->state == SERVICE_STATE_STOPPED, "Stop should succeed");
|
||||
ss_dfprintf(stderr, "\t..done\n");
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -315,14 +315,15 @@ char *create_hex_sha1_sha1_passwd(char *passwd)
|
||||
* Remove duplicate and trailing forward slashes from a path.
|
||||
* @param path Path to clean up
|
||||
*/
|
||||
void clean_up_pathname(char *path)
|
||||
bool clean_up_pathname(char *path)
|
||||
{
|
||||
char *data = path;
|
||||
size_t len = strlen(path);
|
||||
|
||||
if (len > PATH_MAX)
|
||||
{
|
||||
MXS_WARNING("Pathname too long: %s", path);
|
||||
MXS_ERROR("Pathname too long: %s", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (*data != '\0')
|
||||
@ -350,6 +351,8 @@ void clean_up_pathname(char *path)
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,25 +1,28 @@
|
||||
add_library(MySQLAuth SHARED mysql_auth.c)
|
||||
target_link_libraries(MySQLAuth maxscale-common MySQLCommon)
|
||||
set_target_properties(MySQLAuth PROPERTIES VERSION "1.0.0")
|
||||
install_module(MySQLAuth core)
|
||||
add_subdirectory(MySQLAuth)
|
||||
|
||||
add_library(MySQLBackendAuth SHARED mysql_backend_auth.c)
|
||||
target_link_libraries(MySQLBackendAuth maxscale-common MySQLCommon)
|
||||
set_target_properties(MySQLBackendAuth PROPERTIES VERSION "1.0.0")
|
||||
install_module(MySQLBackendAuth core)
|
||||
|
||||
if (GSSAPI_FOUND)
|
||||
include_directories(${GSSAPI_INCS})
|
||||
if (GSSAPI_FOUND AND SQLITE_FOUND)
|
||||
if (NOT SQLITE_VERSION VERSION_LESS "3.7.7")
|
||||
include_directories(${GSSAPI_INCS})
|
||||
include_directories(${SQLITE_INCLUDE_DIR})
|
||||
|
||||
add_library(GSSAPIAuth SHARED gssapi_auth.c gssapi_auth_common.c)
|
||||
target_link_libraries(GSSAPIAuth maxscale-common ${GSSAPI_LIBS} MySQLCommon)
|
||||
set_target_properties(GSSAPIAuth PROPERTIES VERSION "1.0.0")
|
||||
install_module(GSSAPIAuth core)
|
||||
add_library(GSSAPIAuth SHARED gssapi_auth.c gssapi_auth_common.c)
|
||||
target_link_libraries(GSSAPIAuth maxscale-common ${GSSAPI_LIBS} ${SQLITE_LIBRARIES} MySQLCommon)
|
||||
set_target_properties(GSSAPIAuth PROPERTIES VERSION "1.0.0")
|
||||
install_module(GSSAPIAuth core)
|
||||
|
||||
add_library(GSSAPIBackendAuth SHARED gssapi_backend_auth.c gssapi_auth_common.c)
|
||||
target_link_libraries(GSSAPIBackendAuth maxscale-common ${GSSAPI_LIBS} MySQLCommon)
|
||||
set_target_properties(GSSAPIBackendAuth PROPERTIES VERSION "1.0.0")
|
||||
install_module(GSSAPIBackendAuth core)
|
||||
add_library(GSSAPIBackendAuth SHARED gssapi_backend_auth.c gssapi_auth_common.c)
|
||||
target_link_libraries(GSSAPIBackendAuth maxscale-common ${GSSAPI_LIBS} MySQLCommon)
|
||||
set_target_properties(GSSAPIBackendAuth PROPERTIES VERSION "1.0.0")
|
||||
install_module(GSSAPIBackendAuth core)
|
||||
|
||||
else()
|
||||
message(STATUS "Minimum requires SQLite version for GSSAPIAuth is 3.7.7, current SQLite version is ${SQLITE_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(NullAuthAllow SHARED null_auth_allow.c)
|
||||
|
10
server/modules/authenticator/MySQLAuth/CMakeLists.txt
Normal file
10
server/modules/authenticator/MySQLAuth/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
add_library(MySQLAuth SHARED mysql_auth.c dbusers.c)
|
||||
target_link_libraries(MySQLAuth maxscale-common MySQLCommon)
|
||||
set_target_properties(MySQLAuth PROPERTIES VERSION "1.0.0")
|
||||
install_module(MySQLAuth core)
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_executable(test_mysql_users test_mysql_users.c)
|
||||
target_link_libraries(test_mysql_users MySQLAuth MySQLCommon maxscale-common)
|
||||
add_test(TestMySQLUsers test_mysql_users)
|
||||
endif()
|
@ -42,7 +42,7 @@
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/dbusers.h>
|
||||
#include "dbusers.h"
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/secrets.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
@ -235,19 +235,6 @@ static bool host_matches_singlechar_wildcard(const char* user, const char* wild)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the user/passwd form mysql.user table into the service users' hashtable
|
||||
* environment.
|
||||
*
|
||||
* @param service The current service
|
||||
* @return -1 on any error or the number of users inserted (0 means no users at all)
|
||||
*/
|
||||
int
|
||||
load_mysql_users(SERV_LISTENER *listener)
|
||||
{
|
||||
return get_users(listener, listener->users);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the user/passwd form mysql.user table into the service users' hashtable
|
||||
* environment.
|
||||
@ -292,45 +279,19 @@ replace_mysql_users(SERV_LISTENER *listener)
|
||||
return i;
|
||||
}
|
||||
|
||||
USERS *oldusers = listener->users;
|
||||
|
||||
/**
|
||||
* TODO: Comparing the checksum after loading users is not necessary. We
|
||||
* have already queried the server, allocated memory and done the processing
|
||||
* so comparing if a change was made is pointless since the end result is
|
||||
* always the same. We end up with either the same users or a new set of
|
||||
* users. If the new users would always be taken into use, we'd avoid
|
||||
* the costly task of calculating the diff.
|
||||
*
|
||||
* An improvement to the diff calculation would be to push the calculation
|
||||
* to the backend server. This way the bandwidth usage would be minimized
|
||||
* and the backend server would tell us if we need to query for more data.
|
||||
*/
|
||||
if (oldusers != NULL && memcmp(oldusers->cksum, newusers->cksum,
|
||||
SHA_DIGEST_LENGTH) == 0)
|
||||
{
|
||||
/* same data, nothing to do */
|
||||
MXS_DEBUG("%lu [replace_mysql_users] users' tables not switched, checksum is the same",
|
||||
pthread_self());
|
||||
|
||||
/* free the new table */
|
||||
users_free(newusers);
|
||||
i = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* replace the service with effective new data */
|
||||
MXS_DEBUG("%lu [replace_mysql_users] users' tables replaced, checksum differs",
|
||||
pthread_self());
|
||||
listener->users = newusers;
|
||||
}
|
||||
/** TODO: Figure out a way to create a checksum function in the backend server
|
||||
* so that we can avoid querying the complete list of users every time we
|
||||
* need to refresh the users */
|
||||
MXS_DEBUG("%lu [replace_mysql_users] users' tables replaced", pthread_self());
|
||||
USERS *oldusers = listener->users;
|
||||
listener->users = newusers;
|
||||
|
||||
spinlock_release(&listener->lock);
|
||||
|
||||
/* free old resources */
|
||||
resource_free(oldresources);
|
||||
|
||||
if (i && oldusers)
|
||||
if (oldusers)
|
||||
{
|
||||
/* free the old table */
|
||||
users_free(oldusers);
|
||||
@ -1238,7 +1199,6 @@ get_users(SERV_LISTENER *listener, USERS *users)
|
||||
MYSQL_PASSWORD_LEN +
|
||||
sizeof(char) +
|
||||
MYSQL_DATABASE_MAXLEN;
|
||||
int dbnames = 0;
|
||||
int db_grants = 0;
|
||||
char dbnm[MYSQL_DATABASE_MAXLEN + 1];
|
||||
bool anon_user = false;
|
||||
@ -1493,7 +1453,7 @@ get_users(SERV_LISTENER *listener, USERS *users)
|
||||
if (db_grants)
|
||||
{
|
||||
/* load all mysql database names */
|
||||
dbnames = get_databases(listener, con);
|
||||
ss_debug(int dbnames =) get_databases(listener, con);
|
||||
MXS_DEBUG("Loaded %d MySQL Database Names for service [%s]",
|
||||
dbnames, service->name);
|
||||
}
|
@ -32,25 +32,11 @@
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
/* Refresh rate limits for load users from database */
|
||||
#define USERS_REFRESH_TIME 30 /* Allowed time interval (in seconds) after last update*/
|
||||
#define USERS_REFRESH_MAX_PER_TIME 4 /* Max number of load calls within the time interval */
|
||||
|
||||
/** Default timeout values used by the connections which fetch user authentication data */
|
||||
#define DEFAULT_AUTH_CONNECT_TIMEOUT 3
|
||||
#define DEFAULT_AUTH_READ_TIMEOUT 1
|
||||
#define DEFAULT_AUTH_WRITE_TIMEOUT 2
|
||||
|
||||
/* Max length of fields in the mysql.user table */
|
||||
#define MYSQL_USER_MAXLEN 128
|
||||
#define MYSQL_PASSWORD_LEN 41
|
||||
#define MYSQL_HOST_MAXLEN 60
|
||||
#define MYSQL_DATABASE_MAXLEN 128
|
||||
#define MYSQL_TABLE_MAXLEN 64
|
||||
|
||||
/** Cache directory and file names */
|
||||
static const char DBUSERS_DIR[] = "cache";
|
||||
@ -73,11 +59,9 @@ extern int add_mysql_users_with_host_ipv4(USERS *users, const char *user, const
|
||||
extern bool check_service_permissions(SERVICE* service);
|
||||
extern int dbusers_load(USERS *, const char *filename);
|
||||
extern int dbusers_save(USERS *, const char *filename);
|
||||
extern int load_mysql_users(SERV_LISTENER *listener);
|
||||
extern int mysql_users_add(USERS *users, MYSQL_USER_HOST *key, char *auth);
|
||||
extern USERS *mysql_users_alloc();
|
||||
extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
|
||||
extern int reload_mysql_users(SERV_LISTENER *listener);
|
||||
extern int replace_mysql_users(SERV_LISTENER *listener);
|
||||
|
||||
MXS_END_DECLS
|
@ -30,11 +30,18 @@
|
||||
#include <maxscale/gw_authenticator.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/poll.h>
|
||||
#include <maxscale/dbusers.h>
|
||||
#include "dbusers.h"
|
||||
#include <maxscale/gwdirs.h>
|
||||
#include <maxscale/secrets.h>
|
||||
#include <maxscale/utils.h>
|
||||
|
||||
typedef struct mysql_auth
|
||||
{
|
||||
char *cache_dir; /**< Custom cache directory location */
|
||||
bool inject_service_user; /**< Inject the service user into the list of users */
|
||||
} MYSQL_AUTH;
|
||||
|
||||
|
||||
/* @see function load_module in load_utils.c for explanation of the following
|
||||
* lint directives.
|
||||
*/
|
||||
@ -50,6 +57,7 @@ MODULE_INFO info =
|
||||
|
||||
static char *version_str = "V1.1.0";
|
||||
|
||||
static void* mysql_auth_init(char **options);
|
||||
static int mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
static bool mysql_auth_is_client_ssl_capable(DCB *dcb);
|
||||
static int mysql_auth_authenticate(DCB *dcb);
|
||||
@ -61,7 +69,7 @@ static int mysql_auth_load_users(SERV_LISTENER *port);
|
||||
*/
|
||||
static GWAUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
mysql_auth_init, /* Initialize the authenticator */
|
||||
NULL, /* No create entry point */
|
||||
mysql_auth_set_protocol_data, /* Extract data into structure */
|
||||
mysql_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
@ -121,6 +129,66 @@ GWAUTHENTICATOR* GetModuleObject()
|
||||
}
|
||||
/*lint +e14 */
|
||||
|
||||
/**
|
||||
* @brief Initialize the authenticator instance
|
||||
*
|
||||
* @param options Authenticator options
|
||||
* @return New MYSQL_AUTH instance or NULL on error
|
||||
*/
|
||||
static void* mysql_auth_init(char **options)
|
||||
{
|
||||
MYSQL_AUTH *instance = MXS_MALLOC(sizeof(*instance));
|
||||
|
||||
if (instance)
|
||||
{
|
||||
bool error = false;
|
||||
instance->cache_dir = NULL;
|
||||
instance->inject_service_user = true;
|
||||
|
||||
for (int i = 0; options[i]; i++)
|
||||
{
|
||||
char *value = strchr(options[i], '=');
|
||||
|
||||
if (value)
|
||||
{
|
||||
*value++ = '\0';
|
||||
|
||||
if (strcmp(options[i], "cache_dir") == 0)
|
||||
{
|
||||
if ((instance->cache_dir = MXS_STRDUP(value)) == NULL ||
|
||||
!clean_up_pathname(instance->cache_dir))
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
else if (strcmp(options[i], "inject_service_user") == 0)
|
||||
{
|
||||
instance->inject_service_user = config_truth_value(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Unknown authenticator option: %s", options[i]);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Unknown authenticator option: %s", options[i]);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
MXS_FREE(instance->cache_dir);
|
||||
MXS_FREE(instance);
|
||||
instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Authenticates a MySQL user who is a client to MaxScale.
|
||||
*
|
||||
@ -196,7 +264,7 @@ mysql_auth_authenticate(DCB *dcb)
|
||||
}
|
||||
else if (dcb->service->log_auth_warnings)
|
||||
{
|
||||
MXS_NOTICE("%s: login attempt for user '%s'@%s:%d, authentication failed.",
|
||||
MXS_WARNING("%s: login attempt for user '%s'@%s:%d, authentication failed.",
|
||||
dcb->service->name, client_data->user, dcb->remote, ntohs(dcb->ipv4.sin_port));
|
||||
if (dcb->ipv4.sin_addr.s_addr == 0x0100007F &&
|
||||
!dcb->service->localhost_match_wildcard_host)
|
||||
@ -298,46 +366,30 @@ mysql_auth_set_client_data(
|
||||
uint8_t client_auth_packet[client_auth_packet_size];
|
||||
gwbuf_copy_data(buffer, 0, client_auth_packet_size, client_auth_packet);
|
||||
|
||||
/* The numbers are the fixed elements in the client handshake packet */
|
||||
int auth_packet_base_size = MYSQL_AUTH_PACKET_BASE_SIZE;
|
||||
int packet_length_used = 0;
|
||||
|
||||
/* Take data from fixed locations first */
|
||||
memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4);
|
||||
protocol->charset = 0;
|
||||
memcpy(&protocol->charset, client_auth_packet + 4 + 4 + 4, 1);
|
||||
|
||||
/* Make username and database a null string in case none is provided */
|
||||
client_data->user[0] = 0;
|
||||
client_data->db[0] = 0;
|
||||
/* Make authentication token length 0 and token null in case none is provided */
|
||||
client_data->auth_token_len = 0;
|
||||
client_data->auth_token = NULL;
|
||||
|
||||
if (client_auth_packet_size > auth_packet_base_size)
|
||||
if (client_auth_packet_size > MYSQL_AUTH_PACKET_BASE_SIZE)
|
||||
{
|
||||
/* Should have a username */
|
||||
char *first_letter_of_username = (char *)(client_auth_packet + auth_packet_base_size);
|
||||
char *first_letter_of_username = (char *)(client_auth_packet + MYSQL_AUTH_PACKET_BASE_SIZE);
|
||||
int user_length = strlen(first_letter_of_username);
|
||||
if (client_auth_packet_size > (auth_packet_base_size + user_length)
|
||||
&& user_length <= MYSQL_USER_MAXLEN)
|
||||
{
|
||||
strcpy(client_data->user, first_letter_of_username);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Packet has incomplete or too long username */
|
||||
return MXS_AUTH_FAILED;
|
||||
}
|
||||
if (client_auth_packet_size > (auth_packet_base_size + user_length + 1))
|
||||
|
||||
ss_dassert(client_auth_packet_size > (MYSQL_AUTH_PACKET_BASE_SIZE + user_length)
|
||||
&& user_length <= MYSQL_USER_MAXLEN);
|
||||
|
||||
if (client_auth_packet_size > (MYSQL_AUTH_PACKET_BASE_SIZE + user_length + 1))
|
||||
{
|
||||
/* Extra 1 is for the terminating null after user name */
|
||||
packet_length_used = auth_packet_base_size + user_length + 1;
|
||||
packet_length_used = MYSQL_AUTH_PACKET_BASE_SIZE + user_length + 1;
|
||||
/* We should find an authentication token next */
|
||||
/* One byte of packet is the length of authentication token */
|
||||
memcpy(&client_data->auth_token_len,
|
||||
client_auth_packet + packet_length_used,
|
||||
1);
|
||||
client_auth_packet + packet_length_used, 1);
|
||||
|
||||
if (client_auth_packet_size >
|
||||
(packet_length_used + client_data->auth_token_len))
|
||||
{
|
||||
@ -346,7 +398,7 @@ mysql_auth_set_client_data(
|
||||
{
|
||||
/* The extra 1 is for the token length byte, just extracted*/
|
||||
memcpy(client_data->auth_token,
|
||||
client_auth_packet + auth_packet_base_size + user_length + 1 + 1,
|
||||
client_auth_packet + MYSQL_AUTH_PACKET_BASE_SIZE + user_length + 1 + 1,
|
||||
client_data->auth_token_len);
|
||||
}
|
||||
else
|
||||
@ -360,29 +412,6 @@ mysql_auth_set_client_data(
|
||||
/* Packet was too small to contain authentication token */
|
||||
return MXS_AUTH_FAILED;
|
||||
}
|
||||
packet_length_used += 1 + client_data->auth_token_len;
|
||||
/*
|
||||
* Note: some clients may pass empty database, CONNECT_WITH_DB !=0 but database =""
|
||||
*/
|
||||
if ((uint32_t)GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB &
|
||||
gw_mysql_get_byte4((uint8_t *)&protocol->client_capabilities)
|
||||
&& client_auth_packet_size > packet_length_used)
|
||||
{
|
||||
char *database = (char *)(client_auth_packet + packet_length_used);
|
||||
int database_length = strlen(database);
|
||||
if (client_auth_packet_size >
|
||||
(packet_length_used + database_length)
|
||||
&& strlen(database) <= MYSQL_DATABASE_MAXLEN)
|
||||
{
|
||||
strcpy(client_data->db, database);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Packet is too short to contain database string */
|
||||
/* or database string in packet is too long */
|
||||
return MXS_AUTH_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MXS_AUTH_SUCCEEDED;
|
||||
@ -808,50 +837,115 @@ mysql_auth_free_client_data(DCB *dcb)
|
||||
MXS_FREE(dcb->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inject the service user into the cache
|
||||
*
|
||||
* @param port Service listener
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool add_service_user(SERV_LISTENER *port)
|
||||
{
|
||||
char *user = NULL;
|
||||
char *pw = NULL;
|
||||
bool rval = false;
|
||||
|
||||
if (serviceGetUser(port->service, &user, &pw))
|
||||
{
|
||||
pw = decryptPassword(pw);
|
||||
|
||||
if (pw)
|
||||
{
|
||||
char *newpw = create_hex_sha1_sha1_passwd(pw);
|
||||
|
||||
if (newpw)
|
||||
{
|
||||
add_mysql_users_with_host_ipv4(port->users, user, "%", newpw, "Y", "");
|
||||
add_mysql_users_with_host_ipv4(port->users, user, "localhost", newpw, "Y", "");
|
||||
MXS_FREE(newpw);
|
||||
rval = true;
|
||||
}
|
||||
MXS_FREE(pw);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to decrypt service user password.", port->service->name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to retrieve service credentials.", port->service->name);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load MySQL authentication users
|
||||
*
|
||||
* This function loads MySQL users from the backend database.
|
||||
*
|
||||
* @param port Listener definition
|
||||
* @return AUTH_LOADUSERS_OK on success, AUTH_LOADUSERS_ERROR on error
|
||||
* @return MXS_AUTH_LOADUSERS_OK on success, MXS_AUTH_LOADUSERS_ERROR and
|
||||
* MXS_AUTH_LOADUSERS_FATAL on fatal error
|
||||
*/
|
||||
static int mysql_auth_load_users(SERV_LISTENER *port)
|
||||
{
|
||||
int rc = MXS_AUTH_LOADUSERS_OK;
|
||||
SERVICE *service = port->listener->service;
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)port->auth_instance;
|
||||
|
||||
if (port->users == NULL && !check_service_permissions(port->service))
|
||||
{
|
||||
return MXS_AUTH_LOADUSERS_FATAL;
|
||||
}
|
||||
|
||||
int loaded = replace_mysql_users(port);
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (instance->cache_dir)
|
||||
{
|
||||
snprintf(path, sizeof(path) - sizeof(DBUSERS_FILE) - 1, "%s/", instance->cache_dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(path, "%s/%s/%s/%s/", get_cachedir(), service->name, port->name, DBUSERS_DIR);
|
||||
}
|
||||
|
||||
if (loaded < 0)
|
||||
{
|
||||
MXS_ERROR("[%s] Unable to load users for listener %s listening at %s:%d.", service->name,
|
||||
port->name, port->address ? port->address : "0.0.0.0", port->port);
|
||||
|
||||
/* Try loading authentication data from file cache */
|
||||
char path[PATH_MAX];
|
||||
sprintf(path, "%s/%s/%s/%s/%s", get_cachedir(), service->name, port->name,
|
||||
DBUSERS_DIR, DBUSERS_FILE);
|
||||
strcat(path, DBUSERS_FILE);
|
||||
|
||||
if ((loaded = dbusers_load(port->users, path)) == -1)
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to load cached users from '%s'.", service->name, path);;
|
||||
MXS_ERROR("[%s] Failed to load cached users from '%s'.", service->name, path);
|
||||
rc = MXS_AUTH_LOADUSERS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("Using cached credential information.");
|
||||
MXS_WARNING("[%s] Using cached credential information from '%s'.", service->name, path);
|
||||
}
|
||||
|
||||
if (instance->inject_service_user)
|
||||
{
|
||||
/** Inject the service user as a 'backup' user that's available
|
||||
* if loading of the users fails */
|
||||
if (!add_service_user(port))
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to inject service user.", port->service->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Users loaded successfully, save authentication data to file cache */
|
||||
char path[PATH_MAX];
|
||||
sprintf(path, "%s/%s/%s/%s/", get_cachedir(), service->name, port->name, DBUSERS_DIR);
|
||||
|
||||
if (mxs_mkdir_all(path, 0777))
|
||||
{
|
||||
strcat(path, DBUSERS_FILE);
|
||||
dbusers_save(port->users, path);
|
||||
MXS_INFO("[%s] Storing cached credential information at '%s'.", service->name, path);
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/secrets.h>
|
||||
#include <maxscale/dbusers.h>
|
||||
#include "dbusers.h"
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <mysql_auth.h>
|
||||
#include <maxscale/listener.h>
|
@ -16,12 +16,74 @@
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <maxscale/secrets.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
#include <sqlite3.h>
|
||||
#include "gssapi_auth.h"
|
||||
|
||||
/** Default timeout is one minute */
|
||||
#define MXS_SQLITE_BUSY_TIMEOUT 60000
|
||||
|
||||
/**
|
||||
* MySQL queries for retrieving the list of users
|
||||
*/
|
||||
|
||||
/** Query that gets all users that authenticate via the gssapi plugin */
|
||||
const char *gssapi_users_query =
|
||||
"SELECT u.user, u.host, d.db, u.select_priv FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
"ON (u.user = d.user AND u.host = d.host) WHERE u.plugin = 'gssapi' "
|
||||
"UNION "
|
||||
"SELECT u.user, u.host, t.db, u.select_priv FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
"ON (u.user = t.user AND u.host = t.host) WHERE u.plugin = 'gssapi' "
|
||||
"ORDER BY user";
|
||||
|
||||
#define GSSAPI_USERS_QUERY_NUM_FIELDS 4
|
||||
|
||||
/**
|
||||
* SQLite queries for authenticating users
|
||||
*/
|
||||
|
||||
/** Name of the in-memory database */
|
||||
#define GSSAPI_DATABASE_NAME "file:gssapi.db?mode=memory&cache=shared"
|
||||
|
||||
/** The table name where we store the users */
|
||||
#define GSSAPI_TABLE_NAME "gssapi_users"
|
||||
|
||||
/** CREATE TABLE statement for the in-memory table */
|
||||
const char create_sql[] =
|
||||
"CREATE TABLE IF NOT EXISTS " GSSAPI_TABLE_NAME
|
||||
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean)";
|
||||
|
||||
/** The query that is executed when a user is authenticated */
|
||||
static const char gssapi_auth_query[] =
|
||||
"SELECT * FROM " GSSAPI_TABLE_NAME
|
||||
" WHERE user = '%s' AND '%s' LIKE host AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db) LIMIT 1";
|
||||
|
||||
/** Delete query used to clean up the database before loading new users */
|
||||
static const char delete_query[] = "DELETE FROM " GSSAPI_TABLE_NAME;
|
||||
|
||||
/** The insert query template which adds users to the gssapi_users table */
|
||||
static const char insert_sql_pattern[] =
|
||||
"INSERT INTO " GSSAPI_TABLE_NAME " VALUES ('%s', '%s', %s, %s)";
|
||||
|
||||
/** Used for NULL value creation in the INSERT query */
|
||||
static const char null_token[] = "NULL";
|
||||
|
||||
/** Flags for sqlite3_open_v2() */
|
||||
static int db_flags = SQLITE_OPEN_READWRITE |
|
||||
SQLITE_OPEN_CREATE |
|
||||
SQLITE_OPEN_URI |
|
||||
SQLITE_OPEN_SHAREDCACHE;
|
||||
|
||||
/** The instance structure for the client side GSSAPI authenticator, created in
|
||||
* gssapi_auth_init() */
|
||||
typedef struct gssapi_instance
|
||||
{
|
||||
char *principal_name;
|
||||
}GSSAPI_INSTANCE;
|
||||
char *principal_name; /**< Service principal name given to the client */
|
||||
sqlite3 *handle; /**< SQLite3 database handle */
|
||||
} GSSAPI_INSTANCE;
|
||||
|
||||
/**
|
||||
* @brief Initialize the GSSAPI authenticator
|
||||
@ -40,6 +102,24 @@ void* gssapi_auth_init(char **options)
|
||||
{
|
||||
instance->principal_name = NULL;
|
||||
|
||||
if (sqlite3_open_v2(GSSAPI_DATABASE_NAME, &instance->handle, db_flags, NULL) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to open SQLite3 handle.");
|
||||
MXS_FREE(instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *err;
|
||||
|
||||
if (sqlite3_exec(instance->handle, create_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to create database: %s", err);
|
||||
sqlite3_free(err);
|
||||
sqlite3_close_v2(instance->handle);
|
||||
MXS_FREE(instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; options[i]; i++)
|
||||
{
|
||||
if (strstr(options[i], "principal_name"))
|
||||
@ -70,6 +150,43 @@ void* gssapi_auth_init(char **options)
|
||||
return instance;
|
||||
}
|
||||
|
||||
void* gssapi_auth_alloc(void *instance)
|
||||
{
|
||||
gssapi_auth_t* rval = MXS_MALLOC(sizeof(gssapi_auth_t));
|
||||
|
||||
if (rval)
|
||||
{
|
||||
rval->state = GSSAPI_AUTH_INIT;
|
||||
rval->principal_name = NULL;
|
||||
rval->principal_name_len = 0;
|
||||
rval->sequence = 0;
|
||||
|
||||
if (sqlite3_open_v2(GSSAPI_DATABASE_NAME, &rval->handle, db_flags, NULL) == SQLITE_OK)
|
||||
{
|
||||
sqlite3_busy_timeout(rval->handle, MXS_SQLITE_BUSY_TIMEOUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to open SQLite3 handle.");
|
||||
MXS_FREE(rval);
|
||||
rval = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void gssapi_auth_free(void *data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)data;
|
||||
sqlite3_close_v2(auth->handle);
|
||||
MXS_FREE(auth->principal_name);
|
||||
MXS_FREE(auth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a AuthSwitchRequest packet
|
||||
*
|
||||
@ -141,31 +258,8 @@ bool store_client_token(DCB *dcb, GWBUF *buffer)
|
||||
*/
|
||||
static void copy_client_information(DCB *dcb, GWBUF *buffer)
|
||||
{
|
||||
size_t buflen = gwbuf_length(buffer);
|
||||
MySQLProtocol *protocol = (MySQLProtocol*)dcb->protocol;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
/* Store the connection characteristics and sequence number of the current packet */
|
||||
protocol->charset = 0;
|
||||
gwbuf_copy_data(buffer, MYSQL_CHARSET_OFFSET, 1, (uint8_t*)&protocol->charset);
|
||||
gwbuf_copy_data(buffer, MYSQL_CLIENT_CAP_OFFSET, MYSQL_CLIENT_CAP_SIZE,
|
||||
(uint8_t*)&protocol->client_capabilities);
|
||||
gwbuf_copy_data(buffer, MYSQL_SEQ_OFFSET, 1, &auth->sequence);
|
||||
|
||||
if (buflen > MYSQL_AUTH_PACKET_BASE_SIZE)
|
||||
{
|
||||
buflen -= MYSQL_AUTH_PACKET_BASE_SIZE;
|
||||
|
||||
/** TODO: Implement something that can safely iterate bytes of a GWBUF
|
||||
* so that we know where the terminating null character is. For the time
|
||||
* being, we'll just copy everything. */
|
||||
uint8_t data[buflen];
|
||||
gwbuf_copy_data(buffer, MYSQL_AUTH_PACKET_BASE_SIZE, buflen, data);
|
||||
|
||||
MYSQL_session *ses = (MYSQL_session*)dcb->data;
|
||||
/** data is null-terminated so the strcpy is safe */
|
||||
strcpy(ses->user, (char*)data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,6 +371,54 @@ static bool validate_gssapi_token(uint8_t* token, size_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief Callback for sqlite3_exec() */
|
||||
static int auth_cb(void *data, int columns, char** rows, char** row_names)
|
||||
{
|
||||
bool *rv = (bool*)data;
|
||||
*rv = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verify the user has access to the database
|
||||
*
|
||||
* @param auth Authenticator session
|
||||
* @param dcb Client DCB
|
||||
* @param session MySQL session
|
||||
* @return True if the user has access to the database
|
||||
*/
|
||||
static bool validate_user(gssapi_auth_t *auth, DCB *dcb, MYSQL_session *session)
|
||||
{
|
||||
size_t len = sizeof(gssapi_auth_query) + strlen(session->user) +
|
||||
strlen(session->db) + strlen(dcb->remote);
|
||||
char sql[len + 1];
|
||||
bool rval = false;
|
||||
char *err;
|
||||
|
||||
sprintf(sql, gssapi_auth_query, session->user, dcb->remote, session->db, session->db);
|
||||
|
||||
/**
|
||||
* Try authentication twice; first time with the current users, second
|
||||
* time with fresh users
|
||||
*/
|
||||
for (int i = 0; i < 2 && !rval; i++)
|
||||
{
|
||||
if (sqlite3_exec(auth->handle, sql, auth_cb, &rval, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to execute auth query: %s", err);
|
||||
sqlite3_free(err);
|
||||
rval = false;
|
||||
}
|
||||
|
||||
if (!rval)
|
||||
{
|
||||
service_refresh_users(dcb->service);
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Authenticate the client
|
||||
*
|
||||
@ -311,7 +453,8 @@ int gssapi_auth_authenticate(DCB *dcb)
|
||||
|
||||
MYSQL_session *ses = (MYSQL_session*)dcb->data;
|
||||
|
||||
if (validate_gssapi_token(ses->auth_token, ses->auth_token_len))
|
||||
if (validate_gssapi_token(ses->auth_token, ses->auth_token_len) &&
|
||||
validate_user(auth, dcb, ses))
|
||||
{
|
||||
rval = MXS_AUTH_SUCCEEDED;
|
||||
}
|
||||
@ -337,14 +480,121 @@ void gssapi_auth_free_data(DCB *dcb)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dummy function for loadusers entry point
|
||||
* @brief Delete old users from the database
|
||||
* @param handle Database handle
|
||||
*/
|
||||
static void delete_old_users(sqlite3 *handle)
|
||||
{
|
||||
char *err;
|
||||
|
||||
if (sqlite3_exec(handle, delete_query, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to delete old users: %s", err);
|
||||
sqlite3_free(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add new GSSAPI user to the internal user database
|
||||
*
|
||||
* @param handle Database handle
|
||||
* @param user Username
|
||||
* @param host Host
|
||||
* @param db Database
|
||||
* @param anydb Global access to databases
|
||||
*/
|
||||
static void add_gssapi_user(sqlite3 *handle, const char *user, const char *host,
|
||||
const char *db, bool anydb)
|
||||
{
|
||||
size_t dblen = db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
char dbstr[dblen + 1];
|
||||
|
||||
if (db)
|
||||
{
|
||||
sprintf(dbstr, "'%s'", db);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(dbstr, null_token);
|
||||
}
|
||||
|
||||
size_t len = sizeof(insert_sql_pattern) + strlen(user) + strlen(host) + dblen + 1;
|
||||
char insert_sql[len + 1];
|
||||
sprintf(insert_sql, insert_sql_pattern, user, host, dbstr, anydb ? "1" : "0");
|
||||
|
||||
char *err;
|
||||
if (sqlite3_exec(handle, insert_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to insert user: %s", err);
|
||||
sqlite3_free(err);
|
||||
}
|
||||
|
||||
MXS_INFO("Added user: %s", insert_sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load database users that use GSSAPI authentication
|
||||
*
|
||||
* Loading the list of database users that use the 'gssapi' plugin allows us to
|
||||
* give more precise error messages to the clients when authentication fails.
|
||||
*
|
||||
* @param listener Listener definition
|
||||
* @return Always MXS_AUTH_LOADUSERS_OK
|
||||
* @return MXS_AUTH_LOADUSERS_OK on success, MXS_AUTH_LOADUSERS_ERROR on error
|
||||
*/
|
||||
int gssapi_auth_load_users(SERV_LISTENER *listener)
|
||||
{
|
||||
return MXS_AUTH_LOADUSERS_OK;
|
||||
char *user, *pw;
|
||||
int rval = MXS_AUTH_LOADUSERS_ERROR;
|
||||
GSSAPI_INSTANCE *inst = (GSSAPI_INSTANCE*)listener->auth_instance;
|
||||
|
||||
if (serviceGetUser(listener->service, &user, &pw) && (pw = decryptPassword(pw)))
|
||||
{
|
||||
for (SERVER_REF *servers = listener->service->dbref; servers; servers = servers->next)
|
||||
{
|
||||
MYSQL *mysql = mysql_init(NULL);
|
||||
|
||||
if (mxs_mysql_real_connect(mysql, servers->server, user, pw))
|
||||
{
|
||||
if (mysql_query(mysql, gssapi_users_query))
|
||||
{
|
||||
MXS_ERROR("Failed to query server '%s' for GSSAPI users: %s",
|
||||
servers->server->unique_name, mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
MYSQL_RES *res = mysql_store_result(mysql);
|
||||
|
||||
delete_old_users(inst->handle);
|
||||
|
||||
if (res)
|
||||
{
|
||||
ss_dassert(mysql_num_fields(res) == GSSAPI_USERS_QUERY_NUM_FIELDS);
|
||||
MYSQL_ROW row;
|
||||
|
||||
while ((row = mysql_fetch_row(res)))
|
||||
{
|
||||
add_gssapi_user(inst->handle, row[0], row[1], row[2],
|
||||
row[3] && strcasecmp(row[3], "Y") == 0);
|
||||
}
|
||||
|
||||
rval = MXS_AUTH_LOADUSERS_OK;
|
||||
mysql_free_result(res);
|
||||
}
|
||||
}
|
||||
|
||||
mysql_close(mysql);
|
||||
|
||||
if (rval == MXS_AUTH_LOADUSERS_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MXS_FREE(pw);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,7 +609,7 @@ static GWAUTHENTICATOR MyObject =
|
||||
gssapi_auth_authenticate, /* Authenticate user credentials */
|
||||
gssapi_auth_free_data, /* Free the client data held in DCB */
|
||||
gssapi_auth_free, /* Free authenticator data */
|
||||
gssapi_auth_load_users /* Dummy function */
|
||||
gssapi_auth_load_users /* Load database users */
|
||||
};
|
||||
|
||||
MODULE_INFO info =
|
||||
@ -370,7 +620,7 @@ MODULE_INFO info =
|
||||
"GSSAPI authenticator"
|
||||
};
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
static char version_str[] = "V1.0.0";
|
||||
|
||||
/**
|
||||
* Version string entry point
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <gssapi.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
@ -36,19 +37,16 @@ enum gssapi_auth_state
|
||||
GSSAPI_AUTH_FAILED
|
||||
};
|
||||
|
||||
/** Common state tracking structure */
|
||||
/** Common structure for both backend and client authenticators */
|
||||
typedef struct gssapi_auth
|
||||
{
|
||||
enum gssapi_auth_state state; /**< Authentication state*/
|
||||
uint8_t *principal_name; /**< Principal name */
|
||||
size_t principal_name_len; /**< Length of the principal name */
|
||||
uint8_t sequence; /**< The next packet seqence number */
|
||||
sqlite3 *handle; /**< SQLite3 database handle */
|
||||
} gssapi_auth_t;
|
||||
|
||||
/** These functions can used for the `create` and `destroy` entry points */
|
||||
void* gssapi_auth_alloc(void *instance);
|
||||
void gssapi_auth_free(void *data);
|
||||
|
||||
/** Report GSSAPI errors */
|
||||
void report_error(OM_uint32 major, OM_uint32 minor);
|
||||
|
||||
|
@ -15,31 +15,6 @@
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
|
||||
void* gssapi_auth_alloc(void *instance)
|
||||
{
|
||||
gssapi_auth_t* rval = MXS_MALLOC(sizeof(gssapi_auth_t));
|
||||
|
||||
if (rval)
|
||||
{
|
||||
rval->state = GSSAPI_AUTH_INIT;
|
||||
rval->principal_name = NULL;
|
||||
rval->principal_name_len = 0;
|
||||
rval->sequence = 0;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void gssapi_auth_free(void *data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)data;
|
||||
MXS_FREE(auth->principal_name);
|
||||
MXS_FREE(auth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report GSSAPI errors
|
||||
*
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user