libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+libco4_la_SOURCES = callout_library_4.cc callout_library_common.h
+libco4_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco4_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco4_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
# Don't install test libraries.
-noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
+noinst_LTLIBRARIES = libco1.la libco2.la libco3.la libco4.la
# C++ tests
PROGRAM_TESTS = dhcp4_unittests
--- /dev/null
+// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief Callout library for testing execution of the dhcp4_srv_configured
+/// hook point.
+///
+static const int LIBRARY_NUMBER = 4;
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+#include <dhcp4/tests/callout_library_common.h>
+#include <dhcpsrv/srv_config.h>
+
+#include <string>
+#include <vector>
+
+using namespace isc::asiolink;
+using namespace isc::hooks;
+
+namespace {
+
+// Start service method: always throw.
+void start_service(void) {
+ isc_throw(isc::Unexpected, "start service failed");
+};
+
+} // end anonymous
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+int
+do_load_impl(LibraryHandle& handle) {
+ // Determine if this callout is configured to fail.
+ isc::dhcp::SrvConfigPtr config;
+ isc::data::ConstElementPtr const& parameters(handle.getParameters());
+ isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
+ std::string mode(mode_element ? mode_element->stringValue() : "");
+ if (mode == "fail-on-load") {
+ return (1);
+ }
+ return (0);
+}
+
+int (*do_load)(LibraryHandle& handle) = do_load_impl;
+
+int (*do_unload)();
+
+/// @brief Callout which appends library number and provided arguments to
+/// the marker file for dhcp4_srv_configured callout.
+///
+/// @param handle callout handle passed to the callout.
+///
+/// @return 0 on success, 1 otherwise.
+int
+dhcp4_srv_configured(CalloutHandle& handle) {
+
+ // Append library number.
+ if (appendDigit(SRV_CONFIG_MARKER_FILE)) {
+ return (1);
+ }
+
+ // Append argument names.
+ std::vector<std::string> args = handle.getArgumentNames();
+ for (auto const& arg : args) {
+ if (appendArgument(SRV_CONFIG_MARKER_FILE, arg.c_str()) != 0) {
+ return (1);
+ }
+ }
+
+ // Get the IO context to post start_service on it.
+ std::string error("");
+ IOServicePtr io_context;
+ try {
+ handle.getArgument("io_context", io_context);
+ if (!io_context) {
+ error = "null io_context";
+ }
+ io_context->post(start_service);
+ } catch (const std::exception& ex) {
+ error = "no io_context in arguments";
+ }
+ if (!error.empty()) {
+ handle.setArgument("error", error);
+ handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+ }
+
+ return (0);
+}
+
+/// @brief This function is called to retrieve the multi-threading compatibility.
+///
+/// @return 1 which means compatible with multi-threading.
+int multi_threading_compatible() {
+ return (1);
+}
+
+} // end extern "C"
EXPECT_EQ(expected_address, callback_lease4_->addr_);
}
+// Checks that postponed hook start service can fail.
+TEST_F(LoadUnloadDhcpv4SrvTest, StartServiceFail) {
+ boost::shared_ptr<ControlledDhcpv4Srv> srv(new ControlledDhcpv4Srv(0));
+
+ // Ensure no marker files to start with.
+ ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
+ ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+ ASSERT_FALSE(checkMarkerFileExists(SRV_CONFIG_MARKER_FILE));
+
+ // Minimal valid configuration for the server. It includes the
+ // section which loads the callout library #4, which implements
+ // dhcp4_srv_configured callout and a failing start service.
+ string config_str =
+ "{"
+ " \"interfaces-config\": {"
+ " \"interfaces\": [ ]"
+ " },"
+ " \"rebind-timer\": 2000,"
+ " \"renew-timer\": 1000,"
+ " \"subnet4\": [ ],"
+ " \"valid-lifetime\": 4000,"
+ " \"lease-database\": {"
+ " \"type\": \"memfile\","
+ " \"persist\": false"
+ " },"
+ " \"hooks-libraries\": ["
+ " {"
+ " \"library\": \"" + std::string(CALLOUT_LIBRARY_4) + "\""
+ " }"
+ " ]"
+ "}";
+
+ ConstElementPtr config = Element::fromJSON(config_str);
+
+ // Configure the server.
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = srv->processConfig(config));
+
+ // Make sure there was an error with expected message.
+ int status_code;
+ parseAnswer(status_code, answer);
+ EXPECT_EQ(1, status_code);
+ EXPECT_EQ(answer->str(),
+ R"({ "result": 1, "text": "Error initializing hooks: start service failed" })");
+
+ // The hook library should have been loaded.
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "4"));
+ EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+ // The dhcp4_srv_configured should have been invoked and the provided
+ // parameters should be recorded.
+ EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
+ "4io_contextjson_confignetwork_stateserver_config"));
+
+ // Destroy the server, instance which should unload the libraries.
+ srv.reset();
+
+ // The server was destroyed, so the unload() function should now
+ // include the library number in its marker file.
+ EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
+}
+
} // namespace
-// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
const char* const CALLOUT_LIBRARY_3 = "@abs_builddir@/.libs/libco3.so";
+const char* const CALLOUT_LIBRARY_4 = "@abs_builddir@/.libs/libco4.so";
// Name of a library which is not present.
const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+libco4_la_SOURCES = callout_library_4.cc callout_library_common.h
+libco4_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco4_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco4_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
# Don't install test libraries.
-noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
+noinst_LTLIBRARIES = libco1.la libco2.la libco3.la libco4.la
# C++ tests
PROGRAM_TESTS = dhcp6_unittests
--- /dev/null
+// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief Callout library for testing execution of the dhcp6_srv_configured
+/// hook point.
+///
+static const int LIBRARY_NUMBER = 4;
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+#include <dhcp6/tests/callout_library_common.h>
+#include <dhcpsrv/srv_config.h>
+
+#include <string>
+#include <vector>
+
+using namespace isc::asiolink;
+using namespace isc::hooks;
+
+namespace {
+
+// Start service method: always throw.
+void start_service(void) {
+ isc_throw(isc::Unexpected, "start service failed");
+};
+
+} // end anonymous
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+int
+do_load_impl(LibraryHandle& handle) {
+ // Determine if this callout is configured to fail.
+ isc::dhcp::SrvConfigPtr config;
+ isc::data::ConstElementPtr const& parameters(handle.getParameters());
+ isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
+ std::string mode(mode_element ? mode_element->stringValue() : "");
+ if (mode == "fail-on-load") {
+ return (1);
+ }
+ return (0);
+}
+
+int (*do_load)(LibraryHandle& handle) = do_load_impl;
+
+int (*do_unload)();
+
+/// @brief Callout which appends library number and provided arguments to
+/// the marker file for dhcp6_srv_configured callout.
+///
+/// @param handle callout handle passed to the callout.
+///
+/// @return 0 on success, 1 otherwise.
+int
+dhcp6_srv_configured(CalloutHandle& handle) {
+
+ // Append library number.
+ if (appendDigit(SRV_CONFIG_MARKER_FILE)) {
+ return (1);
+ }
+
+ // Append argument names.
+ std::vector<std::string> args = handle.getArgumentNames();
+ for (auto const& arg : args) {
+ if (appendArgument(SRV_CONFIG_MARKER_FILE, arg.c_str()) != 0) {
+ return (1);
+ }
+ }
+
+ // Get the IO context to post start_service on it.
+ std::string error("");
+ IOServicePtr io_context;
+ try {
+ handle.getArgument("io_context", io_context);
+ if (!io_context) {
+ error = "null io_context";
+ }
+ io_context->post(start_service);
+ } catch (const std::exception& ex) {
+ error = "no io_context in arguments";
+ }
+ if (!error.empty()) {
+ handle.setArgument("error", error);
+ handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+ }
+
+ return (0);
+}
+
+/// @brief This function is called to retrieve the multi-threading compatibility.
+///
+/// @return 1 which means compatible with multi-threading.
+int multi_threading_compatible() {
+ return (1);
+}
+
+} // end extern "C"
EXPECT_EQ(1, getStatistic("pkt6-receive-drop"));
}
+// Checks that postponed hook start service can fail.
+TEST_F(LoadUnloadDhcpv6SrvTest, StartServiceFail) {
+ boost::shared_ptr<ControlledDhcpv6Srv> srv(new ControlledDhcpv6Srv(0));
+
+ // Ensure no marker files to start with.
+ ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
+ ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+ ASSERT_FALSE(checkMarkerFileExists(SRV_CONFIG_MARKER_FILE));
+
+ // Minimal valid configuration for the server. It includes the
+ // section which loads the callout library #4, which implements
+ // dhcp6_srv_configured callout and a failing start service.
+ string config_str =
+ "{"
+ " \"interfaces-config\": {"
+ " \"interfaces\": [ ]"
+ " },"
+ " \"preferred-lifetime\": 3000,"
+ " \"rebind-timer\": 2000,"
+ " \"renew-timer\": 1000,"
+ " \"subnet6\": [ ],"
+ " \"valid-lifetime\": 4000,"
+ " \"lease-database\": {"
+ " \"type\": \"memfile\","
+ " \"persist\": false"
+ " },"
+ " \"hooks-libraries\": ["
+ " {"
+ " \"library\": \"" + std::string(CALLOUT_LIBRARY_4) + "\""
+ " }"
+ " ]"
+ "}";
+
+ ConstElementPtr config = Element::fromJSON(config_str);
+
+ // Configure the server.
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = srv->processConfig(config));
+
+ // Make sure there was an error with expected message.
+ int status_code;
+ parseAnswer(status_code, answer);
+ EXPECT_EQ(1, status_code);
+ EXPECT_EQ(answer->str(),
+ R"({ "result": 1, "text": "Error initializing hooks: start service failed" })");
+
+ // The hook library should have been loaded.
+ EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "4"));
+ EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+ // The dhcp6_srv_configured should have been invoked and the provided
+ // parameters should be recorded.
+ EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
+ "4io_contextjson_confignetwork_stateserver_config"));
+
+ // Destroy the server, instance which should unload the libraries.
+ srv.reset();
+
+ // The server was destroyed, so the unload() function should now
+ // include the library number in its marker file.
+ EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
+}
+
} // namespace
-// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
const char* const CALLOUT_LIBRARY_3 = "@abs_builddir@/.libs/libco3.so";
+const char* const CALLOUT_LIBRARY_4 = "@abs_builddir@/.libs/libco4.so";
// Name of a library which is not present.
const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";