add handler logic to handle SysCpldVersion

Add handler logic to handler for SysCpldVersion such that it splits the
true IPMI processing from the business logic.

Tested: Only ran unit-tests (added new ones).
Change-Id: I09d95d8be8fbe75648b3332af898336b00074c2f
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/handler.cpp b/handler.cpp
index 8d06f36..ac2ca3a 100644
--- a/handler.cpp
+++ b/handler.cpp
@@ -20,6 +20,7 @@
 
 #include <ipmid/api.h>
 
+#include <cinttypes>
 #include <cstdio>
 #include <filesystem>
 #include <fstream>
@@ -94,6 +95,51 @@
     return count;
 }
 
+VersionTuple Handler::getCpldVersion(unsigned int id) const
+{
+    std::ostringstream opath;
+    opath << "/run/cpld" << id << ".version";
+    // Check for file
+
+    std::error_code ec;
+    if (!fs::exists(opath.str(), ec))
+    {
+        std::fprintf(stderr, "Path: '%s' doesn't exist.\n",
+                     opath.str().c_str());
+        throw IpmiException(IPMI_CC_INVALID_FIELD_REQUEST);
+    }
+    // We're uninterested in the state of ec.
+
+    // If file exists, read.
+    std::ifstream ifs;
+    ifs.exceptions(std::ifstream::failbit);
+    std::string value;
+    try
+    {
+        ifs.open(opath.str());
+        ifs >> value;
+    }
+    catch (std::ios_base::failure& fail)
+    {
+        throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+    }
+
+    // If value parses as expected, return version.
+    VersionTuple version = std::make_tuple(0, 0, 0, 0);
+
+    int num_fields =
+        std::sscanf(value.c_str(), "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8,
+                    &std::get<0>(version), &std::get<1>(version),
+                    &std::get<2>(version), &std::get<3>(version));
+    if (num_fields == 0)
+    {
+        std::fprintf(stderr, "Invalid version.\n");
+        throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+    }
+
+    return version;
+}
+
 Handler handlerImpl;
 
 } // namespace ipmi