1 #pragma once 2 #include <sys/inotify.h> 3 #include <unistd.h> 4 5 #include <sdbusplus/async/context.hpp> 6 #include <sdbusplus/async/fdio.hpp> 7 #include <sdbusplus/async/task.hpp> 8 9 #include <array> 10 #include <cerrno> 11 #include <cstddef> 12 #include <cstdint> 13 #include <cstring> 14 #include <filesystem> 15 #include <memory> 16 #include <span> 17 #include <string> 18 #include <system_error> 19 namespace phosphor::notify::watch 20 { 21 namespace fs = std::filesystem; 22 template <typename Instance> 23 class NotifyWatch 24 { 25 public: 26 NotifyWatch() = delete; 27 NotifyWatch(const NotifyWatch&) = delete; 28 NotifyWatch& operator=(const NotifyWatch&) = delete; 29 NotifyWatch(NotifyWatch&&) = delete; 30 NotifyWatch& operator=(NotifyWatch&&) = delete; 31 NotifyWatch(sdbusplus::async::context & ctx,const std::string & dir)32 explicit NotifyWatch(sdbusplus::async::context& ctx, 33 const std::string& dir) : notifyCtx(ctx) 34 { 35 std::error_code ec = {}; 36 fs::path dirPath(dir); 37 if (!fs::create_directories(dirPath, ec)) 38 { 39 if (ec) 40 { 41 throw std::system_error(ec, 42 "Failed to create directory " + dir); 43 } 44 } 45 fd = inotify_init1(IN_NONBLOCK); 46 if (-1 == fd) 47 { 48 throw std::system_error(errno, std::system_category(), 49 "inotify_init1 failed"); 50 } 51 wd = inotify_add_watch(fd, dir.c_str(), IN_CLOSE_WRITE); 52 if (-1 == wd) 53 { 54 close(fd); 55 throw std::system_error(errno, std::system_category(), 56 "inotify_add_watch failed"); 57 } 58 fdioInstance = std::make_unique<sdbusplus::async::fdio>(ctx, fd); 59 } ~NotifyWatch()60 ~NotifyWatch() 61 { 62 if (-1 != fd) 63 { 64 if (-1 != wd) 65 { 66 inotify_rm_watch(fd, wd); 67 } 68 close(fd); 69 } 70 } readNotifyAsync()71 sdbusplus::async::task<> readNotifyAsync() 72 { 73 co_await fdioInstance->next(); 74 constexpr size_t maxBytes = 1024; 75 std::array<uint8_t, maxBytes> buffer{}; 76 auto bytes = read(fd, buffer.data(), maxBytes); 77 if (0 > bytes) 78 { 79 throw std::system_error(errno, std::system_category(), 80 "Failed to read notify event"); 81 } 82 auto offset = 0; 83 while (offset < bytes) 84 { 85 // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) 86 std::span<uint32_t> mask{ 87 reinterpret_cast<uint32_t*>( 88 buffer.data() + offset + offsetof(inotify_event, mask)), 89 1}; 90 std::span<uint32_t> len{ 91 reinterpret_cast<uint32_t*>( 92 buffer.data() + offset + offsetof(inotify_event, len)), 93 1}; 94 // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) 95 if (((mask[0] & IN_CLOSE_WRITE) != 0U) && 96 ((mask[0] & IN_ISDIR) == 0U)) 97 { 98 // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) 99 std::span<char> name{ 100 reinterpret_cast<char*>( 101 buffer.data() + offset + offsetof(inotify_event, name)), 102 len[0]}; 103 // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) 104 co_await static_cast<Instance*>(this)->processUpdate( 105 std::string(name.begin(), name.end())); 106 } 107 offset += offsetof(inotify_event, name) + len[0]; 108 } 109 if (!notifyCtx.stop_requested()) 110 { 111 notifyCtx.spawn(readNotifyAsync()); 112 } 113 } 114 115 private: 116 sdbusplus::async::context& notifyCtx; 117 int wd = -1; 118 int fd = -1; 119 std::unique_ptr<sdbusplus::async::fdio> fdioInstance; 120 }; 121 } // namespace phosphor::notify::watch 122