]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3839] backport #3831 to 2_4
authorRazvan Becheriu <razvan@isc.org>
Wed, 14 May 2025 18:18:04 +0000 (21:18 +0300)
committerRazvan Becheriu <razvan@isc.org>
Wed, 14 May 2025 19:54:37 +0000 (22:54 +0300)
32 files changed:
doc/sphinx/arm/hooks-lease-cmds.rst
doc/sphinx/arm/logging.rst
src/bin/admin/tests/memfile_tests.sh.in
src/bin/agent/tests/ca_process_tests.sh.in
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
src/bin/d2/tests/d2_process_tests.sh.in
src/bin/d2/tests/d2_process_unittests.cc
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_process_tests.sh.in
src/bin/dhcp4/tests/direct_client_unittest.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/dhcp6_process_tests.sh.in
src/bin/dhcp6/tests/dhcp6_test_utils.cc
src/bin/dhcp6/tests/dhcp6_test_utils.h
src/bin/keactrl/kea-ctrl-agent.conf.pre
src/bin/keactrl/kea-dhcp-ddns.conf.pre
src/bin/keactrl/kea-dhcp4.conf.pre
src/bin/keactrl/kea-dhcp6.conf.pre
src/bin/keactrl/kea-netconf.conf.pre
src/bin/keactrl/tests/keactrl_tests.sh.in
src/bin/netconf/tests/shtests/netconf_tests.sh.in
src/bin/shell/tests/basic_auth_tests.sh.in
src/bin/shell/tests/shell_process_tests.sh.in
src/bin/shell/tests/tls_ca_process_tests.sh.in
src/hooks/dhcp/lease_cmds/lease_cmds.cc
src/hooks/dhcp/lease_cmds/tests/lease_cmds4_unittest.cc
src/hooks/dhcp/lease_cmds/tests/lease_cmds6_unittest.cc
src/lib/process/Makefile.am
src/lib/process/log_parser.cc
src/lib/process/log_parser.h
src/lib/process/tests/d_cfg_mgr_unittests.cc
src/lib/process/tests/log_parser_unittests.cc

index 61ee7558ca23e88b5aea00f68fe383eaeb4e7057..053609a38f9a658e400fa31319dc7d371a67ad02 100644 (file)
@@ -1066,6 +1066,16 @@ the file in an attempt to synchronize both the files and the in-memory images
 of the lease database. The extension ``.bak`` with server PID number is added
 to the previous filename. For example ``.bak14326``.
 
+.. note::
+
+    As of Kea 2.4.2, the lease file may only be written to the data directory
+    determined during compilation: ``"[kea-install-dir]/var/lib/kea"``. This
+    path may be overridden at startup by setting the environment variable
+    ``KEA_DHCP_DATA_DIRECTORY`` to the desired path.  If a path other than
+    this value is used in ``name``, Kea will emit an error and refuse to start
+    or, if already running, log an unrecoverable error.  For ease of use in
+    specifying a custom file name simply omit the path portion from ``filename``.
+
 .. note::
 
    These commands do not replace the LFC mechanism; they should be used
index 117a277ce25c2c9ccff76fe588e4687d526ff40f..029eaf8dbb63cdb8a96dd34688413b4c8242e1ba 100644 (file)
@@ -624,6 +624,17 @@ output), ``stderr`` (messages are printed on stderr), ``syslog``
 (messages are logged to syslog using a specified name). Any other value is
 interpreted as a filename to which messages should be written.
 
+.. note::
+
+    As of Kea 2.4.2, log files may only be written to the output directory
+    determined during compilation as: ``"[kea-install-dir]/var/log/kea"``. This
+    path may be overridden at startup by setting the environment variable
+    ``KEA_LOG_FILE_DIR`` to the desired path.  If a path other than
+    this value is used in ``output``, Kea will emit an error and refuse to start
+    or, if already running, log an unrecoverable error.  For ease of use simply
+    omit the path component from ``output`` and specify only the file name.
+
+
 The ``flush`` (boolean) Option
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index c3e8c2b6fb6f361ae41387c7c3250ebf16b30f8e..f9e4554e1decf2005bb2aaeaaed6ca3e96ad4940 100644 (file)
@@ -20,6 +20,8 @@ set -eu
 . "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh"
 
 export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/admin/tests"
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/admin/tests"
+
 
 # Locations of memfile tools
 kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin"
index b5331523ec86a841e2101afd05dfb846c8a9807e..e0b8e4199e9e2dd51e0d055801353693ab2aaa31 100644 (file)
@@ -24,6 +24,12 @@ CFG_FILE="@abs_top_builddir@/src/bin/agent/tests/test_config.json"
 # Path to the Control Agent log file.
 LOG_FILE="@abs_top_builddir@/src/bin/agent/tests/test.log"
 
+# Set env KEA_HOOKS_PATH  to override DEFAULT_HOOKS_PATH
+export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/agent/tests/.libs"
+
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/agent/tests"
+
 # Control Agent configuration to be stored in the configuration file.
 CONFIG="{
     \"Control-agent\":
index 7e2aee526365294940b5aa21d12830f4881f5a6a..88af349e3e4d6cba8c5a77b6e1fb188152e84e9f 100644 (file)
@@ -242,7 +242,7 @@ public:
 };
 
 /// @brief Convenience macros for invoking runOrConfig()
-#define RUN_CONFIG_OK(a) (runConfigOrFail(a, NO_ERROR, ""))
+#define RUN_CONFIG_OK(a) (static_cast<void>(runConfigOrFail(a, NO_ERROR, "")))
 #define SYNTAX_ERROR(a,b) ASSERT_TRUE(runConfigOrFail(a,SYNTAX_ERROR,b))
 #define LOGIC_ERROR(a,b) ASSERT_TRUE(runConfigOrFail(a,LOGIC_ERROR,b))
 
index 408e8e0fad27056efa46e0c663e9fa796dd9acf1..556866bbaf912908f7c6a5a16b2b58583d07ce11 100644 (file)
@@ -20,8 +20,13 @@ set -eu
 CFG_FILE="@abs_top_builddir@/src/bin/d2/tests/test_config.json"
 # Path to the D2 log file.
 LOG_FILE="@abs_top_builddir@/src/bin/d2/tests/test.log"
+
 # Set env KEA_HOOKS_PATH  to override DEFAULT_HOOKS_PATH
 export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/d2/tests/.libs"
+
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/d2/tests"
+
 # D2 configuration to be stored in the configuration file.
 CONFIG="{
     \"DhcpDdns\":
index cfff351cfa90122d4c8487d8870dcaa15bdfcc08..ce321d21f55d2bab8da107295f95abc38f45e6d5 100644 (file)
@@ -636,7 +636,7 @@ TEST_F(D2ProcessTest, notLoopbackTest) {
     // Note we don't care nor can we predict if this
     // succeeds or fails. The address and port may or may
     // not be valid on the test host.
-    runWithConfig(config);
+    static_cast<void>(runWithConfig(config));
 }
 
 /// @brief Used to permit visual inspection of logs to ensure
index a135c64ea881c0ded76eb5196710f4f38498a944..3f0f4d678c2299e4f896bf5541edc6b4a78f3d37 100644 (file)
@@ -21,6 +21,7 @@
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <hooks/hooks_manager.h>
 #include <log/logger_support.h>
+#include <process/log_parser.h>
 #include <stats/stats_mgr.h>
 #include <util/multi_threading_mgr.h>
 #include <util/chrono_time_utils.h>
@@ -53,6 +54,7 @@ using namespace isc::hooks;
 using namespace isc::stats;
 using namespace isc::test;
 using namespace isc::util;
+using namespace isc::process;
 namespace ph = std::placeholders;
 
 namespace {
@@ -113,6 +115,7 @@ public:
     ///
     /// Sets socket path to its default value.
     CtrlChannelDhcpv4SrvTest() : interfaces_("\"*\"") {
+        resetLogPath();
         const char* env = getenv("KEA_SOCKET_TEST_DIR");
         if (env) {
             socket_path_ = string(env) + "/kea4.sock";
@@ -127,6 +130,7 @@ public:
 
     /// @brief Destructor
     ~CtrlChannelDhcpv4SrvTest() {
+        resetLogPath();
         LeaseMgrFactory::destroy();
         StatsMgr::instance().removeAll();
 
@@ -143,6 +147,18 @@ public:
         IfaceMgr::instance().detectIfaces();
     };
 
+    /// @brief Sets the log path where log output may be written.
+    /// @param explicit_path path to use as the log path.
+    void setLogTestPath(const std::string explicit_path = "") {
+        LogConfigParser::getLogPath(true, (!explicit_path.empty() ?
+                                           explicit_path : TEST_DATA_BUILDDIR));
+    }
+
+    /// @brief Resets the log path to TEST_DATA_BUILDDIR.
+    void resetLogPath() {
+        LogConfigParser::getLogPath(true);
+    }
+
     /// @brief Returns pointer to the server's IO service.
     ///
     /// @return Pointer to the server's IO service or null pointer if the server
@@ -735,6 +751,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) {
 
 // Check that the "config-set" command will replace current configuration
 TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
+    setLogTestPath("/dev");
     createUnixChannelServer();
 
     // Define strings to permutate the config arguments
@@ -971,6 +988,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configHashGet) {
 
 // Verify that the "config-test" command will do what we expect.
 TEST_F(CtrlChannelDhcpv4SrvTest, configTest) {
+    setLogTestPath("/dev");
     createUnixChannelServer();
 
     // Define strings to permutate the config arguments
index 9b205b393d33533c7af73b97ce39414ad3c1c59d..d09c416e9ad3ffd7317b016f9c34f8983fc64bc0 100644 (file)
@@ -31,6 +31,9 @@ HOOK_PATH="@abs_top_builddir@/src/bin/dhcp4/tests/.libs/libco3.so"
 # Set env KEA_HOOKS_PATH to override DEFAULT_HOOKS_PATH
 export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/dhcp4/tests/.libs"
 
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/dhcp4/tests"
+
 # Kea configuration to be stored in the configuration file.
 CONFIG="{
     \"Dhcp4\":
index 27439e2baa2c4b563e7a4849973f44f29802d628..32030eb7311c8b710f78c3697527b9d4c29bbd73 100644 (file)
@@ -219,7 +219,7 @@ DirectClientTest::createClientMessage(const Pkt4Ptr& msg,
     // local and remote address are set like it was a message sent from the
     // directly connected client.
     Pkt4Ptr received;
-    createPacketFromBuffer(msg, received);
+    static_cast<void>(createPacketFromBuffer(msg, received));
     received->setIface(iface);
     received->setIndex(ifindex);
     received->setLocalAddr(IOAddress("255.255.255.255"));
index ac52dd81832e54681114c13f73f338365c94238b..ed02e4c979912919cde5740fb52fadf5993227f6 100644 (file)
@@ -647,6 +647,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelShutdown) {
 
 // Check that the "config-set" command will replace current configuration
 TEST_F(CtrlChannelDhcpv6SrvTest, configSet) {
+    setLogTestPath("/dev");
     createUnixChannelServer();
 
     // Define strings to permutate the config arguments
@@ -884,6 +885,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configHashGet) {
 
 // Verify that the "config-test" command will do what we expect.
 TEST_F(CtrlChannelDhcpv6SrvTest, configTest) {
+    setLogTestPath("/dev");
     createUnixChannelServer();
 
     // Define strings to permutate the config arguments
index 212d5f80323e860aa3d8b21437ea979ecdd5a18b..a54ec2b1fca37b5dadaa68c7bd2ead1e449c702c 100644 (file)
@@ -28,9 +28,12 @@ export KEA_LFC_EXECUTABLE="@abs_top_builddir@/src/bin/lfc/kea-lfc"
 # Path to test hooks library
 HOOK_PATH="@abs_top_builddir@/src/bin/dhcp6/tests/.libs/libco3.so"
 
-# Set env KEA_HOOKS_PATH  to override DEFAULT_HOOKS_PATH
+# Set env KEA_HOOKS_PATH to override DEFAULT_HOOKS_PATH
 export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/dhcp6/tests/.libs"
 
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/dhcp6/tests"
+
 # Kea configuration to be stored in the configuration file.
 CONFIG="{
     \"Dhcp6\":
index 0ba02d0a5642a8bb9354170de6f24f59b1b4317c..8d0cf955e967f3dfbdba2a1608a6e14ce266fdf4 100644 (file)
@@ -13,6 +13,7 @@
 #include <dhcp6/tests/dhcp6_test_utils.h>
 #include <dhcp6/json_config_parser.h>
 #include <log/logger_support.h>
+#include <process/log_parser.h>
 #include <stats/stats_mgr.h>
 #include <util/pointer_util.h>
 #include <cstdio>
@@ -24,6 +25,7 @@ using namespace isc::dhcp;
 using namespace isc::asiolink;
 using namespace isc::stats;
 using namespace isc::util;
+using namespace isc::process;
 
 namespace isc {
 namespace dhcp {
@@ -36,6 +38,7 @@ BaseServerTest::BaseServerTest() {
     CfgMgr::instance().setFamily(AF_INET6);
     original_datadir_ = CfgMgr::instance().getDataDir();
     CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR);
+    resetLogPath();
 }
 
 BaseServerTest::~BaseServerTest() {
@@ -58,6 +61,18 @@ BaseServerTest::~BaseServerTest() {
 
     // Revert to unit test logging, in case the test reconfigured it.
     isc::log::initLogger();
+    resetLogPath();
+}
+
+void
+BaseServerTest::setLogTestPath(const std::string explicit_path /* = "" */) {
+    LogConfigParser::getLogPath(true, (!explicit_path.empty() ?
+                                       explicit_path : TEST_DATA_BUILDDIR));
+}
+
+void
+BaseServerTest::resetLogPath() {
+    LogConfigParser::getLogPath(true);
 }
 
 Dhcpv6SrvTest::Dhcpv6SrvTest()
index c5150f67720e8aad805e6d52e3d8c3dc89d5615d..d2b2d45af6376690b47544c5a6545ec8c015db94 100644 (file)
@@ -123,6 +123,13 @@ public:
     /// @brief Destructor.
     virtual ~BaseServerTest();
 
+    /// @brief Sets the log path where log output may be written.
+    /// @param explicit_path path to use as the log path.
+    void setLogTestPath(const std::string explicit_path = "");
+
+    /// @brief Resets the log path to TEST_DATA_BUILDDIR.
+    void resetLogPath();
+
 private:
 
     /// @brief Holds the original data directory.
index e6ae8b8ac6701ddbfbb402898e55ffa19222d7ad..d4d8e041f9586e99011b5c53b773754976698676 100644 (file)
@@ -73,7 +73,7 @@
                 // - syslog (logs to syslog)
                 // - syslog:name (logs to syslog using specified name)
                 // Any other value is considered a name of the file
-                "output": "@localstatedir@/log/kea-ctrl-agent.log"
+                "output": "kea-ctrl-agent.log"
 
                 // Shorter log pattern suitable for use with systemd,
                 // avoids redundant information
index 07c204283aadd100ae4f73478cf138d7ee2c393d..f88a3a393f958ec9a3392c1b513f3f246ff276c2 100644 (file)
@@ -44,7 +44,7 @@
                 // - syslog (logs to syslog)
                 // - syslog:name (logs to syslog using specified name)
                 // Any other value is considered a name of the file
-                "output": "@localstatedir@/log/kea-ddns.log"
+                "output": "kea-ddns.log"
 
                 // Shorter log pattern suitable for use with systemd,
                 // avoids redundant information
index 6edb8a14c70d319688ea0a3afbb45c249425ca0e..adb5647914bbf63449fe403afd9ef47b00c9f671 100644 (file)
                 // - syslog (logs to syslog)
                 // - syslog:name (logs to syslog using specified name)
                 // Any other value is considered a name of the file
-                "output": "@localstatedir@/log/kea-dhcp4.log"
+                "output": "kea-dhcp4.log"
 
                 // Shorter log pattern suitable for use with systemd,
                 // avoids redundant information
index b167e1ffe0282df92753f54a830b3525798c2255..ae2f01f88ae866b724e6c5657b3939e77391b7ef 100644 (file)
                 // - syslog (logs to syslog)
                 // - syslog:name (logs to syslog using specified name)
                 // Any other value is considered a name of the file
-                "output": "@localstatedir@/log/kea-dhcp6.log"
+                "output": "kea-dhcp6.log"
 
                 // Shorter log pattern suitable for use with systemd,
                 // avoids redundant information
index 37d15b14a1d816778af2bf5dadd8980b5f4d625a..80372eb266e72fb3bc1cafcdf4cc517f84ce861a 100644 (file)
@@ -69,7 +69,7 @@
                 // - syslog (logs to syslog)
                 // - syslog:name (logs to syslog using specified name)
                 // Any other value is considered a name of a time
-                "output": "@localstatedir@/log/kea-netconf.log"
+                "output": "kea-netconf.log"
 
                 // Shorter log pattern suitable for use with systemd,
                 // avoids redundant information
index b4b49cb625c5f20f7ca45ce94350118f273cbe60..e3ac422212dc1eba68176f803f1842192b57e9df 100644 (file)
@@ -61,6 +61,10 @@ KEACTRL_BUILD_DIR="@abs_top_builddir@"
 KEACTRL_CFG_FILE="@abs_top_builddir@/src/bin/keactrl/tests/keactrl_test.conf"
 # Path to the Kea log file.
 LOG_FILE="@abs_top_builddir@/src/bin/keactrl/tests/test.log"
+
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/keactrl/tests"
+
 # Binaries' names
 wildcard_name="kea-"
 kea4_name="${wildcard_name}dhcp4"
index b6ba61122325b2867c4a217fd75302bb33e7dee6..a5ad5909d91668db401abc40cd2949b3e8d82b09 100644 (file)
@@ -19,6 +19,7 @@ set -eu
 # Path to the temporary configuration file.
 CFG_FILE="@abs_top_builddir@/src/bin/netconf/tests/shtests/test_config.json"
 # Path to the Kea log file.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/netconf/tests/shtests"
 LOG_FILE="@abs_top_builddir@/src/bin/netconf/tests/shtests/test.log"
 
 # Kea-netconf configuration to be stored in the configuration file.
index 36ea16c2fcb2e7b7968a72516626a58fe917a06f..d4bd05871681ce87c48e1032c736960ee54fd137 100644 (file)
@@ -22,6 +22,9 @@ CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 # Path to the Control Agent log file.
 LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
 
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
 # Control Agent configuration to be stored in the configuration file.
 # todo: use actual configuration once we support it.
 CONFIG="{
index 5f2e00a14963523bff3535f7b7da50a0a2e82bba..cbef463c79f61e9db0185617b10a0eeb7bd927c8 100644 (file)
@@ -22,6 +22,9 @@ CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 # Path to the Control Agent log file.
 LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
 
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
 # Control Agent configuration to be stored in the configuration file.
 # todo: use actual configuration once we support it.
 CONFIG="{
index 2c23188c081623b4174c8f82cb302843d430bddb..1e985bd2109a26b8b6e2f096d80fa55ad4593cf6 100644 (file)
@@ -20,10 +20,13 @@ set -eu
 . "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh"
 
 # Path to the temporary configuration file.
-CFG_FILE="@abs_top_builddir@/src/bin/agent/tests/test_config.json"
+CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 
 # Path to the Control Agent log file.
-LOG_FILE="@abs_top_builddir@/src/bin/agent/tests/test.log"
+LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
 
 # Path to the test certificate authority directory.
 TEST_CA_DIR="@abs_top_srcdir@/src/lib/asiolink/testutils/ca"
index f4531aa6aac413c5baa4b62a044efe2ae782f65b..8420d1d85e15a9ca09be409dc508b8ea7f9d5077 100644 (file)
@@ -2671,9 +2671,12 @@ LeaseCmdsImpl::leaseWriteHandler(CalloutHandle& handle) {
         if (file->getType() != Element::string) {
             isc_throw(BadValue, "'filename' parameter must be a string");
         }
-        string filename = file->stringValue();
-        if (filename.empty()) {
-            isc_throw(BadValue, "'filename' parameter is empty");
+
+        std::string filename;
+        try {
+          filename = CfgMgr::instance().validatePath(file->stringValue());
+        } catch (const std::exception& ex) {
+            isc_throw(BadValue, "'filename' parameter is invalid: " << ex.what());
         }
 
         if (v4) {
@@ -2681,6 +2684,7 @@ LeaseCmdsImpl::leaseWriteHandler(CalloutHandle& handle) {
         } else {
             LeaseMgrFactory::instance().writeLeases6(filename);
         }
+
         ostringstream s;
         s << (v4 ? "IPv4" : "IPv6")
           << " lease database into '"
index 444b4be0240a92f6d5a39c0eca9e9bbcbf5ec261..b02cddfec4c0f8e83d17202bc0fb8ef9008516ef 100644 (file)
@@ -3424,8 +3424,23 @@ void Lease4CmdsTest::testLease4Write() {
         "        \"filename\": \"\"\n"
         "    }\n"
         "}";
-    exp_rsp = "'filename' parameter is empty";
+    exp_rsp = "'filename' parameter is invalid: path: '' has no filename";
     testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+    // Filename must use supported path.
+    txt =
+        "{\n"
+        "    \"command\": \"lease4-write\",\n"
+        "    \"arguments\": {"
+        "        \"filename\": \"/tmp/myleases.txt\"\n"
+        "    }\n"
+        "}";
+
+    std::ostringstream os;
+    os << "'filename' parameter is invalid: invalid path specified:"
+       << " '/tmp', supported path is '" << CfgMgr::instance().getDataDir() << "'";
+
+    testCommand(txt, CONTROL_RESULT_ERROR, os.str());
 }
 
 TEST_F(Lease4CmdsTest, lease4AddMissingParams) {
index 9b95b5e08d271598db5b9d1c374a87e954d08d14..466f4daaaaf72389c91e4540559eed3c3c51c000 100644 (file)
@@ -4148,7 +4148,7 @@ void Lease6CmdsTest::testLease6ConflictingBulkApplyAdd() {
 }
 
 void Lease6CmdsTest::testLease6Write() {
-    // lease4-write negative tests. Positive tests are in the
+    // lease6-write negative tests. Positive tests are in the
     // memfile_lease_mgr_unittest.cc file.
 
     // Initialize lease manager (true = v6, false = don't add leases)
@@ -4183,8 +4183,21 @@ void Lease6CmdsTest::testLease6Write() {
         "        \"filename\": \"\"\n"
         "    }\n"
         "}";
-    exp_rsp = "'filename' parameter is empty";
+    exp_rsp = "'filename' parameter is invalid: path: '' has no filename";
     testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+    // Filename must use supported path.
+    txt =
+        "{\n"
+        "    \"command\": \"lease6-write\",\n"
+        "    \"arguments\": {"
+        "        \"filename\": \"/tmp/myleases.txt\"\n"
+        "    }\n"
+        "}";
+
+    std::ostringstream os;
+    os << "'filename' parameter is invalid: invalid path specified:"
+       << " '/tmp', supported path is '" << CfgMgr::instance().getDataDir() << "'";
 }
 
 TEST_F(Lease6CmdsTest, lease6AddMissingParams) {
index cb20ab817ca732c3567a8f97b26c3351eb865a7c..e5a39d1811147ba7da90c59800e6e62ee481372c 100644 (file)
@@ -3,6 +3,8 @@ SUBDIRS = cfgrpt . testutils tests
 dhcp_data_dir = @runstatedir@/@PACKAGE@
 AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += -DDATA_DIR="\"$(dhcp_data_dir)\""
+log_file_dir = @localstatedir@/log/@PACKAGE@
+AM_CPPFLAGS += -DLOGFILE_DIR="\"$(log_file_dir)\""
 
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS  = $(KEA_CXXFLAGS)
index 258aa4eaf55fd17ca1a3be07ede9fc2b9811e7e0..daf397cc6de703214efdc79cd1361a91315a7255 100644 (file)
 #include <log/logger_support.h>
 #include <log/logger_manager.h>
 #include <log/logger_name.h>
+#include <util/filesystem.h>
 
 using namespace isc::data;
 using namespace isc::log;
+using namespace isc::util::file;
 
 namespace isc {
 namespace process {
 
+namespace {
+    // Singleton PathChecker to set and hold valid log file path.
+    PathCheckerPtr log_path_checker_;
+};
+
 LogConfigParser::LogConfigParser(const ConfigPtr& storage)
     :config_(storage), verbose_(false) {
     if (!storage) {
@@ -113,6 +120,28 @@ void LogConfigParser::parseConfigEntry(isc::data::ConstElementPtr entry) {
     config_->addLoggingInfo(info);
 }
 
+std::string
+LogConfigParser::getLogPath(bool reset /* = false */, const std::string explicit_path /* = "" */) {
+    if (!log_path_checker_ || reset) {
+        log_path_checker_.reset(new PathChecker(LOGFILE_DIR, "KEA_LOG_FILE_DIR"));
+        if (!explicit_path.empty()) {
+            log_path_checker_->getPath(true, explicit_path);
+        }
+    }
+
+    return (log_path_checker_->getPath());
+}
+
+std::string
+LogConfigParser::validatePath(const std::string logpath,
+                                   bool enforce_path /* = true */) {
+    if (!log_path_checker_) {
+        getLogPath();
+    }
+
+    return (log_path_checker_->validatePath(logpath, enforce_path));
+}
+
 void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destination,
                                          isc::data::ConstElementPtr output_options) {
     if (!output_options) {
@@ -128,7 +157,20 @@ void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destin
             isc_throw(BadValue, "output_options entry does not have a mandatory 'output' "
                       "element (" << output_option->getPosition() << ")");
         }
-        dest.output_ = output->stringValue();
+
+        auto output_str = output->stringValue();
+        if ((output_str == "stdout") ||
+            (output_str == "stderr") ||
+            (output_str == "syslog")) {
+            dest.output_ = output_str;
+        } else {
+            try {
+                dest.output_ = validatePath(output_str);
+            } catch (const std::exception& ex) {
+                isc_throw(BadValue, "invalid path in `output`: " << ex.what()
+                          << " (" << output_option->getPosition() << ")");
+            }
+        }
 
         isc::data::ConstElementPtr maxver_ptr = output_option->get("maxver");
         if (maxver_ptr) {
index 92209bd17fec2f7f5ffb79e211b56290c589bfc4..e3dc34ddc20dc3f1204897dc842ce78d588c0c0e 100644 (file)
@@ -58,6 +58,29 @@ public:
     void parseConfiguration(const isc::data::ConstElementPtr& log_config,
                             bool verbose = false);
 
+    /// @brief Fetches the supported log file path.
+    ///
+    /// The first call to this function with no arguments will set the default
+    /// hooks path to either the value of LOGFILE_DIR or the environment
+    /// variable KEA_LOG_FILE_DIR if it is defined. Subsequent calls with no
+    /// arguments will simply return this value.
+    ///
+    /// @param reset recalculate when true, defaults to false.
+    /// @param explicit_path set default log path to this value. This is
+    /// for testing purposes only.
+    ///
+    /// @return String containing the default log file path.
+    static std::string getLogPath(bool reset = false, const std::string explicit_path = "");
+
+    /// @brief Validates a library path against the supported path for log files.
+    ///
+    /// @param logpath path to validate.
+    /// @param enforce_path enables validation against the supported path.
+    /// If false verifies only that the path contains a file name.
+    ///
+    /// @return validated path
+    static std::string validatePath(const std::string logpath, bool enforce_path = true);
+
 private:
 
     /// @brief Parses one JSON structure in Server/loggers" array
index faa3e928970dba282ccea15bba5269cd5c68e788..c965eb759b16ff43cb964078c156e69277b1823c 100644 (file)
@@ -261,7 +261,7 @@ TEST_F(DStubCfgMgrTest, simpleParseConfig) {
     string config = "{ \"bool_test\": true , \n"
                     "  \"uint32_test\": 77 , \n"
                     "  \"string_test\": \"hmmm chewy\" }";
-    ASSERT_NO_THROW(fromJSON(config));
+    ASSERT_NO_THROW(static_cast<void>(fromJSON(config)));
 
     answer_ = cfg_mgr_->simpleParseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(0));
@@ -273,7 +273,7 @@ TEST_F(DStubCfgMgrTest, simpleParseConfigWithCallback) {
     string config = "{ \"bool_test\": true , \n"
                     "  \"uint32_test\": 77 , \n"
                     "  \"string_test\": \"hmmm chewy\" }";
-    ASSERT_NO_THROW(fromJSON(config));
+    ASSERT_NO_THROW(static_cast<void>(fromJSON(config)));
 
     answer_ = cfg_mgr_->simpleParseConfig(config_set_, false,
                                           []() {
index 0341dd7b2bb0d23243727900e5b1c78684ef7fc2..bfb0900752dd38b578dc952558794144a3a1c24a 100644 (file)
@@ -32,7 +32,9 @@ namespace {
 class LoggingTest : public ::testing::Test {
     public:
         /// @brief Constructor
-        LoggingTest() {}
+        LoggingTest() {
+            resetLogPath();
+        }
 
         /// @brief Destructor
         ///
@@ -40,6 +42,7 @@ class LoggingTest : public ::testing::Test {
         ~LoggingTest() {
             isc::log::initLogger();
             wipeFiles();
+            resetLogPath();
         }
 
     /// @brief Generates a log file name suffixed with a rotation number
@@ -63,6 +66,19 @@ class LoggingTest : public ::testing::Test {
         static_cast<void>(remove(os.str().c_str()));
     }
 
+    /// @brief Sets the Hooks path from which hooks can be loaded.
+    /// @param explicit_path path to use as the hooks path.
+    void setLogTestPath(const std::string explicit_path = "") {
+        LogConfigParser::getLogPath(true,
+                                     (!explicit_path.empty() ?
+                                      explicit_path : TEST_DATA_BUILDDIR));
+    }
+
+    /// @brief Resets the log path to default.
+    void resetLogPath() {
+        LogConfigParser::getLogPath(true);
+    }
+
     /// @brief Name of the log file
     static const char* TEST_LOG_NAME;
 
@@ -216,7 +232,8 @@ TEST_F(LoggingTest, parsingFile) {
     EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
 
     ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
-    EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
+    EXPECT_EQ(LogConfigParser::validatePath("logfile.txt"),
+              storage->getLoggingInfo()[0].destinations_[0].output_);
     // Default for immediate flush is true
     EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_);
 
@@ -272,14 +289,15 @@ TEST_F(LoggingTest, multipleLoggers) {
     EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_);
     EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
     ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
-    EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
+    EXPECT_EQ(LogConfigParser::validatePath("logfile.txt"),
+              storage->getLoggingInfo()[0].destinations_[0].output_);
     EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_);
-
     EXPECT_EQ("wombat", storage->getLoggingInfo()[1].name_);
     EXPECT_EQ(99, storage->getLoggingInfo()[1].debuglevel_);
     EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[1].severity_);
     ASSERT_EQ(1, storage->getLoggingInfo()[1].destinations_.size());
-    EXPECT_EQ("logfile2.txt" , storage->getLoggingInfo()[1].destinations_[0].output_);
+    EXPECT_EQ(LogConfigParser::validatePath("logfile2.txt"),
+              storage->getLoggingInfo()[1].destinations_[0].output_);
     EXPECT_FALSE(storage->getLoggingInfo()[1].destinations_[0].flush_);
 }
 
@@ -287,7 +305,7 @@ TEST_F(LoggingTest, multipleLoggers) {
 // into Configuration usable by log4cplus. This test checks that more than
 // one logging destination can be configured.
 TEST_F(LoggingTest, multipleLoggingDestinations) {
-
+    setLogTestPath();
     const char* config_txt =
     "{ \"loggers\": ["
     "    {"
@@ -322,7 +340,8 @@ TEST_F(LoggingTest, multipleLoggingDestinations) {
     EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_);
     EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
     ASSERT_EQ(2, storage->getLoggingInfo()[0].destinations_.size());
-    EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
+    EXPECT_EQ(LogConfigParser::validatePath("logfile.txt"),
+              storage->getLoggingInfo()[0].destinations_[0].output_);
     EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_);
     EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[1].output_);
     EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[1].flush_);
@@ -334,6 +353,7 @@ TEST_F(LoggingTest, multipleLoggingDestinations) {
 // we can correctly configure logging such that rotation occurs as
 // expected.
 TEST_F(LoggingTest, logRotate) {
+    setLogTestPath();
     wipeFiles();
 
     std::ostringstream os;
@@ -374,7 +394,7 @@ TEST_F(LoggingTest, logRotate) {
     EXPECT_EQ(TEST_MAX_VERS, server_cfg->getLoggingInfo()[0].destinations_[0].maxver_);
 
     // Make sure we have the initial log file.
-    ASSERT_TRUE(isc::test::fileExists(TEST_LOG_NAME));
+    ASSERT_TRUE(isc::test::fileExists(LogConfigParser::validatePath(TEST_LOG_NAME)));
 
     // Now generate a log we know will be large enough to force a rotation.
     // We borrow a one argument log message for the test.
@@ -384,7 +404,7 @@ TEST_F(LoggingTest, logRotate) {
     for (int i = 1; i < TEST_MAX_VERS + 1; i++) {
         // Output the big log and make sure we get the expected rotation file.
         LOG_INFO(logger, DCTL_CONFIG_COMPLETE).arg(big_arg);
-        EXPECT_TRUE(isc::test::fileExists(logName(i).c_str()));
+        EXPECT_TRUE(isc::test::fileExists(LogConfigParser::validatePath(logName(i).c_str())));
     }
 
     // Clean up.
@@ -511,6 +531,7 @@ void testMaxSize(uint64_t maxsize_candidate, uint64_t expected_maxsize) {
 
 // Test that maxsize can be configured with high values.
 TEST_F(LoggingTest, maxsize) {
+    setLogTestPath();
     testMaxSize(TEST_MAX_SIZE, TEST_MAX_SIZE);
     testMaxSize(std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max());
     testMaxSize(std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max());