From: Tomek Mrugalski Date: Thu, 19 Sep 2019 14:27:10 +0000 (+0200) Subject: [#899,!513] Scripts Hook implemented. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7fd79eba674544f84db68a64c49083ce927ec3d0;p=thirdparty%2Fkea.git [#899,!513] Scripts Hook implemented. --- diff --git a/configure.ac b/configure.ac index 8f78ac2426..84701efe50 100755 --- a/configure.ac +++ b/configure.ac @@ -1703,6 +1703,8 @@ AC_CONFIG_FILES([Makefile src/hooks/dhcp/user_chk/Makefile src/hooks/dhcp/user_chk/tests/Makefile src/hooks/dhcp/user_chk/tests/test_data_files_config.h + src/hooks/dhcp/scripts/Makefile + src/hooks/dhcp/scripts/tests/Makefile src/hooks/dhcp/stat_cmds/Makefile src/hooks/dhcp/stat_cmds/tests/Makefile src/lib/Makefile diff --git a/src/hooks/dhcp/Makefile.am b/src/hooks/dhcp/Makefile.am index 0bb9b878e3..05db00239f 100644 --- a/src/hooks/dhcp/Makefile.am +++ b/src/hooks/dhcp/Makefile.am @@ -4,4 +4,4 @@ if HAVE_MYSQL SUBDIRS += mysql_cb endif -SUBDIRS += stat_cmds user_chk +SUBDIRS += scripts stat_cmds user_chk diff --git a/src/hooks/dhcp/scripts/Makefile.am b/src/hooks/dhcp/scripts/Makefile.am new file mode 100644 index 0000000000..ea54429fae --- /dev/null +++ b/src/hooks/dhcp/scripts/Makefile.am @@ -0,0 +1,77 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +EXTRA_DIST = libdhcp_scripts.dox +# Ensure that the message file is included in the distribution +EXTRA_DIST += scripts_messages.mes + +CLEANFILES = *.gcno *.gcda + +# convenience archive + +noinst_LTLIBRARIES = libscripts.la + +libscripts_la_SOURCES = +libscripts_la_SOURCES += load_unload.cc +libscripts_la_SOURCES += pkt_send_co.cc +libscripts_la_SOURCES += lease_expire_co.cc +libscripts_la_SOURCES += scripts_cfg.cc scripts_cfg.h +libscripts_la_SOURCES += scripts_log.cc scripts_log.h +libscripts_la_SOURCES += scripts_messages.cc scripts_messages.h +libscripts_la_SOURCES += version.cc + +libscripts_la_CXXFLAGS = $(AM_CXXFLAGS) +libscripts_la_CPPFLAGS = $(AM_CPPFLAGS) + +noinst_LTLIBRARIES += libdhcp_scripts.la + +libdhcp_scripts_la_SOURCES = +libdhcp_scripts_la_LDFLAGS = $(AM_LDFLAGS) +libdhcp_scripts_la_LDFLAGS += -avoid-version -export-dynamic -module +# -rpath /nowhere is a hack to trigger libtool to not create a +# convenience archive, resulting in shared modules +libdhcp_scripts_la_LDFLAGS += -rpath /nowhere +libdhcp_scripts_la_LIBADD = libscripts.la +libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la +libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libdhcp_scripts_la_LIBADD += $(LOG4CPLUS_LIBS) + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f scripts_messages.h scripts_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +if GENERATE_MESSAGES + +# Define rule to build logging source files from message file +messages: scripts_messages.h scripts_messages.cc + @echo Message files regenerated + +scripts_messages.h scripts_messages.cc: scripts_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/scripts/scripts_messages.mes + +else + +messages scripts_messages.h scripts_messages.cc: + @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +endif + diff --git a/src/hooks/dhcp/scripts/lease_expire_co.cc b/src/hooks/dhcp/scripts/lease_expire_co.cc new file mode 100644 index 0000000000..e56f7b6dde --- /dev/null +++ b/src/hooks/dhcp/scripts/lease_expire_co.cc @@ -0,0 +1,90 @@ +// Copyright (C) 2019 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 lease_expire_co.cc Defines the lease4_send and lease6_send callout functions. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc::dhcp; +using namespace isc::hooks; +using namespace hooks::scripts; +using namespace std; + +// 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" { + +/// @brief This callout is called at the "lease6_expire" hook. +/// +/// @return 0 upon success, non-zero otherwise. +int lease6_expire(CalloutHandle& handle) { + try { + Lease6Ptr lease; + handle.getArgument("lease6", lease); + + if (!lease) { + // Something is very wrong here. + return (1); + } + + Variables vars; + + stringstream tmp; + switch (lease->type_) { + case Lease::TYPE_NA: + case Lease::TYPE_TA: + case Lease::TYPE_V4: + tmp << "ADDRESS1=" << lease->addr_.toText(); + break; + case Lease::TYPE_PD: + tmp << "PREFIX1=" << lease->addr_.toText() << "/" << static_cast(lease->prefixlen_); + break; + default: + // Unknown lease type. + return (1); + } + vars.push_back(tmp.str()); + + if (lease->duid_) { + tmp.str(""); + tmp << "DUID=" << lease->duid_->toText(); + vars.push_back(tmp.str()); + } + + tmp.str(""); + tmp << "HOSTNAME=" << lease->hostname_; + vars.push_back(tmp.str()); + + callScript(vars); + + } catch (const std::exception& ex) { + std::cout << "DHCP Scripts Hook : lease6_expire unexpected error: " + << ex.what() << std::endl; + return (1); + } + + return (0); +} + +} // extern C + + diff --git a/src/hooks/dhcp/scripts/load_unload.cc b/src/hooks/dhcp/scripts/load_unload.cc new file mode 100644 index 0000000000..1bd0a5636d --- /dev/null +++ b/src/hooks/dhcp/scripts/load_unload.cc @@ -0,0 +1,120 @@ +// Copyright (C) 2019 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 load_unload.cc Defines the load and unload hooks library functions. + +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace isc::data; +using namespace isc::hooks; +using namespace isc::log; +using namespace hooks::scripts; + +// 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" { + +/// @brief Called by the Hooks library manager when the library is loaded. +/// +/// Instantiates the UserRegistry and opens the outcome file. Failure in +/// either results in a failed return code. +/// +/// @return Returns 0 upon success, non-zero upon failure. +int load(LibraryHandle& handle) { + // non-zero indicates an error. + int ret_val = 0; + + ScriptsConfigPtr cfg = getScriptsConfig(); + cfg->clear(); + + try { + // zero out the errno to be safe + errno = 0; + + ConstElementPtr scripts = handle.getParameter("scripts"); + if (!scripts) { + LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR) + .arg("Mandatory parameter 'scripts' missing"); + return (1); + } + if (scripts->getType() != Element::list) { + LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR) + .arg("Parameter 'scripts' specified, but it's not a list"); + return (1); + } + + if (scripts->size() == 0) { + LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR) + .arg("Parameter 'scripts' list specified, but it is empty."); + return (1); + } + + for (int i = 0; i < scripts->size(); i++) { + auto s = scripts->get(i); + if (!s || s->getType() != Element::string) { + stringstream tmp; + tmp << "Element " << i << " of the 'scripts' list is either empty or is not a string"; + LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR) + .arg(tmp.str()); + } + // Store the script name in our config. + cfg->scripts_.push_back(s->stringValue()); + LOG_DEBUG(scripts_logger, DBGLVL_START_SHUT, SCRIPTS_HOOK_SCRIPT_ENTRY) + .arg(s->stringValue()); + } + } + catch (const std::exception& ex) { + // Log the error and return failure. + LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR).arg(ex.what()); + ret_val = 1; + } + + try { + ConstElementPtr async = handle.getParameter("async"); + if (async && async->getType() != Element::boolean) { + LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR) + .arg("Parameter 'async' specified, but it's not a boolean"); + return (1); + } + cfg->async_ = async->boolValue(); + } catch (...) { + // We don't care. The async parameter is optional. + } + + return (ret_val); +} + +/// @brief Called by the Hooks library manager when the library is unloaded. +/// +/// Destroys the UserRegistry and closes the outcome file. +/// +/// @return Always returns 0. +int unload() { + try { + // We only clean the configuration. There's nothing else to clean up. + ScriptsConfigPtr cfg = getScriptsConfig(); + cfg->clear(); + } catch (const std::exception& ex) { + // On the off chance something goes awry, catch it and log it. + // @todo Not sure if we should return a non-zero result or not. + LOG_ERROR(scripts_logger, SCRIPTS_HOOK_UNLOAD_ERROR).arg(ex.what()); + } + + return (0); +} + +} diff --git a/src/hooks/dhcp/scripts/pkt_send_co.cc b/src/hooks/dhcp/scripts/pkt_send_co.cc new file mode 100644 index 0000000000..343be196e4 --- /dev/null +++ b/src/hooks/dhcp/scripts/pkt_send_co.cc @@ -0,0 +1,137 @@ +// Copyright (C) 2019 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 pkt_send_co.cc Defines the pkt4_send and pkt6_send callout functions. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc::dhcp; +using namespace isc::hooks; +using namespace hooks::scripts; +using namespace std; + +// 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" { + +/// @brief This callout is called at the "pkt6_send" hook. +/// +/// This function generates environment variables based on the Pkt6 content. +/// It then calls the external script. +/// +/// @return 0 upon success, non-zero otherwise. +int pkt6_send(CalloutHandle& handle) { + + + try { + Pkt6Ptr query; + handle.getArgument("query6", query); + + Pkt6Ptr response; + handle.getArgument("response6", response); + + Variables vars; + + if (!query || !response) { + // Something is very wrong. + return (1); + } + + stringstream tmp; + tmp << "ACTION=" << Pkt6::getName(query->getType()); + vars.push_back(tmp.str()); + + auto ia_list = response->getOptions(D6O_IA_NA); + int addr_cnt = 1; + for (auto ia : ia_list) { + auto opts = ia.second->getOptions(); + + for (auto opt : opts) { + if (opt.second->getType() != D6O_IAADDR) { + continue; + } + + Option6IAAddrPtr addr = boost::dynamic_pointer_cast(opt.second); + if (!addr) { + // something is wrong. But let's ignore it and move forward. + continue; + } + + tmp.str(""); + tmp << "ADDRESS" << (addr_cnt) << "=" << addr->getAddress(); + vars.push_back(tmp.str()); + + + tmp.str(""); + tmp << "ADDRESS" << (addr_cnt) << "_PREFERRED=" << addr->getPreferred(); + vars.push_back(tmp.str()); + + tmp.str(""); + tmp << "ADDRESS" << (addr_cnt++) << "_VALID=" << addr->getValid(); + vars.push_back(tmp.str()); + } + } + + auto pd_list = response->getOptions(D6O_IA_PD); + int prefix_cnt = 1; + + for (auto pd: pd_list) { + auto opts = pd.second->getOptions(); + + for (auto opt : opts) { + if (opt.second->getType() != D6O_IAPREFIX) { + continue; + } + + Option6IAPrefixPtr prefix = boost::dynamic_pointer_cast(opt.second); + if (!prefix) { + // something is wrong. But let's ignore it and move forward. + continue; + } + + stringstream tmp; + tmp << "PREFIX" << (prefix_cnt) << "=" << prefix->getAddress() << "/" + << prefix->getLength(); + vars.push_back(tmp.str()); + + tmp.str(""); + tmp << "PREFIX" << (prefix_cnt) << "_PREFERRED=" << prefix->getPreferred(); + vars.push_back(tmp.str()); + + tmp.str(""); + tmp << "PREFIX" << (prefix_cnt++) << "_VALID=" << prefix->getValid(); + vars.push_back(tmp.str()); + } + + } + + // Ok, all variables are set, call the scripts! + callScript(vars); + + } catch (const std::exception& ex) { + std::cout << "DHCP Scripts Hook : pkt6_send unexpected error: " + << ex.what() << std::endl; + return (1); + } + + + return (0); +} + +} // extern C diff --git a/src/hooks/dhcp/scripts/scripts.cc b/src/hooks/dhcp/scripts/scripts.cc new file mode 100644 index 0000000000..c3c7f81713 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts.cc @@ -0,0 +1,23 @@ + +#include +#include + +namespace hooks { +namespace scripts { + +bool callScript(const Variables& vars) { + + ScriptsConfigPtr cfg = getScriptsConfig(); + try { + + + } catch (const std::exception& e) { + + return (false); + } + + return (true); +} + +}; +}; diff --git a/src/hooks/dhcp/scripts/scripts.h b/src/hooks/dhcp/scripts/scripts.h new file mode 100644 index 0000000000..f2823d2911 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts.h @@ -0,0 +1,29 @@ +// Copyright (C) 2019 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/. + +#ifndef VARIABLES_H +#define VARIABLES_H + +#include +#include + +namespace hooks { +namespace scripts { + + /// @brief keeps a list of variables. + /// + /// Each entry is supposed to have a string containing VARIABLE=VALUE + typedef std::vector Variables; + + /// @brief calls the actual scripts + /// + /// @param vars environment variables to be passed to the script. + /// @return true if successful, false otherwise + bool callScript(const Variables& vars); +}; +}; + +#endif diff --git a/src/hooks/dhcp/scripts/scripts_cfg.cc b/src/hooks/dhcp/scripts/scripts_cfg.cc new file mode 100644 index 0000000000..d1f1eae722 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts_cfg.cc @@ -0,0 +1,37 @@ +// Copyright (C) 2019 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/. + + +#include + +namespace { +hooks::scripts::ScriptsConfigPtr config; +}; + +namespace hooks { +namespace scripts { + +ScriptsConfig::ScriptsConfig() { + clear(); +} + +void +ScriptsConfig::clear() { + async_ = true; + scripts_.clear(); +} + +ScriptsConfigPtr getScriptsConfig() { + if (!config) { + config.reset(new ScriptsConfig()); + } + return (config); +} + + + +}; +}; diff --git a/src/hooks/dhcp/scripts/scripts_cfg.h b/src/hooks/dhcp/scripts/scripts_cfg.h new file mode 100644 index 0000000000..3c45ff9cc3 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts_cfg.h @@ -0,0 +1,42 @@ +// Copyright (C) 2019 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/. + +#ifndef SCRIPTS_HOOK_H +#define SCRIPTS_HOOK_H + +#include +#include +#include + +namespace hooks { +namespace scripts { + +/// @brief Represents scripts hook configuration +class ScriptsConfig { + public: + /// @brief default constructor + ScriptsConfig(); + + /// @brief Clears configuration. + void clear(); + + /// @brief scripts to be called. + std::vector scripts_; + + /// @brief define whether scripts should be called async (true) or sync (false) + bool async_; +}; + +/// @brief pointer to ScriptsConfig +typedef boost::shared_ptr ScriptsConfigPtr; + +/// @brief returns current Scripts hook configuration +ScriptsConfigPtr getScriptsConfig(); + +}; +}; + +#endif diff --git a/src/hooks/dhcp/scripts/scripts_log.cc b/src/hooks/dhcp/scripts/scripts_log.cc new file mode 100644 index 0000000000..532fc73d6e --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts_log.cc @@ -0,0 +1,18 @@ +// Copyright (C) 2019 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/. + +/// Defines the logger used by the user check hooks library. +#include + +#include + +namespace hooks { +namespace scripts { + +isc::log::Logger scripts_logger("scripts"); + +} // namespace hooks::scripts +} // namespace hooks diff --git a/src/hooks/dhcp/scripts/scripts_log.h b/src/hooks/dhcp/scripts/scripts_log.h new file mode 100644 index 0000000000..ff67d6e559 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts_log.h @@ -0,0 +1,25 @@ +// Copyright (C) 2019 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/. + +#ifndef SCRIPTS_LOG_H +#define SCRIPTS_LOG_H + +#include +#include +#include + +namespace hooks { +namespace scripts { + +/// @brief Scripts Logger +/// +/// Define the logger used to log messages. +extern isc::log::Logger scripts_logger; + +} // end of namespace scripts +} // end of namespace hooks + +#endif // SCRIPTS_LOG_H diff --git a/src/hooks/dhcp/scripts/scripts_messages.cc b/src/hooks/dhcp/scripts/scripts_messages.cc new file mode 100644 index 0000000000..95ec188fa4 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts_messages.cc @@ -0,0 +1,23 @@ +// File created from ../../../../src/hooks/dhcp/scripts/scripts_messages.mes on Thu Sep 19 2019 12:34 + +#include +#include +#include + +extern const isc::log::MessageID SCRIPTS_HOOK_LOAD_ERROR = "SCRIPTS_HOOK_LOAD_ERROR"; +extern const isc::log::MessageID SCRIPTS_HOOK_SCRIPT_ENTRY = "SCRIPTS_HOOK_SCRIPT_ENTRY"; +extern const isc::log::MessageID SCRIPTS_HOOK_UNLOAD_ERROR = "SCRIPTS_HOOK_UNLOAD_ERROR"; + +namespace { + +const char* values[] = { + "SCRIPTS_HOOK_LOAD_ERROR", "DHCP ScriptsHook could not be loaded: %1", + "SCRIPTS_HOOK_SCRIPT_ENTRY", "DHCP Scripts Hook will use the following script: %1", + "SCRIPTS_HOOK_UNLOAD_ERROR", "DHCP Scripts Hook an error occurred unloading the library: %1", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/hooks/dhcp/scripts/scripts_messages.h b/src/hooks/dhcp/scripts/scripts_messages.h new file mode 100644 index 0000000000..47ede1fa46 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts_messages.h @@ -0,0 +1,12 @@ +// File created from ../../../../src/hooks/dhcp/scripts/scripts_messages.mes on Thu Sep 19 2019 12:34 + +#ifndef SCRIPTS_MESSAGES_H +#define SCRIPTS_MESSAGES_H + +#include + +extern const isc::log::MessageID SCRIPTS_HOOK_LOAD_ERROR; +extern const isc::log::MessageID SCRIPTS_HOOK_SCRIPT_ENTRY; +extern const isc::log::MessageID SCRIPTS_HOOK_UNLOAD_ERROR; + +#endif // SCRIPTS_MESSAGES_H diff --git a/src/hooks/dhcp/scripts/scripts_messages.mes b/src/hooks/dhcp/scripts/scripts_messages.mes new file mode 100644 index 0000000000..05fa8aec79 --- /dev/null +++ b/src/hooks/dhcp/scripts/scripts_messages.mes @@ -0,0 +1,19 @@ +# Copyright (C) 2019 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/. + +% SCRIPTS_HOOK_LOAD_ERROR DHCP ScriptsHook could not be loaded: %1 +This is an error message issued when the DHCP Scripts Hook could not be loaded. +The exact cause should be explained in the log message. User subnet selection +will revert to default processing. + +% SCRIPTS_HOOK_UNLOAD_ERROR DHCP Scripts Hook an error occurred unloading the library: %1 +This is an error message issued when an error occurs while unloading the +Scripts Hook library. This is unlikely to occur and normal operations of the +library will likely resume when it is next loaded. + +% SCRIPTS_HOOK_SCRIPT_ENTRY DHCP Scripts Hook will use the following script: %1 +This debug message is printed when Scripts hook library is loaded and it parses +a script name. \ No newline at end of file diff --git a/src/hooks/dhcp/scripts/version.cc b/src/hooks/dhcp/scripts/version.cc new file mode 100644 index 0000000000..1cc6ba0fde --- /dev/null +++ b/src/hooks/dhcp/scripts/version.cc @@ -0,0 +1,19 @@ +// Copyright (C) 2019 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/. +// version.cc + +#include + +#include + +extern "C" { + +/// @brief Version function required by Hooks API for compatibility checks. +int version() { + return (KEA_HOOKS_VERSION); +} + +}