Merge branch 'develop' into MAX-324

This commit is contained in:
Markus Makela
2015-02-06 23:27:59 +02:00
25 changed files with 59552 additions and 959 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
[Search page for MaxScale Documentation]((http://mariadb-corporation.github.io/MaxScale/Search/)
# Contents # Contents
## About MaxScale ## About MaxScale
@ -39,6 +41,11 @@
- [Tee Filter](filters/Tee-Filter.md) - [Tee Filter](filters/Tee-Filter.md)
- [Top N Filter](filters/Top-N-Filter.md) - [Top N Filter](filters/Top-N-Filter.md)
- [Firewall Filter](filters/Firewall-Filter.md) - [Firewall Filter](filters/Firewall-Filter.md)
- [RabbitMQ Filter](filters/RabbitMQ-Filter.md)
## Utilities
- [RabbitMQ Consumer Client](filters/RabbitMQ-Consumer-Client.md)
## Routers ## Routers
@ -57,3 +64,4 @@
- [MaxScale 1.0 Release Notes](Release-Notes/MaxScale-1.0-Release-Notes.md) - [MaxScale 1.0 Release Notes](Release-Notes/MaxScale-1.0-Release-Notes.md)
- [MaxScale 1.0.1 Release Notes](Release-Notes/MaxScale-1.0.1-Release-Notes.md) - [MaxScale 1.0.1 Release Notes](Release-Notes/MaxScale-1.0.1-Release-Notes.md)
- [MaxScale 1.0.3 Release Notes](Release-Notes/MaxScale-1.0.3-Release-Notes.md) - [MaxScale 1.0.3 Release Notes](Release-Notes/MaxScale-1.0.3-Release-Notes.md)

File diff suppressed because it is too large Load Diff

View File

@ -62,82 +62,46 @@ $ cd build
The next step is to run the cmake command to build the Makefile you need to compile Maxscale. There are a number of options you may give to configure cmake and point it to the various packages it requires. These are documented in the MaxScale README file, in this example we will assume the MariaDB developer packages have been installed in a non-standard location and set all the options required to locate these, along with options to build the unit tests and configure the installation target directory. The next step is to run the cmake command to build the Makefile you need to compile Maxscale. There are a number of options you may give to configure cmake and point it to the various packages it requires. These are documented in the MaxScale README file, in this example we will assume the MariaDB developer packages have been installed in a non-standard location and set all the options required to locate these, along with options to build the unit tests and configure the installation target directory.
$ cmake -DMYSQL_DIR=~/usr/include/mysql \ $ cmake -DMYSQL\_DIR=/usr/mariadb-5.5.41-linux-x86_64/include/mysql \
-DEMBEDDED\_LIB=/usr/mariadb-5.5.41-linux-x86\_64/lib/libmysqld.a \
-DEMBEDDED_LIB=~/usr/lib64/libmysqld.a \ -DMYSQLCLIENT\_LIBRARIES=/usr/mariadb-5.5.41-linux-x86_64/lib/libmysqlclient.so \
-DERRMSG=/usr/mariadb-5.5.41-linux-x86\_64/share/english/errmsg.sys \
-DMYSQLCLIENT_LIBRARIES=~/usr/lib64/libmysqlclient.so \ -DINSTALL\_DIR=/home/maxscale/MaxScale -DBUILD_TESTS=Y \
-DINSTALL\_SYSTEM\_FILES=N \
-DERRMSG=~/usr/share/mysql/english/errmsg.sys \ -DBUILD_BINLOG=Y ../
-DINSTALL_DIR=/usr/local/maxscale -DBUILD_TESTS=Y \
-DINSTALL_SYSTEM_FILES=N ../MaxScale
-- CMake version: 2.8.12.2 -- CMake version: 2.8.12.2
-- The C compiler identification is GNU 4.4.7 -- The C compiler identification is GNU 4.4.7
-- The CXX compiler identification is GNU 4.4.7 -- The CXX compiler identification is GNU 4.4.7
-- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works -- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info -- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done -- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works -- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done -- Detecting CXX compiler ABI info - done
-- Library was found at: /lib64/libaio.so -- Library was found at: /lib64/libaio.so
-- Library was found at: /usr/lib64/libssl.so -- Library was found at: /usr/lib64/libssl.so
-- Library was found at: /usr/lib64/libcrypt.so -- Library was found at: /usr/lib64/libcrypt.so
-- Library was found at: /usr/lib64/libcrypto.so -- Library was found at: /usr/lib64/libcrypto.so
-- Library was found at: /usr/lib64/libz.so -- Library was found at: /usr/lib64/libz.so
-- Library was found at: /usr/lib64/libm.so -- Library was found at: /usr/lib64/libm.so
-- Library was found at: /usr/lib64/libdl.so -- Library was found at: /usr/lib64/libdl.so
-- Library was found at: /usr/lib64/librt.so -- Library was found at: /usr/lib64/librt.so
-- Library was found at: /usr/lib64/libpthread.so -- Library was found at: /usr/lib64/libpthread.so
-- Using errmsg.sys found at: /home/maxscale/usr/share/mysql/english/errmsg.sys -- Using errmsg.sys found at: /home/maxscale/usr/share/mysql/english/errmsg.sys
-- Using embedded library: /home/mpinto/usr/lib64/libmysqld.a -- Using embedded library: /home/mpinto/usr/lib64/libmysqld.a
-- Valgrind found: /usr/bin/valgrind -- Valgrind found: /usr/bin/valgrind
-- Found dynamic MySQL client library: /home/maxscale/usr/lib64/libmysqlclient.so -- Found dynamic MySQL client library: /home/maxscale/usr/lib64/libmysqlclient.so
-- Found static MySQL client library: /usr/lib/libmysqlclient.a -- Found static MySQL client library: /usr/lib/libmysqlclient.a
-- C Compiler supports: -Werror=format-security -- C Compiler supports: -Werror=format-security
-- Linking against: /home/mpinto/usr/lib64/libmysqlclient.so -- Linking against: /home/mpinto/usr/lib64/libmysqlclient.so
-- Installing MaxScale to: /usr/local/maxscale/ -- Installing MaxScale to: /usr/local/maxscale/
-- Generating RPM packages -- Generating RPM packages
-- Found Doxygen: /usr/bin/doxygen (found version "1.6.1") -- Found Doxygen: /usr/bin/doxygen (found version "1.6.1")
-- Configuring done -- Configuring done
-- Generating done -- Generating done
-- Build files have been written to: /home/maxscale/develop/build -- Build files have been written to: /home/maxscale/develop/build
-bash-4.1$ make depend -bash-4.1$ make depend

View File

@ -1,29 +1,42 @@
Hint Syntax # Hint Syntax
Use either ’-- ’ (notice the whitespace) or ’#’after the semicolon or ’/* .. */’ before
Use either ’-- ’ (notice the whitespace) or ’#’ after the semicolon or ’/* .. */’ before
the semicolon. the semicolon.
The MySQL manual doesn’t specify if comment blocks, i.e. ’/* .. */’, should contain a w The MySQL manual doesn’t specify if comment blocks, i.e. ’/* .. */’, should contain a w
hitespace character before or after the tags. hitespace character before or after the tags.
All hints must start with the ’maxscale tag’: All hints must start with the ’maxscale tag’:
-- maxscale <hint> -- maxscale <hint>
The hints right now have two types, ones that route to a server and others that contain The hints right now have two types, ones that route to a server and others that contain
name-value pairs. name-value pairs.
Routing queries to a server: Routing queries to a server:
-- maxscale route to [master | slave | server <server name>] -- maxscale route to [master | slave | server <server name>]
The name of the server is the same as in MaxScale.cnf The name of the server is the same as in MaxScale.cnf
Creating a name-value pair: Creating a name-value pair:
-- maxscale <param>=<value> -- maxscale <param>=<value>
Currently the only accepted parameter is Currently the only accepted parameter is
’max_slave_replication_lag’ ’max_slave_replication_lag’
Hints can be either single-use hints, which makes them affect only one query, or named Hints can be either single-use hints, which makes them affect only one query, or named
hints, which can be pushed on and off a stack of active hints. hints, which can be pushed on and off a stack of active hints.
Defining named hints: Defining named hints:
-- maxscale <hint name> prepare <hint content> -- maxscale <hint name> prepare <hint content>
Pushing a hint onto the stack: Pushing a hint onto the stack:
-- maxscale <hint name> begin -- maxscale <hint name> begin
Popping the topmost hint off the stack: Popping the topmost hint off the stack:
-- maxscale end -- maxscale end
You can define and activate a hint in a single command using the following: You can define and activate a hint in a single command using the following:
-- maxscale <hint name> begin <hint content> -- maxscale <hint name> begin <hint content>
You can also push anonymous hints onto the stack which are only used as long as they ar
e on the stack: You can also push anonymous hints onto the stack which are only used as long as they are on the stack:
-- maxscale begin <hint content> -- maxscale begin <hint content>

View File

@ -80,7 +80,7 @@ This parameter is used to provide the stem of the file names that are used to st
### initialfile ### initialfile
This optional parameter allows for the administrator to define the number of the first binlog file to download. In normal circumstances MaxScale will use any existing binlog file to determine what to request from the master. If there are no files it will then ask for the binlog file with the index number defined in the initialfile parameter. If this parameter is not set then MaxScale will ask the master for binlog events from file 1. This optional parameter allows for the administrator to define the number of the first binlog file to download. If MaxScale has previously received binlogs it will use those existing binlog files to determine what to request from the master. If no files have been downloaded MaxScale will then ask for the binlog file with the index number defined in the initialfile parameter. If this parameter is not set then MaxScale will ask the master for binlog events from file 1.
### binlogdir ### binlogdir
@ -99,7 +99,7 @@ A complete example of a service entry for a binlog router service would be as fo
[Replication] [Replication]
type=service type=service
router=binlogrouter router=binlogrouter
servers=maserdb servers=masterdb
version_string=5.6.17-log version_string=5.6.17-log
router_options=uuid=f12fcb7f-b97b-11e3-bc5e-0401152c4c22,server-id=3,user=repl,password=slavepass,master-id=1,filestem=mybin,heartbeat=30,binlogdir=/home/mriddoch/binlogs router_options=uuid=f12fcb7f-b97b-11e3-bc5e-0401152c4c22,server-id=3,user=repl,password=slavepass,master-id=1,filestem=mybin,heartbeat=30,binlogdir=/home/mriddoch/binlogs
user=maxscale user=maxscale

View File

@ -0,0 +1,38 @@
#RabbitMQ Consumer Client
## Overview
This utility tool is used to read messages from a RabbitMQ broker sent by the [RabbitMQ Filter](RabbitMQ-Filter.md) and forward these messages into an SQL database as queries.
## Command Line Arguments
The **RabbitMQ Consumer Client** only has one command line argument.
| Command | Argument |
|---------|-------------------------------------------------|
| -c | Path to the folder containing the configuration file |
## Installation
To install the RabbitMQ Consumer Client you ca either use the provided packages or you can compile it from source code. The source code is included as a part of the MaxScale source code and can be found in the `rabbtmq_consumer` folder. Please refer to the [README](../../rabbitmq_consumer/README) in the folder for more detailed instructions about installation and configuration.
## Configuration
The consumer client requires that the `consumer.cnf` configuration file is either be present in the `etc` folder of the installation directory or in the folder specified by the `-c` argument.
The source broker, the destination database and the message log file can be configured into the separate `consumer.cnf` file.
| Option | Desctiption |
|-----------|---------------------------------------------|
| hostname | Hostname of the RabbitMQ server |
| port | Port of the RabbitMQ server |
| vhost | Virtual host location of the RabbitMQ server |
| user | Username for the RabbitMQ server |
| passwd | Password for the RabbitMQ server |
| queue | Queue to consume from |
| dbserver | Hostname of the SQL server |
| dbport | Port of the SQL server |
| dbname | Name of the SQL database to use |
| dbuser | Database username |
| dbpasswd | Database passwork |
| logfile | Message log filename |

View File

@ -0,0 +1,61 @@
#RabbitMQ Filter
## Overview
This filter is designed to extract queries and transform them into a canonical form e.g. `INSERT INTO dabata.table VALUES ("John Doe", "Downtown",100,50.0);` turns into `INSERT INTO dabata.table VALUES ("?", "?",?,?);`. The filter pushes these canonized queries and their replies in to a RabbitMQ broker where they can later be retrieved. The retrieval can be done with your own application or the [RabbitMQ Consumer Client](RabbitMQ-Consumer-Client.md) utility tool, which reads the messages from the broker and sends the contents of those messages as SQL queries to a database.
## Configuration
The configuration block for the **mqfilter** filter requires the minimal filter options in it’s section within the MaxScale.cnf file, stored in $MAXSCALE_HOME/etc/MaxScale.cnf. Although the filter will start, it will use the default values which only work with a freshly installed RabbitMQ server and use its default values. This setup is mostly intednded for testing the filter.
The following is an example of a mqfilter configuration in the MaxScale.cnf file used for actual logging of queries to a RabbitMQ broker on a different host.
```
[RabbitMQ]
type=filter
module=mqfilter
hostname=192.168.122.100
port=4000
username=messageuser
password=msgpwd
exchange=msg-ex-1
key=MaxScale
logging_trigger=object,schema,source
logging_strict=false
logging_log_all=false
logging_object=my1
logging_schema=test
logging_source_user=maxtest
```
### Filter Options
The mqfilter filter does not support any filter options.
### Filter Parameters
The RabbitMQ filter has parameters to control which queries are logged based on either the attributes of the user or the query itself. These can be combined to to only log queries targeting a certain table in a certain database from a certain user from a certain network address.
Option | Description | Accepted Values | Default |
--------|-------------|-----------------|-------------
logging_trigger | Set the logging level | `all, source, schema, object` | `all` |
logging_strict | Sets whether to trigger when any of the parameters match or only if all parameters match | `true, false` | `false` |
logging_log_all | Log only SELECT, UPDATE, DELETE and INSERT or all possible queries | `true, false` | `true` |
logging_source_user | Comma-separated list of usernames to log | | |
logging_source_host | Comma-separated list of hostnames to log | | |
logging_schema | Comma-separated list of databases | | |
logging_object | Comma-separated list of database objects |
hostname | The server hostname where the messages are sent | | `localhost` |
port | Port to send the messages to | | `5672` |
username | Server login username | | `guest` |
password | Server login password | | `guest` |
vhost | The virtual host location on the server, where the messages are sent | | `/` |
exchange | The name of the exchange | | `default_exchange` |
exchange_type | The type of the exchange | `direct, fanout, topic, headers` | `direct` |
key | The routing key used when sending messages to the exchange | | `key` |
queue | The queue that will be bound to the used exchange | | |
ssl_CA_cert | Path to the CA certificate in PEM format | | |
ssl_client_cert | Path to the client cerificate in PEM format | | |
ssl_client_key | Path to the client public key in PEM format | | |

View File

@ -495,10 +495,19 @@ static skygw_query_type_t resolve_query_type(
{ {
type |= QUERY_TYPE_GSYSVAR_WRITE; type |= QUERY_TYPE_GSYSVAR_WRITE;
} }
/*
* SHOW GLOBAL STATUS - Route to master
*/
else if (lex->sql_command == SQLCOM_SHOW_STATUS)
{
type = QUERY_TYPE_WRITE;
}
/** /**
* REVOKE ALL, ASSIGN_TO_KEYCACHE, * REVOKE ALL, ASSIGN_TO_KEYCACHE,
* PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER * PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER
*/ */
else else
{ {
type |= QUERY_TYPE_GSYSVAR_WRITE; type |= QUERY_TYPE_GSYSVAR_WRITE;

View File

@ -1,9 +1,19 @@
## Example MaxScale.cnf configuration file ## Example MaxScale.cnf configuration file
# #
# Global parameters
#
# Number of worker threads in MaxScale # Number of worker threads in MaxScale
# #
# threads=<number of threads> # threads=<number of threads>
# #
# Enabled logfiles. The message log is enabled by default and
# the error log is always enabled.
#
# log_messages=<1|0>
# log_trace=<1|0>
# log_debug=<1|0>
## Example:
[maxscale] [maxscale]
threads=4 threads=4

View File

@ -1315,4 +1315,186 @@ static int gw_mysql_set_timeouts(
retblock: retblock:
return rc; return rc;
} }
/*
* Serialise a key for the dbusers hashtable to a file
*
* @param fd File descriptor to write to
* @param key The key to write
* @return 0 on error, 1 if the key was written
*/
static int
dbusers_keywrite(int fd, void *key)
{
MYSQL_USER_HOST *dbkey = (MYSQL_USER_HOST *)key;
int tmp;
tmp = strlen(dbkey->user);
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
return 0;
if (write(fd, dbkey->user, tmp) != tmp)
return 0;
if (write(fd, &dbkey->ipv4, sizeof(dbkey->ipv4)) != sizeof(dbkey->ipv4))
return 0;
if (write(fd, &dbkey->netmask, sizeof(dbkey->netmask)) != sizeof(dbkey->netmask))
return 0;
if (dbkey->resource)
{
tmp = strlen(dbkey->resource);
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
return 0;
if (write(fd, dbkey->resource, tmp) != tmp)
return 0;
}
else // NULL is valid, so represent with a length of -1
{
tmp = -1;
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
return 0;
}
return 1;
}
/**
* Serialise a value for the dbusers hashtable to a file
*
* @param fd File descriptor to write to
* @param value The value to write
* @return 0 on error, 1 if the value was written
*/
static int
dbusers_valuewrite(int fd, void *value)
{
int tmp;
tmp = strlen(value);
if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
return 0;
if (write(fd, value, tmp) != tmp)
return 0;
return 1;
}
/**
* Unserialise a key for the dbusers hashtable from a file
*
* @param fd File descriptor to read from
* @return Pointer to the new key or NULL on error
*/
static void *
dbusers_keyread(int fd)
{
MYSQL_USER_HOST *dbkey;
int tmp;
if ((dbkey = (MYSQL_USER_HOST *)malloc(sizeof(MYSQL_USER_HOST))) == NULL)
return NULL;
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
{
free(dbkey);
return NULL;
}
if ((dbkey->user = (char *)malloc(tmp + 1)) == NULL)
{
free(dbkey);
return NULL;
}
if (read(fd, dbkey->user, tmp) != tmp)
{
free(dbkey->user);
free(dbkey);
return NULL;
}
dbkey->user[tmp] = 0; // NULL Terminate
if (read(fd, &dbkey->ipv4, sizeof(dbkey->ipv4)) != sizeof(dbkey->ipv4))
{
free(dbkey->user);
free(dbkey);
return NULL;
}
if (read(fd, &dbkey->netmask, sizeof(dbkey->netmask)) != sizeof(dbkey->netmask))
{
free(dbkey->user);
free(dbkey);
return NULL;
}
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
{
free(dbkey->user);
free(dbkey);
return NULL;
}
if (tmp != -1)
{
if ((dbkey->resource = (char *)malloc(tmp + 1)) == NULL)
{
free(dbkey->user);
free(dbkey);
return NULL;
}
if (read(fd, dbkey->resource, tmp) != tmp)
{
free(dbkey->resource);
free(dbkey->user);
free(dbkey);
return NULL;
}
}
else // NULL is valid, so represent with a length of -1
{
dbkey->resource = NULL;
}
return (void *)dbkey;
}
/**
* Unserialise a value for the dbusers hashtable from a file
*
* @param fd File descriptor to read from
* @return Return the new value data or NULL on error
*/
static void *
dbusers_valueread(int fd)
{
char *value;
int tmp;
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
return NULL;
if ((value = (char *)malloc(tmp + 1)) == NULL)
return NULL;
if (read(fd, value, tmp) != tmp)
{
free(value);
return NULL;
}
value[tmp] = 0;
return (void *)value;
}
/**
* Save the dbusers data to a hashtable file
*
* @param users The hashtable that stores the user data
* @param filename The filename to save the data in
* @return The number of entries saved
*/
int
dbusers_save(USERS *users, char *filename)
{
return hashtable_save(users->data, filename, dbusers_keywrite, dbusers_valuewrite);
}
/**
* Load the dbusers data from a saved hashtable file
*
* @param users The hashtable that stores the user data
* @param filename The filename to laod the data from
* @return The number of entries loaded
*/
int
dbusers_load(USERS *users, char *filename)
{
return hashtable_load(users->data, filename, dbusers_keyread, dbusers_valueread);
}

View File

@ -697,6 +697,12 @@ int rc;
* Successfully connected to backend. Assign file descriptor to dcb * Successfully connected to backend. Assign file descriptor to dcb
*/ */
dcb->fd = fd; dcb->fd = fd;
/**
* Add server pointer to dcb
*/
dcb->server = server;
/** Copy status field to DCB */ /** Copy status field to DCB */
dcb->dcb_server_status = server->status; dcb->dcb_server_status = server->status;
ss_debug(dcb->dcb_port = server->port;) ss_debug(dcb->dcb_port = server->port;)
@ -723,7 +729,7 @@ int rc;
*/ */
atomic_add(&server->stats.n_connections, 1); atomic_add(&server->stats.n_connections, 1);
atomic_add(&server->stats.n_current, 1); atomic_add(&server->stats.n_current, 1);
return dcb; return dcb;
} }

View File

@ -17,7 +17,11 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <hashtable.h> #include <hashtable.h>
/** /**
@ -55,6 +59,7 @@
* 08/01/2014 Massimiliano Pinto Added copy and free funtion pointers for keys and values: * 08/01/2014 Massimiliano Pinto Added copy and free funtion pointers for keys and values:
* it's possible to copy and free different data types via * it's possible to copy and free different data types via
* kcopyfn/kfreefn, vcopyfn/vfreefn * kcopyfn/kfreefn, vcopyfn/vfreefn
* 06/02/2015 Mark Riddoch Addition of hashtable_save and hashtable_load
* *
* @endverbatim * @endverbatim
*/ */
@ -227,7 +232,7 @@ hashtable_memory_fns(
int int
hashtable_add(HASHTABLE *table, void *key, void *value) hashtable_add(HASHTABLE *table, void *key, void *value)
{ {
int hashkey; unsigned int hashkey;
HASHENTRIES *entry; HASHENTRIES *entry;
if (key == NULL || value == NULL) if (key == NULL || value == NULL)
@ -303,7 +308,7 @@ hashtable_add(HASHTABLE *table, void *key, void *value)
int int
hashtable_delete(HASHTABLE *table, void *key) hashtable_delete(HASHTABLE *table, void *key)
{ {
int hashkey = table->hashfn(key) % table->hashsize; unsigned int hashkey = table->hashfn(key) % table->hashsize;
HASHENTRIES *entry, *ptr; HASHENTRIES *entry, *ptr;
hashtable_write_lock(table); hashtable_write_lock(table);
@ -364,7 +369,7 @@ HASHENTRIES *entry, *ptr;
void * void *
hashtable_fetch(HASHTABLE *table, void *key) hashtable_fetch(HASHTABLE *table, void *key)
{ {
int hashkey = table->hashfn(key) % table->hashsize; unsigned int hashkey = table->hashfn(key) % table->hashsize;
HASHENTRIES *entry; HASHENTRIES *entry;
hashtable_read_lock(table); hashtable_read_lock(table);
@ -637,3 +642,109 @@ hashtable_iterator_free(HASHITERATOR *iter)
{ {
free(iter); free(iter);
} }
/**
* Save a hashtable to disk
*
* @param table Hashtable to save
* @param filename Filename to write hashtable into
* @param keywrite Pointer to function that writes a single key
* @param valuewrite Pointer to function that writes a single value
* @return Number of entries written or -1 on error
*/
int
hashtable_save(HASHTABLE *table, char *filename,
int (*keywrite)(int, void*),
int (*valuewrite)(int, void*))
{
int fd, rval = 0;
HASHITERATOR *iter;
void *key, *value;
if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
{
return -1;
}
if (write(fd, "HASHTABLE", 7) != 7) // Magic number
{
close(fd);
return -1;
}
write(fd, &rval, sizeof(rval)); // Write zero counter, will be overrwriten at end
if ((iter = hashtable_iterator(table)) != NULL)
{
while ((key = hashtable_next(iter)) != NULL)
{
if (!(*keywrite)(fd, key))
{
close(fd);
return -1;
}
if ((value = hashtable_fetch(table, key)) == NULL ||
(*valuewrite)(fd, value) == 0)
{
close(fd);
return -1;
}
rval++;
}
}
/* Now go back and write the count of entries */
lseek(fd, 7L, SEEK_SET);
write(fd, &rval, sizeof(rval));
close(fd);
return rval;
}
/**
* Load a hashtable from disk
*
* @param table Hashtable to load
* @param filename Filename to read hashtable from
* @param keyread Pointer to function that reads a single key
* @param valueread Pointer to function that reads a single value
* @return Number of entries read or -1 on error
*/
int
hashtable_load(HASHTABLE *table, char *filename,
void *(*keyread)(int),
void *(*valueread)(int))
{
int fd, count, rval = 0;
void *key, *value;
char buf[40];
if ((fd = open(filename, O_RDONLY)) == -1)
{
return -1;
}
if (read(fd, buf, 7) != 7)
{
close(fd);
return -1;
}
if (strncmp(buf, "HASHTABLE", 7) != 0)
{
close(fd);
return -1;
}
if (read(fd, &count, sizeof(count)) != sizeof(count))
{
close(fd);
return -1;
}
while (count--)
{
key = keyread(fd);
value = valueread(fd);
if (key == NULL || value == NULL)
break;
hashtable_add(table, key, value);
rval++;
}
close(fd);
return rval;
}

View File

@ -33,6 +33,7 @@
* 29/05/14 Mark Riddoch Filter API implementation * 29/05/14 Mark Riddoch Filter API implementation
* 09/09/14 Massimiliano Pinto Added service option for localhost authentication * 09/09/14 Massimiliano Pinto Added service option for localhost authentication
* 13/10/14 Massimiliano Pinto Added hashtable for resources (i.e database names for MySQL services) * 13/10/14 Massimiliano Pinto Added hashtable for resources (i.e database names for MySQL services)
* 06/02/15 Mark Riddoch Added caching of authentication data
* *
* @endverbatim * @endverbatim
*/ */
@ -54,6 +55,8 @@
#include <poll.h> #include <poll.h>
#include <skygw_utils.h> #include <skygw_utils.h>
#include <log_manager.h> #include <log_manager.h>
#include <sys/stat.h>
#include <sys/types.h>
/** Defined in log_manager.cc */ /** Defined in log_manager.cc */
extern int lm_enabled_logfiles_bitmask; extern int lm_enabled_logfiles_bitmask;
@ -215,7 +218,63 @@ GWPROTOCOL *funcs;
(port->address == NULL ? "0.0.0.0" : port->address), (port->address == NULL ? "0.0.0.0" : port->address),
port->port, port->port,
service->name))); service->name)));
{
/* Try loading authentication data from file cache */
char *ptr, path[4096];
strcpy(path, "/usr/local/skysql/MaxScale");
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
{
strncpy(path, ptr, 4096);
}
strncat(path, "/", 4096);
strncat(path, service->name, 4096);
strncat(path, "/.cache/dbusers", 4096);
loaded = dbusers_load(service->users, path);
if (loaded != -1)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Using cached credential information.")));
}
}
if (loaded == -1)
{
hashtable_free(service->users->data);
free(service->users);
dcb_free(port->listener);
port->listener = NULL;
goto retblock;
}
} }
else
{
/* Save authentication data to file cache */
char *ptr, path[4096];
strcpy(path, "/usr/local/skysql/MaxScale");
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
{
strncpy(path, ptr, 4096);
}
strncat(path, "/", 4096);
strncat(path, service->name, 4096);
if (access(path, R_OK) == -1)
mkdir(path, 0777);
strncat(path, "/.cache", 4096);
if (access(path, R_OK) == -1)
mkdir(path, 0777);
strncat(path, "/dbusers", 4096);
dbusers_save(service->users, path);
}
if (loaded == 0)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Service %s: failed to load any user "
"information. Authentication will "
"probably fail as a result.")));
}
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier. /* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
* This way MaxScale could try reloading users' just after startup * This way MaxScale could try reloading users' just after startup
*/ */
@ -823,9 +882,9 @@ struct tm result;
char time_buf[30]; char time_buf[30];
int i; int i;
printf("Service %p\n", service); printf("Service %p\n", (void *)service);
printf("\tService: %s\n", service->name); printf("\tService: %s\n", service->name);
printf("\tRouter: %s (%p)\n", service->routerModule, service->router); printf("\tRouter: %s (%p)\n", service->routerModule, (void *)service->router);
printf("\tStarted: %s", printf("\tStarted: %s",
asctime_r(localtime_r(&service->stats.started, &result), time_buf)); asctime_r(localtime_r(&service->stats.started, &result), time_buf));
printf("\tBackend databases\n"); printf("\tBackend databases\n");
@ -844,7 +903,7 @@ int i;
} }
printf("\n"); printf("\n");
} }
printf("\tUsers data: %p\n", service->users); printf("\tUsers data: %p\n", (void *)service->users);
printf("\tTotal connections: %d\n", service->stats.n_sessions); printf("\tTotal connections: %d\n", service->stats.n_sessions);
printf("\tCurrently connected: %d\n", service->stats.n_current); printf("\tCurrently connected: %d\n", service->stats.n_current);
} }

View File

@ -65,4 +65,6 @@ extern int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host,
extern USERS *mysql_users_alloc(); extern USERS *mysql_users_alloc();
extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key); extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
extern int replace_mysql_users(SERVICE *service); extern int replace_mysql_users(SERVICE *service);
extern int dbusers_save(USERS *, char *);
extern int dbusers_load(USERS *, char *);
#endif #endif

View File

@ -264,6 +264,7 @@ typedef struct dcb {
unsigned int high_water; /**< High water mark */ unsigned int high_water; /**< High water mark */
unsigned int low_water; /**< Low water mark */ unsigned int low_water; /**< Low water mark */
struct server *server; /**< The associated backend server */
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
int dcb_port; /**< port of target server */ int dcb_port; /**< port of target server */
skygw_chk_t dcb_chk_tail; skygw_chk_t dcb_chk_tail;

View File

@ -112,6 +112,14 @@ void hashtable_get_stats(
int* hashsize, int* hashsize,
int* nelems, int* nelems,
int* longest); int* longest);
extern int hashtable_save(HASHTABLE *,
char *,
int (*keywrite)(int, void*),
int (*valuewrite)(int, void*));
extern int hashtable_load(HASHTABLE *,
char *,
void *(*keyread)(int),
void *(*valueread)(int));
extern HASHITERATOR *hashtable_iterator(HASHTABLE *); extern HASHITERATOR *hashtable_iterator(HASHTABLE *);
/**< Allocate an iterator on the hashtable */ /**< Allocate an iterator on the hashtable */

View File

@ -250,6 +250,8 @@ typedef struct router_instance {
char *user; /*< User name to use with master */ char *user; /*< User name to use with master */
char *password; /*< Password to use with master */ char *password; /*< Password to use with master */
char *fileroot; /*< Root of binlog filename */ char *fileroot; /*< Root of binlog filename */
bool master_chksum;/*< Does the master provide checksums */
char *master_uuid; /*< UUID of the master */
DCB *master; /*< DCB for master connection */ DCB *master; /*< DCB for master connection */
DCB *client; /*< DCB for dummy client */ DCB *client; /*< DCB for dummy client */
SESSION *session; /*< Fake session for master connection */ SESSION *session; /*< Fake session for master connection */
@ -436,7 +438,7 @@ extern void blr_master_reconnect(ROUTER_INSTANCE *);
extern int blr_master_connected(ROUTER_INSTANCE *); extern int blr_master_connected(ROUTER_INSTANCE *);
extern int blr_slave_request(ROUTER_INSTANCE *, ROUTER_SLAVE *, GWBUF *); extern int blr_slave_request(ROUTER_INSTANCE *, ROUTER_SLAVE *, GWBUF *);
extern void blr_slave_rotate(ROUTER_SLAVE *slave, uint8_t *ptr); extern void blr_slave_rotate(ROUTER_INSTANCE *, ROUTER_SLAVE *, uint8_t *);
extern int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large); extern int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large);
extern void blr_init_cache(ROUTER_INSTANCE *); extern void blr_init_cache(ROUTER_INSTANCE *);
@ -448,4 +450,8 @@ extern BLFILE *blr_open_binlog(ROUTER_INSTANCE *, char *);
extern GWBUF *blr_read_binlog(ROUTER_INSTANCE *, BLFILE *, unsigned int, REP_HEADER *); extern GWBUF *blr_read_binlog(ROUTER_INSTANCE *, BLFILE *, unsigned int, REP_HEADER *);
extern void blr_close_binlog(ROUTER_INSTANCE *, BLFILE *); extern void blr_close_binlog(ROUTER_INSTANCE *, BLFILE *);
extern unsigned long blr_file_size(BLFILE *); extern unsigned long blr_file_size(BLFILE *);
extern int blr_statistics(ROUTER_INSTANCE *, ROUTER_SLAVE *, GWBUF *);
extern int blr_ping(ROUTER_INSTANCE *, ROUTER_SLAVE *, GWBUF *);
extern int blr_send_custom_error(DCB *, int, int, char *);
extern int blr_file_next_exists(ROUTER_INSTANCE *, ROUTER_SLAVE *);
#endif #endif

View File

@ -568,6 +568,9 @@ int gw_read_client_event(
GWBUF *read_buffer = NULL; GWBUF *read_buffer = NULL;
int rc = 0; int rc = 0;
int nbytes_read = 0; int nbytes_read = 0;
uint8_t cap = 0;
bool stmt_input; /*< router input type */
CHK_DCB(dcb); CHK_DCB(dcb);
protocol = DCB_PROTOCOL(dcb, MySQLProtocol); protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
CHK_PROTOCOL(protocol); CHK_PROTOCOL(protocol);
@ -583,54 +586,132 @@ int gw_read_client_event(
{ {
goto return_rc; goto return_rc;
} }
/**
* if read queue existed appent read to it.
* if length of read buffer is less than 3 or less than mysql packet
* then return.
* else copy mysql packets to separate buffers from read buffer and
* continue.
* else
* if read queue didn't exist, length of read is less than 3 or less
* than mysql packet then
* create read queue and append to it and return.
* if length read is less than mysql packet length append to read queue
* append to it and return.
* else (complete packet was read) continue.
*/
if (dcb->dcb_readqueue)
{
uint8_t* data;
dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer);
nbytes_read = gwbuf_length(dcb->dcb_readqueue);
data = (uint8_t *)GWBUF_DATA(dcb->dcb_readqueue);
if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data))
{
rc = 0;
goto return_rc;
}
else
{
/**
* There is at least one complete mysql packet in
* read_buffer.
*/
read_buffer = dcb->dcb_readqueue;
dcb->dcb_readqueue = NULL;
}
}
else
{
uint8_t* data = (uint8_t *)GWBUF_DATA(read_buffer);
if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data)+4) session = dcb->session;
{
if (protocol->protocol_auth_state == MYSQL_IDLE && session != NULL)
{
CHK_SESSION(session);
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
ss_dassert(rsession != NULL);
if (router_instance != NULL && rsession != NULL) {
/** Ask what type of input the router expects */
cap = router->getCapabilities(router_instance, rsession);
if (cap == 0 || (cap == RCAP_TYPE_PACKET_INPUT))
{
stmt_input = false;
}
else if (cap == RCAP_TYPE_STMT_INPUT)
{
stmt_input = true;
/** Mark buffer to as MySQL type */
gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL);
}
/**
* If router doesn't implement getCapabilities correctly we end
* up here.
*/
else
{
GWBUF* errbuf;
bool succp;
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] Reading router "
"capabilities failed.",
pthread_self())));
errbuf = mysql_create_custom_error(
1,
0,
"Read invalid router capabilities. Routing failed. "
"Session will be closed.");
router->handleError(
router_instance,
rsession,
errbuf,
dcb,
ERRACT_REPLY_CLIENT,
&succp);
gwbuf_free(errbuf);
/**
* If there are not enough backends close
* session
*/
if (!succp)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Routing the query failed. "
"Session will be closed.")));
dcb_close(dcb);
}
rc = 1;
goto return_rc;
}
}
}
if (stmt_input) {
/**
* if read queue existed appent read to it.
* if length of read buffer is less than 3 or less than mysql packet
* then return.
* else copy mysql packets to separate buffers from read buffer and
* continue.
* else
* if read queue didn't exist, length of read is less than 3 or less
* than mysql packet then
* create read queue and append to it and return.
* if length read is less than mysql packet length append to read queue
* append to it and return.
* else (complete packet was read) continue.
*/
if (dcb->dcb_readqueue)
{
uint8_t* data;
dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer);
rc = 0; nbytes_read = gwbuf_length(dcb->dcb_readqueue);
goto return_rc; data = (uint8_t *)GWBUF_DATA(dcb->dcb_readqueue);
}
} if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data))
{
rc = 0;
goto return_rc;
}
else
{
/**
* There is at least one complete mysql packet in
* read_buffer.
*/
read_buffer = dcb->dcb_readqueue;
dcb->dcb_readqueue = NULL;
}
}
else
{
uint8_t* data = (uint8_t *)GWBUF_DATA(read_buffer);
if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data)+4)
{
dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer);
rc = 0;
goto return_rc;
}
}
}
/** /**
* Now there should be at least one complete mysql packet in read_buffer. * Now there should be at least one complete mysql packet in read_buffer.
@ -738,86 +819,19 @@ int gw_read_client_event(
case MYSQL_IDLE: case MYSQL_IDLE:
{ {
uint8_t cap = 0;
uint8_t* payload = NULL; uint8_t* payload = NULL;
bool stmt_input; /*< router input type */
ss_dassert(nbytes_read >= 5);
session = dcb->session; session = dcb->session;
ss_dassert(session!= NULL); ss_dassert(session!= NULL);
if (session != NULL) if (session != NULL)
{ {
CHK_SESSION(session); CHK_SESSION(session);
router = session->service->router;
router_instance =
session->service->router_instance;
rsession = session->router_session;
ss_dassert(rsession != NULL);
} }
/* Now, we are assuming in the first buffer there is /* Now, we are assuming in the first buffer there is
* the information form mysql command */ * the information form mysql command */
payload = GWBUF_DATA(read_buffer); payload = GWBUF_DATA(read_buffer);
/** Ask what type of input the router expects */
cap = router->getCapabilities(router_instance, rsession);
if (cap == 0 || (cap == RCAP_TYPE_PACKET_INPUT))
{
stmt_input = false;
}
else if (cap == RCAP_TYPE_STMT_INPUT)
{
stmt_input = true;
/** Mark buffer to as MySQL type */
gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL);
}
/**
* If router doesn't implement getCapabilities correctly we end
* up here.
*/
else
{
GWBUF* errbuf;
bool succp;
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] Reading router "
"capabilities failed.",
pthread_self())));
errbuf = mysql_create_custom_error(
1,
0,
"Read invalid router capabilities. Routing failed. "
"Session will be closed.");
router->handleError(
router_instance,
rsession,
errbuf,
dcb,
ERRACT_REPLY_CLIENT,
&succp);
gwbuf_free(errbuf);
/**
* If there are not enough backends close
* session
*/
if (!succp)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Routing the query failed. "
"Session will be closed.")));
dcb_close(dcb);
}
rc = 1;
goto return_rc;
}
/** Route COM_QUIT to backend */ /** Route COM_QUIT to backend */
if (MYSQL_IS_COM_QUIT(payload)) if (MYSQL_IS_COM_QUIT(payload))
{ {

View File

@ -220,6 +220,23 @@ int gw_read_backend_handshake(
"from backend. Error code: %d, Msg : %s", "from backend. Error code: %d, Msg : %s",
errcode, errcode,
bufstr))); bufstr)));
/**
* If ER_HOST_IS_BLOCKED is found
* the related server is put in maintenace mode
* This will avoid filling the error log.
*/
if (errcode == 1129) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Server %s has been put into maintenance mode due to the server blocking connections from MaxScale. Run 'mysqladmin -h %s -P %d flush-hosts' on this server before taking this server out of maintenance mode.",
dcb->server->unique_name,
dcb->server->name,
dcb->server->port)));
server_set_status(dcb->server, SERVER_MAINT);
}
free(bufstr); free(bufstr);
} }

View File

@ -181,6 +181,8 @@ unsigned char *defuuid;
spinlock_init(&inst->binlog_lock); spinlock_init(&inst->binlog_lock);
inst->binlog_fd = -1; inst->binlog_fd = -1;
inst->master_chksum = true;
inst->master_uuid = NULL;
inst->low_water = DEF_LOW_WATER; inst->low_water = DEF_LOW_WATER;
inst->high_water = DEF_HIGH_WATER; inst->high_water = DEF_HIGH_WATER;

View File

@ -62,7 +62,7 @@ static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr);
/** /**
* Initialise the binlog file for this instance. MaxScale will look * Initialise the binlog file for this instance. MaxScale will look
* for all the binlogs that it has on local disk, determien the next * for all the binlogs that it has on local disk, determine the next
* binlog to use and initialise it for writing, determining the * binlog to use and initialise it for writing, determining the
* next record to be fetched from the real master. * next record to be fetched from the real master.
* *
@ -354,6 +354,10 @@ int n;
unsigned long filelen = 0; unsigned long filelen = 0;
struct stat statb; struct stat statb;
if (!file)
{
return NULL;
}
if (fstat(file->fd, &statb) == 0) if (fstat(file->fd, &statb) == 0)
filelen = statb.st_size; filelen = statb.st_size;
if (pos >= filelen) if (pos >= filelen)
@ -695,3 +699,26 @@ GWBUF *buf;
close(fd); close(fd);
return buf; return buf;
} }
/**
* Does the next binlog file in the sequence for the slave exist.
*
* @param router The router instance
* @param slave The slave in question
* @retuen 0 if the next file does not exist
*/
int
blr_file_next_exists(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave)
{
char *sptr, buf[80], bigbuf[4096];
int filenum;
if ((sptr = strrchr(slave->binlogfile, '.')) == NULL)
return 0;
filenum = atoi(sptr + 1);
sprintf(buf, BINLOG_NAMEFMT, router->fileroot, filenum + 1);
sprintf(bigbuf, "%s/%s", router->binlogdir, buf);
if (access(bigbuf, R_OK) == -1)
return 0;
return 1;
}

View File

@ -79,6 +79,8 @@ void blr_extract_header(uint8_t *pkt, REP_HEADER *hdr);
inline uint32_t extract_field(uint8_t *src, int bits); inline uint32_t extract_field(uint8_t *src, int bits);
static void blr_log_packet(logfile_id_t file, char *msg, uint8_t *ptr, int len); static void blr_log_packet(logfile_id_t file, char *msg, uint8_t *ptr, int len);
static void blr_master_close(ROUTER_INSTANCE *); static void blr_master_close(ROUTER_INSTANCE *);
static char *blr_extract_column(GWBUF *buf, int col);
static int keepalive = 1; static int keepalive = 1;
/** /**
@ -384,6 +386,9 @@ char query[128];
router->retry_backoff = 1; router->retry_backoff = 1;
break; break;
case BLRM_SERVERID: case BLRM_SERVERID:
{
char *val = blr_extract_column(buf, 1);
// Response to fetch of master's server-id // Response to fetch of master's server-id
if (router->saved_master.server_id) if (router->saved_master.server_id)
GWBUF_CONSUME_ALL(router->saved_master.server_id); GWBUF_CONSUME_ALL(router->saved_master.server_id);
@ -398,6 +403,7 @@ char query[128];
router->master_state = BLRM_HBPERIOD; router->master_state = BLRM_HBPERIOD;
router->master->func.write(router->master, buf); router->master->func.write(router->master, buf);
break; break;
}
case BLRM_HBPERIOD: case BLRM_HBPERIOD:
// Response to set the heartbeat period // Response to set the heartbeat period
if (router->saved_master.heartbeat) if (router->saved_master.heartbeat)
@ -419,6 +425,15 @@ char query[128];
router->master->func.write(router->master, buf); router->master->func.write(router->master, buf);
break; break;
case BLRM_CHKSUM2: case BLRM_CHKSUM2:
{
char *val = blr_extract_column(buf, 1);
if (val && strncasecmp(val, "NONE", 4) == 0)
{
router->master_chksum = false;
}
if (val)
free(val);
// Response to the master_binlog_checksum, should be stored // Response to the master_binlog_checksum, should be stored
if (router->saved_master.chksum2) if (router->saved_master.chksum2)
GWBUF_CONSUME_ALL(router->saved_master.chksum2); GWBUF_CONSUME_ALL(router->saved_master.chksum2);
@ -428,6 +443,7 @@ char query[128];
router->master_state = BLRM_GTIDMODE; router->master_state = BLRM_GTIDMODE;
router->master->func.write(router->master, buf); router->master->func.write(router->master, buf);
break; break;
}
case BLRM_GTIDMODE: case BLRM_GTIDMODE:
// Response to the GTID_MODE, should be stored // Response to the GTID_MODE, should be stored
if (router->saved_master.gtid_mode) if (router->saved_master.gtid_mode)
@ -439,6 +455,10 @@ char query[128];
router->master->func.write(router->master, buf); router->master->func.write(router->master, buf);
break; break;
case BLRM_MUUID: case BLRM_MUUID:
{
char *val = blr_extract_column(buf, 1);
router->master_uuid = val;
// Response to the SERVER_UUID, should be stored // Response to the SERVER_UUID, should be stored
if (router->saved_master.uuid) if (router->saved_master.uuid)
GWBUF_CONSUME_ALL(router->saved_master.uuid); GWBUF_CONSUME_ALL(router->saved_master.uuid);
@ -449,6 +469,7 @@ char query[128];
router->master_state = BLRM_SUUID; router->master_state = BLRM_SUUID;
router->master->func.write(router->master, buf); router->master->func.write(router->master, buf);
break; break;
}
case BLRM_SUUID: case BLRM_SUUID:
// Response to the SET @server_uuid, should be stored // Response to the SET @server_uuid, should be stored
if (router->saved_master.setslaveuuid) if (router->saved_master.setslaveuuid)
@ -874,30 +895,33 @@ static REP_HEADER phdr;
* First check that the checksum we calculate matches the * First check that the checksum we calculate matches the
* checksum in the packet we received. * checksum in the packet we received.
*/ */
uint32_t chksum, pktsum; if (router->master_chksum)
chksum = crc32(0L, NULL, 0);
chksum = crc32(chksum, ptr + 5, hdr.event_size - 4);
pktsum = EXTRACT32(ptr + hdr.event_size + 1);
if (pktsum != chksum)
{ {
router->stats.n_badcrc++; uint32_t chksum, pktsum;
if (msg)
chksum = crc32(0L, NULL, 0);
chksum = crc32(chksum, ptr + 5, hdr.event_size - 4);
pktsum = EXTRACT32(ptr + hdr.event_size + 1);
if (pktsum != chksum)
{ {
free(msg); router->stats.n_badcrc++;
msg = NULL; if (msg)
{
free(msg);
msg = NULL;
}
LOGIF(LE,(skygw_log_write(LOGFILE_ERROR,
"%s: Checksum error in event "
"from master, "
"binlog %s @ %d. "
"Closing master connection.",
router->service->name,
router->binlog_name,
router->binlog_position)));
blr_master_close(router);
blr_master_delayed_connect(router);
return;
} }
LOGIF(LE,(skygw_log_write(LOGFILE_ERROR,
"%s: Checksum error in event "
"from master, "
"binlog %s @ %d. "
"Closing master connection.",
router->service->name,
router->binlog_name,
router->binlog_position)));
blr_master_close(router);
blr_master_delayed_connect(router);
return;
} }
router->stats.n_binlogs++; router->stats.n_binlogs++;
router->lastEventReceived = hdr.event_type; router->lastEventReceived = hdr.event_type;
@ -1145,6 +1169,8 @@ char file[BINLOG_FNAMELEN+1];
pos <<= 32; pos <<= 32;
pos |= extract_field(ptr, 32); pos |= extract_field(ptr, 32);
slen = len - (8 + 4); // Allow for position and CRC slen = len - (8 + 4); // Allow for position and CRC
if (router->master_chksum == 0)
slen += 4;
if (slen > BINLOG_FNAMELEN) if (slen > BINLOG_FNAMELEN)
slen = BINLOG_FNAMELEN; slen = BINLOG_FNAMELEN;
memcpy(file, ptr + 8, slen); memcpy(file, ptr + 8, slen);
@ -1274,7 +1300,7 @@ int action;
memcpy(buf, ptr, hdr->event_size); memcpy(buf, ptr, hdr->event_size);
if (hdr->event_type == ROTATE_EVENT) if (hdr->event_type == ROTATE_EVENT)
{ {
blr_slave_rotate(slave, ptr); blr_slave_rotate(router, slave, ptr);
} }
slave->stats.n_bytes += gwbuf_length(pkt); slave->stats.n_bytes += gwbuf_length(pkt);
slave->dcb->func.write(slave->dcb, pkt); slave->dcb->func.write(slave->dcb, pkt);
@ -1398,3 +1424,59 @@ blr_master_connected(ROUTER_INSTANCE *router)
{ {
return router->master_state == BLRM_BINLOGDUMP; return router->master_state == BLRM_BINLOGDUMP;
} }
/**
* Extract a result value from the set of messages that make up a
* MySQL response packet.
*
* @param buf The GWBUF containing the response
* @param col The column number to return
* @return The result form the column or NULL. The caller must free the result
*/
static char *
blr_extract_column(GWBUF *buf, int col)
{
uint8_t *ptr;
int len, ncol, collen;
char *rval;
ptr = (uint8_t *)GWBUF_DATA(buf);
/* First packet should be the column count */
len = EXTRACT24(ptr);
ptr += 3;
if (*ptr != 1) // Check sequence number is 1
return NULL;
ptr++;
ncol = *ptr++;
if (ncol < col) // Not that many column in result
return NULL;
// Now ptr points at the column definition
while (ncol-- > 0)
{
len = EXTRACT24(ptr);
ptr += 4; // Skip to payload
ptr += len; // Skip over payload
}
// Now we should have an EOF packet
len = EXTRACT24(ptr);
ptr += 4; // Skip to payload
if (*ptr != 0xfe)
return NULL;
ptr += len;
// Finally we have reached the row
len = EXTRACT24(ptr);
ptr += 4;
while (--col > 0)
{
collen = *ptr++;
ptr += collen;
}
collen = *ptr++;
if ((rval = malloc(collen + 1)) == NULL)
return NULL;
memcpy(rval, ptr, collen);
rval[collen] = 0; // NULL terminate
return rval;
}

View File

@ -614,7 +614,7 @@ int len, file_len;
sprintf(file, "%s", router->binlog_name); sprintf(file, "%s", router->binlog_name);
file_len = strlen(file); file_len = strlen(file);
sprintf(position, "%d", router->binlog_position); sprintf(position, "%ld", router->binlog_position);
len = 5 + file_len + strlen(position) + 1 + 3; len = 5 + file_len + strlen(position) + 1 + 3;
if ((pkt = gwbuf_alloc(len)) == NULL) if ((pkt = gwbuf_alloc(len)) == NULL)
return 0; return 0;
@ -859,7 +859,8 @@ int len, actual_len, col_len, seqno, ncols, i;
*ptr++ = 0; *ptr++ = 0;
sprintf(column, "%s", router->uuid); sprintf(column, "%s", router->master_uuid ?
router->master_uuid : router->uuid);
col_len = strlen(column); col_len = strlen(column);
*ptr++ = col_len; // Length of result string *ptr++ = col_len; // Length of result string
strncpy((char *)ptr, column, col_len); // Result string strncpy((char *)ptr, column, col_len); // Result string
@ -1078,6 +1079,15 @@ uint32_t chksum;
ptr = GWBUF_DATA(queue); ptr = GWBUF_DATA(queue);
len = extract_field(ptr, 24); len = extract_field(ptr, 24);
binlognamelen = len - 11; binlognamelen = len - 11;
if (binlognamelen > BINLOG_FNAMELEN)
{
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"blr_slave_binlog_dump truncating binlog filename "
"from %d to %d",
binlognamelen, BINLOG_FNAMELEN)));
binlognamelen = BINLOG_FNAMELEN;
}
ptr += 4; // Skip length and sequence number ptr += 4; // Skip length and sequence number
if (*ptr++ != COM_BINLOG_DUMP) if (*ptr++ != COM_BINLOG_DUMP)
{ {
@ -1097,6 +1107,13 @@ uint32_t chksum;
strncpy(slave->binlogfile, (char *)ptr, binlognamelen); strncpy(slave->binlogfile, (char *)ptr, binlognamelen);
slave->binlogfile[binlognamelen] = 0; slave->binlogfile[binlognamelen] = 0;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%s: COM_BINLOG_DUMP: binlog name '%s', length %d, "
"from position %d.", router->service->name,
slave->binlogfile, binlognamelen,
slave->binlog_pos)));
slave->seqno = 1; slave->seqno = 1;
@ -1333,7 +1350,7 @@ unsigned long beat1 = hkheartbeat;
if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR, "blr_close_binlog took %d beats", LOGFILE_ERROR, "blr_close_binlog took %d beats",
hkheartbeat - beat1))); hkheartbeat - beat1)));
blr_slave_rotate(slave, GWBUF_DATA(record)); blr_slave_rotate(router, slave, GWBUF_DATA(record));
beat1 = hkheartbeat; beat1 = hkheartbeat;
if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL)
{ {
@ -1440,7 +1457,8 @@ if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
if (slave->binlog_pos >= blr_file_size(slave->file) if (slave->binlog_pos >= blr_file_size(slave->file)
&& router->rotating == 0 && router->rotating == 0
&& strcmp(router->binlog_name, slave->binlogfile) != 0 && strcmp(router->binlog_name, slave->binlogfile) != 0
&& blr_master_connected(router)) && (blr_master_connected(router)
|| blr_file_next_exists(router, slave)))
{ {
/* We may have reached the end of file of a non-current /* We may have reached the end of file of a non-current
* binlog file. * binlog file.
@ -1470,7 +1488,7 @@ if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write(
dcb_close(slave->dcb); dcb_close(slave->dcb);
} }
} }
else else if (blr_master_connected(router))
{ {
spinlock_acquire(&slave->catch_lock); spinlock_acquire(&slave->catch_lock);
slave->cstate |= CS_EXPECTCB; slave->cstate |= CS_EXPECTCB;
@ -1537,11 +1555,13 @@ ROUTER_INSTANCE *router = slave->router;
* @param ptr The rotate event (minus header and OK byte) * @param ptr The rotate event (minus header and OK byte)
*/ */
void void
blr_slave_rotate(ROUTER_SLAVE *slave, uint8_t *ptr) blr_slave_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, uint8_t *ptr)
{ {
int len = EXTRACT24(ptr + 9); // Extract the event length int len = EXTRACT24(ptr + 9); // Extract the event length
len = len - (19 + 8 + 4); // Remove length of header, checksum and position len = len - (19 + 8); // Remove length of header and position
if (router->master_chksum)
len -= 4;
if (len > BINLOG_FNAMELEN) if (len > BINLOG_FNAMELEN)
len = BINLOG_FNAMELEN; len = BINLOG_FNAMELEN;
ptr += 19; // Skip header ptr += 19; // Skip header

View File

@ -325,7 +325,7 @@ static int hashkeyfun(
while((c = *ptr++)){ while((c = *ptr++)){
hash = c + (hash << 6) + (hash << 16) - hash; hash = c + (hash << 6) + (hash << 16) - hash;
} }
return *(int *)key; return hash;
} }
static int hashcmpfun( static int hashcmpfun(