Get ignored interfaces from environment

Add several functions to get a comma separated string from environment,
and parse the string into a set.

Tested: Verify the unit tests passes.

Signed-off-by: Lei YU <yulei.sh@bytedance.com>
Change-Id: I0dd7270d9882cf6fa3ce73d8eac516ae42c61fc0
diff --git a/test/test_util.cpp b/test/test_util.cpp
index 9b61c64..a63e205 100644
--- a/test/test_util.cpp
+++ b/test/test_util.cpp
@@ -10,6 +10,7 @@
 #include <string_view>
 #include <xyz/openbmc_project/Common/error.hpp>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 namespace phosphor
@@ -329,6 +330,27 @@
     EXPECT_FALSE(isUnicast(fromString("ff:ff:ff:ff:ff:ff")));
 }
 
+TEST(IgnoredInterfaces, Empty)
+{
+    auto ret = internal::parseInterfaces({});
+    EXPECT_TRUE(ret.empty());
+
+    ret = internal::parseInterfaces(" ,  ,, ");
+    EXPECT_TRUE(ret.empty());
+}
+
+TEST(IgnoredInterfaces, NotEmpty)
+{
+    using ::testing::ContainerEq;
+    std::set<std::string> expected = {"eth0"};
+    auto ret = internal::parseInterfaces("eth0");
+    EXPECT_THAT(ret, ContainerEq(expected));
+
+    expected = {"eth0", "eth1", "bond1", "usb0"};
+    ret = internal::parseInterfaces(" ,eth0, eth1  ,bond1, usb0,,");
+    EXPECT_THAT(ret, ContainerEq(expected));
+}
+
 } // namespace mac_address
 } // namespace network
 } // namespace phosphor
diff --git a/util.cpp b/util.cpp
index 67a9345..ee76cc3 100644
--- a/util.cpp
+++ b/util.cpp
@@ -9,6 +9,7 @@
 #include <sys/wait.h>
 
 #include <algorithm>
+#include <cctype>
 #include <cstdlib>
 #include <cstring>
 #include <filesystem>
@@ -17,6 +18,7 @@
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
+#include <sstream>
 #include <stdexcept>
 #include <stdplus/raw.hpp>
 #include <string>
@@ -91,6 +93,49 @@
     }
 }
 
+/** @brief Get ignored interfaces from environment */
+std::string getIgnoredInterfacesEnv()
+{
+    auto r = std::getenv("IGNORED_INTERFACES");
+    if (r == nullptr)
+    {
+        return {};
+    }
+    return r;
+}
+
+/** @brief Parse the comma separated interface names */
+std::set<std::string> parseInterfaces(const std::string& interfaces)
+{
+    std::set<std::string> result;
+    std::stringstream ss(interfaces);
+    while (ss.good())
+    {
+        std::string str;
+        std::getline(ss, str, ',');
+        // Trim str
+        if (!str.empty())
+        {
+            str.erase(
+                std::remove_if(str.begin(), str.end(),
+                               [](unsigned char c) { return std::isspace(c); }),
+                str.end());
+        }
+        if (!str.empty())
+        {
+            result.insert(str);
+        }
+    }
+    return result;
+}
+
+/** @brief Get the ignored interfaces */
+const std::set<std::string>& getIgnoredInterfaces()
+{
+    static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv());
+    return ignoredInterfaces;
+}
+
 } // namespace internal
 
 uint8_t toCidr(int addressFamily, const std::string& subnetMask)
diff --git a/util.hpp b/util.hpp
index f9eb0e2..9c180ee 100644
--- a/util.hpp
+++ b/util.hpp
@@ -168,6 +168,15 @@
  */
 void executeCommandinChildProcess(const char* path, char** args);
 
+/** @brief Get ignored interfaces from environment */
+std::string getIgnoredInterfacesEnv();
+
+/** @brief Parse the comma separated interface names */
+std::set<std::string> parseInterfaces(const std::string& interfaces);
+
+/** @brief Get the ignored interfaces */
+const std::set<std::string>& getIgnoredInterfaces();
+
 } // namespace internal
 
 /* @brief runs the given command in child process.