blob: a98c3eeb29e5648f600cfb7de035d7e39a754bfb [file] [log] [blame]
/** @file
Information about remap plugin libraries.
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <unistd.h>
#include "RemapPluginInfo.h"
#include "tscore/ink_string.h"
#include "tscore/ink_memory.h"
#include "tscore/ink_apidefs.h"
#include "RemapPluginInfo.h"
#ifdef PLUGIN_DSO_TESTS
#include "unit-tests/plugin_testing_common.h"
#else
#include "tscore/Diags.h"
#endif
/**
* @brief helper function that returns the function address from the plugin DSO
*
* There can be valid defined DSO symbols that are NULL
* but when it comes to functions we can assume that
* if not defined we can return nullptr and a valid address if the are defined.
* @param symbol function symbol name
* @param error error messages in case of symbol is not found
* @return function address or nullptr if not found.
*/
template <class T>
T *
RemapPluginInfo::getFunctionSymbol(const char *symbol)
{
std::string error; /* ignore the error, return nullptr if symbol not defined */
void *address = nullptr;
if (getSymbol(symbol, address, error)) {
Debug(_tag, "plugin '%s' found symbol '%s'", _configPath.c_str(), symbol);
}
return reinterpret_cast<T *>(address);
}
std::string
RemapPluginInfo::missingRequiredSymbolError(const std::string &pluginName, const char *required, const char *requiring)
{
std::string error;
error.assign("plugin ").append(pluginName).append(" missing required function ").append(required);
if (requiring) {
error.append(" if ").append(requiring).append(" is defined");
}
return error;
}
RemapPluginInfo::RemapPluginInfo(const fs::path &configPath, const fs::path &effectivePath, const fs::path &runtimePath)
: PluginDso(configPath, effectivePath, runtimePath)
{
}
bool
RemapPluginInfo::load(std::string &error)
{
error.clear();
if (!PluginDso::load(error)) {
return false;
}
init_cb = getFunctionSymbol<Init_F>(TSREMAP_FUNCNAME_INIT);
pre_config_reload_cb = getFunctionSymbol<PreReload_F>(TSREMAP_FUNCNAME_PRE_CONFIG_RELOAD);
post_config_reload_cb = getFunctionSymbol<PostReload_F>(TSREMAP_FUNCNAME_POST_CONFIG_RELOAD);
done_cb = getFunctionSymbol<Done_F>(TSREMAP_FUNCNAME_DONE);
new_instance_cb = getFunctionSymbol<New_Instance_F>(TSREMAP_FUNCNAME_NEW_INSTANCE);
delete_instance_cb = getFunctionSymbol<Delete_Instance_F>(TSREMAP_FUNCNAME_DELETE_INSTANCE);
do_remap_cb = getFunctionSymbol<Do_Remap_F>(TSREMAP_FUNCNAME_DO_REMAP);
os_response_cb = getFunctionSymbol<OS_Response_F>(TSREMAP_FUNCNAME_OS_RESPONSE);
/* Validate if the callback TSREMAP functions are specified correctly in the plugin. */
bool valid = true;
if (!init_cb) {
error = missingRequiredSymbolError(_configPath.string(), TSREMAP_FUNCNAME_INIT);
valid = false;
} else if (!do_remap_cb) {
error = missingRequiredSymbolError(_configPath.string(), TSREMAP_FUNCNAME_DO_REMAP);
valid = false;
} else if (!new_instance_cb && delete_instance_cb) {
error = missingRequiredSymbolError(_configPath.string(), TSREMAP_FUNCNAME_NEW_INSTANCE, TSREMAP_FUNCNAME_DELETE_INSTANCE);
valid = false;
} else if (new_instance_cb && !delete_instance_cb) {
error = missingRequiredSymbolError(_configPath.string(), TSREMAP_FUNCNAME_DELETE_INSTANCE, TSREMAP_FUNCNAME_NEW_INSTANCE);
valid = false;
}
if (valid) {
Debug(_tag, "plugin '%s' callbacks validated", _configPath.c_str());
} else {
Error("plugin '%s' callbacks validation failed: %s", _configPath.c_str(), error.c_str());
}
return valid;
}
/* Initialize plugin (required). */
bool
RemapPluginInfo::init(std::string &error)
{
TSRemapInterface ri;
bool result = true;
Debug(_tag, "started initializing plugin '%s'", _configPath.c_str());
/* A buffer to get the error from the plugin instance init function, be defensive here. */
char tmpbuf[2048];
ink_zero(tmpbuf);
ink_zero(ri);
ri.size = sizeof(ri);
ri.tsremap_version = TSREMAP_VERSION;
setPluginContext();
if (init_cb && init_cb(&ri, tmpbuf, sizeof(tmpbuf) - 1) != TS_SUCCESS) {
error.assign("failed to initialize plugin ")
.append(_configPath.string())
.append(": ")
.append(tmpbuf[0] ? tmpbuf : "Unknown plugin error");
result = false;
}
resetPluginContext();
Debug(_tag, "finished initializing plugin '%s'", _configPath.c_str());
return result;
}
/* Called when plugin is unloaded (optional). */
void
RemapPluginInfo::done()
{
if (done_cb) {
done_cb();
}
}
bool
RemapPluginInfo::initInstance(int argc, char **argv, void **ih, std::string &error)
{
TSReturnCode res = TS_SUCCESS;
bool result = true;
Debug(_tag, "started initializing instance of plugin '%s'", _configPath.c_str());
/* A buffer to get the error from the plugin instance init function, be defensive here. */
char tmpbuf[2048];
ink_zero(tmpbuf);
if (new_instance_cb) {
#if defined(freebsd) || defined(darwin)
optreset = 1;
#endif
#if defined(__GLIBC__)
optind = 0;
#else
optind = 1;
#endif
opterr = 0;
optarg = nullptr;
setPluginContext();
res = new_instance_cb(argc, argv, ih, tmpbuf, sizeof(tmpbuf) - 1);
resetPluginContext();
if (TS_SUCCESS != res) {
error.assign("failed to create instance for plugin ")
.append(_configPath.string())
.append(": ")
.append(tmpbuf[0] ? tmpbuf : "Unknown plugin error");
result = false;
}
}
Debug(_tag, "finished initializing instance of plugin '%s'", _configPath.c_str());
return result;
}
void
RemapPluginInfo::doneInstance(void *ih)
{
setPluginContext();
if (delete_instance_cb) {
delete_instance_cb(ih);
}
resetPluginContext();
}
TSRemapStatus
RemapPluginInfo::doRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
{
TSRemapStatus result = TSREMAP_NO_REMAP;
setPluginContext();
if (do_remap_cb) {
result = do_remap_cb(ih, rh, rri);
}
resetPluginContext();
return result;
}
void
RemapPluginInfo::osResponse(void *ih, TSHttpTxn rh, int os_response_type)
{
setPluginContext();
if (os_response_cb) {
os_response_cb(ih, rh, os_response_type);
}
resetPluginContext();
}
RemapPluginInfo::~RemapPluginInfo() {}
void
RemapPluginInfo::indicatePreReload()
{
setPluginContext();
if (pre_config_reload_cb) {
pre_config_reload_cb();
}
resetPluginContext();
}
void
RemapPluginInfo::indicatePostReload(TSReturnCode reloadStatus)
{
setPluginContext();
if (post_config_reload_cb) {
post_config_reload_cb(reloadStatus);
}
resetPluginContext();
}
inline void
RemapPluginInfo::setPluginContext()
{
_tempContext = pluginThreadContext;
pluginThreadContext = this;
Debug(_tag, "change plugin context from dso-addr:%p to dso-addr:%p", pluginThreadContext, _tempContext);
}
inline void
RemapPluginInfo::resetPluginContext()
{
Debug(_tag, "change plugin context from dso-addr:%p to dso-addr:%p (restore)", this, pluginThreadContext);
pluginThreadContext = _tempContext;
}