原文地址:Extending MySQL using the Component Infrastructure – part 7: messages to users
原文作者:LEFRED
这篇文章是关于使用组件基础架构扩展 MySQL 的系列文章的第七篇,上面的列表将随着新文章的发布而更新:
- 使用组件基础架构扩展 MySQL – 第 1 部分
- 使用组件基础架构扩展 MySQL - 第 2 部分:构建服务器
- 使用组件基础设施扩展 MySQL - 第 3 部分:组件服务
- 使用组件基础架构扩展 MySQL - 第 4 部分:错误日志记录
- 使用组件基础架构扩展 MySQL - 第 5 部分:权限
- 使用组件基础架构扩展 MySQL - 第 6 部分:函数
- 使用组件基础架构扩展 MySQL - 第 7 部分:给用户的消息
在上一篇文章中,我们编写了检查正确权限的代码,以便能够运行我们的函数。我们能够在错误日志中记录一条消息,但用户没有收到任何消息。
此外,仅当我们希望管理员查看消息时才建议在错误日志中记录消息,现在我们希望用户获取消息。
这正是我们将在本文中解决的问题。
像往常一样,组件基础结构提供了一项服务,我们可以使用它来向用户打印错误:包含在mysql/components/services/mysql_runtime_error_service.h.
让我们修改我们的scan.h以包含该文件并添加所需的服务占位符:
/* Copyright (c) 2017, 2022, 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 */
#define LOG_COMPONENT_TAG "viruscan"
#include <mysql/components/component_implementation.h>
#include <mysql/components/services/log_builtins.h> /* LogComponentErr */
#include <mysqld_error.h> /* Errors */
#include <mysql/components/services/dynamic_privilege.h>
#include <mysql/components/services/udf_metadata.h>
#include <mysql/components/services/udf_registration.h>
#include <mysql/components/services/security_context.h>
#include <mysql/components/services/mysql_current_thread_reader.h>
#include <mysql/components/services/mysql_runtime_error_service.h>
#include <list>
#include <string>
extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins);
extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string);
extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register);
extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration);
extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata);
extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context);
extern REQUIRES_SERVICE_PLACEHOLDER(global_grants_check);
extern REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader);
extern REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error);
extern SERVICE_TYPE(log_builtins) * log_bi;
extern SERVICE_TYPE(log_builtins_string) * log_bs;
这就是我们在第 29 行和第 44 行所做的。
现在我们还可以更新scan.cc以要求该服务并更新我们的viruscan_udf函数以使用该服务,使用正确的错误消息 ( ER_SPECIFIC_ACCESS_DENIED_ERROR) 来替换第 132 行错误日志中的消息(您可以在 中找到这些消息include/mysqld_ername.h):
/* Copyright (c) 2017, 2022, 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 */
#define LOG_COMPONENT_TAG "viruscan"
#define NO_SIGNATURE_CHANGE 0
#define SIGNATURE_CHANGE 1
#include <components/viruscan/scan.h>
REQUIRES_SERVICE_PLACEHOLDER(log_builtins);
REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string);
REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register);
REQUIRES_SERVICE_PLACEHOLDER(udf_registration);
REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata);
REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context);
REQUIRES_SERVICE_PLACEHOLDER(global_grants_check);
REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader);
REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error);
SERVICE_TYPE(log_builtins) * log_bi;
SERVICE_TYPE(log_builtins_string) * log_bs;
static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN";
class udf_list {
typedef std::list<std::string> udf_list_t;
public:
~udf_list() { unregister(); }
bool add_scalar(const char *func_name, enum Item_result return_type,
Udf_func_any func, Udf_func_init init_func = NULL,
Udf_func_deinit deinit_func = NULL) {
if (!mysql_service_udf_registration->udf_register(
func_name, return_type, func, init_func, deinit_func)) {
set.push_back(func_name);
return false;
}
return true;
}
bool unregister() {
udf_list_t delete_set;
/* try to unregister all of the udfs */
for (auto udf : set) {
int was_present = 0;
if (!mysql_service_udf_registration->udf_unregister(udf.c_str(),
&was_present) ||
!was_present)
delete_set.push_back(udf);
}
/* remove the unregistered ones from the list */
for (auto udf : delete_set) set.remove(udf);
/* success: empty set */
if (set.empty()) return false;
/* failure: entries still in the set */
return true;
}
private:
udf_list_t set;
} * list;
namespace udf_impl {
bool have_virus_scan_privilege(void *opaque_thd) {
// get the security context of the thread
Security_context_handle ctx = nullptr;
if (mysql_service_mysql_thd_security_context->get(opaque_thd, &ctx) || !ctx) {
LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
"problem trying to get security context");
return false;
}
if (mysql_service_global_grants_check->has_global_grant(
ctx, SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME)))
return true;
return false;
}
const char *udf_init = "udf_init", *my_udf = "my_udf",
*my_udf_clear = "my_clear", *my_udf_add = "my_udf_add";
static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) {
const char* name = "utf8mb4";
char *value = const_cast<char*>(name);
initid->ptr = const_cast<char *>(udf_init);
if (mysql_service_mysql_udf_metadata->result_set(
initid, "charset",
const_cast<char *>(value))) {
LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset");
return false;
}
return 0;
}
static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) {
assert(initid->ptr == udf_init || initid->ptr == my_udf);
}
const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp,
unsigned long *length, char *is_null, char *error) {
MYSQL_THD thd;
mysql_service_mysql_current_thread_reader->get(&thd);
if(!have_virus_scan_privilege(thd)) {
mysql_error_service_printf(
ER_SPECIFIC_ACCESS_DENIED_ERROR, 0,
SCAN_PRIVILEGE_NAME);
return 0;
}
strcpy(outp, "we do nothing yet");
*length = strlen(outp);
return const_cast<char *>(outp);
}
} /* namespace udf_impl */
static mysql_service_status_t viruscan_service_init() {
mysql_service_status_t result = 0;
log_bi = mysql_service_log_builtins;
log_bs = mysql_service_log_builtins_string;
LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…");
// Registration of the privilege
if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) {
LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
"could not register privilege 'VIRUS_SCAN'.");
result = 1;
} else {
LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG,
"new privilege 'VIRUS_SCAN' has been registered successfully.");
}
list = new udf_list();
if (list->add_scalar("virus_scan", Item_result::STRING_RESULT,
(Udf_func_any)udf_impl::viruscan_udf,
udf_impl::viruscan_udf_init,
udf_impl::viruscan_udf_deinit)) {
delete list;
return 1; /* failure: one of the UDF registrations failed */
}
return result;
}
static mysql_service_status_t viruscan_service_deinit() {
mysql_service_status_t result = 0;
if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) {
LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
"could not unregister privilege 'VIRUS_SCAN'.");
result = 1;
} else {
LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG,
"privilege 'VIRUS_SCAN' has been unregistered successfully.");
}
if (list->unregister()) return 1; /* failure: some UDFs still in use */
delete list;
LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled.");
return result;
}
BEGIN_COMPONENT_PROVIDES(viruscan_service)
END_COMPONENT_PROVIDES();
BEGIN_COMPONENT_REQUIRES(viruscan_service)
REQUIRES_SERVICE(log_builtins),
REQUIRES_SERVICE(log_builtins_string),
REQUIRES_SERVICE(dynamic_privilege_register),
REQUIRES_SERVICE(mysql_udf_metadata),
REQUIRES_SERVICE(udf_registration),
REQUIRES_SERVICE(mysql_thd_security_context),
REQUIRES_SERVICE(global_grants_check),
REQUIRES_SERVICE(mysql_current_thread_reader),
REQUIRES_SERVICE(mysql_runtime_error),
END_COMPONENT_REQUIRES();
/* A list of metadata to describe the Component. */
BEGIN_COMPONENT_METADATA(viruscan_service)
METADATA("mysql.author", "Oracle Corporation"),
METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"),
END_COMPONENT_METADATA();
/* Declaration of the Component. */
DECLARE_COMPONENT(viruscan_service,
"mysql:viruscan_service")
viruscan_service_init,
viruscan_service_deinit END_DECLARE_COMPONENT();
/* Defines list of Components contained in this library. Note that for now
we assume that library will have exactly one Component. */
DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service)
END_DECLARE_LIBRARY_COMPONENTS
view rawscan.cc hosted with ❤ by GitHub
让我们在编译新代码并再次运行mtr后对其进行测试

太好了,这就是我们想要的。
一些补充
在第 91 和 92 行,我们得到了安全上下文。我想表明,从该安全上下文中,我们可以例如获取用户和主机:
Security_context_handle ctx = nullptr;
mysql_service_mysql_thd_security_context->get(thd, &ctx);
MYSQL_LEX_CSTRING user;
MYSQL_LEX_CSTRING host;
mysql_service_mysql_security_context_options->get(ctx, "priv_user",
&user);
mysql_service_mysql_security_context_options->get(ctx, "priv_host",
&host);
结论
在本文中,我们看到了如何使用适当的组件服务向用户返回消息。
下一篇文章将介绍如何从我们的组件中使用libclamav 。
请继续关注我,让我们一起快乐编码并享受 MySQL !
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




