375 lines
12 KiB
C++
375 lines
12 KiB
C++
//
|
|
// Created by zzy on 2022/8/31.
|
|
//
|
|
|
|
#include "../global_defines.h"
|
|
#ifndef MYSQL8
|
|
#define MYSQL_SERVER
|
|
#endif
|
|
#include "mysql/service_command.h"
|
|
#include "sql/mysqld.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/srv_session.h"
|
|
|
|
#include "../coders/callback_command_delegate.h"
|
|
#include "../polarx_rpc.h"
|
|
#include "../server/server_variables.h"
|
|
|
|
#include "session_base.h"
|
|
|
|
namespace polarx_rpc {
|
|
|
|
CsessionBase::~CsessionBase() {
|
|
if (mysql_session_ != nullptr) {
|
|
srv_session_close(mysql_session_);
|
|
mysql_session_ = nullptr;
|
|
}
|
|
plugin_info.total_sessions.fetch_sub(1, std::memory_order_release);
|
|
}
|
|
|
|
err_t CsessionBase::init(uint16_t port) {
|
|
if (mysql_session_ != nullptr)
|
|
return err_t(ER_POLARX_RPC_ERROR_MSG, "Session inited");
|
|
|
|
mysql_session_ =
|
|
srv_session_open(&CsessionBase::default_completion_handler, this);
|
|
if (nullptr == mysql_session_) {
|
|
if (ER_SERVER_ISNT_AVAILABLE == last_sql_errno_)
|
|
return err_t(ER_SERVER_ISNT_AVAILABLE, "Server API not ready");
|
|
return {last_sql_errno_ != 0 ? static_cast<int>(last_sql_errno_)
|
|
: ER_POLARX_RPC_ERROR_MSG,
|
|
"Could not open session"};
|
|
}
|
|
|
|
if (srv_session_info_set_connection_type(mysql_session_, VIO_TYPE_TCPIP) != 0)
|
|
return err_t(ER_POLARX_RPC_ERROR_MSG,
|
|
"Could not set session connection type");
|
|
if (0 != srv_session_info_set_client_port(mysql_session_, port))
|
|
return err_t(ER_POLARX_RPC_ERROR_MSG, "Could not set session client port");
|
|
|
|
mysql_session_->set_safe(true);
|
|
flow_control_.set_thd(get_thd());
|
|
return err_t::Success();
|
|
}
|
|
|
|
void CsessionBase::default_completion_handler(void *ctx, unsigned int sql_errno,
|
|
const char *err_msg) {
|
|
auto self = reinterpret_cast<CsessionBase *>(ctx);
|
|
self->last_sql_errno_ = sql_errno;
|
|
self->last_sql_error_ = err_msg ? err_msg : "";
|
|
}
|
|
|
|
err_t CsessionBase::switch_to_user(const char *username, const char *hostname,
|
|
const char *address, const char *db) {
|
|
MYSQL_SECURITY_CONTEXT scontext;
|
|
|
|
if (thd_get_security_context(get_thd(), &scontext) != 0)
|
|
return err_t(ER_ACCESS_DENIED_ERROR,
|
|
"Error getting security context for session");
|
|
|
|
// security_context_lookup
|
|
// doesn't make a copy of username, hostname, address or db thus we need to
|
|
// make a copy of them and pass our pointers to security_context_lookup
|
|
username_ = username ? username : "";
|
|
hostname_ = hostname ? hostname : "";
|
|
address_ = address ? address : "";
|
|
db_ = db ? db : "";
|
|
|
|
if (security_context_lookup(scontext, username_.c_str(), hostname_.c_str(),
|
|
address_.c_str(), db_.c_str()) != 0)
|
|
return err_t::Error(ER_ACCESS_DENIED_ERROR,
|
|
"Unable to switch context to user %s", username);
|
|
return err_t::Success();
|
|
}
|
|
|
|
void CsessionBase::set_show_hostname(const char *hostname) {
|
|
show_hostname_ = hostname ? hostname : "";
|
|
auto thd = get_thd();
|
|
if (thd != nullptr) {
|
|
auto sec = thd->security_context();
|
|
if (sec != nullptr)
|
|
sec->set_host_or_ip_ptr(show_hostname_.c_str(), show_hostname_.length());
|
|
}
|
|
}
|
|
|
|
err_t CsessionBase::reset() {
|
|
COM_DATA data;
|
|
CcallbackCommandDelegate delegate;
|
|
auto err = execute_server_command(COM_RESET_CONNECTION, data, delegate);
|
|
return err ? err : delegate.get_error();
|
|
}
|
|
|
|
err_t CsessionBase::init_db(const char *db_name, std::size_t db_len,
|
|
CcommandDelegate &delegate) {
|
|
COM_DATA data;
|
|
data.com_init_db.db_name = db_name;
|
|
data.com_init_db.length = static_cast<unsigned int>(db_len);
|
|
return execute_server_command(COM_INIT_DB, data, delegate);
|
|
}
|
|
|
|
template <typename Result_type>
|
|
static bool get_security_context_value(MYSQL_THD thd, const char *option,
|
|
Result_type *result) {
|
|
MYSQL_SECURITY_CONTEXT scontext;
|
|
|
|
if (thd_get_security_context(thd, &scontext))
|
|
return false;
|
|
|
|
return false == security_context_get_option(scontext, option, result);
|
|
}
|
|
|
|
bool CsessionBase::is_acl_disabled() const {
|
|
MYSQL_LEX_CSTRING value{"", 0};
|
|
if (get_security_context_value(get_thd(), "priv_user", &value)) {
|
|
return 0 != value.length && NULL != strstr(value.str, "skip-grants ");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CsessionBase::has_authenticated_user_a_super_priv() const {
|
|
my_svc_bool value = 0;
|
|
if (get_security_context_value(get_thd(), "privilege_super", &value))
|
|
return value != 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
std::string CsessionBase::get_user_name() const {
|
|
MYSQL_LEX_CSTRING result{"", 0};
|
|
|
|
if (get_security_context_value(get_thd(), "user", &result))
|
|
return result.str;
|
|
|
|
return "";
|
|
}
|
|
|
|
std::string CsessionBase::get_host_or_ip() const {
|
|
MYSQL_LEX_CSTRING result{"", 0};
|
|
|
|
if (get_security_context_value(get_thd(), "host_or_ip", &result))
|
|
return result.str;
|
|
|
|
return "";
|
|
}
|
|
|
|
std::string CsessionBase::get_authenticated_user_name() const {
|
|
MYSQL_LEX_CSTRING result{"", 0};
|
|
|
|
if (get_security_context_value(get_thd(), "priv_user", &result))
|
|
return result.str;
|
|
|
|
return "";
|
|
}
|
|
|
|
std::string CsessionBase::get_authenticated_user_host() const {
|
|
MYSQL_LEX_CSTRING result{"", 0};
|
|
|
|
if (get_security_context_value(get_thd(), "priv_host", &result))
|
|
return result.str;
|
|
|
|
return "";
|
|
}
|
|
|
|
/// hack code for no accounts for polarx
|
|
void CsessionBase::switch_to_sys_user() {
|
|
username_ = "polarxsys";
|
|
hostname_ = "127.0.0.1";
|
|
|
|
MYSQL_SECURITY_CONTEXT scontext;
|
|
thd_get_security_context(get_thd(), &scontext);
|
|
|
|
scontext->set_user_ptr(username_.data(),
|
|
static_cast<int>(username_.length()));
|
|
scontext->set_host_ptr(hostname_.data(),
|
|
static_cast<int>(hostname_.length()));
|
|
scontext->set_ip_ptr(nullptr, 0);
|
|
scontext->set_host_or_ip_ptr();
|
|
|
|
scontext->set_master_access(0x1FFFFFFF);
|
|
#ifdef MYSQL8
|
|
scontext->cache_current_db_access(0);
|
|
#else
|
|
scontext->set_db_access(0);
|
|
#endif
|
|
|
|
scontext->assign_priv_user(username_.data(),
|
|
static_cast<int>(username_.length()));
|
|
scontext->assign_priv_host(hostname_.data(),
|
|
static_cast<int>(hostname_.length()));
|
|
scontext->set_password_expired(false);
|
|
}
|
|
|
|
err_t CsessionBase::authenticate(const char *user, const char *host,
|
|
const char *ip, const char *db,
|
|
const std::string &passwd,
|
|
Authentication_interface &account_verification,
|
|
bool allow_expired_passwords) {
|
|
auto error = switch_to_user(user, host, ip, db);
|
|
if (error)
|
|
return err_t::SQLError_access_denied();
|
|
|
|
if (!is_acl_disabled()) {
|
|
auto authenticated_user_name = get_authenticated_user_name();
|
|
auto authenticated_user_host = get_authenticated_user_host();
|
|
|
|
/// force switch to fake sys user
|
|
switch_to_sys_user();
|
|
|
|
if (!is_acl_disabled()) {
|
|
error = account_verification.authenticate_account(
|
|
authenticated_user_name, authenticated_user_host, passwd);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
/// switch to user
|
|
error = switch_to_user(user, host, ip, db);
|
|
}
|
|
|
|
/// check db
|
|
if (!error) {
|
|
if (db && *db) {
|
|
CcallbackCommandDelegate callback_delegate;
|
|
if (init_db(db, ::strlen(db), callback_delegate))
|
|
return err_t(ER_NO_DB_ERROR, "Could not set database");
|
|
error = callback_delegate.get_error();
|
|
}
|
|
|
|
std::string user_name = get_user_name();
|
|
std::string host_or_ip = get_host_or_ip();
|
|
|
|
/*
|
|
Instead of modifying the current security context in switch_user()
|
|
method above, we must create a security_context to do the
|
|
security_context_lookup() on newly created security_context then set
|
|
that in the THD. Until that happens, we have to get the existing security
|
|
context and set that again in the THD. The latter opertion is nedded as
|
|
it may toggle the system_user flag in THD iff security_context has
|
|
SYSTEM_USER privilege.
|
|
*/
|
|
MYSQL_SECURITY_CONTEXT scontext;
|
|
thd_get_security_context(get_thd(), &scontext);
|
|
thd_set_security_context(get_thd(), scontext);
|
|
|
|
#ifdef HAVE_PSI_THREAD_INTERFACE
|
|
PSI_THREAD_CALL(set_thread_account)
|
|
(user_name.c_str(), static_cast<int>(user_name.length()),
|
|
host_or_ip.c_str(), static_cast<int>(host_or_ip.length()));
|
|
#endif // HAVE_PSI_THREAD_INTERFACE
|
|
|
|
return error;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
err_t CsessionBase::execute_server_command(enum_server_command cmd,
|
|
const COM_DATA &cmd_data,
|
|
CcommandDelegate &delegate) {
|
|
delegate.reset();
|
|
if (command_service_run_command(mysql_session_, cmd, &cmd_data, nullptr,
|
|
CcommandDelegate::callbacks(),
|
|
delegate.representation(), &delegate) != 0) {
|
|
/// if no error spec
|
|
if (!delegate.get_error()) {
|
|
/// check killed
|
|
#ifdef MYSQL8
|
|
auto killed = get_thd()->killed.load(std::memory_order_acquire);
|
|
#else
|
|
auto killed = get_thd()->killed;
|
|
#endif
|
|
if (THD::KILL_CONNECTION == killed)
|
|
return err_t(ER_QUERY_INTERRUPTED, "Query execution was interrupted",
|
|
"70100", err_t::FATAL);
|
|
else if (THD::KILL_QUERY == killed)
|
|
return err_t(ER_QUERY_INTERRUPTED, "Query execution was interrupted",
|
|
"70100");
|
|
else
|
|
return err_t(ER_POLARX_RPC_ERROR_MSG,
|
|
"Internal error executing command");
|
|
} else if (THD::KILL_CONNECTION == get_thd()->killed) {
|
|
auto err = delegate.get_error();
|
|
return err_t(err.error, err.message, err.sql_state, err_t::FATAL);
|
|
}
|
|
}
|
|
auto err = delegate.get_error();
|
|
if (UNLIKELY(err && THD::KILL_CONNECTION == get_thd()->killed))
|
|
return err_t(err.error, err.message, err.sql_state, err_t::FATAL);
|
|
return err;
|
|
}
|
|
|
|
err_t CsessionBase::execute_sql(const char *sql, size_t sql_len,
|
|
CcommandDelegate &delegate) {
|
|
COM_DATA data;
|
|
data.com_query.query = sql;
|
|
data.com_query.length = static_cast<unsigned int>(sql_len);
|
|
return execute_server_command(COM_QUERY, data, delegate);
|
|
}
|
|
|
|
err_t CsessionBase::detach() {
|
|
if (nullptr == mysql_session_ || srv_session_detach(mysql_session_) != 0)
|
|
return err_t(ER_POLARX_RPC_ERROR_MSG, "Internal error when detaching");
|
|
/// Note: we should force clear thd, or stale thd may cause bad memory
|
|
/// access
|
|
#ifdef MYSQL8
|
|
current_thd = nullptr;
|
|
THR_MALLOC = nullptr;
|
|
#else
|
|
my_thread_set_THR_THD(nullptr);
|
|
my_thread_set_THR_MALLOC(nullptr);
|
|
#endif
|
|
return err_t::Success();
|
|
}
|
|
|
|
void CsessionBase::remote_kill() {
|
|
if (enable_kill_log) {
|
|
std::lock_guard<std::mutex> plugin_lck(plugin_info.mutex);
|
|
if (plugin_info.plugin_info != nullptr)
|
|
my_plugin_log_message(&plugin_info.plugin_info, MY_WARNING_LEVEL,
|
|
"Session %p sid %lu killing.", this, sid_);
|
|
}
|
|
|
|
/// store exit flag first
|
|
killed_.store(true, std::memory_order_release);
|
|
/// shutdown flow control
|
|
flow_control_.exit();
|
|
|
|
/// do kill
|
|
auto thd = get_thd();
|
|
if (thd != nullptr) {
|
|
mysql_mutex_lock(&thd->LOCK_thd_data);
|
|
// Lock held and awake with kill.
|
|
if (thd->killed != THD::KILL_CONNECTION)
|
|
thd->awake(THD::KILL_CONNECTION);
|
|
mysql_mutex_unlock(&thd->LOCK_thd_data);
|
|
}
|
|
}
|
|
|
|
void CsessionBase::remote_cancel() {
|
|
if (enable_kill_log) {
|
|
std::lock_guard<std::mutex> plugin_lck(plugin_info.mutex);
|
|
if (plugin_info.plugin_info != nullptr)
|
|
my_plugin_log_message(&plugin_info.plugin_info, MY_WARNING_LEVEL,
|
|
"Session %p sid %lu canceling.", this, sid_);
|
|
}
|
|
|
|
auto thd = get_thd();
|
|
if (thd != nullptr) {
|
|
mysql_mutex_lock(&thd->LOCK_thd_data);
|
|
// Lock held and awake with kill.
|
|
if (thd->killed != THD::KILL_CONNECTION && thd->killed != THD::KILL_QUERY)
|
|
thd->awake(THD::KILL_QUERY);
|
|
mysql_mutex_unlock(&thd->LOCK_thd_data);
|
|
}
|
|
}
|
|
|
|
bool CsessionBase::is_api_ready() {
|
|
#ifdef MYSQL8
|
|
return 0 != srv_session_server_is_available() &&
|
|
!connection_events_loop_aborted();
|
|
#else
|
|
return 0 != srv_session_server_is_available() && !::abort_loop;
|
|
#endif
|
|
}
|
|
|
|
} // namespace polarx_rpc
|