Merge branch 'develop' into binlog_server_waitdata_encryption
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c listmanager.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c)
|
||||
add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c hashtable.c hint.c housekeeper.c listmanager.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c gw_ssl.c mysql_utils.c mysql_binlog.c modulecmd.c)
|
||||
|
||||
target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl pthread crypt dl crypto inih z rt m stdc++)
|
||||
|
||||
@ -12,7 +12,7 @@ add_dependencies(maxscale-common pcre2 connector-c)
|
||||
set_target_properties(maxscale-common PROPERTIES VERSION "1.0.0")
|
||||
install_module(maxscale-common core)
|
||||
|
||||
add_executable(maxscale gateway.c)
|
||||
add_executable(maxscale gateway.cc)
|
||||
add_dependencies(maxscale pcre2)
|
||||
|
||||
if(WITH_JEMALLOC)
|
||||
|
||||
@ -2010,7 +2010,7 @@ config_truth_value(char *str)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
MXS_ERROR("Not a boolean value: %s", str);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@ -3294,7 +3294,14 @@ dcb_listen(DCB *listener, const char *config, const char *protocol_name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(listener_socket, 10 * SOMAXCONN) != 0)
|
||||
/**
|
||||
* The use of INT_MAX for backlog length in listen() allows the end-user to
|
||||
* control the backlog length with the net.ipv4.tcp_max_syn_backlog kernel
|
||||
* option since the parameter is silently truncated to the configured value.
|
||||
*
|
||||
* @see man 2 listen
|
||||
*/
|
||||
if (listen(listener_socket, INT_MAX) != 0)
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_ERROR("Failed to start listening on '%s' with protocol '%s': %d, %s",
|
||||
|
||||
@ -159,7 +159,7 @@ static void write_footer(void);
|
||||
static int ntfw_cb(const char*, const struct stat*, int, struct FTW*);
|
||||
static bool file_is_readable(const char* absolute_pathname);
|
||||
static bool file_is_writable(const char* absolute_pathname);
|
||||
bool handle_path_arg(char** dest, const char* path, char* arg, bool rd, bool wr);
|
||||
bool handle_path_arg(char** dest, const char* path, const char* arg, bool rd, bool wr);
|
||||
static void set_log_augmentation(const char* value);
|
||||
static void usage(void);
|
||||
static char* get_expanded_pathname(
|
||||
@ -218,7 +218,8 @@ struct CRYPTO_dynlock_value
|
||||
*/
|
||||
static struct CRYPTO_dynlock_value *ssl_create_dynlock(const char* file, int line)
|
||||
{
|
||||
struct CRYPTO_dynlock_value* lock = MXS_MALLOC(sizeof(struct CRYPTO_dynlock_value));
|
||||
struct CRYPTO_dynlock_value* lock =
|
||||
(struct CRYPTO_dynlock_value*) MXS_MALLOC(sizeof(struct CRYPTO_dynlock_value));
|
||||
if (lock)
|
||||
{
|
||||
spinlock_init(&lock->lock);
|
||||
@ -383,9 +384,9 @@ sigfatal_handler(int i)
|
||||
}
|
||||
fatal_handling = 1;
|
||||
GATEWAY_CONF* cnf = config_get_global_options();
|
||||
fprintf(stderr, "\n\nMaxScale "MAXSCALE_VERSION" received fatal signal %d\n", i);
|
||||
fprintf(stderr, "\n\nMaxScale " MAXSCALE_VERSION " received fatal signal %d\n", i);
|
||||
|
||||
MXS_ALERT("Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i);
|
||||
MXS_ALERT("Fatal: MaxScale " MAXSCALE_VERSION " received fatal signal %d. Attempting backtrace.", i);
|
||||
|
||||
MXS_ALERT("Commit ID: %s System name: %s "
|
||||
"Release string: %s",
|
||||
@ -1298,6 +1299,8 @@ int main(int argc, char **argv)
|
||||
bool config_check = false;
|
||||
bool to_stdout = false;
|
||||
void (*exitfunp[4])(void) = { mxs_log_finish, cleanup_process_datadir, write_footer, NULL };
|
||||
GATEWAY_CONF* cnf = NULL;
|
||||
int numlocks = 0;
|
||||
|
||||
*syslog_enabled = 1;
|
||||
*maxlog_enabled = 1;
|
||||
@ -1321,7 +1324,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (l != 0)
|
||||
{
|
||||
char* fprerr = "Failed to register exit functions for MaxScale";
|
||||
const char* fprerr = "Failed to register exit functions for MaxScale";
|
||||
print_log_n_stderr(false, true, NULL, fprerr, 0);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1351,11 +1354,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
if (cnf_file_arg == NULL)
|
||||
{
|
||||
char* logerr = "Configuration file argument "
|
||||
"identifier \'-f\' was specified but "
|
||||
"the argument didn't specify\n a valid "
|
||||
"configuration file or the argument "
|
||||
"was missing.";
|
||||
const char* logerr =
|
||||
"Configuration file argument "
|
||||
"identifier \'-f\' was specified but "
|
||||
"the argument didn't specify\n a valid "
|
||||
"configuration file or the argument "
|
||||
"was missing.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, 0);
|
||||
usage();
|
||||
succp = false;
|
||||
@ -1391,11 +1395,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
char* logerr = "Configuration file argument "
|
||||
"identifier \'-l\' was specified but "
|
||||
"the argument didn't specify\n a valid "
|
||||
"configuration file or the argument "
|
||||
"was missing.";
|
||||
const char* logerr =
|
||||
"Configuration file argument "
|
||||
"identifier \'-l\' was specified but "
|
||||
"the argument didn't specify\n a valid "
|
||||
"configuration file or the argument "
|
||||
"was missing.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, 0);
|
||||
usage();
|
||||
succp = false;
|
||||
@ -1618,14 +1623,14 @@ int main(int argc, char **argv)
|
||||
|
||||
if (nread == -1)
|
||||
{
|
||||
char* logerr = "Failed to read data from child process pipe.";
|
||||
const char* logerr = "Failed to read data from child process pipe.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, errno);
|
||||
exit(MAXSCALE_INTERNALERROR);
|
||||
}
|
||||
else if (nread == 0)
|
||||
{
|
||||
/** Child process has exited or closed write pipe */
|
||||
char* logerr = "No data read from child process pipe.";
|
||||
const char* logerr = "No data read from child process pipe.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, 0);
|
||||
exit(MAXSCALE_INTERNALERROR);
|
||||
}
|
||||
@ -1652,8 +1657,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (eno != 0)
|
||||
{
|
||||
char* logerr = "Failed to initialise signal mask for MaxScale. "
|
||||
"Exiting.";
|
||||
const char* logerr = "Failed to initialise signal mask for MaxScale. Exiting.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, eno);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1661,7 +1665,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (!utils_init())
|
||||
{
|
||||
char* logerr = "Failed to initialise utility library.";
|
||||
const char* logerr = "Failed to initialise utility library.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, eno);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1670,7 +1674,7 @@ int main(int argc, char **argv)
|
||||
/** OpenSSL initialization */
|
||||
if (!HAVE_OPENSSL_THREADS)
|
||||
{
|
||||
char* logerr = "OpenSSL library does not support multi-threading";
|
||||
const char* logerr = "OpenSSL library does not support multi-threading";
|
||||
print_log_n_stderr(true, true, logerr, logerr, eno);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1679,8 +1683,8 @@ int main(int argc, char **argv)
|
||||
SSL_load_error_strings();
|
||||
OPENSSL_add_all_algorithms_noconf();
|
||||
|
||||
int numlocks = CRYPTO_num_locks();
|
||||
if ((ssl_locks = MXS_MALLOC(sizeof(SPINLOCK) * (numlocks + 1))) == NULL)
|
||||
numlocks = CRYPTO_num_locks();
|
||||
if ((ssl_locks = (SPINLOCK*)MXS_MALLOC(sizeof(SPINLOCK) * (numlocks + 1))) == NULL)
|
||||
{
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1705,10 +1709,12 @@ int main(int argc, char **argv)
|
||||
|
||||
if (l != 0)
|
||||
{
|
||||
char* fprerr = "Failed to register exit function for\n* "
|
||||
"embedded MySQL library.\n* Exiting.";
|
||||
char* logerr = "Failed to register exit function libmysql_done "
|
||||
"for MaxScale. Exiting.";
|
||||
const char* fprerr =
|
||||
"Failed to register exit function for\n* "
|
||||
"embedded MySQL library.\n* Exiting.";
|
||||
const char* logerr =
|
||||
"Failed to register exit function libmysql_done "
|
||||
"for MaxScale. Exiting.";
|
||||
print_log_n_stderr(true, true, logerr, fprerr, 0);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1837,7 +1843,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (!config_load(cnf_file_path))
|
||||
{
|
||||
char* fprerr =
|
||||
const char* fprerr =
|
||||
"Failed to open, read or process the MaxScale configuration "
|
||||
"file. Exiting. See the error log for details.";
|
||||
print_log_n_stderr(false, true, fprerr, fprerr, 0);
|
||||
@ -1848,12 +1854,12 @@ int main(int argc, char **argv)
|
||||
goto return_main;
|
||||
}
|
||||
|
||||
GATEWAY_CONF* cnf = config_get_global_options();
|
||||
cnf = config_get_global_options();
|
||||
ss_dassert(cnf);
|
||||
|
||||
if (!qc_init(cnf->qc_name, cnf->qc_args))
|
||||
{
|
||||
char* logerr = "Failed to initialise query classifier library.";
|
||||
const char* logerr = "Failed to initialise query classifier library.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, eno);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1870,8 +1876,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
if (!daemon_mode)
|
||||
{
|
||||
char* fprerr = "Failed to initialise the MySQL library. "
|
||||
"Exiting.";
|
||||
const char* fprerr = "Failed to initialise the MySQL library. Exiting.";
|
||||
print_log_n_stderr(false, true, fprerr, fprerr, 0);
|
||||
|
||||
if (mysql_errno(NULL) == 2000)
|
||||
@ -1950,7 +1955,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (n_services == 0)
|
||||
{
|
||||
char* logerr = "Failed to start all MaxScale services. Exiting.";
|
||||
const char* logerr = "Failed to start all MaxScale services. Exiting.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, 0);
|
||||
rc = MAXSCALE_NOSERVICES;
|
||||
goto return_main;
|
||||
@ -1962,7 +1967,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (thread_start(&log_flush_thr, log_flush_cb, (void *) &log_flush_timeout_ms) == NULL)
|
||||
{
|
||||
char* logerr = "Failed to start log flushing thread.";
|
||||
const char* logerr = "Failed to start log flushing thread.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, 0);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1973,7 +1978,7 @@ int main(int argc, char **argv)
|
||||
*/
|
||||
if (!hkinit())
|
||||
{
|
||||
char* logerr = "Failed to start housekeeper thread.";
|
||||
const char* logerr = "Failed to start housekeeper thread.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, 0);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -1993,7 +1998,7 @@ int main(int argc, char **argv)
|
||||
if (thread_start(&threads[thread_id], worker_thread_main,
|
||||
(void *)(thread_id + 1)) == NULL)
|
||||
{
|
||||
char* logerr = "Failed to start worker thread.";
|
||||
const char* logerr = "Failed to start worker thread.";
|
||||
print_log_n_stderr(true, true, logerr, logerr, 0);
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
@ -2133,7 +2138,7 @@ static void unlock_pidfile()
|
||||
if (flock(pidfd, LOCK_UN | LOCK_NB) != 0)
|
||||
{
|
||||
char logbuf[STRING_BUFFER_SIZE + PATH_MAX];
|
||||
char* logerr = "Failed to unlock PID file '%s'.";
|
||||
const char* logerr = "Failed to unlock PID file '%s'.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pidfile);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
}
|
||||
@ -2189,7 +2194,7 @@ bool pid_file_exists()
|
||||
|
||||
if ((fd = open(pathbuf, O_RDWR)) == -1)
|
||||
{
|
||||
char* logerr = "Failed to open PID file '%s'.";
|
||||
const char* logerr = "Failed to open PID file '%s'.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pathbuf);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
return true;
|
||||
@ -2198,7 +2203,7 @@ bool pid_file_exists()
|
||||
{
|
||||
if (errno != EWOULDBLOCK)
|
||||
{
|
||||
char* logerr = "Failed to lock PID file '%s'.";
|
||||
const char* logerr = "Failed to lock PID file '%s'.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pathbuf);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
close(fd);
|
||||
@ -2212,7 +2217,7 @@ bool pid_file_exists()
|
||||
|
||||
if (b == -1)
|
||||
{
|
||||
char* logerr = "Failed to read from PID file '%s'.";
|
||||
const char* logerr = "Failed to read from PID file '%s'.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pathbuf);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
unlock_pidfile();
|
||||
@ -2221,24 +2226,26 @@ bool pid_file_exists()
|
||||
else if (b == 0)
|
||||
{
|
||||
/** Empty file */
|
||||
char* logerr = "PID file read from '%s'. File was empty.\n"
|
||||
"If the file is the correct PID file and no other MaxScale processes "
|
||||
"are running, please remove it manually and start MaxScale again.";
|
||||
const char* logerr =
|
||||
"PID file read from '%s'. File was empty.\n"
|
||||
"If the file is the correct PID file and no other MaxScale processes "
|
||||
"are running, please remove it manually and start MaxScale again.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pathbuf);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
unlock_pidfile();
|
||||
return true;
|
||||
}
|
||||
|
||||
pidbuf[b < sizeof(pidbuf) ? b : sizeof(pidbuf) - 1] = '\0';
|
||||
pidbuf[(size_t)b < sizeof(pidbuf) ? (size_t)b : sizeof(pidbuf) - 1] = '\0';
|
||||
pid = strtol(pidbuf, NULL, 0);
|
||||
|
||||
if (pid < 1)
|
||||
{
|
||||
/** Bad PID */
|
||||
char* logerr = "PID file read from '%s'. File contents not valid.\n"
|
||||
"If the file is the correct PID file and no other MaxScale processes "
|
||||
"are running, please remove it manually and start MaxScale again.";
|
||||
const char* logerr =
|
||||
"PID file read from '%s'. File contents not valid.\n"
|
||||
"If the file is the correct PID file and no other MaxScale processes "
|
||||
"are running, please remove it manually and start MaxScale again.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pathbuf);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
unlock_pidfile();
|
||||
@ -2252,7 +2259,7 @@ bool pid_file_exists()
|
||||
/** no such process, old PID file */
|
||||
if (lock_failed)
|
||||
{
|
||||
char* logerr =
|
||||
const char* logerr =
|
||||
"Locking the PID file '%s' failed. "
|
||||
"Read PID from file and no process found with PID %d. "
|
||||
"Confirm that no other process holds the lock on the PID file.";
|
||||
@ -2264,7 +2271,7 @@ bool pid_file_exists()
|
||||
}
|
||||
else
|
||||
{
|
||||
char* logerr = "Failed to check the existence of process %d read from file '%s'";
|
||||
const char* logerr = "Failed to check the existence of process %d read from file '%s'";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pid, pathbuf);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
unlock_pidfile();
|
||||
@ -2272,7 +2279,7 @@ bool pid_file_exists()
|
||||
}
|
||||
else
|
||||
{
|
||||
char* logerr =
|
||||
const char* logerr =
|
||||
"MaxScale is already running. Process id: %d. "
|
||||
"Use another location for the PID file to run multiple "
|
||||
"instances of MaxScale on the same machine.";
|
||||
@ -2283,8 +2290,9 @@ bool pid_file_exists()
|
||||
}
|
||||
else
|
||||
{
|
||||
char* logerr = "Cannot open PID file '%s', no read permissions. "
|
||||
"Please confirm that the user running MaxScale has read permissions on the file.";
|
||||
const char* logerr =
|
||||
"Cannot open PID file '%s', no read permissions. "
|
||||
"Please confirm that the user running MaxScale has read permissions on the file.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pathbuf);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
}
|
||||
@ -2313,7 +2321,7 @@ static int write_pid_file()
|
||||
fd = open(pidfile, O_WRONLY | O_CREAT, 0777);
|
||||
if (fd == -1)
|
||||
{
|
||||
char* logerr = "Failed to open PID file '%s'.";
|
||||
const char* logerr = "Failed to open PID file '%s'.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pidfile);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
return -1;
|
||||
@ -2342,7 +2350,7 @@ static int write_pid_file()
|
||||
/* truncate pidfile content */
|
||||
if (ftruncate(pidfd, 0))
|
||||
{
|
||||
char* logerr = "MaxScale failed to truncate PID file '%s'.";
|
||||
const char* logerr = "MaxScale failed to truncate PID file '%s'.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pidfile);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
unlock_pidfile();
|
||||
@ -2353,7 +2361,7 @@ static int write_pid_file()
|
||||
|
||||
if (pwrite(pidfd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr))
|
||||
{
|
||||
char* logerr = "MaxScale failed to write into PID file '%s'.";
|
||||
const char* logerr = "MaxScale failed to write into PID file '%s'.";
|
||||
snprintf(logbuf, sizeof(logbuf), logerr, pidfile);
|
||||
print_log_n_stderr(true, true, logbuf, logbuf, errno);
|
||||
unlock_pidfile();
|
||||
@ -2364,7 +2372,7 @@ static int write_pid_file()
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool handle_path_arg(char** dest, const char* path, char* arg, bool rd, bool wr)
|
||||
bool handle_path_arg(char** dest, const char* path, const char* arg, bool rd, bool wr)
|
||||
{
|
||||
char pathbuffer[PATH_MAX + 2];
|
||||
char* errstr;
|
||||
667
server/core/modulecmd.c
Normal file
667
server/core/modulecmd.c
Normal file
@ -0,0 +1,667 @@
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
|
||||
*
|
||||
* Change Date: 2019-07-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/config.h>
|
||||
#include <maxscale/modulecmd.h>
|
||||
#include <maxscale/pcre2.h>
|
||||
#include <maxscale/platform.h>
|
||||
#include <maxscale/spinlock.h>
|
||||
|
||||
/** Size of the error buffer */
|
||||
#define MODULECMD_ERRBUF_SIZE 512
|
||||
|
||||
/** Thread local error buffer */
|
||||
thread_local char *errbuf = NULL;
|
||||
|
||||
/** Parameter passed to functions that do not always expect arguments */
|
||||
static const MODULECMD_ARG MODULECMD_NO_ARGUMENTS = {0, NULL};
|
||||
|
||||
/**
|
||||
* A registered domain
|
||||
*/
|
||||
typedef struct modulecmd_domain
|
||||
{
|
||||
char *domain; /**< The domain */
|
||||
MODULECMD *commands; /**< List of registered commands */
|
||||
struct modulecmd_domain *next; /**< Next domain */
|
||||
} MODULECMD_DOMAIN;
|
||||
|
||||
/**
|
||||
* Internal functions
|
||||
*/
|
||||
|
||||
/** The global list of registered domains */
|
||||
static MODULECMD_DOMAIN *modulecmd_domains = NULL;
|
||||
static SPINLOCK modulecmd_lock = SPINLOCK_INIT;
|
||||
|
||||
static inline void prepare_error()
|
||||
{
|
||||
if (errbuf == NULL)
|
||||
{
|
||||
errbuf = MXS_MALLOC(MODULECMD_ERRBUF_SIZE);
|
||||
MXS_ABORT_IF_NULL(errbuf);
|
||||
errbuf[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset error message
|
||||
*
|
||||
* This should be the first function called in every API function that can
|
||||
* generate errors.
|
||||
*/
|
||||
static void reset_error()
|
||||
{
|
||||
prepare_error();
|
||||
errbuf[0] = '\0';
|
||||
}
|
||||
|
||||
static void report_argc_mismatch(const MODULECMD *cmd, int argc)
|
||||
{
|
||||
if (cmd->arg_count_min == cmd->arg_count_max)
|
||||
{
|
||||
modulecmd_set_error("Expected %d arguments, got %d.", cmd->arg_count_min, argc);
|
||||
}
|
||||
else
|
||||
{
|
||||
modulecmd_set_error("Expected between %d and %d arguments, got %d.", cmd->arg_count_min, cmd->arg_count_max,
|
||||
argc);
|
||||
}
|
||||
}
|
||||
|
||||
static MODULECMD_DOMAIN* domain_create(const char *domain)
|
||||
{
|
||||
MODULECMD_DOMAIN *rval = MXS_MALLOC(sizeof(*rval));
|
||||
char *dm = MXS_STRDUP(domain);
|
||||
|
||||
if (rval && dm)
|
||||
{
|
||||
rval->domain = dm;
|
||||
rval->commands = NULL;
|
||||
rval->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_FREE(rval);
|
||||
MXS_FREE(dm);
|
||||
rval = NULL;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static void domain_free(MODULECMD_DOMAIN *dm)
|
||||
{
|
||||
if (dm)
|
||||
{
|
||||
MXS_FREE(dm->domain);
|
||||
MXS_FREE(dm);
|
||||
}
|
||||
}
|
||||
|
||||
static MODULECMD_DOMAIN* get_or_create_domain(const char *domain)
|
||||
{
|
||||
|
||||
MODULECMD_DOMAIN *dm;
|
||||
|
||||
for (dm = modulecmd_domains; dm; dm = dm->next)
|
||||
{
|
||||
if (strcmp(dm->domain, domain) == 0)
|
||||
{
|
||||
return dm;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dm = domain_create(domain)))
|
||||
{
|
||||
dm->next = modulecmd_domains;
|
||||
modulecmd_domains = dm;
|
||||
}
|
||||
|
||||
return dm;
|
||||
}
|
||||
|
||||
static MODULECMD* command_create(const char *identifier, const char *domain,
|
||||
MODULECMDFN entry_point, int argc,
|
||||
modulecmd_arg_type_t* argv)
|
||||
{
|
||||
MODULECMD *rval = MXS_MALLOC(sizeof(*rval));
|
||||
char *id = MXS_STRDUP(identifier);
|
||||
char *dm = MXS_STRDUP(domain);
|
||||
modulecmd_arg_type_t *types = MXS_MALLOC(sizeof(*types) * argc);
|
||||
|
||||
if (rval && id && dm && types)
|
||||
{
|
||||
int argc_min = 0;
|
||||
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
if (MODULECMD_ARG_IS_REQUIRED(&argv[i]))
|
||||
{
|
||||
argc_min++;
|
||||
}
|
||||
types[i] = argv[i];
|
||||
}
|
||||
|
||||
rval->func = entry_point;
|
||||
rval->identifier = id;
|
||||
rval->domain = dm;
|
||||
rval->arg_types = types;
|
||||
rval->arg_count_min = argc_min;
|
||||
rval->arg_count_max = argc;
|
||||
rval->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_FREE(rval);
|
||||
MXS_FREE(id);
|
||||
MXS_FREE(dm);
|
||||
MXS_FREE(types);
|
||||
rval = NULL;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static void command_free(MODULECMD *cmd)
|
||||
{
|
||||
if (cmd)
|
||||
{
|
||||
MXS_FREE(cmd->identifier);
|
||||
MXS_FREE(cmd->domain);
|
||||
MXS_FREE(cmd->arg_types);
|
||||
MXS_FREE(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static bool domain_has_command(MODULECMD_DOMAIN *dm, const char *id)
|
||||
{
|
||||
for (MODULECMD *cmd = dm->commands; cmd; cmd = cmd->next)
|
||||
{
|
||||
if (strcmp(cmd->identifier, id) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool process_argument(modulecmd_arg_type_t *type, const void* value,
|
||||
struct arg_node *arg, const char **err)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if (!MODULECMD_ARG_IS_REQUIRED(type) && value == NULL)
|
||||
{
|
||||
arg->type.type = MODULECMD_ARG_NONE;
|
||||
rval = true;
|
||||
}
|
||||
else if (value)
|
||||
{
|
||||
switch (MODULECMD_GET_TYPE(type))
|
||||
{
|
||||
case MODULECMD_ARG_NONE:
|
||||
arg->type.type = MODULECMD_ARG_NONE;
|
||||
rval = true;
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_STRING:
|
||||
if ((arg->value.string = MXS_STRDUP((char*)value)))
|
||||
{
|
||||
arg->type.type = MODULECMD_ARG_STRING;
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "memory allocation failed";
|
||||
}
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_BOOLEAN:
|
||||
{
|
||||
int truthval = config_truth_value((char*)value);
|
||||
if (truthval != -1)
|
||||
{
|
||||
arg->value.boolean = truthval;
|
||||
arg->type.type = MODULECMD_ARG_BOOLEAN;
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "not a boolean value";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SERVICE:
|
||||
if ((arg->value.service = service_find((char*)value)))
|
||||
{
|
||||
arg->type.type = MODULECMD_ARG_SERVICE;
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "service not found";
|
||||
}
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SERVER:
|
||||
if ((arg->value.server = server_find_by_unique_name((char*)value)))
|
||||
{
|
||||
arg->type.type = MODULECMD_ARG_SERVER;
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "server not found";
|
||||
}
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SESSION:
|
||||
arg->type.type = MODULECMD_ARG_SESSION;
|
||||
arg->value.session = (SESSION*)strtol((char*)value, NULL, 0);
|
||||
rval = true;
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SESSION_PTR:
|
||||
arg->type.type = MODULECMD_ARG_SESSION_PTR;
|
||||
arg->value.session = (SESSION*)value;
|
||||
rval = true;
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_DCB:
|
||||
arg->type.type = MODULECMD_ARG_DCB;
|
||||
arg->value.dcb = (DCB*)strtol((char*)value, NULL, 0);
|
||||
rval = true;
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_DCB_PTR:
|
||||
arg->type.type = MODULECMD_ARG_DCB_PTR;
|
||||
arg->value.dcb = (DCB*)value;
|
||||
rval = true;
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_MONITOR:
|
||||
if ((arg->value.monitor = monitor_find((char*)value)))
|
||||
{
|
||||
arg->type.type = MODULECMD_ARG_MONITOR;
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "monitor not found";
|
||||
}
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_FILTER:
|
||||
if ((arg->value.filter = filter_find((char*)value)))
|
||||
{
|
||||
arg->type.type = MODULECMD_ARG_FILTER;
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "filter not found";
|
||||
}
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_OUTPUT:
|
||||
arg->type.type = MODULECMD_ARG_OUTPUT;
|
||||
arg->value.dcb = (DCB*)value;
|
||||
rval = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(false);
|
||||
MXS_ERROR("Undefined argument type: %0lx", type->type);
|
||||
*err = "internal error";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "required argument";
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static MODULECMD_ARG* modulecmd_arg_create(int argc)
|
||||
{
|
||||
MODULECMD_ARG* arg = MXS_MALLOC(sizeof(*arg));
|
||||
struct arg_node *argv = MXS_CALLOC(argc, sizeof(*argv));
|
||||
|
||||
if (arg && argv)
|
||||
{
|
||||
arg->argc = argc;
|
||||
arg->argv = argv;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_FREE(argv);
|
||||
MXS_FREE(arg);
|
||||
arg = NULL;
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void free_argument(struct arg_node *arg)
|
||||
{
|
||||
switch (arg->type.type)
|
||||
{
|
||||
case MODULECMD_ARG_STRING:
|
||||
MXS_FREE(arg->value.string);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public functions declared in modulecmd.h
|
||||
*/
|
||||
|
||||
bool modulecmd_register_command(const char *domain, const char *identifier,
|
||||
MODULECMDFN entry_point, int argc, modulecmd_arg_type_t *argv)
|
||||
{
|
||||
reset_error();
|
||||
bool rval = false;
|
||||
spinlock_acquire(&modulecmd_lock);
|
||||
|
||||
MODULECMD_DOMAIN *dm = get_or_create_domain(domain);
|
||||
|
||||
if (dm)
|
||||
{
|
||||
if (domain_has_command(dm, identifier))
|
||||
{
|
||||
modulecmd_set_error("Command registered more than once: %s::%s", domain, identifier);
|
||||
MXS_ERROR("Command registered more than once: %s::%s", domain, identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
MODULECMD *cmd = command_create(identifier, domain, entry_point, argc, argv);
|
||||
|
||||
if (cmd)
|
||||
{
|
||||
cmd->next = dm->commands;
|
||||
dm->commands = cmd;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_release(&modulecmd_lock);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
const MODULECMD* modulecmd_find_command(const char *domain, const char *identifier)
|
||||
{
|
||||
reset_error();
|
||||
MODULECMD *rval = NULL;
|
||||
spinlock_acquire(&modulecmd_lock);
|
||||
|
||||
for (MODULECMD_DOMAIN *dm = modulecmd_domains; dm; dm = dm->next)
|
||||
{
|
||||
if (strcmp(domain, dm->domain) == 0)
|
||||
{
|
||||
for (MODULECMD *cmd = dm->commands; cmd; cmd = cmd->next)
|
||||
{
|
||||
if (strcmp(cmd->identifier, identifier) == 0)
|
||||
{
|
||||
rval = cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_release(&modulecmd_lock);
|
||||
|
||||
if (rval == NULL)
|
||||
{
|
||||
modulecmd_set_error("Command not found: %s::%s", domain, identifier);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
MODULECMD_ARG* modulecmd_arg_parse(const MODULECMD *cmd, int argc, const void **argv)
|
||||
{
|
||||
reset_error();
|
||||
|
||||
MODULECMD_ARG* arg = NULL;
|
||||
|
||||
if (argc >= cmd->arg_count_min && argc <= cmd->arg_count_max)
|
||||
{
|
||||
arg = modulecmd_arg_create(cmd->arg_count_max);
|
||||
bool error = false;
|
||||
|
||||
if (arg)
|
||||
{
|
||||
for (int i = 0; i < cmd->arg_count_max && i < argc; i++)
|
||||
{
|
||||
const char *err = "";
|
||||
|
||||
if (!process_argument(&cmd->arg_types[i], argv[i], &arg->argv[i], &err))
|
||||
{
|
||||
error = true;
|
||||
modulecmd_set_error("Argument %d, %s: %s", i + 1, err, argv[i] ? argv[i] : "No argument given");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
modulecmd_arg_free(arg);
|
||||
arg = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
report_argc_mismatch(cmd, argc);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
void modulecmd_arg_free(MODULECMD_ARG* arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
for (int i = 0; i < arg->argc; i++)
|
||||
{
|
||||
free_argument(&arg->argv[i]);
|
||||
}
|
||||
|
||||
MXS_FREE(arg->argv);
|
||||
MXS_FREE(arg);
|
||||
}
|
||||
}
|
||||
|
||||
bool modulecmd_call_command(const MODULECMD *cmd, const MODULECMD_ARG *args)
|
||||
{
|
||||
bool rval = false;
|
||||
reset_error();
|
||||
|
||||
if (cmd->arg_count_min > 0 && args == NULL)
|
||||
{
|
||||
report_argc_mismatch(cmd, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args == NULL)
|
||||
{
|
||||
args = &MODULECMD_NO_ARGUMENTS;
|
||||
}
|
||||
|
||||
rval = cmd->func(args);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void modulecmd_set_error(const char *format, ...)
|
||||
{
|
||||
prepare_error();
|
||||
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
vsnprintf(errbuf, MODULECMD_ERRBUF_SIZE, format, list);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
const char* modulecmd_get_error()
|
||||
{
|
||||
prepare_error();
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
bool modulecmd_foreach(const char *domain_re, const char *ident_re,
|
||||
bool(*fn)(const MODULECMD *cmd, void *data), void *data)
|
||||
{
|
||||
bool rval = true;
|
||||
bool stop = false;
|
||||
spinlock_acquire(&modulecmd_lock);
|
||||
|
||||
for (MODULECMD_DOMAIN *domain = modulecmd_domains; domain && rval && !stop; domain = domain->next)
|
||||
{
|
||||
int err;
|
||||
mxs_pcre2_result_t d_res = domain_re ?
|
||||
mxs_pcre2_simple_match(domain_re, domain->domain, 0, &err) :
|
||||
MXS_PCRE2_MATCH;
|
||||
|
||||
if (d_res == MXS_PCRE2_MATCH)
|
||||
{
|
||||
for (MODULECMD *cmd = domain->commands; cmd && rval; cmd = cmd->next)
|
||||
{
|
||||
mxs_pcre2_result_t i_res = ident_re ?
|
||||
mxs_pcre2_simple_match(ident_re, cmd->identifier, 0, &err) :
|
||||
MXS_PCRE2_MATCH;
|
||||
|
||||
if (i_res == MXS_PCRE2_MATCH)
|
||||
{
|
||||
if (!fn(cmd, data))
|
||||
{
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (i_res == MXS_PCRE2_ERROR)
|
||||
{
|
||||
PCRE2_UCHAR errbuf[MXS_STRERROR_BUFLEN];
|
||||
pcre2_get_error_message(err, errbuf, sizeof(errbuf));
|
||||
MXS_ERROR("Failed to match command identifier with '%s': %s", ident_re, errbuf);
|
||||
modulecmd_set_error("Failed to match command identifier with '%s': %s", ident_re, errbuf);
|
||||
rval = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (d_res == MXS_PCRE2_ERROR)
|
||||
{
|
||||
PCRE2_UCHAR errbuf[MXS_STRERROR_BUFLEN];
|
||||
pcre2_get_error_message(err, errbuf, sizeof(errbuf));
|
||||
MXS_ERROR("Failed to match command domain with '%s': %s", domain_re, errbuf);
|
||||
modulecmd_set_error("Failed to match command domain with '%s': %s", domain_re, errbuf);
|
||||
rval = false;
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_release(&modulecmd_lock);
|
||||
return rval;
|
||||
}
|
||||
|
||||
char* modulecmd_argtype_to_str(modulecmd_arg_type_t *type)
|
||||
{
|
||||
const char *strtype = "UNKNOWN";
|
||||
|
||||
switch (MODULECMD_GET_TYPE(type))
|
||||
{
|
||||
case MODULECMD_ARG_NONE:
|
||||
strtype = "NONE";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_STRING:
|
||||
strtype = "STRING";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_BOOLEAN:
|
||||
strtype = "BOOLEAN";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SERVICE:
|
||||
strtype = "SERVICE";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SERVER:
|
||||
strtype = "SERVER";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SESSION:
|
||||
strtype = "SESSION";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SESSION_PTR:
|
||||
strtype = "SESSION_PTR";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_DCB:
|
||||
strtype = "DCB";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_DCB_PTR:
|
||||
strtype = "DCB_PTR";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_MONITOR:
|
||||
strtype = "MONITOR";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_FILTER:
|
||||
strtype = "FILTER";
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_OUTPUT:
|
||||
strtype = "OUTPUT";
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(false);
|
||||
MXS_ERROR("Unknown type");
|
||||
break;
|
||||
}
|
||||
|
||||
size_t slen = strlen(strtype);
|
||||
size_t extra = MODULECMD_ARG_IS_REQUIRED(type) ? 0 : 2;
|
||||
char *rval = MXS_MALLOC(slen + extra + 1);
|
||||
|
||||
if (rval)
|
||||
{
|
||||
const char *fmtstr = extra ? "[%s]" : "%s";
|
||||
sprintf(rval, fmtstr, strtype);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool modulecmd_arg_is_present(const MODULECMD_ARG *arg, int idx)
|
||||
{
|
||||
return arg->argc > idx &&
|
||||
MODULECMD_GET_TYPE(&arg->argv[idx].type) != MODULECMD_ARG_NONE;
|
||||
}
|
||||
@ -424,9 +424,46 @@ monitorShowAll(DCB *dcb)
|
||||
void
|
||||
monitorShow(DCB *dcb, MONITOR *monitor)
|
||||
{
|
||||
const char *state;
|
||||
|
||||
switch (monitor->state)
|
||||
{
|
||||
case MONITOR_STATE_RUNNING:
|
||||
state = "Running";
|
||||
break;
|
||||
case MONITOR_STATE_STOPPING:
|
||||
state = "Stopping";
|
||||
break;
|
||||
case MONITOR_STATE_STOPPED:
|
||||
state = "Stopped";
|
||||
break;
|
||||
case MONITOR_STATE_ALLOC:
|
||||
state = "Allocated";
|
||||
break;
|
||||
default:
|
||||
state = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb, "Monitor: %p\n", monitor);
|
||||
dcb_printf(dcb, "Name: %s\n", monitor->name);
|
||||
dcb_printf(dcb, "State: %s\n", state);
|
||||
dcb_printf(dcb, "Sampling interval: %lu milliseconds\n", monitor->interval);
|
||||
dcb_printf(dcb, "Connect Timeout: %i seconds\n", monitor->connect_timeout);
|
||||
dcb_printf(dcb, "Read Timeout: %i seconds\n", monitor->read_timeout);
|
||||
dcb_printf(dcb, "Write Timeout: %i seconds\n", monitor->write_timeout);
|
||||
dcb_printf(dcb, "Monitored servers: ");
|
||||
|
||||
const char *sep = "";
|
||||
|
||||
for (MONITOR_SERVERS *db = monitor->databases; db; db = db->next)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
|
||||
sep = ", ";
|
||||
}
|
||||
|
||||
dcb_printf(dcb, "\n");
|
||||
|
||||
dcb_printf(dcb, "Monitor: %p\n", monitor);
|
||||
dcb_printf(dcb, "\tName: %s\n", monitor->name);
|
||||
if (monitor->handle)
|
||||
{
|
||||
if (monitor->module->diagnostics)
|
||||
@ -442,6 +479,7 @@ monitorShow(DCB *dcb, MONITOR *monitor)
|
||||
{
|
||||
dcb_printf(dcb, "\tMonitor failed\n");
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -456,8 +456,14 @@ struct type_name_info type_to_type_name_info(qc_query_type_t type)
|
||||
}
|
||||
break;
|
||||
|
||||
/** Not implemented yet */
|
||||
//case QUERY_TYPE_USERVAR_WRITE:
|
||||
case QUERY_TYPE_USERVAR_WRITE:
|
||||
{
|
||||
static const char name[] = "QUERY_TYPE_USERVAR_WRITE";
|
||||
info.name = name;
|
||||
info.name_len = sizeof(name) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case QUERY_TYPE_USERVAR_READ:
|
||||
{
|
||||
static const char name[] = "QUERY_TYPE_USERVAR_READ";
|
||||
@ -615,8 +621,7 @@ static const qc_query_type_t QUERY_TYPES[] =
|
||||
QUERY_TYPE_WRITE,
|
||||
QUERY_TYPE_MASTER_READ,
|
||||
QUERY_TYPE_SESSION_WRITE,
|
||||
/** Not implemented yet */
|
||||
//QUERY_TYPE_USERVAR_WRITE,
|
||||
QUERY_TYPE_USERVAR_WRITE,
|
||||
QUERY_TYPE_USERVAR_READ,
|
||||
QUERY_TYPE_SYSVAR_READ,
|
||||
/** Not implemented yet */
|
||||
|
||||
@ -1031,6 +1031,7 @@ static struct
|
||||
{ "ndb", SERVER_NDB },
|
||||
{ "maintenance", SERVER_MAINT },
|
||||
{ "maint", SERVER_MAINT },
|
||||
{ "stale", SERVER_STALE_STATUS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
@ -802,6 +802,10 @@ serviceAddBackend(SERVICE *service, SERVER *server)
|
||||
atomic_synchronize();
|
||||
prev->next = new_ref;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_FREE(new_ref);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -828,7 +832,7 @@ void serviceRemoveBackend(SERVICE *service, const SERVER *server)
|
||||
|
||||
for (SERVER_REF *ref = service->dbref; ref; ref = ref->next)
|
||||
{
|
||||
if (ref->server == server)
|
||||
if (ref->server == server && ref->active)
|
||||
{
|
||||
ref->active = false;
|
||||
service->n_dbref--;
|
||||
@ -852,8 +856,12 @@ serviceHasBackend(SERVICE *service, SERVER *server)
|
||||
|
||||
spinlock_acquire(&service->spin);
|
||||
ptr = service->dbref;
|
||||
while (ptr && ptr->server != server)
|
||||
while (ptr)
|
||||
{
|
||||
if (ptr->server == server && ptr->active)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
spinlock_release(&service->spin);
|
||||
|
||||
@ -18,6 +18,7 @@ add_executable(test_users testusers.c)
|
||||
add_executable(testfeedback testfeedback.c)
|
||||
add_executable(testmaxscalepcre2 testmaxscalepcre2.c)
|
||||
add_executable(testmemlog testmemlog.c)
|
||||
add_executable(testmodulecmd testmodulecmd.c)
|
||||
target_link_libraries(test_adminusers maxscale-common)
|
||||
target_link_libraries(test_buffer maxscale-common)
|
||||
target_link_libraries(test_dcb maxscale-common)
|
||||
@ -38,6 +39,7 @@ target_link_libraries(test_users maxscale-common)
|
||||
target_link_libraries(testfeedback maxscale-common)
|
||||
target_link_libraries(testmaxscalepcre2 maxscale-common)
|
||||
target_link_libraries(testmemlog maxscale-common)
|
||||
target_link_libraries(testmodulecmd maxscale-common)
|
||||
add_test(TestAdminUsers test_adminusers)
|
||||
add_test(TestBuffer test_buffer)
|
||||
add_test(TestDCB test_dcb)
|
||||
@ -58,6 +60,7 @@ add_test(TestServer test_server)
|
||||
add_test(TestService test_service)
|
||||
add_test(TestSpinlock test_spinlock)
|
||||
add_test(TestUsers test_users)
|
||||
add_test(TestModulecmd testmodulecmd)
|
||||
|
||||
# This test requires external dependencies and thus cannot be run
|
||||
# as a part of the core test set
|
||||
|
||||
373
server/core/test/testmodulecmd.c
Normal file
373
server/core/test/testmodulecmd.c
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
|
||||
*
|
||||
* Change Date: 2019-07-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test modulecmd.h functionality
|
||||
*/
|
||||
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/modulecmd.h>
|
||||
#include <maxscale/session.h>
|
||||
|
||||
#define TEST(a, b) do{if (!(a)){printf("%s:%d "b"\n", __FILE__, __LINE__);return 1;}}while(false)
|
||||
|
||||
static bool ok = false;
|
||||
|
||||
bool test_fn(const MODULECMD_ARG *arg)
|
||||
{
|
||||
|
||||
ok = (arg->argc == 2 && strcmp(arg->argv[0].value.string, "Hello") == 0 &&
|
||||
arg->argv[1].value.boolean);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int test_arguments()
|
||||
{
|
||||
const void *params1[] = {"Hello", "true"};
|
||||
const void *params2[] = {"Hello", "1"};
|
||||
|
||||
const void *wrong_params1[] = {"Hi", "true"};
|
||||
const void *wrong_params2[] = {"Hello", "false"};
|
||||
|
||||
const void *bad_params1[] = {"Hello", "World!"};
|
||||
const void *bad_params2[] = {"Hello", NULL};
|
||||
const void *bad_params3[] = {NULL, NULL};
|
||||
const void *bad_params4[] = {NULL, "World!"};
|
||||
|
||||
const char *ns = "test_arguments";
|
||||
const char *id = "test_arguments";
|
||||
modulecmd_arg_type_t args1[] =
|
||||
{
|
||||
{MODULECMD_ARG_STRING, ""},
|
||||
{MODULECMD_ARG_BOOLEAN, ""}
|
||||
};
|
||||
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
|
||||
/**
|
||||
* Test command creation
|
||||
*/
|
||||
|
||||
TEST(modulecmd_find_command(ns, id) == NULL, "The registered command should not yet be found");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
|
||||
TEST(modulecmd_register_command(ns, id, test_fn, 2, args1),
|
||||
"Registering a command should succeed");
|
||||
|
||||
TEST(!modulecmd_register_command(ns, id, test_fn, 2, args1),
|
||||
"Registering the command a second time should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
|
||||
const MODULECMD *cmd = modulecmd_find_command(ns, id);
|
||||
TEST(cmd, "The registered command should be found");
|
||||
|
||||
/**
|
||||
* Test bad arguments
|
||||
*/
|
||||
|
||||
TEST(modulecmd_arg_parse(cmd, 0, NULL) == NULL, "Passing no arguments should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
TEST(modulecmd_arg_parse(cmd, 1, params1) == NULL, "Passing one argument should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
TEST(modulecmd_arg_parse(cmd, 3, params1) == NULL, "Passing three arguments should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
|
||||
TEST(modulecmd_arg_parse(cmd, 2, bad_params1) == NULL, "Passing bad arguments should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
TEST(modulecmd_arg_parse(cmd, 2, bad_params2) == NULL, "Passing bad arguments should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
TEST(modulecmd_arg_parse(cmd, 2, bad_params3) == NULL, "Passing bad arguments should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
TEST(modulecmd_arg_parse(cmd, 2, bad_params4) == NULL, "Passing bad arguments should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
|
||||
/**
|
||||
* Test valid arguments
|
||||
*/
|
||||
|
||||
MODULECMD_ARG* alist = modulecmd_arg_parse(cmd, 2, params1);
|
||||
TEST(alist, "Arguments should be parsed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
|
||||
TEST(modulecmd_call_command(cmd, alist), "Module call should be successful");
|
||||
TEST(ok, "Function should receive right parameters");
|
||||
|
||||
ok = false;
|
||||
|
||||
TEST(modulecmd_call_command(cmd, alist), "Second Module call should be successful");
|
||||
TEST(ok, "Function should receive right parameters");
|
||||
|
||||
|
||||
ok = false;
|
||||
modulecmd_arg_free(alist);
|
||||
|
||||
TEST((alist = modulecmd_arg_parse(cmd, 2, params2)), "Arguments should be parsed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, alist), "Module call should be successful");
|
||||
TEST(ok, "Function should receive right parameters");
|
||||
|
||||
modulecmd_arg_free(alist);
|
||||
|
||||
/**
|
||||
* Test valid but wrong arguments
|
||||
*/
|
||||
TEST((alist = modulecmd_arg_parse(cmd, 2, wrong_params1)), "Arguments should be parsed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, alist), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(!ok, "Function should receive wrong parameters");
|
||||
modulecmd_arg_free(alist);
|
||||
|
||||
TEST((alist = modulecmd_arg_parse(cmd, 2, wrong_params2)), "Arguments should be parsed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, alist), "Module call should be successful");
|
||||
TEST(!ok, "Function should receive wrong parameters");
|
||||
modulecmd_arg_free(alist);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool test_fn2(const MODULECMD_ARG *arg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int test_optional_arguments()
|
||||
{
|
||||
const void *params1[] = {"Hello", "true"};
|
||||
const void *params2[] = {NULL, "true"};
|
||||
const void *params3[] = {"Hello", NULL};
|
||||
const void *params4[] = {NULL, NULL};
|
||||
|
||||
const void *ns = "test_optional_arguments";
|
||||
const void *id = "test_optional_arguments";
|
||||
modulecmd_arg_type_t args1[] =
|
||||
{
|
||||
{MODULECMD_ARG_STRING | MODULECMD_ARG_OPTIONAL, ""},
|
||||
{MODULECMD_ARG_BOOLEAN | MODULECMD_ARG_OPTIONAL, ""}
|
||||
};
|
||||
|
||||
TEST(modulecmd_register_command(ns, id, test_fn2, 2, args1),
|
||||
"Registering a command should succeed");
|
||||
|
||||
const MODULECMD *cmd = modulecmd_find_command(ns, id);
|
||||
TEST(cmd, "The registered command should be found");
|
||||
|
||||
MODULECMD_ARG *arg = modulecmd_arg_parse(cmd, 2, params1);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
modulecmd_arg_free(arg);
|
||||
|
||||
arg = modulecmd_arg_parse(cmd, 2, params2);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
modulecmd_arg_free(arg);
|
||||
|
||||
arg = modulecmd_arg_parse(cmd, 2, params3);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
modulecmd_arg_free(arg);
|
||||
|
||||
arg = modulecmd_arg_parse(cmd, 2, params4);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
modulecmd_arg_free(arg);
|
||||
|
||||
arg = modulecmd_arg_parse(cmd, 1, params1);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
modulecmd_arg_free(arg);
|
||||
|
||||
arg = modulecmd_arg_parse(cmd, 1, params2);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
modulecmd_arg_free(arg);
|
||||
|
||||
arg = modulecmd_arg_parse(cmd, 0, params1);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
modulecmd_arg_free(arg);
|
||||
|
||||
TEST(modulecmd_call_command(cmd, NULL), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool test_fn3(const MODULECMD_ARG *arg)
|
||||
{
|
||||
modulecmd_set_error("Something went wrong!");
|
||||
return false;
|
||||
}
|
||||
|
||||
int test_module_errors()
|
||||
{
|
||||
const char *ns = "test_module_errors";
|
||||
const char *id = "test_module_errors";
|
||||
|
||||
TEST(modulecmd_register_command(ns, id, test_fn3, 0, NULL),
|
||||
"Registering a command should succeed");
|
||||
|
||||
const MODULECMD *cmd = modulecmd_find_command(ns, id);
|
||||
TEST(cmd, "The registered command should be found");
|
||||
|
||||
TEST(!modulecmd_call_command(cmd, NULL), "Module call should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool test_fn_map(const MODULECMD_ARG *args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *map_dom = "test_map";
|
||||
|
||||
bool mapfn(const MODULECMD *cmd, void *data)
|
||||
{
|
||||
int *i = (int*)data;
|
||||
(*i)++;
|
||||
return true;
|
||||
}
|
||||
|
||||
int test_map()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
char id[200];
|
||||
sprintf(id, "test_map%d", i + 1);
|
||||
TEST(modulecmd_register_command(map_dom, id, test_fn_map, 0, NULL),
|
||||
"Registering a command should succeed");
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
TEST(modulecmd_foreach(NULL, NULL, mapfn, &n), "Mapping function should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(n >= 10, "Every registered command should be called");
|
||||
|
||||
n = 0;
|
||||
TEST(modulecmd_foreach("test_map", NULL, mapfn, &n), "Mapping function should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(n == 10, "Every registered command should be called");
|
||||
|
||||
n = 0;
|
||||
TEST(modulecmd_foreach(NULL, "test_map", mapfn, &n), "Mapping function should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(n == 10, "Every registered command should be called");
|
||||
|
||||
n = 0;
|
||||
TEST(modulecmd_foreach("test_map", "test_map", mapfn, &n), "Mapping function should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(n == 10, "Every registered command should be called");
|
||||
|
||||
n = 0;
|
||||
TEST(modulecmd_foreach("wrong domain", "test_map", mapfn, &n), "Mapping function should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(n == 0, "No registered command should be called");
|
||||
|
||||
n = 0;
|
||||
TEST(modulecmd_foreach("test_map", "test_map[2-4]", mapfn, &n), "Mapping function should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
TEST(n == 3, "Three registered commands should be called");
|
||||
|
||||
n = 0;
|
||||
TEST(!modulecmd_foreach("(", NULL, mapfn, &n), "Mapping function should fail");
|
||||
TEST(strlen(modulecmd_get_error()), "Error message should not be empty");
|
||||
TEST(n == 0, "No registered command should be called");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DCB my_dcb;
|
||||
static SESSION my_session;
|
||||
|
||||
bool ptrfn(const MODULECMD_ARG *argv)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if (argv->argc == 4 && argv->argv[0].value.dcb == &my_dcb &&
|
||||
argv->argv[1].value.dcb == &my_dcb &&
|
||||
argv->argv[2].value.session == &my_session &&
|
||||
argv->argv[3].value.session == &my_session)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int test_pointers()
|
||||
{
|
||||
const char *ns = "test_pointers";
|
||||
const char *id = "test_pointers";
|
||||
|
||||
modulecmd_arg_type_t args[] =
|
||||
{
|
||||
{MODULECMD_ARG_DCB, ""},
|
||||
{MODULECMD_ARG_DCB_PTR, ""},
|
||||
{MODULECMD_ARG_SESSION, ""},
|
||||
{MODULECMD_ARG_SESSION_PTR, ""}
|
||||
};
|
||||
|
||||
TEST(modulecmd_register_command(ns, id, ptrfn, 4, args), "Registering a command should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
|
||||
const MODULECMD *cmd = modulecmd_find_command(ns, id);
|
||||
TEST(cmd, "The registered command should be found");
|
||||
char dcb_str[200];
|
||||
char session_str[200];
|
||||
|
||||
sprintf(dcb_str, "%p", &my_dcb);
|
||||
sprintf(session_str, "%p", &my_session);
|
||||
|
||||
const void* params[] = {dcb_str, &my_dcb, session_str, &my_session};
|
||||
|
||||
MODULECMD_ARG *arg = modulecmd_arg_parse(cmd, 4, params);
|
||||
TEST(arg, "Parsing arguments should succeed");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
|
||||
TEST(modulecmd_call_command(cmd, arg), "Module call should be successful");
|
||||
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
|
||||
|
||||
modulecmd_arg_free(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc += test_arguments();
|
||||
rc += test_optional_arguments();
|
||||
rc += test_module_errors();
|
||||
rc += test_map();
|
||||
rc += test_pointers();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -156,4 +156,8 @@ null_auth_is_client_ssl_capable(DCB *dcb)
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
null_auth_free_client_data(DCB *dcb) {}
|
||||
null_auth_free_client_data(DCB *dcb)
|
||||
{
|
||||
free(dcb->data);
|
||||
dcb->data = NULL;
|
||||
}
|
||||
|
||||
58
server/modules/filter/cache/cache.c
vendored
58
server/modules/filter/cache/cache.c
vendored
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#define MXS_MODULE_NAME "cache"
|
||||
#include "cache.h"
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/filter.h>
|
||||
#include <maxscale/gwdirs.h>
|
||||
@ -20,7 +21,6 @@
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include "cache.h"
|
||||
#include "rules.h"
|
||||
#include "storage.h"
|
||||
|
||||
@ -95,9 +95,11 @@ typedef struct cache_config
|
||||
{
|
||||
uint32_t max_resultset_rows;
|
||||
uint32_t max_resultset_size;
|
||||
const char* rules;
|
||||
const char *rules;
|
||||
const char *storage;
|
||||
const char *storage_options;
|
||||
char *storage_options;
|
||||
char **storage_argv;
|
||||
int storage_argc;
|
||||
uint32_t ttl;
|
||||
uint32_t debug;
|
||||
} CACHE_CONFIG;
|
||||
@ -109,6 +111,8 @@ static const CACHE_CONFIG DEFAULT_CONFIG =
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
CACHE_DEFAULT_TTL,
|
||||
CACHE_DEFAULT_DEBUG
|
||||
};
|
||||
@ -214,7 +218,11 @@ static FILTER *createInstance(const char *name, char **options, FILTER_PARAMETER
|
||||
|
||||
if (module)
|
||||
{
|
||||
CACHE_STORAGE *storage = module->api->createInstance(name, config.ttl, 0, NULL);
|
||||
uint32_t ttl = config.ttl;
|
||||
int argc = config.storage_argc;
|
||||
char** argv = config.storage_argv;
|
||||
|
||||
CACHE_STORAGE *storage = module->api->createInstance(name, ttl, argc, argv);
|
||||
|
||||
if (storage)
|
||||
{
|
||||
@ -976,7 +984,47 @@ static bool process_params(char **options, FILTER_PARAMETER **params, CACHE_CONF
|
||||
}
|
||||
else if (strcmp(param->name, "storage_options") == 0)
|
||||
{
|
||||
config->storage_options = param->value;
|
||||
config->storage_options = MXS_STRDUP(param->value);
|
||||
|
||||
if (config->storage_options)
|
||||
{
|
||||
int argc = 1;
|
||||
char *arg = config->storage_options;
|
||||
|
||||
while ((arg = strchr(config->storage_options, ',')))
|
||||
{
|
||||
++argc;
|
||||
}
|
||||
|
||||
config->storage_argv = (char**) MXS_MALLOC((argc + 1) * sizeof(char*));
|
||||
|
||||
if (config->storage_argv)
|
||||
{
|
||||
config->storage_argc = argc;
|
||||
|
||||
int i = 0;
|
||||
arg = config->storage_options;
|
||||
config->storage_argv[i++] = arg;
|
||||
|
||||
while ((arg = strchr(config->storage_options, ',')))
|
||||
{
|
||||
*arg = 0;
|
||||
++arg;
|
||||
config->storage_argv[i++] = arg;
|
||||
}
|
||||
|
||||
config->storage_argv[i] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_FREE(config->storage_options);
|
||||
config->storage_options = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
else if (strcmp(param->name, "storage") == 0)
|
||||
{
|
||||
|
||||
1
server/modules/filter/cache/cache.h
vendored
1
server/modules/filter/cache/cache.h
vendored
@ -14,6 +14,7 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <limits.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <openssl/sha.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <fts.h>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <rocksdb/env.h>
|
||||
@ -37,8 +38,6 @@ using std::unique_ptr;
|
||||
namespace
|
||||
{
|
||||
|
||||
string u_storageDirectory;
|
||||
|
||||
const size_t ROCKSDB_KEY_LENGTH = 2 * SHA512_DIGEST_LENGTH;
|
||||
|
||||
#if ROCKSDB_KEY_LENGTH > CACHE_KEY_MAXLEN
|
||||
@ -85,8 +84,116 @@ string toString(const StorageRocksDBVersion& version)
|
||||
|
||||
const char STORAGE_ROCKSDB_VERSION_KEY[] = "MaxScale_Storage_RocksDB_Version";
|
||||
|
||||
/**
|
||||
* Deletes a path, irrespective of whether it represents a file, a directory
|
||||
* or a directory hierarchy. If the path does not exist, then the path is
|
||||
* considered to have been removed.
|
||||
*
|
||||
* @param path A path (file or directory).
|
||||
*
|
||||
* @return True if the path could be deleted, false otherwise.
|
||||
*/
|
||||
bool deletePath(const string& path)
|
||||
{
|
||||
int rv = false;
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (stat(path.c_str(), &st) == -1)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
rv = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_ERROR("Could not stat: %s", strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_NOTICE("Deleting cache storage at '%s'.", path.c_str());
|
||||
|
||||
rv = true;
|
||||
|
||||
char* files[] = { (char *) path.c_str(), NULL };
|
||||
|
||||
// FTS_NOCHDIR - Do not change CWD while traversing.
|
||||
// FTS_PHYSICAL - Don't follow symlinks.
|
||||
// FTS_XDEV - Don't cross filesystem boundaries
|
||||
FTS *pFts = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL);
|
||||
|
||||
if (pFts) {
|
||||
FTSENT* pCurrent;
|
||||
while ((pCurrent = fts_read(pFts)))
|
||||
{
|
||||
switch (pCurrent->fts_info)
|
||||
{
|
||||
case FTS_NS:
|
||||
case FTS_DNR:
|
||||
case FTS_ERR:
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_ERROR("Error while traversing %s: %s",
|
||||
pCurrent->fts_accpath,
|
||||
strerror_r(pCurrent->fts_errno, errbuf, sizeof(errbuf)));
|
||||
rv = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case FTS_DC:
|
||||
case FTS_DOT:
|
||||
case FTS_NSOK:
|
||||
// Not reached unless FTS_LOGICAL, FTS_SEEDOT, or FTS_NOSTAT were
|
||||
// passed to fts_open()
|
||||
break;
|
||||
|
||||
case FTS_D:
|
||||
// Do nothing. Need depth-first search, so directories are deleted
|
||||
// in FTS_DP
|
||||
break;
|
||||
|
||||
case FTS_DP:
|
||||
case FTS_F:
|
||||
case FTS_SL:
|
||||
case FTS_SLNONE:
|
||||
case FTS_DEFAULT:
|
||||
if (remove(pCurrent->fts_accpath) < 0)
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_ERROR("Could not remove '%s', the cache directory may need to "
|
||||
"be deleted manually: %s",
|
||||
pCurrent->fts_accpath,
|
||||
strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
rv = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
}
|
||||
|
||||
if (rv)
|
||||
{
|
||||
MXS_NOTICE("Deleted cache storage at '%s'.", path.c_str());
|
||||
}
|
||||
|
||||
if (pFts) {
|
||||
fts_close(pFts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//private
|
||||
rocksdb::WriteOptions RocksDBStorage::s_writeOptions;
|
||||
|
||||
//private
|
||||
RocksDBStorage::RocksDBStorage(unique_ptr<rocksdb::DBWithTTL>& sDb,
|
||||
const string& name,
|
||||
@ -106,69 +213,98 @@ RocksDBStorage::~RocksDBStorage()
|
||||
//static
|
||||
bool RocksDBStorage::Initialize()
|
||||
{
|
||||
bool initialized = true;
|
||||
auto pEnv = rocksdb::Env::Default();
|
||||
pEnv->SetBackgroundThreads(ROCKSDB_N_LOW_THREADS, rocksdb::Env::LOW);
|
||||
pEnv->SetBackgroundThreads(ROCKSDB_N_HIGH_THREADS, rocksdb::Env::HIGH);
|
||||
|
||||
u_storageDirectory = get_cachedir();
|
||||
u_storageDirectory += "/storage_rocksdb";
|
||||
// No logging; the database will always be deleted at startup, so there's
|
||||
// no reason for usinf space and processing for writing the write ahead log.
|
||||
s_writeOptions.disableWAL = true;
|
||||
|
||||
if (mkdir(u_storageDirectory.c_str(), S_IRWXU) == 0)
|
||||
{
|
||||
MXS_NOTICE("Created storage directory %s.", u_storageDirectory.c_str());
|
||||
}
|
||||
else if (errno != EEXIST)
|
||||
{
|
||||
initialized = false;
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
|
||||
MXS_ERROR("Failed to create storage directory %s: %s",
|
||||
u_storageDirectory.c_str(),
|
||||
strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pEnv = rocksdb::Env::Default();
|
||||
pEnv->SetBackgroundThreads(ROCKSDB_N_LOW_THREADS, rocksdb::Env::LOW);
|
||||
pEnv->SetBackgroundThreads(ROCKSDB_N_HIGH_THREADS, rocksdb::Env::HIGH);
|
||||
}
|
||||
|
||||
return initialized;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//static
|
||||
RocksDBStorage* RocksDBStorage::Create(const char* zName, uint32_t ttl, int argc, char* argv[])
|
||||
{
|
||||
ss_dassert(zName);
|
||||
|
||||
string path(u_storageDirectory);
|
||||
string storageDirectory = get_cachedir();
|
||||
|
||||
path += "/";
|
||||
path += zName;
|
||||
|
||||
rocksdb::Options options;
|
||||
options.env = rocksdb::Env::Default();
|
||||
options.max_background_compactions = ROCKSDB_N_LOW_THREADS;
|
||||
options.max_background_flushes = ROCKSDB_N_HIGH_THREADS;
|
||||
|
||||
rocksdb::DBWithTTL* pDb;
|
||||
rocksdb::Status status;
|
||||
rocksdb::Slice key(STORAGE_ROCKSDB_VERSION_KEY);
|
||||
|
||||
do
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
// Try to open existing.
|
||||
options.create_if_missing = false;
|
||||
options.error_if_exists = false;
|
||||
size_t len = strlen(argv[i]);
|
||||
char arg[len + 1];
|
||||
strcpy(arg, argv[i]);
|
||||
|
||||
status = rocksdb::DBWithTTL::Open(options, path, &pDb, ttl);
|
||||
const char* zValue = NULL;
|
||||
char *zEq = strchr(arg, '=');
|
||||
|
||||
if (status.IsInvalidArgument()) // Did not exist
|
||||
if (zEq)
|
||||
{
|
||||
MXS_NOTICE("Database \"%s\" does not exist, creating.", path.c_str());
|
||||
*zEq = 0;
|
||||
zValue = trim(zEq + 1);
|
||||
}
|
||||
|
||||
const char* zKey = trim(arg);
|
||||
|
||||
if (strcmp(zKey, "cache_directory") == 0)
|
||||
{
|
||||
if (zValue)
|
||||
{
|
||||
storageDirectory = zValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("No value specified for '%s', using default '%s' instead.",
|
||||
zKey, get_cachedir());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("Unknown argument '%s'.", zKey);
|
||||
}
|
||||
}
|
||||
|
||||
storageDirectory += "/storage_rocksdb";
|
||||
|
||||
return Create(storageDirectory, zName, ttl);
|
||||
}
|
||||
|
||||
// static
|
||||
RocksDBStorage* RocksDBStorage::Create(const string& storageDirectory, const char* zName, uint32_t ttl)
|
||||
{
|
||||
RocksDBStorage* pStorage = nullptr;
|
||||
|
||||
if (mkdir(storageDirectory.c_str(), S_IRWXU) == 0)
|
||||
{
|
||||
MXS_NOTICE("Created storage directory %s.", storageDirectory.c_str());
|
||||
}
|
||||
else if (errno != EEXIST)
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_ERROR("Failed to create storage directory %s: %s",
|
||||
storageDirectory.c_str(),
|
||||
strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
else
|
||||
{
|
||||
string path(storageDirectory + "/" + zName);
|
||||
|
||||
if (deletePath(path))
|
||||
{
|
||||
rocksdb::Options options;
|
||||
options.env = rocksdb::Env::Default();
|
||||
options.max_background_compactions = ROCKSDB_N_LOW_THREADS;
|
||||
options.max_background_flushes = ROCKSDB_N_HIGH_THREADS;
|
||||
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = true;
|
||||
|
||||
rocksdb::DBWithTTL* pDb;
|
||||
rocksdb::Status status;
|
||||
rocksdb::Slice key(STORAGE_ROCKSDB_VERSION_KEY);
|
||||
|
||||
status = rocksdb::DBWithTTL::Open(options, path, &pDb, ttl);
|
||||
|
||||
if (status.ok())
|
||||
@ -179,7 +315,7 @@ RocksDBStorage* RocksDBStorage::Create(const char* zName, uint32_t ttl, int argc
|
||||
rocksdb::Slice value(reinterpret_cast<const char*>(&STORAGE_ROCKSDB_VERSION),
|
||||
sizeof(STORAGE_ROCKSDB_VERSION));
|
||||
|
||||
status = pDb->Put(rocksdb::WriteOptions(), key, value);
|
||||
status = pDb->Put(writeOptions(), key, value);
|
||||
|
||||
if (!status.ok())
|
||||
{
|
||||
@ -188,36 +324,6 @@ RocksDBStorage* RocksDBStorage::Create(const char* zName, uint32_t ttl, int argc
|
||||
path.c_str(),
|
||||
status.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (status.IsInvalidArgument());
|
||||
|
||||
RocksDBStorage* pStorage = nullptr;
|
||||
|
||||
if (status.ok())
|
||||
{
|
||||
std::string value;
|
||||
|
||||
status = pDb->Get(rocksdb::ReadOptions(), key, &value);
|
||||
|
||||
if (status.ok())
|
||||
{
|
||||
const StorageRocksDBVersion* pVersion =
|
||||
reinterpret_cast<const StorageRocksDBVersion*>(value.data());
|
||||
|
||||
// When the version is bumped, it needs to be decided what if any
|
||||
// backward compatibility is provided. After all, it's a cache, so
|
||||
// you should be able to delete it at any point and pay a small
|
||||
// price while the cache is rebuilt.
|
||||
if ((pVersion->major == STORAGE_ROCKSDB_MAJOR) &&
|
||||
(pVersion->minor == STORAGE_ROCKSDB_MINOR) &&
|
||||
(pVersion->correction == STORAGE_ROCKSDB_CORRECTION))
|
||||
{
|
||||
MXS_NOTICE("Version of \"%s\" is %s, version of storage_rocksdb is %s.",
|
||||
path.c_str(),
|
||||
toString(*pVersion).c_str(),
|
||||
toString(STORAGE_ROCKSDB_VERSION).c_str());
|
||||
|
||||
unique_ptr<rocksdb::DBWithTTL> sDb(pDb);
|
||||
|
||||
@ -225,31 +331,14 @@ RocksDBStorage* RocksDBStorage::Create(const char* zName, uint32_t ttl, int argc
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Version of RocksDB database \"%s\" is %s, while version required "
|
||||
"is %s. You need to delete the database and restart.",
|
||||
path.c_str(),
|
||||
toString(*pVersion).c_str(),
|
||||
toString(STORAGE_ROCKSDB_VERSION).c_str());
|
||||
delete pDb;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Could not read version information from RocksDB database %s. "
|
||||
"You may need to delete the database and retry. RocksDB error: \"%s\"",
|
||||
path.c_str(),
|
||||
status.ToString().c_str());
|
||||
delete pDb;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Could not open/initialize RocksDB database %s. RocksDB error: \"%s\"",
|
||||
path.c_str(), status.ToString().c_str());
|
||||
MXS_ERROR("Could not create RocksDB database %s. RocksDB error: \"%s\"",
|
||||
path.c_str(), status.ToString().c_str());
|
||||
|
||||
if (status.IsIOError())
|
||||
{
|
||||
MXS_ERROR("Is an other MaxScale process running?");
|
||||
if (status.IsIOError())
|
||||
{
|
||||
MXS_ERROR("Is an other MaxScale process running?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,7 +463,7 @@ cache_result_t RocksDBStorage::putValue(const char* pKey, const GWBUF* pValue)
|
||||
rocksdb::Slice key(pKey, ROCKSDB_KEY_LENGTH);
|
||||
rocksdb::Slice value(static_cast<const char*>(GWBUF_DATA(pValue)), GWBUF_LENGTH(pValue));
|
||||
|
||||
rocksdb::Status status = m_sDb->Put(rocksdb::WriteOptions(), key, value);
|
||||
rocksdb::Status status = m_sDb->Put(writeOptions(), key, value);
|
||||
|
||||
return status.ok() ? CACHE_RESULT_OK : CACHE_RESULT_ERROR;
|
||||
}
|
||||
|
||||
@ -42,11 +42,22 @@ private:
|
||||
RocksDBStorage(const RocksDBStorage&) = delete;
|
||||
RocksDBStorage& operator = (const RocksDBStorage&) = delete;
|
||||
|
||||
static RocksDBStorage* Create(const std::string& storageDirectory,
|
||||
const char* zName,
|
||||
uint32_t ttl);
|
||||
|
||||
static const rocksdb::WriteOptions& writeOptions()
|
||||
{
|
||||
return s_writeOptions;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<rocksdb::DBWithTTL> m_sDb;
|
||||
std::string m_name;
|
||||
std::string m_path;
|
||||
uint32_t m_ttl;
|
||||
|
||||
static rocksdb::WriteOptions s_writeOptions;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -11,12 +11,11 @@ if(BISON_FOUND AND FLEX_FOUND)
|
||||
set_target_properties(dbfwfilter PROPERTIES VERSION "1.0.0")
|
||||
install_module(dbfwfilter core)
|
||||
|
||||
if(BUILD_TOOLS)
|
||||
add_executable(dbfwruleparser dbfwfilter.c ${BISON_ruleparser_OUTPUTS} ${FLEX_token_OUTPUTS})
|
||||
target_compile_definitions(dbfwruleparser PUBLIC "BUILD_RULE_PARSER")
|
||||
target_link_libraries(dbfwruleparser maxscale-common)
|
||||
install_module(dbfwruleparser core)
|
||||
endif()
|
||||
# The offline rule check utility
|
||||
add_executable(dbfwchk dbfw_rule_check.c ${BISON_ruleparser_OUTPUTS} ${FLEX_token_OUTPUTS})
|
||||
target_link_libraries(dbfwchk maxscale-common)
|
||||
install_executable(dbfwchk core)
|
||||
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find Bison or Flex: ${BISON_EXECUTABLE} ${FLEX_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
55
server/modules/filter/dbfwfilter/dbfw_rule_check.c
Normal file
55
server/modules/filter/dbfwfilter/dbfw_rule_check.c
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
|
||||
*
|
||||
* Change Date: 2019-07-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include "dbfwfilter.c"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rval = 1;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("Usage: dbfw_rule_check FILE\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
mxs_log_init("dbfwfilter_rule_parser", ".", MXS_LOG_TARGET_STDOUT);
|
||||
|
||||
if (access(argv[1], R_OK) == 0)
|
||||
{
|
||||
MXS_NOTICE("Parsing rule file: %s", argv[1]);
|
||||
|
||||
RULE* rules;
|
||||
HASHTABLE *users;
|
||||
|
||||
if (process_rule_file(argv[1], &rules, &users))
|
||||
{
|
||||
MXS_NOTICE("Rule parsing was successful.");
|
||||
rval = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to parse rules.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to read file '%s': %d, %s", argv[1], errno, strerror(errno));
|
||||
}
|
||||
|
||||
mxs_log_finish();
|
||||
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
@ -136,108 +136,6 @@ typedef enum
|
||||
RT_CLAUSE /*< WHERE-clause requirement rule */
|
||||
} ruletype_t;
|
||||
|
||||
const char* rule_names[] =
|
||||
{
|
||||
"UNDEFINED",
|
||||
"COLUMN",
|
||||
"THROTTLE",
|
||||
"PERMISSION",
|
||||
"WILDCARD",
|
||||
"REGEX",
|
||||
"CLAUSE"
|
||||
};
|
||||
|
||||
/**
|
||||
* Linked list of strings.
|
||||
*/
|
||||
typedef struct strlink_t
|
||||
{
|
||||
struct strlink_t *next; /*< Next node in the list */
|
||||
char* value; /*< Value of the current node */
|
||||
} STRLINK;
|
||||
|
||||
/**
|
||||
* A structure defining a range of time
|
||||
*/
|
||||
typedef struct timerange_t
|
||||
{
|
||||
struct timerange_t* next; /*< Next node in the list */
|
||||
struct tm start; /*< Start of the time range */
|
||||
struct tm end; /*< End of the time range */
|
||||
} TIMERANGE;
|
||||
|
||||
/**
|
||||
* Query speed measurement and limitation structure
|
||||
*/
|
||||
typedef struct queryspeed_t
|
||||
{
|
||||
time_t first_query; /*< Time when the first query occurred */
|
||||
time_t triggered; /*< Time when the limit was exceeded */
|
||||
int period; /*< Measurement interval in seconds */
|
||||
int cooldown; /*< Time the user is denied access for */
|
||||
int count; /*< Number of queries done */
|
||||
int limit; /*< Maximum number of queries */
|
||||
long id; /*< Unique id of the rule */
|
||||
bool active; /*< If the rule has been triggered */
|
||||
struct queryspeed_t* next; /*< Next node in the list */
|
||||
} QUERYSPEED;
|
||||
|
||||
/**
|
||||
* A structure used to identify individual rules and to store their contents
|
||||
*
|
||||
* Each type of rule has different requirements that are expressed as void pointers.
|
||||
* This allows to match an arbitrary set of rules against a user.
|
||||
*/
|
||||
typedef struct rule_t
|
||||
{
|
||||
void* data; /*< Actual implementation of the rule */
|
||||
char* name; /*< Name of the rule */
|
||||
ruletype_t type; /*< Type of the rule */
|
||||
qc_query_op_t on_queries; /*< Types of queries to inspect */
|
||||
int times_matched; /*< Number of times this rule has been matched */
|
||||
TIMERANGE* active; /*< List of times when this rule is active */
|
||||
struct rule_t *next;
|
||||
} RULE;
|
||||
|
||||
/**
|
||||
* Linked list of pointers to a global pool of RULE structs
|
||||
*/
|
||||
typedef struct rulelist_t
|
||||
{
|
||||
RULE* rule; /*< The rule structure */
|
||||
struct rulelist_t* next; /*< Next node in the list */
|
||||
} RULELIST;
|
||||
|
||||
typedef struct user_template
|
||||
{
|
||||
char *name;
|
||||
enum match_type type; /** Matching type */
|
||||
STRLINK *rulenames; /** names of the rules */
|
||||
struct user_template *next;
|
||||
} user_template_t;
|
||||
|
||||
typedef struct user_t
|
||||
{
|
||||
char* name; /*< Name of the user */
|
||||
SPINLOCK lock; /*< User spinlock */
|
||||
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_and; /*< All of these rules must match for the action to trigger */
|
||||
RULELIST* rules_strict_and; /*< rules that skip the rest of the rules if one of them
|
||||
* fails. This is only for rules paired with 'match strict_all'. */
|
||||
|
||||
} USER;
|
||||
|
||||
/**
|
||||
* Linked list of IP adresses and subnet masks
|
||||
*/
|
||||
typedef struct iprange_t
|
||||
{
|
||||
struct iprange_t* next; /*< Next node in the list */
|
||||
uint32_t ip; /*< IP address */
|
||||
uint32_t mask; /*< Network mask */
|
||||
} IPRANGE;
|
||||
|
||||
/**
|
||||
* Possible actions to take when the query matches a rule
|
||||
*/
|
||||
@ -258,18 +156,116 @@ enum fw_actions
|
||||
/** Maximum length of the match/nomatch messages */
|
||||
#define FW_MAX_SQL_LEN 400
|
||||
|
||||
const char* rule_names[] =
|
||||
{
|
||||
"UNDEFINED",
|
||||
"COLUMN",
|
||||
"THROTTLE",
|
||||
"PERMISSION",
|
||||
"WILDCARD",
|
||||
"REGEX",
|
||||
"CLAUSE"
|
||||
};
|
||||
|
||||
/**
|
||||
* Linked list of strings.
|
||||
*/
|
||||
typedef struct strlink_t
|
||||
{
|
||||
struct strlink_t *next; /*< Next node in the list */
|
||||
char* value; /*< Value of the current node */
|
||||
} STRLINK;
|
||||
|
||||
/**
|
||||
* A structure defining a range of time
|
||||
*/
|
||||
typedef struct timerange_t
|
||||
{
|
||||
struct timerange_t* next; /*< Next node in the list */
|
||||
struct tm start; /*< Start of the time range */
|
||||
struct tm end; /*< End of the time range */
|
||||
} TIMERANGE;
|
||||
|
||||
/**
|
||||
* Query speed measurement and limitation structure
|
||||
*/
|
||||
typedef struct queryspeed_t
|
||||
{
|
||||
time_t first_query; /*< Time when the first query occurred */
|
||||
time_t triggered; /*< Time when the limit was exceeded */
|
||||
int period; /*< Measurement interval in seconds */
|
||||
int cooldown; /*< Time the user is denied access for */
|
||||
int count; /*< Number of queries done */
|
||||
int limit; /*< Maximum number of queries */
|
||||
long id; /*< Unique id of the rule */
|
||||
bool active; /*< If the rule has been triggered */
|
||||
struct queryspeed_t* next; /*< Next node in the list */
|
||||
} QUERYSPEED;
|
||||
|
||||
/**
|
||||
* A structure used to identify individual rules and to store their contents
|
||||
*
|
||||
* Each type of rule has different requirements that are expressed as void pointers.
|
||||
* This allows to match an arbitrary set of rules against a user.
|
||||
*/
|
||||
typedef struct rule_t
|
||||
{
|
||||
void* data; /*< Actual implementation of the rule */
|
||||
char* name; /*< Name of the rule */
|
||||
ruletype_t type; /*< Type of the rule */
|
||||
qc_query_op_t on_queries; /*< Types of queries to inspect */
|
||||
int times_matched; /*< Number of times this rule has been matched */
|
||||
TIMERANGE* active; /*< List of times when this rule is active */
|
||||
struct rule_t *next;
|
||||
} RULE;
|
||||
|
||||
/**
|
||||
* A set of rules that the filter follows
|
||||
*/
|
||||
typedef struct rulebook_t
|
||||
{
|
||||
RULE* rule; /*< The rule structure */
|
||||
struct rulebook_t* next; /*< The next rule in the book */
|
||||
} RULE_BOOK;
|
||||
|
||||
/**
|
||||
* A temporary template structure used in the creation of actual users.
|
||||
* This is also used to link the user definitions with the rules.
|
||||
* @see struct user_t
|
||||
*/
|
||||
typedef struct user_template
|
||||
{
|
||||
char *name;
|
||||
enum match_type type; /** Matching type */
|
||||
STRLINK *rulenames; /** names of the rules */
|
||||
struct user_template *next;
|
||||
} user_template_t;
|
||||
|
||||
/**
|
||||
* A user definition
|
||||
*/
|
||||
typedef struct user_t
|
||||
{
|
||||
char* name; /*< Name of the user */
|
||||
SPINLOCK lock; /*< User spinlock */
|
||||
QUERYSPEED* qs_limit; /*< The query speed structure unique to this user */
|
||||
RULE_BOOK* rules_or; /*< If any of these rules match the action is triggered */
|
||||
RULE_BOOK* rules_and; /*< All of these rules must match for the action to trigger */
|
||||
RULE_BOOK* rules_strict_and; /*< rules that skip the rest of the rules if one of them
|
||||
* fails. This is only for rules paired with 'match strict_all'. */
|
||||
} DBFW_USER;
|
||||
|
||||
/**
|
||||
* The Firewall filter instance.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
HASHTABLE* htable; /*< User hashtable */
|
||||
RULE* rules; /*< List of all the rules */
|
||||
STRLINK* userstrings; /*< Temporary list of raw strings of users */
|
||||
enum fw_actions action; /*< Default operation mode, defaults to deny */
|
||||
int log_match; /*< Log matching and/or non-matching queries */
|
||||
SPINLOCK lock; /*< Instance spinlock */
|
||||
int idgen; /*< UID generator */
|
||||
HASHTABLE* users; /*< User hashtable */
|
||||
RULE* rules; /*< List of all the rules */
|
||||
enum fw_actions action; /*< Default operation mode, defaults to deny */
|
||||
int log_match; /*< Log matching and/or non-matching queries */
|
||||
SPINLOCK lock; /*< Instance spinlock */
|
||||
int idgen; /*< UID generator */
|
||||
} FW_INSTANCE;
|
||||
|
||||
/**
|
||||
@ -277,10 +273,10 @@ typedef struct
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
SESSION* session; /*< Client session structure */
|
||||
char* errmsg; /*< Rule specific error message */
|
||||
DOWNSTREAM down; /*< Next object in the downstream chain */
|
||||
UPSTREAM up; /*< Next object in the upstream chain */
|
||||
SESSION* session; /*< Client session structure */
|
||||
char* errmsg; /*< Rule specific error message */
|
||||
DOWNSTREAM down; /*< Next object in the downstream chain */
|
||||
UPSTREAM up; /*< Next object in the upstream chain */
|
||||
} FW_SESSION;
|
||||
|
||||
bool parse_at_times(const char** tok, char** saveptr, RULE* ruledef);
|
||||
@ -366,9 +362,15 @@ static STRLINK* strlink_reverse_clone(STRLINK* head)
|
||||
return clone;
|
||||
}
|
||||
|
||||
static RULELIST* rulelist_push(RULELIST *head, RULE *rule)
|
||||
/**
|
||||
* Add a rule to a rulebook
|
||||
* @param head
|
||||
* @param rule
|
||||
* @return
|
||||
*/
|
||||
static RULE_BOOK* rulebook_push(RULE_BOOK *head, RULE *rule)
|
||||
{
|
||||
RULELIST *rval = MXS_MALLOC(sizeof(RULELIST));
|
||||
RULE_BOOK *rval = MXS_MALLOC(sizeof(RULE_BOOK));
|
||||
|
||||
if (rval)
|
||||
{
|
||||
@ -378,16 +380,16 @@ static RULELIST* rulelist_push(RULELIST *head, RULE *rule)
|
||||
return rval;
|
||||
}
|
||||
|
||||
static void* rulelist_clone(void* fval)
|
||||
static void* rulebook_clone(void* fval)
|
||||
{
|
||||
|
||||
RULELIST *rule = NULL,
|
||||
*ptr = (RULELIST*) fval;
|
||||
RULE_BOOK *rule = NULL,
|
||||
*ptr = (RULE_BOOK*) fval;
|
||||
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
RULELIST* tmp = (RULELIST*) MXS_MALLOC(sizeof(RULELIST));
|
||||
RULE_BOOK* tmp = (RULE_BOOK*) MXS_MALLOC(sizeof(RULE_BOOK));
|
||||
MXS_ABORT_IF_NULL(tmp);
|
||||
tmp->next = rule;
|
||||
tmp->rule = ptr->rule;
|
||||
@ -398,30 +400,42 @@ static void* rulelist_clone(void* fval)
|
||||
return (void*) rule;
|
||||
}
|
||||
|
||||
static void* rulelist_free(void* fval)
|
||||
static void* rulebook_free(void* fval)
|
||||
{
|
||||
RULELIST *ptr = (RULELIST*) fval;
|
||||
RULE_BOOK *ptr = (RULE_BOOK*) fval;
|
||||
while (ptr)
|
||||
{
|
||||
RULELIST *tmp = ptr;
|
||||
RULE_BOOK *tmp = ptr;
|
||||
ptr = ptr->next;
|
||||
MXS_FREE(tmp);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void huserfree(void* fval)
|
||||
static void dbfw_user_free(void* fval)
|
||||
{
|
||||
USER* value = (USER*) fval;
|
||||
DBFW_USER* value = (DBFW_USER*) fval;
|
||||
|
||||
rulelist_free(value->rules_and);
|
||||
rulelist_free(value->rules_or);
|
||||
rulelist_free(value->rules_strict_and);
|
||||
rulebook_free(value->rules_and);
|
||||
rulebook_free(value->rules_or);
|
||||
rulebook_free(value->rules_strict_and);
|
||||
MXS_FREE(value->qs_limit);
|
||||
MXS_FREE(value->name);
|
||||
MXS_FREE(value);
|
||||
}
|
||||
|
||||
HASHTABLE *dbfw_userlist_create()
|
||||
{
|
||||
HASHTABLE *ht = hashtable_alloc(100, hashtable_item_strhash, hashtable_item_strcmp);
|
||||
|
||||
if (ht)
|
||||
{
|
||||
hashtable_memory_fns(ht, hashtable_item_strdup, NULL, hashtable_item_free, dbfw_user_free);
|
||||
}
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string that contains an IP address and converts the last octet to '%'.
|
||||
* This modifies the string passed as the parameter.
|
||||
@ -694,36 +708,25 @@ FILTER_OBJECT * GetModuleObject()
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given rule string to the list of strings to be parsed for users.
|
||||
* @param rule The rule string, assumed to be null-terminated
|
||||
* @param instance The FW_FILTER instance
|
||||
*/
|
||||
void add_users(char* rule, FW_INSTANCE* instance)
|
||||
{
|
||||
assert(rule != NULL && instance != NULL);
|
||||
instance->userstrings = strlink_push(instance->userstrings, rule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a rule set to a user
|
||||
*
|
||||
* @param instance Filter instance
|
||||
* @param user User name
|
||||
* @param rulelist List of rules to apply
|
||||
* @param rulebook List of rules to apply
|
||||
* @param type Matching type, one of FWTOK_MATCH_ANY, FWTOK_MATCH_ALL or FWTOK_MATCH_STRICT_ALL
|
||||
* @return True of the rules were successfully applied. False if memory allocation
|
||||
* fails
|
||||
*/
|
||||
static bool apply_rule_to_user(FW_INSTANCE *instance, char *username,
|
||||
RULELIST *rulelist, enum match_type type)
|
||||
RULE_BOOK *rulebook, enum match_type type)
|
||||
{
|
||||
USER* user;
|
||||
DBFW_USER* user;
|
||||
ss_dassert(type == FWTOK_MATCH_ANY || type == FWTOK_MATCH_STRICT_ALL || type == FWTOK_MATCH_ALL);
|
||||
if ((user = (USER*) hashtable_fetch(instance->htable, username)) == NULL)
|
||||
if ((user = (DBFW_USER*) hashtable_fetch(instance->users, username)) == NULL)
|
||||
{
|
||||
/**New user*/
|
||||
if ((user = (USER*) MXS_CALLOC(1, sizeof(USER))) == NULL)
|
||||
if ((user = (DBFW_USER*) MXS_CALLOC(1, sizeof(DBFW_USER))) == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -732,8 +735,8 @@ static bool apply_rule_to_user(FW_INSTANCE *instance, char *username,
|
||||
|
||||
user->name = (char*) MXS_STRDUP_A(username);
|
||||
user->qs_limit = NULL;
|
||||
RULELIST *tl = (RULELIST*) rulelist_clone(rulelist);
|
||||
RULELIST *tail = tl;
|
||||
RULE_BOOK *tl = (RULE_BOOK*) rulebook_clone(rulebook);
|
||||
RULE_BOOK *tail = tl;
|
||||
|
||||
while (tail && tail->next)
|
||||
{
|
||||
@ -755,7 +758,7 @@ static bool apply_rule_to_user(FW_INSTANCE *instance, char *username,
|
||||
user->rules_and = tl;
|
||||
break;
|
||||
}
|
||||
hashtable_add(instance->htable, (void *) username, (void *) user);
|
||||
hashtable_add(instance->users, (void *) username, (void *) user);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -763,7 +766,7 @@ static bool apply_rule_to_user(FW_INSTANCE *instance, char *username,
|
||||
* Free a TIMERANGE struct
|
||||
* @param tr pointer to a TIMERANGE struct
|
||||
*/
|
||||
void tr_free(TIMERANGE* tr)
|
||||
void timerange_free(TIMERANGE* tr)
|
||||
{
|
||||
TIMERANGE *node, *tmp;
|
||||
|
||||
@ -899,16 +902,14 @@ bool create_rule(void* scanner, const char* name)
|
||||
* Free a list of rules
|
||||
* @param rule Rules to free
|
||||
*/
|
||||
static void free_rules(RULE* rule)
|
||||
static void rule_free_all(RULE* rule)
|
||||
{
|
||||
while (rule)
|
||||
{
|
||||
RULE *tmp = rule->next;
|
||||
while (rule->active)
|
||||
if (rule->active)
|
||||
{
|
||||
TIMERANGE *tr = rule->active;
|
||||
rule->active = rule->active->next;
|
||||
MXS_FREE(tr);
|
||||
timerange_free(rule->active);
|
||||
}
|
||||
|
||||
switch (rule->type)
|
||||
@ -930,6 +931,7 @@ static void free_rules(RULE* rule)
|
||||
}
|
||||
|
||||
MXS_FREE(rule->name);
|
||||
MXS_FREE(rule);
|
||||
rule = tmp;
|
||||
}
|
||||
}
|
||||
@ -1214,7 +1216,7 @@ static RULE* find_rule_by_name(RULE* rules, const char* name)
|
||||
* @param rules List of all rules
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templates,
|
||||
static bool process_user_templates(HASHTABLE *users, user_template_t *templates,
|
||||
RULE* rules)
|
||||
{
|
||||
bool rval = true;
|
||||
@ -1227,17 +1229,17 @@ static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templ
|
||||
|
||||
while (templates)
|
||||
{
|
||||
USER *user = hashtable_fetch(instance->htable, templates->name);
|
||||
DBFW_USER *user = hashtable_fetch(users, templates->name);
|
||||
|
||||
if (user == NULL)
|
||||
{
|
||||
if ((user = MXS_MALLOC(sizeof(USER))) && (user->name = MXS_STRDUP(templates->name)))
|
||||
if ((user = MXS_MALLOC(sizeof(DBFW_USER))) && (user->name = MXS_STRDUP(templates->name)))
|
||||
{
|
||||
user->rules_and = NULL;
|
||||
user->rules_or = NULL;
|
||||
user->rules_strict_and = NULL;
|
||||
spinlock_init(&user->lock);
|
||||
hashtable_add(instance->htable, user->name, user);
|
||||
hashtable_add(users, user->name, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1247,19 +1249,19 @@ static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templ
|
||||
}
|
||||
}
|
||||
|
||||
RULELIST *foundrules = NULL;
|
||||
RULE_BOOK *foundrules = NULL;
|
||||
RULE *rule;
|
||||
STRLINK *names = templates->rulenames;
|
||||
|
||||
while (names && (rule = find_rule_by_name(rules, names->value)))
|
||||
{
|
||||
foundrules = rulelist_push(foundrules, rule);
|
||||
foundrules = rulebook_push(foundrules, rule);
|
||||
names = names->next;
|
||||
}
|
||||
|
||||
if (foundrules)
|
||||
{
|
||||
RULELIST *tail = foundrules;
|
||||
RULE_BOOK *tail = foundrules;
|
||||
|
||||
while (tail->next)
|
||||
{
|
||||
@ -1302,7 +1304,7 @@ static bool process_user_templates(FW_INSTANCE *instance, user_template_t *templ
|
||||
* @param instance Filter instance
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
static bool process_rule_file(const char* filename, FW_INSTANCE* instance)
|
||||
static bool process_rule_file(const char* filename, RULE** rules, HASHTABLE **users)
|
||||
{
|
||||
int rc = 1;
|
||||
FILE *file = fopen(filename, "r");
|
||||
@ -1328,15 +1330,18 @@ static bool process_rule_file(const char* filename, FW_INSTANCE* instance)
|
||||
dbfw_yy_delete_buffer(buf, scanner);
|
||||
dbfw_yylex_destroy(scanner);
|
||||
fclose(file);
|
||||
HASHTABLE *new_users = dbfw_userlist_create();
|
||||
|
||||
if (rc == 0 && process_user_templates(instance, pstack.templates, pstack.rule))
|
||||
if (rc == 0 && new_users && process_user_templates(new_users, pstack.templates, pstack.rule))
|
||||
{
|
||||
instance->rules = pstack.rule;
|
||||
*rules = pstack.rule;
|
||||
*users = new_users;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = 1;
|
||||
free_rules(pstack.rule);
|
||||
rule_free_all(pstack.rule);
|
||||
hashtable_free(new_users);
|
||||
MXS_ERROR("Failed to process rule file '%s'.", filename);
|
||||
}
|
||||
|
||||
@ -1355,6 +1360,41 @@ static bool process_rule_file(const char* filename, FW_INSTANCE* instance)
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Replace the rule file used by this filter instance
|
||||
*
|
||||
* This function does no locking. An external lock needs to protect this function
|
||||
* call to prevent any connections from using the data when it is being replaced.
|
||||
*
|
||||
* @param filename File where the rules are located
|
||||
* @param instance Filter instance
|
||||
* @return True on success, false on error. If the return value is false, the
|
||||
* old rules remain active.
|
||||
*/
|
||||
bool replace_rule_file(const char* filename, FW_INSTANCE* instance)
|
||||
{
|
||||
bool rval = false;
|
||||
RULE *rules;
|
||||
HASHTABLE *users;
|
||||
|
||||
if (process_rule_file(filename, &rules, &users))
|
||||
{
|
||||
/** Rules processed successfully, free the old ones */
|
||||
rule_free_all(instance->rules);
|
||||
hashtable_free(instance->users);
|
||||
instance->rules = rules;
|
||||
instance->users = users;
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to process rule file at '%s', old rules are still active.", filename);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the filter for a particular service
|
||||
* within MaxScale.
|
||||
@ -1370,7 +1410,6 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
|
||||
{
|
||||
FW_INSTANCE *my_instance;
|
||||
int i;
|
||||
HASHTABLE* ht;
|
||||
char *filename = NULL;
|
||||
bool err = false;
|
||||
|
||||
@ -1381,20 +1420,8 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
|
||||
}
|
||||
|
||||
spinlock_init(&my_instance->lock);
|
||||
|
||||
if ((ht = hashtable_alloc(100, hashtable_item_strhash, hashtable_item_strcmp)) == NULL)
|
||||
{
|
||||
MXS_ERROR("Unable to allocate hashtable.");
|
||||
MXS_FREE(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hashtable_memory_fns(ht, hashtable_item_strdup, NULL, hashtable_item_free, huserfree);
|
||||
|
||||
my_instance->htable = ht;
|
||||
my_instance->action = FW_ACTION_BLOCK;
|
||||
my_instance->log_match = FW_LOG_NONE;
|
||||
my_instance->userstrings = NULL;
|
||||
|
||||
for (i = 0; params[i]; i++)
|
||||
{
|
||||
@ -1447,9 +1474,8 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
|
||||
err = true;
|
||||
}
|
||||
|
||||
if (err || !process_rule_file(filename, my_instance))
|
||||
if (err || !process_rule_file(filename, &my_instance->rules, &my_instance->users))
|
||||
{
|
||||
hashtable_free(my_instance->htable);
|
||||
MXS_FREE(my_instance);
|
||||
my_instance = NULL;
|
||||
}
|
||||
@ -1691,15 +1717,15 @@ static char* create_parse_error(FW_INSTANCE* my_instance,
|
||||
* @param my_instance Fwfilter instance
|
||||
* @param my_session Fwfilter session
|
||||
* @param queue The GWBUF containing the query
|
||||
* @param rulelist The rule to check
|
||||
* @param rulebook The rule to check
|
||||
* @param query Pointer to the null-terminated query string
|
||||
* @return true if the query matches the rule
|
||||
*/
|
||||
bool rule_matches(FW_INSTANCE* my_instance,
|
||||
FW_SESSION* my_session,
|
||||
GWBUF *queue,
|
||||
USER* user,
|
||||
RULELIST *rulelist,
|
||||
DBFW_USER* user,
|
||||
RULE_BOOK *rulebook,
|
||||
char* query)
|
||||
{
|
||||
char *ptr, *msg = NULL;
|
||||
@ -1735,23 +1761,23 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
|
||||
if (parse_result != QC_QUERY_PARSED)
|
||||
{
|
||||
if ((rulelist->rule->type == RT_COLUMN) ||
|
||||
(rulelist->rule->type == RT_WILDCARD) ||
|
||||
(rulelist->rule->type == RT_CLAUSE))
|
||||
if ((rulebook->rule->type == RT_COLUMN) ||
|
||||
(rulebook->rule->type == RT_WILDCARD) ||
|
||||
(rulebook->rule->type == RT_CLAUSE))
|
||||
{
|
||||
switch (optype)
|
||||
{
|
||||
case QUERY_OP_SELECT:
|
||||
case QUERY_OP_UPDATE:
|
||||
case QUERY_OP_INSERT:
|
||||
case QUERY_OP_DELETE:
|
||||
// In these cases, we have to be able to trust what qc_get_field_info
|
||||
// returns. Unless the query was parsed completely, we cannot do that.
|
||||
msg = create_parse_error(my_instance, "parsed completely", query, &matches);
|
||||
goto queryresolved;
|
||||
case QUERY_OP_SELECT:
|
||||
case QUERY_OP_UPDATE:
|
||||
case QUERY_OP_INSERT:
|
||||
case QUERY_OP_DELETE:
|
||||
// In these cases, we have to be able to trust what qc_get_field_info
|
||||
// returns. Unless the query was parsed completely, we cannot do that.
|
||||
msg = create_parse_error(my_instance, "parsed completely", query, &matches);
|
||||
goto queryresolved;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1762,12 +1788,12 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
is_real = false;
|
||||
}
|
||||
|
||||
if (rulelist->rule->on_queries == QUERY_OP_UNDEFINED ||
|
||||
rulelist->rule->on_queries & optype ||
|
||||
if (rulebook->rule->on_queries == QUERY_OP_UNDEFINED ||
|
||||
rulebook->rule->on_queries & optype ||
|
||||
(MYSQL_IS_COM_INIT_DB((uint8_t*)GWBUF_DATA(queue)) &&
|
||||
rulelist->rule->on_queries & QUERY_OP_CHANGE_DB))
|
||||
rulebook->rule->on_queries & QUERY_OP_CHANGE_DB))
|
||||
{
|
||||
switch (rulelist->rule->type)
|
||||
switch (rulebook->rule->type)
|
||||
{
|
||||
case RT_UNDEFINED:
|
||||
MXS_ERROR("Undefined rule type found.");
|
||||
@ -1777,11 +1803,11 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
if (query)
|
||||
{
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(
|
||||
rulelist->rule->data, NULL);
|
||||
rulebook->rule->data, NULL);
|
||||
|
||||
if (mdata)
|
||||
{
|
||||
if (pcre2_match((pcre2_code*) rulelist->rule->data,
|
||||
if (pcre2_match((pcre2_code*) rulebook->rule->data,
|
||||
(PCRE2_SPTR) query, PCRE2_ZERO_TERMINATED,
|
||||
0, 0, mdata, NULL) > 0)
|
||||
{
|
||||
@ -1791,7 +1817,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
if (matches)
|
||||
{
|
||||
msg = MXS_STRDUP_A("Permission denied, query matched regular expression.");
|
||||
MXS_INFO("dbfwfilter: rule '%s': regex matched on query", rulelist->rule->name);
|
||||
MXS_INFO("dbfwfilter: rule '%s': regex matched on query", rulebook->rule->name);
|
||||
goto queryresolved;
|
||||
}
|
||||
}
|
||||
@ -1804,15 +1830,15 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
break;
|
||||
|
||||
case RT_PERMISSION:
|
||||
{
|
||||
matches = true;
|
||||
msg = MXS_STRDUP_A("Permission denied at this time.");
|
||||
char buffer[32]; // asctime documentation requires 26
|
||||
asctime_r(&tm_now, buffer);
|
||||
MXS_INFO("dbfwfilter: rule '%s': query denied at: %s", rulelist->rule->name, buffer);
|
||||
goto queryresolved;
|
||||
}
|
||||
break;
|
||||
{
|
||||
matches = true;
|
||||
msg = MXS_STRDUP_A("Permission denied at this time.");
|
||||
char buffer[32]; // asctime documentation requires 26
|
||||
asctime_r(&tm_now, buffer);
|
||||
MXS_INFO("dbfwfilter: rule '%s': query denied at: %s", rulebook->rule->name, buffer);
|
||||
goto queryresolved;
|
||||
}
|
||||
break;
|
||||
|
||||
case RT_COLUMN:
|
||||
if (is_sql && is_real)
|
||||
@ -1825,7 +1851,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
{
|
||||
const char* tok = infos[i].column;
|
||||
|
||||
STRLINK* strln = (STRLINK*) rulelist->rule->data;
|
||||
STRLINK* strln = (STRLINK*) rulebook->rule->data;
|
||||
while (strln)
|
||||
{
|
||||
if (strcasecmp(tok, strln->value) == 0)
|
||||
@ -1834,7 +1860,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
|
||||
sprintf(emsg, "Permission denied to column '%s'.", strln->value);
|
||||
MXS_INFO("dbfwfilter: rule '%s': query targets forbidden column: %s",
|
||||
rulelist->rule->name, strln->value);
|
||||
rulebook->rule->name, strln->value);
|
||||
msg = MXS_STRDUP_A(emsg);
|
||||
goto queryresolved;
|
||||
}
|
||||
@ -1860,7 +1886,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
matches = true;
|
||||
msg = MXS_STRDUP_A("Usage of wildcard denied.");
|
||||
MXS_INFO("dbfwfilter: rule '%s': query contains a wildcard.",
|
||||
rulelist->rule->name);
|
||||
rulebook->rule->name);
|
||||
goto queryresolved;
|
||||
}
|
||||
}
|
||||
@ -1873,7 +1899,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
* and initialize a new QUERYSPEED struct for this session.
|
||||
*/
|
||||
spinlock_acquire(&my_instance->lock);
|
||||
rule_qs = (QUERYSPEED*) rulelist->rule->data;
|
||||
rule_qs = (QUERYSPEED*) rulebook->rule->data;
|
||||
spinlock_release(&my_instance->lock);
|
||||
|
||||
spinlock_acquire(&user->lock);
|
||||
@ -1913,7 +1939,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
|
||||
sprintf(emsg, "Queries denied for %f seconds", blocked_for);
|
||||
MXS_INFO("dbfwfilter: rule '%s': user denied for %f seconds",
|
||||
rulelist->rule->name, blocked_for);
|
||||
rulebook->rule->name, blocked_for);
|
||||
msg = MXS_STRDUP_A(emsg);
|
||||
matches = true;
|
||||
}
|
||||
@ -1933,7 +1959,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
|
||||
MXS_INFO("dbfwfilter: rule '%s': query limit triggered (%d queries in %d seconds), "
|
||||
"denying queries from user for %d seconds.",
|
||||
rulelist->rule->name,
|
||||
rulebook->rule->name,
|
||||
queryspeed->limit,
|
||||
queryspeed->period,
|
||||
queryspeed->cooldown);
|
||||
@ -1962,7 +1988,7 @@ bool rule_matches(FW_INSTANCE* my_instance,
|
||||
matches = true;
|
||||
msg = MXS_STRDUP_A("Required WHERE/HAVING clause is missing.");
|
||||
MXS_INFO("dbfwfilter: rule '%s': query has no where/having "
|
||||
"clause, query is denied.", rulelist->rule->name);
|
||||
"clause, query is denied.", rulebook->rule->name);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1985,45 +2011,45 @@ queryresolved:
|
||||
|
||||
if (matches)
|
||||
{
|
||||
rulelist->rule->times_matched++;
|
||||
rulebook->rule->times_matched++;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the query matches any of the rules in the user's rulelist.
|
||||
* Check if the query matches any of the rules in the user's rulebook.
|
||||
* @param my_instance Fwfilter instance
|
||||
* @param my_session Fwfilter session
|
||||
* @param queue The GWBUF containing the query
|
||||
* @param user The user whose rulelist is checked
|
||||
* @param user The user whose rulebook is checked
|
||||
* @return True if the query matches at least one of the rules otherwise false
|
||||
*/
|
||||
bool check_match_any(FW_INSTANCE* my_instance, FW_SESSION* my_session,
|
||||
GWBUF *queue, USER* user, char** rulename)
|
||||
GWBUF *queue, DBFW_USER* user, char** rulename)
|
||||
{
|
||||
RULELIST* rulelist;
|
||||
RULE_BOOK* rulebook;
|
||||
bool rval = false;
|
||||
|
||||
if ((rulelist = user->rules_or) &&
|
||||
if ((rulebook = user->rules_or) &&
|
||||
(modutil_is_SQL(queue) || modutil_is_SQL_prepare(queue) ||
|
||||
MYSQL_IS_COM_INIT_DB((uint8_t*)GWBUF_DATA(queue))))
|
||||
{
|
||||
char *fullquery = modutil_get_SQL(queue);
|
||||
while (rulelist)
|
||||
while (rulebook)
|
||||
{
|
||||
if (!rule_is_active(rulelist->rule))
|
||||
if (!rule_is_active(rulebook->rule))
|
||||
{
|
||||
rulelist = rulelist->next;
|
||||
rulebook = rulebook->next;
|
||||
continue;
|
||||
}
|
||||
if (rule_matches(my_instance, my_session, queue, user, rulelist, fullquery))
|
||||
if (rule_matches(my_instance, my_session, queue, user, rulebook, fullquery))
|
||||
{
|
||||
*rulename = MXS_STRDUP_A(rulelist->rule->name);
|
||||
*rulename = MXS_STRDUP_A(rulebook->rule->name);
|
||||
rval = true;
|
||||
break;
|
||||
}
|
||||
rulelist = rulelist->next;
|
||||
rulebook = rulebook->next;
|
||||
}
|
||||
|
||||
MXS_FREE(fullquery);
|
||||
@ -2067,39 +2093,39 @@ void append_string(char** dest, size_t* size, const char* src)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the query matches all rules in the user's rulelist.
|
||||
* Check if the query matches all rules in the user's rulebook.
|
||||
* @param my_instance Fwfilter instance
|
||||
* @param my_session Fwfilter session
|
||||
* @param queue The GWBUF containing the query
|
||||
* @param user The user whose rulelist is checked
|
||||
* @param user The user whose rulebook is checked
|
||||
* @return True if the query matches all of the rules otherwise false
|
||||
*/
|
||||
bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session,
|
||||
GWBUF *queue, USER* user, bool strict_all, char** rulename)
|
||||
GWBUF *queue, DBFW_USER* user, bool strict_all, char** rulename)
|
||||
{
|
||||
bool rval = false;
|
||||
bool have_active_rule = false;
|
||||
RULELIST* rulelist = strict_all ? user->rules_strict_and : user->rules_and;
|
||||
RULE_BOOK* rulebook = strict_all ? user->rules_strict_and : user->rules_and;
|
||||
char *matched_rules = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
if (rulelist && (modutil_is_SQL(queue) || modutil_is_SQL_prepare(queue)))
|
||||
if (rulebook && (modutil_is_SQL(queue) || modutil_is_SQL_prepare(queue)))
|
||||
{
|
||||
char *fullquery = modutil_get_SQL(queue);
|
||||
rval = true;
|
||||
while (rulelist)
|
||||
while (rulebook)
|
||||
{
|
||||
if (!rule_is_active(rulelist->rule))
|
||||
if (!rule_is_active(rulebook->rule))
|
||||
{
|
||||
rulelist = rulelist->next;
|
||||
rulebook = rulebook->next;
|
||||
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, rulebook, fullquery))
|
||||
{
|
||||
append_string(&matched_rules, &size, rulelist->rule->name);
|
||||
append_string(&matched_rules, &size, rulebook->rule->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2110,7 +2136,7 @@ bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session,
|
||||
}
|
||||
}
|
||||
|
||||
rulelist = rulelist->next;
|
||||
rulebook = rulebook->next;
|
||||
}
|
||||
|
||||
if (!have_active_rule)
|
||||
@ -2135,17 +2161,17 @@ bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session,
|
||||
* @param remote Remove network address
|
||||
* @return The user data or NULL if it was not found
|
||||
*/
|
||||
USER* find_user_data(HASHTABLE *hash, const char *name, const char *remote)
|
||||
DBFW_USER* find_user_data(HASHTABLE *hash, const char *name, const char *remote)
|
||||
{
|
||||
char nameaddr[strlen(name) + strlen(remote) + 2];
|
||||
snprintf(nameaddr, sizeof(nameaddr), "%s@%s", name, remote);
|
||||
USER* user = (USER*) hashtable_fetch(hash, nameaddr);
|
||||
DBFW_USER* user = (DBFW_USER*) hashtable_fetch(hash, nameaddr);
|
||||
if (user == NULL)
|
||||
{
|
||||
char *ip_start = strchr(nameaddr, '@') + 1;
|
||||
while (user == NULL && next_ip_class(ip_start))
|
||||
{
|
||||
user = (USER*) hashtable_fetch(hash, nameaddr);
|
||||
user = (DBFW_USER*) hashtable_fetch(hash, nameaddr);
|
||||
}
|
||||
|
||||
if (user == NULL)
|
||||
@ -2154,7 +2180,7 @@ USER* find_user_data(HASHTABLE *hash, const char *name, const char *remote)
|
||||
ip_start = strchr(nameaddr, '@') + 1;
|
||||
while (user == NULL && next_ip_class(ip_start))
|
||||
{
|
||||
user = (USER*) hashtable_fetch(hash, nameaddr);
|
||||
user = (DBFW_USER*) hashtable_fetch(hash, nameaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2191,7 +2217,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
}
|
||||
else
|
||||
{
|
||||
USER *user = find_user_data(my_instance->htable, dcb->user, dcb->remote);
|
||||
DBFW_USER *user = find_user_data(my_instance->users, dcb->user, dcb->remote);
|
||||
bool query_ok = false;
|
||||
|
||||
if (user)
|
||||
@ -2335,86 +2361,3 @@ static uint64_t getCapabilities(void)
|
||||
{
|
||||
return RCAP_TYPE_STMT_INPUT;
|
||||
}
|
||||
|
||||
#ifdef BUILD_RULE_PARSER
|
||||
// TODO: Not ok to include file from other component's test directory.
|
||||
#include "../../../core/test/test_utils.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
char ch;
|
||||
bool have_icase = false;
|
||||
char *home;
|
||||
char cwd[PATH_MAX];
|
||||
char* opts[2] = {NULL, NULL};
|
||||
FILTER_PARAMETER ruleparam;
|
||||
FILTER_PARAMETER * paramlist[2];
|
||||
|
||||
opterr = 0;
|
||||
while ((ch = getopt(argc, argv, "h?")) != -1)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '?':
|
||||
case 'h':
|
||||
printf("Usage: %s [OPTION]... RULEFILE\n"
|
||||
"Options:\n"
|
||||
"\t-?\tPrint this information\n",
|
||||
argv[0]);
|
||||
return 0;
|
||||
default:
|
||||
printf("Unknown option '%c'.\n", ch);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("Usage: %s [OPTION]... RULEFILE\n"
|
||||
"-?\tPrint this information\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
home = MXS_MALLOC(sizeof(char) * (PATH_MAX + 1));
|
||||
MXS_ABORT_IF_NULL(home);
|
||||
if (getcwd(home, PATH_MAX) == NULL)
|
||||
{
|
||||
MXS_FREE(home);
|
||||
home = NULL;
|
||||
}
|
||||
|
||||
printf("Log files written to: %s\n", home ? home : "/tpm");
|
||||
|
||||
int argc_ = 2;
|
||||
char* argv_[] =
|
||||
{
|
||||
"log_manager",
|
||||
"-o",
|
||||
NULL
|
||||
};
|
||||
|
||||
mxs_log_init(NULL, NULL, MXS_LOG_TARGET_DEFAULT);
|
||||
|
||||
|
||||
init_test_env(home);
|
||||
ruleparam.name = MXS_STRDUP_A("rules");
|
||||
ruleparam.value = MXS_STRDUP_A(argv[1]);
|
||||
paramlist[0] = &ruleparam;
|
||||
paramlist[1] = NULL;
|
||||
|
||||
if (createInstance(opts, paramlist))
|
||||
{
|
||||
printf("Rule parsing was successful.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Failed to parse rule. Read the error log for the reason of the failure.\n");
|
||||
}
|
||||
|
||||
mxs_log_flush_sync();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -250,41 +250,11 @@ static void
|
||||
diagnostics(DCB *dcb, const MONITOR *mon)
|
||||
{
|
||||
const GALERA_MONITOR *handle = (const GALERA_MONITOR *) mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb, "\tMaster Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on");
|
||||
dcb_printf(dcb, "\tAvailable when Donor:\t%s\n", (handle->availableWhenDonor == 1) ? "on" : "off");
|
||||
dcb_printf(dcb, "\tMaster Role Setting Disabled:\t%s\n",
|
||||
dcb_printf(dcb, "Master Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on");
|
||||
dcb_printf(dcb, "Available when Donor:\t%s\n", (handle->availableWhenDonor == 1) ? "on" : "off");
|
||||
dcb_printf(dcb, "Master Role Setting Disabled:\t%s\n",
|
||||
(handle->disableMasterRoleSetting == 1) ? "on" : "off");
|
||||
dcb_printf(dcb, "\tConnect Timeout:\t%i seconds\n", mon->connect_timeout);
|
||||
dcb_printf(dcb, "\tRead Timeout:\t\t%i seconds\n", mon->read_timeout);
|
||||
dcb_printf(dcb, "\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = mon->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -218,35 +218,8 @@ stopMonitor(MONITOR *mon)
|
||||
static void diagnostics(DCB *dcb, const MONITOR *mon)
|
||||
{
|
||||
const MM_MONITOR *handle = (const MM_MONITOR *) mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb, "\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = mon->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
dcb_printf(dcb, "Detect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -403,35 +403,14 @@ stopMonitor(MONITOR *mon)
|
||||
*/
|
||||
static void diagnostics(DCB *dcb, const MONITOR *mon)
|
||||
{
|
||||
const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *) mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *)mon->handle;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
dcb_printf(dcb, "MaxScale MonitorId:\t%lu\n", handle->id);
|
||||
dcb_printf(dcb, "Replication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb, "Detect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb, "Server information\n\n");
|
||||
|
||||
dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb, "\tMaxScale MonitorId:\t%lu\n", handle->id);
|
||||
dcb_printf(dcb, "\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb, "\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb, "\tConnect Timeout:\t%i seconds\n", mon->connect_timeout);
|
||||
dcb_printf(dcb, "\tRead Timeout:\t\t%i seconds\n", mon->read_timeout);
|
||||
dcb_printf(dcb, "\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout);
|
||||
dcb_printf(dcb, "\nMonitored servers\n\n");
|
||||
|
||||
db = mon->databases;
|
||||
|
||||
while (db)
|
||||
for (MONITOR_SERVERS *db = mon->databases; db; db = db->next)
|
||||
{
|
||||
MYSQL_SERVER_INFO *serv_info = hashtable_fetch(handle->server_info, db->server->unique_name);
|
||||
dcb_printf(dcb, "Server: %s\n", db->server->unique_name);
|
||||
@ -450,7 +429,6 @@ static void diagnostics(DCB *dcb, const MONITOR *mon)
|
||||
}
|
||||
|
||||
dcb_printf(dcb, "\n");
|
||||
db = db->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -210,38 +210,6 @@ stopMonitor(MONITOR *mon)
|
||||
static void
|
||||
diagnostics(DCB *dcb, const MONITOR *mon)
|
||||
{
|
||||
const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *) mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb, "\tConnect Timeout:\t%i seconds\n", mon->connect_timeout);
|
||||
dcb_printf(dcb, "\tRead Timeout:\t\t%i seconds\n", mon->read_timeout);
|
||||
dcb_printf(dcb, "\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = mon->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -40,9 +40,9 @@
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/resultset.h>
|
||||
|
||||
/* @see function load_module in load_utils.c for explanation of the following
|
||||
* lint directives.
|
||||
*/
|
||||
/* @see function load_module in load_utils.c for explanation of the following
|
||||
* lint directives.
|
||||
*/
|
||||
/*lint -e14 */
|
||||
MODULE_INFO info =
|
||||
{
|
||||
@ -148,9 +148,9 @@ static int httpd_read_event(DCB* dcb)
|
||||
SESSION *session = dcb->session;
|
||||
|
||||
int numchars = 1;
|
||||
char buf[HTTPD_REQUESTLINE_MAXLEN-1] = "";
|
||||
char buf[HTTPD_REQUESTLINE_MAXLEN - 1] = "";
|
||||
char *query_string = NULL;
|
||||
char method[HTTPD_METHOD_MAXLEN-1] = "";
|
||||
char method[HTTPD_METHOD_MAXLEN - 1] = "";
|
||||
char url[HTTPD_SMALL_BUFFER] = "";
|
||||
size_t i, j;
|
||||
int headers_read = 0;
|
||||
@ -166,11 +166,13 @@ static int httpd_read_event(DCB* dcb)
|
||||
|
||||
numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));
|
||||
|
||||
i = 0; j = 0;
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
|
||||
{
|
||||
method[i] = buf[j];
|
||||
i++; j++;
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
method[i] = '\0';
|
||||
|
||||
@ -193,7 +195,8 @@ static int httpd_read_event(DCB* dcb)
|
||||
while ((j < sizeof(buf) - 1) && !ISspace(buf[j]) && (i < sizeof(url) - 1))
|
||||
{
|
||||
url[i] = buf[j];
|
||||
i++; j++;
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
url[i] = '\0';
|
||||
@ -233,7 +236,7 @@ static int httpd_read_event(DCB* dcb)
|
||||
{
|
||||
*value = '\0';
|
||||
value++;
|
||||
end = &value[strlen(value) -1];
|
||||
end = &value[strlen(value) - 1];
|
||||
*end = '\0';
|
||||
|
||||
if (strncasecmp(buf, "Hostname", 6) == 0)
|
||||
|
||||
@ -734,37 +734,42 @@ struct subcommand disableoptions[] =
|
||||
|
||||
static void telnetdAddUser(DCB *, char *user, char *password);
|
||||
|
||||
static void cmd_AddServer(DCB *dcb, void *a, void *b)
|
||||
static void cmd_AddServer(DCB *dcb, SERVER *server, char *v1, char *v2, char *v3,
|
||||
char *v4, char *v5, char *v6, char *v7, char *v8, char *v9,
|
||||
char *v10, char *v11)
|
||||
{
|
||||
SERVER *server = (SERVER*)a;
|
||||
char *name = (char*)b;
|
||||
char *values[11] = {v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11};
|
||||
const int items = sizeof(values) / sizeof(values[0]);
|
||||
|
||||
SERVICE *service = service_find(name);
|
||||
MONITOR *monitor = monitor_find(name);
|
||||
|
||||
if (service || monitor)
|
||||
for (int i = 0; i < items && values[i]; i++)
|
||||
{
|
||||
ss_dassert(service == NULL || monitor == NULL);
|
||||
SERVICE *service = service_find(values[i]);
|
||||
MONITOR *monitor = monitor_find(values[i]);
|
||||
|
||||
if (service)
|
||||
if (service || monitor)
|
||||
{
|
||||
serviceAddBackend(service, server);
|
||||
service_serialize_servers(service);
|
||||
ss_dassert(service == NULL || monitor == NULL);
|
||||
|
||||
if (service)
|
||||
{
|
||||
serviceAddBackend(service, server);
|
||||
service_serialize_servers(service);
|
||||
}
|
||||
else if (monitor)
|
||||
{
|
||||
monitorAddServer(monitor, server);
|
||||
monitor_serialize_servers(monitor);
|
||||
}
|
||||
|
||||
const char *target = service ? "service" : "monitor";
|
||||
|
||||
MXS_NOTICE("Added server '%s' to %s '%s'", server->unique_name, target, values[i]);
|
||||
dcb_printf(dcb, "Added server '%s' to %s '%s'\n", server->unique_name, target, values[i]);
|
||||
}
|
||||
else if (monitor)
|
||||
else
|
||||
{
|
||||
monitorAddServer(monitor, server);
|
||||
monitor_serialize_servers(monitor);
|
||||
dcb_printf(dcb, "No service or monitor with the name '%s'\n", values[i]);
|
||||
}
|
||||
|
||||
const char *target = service ? "service" : "monitor";
|
||||
|
||||
MXS_NOTICE("Added server '%s' to %s '%s'", server->unique_name, target, name);
|
||||
dcb_printf(dcb, "Added server '%s' to %s '%s'\n", server->unique_name, target, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "No service or monitor with the name '%s'\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -781,11 +786,15 @@ struct subcommand addoptions[] =
|
||||
{ARG_TYPE_STRING, ARG_TYPE_STRING, 0}
|
||||
},
|
||||
{
|
||||
"server", 2, 2, cmd_AddServer,
|
||||
"server", 2, 12, cmd_AddServer,
|
||||
"Add a new server to a service",
|
||||
"Usage: add server SERVER TARGET\n"
|
||||
"The TARGET must be either a service or a monitor",
|
||||
{ARG_TYPE_SERVER, ARG_TYPE_STRING, 0}
|
||||
"Usage: add server SERVER TARGET...\n"
|
||||
"The TARGET must be a list of service and monitor names\n"
|
||||
"e.g. add server my-db my-service 'Cluster Monitor'\n"
|
||||
"A server can be assigned to a maximum of 11 objects in one command",
|
||||
{ARG_TYPE_SERVER, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
|
||||
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
|
||||
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING}
|
||||
},
|
||||
{ EMPTY_OPTION}
|
||||
};
|
||||
@ -793,35 +802,41 @@ struct subcommand addoptions[] =
|
||||
|
||||
static void telnetdRemoveUser(DCB *, char *user, char *password);
|
||||
|
||||
static void cmd_RemoveServer(DCB *dcb, void *a, void *b)
|
||||
static void cmd_RemoveServer(DCB *dcb, SERVER *server, char *v1, char *v2, char *v3,
|
||||
char *v4, char *v5, char *v6, char *v7, char *v8, char *v9,
|
||||
char *v10, char *v11)
|
||||
{
|
||||
SERVER *server = (SERVER*)a;
|
||||
char *name = (char*)b;
|
||||
SERVICE *service = service_find(name);
|
||||
MONITOR *monitor = monitor_find(name);
|
||||
char *values[11] = {v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11};
|
||||
const int items = sizeof(values) / sizeof(values[0]);
|
||||
|
||||
if (service || monitor)
|
||||
for (int i = 0; i < items && values[i]; i++)
|
||||
{
|
||||
ss_dassert(service == NULL || monitor == NULL);
|
||||
SERVICE *service = service_find(values[i]);
|
||||
MONITOR *monitor = monitor_find(values[i]);
|
||||
|
||||
if (service)
|
||||
if (service || monitor)
|
||||
{
|
||||
serviceRemoveBackend(service, server);
|
||||
service_serialize_servers(service);
|
||||
}
|
||||
else if (monitor)
|
||||
{
|
||||
monitorRemoveServer(monitor, server);
|
||||
monitor_serialize_servers(monitor);
|
||||
}
|
||||
ss_dassert(service == NULL || monitor == NULL);
|
||||
|
||||
const char *target = service ? "service" : "monitor";
|
||||
MXS_NOTICE("Removed server '%s' from %s '%s'", server->unique_name, target, name);
|
||||
dcb_printf(dcb, "Removed server '%s' from %s '%s'\n", server->unique_name, target, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "No service or monitor with the name '%s'\n", name);
|
||||
if (service)
|
||||
{
|
||||
serviceRemoveBackend(service, server);
|
||||
service_serialize_servers(service);
|
||||
}
|
||||
else if (monitor)
|
||||
{
|
||||
monitorRemoveServer(monitor, server);
|
||||
monitor_serialize_servers(monitor);
|
||||
}
|
||||
|
||||
const char *target = service ? "service" : "monitor";
|
||||
MXS_NOTICE("Removed server '%s' from %s '%s'", server->unique_name, target, values[i]);
|
||||
dcb_printf(dcb, "Removed server '%s' from %s '%s'\n", server->unique_name, target, values[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "No service or monitor with the name '%s'\n", values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -840,11 +855,15 @@ struct subcommand removeoptions[] =
|
||||
{ARG_TYPE_STRING, ARG_TYPE_STRING}
|
||||
},
|
||||
{
|
||||
"server", 2, 2, cmd_RemoveServer,
|
||||
"server", 2, 12, cmd_RemoveServer,
|
||||
"Remove a server from a service or a monitor",
|
||||
"Usage: remove server SERVER TARGET\n"
|
||||
"The TARGET must be either a service or a monitor",
|
||||
{ARG_TYPE_SERVER, ARG_TYPE_STRING}
|
||||
"Usage: remove server SERVER TARGET...\n"
|
||||
"The TARGET must be a list of service and monitor names\n"
|
||||
"e.g. remove server my-db my-service 'Cluster Monitor'\n"
|
||||
"A server can be removed from a maximum of 11 objects in one command",
|
||||
{ARG_TYPE_SERVER, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
|
||||
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
|
||||
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING}
|
||||
},
|
||||
{
|
||||
EMPTY_OPTION
|
||||
@ -1116,41 +1135,38 @@ static void alterServer(DCB *dcb, SERVER *server, char *v1, char *v2, char *v3,
|
||||
const int items = sizeof(values) / sizeof(values[0]);
|
||||
CONFIG_CONTEXT *obj = NULL;
|
||||
|
||||
for (int i = 0; i < items; i++)
|
||||
for (int i = 0; i < items && values[i]; i++)
|
||||
{
|
||||
if (values[i])
|
||||
char *key = values[i];
|
||||
char *value = strchr(key, '=');
|
||||
|
||||
if (value)
|
||||
{
|
||||
char *key = values[i];
|
||||
char *value = strchr(key, '=');
|
||||
*value++ = '\0';
|
||||
|
||||
if (value)
|
||||
if (config_is_ssl_parameter(key))
|
||||
{
|
||||
*value++ = '\0';
|
||||
|
||||
if (config_is_ssl_parameter(key))
|
||||
/**
|
||||
* All the required SSL parameters must be defined at once to
|
||||
* enable SSL for created servers. This removes the problem
|
||||
* of partial configuration and allows a somewhat atomic
|
||||
* operation.
|
||||
*/
|
||||
if ((obj == NULL && (obj = config_context_create(server->unique_name)) == NULL) ||
|
||||
(!config_add_param(obj, key, value)))
|
||||
{
|
||||
/**
|
||||
* All the required SSL parameters must be defined at once to
|
||||
* enable SSL for created servers. This removes the problem
|
||||
* of partial configuration and allows a somewhat atomic
|
||||
* operation.
|
||||
*/
|
||||
if ((obj == NULL && (obj = config_context_create(server->unique_name)) == NULL) ||
|
||||
(!config_add_param(obj, key, value)))
|
||||
{
|
||||
dcb_printf(dcb, "Internal error, see log for more details\n");
|
||||
}
|
||||
}
|
||||
else if (!handle_alter_server(server, key, value))
|
||||
{
|
||||
dcb_printf(dcb, "Error: Bad key-value parameter: %s=%s\n", key, value);
|
||||
dcb_printf(dcb, "Internal error, see log for more details\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!handle_alter_server(server, key, value))
|
||||
{
|
||||
dcb_printf(dcb, "Error: not a key-value parameter: %s\n", values[i]);
|
||||
dcb_printf(dcb, "Error: Bad key-value parameter: %s=%s\n", key, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "Error: not a key-value parameter: %s\n", values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj)
|
||||
@ -1243,27 +1259,24 @@ static void alterMonitor(DCB *dcb, MONITOR *monitor, char *v1, char *v2, char *v
|
||||
char *values[11] = {v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11};
|
||||
const int items = sizeof(values) / sizeof(values[0]);
|
||||
|
||||
for (int i = 0; i < items; i++)
|
||||
for (int i = 0; i < items && values[i]; i++)
|
||||
{
|
||||
if (values[i])
|
||||
char *key = values[i];
|
||||
char *value = strchr(key, '=');
|
||||
|
||||
if (value)
|
||||
{
|
||||
char *key = values[i];
|
||||
char *value = strchr(key, '=');
|
||||
*value++ = '\0';
|
||||
|
||||
if (value)
|
||||
if (!handle_alter_monitor(monitor, key, value))
|
||||
{
|
||||
*value++ = '\0';
|
||||
|
||||
if (!handle_alter_monitor(monitor, key, value))
|
||||
{
|
||||
dcb_printf(dcb, "Error: Bad key-value parameter: %s=%s\n", key, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "Error: not a key-value parameter: %s\n", values[i]);
|
||||
dcb_printf(dcb, "Error: Bad key-value parameter: %s=%s\n", key, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "Error: not a key-value parameter: %s\n", values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -801,8 +801,8 @@ typedef void *(*STATSFUNC)();
|
||||
*/
|
||||
static struct
|
||||
{
|
||||
char *name;
|
||||
int type;
|
||||
char *name;
|
||||
int type;
|
||||
STATSFUNC func;
|
||||
} variables[] =
|
||||
{
|
||||
@ -820,10 +820,9 @@ static struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int index;
|
||||
int index;
|
||||
char *like;
|
||||
} VARCONTEXT;
|
||||
|
||||
/**
|
||||
* Callback function to populate rows of the show variable
|
||||
* command
|
||||
@ -860,10 +859,14 @@ variable_row(RESULTSET *result, void *data)
|
||||
(long)(*variables[context->index].func)());
|
||||
resultset_row_set(row, 1, buf);
|
||||
break;
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
context->index++;
|
||||
return row;
|
||||
}
|
||||
// We only get to this point once all variables have been printed
|
||||
MXS_FREE(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -909,15 +912,19 @@ RESULTSET *
|
||||
maxinfo_variables()
|
||||
{
|
||||
RESULTSET *result;
|
||||
static VARCONTEXT context;
|
||||
|
||||
context.like = NULL;
|
||||
context.index = 0;
|
||||
|
||||
if ((result = resultset_create(variable_row, &context)) == NULL)
|
||||
VARCONTEXT *context;
|
||||
if ((context = MXS_MALLOC(sizeof(VARCONTEXT))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
context->like = NULL;
|
||||
context->index = 0;
|
||||
|
||||
if ((result = resultset_create(variable_row, context)) == NULL)
|
||||
{
|
||||
MXS_FREE(context);
|
||||
return NULL;
|
||||
}
|
||||
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
||||
resultset_add_column(result, "Value", 40, COL_TYPE_VARCHAR);
|
||||
return result;
|
||||
@ -1138,10 +1145,14 @@ status_row(RESULTSET *result, void *data)
|
||||
(long)(*status[context->index].func)());
|
||||
resultset_row_set(row, 1, buf);
|
||||
break;
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
context->index++;
|
||||
return row;
|
||||
}
|
||||
// We only get to this point once all status elements have been printed
|
||||
MXS_FREE(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1186,16 +1197,20 @@ exec_show_status(DCB *dcb, MAXINFO_TREE *filter)
|
||||
RESULTSET *
|
||||
maxinfo_status()
|
||||
{
|
||||
RESULTSET *result;
|
||||
static VARCONTEXT context;
|
||||
|
||||
context.like = NULL;
|
||||
context.index = 0;
|
||||
|
||||
if ((result = resultset_create(status_row, &context)) == NULL)
|
||||
RESULTSET *result;
|
||||
VARCONTEXT *context;
|
||||
if ((context = MXS_MALLOC(sizeof(VARCONTEXT))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
context->like = NULL;
|
||||
context->index = 0;
|
||||
|
||||
if ((result = resultset_create(status_row, context)) == NULL)
|
||||
{
|
||||
MXS_FREE(context);
|
||||
return NULL;
|
||||
}
|
||||
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
||||
resultset_add_column(result, "Value", 40, COL_TYPE_VARCHAR);
|
||||
return result;
|
||||
@ -1220,14 +1235,13 @@ exec_select(DCB *dcb, MAXINFO_TREE *tree)
|
||||
*
|
||||
* @param pattern Pattern to match
|
||||
* @param str String to match against pattern
|
||||
* @return Zero on match
|
||||
* @return Zero on match
|
||||
*/
|
||||
static int
|
||||
maxinfo_pattern_match(char *pattern, char *str)
|
||||
{
|
||||
int anchor = 0, len, trailing;
|
||||
char *fixed;
|
||||
extern char *strcasestr();
|
||||
|
||||
if (*pattern != '%')
|
||||
{
|
||||
|
||||
@ -718,7 +718,7 @@ return_succp:
|
||||
* if the query would otherwise be routed to slave.
|
||||
*/
|
||||
route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
qc_query_type_t qtype, HINT *hint)
|
||||
qc_query_type_t qtype, HINT *hint)
|
||||
{
|
||||
bool trx_active = rses->rses_transaction_active;
|
||||
bool load_active = rses->rses_load_active;
|
||||
@ -735,9 +735,10 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
*/
|
||||
else if (!load_active &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||
/** Configured to allow writing variables to all nodes */
|
||||
/** Configured to allow writing user variables to all nodes */
|
||||
(use_sql_variables_in == TYPE_ALL &&
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE)) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE)) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) ||
|
||||
/** enable or disable autocommit are always routed to all */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT)))
|
||||
@ -777,44 +778,29 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
* Hints may affect on routing of the following queries
|
||||
*/
|
||||
else if (!trx_active && !load_active &&
|
||||
!qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) &&
|
||||
!qc_query_is_type(qtype, QUERY_TYPE_WRITE) &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_READ) || /*< any SELECT */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || /*< 'SHOW TABLES' */
|
||||
qc_query_is_type(qtype,
|
||||
QUERY_TYPE_USERVAR_READ) || /*< read user var */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || /*< read sys var */
|
||||
qc_query_is_type(qtype,
|
||||
QUERY_TYPE_EXEC_STMT) || /*< prepared stmt exec */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) ||
|
||||
qc_query_is_type(qtype,
|
||||
QUERY_TYPE_GSYSVAR_READ))) /*< read global sys var */
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)))
|
||||
{
|
||||
/** First set expected targets before evaluating hints */
|
||||
if (!qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || /*< 'SHOW TABLES' */
|
||||
/** Configured to allow reading variables from slaves */
|
||||
(use_sql_variables_in == TYPE_ALL &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)))))
|
||||
if (qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ))
|
||||
{
|
||||
if (use_sql_variables_in == TYPE_ALL)
|
||||
{
|
||||
target = TARGET_SLAVE;
|
||||
}
|
||||
}
|
||||
else if (qc_query_is_type(qtype, QUERY_TYPE_READ) || // Normal read
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || // SHOW TABLES
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || // System variable
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)) // Global system variable
|
||||
{
|
||||
target = TARGET_SLAVE;
|
||||
}
|
||||
|
||||
if (qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) ||
|
||||
/** Configured not to allow reading variables from slaves */
|
||||
(use_sql_variables_in == TYPE_MASTER &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ))))
|
||||
{
|
||||
target = TARGET_MASTER;
|
||||
}
|
||||
|
||||
/** If nothing matches then choose the master */
|
||||
if ((target & (TARGET_ALL | TARGET_SLAVE | TARGET_MASTER)) == 0)
|
||||
{
|
||||
@ -823,8 +809,7 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
}
|
||||
else
|
||||
{
|
||||
/** hints don't affect on routing */
|
||||
ss_dassert(trx_active ||
|
||||
ss_dassert(trx_active || load_active ||
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_WRITE) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||
@ -836,6 +821,8 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
use_sql_variables_in == TYPE_MASTER) ||
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) &&
|
||||
use_sql_variables_in == TYPE_MASTER) ||
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE) &&
|
||||
use_sql_variables_in == TYPE_MASTER) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_BEGIN_TRX) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT) ||
|
||||
@ -844,7 +831,10 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_CREATE_TMP_TABLE) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_READ_TMP_TABLE) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_UNKNOWN)));
|
||||
qc_query_is_type(qtype, QUERY_TYPE_UNKNOWN)) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT));
|
||||
target = TARGET_MASTER;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user