Merge branch 'develop' into 1.2.1-binlog_router
Conflicts: server/core/server.c server/include/server.h server/modules/include/blr.h server/modules/routing/binlog/blr.c server/modules/routing/binlog/blr_file.c server/modules/routing/binlog/blr_master.c server/modules/routing/binlog/blr_slave.c
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
if(BUILD_TESTS OR BUILD_TOOLS)
|
||||
add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c)
|
||||
add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c externcmd.c)
|
||||
if(WITH_JEMALLOC)
|
||||
target_link_libraries(fullcore ${JEMALLOC_LIBRARIES})
|
||||
elseif(WITH_TCMALLOC)
|
||||
@ -12,7 +12,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c
|
||||
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c
|
||||
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c
|
||||
monitor.c adminusers.c secrets.c filter.c modutil.c hint.c
|
||||
housekeeper.c memlog.c resultset.c)
|
||||
housekeeper.c memlog.c resultset.c gwdirs.c externcmd.c)
|
||||
|
||||
if(WITH_JEMALLOC)
|
||||
target_link_libraries(maxscale ${JEMALLOC_LIBRARIES})
|
||||
@ -21,15 +21,15 @@ elseif(WITH_TCMALLOC)
|
||||
endif()
|
||||
|
||||
target_link_libraries(maxscale ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++)
|
||||
install(TARGETS maxscale DESTINATION bin)
|
||||
install(TARGETS maxscale DESTINATION ${MAXSCALE_BINDIR})
|
||||
|
||||
add_executable(maxkeys maxkeys.c secrets.c utils.c)
|
||||
add_executable(maxkeys maxkeys.c secrets.c utils.c gwdirs.c)
|
||||
target_link_libraries(maxkeys log_manager utils pthread crypt crypto)
|
||||
install(TARGETS maxkeys DESTINATION bin)
|
||||
install(TARGETS maxkeys DESTINATION ${MAXSCALE_BINDIR})
|
||||
|
||||
add_executable(maxpasswd maxpasswd.c secrets.c utils.c)
|
||||
add_executable(maxpasswd maxpasswd.c secrets.c utils.c gwdirs.c)
|
||||
target_link_libraries(maxpasswd log_manager utils pthread crypt crypto)
|
||||
install(TARGETS maxpasswd DESTINATION bin)
|
||||
install(TARGETS maxpasswd DESTINATION ${MAXSCALE_BINDIR})
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
|
||||
@ -19,13 +19,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#define _XOPEN_SOURCE
|
||||
#ifndef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 700
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <crypt.h>
|
||||
#include <users.h>
|
||||
#include <adminusers.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <gwdirs.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
@ -119,12 +122,7 @@ char fname[1024], *home;
|
||||
char uname[80], passwd[80];
|
||||
|
||||
initialise();
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){
|
||||
sprintf(fname, "%s/etc/passwd", home);
|
||||
}
|
||||
else{
|
||||
sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd");
|
||||
}
|
||||
sprintf(fname, "%s/passwd", get_datadir());
|
||||
if ((fp = fopen(fname, "r")) == NULL)
|
||||
return NULL;
|
||||
if ((rval = users_alloc()) == NULL)
|
||||
@ -155,12 +153,7 @@ FILE *fp;
|
||||
char fname[1024], *home, *cpasswd;
|
||||
|
||||
initialise();
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){
|
||||
sprintf(fname, "%s/etc/passwd", home);
|
||||
}
|
||||
else{
|
||||
sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd");
|
||||
}
|
||||
sprintf(fname, "%s/passwd", get_datadir());
|
||||
|
||||
if (users == NULL)
|
||||
{
|
||||
@ -253,15 +246,8 @@ char* admin_remove_user(
|
||||
/**
|
||||
* Open passwd file and remove user from the file.
|
||||
*/
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL &&
|
||||
strnlen(home,PATH_MAX) < PATH_MAX &&
|
||||
strnlen(home,PATH_MAX) > 0) {
|
||||
sprintf(fname, "%s/etc/passwd", home);
|
||||
sprintf(fname_tmp, "%s/etc/passwd_tmp", home);
|
||||
} else {
|
||||
sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd");
|
||||
sprintf(fname_tmp, "/usr/local/mariadb-maxscale/etc/passwd_tmp");
|
||||
}
|
||||
sprintf(fname, "%s/passwd", get_datadir());
|
||||
sprintf(fname_tmp, "%s/passwd_tmp", get_datadir());
|
||||
/**
|
||||
* Rewrite passwd file from memory.
|
||||
*/
|
||||
|
||||
@ -197,6 +197,7 @@ GWBUF *rval;
|
||||
rval->gwbuf_info = buf->gwbuf_info;
|
||||
rval->gwbuf_bufobj = buf->gwbuf_bufobj;
|
||||
rval->tail = rval;
|
||||
rval->next = NULL;
|
||||
CHK_GWBUF(rval);
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -41,9 +41,9 @@
|
||||
* 30/10/14 Massimiliano Pinto Added disable_master_failback parameter
|
||||
* 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write
|
||||
* 20/02/15 Markus Mäkelä Added connection_timeout parameter for services
|
||||
* 05/03/15 Massimiliano Pinto Added notification_feedback support
|
||||
* 05/03/15 Massimiliano Pinto Added notification_feedback support
|
||||
* 20/04/15 Guillaume Lefranc Added available_when_donor parameter
|
||||
* 22/04/15 Martin Brampton Added disable_master_role_setting parameter
|
||||
* 22/04/15 Martin Brampton Added disable_master_role_setting parameter
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -85,6 +85,7 @@ static int process_config_context(CONFIG_CONTEXT *);
|
||||
static int process_config_update(CONFIG_CONTEXT *);
|
||||
static void free_config_context(CONFIG_CONTEXT *);
|
||||
static char *config_get_value(CONFIG_PARAMETER *, const char *);
|
||||
static const char *config_get_value_string(CONFIG_PARAMETER *, const char *);
|
||||
static int handle_global_item(const char *, const char *);
|
||||
static int handle_feedback_item(const char *, const char *);
|
||||
static void global_defaults();
|
||||
@ -300,18 +301,18 @@ int rval;
|
||||
static int
|
||||
process_config_context(CONFIG_CONTEXT *context)
|
||||
{
|
||||
CONFIG_CONTEXT *obj;
|
||||
int error_count = 0;
|
||||
HASHTABLE* monitorhash;
|
||||
CONFIG_CONTEXT *obj;
|
||||
int error_count = 0;
|
||||
HASHTABLE* monitorhash;
|
||||
|
||||
if((monitorhash = hashtable_alloc(5,simple_str_hash,strcmp)) == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Failed to allocate ,onitor configuration check hashtable.");
|
||||
return 0;
|
||||
}
|
||||
if((monitorhash = hashtable_alloc(5,simple_str_hash,strcmp)) == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Failed to allocate ,monitor configuration check hashtable.");
|
||||
return 0;
|
||||
}
|
||||
hashtable_memory_fns(monitorhash,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,NULL);
|
||||
|
||||
hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
|
||||
/**
|
||||
/**
|
||||
* Process the data and create the services and servers defined
|
||||
* in the data.
|
||||
*/
|
||||
@ -345,6 +346,8 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
|
||||
char *weightby;
|
||||
char *version_string;
|
||||
char *subservices;
|
||||
char *ssl,*ssl_cert,*ssl_key,*ssl_ca_cert,*ssl_version;
|
||||
char* ssl_cert_verify_depth;
|
||||
bool is_rwsplit = false;
|
||||
bool is_schemarouter = false;
|
||||
char *allow_localhost_match_wildcard_host;
|
||||
@ -353,6 +356,12 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
|
||||
user = config_get_value(obj->parameters, "user");
|
||||
auth = config_get_value(obj->parameters, "passwd");
|
||||
subservices = config_get_value(obj->parameters, "subservices");
|
||||
ssl = config_get_value(obj->parameters, "ssl");
|
||||
ssl_cert = config_get_value(obj->parameters, "ssl_cert");
|
||||
ssl_key = config_get_value(obj->parameters, "ssl_key");
|
||||
ssl_ca_cert = config_get_value(obj->parameters, "ssl_ca_cert");
|
||||
ssl_version = config_get_value(obj->parameters, "ssl_version");
|
||||
ssl_cert_verify_depth = config_get_value(obj->parameters, "ssl_cert_verify_depth");
|
||||
enable_root_user = config_get_value(
|
||||
obj->parameters,
|
||||
"enable_root_user");
|
||||
@ -443,7 +452,84 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
|
||||
max_slave_rlag_str =
|
||||
config_get_value(obj->parameters,
|
||||
"max_slave_replication_lag");
|
||||
|
||||
|
||||
if(ssl)
|
||||
{
|
||||
if(ssl_cert == NULL)
|
||||
{
|
||||
error_count++;
|
||||
skygw_log_write(LE,"Error: Server certificate missing for service '%s'."
|
||||
"Please provide the path to the server certificate by adding the ssl_cert=<path> parameter",
|
||||
obj->object);
|
||||
}
|
||||
if(ssl_ca_cert == NULL)
|
||||
{
|
||||
error_count++;
|
||||
skygw_log_write(LE,"Error: CA Certificate missing for service '%s'."
|
||||
"Please provide the path to the certificate authority certificate by adding the ssl_ca_cert=<path> parameter",
|
||||
obj->object);
|
||||
}
|
||||
if(ssl_key == NULL)
|
||||
{
|
||||
error_count++;
|
||||
skygw_log_write(LE,"Error: Server private key missing for service '%s'. "
|
||||
"Please provide the path to the server certificate key by adding the ssl_key=<path> parameter"
|
||||
,obj->object);
|
||||
}
|
||||
|
||||
if(access(ssl_ca_cert,F_OK) != 0)
|
||||
{
|
||||
skygw_log_write(LE,"Error: Certificate authority file for service '%s' not found: %s",
|
||||
obj->object,
|
||||
ssl_ca_cert);
|
||||
error_count++;
|
||||
}
|
||||
if(access(ssl_cert,F_OK) != 0)
|
||||
{
|
||||
skygw_log_write(LE,"Error: Server certificate file for service '%s' not found: %s",
|
||||
obj->object,
|
||||
ssl_cert);
|
||||
error_count++;
|
||||
}
|
||||
if(access(ssl_key,F_OK) != 0)
|
||||
{
|
||||
skygw_log_write(LE,"Error: Server private key file for service '%s' not found: %s",
|
||||
obj->object,
|
||||
ssl_key);
|
||||
error_count++;
|
||||
}
|
||||
|
||||
if(error_count == 0)
|
||||
{
|
||||
if(serviceSetSSL(obj->element,ssl) != 0)
|
||||
{
|
||||
skygw_log_write(LE,"Error: Unknown parameter for service '%s': %s",obj->object,ssl);
|
||||
error_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceSetCertificates(obj->element,ssl_cert,ssl_key,ssl_ca_cert);
|
||||
if(ssl_version)
|
||||
{
|
||||
if(serviceSetSSLVersion(obj->element,ssl_version) != 0)
|
||||
{
|
||||
skygw_log_write(LE,"Error: Unknown parameter value for 'ssl_version' for service '%s': %s",obj->object,ssl_version);
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
if(ssl_cert_verify_depth)
|
||||
{
|
||||
if(serviceSetSSLVerifyDepth(obj->element,atoi(ssl_cert_verify_depth)) != 0)
|
||||
{
|
||||
skygw_log_write(LE,"Error: Invalid parameter value for 'ssl_cert_verify_depth' for service '%s': %s",obj->object,ssl_cert_verify_depth);
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (enable_root_user)
|
||||
serviceEnableRootUser(
|
||||
obj->element,
|
||||
@ -678,6 +764,9 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
|
||||
}
|
||||
if (obj->element)
|
||||
{
|
||||
SERVER *server = obj->element;
|
||||
server->persistpoolmax = strtol(config_get_value_string(obj->parameters, "persistpoolmax"), NULL, 0);
|
||||
server->persistmaxtime = strtol(config_get_value_string(obj->parameters, "persistmaxtime"), NULL, 0);
|
||||
CONFIG_PARAMETER *params = obj->parameters;
|
||||
while (params)
|
||||
{
|
||||
@ -691,6 +780,10 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
|
||||
"monitorpw")
|
||||
&& strcmp(params->name,
|
||||
"type")
|
||||
&& strcmp(params->name,
|
||||
"persistpoolmax")
|
||||
&& strcmp(params->name,
|
||||
"persistmaxtime")
|
||||
)
|
||||
{
|
||||
serverAddParameter(obj->element,
|
||||
@ -847,62 +940,69 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
|
||||
/* if id is not set, do it now */
|
||||
if (gateway.id == 0) {
|
||||
setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address);
|
||||
gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + port != NULL ? atoi(port) : 0 + getpid());
|
||||
}
|
||||
|
||||
if (service && socket && protocol) {
|
||||
CONFIG_CONTEXT *ptr = context;
|
||||
while (ptr && strcmp(ptr->object, service) != 0)
|
||||
ptr = ptr->next;
|
||||
if (ptr && ptr->element)
|
||||
{
|
||||
serviceAddProtocol(ptr->element,
|
||||
protocol,
|
||||
socket,
|
||||
0);
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Listener '%s', "
|
||||
"service '%s' not found. "
|
||||
"Listener will not execute for socket %s.",
|
||||
obj->object, service, socket)));
|
||||
error_count++;
|
||||
}
|
||||
gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + (port != NULL ? atoi(port) : 0 + getpid()));
|
||||
}
|
||||
|
||||
if (service && port && protocol) {
|
||||
if(service && protocol && (socket || port))
|
||||
{
|
||||
if (socket)
|
||||
{
|
||||
CONFIG_CONTEXT *ptr = context;
|
||||
while (ptr && strcmp(ptr->object, service) != 0)
|
||||
ptr = ptr->next;
|
||||
ptr = ptr->next;
|
||||
if (ptr && ptr->element)
|
||||
{
|
||||
serviceAddProtocol(ptr->element,
|
||||
protocol,
|
||||
address,
|
||||
atoi(port));
|
||||
serviceAddProtocol(ptr->element,
|
||||
protocol,
|
||||
socket,
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Listener '%s', "
|
||||
"service '%s' not found. "
|
||||
"Listener will not execute for socket %s.",
|
||||
obj->object, service, socket)));
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (port)
|
||||
{
|
||||
CONFIG_CONTEXT *ptr = context;
|
||||
while (ptr && strcmp(ptr->object, service) != 0)
|
||||
ptr = ptr->next;
|
||||
if (ptr && ptr->element)
|
||||
{
|
||||
serviceAddProtocol(ptr->element,
|
||||
protocol,
|
||||
address,
|
||||
atoi(port));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Listener '%s', "
|
||||
"service '%s' not found. "
|
||||
"Listener will not execute.",
|
||||
obj->object, service)));
|
||||
error_count++;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Listener '%s', "
|
||||
"service '%s' not found. "
|
||||
"Listener will not execute.",
|
||||
obj->object, service)));
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Listener '%s' is misisng a "
|
||||
"required "
|
||||
"parameter. A Listener must have a "
|
||||
"service, port and protocol defined.",
|
||||
obj->object)));
|
||||
error_count++;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Listener '%s' is missing a "
|
||||
"required "
|
||||
"parameter. A Listener must have a "
|
||||
"service, port and protocol defined.",
|
||||
obj->object)));
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(type, "monitor"))
|
||||
@ -1081,6 +1181,25 @@ config_get_value(CONFIG_PARAMETER *params, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a config parameter as a string
|
||||
*
|
||||
* @param params The linked list of config parameters
|
||||
* @param name The parameter to return
|
||||
* @return the parameter value or null string if not found
|
||||
*/
|
||||
static const char *
|
||||
config_get_value_string(CONFIG_PARAMETER *params, const char *name)
|
||||
{
|
||||
while (params)
|
||||
{
|
||||
if (!strcmp(params->name, name))
|
||||
return (const char *)params->value;
|
||||
params = params->next;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
CONFIG_PARAMETER* config_get_param(
|
||||
CONFIG_PARAMETER* params,
|
||||
@ -1339,7 +1458,7 @@ int i;
|
||||
}
|
||||
else if (strcmp(name, "ms_timestamp") == 0)
|
||||
{
|
||||
skygw_set_highp(config_truth_value(value));
|
||||
skygw_set_highp(config_truth_value((char*)value));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1347,7 +1466,7 @@ int i;
|
||||
{
|
||||
if (strcasecmp(name, lognames[i].logname) == 0)
|
||||
{
|
||||
if (config_truth_value(value))
|
||||
if (config_truth_value((char*)value))
|
||||
skygw_log_enable(lognames[i].logfile);
|
||||
else
|
||||
skygw_log_disable(lognames[i].logfile);
|
||||
@ -1645,9 +1764,6 @@ SERVER *server;
|
||||
char *enable_root_user;
|
||||
char *connection_timeout;
|
||||
char *allow_localhost_match_wildcard_host;
|
||||
char *auth_all_servers;
|
||||
char *optimize_wildcard;
|
||||
char *strip_db_esc;
|
||||
|
||||
enable_root_user =
|
||||
config_get_value(obj->parameters,
|
||||
@ -1655,16 +1771,6 @@ SERVER *server;
|
||||
|
||||
connection_timeout = config_get_value(obj->parameters,
|
||||
"connection_timeout");
|
||||
|
||||
auth_all_servers =
|
||||
config_get_value(obj->parameters,
|
||||
"auth_all_servers");
|
||||
optimize_wildcard =
|
||||
config_get_value(obj->parameters,
|
||||
"optimize_wildcard");
|
||||
strip_db_esc =
|
||||
config_get_value(obj->parameters,
|
||||
"strip_db_esc");
|
||||
|
||||
allow_localhost_match_wildcard_host =
|
||||
config_get_value(obj->parameters, "localhost_match_wildcard_host");
|
||||
@ -1735,6 +1841,9 @@ SERVER *server;
|
||||
obj->element = server_alloc(address,
|
||||
protocol,
|
||||
atoi(port));
|
||||
|
||||
server_set_unique_name(obj->element, obj->object);
|
||||
|
||||
if (obj->element && monuser && monpw)
|
||||
{
|
||||
serverAddMonUser(obj->element,
|
||||
@ -1922,6 +2031,12 @@ static char *service_params[] =
|
||||
"version_string",
|
||||
"filters",
|
||||
"weightby",
|
||||
"ssl_cert",
|
||||
"ssl_ca_cert",
|
||||
"ssl",
|
||||
"ssl_key",
|
||||
"ssl_version",
|
||||
"ssl_cert_verify_depth",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -1943,6 +2058,9 @@ static char *monitor_params[] =
|
||||
"servers",
|
||||
"user",
|
||||
"passwd",
|
||||
"script",
|
||||
"events",
|
||||
"mysql51_replication",
|
||||
"monitor_interval",
|
||||
"detect_replication_lag",
|
||||
"detect_stale_master",
|
||||
|
||||
@ -210,7 +210,7 @@ HASHTABLE *oldresources;
|
||||
oldusers = service->users;
|
||||
|
||||
/* digest compare */
|
||||
if (memcmp(oldusers->cksum, newusers->cksum, SHA_DIGEST_LENGTH) == 0) {
|
||||
if (oldusers != NULL && memcmp(oldusers->cksum, newusers->cksum, SHA_DIGEST_LENGTH) == 0) {
|
||||
/* same data, nothing to do */
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
@ -234,7 +234,7 @@ HASHTABLE *oldresources;
|
||||
|
||||
spinlock_release(&service->spin);
|
||||
|
||||
if (i) {
|
||||
if (i && oldusers) {
|
||||
/* free the old table */
|
||||
users_free(oldusers);
|
||||
}
|
||||
@ -974,7 +974,7 @@ getAllUsers(SERVICE *service, USERS *users)
|
||||
}
|
||||
}
|
||||
|
||||
if(service->optimize_wildcard && havedb && wildcard_db_grant(dbnm))
|
||||
if(havedb && wildcard_db_grant(dbnm) && service->optimize_wildcard)
|
||||
{
|
||||
rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources);
|
||||
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc);
|
||||
@ -984,9 +984,9 @@ getAllUsers(SERVICE *service, USERS *users)
|
||||
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], havedb ? dbnm : NULL);
|
||||
}
|
||||
|
||||
skygw_log_write(LOGFILE_DEBUG,"%s: Adding user:%s host:%s anydb:%s db:%s.",
|
||||
LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG,"%s: Adding user:%s host:%s anydb:%s db:%s.",
|
||||
service->name,row[0],row[1],row[4],
|
||||
havedb ? dbnm : NULL);
|
||||
havedb ? dbnm : NULL)));
|
||||
} else {
|
||||
/* we don't have dbgrants, simply set ANY DB for the user */
|
||||
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL);
|
||||
@ -1033,8 +1033,8 @@ getAllUsers(SERVICE *service, USERS *users)
|
||||
|
||||
} else if(rc == -1) {
|
||||
/** Duplicate user*/
|
||||
LOGIF(LE,(skygw_log_write(LT|LE,
|
||||
"Warning: Duplicate MySQL user found for service [%s]: %s@%s%s%s",
|
||||
LOGIF(LT,(skygw_log_write(LT,
|
||||
"Duplicate MySQL user found for service [%s]: %s@%s%s%s",
|
||||
service->name,
|
||||
row[0],row[1],havedb?" for database: ":"",
|
||||
havedb ?dbnm:"")));
|
||||
@ -1118,6 +1118,7 @@ getUsers(SERVICE *service, USERS *users)
|
||||
MYSQL_DATABASE_MAXLEN;
|
||||
int dbnames = 0;
|
||||
int db_grants = 0;
|
||||
char dbnm[MYSQL_DATABASE_MAXLEN+1];
|
||||
|
||||
if (serviceGetUser(service, &service_user, &service_passwd) == 0)
|
||||
{
|
||||
@ -1474,16 +1475,40 @@ getUsers(SERVICE *service, USERS *users)
|
||||
*/
|
||||
|
||||
if (db_grants) {
|
||||
/* we have dbgrants, store them */
|
||||
bool havedb = false;
|
||||
/* we have dbgrants, store them */
|
||||
if(row[5]){
|
||||
unsigned long *rowlen = mysql_fetch_lengths(result);
|
||||
memcpy(dbnm,row[5],rowlen[5]);
|
||||
memset(dbnm + rowlen[5],0,1);
|
||||
havedb = true;
|
||||
if(service->strip_db_esc) {
|
||||
strip_escape_chars(dbnm);
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"[%s]: %s -> %s",
|
||||
service->name,
|
||||
row[5],
|
||||
dbnm)));
|
||||
}
|
||||
}
|
||||
|
||||
if(service->optimize_wildcard && wildcard_db_grant(row[5]))
|
||||
if(havedb && wildcard_db_grant(row[5]))
|
||||
{
|
||||
rc = add_wildcard_users(users, row[0], row[1], password, row[4], row[5], service->resources);
|
||||
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,row[5],rc);
|
||||
if(service->optimize_wildcard)
|
||||
{
|
||||
rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources);
|
||||
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,row[5],rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Use ANYDB for wildcard grants */
|
||||
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], row[5]);
|
||||
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], havedb ? dbnm : NULL);
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -1683,6 +1708,41 @@ static int uh_cmpfun( void* v1, void* v2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(hu2->resource && strlen(hu2->resource) && strchr(hu2->resource,'%') != NULL)
|
||||
{
|
||||
regex_t re;
|
||||
char db[MYSQL_DATABASE_MAXLEN*2 +1];
|
||||
strcpy(db,hu2->resource);
|
||||
int len = strlen(db);
|
||||
char* ptr = strrchr(db,'%');
|
||||
|
||||
if(ptr == NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
while(ptr)
|
||||
{
|
||||
memmove(ptr+1,ptr,(len - (ptr - db)) + 1);
|
||||
*ptr = '.';
|
||||
*(ptr + 1) = '*';
|
||||
len = strlen(db);
|
||||
ptr = strrchr(db,'%');
|
||||
}
|
||||
|
||||
if((regcomp(&re,db,REG_ICASE|REG_NOSUB)))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(regexec(&re,hu1->resource,0,NULL,0) == 0)
|
||||
{
|
||||
regfree(&re);
|
||||
return 0;
|
||||
}
|
||||
regfree(&re);
|
||||
}
|
||||
|
||||
/* no matches, deny auth */
|
||||
return 1;
|
||||
}
|
||||
@ -1792,18 +1852,6 @@ char *mysql_format_user_entry(void *data)
|
||||
return mysql_user;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hash function we use for storing MySQL database names.
|
||||
*
|
||||
* @param key The key value
|
||||
* @return The hash key
|
||||
*/
|
||||
int
|
||||
resource_hash(char *key)
|
||||
{
|
||||
return (*key + *(key + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the resources table
|
||||
*
|
||||
@ -1827,7 +1875,7 @@ resource_alloc()
|
||||
{
|
||||
HASHTABLE *resources;
|
||||
|
||||
if ((resources = hashtable_alloc(10, resource_hash, strcmp)) == NULL)
|
||||
if ((resources = hashtable_alloc(10, simple_str_hash, strcmp)) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
2513
server/core/dcb.c
2513
server/core/dcb.c
File diff suppressed because it is too large
Load Diff
169
server/core/externcmd.c
Normal file
169
server/core/externcmd.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include <externcmd.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
extern __thread log_info_t tls_log_info;
|
||||
|
||||
/**
|
||||
* Tokenize a string into arguments suitable for a execvp call.
|
||||
* @param args Argument string
|
||||
* @param argv Array of char pointers to be filled with tokenized arguments
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int tokenize_arguments(char* args, char** argv)
|
||||
{
|
||||
int i = 0;
|
||||
bool quoted = false;
|
||||
bool read = false;
|
||||
bool escaped = false;
|
||||
char *ptr,*start;
|
||||
char qc;
|
||||
|
||||
start = args;
|
||||
ptr = start;
|
||||
|
||||
while(*ptr != '\0' && i < MAXSCALE_EXTCMD_ARG_MAX)
|
||||
{
|
||||
if(escaped)
|
||||
{
|
||||
escaped = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*ptr == '\\')
|
||||
{
|
||||
escaped = true;
|
||||
}
|
||||
else if(quoted && !escaped && *ptr == qc) /** End of quoted string */
|
||||
{
|
||||
*ptr = '\0';
|
||||
argv[i++] = strdup(start);
|
||||
read = false;
|
||||
quoted = false;
|
||||
}
|
||||
else if (!quoted)
|
||||
{
|
||||
if(isspace(*ptr))
|
||||
{
|
||||
*ptr = '\0';
|
||||
if(read) /** New token */
|
||||
{
|
||||
argv[i++] = strdup(start);
|
||||
read = false;
|
||||
}
|
||||
}
|
||||
else if( *ptr == '\"' || *ptr == '\'')
|
||||
{
|
||||
/** New quoted token, strip quotes */
|
||||
quoted = true;
|
||||
qc = *ptr;
|
||||
start = ptr + 1;
|
||||
}
|
||||
else if(!read)
|
||||
{
|
||||
start = ptr;
|
||||
read = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
if(read)
|
||||
argv[i++] = strdup(start);
|
||||
argv[i] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new external command.
|
||||
* The name and parameters are copied into the external command structure so
|
||||
* the original memory can be freed if needed.
|
||||
* @param command Command to execute with the parameters
|
||||
* @return Pointer to new external command struct or NULL if an error occurred
|
||||
*/
|
||||
EXTERNCMD* externcmd_allocate(char* argstr)
|
||||
{
|
||||
EXTERNCMD* cmd;
|
||||
|
||||
if(argstr == NULL)
|
||||
return NULL;
|
||||
|
||||
if((cmd = (EXTERNCMD*)malloc(sizeof(EXTERNCMD))) != NULL)
|
||||
{
|
||||
if(tokenize_arguments(argstr,cmd->parameters) == -1)
|
||||
{
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
if(access(cmd->parameters[0],F_OK) != 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: Cannot find file: %s",
|
||||
cmd->parameters[0]);
|
||||
externcmd_free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(access(cmd->parameters[0],X_OK) != 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: Cannot execute file: %s",
|
||||
cmd->parameters[0]);
|
||||
externcmd_free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a previously allocated external command.
|
||||
* @param cmd Command to free
|
||||
*/
|
||||
void externcmd_free(EXTERNCMD* cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0;cmd->parameters[i] != NULL;i++)
|
||||
{
|
||||
free(cmd->parameters[i]);
|
||||
}
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
*Execute a command in a separate process.
|
||||
*@param cmd Command to execute
|
||||
*@return 0 on success, -1 on error.
|
||||
*/
|
||||
int externcmd_execute(EXTERNCMD* cmd)
|
||||
{
|
||||
int rval = 0;
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if(pid < 0)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s', fork failed: [%d] %s",
|
||||
cmd->parameters[0],errno,strerror(errno));
|
||||
rval = -1;
|
||||
}
|
||||
else if(pid == 0)
|
||||
{
|
||||
/** Child process, execute command */
|
||||
execvp(cmd->parameters[0],cmd->parameters);
|
||||
_exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->child = pid;
|
||||
cmd->n_exec++;
|
||||
LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd));
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
29
server/core/gwdirs.c
Normal file
29
server/core/gwdirs.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include <gwdirs.h>
|
||||
|
||||
/**
|
||||
* Get the directory with all the modules.
|
||||
* @return The module directory
|
||||
*/
|
||||
char* get_libdir()
|
||||
{
|
||||
return libdir?libdir:(char*)default_libdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service cache directory
|
||||
* @return The path to the cache directory
|
||||
*/
|
||||
char* get_cachedir()
|
||||
{
|
||||
return cachedir?cachedir:(char*)default_cachedir;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the service cache directory
|
||||
* @return The path to the cache directory
|
||||
*/
|
||||
char* get_datadir()
|
||||
{
|
||||
return maxscaledatadir?maxscaledatadir:(char*)default_datadir;
|
||||
}
|
||||
@ -170,6 +170,9 @@ hashtable_free(HASHTABLE *table)
|
||||
int i;
|
||||
HASHENTRIES *entry, *ptr;
|
||||
|
||||
if(table == NULL)
|
||||
return;
|
||||
|
||||
hashtable_write_lock(table);
|
||||
for (i = 0; i < table->hashsize; i++)
|
||||
{
|
||||
@ -235,7 +238,7 @@ hashtable_add(HASHTABLE *table, void *key, void *value)
|
||||
unsigned int hashkey;
|
||||
HASHENTRIES *entry;
|
||||
|
||||
if (key == NULL || value == NULL)
|
||||
if (table == NULL || key == NULL || value == NULL)
|
||||
return 0;
|
||||
|
||||
if (table->hashsize <= 0) {
|
||||
@ -308,9 +311,13 @@ hashtable_add(HASHTABLE *table, void *key, void *value)
|
||||
int
|
||||
hashtable_delete(HASHTABLE *table, void *key)
|
||||
{
|
||||
unsigned int hashkey = table->hashfn(key) % table->hashsize;
|
||||
unsigned int hashkey;
|
||||
HASHENTRIES *entry, *ptr;
|
||||
|
||||
if(table == NULL || key == NULL)
|
||||
return 0;
|
||||
|
||||
hashkey = table->hashfn(key) % table->hashsize;
|
||||
hashtable_write_lock(table);
|
||||
entry = table->entries[hashkey % table->hashsize];
|
||||
while (entry && entry->key && table->cmpfn(key, entry->key) != 0)
|
||||
@ -369,9 +376,13 @@ HASHENTRIES *entry, *ptr;
|
||||
void *
|
||||
hashtable_fetch(HASHTABLE *table, void *key)
|
||||
{
|
||||
unsigned int hashkey = table->hashfn(key) % table->hashsize;
|
||||
unsigned int hashkey;
|
||||
HASHENTRIES *entry;
|
||||
|
||||
if(table == NULL || key == NULL)
|
||||
return NULL;
|
||||
|
||||
hashkey = table->hashfn(key) % table->hashsize;
|
||||
hashtable_read_lock(table);
|
||||
entry = table->entries[hashkey % table->hashsize];
|
||||
while (entry && entry->key && table->cmpfn(key, entry->key) != 0)
|
||||
@ -401,6 +412,9 @@ hashtable_stats(HASHTABLE *table)
|
||||
int total, longest, i, j;
|
||||
HASHENTRIES *entries;
|
||||
|
||||
if(table == NULL)
|
||||
return;
|
||||
|
||||
printf("Hashtable: %p, size %d\n", table, table->hashsize);
|
||||
total = 0;
|
||||
longest = 0;
|
||||
@ -606,6 +620,9 @@ hashtable_next(HASHITERATOR *iter)
|
||||
int i;
|
||||
HASHENTRIES *entries;
|
||||
|
||||
if(iter == NULL)
|
||||
return NULL;
|
||||
|
||||
iter->depth++;
|
||||
while (iter->chain < iter->table->hashsize)
|
||||
{
|
||||
|
||||
@ -48,6 +48,8 @@
|
||||
#include <curl/curl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <gw.h>
|
||||
#include <gwdirs.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
@ -104,21 +106,10 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
return realsize;
|
||||
}
|
||||
|
||||
char* get_maxscale_home(void)
|
||||
{
|
||||
char* home = getenv("MAXSCALE_HOME");
|
||||
if (home == NULL)
|
||||
{
|
||||
home = "/usr/local/mariadb-maxscale";
|
||||
}
|
||||
return home;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the dynamic library related to a gateway module. The routine
|
||||
* will look for library files in the current directory,
|
||||
* $MAXSCALE_HOME/modules and /usr/local/mariadb-maxscale/modules.
|
||||
* the configured folder and /usr/lib64/maxscale.
|
||||
*
|
||||
* @param module Name of the module to load
|
||||
* @param type Type of module, used purely for registration
|
||||
@ -142,22 +133,17 @@ MODULE_INFO *mod_info = NULL;
|
||||
*
|
||||
* Search of the shared object.
|
||||
*/
|
||||
snprintf(fname,MAXPATHLEN+1, "./lib%s.so", module);
|
||||
|
||||
|
||||
snprintf(fname, MAXPATHLEN+1,"%s/lib%s.so", get_libdir(), module);
|
||||
|
||||
if (access(fname, F_OK) == -1)
|
||||
{
|
||||
home = get_maxscale_home ();
|
||||
snprintf(fname, MAXPATHLEN+1,"%s/modules/lib%s.so", home, module);
|
||||
|
||||
if (access(fname, F_OK) == -1)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to find library for "
|
||||
"module: %s.",
|
||||
module)));
|
||||
return NULL;
|
||||
}
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to find library for "
|
||||
"module: %s. Module dir: %s",
|
||||
module, get_libdir())));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((dlhandle = dlopen(fname, RTLD_NOW|RTLD_LOCAL)) == NULL)
|
||||
|
||||
@ -31,19 +31,25 @@
|
||||
#include <secrets.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <gwdirs.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int arg_count = 6;
|
||||
int arg_count = 4;
|
||||
char *home;
|
||||
char *keyfile;
|
||||
char** arg_vector;
|
||||
int rval = 0;
|
||||
|
||||
if (argc != 2)
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
|
||||
return 1;
|
||||
keyfile = "/var/lib/maxscale/";
|
||||
fprintf(stderr, "Generating .secrets file in /var/lib/maxscale/ ...\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
keyfile = argv[1];
|
||||
}
|
||||
|
||||
arg_vector = malloc(sizeof(char*)*(arg_count + 1));
|
||||
|
||||
if(arg_vector == NULL)
|
||||
@ -54,26 +60,14 @@ int main(int argc, char **argv)
|
||||
|
||||
arg_vector[0] = "logmanager";
|
||||
arg_vector[1] = "-j";
|
||||
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
arg_vector[2] = (char*)malloc((strlen(home) + strlen("/log"))*sizeof(char));
|
||||
sprintf(arg_vector[2],"%s/log",home);
|
||||
}
|
||||
else
|
||||
{
|
||||
arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log");
|
||||
}
|
||||
arg_vector[3] = "-o";
|
||||
arg_vector[4] = "-l";
|
||||
arg_vector[5] = "LOGFILE_ERROR";
|
||||
arg_vector[6] = NULL;
|
||||
arg_vector[2] = "/var/log/maxscale/maxkeys";
|
||||
arg_vector[3] = "-o";
|
||||
arg_vector[4] = NULL;
|
||||
skygw_logmanager_init(arg_count,arg_vector);
|
||||
free(arg_vector[2]);
|
||||
free(arg_vector);
|
||||
|
||||
|
||||
if (secrets_writeKeys(argv[1]))
|
||||
if (secrets_writeKeys(keyfile))
|
||||
{
|
||||
fprintf(stderr, "Failed to encode the password\n");
|
||||
rval = 1;
|
||||
|
||||
@ -46,9 +46,9 @@ main(int argc, char **argv)
|
||||
char** arg_vector;
|
||||
int rval = 0;
|
||||
|
||||
if (argc != 2)
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <password>\n", argv[0]);
|
||||
fprintf(stderr, "Usage: %s <file> <password>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -60,18 +60,9 @@ main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
arg_vector[0] = "logmanager";
|
||||
arg_vector[1] = "-j";
|
||||
|
||||
if ((home = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
arg_vector[2] = (char*)malloc((strlen(home) + strlen("/log"))*sizeof(char));
|
||||
sprintf(arg_vector[2],"%s/log",home);
|
||||
}
|
||||
else
|
||||
{
|
||||
arg_vector[2] = strdup("/usr/local/mariadb-maxscale/log");
|
||||
}
|
||||
arg_vector[0] = strdup("logmanager");
|
||||
arg_vector[1] = strdup("-j");
|
||||
arg_vector[2] = strdup("/var/log/maxscale");
|
||||
|
||||
arg_vector[3] = "-o";
|
||||
arg_vector[4] = "-l";
|
||||
@ -88,9 +79,9 @@ main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
strncpy(pw,argv[1],80);
|
||||
strncpy(pw,argv[2],80);
|
||||
|
||||
if ((enc = encryptPassword(pw)) != NULL){
|
||||
if ((enc = encryptPassword(argv[1],pw)) != NULL){
|
||||
printf("%s\n", enc);
|
||||
}else{
|
||||
fprintf(stderr, "Failed to encode the password\n");
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static MEMLOG *memlogs = NULL;
|
||||
static SPINLOCK memlock = SPINLOCK_INIT;
|
||||
@ -133,7 +134,7 @@ memlog_log(MEMLOG *log, void *value)
|
||||
switch (log->type)
|
||||
{
|
||||
case ML_INT:
|
||||
((int *)(log->values))[log->offset] = (int)value;
|
||||
((int *)(log->values))[log->offset] = (intptr_t)value;
|
||||
break;
|
||||
case ML_LONG:
|
||||
((long *)(log->values))[log->offset] = (long)value;
|
||||
|
||||
@ -540,7 +540,7 @@ GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf)
|
||||
{
|
||||
GWBUF *buff = NULL, *packet;
|
||||
uint8_t *ptr;
|
||||
int len,blen,total = 0;
|
||||
uint32_t len,blen,total = 0;
|
||||
|
||||
if(p_readbuf == NULL || (*p_readbuf) == NULL ||
|
||||
gwbuf_length(*p_readbuf) < 3)
|
||||
@ -841,4 +841,4 @@ int modutil_count_statements(GWBUF* buffer)
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
* and monitor id
|
||||
* 30/10/14 Massimiliano Pinto Addition of disable_master_failback parameter
|
||||
* 07/11/14 Massimiliano Pinto Addition of monitor network timeouts
|
||||
* 08/05/15 Markus Makela Moved common monitor variables to MONITOR struct
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -65,8 +66,6 @@ MONITOR *mon;
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
mon->state = MONITOR_STATE_ALLOC;
|
||||
mon->name = strdup(name);
|
||||
|
||||
if ((mon->module = load_module(module, MODULE_MONITOR)) == NULL)
|
||||
{
|
||||
@ -74,13 +73,21 @@ MONITOR *mon;
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to load monitor module '%s'.",
|
||||
name)));
|
||||
free(mon->name);
|
||||
free(mon);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mon->state = MONITOR_STATE_ALLOC;
|
||||
mon->name = strdup(name);
|
||||
mon->handle = NULL;
|
||||
|
||||
mon->databases = NULL;
|
||||
mon->password = NULL;
|
||||
mon->user = NULL;
|
||||
mon->password = NULL;
|
||||
mon->read_timeout = DEFAULT_READ_TIMEOUT;
|
||||
mon->write_timeout = DEFAULT_WRITE_TIMEOUT;
|
||||
mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
mon->interval = MONITOR_INTERVAL;
|
||||
spinlock_init(&mon->lock);
|
||||
spinlock_acquire(&monLock);
|
||||
mon->next = allMonitors;
|
||||
allMonitors = mon;
|
||||
@ -100,7 +107,7 @@ monitor_free(MONITOR *mon)
|
||||
{
|
||||
MONITOR *ptr;
|
||||
|
||||
mon->module->stopMonitor(mon->handle);
|
||||
mon->module->stopMonitor(mon);
|
||||
mon->state = MONITOR_STATE_FREED;
|
||||
spinlock_acquire(&monLock);
|
||||
if (allMonitors == mon)
|
||||
@ -127,8 +134,10 @@ MONITOR *ptr;
|
||||
void
|
||||
monitorStart(MONITOR *monitor, void* params)
|
||||
{
|
||||
monitor->handle = (*monitor->module->startMonitor)(monitor->handle,params);
|
||||
spinlock_acquire(&monitor->lock);
|
||||
monitor->handle = (*monitor->module->startMonitor)(monitor,params);
|
||||
monitor->state = MONITOR_STATE_RUNNING;
|
||||
spinlock_release(&monitor->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +151,7 @@ monitorStop(MONITOR *monitor)
|
||||
if(monitor->state != MONITOR_STATE_STOPPED)
|
||||
{
|
||||
monitor->state = MONITOR_STATE_STOPPING;
|
||||
monitor->module->stopMonitor(monitor->handle);
|
||||
monitor->module->stopMonitor(monitor);
|
||||
monitor->state = MONITOR_STATE_STOPPED;
|
||||
}
|
||||
}
|
||||
@ -175,7 +184,32 @@ MONITOR *ptr;
|
||||
void
|
||||
monitorAddServer(MONITOR *mon, SERVER *server)
|
||||
{
|
||||
mon->module->registerServer(mon->handle, server);
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
db->mon_err_count = 0;
|
||||
db->log_version_err = true;
|
||||
/** Server status is uninitialized */
|
||||
db->mon_prev_status = -1;
|
||||
/* pending status is updated by get_replication_tree */
|
||||
db->pending_status = 0;
|
||||
|
||||
spinlock_acquire(&mon->lock);
|
||||
|
||||
if (mon->databases == NULL)
|
||||
mon->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = mon->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&mon->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +223,8 @@ monitorAddServer(MONITOR *mon, SERVER *server)
|
||||
void
|
||||
monitorAddUser(MONITOR *mon, char *user, char *passwd)
|
||||
{
|
||||
mon->module->defaultUser(mon->handle, user, passwd);
|
||||
mon->user = strdup(user);
|
||||
mon->password = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,7 +244,7 @@ MONITOR *ptr;
|
||||
dcb_printf(dcb, "Monitor: %p\n", ptr);
|
||||
dcb_printf(dcb, "\tName: %s\n", ptr->name);
|
||||
if (ptr->module->diagnostics)
|
||||
ptr->module->diagnostics(dcb, ptr->handle);
|
||||
ptr->module->diagnostics(dcb, ptr);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
spinlock_release(&monLock);
|
||||
@ -227,7 +262,7 @@ monitorShow(DCB *dcb, MONITOR *monitor)
|
||||
dcb_printf(dcb, "Monitor: %p\n", monitor);
|
||||
dcb_printf(dcb, "\tName: %s\n", monitor->name);
|
||||
if (monitor->module->diagnostics)
|
||||
monitor->module->diagnostics(dcb, monitor->handle);
|
||||
monitor->module->diagnostics(dcb, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -288,10 +323,7 @@ MONITOR *ptr;
|
||||
void
|
||||
monitorSetInterval (MONITOR *mon, unsigned long interval)
|
||||
{
|
||||
if (mon->module->setInterval != NULL) {
|
||||
mon->interval = interval;
|
||||
mon->module->setInterval(mon->handle, interval);
|
||||
}
|
||||
mon->interval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -303,9 +335,55 @@ monitorSetInterval (MONITOR *mon, unsigned long interval)
|
||||
*/
|
||||
void
|
||||
monitorSetNetworkTimeout(MONITOR *mon, int type, int value) {
|
||||
if (mon->module->setNetworkTimeout != NULL) {
|
||||
mon->module->setNetworkTimeout(mon->handle, type, value);
|
||||
|
||||
int max_timeout = (int)(mon->interval/1000);
|
||||
int new_timeout = max_timeout -1;
|
||||
|
||||
if (new_timeout <= 0)
|
||||
new_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
switch(type) {
|
||||
case MONITOR_CONNECT_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&mon->connect_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&mon->connect_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_READ_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&mon->read_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&mon->read_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_WRITE_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&mon->write_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&mon->write_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor setNetworkTimeout received an unsupported action type %i", type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -15,10 +15,12 @@
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
@ -71,6 +73,7 @@ int max_poll_sleep;
|
||||
* in the loop after the epoll_wait. This allows for better
|
||||
* thread utilisaiton and fairer scheduling of the event
|
||||
* processing.
|
||||
* 07/07/15 Martin Brampton Simplified add and remove DCB, improve error handling.
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -186,6 +189,11 @@ static struct {
|
||||
*/
|
||||
static void poll_loadav(void *);
|
||||
|
||||
/**
|
||||
* Function to analyse error return from epoll_ctl
|
||||
*/
|
||||
static int poll_resolve_error(DCB *, int, bool);
|
||||
|
||||
/**
|
||||
* Initialise the polling system we are using for the gateway.
|
||||
*
|
||||
@ -247,7 +255,7 @@ int
|
||||
poll_add_dcb(DCB *dcb)
|
||||
{
|
||||
int rc = -1;
|
||||
dcb_state_t old_state = DCB_STATE_UNDEFINED;
|
||||
dcb_state_t old_state = dcb->state;
|
||||
dcb_state_t new_state;
|
||||
struct epoll_event ev;
|
||||
|
||||
@ -263,58 +271,67 @@ poll_add_dcb(DCB *dcb)
|
||||
/*<
|
||||
* Choose new state according to the role of dcb.
|
||||
*/
|
||||
spinlock_acquire(&dcb->dcb_initlock);
|
||||
if (dcb->dcb_role == DCB_ROLE_REQUEST_HANDLER) {
|
||||
new_state = DCB_STATE_POLLING;
|
||||
} else {
|
||||
ss_dassert(dcb->dcb_role == DCB_ROLE_SERVICE_LISTENER);
|
||||
new_state = DCB_STATE_LISTENING;
|
||||
}
|
||||
/*<
|
||||
* If dcb is in unexpected state, state change fails indicating that dcb
|
||||
* is not polling anymore.
|
||||
/*
|
||||
* Check DCB current state seems sensible
|
||||
*/
|
||||
if (dcb_set_state(dcb, new_state, &old_state)) {
|
||||
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dcb->fd, &ev);
|
||||
|
||||
if (rc != 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Adding dcb %p in state %s "
|
||||
"to poll set failed. epoll_ctl failed due "
|
||||
"%d, %s.",
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state),
|
||||
eno,
|
||||
strerror(eno))));
|
||||
} else {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [poll_add_dcb] Added dcb %p in state %s to "
|
||||
"poll set.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
ss_info_dassert(rc == 0, "Unable to add poll"); /*< trap in debug */
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to set new state for dcb %p "
|
||||
"in state %s. Adding to poll set failed.",
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
if (DCB_STATE_DISCONNECTED == dcb->state
|
||||
|| DCB_STATE_ZOMBIE == dcb->state
|
||||
|| DCB_STATE_UNDEFINED == dcb->state)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [poll_add_dcb] Error : existing state of dcb %p "
|
||||
"is %s, but this should be impossible, crashing.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
raise(SIGABRT);
|
||||
}
|
||||
|
||||
if (DCB_STATE_POLLING == dcb->state
|
||||
|| DCB_STATE_LISTENING == dcb->state)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [poll_add_dcb] Error : existing state of dcb %p "
|
||||
"is %s, but this is probably an error, not crashing.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
dcb->state = new_state;
|
||||
spinlock_release(&dcb->dcb_initlock);
|
||||
/*
|
||||
* The only possible failure that will not cause a crash is
|
||||
* running out of system resources.
|
||||
*/
|
||||
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dcb->fd, &ev);
|
||||
if (rc)
|
||||
{
|
||||
rc = poll_resolve_error(dcb, errno, true);
|
||||
}
|
||||
if (0 == rc)
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [poll_add_dcb] Added dcb %p in state %s to poll set.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
else dcb->state = old_state;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a descriptor from the set of descriptors within the
|
||||
* polling environment.
|
||||
* The state change command may fail because concurrent threads may call
|
||||
* dcb_set_state simultaneously and the conflict is prevented in dcb_set_state.
|
||||
*
|
||||
* @param dcb The descriptor to remove
|
||||
* @return -1 on error or 0 on success
|
||||
@ -322,63 +339,127 @@ poll_add_dcb(DCB *dcb)
|
||||
int
|
||||
poll_remove_dcb(DCB *dcb)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int rc = -1;
|
||||
dcb_state_t old_state = DCB_STATE_UNDEFINED;
|
||||
dcb_state_t new_state = DCB_STATE_NOPOLLING;
|
||||
|
||||
struct epoll_event ev;
|
||||
CHK_DCB(dcb);
|
||||
|
||||
spinlock_acquire(&dcb->dcb_initlock);
|
||||
/*< It is possible that dcb has already been removed from the set */
|
||||
if (dcb->state != DCB_STATE_POLLING)
|
||||
{
|
||||
if (dcb->state == DCB_STATE_NOPOLLING ||
|
||||
dcb->state == DCB_STATE_ZOMBIE)
|
||||
{
|
||||
rc = 0;
|
||||
}
|
||||
goto return_rc;
|
||||
if (dcb->state == DCB_STATE_NOPOLLING ||
|
||||
dcb->state == DCB_STATE_ZOMBIE)
|
||||
{
|
||||
spinlock_release(&dcb->dcb_initlock);
|
||||
return 0;
|
||||
}
|
||||
if (DCB_STATE_POLLING != dcb->state
|
||||
&& DCB_STATE_LISTENING != dcb->state)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [poll_remove_dcb] Error : existing state of dcb %p "
|
||||
"is %s, but this is probably an error, not crashing.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
/*<
|
||||
* Set state to NOPOLLING and remove dcb from poll set.
|
||||
*/
|
||||
if (dcb_set_state(dcb, new_state, &old_state))
|
||||
{
|
||||
/**
|
||||
* Only positive fds can be removed from epoll set.
|
||||
*/
|
||||
if (dcb->fd > 0)
|
||||
{
|
||||
rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev);
|
||||
dcb->state = DCB_STATE_NOPOLLING;
|
||||
spinlock_release(&dcb->dcb_initlock);
|
||||
|
||||
if (rc != 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : epoll_ctl failed due %d, %s.",
|
||||
eno,
|
||||
strerror(eno))));
|
||||
}
|
||||
ss_dassert(rc == 0); /*< trap in debug */
|
||||
}
|
||||
}
|
||||
/*<
|
||||
* This call was redundant, but the end result is correct.
|
||||
*/
|
||||
else if (old_state == new_state)
|
||||
/**
|
||||
* Only positive fds can be removed from epoll set.
|
||||
* Cloned DCBs can have a state of DCB_STATE_POLLING but are not in
|
||||
* the epoll set and do not have a valid file descriptor. Hence the
|
||||
* only action for them is already done - the change of state to
|
||||
* DCB_STATE_NOPOLLING.
|
||||
*/
|
||||
spinlock_release(&dcb->dcb_initlock);
|
||||
if (dcb->fd > 0)
|
||||
{
|
||||
rc = 0;
|
||||
goto return_rc;
|
||||
rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev);
|
||||
/**
|
||||
* The poll_resolve_error function will always
|
||||
* return 0 or crash. So if it returns non-zero result,
|
||||
* things have gone wrong and we crash.
|
||||
*/
|
||||
if (rc) rc = poll_resolve_error(dcb, errno, false);
|
||||
if (rc) raise(SIGABRT);
|
||||
/*< Set bit for each maxscale thread */
|
||||
bitmask_copy(&dcb->memdata.bitmask, poll_bitmask());
|
||||
}
|
||||
|
||||
/*< Set bit for each maxscale thread */
|
||||
bitmask_copy(&dcb->memdata.bitmask, poll_bitmask());
|
||||
rc = 0;
|
||||
return_rc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check error returns from epoll_ctl. Most result in a crash since they
|
||||
* are "impossible". Adding when already present is assumed non-fatal.
|
||||
* Likewise, removing when not present is assumed non-fatal.
|
||||
* It is assumed that callers to poll routines can handle the failure
|
||||
* that results from hitting system limit, although an error is written
|
||||
* here to record the problem.
|
||||
*
|
||||
* @param errornum The errno set by epoll_ctl
|
||||
* @param adding True for adding to poll list, false for removing
|
||||
* @return -1 on error or 0 for possibly revised return code
|
||||
*/
|
||||
static int
|
||||
poll_resolve_error(DCB *dcb, int errornum, bool adding)
|
||||
{
|
||||
if (adding)
|
||||
{
|
||||
if (EEXIST == errornum)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [poll_resolve_error] Error : epoll_ctl could not add, "
|
||||
"already exists for DCB %p.",
|
||||
pthread_self(),
|
||||
dcb)));
|
||||
// Assume another thread added and no serious harm done
|
||||
return 0;
|
||||
}
|
||||
if (ENOSPC == errornum)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [poll_resolve_error] The limit imposed by "
|
||||
"/proc/sys/fs/epoll/max_user_watches was "
|
||||
"encountered while trying to register (EPOLL_CTL_ADD) a new "
|
||||
"file descriptor on an epoll instance for dcb %p.",
|
||||
pthread_self(),
|
||||
dcb)));
|
||||
/* Failure - assume handled by callers */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Must be removing */
|
||||
if (ENOENT == errornum)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [poll_resolve_error] Error : epoll_ctl could not remove, "
|
||||
"not found, for dcb %p.",
|
||||
pthread_self(),
|
||||
dcb)));
|
||||
// Assume another thread removed and no serious harm done
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Common checks for add or remove - crash MaxScale */
|
||||
if (EBADF == errornum) raise(SIGABRT);
|
||||
if (EINVAL == errornum) raise(SIGABRT);
|
||||
if (ENOMEM == errornum) raise(SIGABRT);
|
||||
if (EPERM == errornum) raise(SIGABRT);
|
||||
/* Undocumented error number */
|
||||
raise(SIGABRT);
|
||||
/* The following statement should never be reached, but avoids compiler warning */
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define BLOCKINGPOLL 0 /*< Set BLOCKING POLL to 1 if using a single thread and to make
|
||||
* debugging easier.
|
||||
*/
|
||||
@ -440,7 +521,6 @@ poll_waitevents(void *arg)
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
int i, nfds, timeout_bias = 1;
|
||||
intptr_t thread_id = (intptr_t)arg;
|
||||
DCB *zombies = NULL;
|
||||
int poll_spins = 0;
|
||||
|
||||
/** Add this thread to the bitmask of running polling threads */
|
||||
@ -611,7 +691,7 @@ int poll_spins = 0;
|
||||
|
||||
if (thread_data)
|
||||
thread_data[thread_id].state = THREAD_ZPROCESSING;
|
||||
zombies = dcb_process_zombies(thread_id);
|
||||
dcb_process_zombies(thread_id);
|
||||
if (thread_data)
|
||||
thread_data[thread_id].state = THREAD_IDLE;
|
||||
|
||||
@ -796,16 +876,6 @@ unsigned long qtime;
|
||||
eno = gw_getsockerrno(dcb->fd);
|
||||
|
||||
if (eno == 0) {
|
||||
#if MUTEX_BLOCK
|
||||
simple_mutex_lock(&dcb->dcb_write_lock, true);
|
||||
ss_info_dassert(!dcb->dcb_write_active,
|
||||
"Write already active");
|
||||
dcb->dcb_write_active = TRUE;
|
||||
atomic_add(&pollStats.n_write, 1);
|
||||
dcb->func.write_ready(dcb);
|
||||
dcb->dcb_write_active = FALSE;
|
||||
simple_mutex_unlock(&dcb->dcb_write_lock);
|
||||
#else
|
||||
atomic_add(&pollStats.n_write, 1);
|
||||
/** Read session id to thread's local storage */
|
||||
LOGIF_MAYBE(LT, (dcb_get_ses_log_info(
|
||||
@ -813,7 +883,6 @@ unsigned long qtime;
|
||||
&tls_log_info.li_sesid,
|
||||
&tls_log_info.li_enabled_logs)));
|
||||
dcb->func.write_ready(dcb);
|
||||
#endif
|
||||
} else {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
@ -829,12 +898,6 @@ unsigned long qtime;
|
||||
}
|
||||
if (ev & EPOLLIN)
|
||||
{
|
||||
#if MUTEX_BLOCK
|
||||
simple_mutex_lock(&dcb->dcb_read_lock, true);
|
||||
ss_info_dassert(!dcb->dcb_read_active, "Read already active");
|
||||
dcb->dcb_read_active = TRUE;
|
||||
#endif
|
||||
|
||||
if (dcb->state == DCB_STATE_LISTENING)
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
@ -868,11 +931,6 @@ unsigned long qtime;
|
||||
&tls_log_info.li_enabled_logs)));
|
||||
dcb->func.read(dcb);
|
||||
}
|
||||
#if MUTEX_BLOCK
|
||||
dcb->dcb_read_active = FALSE;
|
||||
simple_mutex_unlock(
|
||||
&dcb->dcb_read_lock);
|
||||
#endif
|
||||
}
|
||||
if (ev & EPOLLERR)
|
||||
{
|
||||
@ -1604,7 +1662,7 @@ RESULT_ROW *row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a resultset that has the current set of services in it
|
||||
* Return a result set that has the current set of services in it
|
||||
*
|
||||
* @return A Result set
|
||||
*/
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include <log_manager.h>
|
||||
#include <ctype.h>
|
||||
#include <mysql_client_server_protocol.h>
|
||||
#include <gwdirs.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
@ -52,31 +53,26 @@ int i;
|
||||
}
|
||||
|
||||
/**
|
||||
* secrets_readKeys
|
||||
*
|
||||
* This routine reads data from a binary file and extracts the AES encryption key
|
||||
* and the AES Init Vector
|
||||
*
|
||||
* This routine reads data from a binary file named ".secrets" and extracts the AES encryption key
|
||||
* and the AES Init Vector.
|
||||
* If the path parameter is not null the custom path is interpreted as a folder
|
||||
* containing the .secrets file. Otherwise the default location is used.
|
||||
* @return The keys structure or NULL on error
|
||||
*/
|
||||
static MAXKEYS *
|
||||
secrets_readKeys()
|
||||
secrets_readKeys(char* path)
|
||||
{
|
||||
char secret_file[255];
|
||||
char secret_file[PATH_MAX+1];
|
||||
char *home;
|
||||
MAXKEYS *keys;
|
||||
struct stat secret_stats;
|
||||
int fd;
|
||||
int len;
|
||||
static int reported = 0;
|
||||
|
||||
home = getenv("MAXSCALE_HOME");
|
||||
|
||||
if (home == NULL) {
|
||||
home = "/usr/local/mariadb-maxscale";
|
||||
}
|
||||
snprintf(secret_file, 255, "%s/etc/.secrets", home);
|
||||
|
||||
if(path != NULL)
|
||||
snprintf(secret_file, PATH_MAX, "%s/.secrets", path);
|
||||
else
|
||||
snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir());
|
||||
/* Try to access secrets file */
|
||||
if (access(secret_file, R_OK) == -1)
|
||||
{
|
||||
@ -225,11 +221,20 @@ static int reported = 0;
|
||||
* @param secret_file The file with secret keys
|
||||
* @return 0 on success and 1 on failure
|
||||
*/
|
||||
int secrets_writeKeys(char *secret_file)
|
||||
int secrets_writeKeys(char *path)
|
||||
{
|
||||
int fd,randfd;
|
||||
unsigned int randval;
|
||||
MAXKEYS key;
|
||||
char secret_file[PATH_MAX + 10];
|
||||
|
||||
if(strlen(path) > PATH_MAX)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Pathname too long.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
sprintf(secret_file,"%s/.secrets",path);
|
||||
|
||||
/* Open for writing | Create | Truncate the file for writing */
|
||||
if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0)
|
||||
@ -332,7 +337,7 @@ char *ptr;
|
||||
unsigned char encrypted[80];
|
||||
int enlen;
|
||||
|
||||
keys = secrets_readKeys();
|
||||
keys = secrets_readKeys(NULL);
|
||||
if (!keys)
|
||||
return strdup(crypt);
|
||||
/*
|
||||
@ -369,12 +374,12 @@ int enlen;
|
||||
* Encrypt a password that can be stored in the MaxScale configuration file.
|
||||
*
|
||||
* Note the return is always a malloc'd string that the caller must free
|
||||
*
|
||||
* @param path Path the the .secrets file
|
||||
* @param password The password to encrypt
|
||||
* @return The encrypted password
|
||||
*/
|
||||
char *
|
||||
encryptPassword(char *password)
|
||||
encryptPassword(char* path, char *password)
|
||||
{
|
||||
MAXKEYS *keys;
|
||||
AES_KEY aeskey;
|
||||
@ -383,7 +388,7 @@ char *hex_output;
|
||||
unsigned char padded_passwd[80];
|
||||
unsigned char encrypted[80];
|
||||
|
||||
if ((keys = secrets_readKeys()) == NULL)
|
||||
if ((keys = secrets_readKeys(path)) == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(padded_passwd, 0, 80);
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
* 30/08/14 Massimiliano Pinto Addition of new service status description
|
||||
* 30/10/14 Massimiliano Pinto Addition of SERVER_MASTER_STICKINESS description
|
||||
* 01/06/15 Massimiliano Pinto Addition of server_update_address/port
|
||||
* 19/06/15 Martin Brampton Extra code for persistent connections
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -43,6 +44,7 @@
|
||||
#include <server.h>
|
||||
#include <spinlock.h>
|
||||
#include <dcb.h>
|
||||
#include <poll.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
@ -54,6 +56,8 @@ extern __thread log_info_t tls_log_info;
|
||||
static SPINLOCK server_spin = SPINLOCK_INIT;
|
||||
static SERVER *allServers = NULL;
|
||||
|
||||
static void spin_reporter(void *, char *, int);
|
||||
|
||||
/**
|
||||
* Allocate a new server withn the gateway
|
||||
*
|
||||
@ -71,6 +75,10 @@ SERVER *server;
|
||||
|
||||
if ((server = (SERVER *)calloc(1, sizeof(SERVER))) == NULL)
|
||||
return NULL;
|
||||
#if defined(SS_DEBUG)
|
||||
server->server_chk_top = CHK_NUM_SERVER;
|
||||
server->server_chk_tail = CHK_NUM_SERVER;
|
||||
#endif
|
||||
server->name = strdup(servname);
|
||||
server->protocol = strdup(protocol);
|
||||
server->port = port;
|
||||
@ -79,6 +87,9 @@ SERVER *server;
|
||||
server->rlag = -2;
|
||||
server->master_id = -1;
|
||||
server->depth = -1;
|
||||
server->persistent = NULL;
|
||||
server->persistmax = 0;
|
||||
spinlock_init(&server->persistlock);
|
||||
|
||||
spinlock_acquire(&server_spin);
|
||||
server->next = allServers;
|
||||
@ -96,39 +107,104 @@ SERVER *server;
|
||||
* @return Returns true if the server was freed
|
||||
*/
|
||||
int
|
||||
server_free(SERVER *server)
|
||||
server_free(SERVER *tofreeserver)
|
||||
{
|
||||
SERVER *ptr;
|
||||
SERVER *server;
|
||||
|
||||
/* First of all remove from the linked list */
|
||||
spinlock_acquire(&server_spin);
|
||||
if (allServers == server)
|
||||
if (allServers == tofreeserver)
|
||||
{
|
||||
allServers = server->next;
|
||||
allServers = tofreeserver->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = allServers;
|
||||
while (ptr && ptr->next != server)
|
||||
server = allServers;
|
||||
while (server && server->next != tofreeserver)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
server = server->next;
|
||||
}
|
||||
if (ptr)
|
||||
ptr->next = server->next;
|
||||
if (server)
|
||||
server->next = tofreeserver->next;
|
||||
}
|
||||
spinlock_release(&server_spin);
|
||||
|
||||
/* Clean up session and free the memory */
|
||||
free(server->name);
|
||||
free(server->protocol);
|
||||
if (server->unique_name)
|
||||
free(server->unique_name);
|
||||
if (server->server_string)
|
||||
free(server->server_string);
|
||||
free(server);
|
||||
free(tofreeserver->name);
|
||||
free(tofreeserver->protocol);
|
||||
if (tofreeserver->unique_name)
|
||||
free(tofreeserver->unique_name);
|
||||
if (tofreeserver->server_string)
|
||||
free(tofreeserver->server_string);
|
||||
if (tofreeserver->persistent)
|
||||
dcb_persistent_clean_count(tofreeserver->persistent, true);
|
||||
free(tofreeserver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DCB from the persistent connection pool, if possible
|
||||
*
|
||||
* @param server The server to set the name on
|
||||
* @param user The name of the user needing the connection
|
||||
* @param protocol The name of the protocol needed for the connection
|
||||
*/
|
||||
DCB *
|
||||
server_get_persistent(SERVER *server, char *user, const char *protocol)
|
||||
{
|
||||
DCB *dcb, *previous = NULL;
|
||||
|
||||
if (server->persistent && dcb_persistent_clean_count(server->persistent, false) && server->persistent)
|
||||
{
|
||||
spinlock_acquire(&server->persistlock);
|
||||
dcb = server->persistent;
|
||||
while (dcb) {
|
||||
if (dcb->user
|
||||
&& dcb->protoname
|
||||
&& !dcb-> dcb_errhandle_called
|
||||
&& !(dcb->flags & DCBF_HUNG)
|
||||
&& 0 == strcmp(dcb->user, user)
|
||||
&& 0 == strcmp(dcb->protoname, protocol))
|
||||
{
|
||||
if (NULL == previous)
|
||||
{
|
||||
server->persistent = dcb->nextpersistent;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous->nextpersistent = dcb->nextpersistent;
|
||||
}
|
||||
free(dcb->user);
|
||||
dcb->user = NULL;
|
||||
spinlock_release(&server->persistlock);
|
||||
atomic_add(&server->stats.n_persistent, -1);
|
||||
atomic_add(&server->stats.n_current, 1);
|
||||
return dcb;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [server_get_persistent] Rejected dcb "
|
||||
"%p from pool, user %s looking for %s, protocol %s "
|
||||
"looking for %s, hung flag %s, error handle called %s.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
dcb->user ? dcb->user : "NULL",
|
||||
user,
|
||||
dcb->protoname ? dcb->protoname : "NULL",
|
||||
protocol,
|
||||
(dcb->flags & DCBF_HUNG) ? "true" : "false",
|
||||
dcb-> dcb_errhandle_called ? "true" : "false")));
|
||||
}
|
||||
previous = dcb;
|
||||
dcb = dcb->nextpersistent;
|
||||
}
|
||||
spinlock_release(&server->persistlock);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a unique name for the server
|
||||
*
|
||||
@ -204,6 +280,8 @@ printServer(SERVER *server)
|
||||
printf("\tPort: %d\n", server->port);
|
||||
printf("\tTotal connections: %d\n", server->stats.n_connections);
|
||||
printf("\tCurrent connections: %d\n", server->stats.n_current);
|
||||
printf("\tPersistent connections: %d\n", server->stats.n_persistent);
|
||||
printf("\tPersistent actual max: %d\n", server->persistmax);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,14 +293,14 @@ printServer(SERVER *server)
|
||||
void
|
||||
printAllServers()
|
||||
{
|
||||
SERVER *ptr;
|
||||
SERVER *server;
|
||||
|
||||
spinlock_acquire(&server_spin);
|
||||
ptr = allServers;
|
||||
while (ptr)
|
||||
server = allServers;
|
||||
while (server)
|
||||
{
|
||||
printServer(ptr);
|
||||
ptr = ptr->next;
|
||||
printServer(server);
|
||||
server = server->next;
|
||||
}
|
||||
spinlock_release(&server_spin);
|
||||
}
|
||||
@ -236,60 +314,73 @@ SERVER *ptr;
|
||||
void
|
||||
dprintAllServers(DCB *dcb)
|
||||
{
|
||||
SERVER *ptr;
|
||||
SERVER *server;
|
||||
char *stat;
|
||||
|
||||
spinlock_acquire(&server_spin);
|
||||
ptr = allServers;
|
||||
while (ptr)
|
||||
server = allServers;
|
||||
while (server)
|
||||
{
|
||||
dcb_printf(dcb, "Server %p (%s)\n", ptr, ptr->unique_name);
|
||||
dcb_printf(dcb, "Server %p (%s)\n", server, server->unique_name);
|
||||
dcb_printf(dcb, "\tServer: %s\n",
|
||||
ptr->name);
|
||||
stat = server_status(ptr);
|
||||
server->name);
|
||||
stat = server_status(server);
|
||||
dcb_printf(dcb, "\tStatus: %s\n",
|
||||
stat);
|
||||
free(stat);
|
||||
dcb_printf(dcb, "\tProtocol: %s\n",
|
||||
ptr->protocol);
|
||||
server->protocol);
|
||||
dcb_printf(dcb, "\tPort: %d\n",
|
||||
ptr->port);
|
||||
if (ptr->server_string)
|
||||
server->port);
|
||||
if (server->server_string)
|
||||
dcb_printf(dcb, "\tServer Version:\t\t\t%s\n",
|
||||
ptr->server_string);
|
||||
server->server_string);
|
||||
dcb_printf(dcb, "\tNode Id: %d\n",
|
||||
ptr->node_id);
|
||||
server->node_id);
|
||||
dcb_printf(dcb, "\tMaster Id: %d\n",
|
||||
ptr->master_id);
|
||||
if (ptr->slaves) {
|
||||
server->master_id);
|
||||
if (server->slaves) {
|
||||
int i;
|
||||
dcb_printf(dcb, "\tSlave Ids: ");
|
||||
for (i = 0; ptr->slaves[i]; i++)
|
||||
for (i = 0; server->slaves[i]; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
dcb_printf(dcb, "%li", ptr->slaves[i]);
|
||||
dcb_printf(dcb, "%li", server->slaves[i]);
|
||||
else
|
||||
dcb_printf(dcb, ", %li ", ptr->slaves[i]);
|
||||
dcb_printf(dcb, ", %li ", server->slaves[i]);
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
dcb_printf(dcb, "\tRepl Depth: %d\n",
|
||||
ptr->depth);
|
||||
if (SERVER_IS_SLAVE(ptr) || SERVER_IS_RELAY_SERVER(ptr)) {
|
||||
if (ptr->rlag >= 0) {
|
||||
dcb_printf(dcb, "\tSlave delay:\t\t%d\n", ptr->rlag);
|
||||
server->depth);
|
||||
if (SERVER_IS_SLAVE(server) || SERVER_IS_RELAY_SERVER(server)) {
|
||||
if (server->rlag >= 0) {
|
||||
dcb_printf(dcb, "\tSlave delay:\t\t%d\n", server->rlag);
|
||||
}
|
||||
}
|
||||
if (ptr->node_ts > 0) {
|
||||
dcb_printf(dcb, "\tLast Repl Heartbeat:\t%lu\n", ptr->node_ts);
|
||||
if (server->node_ts > 0) {
|
||||
dcb_printf(dcb, "\tLast Repl Heartbeat:\t%lu\n", server->node_ts);
|
||||
}
|
||||
dcb_printf(dcb, "\tNumber of connections: %d\n",
|
||||
ptr->stats.n_connections);
|
||||
server->stats.n_connections);
|
||||
dcb_printf(dcb, "\tCurrent no. of conns: %d\n",
|
||||
ptr->stats.n_current);
|
||||
server->stats.n_current);
|
||||
dcb_printf(dcb, "\tCurrent no. of operations: %d\n",
|
||||
ptr->stats.n_current_ops);
|
||||
ptr = ptr->next;
|
||||
server->stats.n_current_ops);
|
||||
if (server->persistpoolmax)
|
||||
{
|
||||
dcb_printf(dcb, "\tPersistent pool size: %d\n",
|
||||
server->stats.n_persistent);
|
||||
dcb_printf(dcb, "\tPersistent measured pool size: %d\n",
|
||||
dcb_persistent_clean_count(server->persistent, false));
|
||||
dcb_printf(dcb, "\tPersistent max size achieved: %d\n",
|
||||
server->persistmax);
|
||||
dcb_printf(dcb, "\tPersistent pool size limit: %d\n",
|
||||
server->persistpoolmax);
|
||||
dcb_printf(dcb, "\tPersistent max time (secs): %d\n",
|
||||
server->persistmaxtime);
|
||||
}
|
||||
server = server->next;
|
||||
}
|
||||
spinlock_release(&server_spin);
|
||||
}
|
||||
@ -303,74 +394,74 @@ char *stat;
|
||||
void
|
||||
dprintAllServersJson(DCB *dcb)
|
||||
{
|
||||
SERVER *ptr;
|
||||
SERVER *server;
|
||||
char *stat;
|
||||
int len = 0;
|
||||
int el = 1;
|
||||
|
||||
spinlock_acquire(&server_spin);
|
||||
ptr = allServers;
|
||||
while (ptr)
|
||||
server = allServers;
|
||||
while (server)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
server = server->next;
|
||||
len++;
|
||||
}
|
||||
ptr = allServers;
|
||||
server = allServers;
|
||||
dcb_printf(dcb, "[\n");
|
||||
while (ptr)
|
||||
while (server)
|
||||
{
|
||||
dcb_printf(dcb, " {\n \"server\": \"%s\",\n",
|
||||
ptr->name);
|
||||
stat = server_status(ptr);
|
||||
server->name);
|
||||
stat = server_status(server);
|
||||
dcb_printf(dcb, " \"status\": \"%s\",\n",
|
||||
stat);
|
||||
free(stat);
|
||||
dcb_printf(dcb, " \"protocol\": \"%s\",\n",
|
||||
ptr->protocol);
|
||||
server->protocol);
|
||||
dcb_printf(dcb, " \"port\": \"%d\",\n",
|
||||
ptr->port);
|
||||
if (ptr->server_string)
|
||||
server->port);
|
||||
if (server->server_string)
|
||||
dcb_printf(dcb, " \"version\": \"%s\",\n",
|
||||
ptr->server_string);
|
||||
server->server_string);
|
||||
dcb_printf(dcb, " \"nodeId\": \"%d\",\n",
|
||||
ptr->node_id);
|
||||
server->node_id);
|
||||
dcb_printf(dcb, " \"masterId\": \"%d\",\n",
|
||||
ptr->master_id);
|
||||
if (ptr->slaves) {
|
||||
server->master_id);
|
||||
if (server->slaves) {
|
||||
int i;
|
||||
dcb_printf(dcb, " \"slaveIds\": [ ");
|
||||
for (i = 0; ptr->slaves[i]; i++)
|
||||
for (i = 0; server->slaves[i]; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
dcb_printf(dcb, "%li", ptr->slaves[i]);
|
||||
dcb_printf(dcb, "%li", server->slaves[i]);
|
||||
else
|
||||
dcb_printf(dcb, ", %li ", ptr->slaves[i]);
|
||||
dcb_printf(dcb, ", %li ", server->slaves[i]);
|
||||
}
|
||||
dcb_printf(dcb, "],\n");
|
||||
}
|
||||
dcb_printf(dcb, " \"replDepth\": \"%d\",\n",
|
||||
ptr->depth);
|
||||
if (SERVER_IS_SLAVE(ptr) || SERVER_IS_RELAY_SERVER(ptr)) {
|
||||
if (ptr->rlag >= 0) {
|
||||
dcb_printf(dcb, " \"slaveDelay\": \"%d\",\n", ptr->rlag);
|
||||
server->depth);
|
||||
if (SERVER_IS_SLAVE(server) || SERVER_IS_RELAY_SERVER(server)) {
|
||||
if (server->rlag >= 0) {
|
||||
dcb_printf(dcb, " \"slaveDelay\": \"%d\",\n", server->rlag);
|
||||
}
|
||||
}
|
||||
if (ptr->node_ts > 0) {
|
||||
dcb_printf(dcb, " \"lastReplHeartbeat\": \"%lu\",\n", ptr->node_ts);
|
||||
if (server->node_ts > 0) {
|
||||
dcb_printf(dcb, " \"lastReplHeartbeat\": \"%lu\",\n", server->node_ts);
|
||||
}
|
||||
dcb_printf(dcb, " \"totalConnections\": \"%d\",\n",
|
||||
ptr->stats.n_connections);
|
||||
server->stats.n_connections);
|
||||
dcb_printf(dcb, " \"currentConnections\": \"%d\",\n",
|
||||
ptr->stats.n_current);
|
||||
server->stats.n_current);
|
||||
dcb_printf(dcb, " \"currentOps\": \"%d\"\n",
|
||||
ptr->stats.n_current_ops);
|
||||
server->stats.n_current_ops);
|
||||
if (el < len) {
|
||||
dcb_printf(dcb, " },\n");
|
||||
}
|
||||
else {
|
||||
dcb_printf(dcb, " }\n");
|
||||
}
|
||||
ptr = ptr->next;
|
||||
server = server->next;
|
||||
el++;
|
||||
}
|
||||
dcb_printf(dcb, "]\n");
|
||||
@ -440,6 +531,57 @@ SERVER_PARAM *param;
|
||||
dcb_printf(dcb, "\tCurrent no. of conns: %d\n",
|
||||
server->stats.n_current);
|
||||
dcb_printf(dcb, "\tCurrent no. of operations: %d\n", server->stats.n_current_ops);
|
||||
if (server->persistpoolmax)
|
||||
{
|
||||
dcb_printf(dcb, "\tPersistent pool size: %d\n",
|
||||
server->stats.n_persistent);
|
||||
dcb_printf(dcb, "\tPersistent measured pool size: %d\n",
|
||||
dcb_persistent_clean_count(server->persistent, false));
|
||||
dcb_printf(dcb, "\tPersistent actual size max: %d\n",
|
||||
server->persistmax);
|
||||
dcb_printf(dcb, "\tPersistent pool size limit: %d\n",
|
||||
server->persistpoolmax);
|
||||
dcb_printf(dcb, "\tPersistent max time (secs): %d\n",
|
||||
server->persistmaxtime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an entry from the spinlock statistics data
|
||||
*
|
||||
* @param dcb The DCB to print to
|
||||
* @param desc Description of the statistic
|
||||
* @param value The statistic value
|
||||
*/
|
||||
static void
|
||||
spin_reporter(void *dcb, char *desc, int value)
|
||||
{
|
||||
dcb_printf((DCB *)dcb, "\t\t%-40s %d\n", desc, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostic to print all DCBs in persistent pool for a server
|
||||
*
|
||||
* @param pdcb DCB to print results to
|
||||
* @param server SERVER for which DCBs are to be printed
|
||||
*/
|
||||
void
|
||||
dprintPersistentDCBs(DCB *pdcb, SERVER *server)
|
||||
{
|
||||
DCB *dcb;
|
||||
|
||||
spinlock_acquire(&server->persistlock);
|
||||
#if SPINLOCK_PROFILE
|
||||
dcb_printf(pdcb, "DCB List Spinlock Statistics:\n");
|
||||
spinlock_stats(&server->persistlock, spin_reporter, pdcb);
|
||||
#endif
|
||||
dcb = server->persistent;
|
||||
while (dcb)
|
||||
{
|
||||
dprintOneDCB(pdcb, dcb);
|
||||
dcb = dcb->nextpersistent;
|
||||
}
|
||||
spinlock_release(&server->persistlock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -449,12 +591,12 @@ SERVER_PARAM *param;
|
||||
void
|
||||
dListServers(DCB *dcb)
|
||||
{
|
||||
SERVER *ptr;
|
||||
SERVER *server;
|
||||
char *stat;
|
||||
|
||||
spinlock_acquire(&server_spin);
|
||||
ptr = allServers;
|
||||
if (ptr)
|
||||
server = allServers;
|
||||
if (server)
|
||||
{
|
||||
dcb_printf(dcb, "Servers.\n");
|
||||
dcb_printf(dcb, "-------------------+-----------------+-------+-------------+--------------------\n");
|
||||
@ -462,15 +604,15 @@ char *stat;
|
||||
"Server", "Address", "Status");
|
||||
dcb_printf(dcb, "-------------------+-----------------+-------+-------------+--------------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
while (server)
|
||||
{
|
||||
stat = server_status(ptr);
|
||||
stat = server_status(server);
|
||||
dcb_printf(dcb, "%-18s | %-15s | %5d | %11d | %s\n",
|
||||
ptr->unique_name, ptr->name,
|
||||
ptr->port,
|
||||
ptr->stats.n_current, stat);
|
||||
server->unique_name, server->name,
|
||||
server->port,
|
||||
server->stats.n_current, stat);
|
||||
free(stat);
|
||||
ptr = ptr->next;
|
||||
server = server->next;
|
||||
}
|
||||
if (allServers)
|
||||
dcb_printf(dcb, "-------------------+-----------------+-------+-------------+--------------------\n");
|
||||
@ -489,7 +631,7 @@ server_status(SERVER *server)
|
||||
{
|
||||
char *status = NULL;
|
||||
|
||||
if ((status = (char *)malloc(256)) == NULL)
|
||||
if (NULL == server || (status = (char *)malloc(256)) == NULL)
|
||||
return NULL;
|
||||
status[0] = 0;
|
||||
if (server->status & SERVER_MAINT)
|
||||
@ -641,7 +783,7 @@ SERVER_PARAM *param;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive a parameter value from a server
|
||||
* Retrieve a parameter value from a server
|
||||
*
|
||||
* @param server The server we are looking for a parameter of
|
||||
* @param name The name of the parameter we require
|
||||
@ -675,16 +817,16 @@ int *rowno = (int *)data;
|
||||
int i = 0;;
|
||||
char *stat, buf[20];
|
||||
RESULT_ROW *row;
|
||||
SERVER *ptr;
|
||||
SERVER *server;
|
||||
|
||||
spinlock_acquire(&server_spin);
|
||||
ptr = allServers;
|
||||
while (i < *rowno && ptr)
|
||||
server = allServers;
|
||||
while (i < *rowno && server)
|
||||
{
|
||||
i++;
|
||||
ptr = ptr->next;
|
||||
server = server->next;
|
||||
}
|
||||
if (ptr == NULL)
|
||||
if (server == NULL)
|
||||
{
|
||||
spinlock_release(&server_spin);
|
||||
free(data);
|
||||
@ -692,13 +834,13 @@ SERVER *ptr;
|
||||
}
|
||||
(*rowno)++;
|
||||
row = resultset_make_row(set);
|
||||
resultset_row_set(row, 0, ptr->unique_name);
|
||||
resultset_row_set(row, 1, ptr->name);
|
||||
sprintf(buf, "%d", ptr->port);
|
||||
resultset_row_set(row, 0, server->unique_name);
|
||||
resultset_row_set(row, 1, server->name);
|
||||
sprintf(buf, "%d", server->port);
|
||||
resultset_row_set(row, 2, buf);
|
||||
sprintf(buf, "%d", ptr->stats.n_current);
|
||||
sprintf(buf, "%d", server->stats.n_current);
|
||||
resultset_row_set(row, 3, buf);
|
||||
stat = server_status(ptr);
|
||||
stat = server_status(server);
|
||||
resultset_row_set(row, 4, stat);
|
||||
free(stat);
|
||||
spinlock_release(&server_spin);
|
||||
|
||||
@ -36,7 +36,8 @@
|
||||
* 06/02/15 Mark Riddoch Added caching of authentication data
|
||||
* 18/02/15 Mark Riddoch Added result set management
|
||||
* 03/03/15 Massimiliano Pinto Added config_enable_feedback_task() call in serviceStartAll
|
||||
*
|
||||
* 19/06/15 Martin Brampton More meaningful names for temp variables
|
||||
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
@ -61,12 +62,17 @@
|
||||
#include <sys/types.h>
|
||||
#include <housekeeper.h>
|
||||
#include <resultset.h>
|
||||
#include <gw.h>
|
||||
#include <gwdirs.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
extern __thread log_info_t tls_log_info;
|
||||
|
||||
static RSA *rsa_512 = NULL;
|
||||
static RSA *rsa_1024 = NULL;
|
||||
|
||||
/** To be used with configuration type checks */
|
||||
typedef struct typelib_st {
|
||||
int tl_nelems;
|
||||
@ -112,7 +118,7 @@ SERVICE *service;
|
||||
return NULL;
|
||||
if ((service->router = load_module(router, MODULE_ROUTER)) == NULL)
|
||||
{
|
||||
char* home = get_maxscale_home();
|
||||
char* home = get_libdir();
|
||||
char* ldpath = getenv("LD_LIBRARY_PATH");
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -120,12 +126,13 @@ SERVICE *service;
|
||||
"Error : Unable to load %s module \"%s\".\n\t\t\t"
|
||||
" Ensure that lib%s.so exists in one of the "
|
||||
"following directories :\n\t\t\t "
|
||||
"- %s/modules\n\t\t\t - %s",
|
||||
"- %s\n%s%s",
|
||||
MODULE_ROUTER,
|
||||
router,
|
||||
router,
|
||||
home,
|
||||
ldpath)));
|
||||
ldpath?"\t\t\t - ":"",
|
||||
ldpath?ldpath:"")));
|
||||
free(service);
|
||||
return NULL;
|
||||
}
|
||||
@ -133,7 +140,14 @@ SERVICE *service;
|
||||
service->routerModule = strdup(router);
|
||||
service->users_from_all = false;
|
||||
service->resources = NULL;
|
||||
|
||||
service->ssl_mode = SSL_DISABLED;
|
||||
service->ssl_init_done = false;
|
||||
service->ssl_ca_cert = NULL;
|
||||
service->ssl_cert = NULL;
|
||||
service->ssl_key = NULL;
|
||||
service->ssl_cert_verify_depth = DEFAULT_SSL_CERT_VERIFY_DEPTH;
|
||||
/** Support the highest possible SSL/TLS methods available as the default */
|
||||
service->ssl_method_type = SERVICE_SSL_TLS_MAX;
|
||||
if (service->name == NULL || service->routerModule == NULL)
|
||||
{
|
||||
if (service->name)
|
||||
@ -163,19 +177,19 @@ SERVICE *service;
|
||||
int
|
||||
service_isvalid(SERVICE *service)
|
||||
{
|
||||
SERVICE *ptr;
|
||||
SERVICE *checkservice;
|
||||
int rval = 0;
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
ptr = allServices;
|
||||
while (ptr)
|
||||
checkservice = allServices;
|
||||
while (checkservice)
|
||||
{
|
||||
if (ptr == service)
|
||||
if (checkservice == service)
|
||||
{
|
||||
rval = 1;
|
||||
break;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
checkservice = checkservice->next;
|
||||
}
|
||||
spinlock_release(&service_spin);
|
||||
return rval;
|
||||
@ -229,11 +243,7 @@ GWPROTOCOL *funcs;
|
||||
{
|
||||
/* Try loading authentication data from file cache */
|
||||
char *ptr, path[4097];
|
||||
strcpy(path, "/usr/local/mariadb-maxscale");
|
||||
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
strncpy(path, ptr, 4096);
|
||||
}
|
||||
strcpy(path, get_cachedir());
|
||||
strncat(path, "/", 4096);
|
||||
strncat(path, service->name, 4096);
|
||||
strncat(path, "/.cache/dbusers", 4096);
|
||||
@ -249,7 +259,7 @@ GWPROTOCOL *funcs;
|
||||
{
|
||||
hashtable_free(service->users->data);
|
||||
free(service->users);
|
||||
dcb_free(port->listener);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
@ -257,15 +267,11 @@ GWPROTOCOL *funcs;
|
||||
else
|
||||
{
|
||||
/* Save authentication data to file cache */
|
||||
char *ptr, path[4097];
|
||||
char *ptr, path[PATH_MAX + 1];
|
||||
int mkdir_rval = 0;
|
||||
strcpy(path, "/usr/local/mariadb-maxscale");
|
||||
if ((ptr = getenv("MAXSCALE_HOME")) != NULL)
|
||||
{
|
||||
strncpy(path, ptr, 4096);
|
||||
}
|
||||
strncpy(path, get_cachedir(),PATH_MAX);
|
||||
strncat(path, "/", 4096);
|
||||
strncat(path, service->name, 4096);
|
||||
strncat(path, service->name, PATH_MAX);
|
||||
if (access(path, R_OK) == -1)
|
||||
{
|
||||
mkdir_rval = mkdir(path, 0777);
|
||||
@ -273,14 +279,17 @@ GWPROTOCOL *funcs;
|
||||
|
||||
if(mkdir_rval)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s",
|
||||
path,
|
||||
errno,
|
||||
strerror(errno));
|
||||
if(errno != EEXIST)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s",
|
||||
path,
|
||||
errno,
|
||||
strerror(errno));
|
||||
}
|
||||
mkdir_rval = 0;
|
||||
}
|
||||
|
||||
strncat(path, "/.cache", 4096);
|
||||
strncat(path, "/.cache", PATH_MAX);
|
||||
if (access(path, R_OK) == -1)
|
||||
{
|
||||
mkdir_rval = mkdir(path, 0777);
|
||||
@ -288,13 +297,16 @@ GWPROTOCOL *funcs;
|
||||
|
||||
if(mkdir_rval)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s",
|
||||
path,
|
||||
errno,
|
||||
strerror(errno));
|
||||
if(errno != EEXIST)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s",
|
||||
path,
|
||||
errno,
|
||||
strerror(errno));
|
||||
}
|
||||
mkdir_rval = 0;
|
||||
}
|
||||
strncat(path, "/dbusers", 4096);
|
||||
strncat(path, "/dbusers", PATH_MAX);
|
||||
dbusers_save(service->users, path);
|
||||
}
|
||||
if (loaded == 0)
|
||||
@ -318,7 +330,7 @@ GWPROTOCOL *funcs;
|
||||
"Loaded %d MySQL Users for service [%s].",
|
||||
loaded, service->name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (service->users == NULL) {
|
||||
@ -330,12 +342,8 @@ GWPROTOCOL *funcs;
|
||||
if ((funcs=(GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL))
|
||||
== NULL)
|
||||
{
|
||||
if (service->users->data)
|
||||
{
|
||||
hashtable_free(service->users->data);
|
||||
}
|
||||
free(service->users);
|
||||
dcb_free(port->listener);
|
||||
users_free(service->users);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
@ -369,11 +377,7 @@ GWPROTOCOL *funcs;
|
||||
"Error : Failed to create session to service %s.",
|
||||
service->name)));
|
||||
|
||||
if (service->users->data)
|
||||
{
|
||||
hashtable_free(service->users->data);
|
||||
}
|
||||
free(service->users);
|
||||
users_free(service->users);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
@ -387,11 +391,7 @@ GWPROTOCOL *funcs;
|
||||
port->port,
|
||||
port->protocol,
|
||||
service->name)));
|
||||
if (service->users->data)
|
||||
{
|
||||
hashtable_free(service->users->data);
|
||||
}
|
||||
free(service->users);
|
||||
users_free(service->users);
|
||||
dcb_close(port->listener);
|
||||
port->listener = NULL;
|
||||
}
|
||||
@ -417,6 +417,17 @@ serviceStart(SERVICE *service)
|
||||
SERV_PROTOCOL *port;
|
||||
int listeners = 0;
|
||||
|
||||
if(service->ssl_mode != SSL_DISABLED)
|
||||
{
|
||||
if(serviceInitSSL(service) != 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"%s: SSL initialization failed. Service not started.",
|
||||
service->name)));
|
||||
service->state = SERVICE_STATE_FAILED;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ((service->router_instance = service->router->createInstance(service,
|
||||
service->routerOptions)) == NULL)
|
||||
{
|
||||
@ -518,11 +529,16 @@ int listeners = 0;
|
||||
port = service->ports;
|
||||
while (port)
|
||||
{
|
||||
poll_remove_dcb(port->listener);
|
||||
port->listener->session->state = SESSION_STATE_LISTENER_STOPPED;
|
||||
listeners++;
|
||||
|
||||
port = port->next;
|
||||
if(port->listener &&
|
||||
port->listener->session->state == SESSION_STATE_LISTENER)
|
||||
{
|
||||
if(poll_remove_dcb(port->listener) == 0)
|
||||
{
|
||||
port->listener->session->state = SESSION_STATE_LISTENER_STOPPED;
|
||||
listeners++;
|
||||
}
|
||||
}
|
||||
port = port->next;
|
||||
}
|
||||
service->state = SERVICE_STATE_STOPPED;
|
||||
|
||||
@ -546,13 +562,18 @@ int listeners = 0;
|
||||
port = service->ports;
|
||||
while (port)
|
||||
{
|
||||
if (poll_add_dcb(port->listener) == 0) {
|
||||
port->listener->session->state = SESSION_STATE_LISTENER;
|
||||
listeners++;
|
||||
}
|
||||
port = port->next;
|
||||
if(port->listener &&
|
||||
port->listener->session->state == SESSION_STATE_LISTENER_STOPPED)
|
||||
{
|
||||
if(poll_add_dcb(port->listener) == 0)
|
||||
{
|
||||
port->listener->session->state = SESSION_STATE_LISTENER;
|
||||
listeners++;
|
||||
}
|
||||
}
|
||||
port = port->next;
|
||||
}
|
||||
|
||||
service->state = SERVICE_STATE_STARTED;
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@ -860,6 +881,99 @@ serviceOptimizeWildcard(SERVICE *service, int action)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locations of the server's SSL certificate, server's private key and the CA
|
||||
* certificate which both the client and the server should trust.
|
||||
* @param service Service to configure
|
||||
* @param cert SSL certificate
|
||||
* @param key SSL private key
|
||||
* @param ca_cert SSL CA certificate
|
||||
*/
|
||||
void
|
||||
serviceSetCertificates(SERVICE *service, char* cert,char* key, char* ca_cert)
|
||||
{
|
||||
if(service->ssl_cert)
|
||||
free(service->ssl_cert);
|
||||
service->ssl_cert = strdup(cert);
|
||||
|
||||
if(service->ssl_key)
|
||||
free(service->ssl_key);
|
||||
service->ssl_key = strdup(key);
|
||||
|
||||
if(service->ssl_ca_cert)
|
||||
free(service->ssl_ca_cert);
|
||||
service->ssl_ca_cert = strdup(ca_cert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum SSL/TLS version the service will support
|
||||
* @param service Service to configure
|
||||
* @param version SSL/TLS version string
|
||||
* @return 0 on success, -1 on invalid version string
|
||||
*/
|
||||
int
|
||||
serviceSetSSLVersion(SERVICE *service, char* version)
|
||||
{
|
||||
if(strcasecmp(version,"SSLV3") == 0)
|
||||
service->ssl_method_type = SERVICE_SSLV3;
|
||||
else if(strcasecmp(version,"TLSV10") == 0)
|
||||
service->ssl_method_type = SERVICE_TLS10;
|
||||
#ifdef OPENSSL_1_0
|
||||
else if(strcasecmp(version,"TLSV11") == 0)
|
||||
service->ssl_method_type = SERVICE_TLS11;
|
||||
else if(strcasecmp(version,"TLSV12") == 0)
|
||||
service->ssl_method_type = SERVICE_TLS12;
|
||||
#endif
|
||||
else if(strcasecmp(version,"MAX") == 0)
|
||||
service->ssl_method_type = SERVICE_SSL_TLS_MAX;
|
||||
else return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service's SSL certificate verification depth. Depth of 0 means the peer
|
||||
* certificate, 1 is the CA and 2 is a higher CA and so on.
|
||||
* @param service Service to configure
|
||||
* @param depth Certificate verification depth
|
||||
* @return 0 on success, -1 on incorrect depth value
|
||||
*/
|
||||
int serviceSetSSLVerifyDepth(SERVICE* service, int depth)
|
||||
{
|
||||
if(depth < 0)
|
||||
return -1;
|
||||
|
||||
service->ssl_cert_verify_depth = depth;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the service SSL capability of a service.
|
||||
* The SSL mode string passed as a parameter should be one of required, enabled
|
||||
* or disabled. Required requires all connections to use SSL encryption, enabled
|
||||
* allows both SSL and non-SSL connections and disabled does not use SSL encryption.
|
||||
* If the service SSL mode is set to enabled, then the client will decide whether
|
||||
* SSL encryption is used.
|
||||
* @param service Service to configure
|
||||
* @param action Mode string. One of required, enabled or disabled.
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
serviceSetSSL(SERVICE *service, char* action)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
if(strcasecmp(action,"required") == 0)
|
||||
service->ssl_mode = SSL_REQUIRED;
|
||||
else if(strcasecmp(action,"enabled") == 0)
|
||||
service->ssl_mode = SSL_ENABLED;
|
||||
else if(strcasecmp(action,"disabled") == 0)
|
||||
service->ssl_mode = SSL_DISABLED;
|
||||
else
|
||||
rval = -1;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to strip escape characters from the name of the database the client
|
||||
* is connecting to.
|
||||
@ -1023,6 +1137,8 @@ int i;
|
||||
printf("\tUsers data: %p\n", (void *)service->users);
|
||||
printf("\tTotal connections: %d\n", service->stats.n_sessions);
|
||||
printf("\tCurrently connected: %d\n", service->stats.n_current);
|
||||
printf("\tSSL: %s\n", service->ssl_mode == SSL_DISABLED ? "Disabled":
|
||||
(service->ssl_mode == SSL_ENABLED ? "Enabled":"Required"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1132,6 +1248,8 @@ int i;
|
||||
service->stats.n_sessions);
|
||||
dcb_printf(dcb, "\tCurrently connected: %d\n",
|
||||
service->stats.n_current);
|
||||
dcb_printf(dcb,"\tSSL: %s\n", service->ssl_mode == SSL_DISABLED ? "Disabled":
|
||||
(service->ssl_mode == SSL_ENABLED ? "Enabled":"Required"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1142,11 +1260,11 @@ int i;
|
||||
void
|
||||
dListServices(DCB *dcb)
|
||||
{
|
||||
SERVICE *ptr;
|
||||
SERVICE *service;
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
ptr = allServices;
|
||||
if (ptr)
|
||||
service = allServices;
|
||||
if (service)
|
||||
{
|
||||
dcb_printf(dcb, "Services.\n");
|
||||
dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n");
|
||||
@ -1154,13 +1272,13 @@ SERVICE *ptr;
|
||||
"Service Name", "Router Module");
|
||||
dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
while (service)
|
||||
{
|
||||
ss_dassert(ptr->stats.n_current >= 0);
|
||||
ss_dassert(service->stats.n_current >= 0);
|
||||
dcb_printf(dcb, "%-25s | %-20s | %6d | %5d\n",
|
||||
ptr->name, ptr->routerModule,
|
||||
ptr->stats.n_current, ptr->stats.n_sessions);
|
||||
ptr = ptr->next;
|
||||
service->name, service->routerModule,
|
||||
service->stats.n_current, service->stats.n_sessions);
|
||||
service = service->next;
|
||||
}
|
||||
if (allServices)
|
||||
dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n\n");
|
||||
@ -1175,12 +1293,12 @@ SERVICE *ptr;
|
||||
void
|
||||
dListListeners(DCB *dcb)
|
||||
{
|
||||
SERVICE *ptr;
|
||||
SERVICE *service;
|
||||
SERV_PROTOCOL *lptr;
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
ptr = allServices;
|
||||
if (ptr)
|
||||
service = allServices;
|
||||
if (service)
|
||||
{
|
||||
dcb_printf(dcb, "Listeners.\n");
|
||||
dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+--------\n");
|
||||
@ -1188,13 +1306,13 @@ SERV_PROTOCOL *lptr;
|
||||
"Service Name", "Protocol Module", "Address");
|
||||
dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+--------\n");
|
||||
}
|
||||
while (ptr)
|
||||
while (service)
|
||||
{
|
||||
lptr = ptr->ports;
|
||||
lptr = service->ports;
|
||||
while (lptr)
|
||||
{
|
||||
dcb_printf(dcb, "%-20s | %-18s | %-15s | %5d | %s\n",
|
||||
ptr->name, lptr->protocol,
|
||||
service->name, lptr->protocol,
|
||||
(lptr && lptr->address) ? lptr->address : "*",
|
||||
lptr->port,
|
||||
(!lptr->listener ||
|
||||
@ -1205,7 +1323,7 @@ SERV_PROTOCOL *lptr;
|
||||
|
||||
lptr = lptr->next;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
service = service->next;
|
||||
}
|
||||
if (allServices)
|
||||
dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+--------\n\n");
|
||||
@ -1260,7 +1378,14 @@ void *router_obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Refresh the database users for the service
|
||||
* This function replaces the MySQL users used by the service with the latest
|
||||
* version found on the backend servers. There is a limit on how often the users
|
||||
* can be reloaded and if this limit is exceeded, the reload will fail.
|
||||
* @param service Service to reload
|
||||
* @return 0 on success and 1 on error
|
||||
*/
|
||||
int service_refresh_users(SERVICE *service) {
|
||||
int ret = 1;
|
||||
/* check for another running getUsers request */
|
||||
@ -1613,15 +1738,15 @@ void service_shutdown()
|
||||
int
|
||||
serviceSessionCountAll()
|
||||
{
|
||||
SERVICE *ptr;
|
||||
SERVICE *service;
|
||||
int rval = 0;
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
ptr = allServices;
|
||||
while (ptr)
|
||||
service = allServices;
|
||||
while (service)
|
||||
{
|
||||
rval += ptr->stats.n_current;
|
||||
ptr = ptr->next;
|
||||
rval += service->stats.n_current;
|
||||
service = service->next;
|
||||
}
|
||||
spinlock_release(&service_spin);
|
||||
return rval;
|
||||
@ -1642,16 +1767,16 @@ int *rowno = (int *)data;
|
||||
int i = 0;;
|
||||
char buf[20];
|
||||
RESULT_ROW *row;
|
||||
SERVICE *ptr;
|
||||
SERVICE *service;
|
||||
SERV_PROTOCOL *lptr = NULL;
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
ptr = allServices;
|
||||
if (ptr)
|
||||
lptr = ptr->ports;
|
||||
while (i < *rowno && ptr)
|
||||
service = allServices;
|
||||
if (service)
|
||||
lptr = service->ports;
|
||||
while (i < *rowno && service)
|
||||
{
|
||||
lptr = ptr->ports;
|
||||
lptr = service->ports;
|
||||
while (i < *rowno && lptr)
|
||||
{
|
||||
if ((lptr = lptr->next) != NULL)
|
||||
@ -1659,8 +1784,8 @@ SERV_PROTOCOL *lptr = NULL;
|
||||
}
|
||||
if (i < *rowno)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
if (ptr && (lptr = ptr->ports) != NULL)
|
||||
service = service->next;
|
||||
if (service && (lptr = service->ports) != NULL)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@ -1672,7 +1797,7 @@ SERV_PROTOCOL *lptr = NULL;
|
||||
}
|
||||
(*rowno)++;
|
||||
row = resultset_make_row(set);
|
||||
resultset_row_set(row, 0, ptr->name);
|
||||
resultset_row_set(row, 0, service->name);
|
||||
resultset_row_set(row, 1, lptr->protocol);
|
||||
resultset_row_set(row, 2, (lptr && lptr->address) ? lptr->address : "*");
|
||||
sprintf(buf, "%d", lptr->port);
|
||||
@ -1727,16 +1852,16 @@ int *rowno = (int *)data;
|
||||
int i = 0;;
|
||||
char buf[20];
|
||||
RESULT_ROW *row;
|
||||
SERVICE *ptr;
|
||||
SERVICE *service;
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
ptr = allServices;
|
||||
while (i < *rowno && ptr)
|
||||
service = allServices;
|
||||
while (i < *rowno && service)
|
||||
{
|
||||
i++;
|
||||
ptr = ptr->next;
|
||||
service = service->next;
|
||||
}
|
||||
if (ptr == NULL)
|
||||
if (service == NULL)
|
||||
{
|
||||
spinlock_release(&service_spin);
|
||||
free(data);
|
||||
@ -1744,11 +1869,11 @@ SERVICE *ptr;
|
||||
}
|
||||
(*rowno)++;
|
||||
row = resultset_make_row(set);
|
||||
resultset_row_set(row, 0, ptr->name);
|
||||
resultset_row_set(row, 1, ptr->routerModule);
|
||||
sprintf(buf, "%d", ptr->stats.n_current);
|
||||
resultset_row_set(row, 0, service->name);
|
||||
resultset_row_set(row, 1, service->routerModule);
|
||||
sprintf(buf, "%d", service->stats.n_current);
|
||||
resultset_row_set(row, 2, buf);
|
||||
sprintf(buf, "%d", ptr->stats.n_sessions);
|
||||
sprintf(buf, "%d", service->stats.n_sessions);
|
||||
resultset_row_set(row, 3, buf);
|
||||
spinlock_release(&service_spin);
|
||||
return row;
|
||||
@ -1780,3 +1905,140 @@ int *data;
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The RSA ket generation callback function for OpenSSL.
|
||||
* @param s SSL structure
|
||||
* @param is_export Not used
|
||||
* @param keylength Length of the key
|
||||
* @return Pointer to RSA structure
|
||||
*/
|
||||
RSA *tmp_rsa_callback(SSL *s, int is_export, int keylength)
|
||||
{
|
||||
RSA *rsa_tmp=NULL;
|
||||
|
||||
switch (keylength) {
|
||||
case 512:
|
||||
if (rsa_512)
|
||||
rsa_tmp = rsa_512;
|
||||
else { /* generate on the fly, should not happen in this example */
|
||||
rsa_tmp = RSA_generate_key(keylength,RSA_F4,NULL,NULL);
|
||||
rsa_512 = rsa_tmp; /* Remember for later reuse */
|
||||
}
|
||||
break;
|
||||
case 1024:
|
||||
if (rsa_1024)
|
||||
rsa_tmp=rsa_1024;
|
||||
break;
|
||||
default:
|
||||
/* Generating a key on the fly is very costly, so use what is there */
|
||||
if (rsa_1024)
|
||||
rsa_tmp=rsa_1024;
|
||||
else
|
||||
rsa_tmp=rsa_512; /* Use at least a shorter key */
|
||||
}
|
||||
return(rsa_tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the servce's SSL context. This sets up the generated RSA
|
||||
* encryption keys, chooses the server encryption level and configures the server
|
||||
* certificate, private key and certificate authority file.
|
||||
* @param service
|
||||
* @return
|
||||
*/
|
||||
int serviceInitSSL(SERVICE* service)
|
||||
{
|
||||
DH* dh;
|
||||
RSA* rsa;
|
||||
|
||||
if(!service->ssl_init_done)
|
||||
{
|
||||
switch(service->ssl_method_type)
|
||||
{
|
||||
case SERVICE_SSLV3:
|
||||
service->method = (SSL_METHOD*)SSLv3_server_method();
|
||||
break;
|
||||
case SERVICE_TLS10:
|
||||
service->method = (SSL_METHOD*)TLSv1_server_method();
|
||||
break;
|
||||
#ifdef OPENSSL_1_0
|
||||
case SERVICE_TLS11:
|
||||
service->method = (SSL_METHOD*)TLSv1_1_server_method();
|
||||
break;
|
||||
case SERVICE_TLS12:
|
||||
service->method = (SSL_METHOD*)TLSv1_2_server_method();
|
||||
break;
|
||||
#endif
|
||||
/** Rest of these use the maximum available SSL/TLS methods */
|
||||
case SERVICE_SSL_MAX:
|
||||
service->method = (SSL_METHOD*)SSLv23_server_method();
|
||||
break;
|
||||
case SERVICE_TLS_MAX:
|
||||
service->method = (SSL_METHOD*)SSLv23_server_method();
|
||||
break;
|
||||
case SERVICE_SSL_TLS_MAX:
|
||||
service->method = (SSL_METHOD*)SSLv23_server_method();
|
||||
break;
|
||||
default:
|
||||
service->method = (SSL_METHOD*)SSLv23_server_method();
|
||||
break;
|
||||
}
|
||||
|
||||
service->ctx = SSL_CTX_new(service->method);
|
||||
|
||||
/** Enable all OpenSSL bug fixes */
|
||||
SSL_CTX_set_options(service->ctx,SSL_OP_ALL);
|
||||
|
||||
/** Generate the 512-bit and 1024-bit RSA keys */
|
||||
if(rsa_512 == NULL)
|
||||
{
|
||||
rsa_512 = RSA_generate_key(512,RSA_F4,NULL,NULL);
|
||||
if (rsa_512 == NULL)
|
||||
skygw_log_write(LE,"Error: 512-bit RSA key generation failed.");
|
||||
}
|
||||
if(rsa_1024 == NULL)
|
||||
{
|
||||
rsa_1024 = RSA_generate_key(1024,RSA_F4,NULL,NULL);
|
||||
if (rsa_1024 == NULL)
|
||||
skygw_log_write(LE,"Error: 1024-bit RSA key generation failed.");
|
||||
}
|
||||
|
||||
if(rsa_512 != NULL && rsa_1024 != NULL)
|
||||
SSL_CTX_set_tmp_rsa_callback(service->ctx,tmp_rsa_callback);
|
||||
|
||||
/** Load the server sertificate */
|
||||
if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) {
|
||||
skygw_log_write(LE,"Error: Failed to set server SSL certificate.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load the private-key corresponding to the server certificate */
|
||||
if (SSL_CTX_use_PrivateKey_file(service->ctx, service->ssl_key, SSL_FILETYPE_PEM) <= 0) {
|
||||
skygw_log_write(LE,"Error: Failed to set server SSL key.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the server certificate and private-key matches */
|
||||
if (!SSL_CTX_check_private_key(service->ctx)) {
|
||||
skygw_log_write(LE,"Error: Server SSL certificate and key do not match.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Load the RSA CA certificate into the SSL_CTX structure */
|
||||
if (!SSL_CTX_load_verify_locations(service->ctx, service->ssl_ca_cert, NULL)) {
|
||||
skygw_log_write(LE,"Error: Failed to set Certificate Authority file.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set to require peer (client) certificate verification */
|
||||
SSL_CTX_set_verify(service->ctx,SSL_VERIFY_PEER,NULL);
|
||||
|
||||
/* Set the verification depth */
|
||||
SSL_CTX_set_verify_depth(service->ctx,service->ssl_cert_verify_depth);
|
||||
service->ssl_init_done = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -962,7 +962,7 @@ void session_close_timeouts(void* data)
|
||||
ses->service->conn_timeout > 0 &&
|
||||
hkheartbeat - ses->client->last_read > ses->service->conn_timeout * 10)
|
||||
{
|
||||
ses->client->func.hangup(ses->client);
|
||||
dcb_close(ses->client);
|
||||
}
|
||||
|
||||
spinlock_acquire(&session_spin);
|
||||
|
||||
@ -44,19 +44,4 @@ add_test(Internal-TestUsers test_users)
|
||||
add_test(Internal-TestAdminUsers test_adminusers)
|
||||
add_test(Internal-TestMemlog testmemlog)
|
||||
add_test(TestFeedback testfeedback)
|
||||
set_tests_properties(Internal-TestMySQLUsers
|
||||
Internal-TestHash
|
||||
Internal-TestHint
|
||||
Internal-TestSpinlock
|
||||
Internal-TestFilter
|
||||
Internal-TestBuffer
|
||||
Internal-TestDCB
|
||||
Internal-TestModutil
|
||||
Internal-TestPoll
|
||||
Internal-TestService
|
||||
Internal-TestServer
|
||||
Internal-TestUsers
|
||||
Internal-TestAdminUsers
|
||||
Internal-TestMemlog
|
||||
TestFeedback PROPERTIES ENVIRONMENT MAXSCALE_HOME=${CMAKE_BINARY_DIR}/)
|
||||
set_tests_properties(TestFeedback PROPERTIES TIMEOUT 30)
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gwdirs.h>
|
||||
#include <adminusers.h>
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
* Test that the username password admin/mariadb is accepted if no users
|
||||
* have been created and that no other users are accepted
|
||||
*
|
||||
* WARNING: $MAXSCALE_HOME/etc/passwd must be removed before this test is run
|
||||
* WARNING: The passwd file must be removed before this test is run
|
||||
*/
|
||||
static int
|
||||
test1()
|
||||
@ -269,9 +269,8 @@ int result = 0;
|
||||
char *home, buf[1024];
|
||||
|
||||
/* Unlink any existing password file before running this test */
|
||||
if ((home = getenv("MAXSCALE_HOME")) == NULL || strlen(home) >= 1024)
|
||||
home = "/usr/local/mariadb-maxscale";
|
||||
sprintf(buf, "%s/etc/passwd", home);
|
||||
|
||||
sprintf(buf, "%s/passwd", default_cachedir);
|
||||
if(!is_valid_posix_path(buf))
|
||||
exit(1);
|
||||
if (strcmp(buf, "/etc/passwd") != 0)
|
||||
|
||||
@ -64,7 +64,7 @@ int buflen;
|
||||
ss_info_dassert(!dcb_isvalid(dcb), "Freed DCB must not be valid");
|
||||
ss_dfprintf(stderr, "\t..done\nMake clone DCB a zombie");
|
||||
clone->state = DCB_STATE_NOPOLLING;
|
||||
dcb_add_to_zombieslist(clone);
|
||||
dcb_close(clone);
|
||||
ss_info_dassert(dcb_get_zombies() == clone, "Clone DCB must be start of zombie list now");
|
||||
ss_dfprintf(stderr, "\t..done\nProcess the zombies list");
|
||||
dcb_process_zombies(0);
|
||||
|
||||
@ -73,17 +73,8 @@ int main(int argc, char** argv)
|
||||
char* cnf;
|
||||
|
||||
hkinit();
|
||||
home = getenv("MAXSCALE_HOME");
|
||||
|
||||
if(home == NULL)
|
||||
{
|
||||
FAILTEST("MAXSCALE_HOME was not defined.");
|
||||
}
|
||||
printf("Home: %s\n",home);
|
||||
|
||||
cnf = malloc(strlen(home) + strlen("/etc/MaxScale.cnf") + 1);
|
||||
strcpy(cnf,home);
|
||||
strcat(cnf,"/etc/MaxScale.cnf");
|
||||
cnf = strdup("/etc/MaxScale.cnf");
|
||||
|
||||
printf("Config: %s\n",cnf);
|
||||
|
||||
@ -116,4 +107,4 @@ int main(int argc, char** argv)
|
||||
}
|
||||
mysql_library_end();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
#include <users.h>
|
||||
#include <atomic.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
/**
|
||||
* @file users.c User table maintenance routines
|
||||
@ -59,12 +60,16 @@ users_alloc()
|
||||
USERS *rval;
|
||||
|
||||
if ((rval = calloc(1, sizeof(USERS))) == NULL)
|
||||
return NULL;
|
||||
{
|
||||
skygw_log_write(LE,"[%s:%d] Error: Memory allocation failed.",__FUNCTION__,__LINE__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((rval->data = hashtable_alloc(USERS_HASHTABLE_DEFAULT_SIZE, user_hash, strcmp)) == NULL)
|
||||
{
|
||||
free(rval);
|
||||
return NULL;
|
||||
skygw_log_write(LE,"[%s:%d] Error: Memory allocation failed.",__FUNCTION__,__LINE__);
|
||||
free(rval);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hashtable_memory_fns(rval->data, (HASHMEMORYFN)strdup, (HASHMEMORYFN)strdup, (HASHMEMORYFN)free, (HASHMEMORYFN)free);
|
||||
@ -80,8 +85,15 @@ USERS *rval;
|
||||
void
|
||||
users_free(USERS *users)
|
||||
{
|
||||
if(users == NULL)
|
||||
{
|
||||
skygw_log_write(LE,"[%s:%d] Error: NULL parameter.",__FUNCTION__,__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if(users->data)
|
||||
hashtable_free(users->data);
|
||||
free(users);
|
||||
free(users);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user