Support reading VERSION_ID without quotes

The VERSION_ID value in /etc/os-release recently had its surrounding
quotation marks removed.  Update the code to be able to read values out
of /etc/os-release either with or without quotes, and make it a utility
function so that it can be used in multiple places.

Props to the phosphor-bmc-code-mgmt repo for the elegant implementation.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I348625ba49214ab2977d9e70a4337299bef5ed3a

Change-Id: I9d201954a8116dfda32d096a347e150d93fbfb46
diff --git a/Makefile.am b/Makefile.am
index 2c6aedc..0ef7221 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,7 +57,8 @@
 	elog_meta.cpp \
 	elog_serialize.cpp \
 	sdjournal.cpp \
-	extensions.cpp
+	extensions.cpp \
+	util.cpp
 
 # Be sure to build needed files before compiling
 BUILT_SOURCES = \
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index db80757..9db6df8 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -17,6 +17,8 @@
 
 #include "data_interface.hpp"
 
+#include "util.hpp"
+
 #include <fstream>
 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
 
@@ -225,35 +227,10 @@
     return std::string{};
 }
 
-/**
- * @brief Return a value found in the /etc/os-release file
- *
- * @param[in] key - The key name, like "VERSION"
- *
- * @return std::optional<std::string> - The value
- */
-std::optional<std::string> getOSReleaseValue(const std::string& key)
-{
-    std::ifstream versionFile{BMC_VERSION_FILE};
-    std::string line;
-    std::string keyPattern{key + '='};
-
-    while (std::getline(versionFile, line))
-    {
-        if (line.find(keyPattern) != std::string::npos)
-        {
-            auto pos = line.find_first_of('"') + 1;
-            auto value = line.substr(pos, line.find_last_of('"') - pos);
-            return value;
-        }
-    }
-
-    return std::nullopt;
-}
-
 void DataInterface::readBMCFWVersion()
 {
-    _bmcFWVersion = getOSReleaseValue("VERSION").value_or("");
+    _bmcFWVersion =
+        phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
 }
 
 void DataInterface::readServerFWVersion()
@@ -263,7 +240,8 @@
 
 void DataInterface::readBMCFWVersionID()
 {
-    _bmcFWVersionID = getOSReleaseValue("VERSION_ID").value_or("");
+    _bmcFWVersionID =
+        phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
 }
 
 void DataInterface::readMotherboardCCIN()
diff --git a/log_manager.cpp b/log_manager.cpp
index cbfe434..bcc94a5 100644
--- a/log_manager.cpp
+++ b/log_manager.cpp
@@ -6,6 +6,7 @@
 #include "elog_meta.hpp"
 #include "elog_serialize.hpp"
 #include "extensions.hpp"
+#include "util.hpp"
 
 #include <poll.h>
 #include <sys/inotify.h>
@@ -739,27 +740,14 @@
 
 std::string Manager::readFWVersion()
 {
-    std::string version;
-    std::ifstream versionFile{BMC_VERSION_FILE};
-    std::string line;
-    static constexpr auto VERSION_ID = "VERSION_ID=";
+    auto version = util::getOSReleaseValue("VERSION_ID");
 
-    while (std::getline(versionFile, line))
-    {
-        if (line.find(VERSION_ID) != std::string::npos)
-        {
-            auto pos = line.find_first_of('"') + 1;
-            version = line.substr(pos, line.find_last_of('"') - pos);
-            break;
-        }
-    }
-
-    if (version.empty())
+    if (!version)
     {
         log<level::ERR>("Unable to read BMC firmware version");
     }
 
-    return version;
+    return version.value_or("");
 }
 
 void Manager::create(const std::string& message, Entry::Level severity,
diff --git a/test/Makefile.am b/test/Makefile.am
index ab1378a..b14c0ae 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -54,6 +54,7 @@
 	$(top_builddir)/elog-process-metadata.o \
 	$(top_builddir)/sdjournal.o \
 	$(top_builddir)/extensions.o \
+	$(top_builddir)/util.o \
 	$(CODE_COVERAGE_LIBS)
 
 remote_logging_test_ldadd = \
@@ -120,6 +121,7 @@
 	$(top_builddir)/elog-process-metadata.o \
 	$(top_builddir)/elog_serialize.o \
 	$(top_builddir)/log_manager.o \
+	$(top_builddir)/util.o \
 	$(top_builddir)/xyz/openbmc_project/Logging/Internal/Manager/server.o
 extensions_test_LDFLAGS = $(test_ldflags)
 
diff --git a/util.cpp b/util.cpp
new file mode 100644
index 0000000..a89e754
--- /dev/null
+++ b/util.cpp
@@ -0,0 +1,46 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+
+#include "util.hpp"
+
+namespace phosphor::logging::util
+{
+
+std::optional<std::string> getOSReleaseValue(const std::string& key)
+{
+    std::ifstream versionFile{BMC_VERSION_FILE};
+    std::string line;
+    std::string keyPattern{key + '='};
+
+    while (std::getline(versionFile, line))
+    {
+        if (line.substr(0, keyPattern.size()).find(keyPattern) !=
+            std::string::npos)
+        {
+            // If the value isn't surrounded by quotes, then pos will be
+            // npos + 1 = 0, and the 2nd arg to substr() will be npos
+            // which means get the rest of the string.
+            auto value = line.substr(keyPattern.size());
+            std::size_t pos = value.find_first_of('"') + 1;
+            return value.substr(pos, value.find_last_of('"') - pos);
+        }
+    }
+
+    return std::nullopt;
+}
+
+} // namespace phosphor::logging::util
diff --git a/util.hpp b/util.hpp
new file mode 100644
index 0000000..c348916
--- /dev/null
+++ b/util.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <fstream>
+#include <optional>
+#include <string>
+
+namespace phosphor::logging::util
+{
+
+/**
+ * @brief Return a value found in the /etc/os-release file
+ *
+ * @param[in] key - The key name, like "VERSION"
+ *
+ * @return std::optional<std::string> - The value
+ */
+std::optional<std::string> getOSReleaseValue(const std::string& key);
+
+} // namespace phosphor::logging::util