Add support to watch for OCC errors

Change-Id: I98d95020a2d01e281e5c8efa825d6b4bd4c6c160
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/occ_errors.cpp b/occ_errors.cpp
new file mode 100644
index 0000000..f2da49f
--- /dev/null
+++ b/occ_errors.cpp
@@ -0,0 +1,145 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <org/open_power/OCC/Device/error.hpp>
+#include "occ_errors.hpp"
+#include "elog-errors.hpp"
+namespace open_power
+{
+namespace occ
+{
+
+// Value in error file indicating success
+constexpr auto NO_ERROR = '0';
+
+using namespace phosphor::logging;
+using namespace sdbusplus::org::open_power::OCC::Device::Error;
+using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
+                                Error::InternalFailure;
+
+// Populate the file descriptor on the error file
+void Error::openFile()
+{
+    using namespace phosphor::logging;
+
+    fd = open(file.c_str(), O_RDONLY | O_NONBLOCK);
+    if (fd < 0)
+    {
+        elog<OpenFailure>(
+            phosphor::logging::org::open_power::OCC::Device::
+                OpenFailure::CALLOUT_ERRNO(errno),
+            phosphor::logging::org::open_power::OCC::Device::
+                OpenFailure::CALLOUT_DEVICE_PATH(file.c_str()));
+    }
+}
+
+// Attaches the FD to event loop and registers the callback handler
+void Error::registerCallBack()
+{
+    decltype(eventSource.get()) sourcePtr = nullptr;
+    auto r = sd_event_add_io(event.get(), &sourcePtr, fd,
+                             EPOLLIN, processEvents, this);
+    eventSource.reset(sourcePtr);
+
+    if (r < 0)
+    {
+        log<level::ERR>("Failed to register callback handler",
+                entry("ERROR=%s", strerror(-r)));
+        elog<InternalFailure>();
+    }
+}
+
+// Starts to watch for errors
+void Error::addWatch()
+{
+    // Open the file
+    openFile();
+
+    // register the callback handler
+    registerCallBack();
+}
+
+// Stops watching for errors
+void Error::removeWatch()
+{
+    // Close the file
+    if (fd >= 0)
+    {
+        close(fd);
+    }
+
+    // Reduce the reference count. Since there is only one instances
+    // of add_io, this will result empty loop
+    eventSource.reset();
+}
+
+// Callback handler when there is an activity on the FD
+int Error::processEvents(sd_event_source* es, int fd,
+                         uint32_t revents, void* userData)
+{
+    log<level::INFO>("Error file updated");
+    auto error = static_cast<Error*>(userData);
+
+    error->analyzeEvent();
+    return 0;
+}
+
+// Reads the error file and analyzes the data
+void Error::analyzeEvent()
+{
+    // Get the number of bytes to read
+    int len = -1;
+    auto r = ioctl(fd, FIONREAD, &len);
+    if (r < 0)
+    {
+        elog<ConfigFailure>(
+            phosphor::logging::org::open_power::OCC::Device::
+                ConfigFailure::CALLOUT_ERRNO(errno),
+            phosphor::logging::org::open_power::OCC::Device::
+                ConfigFailure::CALLOUT_DEVICE_PATH(file.c_str()));
+    }
+
+    // A non-zero data indicates an error condition
+    // Let the caller take appropriate action on this
+    auto data = readFile(len);
+    if (data.empty() ||
+            data.front() == NO_ERROR)
+    {
+        return;
+    }
+
+    // This must be an error
+    if (callBack)
+    {
+        callBack();
+    }
+    return;
+}
+
+// Reads so many bytes as passed in
+std::string Error::readFile(int len) const
+{
+    auto data = std::make_unique<char[]>(len+1);
+
+    // This file get created soon after binding. A value of 0 is
+    // deemed success and anything else is a Failure
+    // Since all the sysfs files would have size of 4096, if we read 0
+    // bytes -or- value '0', then it just means we are fine
+    auto r = read(fd, data.get(), len);
+    if (r < 0)
+    {
+        elog<ReadFailure>(
+            phosphor::logging::org::open_power::OCC::Device::
+                ReadFailure::CALLOUT_ERRNO(errno),
+            phosphor::logging::org::open_power::OCC::Device::
+                ReadFailure::CALLOUT_DEVICE_PATH(file.c_str()));
+    }
+    return std::string(data.get());
+}
+
+} // namespace occ
+} // namespace open_power