blob: 8b5c79570cbd14b37d6ef0a2191702c7720eb010 [file] [log] [blame]
Alexander Hansenf2c95a02024-11-26 11:16:44 +01001#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>
19namespace phosphor::notify::watch
20{
21namespace fs = std::filesystem;
22template <typename Instance>
23class 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
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 }
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 }
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