diff --git a/Documentation/REST-API/Resources-MaxScale.md b/Documentation/REST-API/Resources-MaxScale.md index 6a19a52ab..5e6756e00 100644 --- a/Documentation/REST-API/Resources-MaxScale.md +++ b/Documentation/REST-API/Resources-MaxScale.md @@ -304,89 +304,69 @@ GET /v1/maxscale/modules "self": "http://localhost:8989/v1/maxscale/modules/" }, "data": { - "id": "readwritesplit", + "id": "dbfwfilter", "type": "module", "attributes": { - "module_type": "Router", - "version": "V1.1.0", - "description": "A Read/Write splitting router for enhancement read scalability", - "api": "router", + "module_type": "Filter", + "version": "V1.2.0", + "description": "Firewall Filter", + "api": "filter", "status": "GA", + "commands": [ + { + "id": "rules/reload", + "type": "module_command", + "links": { + "self": "http://localhost:8989/v1/modules/dbfwfilter/rules/reload" + }, + "attributes": { + "method": "POST", + "arg_min": 1, + "arg_max": 2, + "parameters": [ + { + "description": "Filter to reload", + "type": "FILTER", + "required": true + }, + { + "description": "Path to rule file", + "type": "[STRING]", + "required": false + } + ] + } + } + ], "parameters": [ { - "name": "use_sql_variables_in", - "type": "enum", - "default_value": "all", - "enum_values": [ - "all", - "master" - ] + "name": "rules", + "type": "path" }, { - "name": "slave_selection_criteria", - "type": "enum", - "default_value": "LEAST_CURRENT_OPERATIONS", - "enum_values": [ - "LEAST_GLOBAL_CONNECTIONS", - "LEAST_ROUTER_CONNECTIONS", - "LEAST_BEHIND_MASTER", - "LEAST_CURRENT_OPERATIONS" - ] - }, - { - "name": "master_failure_mode", - "type": "enum", - "default_value": "fail_instantly", - "enum_values": [ - "fail_instantly", - "fail_on_write", - "error_on_write" - ] - }, - { - "name": "max_slave_replication_lag", - "type": "int", - "default_value": "-1" - }, - { - "name": "max_slave_connections", - "type": "string", - "default_value": "255" - }, - { - "name": "retry_failed_reads", - "type": "bool", - "default_value": "true" - }, - { - "name": "disable_sescmd_history", - "type": "bool", - "default_value": "true" - }, - { - "name": "max_sescmd_history", - "type": "count", - "default_value": "0" - }, - { - "name": "strict_multi_stmt", - "type": "bool", - "default_value": "true" - }, - { - "name": "master_accept_reads", + "name": "log_match", "type": "bool", "default_value": "false" }, { - "name": "connection_keepalive", - "type": "count", - "default_value": "0" + "name": "log_no_match", + "type": "bool", + "default_value": "false" + }, + { + "name": "action", + "type": "enum", + "default_value": "block", + "enum_values": [ + "allow", + "block", + "ignore" + ] } ] }, "links": { - "self": "http://localhost:8989/v1/modules/readwritesplit" + "self": "http://localhost:8989/v1/modules/dbfwfilter" } } } diff --git a/examples/roundrobinrouter.cpp b/examples/roundrobinrouter.cpp index 22aff4793..53dd34e69 100644 --- a/examples/roundrobinrouter.cpp +++ b/examples/roundrobinrouter.cpp @@ -673,7 +673,8 @@ static MXS_ROUTER* createInstance(SERVICE* service, char** options) * the pointer. */ /* Register a custom command */ - if (!modulecmd_register_command("rrrouter", "test_command", custom_cmd_example, + if (!modulecmd_register_command("rrrouter", "test_command", + MODULECMD_TYPE_ACTIVE, custom_cmd_example, 2, custom_cmd_args)) { MXS_ERROR("Module command registration failed."); diff --git a/include/maxscale/config.h b/include/maxscale/config.h index a97fced50..e7d6baacc 100644 --- a/include/maxscale/config.h +++ b/include/maxscale/config.h @@ -108,6 +108,7 @@ extern const char CN_LOG_THROTTLING[]; extern const char CN_MAXSCALE[]; extern const char CN_MAX_CONNECTIONS[]; extern const char CN_MAX_RETRY_INTERVAL[]; +extern const char CN_METHOD[]; extern const char CN_MODULE[]; extern const char CN_MODULES[]; extern const char CN_MODULE_COMMAND[]; diff --git a/include/maxscale/modulecmd.h b/include/maxscale/modulecmd.h index 1ea168f5b..bc7f69282 100644 --- a/include/maxscale/modulecmd.h +++ b/include/maxscale/modulecmd.h @@ -65,6 +65,13 @@ typedef struct This should always be the first argument if the function requires an output DCB. */ +/** What type of an action does the command perform? */ +enum modulecmd_type +{ + MODULECMD_TYPE_PASSIVE, /**< Command only displays data */ + MODULECMD_TYPE_ACTIVE /**< Command can modify data */ +}; + /** * Options for arguments, bits 9 through 32 */ @@ -123,6 +130,7 @@ typedef struct modulecmd { char *identifier; /**< Unique identifier */ char *domain; /**< Command domain */ + enum modulecmd_type type; /**< Command type, either active or passive */ MODULECMDFN func; /**< The registered function */ int arg_count_min; /**< Minimum number of arguments */ int arg_count_max; /**< Maximum number of arguments */ @@ -130,6 +138,9 @@ typedef struct modulecmd struct modulecmd *next; /**< Next command */ } MODULECMD; +/** Check if the module command can modify the data/state of the module */ +#define MODULECMD_MODIFIES_DATA(t) (t->type == MODULECMD_TYPE_ACTIVE) + /** * @brief Register a new command * @@ -143,7 +154,8 @@ typedef struct modulecmd * @return True if the module was successfully registered, false on error */ bool modulecmd_register_command(const char *domain, const char *identifier, - MODULECMDFN entry_point, int argc, modulecmd_arg_type_t *argv); + enum modulecmd_type type, MODULECMDFN entry_point, + int argc, modulecmd_arg_type_t *argv); /** * @brief Find a registered command @@ -196,6 +208,15 @@ void modulecmd_arg_free(MODULECMD_ARG *arg); */ bool modulecmd_arg_is_present(const MODULECMD_ARG *arg, int idx); +/** + * @brief Check if module command requires an output DCB + * + * @param cmd Command to check + * + * @return True if module requires a DCB for printing output + */ +bool modulecmd_requires_output_dcb(const MODULECMD* cmd); + /** * @brief Call a registered command * diff --git a/server/core/config.cc b/server/core/config.cc index d92cce825..bb111d613 100644 --- a/server/core/config.cc +++ b/server/core/config.cc @@ -92,6 +92,7 @@ const char CN_LOG_THROTTLING[] = "log_throttling"; const char CN_MAXSCALE[] = "maxscale"; const char CN_MAX_CONNECTIONS[] = "max_connections"; const char CN_MAX_RETRY_INTERVAL[] = "max_retry_interval"; +const char CN_METHOD[] = "method"; const char CN_MODULE[] = "module"; const char CN_MODULES[] = "modules"; const char CN_MODULE_COMMAND[] = "module_command"; diff --git a/server/core/load_utils.cc b/server/core/load_utils.cc index 95cf9dd34..45c8f6881 100644 --- a/server/core/load_utils.cc +++ b/server/core/load_utils.cc @@ -417,6 +417,12 @@ struct cb_param bool modulecmd_cb(const MODULECMD *cmd, void *data) { + if (modulecmd_requires_output_dcb(cmd)) + { + /** Module requires an output DCB, don't print it */ + return true; + } + cb_param* d = static_cast(data); json_t* obj = json_object(); @@ -424,6 +430,8 @@ bool modulecmd_cb(const MODULECMD *cmd, void *data) json_object_set_new(obj, CN_TYPE, json_string(CN_MODULE_COMMAND)); json_t* attr = json_object(); + const char* method = MODULECMD_MODIFIES_DATA(cmd) ? "POST" : "GET"; + json_object_set_new(attr, CN_METHOD, json_string(method)); json_object_set_new(attr, CN_ARG_MIN, json_integer(cmd->arg_count_min)); json_object_set_new(attr, CN_ARG_MAX, json_integer(cmd->arg_count_max)); diff --git a/server/core/maxscale/httprequest.hh b/server/core/maxscale/httprequest.hh index bfa40527d..a45f309d3 100644 --- a/server/core/maxscale/httprequest.hh +++ b/server/core/maxscale/httprequest.hh @@ -43,6 +43,34 @@ static int value_iterator(void *cls, return MHD_YES; } +static int value_sum_iterator(void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value) +{ + size_t& count = *(size_t*)cls; + count++; + return MHD_YES; +} + +static int value_copy_iterator(void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value) +{ + std::string k = key; + if (value) + { + k += "="; + k += value; + } + + char**& dest = *(char***) cls; + *dest = MXS_STRDUP_A(k.c_str()); + dest++; + + return MHD_YES; +} class HttpRequest { @@ -106,6 +134,34 @@ public: return p.second; } + /** + * @brief Get request option count + * + * @return Number of options in the request + */ + size_t get_option_count() const + { + size_t rval = 0; + MHD_get_connection_values(m_connection, MHD_GET_ARGUMENT_KIND, + value_sum_iterator, &rval); + + return rval; + } + + /** + * @brief Copy options to an array + * + * The @c dest parameter must be able to hold at least get_option_count() + * pointers. The values stored need to be freed by the caller. + * + * @param dest Destination where options are copied + */ + void copy_options(char** dest) const + { + MHD_get_connection_values(m_connection, MHD_GET_ARGUMENT_KIND, + value_copy_iterator, &dest); + } + /** * @brief Return request body * @@ -131,7 +187,7 @@ public: * * @return The complete request URI */ - const std::string& get_uri() const + std::string get_uri() const { return m_resource; } @@ -143,11 +199,39 @@ public: * * @return The request URI part or empty string if no part was found */ - const std::string uri_part(uint32_t idx) const + std::string uri_part(uint32_t idx) const { return m_resource_parts.size() > idx ? m_resource_parts[idx] : ""; } + /** + * @brief Return a segment of the URI + * + * Combines a range of parts into a segment of the URI. Each part is + * separated by a forward slash. + * + * @param start Start of range + * @param end End of range, not inclusive + * + * @return The URI segment that matches this range + */ + std::string uri_segment(uint32_t start, uint32_t end) const + { + std::string rval; + + for (uint32_t i = start; i < end && i < m_resource_parts.size(); i++) + { + if (i > start) + { + rval += "/"; + } + + rval += m_resource_parts[i]; + } + + return rval; + } + /** * @brief Return how many parts are in the URI * @@ -158,12 +242,12 @@ public: return m_resource_parts.size(); } - /** - * @brief Return the last part of the URI - * - * @return The last URI part - */ - const std::string last_uri_part() const + /** + * @brief Return the last part of the URI + * + * @return The last URI part + */ + std::string last_uri_part() const { return m_resource_parts.size() > 0 ? m_resource_parts[m_resource_parts.size() - 1] : ""; } diff --git a/server/core/maxscale/resource.hh b/server/core/maxscale/resource.hh index 528478c82..7b8e3f2c3 100644 --- a/server/core/maxscale/resource.hh +++ b/server/core/maxscale/resource.hh @@ -64,6 +64,7 @@ private: ResourceCallback m_cb; /**< Resource handler callback */ std::deque m_path; /**< Path components */ + bool m_is_glob; /**< Does this path glob? */ }; /** diff --git a/server/core/modulecmd.cc b/server/core/modulecmd.cc index 85173cc53..e6ccf39c1 100644 --- a/server/core/modulecmd.cc +++ b/server/core/modulecmd.cc @@ -137,8 +137,8 @@ static MODULECMD_DOMAIN* get_or_create_domain(const char *domain) } static MODULECMD* command_create(const char *identifier, const char *domain, - MODULECMDFN entry_point, int argc, - modulecmd_arg_type_t* argv) + enum modulecmd_type type, MODULECMDFN entry_point, + int argc, modulecmd_arg_type_t* argv) { ss_dassert((argc && argv) || (argc == 0 && argv == NULL)); MODULECMD *rval = (MODULECMD*)MXS_MALLOC(sizeof(*rval)); @@ -166,6 +166,7 @@ static MODULECMD* command_create(const char *identifier, const char *domain, types[0].description = ""; } + rval->type = type; rval->func = entry_point; rval->identifier = id; rval->domain = dm; @@ -413,7 +414,8 @@ static void free_argument(struct arg_node *arg) */ bool modulecmd_register_command(const char *domain, const char *identifier, - MODULECMDFN entry_point, int argc, modulecmd_arg_type_t *argv) + enum modulecmd_type type, MODULECMDFN entry_point, + int argc, modulecmd_arg_type_t *argv) { reset_error(); bool rval = false; @@ -430,7 +432,7 @@ bool modulecmd_register_command(const char *domain, const char *identifier, } else { - MODULECMD *cmd = command_create(identifier, domain, entry_point, argc, argv); + MODULECMD *cmd = command_create(identifier, domain, type, entry_point, argc, argv); if (cmd) { @@ -687,3 +689,17 @@ bool modulecmd_arg_is_present(const MODULECMD_ARG *arg, int idx) return arg->argc > idx && MODULECMD_GET_TYPE(&arg->argv[idx].type) != MODULECMD_ARG_NONE; } + +bool modulecmd_requires_output_dcb(const MODULECMD* cmd) +{ + for (int i = 0; i < cmd->arg_count_max; i++) + { + if (cmd->arg_types[i].type == MODULECMD_ARG_OUTPUT) + { + /** We can't call this as it requries a DCB for output so don't show it */ + return true; + } + } + + return false; +} diff --git a/server/core/resource.cc b/server/core/resource.cc index 981838343..dc9da38e9 100644 --- a/server/core/resource.cc +++ b/server/core/resource.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "maxscale/httprequest.hh" #include "maxscale/httpresponse.hh" @@ -103,7 +104,8 @@ private: }; Resource::Resource(ResourceCallback cb, int components, ...) : - m_cb(cb) + m_cb(cb), + m_is_glob(false) { va_list args; va_start(args, components); @@ -112,6 +114,10 @@ Resource::Resource(ResourceCallback cb, int components, ...) : { string part = va_arg(args, const char*); m_path.push_back(part); + if (part == "?") + { + m_is_glob = true; + } } va_end(args); } @@ -124,11 +130,12 @@ bool Resource::match(const HttpRequest& request) const { bool rval = false; - if (request.uri_part_count() == m_path.size()) + if (request.uri_part_count() == m_path.size() || m_is_glob) { rval = true; + size_t parts = MXS_MIN(request.uri_part_count(), m_path.size()); - for (size_t i = 0; i < request.uri_part_count(); i++) + for (size_t i = 0; i < parts; i++) { if (m_path[i] != request.uri_part(i) && !matching_variable_path(m_path[i], request.uri_part(i))) @@ -185,6 +192,11 @@ bool Resource::matching_variable_path(const string& path, const string& target) } } } + else if (path == "?") + { + /** Wildcard match */ + rval = true; + } return rval; } @@ -550,6 +562,43 @@ HttpResponse cb_delete_user(const HttpRequest& request) return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error()); } +HttpResponse cb_modulecmd(const HttpRequest& request) +{ + std::string module = request.uri_part(2); + std::string identifier = request.uri_segment(3, request.uri_part_count()); + std::string verb = request.get_verb(); + + const MODULECMD* cmd = modulecmd_find_command(module.c_str(), identifier.c_str()); + + if (cmd && !modulecmd_requires_output_dcb(cmd)) + { + if ((!MODULECMD_MODIFIES_DATA(cmd) && verb == MHD_HTTP_METHOD_GET) || + (MODULECMD_MODIFIES_DATA(cmd) && verb == MHD_HTTP_METHOD_POST)) + { + int n_opts = (int)request.get_option_count(); + char* opts[n_opts]; + request.copy_options(opts); + + MODULECMD_ARG* args = modulecmd_arg_parse(cmd, n_opts, (const void**)opts); + bool rval = false; + + if (args) + { + rval = modulecmd_call_command(cmd, args); + } + + for (int i = 0; i < n_opts; i++) + { + MXS_FREE(opts[i]); + } + + return HttpResponse(rval ? MHD_HTTP_OK : MHD_HTTP_INTERNAL_SERVER_ERROR); + } + } + + return HttpResponse(MHD_HTTP_NOT_FOUND); +} + HttpResponse cb_send_ok(const HttpRequest& request) { return HttpResponse(MHD_HTTP_OK); @@ -563,6 +612,18 @@ public: typedef std::shared_ptr SResource; typedef list ResourceList; + /** + * Create REST API resources + * + * Each resource represents either a collection of resources, an individual + * resource, a sub-resource of a resource or an "action" endpoint which + * executes an action. + * + * The resources are defined by the Resource class. Each resource maps to a + * HTTP method and one or more paths. The path components can contain either + * an explicit string, a colon-prefixed object type or a question mark for + * a path component that matches everything. + */ RootResource() { // Special resources required by OPTION etc. @@ -594,6 +655,9 @@ public: m_get.push_back(SResource(new Resource(cb_all_modules, 2, "maxscale", "modules"))); m_get.push_back(SResource(new Resource(cb_module, 3, "maxscale", "modules", ":module"))); + /** For all read-only module commands */ + m_get.push_back(SResource(new Resource(cb_modulecmd, 4, "maxscale", "modules", ":module", "?"))); + m_get.push_back(SResource(new Resource(cb_all_users, 1, "users"))); m_get.push_back(SResource(new Resource(cb_all_inet_users, 2, "users", "inet"))); m_get.push_back(SResource(new Resource(cb_all_unix_users, 2, "users", "unix"))); @@ -609,6 +673,9 @@ public: m_post.push_back(SResource(new Resource(cb_create_user, 2, "users", "inet"))); m_post.push_back(SResource(new Resource(cb_create_user, 2, "users", "unix"))); + /** For all module commands that modify state/data */ + m_post.push_back(SResource(new Resource(cb_modulecmd, 4, "maxscale", "modules", ":module", "?"))); + /** Update resources */ m_put.push_back(SResource(new Resource(cb_alter_server, 2, "servers", ":server"))); m_put.push_back(SResource(new Resource(cb_alter_monitor, 2, "monitors", ":monitor"))); diff --git a/server/core/test/testmodulecmd.cc b/server/core/test/testmodulecmd.cc index c1674939d..e98ba442d 100644 --- a/server/core/test/testmodulecmd.cc +++ b/server/core/test/testmodulecmd.cc @@ -66,10 +66,10 @@ int test_arguments() TEST(modulecmd_find_command(ns, id) == NULL, "The registered command should not yet be found"); TEST(strlen(modulecmd_get_error()), "Error message should not be empty"); - TEST(modulecmd_register_command(ns, id, test_fn, 2, args1), + TEST(modulecmd_register_command(ns, id, MODULECMD_TYPE_ACTIVE, test_fn, 2, args1), "Registering a command should succeed"); - TEST(!modulecmd_register_command(ns, id, test_fn, 2, args1), + TEST(!modulecmd_register_command(ns, id, MODULECMD_TYPE_ACTIVE, test_fn, 2, args1), "Registering the command a second time should fail"); TEST(strlen(modulecmd_get_error()), "Error message should not be empty"); @@ -162,7 +162,7 @@ int test_optional_arguments() {MODULECMD_ARG_BOOLEAN | MODULECMD_ARG_OPTIONAL, ""} }; - TEST(modulecmd_register_command(ns, id, test_fn2, 2, args1), + TEST(modulecmd_register_command(ns, id, MODULECMD_TYPE_ACTIVE, test_fn2, 2, args1), "Registering a command should succeed"); const MODULECMD *cmd = modulecmd_find_command(ns, id); @@ -234,7 +234,7 @@ int test_module_errors() const char *ns = "test_module_errors"; const char *id = "test_module_errors"; - TEST(modulecmd_register_command(ns, id, test_fn3, 0, NULL), + TEST(modulecmd_register_command(ns, id, MODULECMD_TYPE_ACTIVE, test_fn3, 0, NULL), "Registering a command should succeed"); const MODULECMD *cmd = modulecmd_find_command(ns, id); @@ -266,7 +266,7 @@ int test_map() { char id[200]; sprintf(id, "test_map%d", i + 1); - TEST(modulecmd_register_command(map_dom, id, test_fn_map, 0, NULL), + TEST(modulecmd_register_command(map_dom, id, MODULECMD_TYPE_ACTIVE, test_fn_map, 0, NULL), "Registering a command should succeed"); } @@ -332,7 +332,8 @@ int test_pointers() {MODULECMD_ARG_DCB, ""} }; - TEST(modulecmd_register_command(ns, id, ptrfn, 1, args), "Registering a command should succeed"); + TEST(modulecmd_register_command(ns, id, MODULECMD_TYPE_ACTIVE, 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); @@ -366,7 +367,8 @@ int test_domain_matching() {MODULECMD_ARG_MONITOR | MODULECMD_ARG_NAME_MATCHES_DOMAIN, ""} }; - TEST(modulecmd_register_command(ns, id, monfn, 1, args), "Registering a command should succeed"); + TEST(modulecmd_register_command(ns, id, MODULECMD_TYPE_ACTIVE, monfn, 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); diff --git a/server/modules/authenticator/CDCPlainAuth/cdc_plain_auth.c b/server/modules/authenticator/CDCPlainAuth/cdc_plain_auth.c index eb9822e66..dff5dadc5 100644 --- a/server/modules/authenticator/CDCPlainAuth/cdc_plain_auth.c +++ b/server/modules/authenticator/CDCPlainAuth/cdc_plain_auth.c @@ -154,7 +154,7 @@ MXS_MODULE* MXS_CREATE_MODULE() { MODULECMD_ARG_STRING, "Password of the user"} }; - modulecmd_register_command("cdc", "add_user", cdc_add_new_user, 3, args); + modulecmd_register_command("cdc", "add_user", MODULECMD_TYPE_ACTIVE, cdc_add_new_user, 3, args); static MXS_AUTHENTICATOR MyObject = { diff --git a/server/modules/filter/cache/cachefilter.cc b/server/modules/filter/cache/cachefilter.cc index b7e6e8ea1..c851bad8e 100644 --- a/server/modules/filter/cache/cachefilter.cc +++ b/server/modules/filter/cache/cachefilter.cc @@ -147,8 +147,8 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE() { MODULECMD_ARG_FILTER | MODULECMD_ARG_NAME_MATCHES_DOMAIN, "Cache name" } }; - modulecmd_register_command(MXS_MODULE_NAME, "show", cache_command_show, - MXS_ARRAY_NELEMS(show_argv), show_argv); + modulecmd_register_command(MXS_MODULE_NAME, "show", MODULECMD_TYPE_PASSIVE, + cache_command_show, MXS_ARRAY_NELEMS(show_argv), show_argv); MXS_NOTICE("Initialized cache module %s.\n", VERSION_STRING); diff --git a/server/modules/filter/dbfwfilter/dbfwfilter.c b/server/modules/filter/dbfwfilter/dbfwfilter.c index 2c7546eb5..094e4d199 100644 --- a/server/modules/filter/dbfwfilter/dbfwfilter.c +++ b/server/modules/filter/dbfwfilter/dbfwfilter.c @@ -826,7 +826,8 @@ MXS_MODULE* MXS_CREATE_MODULE() {MODULECMD_ARG_STRING | MODULECMD_ARG_OPTIONAL, "Path to rule file"} }; - modulecmd_register_command(MXS_MODULE_NAME, "rules/reload", dbfw_reload_rules, 2, args_rules_reload); + modulecmd_register_command(MXS_MODULE_NAME, "rules/reload", MODULECMD_TYPE_ACTIVE, + dbfw_reload_rules, 2, args_rules_reload); modulecmd_arg_type_t args_rules_show[] = { @@ -834,7 +835,8 @@ MXS_MODULE* MXS_CREATE_MODULE() {MODULECMD_ARG_FILTER | MODULECMD_ARG_NAME_MATCHES_DOMAIN, "Filter to inspect"} }; - modulecmd_register_command(MXS_MODULE_NAME, "rules", dbfw_show_rules, 2, args_rules_show); + modulecmd_register_command(MXS_MODULE_NAME, "rules", MODULECMD_TYPE_PASSIVE, + dbfw_show_rules, 2, args_rules_show); static MXS_FILTER_OBJECT MyObject = { diff --git a/server/modules/filter/masking/maskingfilter.cc b/server/modules/filter/masking/maskingfilter.cc index 0147c1478..c1bf792d5 100644 --- a/server/modules/filter/masking/maskingfilter.cc +++ b/server/modules/filter/masking/maskingfilter.cc @@ -64,7 +64,8 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE() { MODULECMD_ARG_FILTER | MODULECMD_ARG_NAME_MATCHES_DOMAIN, "Masking name" } }; - modulecmd_register_command(MXS_MODULE_NAME, "reload", masking_command_reload, + modulecmd_register_command(MXS_MODULE_NAME, "reload", + MODULECMD_TYPE_ACTIVE, masking_command_reload, MXS_ARRAY_NELEMS(reload_argv), reload_argv); MXS_NOTICE("Masking module %s initialized.", VERSION_STRING); diff --git a/server/modules/routing/avrorouter/avro.c b/server/modules/routing/avrorouter/avro.c index 589172596..28c395d51 100644 --- a/server/modules/routing/avrorouter/avro.c +++ b/server/modules/routing/avrorouter/avro.c @@ -148,7 +148,7 @@ MXS_MODULE* MXS_CREATE_MODULE() { MODULECMD_ARG_SERVICE | MODULECMD_ARG_NAME_MATCHES_DOMAIN, "The avrorouter service" }, { MODULECMD_ARG_STRING, "Action, whether to 'start' or 'stop' the conversion process" } }; - modulecmd_register_command(MXS_MODULE_NAME, "convert", avro_handle_convert, 2, args); + modulecmd_register_command(MXS_MODULE_NAME, "convert", MODULECMD_TYPE_ACTIVE, avro_handle_convert, 2, args); static MXS_ROUTER_OBJECT MyObject = {