util: Combine systemd value parsers

We want to use the same function for parsing out the last property of
a systemd file. This combines all of the lookups into a single function.

Change-Id: I590d31e5e1019cd38a78d640188e486b8854a544
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/dhcp_configuration.cpp b/src/dhcp_configuration.cpp
index 6684713..53d4e91 100644
--- a/src/dhcp_configuration.cpp
+++ b/src/dhcp_configuration.cpp
@@ -1,11 +1,8 @@
-#include "config.h"
-
 #include "dhcp_configuration.hpp"
 
+#include "config_parser.hpp"
 #include "network_manager.hpp"
 
-#include <fmt/format.h>
-
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
@@ -20,6 +17,29 @@
 using namespace phosphor::network;
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Configuration::Configuration(sdbusplus::bus_t& bus, const std::string& objPath,
+                             Manager& parent) :
+    Iface(bus, objPath.c_str(), Iface::action::defer_emit),
+    bus(bus), manager(parent)
+{
+    config::Parser conf;
+    {
+        auto interfaceStrList = getInterfaces();
+        if (!interfaceStrList.empty())
+        {
+            conf.setFile(config::pathForIntfConf(manager.getConfDir(),
+                                                 *interfaceStrList.begin()));
+        }
+    }
+
+    ConfigIntf::dnsEnabled(getDHCPProp(conf, "UseDNS"));
+    ConfigIntf::ntpEnabled(getDHCPProp(conf, "UseNTP"));
+    ConfigIntf::hostNameEnabled(getDHCPProp(conf, "UseHostname"));
+    ConfigIntf::sendHostNameEnabled(getDHCPProp(conf, "SendHostname"));
+    emit_object_added();
+}
+
 bool Configuration::sendHostNameEnabled(bool value)
 {
     if (value == sendHostNameEnabled())
@@ -77,30 +97,6 @@
     return dns;
 }
 
-bool Configuration::getDHCPPropFromConf(const std::string& prop)
-{
-    auto interfaceStrList = getInterfaces();
-    // systemd default behaviour is all DHCP fields should be enabled by
-    // default.
-    config::Parser parser(config::pathForIntfConf(manager.getConfDir(),
-                                                  *interfaceStrList.begin()));
-
-    auto value = parser.map.getLastValueString("DHCP", prop);
-    if (value == nullptr)
-    {
-        auto msg = fmt::format("Missing config section DHCP[{}]", prop);
-        log<level::NOTICE>(msg.c_str(), entry("PROP=%s", prop.c_str()));
-        return true;
-    }
-    auto ret = config::parseBool(*value);
-    if (!ret.has_value())
-    {
-        auto msg =
-            fmt::format("Failed to parse section DHCP[{}]: `{}`", prop, *value);
-        log<level::NOTICE>(msg.c_str(), entry("PROP=%s", prop.c_str()));
-    }
-    return ret.value_or(true);
-}
 } // namespace dhcp
 } // namespace network
 } // namespace phosphor
diff --git a/src/dhcp_configuration.hpp b/src/dhcp_configuration.hpp
index f35701b..8174ec7 100644
--- a/src/dhcp_configuration.hpp
+++ b/src/dhcp_configuration.hpp
@@ -43,16 +43,7 @@
      *  @param[in] parent - Parent object.
      */
     Configuration(sdbusplus::bus_t& bus, const std::string& objPath,
-                  Manager& parent) :
-        Iface(bus, objPath.c_str(), Iface::action::defer_emit),
-        bus(bus), manager(parent)
-    {
-        ConfigIntf::dnsEnabled(getDHCPPropFromConf("UseDNS"));
-        ConfigIntf::ntpEnabled(getDHCPPropFromConf("UseNTP"));
-        ConfigIntf::hostNameEnabled(getDHCPPropFromConf("UseHostname"));
-        ConfigIntf::sendHostNameEnabled(getDHCPPropFromConf("SendHostname"));
-        emit_object_added();
-    }
+                  Manager& parent);
 
     /** @brief If true then DNS servers received from the DHCP server
      *         will be used and take precedence over any statically
@@ -84,11 +75,6 @@
      */
     bool sendHostNameEnabled(bool value) override;
 
-    /** @brief read the DHCP Prop value from the configuration file
-     *  @param[in] prop - DHCP Prop name.
-     */
-    bool getDHCPPropFromConf(const std::string& prop);
-
     /* @brief Network Manager needed the below function to know the
      *        value of the properties (ntpEnabled,dnsEnabled,hostnameEnabled
               sendHostNameEnabled).
diff --git a/src/util.cpp b/src/util.cpp
index 54028af..c5543f0 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -366,6 +366,48 @@
     return "eth" + std::to_string(idx) + "addr";
 }
 
+static std::optional<DHCPVal> systemdParseDHCP(std::string_view str)
+{
+    if (config::icaseeq(str, "ipv4"))
+    {
+        return DHCPVal{.v4 = true, .v6 = false};
+    }
+    if (config::icaseeq(str, "ipv6"))
+    {
+        return DHCPVal{.v4 = false, .v6 = true};
+    }
+    if (auto b = config::parseBool(str); b)
+    {
+        return DHCPVal{.v4 = *b, .v6 = *b};
+    }
+    return std::nullopt;
+}
+
+inline auto systemdParseLast(const config::Parser& config,
+                             std::string_view section, std::string_view key,
+                             auto&& fun)
+{
+    if (auto str = config.map.getLastValueString(section, key); str == nullptr)
+    {
+        auto err = fmt::format("Unable to get the value of {}[{}] from {}",
+                               section, key, config.getFilename().native());
+        log<level::NOTICE>(err.c_str(),
+                           entry("FILE=%s", config.getFilename().c_str()));
+    }
+    else if (auto val = fun(*str); !val)
+    {
+        auto err = fmt::format("Invalid value of {}[{}] from {}: {}", section,
+                               key, config.getFilename().native(), *str);
+        log<level::NOTICE>(err.c_str(), entry("VALUE=%s", str->c_str()),
+                           entry("FILE=%s", config.getFilename().c_str()));
+    }
+    else
+    {
+        return val;
+    }
+    return decltype(fun(std::string_view{}))(std::nullopt);
+}
+
 bool getIPv6AcceptRA(const config::Parser& config)
 {
 #ifdef ENABLE_IPV6_ACCEPT_RA
@@ -373,64 +415,21 @@
 #else
     constexpr bool def = false;
 #endif
-
-    auto value = config.map.getLastValueString("Network", "IPv6AcceptRA");
-    if (value == nullptr)
-    {
-        auto msg = fmt::format(
-            "Unable to get the value for Network[IPv6AcceptRA] from {}",
-            config.getFilename().native());
-        log<level::NOTICE>(msg.c_str(),
-                           entry("FILE=%s", config.getFilename().c_str()));
-        return def;
-    }
-    auto ret = config::parseBool(*value);
-    if (!ret.has_value())
-    {
-        auto msg = fmt::format(
-            "Failed to parse section Network[IPv6AcceptRA] from {}: `{}`",
-            config.getFilename().native(), *value);
-        log<level::NOTICE>(msg.c_str(),
-                           entry("FILE=%s", config.getFilename().c_str()),
-                           entry("VALUE=%s", value->c_str()));
-    }
-    return ret.value_or(def);
+    return systemdParseLast(config, "Network", "IPv6AcceptRA",
+                            config::parseBool)
+        .value_or(def);
 }
 
 DHCPVal getDHCPValue(const config::Parser& config)
 {
-    constexpr auto def = DHCPVal{.v4 = true, .v6 = true};
+    return systemdParseLast(config, "Network", "DHCP", systemdParseDHCP)
+        .value_or(DHCPVal{.v4 = true, .v6 = true});
+}
 
-    const auto value = config.map.getLastValueString("Network", "DHCP");
-    if (value == nullptr)
-    {
-        auto msg =
-            fmt::format("Unable to get the value for Network[DHCP] from {}",
-                        config.getFilename().native());
-        log<level::NOTICE>(msg.c_str(),
-                           entry("FILE=%s", config.getFilename().c_str()));
-        return def;
-    }
-    if (config::icaseeq(*value, "ipv4"))
-    {
-        return DHCPVal{.v4 = true, .v6 = false};
-    }
-    if (config::icaseeq(*value, "ipv6"))
-    {
-        return DHCPVal{.v4 = false, .v6 = true};
-    }
-    auto ret = config::parseBool(*value);
-    if (!ret.has_value())
-    {
-        auto str = fmt::format("Unable to parse Network[DHCP] from {}: `{}`",
-                               config.getFilename().native(), *value);
-        log<level::NOTICE>(str.c_str(),
-                           entry("FILE=%s", config.getFilename().c_str()),
-                           entry("VALUE=%s", value->c_str()));
-        return def;
-    }
-    return *ret ? DHCPVal{.v4 = true, .v6 = true}
-                : DHCPVal{.v4 = false, .v6 = false};
+bool getDHCPProp(const config::Parser& config, std::string_view key)
+{
+    return systemdParseLast(config, "DHCP", key, config::parseBool)
+        .value_or(true);
 }
 
 namespace mac_address
diff --git a/src/util.hpp b/src/util.hpp
index 055c684..cc7ab03 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -138,6 +138,12 @@
 };
 DHCPVal getDHCPValue(const config::Parser& config);
 
+/** @brief Read a boolean DHCP property from a conf file
+ *  @param[in] config - The parsed configuration.
+ *  @param[in] key - The property name.
+ */
+bool getDHCPProp(const config::Parser& config, std::string_view key);
+
 namespace internal
 {