Add support for generic inotify based directory watch.
Watch a directory for the changes based on user configuration
and then report changes to the user.
Change-Id: I9f53d3135dd4bff6187840c0c53d2a64509808cd
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 72401e1..85149c4 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,8 @@
dump_watch.hpp \
dump_internal.hpp \
dump_manager.hpp \
- dump_utils.hpp
+ dump_utils.hpp \
+ watch.hpp
nobase_nodist_include_HEADERS = \
xyz/openbmc_project/Dump/Monitor/error.hpp \
@@ -20,8 +21,8 @@
dump_manager_main.cpp \
dump_entry.cpp \
dump_manager.cpp \
- xyz/openbmc_project/Dump/Internal/Create/server.cpp \
- xyz/openbmc_project/Dump/Monitor/error.cpp
+ watch.cpp \
+ xyz/openbmc_project/Dump/Internal/Create/server.cpp
phosphor_dump_monitor_SOURCES = \
dump_watch_main.cpp \
diff --git a/dump_manager.hpp b/dump_manager.hpp
index 3bbcf3e..b4c09c1 100644
--- a/dump_manager.hpp
+++ b/dump_manager.hpp
@@ -1,12 +1,14 @@
#pragma once
+#include <experimental/filesystem>
+
#include <sdbusplus/bus.hpp>
#include <sdbusplus/server/object.hpp>
#include <xyz/openbmc_project/Dump/Create/server.hpp>
-#include <experimental/filesystem>
#include "xyz/openbmc_project/Dump/Internal/Create/server.hpp"
#include "dump_entry.hpp"
+#include "dump_utils.hpp"
namespace phosphor
{
@@ -19,22 +21,13 @@
} // namespace internal
-namespace fs = std::experimental::filesystem;
using Type =
sdbusplus::xyz::openbmc_project::Dump::Internal::server::Create::Type;
using CreateIface = sdbusplus::server::object::object<
sdbusplus::xyz::openbmc_project::Dump::server::Create>;
-/* Need a custom deleter for freeing up sd_event */
-struct EventDeleter
-{
- void operator()(sd_event* event) const
- {
- event = sd_event_unref(event);
- }
-};
-using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
+namespace fs = std::experimental::filesystem;
/** @class Manager
* @brief OpenBMC Dump manager implementation.
diff --git a/dump_manager_main.cpp b/dump_manager_main.cpp
index cd1d83b..6be9a29 100644
--- a/dump_manager_main.cpp
+++ b/dump_manager_main.cpp
@@ -1,3 +1,4 @@
+#include <sdbusplus/bus.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include "xyz/openbmc_project/Common/error.hpp"
@@ -7,11 +8,11 @@
int main(int argc, char* argv[])
{
- auto bus = sdbusplus::bus::new_default();
using namespace phosphor::logging;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+ auto bus = sdbusplus::bus::new_default();
sd_event* event = nullptr;
auto rc = sd_event_default(&event);
if (rc < 0)
@@ -41,7 +42,6 @@
elog<InternalFailure>();
}
}
-
catch (InternalFailure& e)
{
commit<InternalFailure>();
diff --git a/dump_utils.hpp b/dump_utils.hpp
index a71458c..6ebee33 100644
--- a/dump_utils.hpp
+++ b/dump_utils.hpp
@@ -1,10 +1,23 @@
#pragma once
+#include <memory>
+#include <unistd.h>
+
namespace phosphor
{
namespace dump
{
+/* Need a custom deleter for freeing up sd_event */
+struct EventDeleter
+{
+ void operator()(sd_event* event) const
+ {
+ event = sd_event_unref(event);
+ }
+};
+using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
+
/** @struct CustomFd
*
* RAII wrapper for file descriptor.
diff --git a/watch.cpp b/watch.cpp
new file mode 100644
index 0000000..bc7fb1f
--- /dev/null
+++ b/watch.cpp
@@ -0,0 +1,140 @@
+#include <phosphor-logging/elog-errors.hpp>
+
+#include "xyz/openbmc_project/Common/error.hpp"
+#include "watch.hpp"
+
+namespace phosphor
+{
+namespace dump
+{
+namespace inotify
+{
+
+using namespace std::string_literals;
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Watch::~Watch()
+{
+ if ((fd() >= 0) && (wd >= 0))
+ {
+ inotify_rm_watch(fd(), wd);
+ }
+}
+
+Watch::Watch(const EventPtr& eventObj,
+ const int flags,
+ const uint32_t mask,
+ const uint32_t events,
+ const fs::path& path,
+ UserType userFunc):
+ flags(flags),
+ mask(mask),
+ events(events),
+ path(path),
+ fd(inotifyInit()),
+ userFunc(userFunc)
+{
+ // Check if watch DIR exists.
+ if (!fs::is_directory(path))
+ {
+ log<level::ERR>("Watch directory doesn't exist",
+ entry("dir=%s", path.c_str()));
+ elog<InternalFailure>();
+ }
+
+ wd = inotify_add_watch(fd(), path.c_str(), mask);
+ if (-1 == wd)
+ {
+ auto error = errno;
+ log<level::ERR>("Error occurred during the inotify_add_watch call",
+ entry("ERRNO=%d", error));
+ elog<InternalFailure>();
+ }
+
+ auto rc = sd_event_add_io(eventObj.get(),
+ nullptr,
+ fd(),
+ events,
+ callback,
+ this);
+ if (0 > rc)
+ {
+ // Failed to add to event loop
+ log<level::ERR>("Error occurred during the sd_event_add_io call",
+ entry("rc=%d", rc));
+ elog<InternalFailure>();
+ }
+}
+
+int Watch::inotifyInit()
+{
+ auto fd = inotify_init1(flags);
+
+ if (-1 == fd)
+ {
+ auto error = errno;
+ log<level::ERR>("Error occurred during the inotify_init1",
+ entry("ERRNO=%d", error));
+ elog<InternalFailure>();
+ }
+
+ return fd;
+}
+
+int Watch::callback(sd_event_source* s,
+ int fd,
+ uint32_t revents,
+ void* userdata)
+{
+ if (!(revents & static_cast<Watch*>(userdata)->events))
+ {
+ return 0;
+ }
+
+ //Maximum inotify events supported in the buffer
+ constexpr auto maxBytes = sizeof(struct inotify_event) + NAME_MAX + 1;
+ uint8_t buffer[maxBytes];
+
+ auto bytes = read(fd, buffer, maxBytes);
+ if (0 > bytes)
+ {
+ //Failed to read inotify event
+ //Report error and return
+ auto error = errno;
+ log<level::ERR>("Error occurred during the read",
+ entry("ERRNO=%d", error));
+ report<InternalFailure>();
+ return 0;
+ }
+
+ auto offset = 0;
+
+ UserMap userMap;
+
+ while (offset < bytes)
+ {
+ auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
+ auto mask = event->mask & static_cast<Watch*>(userdata)->mask;
+
+ if (mask && !(event->mask & IN_ISDIR))
+ {
+ userMap.emplace(
+ (static_cast<Watch*>(userdata)->path / event->name), mask);
+ }
+
+ offset += offsetof(inotify_event, name) + event->len;
+ }
+
+ //Call user call back function incase valid data in the map
+ if (!userMap.empty())
+ {
+ static_cast<Watch*>(userdata)->userFunc(userMap);
+ }
+
+ return 0;
+}
+
+} // namespace inotify
+} // namespace dump
+} // namespace phosphor
diff --git a/watch.hpp b/watch.hpp
new file mode 100644
index 0000000..bbfa476
--- /dev/null
+++ b/watch.hpp
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <experimental/filesystem>
+#include <systemd/sd-event.h>
+#include <sys/inotify.h>
+#include <map>
+
+#include "dump_utils.hpp"
+
+namespace phosphor
+{
+namespace dump
+{
+namespace inotify
+{
+
+namespace fs = std::experimental::filesystem;
+
+//User specfic call back function input map(path:event) type.
+using UserMap = std::map<fs::path, uint32_t>;
+
+//User specific callback function wrapper type.
+using UserType = std::function<void(const UserMap&)>;
+
+/** @class Watch
+ *
+ * @brief Adds inotify watch on directory.
+ *
+ * The inotify watch is hooked up with sd-event, so that on call back,
+ * appropriate actions are taken to collect files from the directory
+ * initialized by the object.
+ */
+class Watch
+{
+ public:
+ /** @brief ctor - hook inotify watch with sd-event
+ *
+ * @param[in] eventObj - Event loop object
+ * @param[in] flags - inotify flags
+ * @param[in] mask - Mask of events
+ * @param[in] events - Events to be watched
+ * @param[in] path - File path to be watched
+ * @param[in] userFunc - User specific callback fnction wrapper.
+ *
+ */
+ Watch(const EventPtr& eventObj,
+ int flags,
+ uint32_t mask,
+ uint32_t events,
+ const fs::path& path,
+ UserType userFunc);
+
+ Watch(const Watch&) = delete;
+ Watch& operator=(const Watch&) = delete;
+ Watch(Watch&&) = default;
+ Watch& operator=(Watch&&) = default;
+
+ /* @brief dtor - remove inotify watch and close fd's */
+ ~Watch();
+
+ private:
+ /** @brief sd-event callback.
+ * @details Collects the files and event info and call the
+ * appropriate user function for further action.
+ *
+ * @param[in] s - event source, floating (unused) in our case
+ * @param[in] fd - inotify fd
+ * @param[in] revents - events that matched for fd
+ * @param[in] userdata - pointer to Watch object
+ *
+ * @returns 0 on success, -1 on fail
+ */
+ static int callback(sd_event_source* s,
+ int fd,
+ uint32_t revents,
+ void* userdata);
+
+ /** initialize an inotify instance and returns file descriptor */
+ int inotifyInit();
+
+ /** @brief inotify flags */
+ int flags;
+
+ /** @brief Mask of events */
+ uint32_t mask;
+
+ /** @brief Events to be watched */
+ uint32_t events;
+
+ /** @brief File path to be watched */
+ fs::path path;
+
+ /** @brief dump file directory watch descriptor */
+ int wd = -1;
+
+ /** @brief file descriptor manager */
+ CustomFd fd;
+
+ /** @brief The user level callback function wrapper */
+ UserType userFunc;
+};
+
+} // namespace inotify
+} // namespace dump
+} // namespace phosphor