#include "watch.hpp" #include #include #include #include #include namespace phosphor { namespace network { namespace inotify { using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::Error; Watch::Watch(phosphor::network::EventPtr& eventPtr, fs::path path, UserCallBack userFunc, int flags, uint32_t mask, uint32_t events) : path(path), userFunc(userFunc), flags(flags), mask(mask), events(events), fd(inotifyInit()) { // Check if watch file exists // This is supposed to be there always if (!fs::is_regular_file(path)) { log("Watch file doesn't exist", entry("FILE=%s", path.c_str())); elog(); } auto dirPath = path.parent_path(); wd = inotify_add_watch(fd(), dirPath.c_str(), mask); if (wd == -1) { log("Error from inotify_add_watch", entry("ERRNO=%d", errno)); elog(); } // Register the fd with sd_event infrastructure and setup a // callback handler to be invoked on events auto rc = sd_event_add_io(eventPtr.get(), nullptr, fd(), events, Watch::processEvents, this); if (rc < 0) { // Failed to add to event loop log("Error registering with sd_event_add_io", entry("RC=%d", rc)); elog(); } } int Watch::inotifyInit() { auto fd = inotify_init1(flags); if (fd < 0) { log("Error from inotify_init1", entry("ERRNO=%d", errno)); elog(); } return fd; } int Watch::processEvents(sd_event_source* eventSource, int fd, uint32_t retEvents, void* userData) { auto watch = static_cast(userData); // Not the ones we are interested in if (!(retEvents & watch->events)) { return 0; } // Buffer size to be used while reading events. // per inotify(7), below number should be fine for reading // at-least one event constexpr auto maxBytes = sizeof(struct inotify_event) + NAME_MAX + 1; uint8_t eventData[maxBytes]{}; auto bytes = read(fd, eventData, maxBytes); if (bytes <= 0) { // Failed to read inotify event data // Report error and return log("Error reading inotify event", entry("ERRNO=%d", errno)); report(); return 0; } auto offset = 0; auto stateFile = watch->path.filename(); while (offset < bytes) { auto event = reinterpret_cast(&eventData[offset]); // Filter the interesting ones auto mask = event->mask & watch->mask; if (mask) { if ((event->len > 0) && (strstr(event->name, stateFile.string().c_str()))) { if (watch->userFunc) { watch->userFunc(watch->path); } // Found the event of interest break; } } // Move past this entry offset += offsetof(inotify_event, name) + event->len; } return 0; } } // namespace inotify } // namespace network } // namespace phosphor