Add support to read binary PMBus data

PMBus::readBinary() can read a sysfs file that
contains binary data and returns a vector of bytes
representing that data.

Change-Id: I3c729f502e155b007b9ab22a4eeefec437f150e1
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/pmbus.cpp b/pmbus.cpp
index f000c75..76ab39c 100644
--- a/pmbus.cpp
+++ b/pmbus.cpp
@@ -31,6 +31,17 @@
 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
 namespace fs = std::experimental::filesystem;
 
+/**
+ * @brief Helper to close a file handle
+ */
+struct FileCloser
+{
+    void operator()(FILE* fp) const
+    {
+        fclose(fp);
+    }
+};
+
 std::string PMBus::insertPageNum(const std::string& templateName,
                                  size_t page)
 {
@@ -224,6 +235,52 @@
     return data;
 }
 
+std::vector<uint8_t> PMBus::readBinary(const std::string& name,
+                                       Type type,
+                                       size_t length)
+{
+    auto path = getPath(type) / name;
+
+    //Use C style IO because it's easier to handle telling the difference
+    //between hitting EOF or getting an actual error.
+    std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")};
+
+    if (file)
+    {
+        std::vector<uint8_t> data(length, 0);
+
+        auto bytes = fread(data.data(),
+                           sizeof(decltype(data[0])),
+                           length,
+                           file.get());
+
+        if (bytes != length)
+        {
+            //If hit EOF, just return the amount of data that was read.
+            if (feof(file.get()))
+            {
+               data.erase(data.begin() + bytes, data.end());
+            }
+            else if (ferror(file.get()))
+            {
+                auto rc = errno;
+                log<level::ERR>("Failed to read sysfs file",
+                                entry("FILENAME=%s", path.c_str()));
+
+                using metadata = xyz::openbmc_project::Common::
+                        Device::ReadFailure;
+
+                elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc),
+                                  metadata::CALLOUT_DEVICE_PATH(
+                                          fs::canonical(basePath).c_str()));
+            }
+        }
+        return data;
+    }
+
+    return std::vector<uint8_t>{};
+}
+
 void PMBus::write(const std::string& name, int value, Type type)
 {
     std::ofstream file;
diff --git a/pmbus.hpp b/pmbus.hpp
index cad77bf..55e3ff6 100644
--- a/pmbus.hpp
+++ b/pmbus.hpp
@@ -209,6 +209,19 @@
         std::string readString(const std::string& name, Type type);
 
         /**
+         * Read data from a binary file in sysfs.
+         *
+         * @param[in] name   - path concatenated to basePath to read
+         * @param[in] type   - Path type
+         * @param[in] length - length of data to read, in bytes
+         *
+         * @return vector<uint8_t> - The data read from the file.
+         */
+        std::vector<uint8_t> readBinary(const std::string& name,
+                                        Type type,
+                                        size_t length);
+
+        /**
          * Writes an integer value to the file, therefore doing
          * a PMBus write.
          *