Merge branch 'develop' into plainrouter

This commit is contained in:
Markus Makela
2015-03-31 12:56:29 +03:00
18 changed files with 779 additions and 309 deletions

View File

@ -14,8 +14,6 @@ This section describes the limitations that are common to all configuration of p
Both capabilities are not included in MySQL server handshake Both capabilities are not included in MySQL server handshake
* LOAD DATA LOCAL INFILE currently not supported
## Limitations with MySQL Master/Slave Replication monitoring ## Limitations with MySQL Master/Slave Replication monitoring
## Limitations with Galera Cluster Monitoring ## Limitations with Galera Cluster Monitoring

View File

@ -10,7 +10,6 @@
- [Limitations](About/Limitations.md) - [Limitations](About/Limitations.md)
- [COPYRIGHT](About/COPYRIGHT.md) - [COPYRIGHT](About/COPYRIGHT.md)
- [LICENSE](About/LICENSE.md) - [LICENSE](About/LICENSE.md)
- [SETUP](About/SETUP.md)
## Getting Started ## Getting Started
@ -18,6 +17,10 @@
- [Building MaxScale from Source Code](Getting-Started/Building-MaxScale-from-Source-Code.md) - [Building MaxScale from Source Code](Getting-Started/Building-MaxScale-from-Source-Code.md)
- [Configuration Guide](Getting-Started/Configuration-Guide.md) - [Configuration Guide](Getting-Started/Configuration-Guide.md)
## Upgrading MaxScale
- [Upgrading MaxScale to 1.1.0](Upgrading-To-MaxScale-1.1.0.md)
## Reference ## Reference
- [MaxAdmin](Reference/MaxAdmin.md) - [MaxAdmin](Reference/MaxAdmin.md)
@ -37,6 +40,7 @@
- [MySQL Cluster Setup](Tutorials/MySQL-Cluster-Setup.md) - [MySQL Cluster Setup](Tutorials/MySQL-Cluster-Setup.md)
- [Replication Proxy with the Binlog Router Tutorial](Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md) - [Replication Proxy with the Binlog Router Tutorial](Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md)
- [RabbitMQ Setup and MaxScale Integration Tutorial](Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md) - [RabbitMQ Setup and MaxScale Integration Tutorial](Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md)
- [Nagios Plugins for MaxScale Tutorial](Tutorials/Nagios-Plugins.md)
## Filters ## Filters

View File

@ -225,7 +225,7 @@ Other useful targets for Make are `documentation`, which generates the Doxygen d
MaxScale has a core test suite for internal components and an extended suite of test for modules. To run the core tests, run `make testcore`. This will test the core maxscale executable. MaxScale has a core test suite for internal components and an extended suite of test for modules. To run the core tests, run `make testcore`. This will test the core maxscale executable.
To run `make testall`, the full test suite, you need to have four mysqld servers running on localhost. It assumes a master-slave replication setup with one slave and three slaves. To run `make testall`, the full test suite, you need to have four mysqld servers running on localhost. It assumes a master-slave replication setup with one master and three slaves.
The ports to which these servers are listening and the credentials to use for testing can be specified in the `macros.cmake` file found in the root source folder. The ports to which these servers are listening and the credentials to use for testing can be specified in the `macros.cmake` file found in the root source folder.

View File

@ -1,11 +1,11 @@
#Database Firewall filter #Database Firewall filter
## Overview ## Overview
The database firewall filter is used to block queries that match a set of rules. It can be used to prevent harmful queries into the database or to limit the access to the database based on a more defined set of rules compared to the traditional GRANT-based rights management. The database firewall filter is used to block queries that match a set of rules. It can be used to prevent harmful queries from reaching the backend database instances or to limit access to the database based on a more flexible set of rules compared to the traditional GRANT-based privilege system.
## Configuration ## Configuration
The database firewall filter only requires a minimal set of configurations in the MaxScale.cnf file. The actual rules of the database firewall filter are located in a separate text file. The following is an example of a database firewall filter configuration in the MaxScale.cnf file. The database firewall filter only requires minimal configuration in the MaxScale.cnf file. The actual rules of the database firewall filter are located in a separate text file. The following is an example of a database firewall filter configuration in MaxScale.cnf.
``` ```
[Database Firewall] [Database Firewall]
@ -24,12 +24,12 @@ The database firewall filter has one mandatory parameter that defines the locati
## Rule syntax ## Rule syntax
The rules are defined by using the following syntax. The rules are defined by using the following syntax:
``` ```
rule NAME deny [wildcard | columns VALUE ... | rule NAME deny [wildcard | columns VALUE ... |
regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF | regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF |
no_where_clause] [at_times VALUE...] [on_queries [select|update|insert|delete]]` no_where_clause] [at_times VALUE...] [on_queries [select|update|insert|delete]]
``` ```
Rules always define a blocking action so the basic mode for the database firewall filter is to allow all queries that do not match a given set of rules. Rules are identified by their name and have a mandatory part and optional parts. Rules always define a blocking action so the basic mode for the database firewall filter is to allow all queries that do not match a given set of rules. Rules are identified by their name and have a mandatory part and optional parts.
@ -40,45 +40,47 @@ The first step of defining a rule is to start with the keyword `rule` which iden
The database firewall filter's rules expect a single mandatory parameter for a rule. You can define multiple rules to cover situations where you would like to apply multiple mandatory rules to a query. The database firewall filter's rules expect a single mandatory parameter for a rule. You can define multiple rules to cover situations where you would like to apply multiple mandatory rules to a query.
#### Wildcard #### wildcard
This rule blocks all queries that use the wildcard character *. This rule blocks all queries that use the wildcard character *.
#### Columns #### columns
This rule expects a list of values after the `columns` keyword. These values are interpreted as column names and if a query targets any of these, it is blocked. This rule expects a list of values after the `columns` keyword. These values are interpreted as column names and if a query targets any of these, it is blocked.
#### Regex #### regex
This rule blocks all queries matching a regex enclosed in single or double quotes. This rule blocks all queries matching a regex enclosed in single or double quotes.
#### Limit_queries #### limit_queries
The limit_queries rule expects three parameters. The first parameter is the number of allowed queries during the time period. The second is the time period in seconds and the third is the amount of time for which the rule is considered active and blocking. The limit_queries rule expects three parameters. The first parameter is the number of allowed queries during the time period. The second is the time period in seconds and the third is the amount of time for which the rule is considered active and blocking.
#### No_where_clause #### no_where_clause
This rule inspects the query and blocks it if it has no where clause. This way you can't do a DELETE FROM ... query without having the where clause. This does not prevent wrongful usage of the where clause e.g. DELETE FROM ... WHERE 1=1. This rule inspects the query and blocks it if it has no WHERE clause. For example, this would disallow a `DELETE FROM ...` query without a `WHERE` clause. This does not prevent wrongful usage of the `WHERE` clause e.g. `DELETE FROM ... WHERE 1=1`.
### Optional rule parameters ### Optional rule parameters
Each mandatory rule accepts one or more optional parameters. These are to be defined after the mandatory part of the rule. Each mandatory rule accepts one or more optional parameters. These are to be defined after the mandatory part of the rule.
#### At_times #### at_times
This rule expects a list of time ranges that define the times when the rule in question is active. The time formats are expected to be ISO-8601 compliant and to be separated by a single dash (the - character). For example defining the active period of a rule to be 17:00 to 19:00 you would add `at times 17:00:00-19:00:00` to the end of the rule. This rule expects a list of time ranges that define the times when the rule in question is active. The time formats are expected to be ISO-8601 compliant and to be separated by a single dash (the - character). For example, to define the active period of a rule to be 5pm to 7pm, you would include `at times 17:00:00-19:00:00` in the rule definition.
#### On_queries #### on_queries
This limits the rule to be active only on certain types of queries. This limits the rule to be active only on certain types of queries.
### Applying rules to users ### Applying rules to users
To apply the defined rules to users use the following syntax. The `users` directive defines the users to which the rule should be applied.
`users NAME ... match [any|all|strict_all] rules RULE ...` `users NAME ... match [any|all|strict_all] rules RULE [,...]`
The first keyword is users which identifies this line as a user definition line. After this a list of user names and network addresses in the format `user@0.0.0.0` is expected. The first part is the user name and the second part is the network address. You can use the `%` character as the wildcard to enable user name matching from any address or network matching for all users. After the list of users and networks the keyword match is expected. The first keyword is `users`, which identifies this line as a user definition line.
The second component is a list of user names and network addresses in the format *`user`*`@`*`0.0.0.0`*. The first part is the user name and the second part is the network address. You can use the `%` character as the wildcard to enable user name matching from any address or network matching for all users. After the list of users and networks the keyword match is expected.
After this either the keyword `any` `all` or `strict_all` is expected. This defined how the rules are matched. If `any` is used when the first rule is matched the query is considered blocked and the rest of the rules are skipped. If instead the `all` keyword is used all rules must match for the query to be blocked. The `strict_all` is the same as `all` but it checks the rules from left to right in the order they were listed. If one of these does not match, the rest of the rules are not checked. This could be usedful in situations where you would for example combine `limit_queries` and `regex` rules. By using `strict_all` you can have the `regex` rule first and the `limit_queries` rule second. This way the rule only matches if the `regex` rule matches enough times for the `limit_queries` rule to match. After this either the keyword `any` `all` or `strict_all` is expected. This defined how the rules are matched. If `any` is used when the first rule is matched the query is considered blocked and the rest of the rules are skipped. If instead the `all` keyword is used all rules must match for the query to be blocked. The `strict_all` is the same as `all` but it checks the rules from left to right in the order they were listed. If one of these does not match, the rest of the rules are not checked. This could be usedful in situations where you would for example combine `limit_queries` and `regex` rules. By using `strict_all` you can have the `regex` rule first and the `limit_queries` rule second. This way the rule only matches if the `regex` rule matches enough times for the `limit_queries` rule to match.

View File

@ -2507,6 +2507,7 @@ static bool logfile_init(
char* c; char* c;
pid_t pid = getpid(); pid_t pid = getpid();
int len = strlen(shm_pathname_prefix)+ int len = strlen(shm_pathname_prefix)+
+ strlen("maxscale.") +
get_decimal_len((size_t)pid) + 1; get_decimal_len((size_t)pid) + 1;
c = (char *)calloc(len, sizeof(char)); c = (char *)calloc(len, sizeof(char));
@ -2516,7 +2517,7 @@ static bool logfile_init(
succp = false; succp = false;
goto return_with_succp; goto return_with_succp;
} }
sprintf(c, "%s%d", shm_pathname_prefix, pid); sprintf(c, "%smaxscale.%d", shm_pathname_prefix, pid);
logfile->lf_filepath = c; logfile->lf_filepath = c;
if (mkdir(c, S_IRWXU | S_IRWXG) != 0 && if (mkdir(c, S_IRWXU | S_IRWXG) != 0 &&

View File

@ -1,3 +1,5 @@
#ifndef QUERY_CLASSIFIER_HG
#define QUERY_CLASSIFIER_HG
/* /*
This file is distributed as part of the MariaDB Corporation MaxScale. It is free This file is distributed as part of the MariaDB Corporation MaxScale. It is free
software: you can redistribute it and/or modify it under the terms of the software: you can redistribute it and/or modify it under the terms of the
@ -116,3 +118,4 @@ char** skygw_get_database_names(GWBUF* querybuf,int* size);
EXTERN_C_BLOCK_END EXTERN_C_BLOCK_END
#endif

View File

@ -312,6 +312,7 @@ int error_count = 0;
char *strip_db_esc; char *strip_db_esc;
char *weightby; char *weightby;
char *version_string; char *version_string;
char *subservices;
bool is_rwsplit = false; bool is_rwsplit = false;
bool is_schemarouter = false; bool is_schemarouter = false;
char *allow_localhost_match_wildcard_host; char *allow_localhost_match_wildcard_host;
@ -319,6 +320,7 @@ int error_count = 0;
obj->element = service_alloc(obj->object, router); obj->element = service_alloc(obj->object, router);
user = config_get_value(obj->parameters, "user"); user = config_get_value(obj->parameters, "user");
auth = config_get_value(obj->parameters, "passwd"); auth = config_get_value(obj->parameters, "passwd");
subservices = config_get_value(obj->parameters, "subservices");
enable_root_user = config_get_value( enable_root_user = config_get_value(
obj->parameters, obj->parameters,
"enable_root_user"); "enable_root_user");
@ -346,6 +348,15 @@ int error_count = 0;
version_string = config_get_value(obj->parameters, version_string = config_get_value(obj->parameters,
"version_string"); "version_string");
if(subservices)
{
service_set_param_value(obj->element,
obj->parameters,
subservices,
1,STRING_TYPE);
}
/** flag for rwsplit-specific parameters */ /** flag for rwsplit-specific parameters */
if (strncmp(router, "readwritesplit", strlen("readwritesplit")+1) == 0) if (strncmp(router, "readwritesplit", strlen("readwritesplit")+1) == 0)
{ {

View File

@ -826,6 +826,7 @@ static bool file_is_readable(
absolute_pathname, absolute_pathname,
eno, eno,
strerror(eno)))); strerror(eno))));
LOGIF(LE,(skygw_log_sync_all()));
succp = false; succp = false;
} }
return succp; return succp;

View File

@ -29,19 +29,60 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <secrets.h> #include <secrets.h>
#include <skygw_utils.h>
#include <log_manager.h>
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int arg_count = 3;
char *home;
char** arg_vector;
if (argc != 2) if (argc != 2)
{ {
fprintf(stderr, "Usage: %s <filename>\n", argv[0]); fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
exit(1); exit(1);
} }
arg_vector = malloc(sizeof(char*)*4);
if(arg_vector == NULL)
{
fprintf(stderr,"Error: Memory allocation failed.\n");
return 1;
}
arg_vector[0] = strdup("logmanager");
arg_vector[1] = strdup("-j");
if ((home = getenv("MAXSCALE_HOME")) != NULL)
{
arg_vector[2] = (char*)malloc((strlen(home) + strlen("/log"))*sizeof(char));
sprintf(arg_vector[2],"%s/log",home);
}
else
{
arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log");
}
arg_vector[3] = NULL;
skygw_logmanager_init(arg_count,arg_vector);
skygw_log_enable(LOGFILE_TRACE);
skygw_log_enable(LOGFILE_DEBUG);
free(arg_vector[0]);
free(arg_vector[1]);
free(arg_vector[2]);
free(arg_vector);
if (secrets_writeKeys(argv[1])) if (secrets_writeKeys(argv[1]))
{ {
fprintf(stderr, "Failed to encode the password\n"); fprintf(stderr, "Failed to encode the password\n");
exit(1); exit(1);
} }
exit(0);
skygw_log_sync_all();
skygw_logmanager_done();
return 0;
} }

View File

@ -29,7 +29,8 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <secrets.h> #include <secrets.h>
#include <skygw_utils.h>
#include <log_manager.h>
/** /**
* Encrypt a password for storing in the MaxScale.cnf file * Encrypt a password for storing in the MaxScale.cnf file
* *
@ -40,6 +41,10 @@ int
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *enc, *pw; char *enc, *pw;
int arg_count = 3;
char *home;
char** arg_vector;
if (argc != 2) if (argc != 2)
{ {
@ -47,6 +52,36 @@ main(int argc, char **argv)
exit(1); exit(1);
} }
arg_vector = malloc(sizeof(char*)*4);
if(arg_vector == NULL)
{
fprintf(stderr,"Error: Memory allocation failed.\n");
return 1;
}
arg_vector[0] = strdup("logmanager");
arg_vector[1] = strdup("-j");
if ((home = getenv("MAXSCALE_HOME")) != NULL)
{
arg_vector[2] = (char*)malloc((strlen(home) + strlen("/log"))*sizeof(char));
sprintf(arg_vector[2],"%s/log",home);
}
else
{
arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log");
}
arg_vector[3] = NULL;
skygw_logmanager_init(arg_count,arg_vector);
skygw_log_enable(LOGFILE_TRACE);
skygw_log_enable(LOGFILE_DEBUG);
free(arg_vector[0]);
free(arg_vector[1]);
free(arg_vector[2]);
free(arg_vector);
pw = calloc(81,sizeof(char)); pw = calloc(81,sizeof(char));
if(pw == NULL){ if(pw == NULL){
@ -63,5 +98,7 @@ main(int argc, char **argv)
} }
free(pw); free(pw);
skygw_log_sync_all();
skygw_logmanager_done();
return 0; return 0;
} }

View File

@ -17,7 +17,7 @@
*/ */
/** /**
* @file fwfilter.c * @file dbfwfilter.c
* @author Markus Mäkelä * @author Markus Mäkelä
* @date 13.2.2015 * @date 13.2.2015
* @version 1.0.0 * @version 1.0.0
@ -61,11 +61,9 @@
* users NAME ... match [any|all|strict_all] rules RULE ... * users NAME ... match [any|all|strict_all] rules RULE ...
*@endcode *@endcode
*/ */
#include <my_config.h> #include <my_config.h>
#include <stdint.h>
#include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <fcntl.h>
#include <filter.h> #include <filter.h>
#include <string.h> #include <string.h>
#include <atomic.h> #include <atomic.h>
@ -74,13 +72,11 @@
#include <query_classifier.h> #include <query_classifier.h>
#include <mysql_client_server_protocol.h> #include <mysql_client_server_protocol.h>
#include <spinlock.h> #include <spinlock.h>
#include <session.h>
#include <plugin.h>
#include <skygw_types.h> #include <skygw_types.h>
#include <skygw_debug.h>
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <regex.h> #include <regex.h>
MODULE_INFO info = { MODULE_INFO info = {
MODULE_API_FILTER, MODULE_API_FILTER,
MODULE_ALPHA_RELEASE, MODULE_ALPHA_RELEASE,
@ -197,7 +193,7 @@ typedef struct rulelist_t{
typedef struct user_t{ typedef struct user_t{
char* name;/*< Name of the user */ char* name;/*< Name of the user */
SPINLOCK* lock;/*< User spinlock */ SPINLOCK lock;/*< User spinlock */
QUERYSPEED* qs_limit;/*< The query speed structure unique to this user */ QUERYSPEED* qs_limit;/*< The query speed structure unique to this user */
RULELIST* rules_or;/*< If any of these rules match the action is triggered */ RULELIST* rules_or;/*< If any of these rules match the action is triggered */
RULELIST* rules_and;/*< All of these rules must match for the action to trigger */ RULELIST* rules_and;/*< All of these rules must match for the action to trigger */
@ -298,24 +294,27 @@ void* rlistdup(void* fval)
static void* hrulefree(void* fval) static void* hrulefree(void* fval)
{ {
USER* user = (USER*)fval; RULELIST *ptr = (RULELIST*)fval;
RULELIST *ptr = user->rules_or,*tmp;
while(ptr){ while(ptr){
tmp = ptr; RULELIST *tmp = ptr;
ptr = ptr->next; ptr = ptr->next;
free(tmp); free(tmp);
} }
ptr = user->rules_and;
while(ptr){
tmp = ptr;
ptr = ptr->next;
free(tmp);
}
free(user->name);
free(user);
return NULL; return NULL;
} }
static void* huserfree(void* fval)
{
USER* value = (USER*)fval;
hrulefree(value->rules_and);
hrulefree(value->rules_or);
hrulefree(value->rules_strict_and);
free(value->qs_limit);
free(value->name);
free(value);
return NULL;
}
/** /**
* Strips the single or double quotes from a string. * Strips the single or double quotes from a string.
@ -445,7 +444,7 @@ bool check_time(char* str)
char* ptr = str; char* ptr = str;
int colons = 0,numbers = 0,dashes = 0; int colons = 0,numbers = 0,dashes = 0;
while(*ptr){ while(*ptr && ptr - str < 18){
if(isdigit(*ptr)){numbers++;} if(isdigit(*ptr)){numbers++;}
else if(*ptr == ':'){colons++;} else if(*ptr == ':'){colons++;}
else if(*ptr == '-'){dashes++;} else if(*ptr == '-'){dashes++;}
@ -486,7 +485,7 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
tr = (TIMERANGE*)calloc(1,sizeof(TIMERANGE)); tr = (TIMERANGE*)calloc(1,sizeof(TIMERANGE));
if(tr == NULL){ if(tr == NULL){
skygw_log_write(LOGFILE_ERROR, "fwfilter: malloc returned NULL."); skygw_log_write(LOGFILE_ERROR, "dbfwfilter: malloc returned NULL.");
return NULL; return NULL;
} }
@ -499,7 +498,7 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
while(ptr - str < 19){ while(ptr - str < 19){
if(isdigit(*ptr)){ if(isdigit(*ptr)){
*sdest = *ptr; *sdest = *ptr;
}else if(*ptr == ':' ||*ptr == '-' || *ptr == '\0'){ }else if(*ptr == ':' ||*ptr == '-' || *ptr == '\0' || *ptr == ' '){
*sdest = '\0'; *sdest = '\0';
*idest++ = atoi(strbuffer); *idest++ = atoi(strbuffer);
sdest = strbuffer; sdest = strbuffer;
@ -512,7 +511,7 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
CHK_TIMES(tmptr); CHK_TIMES(tmptr);
if(*ptr == '\0'){ if(*ptr == '\0' || *ptr == ' '){
return tr; return tr;
} }
@ -526,8 +525,8 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
sdest++; sdest++;
} }
free(tr);
return tr; return NULL;
} }
@ -636,13 +635,14 @@ void add_users(char* rule, FW_INSTANCE* instance)
* @param rule Rule string to parse * @param rule Rule string to parse
* @param instance The FW_FILTER instance * @param instance The FW_FILTER instance
*/ */
void link_rules(char* rule, FW_INSTANCE* instance) bool link_rules(char* orig, FW_INSTANCE* instance)
{ {
assert(rule != NULL && instance != NULL);
/**Apply rules to users*/ /**Apply rules to users*/
bool match_any = true; bool match_any = true;
bool rval = true;
char *rule = strdup(orig);
char *tok, *ruleptr, *userptr, *modeptr; char *tok, *ruleptr, *userptr, *modeptr;
char *saveptr = NULL; char *saveptr = NULL;
RULELIST* rulelist = NULL; RULELIST* rulelist = NULL;
@ -653,16 +653,30 @@ void link_rules(char* rule, FW_INSTANCE* instance)
if((userptr == NULL || ruleptr == NULL || modeptr == NULL)|| if((userptr == NULL || ruleptr == NULL || modeptr == NULL)||
(userptr > modeptr || userptr > ruleptr || modeptr > ruleptr)) { (userptr > modeptr || userptr > ruleptr || modeptr > ruleptr)) {
skygw_log_write(LOGFILE_ERROR, "fwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",rule); skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig);
return; rval = false;
goto parse_err;
} }
*modeptr++ = '\0'; *modeptr++ = '\0';
*ruleptr++ = '\0'; *ruleptr++ = '\0';
tok = strtok_r(modeptr," ",&saveptr); tok = strtok_r(modeptr," ",&saveptr);
if(tok && strcmp(tok,"match") == 0){
if(tok == NULL)
{
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig);
rval = false;
goto parse_err;
}
if(strcmp(tok,"match") == 0){
tok = strtok_r(NULL," ",&saveptr); tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL)
{
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, missing keyword after 'match': %s",orig);
rval = false;
goto parse_err;
}
if(strcmp(tok,"any") == 0){ if(strcmp(tok,"any") == 0){
match_any = true; match_any = true;
}else if(strcmp(tok,"all") == 0){ }else if(strcmp(tok,"all") == 0){
@ -671,28 +685,40 @@ void link_rules(char* rule, FW_INSTANCE* instance)
match_any = false; match_any = false;
strict = true; strict = true;
}else{ }else{
skygw_log_write(LOGFILE_ERROR, "fwfilter: Rule syntax incorrect, 'match' was not followed by 'any' or 'all': %s",rule); skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, 'match' was not followed by correct keyword: %s",orig);
return; rval = false;
goto parse_err;
} }
} }
tok = strtok_r(ruleptr," ",&saveptr); tok = strtok_r(ruleptr," ",&saveptr);
tok = strtok_r(NULL," ",&saveptr); tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL)
{
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no rules given: %s",orig);
rval = false;
goto parse_err;
}
while(tok) while(tok)
{ {
RULE* rule_found = NULL; RULE* rule_found = NULL;
if((rule_found = find_rule(tok,instance)) != NULL) if((rule_found = find_rule(tok,instance)) != NULL)
{ {
RULELIST* tmp_rl = (RULELIST*)calloc(1,sizeof(RULELIST)); RULELIST* tmp_rl = (RULELIST*)calloc(1,sizeof(RULELIST));
tmp_rl->rule = rule_found; tmp_rl->rule = rule_found;
tmp_rl->next = rulelist; tmp_rl->next = rulelist;
rulelist = tmp_rl; rulelist = tmp_rl;
} }
tok = strtok_r(NULL," ",&saveptr); else
} {
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, could not find rule '%s'.",tok);
}
tok = strtok_r(NULL," ",&saveptr);
}
/** /**
* Apply this list of rules to all the listed users * Apply this list of rules to all the listed users
@ -702,8 +728,22 @@ void link_rules(char* rule, FW_INSTANCE* instance)
userptr = strtok_r(rule," ",&saveptr); userptr = strtok_r(rule," ",&saveptr);
userptr = strtok_r(NULL," ",&saveptr); userptr = strtok_r(NULL," ",&saveptr);
if(userptr == NULL)
{
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no users given: %s",orig);
rval = false;
goto parse_err;
}
if(rulelist == NULL)
{
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no rules found: %s",orig);
rval = false;
goto parse_err;
}
while(userptr) while(userptr)
{ {
USER* user; USER* user;
RULELIST *tl = NULL,*tail = NULL; RULELIST *tl = NULL,*tail = NULL;
@ -713,17 +753,12 @@ void link_rules(char* rule, FW_INSTANCE* instance)
user = (USER*)calloc(1,sizeof(USER)); user = (USER*)calloc(1,sizeof(USER));
if(user == NULL){ if(user == NULL){
free(rulelist); skygw_log_write(LOGFILE_ERROR,"Error: dbfwfilter: failed to allocate memory when parsing rules.");
return; rval = false;
goto parse_err;
} }
if((user->lock = (SPINLOCK*)malloc(sizeof(SPINLOCK))) == NULL){ spinlock_init(&user->lock);
free(user);
free(rulelist);
return;
}
spinlock_init(user->lock);
} }
user->name = (char*)strdup(userptr); user->name = (char*)strdup(userptr);
@ -753,8 +788,10 @@ void link_rules(char* rule, FW_INSTANCE* instance)
(void *)user); (void *)user);
userptr = strtok_r(NULL," ",&saveptr); userptr = strtok_r(NULL," ",&saveptr);
} }
parse_err:
free(rule);
while(rulelist) while(rulelist)
{ {
@ -762,92 +799,131 @@ void link_rules(char* rule, FW_INSTANCE* instance)
rulelist = rulelist->next; rulelist = rulelist->next;
free(tmp); free(tmp);
} }
return rval;
} }
/**
* Free a TIMERANGE struct
* @param tr pointer to a TIMERANGE struct
*/
void tr_free(TIMERANGE* tr)
{
TIMERANGE *node,*tmp;
node = tr;
while(node)
{
tmp = node;
node = node->next;
free(tmp);
}
}
/** /**
* Parse the configuration value either as a new rule or a list of users. * Parse the configuration value either as a new rule or a list of users.
* @param rule The string to parse * @param rule The string to parse
* @param instance The FW_FILTER instance * @param instance The FW_FILTER instance
*/ */
void parse_rule(char* rule, FW_INSTANCE* instance) bool parse_rule(char* rule, FW_INSTANCE* instance)
{ {
ss_dassert(rule != NULL && instance != NULL); ss_dassert(rule != NULL && instance != NULL);
char *rulecpy = strdup(rule); char *rulecpy = strdup(rule);
char *saveptr = NULL; char *saveptr = NULL;
char *tok = strtok_r(rulecpy," ,",&saveptr); char *tok = strtok_r(rulecpy," ,",&saveptr);
bool allow,deny,mode; bool allow,deny,mode;
RULE* ruledef = NULL; RULE* ruledef = NULL;
bool rval = true;
if(tok == NULL) goto retblock; if(tok == NULL)
{
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, no rule rule: %s",rule);
rval = false;
goto retblock;
}
if(strcmp("rule",tok) == 0){ /**Define a new rule*/
tok = strtok_r(NULL," ,",&saveptr); if(strcmp("rule",tok) == 0)
{
/**Define a new rule*/
if(tok == NULL) goto retblock; tok = strtok_r(NULL," ,",&saveptr);
RULELIST* rlist = NULL; if(tok == NULL)
{
ruledef = (RULE*)calloc(1,sizeof(RULE)); skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, incomplete rule: %s",rule);
rval = false;
if(ruledef == NULL) goto retblock;
{
skygw_log_write(LOGFILE_ERROR,"Error : Memory allocation failed.");
goto retblock;
}
rlist = (RULELIST*)calloc(1,sizeof(RULELIST));
if(rlist == NULL)
{
free(ruledef);
skygw_log_write(LOGFILE_ERROR,"Error : Memory allocation failed.");
goto retblock;
}
ruledef->name = strdup(tok);
ruledef->type = RT_UNDEFINED;
ruledef->on_queries = QUERY_OP_UNDEFINED;
rlist->rule = ruledef;
rlist->next = instance->rules;
instance->rules = rlist;
}else if(strcmp("users",tok) == 0){
/**Apply rules to users*/
add_users(rule, instance);
goto retblock;
} }
else
{
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule file: %s",tok);
goto retblock;
}
RULELIST* rlist = NULL;
ruledef = (RULE*)calloc(1,sizeof(RULE));
if(ruledef == NULL)
{
skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed.");
rval = false;
goto retblock;
}
rlist = (RULELIST*)calloc(1,sizeof(RULELIST));
if(rlist == NULL)
{
free(ruledef);
skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed.");
rval = false;
goto retblock;
}
ruledef->name = strdup(tok);
ruledef->type = RT_UNDEFINED;
ruledef->on_queries = QUERY_OP_UNDEFINED;
rlist->rule = ruledef;
rlist->next = instance->rules;
instance->rules = rlist;
}else if(strcmp("users",tok) == 0)
{
/**Apply rules to users*/
add_users(rule, instance);
goto retblock;
}
else
{
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule file: %s",tok);
rval = false;
goto retblock;
}
tok = strtok_r(NULL, " ,",&saveptr);
if((allow = (strcmp(tok,"allow") == 0)) ||
(deny = (strcmp(tok,"deny") == 0)))
{
mode = allow ? true:false;
ruledef->allow = mode;
ruledef->type = RT_PERMISSION;
tok = strtok_r(NULL, " ,",&saveptr); tok = strtok_r(NULL, " ,",&saveptr);
if((allow = (strcmp(tok,"allow") == 0)) || while(tok)
(deny = (strcmp(tok,"deny") == 0))){ {
if(strcmp(tok,"wildcard") == 0)
mode = allow ? true:false;
ruledef->allow = mode;
ruledef->type = RT_PERMISSION;
tok = strtok_r(NULL, " ,",&saveptr);
while(tok){
if(strcmp(tok,"wildcard") == 0)
{ {
ruledef->type = RT_WILDCARD; ruledef->type = RT_WILDCARD;
} }
else if(strcmp(tok,"columns") == 0) else if(strcmp(tok,"columns") == 0)
{ {
STRLINK *tail = NULL,*current; STRLINK *tail = NULL,*current;
ruledef->type = RT_COLUMN; ruledef->type = RT_COLUMN;
tok = strtok_r(NULL, " ,",&saveptr); tok = strtok_r(NULL, " ,",&saveptr);
while(tok && strcmp(tok,"at_times") != 0){ while(tok && strcmp(tok,"at_times") != 0 &&
strcmp(tok,"on_queries") != 0){
current = malloc(sizeof(STRLINK)); current = malloc(sizeof(STRLINK));
current->value = strdup(tok); current->value = strdup(tok);
current->next = tail; current->next = tail;
@ -859,15 +935,32 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
continue; continue;
} }
else if(strcmp(tok,"at_times") == 0) else if(strcmp(tok,"at_times") == 0)
{ {
tok = strtok_r(NULL, " ,",&saveptr); tok = strtok_r(NULL, " ,",&saveptr);
TIMERANGE *tr = NULL; TIMERANGE *tr = NULL;
bool not_valid = false;
while(tok){ while(tok){
if(!check_time(tok))
{
not_valid = true;
break;
}
TIMERANGE *tmp = parse_time(tok,instance); TIMERANGE *tmp = parse_time(tok,instance);
if(IS_RVRS_TIME(tmp)){ if(tmp == NULL)
{
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, unexpected characters after time definition.");
rval = false;
tr_free(tr);
goto retblock;
}
if(IS_RVRS_TIME(tmp))
{
tmp = split_reverse_time(tmp); tmp = split_reverse_time(tmp);
} }
tmp->next = tr; tmp->next = tr;
@ -875,44 +968,68 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
tok = strtok_r(NULL, " ,",&saveptr); tok = strtok_r(NULL, " ,",&saveptr);
} }
ruledef->active = tr; ruledef->active = tr;
if(not_valid)
{
continue;
}
} }
else if(strcmp(tok,"regex") == 0) else if(strcmp(tok,"regex") == 0)
{ {
bool escaped = false; bool escaped = false;
regex_t *re; regex_t *re;
char* start, *str; char* start, *str;
tok = strtok_r(NULL," ",&saveptr); tok = strtok_r(NULL," ",&saveptr);
char delim = '\''; char delim = '\'';
while(*tok == '\'' || *tok == '"'){ int n_char = 0;
while(*tok == '\'' || *tok == '"')
{
delim = *tok; delim = *tok;
tok++; tok++;
} }
start = tok; start = tok;
while(isspace(*tok) || *tok == delim){ while(isspace(*tok) || *tok == delim)
{
tok++; tok++;
} }
while(true){ while(n_char < 2048)
{
if((*tok == delim) && !escaped){ /** Hard-coded regex length cap */
if((*tok == delim) && !escaped)
{
break; break;
} }
escaped = (*tok == '\\'); escaped = (*tok == '\\');
tok++; tok++;
n_char++;
} }
if(n_char >= 2048)
{
skygw_log_write_flush(LOGFILE_ERROR, "dbfwfilter: Failed to parse rule, regular expression length is over 2048 characters.");
rval = false;
goto retblock;
}
str = calloc(((tok - start) + 1),sizeof(char)); str = calloc(((tok - start) + 1),sizeof(char));
if(str == NULL) if(str == NULL)
{ {
skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL."); skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL.");
rval = false;
goto retblock; goto retblock;
} }
re = (regex_t*)malloc(sizeof(regex_t)); re = (regex_t*)malloc(sizeof(regex_t));
if(re == NULL){ if(re == NULL){
skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL."); skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL.");
rval = false;
free(str); free(str);
goto retblock; goto retblock;
} }
@ -920,8 +1037,10 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
memcpy(str, start, (tok-start)); memcpy(str, start, (tok-start));
if(regcomp(re, str,REG_NOSUB)){ if(regcomp(re, str,REG_NOSUB)){
skygw_log_write(LOGFILE_ERROR, "fwfilter: Invalid regular expression '%s'.", str); skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Invalid regular expression '%s'.", str);
rval = false;
free(re); free(re);
goto retblock;
} }
else else
{ {
@ -931,7 +1050,7 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
free(str); free(str);
} }
else if(strcmp(tok,"limit_queries") == 0) else if(strcmp(tok,"limit_queries") == 0)
{ {
QUERYSPEED* qs = (QUERYSPEED*)calloc(1,sizeof(QUERYSPEED)); QUERYSPEED* qs = (QUERYSPEED*)calloc(1,sizeof(QUERYSPEED));
@ -943,6 +1062,8 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
tok = strtok_r(NULL," ",&saveptr); tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL){ if(tok == NULL){
free(qs); free(qs);
rval = false;
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
goto retblock; goto retblock;
} }
qs->limit = atoi(tok); qs->limit = atoi(tok);
@ -950,41 +1071,57 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
tok = strtok_r(NULL," ",&saveptr); tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL){ if(tok == NULL){
free(qs); free(qs);
rval = false;
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
goto retblock; goto retblock;
} }
qs->period = atof(tok); qs->period = atof(tok);
tok = strtok_r(NULL," ",&saveptr); tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL){ if(tok == NULL){
free(qs); free(qs);
rval = false;
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
goto retblock; goto retblock;
} }
qs->cooldown = atof(tok); qs->cooldown = atof(tok);
ruledef->type = RT_THROTTLE; ruledef->type = RT_THROTTLE;
ruledef->data = (void*)qs; ruledef->data = (void*)qs;
} }
else if(strcmp(tok,"no_where_clause") == 0) else if(strcmp(tok,"no_where_clause") == 0)
{ {
ruledef->type = RT_CLAUSE; ruledef->type = RT_CLAUSE;
ruledef->data = (void*)mode; ruledef->data = (void*)mode;
} }
else if(strcmp(tok,"on_operations") == 0) else if(strcmp(tok,"on_queries") == 0)
{ {
tok = strtok_r(NULL," ",&saveptr); tok = strtok_r(NULL," ",&saveptr);
if(!parse_querytypes(tok,ruledef)){
skygw_log_write(LOGFILE_ERROR, if(tok == NULL)
"fwfilter: Invalid query type" {
"requirements on where/having clauses: %s." skygw_log_write(LOGFILE_ERROR,
,tok); "dbfwfilter: Missing parameter for 'on_queries'.");
} rval = false;
} goto retblock;
tok = strtok_r(NULL," ,",&saveptr);
} }
goto retblock; if(!parse_querytypes(tok,ruledef)){
skygw_log_write(LOGFILE_ERROR,
"dbfwfilter: Invalid query type"
"requirements: %s."
,tok);
rval = false;
goto retblock;
}
}
tok = strtok_r(NULL," ,",&saveptr);
} }
goto retblock;
}
retblock: retblock:
free(rulecpy); free(rulecpy);
return rval;
} }
@ -1006,6 +1143,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
char *filename = NULL, *nl; char *filename = NULL, *nl;
char buffer[2048]; char buffer[2048];
FILE* file; FILE* file;
bool err = false;
if ((my_instance = calloc(1, sizeof(FW_INSTANCE))) == NULL || if ((my_instance = calloc(1, sizeof(FW_INSTANCE))) == NULL ||
(my_instance->lock = (SPINLOCK*)malloc(sizeof(SPINLOCK))) == NULL){ (my_instance->lock = (SPINLOCK*)malloc(sizeof(SPINLOCK))) == NULL){
@ -1021,7 +1159,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
return NULL; return NULL;
} }
hashtable_memory_fns(ht,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,hrulefree); hashtable_memory_fns(ht,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,huserfree);
my_instance->htable = ht; my_instance->htable = ht;
my_instance->def_op = true; my_instance->def_op = true;
@ -1077,19 +1215,41 @@ createInstance(char **options, FILTER_PARAMETER **params)
*nl = '\0'; *nl = '\0';
} }
parse_rule(buffer,my_instance); if(!parse_rule(buffer,my_instance))
{
fclose(file);
err = true;
goto retblock;
}
} }
fclose(file); fclose(file);
/**Apply the rules to users*/ /**Apply the rules to users*/
ptr = my_instance->userstrings; ptr = my_instance->userstrings;
while(ptr){ while(ptr){
link_rules(ptr->value,my_instance);
tmp = ptr; if(!link_rules(ptr->value,my_instance))
ptr = ptr->next; {
free(tmp->value); skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Failed to parse rule: %s",ptr->value);
free(tmp); err = true;
}
tmp = ptr;
ptr = ptr->next;
free(tmp->value);
free(tmp);
}
retblock:
if(err)
{
hrulefree(my_instance->rules);
hashtable_free(my_instance->htable);
free(my_instance);
my_instance = NULL;
} }
return (FILTER *)my_instance; return (FILTER *)my_instance;
@ -1340,7 +1500,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
if(!rulelist->rule->allow){ if(!rulelist->rule->allow){
msg = strdup("Permission denied, query matched regular expression."); msg = strdup("Permission denied, query matched regular expression.");
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': regex matched on query",rulelist->rule->name); skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': regex matched on query",rulelist->rule->name);
goto queryresolved; goto queryresolved;
}else{ }else{
break; break;
@ -1353,7 +1513,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
if(!rulelist->rule->allow){ if(!rulelist->rule->allow){
matches = true; matches = true;
msg = strdup("Permission denied at this time."); msg = strdup("Permission denied at this time.");
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now)); skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now));
goto queryresolved; goto queryresolved;
}else{ }else{
break; break;
@ -1376,7 +1536,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
if(!rulelist->rule->allow){ if(!rulelist->rule->allow){
sprintf(emsg,"Permission denied to column '%s'.",strln->value); sprintf(emsg,"Permission denied to column '%s'.",strln->value);
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value); skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
msg = strdup(emsg); msg = strdup(emsg);
goto queryresolved; goto queryresolved;
}else{ }else{
@ -1406,7 +1566,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
matches = true; matches = true;
msg = strdup("Usage of wildcard denied."); msg = strdup("Usage of wildcard denied.");
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name); skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
goto queryresolved; goto queryresolved;
} }
} }
@ -1424,9 +1584,9 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
rule_qs = (QUERYSPEED*)rulelist->rule->data; rule_qs = (QUERYSPEED*)rulelist->rule->data;
spinlock_release(my_instance->lock); spinlock_release(my_instance->lock);
spinlock_acquire(user->lock); spinlock_acquire(&user->lock);
queryspeed = user->qs_limit; queryspeed = user->qs_limit;
spinlock_release(user->lock); spinlock_release(&user->lock);
while(queryspeed){ while(queryspeed){
if(queryspeed->id == rule_qs->id){ if(queryspeed->id == rule_qs->id){
@ -1457,7 +1617,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered); double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered);
sprintf(emsg,"Queries denied for %f seconds",blocked_for); sprintf(emsg,"Queries denied for %f seconds",blocked_for);
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': user denied for %f seconds",rulelist->rule->name,blocked_for); skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': user denied for %f seconds",rulelist->rule->name,blocked_for);
msg = strdup(emsg); msg = strdup(emsg);
matches = true; matches = true;
@ -1478,7 +1638,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
queryspeed->active = true; queryspeed->active = true;
skygw_log_write(LOGFILE_TRACE, skygw_log_write(LOGFILE_TRACE,
"fwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user for %f seconds.", "dbfwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user for %f seconds.",
rulelist->rule->name, rulelist->rule->name,
queryspeed->limit, queryspeed->limit,
queryspeed->period, queryspeed->period,
@ -1508,7 +1668,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
{ {
matches = true; matches = true;
msg = strdup("Required WHERE/HAVING clause is missing."); msg = strdup("Required WHERE/HAVING clause is missing.");
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query has no where/having clause, query is denied.", skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query has no where/having clause, query is denied.",
rulelist->rule->name); rulelist->rule->name);
} }
break; break;
@ -1600,6 +1760,7 @@ bool check_match_any(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu
bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue, USER* user,bool strict_all) bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue, USER* user,bool strict_all)
{ {
bool is_sql, rval = true; bool is_sql, rval = true;
bool have_active_rule = false;
int qlen; int qlen;
unsigned char* memptr = (unsigned char*)queue->start; unsigned char* memptr = (unsigned char*)queue->start;
char *fullquery = NULL,*ptr; char *fullquery = NULL,*ptr;
@ -1641,6 +1802,7 @@ bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu
continue; continue;
} }
have_active_rule = true;
if(!rule_matches(my_instance,my_session,queue,user,rulelist,fullquery)){ if(!rule_matches(my_instance,my_session,queue,user,rulelist,fullquery)){
rval = false; rval = false;
@ -1650,6 +1812,12 @@ bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu
rulelist = rulelist->next; rulelist = rulelist->next;
} }
if(!have_active_rule)
{
/** No active rules */
rval = false;
}
retblock: retblock:
free(fullquery); free(fullquery);

View File

@ -1063,6 +1063,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
if(!my_session->active) if(!my_session->active)
{ {
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
gwbuf_free(reply); gwbuf_free(reply);
rc = 0; rc = 0;
if(my_session->waiting[PARENT]) if(my_session->waiting[PARENT])
@ -1104,6 +1105,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
if(my_session->replies[branch] == 0) if(my_session->replies[branch] == 0)
{ {
skygw_log_write(LOGFILE_TRACE,"Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD");
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet. /* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
* Otherwise the reply is a result set and the amount of packets is unknown. * Otherwise the reply is a result set and the amount of packets is unknown.
*/ */
@ -1116,6 +1118,11 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
{ {
flags = get_response_flags(ptr,true); flags = get_response_flags(ptr,true);
more_results = (flags & 0x08) && my_session->client_multistatement; more_results = (flags & 0x08) && my_session->client_multistatement;
if(more_results)
{
skygw_log_write(LOGFILE_TRACE,
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
}
} }
} }
#ifdef SS_DEBUG #ifdef SS_DEBUG

View File

@ -161,6 +161,19 @@ typedef struct subservice_t{
bool mapped; bool mapped;
}SUBSERVICE; }SUBSERVICE;
/**
* Bitmask values for the router session's initialization. These values are used
* to prevent responses from internal commands being forwarded to the client.
*/
typedef enum shard_init_mask
{
INIT_READY = 0x0,
INIT_MAPPING = 0x1,
INIT_USE_DB = 0x02,
INIT_UNINT = 0x04
} shard_init_mask_t;
/** /**
* The client session structure used within this router. * The client session structure used within this router.
*/ */
@ -172,8 +185,8 @@ struct router_client_session {
int rses_versno; /*< even = no active update, else odd. not used 4/14 */ int rses_versno; /*< even = no active update, else odd. not used 4/14 */
bool rses_closed; /*< true when closeSession is called */ bool rses_closed; /*< true when closeSession is called */
DCB* rses_client_dcb; DCB* rses_client_dcb;
DCB* dummy_dcb; /* DCB used to send the client write messages from the router itself */ DCB* replydcb; /* DCB used to send the client write messages from the router itself */
DCB* queue_dcb; /* DCB used to send queued queries to the router */ DCB* routedcb; /* DCB used to send queued queries to the router */
MYSQL_session* rses_mysql_session; MYSQL_session* rses_mysql_session;
/** Properties listed by their type */ /** Properties listed by their type */
rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT]; rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT];
@ -190,6 +203,8 @@ struct router_client_session {
bool hash_init; bool hash_init;
SESSION* session; SESSION* session;
GWBUF* queue; GWBUF* queue;
char connect_db[MYSQL_DATABASE_MAXLEN+1]; /*< Database the user was trying to connect to */
shard_init_mask_t init; /*< Initialization state bitmask */
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
skygw_chk_t rses_chk_tail; skygw_chk_t rses_chk_tail;
#endif #endif

View File

@ -530,6 +530,12 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
if (auth_ret == 0) { if (auth_ret == 0) {
dcb->user = strdup(client_data->user); dcb->user = strdup(client_data->user);
} }
else
{
skygw_log_write(LOGFILE_ERROR,
"%s: login attempt for user '%s', authentication failed.",
dcb->service->name, username);
}
/* let's free the auth_token now */ /* let's free the auth_token now */
if (auth_token) { if (auth_token) {
@ -900,7 +906,7 @@ int gw_read_client_event(
dcb, dcb,
ERRACT_NEW_CONNECTION, ERRACT_NEW_CONNECTION,
&succp); &succp);
free(errbuf); gwbuf_free(errbuf);
/** /**
* If there are not enough backends close * If there are not enough backends close
* session * session

View File

@ -1530,14 +1530,20 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
* user@% not found. * user@% not found.
*/ */
LOGIF(LD, LOGIF(LD,
(skygw_log_write_flush( (skygw_log_write_flush(
LOGFILE_DEBUG, LOGFILE_DEBUG,
"%lu [MySQL Client Auth], user [%s@%s] not existent", "%lu [MySQL Client Auth], user [%s@%s] not existent",
pthread_self(), pthread_self(),
key.user, key.user,
dcb->remote))); dcb->remote)));
break;
LOGIF(LT,skygw_log_write_flush(
LOGFILE_ERROR,
"Authentication Failed: user [%s@%s] not found.",
key.user,
dcb->remote));
break;
} }
break; break;

View File

@ -89,6 +89,8 @@
#include <mysql_client_server_protocol.h> #include <mysql_client_server_protocol.h>
#include "modutil.h"
/** Defined in log_manager.cc */ /** Defined in log_manager.cc */
extern int lm_enabled_logfiles_bitmask; extern int lm_enabled_logfiles_bitmask;
extern size_t log_ses_count[]; extern size_t log_ses_count[];
@ -535,6 +537,7 @@ BACKEND *master_host = NULL;
pthread_self(), pthread_self(),
candidate->server->port, candidate->server->port,
candidate->current_connection_count))); candidate->current_connection_count)));
/* /*
* Open a backend connection, putting the DCB for this * Open a backend connection, putting the DCB for this
* connection in the client_rses->backend_dcb * connection in the client_rses->backend_dcb
@ -565,6 +568,12 @@ BACKEND *master_host = NULL;
CHK_CLIENT_RSES(client_rses); CHK_CLIENT_RSES(client_rses);
skygw_log_write(
LOGFILE_TRACE,
"Readconnroute: New session for server %s. "
"Connections : %d",
candidate->server->unique_name,
candidate->current_connection_count);
return (void *)client_rses; return (void *)client_rses;
} }
@ -718,10 +727,19 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
"Error : Failed to route MySQL command %d to backend " "Error : Failed to route MySQL command %d to backend "
"server.", "server.",
mysql_command))); mysql_command)));
skygw_log_write(
LOGFILE_ERROR,
"Error : Failed to route MySQL command %d to backend "
"server %s.",
mysql_command,
router_cli_ses->backend->server->unique_name);
rc = 0; rc = 0;
goto return_rc; goto return_rc;
} }
char* trc = NULL;
switch(mysql_command) { switch(mysql_command) {
case MYSQL_COM_CHANGE_USER: case MYSQL_COM_CHANGE_USER:
rc = backend_dcb->func.auth( rc = backend_dcb->func.auth(
@ -730,7 +748,8 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
backend_dcb->session, backend_dcb->session,
queue); queue);
break; break;
case MYSQL_COM_QUERY:
LOGIF(LOGFILE_TRACE,(trc = modutil_get_SQL(queue)));
default: default:
rc = backend_dcb->func.write(backend_dcb, queue); rc = backend_dcb->func.write(backend_dcb, queue);
break; break;
@ -745,6 +764,15 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
mysql_command, mysql_command,
backend_dcb, backend_dcb,
rc))); rc)));
LOGIF(LOGFILE_TRACE,skygw_log_write(
LOGFILE_TRACE,
"Routed command [%#x] to '%s'%s%s",
mysql_command,
backend_dcb->server->unique_name,
trc?": ":".",
trc?trc:""));
free(trc);
return_rc: return_rc:
return rc; return rc;
} }

View File

@ -402,7 +402,7 @@ int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
rval); rval);
} }
} }
gwbuf_free(buffer);
return !rval; return !rval;
} }
@ -514,6 +514,10 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client,
*/ */
rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db); rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db);
if(rval)
{
skygw_log_write(LOGFILE_TRACE,"schemarouter: Using active database '%s'",client->rses_mysql_session->db);
}
} }
return rval; return rval;
@ -1932,6 +1936,7 @@ static int routeQuery(
*/ */
route_target = TARGET_ANY; route_target = TARGET_ANY;
skygw_log_write(LOGFILE_TRACE,"schemarouter: Routing query to first available backend.");
} }
else else
@ -2008,6 +2013,9 @@ static int routeQuery(
/**No valid backends alive*/ /**No valid backends alive*/
skygw_log_write(LOGFILE_TRACE,"schemarouter: No backends are running"); skygw_log_write(LOGFILE_TRACE,"schemarouter: No backends are running");
skygw_log_write(LOGFILE_ERROR,
"Error: Schemarouter: Failed to route query, "
"no backends are available.");
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
ret = 0; ret = 0;
goto retblock; goto retblock;
@ -2215,6 +2223,7 @@ static void clientReply (
*/ */
if (!rses_begin_locked_router_action(router_cli_ses)) if (!rses_begin_locked_router_action(router_cli_ses))
{ {
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
goto lock_failed; goto lock_failed;
} }
/** Holding lock ensures that router session remains open */ /** Holding lock ensures that router session remains open */
@ -2244,6 +2253,7 @@ static void clientReply (
if (!rses_begin_locked_router_action(router_cli_ses)) if (!rses_begin_locked_router_action(router_cli_ses))
{ {
/** Log to debug that router was closed */ /** Log to debug that router was closed */
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
goto lock_failed; goto lock_failed;
} }
bref = get_bref_from_dcb(router_cli_ses, backend_dcb); bref = get_bref_from_dcb(router_cli_ses, backend_dcb);
@ -2252,6 +2262,7 @@ static void clientReply (
{ {
/** Unlock router session */ /** Unlock router session */
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
goto lock_failed; goto lock_failed;
} }
@ -2299,7 +2310,7 @@ static void clientReply (
} }
} }
gwbuf_free(writebuf); while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
if(mapped) if(mapped)
{ {
@ -2322,7 +2333,10 @@ static void clientReply (
router_cli_ses->connect_db); router_cli_ses->connect_db);
router_cli_ses->rses_closed = true; router_cli_ses->rses_closed = true;
if(router_cli_ses->queue) if(router_cli_ses->queue)
gwbuf_free(router_cli_ses->queue); {
while((router_cli_ses->queue = gwbuf_consume(
router_cli_ses->queue,gwbuf_length(router_cli_ses->queue))));
}
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
return; return;
} }
@ -2416,6 +2430,8 @@ static void clientReply (
strcpy(router_cli_ses->rses_mysql_session->db,router_cli_ses->connect_db); strcpy(router_cli_ses->rses_mysql_session->db,router_cli_ses->connect_db);
ss_dassert(router_cli_ses->init == INIT_READY); ss_dassert(router_cli_ses->init == INIT_READY);
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
if(writebuf)
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
return; return;
} }

View File

@ -119,7 +119,6 @@ static route_target_t get_shard_route_target(
static uint8_t getCapabilities(ROUTER* inst, void* router_session); static uint8_t getCapabilities(ROUTER* inst, void* router_session);
//bool parse_db_ignore_list(ROUTER_INSTANCE* router, char* param);
static void subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state); static void subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state);
static void subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state); static void subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state);
static bool get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target); static bool get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target);
@ -414,8 +413,14 @@ bool subsvc_is_valid(SUBSERVICE* sub)
return false; return false;
} }
/**
* Map the databases of all subservices.
* @param inst router instance
* @param session router session
* @return 0 on success, 1 on error
*/
int int
gen_tablelist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session) gen_subsvc_dblist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
{ {
const char* query = "SHOW DATABASES;"; const char* query = "SHOW DATABASES;";
GWBUF *buffer, *clone; GWBUF *buffer, *clone;
@ -475,7 +480,6 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
if(sz > 0) if(sz > 0)
{ {
has_dbs = true;
for(i = 0; i < sz; i++) for(i = 0; i < sz; i++)
{ {
@ -489,9 +493,8 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
else else
{ {
skygw_log_write(LOGFILE_TRACE,"shardrouter: Query targets database '%s' on server '%s",dbnms[i],rval); skygw_log_write(LOGFILE_TRACE,"shardrouter: Query targets database '%s' on server '%s",dbnms[i],rval);
has_dbs = true;
} }
for(j = i; j < sz; j++) free(dbnms[j]);
break;
} }
free(dbnms[i]); free(dbnms[i]);
} }
@ -554,6 +557,10 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
*/ */
rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db); rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db);
if(rval)
{
skygw_log_write(LOGFILE_TRACE,"shardrouter: Using active database '%s'",client->rses_mysql_session->db);
}
} }
return rval; return rval;
@ -612,64 +619,160 @@ filterReply(FILTER* instance, void *session, GWBUF *reply)
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES*) instance; ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES*) instance;
SUBSERVICE* subsvc; SUBSERVICE* subsvc;
int i, rv = 1; int i, rv = 1;
bool mapped = true;
sescmd_cursor_t* scur; sescmd_cursor_t* scur;
GWBUF* tmp = NULL; GWBUF* tmp = NULL;
skygw_log_write_flush(LOGFILE_TRACE,"shardrouter: filterReply mapped: %s",rses->hash_init ? "true" : "false");
if(!rses_begin_locked_router_action(rses)) if(!rses_begin_locked_router_action(rses))
{ {
tmp = reply;
while((tmp = gwbuf_consume(tmp,gwbuf_length(tmp))));
return 0; return 0;
} }
subsvc = get_subsvc_from_ses(rses, session); subsvc = get_subsvc_from_ses(rses, session);
if(SUBSVC_IS_WAITING(subsvc)) if(rses->init & INIT_MAPPING)
{ {
subsvc_clear_state(subsvc, SUBSVC_WAITING_RESULT); bool mapped = true, logged = false;
int i;
for(i = 0; i < rses->n_subservice; i++)
{
if(subsvc->session == rses->subservice[i]->session &&
!SUBSVC_IS_MAPPED(rses->subservice[i]))
{
rses->subservice[i]->state |= SUBSVC_MAPPED;
parse_mapping_response(rses,
rses->subservice[i]->service->name,
reply);
}
if(SUBSVC_IS_OK(rses->subservice[i]) &&
!SUBSVC_IS_MAPPED(rses->subservice[i]))
{
mapped = false;
if(!logged)
{
/*
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Still waiting for reply to SHOW DATABASES from %s for session %p",
bkrf[i].bref_backend->backend_server->unique_name,
rses->rses_client_dcb->session);
*/
logged = true;
}
}
}
if(mapped)
{
/*
* Check if the session is reconnecting with a database name
* that is not in the hashtable. If the database is not found
* then close the session.
*/
rses->init &= ~INIT_MAPPING;
if(rses->init & INIT_USE_DB)
{
char* target;
if((target = hashtable_fetch(rses->dbhash,
rses->connect_db)) == NULL)
{
skygw_log_write_flush(LOGFILE_TRACE,"schemarouter: Connecting to a non-existent database '%s'",
rses->connect_db);
rses->rses_closed = true;
if(rses->queue)
{
while((rses->queue = gwbuf_consume(
rses->queue,gwbuf_length(rses->queue))));
}
rses_end_locked_router_action(rses);
goto retblock;
}
/* Send a COM_INIT_DB packet to the server with the right database
* and set it as the client's active database */
unsigned int qlen;
GWBUF* buffer;
qlen = strlen(rses->connect_db);
buffer = gwbuf_alloc(qlen + 5);
if(buffer == NULL)
{
skygw_log_write_flush(LOGFILE_ERROR,"Error : Buffer allocation failed.");
rses->rses_closed = true;
if(rses->queue)
gwbuf_free(rses->queue);
goto retblock;
}
gw_mysql_set_byte3((unsigned char*)buffer->start,qlen+1);
gwbuf_set_type(buffer,GWBUF_TYPE_MYSQL);
*((unsigned char*)buffer->start + 3) = 0x0;
*((unsigned char*)buffer->start + 4) = 0x2;
memcpy(buffer->start+5,rses->connect_db,qlen);
DCB* dcb = NULL;
SESSION_ROUTE_QUERY(subsvc->session,buffer);
goto retblock;
}
if(rses->queue)
{
GWBUF* tmp = rses->queue;
rses->queue = rses->queue->next;
tmp->next = NULL;
char* querystr = modutil_get_SQL(tmp);
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Sending queued buffer for session %p: %s",
rses->rses_client_dcb->session,
querystr);
poll_add_epollin_event_to_dcb(rses->routedcb,tmp);
free(querystr);
}
skygw_log_write_flush(LOGFILE_DEBUG,"session [%p] database map finished.",
rses);
}
goto retblock;
} }
subsvc_clear_state(subsvc, SUBSVC_QUERY_ACTIVE);
if(!rses->hash_init)
{
subsvc_set_state(subsvc, SUBSVC_MAPPED);
parse_mapping_response(rses, subsvc->service->name, reply);
for(i = 0; i < rses->n_subservice; i++)
{
if(SUBSVC_IS_OK(rses->subservice[i]) && !SUBSVC_IS_MAPPED(rses->subservice[i]))
{
mapped = false;
break;
}
}
gwbuf_free(reply);
if(mapped)
{
rses->hash_init = true;
if(rses->queue)
{
tmp = rses->queue;
rses->queue = rses->queue->next;
}
}
goto retblock;
}
if(rses->queue) if(rses->queue)
{ {
tmp = rses->queue; GWBUF* tmp = rses->queue;
rses->queue = rses->queue->next; rses->queue = rses->queue->next;
tmp->next = NULL;
char* querystr = modutil_get_SQL(tmp);
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Sending queued buffer for session %p: %s",
rses->rses_client_dcb->session,
querystr);
poll_add_epollin_event_to_dcb(rses->routedcb,tmp);
free(querystr);
tmp = NULL;
} }
if(rses->init & INIT_USE_DB)
{
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Reply to USE '%s' received for session %p",
rses->connect_db,
rses->rses_client_dcb->session);
rses->init &= ~INIT_USE_DB;
strcpy(rses->rses_mysql_session->db,rses->connect_db);
ss_dassert(rses->init == INIT_READY);
if(reply)
{
tmp = reply;
while((tmp = gwbuf_consume(tmp,gwbuf_length(tmp))));
tmp = NULL;
}
goto retblock;
}
scur = subsvc->scur; scur = subsvc->scur;
@ -688,12 +791,8 @@ filterReply(FILTER* instance, void *session, GWBUF *reply)
rv = SESSION_ROUTE_REPLY(rses->session, reply); rv = SESSION_ROUTE_REPLY(rses->session, reply);
retblock: retblock:
rses_end_locked_router_action(rses); rses_end_locked_router_action(rses);
if(tmp)
{
poll_add_epollin_event_to_dcb(rses->queue_dcb,tmp);
}
return rv; return rv;
} }
@ -843,7 +942,7 @@ static ROUTER *
createInstance(SERVICE *service, char **options) createInstance(SERVICE *service, char **options)
{ {
ROUTER_INSTANCE* router; ROUTER_INSTANCE* router;
char *services, *tok; char *services, *tok, *saveptr;
SERVICE **res_svc, **temp; SERVICE **res_svc, **temp;
CONFIG_PARAMETER* conf; CONFIG_PARAMETER* conf;
int i = 0, sz; int i = 0, sz;
@ -858,7 +957,6 @@ createInstance(SERVICE *service, char **options)
spinlock_init(&router->lock); spinlock_init(&router->lock);
conf = config_get_param(service->svc_config_param, "subservices"); conf = config_get_param(service->svc_config_param, "subservices");
/*
if(conf == NULL) if(conf == NULL)
{ {
@ -869,14 +967,20 @@ createInstance(SERVICE *service, char **options)
} }
services = strdup(conf->value); services = strdup(conf->value);
*/
sz = 2; sz = 2;
res_svc = calloc(sz, sizeof(SERVICE*));
/*
tok = strtok(services, ",");
*/
while(options[i]) if((res_svc = calloc(sz, sizeof(SERVICE*))) == NULL)
{
free(router);
skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed.");
return NULL;
}
tok = strtok_r(services, ",",&saveptr);
while(tok)
{ {
if(sz <= i) if(sz <= i)
{ {
@ -884,9 +988,9 @@ createInstance(SERVICE *service, char **options)
if(temp == NULL) if(temp == NULL)
{ {
skygw_log_write(LOGFILE_ERROR, "Error : Memory reallocation failed."); skygw_log_write(LOGFILE_ERROR, "Error : Memory reallocation failed.");
skygw_log_write(LOGFILE_DEBUG, "shardrouter.c: realloc returned NULL. " LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG, "shardrouter.c: realloc returned NULL. "
"service count[%d] buffer size [%u] tried to allocate [%u]", "service count[%d] buffer size [%u] tried to allocate [%u]",
sz, sizeof(SERVICE*)*(sz), sizeof(SERVICE*)*(sz * 2)); sz, sizeof(SERVICE*)*(sz), sizeof(SERVICE*)*(sz * 2))));
free(res_svc); free(res_svc);
free(router); free(router);
return NULL; return NULL;
@ -895,19 +999,27 @@ createInstance(SERVICE *service, char **options)
res_svc = temp; res_svc = temp;
} }
res_svc[i] = service_find(options[i]); res_svc[i] = service_find(tok);
if(res_svc[i] == NULL)
{
free(res_svc);
free(router);
skygw_log_write(LOGFILE_ERROR, "Error : No service named '%s' found.", options[i]);
return NULL;
}
i++; i++;
tok = strtok_r(NULL,",",&saveptr);
} }
/*
free(services); free(services);
*/
router->services = res_svc; router->services = res_svc;
router->n_services = i; router->n_services = i;
if(i < min_nsvc) if(i < min_nsvc)
{ {
skygw_log_write(LOGFILE_ERROR, "Error : Not enough services. Shardrouter requires at least %d " skygw_log_write(LOGFILE_ERROR, "Error : Not enough parameters for 'subservice' router option. Shardrouter requires at least %d "
"configured services to work.", min_nsvc); "configured services to work.", min_nsvc);
free(router->services); free(router->services);
free(router); free(router);
@ -980,15 +1092,15 @@ newSession(
client_rses->rses_autocommit_enabled = true; client_rses->rses_autocommit_enabled = true;
client_rses->rses_transaction_active = false; client_rses->rses_transaction_active = false;
client_rses->session = session; client_rses->session = session;
client_rses->dummy_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); client_rses->replydcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
client_rses->dummy_dcb->func.read = fakeReply; client_rses->replydcb->func.read = fakeReply;
client_rses->dummy_dcb->state = DCB_STATE_POLLING; client_rses->replydcb->state = DCB_STATE_POLLING;
client_rses->dummy_dcb->session = session; client_rses->replydcb->session = session;
client_rses->queue_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); client_rses->routedcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
client_rses->queue_dcb->func.read = fakeQuery; client_rses->routedcb->func.read = fakeQuery;
client_rses->queue_dcb->state = DCB_STATE_POLLING; client_rses->routedcb->state = DCB_STATE_POLLING;
client_rses->queue_dcb->session = session; client_rses->routedcb->session = session;
spinlock_init(&client_rses->rses_lock); spinlock_init(&client_rses->rses_lock);
@ -1168,10 +1280,10 @@ closeSession(
} }
router_cli_ses->subservice[i]->state = SUBSVC_CLOSED; router_cli_ses->subservice[i]->state = SUBSVC_CLOSED;
} }
router_cli_ses->dummy_dcb->session = NULL; router_cli_ses->replydcb->session = NULL;
router_cli_ses->queue_dcb->session = NULL; router_cli_ses->routedcb->session = NULL;
dcb_close(router_cli_ses->dummy_dcb); dcb_close(router_cli_ses->replydcb);
dcb_close(router_cli_ses->queue_dcb); dcb_close(router_cli_ses->routedcb);
/** Unlock */ /** Unlock */
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
@ -1480,31 +1592,45 @@ routeQuery(ROUTER* instance,
ret = 0; ret = 0;
goto retblock; goto retblock;
} }
if(!(rses_is_closed = router_cli_ses->rses_closed))
if(!router_cli_ses->hash_init)
{
gen_tablelist(inst, router_cli_ses);
skygw_log_write(LOGFILE_TRACE,"shardrouter: got a query while mapping databases.");
GWBUF* tmp = router_cli_ses->queue;
while(tmp && tmp->next)
{ {
tmp = tmp->next; if(router_cli_ses->init & INIT_UNINT)
} {
/* Generate database list */
gen_subsvc_dblist(inst,router_cli_ses);
if(tmp == NULL) }
{
router_cli_ses->queue = querybuf;
}
else
{
tmp->next = querybuf;
}
rses_end_locked_router_action(router_cli_ses); if(router_cli_ses->init & INIT_MAPPING)
return 1; {
}
char* querystr = modutil_get_SQL(querybuf);
skygw_log_write(LOGFILE_DEBUG,"shardrouter: Storing query for session %p: %s",
router_cli_ses->rses_client_dcb->session,
querystr);
free(querystr);
gwbuf_make_contiguous(querybuf);
GWBUF* ptr = router_cli_ses->queue;
while(ptr && ptr->next)
{
ptr = ptr->next;
}
if(ptr == NULL)
{
router_cli_ses->queue = querybuf;
}
else
{
ptr->next = querybuf;
}
rses_end_locked_router_action(router_cli_ses);
return 1;
}
}
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
@ -1608,7 +1734,7 @@ routeQuery(ROUTER* instance,
*/ */
GWBUF* dbres = gen_show_dbs_response(inst,router_cli_ses); GWBUF* dbres = gen_show_dbs_response(inst,router_cli_ses);
poll_add_epollin_event_to_dcb(router_cli_ses->dummy_dcb,dbres); poll_add_epollin_event_to_dcb(router_cli_ses->replydcb,dbres);
ret = 1; ret = 1;
goto retblock; goto retblock;
} }
@ -2982,7 +3108,7 @@ reply_error:
* Create an incoming event for randomly selected backend DCB which * Create an incoming event for randomly selected backend DCB which
* will then be notified and replied 'back' to the client. * will then be notified and replied 'back' to the client.
*/ */
poll_add_epollin_event_to_dcb(rses->dummy_dcb, poll_add_epollin_event_to_dcb(rses->replydcb,
gwbuf_clone(errbuf)); gwbuf_clone(errbuf));
gwbuf_free(errbuf); gwbuf_free(errbuf);
} }