Merge remote-tracking branch 'origin/develop' into MXS-105

Conflicts (resolved):
	server/modules/routing/schemarouter/schemarouter.c
This commit is contained in:
counterpoint 2015-05-05 10:54:09 +01:00
commit 8647b30184
39 changed files with 853 additions and 784 deletions

View File

@ -29,7 +29,7 @@ find_package(MySQLClient)
find_package(MySQL)
find_package(Pandoc)
find_package(TCMalloc)
find_package(Jemalloc)
# You can find the variables set by this in the FindCURL.cmake file
# which is a default module in CMake.
find_package(CURL)
@ -93,12 +93,12 @@ if(PROFILE)
endif()
set(CMAKE_C_FLAGS "${FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG")
set(CMAKE_C_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG -DLOG_ASSERT")
set(CMAKE_C_FLAGS_RELEASE "")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-ggdb")
set(CMAKE_CXX_FLAGS "${FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG -DLOG_ASSERT")
set(CMAKE_CXX_FLAGS_RELEASE "")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-ggdb")
@ -149,6 +149,7 @@ install(FILES ${ERRMSG} DESTINATION mysql)
install(FILES ${CMAKE_SOURCE_DIR}/COPYRIGHT DESTINATION .)
install(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION .)
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION .)
install(DIRECTORY DESTINATION log)
# Install startup scripts and ldconfig files
if(WITH_SCRIPTS)

View File

@ -410,6 +410,8 @@ In order for the various router modules to function correctly they require infor
Monitors are defined in much the same way as other elements in the configuration file, with the section name being the name of the monitor instance and the type being set to monitor.
This is an example configuration of the MySQL monitor module. It is intended for Master-Slave replication clusters and allows for replication lag detection.
```
[MySQL Monitor]
type=monitor
@ -425,7 +427,11 @@ backend_write_timeout=2
# mysqlmon specific options
detect_replication_lag=0
detect_stale_master=0
```
Here is an example configuration of the Galera cluster monitor. It detects when nodes are in sync and also assigns master and slave roles to nodes within MaxScale, allowing it to be used with modules designed for Master-Slave replication clusters.
```
[Galera Monitor]
type=monitor
module=galeramon
@ -451,6 +457,8 @@ The module parameter defines the name of the loadable module that implements the
The servers parameter is a comma separated list of server names to monitor, these are the names defined elsewhere in the configuration file. The set of servers monitored by a single monitor need not be the same as the set of servers used within any particular server, a single monitor instance may monitor servers in multiple servers.
Multiple monitors monitoring the same servers should be avoided. They can possibly make the whole cluster inoperable and a good example is the mixed use of the MySQL and the Galera monitors. The MySQL monitor requires a working Master-Slave replication for it to assign the Master and Slave roles inside MaxScale but the Galera monitor only looks for Galera specific status variables. These two monitors will cause a conflict when one tries to clear server states it sees as valid while the other is simultaneously setting new states to the rest of the servers.
#### `user`
The user parameter defines the username that the monitor will use to connect to the monitored databases. Depending on the monitoring module used this user will require specific privileges in order to determine the state of the nodes, details of those privileges can be found in the sections on each of the monitor modules.

View File

@ -74,7 +74,7 @@ Each mandatory rule accepts one or more optional parameters. These are to be def
#### `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, 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.
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. The rule uses local time to check if the rule is active and has a precision of one second.
#### `on_queries`

View File

@ -32,7 +32,7 @@ The regex filter accepts the options ignorecase or case. These define if the pat
The Regex filter requires two mandatory parameters to be defined.
### Match
### `match`
A parameter that can be used to match text in the SQL statement which should be replaced.
@ -42,7 +42,7 @@ match=TYPE[ ]*=
If the filter option ignorecase is used all regular expressions are evaluated with the option to ignore the case of the text, therefore a match option of select will match both type, TYPE and any form of the word with upper or lowercase characters.
### Replace
### `replace`
The replace parameter defines the text that should replace the text in the SQL text which matches the match.
@ -50,7 +50,7 @@ The replace parameter defines the text that should replace the text in the SQL t
replace=ENGINE =
```
### Source
### `source`
The optional source parameter defines an address that is used to match against the address from which the client connection to MaxScale originates. Only sessions that originate from this address will have the match and replacement applied to them.
@ -58,7 +58,7 @@ The optional source parameter defines an address that is used to match against t
source=127.0.0.1
```
### User
### `user`
The optional user parameter defines a user name that is used to match against the user from which the client connection to MaxScale originates. Only sessions that are connected using this username will have the match and replacement applied to them.
@ -66,6 +66,22 @@ The optional user parameter defines a user name that is used to match against th
user=john
```
### `log_file`
The optional log_file parameter defines a log file in which the filter writes all queries that are not mached and maching queries with their replacement queries. All sessions will log to this file so this should only be used for diagnostic purposes.
```
log_file=/tmp/regexfilter.log
```
### `log_trace`
The optional log_trace parameter toggles the logging of non-matching and matching queries with their replacements into the trace log file. This is the preferred method of diagnosing the matching of queries since the trace log can be disabled mid-session if such a need rises.
```
log_trace=true
```
## Examples
### Example 1 - Replace MySQL 5.1 create table syntax with that for later versions

11
cmake/FindJemalloc.cmake Normal file
View File

@ -0,0 +1,11 @@
# this CMake file defines the following variables
# JEMALLOC_FOUND - Jemalloc was found
# JEMALLOC_LIBRARIES - Jemalloc library
find_library(JEMALLOC_LIBRARIES NAMES jemalloc libjemalloc.so.4 libjemalloc.so.4.2.2)
if(JEMALLOC_LIBRARIES)
set(JEMALLOC_FOUND TRUE CACHE INTERNAL "")
message(STATUS "Found libjemalloc: ${JEMALLOC_LIBRARIES}")
else()
set(JEMALLOC_FOUND FALSE CACHE INTERNAL "")
message(STATUS "Could not find libjemalloc, using system default malloc instead.")
endif()

View File

@ -30,7 +30,7 @@ message(STATUS "MySQL version: ${MYSQL_VERSION}")
message(STATUS "MySQL provider: ${MYSQL_PROVIDER}")
if(NOT MYSQL_PROVIDER STREQUAL "MariaDB")
message(WARNING "Not using MariaDB server.")
message(WARNING "Not using a release version of MariaDB server. If this is intentional, please ignore this warning. Otherwise make sure the right libraries are installed and CMake finds the right libraries.")
endif()
if(MYSQL_VERSION VERSION_LESS 5.5.41)
message(WARNING "MySQL version is ${MYSQL_VERSION}. Minimum supported version is 5.5.41.")

View File

@ -77,6 +77,8 @@ stop() {
reload() {
log_daemon_msg "Reloading MaxScale"
kill -HUP $(cat $MAXSCALE_PIDFILE)
log_end_msg $?
}

View File

@ -54,7 +54,7 @@ static simple_mutex_t msg_mutex;
static int highprec = 0;
static int do_syslog = 1;
static int do_maxscalelog = 1;
static int use_stdout = 0;
/**
* Variable holding the enabled logfiles information.
* Used from log users to check enabled logs prior calling
@ -1331,12 +1331,14 @@ static bool logfile_set_enabled(
}
lf = &lm->lm_logfile[id];
CHK_LOGFILE(lf);
if (val) {
logstr = strdup("---\tLogging to file is enabled\t--");
} else {
logstr = strdup("---\tLogging to file is disabled\t--");
}
if(use_stdout == 0)
{
if (val) {
logstr = strdup("---\tLogging to file is enabled\t--");
} else {
logstr = strdup("---\tLogging to file is disabled\t--");
}
oldval = lf->lf_enabled;
lf->lf_enabled = val;
err = logmanager_write_log(id,
@ -1348,7 +1350,7 @@ static bool logfile_set_enabled(
logstr,
notused);
free(logstr);
}
if (err != 0) {
lf->lf_enabled = oldval;
fprintf(stderr,
@ -1445,7 +1447,7 @@ int skygw_log_write(
* Find out the length of log string (to be formatted str).
*/
va_start(valist, str);
len = vsnprintf(NULL, 0, str, valist);
len = vsnprintf(NULL, 0, str, valist);
va_end(valist);
/**
* Add one for line feed.
@ -1695,9 +1697,12 @@ static bool fnames_conf_init(
fn->fn_chk_tail = CHK_NUM_FNAMES;
#endif
optind = 1; /**<! reset getopt index */
while ((opt = getopt(argc, argv, "+a:b:c:d:e:f:g:h:i:j:l:m:s:")) != -1)
while ((opt = getopt(argc, argv, "+a:b:c:d:e:f:g:h:i:j:l:m:s:o")) != -1)
{
switch (opt) {
case 'o':
use_stdout = 1;
break;
case 'a':
fn->fn_debug_prefix = strndup(optarg, MAX_PREFIXLEN);
break;
@ -2153,8 +2158,14 @@ static bool logfile_open_file(
bool succp;
char* start_msg_str;
int err;
if (lf->lf_store_shmem)
if(use_stdout)
{
fw->fwr_file[lf->lf_id] = skygw_file_alloc (
lf->lf_full_file_name);
fw->fwr_file[lf->lf_id]->sf_file = stdout;
}
else if (lf->lf_store_shmem)
{
/** Create symlink pointing to log file */
fw->fwr_file[lf->lf_id] = skygw_file_init(
@ -2177,32 +2188,35 @@ static bool logfile_open_file(
succp = false;
goto return_succp;
}
if (lf->lf_enabled)
{
if(use_stdout == 0)
{
if (lf->lf_enabled)
{
start_msg_str = strdup("---\tLogging is enabled.\n");
}
else
{
}
else
{
start_msg_str = strdup("---\tLogging is disabled.\n");
}
err = skygw_file_write(fw->fwr_file[lf->lf_id],
(void *)start_msg_str,
strlen(start_msg_str),
true);
if (err != 0)
{
}
err = skygw_file_write(fw->fwr_file[lf->lf_id],
(void *)start_msg_str,
strlen(start_msg_str),
true);
if (err != 0)
{
fprintf(stderr,
"Error : writing to file %s failed due to %d, %s. "
"Error : writing to file %s failed due to %d, %s. "
"Exiting MaxScale.\n",
lf->lf_full_file_name,
err,
strerror(err));
lf->lf_full_file_name,
err,
strerror(err));
succp = false;
goto return_succp;
}
free(start_msg_str);
}
free(start_msg_str);
}
succp = true;
return_succp:
@ -2727,7 +2741,10 @@ static void filewriter_done(
for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++)
{
id = (logfile_id_t)i;
skygw_file_close(fw->fwr_file[id], true);
if(use_stdout)
skygw_file_free(fw->fwr_file[id]);
else
skygw_file_close(fw->fwr_file[id], true);
}
fw->fwr_state = DONE;
case DONE:
@ -2859,6 +2876,9 @@ static void* thr_filewriter_fun(
}
else if ((succp = logfile_open_file(fwr, lf)))
{
if(use_stdout)
skygw_file_free (file);
else
skygw_file_close(file, false); /*< close old file */
}

View File

@ -10,7 +10,7 @@ macro(set_maxscale_version)
# MaxScale version number
set(MAXSCALE_VERSION_MAJOR "1")
set(MAXSCALE_VERSION_MINOR "1")
set(MAXSCALE_VERSION_PATCH "0")
set(MAXSCALE_VERSION_PATCH "1")
set(MAXSCALE_VERSION_NUMERIC "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")
set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")

View File

@ -1204,15 +1204,15 @@ inline void add_str(char** buf, int* buflen, int* bufsize, char* str)
int isize = strlen(str) + 1;
if(*buf == NULL || isize + *buflen >= *bufsize)
{
char *tmp = (char*)calloc((*bufsize) * 2 + isize, sizeof(char));
if(tmp){
memcpy(tmp,*buf,*bufsize);
if(*buf){
free(*buf);
}
*buf = tmp;
*bufsize = (*bufsize) * 2 + isize;
*bufsize = (*bufsize) * 2 + isize;
char *tmp = (char*)realloc(*buf,(*bufsize)* sizeof(char));
if(tmp == NULL){
skygw_log_write_flush (LE,"Error: memory reallocation failed");
free(*buf);
*buf = NULL;
*bufsize = 0;
}
*buf = tmp;
}
if(*buflen > 0){
@ -1248,7 +1248,12 @@ char* skygw_get_affected_fields(GWBUF* buf)
}
lex->current_select = lex->all_selects_list;
if((where = (char*)malloc(sizeof(char)*1)) == NULL)
{
skygw_log_write_flush(LE,"Error: Memory allocation failed.");
return NULL;
}
*where = '\0';
while(lex->current_select)
{

View File

@ -2,14 +2,10 @@
#
# Global parameters
#
# Number of worker threads in MaxScale.
# Number of worker threads in MaxScale
#
# threads=<number of threads>
#
# Directory for the MaxScale log files. Default is /var/log/maxscale/.
#
# logdir=<path to directory>
#
# Enabled logfiles. The message log is enabled by default and
# the error log is always enabled.
#

View File

@ -1,10 +1,11 @@
if(BUILD_TESTS OR BUILD_TOOLS)
file(GLOB FULLCORE_SRC *.c)
add_library(fullcore STATIC ${FULLCORE_SRC})
if(WITH_TCMALLOC)
add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c)
if(WITH_JEMALLOC)
target_link_libraries(fullcore ${JEMALLOC_LIBRARIES})
elseif(WITH_TCMALLOC)
target_link_libraries(fullcore ${TCMALLOC_LIBRARIES})
endif()
target_link_libraries(fullcore ${CURL_LIBRARIES} log_manager utils pthread ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ssl aio rt crypt dl crypto inih z m stdc++)
target_link_libraries(fullcore ${CURL_LIBRARIES} utils log_manager pthread ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ssl aio rt crypt dl crypto inih z m stdc++)
endif()
add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c
@ -13,7 +14,9 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c
monitor.c adminusers.c secrets.c filter.c modutil.c hint.c
housekeeper.c memlog.c resultset.c)
if(WITH_TCMALLOC)
if(WITH_JEMALLOC)
target_link_libraries(maxscale ${JEMALLOC_LIBRARIES})
elseif(WITH_TCMALLOC)
target_link_libraries(maxscale ${TCMALLOC_LIBRARIES})
endif()

View File

@ -210,12 +210,33 @@ int rval;
conn = mysql_init(NULL);
if (conn) {
if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0)) {
char *ptr;
version_string = (char *)mysql_get_server_info(conn);
ptr = strstr(version_string, "-embedded");
char *ptr,*tmp;
tmp = (char *)mysql_get_server_info(conn);
unsigned int server_version = mysql_get_server_version(conn);
if(version_string)
free(version_string);
if((version_string = malloc(strlen(tmp) + strlen("5.5.5-") + 1)) == NULL)
return 0;
if (server_version >= 100000)
{
strcpy(version_string,"5.5.5-");
strcat(version_string,tmp);
}
else
{
strcpy(version_string,tmp);
}
ptr = strstr(tmp, "-embedded");
if (ptr) {
*ptr = '\0';
}
}
mysql_close(conn);
}
@ -1288,7 +1309,7 @@ int i;
}
else if (strcmp(name, "ms_timestamp") == 0)
{
skygw_set_highp(atoi(value));
skygw_set_highp(config_truth_value(value));
}
else
{
@ -1296,7 +1317,7 @@ int i;
{
if (strcasecmp(name, lognames[i].logname) == 0)
{
if (atoi(value))
if (config_truth_value(value))
skygw_log_enable(lognames[i].logfile);
else
skygw_log_disable(lognames[i].logfile);
@ -1475,10 +1496,10 @@ SERVER *server;
user,
auth);
if (enable_root_user)
serviceEnableRootUser(service, atoi(enable_root_user));
serviceEnableRootUser(service, config_truth_value(enable_root_user));
if (connection_timeout)
serviceSetTimeout(service, atoi(connection_timeout));
serviceSetTimeout(service, config_truth_value(connection_timeout));
if(auth_all_servers)
@ -1491,7 +1512,7 @@ SERVER *server;
if (allow_localhost_match_wildcard_host)
serviceEnableLocalhostMatchWildcardHost(
service,
atoi(allow_localhost_match_wildcard_host));
config_truth_value(allow_localhost_match_wildcard_host));
/** Read, validate and set max_slave_connections */
max_slave_conn_str =
@ -1631,7 +1652,7 @@ SERVER *server;
user,
auth);
if (enable_root_user)
serviceEnableRootUser(obj->element, atoi(enable_root_user));
serviceEnableRootUser(obj->element, config_truth_value(enable_root_user));
if (connection_timeout)
serviceSetTimeout(obj->element, atoi(connection_timeout));
@ -1639,7 +1660,7 @@ SERVER *server;
if (allow_localhost_match_wildcard_host)
serviceEnableLocalhostMatchWildcardHost(
obj->element,
atoi(allow_localhost_match_wildcard_host));
config_truth_value(allow_localhost_match_wildcard_host));
}
}
}
@ -2014,15 +2035,18 @@ bool config_set_qualified_param(
int
config_truth_value(char *str)
{
if (strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0)
if (strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0 ||
strcasecmp(str, "yes") == 0 || strcasecmp(str, "1") == 0)
{
return 1;
}
if (strcasecmp(str, "false") == 0 || strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0)
if (strcasecmp(str, "false") == 0 || strcasecmp(str, "off") == 0 ||
strcasecmp(str, "no") == 0|| strcasecmp(str, "0") == 0)
{
return 0;
}
return atoi(str);
skygw_log_write(LOGFILE_ERROR,"Error: Not a boolean value: %s",str);
return -1;
}

View File

@ -74,8 +74,6 @@
#include <execinfo.h>
#include <ini.h>
/** for procname */
#if !defined(_GNU_SOURCE)
# define _GNU_SOURCE
@ -115,11 +113,6 @@ const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1;
const char* default_cnf_fname = "etc/MaxScale.cnf";
const char* default_configdir = "/etc/";
const char* default_logdir = "/var/log/maxscale/";
const char* default_libdir = "/lib64/maxscale/lib/";
const char* default_moddir = "/lib64/maxscale/modules/";
static char* server_groups[] = {
"embedded",
"server",
@ -136,10 +129,6 @@ static char datadir[PATH_MAX+1] = "";
/* The data directory we created for this gateway instance */
static char pidfile[PATH_MAX+1] = "";
static char* configdir = NULL;
static char* logdir = NULL;
static char* libdir = NULL;
static char* moddir = NULL;
/**
* exit flag for log flusher.
*/
@ -161,14 +150,13 @@ static struct option long_options[] = {
{"config", required_argument, 0, 'f'},
{"nodaemon", no_argument, 0, 'd'},
{"log", required_argument, 0, 'l'},
{"logdir", required_argument, 0, 'L'},
{"syslog", required_argument, 0, 's'},
{"maxscalelog", required_argument, 0, 'S'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, '?'},
{0, 0, 0, 0}
};
static int cnf_preparser(void* data, const char* section, const char* name, const char* value);
static void log_flush_shutdown(void);
static void log_flush_cb(void* arg);
static int write_pid_file(char *); /* write MaxScale pidfile */
@ -180,7 +168,6 @@ static void write_footer(void);
static int ntfw_cb(const char*, const struct stat*, int, struct FTW*);
static bool file_is_readable(char* absolute_pathname);
static bool file_is_writable(char* absolute_pathname);
bool handle_path_arg(char** dest, char* path, char* arg, bool rd, bool wr);
static void usage(void);
static char* get_expanded_pathname(
char** abs_path,
@ -199,7 +186,7 @@ static bool resolve_maxscale_conf_fname(
static bool resolve_maxscale_homedir(
char** p_home_dir);
static char* check_dir_access(char* dirname,bool,bool);
static char* check_dir_access(char* dirname);
/**
* Handler for SIGHUP signal. Reload the configuration for the
@ -741,9 +728,8 @@ return_succp:
* read or write is not permitted.
*/
static char* check_dir_access(
char* dirname, bool rd, bool wr)
char* dirname)
{
char errbuf[PATH_MAX*2];
char* errstr = NULL;
if (dirname == NULL)
@ -751,27 +737,18 @@ static char* check_dir_access(
errstr = strdup("Directory argument is NULL");
goto retblock;
}
if(access(dirname,F_OK) != 0)
if (!file_is_readable(dirname))
{
sprintf(errbuf,"Can't access '%s'.",dirname);
errstr = strdup(errbuf);
goto retblock;
}
if (rd && !file_is_readable(dirname))
{
sprintf(errbuf,"MaxScale doesn't have read permission "
"to '%s'.",dirname);
errstr = strdup(errbuf);
errstr = strdup("MaxScale doesn't have read permission "
"to MAXSCALE_HOME.");
goto retblock;
}
if (wr && !file_is_writable(dirname))
if (!file_is_writable(dirname))
{
sprintf(errbuf,"MaxScale doesn't have write permission "
"to '%s'.",dirname);
errstr = strdup(errbuf);
errstr = strdup("MaxScale doesn't have write permission "
"to MAXSCALE_HOME. Exiting.");
goto retblock;
}
@ -1021,8 +998,6 @@ static void usage(void)
" (default: $MAXSCALE_HOME/etc/MaxScale.cnf)\n"
" -l|--log=... log to file or shared memory\n"
" -lfile or -lshm - defaults to shared memory\n"
" -L|--logdir=... path to log file directory\n"
" (default: /var/log/maxscale)\n"
" -s|--syslog= log messages to syslog."
" True or false - defaults to true\n"
" -S|--maxscalelog= log messages to MaxScale log."
@ -1087,8 +1062,6 @@ int main(int argc, char **argv)
char* cnf_file_path = NULL; /*< conf file, to be freed */
char* cnf_file_arg = NULL; /*< conf filename from cmd-line arg */
void* log_flush_thr = NULL;
char* tmp_path;
char* tmp_var;
int option_index;
int logtofile = 0; /* Use shared memory or file */
int syslog_enabled = 1; /** Log to syslog */
@ -1132,8 +1105,7 @@ int main(int argc, char **argv)
goto return_main;
}
}
while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?L:",
while ((opt = getopt_long(argc, argv, "dc:f:l:vs:S:?",
long_options, &option_index)) != -1)
{
bool succp = true;
@ -1238,14 +1210,6 @@ int main(int argc, char **argv)
succp = false;
}
break;
case 'L':
if(handle_path_arg(&tmp_path,optarg,NULL,true,false))
{
logdir = tmp_path;
}
break;
case 'S':
if(strstr(optarg,"="))
{
@ -1568,7 +1532,7 @@ int main(int argc, char **argv)
char* log_context = strdup("Home directory command-line argument");
char* errstr;
errstr = check_dir_access(home_dir,true,true);
errstr = check_dir_access(home_dir);
if (errstr != NULL)
{
@ -1602,12 +1566,6 @@ int main(int argc, char **argv)
free(log_context);
}
char pbuf[PATH_MAX];
sprintf(pbuf,"%s/etc/MaxScale.cnf",home_dir);
ini_parse(pbuf,cnf_preparser,NULL);
/**
* Init Log Manager for MaxScale.
* If $MAXSCALE_HOME is set then write the logs into $MAXSCALE_HOME/log.
@ -1619,27 +1577,22 @@ int main(int argc, char **argv)
char buf[1024];
char *argv[8];
bool succp;
/** Set log directory under $MAXSCALE_HOME/log */
sprintf(buf, "%s/log", home_dir);
/** Use default log directory /var/log/maxscale/ */
if(logdir == NULL)
if(mkdir(buf, 0777) != 0)
{
if(access(default_logdir,F_OK) != 0)
{
if(mkdir(logdir,0555) != 0)
if(errno != EEXIST)
{
fprintf(stderr,
"Error: Cannot create log directory: %s\n",
default_logdir);
goto return_main;
fprintf(stderr,
"Error: Cannot create log directory: %s\n",
buf);
goto return_main;
}
}
logdir = strdup(default_logdir);
}
argv[0] = "MaxScale";
argv[1] = "-j";
argv[2] = logdir;
argv[2] = buf;
if(!syslog_enabled)
{
@ -1722,11 +1675,11 @@ int main(int argc, char **argv)
fprintf(stderr,
"Home directory : %s"
"\nConfiguration file : %s"
"\nLog directory : %s"
"\nLog directory : %s/log"
"\nData directory : %s\n\n",
home_dir,
cnf_file_path,
logdir,
home_dir,
datadir);
}
LOGIF(LM, (skygw_log_write_flush(
@ -2050,73 +2003,3 @@ MaxScaleUptime()
{
return time(0) - MaxScaleStarted;
}
bool handle_path_arg(char** dest, char* path, char* arg, bool rd, bool wr)
{
char pathbuffer[PATH_MAX+2];
char* errstr;
bool rval = false;
if(path == NULL && arg == NULL)
return rval;
if(path)
{
snprintf(pathbuffer,PATH_MAX,"%s",path);
if(pathbuffer[strlen(path) - 1] != '/')
{
strcat(pathbuffer,"/");
}
if(arg && strlen(pathbuffer) + strlen(arg) + 1 < PATH_MAX)
strcat(pathbuffer,arg);
if((errstr = check_dir_access(pathbuffer,rd,wr)) == NULL)
{
*dest = strdup(pathbuffer);
rval = true;
}
else
{
fprintf(stderr,"%s\n",errstr);
free(errstr);
errstr = NULL;
}
}
return rval;
}
/**
* Pre-parse the MaxScale.cnf for config, log and module directories.
* @param data Parameter passed by inih
* @param section Section name
* @param name Parameter name
* @param value Parameter value
* @return 1 in all cases
*/
static int cnf_preparser(void* data, const char* section, const char* name, const char* value)
{
char pathbuffer[PATH_MAX];
char* errstr;
if(strcasecmp(section,"maxscale") == 0)
{
if(strcmp(name, "logdir") == 0)
{
/** logdir is only NULL if no command line parameter was given */
if(logdir == NULL)
handle_path_arg(&logdir,(char*)value,NULL,true,true);
}
else if(strcmp(name, "moddir") == 0)
{
handle_path_arg(&moddir,(char*)value,NULL,true,false);
}
else if(strcmp(name, "libdir") == 0)
{
handle_path_arg(&libdir,(char*)value,NULL,true,false);
}
}
return 1;
}

View File

@ -33,7 +33,7 @@
#include <log_manager.h>
int main(int argc, char **argv)
{
int arg_count = 3;
int arg_count = 4;
char *home;
char** arg_vector;
@ -44,7 +44,7 @@ int main(int argc, char **argv)
exit(1);
}
arg_vector = malloc(sizeof(char*)*4);
arg_vector = malloc(sizeof(char*)*5);
if(arg_vector == NULL)
{
@ -64,8 +64,8 @@ int main(int argc, char **argv)
{
arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log");
}
arg_vector[3] = NULL;
arg_vector[3] = "-o";
arg_vector[4] = NULL;
skygw_logmanager_init(arg_count,arg_vector);
skygw_log_enable(LOGFILE_TRACE);
skygw_log_enable(LOGFILE_DEBUG);

View File

@ -41,7 +41,7 @@ int
main(int argc, char **argv)
{
char *enc, *pw;
int arg_count = 3;
int arg_count = 4;
char *home;
char** arg_vector;
@ -52,7 +52,7 @@ main(int argc, char **argv)
exit(1);
}
arg_vector = malloc(sizeof(char*)*4);
arg_vector = malloc(sizeof(char*)*5);
if(arg_vector == NULL)
{
@ -73,7 +73,8 @@ main(int argc, char **argv)
arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log");
}
arg_vector[3] = NULL;
arg_vector[3] = "-o";
arg_vector[4] = NULL;
skygw_logmanager_init(arg_count,arg_vector);
skygw_log_enable(LOGFILE_TRACE);
skygw_log_enable(LOGFILE_DEBUG);

View File

@ -538,7 +538,7 @@ return_packetbuf:
GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf)
{
GWBUF *buff = NULL, *packet;
uint8_t *ptr,*end;
uint8_t *ptr;
int len,blen,total = 0;
if(p_readbuf == NULL || (*p_readbuf) == NULL ||
@ -583,7 +583,6 @@ GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf)
"Error: Failed to partially clone buffer.");
return NULL;
}
gwbuf_consume(packet,total);
return buff;
}

View File

@ -15,11 +15,11 @@ add_executable(test_adminusers testadminusers.c)
add_executable(testmemlog testmemlog.c)
add_executable(testfeedback testfeedback.c)
target_link_libraries(test_mysql_users MySQLClient fullcore)
target_link_libraries(test_hash fullcore)
target_link_libraries(test_hint fullcore)
target_link_libraries(test_spinlock fullcore)
target_link_libraries(test_hash fullcore log_manager)
target_link_libraries(test_hint fullcore log_manager)
target_link_libraries(test_spinlock fullcore log_manager)
target_link_libraries(test_filter fullcore)
target_link_libraries(test_buffer fullcore)
target_link_libraries(test_buffer fullcore log_manager)
target_link_libraries(test_dcb fullcore)
target_link_libraries(test_modutil fullcore)
target_link_libraries(test_poll fullcore)
@ -27,7 +27,7 @@ target_link_libraries(test_service fullcore)
target_link_libraries(test_server fullcore)
target_link_libraries(test_users fullcore)
target_link_libraries(test_adminusers fullcore)
target_link_libraries(testmemlog fullcore)
target_link_libraries(testmemlog fullcore log_manager)
target_link_libraries(testfeedback fullcore)
add_test(Internal-TestMySQLUsers test_mysql_users)
add_test(Internal-TestHash test_hash)

View File

@ -1,2 +1 @@
add_library(inih ini.c)
target_compile_definitions(inih PUBLIC INI_MAX_LINE=1024)
add_library(inih ini.c)

View File

@ -86,6 +86,14 @@ MODULE_INFO info = {
static char *version_str = "V1.0.0";
static char* required_rules[] = {
"wildcard",
"columns",
"regex",
"limit_queries",
"no_where_clause",
NULL
};
/*
* The filter entry points
*/
@ -456,9 +464,9 @@ bool check_time(char* str)
#ifdef SS_DEBUG
#define CHK_TIMES(t)(ss_dassert(t->tm_sec > -1 && t->tm_sec < 62 \
#define CHK_TIMES(t) ss_dassert(t->tm_sec > -1 && t->tm_sec < 62 \
&& t->tm_min > -1 && t->tm_min < 60 \
&& t->tm_hour > -1 && t->tm_hour < 24))
&& t->tm_hour > -1 && t->tm_hour < 24)
#else
#define CHK_TIMES(t)
#endif
@ -861,6 +869,10 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
bool allow,deny,mode;
RULE* ruledef = NULL;
bool rval = true;
bool req_defined,oq_def,at_def;
int i;
req_defined = oq_def = at_def = false;
if(tok == NULL)
{
@ -920,7 +932,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
}
else
{
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule file: %s",tok);
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule '%s': %s",rule,tok);
rval = false;
goto retblock;
}
@ -946,6 +958,25 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
while(tok)
{
reparse_rule:
for(i = 0;required_rules[i] != NULL;i++)
{
if(strcmp(tok,required_rules[i]) == 0)
{
if(req_defined)
{
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, Multiple non-optional rules: %s",rule);
rval = false;
goto retblock;
}
else
{
req_defined = true;
}
}
}
if(strcmp(tok,"wildcard") == 0)
{
ruledef->type = RT_WILDCARD;
@ -970,11 +1001,18 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
}
else if(strcmp(tok,"at_times") == 0)
{
if(at_def)
{
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, multiple 'at_times' tokens: %s",rule);
rval = false;
goto retblock;
}
at_def = true;
tok = strtok_r(NULL, " ,",&saveptr);
TIMERANGE *tr = NULL;
while(tok){
if(strcmp(tok,"on_queries") == 0)
break;
if(!check_time(tok))
{
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, malformed time definition: %s",tok);
@ -1000,8 +1038,12 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
tr = tmp;
tok = strtok_r(NULL, " ,",&saveptr);
}
ruledef->active = tr;
if(tok && strcmp(tok,"on_queries") == 0)
goto reparse_rule;
}
else if(strcmp(tok,"regex") == 0)
{
@ -1119,14 +1161,24 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule parsing failed, not a number: '%s'.", tok);
goto retblock;
}
if(qs->limit < 1){
free(qs);
rval = false;
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad query amount: %s", tok);
goto retblock;
}
errptr = NULL;
tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL){
free(qs);
rval = false;
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
goto retblock;
}
qs->period = strtod(tok,&errptr);
if(errptr && *errptr != '\0')
@ -1136,9 +1188,17 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule parsing failed, not a number: '%s'.", tok);
goto retblock;
}
errptr = NULL;
if(qs->period < 1){
free(qs);
rval = false;
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad time period: %s", tok);
goto retblock;
}
errptr = NULL;
tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL){
free(qs);
rval = false;
@ -1155,6 +1215,13 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
goto retblock;
}
if(qs->cooldown < 1){
free(qs);
rval = false;
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad blocking period: %s", tok);
goto retblock;
}
ruledef->type = RT_THROTTLE;
ruledef->data = (void*)qs;
}
@ -1165,6 +1232,13 @@ bool parse_rule(char* rule, FW_INSTANCE* instance)
}
else if(strcmp(tok,"on_queries") == 0)
{
if(oq_def)
{
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, multiple 'on_queries' tokens: %s",rule);
rval = false;
goto retblock;
}
oq_def = true;
tok = strtok_r(NULL," ",&saveptr);
if(tok == NULL)
@ -1650,29 +1724,36 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
case RT_COLUMN:
if(is_sql && is_real){
strln = (STRLINK*)rulelist->rule->data;
if(is_sql && is_real)
{
where = skygw_get_affected_fields(queue);
if(where != NULL){
char* saveptr;
char* tok = strtok_r(where," ",&saveptr);
while(tok)
{
strln = (STRLINK*)rulelist->rule->data;
while(strln)
{
if(strcasecmp(tok,strln->value) == 0)
{
matches = true;
while(strln){
if(strstr(where,strln->value)){
matches = true;
if(!rulelist->rule->allow){
sprintf(emsg,"Permission denied to column '%s'.",strln->value);
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
msg = strdup(emsg);
goto queryresolved;
}else{
break;
}
}
strln = strln->next;
}
if(!rulelist->rule->allow)
{
sprintf(emsg,"Permission denied to column '%s'.",strln->value);
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
msg = strdup(emsg);
goto queryresolved;
}
else
break;
}
strln = strln->next;
}
tok = strtok_r(NULL,",",&saveptr);
}
free(where);
}
}
@ -1687,16 +1768,16 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
if(where != NULL){
strptr = where;
}else{
strptr = query;
}
if(strchr(strptr,'*')){
matches = true;
msg = strdup("Usage of wildcard denied.");
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
goto queryresolved;
}
if(strchr(strptr,'*')){
matches = true;
msg = strdup("Usage of wildcard denied.");
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
goto queryresolved;
}
free(where);
}
}
break;
@ -2144,15 +2225,11 @@ int main(int argc, char** argv)
}
printf("Log files written to: %s\n",home?home:"/tpm");
int argc_ = 11;
int argc_ = 2;
char* argv_[] =
{
"log_manager",
"-j",home?home:"/tmp",
"-a","ruleparser_debug",
"-c","ruleparser_trace",
"-e","ruleparser_message",
"-g","ruleparser_error",
"-o",
NULL
};

View File

@ -95,7 +95,7 @@ static FILTER_OBJECT MyObject = {
* are logged.
*
* To this base a session number is attached such that each session will
* have a nique name.
* have a unique name.
*/
typedef struct {
int sessions; /* The count of sessions */

View File

@ -24,6 +24,8 @@
#include <string.h>
#include <regex.h>
#include "maxconfig.h"
/** Defined in log_manager.cc */
extern int lm_enabled_logfiles_bitmask;
extern size_t log_ses_count[];
@ -86,6 +88,8 @@ typedef struct {
char *match; /* Regular expression to match */
char *replace; /* Replacement text */
regex_t re; /* Compiled regex text */
FILE* logfile;
bool log_trace;
} REGEX_INSTANCE;
/**
@ -98,6 +102,9 @@ typedef struct {
int active; /* Is filter active */
} REGEX_SESSION;
void log_match(REGEX_INSTANCE* inst,char* re, char* old, char* new);
void log_nomatch(REGEX_INSTANCE* inst, char* re, char* old);
/**
* Implementation of the mandatory version entry point
*
@ -146,7 +153,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
{
REGEX_INSTANCE *my_instance;
int i, cflags = REG_ICASE;
char *logfile = NULL;
if ((my_instance = calloc(1, sizeof(REGEX_INSTANCE))) != NULL)
{
my_instance->match = NULL;
@ -162,6 +169,10 @@ int i, cflags = REG_ICASE;
my_instance->source = strdup(params[i]->value);
else if (!strcmp(params[i]->name, "user"))
my_instance->user = strdup(params[i]->value);
else if (!strcmp(params[i]->name, "log_trace"))
my_instance->log_trace = config_truth_value(params[i]->value);
else if (!strcmp(params[i]->name, "log_file"))
logfile = strdup(params[i]->value);
else if (!filter_standard_parameter(params[i]->name))
{
LOGIF(LE, (skygw_log_write_flush(
@ -209,6 +220,25 @@ int i, cflags = REG_ICASE;
free(my_instance);
return NULL;
}
if(logfile != NULL)
{
if((my_instance->logfile = fopen(logfile,"a")) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"regexfilter: Failed to open file '%s'.\n",
logfile)));
free(my_instance->match);
free(my_instance->replace);
free(my_instance);
free(logfile);
return NULL;
}
fprintf(my_instance->logfile,"\nOpened regex filter log\n");
fflush(my_instance->logfile);
}
free(logfile);
}
return (FILTER *)my_instance;
}
@ -320,11 +350,15 @@ char *sql, *newsql;
{
queue = modutil_replace_SQL(queue, newsql);
queue = gwbuf_make_contiguous(queue);
log_match(my_instance,my_instance->match,sql,newsql);
free(newsql);
my_session->replacements++;
}
else
{
log_nomatch(my_instance,my_instance->match,sql);
my_session->no_change++;
}
free(sql);
}
@ -441,3 +475,43 @@ regmatch_t match[10];
return result;
}
/**
* Log a matching query to either MaxScale's trace log or a separate log file.
* The old SQL and the new SQL statements are printed in the log.
* @param inst Regex filter instance
* @param re Regular expression
* @param old Old SQL statement
* @param new New SQL statement
*/
void log_match(REGEX_INSTANCE* inst, char* re, char* old, char* new)
{
if(inst->logfile)
{
fprintf(inst->logfile,"Matched %s: [%s] -> [%s]\n",re,old,new);
fflush(inst->logfile);
}
if(inst->log_trace)
{
LOGIF(LT,(skygw_log_write(LT,"Match %s: [%s] -> [%s]",re,old,new)));
}
}
/**
* Log a non-matching query to either MaxScale's trace log or a separate log file.
* @param inst Regex filter instance
* @param re Regular expression
* @param old SQL statement
*/
void log_nomatch(REGEX_INSTANCE* inst, char* re, char* old)
{
if(inst->logfile)
{
fprintf(inst->logfile,"No match %s: [%s]\n",re,old);
fflush(inst->logfile);
}
if(inst->log_trace)
{
LOGIF(LT,(skygw_log_write(LT,"No match %s: [%s]",re,old)));
}
}

View File

@ -41,10 +41,10 @@ extern __thread log_info_t tls_log_info;
* Two optional parameters that define the behavior after a data modifying query
* is executed:
*
* count=<number of queries> Queries to route to master after data modification.
* time=<time period> Seconds to wait before queries are routed to slaves.
* match=<regex> Regex for matching
* ignore=<regex> Regex for ignoring
* count=<number of queries> Queries to route to master after data modification.
* time=<time period> Seconds to wait before queries are routed to slaves.
* match=<regex> Regex for matching
* ignore=<regex> Regex for ignoring
*
* The filter also has two options: @c case, which makes the regex case-sensitive, and @c ignorecase, which does the opposite.
* Date Who Description

View File

@ -1,13 +1,7 @@
aux_source_directory(${CMAKE_SOURCE_DIR}/server/core CORE_ALL)
foreach(VAR ${CORE_ALL})
if(NOT( (${VAR} MATCHES "max[a-z_]*.c") OR (${VAR} MATCHES "gateway.c")))
list(APPEND CORE ${VAR})
endif()
endforeach()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(harness_ui harness_ui.c harness_common.c)
add_executable(harness harness_util.c harness_common.c ${CORE})
add_executable(harness harness_util.c harness_common.c)
target_link_libraries(harness_ui fullcore log_manager utils)
target_link_libraries(harness fullcore)
execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR})

View File

@ -0,0 +1,18 @@
#ifndef _SHARDING_COMMON_HG
#define _SHARDING_COMMON_HG
#include <poll.h>
#include <buffer.h>
#include <modutil.h>
#include <mysql_client_server_protocol.h>
#include <hashtable.h>
#include <log_manager.h>
#include <query_classifier.h>
bool extract_database(GWBUF* buf, char* str);
void create_error_reply(char* fail_str,DCB* dcb);
bool change_current_db(MYSQL_session* mysql_session,
HASHTABLE* dbhash,
GWBUF* buf);
#endif

View File

@ -531,18 +531,20 @@ char *server_string;
}
/* get variable 'read_only' set by an external component */
if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result)))
{
if (strncasecmp(row[1], "OFF", 3) == 0) {
ismaster = 1;
}
}
mysql_free_result(result);
}
if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result)))
{
if (strncasecmp(row[1], "OFF", 3) == 0) {
ismaster = 1;
} else {
isslave = 1;
}
}
mysql_free_result(result);
}
/* Remove addition info */
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
@ -563,7 +565,7 @@ char *server_string;
}
/* Set the Master role */
if (isslave && ismaster)
if (ismaster)
{
monitor_clear_pending_status(database, SERVER_SLAVE);
monitor_set_pending_status(database, SERVER_MASTER);

View File

@ -796,7 +796,7 @@ int log_no_master = 1;
mon_status_changed(root_master) &&
!(root_master->server->status & SERVER_STALE_STATUS))
{
if (root_master->pending_status & (SERVER_MASTER)) {
if (root_master->pending_status & (SERVER_MASTER) && SERVER_IS_RUNNING(root_master->server)) {
if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) &&
!(root_master->server->status & SERVER_MAINT))
{

View File

@ -5,14 +5,6 @@ if(BUILD_TESTS)
install(TARGETS testroute DESTINATION modules)
endif()
add_library(schemarouter SHARED schemarouter/schemarouter.c)
target_link_libraries(schemarouter log_manager utils query_classifier)
install(TARGETS schemarouter DESTINATION modules)
add_library(shardrouter SHARED schemarouter/shardrouter.c)
target_link_libraries(shardrouter log_manager utils query_classifier)
install(TARGETS shardrouter DESTINATION modules)
add_library(readconnroute SHARED readconnroute.c)
target_link_libraries(readconnroute log_manager utils)
install(TARGETS readconnroute DESTINATION modules)
@ -26,7 +18,7 @@ target_link_libraries(cli log_manager utils)
install(TARGETS cli DESTINATION modules)
add_subdirectory(readwritesplit)
add_subdirectory(schemarouter/test)
add_subdirectory(schemarouter)
if(BUILD_BINLOG)
add_subdirectory(binlog)
endif()

View File

@ -258,6 +258,7 @@ int fd;
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
"%s: binlog file %s has an invalid length %d.",
router->service->name, path, router->binlog_position)));
close(fd);
return;
}
}

View File

@ -1883,7 +1883,6 @@ static int routeQuery(
bool succp = false;
CHK_CLIENT_RSES(router_cli_ses);
/**
* GWBUF is called "type undefined" when the incoming data isn't parsed
* and MySQL packets haven't been extracted to separate buffers.
@ -3716,13 +3715,11 @@ static GWBUF* sescmd_cursor_process_replies(
mysql_sescmd_t* scmd;
sescmd_cursor_t* scur;
ROUTER_CLIENT_SES* ses;
ROUTER_INSTANCE* router;
scur = &bref->bref_sescmd_cur;
ss_dassert(SPINLOCK_IS_LOCKED(&(scur->scmd_cur_rses->rses_lock)));
scmd = sescmd_cursor_get_command(scur);
ses = (*scur->scmd_cur_ptr_property)->rses_prop_rsession;
router = ses->router;
CHK_GWBUF(replybuf);
/**

View File

@ -0,0 +1,11 @@
add_library(schemarouter SHARED schemarouter.c sharding_common.c)
target_link_libraries(schemarouter log_manager utils query_classifier)
install(TARGETS schemarouter DESTINATION modules)
add_library(shardrouter SHARED shardrouter.c svcconn.c sharding_common.c)
target_link_libraries(shardrouter log_manager utils query_classifier)
install(TARGETS shardrouter DESTINATION modules)
if(BUILD_TESTS)
add_subdirectory(test)
endif()

View File

@ -23,6 +23,7 @@
#include <stdint.h>
#include <router.h>
#include <schemarouter.h>
#include <sharding_common.h>
#include <secrets.h>
#include <mysql.h>
#include <skygw_utils.h>
@ -208,12 +209,6 @@ static ROUTER_INSTANCE* instances;
static int hashkeyfun(void* key);
static int hashcmpfun (void *, void *);
static bool change_current_db(
ROUTER_INSTANCE* inst,
ROUTER_CLIENT_SES* rses,
GWBUF* buf);
static int hashkeyfun(void* key)
{
if(key == NULL){
@ -1169,6 +1164,10 @@ static void closeSession(
dcb_close(router_cli_ses->dcb_reply);
dcb_close(router_cli_ses->dcb_route);
if(router_cli_ses->queue)
router_cli_ses->queue = gwbuf_consume(
router_cli_ses->queue,gwbuf_length(router_cli_ses->queue));
/** Unlock */
rses_end_locked_router_action(router_cli_ses);
@ -1178,7 +1177,7 @@ static void closeSession(
double ses_time = difftime(time(NULL),router_cli_ses->rses_client_dcb->session->stats.connect);
if(inst->stats.ses_longest < ses_time)
inst->stats.ses_longest = ses_time;
if(inst->stats.ses_shortest > ses_time)
if(inst->stats.ses_shortest > ses_time && inst->stats.ses_shortest > 0)
inst->stats.ses_shortest = ses_time;
inst->stats.ses_average =
@ -1824,12 +1823,12 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
static int routeQuery(
ROUTER* instance,
void* router_session,
GWBUF* querybuf)
GWBUF* qbuf)
{
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
mysql_server_cmd_t packet_type;
uint8_t* packet;
int ret = 0;
int i,ret = 0;
DCB* target_dcb = NULL;
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
@ -1838,8 +1837,9 @@ static int routeQuery(
route_target_t route_target = TARGET_UNDEFINED;
bool succp = false;
char* tname = NULL;
GWBUF* querybuf = qbuf;
char db[MYSQL_DATABASE_MAXLEN + 1];
char errbuf[26+MYSQL_DATABASE_MAXLEN];
CHK_CLIENT_RSES(router_cli_ses);
/** Dirty read for quick check if router is closed. */
@ -1875,7 +1875,7 @@ static int routeQuery(
router_cli_ses->rses_client_dcb->session,
querystr);
free(querystr);
gwbuf_make_contiguous(querybuf);
querybuf = gwbuf_make_contiguous(querybuf);
GWBUF* ptr = router_cli_ses->queue;
while(ptr && ptr->next)
@ -2012,12 +2012,25 @@ static int routeQuery(
if (packet_type == MYSQL_COM_INIT_DB ||
op == QUERY_OP_CHANGE_DB)
{
if (!(change_successful = change_current_db(inst, router_cli_ses, querybuf)))
if (!(change_successful = change_current_db(router_cli_ses->rses_mysql_session,
router_cli_ses->dbhash,
querybuf)))
{
extract_database(querybuf,db);
snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db);
for(i = 0;i<router_cli_ses->rses_nbackends;i++)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Changing database failed.")));
if(SERVER_IS_RUNNING(router_cli_ses->rses_backend_ref[i].bref_backend->backend_server))
{
create_error_reply(errbuf,router_cli_ses->rses_backend_ref[i].bref_dcb);
break;
}
}
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Changing database failed.")));
}
}
if(QUERY_IS_TYPE(qtype, QUERY_TYPE_SHOW_DATABASES))
@ -2747,8 +2760,7 @@ static void clientReply (
/** There is one pending session command to be executed. */
if (sescmd_cursor_is_active(scur))
{
bool succp;
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Backend %s:%d processed reply and starts to execute "
@ -2756,9 +2768,7 @@ static void clientReply (
bref->bref_backend->backend_server->name,
bref->bref_backend->backend_server->port)));
succp = execute_sescmd_in_backend(bref);
ss_dassert(succp);
execute_sescmd_in_backend(bref);
}
else if (bref->bref_pending_cmd != NULL) /*< non-sescmd is waiting to be routed */
{
@ -4312,8 +4322,6 @@ router_handle_state_switch(
{
backend_ref_t* bref;
int rc = 1;
ROUTER_CLIENT_SES* rses;
SESSION* ses;
SERVER* srv;
CHK_DCB(dcb);
@ -4326,12 +4334,7 @@ router_handle_state_switch(
{
goto return_rc;
}
ses = dcb->session;
CHK_SESSION(ses);
rses = (ROUTER_CLIENT_SES *) dcb->session->router_session;
CHK_CLIENT_RSES(rses);
switch(reason)
{
case DCB_REASON_NOT_RESPONDING:
@ -4361,172 +4364,4 @@ static sescmd_cursor_t* backend_ref_get_sescmd_cursor (
return scur;
}
/**
* Read new database name from MYSQL_COM_INIT_DB packet, check that it exists
* in the hashtable and copy its name to MYSQL_session.
*
* @param inst Router instance
* @param rses Router client session
* @param buf Query buffer
*
* @return true if new database is set, false if non-existent database was tried
* to be set
*/
static bool change_current_db(
ROUTER_INSTANCE* inst,
ROUTER_CLIENT_SES* rses,
GWBUF* buf)
{
bool succp;
uint8_t* packet;
unsigned int plen;
int message_len;
char* fail_str,*target;
if(GWBUF_LENGTH(buf) <= MYSQL_DATABASE_MAXLEN - 5)
{
packet = GWBUF_DATA(buf);
plen = gw_mysql_get_byte3(packet) - 1;
/** Copy database name from MySQL packet to session */
if(query_classifier_get_operation(buf) == QUERY_OP_CHANGE_DB)
{
char* query = modutil_get_SQL(buf);
char *saved,*tok;
tok = strtok_r(query," ;",&saved);
if(tok == NULL || strcasecmp(tok,"use") != 0)
{
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
free(query);
message_len = 25 + MYSQL_DATABASE_MAXLEN;
fail_str = calloc(1, message_len+1);
snprintf(fail_str,
message_len,
"Unknown database '%s'",
(char*)rses->rses_mysql_session->db);
rses->rses_mysql_session->db[0] = '\0';
succp = false;
goto reply_error;
}
tok = strtok_r(NULL," ;",&saved);
if(tok == NULL)
{
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
free(query);
message_len = 25 + MYSQL_DATABASE_MAXLEN;
fail_str = calloc(1, message_len+1);
snprintf(fail_str,
message_len,
"Unknown database '%s'",
(char*)rses->rses_mysql_session->db);
rses->rses_mysql_session->db[0] = '\0';
succp = false;
goto reply_error;
}
strncpy(rses->rses_mysql_session->db,tok,MYSQL_DATABASE_MAXLEN);
free(query);
query = NULL;
}
else
{
memcpy(rses->rses_mysql_session->db,packet + 5,plen);
memset(rses->rses_mysql_session->db + plen,0,1);
}
skygw_log_write(LOGFILE_TRACE,"schemarouter: INIT_DB with database '%s'",
rses->rses_mysql_session->db);
/**
* Update the session's active database only if it's in the hashtable.
* If it isn't found, send a custom error packet to the client.
*/
if((target = (char*)hashtable_fetch(
rses->dbhash,
(char*)rses->rses_mysql_session->db)) == NULL)
{
/** Create error message */
message_len = 25 + MYSQL_DATABASE_MAXLEN;
fail_str = calloc(1, message_len+1);
snprintf(fail_str,
message_len,
"Unknown database '%s'",
(char*)rses->rses_mysql_session->db);
rses->rses_mysql_session->db[0] = '\0';
succp = false;
goto reply_error;
}
else
{
skygw_log_write(LOGFILE_TRACE,"schemarouter: database is on server: '%s'.",target);
succp = true;
goto retblock;
}
}
else
{
/** Create error message */
skygw_log_write_flush(LOGFILE_ERROR,
"schemarouter: failed to change database: Query buffer too large");
skygw_log_write_flush(LOGFILE_TRACE,
"schemarouter: failed to change database: Query buffer too large [%d bytes]",GWBUF_LENGTH(buf));
message_len = 25 + MYSQL_DATABASE_MAXLEN;
fail_str = calloc(1, message_len+1);
snprintf(fail_str,
message_len,
"Unknown database '%s'",
(char*)rses->rses_mysql_session->db);
succp = false;
goto reply_error;
}
reply_error:
{
GWBUF* errbuf;
skygw_log_write_flush(
LOGFILE_TRACE,
"schemarouter: failed to change database: %s", fail_str);
errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
free(fail_str);
if (errbuf == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Creating buffer for error message failed.")));
goto retblock;
}
/** Set flags that help router to identify session commans reply */
gwbuf_set_type(errbuf, GWBUF_TYPE_MYSQL);
gwbuf_set_type(errbuf, GWBUF_TYPE_SESCMD_RESPONSE);
gwbuf_set_type(errbuf, GWBUF_TYPE_RESPONSE_END);
/**
* Create an incoming event for randomly selected backend DCB which
* will then be notified and replied 'back' to the client.
*/
DCB *dcb = NULL;
int i;
for(i = 0;i<rses->rses_nbackends;i++)
{
if(rses->rses_backend_ref[i].bref_dcb){
dcb = rses->rses_backend_ref[i].bref_dcb;
break;
}
}
if(dcb == NULL)
{
skygw_log_write_flush(LOGFILE_ERROR,"Error : All backend connections are down.");
return false;
}
poll_add_epollin_event_to_dcb(rses->dcb_reply,
gwbuf_clone(errbuf));
gwbuf_free(errbuf);
}
retblock:
return succp;
}

View File

@ -0,0 +1,161 @@
/*
* 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
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright MariaDB Corporation Ab 2013-2015
*/
#include <sharding_common.h>
/** Defined in log_manager.cc */
extern int lm_enabled_logfiles_bitmask;
extern size_t log_ses_count[];
extern __thread log_info_t tls_log_info;
/**
* Extract the database name from a COM_INIT_DB or literal USE ... query.
* @param buf Buffer with the database change query
* @param str Pointer where the database name is copied
* @return True for success, false for failure
*/
bool extract_database(GWBUF* buf, char* str)
{
uint8_t* packet;
char *saved,*tok,*query = NULL;
bool succp = true;
unsigned int plen;
packet = GWBUF_DATA(buf);
plen = gw_mysql_get_byte3(packet) - 1;
/** Copy database name from MySQL packet to session */
if(query_classifier_get_operation(buf) == QUERY_OP_CHANGE_DB)
{
query = modutil_get_SQL(buf);
tok = strtok_r(query," ;",&saved);
if(tok == NULL || strcasecmp(tok,"use") != 0)
{
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
succp = false;
goto retblock;
}
tok = strtok_r(NULL," ;",&saved);
if(tok == NULL)
{
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
succp = false;
goto retblock;
}
strncpy(str,tok,MYSQL_DATABASE_MAXLEN);
}
else
{
memcpy(str,packet + 5,plen);
memset(str + plen,0,1);
}
retblock:
free(query);
return succp;
}
/**
* Create a fake error message from a DCB.
* @param fail_str Custom error message
* @param dcb DCB to use as the origin of the error
*/
void create_error_reply(char* fail_str,DCB* dcb)
{
skygw_log_write_flush(
LOGFILE_TRACE,
"change_current_db: failed to change database: %s", fail_str);
GWBUF* errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
if (errbuf == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Creating buffer for error message failed.")));
return;
}
/** Set flags that help router to identify session commands reply */
gwbuf_set_type(errbuf, GWBUF_TYPE_MYSQL);
gwbuf_set_type(errbuf, GWBUF_TYPE_SESCMD_RESPONSE);
gwbuf_set_type(errbuf, GWBUF_TYPE_RESPONSE_END);
poll_add_epollin_event_to_dcb(dcb,
errbuf);
}
/**
* Read new database name from MYSQL_COM_INIT_DB packet or a literal USE ... COM_QUERY packet, check that it exists
* in the hashtable and copy its name to MYSQL_session.
*
* @param mysql_session The MySQL session structure
* @param dbhash Hashtable containing valid databases
* @param buf Buffer containing the database change query
*
* @return true if new database is set, false if non-existent database was tried
* to be set
*/
bool change_current_db(MYSQL_session* mysql_session,
HASHTABLE* dbhash,
GWBUF* buf)
{
char* target;
bool succp;
char db[MYSQL_DATABASE_MAXLEN+1];
if(GWBUF_LENGTH(buf) <= MYSQL_DATABASE_MAXLEN - 5)
{
/** Copy database name from MySQL packet to session */
if(!extract_database(buf,db))
{
succp = false;
goto retblock;
}
skygw_log_write(LOGFILE_TRACE,"change_current_db: INIT_DB with database '%s'",
db);
/**
* Update the session's active database only if it's in the hashtable.
* If it isn't found, send a custom error packet to the client.
*/
if((target = (char*)hashtable_fetch(dbhash,(char*)db)) == NULL)
{
succp = false;
goto retblock;
}
else
{
strncpy(mysql_session->db,db,MYSQL_DATABASE_MAXLEN);
skygw_log_write(LOGFILE_TRACE,"change_current_db: database is on server: '%s'.",target);
succp = true;
goto retblock;
}
}
else
{
/** Create error message */
skygw_log_write_flush(LOGFILE_ERROR,
"change_current_db: failed to change database: Query buffer too large");
skygw_log_write_flush(LOGFILE_TRACE,
"change_current_db: failed to change database: Query buffer too large [%d bytes]",GWBUF_LENGTH(buf));
succp = false;
goto retblock;
}
retblock:
return succp;
}

View File

@ -13,8 +13,9 @@
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright MariaDB Corporation Ab 2013-2014
* Copyright MariaDB Corporation Ab 2013-2015
*/
#include <my_config.h>
#include <stdio.h>
#include <strings.h>
@ -24,6 +25,7 @@
#include <router.h>
#include <shardrouter.h>
#include <sharding_common.h>
#include <secrets.h>
#include <mysql.h>
#include <skygw_utils.h>
@ -119,9 +121,9 @@ static route_target_t get_shard_route_target(
static uint8_t getCapabilities(ROUTER* inst, void* router_session);
static void subsvc_clear_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);
void subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state);
void subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state);
bool get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target);
static ROUTER_OBJECT MyObject = {
createInstance,
@ -205,19 +207,6 @@ static void refreshInstance(
CONFIG_PARAMETER* param);
static int router_handle_state_switch(DCB* dcb, DCB_REASON reason, void* data);
/*
static bool handle_error_new_connection(
ROUTER_INSTANCE* inst,
ROUTER_CLIENT_SES* rses,
DCB* backend_dcb,
GWBUF* errmsg);
static void handle_error_reply_client(
SESSION* ses,
ROUTER_CLIENT_SES* rses,
DCB* backend_dcb,
GWBUF* errmsg);
*/
static SPINLOCK instlock;
static ROUTER_INSTANCE* instances;
@ -225,11 +214,6 @@ static ROUTER_INSTANCE* instances;
static int hashkeyfun(void* key);
static int hashcmpfun(void *, void *);
static bool change_current_db(
ROUTER_INSTANCE* inst,
ROUTER_CLIENT_SES* rses,
GWBUF* buf);
static int
hashkeyfun(void* key)
{
@ -1186,7 +1170,7 @@ newSession(
atomic_add(&client_rses->rses_versno, 2);
ss_dassert(client_rses->rses_versno == 2);
client_rses->dbhash = hashtable_alloc(100, hashkeyfun, hashcmpfun);
client_rses->dbhash = hashtable_alloc(100, simple_str_hash,strcmp);
hashtable_memory_fns(client_rses->dbhash, (HASHMEMORYFN) strdup,
(HASHMEMORYFN) strdup,
(HASHMEMORYFN) free,
@ -1559,7 +1543,7 @@ routeQuery(ROUTER* instance,
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
mysql_server_cmd_t packet_type;
uint8_t* packet;
int ret = 1;
int i,ret = 1;
SUBSERVICE* target_subsvc;
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *) instance;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *) router_session;
@ -1568,7 +1552,10 @@ routeQuery(ROUTER* instance,
route_target_t route_target = TARGET_UNDEFINED;
bool succp = false;
char* tname = NULL;
skygw_log_write_flush(LOGFILE_TRACE,"shardrouter: routeQuery");
char db[MYSQL_DATABASE_MAXLEN + 1];
char errbuf[26+MYSQL_DATABASE_MAXLEN];
skygw_log_write_flush(LOGFILE_DEBUG,"shardrouter: routeQuery");
CHK_CLIENT_RSES(router_cli_ses);
/** Dirty read for quick check if router is closed. */
@ -1709,8 +1696,13 @@ routeQuery(ROUTER* instance,
if(packet_type == MYSQL_COM_INIT_DB)
{
if(!(change_successful = change_current_db(inst, router_cli_ses, querybuf)))
if(!(change_successful = change_current_db(router_cli_ses->rses_mysql_session,
router_cli_ses->dbhash,
querybuf)))
{
extract_database(querybuf,db);
snprintf(errbuf,"Unknown database: %s",db);
create_error_reply(errbuf,router_cli_ses->replydcb);
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Changing database failed.")));
@ -2061,34 +2053,6 @@ clientReply(
return;
}
static void
subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state)
{
if(state & SUBSVC_WAITING_RESULT)
{
/** Increase waiter count */
atomic_add(&svc->n_res_waiting, 1);
}
svc->state |= state;
}
static void
subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state)
{
if(state & SUBSVC_WAITING_RESULT)
{
/** Decrease waiter count */
atomic_add(&svc->n_res_waiting, -1);
}
svc->state &= ~state;
}
/**
* Create a generic router session property strcture.
*/
@ -2629,8 +2593,7 @@ mysql_sescmd_get_property(
* capabilities specified, rc > 0 when there are capabilities.
*/
static uint8_t
getCapabilities(
ROUTER* inst,
getCapabilities(ROUTER* inst,
void* router_session)
{
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *) router_session;
@ -2900,37 +2863,7 @@ handleError(
}
static bool
get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target)
{
int i;
if(subsvc == NULL || session == NULL || target == NULL)
return false;
for(i = 0;i<session->n_subservice;i++)
{
if(strcmp(session->subservice[i]->service->name,target) == 0)
{
if (SUBSVC_IS_OK(session->subservice[i]))
{
if(subsvc_is_valid(session->subservice[i])){
*subsvc = session->subservice[i];
return true;
}
/**
* The service has failed
*/
subsvc_set_state(session->subservice[i],SUBSVC_FAILED);
}
}
}
return false;
}
/**
* Finds the subservice who owns this session.
* @param rses Router client session
@ -2998,110 +2931,3 @@ return_rc:
return rc;
#endif
}
/**
* Read new database nbame from MYSQL_COM_INIT_DB packet, check that it exists
* in the hashtable and copy its name to MYSQL_session.
*
* @param inst Router instance
* @param rses Router client session
* @param buf Query buffer
*
* @return true if new database is set, false if non-existent database was tried
* to be set
*/
static bool
change_current_db(
ROUTER_INSTANCE* inst,
ROUTER_CLIENT_SES* rses,
GWBUF* buf)
{
bool succp;
uint8_t* packet;
unsigned int plen;
int message_len;
char* fail_str;
if(GWBUF_LENGTH(buf) <= MYSQL_DATABASE_MAXLEN - 5)
{
packet = GWBUF_DATA(buf);
plen = gw_mysql_get_byte3(packet) - 1;
/** Copy database name from MySQL packet to session */
memcpy(rses->rses_mysql_session->db,
packet + 5,
plen);
memset(rses->rses_mysql_session->db + plen, 0, 1);
/**
* Update the session's active database only if it's in the hashtable.
* If it isn't found, send a custom error packet to the client.
*/
if(hashtable_fetch(
rses->dbhash,
(char*) rses->rses_mysql_session->db) == NULL)
{
/** Create error message */
message_len = 25 + MYSQL_DATABASE_MAXLEN;
fail_str = calloc(1, message_len + 1);
snprintf(fail_str,
message_len,
"Unknown database '%s'",
(char*) rses->rses_mysql_session->db);
rses->rses_mysql_session->db[0] = '\0';
succp = false;
goto reply_error;
}
else
{
succp = true;
goto retblock;
}
}
else
{
/** Create error message */
message_len = 25 + MYSQL_DATABASE_MAXLEN;
fail_str = calloc(1, message_len + 1);
snprintf(fail_str,
message_len,
"Unknown database '%s'",
(char*) rses->rses_mysql_session->db);
succp = false;
goto reply_error;
}
reply_error:
{
GWBUF* errbuf;
skygw_log_write_flush(
LOGFILE_TRACE,
"shardrouter: failed to change database: %s", fail_str);
errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
free(fail_str);
if(errbuf == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Creating buffer for error message failed.")));
goto retblock;
}
/** Set flags that help router to identify session commans reply */
gwbuf_set_type(errbuf, GWBUF_TYPE_MYSQL);
gwbuf_set_type(errbuf, GWBUF_TYPE_SESCMD_RESPONSE);
gwbuf_set_type(errbuf, GWBUF_TYPE_RESPONSE_END);
/**
* Create an incoming event for randomly selected backend DCB which
* will then be notified and replied 'back' to the client.
*/
poll_add_epollin_event_to_dcb(rses->replydcb,
gwbuf_clone(errbuf));
gwbuf_free(errbuf);
}
retblock:
return succp;
}

View File

@ -0,0 +1,78 @@
/*
* 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
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright MariaDB Corporation Ab 2013-2014
*/
#include <shardrouter.h>
void
subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state)
{
if(state & SUBSVC_WAITING_RESULT)
{
/** Increase waiter count */
atomic_add(&svc->n_res_waiting, 1);
}
svc->state |= state;
}
void
subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state)
{
if(state & SUBSVC_WAITING_RESULT)
{
/** Decrease waiter count */
atomic_add(&svc->n_res_waiting, -1);
}
svc->state &= ~state;
}
bool
get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target)
{
int i;
if(subsvc == NULL || session == NULL || target == NULL)
return false;
for(i = 0;i<session->n_subservice;i++)
{
if(strcmp(session->subservice[i]->service->name,target) == 0)
{
if (SUBSVC_IS_OK(session->subservice[i]))
{
if(subsvc_is_valid(session->subservice[i])){
*subsvc = session->subservice[i];
return true;
}
/**
* The service has failed
*/
subsvc_set_state(session->subservice[i],SUBSVC_FAILED);
}
}
}
return false;
}

View File

@ -1,2 +1,3 @@
add_library(utils skygw_utils.cc)
target_link_libraries(utils stdc++)
add_dependencies(utils log_manager)
target_link_libraries(utils stdc++ log_manager)

View File

@ -50,7 +50,21 @@
# define ss_prof(exp)
#endif /* SS_DEBUG || SS_PROF */
#if defined(SS_DEBUG)
#if defined(SS_DEBUG) && defined(LOG_ASSERT)
#include <log_manager.h>
# define ss_dassert(exp) if(!(exp)){(skygw_log_write(LE,\
"debug assert %s:%d\n", \
(char*)__FILE__, \
__LINE__));skygw_log_sync_all();assert(exp);}
#define ss_info_dassert(exp,info) if(!(exp)){(skygw_log_write(LE,\
"debug assert %s:%d %s\n", \
(char*)__FILE__, \
__LINE__,info));skygw_log_sync_all();assert(exp);}
# define ss_debug(exp) exp
# define ss_dfprintf fprintf
# define ss_dfflush fflush
# define ss_dfwrite fwrite
#elif defined(SS_DEBUG)
# define ss_debug(exp) exp
# define ss_dfprintf fprintf
@ -199,6 +213,7 @@ typedef enum skygw_chk_t {
((s) == SESSION_STATE_READY ? "SESSION_STATE_READY" : \
((s) == SESSION_STATE_LISTENER ? "SESSION_STATE_LISTENER" : \
((s) == SESSION_STATE_LISTENER_STOPPED ? "SESSION_STATE_LISTENER_STOPPED" : \
(s) == SESSION_STATE_ROUTER_READY ? "SESSION_STATE_ROUTER_READY":\
"SESSION_STATE_UNKNOWN"))))
#define STRPROTOCOLSTATE(s) ((s) == MYSQL_ALLOC ? "MYSQL_ALLOC" : \

View File

@ -30,76 +30,6 @@
#include <sys/time.h>
#include "skygw_utils.h"
const char* timestamp_formatstr = "%04d-%02d-%02d %02d:%02d:%02d ";
/** One for terminating '\0' */
const size_t timestamp_len = (4+1 +2+1 +2+1 +2+1 +2+1 +2+3 +1) * sizeof(char);
const char* timestamp_formatstr_hp = "%04d-%02d-%02d %02d:%02d:%02d.%03d ";
/** One for terminating '\0' */
const size_t timestamp_len_hp = (4+1 +2+1 +2+1 +2+1 +2+1 +2+1+3+3 +1) * sizeof(char);
/** Single-linked list for storing test cases */
struct slist_node_st {
skygw_chk_t slnode_chk_top;
slist_t* slnode_list;
slist_node_t* slnode_next;
void* slnode_data;
size_t slnode_cursor_refcount;
skygw_chk_t slnode_chk_tail;
};
struct slist_st {
skygw_chk_t slist_chk_top;
slist_node_t* slist_head;
slist_node_t* slist_tail;
int slist_nelems;
slist_t* slist_cursors_list;
skygw_chk_t slist_chk_tail;
};
struct slist_cursor_st {
skygw_chk_t slcursor_chk_top;
slist_t* slcursor_list;
slist_node_t* slcursor_pos;
skygw_chk_t slcursor_chk_tail;
};
struct skygw_thread_st {
skygw_chk_t sth_chk_top;
bool sth_must_exit;
simple_mutex_t* sth_mutex;
pthread_t sth_parent;
pthread_t sth_thr;
int sth_errno;
#if defined(SS_DEBUG)
skygw_thr_state_t sth_state;
#endif
char* sth_name;
void* (*sth_thrfun)(void* data);
void* sth_data;
skygw_chk_t sth_chk_tail;
};
struct skygw_message_st {
skygw_chk_t mes_chk_top;
bool mes_sent;
pthread_mutex_t mes_mutex;
pthread_cond_t mes_cond;
skygw_chk_t mes_chk_tail;
};
struct skygw_file_st {
skygw_chk_t sf_chk_top;
char* sf_fname;
FILE* sf_file;
int sf_fd;
skygw_chk_t sf_chk_tail;
};
/** End of structs and types */
#if defined(MLIST)
@ -1952,24 +1882,37 @@ return_rc:
return rc;
}
skygw_file_t* skygw_file_alloc(
char* fname)
{
skygw_file_t* file;
if ((file = (skygw_file_t *)calloc(1, sizeof(skygw_file_t))) == NULL)
{
fprintf(stderr,
"* Error : Memory allocation for file %s failed.\n",
fname);
perror("SkyGW file allocation\n");
return NULL;
}
ss_dassert(file != NULL);
file->sf_chk_top = CHK_NUM_FILE;
file->sf_chk_tail = CHK_NUM_FILE;
file->sf_fname = strdup(fname);
return file;
}
skygw_file_t* skygw_file_init(
char* fname,
char* symlinkname)
{
skygw_file_t* file;
if ((file = (skygw_file_t *)calloc(1, sizeof(skygw_file_t))) == NULL)
if ((file = skygw_file_alloc (fname)) == NULL)
{
fprintf(stderr,
"* Error : Memory allocation for file %s failed.\n",
fname);
perror("SkyGW file allocation\n");
/** Error was reported in skygw_file_alloc */
goto return_file;
}
ss_dassert(file != NULL);
file->sf_chk_top = CHK_NUM_FILE;
file->sf_chk_tail = CHK_NUM_FILE;
file->sf_fname = strdup(fname);
if ((file->sf_file = fopen(file->sf_fname, "a")) == NULL)
{
@ -2033,6 +1976,12 @@ return_file:
return file;
}
void skygw_file_free(skygw_file_t* file)
{
free(file->sf_fname);
free(file);
}
void skygw_file_close(
skygw_file_t* file,
bool shutdown)
@ -2053,7 +2002,7 @@ void skygw_file_close(
}
fd = fileno(file->sf_file);
fsync(fd);
if ((err = fclose(file->sf_file)) != 0)
{
fprintf(stderr,
@ -2065,8 +2014,7 @@ void skygw_file_close(
else
{
ss_dfprintf(stderr, "Closed %s\n", file->sf_fname);
free(file->sf_fname);
free(file);
skygw_file_free (file);
}
}
}

View File

@ -80,6 +80,75 @@ struct mlist_node_st {
typedef enum { THR_INIT, THR_RUNNING, THR_STOPPED, THR_DONE } skygw_thr_state_t;
typedef enum { MES_RC_FAIL, MES_RC_SUCCESS, MES_RC_TIMEOUT } skygw_mes_rc_t;
static const char* timestamp_formatstr = "%04d-%02d-%02d %02d:%02d:%02d ";
/** One for terminating '\0' */
static const size_t timestamp_len = (4+1 +2+1 +2+1 +2+1 +2+1 +2+3 +1) * sizeof(char);
static const char* timestamp_formatstr_hp = "%04d-%02d-%02d %02d:%02d:%02d.%03d ";
/** One for terminating '\0' */
static const size_t timestamp_len_hp = (4+1 +2+1 +2+1 +2+1 +2+1 +2+1+3+3 +1) * sizeof(char);
/** Single-linked list for storing test cases */
struct slist_node_st {
skygw_chk_t slnode_chk_top;
slist_t* slnode_list;
slist_node_t* slnode_next;
void* slnode_data;
size_t slnode_cursor_refcount;
skygw_chk_t slnode_chk_tail;
};
struct slist_st {
skygw_chk_t slist_chk_top;
slist_node_t* slist_head;
slist_node_t* slist_tail;
int slist_nelems;
slist_t* slist_cursors_list;
skygw_chk_t slist_chk_tail;
};
struct slist_cursor_st {
skygw_chk_t slcursor_chk_top;
slist_t* slcursor_list;
slist_node_t* slcursor_pos;
skygw_chk_t slcursor_chk_tail;
};
struct skygw_thread_st {
skygw_chk_t sth_chk_top;
bool sth_must_exit;
simple_mutex_t* sth_mutex;
pthread_t sth_parent;
pthread_t sth_thr;
int sth_errno;
#if defined(SS_DEBUG)
skygw_thr_state_t sth_state;
#endif
char* sth_name;
void* (*sth_thrfun)(void* data);
void* sth_data;
skygw_chk_t sth_chk_tail;
};
struct skygw_message_st {
skygw_chk_t mes_chk_top;
bool mes_sent;
pthread_mutex_t mes_mutex;
pthread_cond_t mes_cond;
skygw_chk_t mes_chk_tail;
};
struct skygw_file_st {
skygw_chk_t sf_chk_top;
char* sf_fname;
FILE* sf_file;
int sf_fd;
skygw_chk_t sf_chk_tail;
};
EXTERN_C_BLOCK_BEGIN
slist_cursor_t* slist_init(void);
@ -147,6 +216,8 @@ EXTERN_C_BLOCK_END
/** Skygw thread routines */
/** Skygw file routines */
skygw_file_t* skygw_file_alloc(char* fname);
void skygw_file_free(skygw_file_t* file);
skygw_file_t* skygw_file_init(char* fname, char* symlinkname);
void skygw_file_close(skygw_file_t* file, bool shutdown);
int skygw_file_write(