#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace phosphor::notify::watch { namespace fs = std::filesystem; template class NotifyWatch { public: NotifyWatch() = delete; NotifyWatch(const NotifyWatch&) = delete; NotifyWatch& operator=(const NotifyWatch&) = delete; NotifyWatch(NotifyWatch&&) = delete; NotifyWatch& operator=(NotifyWatch&&) = delete; explicit NotifyWatch(sdbusplus::async::context& ctx, const std::string& dir) : notifyCtx(ctx) { std::error_code ec = {}; fs::path dirPath(dir); if (!fs::create_directories(dirPath, ec)) { if (ec) { throw std::system_error(ec, "Failed to create directory " + dir); } } fd = inotify_init1(IN_NONBLOCK); if (-1 == fd) { throw std::system_error(errno, std::system_category(), "inotify_init1 failed"); } wd = inotify_add_watch(fd, dir.c_str(), IN_CLOSE_WRITE); if (-1 == wd) { close(fd); throw std::system_error(errno, std::system_category(), "inotify_add_watch failed"); } fdioInstance = std::make_unique(ctx, fd); } ~NotifyWatch() { if (-1 != fd) { if (-1 != wd) { inotify_rm_watch(fd, wd); } close(fd); } } sdbusplus::async::task<> readNotifyAsync() { co_await fdioInstance->next(); constexpr size_t maxBytes = 1024; std::array buffer{}; auto bytes = read(fd, buffer.data(), maxBytes); if (0 > bytes) { throw std::system_error(errno, std::system_category(), "Failed to read notify event"); } auto offset = 0; while (offset < bytes) { // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) std::span mask{ reinterpret_cast( buffer.data() + offset + offsetof(inotify_event, mask)), 1}; std::span len{ reinterpret_cast( buffer.data() + offset + offsetof(inotify_event, len)), 1}; // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) if (((mask[0] & IN_CLOSE_WRITE) != 0U) && ((mask[0] & IN_ISDIR) == 0U)) { // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) std::span name{ reinterpret_cast( buffer.data() + offset + offsetof(inotify_event, name)), len[0]}; // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) co_await static_cast(this)->processUpdate( std::string(name.begin(), name.end())); } offset += offsetof(inotify_event, name) + len[0]; } if (!notifyCtx.stop_requested()) { notifyCtx.spawn(readNotifyAsync()); } } private: sdbusplus::async::context& notifyCtx; int wd = -1; int fd = -1; std::unique_ptr fdioInstance; }; } // namespace phosphor::notify::watch