Files
MaxScale/server/core/config.c
VilhoRaatikka fd11e6a7f5 Session-specific logging. Added functions
void session_enable_log(SESSION* ses, logfile_id_t id)
and
void session_disable_log(SESSION* ses, logfile_id_t id)

Which switch specific log type on/off if the log type in question is not generally enabled.

Each thread carries a thread-specific struct log_info_t which includes members for current session id and bitfield for enabled log types for the current session. That information is checked before actual log write functions are called.

Each file where session-specific logging is used, must include the following exports:

/** 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;
2014-11-19 00:08:59 +02:00

1818 lines
56 KiB
C

/*
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
* software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright MariaDB Corporation Ab 2013-2014
*/
/**
* @file config.c - Read the gateway.cnf configuration file
*
* @verbatim
* Revision History
*
* Date Who Description
* 21/06/13 Mark Riddoch Initial implementation
* 08/07/13 Mark Riddoch Addition on monitor module support
* 23/07/13 Mark Riddoch Addition on default monitor password
* 06/02/14 Massimiliano Pinto Added support for enable/disable root user in services
* 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list
* 11/03/14 Massimiliano Pinto Added Unix socket support
* 11/05/14 Massimiliano Pinto Added version_string support to service
* 19/05/14 Mark Riddoch Added unique names from section headers
* 29/05/14 Mark Riddoch Addition of filter definition
* 23/05/14 Massimiliano Pinto Added automatic set of maxscale-id: first listening ipv4_raw + port + pid
* 28/05/14 Massimiliano Pinto Added detect_replication_lag parameter
* 28/08/14 Massimiliano Pinto Added detect_stale_master parameter
* 09/09/14 Massimiliano Pinto Added localhost_match_wildcard_host parameter
* 12/09/14 Mark Riddoch Addition of checks on servers list and
* internal router suppression of messages
* 30/10/14 Massimiliano Pinto Added disable_master_failback parameter
* 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write
*
* @endverbatim
*/
#include <my_config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <ini.h>
#include <config.h>
#include <service.h>
#include <server.h>
#include <users.h>
#include <monitor.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <mysql.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;
extern int setipaddress(struct in_addr *, char *);
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 int handle_global_item(const char *, const char *);
static void global_defaults();
static void check_config_objects(CONFIG_CONTEXT *context);
static int config_truth_value(char *str);
static int internalService(char *router);
static char *config_file = NULL;
static GATEWAY_CONF gateway;
char *version_string = NULL;
/**
* Trim whitespace from the front and rear of a string
*
* @param str String to trim
* @return Trimmed string, changes are done in situ
*/
static char *
trim(char *str)
{
char *ptr;
while (isspace(*str))
str++;
/* Point to last character of the string */
ptr = str + strlen(str) - 1;
while (ptr > str && isspace(*ptr))
*ptr-- = 0;
return str;
}
/**
* Config item handler for the ini file reader
*
* @param userdata The config context element
* @param section The config file section
* @param name The Parameter name
* @param value The Parameter value
* @return zero on error
*/
static int
handler(void *userdata, const char *section, const char *name, const char *value)
{
CONFIG_CONTEXT *cntxt = (CONFIG_CONTEXT *)userdata;
CONFIG_CONTEXT *ptr = cntxt;
CONFIG_PARAMETER *param, *p1;
if (strcmp(section, "gateway") == 0 || strcasecmp(section, "MaxScale") == 0)
{
return handle_global_item(name, value);
}
/*
* If we already have some parameters for the object
* add the parameters to that object. If not create
* a new object.
*/
while (ptr && strcmp(ptr->object, section) != 0)
ptr = ptr->next;
if (!ptr)
{
if ((ptr = (CONFIG_CONTEXT *)malloc(sizeof(CONFIG_CONTEXT))) == NULL)
return 0;
ptr->object = strdup(section);
ptr->parameters = NULL;
ptr->next = cntxt->next;
ptr->element = NULL;
cntxt->next = ptr;
}
/* Check to see if the parameter already exists for the section */
p1 = ptr->parameters;
while (p1)
{
if (!strcmp(p1->name, name))
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Configuration object '%s' has multiple "
"parameters names '%s'.",
ptr->object, name)));
return 0;
}
p1 = p1->next;
}
if ((param = (CONFIG_PARAMETER *)malloc(sizeof(CONFIG_PARAMETER))) == NULL)
return 0;
param->name = strdup(name);
param->value = strdup(value);
param->next = ptr->parameters;
ptr->parameters = param;
return 1;
}
/**
* Load the configuration file for the MaxScale
*
* @param file The filename of the configuration file
* @return A zero return indicates a fatal error reading the configuration
*/
int
config_load(char *file)
{
CONFIG_CONTEXT config;
int rval;
MYSQL *conn;
conn = mysql_init(NULL);
if (conn) {
if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0)) {
char *ptr;
version_string = (char *)mysql_get_server_info(conn);
ptr = strstr(version_string, "-embedded");
if (ptr) {
*ptr = '\0';
}
}
mysql_close(conn);
}
global_defaults();
config.object = "";
config.next = NULL;
if (ini_parse(file, handler, &config) < 0)
return 0;
config_file = file;
check_config_objects(config.next);
rval = process_config_context(config.next);
free_config_context(config.next);
return rval;
}
/**
* Reload the configuration file for the MaxScale
*
* @return A zero return indicates a fatal error reading the configuration
*/
int
config_reload()
{
CONFIG_CONTEXT config;
int rval;
if (!config_file)
return 0;
if (gateway.version_string)
free(gateway.version_string);
global_defaults();
config.object = "";
config.next = NULL;
if (ini_parse(config_file, handler, &config) < 0)
return 0;
rval = process_config_update(config.next);
free_config_context(config.next);
return rval;
}
/**
* Process a configuration context and turn it into the set of object
* we need.
*
* @param context The configuration data
* @return A zero result indicates a fatal error
*/
static int
process_config_context(CONFIG_CONTEXT *context)
{
CONFIG_CONTEXT *obj;
int error_count = 0;
/**
* Process the data and create the services and servers defined
* in the data.
*/
obj = context;
while (obj)
{
char *type = config_get_value(obj->parameters, "type");
if (type == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Configuration object '%s' has no type.",
obj->object)));
error_count++;
}
else if (!strcmp(type, "service"))
{
char *router = config_get_value(obj->parameters,
"router");
if (router)
{
char* max_slave_conn_str;
char* max_slave_rlag_str;
char *user;
char *auth;
char *enable_root_user;
char *weightby;
char *version_string;
bool is_rwsplit = false;
obj->element = service_alloc(obj->object, router);
user = config_get_value(obj->parameters, "user");
auth = config_get_value(obj->parameters, "passwd");
enable_root_user = config_get_value(
obj->parameters,
"enable_root_user");
weightby = config_get_value(obj->parameters, "weightby");
version_string = config_get_value(obj->parameters,
"version_string");
/** flag for rwsplit-specific parameters */
if (strncmp(router, "readwritesplit", strlen("readwritesplit")+1) == 0)
{
is_rwsplit = true;
}
char *allow_localhost_match_wildcard_host =
config_get_value(obj->parameters, "localhost_match_wildcard_host");
if (obj->element == NULL) /*< if module load failed */
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Reading configuration "
"for router service '%s' failed. "
"Router %s is not loaded.",
obj->object,
obj->object)));
obj = obj->next;
continue; /*< process next obj */
}
if (version_string) {
((SERVICE *)(obj->element))->version_string = strdup(version_string);
} else {
if (gateway.version_string)
((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string);
}
max_slave_conn_str =
config_get_value(obj->parameters,
"max_slave_connections");
max_slave_rlag_str =
config_get_value(obj->parameters,
"max_slave_replication_lag");
if (enable_root_user)
serviceEnableRootUser(
obj->element,
config_truth_value(enable_root_user));
if (weightby)
serviceWeightBy(obj->element, weightby);
if (allow_localhost_match_wildcard_host)
serviceEnableLocalhostMatchWildcardHost(
obj->element,
config_truth_value(allow_localhost_match_wildcard_host));
if (!auth)
auth = config_get_value(obj->parameters,
"auth");
if (obj->element && user && auth)
{
serviceSetUser(obj->element,
user,
auth);
}
else if (user && auth == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Service '%s' has a "
"user defined but no "
"corresponding password.",
obj->object)));
}
/** Read, validate and set max_slave_connections */
if (max_slave_conn_str != NULL)
{
CONFIG_PARAMETER* param;
bool succp;
param = config_get_param(obj->parameters,
"max_slave_connections");
if (param == NULL)
{
succp = false;
}
else
{
succp = service_set_param_value(
obj->element,
param,
max_slave_conn_str,
COUNT_ATMOST,
(COUNT_TYPE|PERCENT_TYPE));
}
if (!succp)
{
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"* Warning : invalid value type "
"for parameter \'%s.%s = %s\'\n\tExpected "
"type is either <int> for slave connection "
"count or\n\t<int>%% for specifying the "
"maximum percentage of available the "
"slaves that will be connected.",
((SERVICE*)obj->element)->name,
param->name,
param->value)));
}
}
/** Read, validate and set max_slave_replication_lag */
if (max_slave_rlag_str != NULL)
{
CONFIG_PARAMETER* param;
bool succp;
param = config_get_param(
obj->parameters,
"max_slave_replication_lag");
if (param == NULL)
{
succp = false;
}
else
{
succp = service_set_param_value(
obj->element,
param,
max_slave_rlag_str,
COUNT_ATMOST,
COUNT_TYPE);
}
if (!succp)
{
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"* Warning : invalid value type "
"for parameter \'%s.%s = %s\'\n\tExpected "
"type is <int> for maximum "
"slave replication lag.",
((SERVICE*)obj->element)->name,
param->name,
param->value)));
}
}
/** Parameters for rwsplit router only */
if (is_rwsplit)
{
CONFIG_PARAMETER* param;
char* use_sql_variables_in;
bool succp;
use_sql_variables_in =
config_get_value(obj->parameters,
"use_sql_variables_in");
if (use_sql_variables_in != NULL)
{
param = config_get_param(
obj->parameters,
"use_sql_variables_in");
if (param == NULL)
{
succp = false;
}
else
{
succp = service_set_param_value(obj->element,
param,
use_sql_variables_in,
COUNT_NONE,
SQLVAR_TARGET_TYPE);
}
if (!succp)
{
if(param){
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"* Warning : invalid value type "
"for parameter \'%s.%s = %s\'\n\tExpected "
"type is [master|all] for "
"use sql variables in.",
((SERVICE*)obj->element)->name,
param->name,
param->value)));
}else{
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"Error : parameter was NULL")));
}
}
}
} /*< if (rw_split) */
} /*< if (router) */
else
{
obj->element = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : No router defined for service "
"'%s'\n",
obj->object)));
error_count++;
}
}
else if (!strcmp(type, "server"))
{
char *address;
char *port;
char *protocol;
char *monuser;
char *monpw;
address = config_get_value(obj->parameters, "address");
port = config_get_value(obj->parameters, "port");
protocol = config_get_value(obj->parameters, "protocol");
monuser = config_get_value(obj->parameters,
"monitoruser");
monpw = config_get_value(obj->parameters, "monitorpw");
if (address && port && protocol)
{
obj->element = server_alloc(address,
protocol,
atoi(port));
server_set_unique_name(obj->element, obj->object);
}
else
{
obj->element = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Server '%s' is missing a "
"required configuration parameter. A "
"server must "
"have address, port and protocol "
"defined.",
obj->object)));
error_count++;
}
if (obj->element && monuser && monpw)
serverAddMonUser(obj->element, monuser, monpw);
else if (monuser && monpw == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Server '%s' has a monitoruser"
"defined but no corresponding password.",
obj->object)));
}
if (obj->element)
{
CONFIG_PARAMETER *params = obj->parameters;
while (params)
{
if (strcmp(params->name, "address")
&& strcmp(params->name, "port")
&& strcmp(params->name,
"protocol")
&& strcmp(params->name,
"monitoruser")
&& strcmp(params->name,
"monitorpw")
&& strcmp(params->name,
"type")
)
{
serverAddParameter(obj->element,
params->name,
params->value);
}
params = params->next;
}
}
}
else if (!strcmp(type, "filter"))
{
char *module = config_get_value(obj->parameters,
"module");
char *options = config_get_value(obj->parameters,
"options");
if (module)
{
obj->element = filter_alloc(obj->object, module);
}
else
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Filter '%s' has no module "
"defined defined to load.",
obj->object)));
error_count++;
}
if (obj->element && options)
{
char *s = strtok(options, ",");
while (s)
{
filterAddOption(obj->element, s);
s = strtok(NULL, ",");
}
}
if (obj->element)
{
CONFIG_PARAMETER *params = obj->parameters;
while (params)
{
if (strcmp(params->name, "module")
&& strcmp(params->name,
"options"))
{
filterAddParameter(obj->element,
params->name,
params->value);
}
params = params->next;
}
}
}
obj = obj->next;
}
/*
* Now we have the services we can add the servers to the services
* add the protocols to the services
*/
obj = context;
while (obj)
{
char *type = config_get_value(obj->parameters, "type");
if (type == NULL)
;
else if (!strcmp(type, "service"))
{
char *servers;
char *roptions;
char *router;
char *filters = config_get_value(obj->parameters,
"filters");
servers = config_get_value(obj->parameters, "servers");
roptions = config_get_value(obj->parameters,
"router_options");
router = config_get_value(obj->parameters, "router");
if (servers && obj->element)
{
char *s = strtok(servers, ",");
while (s)
{
CONFIG_CONTEXT *obj1 = context;
int found = 0;
while (obj1)
{
if (strcmp(trim(s), obj1->object) == 0 &&
obj->element && obj1->element)
{
found = 1;
serviceAddBackend(
obj->element,
obj1->element);
}
obj1 = obj1->next;
}
if (!found)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Unable to find "
"server '%s' that is "
"configured as part of "
"service '%s'.",
s, obj->object)));
}
s = strtok(NULL, ",");
}
}
else if (servers == NULL && internalService(router) == 0)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Warning: The service '%s' is missing a "
"definition of the servers that provide "
"the service.",
obj->object)));
}
if (roptions && obj->element)
{
char *s = strtok(roptions, ",");
while (s)
{
serviceAddRouterOption(obj->element, s);
s = strtok(NULL, ",");
}
}
if (filters && obj->element)
{
serviceSetFilters(obj->element, filters);
}
}
else if (!strcmp(type, "listener"))
{
char *service;
char *address;
char *port;
char *protocol;
char *socket;
struct sockaddr_in serv_addr;
service = config_get_value(obj->parameters, "service");
port = config_get_value(obj->parameters, "port");
address = config_get_value(obj->parameters, "address");
protocol = config_get_value(obj->parameters, "protocol");
socket = config_get_value(obj->parameters, "socket");
/* 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 + 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++;
}
}
if (service && port && protocol) {
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++;
}
}
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++;
}
}
else if (!strcmp(type, "monitor"))
{
char *module;
char *servers;
char *user;
char *passwd;
unsigned long interval = 0;
int replication_heartbeat = 0;
int detect_stale_master = 0;
int disable_master_failback = 0;
int connect_timeout = 0;
int read_timeout = 0;
int write_timeout = 0;
module = config_get_value(obj->parameters, "module");
servers = config_get_value(obj->parameters, "servers");
user = config_get_value(obj->parameters, "user");
passwd = config_get_value(obj->parameters, "passwd");
if (config_get_value(obj->parameters, "monitor_interval")) {
interval = strtoul(config_get_value(obj->parameters, "monitor_interval"), NULL, 10);
}
if (config_get_value(obj->parameters, "detect_replication_lag")) {
replication_heartbeat = atoi(config_get_value(obj->parameters, "detect_replication_lag"));
}
if (config_get_value(obj->parameters, "detect_stale_master")) {
detect_stale_master = atoi(config_get_value(obj->parameters, "detect_stale_master"));
}
if (config_get_value(obj->parameters, "disable_master_failback")) {
disable_master_failback = atoi(config_get_value(obj->parameters, "disable_master_failback"));
}
if (config_get_value(obj->parameters, "backend_connect_timeout")) {
connect_timeout = atoi(config_get_value(obj->parameters, "backend_connect_timeout"));
}
if (config_get_value(obj->parameters, "backend_read_timeout")) {
read_timeout = atoi(config_get_value(obj->parameters, "backend_read_timeout"));
}
if (config_get_value(obj->parameters, "backend_write_timeout")) {
write_timeout = atoi(config_get_value(obj->parameters, "backend_write_timeout"));
}
if (module)
{
obj->element = monitor_alloc(obj->object, module);
if (servers && obj->element)
{
char *s;
/* if id is not set, compute it now with pid only */
if (gateway.id == 0) {
gateway.id = getpid();
}
/* add the maxscale-id to monitor data */
monitorSetId(obj->element, gateway.id);
/* set monitor interval */
if (interval > 0)
monitorSetInterval(obj->element, interval);
/* set replication heartbeat */
if(replication_heartbeat == 1)
monitorSetReplicationHeartbeat(obj->element, replication_heartbeat);
/* detect stale master */
if(detect_stale_master == 1)
monitorDetectStaleMaster(obj->element, detect_stale_master);
/* disable master failback */
if(disable_master_failback == 1)
monitorDisableMasterFailback(obj->element, disable_master_failback);
/* set timeouts */
if (connect_timeout > 0)
monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, connect_timeout);
if (read_timeout > 0)
monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, read_timeout);
if (write_timeout > 0)
monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, write_timeout);
/* get the servers to monitor */
s = strtok(servers, ",");
while (s)
{
CONFIG_CONTEXT *obj1 = context;
int found = 0;
while (obj1)
{
if (strcmp(trim(s), obj1->object) == 0 &&
obj->element && obj1->element)
{
found = 1;
monitorAddServer(
obj->element,
obj1->element);
}
obj1 = obj1->next;
}
if (!found)
LOGIF(LE,
(skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Unable to find "
"server '%s' that is "
"configured in the "
"monitor '%s'.",
s, obj->object)));
s = strtok(NULL, ",");
}
}
if (obj->element && user && passwd)
{
monitorAddUser(obj->element,
user,
passwd);
}
else if (obj->element && user)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, "Error: "
"Monitor '%s' defines a "
"username with no password.",
obj->object)));
error_count++;
}
}
else
{
obj->element = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Monitor '%s' is missing a "
"require module parameter.",
obj->object)));
error_count++;
}
}
else if (strcmp(type, "server") != 0
&& strcmp(type, "filter") != 0)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Configuration object '%s' has an "
"invalid type specified.",
obj->object)));
error_count++;
}
obj = obj->next;
} /*< while */
/** TODO: consistency check function */
/**
* error_count += consistency_checks();
*/
if (error_count)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : %d errors where encountered processing the "
"configuration file '%s'.",
error_count,
config_file)));
return 0;
}
return 1;
}
/**
* Get the value of a config parameter
*
* @param params The linked list of config parameters
* @param name The parameter to return
* @return the parameter value or NULL if not found
*/
static char *
config_get_value(CONFIG_PARAMETER *params, const char *name)
{
while (params)
{
if (!strcmp(params->name, name))
return params->value;
params = params->next;
}
return NULL;
}
CONFIG_PARAMETER* config_get_param(
CONFIG_PARAMETER* params,
const char* name)
{
while (params)
{
if (!strcmp(params->name, name))
return params;
params = params->next;
}
return NULL;
}
config_param_type_t config_get_paramtype(
CONFIG_PARAMETER* param)
{
return param->qfd_param_type;
}
bool config_get_valint(
int* val,
CONFIG_PARAMETER* param,
const char* name, /*< if NULL examine current param only */
config_param_type_t ptype)
{
bool succp = false;;
ss_dassert((ptype == COUNT_TYPE || ptype == PERCENT_TYPE) && param != NULL);
while (param)
{
if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN))
{
switch (ptype) {
case COUNT_TYPE:
*val = param->qfd.valcount;
succp = true;
goto return_succp;
case PERCENT_TYPE:
*val = param->qfd.valpercent;
succp =true;
goto return_succp;
default:
goto return_succp;
}
}
param = param->next;
}
return_succp:
return succp;
}
bool config_get_valbool(
bool* val,
CONFIG_PARAMETER* param,
const char* name,
config_param_type_t ptype)
{
bool succp;
ss_dassert(ptype == BOOL_TYPE);
ss_dassert(param != NULL);
if (ptype != BOOL_TYPE || param == NULL)
{
succp = false;
goto return_succp;
}
while (param)
{
if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN))
{
*val = param->qfd.valbool;
succp = true;
goto return_succp;
}
param = param->next;
}
succp = false;
return_succp:
return succp;
}
bool config_get_valtarget(
target_t* val,
CONFIG_PARAMETER* param,
const char* name,
config_param_type_t ptype)
{
bool succp;
ss_dassert(ptype == SQLVAR_TARGET_TYPE);
ss_dassert(param != NULL);
if (ptype != SQLVAR_TARGET_TYPE || param == NULL)
{
succp = false;
goto return_succp;
}
while (param)
{
if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN))
{
*val = param->qfd.valtarget;
succp = true;
goto return_succp;
}
param = param->next;
}
succp = false;
return_succp:
return succp;
}
CONFIG_PARAMETER* config_clone_param(
CONFIG_PARAMETER* param)
{
CONFIG_PARAMETER* p2;
p2 = (CONFIG_PARAMETER*) malloc(sizeof(CONFIG_PARAMETER));
if (p2 == NULL)
{
goto return_p2;
}
memcpy(p2, param, sizeof(CONFIG_PARAMETER));
p2->name = strndup(param->name, MAX_PARAM_LEN);
p2->value = strndup(param->value, MAX_PARAM_LEN);
if (param->qfd_param_type == STRING_TYPE)
{
p2->qfd.valstr = strndup(param->qfd.valstr, MAX_PARAM_LEN);
}
return_p2:
return p2;
}
/**
* Free a config tree
*
* @param context The configuration data
*/
static void
free_config_context(CONFIG_CONTEXT *context)
{
CONFIG_CONTEXT *obj;
CONFIG_PARAMETER *p1, *p2;
while (context)
{
free(context->object);
p1 = context->parameters;
while (p1)
{
free(p1->name);
free(p1->value);
p2 = p1->next;
free(p1);
p1 = p2;
}
obj = context->next;
free(context);
context = obj;
}
}
/**
* Return the number of configured threads
*
* @return The number of threads configured in the config file
*/
int
config_threadcount()
{
return gateway.n_threads;
}
static struct {
char *logname;
logfile_id_t logfile;
} lognames[] = {
{ "log_messages", LOGFILE_MESSAGE },
{ "log_trace", LOGFILE_TRACE },
{ "log_debug", LOGFILE_DEBUG },
{ NULL, 0 }
};
/**
* Configuration handler for items in the global [MaxScale] section
*
* @param name The item name
* @param value The item value
* @return 0 on error
*/
static int
handle_global_item(const char *name, const char *value)
{
int i;
if (strcmp(name, "threads") == 0) {
gateway.n_threads = atoi(value);
} else {
for (i = 0; lognames[i].logname; i++)
{
if (strcasecmp(name, lognames[i].logname) == 0)
{
if (atoi(value))
skygw_log_enable(lognames[i].logfile);
else
skygw_log_disable(lognames[i].logfile);
}
}
}
return 1;
}
/**
* Set the defaults for the global configuration options
*/
static void
global_defaults()
{
gateway.n_threads = 1;
if (version_string != NULL)
gateway.version_string = strdup(version_string);
else
gateway.version_string = NULL;
gateway.id=0;
}
/**
* Process a configuration context update and turn it into the set of object
* we need.
*
* @param context The configuration data
*/
static int
process_config_update(CONFIG_CONTEXT *context)
{
CONFIG_CONTEXT *obj;
SERVICE *service;
SERVER *server;
/**
* Process the data and create the services and servers defined
* in the data.
*/
obj = context;
while (obj)
{
char *type = config_get_value(obj->parameters, "type");
if (type == NULL)
{
LOGIF(LE,
(skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Configuration object %s has no type.",
obj->object)));
}
else if (!strcmp(type, "service"))
{
char *router = config_get_value(obj->parameters,
"router");
if (router)
{
if ((service = service_find(obj->object)) != NULL)
{
char *user;
char *auth;
char *enable_root_user;
char* max_slave_conn_str;
char* max_slave_rlag_str;
char *version_string;
char *allow_localhost_match_wildcard_host;
enable_root_user = config_get_value(obj->parameters, "enable_root_user");
user = config_get_value(obj->parameters,
"user");
auth = config_get_value(obj->parameters,
"passwd");
version_string = config_get_value(obj->parameters, "version_string");
allow_localhost_match_wildcard_host = config_get_value(obj->parameters, "localhost_match_wildcard_host");
if (version_string) {
if (service->version_string) {
free(service->version_string);
}
service->version_string = strdup(version_string);
}
if (user && auth) {
service_update(service, router,
user,
auth);
if (enable_root_user)
serviceEnableRootUser(service, atoi(enable_root_user));
if (allow_localhost_match_wildcard_host)
serviceEnableLocalhostMatchWildcardHost(
service,
atoi(allow_localhost_match_wildcard_host));
/** Read, validate and set max_slave_connections */
max_slave_conn_str =
config_get_value(
obj->parameters,
"max_slave_connections");
if (max_slave_conn_str != NULL)
{
CONFIG_PARAMETER* param;
bool succp;
param = config_get_param(obj->parameters,
"max_slave_connections");
if (param == NULL)
{
succp = false;
}
else
{
succp = service_set_param_value(
service,
param,
max_slave_conn_str,
COUNT_ATMOST,
(PERCENT_TYPE|COUNT_TYPE));
}
if (!succp && param != NULL)
{
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"* Warning : invalid value type "
"for parameter \'%s.%s = %s\'\n\tExpected "
"type is either <int> for slave connection "
"count or\n\t<int>%% for specifying the "
"maximum percentage of available the "
"slaves that will be connected.",
((SERVICE*)obj->element)->name,
param->name,
param->value)));
}
}
/** Read, validate and set max_slave_replication_lag */
max_slave_rlag_str =
config_get_value(obj->parameters,
"max_slave_replication_lag");
if (max_slave_rlag_str != NULL)
{
CONFIG_PARAMETER* param;
bool succp;
param = config_get_param(
obj->parameters,
"max_slave_replication_lag");
if (param == NULL)
{
succp = false;
}
else
{
succp = service_set_param_value(
service,
param,
max_slave_rlag_str,
COUNT_ATMOST,
COUNT_TYPE);
}
if (!succp)
{
if(param){
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"* Warning : invalid value type "
"for parameter \'%s.%s = %s\'\n\tExpected "
"type is <int> for maximum "
"slave replication lag.",
((SERVICE*)obj->element)->name,
param->name,
param->value)));
}else{
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"Error : parameter was NULL")));
}
}
}
}
obj->element = service;
}
else
{
char *user;
char *auth;
char *enable_root_user;
char *allow_localhost_match_wildcard_host;
enable_root_user =
config_get_value(obj->parameters,
"enable_root_user");
allow_localhost_match_wildcard_host =
config_get_value(obj->parameters, "localhost_match_wildcard_host");
user = config_get_value(obj->parameters,
"user");
auth = config_get_value(obj->parameters,
"passwd");
obj->element = service_alloc(obj->object,
router);
if (obj->element && user && auth)
{
serviceSetUser(obj->element,
user,
auth);
if (enable_root_user)
serviceEnableRootUser(obj->element, atoi(enable_root_user));
if (allow_localhost_match_wildcard_host)
serviceEnableLocalhostMatchWildcardHost(
obj->element,
atoi(allow_localhost_match_wildcard_host));
}
}
}
else
{
obj->element = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : No router defined for service "
"'%s'.",
obj->object)));
}
}
else if (!strcmp(type, "server"))
{
char *address;
char *port;
char *protocol;
char *monuser;
char *monpw;
address = config_get_value(obj->parameters, "address");
port = config_get_value(obj->parameters, "port");
protocol = config_get_value(obj->parameters, "protocol");
monuser = config_get_value(obj->parameters,
"monitoruser");
monpw = config_get_value(obj->parameters, "monitorpw");
if (address && port && protocol)
{
if ((server =
server_find(address, atoi(port))) != NULL)
{
server_update(server,
protocol,
monuser,
monpw);
obj->element = server;
}
else
{
obj->element = server_alloc(address,
protocol,
atoi(port));
if (obj->element && monuser && monpw)
{
serverAddMonUser(obj->element,
monuser,
monpw);
}
}
}
else
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Server '%s' is missing a "
"required "
"configuration parameter. A server must "
"have address, port and protocol "
"defined.",
obj->object)));
}
}
obj = obj->next;
}
/*
* Now we have the services we can add the servers to the services
* add the protocols to the services
*/
obj = context;
while (obj)
{
char *type = config_get_value(obj->parameters, "type");
if (type == NULL)
;
else if (!strcmp(type, "service"))
{
char *servers;
char *roptions;
char *filters;
servers = config_get_value(obj->parameters, "servers");
roptions = config_get_value(obj->parameters,
"router_options");
filters = config_get_value(obj->parameters, "filters");
if (servers && obj->element)
{
char *s = strtok(servers, ",");
while (s)
{
CONFIG_CONTEXT *obj1 = context;
int found = 0;
while (obj1)
{
if (strcmp(trim(s), obj1->object) == 0 &&
obj->element && obj1->element)
{
found = 1;
if (!serviceHasBackend(obj->element, obj1->element))
{
serviceAddBackend(
obj->element,
obj1->element);
}
}
obj1 = obj1->next;
}
if (!found)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Unable to find "
"server '%s' that is "
"configured as part of "
"service '%s'.",
s, obj->object)));
}
s = strtok(NULL, ",");
}
}
if (roptions && obj->element)
{
char *s = strtok(roptions, ",");
serviceClearRouterOptions(obj->element);
while (s)
{
serviceAddRouterOption(obj->element, s);
s = strtok(NULL, ",");
}
}
if (filters && obj->element)
serviceSetFilters(obj->element, filters);
}
else if (!strcmp(type, "listener"))
{
char *service;
char *port;
char *protocol;
char *address;
char *socket;
service = config_get_value(obj->parameters, "service");
address = config_get_value(obj->parameters, "address");
port = config_get_value(obj->parameters, "port");
protocol = config_get_value(obj->parameters, "protocol");
socket = config_get_value(obj->parameters, "socket");
if (service && socket && protocol)
{
CONFIG_CONTEXT *ptr = context;
while (ptr && strcmp(ptr->object, service) != 0)
ptr = ptr->next;
if (ptr &&
ptr->element &&
serviceHasProtocol(ptr->element,
protocol,
0) == 0)
{
serviceAddProtocol(ptr->element,
protocol,
socket,
0);
serviceStartProtocol(ptr->element,
protocol,
0);
}
}
if (service && port && protocol)
{
CONFIG_CONTEXT *ptr = context;
while (ptr && strcmp(ptr->object, service) != 0)
ptr = ptr->next;
if (ptr &&
ptr->element &&
serviceHasProtocol(ptr->element,
protocol,
atoi(port)) == 0)
{
serviceAddProtocol(ptr->element,
protocol,
address,
atoi(port));
serviceStartProtocol(ptr->element,
protocol,
atoi(port));
}
}
}
else if (strcmp(type, "server") != 0 &&
strcmp(type, "monitor") != 0 &&
strcmp(type, "filter") != 0)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Configuration object %s has an invalid "
"type specified.",
obj->object)));
}
obj = obj->next;
}
return 1;
}
static char *service_params[] =
{
"type",
"router",
"router_options",
"servers",
"user",
"passwd",
"enable_root_user",
"localhost_match_wildcard_host",
"max_slave_connections",
"max_slave_replication_lag",
"use_sql_variables_in", /*< rwsplit only */
"version_string",
"filters",
"weightby",
NULL
};
static char *server_params[] =
{
"type",
"address",
"port",
"protocol",
"monitorpw",
"monitoruser",
NULL
};
static char *listener_params[] =
{
"type",
"service",
"protocol",
"port",
"address",
"socket",
NULL
};
static char *monitor_params[] =
{
"type",
"module",
"servers",
"user",
"passwd",
"monitor_interval",
"detect_replication_lag",
"detect_stale_master",
"disable_master_failback",
"backend_connect_timeout",
"backend_read_timeout",
"backend_write_timeout",
NULL
};
/**
* Check the configuration objects have valid parameters
*/
static void
check_config_objects(CONFIG_CONTEXT *context)
{
CONFIG_CONTEXT *obj;
CONFIG_PARAMETER *params;
char *type, **param_set;
int i;
/**
* Process the data and create the services and servers defined
* in the data.
*/
obj = context;
while (obj)
{
param_set = NULL;
if (obj->parameters &&
(type = config_get_value(obj->parameters, "type")))
{
if (!strcmp(type, "service"))
param_set = service_params;
else if (!strcmp(type, "listener"))
param_set = listener_params;
else if (!strcmp(type, "monitor"))
param_set = monitor_params;
}
if (param_set != NULL)
{
params = obj->parameters;
while (params)
{
int found = 0;
for (i = 0; param_set[i]; i++)
if (!strcmp(params->name, param_set[i]))
found = 1;
if (found == 0)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Unexpected parameter "
"'%s' for object '%s' of type "
"'%s'.",
params->name,
obj->object,
type)));
params = params->next;
}
}
obj = obj->next;
}
}
/**
* Set qualified parameter value to CONFIG_PARAMETER struct.
*/
bool config_set_qualified_param(
CONFIG_PARAMETER* param,
void* val,
config_param_type_t type)
{
bool succp;
switch (type) {
case STRING_TYPE:
param->qfd.valstr = strndup((const char *)val, MAX_PARAM_LEN);
succp = true;
break;
case COUNT_TYPE:
param->qfd.valcount = *(int *)val;
succp = true;
break;
case PERCENT_TYPE:
param->qfd.valpercent = *(int *)val;
succp = true;
break;
case BOOL_TYPE:
param->qfd.valbool = *(bool *)val;
succp = true;
break;
case SQLVAR_TARGET_TYPE:
param->qfd.valtarget = *(target_t *)val;
succp = true;
break;
default:
succp = false;
break;
}
if (succp)
{
param->qfd_param_type = type;
}
return succp;
}
/**
* Used for boolean settings where values may be 1, yes or true
* to enable a setting or -, no, false to disable a setting.
*
* @param str String to convert to a boolean
* @return Truth value
*/
static int
config_truth_value(char *str)
{
if (strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0)
{
return 1;
}
if (strcasecmp(str, "flase") == 0 || strcasecmp(str, "off") == 0)
{
return 0;
}
return atoi(str);
}
static char *InternalRouters[] = {
"debugcli",
"cli",
NULL
};
/**
* Determine if the router is one of the special internal services that
* MaxScale offers.
*
* @param router The router name
* @return Non-zero if the router is in the InternalRouters table
*/
static int
internalService(char *router)
{
int i;
if (router)
{
for (i = 0; InternalRouters[i]; i++)
if (strcmp(router, InternalRouters[i]) == 0)
return 1;
}
return 0;
}