/* * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is also distributed with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have included with MySQL. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "connection_manager.h" #include #include #include #include #include "my_dbug.h" #include "../processor/variable_names.h" google::protobuf::LogHandler *g_lh = nullptr; Connection_manager::Connection_manager(const Connection_options &co, Variable_container *variables, const Console &console) : m_default_connection_options(co), m_variables(variables), m_console(console) { g_lh = google::protobuf::SetLogHandler([](google::protobuf::LogLevel level, const char *filename, int line, const std::string &message) { if (g_lh) g_lh(level, filename, line, message); DBUG_LOG("debug", "Protobuf error (level:" << level << ", filename:" << filename << ":" << line << ", text:" << message); }); m_variables->make_special_variable( k_variable_option_user, new Variable_dynamic_string(m_default_connection_options.user)); m_variables->make_special_variable( k_variable_option_pass, new Variable_dynamic_string(m_default_connection_options.password)); m_variables->make_special_variable( k_variable_option_host, new Variable_dynamic_string(m_default_connection_options.host)); m_variables->make_special_variable( k_variable_option_socket, new Variable_dynamic_string(m_default_connection_options.socket)); m_variables->make_special_variable( k_variable_option_schema, new Variable_dynamic_string(m_default_connection_options.schema)); m_variables->make_special_variable( k_variable_option_port, new Variable_dynamic_int(m_default_connection_options.port)); m_variables->make_special_variable( k_variable_option_ssl_mode, new Variable_dynamic_string(m_default_connection_options.ssl_mode)); m_variables->make_special_variable( k_variable_option_ssl_cipher, new Variable_dynamic_string(m_default_connection_options.ssl_cipher)); m_variables->make_special_variable( k_variable_option_tls_version, new Variable_dynamic_string(m_default_connection_options.allowed_tls)); m_variables->make_special_variable( k_variable_option_compression_algorithm, new Variable_dynamic_array_of_strings( m_default_connection_options.compression_algorithm)); m_variables->make_special_variable( k_variable_option_compression_server_style, new Variable_dynamic_array_of_strings( m_default_connection_options.compression_server_style)); m_variables->make_special_variable( k_variable_option_compression_client_style, new Variable_dynamic_array_of_strings( m_default_connection_options.compression_client_style)); m_active_holder.reset(new Session_holder(xcl::create_session(), m_console, m_default_connection_options)); m_session_holders[""] = m_active_holder; } Connection_manager::~Connection_manager() { std::vector disconnect_following; for (const auto &connection : m_session_holders) { if (connection.first != "") { disconnect_following.push_back(connection.first); } } for (auto &name : disconnect_following) { safe_close(name); } if (m_session_holders.count("") > 0) { safe_close(""); } } void Connection_manager::get_credentials(std::string *ret_user, std::string *ret_pass) { assert(ret_user); assert(ret_pass); *ret_user = m_default_connection_options.user; *ret_pass = m_default_connection_options.password; } void Connection_manager::safe_close(const std::string &name) { try { set_active(name, true); close_active(true, true); } catch (const std::exception &) { } catch (const xcl::XError &) { } } void Connection_manager::connect_default(const bool send_cap_password_expired, const bool client_interactive, const bool no_auth, const bool connect_attrs) { m_console.print_verbose("Connecting...\n"); auto session = m_active_holder->get_session(); if (send_cap_password_expired) { session->set_capability( xcl::XSession::Capability_can_handle_expired_password, true); } if (client_interactive) { session->set_capability(xcl::XSession::Capability_client_interactive, true); } if (connect_attrs) { auto attrs = session->get_connect_attrs(); attrs.emplace_back("program_name", xcl::Argument_value{"mysqlxtest"}); session->set_capability(xcl::XSession::Capability_session_connect_attrs, attrs, false); } xcl::XError error = m_active_holder->connect(no_auth); if (error) { // In case of configuration error, lets do safe_close to synchronize // closing of socket with exist of mysqlxtest (in other case mysqlxtest // is going to exit and after a while the connection is going to be // accepted on server side). if (CR_X_TLS_WRONG_CONFIGURATION != error.error() || no_auth) { session->get_protocol().get_connection().close(); } throw error; } setup_variables(session); m_console.print_verbose("Connected client #", session->client_id(), "\n"); } void Connection_manager::create(const std::string &name, const std::string &user, const std::string &password, const std::string &db, const std::vector &auth_methods, const bool is_raw_connection) { if (m_session_holders.count(name)) throw std::runtime_error("a session named " + name + " already exists"); m_console.print_verbose("Connecting...\n"); Connection_options co = m_default_connection_options; if (!user.empty()) { co.user = user; co.password = password; } if (!db.empty()) { co.schema = db; } if (!auth_methods.empty()) { co.auth_methods = auth_methods; } auto session = xcl::create_session(); std::shared_ptr holder{ new Session_holder(std::move(session), m_console, co)}; xcl::XError error = holder->connect(is_raw_connection); if (error) { throw error; } m_active_holder = holder; m_session_holders[name] = holder; m_active_session_name = name; setup_variables(active_xsession()); m_console.print_verbose("Connected client #", active_xsession()->client_id(), "\n"); } void Connection_manager::abort_active() { if (m_active_holder) { if (!m_active_session_name.empty()) std::cout << "aborting session " << m_active_session_name << "\n"; /* Close connection first, to stop XSession from executing Disconnection flow */ active_xconnection()->close(); m_active_holder.reset(); m_session_holders.erase(m_active_session_name); if (m_active_session_name != "") set_active(""); } else { throw std::runtime_error("no active session"); } } bool Connection_manager::is_default_active() { return m_active_session_name.empty(); } void Connection_manager::close_active(const bool shutdown, const bool be_quiet) { if (m_active_holder) { if (m_active_session_name.empty() && !shutdown) throw std::runtime_error("cannot close default session"); try { if (!m_active_session_name.empty() && !be_quiet) m_console.print("closing session ", m_active_session_name, "\n"); if (active_xconnection()->state().is_connected()) { /* no close needed // send a close message and wait for the corresponding Ok message active_xprotocol()->send(Polarx::Connection::Close()); xcl::XProtocol::Server_message_type_id msgid; xcl::XError error; Message_ptr msg{ active_xprotocol()->recv_single_message(&msgid, &error)}; if (error) throw error; if (!be_quiet) m_console.print(*msg); if (Polarx::ServerMessages::OK != msgid) throw xcl::XError(CR_COMMANDS_OUT_OF_SYNC, "Disconnect was expecting Mysqlx.Ok(bye!), but " "got the one above (one or more calls to -->recv " "are probably missing)"); std::string text = static_cast(msg.get())->msg(); if (text != "bye!" && text != "tchau!") throw xcl::XError(CR_COMMANDS_OUT_OF_SYNC, "Disconnect was expecting Mysqlx.Ok(bye!), but " "got the one above (one or more calls to -->recv " "are probably missing)"); if (!m_default_connection_options.dont_wait_for_disconnect) { Message_ptr msg{ active_xprotocol()->recv_single_message(&msgid, &error)}; if (!error && !be_quiet) { m_console.print_error("Was expecting closure but got message:", *msg); } } */ active_xconnection()->close(); } m_session_holders.erase(m_active_session_name); if (!shutdown) set_active("", be_quiet); } catch (const std::exception &error) { active_xconnection()->close(); m_session_holders.erase(m_active_session_name); if (!shutdown) set_active("", be_quiet); throw error; } catch (const xcl::XError &error) { active_xconnection()->close(); m_session_holders.erase(m_active_session_name); if (!shutdown) set_active("", be_quiet); throw error; } } else { if (!shutdown) throw std::runtime_error("no active session"); } } void Connection_manager::set_active(const std::string &name, const bool be_quiet) { if (m_session_holders.count(name) == 0) { std::string slist; for (auto it = m_session_holders.begin(); it != m_session_holders.end(); ++it) slist.append(it->first).append(", "); if (!slist.empty()) slist.resize(slist.length() - 2); throw std::runtime_error("no session named '" + name + "': " + slist); } m_active_holder = m_session_holders[name]; m_active_session_name = name; setup_variables(active_xsession()); if (!be_quiet) m_console.print( "switched to session ", (m_active_session_name.empty() ? "default" : m_active_session_name), "\n"); } Session_holder &Connection_manager::active_holder() { if (!m_active_holder) throw std::runtime_error("no active session"); return *m_active_holder; } xcl::XSession *Connection_manager::active_xsession() { if (!m_active_holder) throw std::runtime_error("no active session"); return m_active_holder->get_session(); } xcl::XProtocol *Connection_manager::active_xprotocol() { return &active_xsession()->get_protocol(); } xcl::XConnection *Connection_manager::active_xconnection() { return &active_xprotocol()->get_connection(); } uint64_t Connection_manager::active_session_messages_received( const std::string &message_name) const { uint64_t result = 0; m_active_holder->try_get_number_of_received_messages(message_name, &result); return result; } void Connection_manager::setup_variables(xcl::XSession *session) { auto &connection = session->get_protocol().get_connection(); m_variables->set(k_variable_active_client_id, std::to_string(session->client_id())); m_variables->set(k_variable_active_socket_id, std::to_string(connection.get_socket_fd())); }