You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bc...@apache.org on 2020/07/17 17:19:07 UTC
[trafficserver] branch master updated: Add new API /
TSPluginDSOReloadEnable that overrides the configuration variable
`proxy.config.plugin.dynamic_reload_mode` for a particular plugin. (#6880)
This is an automated email from the ASF dual-hosted git repository.
bcall pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new acd5617 Add new API / TSPluginDSOReloadEnable that overrides the configuration variable `proxy.config.plugin.dynamic_reload_mode` for a particular plugin. (#6880)
acd5617 is described below
commit acd5617480346db92091e5678635bcfe68332bca
Author: Damian Meden <da...@gmail.com>
AuthorDate: Fri Jul 17 18:18:53 2020 +0100
Add new API / TSPluginDSOReloadEnable that overrides the configuration variable `proxy.config.plugin.dynamic_reload_mode` for a particular plugin. (#6880)
---
.../api/functions/TSPluginDSOReloadEnable.en.rst | 51 +++++++
include/ts/ts.h | 14 ++
proxy/http/remap/PluginDso.cc | 49 +++++-
proxy/http/remap/PluginDso.h | 31 ++++
proxy/http/remap/PluginFactory.cc | 7 +
proxy/http/remap/PluginFactory.h | 9 ++
proxy/http/remap/unit-tests/test_PluginFactory.cc | 164 ++++++++++++++++++++-
src/traffic_server/InkAPI.cc | 17 +++
8 files changed, 336 insertions(+), 6 deletions(-)
diff --git a/doc/developer-guide/api/functions/TSPluginDSOReloadEnable.en.rst b/doc/developer-guide/api/functions/TSPluginDSOReloadEnable.en.rst
new file mode 100644
index 0000000..2f10e81
--- /dev/null
+++ b/doc/developer-guide/api/functions/TSPluginDSOReloadEnable.en.rst
@@ -0,0 +1,51 @@
+.. 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:: ../../../common.defs
+
+.. default-domain:: c
+
+TSPluginDSOReloadEnable
+*************************
+
+Control whether this plugin will take part in the remap dynamic reload preocess (remap.config)
+
+Synopsis
+========
+
+.. code-block:: cpp
+
+ #include <ts/ts.h>
+
+.. function:: TSReturnCode TSPluginDSOReloadEnable(int enabled)
+
+Description
+===========
+
+This function provides the ability to enable/disable programmatically the plugin
+dynamic reloading when the same Dynamic Shared Object (DSO) is also used as a remap plugin.
+This overrides :ts:cv:`proxy.config.plugin.dynamic_reload_mode`.
+
+.. warning:: This function should be called from within :func:`TSPluginInit`
+
+The function will return :type:`TS_ERROR` in any of the following cases:
+ - The function was not called from within :func:`TSPluginInit`
+ - TS is unable to get the canonical path from the plugin's path.
+
+See Also
+========
+
+:manpage:`TSAPI(3ts)`
diff --git a/include/ts/ts.h b/include/ts/ts.h
index f7289ed..2ed3041 100644
--- a/include/ts/ts.h
+++ b/include/ts/ts.h
@@ -162,6 +162,20 @@ int TSTrafficServerVersionGetPatch(void);
*/
tsapi TSReturnCode TSPluginRegister(const TSPluginRegistrationInfo *plugin_info);
+/**
+ This function provides the ability to enable/disable programmatically
+ the plugin dynamic reloading when the same Dynamic Shared Object (DSO)
+ is also used as a remap plugin. This overrides `proxy.config.plugin.dynamic_reload_mode`
+ configuration variable.
+
+ @param enabled boolean flag. 0/false will disable the reload on the caller plugin.
+ @return TS_ERROR if the function is not called from within TSPluginInit or if TS is
+ unable to get the canonical path from the plugin's path. TS_SUCCESS otherwise.
+
+ @note This function should be called from within TSPluginInit
+ */
+tsapi TSReturnCode TSPluginDSOReloadEnable(int enabled);
+
/* --------------------------------------------------------------------------
Files */
/**
diff --git a/proxy/http/remap/PluginDso.cc b/proxy/http/remap/PluginDso.cc
index 778adab..76e302f 100644
--- a/proxy/http/remap/PluginDso.cc
+++ b/proxy/http/remap/PluginDso.cc
@@ -308,7 +308,7 @@ PluginDso::LoadedPlugins::remove(PluginDso *plugin)
}
/* check if need to reload the plugin DSO
- * if dynamic reload not enabled: check if plugin Dso with same effective path aready loaded
+ * if dynamic reload not enabled: check if plugin Dso with same effective path already loaded
* if dynamic reload enabled: check if plugin Dso with same effective path and same time stamp already loaded
* return pointer to already loaded plugin if found, else return null
*/
@@ -361,4 +361,51 @@ PluginDso::LoadedPlugins::indicatePostReload(bool reloadSuccessful, const std::u
}
}
+bool
+PluginDso::LoadedPlugins::addPluginPathToDsoOptOutTable(std::string_view pluginPath)
+{
+ std::error_code ec;
+ auto effectivePath = fs::canonical(fs::path{pluginPath}, ec);
+
+ if (ec) {
+ PluginError("Error getting the canonical path: %s", ec.message().c_str());
+ return false;
+ }
+
+ {
+ SCOPED_MUTEX_LOCK(lock, _mutex, this_ethread());
+ _optoutDsoReloadPlugins.push_front(DisableDSOReloadPluginInfo{effectivePath});
+ }
+
+ return true;
+}
+
+void
+PluginDso::LoadedPlugins::removePluginPathFromDsoOptOutTable(std::string_view pluginPath)
+{
+ std::error_code ec;
+ auto effectivePath = fs::canonical(fs::path{pluginPath}, ec);
+
+ if (ec) {
+ PluginError("Error getting the canonical path: %s", ec.message().c_str());
+ return;
+ }
+
+ {
+ SCOPED_MUTEX_LOCK(lock, _mutex, this_ethread());
+ _optoutDsoReloadPlugins.remove_if([&effectivePath](auto const &info) { return info.dsoEffectivePath == effectivePath; });
+ }
+}
+
+bool
+PluginDso::LoadedPlugins::isPluginInDsoOptOutTable(const fs::path &effectivePath)
+{
+ SCOPED_MUTEX_LOCK(lock, _mutex, this_ethread());
+ const auto found = std::find_if(std::begin(this->_optoutDsoReloadPlugins), std::end(this->_optoutDsoReloadPlugins),
+ [&effectivePath](const auto &info) { return info.dsoEffectivePath == effectivePath; });
+
+ // if is found, then the plugin opt out.
+ return (found != std::end(this->_optoutDsoReloadPlugins));
+}
+
Ptr<PluginDso::LoadedPlugins> PluginDso::_plugins;
diff --git a/proxy/http/remap/PluginDso.h b/proxy/http/remap/PluginDso.h
index be43803..9e6df2d 100644
--- a/proxy/http/remap/PluginDso.h
+++ b/proxy/http/remap/PluginDso.h
@@ -32,6 +32,7 @@
#include <unordered_map>
#include <dlfcn.h>
#include <vector>
+#include <forward_list>
#include <ctime>
#include "ts/apidefs.h"
@@ -104,9 +105,39 @@ public:
void indicatePreReload(const char *factoryId);
void indicatePostReload(bool reloadSuccessful, const std::unordered_map<PluginDso *, int> &pluginUsed, const char *factoryId);
+ /**
+ * @brief Check if the opt out table contains the passed plugin's effective path.
+ * @return true If the plugin was marked to not participate in the dynamic reload by
+ * TSPluginDSOReloadEnable() API.
+ * @param effectivePath canonical value of the plugin's path.
+ */
+ bool isPluginInDsoOptOutTable(const fs::path &effectivePath);
+ /**
+ * @brief Add the plugin's path to the opt out table in order to let the Plugin Factory
+ * that this plugin is not interested in taking part of the dynamic reloading.
+ * This function will store the plugin's canonical path.
+ * @param effectivePath Plugin's path.
+ * @return false if any errors converting the plugin's path to a canonical path, true otherwise.
+ */
+ bool addPluginPathToDsoOptOutTable(std::string_view pluginPath);
+ /**
+ * @brief Removes the passed plugin's effective path from the opt out list.
+ * @note This is mostly used by unit test than needs to remove the plugin's effectivePath
+ * from the out out list.
+ * @param pluginPath plugin's path.
+ * @note This function is mainly for unit tests purposes.
+ */
+ void removePluginPathFromDsoOptOutTable(std::string_view pluginPath);
+
private:
PluginList _list; /** @brief plugin list */
Ptr<ProxyMutex> _mutex; /** @brief mutex used when updating the plugin list from multiple threads */
+
+ struct DisableDSOReloadPluginInfo {
+ fs::path dsoEffectivePath;
+ };
+
+ std::forward_list<DisableDSOReloadPluginInfo> _optoutDsoReloadPlugins;
};
static const Ptr<LoadedPlugins> &
diff --git a/proxy/http/remap/PluginFactory.cc b/proxy/http/remap/PluginFactory.cc
index 0cb5dfb..1837ddd 100644
--- a/proxy/http/remap/PluginFactory.cc
+++ b/proxy/http/remap/PluginFactory.cc
@@ -152,6 +152,13 @@ PluginFactory::getRemapPlugin(const fs::path &configPath, int argc, char **argv,
return nullptr;
}
+ // The plugin may have opt out by `TSPluginDSOReloadEnable`, let's check and overwrite
+ if (dynamicReloadEnabled && PluginDso::loadedPlugins()->isPluginInDsoOptOutTable(effectivePath)) {
+ // plugin not interested to be reload.
+ PluginDebug(_tag, "Plugin %s not interested in taking part of the reload.", effectivePath.c_str());
+ dynamicReloadEnabled = false;
+ }
+
/* Only one plugin with this effective path can be loaded by a plugin factory */
RemapPluginInfo *plugin = dynamic_cast<RemapPluginInfo *>(findByEffectivePath(effectivePath, dynamicReloadEnabled));
RemapPluginInst *inst = nullptr;
diff --git a/proxy/http/remap/PluginFactory.h b/proxy/http/remap/PluginFactory.h
index 4f6ee6d..6b5a2f8 100644
--- a/proxy/http/remap/PluginFactory.h
+++ b/proxy/http/remap/PluginFactory.h
@@ -82,6 +82,8 @@ public:
* filesystem links and different dl library implementations.
*
* @note This is meant to unify the way global and remap plugins are (re)loaded (global plugin support is not implemented yet).
+ * @note In the case of a mixed plugin, getRemapPlugin/dynamicReloadEnabled can be internally overwritten if the plugin opt out to
+ * take part in the dynamic reload process.
*/
class PluginFactory
{
@@ -116,5 +118,12 @@ protected:
std::error_code _ec;
bool _preventiveCleaning = true;
+ // Hold the full path for global plugins not taking part of the dynamic reloading.
+ struct DisableReloadPluginInfo {
+ fs::path fullPath;
+ };
+
+ std::forward_list<DisableReloadPluginInfo> optoutPlugins;
+
static constexpr const char *const _tag = "plugin_factory"; /** @brief log tag used by this class */
};
diff --git a/proxy/http/remap/unit-tests/test_PluginFactory.cc b/proxy/http/remap/unit-tests/test_PluginFactory.cc
index 8623df0..c2ceca1 100644
--- a/proxy/http/remap/unit-tests/test_PluginFactory.cc
+++ b/proxy/http/remap/unit-tests/test_PluginFactory.cc
@@ -298,7 +298,6 @@ SCENARIO("loading plugins", "[plugin][core]")
{
fs::path configPath = searchDir / "subdir" / pluginName;
CHECK(configPath.is_absolute()); /* make sure this is absolute path - this is what we are testing */
-
setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
@@ -321,7 +320,6 @@ SCENARIO("loading plugins", "[plugin][core]")
disablePluginDynamicReload();
fs::path configPath = searchDir / "subdir" / pluginName;
CHECK(configPath.is_absolute()); /* make sure this is absolute path - this is what we are testing */
-
setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
@@ -340,6 +338,39 @@ SCENARIO("loading plugins", "[plugin][core]")
enablePluginDynamicReload();
}
+ WHEN("config is using plugin absolute path - dynamic reload is ENABLED but overwritten by optout")
+ {
+ enablePluginDynamicReload();
+ fs::path configPath = searchDir / "subdir" / pluginName;
+ CHECK(configPath.is_absolute()); /* make sure this is absolute path - this is what we are testing */
+
+ // We have to force the path to be the effective == runtime
+ disablePluginDynamicReload();
+ setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
+ enablePluginDynamicReload();
+
+ PluginFactoryUnitTest *factory = getFactory(tempComponent);
+ // make the factory take this plugin as it opt out.
+ PluginDso::loadedPlugins()->addPluginPathToDsoOptOutTable(effectivePath.string());
+
+ RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
+
+ THEN("expect it to successfully load")
+ {
+ validateSuccessfulConfigPathTest(plugin, error, effectivePath, runtimePath);
+ static const bool disableDynamicReloadByOptOut{false};
+ CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath, disableDynamicReloadByOptOut));
+
+ // check Dso still exists
+ CHECK(plugin->_plugin.effectivePath() == plugin->_plugin.runtimePath());
+ CHECK(fs::exists(plugin->_plugin.effectivePath()));
+ }
+ // we need to remove this from the list so next tests can work as expected.
+ PluginDso::loadedPlugins()->removePluginPathFromDsoOptOutTable(effectivePath.string());
+ teardownConfigPathTest(factory);
+ enablePluginDynamicReload();
+ }
+
WHEN("config using nonexisting relative plugin file name")
{
fs::path relativeExistingPath = pluginName;
@@ -587,7 +618,7 @@ checkTwoLoadedVersionsDifferent(const RemapPluginInst *plugin_v1, const RemapPlu
}
void
-checkTwoLoadedVersionsSame(RemapPluginInst *plugin_v1, RemapPluginInst *plugin_v2)
+checkTwoLoadedVersionsSame(RemapPluginInst *plugin_v1, RemapPluginInst *plugin_v2, bool pluginOptOut = false)
{
void *tsRemapInitSym_v1_t2 = nullptr; /* callback address from DSO v1 at moment t2 */
void *tsRemapInitSym_v2_t2 = nullptr; /* callback address from DSO v2 at moment t2 */
@@ -610,9 +641,9 @@ checkTwoLoadedVersionsSame(RemapPluginInst *plugin_v1, RemapPluginInst *plugin_v
CHECK(tsRemapInitSym_v1_t2 == tsRemapInitSym_v2_t2);
// 2 versions can be same even when dynamic reload is enabled
- // 2 versions must be same when dynamic reload is disabled
+ // 2 versions must be same when dynamic reload is disabled or the plugin opt out
// check presence/absence of Dso files
- if (!isPluginDynamicReloadEnabled()) {
+ if (pluginOptOut || !isPluginDynamicReloadEnabled()) {
CHECK(plugin_v1->_plugin.effectivePath() == plugin_v1->_plugin.runtimePath());
CHECK(fs::exists(plugin_v1->_plugin.effectivePath()));
} else {
@@ -634,6 +665,27 @@ testSetupLoadPlugin(const fs::path &configName, const fs::path &buildPath, const
return {pluginInst, factory};
}
+std::tuple<RemapPluginInst *, PluginFactoryUnitTest *>
+testSetupLoadPluginWithOptOut(const fs::path &configName, const fs::path &buildPath, const fs::path &uuid, const time_t mtime,
+ fs::path &effectivePath, fs::path &runtimePath)
+{
+ std::string error;
+
+ // force paths to be as dynamic disabled which will be the case for a plugin that opt out.
+ disablePluginDynamicReload();
+ setupConfigPathTest(configName, buildPath, uuid, effectivePath, runtimePath, mtime);
+ enablePluginDynamicReload();
+
+ auto factory = getFactory(uuid);
+
+ // Set factory's opt out plugin information.
+ PluginDso::loadedPlugins()->addPluginPathToDsoOptOutTable(effectivePath.string());
+ auto pluginInst = factory->getRemapPlugin(configName, 0, nullptr, error, isPluginDynamicReloadEnabled());
+ // make sure we remove it so there is no evidence of this in the Plugin's list.
+ PluginDso::loadedPlugins()->removePluginPathFromDsoOptOutTable(effectivePath.string());
+ return {pluginInst, factory};
+}
+
SCENARIO("loading multiple version of the same plugin at the same time", "[plugin][core]")
{
REQUIRE_FALSE(sandboxDir.empty());
@@ -778,6 +830,50 @@ SCENARIO("loading multiple version of the same plugin at the same time", "[plugi
}
}
+ GIVEN("two different versions v1 and v2 of same plugin with different time stamps - dynamic plugin reload ENABLED")
+ {
+ WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
+ "(*) v1 and v2 DSOs modification time are different (changed)")
+ {
+ enablePluginDynamicReload();
+
+ /* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
+ auto [plugin_v1, factory1] =
+ testSetupLoadPluginWithOptOut(configName, buildPath_v1, uuid_t1, 1556825556, effectivePath_v1, runtimePath_v1);
+
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
+
+ /* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
+ /* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so which was v1 */
+ auto [plugin_v2, factory2] =
+ testSetupLoadPluginWithOptOut(configName, buildPath_v2, uuid_t2, 1556825557, effectivePath_v2, runtimePath_v2);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+
+ /* Make sure plugin.so was overriden */
+ CHECK(effectivePath_v1 == effectivePath_v2);
+
+ /* since dynamic reload is disabled, runtimepaths should be same */
+ CHECK(runtimePath_v1 == runtimePath_v2);
+
+ THEN("expect v1 plugin to remain loaded since dynamic reload is disabled, even though the timestamp has changed")
+ {
+ /* Both getRemapPlugin() calls should succeed but only v1 plugin DSO should be used */
+ validateSuccessfulConfigPathTest(plugin_v1, error1, effectivePath_v1, runtimePath_v1);
+ validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v1);
+
+ const bool pluginOptOut{true};
+ checkTwoLoadedVersionsSame(plugin_v1, plugin_v2, pluginOptOut);
+
+ /* Make sure v1 callback functions addresses did not change for v1 after v2 was loaded */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
+ }
+
+ teardownConfigPathTest(factory1);
+ teardownConfigPathTest(factory2);
+ enablePluginDynamicReload();
+ }
+ }
+
GIVEN("two different versions v1 and v2 of same plugin with same time stamp - dynamic plugin reload DISABLED")
{
WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
@@ -923,6 +1019,64 @@ SCENARIO("loading multiple version of the same plugin in mixed mode - global as
}
}
+ GIVEN("two different versions v1 and v2 of same plugin in mixed mode - dynamic plugin reload ENABLED but overwritten by opt out")
+ {
+ WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
+ "(*) v1 and v2 DSOs modification time are different (changed)")
+ {
+ // just to get the proper path's
+ disablePluginDynamicReload();
+ /* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
+ setupConfigPathTest(configName, buildPath_v1, uuid_t1, effectivePath_v1, runtimePath_v1, 1556825556);
+
+ GlobalPluginInfo global_plugin_v1;
+ CHECK(global_plugin_v1.loadDso(effectivePath_v1) == true);
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
+
+ /* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
+ /* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so which was v1 */
+ setupConfigPathTest(configName, buildPath_v2, uuid_t2, effectivePath_v2, runtimePath_v2, 1556825557);
+ enablePluginDynamicReload();
+ PluginFactoryUnitTest *factory = getFactory(uuid_t2);
+
+ // Set factory's opt out plugin information.
+ PluginDso::loadedPlugins()->addPluginPathToDsoOptOutTable(effectivePath_v1.string());
+
+ RemapPluginInst *plugin_v2 = factory->getRemapPlugin(configName, 0, nullptr, error2, isPluginDynamicReloadEnabled());
+
+ /* Make sure plugin.so was overriden */
+ CHECK(effectivePath_v1 == effectivePath_v2);
+
+ /* since dynamic reload is disabled, runtimepaths should be same */
+ CHECK(runtimePath_v1 == runtimePath_v2);
+
+ THEN("expect v1 plugin to remain loaded since dynamic reload is disabled, even though the timestamp has changed")
+ {
+ validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v2);
+
+ /* Make sure we ended up with the same DSO object and runtime paths should be same - no new plugin was loaded */
+ CHECK(global_plugin_v1.dlOpenHandle() == plugin_v2->_plugin.dlOpenHandle());
+
+ /* Make sure v2 DSO was NOT really loaded - both instances should return same v1 version */
+ CHECK(1 == global_plugin_v1.getPluginVersion());
+ CHECK(1 == getPluginVersion(plugin_v2->_plugin));
+
+ /* Make sure the symbols we get from the 2 loaded plugins yield the same callback function pointer */
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+ plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
+ CHECK(nullptr != tsRemapInitSym_v1_t2);
+ CHECK(nullptr != tsRemapInitSym_v2_t2);
+ CHECK(tsRemapInitSym_v1_t2 == tsRemapInitSym_v2_t2);
+
+ /* check that the symbol we got originally is still valid */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
+ }
+ PluginDso::loadedPlugins()->removePluginPathFromDsoOptOutTable(effectivePath_v1.string());
+ teardownConfigPathTest(factory);
+ enablePluginDynamicReload();
+ }
+ }
+
GIVEN("two different versions v1 and v2 of same plugin in mixed mode - dynamic plugin reload DISABLED")
{
WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc
index 11edff1..ce6c42f 100644
--- a/src/traffic_server/InkAPI.cc
+++ b/src/traffic_server/InkAPI.cc
@@ -1966,6 +1966,23 @@ TSPluginRegister(const TSPluginRegistrationInfo *plugin_info)
return TS_SUCCESS;
}
+TSReturnCode
+TSPluginDSOReloadEnable(int enabled)
+{
+ TSReturnCode ret = TS_SUCCESS;
+ if (!plugin_reg_current) {
+ return TS_ERROR;
+ }
+
+ if (!enabled) {
+ if (!PluginDso::loadedPlugins()->addPluginPathToDsoOptOutTable(plugin_reg_current->plugin_path)) {
+ ret = TS_ERROR;
+ }
+ }
+
+ return ret;
+}
+
////////////////////////////////////////////////////////////////////
//
// API file management