Update hwmon fan target sysfs entries

Override the default FanSpeed.Target set implementation so when a target
value is written to the FanSpeed interface it is also updated in the
related fan target sysfs file. This sets a particular fan to the given
target speed.

Resolves openbmc/openbmc#962
Resolves openbmc/phosphor-hwmon#1

Change-Id: I867811737269b3f42d2a0dc15b37782a74f147b8
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/sysfs.cpp b/sysfs.cpp
index 75a1376..8b67f3e 100644
--- a/sysfs.cpp
+++ b/sysfs.cpp
@@ -16,10 +16,14 @@
 #include <cstdlib>
 #include <experimental/filesystem>
 #include <memory>
-#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Control/Device/error.hpp>
 #include "sysfs.hpp"
 #include "util.hpp"
 
+using namespace phosphor::logging;
+
 std::string findHwmon(const std::string& ofNode)
 {
     namespace fs = std::experimental::filesystem;
@@ -89,4 +93,54 @@
     return value;
 }
 
+uint64_t writeSysfsWithCallout(const uint64_t& value,
+                               const std::string& root,
+                               const std::string& instance,
+                               const std::string& type,
+                               const std::string& id,
+                               const std::string& sensor)
+{
+    namespace fs = std::experimental::filesystem;
+
+    std::string valueStr = std::to_string(value);
+    std::ofstream ofs;
+    fs::path instancePath{root};
+    instancePath /= instance;
+    std::string fullPath = make_sysfs_path(instancePath,
+                                           type, id, sensor);
+
+    ofs.exceptions(std::ofstream::failbit
+                   | std::ofstream::badbit
+                   | std::ofstream::eofbit);
+    try
+    {
+        ofs.open(fullPath);
+        ofs << valueStr;
+    }
+    catch (const std::exception& e)
+    {
+        // errno should still reflect the error from the failing open
+        // or write system calls that got us here.
+        auto rc = errno;
+        instancePath /= "device";
+        using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
+        try
+        {
+            elog<WriteFailure>(
+                xyz::openbmc_project::Control::Device::
+                    WriteFailure::CALLOUT_ERRNO(rc),
+                xyz::openbmc_project::Control::Device::
+                    WriteFailure::CALLOUT_DEVICE_PATH(
+                        fs::canonical(instancePath).c_str()));
+        }
+        catch (WriteFailure& elog)
+        {
+            commit(elog.name());
+        }
+        exit(EXIT_FAILURE);
+    }
+
+    return value;
+}
+
 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4