]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3831] Error on bad lease file path
authorThomas Markwalder <tmark@isc.org>
Thu, 8 May 2025 19:14:08 +0000 (15:14 -0400)
committerAndrei Pavel <andrei@isc.org>
Fri, 16 May 2025 09:20:43 +0000 (12:20 +0300)
/doc/examples/kea4/all-keys-netconf.json
/doc/examples/kea4/all-keys.json
/doc/examples/kea4/dhcpv4-over-dhcpv6.json
/doc/examples/kea6/all-keys-netconf.json
/doc/examples/kea6/all-keys.json
/doc/examples/kea6/dhcpv4-over-dhcpv6.json
    Remove "/tmp" from lease file names

/src/bin/admin/tests/memfile_tests.sh.in
/src/bin/dhcp4/tests/dhcp4_process_tests.sh.in
/src/bin/dhcp6/tests/dhcp6_process_tests.sh.in
    export KEA_DHCP_DATA_DIR

/src/bin/shell/tests/dhcp4_basic_auth_tests.sh.in
/src/bin/shell/tests/dhcp6_basic_auth_tests.sh.in

/src/bin/shell/tests/shell_dhcp4_process_tests.sh.in
/src/bin/shell/tests/shell_dhcp6_process_tests.sh.in

/src/bin/shell/tests/tls_dhcp4_process_tests.sh.in
/src/bin/shell/tests/tls_dhcp6_process_tests.sh.in

/src/lib/dhcpsrv/dhcpsrv_messages.*
    Replace WARN message with DHCPSRV_MEMFILE_FAILED_TO_OPEN error

/src/lib/dhcpsrv/memfile_lease_mgr.cc
    Memfile_LeaseMgr::getDefaultLeaseFilePath()
    - uses CfgMgr::getDataDir()
    Memfile_LeaseMgr::initLeaseFilePath()
     - call CfgMgr::validatePath() without try-catch
    Memfile_LeaseMgr::factory()
    - log error and rethrow

/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
    Added use of EnvVarWrapper

/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
    Updated tests
    TEST_F(MemfileLeaseMgrTest, defaultDataDir)
    TEST_F(MemfileLeaseMgrTest, dataDirEnvVarOverride)
    TEST_F(MemfileLeaseMgrTest, dataDirExplicitOveride)
    - new tests

/src/lib/testutils/Makefile.am
   Added env_var_wrapper.*

25 files changed:
doc/examples/kea4/all-keys-netconf.json
doc/examples/kea4/all-keys.json
doc/examples/kea4/dhcpv4-over-dhcpv6.json
doc/examples/kea6/all-keys-netconf.json
doc/examples/kea6/all-keys.json
doc/examples/kea6/dhcpv4-over-dhcpv6.json
src/bin/admin/tests/memfile_tests.sh.in
src/bin/dhcp4/tests/dhcp4_process_tests.sh.in
src/bin/dhcp6/tests/dhcp6_process_tests.sh.in
src/bin/shell/tests/dhcp4_basic_auth_tests.sh.in
src/bin/shell/tests/dhcp6_basic_auth_tests.sh.in
src/bin/shell/tests/shell_dhcp4_process_tests.sh.in
src/bin/shell/tests/shell_dhcp6_process_tests.sh.in
src/bin/shell/tests/tls_dhcp4_process_tests.sh.in
src/bin/shell/tests/tls_dhcp6_process_tests.sh.in
src/lib/dhcpsrv/dhcpsrv_messages.cc
src/lib/dhcpsrv/dhcpsrv_messages.h
src/lib/dhcpsrv/dhcpsrv_messages.mes
src/lib/dhcpsrv/memfile_lease_mgr.cc
src/lib/dhcpsrv/memfile_lease_mgr.h
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
src/lib/testutils/Makefile.am
src/lib/testutils/env_var_wrapper.cc [new file with mode: 0644]
src/lib/testutils/env_var_wrapper.h [new file with mode: 0644]

index 1b956edec8874a6ae10834b5905b4413c7209e39..b595fe703aba99f0592d97498913d44e66bbbf31 100644 (file)
 
             // Name of the lease file. In the case of a database it specifies the
             // database name.
-            "name": "/tmp/kea-dhcp4.csv",
+            "name": "kea-dhcp4.csv",
 
             // memfile-specific parameter indicating whether leases should
             // be saved on persistent storage (disk) or not. The true value
index 0582233e2c02557669f40d5a7087df9391419231..5117e04a3b8cf970a1aa44fa31adf08ed1609ced 100644 (file)
 
             // Name of the lease file. In the case of a database it specifies the
             // database name.
-            "name": "/tmp/kea-dhcp4.csv",
+            "name": "kea-dhcp4.csv",
 
             // memfile-specific parameter indicating whether leases should
             // be saved on persistent storage (disk) or not. The true value
index c622c4225e43e08670d8090b967971e0cf48d898..8cf61b7782f63f9faa69bb0660383a931cb3390b 100644 (file)
@@ -12,7 +12,7 @@
 
   "lease-database": {
       "type": "memfile",
-      "name": "/tmp/kea-dhcp4.csv",
+      "name": "kea-dhcp4.csv",
       "lfc-interval": 3600
   },
 
index 8f812e0f5b784a09e8880041e098b4b496e45dc2..7a980f80ebd5861970191150da8b0424d4deea5b 100644 (file)
 
             // Name of the lease file. In the case of a database it specifies the
             // database name.
-            "name": "/tmp/kea-dhcp6.csv",
+            "name": "kea-dhcp6.csv",
 
             // memfile-specific parameter indicating whether leases should
             // be saved on persistent storage (disk) or not. The true value
index bb6842ab4978f4a8b4b60b58f956e488976b0d53..1b2dc6e11d2fda8693aefa2e6b5cfe306779a9ce 100644 (file)
 
             // Name of the lease file. In the case of a database it specifies the
             // database name.
-            "name": "/tmp/kea-dhcp6.csv",
+            "name": "kea-dhcp6.csv",
 
             // memfile-specific parameter indicating whether leases should
             // be saved on persistent storage (disk) or not. The true value
index 56e371e3ceeb921b53c213f4d70740ab3f3e26d9..d832ca56516434e6ab7c3364cb68dbeb1bb258e8 100644 (file)
@@ -13,7 +13,7 @@
 
   "lease-database": {
       "type": "memfile",
-      "name": "/tmp/kea-dhcp6.csv"
+      "name": "kea-dhcp6.csv"
   },
 
   "preferred-lifetime": 3000,
index 14b1904e85150ecbe402fe0d96a3f3987ec116d4..bd92ed672be4b657c6e9bffec782db08b71be04f 100755 (executable)
@@ -14,6 +14,8 @@ set -eu
 # shellcheck source=src/lib/testutils/dhcp_test_lib.sh.in
 . "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh"
 
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/admin/tests"
+
 # Locations of memfile tools
 kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin"
 kea_lfc="@abs_top_builddir@/src/bin/lfc/kea-lfc"
index cd2d4bf17e050d182132da815c1742258efc7056..1b6b87f595183b75832fcd6d41f980911e4ecf57 100755 (executable)
@@ -15,6 +15,7 @@ CFG_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/test_config.json"
 # Path to the Kea log file.
 LOG_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/test.log"
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/dhcp4/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/test_leases.csv"
 # Path to the Kea LFC application
 export KEA_LFC_EXECUTABLE="@abs_top_builddir@/src/bin/lfc/kea-lfc"
index 20dfcc04db7d18c346897385a24e06d471493e34..06356538de5f0728e5272211e543da08410cad21 100755 (executable)
@@ -15,6 +15,7 @@ CFG_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/test_config.json"
 # Path to the Kea log file.
 LOG_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/test.log"
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/dhcp6/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/test_leases.csv"
 # Path to the Kea LFC application
 export KEA_LFC_EXECUTABLE="@abs_top_builddir@/src/bin/lfc/kea-lfc"
index 36e044edd002fa770afa3cb60d644496061e6535..33b737ec8fabf5744a67829a2049fce606561714 100755 (executable)
@@ -20,6 +20,7 @@ test_suite="dhcp4"
 CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/shell/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/shell/tests/test_leases.csv"
 
 # Path to the Dhcp4 Server log file.
index b02b083df3c50c4343cbab1eddeda0d735de59df..07bf165077cdfcb5905c6d0856a99d38b8d663e6 100755 (executable)
@@ -20,6 +20,7 @@ test_suite="dhcp6"
 CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/shell/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/shell/tests/test_leases.csv"
 
 # Path to the Dhcp6 Server log file.
index a3805cf173ed62feb005db12c559e5b90f4b76f1..1b06ef1eb540dc1c48783497ff8199ef9932b0dd 100755 (executable)
@@ -20,6 +20,7 @@ test_suite="dhcp4"
 CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/shell/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/shell/tests/test_leases.csv"
 
 # Path to the Dhcp4 Server log file.
index f253f66c50d75dca0ffe05427a9f0287f427dde4..26e4711b63fa826189c6a6473f94659f575b6bd3 100755 (executable)
@@ -20,6 +20,7 @@ test_suite="dhcp6"
 CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/shell/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/shell/tests/test_leases.csv"
 
 # Path to the Dhcp6 Server log file.
index bd59b1cf766af6893dd60b31b17909ebb26008c1..7e200994de5ad92bcb1087a82005a36ccd0f6b82 100755 (executable)
@@ -21,6 +21,7 @@ test_suite="dhcp4"
 CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/shell/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/shell/tests/test_leases.csv"
 
 # Path to the Dhcp4 Server log file.
index 6df92a2ccf818dd9a304ade3feda9486d713d95b..7f5269d9f7e436cdfb1d331e64032d62407b2d89 100755 (executable)
@@ -21,6 +21,7 @@ test_suite="dhcp6"
 CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"
 
 # Path to the Kea lease file.
+export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/shell/tests"
 LEASE_FILE="@abs_top_builddir@/src/bin/shell/tests/test_leases.csv"
 
 # Path to the Dhcp6 Server log file.
index 6ab28a4192133d5dab304b5064b0e99e6ad9d873..f47bbfe4645e9d1b65d88c95c0106496dbeada99 100644 (file)
@@ -100,6 +100,7 @@ extern const isc::log::MessageID DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6 = "DH
 extern const isc::log::MessageID DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START = "DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START";
 extern const isc::log::MessageID DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4 = "DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4";
 extern const isc::log::MessageID DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4_ERROR = "DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4_ERROR";
+extern const isc::log::MessageID DHCPSRV_MEMFILE_FAILED_TO_OPEN = "DHCPSRV_MEMFILE_FAILED_TO_OPEN";
 extern const isc::log::MessageID DHCPSRV_MEMFILE_GET4 = "DHCPSRV_MEMFILE_GET4";
 extern const isc::log::MessageID DHCPSRV_MEMFILE_GET6 = "DHCPSRV_MEMFILE_GET6";
 extern const isc::log::MessageID DHCPSRV_MEMFILE_GET6_DUID = "DHCPSRV_MEMFILE_GET6_DUID";
@@ -275,6 +276,7 @@ const char* values[] = {
     "DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START", "starting deletion of %1 expired-reclaimed leases",
     "DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4", "extracting extended info saw %1 leases, extended info sanity checks modified %2 / updated %3 leases and %4 leases have relay or remote id",
     "DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4_ERROR", "extracting extended info got an exception on the lease for %1: %2",
+    "DHCPSRV_MEMFILE_FAILED_TO_OPEN", "Could not open lease file: %1",
     "DHCPSRV_MEMFILE_GET4", "obtaining all IPv4 leases",
     "DHCPSRV_MEMFILE_GET6", "obtaining all IPv6 leases",
     "DHCPSRV_MEMFILE_GET6_DUID", "obtaining IPv6 leases for DUID %1",
index 6a2c7e6ed1702ac4f35da3aa1532c42cccdf51d4..bc69ddb0efcb7da86f8f1be0c24d9da8a063c22e 100644 (file)
@@ -101,6 +101,7 @@ extern const isc::log::MessageID DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6;
 extern const isc::log::MessageID DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START;
 extern const isc::log::MessageID DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4;
 extern const isc::log::MessageID DHCPSRV_MEMFILE_EXTRACT_EXTENDED_INFO4_ERROR;
+extern const isc::log::MessageID DHCPSRV_MEMFILE_FAILED_TO_OPEN;
 extern const isc::log::MessageID DHCPSRV_MEMFILE_GET4;
 extern const isc::log::MessageID DHCPSRV_MEMFILE_GET6;
 extern const isc::log::MessageID DHCPSRV_MEMFILE_GET6_DUID;
index e8f82ac1e2beaed53057520ebc715781d06b3f39..78586d5c1997e5baf11dda0ebf0135282a549618 100644 (file)
@@ -994,3 +994,11 @@ included in the message.
 % DHCPSRV_UNKNOWN_DB unknown database type: %1
 The database access string specified a database type (given in the
 message) that is unknown to the software. This is a configuration error.
+
+% DHCPSRV_MEMFILE_FAILED_TO_OPEN Could not open lease file: %1
+This error is issued when the lease file could not be opened. The
+argument contains the details. The most likely cause is that the `name`
+parameter for memfile back end contains a path other than the supported
+path. The argument will contain the details. The path component may
+simply be omitted. To override the default supported path, set the
+environment variable KEA_DHCP_DATA_DIR prior to starting the server.
index 7e9630d53d6f80d075e38f947977ea603aa08f8f..9bc45d803424ca915fd85190ea0e87ea23456c70 100644 (file)
@@ -2257,11 +2257,18 @@ Memfile_LeaseMgr::appendSuffix(const std::string& file_name,
 }
 
 std::string
-Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const {
-    std::ostringstream s;
-    s << CfgMgr::instance().getDataDir() << "/kea-leases";
-    s << (u == V4 ? "4" : "6");
-    s << ".csv";
+Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u,
+                                          std::string filename /* = "" */) const {
+    std::ostringstream s;;
+    s << CfgMgr::instance().getDataDir();
+    if (filename.empty()) {
+        s << "/kea-leases";
+        s << (u == V4 ? "4" : "6");
+        s << ".csv";
+    } else {
+        s << "/" << filename;
+    }
+
     return (s.str());
 }
 
@@ -2310,8 +2317,12 @@ Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
     try {
         lease_file = conn_.getParameter("name");
     } catch (const Exception&) {
-        lease_file = getDefaultLeaseFilePath(u);
+        // Not specified, use the defualt.
+        return (getDefaultLeaseFilePath(u));
     }
+
+    // If path is invalid this will throw.
+    lease_file = CfgMgr::instance().validatePath(lease_file);
     return (lease_file);
 }
 
@@ -3487,9 +3498,15 @@ Memfile_LeaseMgr::writeLeases6Internal(const std::string& filename) {
 
 TrackingLeaseMgrPtr
 Memfile_LeaseMgr::factory(const isc::db::DatabaseConnection::ParameterMap& parameters) {
-    LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB)
-        .arg(DatabaseConnection::redactedAccessString(parameters));
-    return (TrackingLeaseMgrPtr(new Memfile_LeaseMgr(parameters)));
+    try {
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB)
+            .arg(DatabaseConnection::redactedAccessString(parameters));
+        return (TrackingLeaseMgrPtr(new Memfile_LeaseMgr(parameters)));
+    } catch (const std::exception& ex) {
+        LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_FAILED_TO_OPEN)
+            .arg(ex.what());
+        throw ex;
+    }
 }
 
 }  // namespace dhcp
index e1a3d733ae98e462a959d7961e93a346a981fd85..ae77a5e9c1a35029c8d767acdfb17f128fea5e55 100644 (file)
@@ -992,7 +992,9 @@ public:
     /// @brief Returns default path to the lease file.
     ///
     /// @param u Universe (V4 or V6).
-    std::string getDefaultLeaseFilePath(Universe u) const;
+    /// @param filename optional filename to use.
+    std::string getDefaultLeaseFilePath(Universe u,
+                                        const std::string filename = "") const;
 
     /// @brief Returns an absolute path to the lease file.
     ///
index 1b72f665fecae5a835560935a28a3f677f59afaa..1a2ce099646c88a45ab9efba92e78a2d1f7866ad 100644 (file)
@@ -17,6 +17,7 @@
 #include <process/logging_info.h>
 #include <stats/stats_mgr.h>
 #include <util/chrono_time_utils.h>
+#include <testutils/env_var_wrapper.h>
 
 #include <gtest/gtest.h>
 
@@ -33,6 +34,7 @@ using namespace isc::dhcp::test;
 using namespace isc::util;
 using namespace isc::stats;
 using namespace isc::process;
+using namespace isc::test;
 using namespace isc;
 
 namespace {
@@ -256,7 +258,7 @@ TEST(ValueStorageTest, StringTesting) {
 
 class CfgMgrTest : public ::testing::Test {
 public:
-    CfgMgrTest() {
+    CfgMgrTest() : data_dir_env_var_("KEA_DHCP_DATA_DIR") {
         // make sure we start with a clean configuration
         clear();
     }
@@ -313,6 +315,9 @@ public:
         CfgMgr::instance().setFamily(family);
     }
 
+    /// @brief RAII wrapper for KEA_DHCP_DATA_DIR env variable.
+    EnvVarWrapper data_dir_env_var_;
+
     /// used in client classification (or just empty container for other tests)
     isc::dhcp::ClientClasses classify_;
 };
index afe22f7a70c55629f76331969ff1cc617f30c740..0faa336482d8bafb34dd08ea228e9c6948234071 100644 (file)
@@ -26,6 +26,7 @@
 #include <util/pid_file.h>
 #include <util/range_utilities.h>
 #include <util/stopwatch.h>
+#include <testutils/env_var_wrapper.h>
 
 #include <gtest/gtest.h>
 
@@ -45,6 +46,7 @@ using namespace isc::db;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 using namespace isc::util;
+using namespace isc::test;
 
 namespace {
 
@@ -105,16 +107,19 @@ public:
 /// @brief Test fixture class for @c Memfile_LeaseMgr
 class MemfileLeaseMgrTest : public GenericLeaseMgrTest {
 public:
-
     /// @brief memfile lease mgr test constructor
     ///
     /// Creates memfile and stores it in lmptr_ pointer
     MemfileLeaseMgrTest() :
-        io4_(getLeaseFilePath("leasefile4_0.csv")),
-        io6_(getLeaseFilePath("leasefile6_0.csv")),
         io_service_(getIOService()),
         timer_mgr_(TimerMgr::instance()),
-        extra_files_() {
+        extra_files_(),
+        data_dir_env_var_("KEA_DHCP_DATA_DIR") {
+
+        // Save the pre-test data dir and set it to the test directory.
+        CfgMgr::instance().clear();
+        original_datadir_ = CfgMgr::instance().getDataDir();
+        CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR);
 
         timer_mgr_->setIOService(io_service_);
         DatabaseConnection::setIOService(io_service_);
@@ -165,6 +170,9 @@ public:
         MultiThreadingMgr::instance().setMode(false);
 
         getIOService()->stopAndPoll();
+
+        // Revert to original data directory.
+        CfgMgr::instance().getDataDir(true, original_datadir_);
     }
 
     /// @brief Remove files being products of Lease File Cleanup.
@@ -202,7 +210,7 @@ public:
     /// @return Full path to the lease file.
     static std::string getLeaseFilePath(const std::string& filename) {
         std::ostringstream s;
-        s << TEST_DATA_BUILDDIR << "/" << filename;
+        s << CfgMgr::instance().getDataDir() << "/" << filename;
         return (s.str());
     }
 
@@ -386,12 +394,6 @@ public:
         return (lease);
     }
 
-    /// @brief Object providing access to v4 lease IO.
-    LeaseFileIO io4_;
-
-    /// @brief Object providing access to v6 lease IO.
-    LeaseFileIO io6_;
-
     /// @brief Pointer to the IO service used by the tests.
     IOServicePtr io_service_;
 
@@ -400,6 +402,12 @@ public:
 
     /// @brief List of names of other files to removed.
     vector<string> extra_files_;
+
+    /// @brief RAII wrapper for KEA_DHCP_DATA_DIR env variable.
+    EnvVarWrapper data_dir_env_var_;
+
+    /// @brief Stores the pre-test DHCP data directory.
+    std::string original_datadir_;
 };
 
 /// @brief This test checks if the LeaseMgr can be instantiated and that it
@@ -446,6 +454,68 @@ TEST_F(MemfileLeaseMgrTest, constructor) {
     EXPECT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
 }
 
+/// @brief Verifies that the supported path is the enforced.
+TEST_F(MemfileLeaseMgrTest, defaultDataDir) {
+    ASSERT_TRUE(data_dir_env_var_.getValue().empty());
+    DatabaseConnection::ParameterMap pmap;
+    pmap["universe"] = "6";
+    pmap["persist"] = "true";
+    pmap["lfc-interval"] = "0";
+    pmap["name"] = "leasefile6_1.csv";
+    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr;
+
+    ASSERT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
+    EXPECT_EQ(lease_mgr->getLeaseFilePath(Memfile_LeaseMgr::V6),
+              CfgMgr::instance().validatePath("leasefile6_1.csv"));
+
+    pmap["name"] = "/tmp/leasefile6_1.csv";
+    std::ostringstream os;
+    os << "invalid path specified: '/tmp', supported path is '"
+       << CfgMgr::instance().getDataDir() << "'";
+
+    ASSERT_THROW_MSG(lease_mgr.reset(new Memfile_LeaseMgr(pmap)),
+                     BadValue, os.str());
+}
+
+/// @brief Verifies that the supported path may be overriden witn
+/// the environment variable KEA_DHCP_DATA_DIR.
+TEST_F(MemfileLeaseMgrTest, dataDirEnvVarOverride) {
+    ASSERT_TRUE(data_dir_env_var_.getValue().empty());
+    data_dir_env_var_.setValue("/tmp");
+    auto valid_path = CfgMgr::instance().getDataDir(true);
+    ASSERT_EQ(valid_path, "/tmp");
+
+    DatabaseConnection::ParameterMap pmap;
+    pmap["universe"] = "6";
+    pmap["persist"] = "true";
+    pmap["lfc-interval"] = "0";
+    pmap["name"] = "leasefile6_1.csv";
+    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr;
+
+    ASSERT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
+    EXPECT_EQ(lease_mgr->getLeaseFilePath(Memfile_LeaseMgr::V6),
+              "/tmp/leasefile6_1.csv");
+}
+
+/// @brief Verifies that the supported path may be overriden with
+/// an explicit path even though its really only for UT testing.
+TEST_F(MemfileLeaseMgrTest, dataDirExplicitOveride) {
+    ASSERT_TRUE(data_dir_env_var_.getValue().empty());
+    auto valid_path = CfgMgr::instance().getDataDir(true, "/tmp");
+    ASSERT_EQ(valid_path, "/tmp");
+
+    DatabaseConnection::ParameterMap pmap;
+    pmap["universe"] = "6";
+    pmap["persist"] = "true";
+    pmap["lfc-interval"] = "0";
+    pmap["name"] = "leasefile6_1.csv";
+    boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr;
+
+    ASSERT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
+    EXPECT_EQ(lease_mgr->getLeaseFilePath(Memfile_LeaseMgr::V6),
+              "/tmp/leasefile6_1.csv");
+}
+
 /// @brief Checks if there is no lease manager NoLeaseManager is thrown.
 TEST_F(MemfileLeaseMgrTest, noLeaseManager) {
     LeaseMgrFactory::destroy();
index 34942b5158ed746b3497a5ca0e519f0dbdb00238..266fa86a8c4bf256fd1f2db5562cd6522247e484 100644 (file)
@@ -16,6 +16,7 @@ libkea_testutils_la_SOURCES += unix_control_client.cc unix_control_client.h
 libkea_testutils_la_SOURCES += user_context_utils.cc user_context_utils.h
 libkea_testutils_la_SOURCES += gtest_utils.h
 libkea_testutils_la_SOURCES += multi_threading_utils.h
+libkea_testutils_la_SOURCES += env_var_wrapper.cc env_var_wrapper.h
 libkea_testutils_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 libkea_testutils_la_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
diff --git a/src/lib/testutils/env_var_wrapper.cc b/src/lib/testutils/env_var_wrapper.cc
new file mode 100644 (file)
index 0000000..4b3b3dd
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (C) 2025 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 <config.h>
+
+#include <testutils/env_var_wrapper.h>
+
+namespace isc {
+namespace test {
+
+EnvVarWrapper::EnvVarWrapper(const std::string& name) : name_(name) {
+    original_value_ = getValue();
+}
+
+EnvVarWrapper::~EnvVarWrapper() {
+    setValue(original_value_);
+}
+
+std::string
+EnvVarWrapper::getOriginalValue() const {
+    return (original_value_);
+}
+
+std::string
+EnvVarWrapper::getValue() const {
+    auto value = getenv(name_.c_str());
+    return (value ? std::string(value) : std::string(""));
+}
+
+void
+EnvVarWrapper::setValue(const std::string value /* = "" */) {
+    if (value.empty()) {
+        unsetenv(name_.c_str());
+    } else {
+        setenv(name_.c_str(), value.c_str(), 1);
+    }
+}
+
+}  // end of isc::test namespace
+}  // end of isc namespace
diff --git a/src/lib/testutils/env_var_wrapper.h b/src/lib/testutils/env_var_wrapper.h
new file mode 100644 (file)
index 0000000..0f43af3
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright (C) 2025 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 ENV_VAR_WRAPPER_H
+#define ENV_VAR_WRAPPER_H
+
+#include <string>
+
+namespace isc {
+namespace test {
+
+/// @brief Wrapper for environment varible that restores
+class EnvVarWrapper {
+public:
+    /// @brief Constructor
+    ///
+    /// Fetchs and retains the variable's current value as
+    /// the "original" value.
+    EnvVarWrapper(const std::string& name);
+
+    /// @brief Destructor.
+    ///
+    /// Restores the original value (if one), otherwise it
+    /// unsets it.
+    ~EnvVarWrapper();
+
+    /// @brief Fetches the original value of the env variable.
+    std::string getOriginalValue() const;
+
+    /// @brief Fetches the current value of the env variable.
+    std::string getValue() const;
+
+    /// @brief Sets the current value of the env variable.
+    ///
+    /// @param value new value of the variable. If empty the
+    /// variable is unset.
+    void setValue(const std::string value = "" );
+
+private:
+    /// @brief Name of the variable.
+    std::string name_;
+
+    /// @brief Value of the env variable at the time the wrapper
+    /// was constructed. 
+    std::string original_value_;
+};
+
+}  // end of isc::test namespace
+}  // end of isc namespace
+
+#endif // ENV_VAR_WRAPPER_H