fw-update: Implement firmware UpdateManager

The UpdateManager parses the PLDM package and co-ordinates with
the DeviceUpdater to update all the PLDM enabled firmware devices.

Tested: Completed firmware update using PLDM for an FD

Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: Ia87675e0a88cb1f72ad82934e539739db193b9f6
diff --git a/fw-update/watch.cpp b/fw-update/watch.cpp
new file mode 100644
index 0000000..208d818
--- /dev/null
+++ b/fw-update/watch.cpp
@@ -0,0 +1,111 @@
+#include "watch.hpp"
+
+#include <sys/inotify.h>
+#include <unistd.h>
+
+#include <cstddef>
+#include <cstring>
+#include <filesystem>
+#include <stdexcept>
+#include <string>
+
+namespace pldm
+{
+
+namespace fw_update
+{
+
+// using namespace phosphor::logging;
+using namespace std::string_literals;
+namespace fs = std::filesystem;
+
+Watch::Watch(sd_event* loop, std::function<int(std::string&)> imageCallback) :
+    imageCallback(imageCallback)
+{
+    // Check if IMAGE DIR exists.
+    fs::path imgDirPath("/tmp/images");
+    if (!fs::is_directory(imgDirPath))
+    {
+        fs::create_directories(imgDirPath);
+    }
+
+    fd = inotify_init1(IN_NONBLOCK);
+    if (-1 == fd)
+    {
+        // Store a copy of errno, because the string creation below will
+        // invalidate errno due to one more system calls.
+        auto error = errno;
+        throw std::runtime_error("inotify_init1 failed, errno="s +
+                                 std::strerror(error));
+    }
+
+    wd = inotify_add_watch(fd, "/tmp/images", IN_CLOSE_WRITE);
+    if (-1 == wd)
+    {
+        auto error = errno;
+        close(fd);
+        throw std::runtime_error("inotify_add_watch failed, errno="s +
+                                 std::strerror(error));
+    }
+
+    auto rc = sd_event_add_io(loop, nullptr, fd, EPOLLIN, callback, this);
+    if (0 > rc)
+    {
+        throw std::runtime_error("failed to add to event loop, rc="s +
+                                 std::strerror(-rc));
+    }
+}
+
+Watch::~Watch()
+{
+    if (-1 != fd)
+    {
+        if (-1 != wd)
+        {
+            inotify_rm_watch(fd, wd);
+        }
+        close(fd);
+    }
+}
+
+int Watch::callback(sd_event_source* /* s */, int fd, uint32_t revents,
+                    void* userdata)
+{
+    if (!(revents & EPOLLIN))
+    {
+        return 0;
+    }
+
+    constexpr auto maxBytes = 1024;
+    uint8_t buffer[maxBytes];
+    auto bytes = read(fd, buffer, maxBytes);
+    if (0 > bytes)
+    {
+        auto error = errno;
+        throw std::runtime_error("failed to read inotify event, errno="s +
+                                 std::strerror(error));
+    }
+
+    auto offset = 0;
+    while (offset < bytes)
+    {
+        auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
+        if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR))
+        {
+            auto tarballPath = std::string{"/tmp/images"} + '/' + event->name;
+            auto rc = static_cast<Watch*>(userdata)->imageCallback(tarballPath);
+            if (rc < 0)
+            {
+                // log<level::ERR>("Error processing image",
+                //                 entry("IMAGE=%s", tarballPath.c_str()));
+            }
+        }
+
+        offset += offsetof(inotify_event, name) + event->len;
+    }
+
+    return 0;
+}
+
+} // namespace fw_update
+} // namespace pldm