| #include "watch.hpp" |
| |
| #include <errno.h> |
| #include <sys/inotify.h> |
| |
| #include <phosphor-logging/elog-errors.hpp> |
| #include <phosphor-logging/log.hpp> |
| #include <xyz/openbmc_project/Common/error.hpp> |
| |
| 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<level::ERR>("Watch file doesn't exist", |
| entry("FILE=%s", path.c_str())); |
| elog<InternalFailure>(); |
| } |
| |
| auto dirPath = path.parent_path(); |
| wd = inotify_add_watch(fd(), dirPath.c_str(), mask); |
| if (wd == -1) |
| { |
| log<level::ERR>("Error from inotify_add_watch", |
| entry("ERRNO=%d", errno)); |
| elog<InternalFailure>(); |
| } |
| |
| // 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<level::ERR>("Error registering with sd_event_add_io", |
| entry("RC=%d", rc)); |
| elog<InternalFailure>(); |
| } |
| } |
| |
| int Watch::inotifyInit() |
| { |
| auto fd = inotify_init1(flags); |
| if (fd < 0) |
| { |
| log<level::ERR>("Error from inotify_init1", entry("ERRNO=%d", errno)); |
| elog<InternalFailure>(); |
| } |
| return fd; |
| } |
| |
| int Watch::processEvents(sd_event_source* eventSource, int fd, |
| uint32_t retEvents, void* userData) |
| { |
| auto watch = static_cast<Watch*>(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<level::ERR>("Error reading inotify event", |
| entry("ERRNO=%d", errno)); |
| report<InternalFailure>(); |
| return 0; |
| } |
| |
| auto offset = 0; |
| auto stateFile = watch->path.filename(); |
| while (offset < bytes) |
| { |
| auto event = reinterpret_cast<inotify_event*>(&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 |