diff --git a/Documentation/REST-API/Resources-Service.md b/Documentation/REST-API/Resources-Service.md index 152ada2ac..a7ee5e660 100644 --- a/Documentation/REST-API/Resources-Service.md +++ b/Documentation/REST-API/Resources-Service.md @@ -473,7 +473,7 @@ Invalid JSON body: Stops a started service. ``` -PUT /v1/service/:name/stop +PUT /v1/services/:name/stop ``` #### Response @@ -487,7 +487,7 @@ Service is stopped: Starts a stopped service. ``` -PUT /v1/service/:name/start +PUT /v1/services/:name/start ``` #### Response diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index 3eddba677..32376c680 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -98,7 +98,9 @@ function(install_script target component) list(FIND TARGET_COMPONENT ${component} BUILD_COMPONENT) if(BUILD_COMPONENT GREATER -1 OR BUILD_ALL GREATER -1) - install(PROGRAMS ${target} DESTINATION ${MAXSCALE_BINDIR} COMPONENT "${component}") + install(PROGRAMS ${target} DESTINATION ${MAXSCALE_BINDIR} + PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE OWNER_READ GROUP_READ WORLD_READ + COMPONENT "${component}") endif() endfunction() diff --git a/include/maxscale/protocol/mysql.h b/include/maxscale/protocol/mysql.h index ebe695e6c..dafdf31b5 100644 --- a/include/maxscale/protocol/mysql.h +++ b/include/maxscale/protocol/mysql.h @@ -353,6 +353,7 @@ typedef struct int ignore_replies; /*< How many replies should be discarded */ GWBUF* stored_query; /*< Temporarily stored queries */ bool collect_result; /*< Collect the next result set as one buffer */ + bool changing_user; uint32_t num_eof_packets; /*< Encountered eof packet number, used for check packet type */ #if defined(SS_DEBUG) skygw_chk_t protocol_chk_tail; diff --git a/maxctrl/maxctrl.js b/maxctrl/maxctrl.js index f1206dadb..4c2a7b7cc 100644 --- a/maxctrl/maxctrl.js +++ b/maxctrl/maxctrl.js @@ -21,6 +21,11 @@ function print(out) { } } +function err(out) { + print(out) + process.exit(1); +} + // Mangle the arguments if we are being called from the command line if (process.argv[0] == process.execPath) { process.argv.shift() @@ -29,4 +34,4 @@ if (process.argv[0] == process.execPath) { } maxctrl.execute(process.argv) - .then(print, print) + .then(print, err) diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index 10afe9eb1..7b04c32b0 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -701,6 +701,10 @@ add_test_executable(mxs1808_long_data.cpp mxs1808_long_data replication LABELS r # https://jira.mariadb.org/browse/MXS-1824 add_test_executable(mxs1824_double_cursor.cpp mxs1824_double_cursor replication LABELS readwritesplit REPL_BACKEND) +# MXS-1831: No error on invalid monitor parameter alteration +# https://jira.mariadb.org/browse/MXS-1831 +add_test_executable(mxs1831_unknown_param.cpp mxs1831_unknown_param replication LABELS REPL_BACKEND) + # 'namedserverfilter' test add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND) diff --git a/maxscale-system-test/mxs1831_unknown_param.cpp b/maxscale-system-test/mxs1831_unknown_param.cpp new file mode 100644 index 000000000..e42c2861e --- /dev/null +++ b/maxscale-system-test/mxs1831_unknown_param.cpp @@ -0,0 +1,19 @@ +/** + * MXS-1831: No error on invalid monitor parameter alteration + * + * https://jira.mariadb.org/browse/MXS-1831 + */ + +#include "testconnections.h" + +int main(int argc, char** argv) +{ + TestConnections test(argc, argv); + + int rc = test.maxscales->ssh_node_f(0, true, "maxctrl alter monitor MySQL-Monitor not_a_parameter not_a_value|grep Error"); + test.assert(rc == 0, "Altering unknown parameter should cause an error"); + rc = test.maxscales->ssh_node_f(0, true, "maxctrl alter monitor MySQL-Monitor ignore_external_masters on_sunday_afternoons|grep Error"); + test.assert(rc == 0, "Invalid parameter value should cause an error"); + + return test.global_result; +} diff --git a/server/core/config_runtime.cc b/server/core/config_runtime.cc index a8725ba8d..f7cb435cd 100644 --- a/server/core/config_runtime.cc +++ b/server/core/config_runtime.cc @@ -448,6 +448,7 @@ bool runtime_alter_monitor(MXS_MONITOR *monitor, const char *key, const char *va { spinlock_acquire(&crt_lock); bool valid = false; + const MXS_MODULE *mod = get_module(monitor->module_name, MODULE_MONITOR); if (strcmp(key, CN_USER) == 0) { @@ -522,7 +523,7 @@ bool runtime_alter_monitor(MXS_MONITOR *monitor, const char *key, const char *va monitorSetScriptTimeout(monitor, ival); } } - else + else if (config_param_is_valid(mod->parameters, key, value, NULL)) { /** We're modifying module specific parameters and we need to stop the monitor */ monitorStop(monitor); diff --git a/server/core/dcb.cc b/server/core/dcb.cc index 7c604b47d..36397beac 100644 --- a/server/core/dcb.cc +++ b/server/core/dcb.cc @@ -3458,13 +3458,14 @@ int poll_add_dcb(DCB *dcb) worker_id = MXS_WORKER_ALL; } else if (dcb->dcb_role == DCB_ROLE_CLIENT_HANDLER && - strcasecmp(dcb->service->routerModule, "cli") == 0) + (strcasecmp(dcb->service->routerModule, "cli") == 0 || + strcasecmp(dcb->service->routerModule, "maxinfo") == 0)) { - // If the DCB refers to an accepted maxadmin socket, we force it + // If the DCB refers to an accepted maxadmin/maxinfo socket, we force it // to the main thread. That's done in order to prevent a deadlock - // that may happen if there are multiple concurrent maxadmin calls, + // that may happen if there are multiple concurrent administrative calls, // handled by different worker threads. - // See: https://jira.mariadb.org/browse/MXS-1805 + // See: https://jira.mariadb.org/browse/MXS-1805 and https://jira.mariadb.org/browse/MXS-1833 new_state = DCB_STATE_POLLING; dcb->poll.thread.id = 0; worker_id = dcb->poll.thread.id; diff --git a/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.c b/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.c index 023a3b6ef..f53558e77 100644 --- a/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.c +++ b/server/modules/protocol/MySQL/mariadbbackend/mysql_backend.c @@ -708,6 +708,37 @@ static inline bool not_err_packet(const GWBUF* buffer) return GWBUF_DATA(buffer)[4] != MYSQL_REPLY_ERR; } +static inline bool auth_change_requested(GWBUF* buf) +{ + return mxs_mysql_get_command(buf) == MYSQL_REPLY_AUTHSWITCHREQUEST && + gwbuf_length(buf) > MYSQL_EOF_PACKET_LEN; +} + +static bool handle_auth_change_response(GWBUF* reply, MySQLProtocol* proto, DCB* dcb) +{ + bool rval = false; + + if (strcmp((char*)GWBUF_DATA(reply) + 5, DEFAULT_MYSQL_AUTH_PLUGIN) == 0) + { + /** + * The server requested a change of authentication methods. + * If we're changing the authentication method to the same one we + * are using now, it means that the server is simply generating + * a new scramble for the re-authentication process. + */ + + // Load the new scramble into the protocol... + gwbuf_copy_data(reply, 5 + strlen(DEFAULT_MYSQL_AUTH_PLUGIN) + 1, + GW_MYSQL_SCRAMBLE_SIZE, proto->scramble); + + /// ... and use it to send the encrypted password to the server + rval = send_mysql_native_password_response(dcb); + + } + + return rval; +} + /** * @brief With authentication completed, read new data and write to backend * @@ -829,6 +860,19 @@ gw_read_and_write(DCB *dcb) } } + if (proto->changing_user) + { + if (auth_change_requested(read_buffer) && + handle_auth_change_response(read_buffer, proto, dcb)) + { + return 0; + } + else + { + proto->changing_user = false; + } + } + if (proto->ignore_replies > 0) { /** The reply to a COM_CHANGE_USER is in packet */ @@ -855,28 +899,16 @@ gw_read_and_write(DCB *dcb) MXS_INFO("Response to COM_CHANGE_USER is OK, writing stored query"); rval = query ? dcb->func.write(dcb, query) : 1; } - else if (result == MYSQL_REPLY_AUTHSWITCHREQUEST && - gwbuf_length(reply) > MYSQL_EOF_PACKET_LEN) + else if (auth_change_requested(reply)) { - /** - * The server requested a change of authentication methods. - * If we're changing the authentication method to the same one we - * are using now, it means that the server is simply generating - * a new scramble for the re-authentication process. - */ - if (strcmp((char*)GWBUF_DATA(reply) + 5, DEFAULT_MYSQL_AUTH_PLUGIN) == 0) + if (handle_auth_change_response(reply, proto, dcb)) { - /** Load the new scramble into the protocol... */ - gwbuf_copy_data(reply, 5 + strlen(DEFAULT_MYSQL_AUTH_PLUGIN) + 1, - GW_MYSQL_SCRAMBLE_SIZE, proto->scramble); - - /** ... and use it to send the encrypted password to the server */ - rval = send_mysql_native_password_response(dcb); - /** Store the query until we know the result of the authentication * method switch. */ proto->stored_query = query; proto->ignore_replies++; + + gwbuf_free(reply); return rval; } else @@ -889,7 +921,6 @@ gw_read_and_write(DCB *dcb) // TODO: Use the authenticators to handle COM_CHANGE_USER responses MXS_ERROR("Received AuthSwitchRequest to '%s' when '%s' was expected", (char*)GWBUF_DATA(reply) + 5, DEFAULT_MYSQL_AUTH_PLUGIN); - } } else @@ -1993,8 +2024,10 @@ gw_send_change_user_to_backend(char *dbname, if (rc != 0) { + conn->changing_user = true; rc = 1; } + return rc; } diff --git a/server/modules/protocol/MySQL/mysql_common.cc b/server/modules/protocol/MySQL/mysql_common.cc index 11c1c8172..3c4470420 100644 --- a/server/modules/protocol/MySQL/mysql_common.cc +++ b/server/modules/protocol/MySQL/mysql_common.cc @@ -71,6 +71,7 @@ MySQLProtocol* mysql_protocol_init(DCB* dcb, int fd) p->extra_capabilities = 0; p->ignore_replies = 0; p->collect_result = false; + p->changing_user = false; p->num_eof_packets = 0; #if defined(SS_DEBUG) p->protocol_chk_top = CHK_NUM_PROTOCOL;