Develop merge

Develop merge:
this includes the new handling of events larger than 16MBytes
This commit is contained in:
MassimilianoPinto 2016-12-05 16:38:48 +01:00
commit b2e8a2d8c5
77 changed files with 1178 additions and 862 deletions

View File

@ -14,6 +14,25 @@ options. The `authenticator_options` parameter is supported by listeners
and servers and expects a comma-separated list of key-value pairs. The
following options contain examples on how to define it.
### `skip_authentication`
This option takes a boolean value which controls whether MaxScale will fully
authenticate users. This option is disabled by default.
Disabling authentication in MaxScale will allow MaxScale to act as a security
gateway to the server. The authentication of users is offloaded to the backend
server.
For example, creating the user `jdoe@%` will allow the user _jdoe_ to connect
from any IP address. This can be a problem if all traffic needs to go through
MaxScale. By enabling this option and replacing the user with
`jdoe@maxscale-IP`, the users can still connect from any client IP but will be
forced to go though MaxScale.
```
authenticator_options=skip_authentication=true
```
### `cache_dir`
The location where the user credential cache is stored. The default value

View File

@ -25,14 +25,18 @@ filters=MyLogFilter
The QLA filter accepts the following options.
|Option |Description |
|----------|--------------------------------------------|
|ignorecase|Use case-insensitive matching |
|case |Use case-sensitive matching |
|extended |Use extended regular expression syntax (ERE)|
|session_file| Use session-specific file (default)|
|unified_file| Use one file for all sessions|
|flush_writes| Flush after every write|
Option | Description
-------| -----------
ignorecase | Use case-insensitive matching
case | Use case-sensitive matching
extended | Use extended regular expression syntax (ERE)
session_file | Write to session-specific files (default)
unified_file | Use one file for all sessions
flush_writes | Flush after every write
append | Append log entries instead of overwriting files
print_service | Add service name to log entries
print_session | Add session id to log entries (ignored for session-files)
To use multiple filter options, list them in a comma-separated list. If no file settings are given, default will be used. Multiple file settings can be enabled simultaneously.
```

View File

@ -104,9 +104,7 @@ following new commands were added to maxadmin, see output of `maxadmin help
With these new features, you can start MaxScale without the servers and define
them later.
# Module commands
## Module commands
### Module commands
Introduced in MaxScale 2.1, the module commands are special, module-specific
commands. They allow the modules to expand beyound the capabilities of the
@ -145,6 +143,13 @@ aimed for two node master-slave clusters where the slave can act as a
master in case the original master fails. For more details, please read
the [MySQL Monitor Documentation](../Monitors/MySQL-Monitor.md).
### Permissive authentication mode for MySQLAuth
The MySQL authentication module supports the `skip_authentication` option which
allows authentication to always succedd in MaxScale. This option offloads the
actual authentication to the backend server and it can be used to implement a
secure version of a wildcard user.
## Bug fixes
[Here is a list of bugs fixed since the release of MaxScale 2.0.X.](https://jira.mariadb.org/browse/MXS-739?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%202.0.0)

View File

@ -110,6 +110,7 @@ typedef struct config_context
*/
typedef struct
{
bool config_check; /**< Only check config */
int n_threads; /**< Number of polling threads */
char *version_string; /**< The version string of embedded db library */
char release_string[_RELEASE_STR_LENGTH]; /**< The release name string of the system */
@ -238,7 +239,7 @@ bool config_set_qualified_param(CONFIG_PARAMETER* param,
config_param_type_t type);
int config_threadcount();
int config_truth_value(char *);
void free_config_parameter(CONFIG_PARAMETER* p1);
void config_parameter_free(CONFIG_PARAMETER* p1);
bool is_internal_service(const char *router);
MXS_END_DECLS

View File

@ -109,17 +109,17 @@ typedef struct filter_def
struct filter_def *next; /**< Next filter in the chain of all filters */
} FILTER_DEF;
FILTER_DEF *filter_alloc(char *, char *);
FILTER_DEF *filter_alloc(const char *, const char *);
void filter_free(FILTER_DEF *);
bool filter_load(FILTER_DEF* filter);
FILTER_DEF *filter_find(char *);
void filterAddOption(FILTER_DEF *, char *);
void filterAddParameter(FILTER_DEF *, char *, char *);
DOWNSTREAM *filterApply(FILTER_DEF *, SESSION *, DOWNSTREAM *);
UPSTREAM *filterUpstream(FILTER_DEF *, void *, UPSTREAM *);
int filter_standard_parameter(char *);
FILTER_DEF *filter_find(const char *);
void filter_add_option(FILTER_DEF *, const char *);
void filter_add_parameter(FILTER_DEF *, const char *, const char *);
DOWNSTREAM *filter_apply(FILTER_DEF *, SESSION *, DOWNSTREAM *);
UPSTREAM *filter_upstream(FILTER_DEF *, void *, UPSTREAM *);
int filter_standard_parameter(const char *);
void dprintAllFilters(DCB *);
void dprintFilter(DCB *, FILTER_DEF *);
void dprintFilter(DCB *, const FILTER_DEF *);
void dListFilters(DCB *);
/**

View File

@ -55,14 +55,12 @@ typedef struct
#define MODULECMD_ARG_NONE 0 /**< Empty argument */
#define MODULECMD_ARG_STRING 1 /**< String */
#define MODULECMD_ARG_BOOLEAN 2 /**< Boolean value */
#define MODULECMD_ARG_SERVICE 3 /**< Service name */
#define MODULECMD_ARG_SERVER 4 /**< Server name */
#define MODULECMD_ARG_SESSION 5 /**< SESSION pointer in string format */
#define MODULECMD_ARG_SESSION_PTR 6 /**< Raw SESSION pointer */
#define MODULECMD_ARG_DCB 7 /**< DCB pointer in string format*/
#define MODULECMD_ARG_DCB_PTR 8 /**< Raw DCB pointer*/
#define MODULECMD_ARG_MONITOR 9 /**< Monitor name */
#define MODULECMD_ARG_FILTER 10 /**< Filter name */
#define MODULECMD_ARG_SERVICE 3 /**< Service */
#define MODULECMD_ARG_SERVER 4 /**< Server */
#define MODULECMD_ARG_SESSION 6 /**< Session */
#define MODULECMD_ARG_DCB 8 /**< DCB */
#define MODULECMD_ARG_MONITOR 9 /**< Monitor */
#define MODULECMD_ARG_FILTER 10 /**< Filter */
#define MODULECMD_ARG_OUTPUT 11 /**< DCB suitable for writing results to.
This should always be the first argument
if the function requires an output DCB. */
@ -157,6 +155,20 @@ const MODULECMD* modulecmd_find_command(const char *domain, const char *identifi
/**
* @brief Parse arguments for a command
*
* The argument types expect different forms of input.
*
* | Argument type | Expected input |
* |-----------------------|-------------------|
* | MODULECMD_ARG_SERVICE | Service name |
* | MODULECMD_ARG_SERVER | Server name |
* | MODULECMD_ARG_SESSION | Session unique ID |
* | MODULECMD_ARG_MONITOR | Monitor name   |
* | MODULECMD_ARG_FILTER | Filter name |
* | MODULECMD_ARG_STRING | String |
* | MODULECMD_ARG_BOOLEAN | Boolean value |
* | MODULECMD_ARG_DCB | Raw DCB pointer |
* | MODULECMD_ARG_OUTPUT | DCB for output |
*
* @param cmd Command for which the parameters are parsed
* @param argc Number of arguments
* @param argv Argument list in string format of size @c argc

View File

@ -199,6 +199,7 @@ struct monitor
char *module_name; /**< Name of the monitor module */
void *handle; /**< Handle returned from startMonitor */
size_t interval; /**< The monitor interval */
bool created_online; /**< Whether this monitor was created at runtime */
struct monitor *next; /**< Next monitor in the linked list */
};
@ -209,6 +210,7 @@ extern void monitorAddServer(MONITOR *mon, SERVER *server);
extern void monitorRemoveServer(MONITOR *mon, SERVER *server);
extern void monitorAddUser(MONITOR *, char *, char *);
extern void monitorAddParameters(MONITOR *monitor, CONFIG_PARAMETER *params);
extern bool monitorRemoveParameter(MONITOR *monitor, const char *key);
extern void monitorStop(MONITOR *);
extern void monitorStart(MONITOR *, void*);
extern void monitorStopAll();

View File

@ -242,6 +242,26 @@ SERVICE* service_find(const char *name);
// TODO: Change binlogrouter to use the functions in config_runtime.h
void serviceAddBackend(SERVICE *service, SERVER *server);
/**
* @brief Check if a service uses a server
* @param service Service to check
* @param server Server being used
* @return True if service uses the server
*/
bool serviceHasBackend(SERVICE *service, SERVER *server);
/**
* @brief Check if a service has a listener
*
* @param service Service to check
* @param protocol Listener protocol
* @param address Listener address
* @param port Listener port
* @return True if service has the listener
*/
bool serviceHasListener(SERVICE *service, const char *protocol,
const char* address, unsigned short port);
int serviceGetUser(SERVICE *service, char **user, char **auth);
int serviceSetUser(SERVICE *service, char *user, char *auth);
bool serviceSetFilters(SERVICE *service, char *filters);

View File

@ -204,7 +204,6 @@ typedef struct session
SESSION *session_alloc(struct service *, struct dcb *);
SESSION *session_set_dummy(struct dcb *);
bool session_free(SESSION *);
int session_isvalid(SESSION *);
int session_reply(void *inst, void *session, GWBUF *data);
char *session_get_remote(SESSION *);
@ -339,4 +338,36 @@ static inline bool session_set_autocommit(SESSION* ses, bool autocommit)
return prev_autocommit;
}
/**
* @brief Get a session reference by ID
*
* This creates an additional reference to a session whose unique ID matches @c id.
*
* @param id Unique session ID
* @return Reference to a SESSION or NULL if the session was not found
*
* @note The caller must free the session reference by calling session_put_ref
*/
SESSION* session_get_by_id(int id);
/**
* @brief Get a session reference
*
* This creates an additional reference to a session which allows it to live
* as long as it is needed.
*
* @param session Session reference to get
* @return Reference to a SESSION
*
* @note The caller must free the session reference by calling session_put_ref
*/
SESSION* session_get_ref(SESSION *sessoin);
/**
* @brief Release a session reference
*
* @param session Session reference to release
*/
void session_put_ref(SESSION *session);
MXS_END_DECLS

View File

@ -928,11 +928,23 @@ static uint32_t resolve_query_type(parsing_info_t *pi, THD* thd)
/** System session variable */
case Item_func::GSYSVAR_FUNC:
func_qtype |= QUERY_TYPE_SYSVAR_READ;
MXS_DEBUG("%lu [resolve_query_type] "
"functype GSYSVAR_FUNC, system "
"variable read.",
pthread_self());
{
const char* name = item->name;
if (name &&
((strcasecmp(name, "@@last_insert_id") == 0) ||
(strcasecmp(name, "@@identity") == 0)))
{
func_qtype |= QUERY_TYPE_MASTER_READ;
}
else
{
func_qtype |= QUERY_TYPE_SYSVAR_READ;
}
MXS_DEBUG("%lu [resolve_query_type] "
"functype GSYSVAR_FUNC, system "
"variable read.",
pthread_self());
}
break;
/** User-defined variable read */

View File

@ -833,7 +833,15 @@ static void update_field_infos(QC_SQLITE_INFO* info,
}
else
{
info->types |= QUERY_TYPE_SYSVAR_READ;
if ((strcasecmp(&zToken[2], "identity") == 0) ||
(strcasecmp(&zToken[2], "last_insert_id") == 0))
{
info->types |= QUERY_TYPE_MASTER_READ;
}
else
{
info->types |= QUERY_TYPE_SYSVAR_READ;
}
}
}
else

View File

@ -12,3 +12,6 @@ QUERY_TYPE_BEGIN_TRX
QUERY_TYPE_ROLLBACK
QUERY_TYPE_COMMIT
QUERY_TYPE_SESSION_WRITE
QUERY_TYPE_READ|QUERY_TYPE_MASTER_READ
QUERY_TYPE_READ|QUERY_TYPE_MASTER_READ
QUERY_TYPE_READ|QUERY_TYPE_MASTER_READ

View File

@ -12,3 +12,6 @@ BEGIN;
ROLLBACK;
COMMIT;
use X;
select last_insert_id();
select @@last_insert_id;
select @@identity;

View File

@ -1070,7 +1070,7 @@ return_p2:
* Free a configuration parameter
* @param p1 Parameter to free
*/
void free_config_parameter(CONFIG_PARAMETER* p1)
void config_parameter_free(CONFIG_PARAMETER* p1)
{
while (p1)
{
@ -1089,7 +1089,7 @@ void config_context_free(CONFIG_CONTEXT *context)
while (context)
{
obj = context->next;
free_config_parameter(context->parameters);
config_parameter_free(context->parameters);
MXS_FREE(context->object);
MXS_FREE(context);
context = obj;
@ -1561,6 +1561,7 @@ global_defaults()
{
uint8_t mac_addr[6] = "";
struct utsname uname_data;
gateway.config_check = false;
gateway.n_threads = DEFAULT_NTHREADS;
gateway.n_nbpoll = DEFAULT_NBPOLLS;
gateway.pollsleep = DEFAULT_POLLSLEEP;
@ -3110,7 +3111,7 @@ int create_new_listener(CONFIG_CONTEXT *obj)
SSL_LISTENER *ssl_info = make_ssl_structure(obj, true, &error_count);
if (socket)
{
if (serviceHasProtocol(service, protocol, address, 0))
if (serviceHasListener(service, protocol, address, 0))
{
MXS_ERROR("Listener '%s' for service '%s' already has a socket at '%s.",
obj->object, service_name, socket);
@ -3125,7 +3126,7 @@ int create_new_listener(CONFIG_CONTEXT *obj)
if (port)
{
if (serviceHasProtocol(service, protocol, address, atoi(port)))
if (serviceHasListener(service, protocol, address, atoi(port)))
{
MXS_ERROR("Listener '%s', for service '%s', already have port %s.",
obj->object,
@ -3183,7 +3184,7 @@ int create_new_filter(CONFIG_CONTEXT *obj)
char *s = strtok_r(options, ",", &lasts);
while (s)
{
filterAddOption(obj->element, s);
filter_add_option(obj->element, s);
s = strtok_r(NULL, ",", &lasts);
}
}
@ -3193,7 +3194,7 @@ int create_new_filter(CONFIG_CONTEXT *obj)
{
if (strcmp(params->name, "module") && strcmp(params->name, "options"))
{
filterAddParameter(obj->element, params->name, params->value);
filter_add_parameter(obj->element, params->name, params->value);
}
params = params->next;
}

View File

@ -361,8 +361,27 @@ bool runtime_alter_monitor(MONITOR *monitor, char *key, char *value)
monitorSetNetworkTimeout(monitor, MONITOR_READ_TIMEOUT, ival);
}
}
else
{
/** We're modifying module specific parameters and we need to stop the monitor */
monitorStop(monitor);
if (valid)
if (monitorRemoveParameter(monitor, key) || value[0])
{
/** Either we're removing an existing parameter or adding a new one */
valid = true;
if (value[0])
{
CONFIG_PARAMETER p = {.name = key, .value = value};
monitorAddParameters(monitor, &p);
}
}
monitorStart(monitor, monitor->parameters);
}
if (valid && monitor->created_online)
{
monitor_serialize(monitor);
}
@ -377,8 +396,6 @@ bool runtime_create_listener(SERVICE *service, const char *name, const char *add
const char *ssl_cert, const char *ssl_ca,
const char *ssl_version, const char *ssl_depth)
{
SSL_LISTENER *ssl = NULL;
bool rval = true;
if (addr == NULL || strcasecmp(addr, "default") == 0)
{
@ -407,34 +424,42 @@ bool runtime_create_listener(SERVICE *service, const char *name, const char *add
unsigned short u_port = atoi(port);
if (ssl_key && ssl_cert && ssl_ca)
{
ssl = create_ssl(name, ssl_key, ssl_cert, ssl_ca, ssl_version, ssl_depth);
if (ssl == NULL)
{
MXS_ERROR("SSL initialization for listener '%s' failed.", name);
rval = false;
}
}
spinlock_acquire(&crt_lock);
if (rval)
{
const char *print_addr = addr ? addr : "0.0.0.0";
SERV_LISTENER *listener = serviceCreateListener(service, name, proto, addr,
u_port, auth, auth_opt, ssl);
SSL_LISTENER *ssl = NULL;
bool rval = false;
if (listener && listener_serialize(listener) && serviceLaunchListener(service, listener))
if (!serviceHasListener(service, proto, addr, u_port))
{
rval = true;
if (ssl_key && ssl_cert && ssl_ca)
{
MXS_NOTICE("Listener '%s' at %s:%s for service '%s' created",
name, print_addr, port, service->name);
ssl = create_ssl(name, ssl_key, ssl_cert, ssl_ca, ssl_version, ssl_depth);
if (ssl == NULL)
{
MXS_ERROR("SSL initialization for listener '%s' failed.", name);
rval = false;
}
}
else
if (rval)
{
MXS_ERROR("Failed to start listener '%s' at %s:%s.", name, print_addr, port);
rval = false;
const char *print_addr = addr ? addr : "0.0.0.0";
SERV_LISTENER *listener = serviceCreateListener(service, name, proto, addr,
u_port, auth, auth_opt, ssl);
if (listener && listener_serialize(listener) && serviceLaunchListener(service, listener))
{
MXS_NOTICE("Listener '%s' at %s:%s for service '%s' created",
name, print_addr, port, service->name);
}
else
{
MXS_ERROR("Failed to start listener '%s' at %s:%s.", name, print_addr, port);
rval = false;
}
}
}
@ -495,11 +520,21 @@ bool runtime_create_monitor(const char *name, const char *module)
{
spinlock_acquire(&crt_lock);
bool rval = false;
MONITOR *monitor = monitor_alloc((char*)name, (char*)module);
if (monitor && monitor_serialize(monitor))
if (monitor_find(name) == NULL)
{
rval = true;
MONITOR *monitor = monitor_alloc((char*)name, (char*)module);
if (monitor)
{
/** Mark that this monitor was created after MaxScale was started */
monitor->created_online = true;
if (monitor_serialize(monitor))
{
rval = true;
}
}
}
spinlock_release(&crt_lock);

View File

@ -348,7 +348,7 @@ dcb_final_free(DCB *dcb)
bool is_client_dcb = (DCB_ROLE_CLIENT_HANDLER == dcb->dcb_role ||
DCB_ROLE_INTERNAL == dcb->dcb_role);
session_free(local_session);
session_put_ref(local_session);
if (is_client_dcb)
{
@ -1714,7 +1714,7 @@ dcb_maybe_add_persistent(DCB *dcb)
CHK_SESSION(local_session);
if (SESSION_STATE_DUMMY != local_session->state)
{
session_free(local_session);
session_put_ref(local_session);
}
}
spinlock_acquire(&dcb->cb_lock);

View File

@ -48,22 +48,22 @@ static void filter_free_parameters(FILTER_DEF *filter);
* @return The newly created filter or NULL if an error occured
*/
FILTER_DEF *
filter_alloc(char *name, char *module)
filter_alloc(const char *name, const char *module)
{
name = MXS_STRDUP(name);
module = MXS_STRDUP(module);
char* my_name = MXS_STRDUP(name);
char* my_module = MXS_STRDUP(module);
FILTER_DEF *filter = (FILTER_DEF *)MXS_MALLOC(sizeof(FILTER_DEF));
if (!name || !module || !filter)
if (!my_name || !my_module || !filter)
{
MXS_FREE(name);
MXS_FREE(module);
MXS_FREE(my_name);
MXS_FREE(my_module);
MXS_FREE(filter);
return NULL;
}
filter->name = name;
filter->module = module;
filter->name = my_name;
filter->module = my_module;
filter->filter = NULL;
filter->options = NULL;
filter->obj = NULL;
@ -140,7 +140,7 @@ filter_free(FILTER_DEF *filter)
* @return The server or NULL if not found
*/
FILTER_DEF *
filter_find(char *name)
filter_find(const char *name)
{
FILTER_DEF *filter;
@ -164,7 +164,7 @@ filter_find(char *name)
* @param name Parameter name to check
*/
int
filter_standard_parameter(char *name)
filter_standard_parameter(const char *name)
{
if (strcmp(name, "type") == 0 || strcmp(name, "module") == 0)
{
@ -220,7 +220,7 @@ dprintAllFilters(DCB *dcb)
* to display all active filters in MaxScale
*/
void
dprintFilter(DCB *dcb, FILTER_DEF *filter)
dprintFilter(DCB *dcb, const FILTER_DEF *filter)
{
int i;
@ -287,7 +287,7 @@ dListFilters(DCB *dcb)
* @param option The option string
*/
void
filterAddOption(FILTER_DEF *filter, char *option)
filter_add_option(FILTER_DEF *filter, const char *option)
{
int i;
@ -323,7 +323,7 @@ filterAddOption(FILTER_DEF *filter, char *option)
* @param value The parameter value
*/
void
filterAddParameter(FILTER_DEF *filter, char *name, char *value)
filter_add_parameter(FILTER_DEF *filter, const char *name, const char *value)
{
int i;
@ -344,13 +344,13 @@ filterAddParameter(FILTER_DEF *filter, char *name, char *value)
(i + 2) * sizeof(FILTER_PARAMETER *));
}
FILTER_PARAMETER *parameter = MXS_CALLOC(1, sizeof(FILTER_PARAMETER));
name = MXS_STRDUP(name);
value = MXS_STRDUP(value);
char* my_name = MXS_STRDUP(name);
char* my_value = MXS_STRDUP(value);
MXS_ABORT_IF_TRUE(!parameters || !parameter || !name || !value);
MXS_ABORT_IF_TRUE(!parameters || !parameter || !my_name || !my_value);
parameter->name = name;
parameter->value = value;
parameter->name = my_name;
parameter->value = my_value;
parameters[i] = parameter;
parameters[i + 1] = NULL;
filter->parameters = parameters;
@ -433,7 +433,7 @@ bool filter_load(FILTER_DEF* filter)
* if the filter could not be created
*/
DOWNSTREAM *
filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream)
filter_apply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream)
{
DOWNSTREAM *me;
@ -468,7 +468,7 @@ filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream)
* @return The upstream component for the next filter
*/
UPSTREAM *
filterUpstream(FILTER_DEF *filter, void *fsession, UPSTREAM *upstream)
filter_upstream(FILTER_DEF *filter, void *fsession, UPSTREAM *upstream)
{
UPSTREAM *me = NULL;

View File

@ -1865,12 +1865,7 @@ int main(int argc, char **argv)
goto return_main;
}
if (config_check)
{
MXS_NOTICE("Configuration was successfully verified.");
rc = MAXSCALE_SHUTDOWN;
goto return_main;
}
cnf->config_check = config_check;
if (mysql_library_init(0, NULL, NULL))
{
@ -1918,21 +1913,24 @@ int main(int argc, char **argv)
}
libmysql_initialized = TRUE;
/** Check if a MaxScale process is already running */
if (pid_file_exists())
if (!config_check)
{
/** There is a process with the PID of the maxscale.pid file running.
* Assuming that this is an already running MaxScale process, we
* should exit with an error code. */
rc = MAXSCALE_ALREADYRUNNING;
goto return_main;
}
/** Check if a MaxScale process is already running */
if (pid_file_exists())
{
/** There is a process with the PID of the maxscale.pid file running.
* Assuming that this is an already running MaxScale process, we
* should exit with an error code. */
rc = MAXSCALE_ALREADYRUNNING;
goto return_main;
}
/* Write process pid into MaxScale pidfile */
if (write_pid_file() != 0)
{
rc = MAXSCALE_ALREADYRUNNING;
goto return_main;
/* Write process pid into MaxScale pidfile */
if (write_pid_file() != 0)
{
rc = MAXSCALE_ALREADYRUNNING;
goto return_main;
}
}
/** Initialize statistics */
@ -1961,6 +1959,14 @@ int main(int argc, char **argv)
rc = MAXSCALE_NOSERVICES;
goto return_main;
}
if (config_check)
{
MXS_NOTICE("Configuration was successfully verified.");
rc = MAXSCALE_SHUTDOWN;
goto return_main;
}
/*<
* Start periodic log flusher thread.
*/

View File

@ -68,10 +68,8 @@ SERV_LISTENER* serviceCreateListener(SERVICE *service, const char *name,
const char *protocol, const char *address,
unsigned short port, const char *authenticator,
const char *options, SSL_LISTENER *ssl);
int serviceHasProtocol(SERVICE *service, const char *protocol,
const char* address, unsigned short port);
void serviceRemoveBackend(SERVICE *service, const SERVER *server);
bool serviceHasBackend(SERVICE *service, SERVER *server);
/**
* @brief Serialize a service to a file

View File

@ -277,25 +277,15 @@ static bool process_argument(modulecmd_arg_type_t *type, const void* value,
break;
case MODULECMD_ARG_SESSION:
arg->type.type = MODULECMD_ARG_SESSION;
arg->value.session = (SESSION*)strtol((char*)value, NULL, 0);
rval = true;
break;
case MODULECMD_ARG_SESSION_PTR:
arg->type.type = MODULECMD_ARG_SESSION_PTR;
arg->value.session = (SESSION*)value;
if ((arg->value.session = session_get_by_id(atoi(value))))
{
arg->type.type = MODULECMD_ARG_SESSION;
}
rval = true;
break;
case MODULECMD_ARG_DCB:
arg->type.type = MODULECMD_ARG_DCB;
arg->value.dcb = (DCB*)strtol((char*)value, NULL, 0);
rval = true;
break;
case MODULECMD_ARG_DCB_PTR:
arg->type.type = MODULECMD_ARG_DCB_PTR;
arg->value.dcb = (DCB*)value;
rval = true;
break;
@ -373,6 +363,10 @@ static void free_argument(struct arg_node *arg)
MXS_FREE(arg->value.string);
break;
case MODULECMD_ARG_SESSION:
session_put_ref(arg->value.session);
break;
default:
break;
}
@ -625,18 +619,10 @@ char* modulecmd_argtype_to_str(modulecmd_arg_type_t *type)
strtype = "SESSION";
break;
case MODULECMD_ARG_SESSION_PTR:
strtype = "SESSION_PTR";
break;
case MODULECMD_ARG_DCB:
strtype = "DCB";
break;
case MODULECMD_ARG_DCB_PTR:
strtype = "DCB_PTR";
break;
case MODULECMD_ARG_MONITOR:
strtype = "MONITOR";
break;

View File

@ -104,6 +104,7 @@ monitor_alloc(char *name, char *module)
mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
mon->interval = MONITOR_INTERVAL;
mon->parameters = NULL;
mon->created_online = false;
spinlock_init(&mon->lock);
spinlock_acquire(&monLock);
mon->next = allMonitors;
@ -144,7 +145,7 @@ monitor_free(MONITOR *mon)
}
}
spinlock_release(&monLock);
free_config_parameter(mon->parameters);
config_parameter_free(mon->parameters);
monitor_server_free_all(mon->databases);
MXS_FREE(mon->name);
MXS_FREE(mon->module_name);
@ -396,8 +397,15 @@ void monitorRemoveServer(MONITOR *mon, SERVER *server)
void
monitorAddUser(MONITOR *mon, char *user, char *passwd)
{
snprintf(mon->user, sizeof(mon->user), "%s", user);
snprintf(mon->password, sizeof(mon->password), "%s", passwd);
if (user != mon->user)
{
snprintf(mon->user, sizeof(mon->user), "%s", user);
}
if (passwd != mon->password)
{
snprintf(mon->password, sizeof(mon->password), "%s", passwd);
}
}
/**
@ -767,6 +775,32 @@ void monitorAddParameters(MONITOR *monitor, CONFIG_PARAMETER *params)
}
}
bool monitorRemoveParameter(MONITOR *monitor, const char *key)
{
CONFIG_PARAMETER *prev = NULL;
for (CONFIG_PARAMETER *p = monitor->parameters; p; p = p->next)
{
if (strcmp(p->name, key) == 0)
{
if (p == monitor->parameters)
{
monitor->parameters = monitor->parameters->next;
p->next = NULL;
}
else
{
prev->next = p->next;
p->next = NULL;
}
config_parameter_free(p);
return true;
}
prev = p;
}
return false;
}
/**
* Set a pending status bit in the monitor server
*
@ -1323,6 +1357,7 @@ static bool create_monitor_config(const MONITOR *monitor, const char *filename)
* TODO: Check for return values on all of the dprintf calls
*/
dprintf(file, "[%s]\n", monitor->name);
dprintf(file, "type=monitor\n");
dprintf(file, "module=%s\n", monitor->module_name);
dprintf(file, "user=%s\n", monitor->user);
dprintf(file, "password=%s\n", monitor->password);

View File

@ -478,7 +478,15 @@ int serviceInitialize(SERVICE *service)
if ((service->router_instance = service->router->createInstance(service, router_options)))
{
listeners = serviceStartAllPorts(service);
if (!config_get_global_options()->config_check)
{
listeners = serviceStartAllPorts(service);
}
else
{
/** We're only checking that the configuration is valid */
listeners++;
}
}
else
{
@ -665,7 +673,7 @@ void service_free(SERVICE *service)
MXS_FREE(service->credentials.name);
MXS_FREE(service->credentials.authdata);
free_config_parameter(service->svc_config_param);
config_parameter_free(service->svc_config_param);
serviceClearRouterOptions(service);
MXS_FREE(service);
@ -708,9 +716,9 @@ SERV_LISTENER* serviceCreateListener(SERVICE *service, const char *name, const c
* @param protocol The name of the protocol module
* @param address The address to listen on
* @param port The port to listen on
* @return TRUE if the protocol/port is already part of the service
* @return True if the protocol/port is already part of the service
*/
int serviceHasProtocol(SERVICE *service, const char *protocol,
bool serviceHasListener(SERVICE *service, const char *protocol,
const char* address, unsigned short port)
{
SERV_LISTENER *proto;

View File

@ -336,31 +336,14 @@ session_simple_free(SESSION *session, DCB *dcb)
*
* @param session The session to deallocate
*/
bool
session_free(SESSION *session)
static void session_free(SESSION *session)
{
if (NULL == session || SESSION_STATE_DUMMY == session->state)
{
return true;
}
CHK_SESSION(session);
ss_dassert(session->refcount == 0);
/*
* Remove one reference. If there are no references left,
* free session.
*/
if (atomic_add(&session->refcount, -1) > 1)
{
/* Must be one or more references left */
return false;
}
session->state = SESSION_STATE_TO_BE_FREED;
atomic_add(&session->service->stats.n_current, -1);
/***
*
*/
if (session->client_dcb)
{
dcb_free_all_memory(session->client_dcb);
@ -396,9 +379,7 @@ session_free(SESSION *session)
MXS_FREE(session->filters);
}
MXS_INFO("Stopped %s client session [%lu]",
session->service->name,
session->ses_id);
MXS_INFO("Stopped %s client session [%lu]", session->service->name, session->ses_id);
/** Disable trace and decrease trace logger counter */
session_disable_log_priority(session, LOG_INFO);
@ -409,7 +390,6 @@ session_free(SESSION *session)
session->state = SESSION_STATE_FREE;
session_final_free(session);
}
return true;
}
static void
@ -661,8 +641,8 @@ session_setup_filters(SESSION *session)
MXS_ERROR("Service '%s' contians an unresolved filter.", service->name);
return 0;
}
if ((head = filterApply(service->filters[i], session,
&session->head)) == NULL)
if ((head = filter_apply(service->filters[i], session,
&session->head)) == NULL)
{
MXS_ERROR("Failed to create filter '%s' for "
"service '%s'.\n",
@ -679,9 +659,9 @@ session_setup_filters(SESSION *session)
for (i = 0; i < service->n_filters; i++)
{
if ((tail = filterUpstream(service->filters[i],
session->filters[i].session,
&session->tail)) == NULL)
if ((tail = filter_upstream(service->filters[i],
session->filters[i].session,
&session->tail)) == NULL)
{
MXS_ERROR("Failed to create filter '%s' for service '%s'.",
service->filters[i]->name,
@ -690,7 +670,7 @@ session_setup_filters(SESSION *session)
}
/*
* filterUpstream may simply return the 3 parameter if
* filter_upstream may simply return the 3 parameter if
* the filter has no upstream entry point. So no need
* to copy the contents or free tail in this case.
*/
@ -915,3 +895,47 @@ const char* session_trx_state_to_string(session_trx_state_t state)
MXS_ERROR("Unknown session_trx_state_t value: %d", (int)state);
return "UNKNOWN";
}
static bool ses_find_id(DCB *dcb, void *data)
{
void **params = (void**)data;
SESSION **ses = (SESSION**)params[0];
int *id = (int*)params[1];
bool rval = true;
if (dcb->session->ses_id == *id)
{
*ses = session_get_ref(dcb->session);
rval = false;
}
return rval;
}
SESSION* session_get_by_id(int id)
{
SESSION *session = NULL;
void *params[] = {&session, &id};
dcb_foreach(ses_find_id, params);
return session;
}
SESSION* session_get_ref(SESSION *session)
{
atomic_add(&session->refcount, 1);
return session;
}
void session_put_ref(SESSION *session)
{
if (session && session->state != SESSION_STATE_DUMMY)
{
/** Remove one reference. If there are no references left, free session */
if (atomic_add(&session->refcount, -1) == 1)
{
session_free(session);
}
}
}

View File

@ -83,12 +83,12 @@ test2()
fprintf(stderr, "filter_alloc: test 1 failed.\n");
return 1;
}
filterAddOption(f1, "option1");
filterAddOption(f1, "option2");
filterAddOption(f1, "option3");
filterAddParameter(f1, "name1", "value1");
filterAddParameter(f1, "name2", "value2");
filterAddParameter(f1, "name3", "value3");
filter_add_option(f1, "option1");
filter_add_option(f1, "option2");
filter_add_option(f1, "option3");
filter_add_parameter(f1, "name1", "value1");
filter_add_parameter(f1, "name2", "value2");
filter_add_parameter(f1, "name3", "value3");
return 0;
}

View File

@ -305,16 +305,12 @@ int test_map()
}
static DCB my_dcb;
static SESSION my_session;
bool ptrfn(const MODULECMD_ARG *argv)
{
bool rval = false;
if (argv->argc == 4 && argv->argv[0].value.dcb == &my_dcb &&
argv->argv[1].value.dcb == &my_dcb &&
argv->argv[2].value.session == &my_session &&
argv->argv[3].value.session == &my_session)
if (argv->argc == 1 && argv->argv[0].value.dcb == &my_dcb)
{
rval = true;
}
@ -329,26 +325,18 @@ int test_pointers()
modulecmd_arg_type_t args[] =
{
{MODULECMD_ARG_DCB, ""},
{MODULECMD_ARG_DCB_PTR, ""},
{MODULECMD_ARG_SESSION, ""},
{MODULECMD_ARG_SESSION_PTR, ""}
{MODULECMD_ARG_DCB, ""}
};
TEST(modulecmd_register_command(ns, id, ptrfn, 4, args), "Registering a command should succeed");
TEST(modulecmd_register_command(ns, id, ptrfn, 1, args), "Registering a command should succeed");
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");
const MODULECMD *cmd = modulecmd_find_command(ns, id);
TEST(cmd, "The registered command should be found");
char dcb_str[200];
char session_str[200];
sprintf(dcb_str, "%p", &my_dcb);
sprintf(session_str, "%p", &my_session);
const void* params[] = {&my_dcb};
const void* params[] = {dcb_str, &my_dcb, session_str, &my_session};
MODULECMD_ARG *arg = modulecmd_arg_parse(cmd, 4, params);
MODULECMD_ARG *arg = modulecmd_arg_parse(cmd, 1, params);
TEST(arg, "Parsing arguments should succeed");
TEST(strlen(modulecmd_get_error()) == 0, "Error message should be empty");

View File

@ -72,7 +72,7 @@ test1()
ss_info_dassert(serviceCreateListener(service, "TestProtocol", "testprotocol",
"localhost", 9876, "MySQLAuth", NULL, NULL),
"Add Protocol should succeed");
ss_info_dassert(0 != serviceHasProtocol(service, "testprotocol", "localhost", 9876),
ss_info_dassert(0 != serviceHasListener(service, "testprotocol", "localhost", 9876),
"Service should have new protocol as requested");
return 0;

View File

@ -0,0 +1,6 @@
if(BUILD_CDC)
add_library(CDCPlainAuth SHARED cdc_plain_auth.c)
target_link_libraries(CDCPlainAuth maxscale-common)
set_target_properties(CDCPlainAuth PROPERTIES VERSION "1.0.0")
install_module(CDCPlainAuth core)
endif()

View File

@ -1,49 +1,11 @@
add_subdirectory(CDCPlainAuth)
add_subdirectory(GSSAPI)
add_subdirectory(HTTPAuth)
add_subdirectory(MaxAdminAuth)
add_subdirectory(MySQLAuth)
add_library(MySQLBackendAuth SHARED mysql_backend_auth.c)
target_link_libraries(MySQLBackendAuth maxscale-common MySQLCommon)
set_target_properties(MySQLBackendAuth PROPERTIES VERSION "1.0.0")
install_module(MySQLBackendAuth core)
if (GSSAPI_FOUND AND SQLITE_FOUND)
if (NOT SQLITE_VERSION VERSION_LESS "3.7.7")
include_directories(${GSSAPI_INCS})
include_directories(${SQLITE_INCLUDE_DIR})
add_library(GSSAPIAuth SHARED gssapi_auth.c gssapi_auth_common.c)
target_link_libraries(GSSAPIAuth maxscale-common ${GSSAPI_LIBS} ${SQLITE_LIBRARIES} MySQLCommon)
set_target_properties(GSSAPIAuth PROPERTIES VERSION "1.0.0")
install_module(GSSAPIAuth core)
add_library(GSSAPIBackendAuth SHARED gssapi_backend_auth.c gssapi_auth_common.c)
target_link_libraries(GSSAPIBackendAuth maxscale-common ${GSSAPI_LIBS} MySQLCommon)
set_target_properties(GSSAPIBackendAuth PROPERTIES VERSION "1.0.0")
install_module(GSSAPIBackendAuth core)
else()
message(STATUS "Minimum requires SQLite version for GSSAPIAuth is 3.7.7, current SQLite version is ${SQLITE_VERSION}")
endif()
endif()
add_library(NullAuthAllow SHARED null_auth_allow.c)
target_link_libraries(NullAuthAllow maxscale-common)
set_target_properties(NullAuthAllow PROPERTIES VERSION "1.0.0")
install_module(NullAuthAllow core)
add_library(NullAuthDeny SHARED null_auth_deny.c)
target_link_libraries(NullAuthDeny maxscale-common)
set_target_properties(NullAuthDeny PROPERTIES VERSION "1.0.0")
install_module(NullAuthDeny core)
add_library(MaxAdminAuth SHARED max_admin_auth.c)
target_link_libraries(MaxAdminAuth maxscale-common)
set_target_properties(MaxAdminAuth PROPERTIES VERSION "1.0.0")
install_module(MaxAdminAuth core)
add_library(HTTPAuth SHARED http_auth.c)
target_link_libraries(HTTPAuth maxscale-common)
set_target_properties(HTTPAuth PROPERTIES VERSION "1.0.0")
install_module(HTTPAuth core)
add_subdirectory(MySQLBackendAuth)
add_subdirectory(NullAuthAllow)
add_subdirectory(NullAuthDeny)
# if(BUILD_TESTS)
# add_library(testprotocol SHARED testprotocol.c)
@ -51,10 +13,4 @@ install_module(HTTPAuth core)
# target_link_libraries(testprotocol maxscale-common)
# install_module(testprotocol core)
# endif()
if(BUILD_CDC)
add_library(CDCPlainAuth SHARED cdc_plain_auth.c)
target_link_libraries(CDCPlainAuth maxscale-common)
set_target_properties(CDCPlainAuth PROPERTIES VERSION "1.0.0")
install_module(CDCPlainAuth core)
endif()

View File

@ -0,0 +1,11 @@
if (GSSAPI_FOUND AND SQLITE_FOUND)
if (NOT SQLITE_VERSION VERSION_LESS "3.7.7")
include_directories(${GSSAPI_INCS})
include_directories(${SQLITE_INCLUDE_DIR})
add_subdirectory(GSSAPIAuth)
add_subdirectory(GSSAPIBackendAuth)
else()
message(STATUS "Minimum requires SQLite version for GSSAPIAuth is 3.7.7, current SQLite version is ${SQLITE_VERSION}")
endif()
endif()

View File

@ -0,0 +1,4 @@
add_library(GSSAPIAuth SHARED gssapi_auth.c ../gssapi_auth_common.c)
target_link_libraries(GSSAPIAuth maxscale-common ${GSSAPI_LIBS} ${SQLITE_LIBRARIES} MySQLCommon)
set_target_properties(GSSAPIAuth PROPERTIES VERSION "1.0.0")
install_module(GSSAPIAuth core)

View File

@ -19,7 +19,7 @@
#include <maxscale/secrets.h>
#include <maxscale/mysql_utils.h>
#include <sqlite3.h>
#include "gssapi_auth.h"
#include "../gssapi_auth.h"
/** Default timeout is one minute */
#define MXS_SQLITE_BUSY_TIMEOUT 60000

View File

@ -0,0 +1,4 @@
add_library(GSSAPIBackendAuth SHARED gssapi_backend_auth.c ../gssapi_auth_common.c)
target_link_libraries(GSSAPIBackendAuth maxscale-common ${GSSAPI_LIBS} MySQLCommon)
set_target_properties(GSSAPIBackendAuth PROPERTIES VERSION "1.0.0")
install_module(GSSAPIBackendAuth core)

View File

@ -16,7 +16,7 @@
#include <maxscale/dcb.h>
#include <maxscale/log_manager.h>
#include <maxscale/protocol/mysql.h>
#include "gssapi_auth.h"
#include "../gssapi_auth.h"
/**
* @file gssapi_backend_auth.c - GSSAPI backend authenticator

View File

@ -0,0 +1,4 @@
add_library(HTTPAuth SHARED http_auth.c)
target_link_libraries(HTTPAuth maxscale-common)
set_target_properties(HTTPAuth PROPERTIES VERSION "1.0.0")
install_module(HTTPAuth core)

View File

@ -0,0 +1,4 @@
add_library(MaxAdminAuth SHARED max_admin_auth.c)
target_link_libraries(MaxAdminAuth maxscale-common)
set_target_properties(MaxAdminAuth PROPERTIES VERSION "1.0.0")
install_module(MaxAdminAuth core)

View File

@ -39,6 +39,7 @@ typedef struct mysql_auth
{
char *cache_dir; /**< Custom cache directory location */
bool inject_service_user; /**< Inject the service user into the list of users */
bool skip_auth; /**< Authentication will always be successful */
} MYSQL_AUTH;
@ -144,6 +145,7 @@ static void* mysql_auth_init(char **options)
bool error = false;
instance->cache_dir = NULL;
instance->inject_service_user = true;
instance->skip_auth = false;
for (int i = 0; options[i]; i++)
{
@ -165,6 +167,10 @@ static void* mysql_auth_init(char **options)
{
instance->inject_service_user = config_truth_value(value);
}
else if (strcmp(options[i], "skip_authentication") == 0)
{
instance->skip_auth = config_truth_value(value);
}
else
{
MXS_ERROR("Unknown authenticator option: %s", options[i]);
@ -248,17 +254,21 @@ mysql_auth_authenticate(DCB *dcb)
auth_ret = combined_auth_check(dcb, client_data->auth_token, client_data->auth_token_len,
protocol, client_data->user, client_data->client_sha1, client_data->db);
MYSQL_AUTH *instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
/* On failed authentication try to load user table from backend database */
/* Success for service_refresh_users returns 0 */
if (MXS_AUTH_SUCCEEDED != auth_ret && 0 == service_refresh_users(dcb->service))
if (MXS_AUTH_SUCCEEDED != auth_ret && !instance->skip_auth &&
0 == service_refresh_users(dcb->service))
{
auth_ret = combined_auth_check(dcb, client_data->auth_token, client_data->auth_token_len, protocol,
client_data->user, client_data->client_sha1, client_data->db);
}
/* on successful authentication, set user into dcb field */
if (MXS_AUTH_SUCCEEDED == auth_ret)
if (MXS_AUTH_SUCCEEDED == auth_ret || instance->skip_auth)
{
auth_ret = MXS_AUTH_SUCCEEDED;
dcb->user = MXS_STRDUP_A(client_data->user);
/** Send an OK packet to the client */
}

View File

@ -0,0 +1,4 @@
add_library(MySQLBackendAuth SHARED mysql_backend_auth.c)
target_link_libraries(MySQLBackendAuth maxscale-common MySQLCommon)
set_target_properties(MySQLBackendAuth PROPERTIES VERSION "1.0.0")
install_module(MySQLBackendAuth core)

View File

@ -0,0 +1,4 @@
add_library(NullAuthAllow SHARED null_auth_allow.c)
target_link_libraries(NullAuthAllow maxscale-common)
set_target_properties(NullAuthAllow PROPERTIES VERSION "1.0.0")
install_module(NullAuthAllow core)

View File

@ -0,0 +1,4 @@
add_library(NullAuthDeny SHARED null_auth_deny.c)
target_link_libraries(NullAuthDeny maxscale-common)
set_target_properties(NullAuthDeny PROPERTIES VERSION "1.0.0")
install_module(NullAuthDeny core)

View File

@ -74,6 +74,7 @@ static int routeQuery(FILTER* pInstance, void* pSessionData, GWBUF* pPacket
static int clientReply(FILTER* pInstance, void* pSessionData, GWBUF* pPacket);
static void diagnostics(FILTER* pInstance, void* pSessionData, DCB* pDcb);
static uint64_t getCapabilities(void);
static void destroyInstance(FILTER* pInstance);
static bool process_params(char **pzOptions, FILTER_PARAMETER **ppParams, CACHE_CONFIG& config);
@ -121,7 +122,7 @@ extern "C" FILTER_OBJECT *GetModuleObject()
clientReply,
diagnostics,
getCapabilities,
NULL, // destroyInstance
destroyInstance,
};
return &object;
@ -301,7 +302,6 @@ static void diagnostics(FILTER* pInstance, void* pSessionData, DCB* pDcb)
CPP_GUARD(pSessionCache->diagnostics(pDcb));
}
/**
* Capability routine.
*
@ -312,6 +312,19 @@ static uint64_t getCapabilities(void)
return RCAP_TYPE_TRANSACTION_TRACKING;
}
/**
* Destroy the filter instance.
*
* @param pInstance The filter instance.
*/
static void destroyInstance(FILTER* pInstance)
{
MXS_NOTICE("Deleting Cache filter instance.");
CACHE_FILTER* pFilter = reinterpret_cast<CACHE_FILTER*>(pInstance);
delete pFilter;
}
//
// API Implementation END
//

View File

@ -38,6 +38,14 @@ class StorageFactory;
#define CACHE_DEBUG_MIN CACHE_DEBUG_NONE
#define CACHE_DEBUG_MAX (CACHE_DEBUG_RULES | CACHE_DEBUG_USAGE | CACHE_DEBUG_DECISIONS)
#if !defined(UINT32_MAX)
#define UINT32_MAX (4294967295U)
#endif
#if !defined(UINT64_MAX)
#define UINT64_MAX (18446744073709551615UL)
#endif
// Count
#define CACHE_DEFAULT_MAX_RESULTSET_ROWS UINT32_MAX
// Bytes

View File

@ -41,14 +41,14 @@ CACHE_STORAGE* createInstance(cache_thread_model_t model,
if (max_count != 0)
{
MXS_WARNING("A maximum item count of %" PRIu32 " specified, although 'storage_inMemory' "
"does not enforce such a limit.", max_count);
MXS_WARNING("A maximum item count of %u specified, although 'storage_inMemory' "
"does not enforce such a limit.", (unsigned int)max_count);
}
if (max_size != 0)
{
MXS_WARNING("A maximum size of %" PRIu64 " specified, although 'storage_inMemory' "
"does not enforce such a limit.", max_size);
MXS_WARNING("A maximum size of %lu specified, although 'storage_inMemory' "
"does not enforce such a limit.", (unsigned long)max_size);
}
try

View File

@ -40,14 +40,14 @@ CACHE_STORAGE* createInstance(cache_thread_model_t, // Ignored, RocksDB always M
if (maxCount != 0)
{
MXS_WARNING("A maximum item count of %" PRIu32 " specifed, although 'storage_rocksdb' "
"does not enforce such a limit.", maxCount);
MXS_WARNING("A maximum item count of %u specifed, although 'storage_rocksdb' "
"does not enforce such a limit.", (unsigned int)maxCount);
}
if (maxSize != 0)
{
MXS_WARNING("A maximum size of %" PRIu64 " specified, although 'storage_rocksdb' "
"does not enforce such a limit.", maxSize);
MXS_WARNING("A maximum size of %lu specified, although 'storage_rocksdb' "
"does not enforce such a limit.", (unsigned long)maxSize);
}
try

View File

@ -70,7 +70,7 @@ version()
}
/**
* The module initialisation routine, called when the module
* The module initialization routine, called when the module
* is first loaded.
* @see function load_module in load_utils.c for explanation of lint
*/
@ -205,7 +205,7 @@ setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
/**
* The routeQuery entry point. This is passed the query buffer
* to which the filter should be applied. Once applied the
* query shoudl normally be passed to the downstream component
* query should normally be passed to the downstream component
* (filter or router) in the filter chain.
*
* @param instance The filter instance data
@ -216,51 +216,18 @@ static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
HINT_SESSION *my_session = (HINT_SESSION *)session;
char *ptr;
int rval, len, residual;
HINT *hint;
if (my_session->request == NULL)
if (modutil_is_SQL(queue))
{
/*
* No stored buffer, so this must be the first
* buffer of a new request.
*/
if (modutil_MySQL_Query(queue, &ptr, &len, &residual) == 0)
{
return my_session->down.routeQuery(
my_session->down.instance,
my_session->down.session, queue);
}
my_session->request = queue;
my_session->query_len = len;
my_session->request = NULL;
my_session->query_len = 0;
HINT *hint = hint_parser(my_session, queue);
queue->hint = hint;
}
else
{
gwbuf_append(my_session->request, queue);
}
if (gwbuf_length(my_session->request) < my_session->query_len)
{
/*
* We have not got the entire SQL text, buffer and wait for
* the remainder.
*/
return 1;
}
/* We have the entire SQL text, parse for hints and attach to the
* buffer at the head of the queue.
*/
queue = my_session->request;
my_session->request = NULL;
my_session->query_len = 0;
hint = hint_parser(my_session, queue);
queue->hint = hint;
/* Now process the request */
rval = my_session->down.routeQuery(my_session->down.instance,
return my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
return rval;
}
/**
@ -289,5 +256,5 @@ diagnostic(FILTER *instance, void *fsession, DCB *dcb)
*/
static uint64_t getCapabilities(void)
{
return RCAP_TYPE_STMT_INPUT;
return RCAP_TYPE_CONTIGUOUS_INPUT;
}

View File

@ -48,6 +48,7 @@
#include <string.h>
#include <maxscale/atomic.h>
#include <maxscale/alloc.h>
#include <maxscale/service.h>
MODULE_INFO info =
{
@ -59,13 +60,23 @@ MODULE_INFO info =
static char *version_str = "V1.1.1";
/** Formatting buffer size */
#define QLA_STRING_BUFFER_SIZE 1024
/** Date string buffer size */
#define QLA_DATE_BUFFER_SIZE 20
/** Log file settings flags */
/** Log file save mode flags */
#define CONFIG_FILE_SESSION (1 << 0) // Default value, session specific files
#define CONFIG_FILE_UNIFIED (1 << 1) // One file shared by all sessions
/* Flags for controlling extra log entry contents. The default items are
* always on, for now.
*/
#define LOG_DATA_SERVICE (1 << 0)
#define LOG_DATA_SESSION (1 << 1)
#define LOG_DATA_DATE (1 << 2)
#define LOG_DATA_USER (1 << 3)
#define LOG_DATA_QUERY (1 << 4)
#define LOG_DATA_DEFAULT (LOG_DATA_DATE | LOG_DATA_USER | LOG_DATA_QUERY)
/*
* The filter entry points
*/
@ -105,17 +116,21 @@ static FILTER_OBJECT MyObject =
typedef struct
{
int sessions; /* The count of sessions */
char *name; /* Filter definition name */
char *filebase; /* The filename base */
char *source; /* The source of the client connection */
char *userName; /* The user name to filter on */
char *source; /* The source of the client connection to filter on */
char *user_name; /* The user name to filter on */
char *match; /* Optional text to match against */
regex_t re; /* Compiled regex text */
char *nomatch; /* Optional text to match against for exclusion */
regex_t nore; /* Compiled regex nomatch text */
uint32_t log_mode_flags; /* Log file mode settings */
uint32_t log_file_data_flags; /* What data is saved to the files */
FILE *unified_fp; /* Unified log file. The pointer needs to be shared here
* to avoid garbled printing. */
bool flush_writes; /* Flush log file after every write */
bool flush_writes; /* Flush log file after every write? */
bool append; /* Open files in append-mode? */
bool write_warning_given; /* To make sure some warning are only given once */
} QLA_INSTANCE;
/**
@ -128,15 +143,20 @@ typedef struct
*/
typedef struct
{
DOWNSTREAM down;
char *filename;
FILE *fp;
int active;
char *user;
DOWNSTREAM down;
char *filename; /* The session-specific log file name */
FILE *fp; /* The session-specific log file */
char *remote;
size_t ses_id; /* The session this filter serves */
char *service; /* The service name this filter is attached to. Not owned. */
size_t ses_id; /* The session this filter serves */
char *user; /* The client */
} QLA_SESSION;
static FILE* open_log_file(uint32_t, QLA_INSTANCE *, const char *);
static int write_log_entry(uint32_t, FILE*, QLA_INSTANCE*, QLA_SESSION*, const char*,
const char*, size_t);
/**
* Implementation of the mandatory version entry point
*
@ -191,14 +211,18 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
if (my_instance)
{
my_instance->source = NULL;
my_instance->userName = NULL;
my_instance->match = NULL;
my_instance->nomatch = NULL;
my_instance->append = false;
my_instance->filebase = NULL;
my_instance->log_mode_flags = 0;
my_instance->unified_fp = NULL;
my_instance->flush_writes = false;
my_instance->log_file_data_flags = LOG_DATA_DEFAULT;
my_instance->log_mode_flags = 0;
my_instance->match = NULL;
my_instance->name = MXS_STRDUP(name);
my_instance->nomatch = NULL;
my_instance->source = NULL;
my_instance->unified_fp = NULL;
my_instance->user_name = NULL;
my_instance->write_warning_given = false;
bool error = false;
if (params)
@ -219,7 +243,7 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
}
else if (!strcmp(params[i]->name, "user"))
{
my_instance->userName = MXS_STRDUP_A(params[i]->value);
my_instance->user_name = MXS_STRDUP_A(params[i]->value);
}
else if (!strcmp(params[i]->name, "filebase"))
{
@ -264,6 +288,18 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
{
my_instance->flush_writes = true;
}
else if (!strcasecmp(options[i], "append"))
{
my_instance->append = true;
}
else if (!strcasecmp(options[i], "print_service"))
{
my_instance->log_file_data_flags |= LOG_DATA_SERVICE;
}
else if (!strcasecmp(options[i], "print_session"))
{
my_instance->log_file_data_flags |= LOG_DATA_SESSION;
}
else
{
MXS_ERROR("qlafilter: Unsupported option '%s'.",
@ -305,8 +341,8 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
error = true;
}
// Try to open the unified log file
if (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED &&
my_instance->filebase != NULL)
if (!error && (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED) &&
(my_instance->filebase != NULL))
{
// First calculate filename length
const char UNIFIED[] = ".unified";
@ -316,7 +352,9 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
{
snprintf(filename, namelen, "%s.unified", my_instance->filebase);
// Open the file. It is only closed at program exit
my_instance->unified_fp = fopen(filename, "w");
my_instance->unified_fp = open_log_file(my_instance->log_file_data_flags,
my_instance, filename);
if (my_instance->unified_fp == NULL)
{
char errbuf[MXS_STRERROR_BUFLEN];
@ -353,7 +391,7 @@ createInstance(const char *name, char **options, FILTER_PARAMETER **params)
}
MXS_FREE(my_instance->filebase);
MXS_FREE(my_instance->source);
MXS_FREE(my_instance->userName);
MXS_FREE(my_instance->user_name);
MXS_FREE(my_instance);
my_instance = NULL;
}
@ -392,8 +430,8 @@ newSession(FILTER *instance, SESSION *session)
if ((my_instance->source && remote &&
strcmp(remote, my_instance->source)) ||
(my_instance->userName && userName &&
strcmp(userName, my_instance->userName)))
(my_instance->user_name && userName &&
strcmp(userName, my_instance->user_name)))
{
my_session->active = 0;
}
@ -401,6 +439,7 @@ newSession(FILTER *instance, SESSION *session)
my_session->user = userName;
my_session->remote = remote;
my_session->ses_id = session->ses_id;
my_session->service = session->service->name;
sprintf(my_session->filename, "%s.%lu",
my_instance->filebase,
@ -410,9 +449,11 @@ newSession(FILTER *instance, SESSION *session)
atomic_add(&(my_instance->sessions), 1);
// Only open the session file if the corresponding mode setting is used
if (my_session->active && (my_instance->log_mode_flags | CONFIG_FILE_SESSION))
if (my_session->active && (my_instance->log_mode_flags & CONFIG_FILE_SESSION))
{
my_session->fp = fopen(my_session->filename, "w");
uint32_t data_flags = (my_instance->log_file_data_flags &
~LOG_DATA_SESSION); // No point printing "Session"
my_session->fp = open_log_file(data_flags, my_instance, my_session->filename);
if (my_session->fp == NULL)
{
@ -496,21 +537,21 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
QLA_INSTANCE *my_instance = (QLA_INSTANCE *) instance;
QLA_SESSION *my_session = (QLA_SESSION *) session;
char *ptr;
char *ptr = NULL;
int length = 0;
struct tm t;
struct timeval tv;
if (my_session->active)
{
if ((ptr = modutil_get_SQL(queue)) != NULL)
if (modutil_extract_SQL(queue, &ptr, &length))
{
if ((my_instance->match == NULL ||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
(my_instance->nomatch == NULL ||
regexec(&my_instance->nore, ptr, 0, NULL, 0) != 0))
{
char buffer[QLA_STRING_BUFFER_SIZE];
char buffer[QLA_DATE_BUFFER_SIZE];
gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &t);
strftime(buffer, sizeof(buffer), "%F %T", &t);
@ -519,28 +560,39 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
* Loop over all the possible log file modes and write to
* the enabled files.
*/
char *sql_string = trim(squeeze_whitespace(ptr));
char *sql_string = ptr;
bool write_error = false;
if (my_instance->log_mode_flags & CONFIG_FILE_SESSION)
{
fprintf(my_session->fp, "%s,%s@%s,%s\n", buffer, my_session->user,
my_session->remote, sql_string);
if (my_instance->flush_writes)
// In this case there is no need to write the session
// number into the files.
uint32_t data_flags = (my_instance->log_file_data_flags &
~LOG_DATA_SESSION);
if (write_log_entry(data_flags, my_session->fp,
my_instance, my_session, buffer, sql_string, length) < 0)
{
fflush(my_session->fp);
write_error = true;
}
}
if (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED)
{
fprintf(my_instance->unified_fp, "S%zd,%s,%s@%s,%s\n",
my_session->ses_id, buffer, my_session->user,
my_session->remote, sql_string);
if (my_instance->flush_writes)
uint32_t data_flags = my_instance->log_file_data_flags;
if (write_log_entry(data_flags, my_instance->unified_fp,
my_instance, my_session, buffer, sql_string, length) < 0)
{
fflush(my_instance->unified_fp);
write_error = true;
}
}
if (write_error && !my_instance->write_warning_given)
{
MXS_ERROR("qla-filter '%s': Log file write failed. "
"Suppressing further similar warnings.",
my_instance->name);
my_instance->write_warning_given = true;
}
}
MXS_FREE(ptr);
}
}
/* Pass the query downstream */
@ -575,10 +627,10 @@ diagnostic(FILTER *instance, void *fsession, DCB *dcb)
dcb_printf(dcb, "\t\tLimit logging to connections from %s\n",
my_instance->source);
}
if (my_instance->userName)
if (my_instance->user_name)
{
dcb_printf(dcb, "\t\tLimit logging to user %s\n",
my_instance->userName);
my_instance->user_name);
}
if (my_instance->match)
{
@ -601,3 +653,244 @@ static uint64_t getCapabilities(void)
{
return RCAP_TYPE_CONTIGUOUS_INPUT;
}
/**
* Open the log file and print a header if appropriate.
* @param data_flags Data save settings flags
* @param instance The filter instance
* @param filename Target file path
* @return A valid file on success, null otherwise.
*/
static FILE* open_log_file(uint32_t data_flags, QLA_INSTANCE *instance, const char *filename)
{
bool file_existed = false;
FILE *fp = NULL;
if (instance->append == false)
{
// Just open the file (possibly overwriting) and then print header.
fp = fopen(filename, "w");
}
else
{
// Using fopen() with 'a+' means we will always write to the end but can read
// anywhere. Depending on the "append"-setting the file has been
// opened in different modes, which should be considered if file handling
// changes later (e.g. rewinding).
if ((fp = fopen(filename, "a+")) != NULL)
{
// Check to see if file already has contents
fseek(fp, 0, SEEK_END);
if (ftell(fp) > 0) // Any errors in ftell cause overwriting
{
file_existed = true;
}
}
}
if (fp && !file_existed)
{
// Print a header. Luckily, we know the header has limited length
const char SERVICE[] = "Service,";
const char SESSION[] = "Session,";
const char DATE[] = "Date,";
const char USERHOST[] = "User@Host,";
const char QUERY[] = "Query,";
const int headerlen = sizeof(SERVICE) + sizeof(SERVICE) + sizeof(DATE) +
sizeof(USERHOST) + sizeof(QUERY);
char print_str[headerlen];
memset(print_str, '\0', headerlen);
char *current_pos = print_str;
if (instance->log_file_data_flags & LOG_DATA_SERVICE)
{
strcat(current_pos, SERVICE);
current_pos += sizeof(SERVICE) - 1;
}
if (instance->log_file_data_flags & LOG_DATA_SESSION)
{
strcat(current_pos, SESSION);
current_pos += sizeof(SERVICE) - 1;
}
if (instance->log_file_data_flags & LOG_DATA_DATE)
{
strcat(current_pos, DATE);
current_pos += sizeof(DATE) - 1;
}
if (instance->log_file_data_flags & LOG_DATA_USER)
{
strcat(current_pos, USERHOST);
current_pos += sizeof(USERHOST) - 1;
}
if (instance->log_file_data_flags & LOG_DATA_QUERY)
{
strcat(current_pos, QUERY);
current_pos += sizeof(QUERY) - 1;
}
if (current_pos > print_str)
{
// Overwrite the last ','.
*(current_pos - 1) = '\n';
}
else
{
// Nothing to print
return fp;
}
// Finally, write the log header.
int written = fprintf(fp, "%s", print_str);
if ((written <= 0) ||
((instance->flush_writes) && (fflush(fp) < 0)))
{
// Weird error, file opened but a write failed. Best to stop.
fclose(fp);
MXS_ERROR("qlafilter: Failed to print header to file %s.", filename);
return NULL;
}
}
return fp;
}
/**
* Write an entry to the log file.
* @param data_flags Controls what to write
* @param logfile Target file
* @param instance Filter instance
* @param session Filter session
* @param time_string Date entry
* @param sql_string SQL-query, not NULL terminated!
* @param sql_str_len Length of SQL-string
* @return The number of characters written, or a negative value on failure
*/
static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *instance,
QLA_SESSION *session, const char *time_string, const char *sql_string,
size_t sql_str_len)
{
ss_dassert(logfile != NULL);
size_t print_len = 0;
// First calculate an upper limit for the total length. The strlen()-calls
// could be removed if the values would be saved into the instance or session
// or if we had some reasonable max lengths. (Apparently there are max lengths
// but they are much higher than what is typically needed)
// The numbers have some extra for delimiters.
if (data_flags & LOG_DATA_SERVICE)
{
print_len += strlen(session->service) + 1;
}
if (data_flags & LOG_DATA_SESSION)
{
print_len += 20; // To print a 64bit integer
}
if (data_flags & LOG_DATA_DATE)
{
print_len += QLA_DATE_BUFFER_SIZE + 1;
}
if (data_flags & LOG_DATA_USER)
{
print_len += strlen(session->user) + strlen(session->remote) + 2;
}
if (data_flags & LOG_DATA_QUERY)
{
print_len += sql_str_len + 1; // Can't use strlen, not null-terminated
}
if (print_len == 0)
{
return 0; // Nothing to print
}
// Allocate space for a buffer. Printing to the file in parts would likely
// cause garbled printing if several threads write simultaneously, so we
// have to first print to a string.
char *print_str = NULL;
if ((print_str = MXS_CALLOC(print_len, sizeof(char))) == NULL)
{
return -1;
}
bool error = false;
char *current_pos = print_str;
int rval = 0;
if (!error && (data_flags & LOG_DATA_SERVICE))
{
if ((rval = sprintf(current_pos, "%s,", session->service)) < 0)
{
error = true;
}
else
{
current_pos += rval;
}
}
if (!error && (data_flags & LOG_DATA_SESSION))
{
if ((rval = sprintf(current_pos, "%lu,", session->ses_id)) < 0)
{
error = true;
}
else
{
current_pos += rval;
}
}
if (!error && (data_flags & LOG_DATA_DATE))
{
if ((rval = sprintf(current_pos, "%s,", time_string)) < 0)
{
error = true;
}
else
{
current_pos += rval;
}
}
if (!error && (data_flags & LOG_DATA_USER))
{
if ((rval = sprintf(current_pos, "%s@%s,", session->user, session->remote)) < 0)
{
error = true;
}
else
{
current_pos += rval;
}
}
if (!error && (data_flags & LOG_DATA_QUERY))
{
strncat(current_pos, sql_string, sql_str_len); // non-null-terminated string
current_pos += sql_str_len + 1; // +1 to move to the next char after
}
if (error || current_pos <= print_str)
{
MXS_FREE(print_str);
MXS_ERROR("qlafilter ('%s'): Failed to format log event.", instance->name);
return -1;
}
else
{
// Overwrite the last ','. The rest is already filled with 0.
*(current_pos - 1) = '\n';
}
// Finally, write the log event.
int written = fprintf(logfile, "%s", print_str);
MXS_FREE(print_str);
if ((!instance->flush_writes) || (written <= 0))
{
return written;
}
else
{
// Try flushing. If successful, still return the characters written.
int rval = fflush(logfile);
if (rval >= 0)
{
return written;
}
return rval;
}
}

View File

@ -596,8 +596,7 @@ newSession(FILTER *instance, SESSION *session)
my_session->branch_dcb = dcb;
my_session->dummy_filterdef = dummy;
if ((dummy_upstream = filterUpstream(
dummy, my_session, &ses->tail)) == NULL)
if ((dummy_upstream = filter_upstream(dummy, my_session, &ses->tail)) == NULL)
{
filter_free(dummy);
closeSession(instance, (void*) my_session);
@ -702,7 +701,7 @@ freeSession(FILTER *instance, void *session)
if (state == SESSION_STATE_ROUTER_READY)
{
session_free(ses);
session_put_ref(ses);
}
else if (state == SESSION_STATE_TO_BE_FREED)
{

View File

@ -1,8 +1,8 @@
if(BUILD_AVRO)
add_subdirectory(avro)
add_subdirectory(avrorouter)
endif()
if(BUILD_BINLOG)
add_subdirectory(binlog)
add_subdirectory(binlogrouter)
endif()
add_subdirectory(cli)

View File

@ -1,7 +1,7 @@
if(AVRO_FOUND AND JANSSON_FOUND)
include_directories(${AVRO_INCLUDE_DIR})
include_directories(${JANSSON_INCLUDE_DIR})
add_library(avrorouter SHARED avro.c ../binlog/binlog_common.c avro_client.c avro_schema.c avro_rbr.c avro_file.c avro_index.c)
add_library(avrorouter SHARED avro.c ../binlogrouter/binlog_common.c avro_client.c avro_schema.c avro_rbr.c avro_file.c avro_index.c)
set_target_properties(avrorouter PROPERTIES VERSION "1.0.0")
set_target_properties(avrorouter PROPERTIES LINK_FLAGS -Wl,-z,defs)
target_link_libraries(avrorouter maxscale-common ${JANSSON_LIBRARIES} ${AVRO_LIBRARIES} maxavro sqlite3 lzma)

View File

@ -568,7 +568,6 @@ createInstance(SERVICE *service, char **options)
inst->reconnect_pending = 0;
inst->handling_threads = 0;
inst->rotating = 0;
inst->residual = NULL;
inst->slaves = NULL;
inst->next = NULL;
inst->lastEventTimestamp = 0;
@ -1847,7 +1846,7 @@ static void rses_end_locked_router_action(ROUTER_SLAVE *rses)
static uint64_t getCapabilities(void)
{
return RCAP_TYPE_NO_RSESSION;
return RCAP_TYPE_NO_RSESSION | RCAP_TYPE_CONTIGUOUS_OUTPUT;
}
/**
@ -2413,13 +2412,6 @@ destroyInstance(ROUTER *instance)
}
}
/* Discard the queued residual data */
while (inst->residual)
{
inst->residual = gwbuf_consume(inst->residual, GWBUF_LENGTH(inst->residual));
}
inst->residual = NULL;
MXS_INFO("%s is being stopped by MaxScale shudown. Disconnecting from master %s:%d, "
"read up to log %s, pos %lu, transaction safe pos %lu",
inst->service->name,

View File

@ -251,12 +251,9 @@ static const char BLR_DBUSERS_FILE[] = "dbusers";
/** Possible states of an event sent by the master */
enum blr_event_state
{
BLR_EVENT_DONE, /*< No event being processed */
BLR_EVENT_STARTED, /*< The first packet of an event which spans multiple packets
* has been received */
BLR_EVENT_STARTED, /*< The first packet of an event has been received */
BLR_EVENT_ONGOING, /*< Other packets of a multi-packet event are being processed */
BLR_EVENT_COMPLETE /*< A multi-packet event has been successfully processed
* but the router is not yet ready to process another one */
BLR_EVENT_DONE, /*< The complete event was received */
};
/* Master Server configuration struct */
@ -524,19 +521,14 @@ typedef struct router_instance
unsigned int master_state; /*< State of the master FSM */
uint8_t lastEventReceived; /*< Last even received */
uint32_t lastEventTimestamp; /*< Timestamp from last event */
GWBUF *residual; /*< Any residual binlog event */
MASTER_RESPONSES saved_master; /*< Saved master responses */
char *binlogdir; /*< The directory with the binlog files */
SPINLOCK binlog_lock; /*< Lock to control update of the binlog position */
int trx_safe; /*< Detect and handle partial transactions */
int pending_transaction; /*< Pending transaction */
enum blr_event_state master_event_state; /*< Packet read state */
uint32_t stored_checksum; /*< The current value of the checksum */
uint8_t partial_checksum[MYSQL_CHECKSUM_LEN]; /*< The partial value of the checksum
* received from the master */
uint8_t partial_checksum_bytes; /*< How many bytes of the checksum we have read */
uint64_t checksum_size; /*< Data size for the checksum */
REP_HEADER stored_header; /*< Relication header of the event the master is sending */
GWBUF *stored_event; /*< Buffer where partial events are stored */
uint64_t last_safe_pos; /* last committed transaction */
char binlog_name[BINLOG_FNAMELEN + 1];
/*< Name of the current binlog file */

View File

@ -104,8 +104,6 @@ static void blr_extract_header_semisync(uint8_t *pkt, REP_HEADER *hdr);
static int blr_send_semisync_ack (ROUTER_INSTANCE *router, uint64_t pos);
static int blr_get_master_semisync(GWBUF *buf);
int blr_write_data_into_binlog(ROUTER_INSTANCE *router, uint32_t data_len, uint8_t *buf);
void extract_checksum(ROUTER_INSTANCE* router, uint8_t *cksumptr, uint8_t len);
static void blr_terminate_master_replication(ROUTER_INSTANCE *router, uint8_t* ptr, int len);
void blr_notify_all_slaves(ROUTER_INSTANCE *router);
extern bool blr_notify_waiting_slave(ROUTER_SLAVE *slave);
@ -168,13 +166,6 @@ blr_start_master(void* data)
}
router->master_state = BLRM_CONNECTING;
/* Discard the queued residual data */
while (router->residual)
{
router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual));
}
router->residual = NULL;
spinlock_release(&router->lock);
if ((client = dcb_alloc(DCB_ROLE_INTERNAL, NULL)) == NULL)
{
@ -242,13 +233,6 @@ blr_restart_master(ROUTER_INSTANCE *router)
{
dcb_close(router->client);
/* Discard the queued residual data */
while (router->residual)
{
router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual));
}
router->residual = NULL;
/* Now it is safe to unleash other threads on this router instance */
spinlock_acquire(&router->lock);
router->reconnect_pending = 0;
@ -335,6 +319,8 @@ blr_master_close(ROUTER_INSTANCE *router)
dcb_close(router->master);
router->master_state = BLRM_UNCONNECTED;
router->master_event_state = BLR_EVENT_DONE;
gwbuf_free(router->stored_event);
router->stored_event = NULL;
}
/**
@ -987,6 +973,63 @@ encode_value(unsigned char *data, unsigned int value, int len)
}
}
/**
* Check that the stored event checksum matches the calculated checksum
*/
static bool verify_checksum(ROUTER_INSTANCE *router, size_t len, uint8_t *ptr)
{
bool rval = true;
uint32_t offset = MYSQL_HEADER_LEN + 1;
uint32_t size = len - (offset + MYSQL_CHECKSUM_LEN);
uint32_t checksum = crc32(0L, ptr + offset, size);
uint32_t pktsum = EXTRACT32(ptr + offset + size);
if (pktsum != checksum)
{
rval = false;
MXS_ERROR("%s: Checksum error in event from master, "
"binlog %s @ %lu. Closing master connection.",
router->service->name, router->binlog_name,
router->current_pos);
router->stats.n_badcrc++;
}
return rval;
}
/**
* @brief Reset router errors
*
* @param router Router instance
* @param hdr Replication header
*/
static void reset_errors(ROUTER_INSTANCE *router, REP_HEADER *hdr)
{
spinlock_acquire(&router->lock);
/* set mysql errno to 0 */
router->m_errno = 0;
/* Remove error message */
if (router->m_errmsg)
{
MXS_FREE(router->m_errmsg);
}
router->m_errmsg = NULL;
spinlock_release(&router->lock);
#ifdef SHOW_EVENTS
printf("blr: len %lu, event type 0x%02x, flags 0x%04x, "
"event size %d, event timestamp %lu\n",
(unsigned long)len - 4,
hdr->event_type,
hdr->flags,
hdr->event_size,
(unsigned long)hdr->timestamp);
#endif
}
/**
* blr_handle_binlog_record - we have received binlog records from
* the master and we must now work out what to do with them.
@ -997,134 +1040,24 @@ encode_value(unsigned char *data, unsigned int value, int len)
void
blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
{
uint8_t *msg = NULL, *ptr, *pdata;
uint8_t *msg = NULL, *ptr;
REP_HEADER hdr;
unsigned int len = 0, reslen;
unsigned int pkt_length;
int no_residual = 1;
int preslen = -1;
unsigned int len = 0;
int prev_length = -1;
int n_bufs = -1, pn_bufs = -1;
int check_packet_len;
int semisync_bytes;
int semi_sync_send_ack = 0;
/*
* Prepend any residual buffer to the buffer chain we have
* been called with.
*/
if (router->residual)
{
pkt = gwbuf_append(router->residual, pkt);
router->residual = NULL;
no_residual = 0;
}
pkt_length = gwbuf_length(pkt);
/*
* Loop over all the packets while we still have some data
* and the packet length is enough to hold a replication event
* header.
*/
while (pkt && pkt_length > 24)
while (pkt)
{
reslen = GWBUF_LENGTH(pkt);
pdata = GWBUF_DATA(pkt);
if (reslen < 3) // Payload length straddles buffers
{
/* Get the length of the packet from the residual and new packet */
if (reslen >= 3)
{
len = EXTRACT24(pdata);
}
else if (reslen == 2)
{
len = EXTRACT16(pdata);
len |= (*(uint8_t *)GWBUF_DATA(pkt->next) << 16);
}
else if (reslen == 1)
{
len = *pdata;
len |= (EXTRACT16(GWBUF_DATA(pkt->next)) << 8);
}
len += 4; // Allow space for the header
}
else
{
len = EXTRACT24(pdata) + 4;
}
/* len is now the payload length for the packet we are working on */
if (reslen < len && pkt_length >= len)
{
/*
* The message is contained in more than the current
* buffer, however we have the complete messasge in
* this buffer and the chain of remaining buffers.
*
* Allocate a contiguous buffer for the binlog message
* and copy the complete message into this buffer.
*/
int msg_remainder = len;
GWBUF *p = pkt;
if ((msg = MXS_MALLOC(len)) == NULL)
{
break;
}
n_bufs = 0;
ptr = msg;
while (p && msg_remainder > 0)
{
int plen = GWBUF_LENGTH(p);
int n = (msg_remainder > plen ? plen : msg_remainder);
memcpy(ptr, GWBUF_DATA(p), n);
msg_remainder -= n;
ptr += n;
if (msg_remainder > 0)
{
p = p->next;
}
n_bufs++;
}
if (msg_remainder)
{
MXS_ERROR("Expected entire message in buffer "
"chain, but failed to create complete "
"message as expected. %s @ %lu",
router->binlog_name,
router->current_pos);
MXS_FREE(msg);
/* msg = NULL; Not needed unless msg will be referred to again */
break;
}
ptr = msg;
}
else if (reslen < len)
{
/*
* The message is not fully contained in the current
* and we do not have the complete message in the
* buffer chain. Therefore we must stop processing
* until we receive the next buffer.
*/
router->stats.n_residuals++;
MXS_DEBUG("Residual data left after %lu records. %s @ %lu",
router->stats.n_binlogs,
router->binlog_name, router->current_pos);
break;
}
else
{
/*
* The message is fully contained in the current buffer
*/
ptr = pdata;
n_bufs = 1;
}
ptr = GWBUF_DATA(pkt);
len = gw_mysql_get_byte3(ptr);
semisync_bytes = 0;
/*
@ -1135,7 +1068,7 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
if (len < BINLOG_EVENT_HDR_LEN && router->master_event_state != BLR_EVENT_ONGOING)
{
char *event_msg = "";
char *event_msg = "unknown";
/* Packet is too small to be a binlog event */
if (ptr[4] == 0xfe) /* EOF Packet */
@ -1147,11 +1080,13 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
event_msg = "error";
}
MXS_NOTICE("Non-event message (%s) from master.", event_msg);
pkt = gwbuf_consume(pkt, len);
}
else
{
if (router->master_event_state == BLR_EVENT_DONE)
{
/** This is the start of a new event */
spinlock_acquire(&router->lock);
router->stats.n_binlogs++;
router->stats.n_binlogs_ses++;
@ -1187,20 +1122,16 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
/* Sanity check */
if (hdr.ok == 0)
{
if (hdr.event_size != len - check_packet_len &&
if (hdr.event_size != len - (check_packet_len - MYSQL_HEADER_LEN) &&
(hdr.event_size + (check_packet_len - MYSQL_HEADER_LEN)) < MYSQL_PACKET_LENGTH_MAX)
{
MXS_ERROR("Packet length is %d, but event size is %d, "
"binlog file %s position %lu "
"reslen is %d and preslen is %d, "
"length of previous event %d. %s",
"binlog file %s position %lu, "
"length of previous event %d.",
len, hdr.event_size,
router->binlog_name,
router->current_pos,
reslen, preslen, prev_length,
(prev_length == -1 ?
(no_residual ? "No residual data from previous call" :
"Residual data from previous call") : ""));
prev_length);
blr_log_packet(LOG_ERR, "Packet:", ptr, len);
@ -1216,18 +1147,13 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
break;
}
else if (hdr.event_size + (check_packet_len - MYSQL_HEADER_LEN) >= MYSQL_PACKET_LENGTH_MAX)
{
router->master_event_state = BLR_EVENT_STARTED;
/** Store the header for later use */
memcpy(&router->stored_header, &hdr, sizeof(hdr));
}
/** Prepare the checksum variables for this event */
router->stored_checksum = crc32(0L, NULL, 0);
router->checksum_size = hdr.event_size - MYSQL_CHECKSUM_LEN;
router->partial_checksum_bytes = 0;
/** This is the first (and possibly last) packet of a replication
* event. We store the header in case the event is large and
* it is transmitted over multiple network packets. */
router->master_event_state = BLR_EVENT_STARTED;
memcpy(&router->stored_header, &hdr, sizeof(hdr));
reset_errors(router, &hdr);
}
else
{
@ -1236,159 +1162,96 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
gwbuf_free(pkt);
pkt = NULL;
pkt_length = 0;
break;
}
if (hdr.ok == 0)
{
spinlock_acquire(&router->lock);
/* set mysql errno to 0 */
router->m_errno = 0;
/* Remove error message */
if (router->m_errmsg)
{
MXS_FREE(router->m_errmsg);
}
router->m_errmsg = NULL;
spinlock_release(&router->lock);
#ifdef SHOW_EVENTS
printf("blr @ %lu: len %lu, event type 0x%02x, flags 0x%04x, "
"event size %d, event timestamp %lu, next pos %lu\n",
router->current_pos,
(unsigned long)len - 4,
hdr.event_type,
hdr.flags,
hdr.event_size,
(unsigned long)hdr.timestamp,
(unsigned long)hdr.next_pos);
#endif
}
}
else
{
/** We're processing a multi-packet replication event */
ss_dassert(router->master_event_state == BLR_EVENT_ONGOING);
}
/* pending large event */
if (router->master_event_state != BLR_EVENT_DONE)
/** Gather the event into one big buffer */
GWBUF *part = gwbuf_split(&pkt, len + MYSQL_HEADER_LEN);
if (semisync_bytes)
{
if (len - MYSQL_HEADER_LEN < MYSQL_PACKET_LENGTH_MAX)
/** Consume the two semi-sync bytes */
part = gwbuf_consume(part, semisync_bytes);
}
ss_dassert(router->master_event_state == BLR_EVENT_STARTED ||
router->master_event_state == BLR_EVENT_ONGOING);
if (router->master_event_state == BLR_EVENT_ONGOING)
{
/**
* Consume the network header so that we can append the raw
* event data to the original buffer. This allows both checksum
* calculations and encryption to process it as a contiguous event
*/
part = gwbuf_consume(part, MYSQL_HEADER_LEN);
}
router->stored_event = gwbuf_append(router->stored_event, part);
if (len < MYSQL_PACKET_LENGTH_MAX)
{
/**
* This is either the only packet for the event or the last
* packet in a series for this event. The buffer now contains
* the network header of the first packet (4 bytes) and one OK byte.
* The semi-sync bytes are always consumed at an earlier stage.
*/
ss_dassert(router->master_event_state != BLR_EVENT_DONE);
if (router->master_event_state != BLR_EVENT_STARTED)
{
/** This is the last packet, we can now proceed to distribute
* the event afer it has been written to disk */
ss_dassert(router->master_event_state != BLR_EVENT_COMPLETE);
router->master_event_state = BLR_EVENT_COMPLETE;
/**
* This is not the first packet for this event. We must use
* the stored header.
*/
memcpy(&hdr, &router->stored_header, sizeof(hdr));
}
else
{
/* current partial event is being written to disk file */
uint32_t offset = MYSQL_HEADER_LEN;
uint32_t extra_bytes = MYSQL_HEADER_LEN;
/** Don't write the OK byte into the binlog */
if (router->master_event_state == BLR_EVENT_STARTED)
{
offset = MYSQL_HEADER_LEN + 1;
router->master_event_state = BLR_EVENT_ONGOING;
extra_bytes = MYSQL_HEADER_LEN + 1;
}
ss_dassert(len - extra_bytes - semisync_bytes > 0);
uint32_t bytes_available = len - extra_bytes - semisync_bytes;
if (router->master_chksum)
{
uint32_t size = MXS_MIN(len - extra_bytes - semisync_bytes,
router->checksum_size);
router->stored_checksum = crc32(router->stored_checksum,
ptr + offset,
size);
router->checksum_size -= size;
if (router->checksum_size == 0 && size < bytes_available)
{
extract_checksum(router, ptr + offset + size,
bytes_available - size);
}
}
if (blr_write_data_into_binlog(router, bytes_available,
ptr + offset) == 0)
{
/** Failed to write to the binlog file, destroy the buffer
* chain and close the connection with the master */
while (pkt)
{
pkt = GWBUF_CONSUME_ALL(pkt);
}
blr_master_close(router);
blr_master_delayed_connect(router);
return;
}
pkt = gwbuf_consume(pkt, len);
pkt_length -= len;
continue;
}
/** The event is now complete */
router->master_event_state = BLR_EVENT_DONE;
}
else
{
/**
* This packet is a part of a series of packets that contain an
* event larger than MYSQL_PACKET_LENGTH_MAX bytes.
*
* For each partial event chunk, we remove the network header and
* append it to router->stored_event. The first event is an
* exception to this and it is appended as-is with the network
* header and the extra OK byte.
*/
ss_dassert(len == MYSQL_PACKET_LENGTH_MAX);
router->master_event_state = BLR_EVENT_ONGOING;
continue;
}
/*
* First check that the checksum we calculate matches the
* checksum in the packet we received.
/** We now have the complete event in one contiguous buffer */
router->stored_event = gwbuf_make_contiguous(router->stored_event);
ptr = GWBUF_DATA(router->stored_event);
/** len is now the length of the complete event plus 4 bytes of network
* header and one OK byte. Semi-sync bytes are never stored. */
len = gwbuf_length(router->stored_event);
/**
* If checksums are enabled, verify that the stored checksum
* matches the one we calculated
*/
if (router->master_chksum)
if (router->master_chksum && !verify_checksum(router, len, ptr))
{
uint32_t offset = MYSQL_HEADER_LEN;
uint32_t size = len - MYSQL_HEADER_LEN - MYSQL_CHECKSUM_LEN;
if (router->master_event_state == BLR_EVENT_DONE)
{
/** Set the pointer offset to the first byte after
* the header and OK byte */
offset = MYSQL_HEADER_LEN + 1;
size = len - (check_packet_len + MYSQL_CHECKSUM_LEN);
}
size = MXS_MIN(size, router->checksum_size);
if (router->checksum_size > 0)
{
router->stored_checksum = crc32(router->stored_checksum,
ptr + offset, size);
router->checksum_size -= size;
}
if(router->checksum_size == 0 && size < (len - offset - semisync_bytes))
{
extract_checksum(router, ptr + offset + size,
len - offset - size - semisync_bytes);
}
if (router->partial_checksum_bytes == MYSQL_CHECKSUM_LEN)
{
uint32_t pktsum = EXTRACT32(router->partial_checksum);
if (pktsum != router->stored_checksum)
{
router->stats.n_badcrc++;
MXS_FREE(msg);
/* msg = NULL; Not needed unless msg will be referred to again */
MXS_ERROR("%s: Checksum error in event from master, "
"binlog %s @ %lu. Closing master connection.",
router->service->name, router->binlog_name,
router->current_pos);
blr_master_close(router);
blr_master_delayed_connect(router);
return;
}
}
else
{
pkt = gwbuf_consume(pkt, len);
pkt_length -= len;
continue;
}
MXS_FREE(msg);
blr_master_close(router);
blr_master_delayed_connect(router);
return;
}
if (hdr.ok == 0)
@ -1583,16 +1446,6 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
}
else if (hdr.flags != LOG_EVENT_ARTIFICIAL_F)
{
ptr = ptr + 4; // Skip header
uint32_t offset = 4;
if (router->master_event_state == BLR_EVENT_STARTED ||
router->master_event_state == BLR_EVENT_DONE)
{
ptr++;
offset++;
}
if (hdr.event_type == ROTATE_EVENT)
{
spinlock_acquire(&router->binlog_lock);
@ -1600,54 +1453,36 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
spinlock_release(&router->binlog_lock);
}
/* Current event is being written to disk file.
* It is possible for an empty packet to be sent if an
* event is exactly 2^24 bytes long. In this case the
* empty packet should be discarded. */
if (len > MYSQL_HEADER_LEN &&
blr_write_binlog_record(router, &hdr, len - offset - semisync_bytes, ptr) == 0)
uint32_t offset = MYSQL_HEADER_LEN + 1; // Skip header and OK byte
/**
* Write the raw event data to disk without the network
* header or the OK byte
*/
if (blr_write_binlog_record(router, &hdr, len - offset, ptr + offset) == 0)
{
/*
* Failed to write to the
* binlog file, destroy the
* buffer chain and close the
* connection with the master
*/
while ((pkt = gwbuf_consume(pkt, GWBUF_LENGTH(pkt))) != NULL)
{
;
}
gwbuf_free(pkt);
blr_master_close(router);
blr_master_delayed_connect(router);
return;
}
/* Check for rotete event */
/* Check for rotate event */
if (hdr.event_type == ROTATE_EVENT)
{
if (!blr_rotate_event(router, ptr, &hdr))
{
/*
* Failed to write to the
* binlog file, destroy the
* buffer chain and close the
* connection with the master
*/
while ((pkt = gwbuf_consume(pkt, GWBUF_LENGTH(pkt))) != NULL)
{
;
}
gwbuf_free(pkt);
blr_master_close(router);
blr_master_delayed_connect(router);
return;
}
}
/* Handle semi-sync request fom master */
/* Handle semi-sync request from master */
if (router->master_semi_sync != MASTER_SEMISYNC_NOT_AVAILABLE &&
semi_sync_send_ack == BLR_MASTER_SEMI_SYNC_ACK_REQ &&
(router->master_event_state == BLR_EVENT_COMPLETE ||
router->master_event_state == BLR_EVENT_DONE))
(router->master_event_state == BLR_EVENT_DONE))
{
MXS_DEBUG("%s: binlog record in file %s, pos %lu has "
@ -1733,16 +1568,7 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
spinlock_release(&router->binlog_lock);
if (!blr_rotate_event(router, ptr, &hdr))
{
/*
* Failed to write to the
* binlog file, destroy the
* buffer chain and close the
* connection with the master
*/
while ((pkt = gwbuf_consume(pkt, GWBUF_LENGTH(pkt))) != NULL)
{
;
}
gwbuf_free(pkt);
blr_master_close(router);
blr_master_delayed_connect(router);
return;
@ -1750,17 +1576,15 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
}
}
}
/** A large event is now fully received and processed */
if (router->master_event_state == BLR_EVENT_COMPLETE)
{
router->master_event_state = BLR_EVENT_DONE;
}
}
else
{
blr_terminate_master_replication(router, ptr, len);
}
/** Finished processing the event */
gwbuf_free(router->stored_event);
router->stored_event = NULL;
}
if (msg)
@ -1768,33 +1592,8 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
MXS_FREE(msg);
msg = NULL;
}
prev_length = len;
while (len > 0)
{
unsigned int n, plen;
plen = GWBUF_LENGTH(pkt);
n = (plen < len ? plen : len);
pkt = gwbuf_consume(pkt, n);
len -= n;
pkt_length -= n;
}
preslen = reslen;
pn_bufs = n_bufs;
}
/*
* Check if we have a residual, part binlog message to deal with.
* Just simply store the GWBUF for next time
*/
if (pkt)
{
router->residual = pkt;
ss_dassert(pkt_length != 0);
}
else
{
ss_dassert(pkt_length == 0);
}
blr_file_flush(router);
}
@ -2242,13 +2041,6 @@ blr_stop_start_master(ROUTER_INSTANCE *router)
}
}
/* Discard the queued residual data */
while (router->residual)
{
router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual));
}
router->residual = NULL;
router->master_state = BLRM_UNCONNECTED;
spinlock_release(&router->lock);
@ -2598,26 +2390,6 @@ bool blr_send_event(blr_thread_role_t role,
return rval;
}
/**
* Extract the checksum from the binlogs
*
* This updates the internal state of the router and will allow us to detect
* if the checksum is split across two packets.
* @param router Router instance
* @param cksumptr Pointer to the checksum
* @param len How much of the data is readable
*/
void extract_checksum(ROUTER_INSTANCE* router, uint8_t *cksumptr, uint8_t len)
{
uint8_t *ptr = cksumptr;
while (ptr - cksumptr < len && router->partial_checksum_bytes < MYSQL_CHECKSUM_LEN)
{
router->partial_checksum[router->partial_checksum_bytes] = *ptr;
ptr++;
router->partial_checksum_bytes++;
}
}
/**
* Stop the slave connection and log errors
*

View File

@ -348,6 +348,7 @@ blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
int query_len;
char *ptr;
extern char *strcasestr();
bool unexpected = true;
qtext = (char*)GWBUF_DATA(queue);
query_len = extract_field((uint8_t *)qtext, 24) - 1;
@ -534,6 +535,10 @@ blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
return blr_slave_send_var_value(router, slave, heading, server_id, BLR_TYPE_INT);
}
else if (strcasestr(word, "binlog_gtid_pos"))
{
unexpected = false;
}
}
else if (strcasecmp(word, "SHOW") == 0)
{
@ -1122,7 +1127,17 @@ blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
MXS_FREE(query_text);
query_text = strndup(qtext, query_len);
MXS_ERROR("Unexpected query from '%s'@'%s': %s", slave->dcb->user, slave->dcb->remote, query_text);
if (unexpected)
{
MXS_ERROR("Unexpected query from '%s'@'%s': %s", slave->dcb->user, slave->dcb->remote, query_text);
}
else
{
MXS_INFO("Unexpected query from '%s'@'%s', possibly a 10.1 slave: %s",
slave->dcb->user, slave->dcb->remote, query_text);
}
MXS_FREE(query_text);
blr_slave_send_error(router, slave,
"You have an error in your SQL syntax; Check the syntax "
@ -3444,13 +3459,6 @@ blr_stop_slave(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave)
}
}
/* Discard the queued residual data */
while (router->residual)
{
router->residual = gwbuf_consume(router->residual, GWBUF_LENGTH(router->residual));
}
router->residual = NULL;
/* Now it is safe to unleash other threads on this router instance */
router->reconnect_pending = 0;
router->active_logs = 0;

View File

@ -290,8 +290,8 @@ bool listfuncs_cb(const MODULECMD *cmd, void *data)
{
DCB *dcb = (DCB*)data;
dcb_printf(dcb, "%s::%s(", cmd->domain, cmd->identifier);
dcb_printf(dcb, "Command: %s %s\n", cmd->domain, cmd->identifier);
dcb_printf(dcb, "Parameters: ");
for (int i = 0; i < cmd->arg_count_max; i++)
{
@ -309,8 +309,7 @@ bool listfuncs_cb(const MODULECMD *cmd, void *data)
}
}
dcb_printf(dcb, ")\n");
dcb_printf(dcb, "\n\n");
for (int i = 0; i < cmd->arg_count_max; i++)
{
@ -334,9 +333,9 @@ bool listfuncs_cb(const MODULECMD *cmd, void *data)
return true;
}
void dListFunctions(DCB *dcb)
void dListCommands(DCB *dcb, const char *domain, const char *ident)
{
modulecmd_foreach(NULL, NULL, listfuncs_cb, dcb);
modulecmd_foreach(domain, ident, listfuncs_cb, dcb);
}
/**
@ -405,10 +404,13 @@ struct subcommand listoptions[] =
{0, 0, 0}
},
{
"functions", 0, 0, dListFunctions,
"List registered functions",
"List all registered functions",
{0}
"commands", 0, 2, dListCommands,
"List registered commands",
"Usage list commands [DOMAIN] [COMMAND]\n"
"Parameters:\n"
"DOMAIN Regular expressions for filtering module domains\n"
"COMMAND Regular expressions for filtering module commands\n",
{ARG_TYPE_STRING, ARG_TYPE_STRING}
},
{ EMPTY_OPTION}
};
@ -1081,6 +1083,22 @@ static void createListener(DCB *dcb, SERVICE *service, char *name, char *address
}
}
static void createMonitor(DCB *dcb, const char *name, const char *module)
{
if (monitor_find(name))
{
dcb_printf(dcb, "Monitor '%s' already exists\n", name);
}
else if (runtime_create_monitor(name, module))
{
dcb_printf(dcb, "Created monitor '%s'\n", name);
}
else
{
dcb_printf(dcb, "Failed to create monitor '%s', see log for more details\n", name);
}
}
struct subcommand createoptions[] =
{
{
@ -1127,6 +1145,16 @@ struct subcommand createoptions[] =
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
}
},
{
"monitor", 2, 2, createMonitor,
"Create a new monitor",
"Usage: create monitor NAME MODULE\n"
"NAME Monitor name\n"
"MODULE Monitor module\n",
{
ARG_TYPE_STRING, ARG_TYPE_STRING
}
},
{
EMPTY_OPTION
}
@ -1162,6 +1190,22 @@ static void destroyListener(DCB *dcb, SERVICE *service, const char *name)
}
}
static void destroyMonitor(DCB *dcb, MONITOR *monitor)
{
char name[strlen(monitor->name) + 1];
strcpy(name, monitor->name);
if (runtime_destroy_monitor(monitor))
{
dcb_printf(dcb, "Destroyed monitor '%s'\n", name);
}
else
{
dcb_printf(dcb, "Failed to destroy monitor '%s', see log file for more details\n", name);
}
}
struct subcommand destroyoptions[] =
{
{
@ -1176,6 +1220,12 @@ struct subcommand destroyoptions[] =
"Usage: destroy listener SERVICE NAME",
{ARG_TYPE_SERVICE, ARG_TYPE_STRING}
},
{
"monitor", 1, 1, destroyMonitor,
"Destroy a monitor",
"Usage: destroy monitor NAME",
{ARG_TYPE_MONITOR}
},
{
EMPTY_OPTION
}
@ -1290,6 +1340,13 @@ static void alterMonitor(DCB *dcb, MONITOR *monitor, char *v1, char *v2, char *v
{
dcb_printf(dcb, "Error: Bad key-value parameter: %s=%s\n", key, value);
}
else if (!monitor->created_online)
{
dcb_printf(dcb, "Warning: Altered monitor '%s' which is in the "
"main\nconfiguration file. These changes will not be "
"persisted and need\nto be manually added or set again"
"after a restart.\n", monitor->name);
}
}
else
{
@ -1316,9 +1373,8 @@ struct subcommand alteroptions[] =
"monitor", 2, 12, alterMonitor,
"Alter monitor parameters",
"Usage: alter monitor NAME KEY=VALUE ...\n"
"This will alter an existing parameter of a monitor. The accepted values\n"
"for KEY are: 'user', 'password', 'monitor_interval',\n"
"'backend_connect_timeout', 'backend_write_timeout', 'backend_read_timeout'\n"
"This will alter an existing parameter of a monitor. To remove parameters,\n"
"pass an empty value for a key e.g. 'maxadmin alter monitor my-monitor my-key='\n"
"A maximum of 11 parameters can be changed at one time",
{ARG_TYPE_MONITOR, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
@ -1335,9 +1391,9 @@ static inline bool requires_output_dcb(const MODULECMD *cmd)
return cmd->arg_count_max > 0 && MODULECMD_GET_TYPE(type) == MODULECMD_ARG_OUTPUT;
}
static void callFunction(DCB *dcb, char *domain, char *id, char *v3,
char *v4, char *v5, char *v6, char *v7, char *v8, char *v9,
char *v10, char *v11, char *v12)
static void callModuleCommand(DCB *dcb, char *domain, char *id, char *v3,
char *v4, char *v5, char *v6, char *v7, char *v8, char *v9,
char *v10, char *v11, char *v12)
{
const void *values[11] = {v3, v4, v5, v6, v7, v8, v9, v10, v11, v12};
const int valuelen = sizeof(values) / sizeof(values[0]);
@ -1354,7 +1410,7 @@ static void callFunction(DCB *dcb, char *domain, char *id, char *v3,
{
if (requires_output_dcb(cmd))
{
/** The function requires a DCB for output, add the client DCB
/** The command requires a DCB for output, add the client DCB
* as the first argument */
for (int i = valuelen - 1; i > 0; i--)
{
@ -1370,28 +1426,28 @@ static void callFunction(DCB *dcb, char *domain, char *id, char *v3,
{
if (!modulecmd_call_command(cmd, arg))
{
dcb_printf(dcb, "Failed to call function: %s\n", modulecmd_get_error());
dcb_printf(dcb, "Error: %s\n", modulecmd_get_error());
}
modulecmd_arg_free(arg);
}
else
{
dcb_printf(dcb, "Failed to parse arguments: %s\n", modulecmd_get_error());
dcb_printf(dcb, "Error: %s\n", modulecmd_get_error());
}
}
else
{
dcb_printf(dcb, "Function not found: %s\n", modulecmd_get_error());
dcb_printf(dcb, "Error: %s\n", modulecmd_get_error());
}
}
struct subcommand calloptions[] =
{
{
"function", 2, 12, callFunction,
"Call module function",
"Usage: call function NAMESPACE FUNCTION ARGS...\n"
"To list all registered functions, run 'list functions'.\n",
"command", 2, 12, callModuleCommand,
"Call module command",
"Usage: call command NAMESPACE COMMAND ARGS...\n"
"To list all registered commands, run 'list commands'.\n",
{ ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING,
ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING, ARG_TYPE_STRING}
@ -1703,6 +1759,7 @@ execute_cmd(CLI_SESSION *cli)
else
{
unsigned long arg_list[MAXARGS] = {};
bool ok = true;
for (int k = 0; k < cmds[i].options[j].argc_max && k < argc; k++)
{
@ -1710,72 +1767,75 @@ execute_cmd(CLI_SESSION *cli)
if (arg_list[k] == 0)
{
dcb_printf(dcb, "Invalid argument: %s\n", args[k + 2]);
break;
ok = false;
}
}
switch (cmds[i].options[j].argc_max)
if (ok)
{
case 0:
cmds[i].options[j].fn(dcb);
break;
case 1:
cmds[i].options[j].fn(dcb, arg_list[0]);
break;
case 2:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1]);
break;
case 3:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2]);
break;
case 4:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3]);
break;
case 5:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4]);
break;
case 6:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5]);
break;
case 7:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6]);
break;
case 8:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7]);
break;
case 9:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8]);
break;
case 10:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8],
arg_list[9]);
break;
case 11:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8],
arg_list[9], arg_list[10]);
break;
case 12:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8],
arg_list[9], arg_list[10], arg_list[11]);
break;
default:
dcb_printf(dcb, "Error: Maximum argument count is %d.\n", MAXARGS);
break;
switch (cmds[i].options[j].argc_max)
{
case 0:
cmds[i].options[j].fn(dcb);
break;
case 1:
cmds[i].options[j].fn(dcb, arg_list[0]);
break;
case 2:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1]);
break;
case 3:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2]);
break;
case 4:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3]);
break;
case 5:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4]);
break;
case 6:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5]);
break;
case 7:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6]);
break;
case 8:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7]);
break;
case 9:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8]);
break;
case 10:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8],
arg_list[9]);
break;
case 11:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8],
arg_list[9], arg_list[10]);
break;
case 12:
cmds[i].options[j].fn(dcb, arg_list[0], arg_list[1], arg_list[2],
arg_list[3], arg_list[4], arg_list[5],
arg_list[6], arg_list[7], arg_list[8],
arg_list[9], arg_list[10], arg_list[11]);
break;
default:
dcb_printf(dcb, "Error: Maximum argument count is %d.\n", MAXARGS);
break;
}
}
}
}