1 #include "filesystem_log_watcher.hpp"
2
3 #include "event_service_manager.hpp"
4 #include "logging.hpp"
5
6 #include <sys/inotify.h>
7
8 #include <boost/asio/buffer.hpp>
9 #include <boost/asio/error.hpp>
10 #include <boost/asio/io_context.hpp>
11 #include <boost/asio/posix/stream_descriptor.hpp>
12
13 #include <array>
14 #include <cstddef>
15 #include <cstring>
16 #include <optional>
17 #include <string>
18
19 namespace redfish
20 {
21
22 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
23 static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
24 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
25 static int inotifyFd = -1;
26 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
27 static int dirWatchDesc = -1;
28 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
29 static int fileWatchDesc = -1;
30 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
31 static std::array<char, 1024> readBuffer{};
32
33 static constexpr const char* redfishEventLogDir = "/var/log";
34 static constexpr const size_t iEventSize = sizeof(inotify_event);
35
watchRedfishEventLogFile()36 static void watchRedfishEventLogFile()
37 {
38 if (!inotifyConn)
39 {
40 BMCWEB_LOG_ERROR("inotify Connection is not present");
41 return;
42 }
43
44 inotifyConn->async_read_some(
45 boost::asio::buffer(readBuffer),
46 [&](const boost::system::error_code& ec,
47 const std::size_t& bytesTransferred) {
48 if (ec == boost::asio::error::operation_aborted)
49 {
50 BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
51 return;
52 }
53 if (ec)
54 {
55 BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
56 return;
57 }
58
59 BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
60
61 std::size_t index = 0;
62 while ((index + iEventSize) <= bytesTransferred)
63 {
64 struct inotify_event event
65 {};
66 std::memcpy(&event, &readBuffer[index], iEventSize);
67 if (event.wd == dirWatchDesc)
68 {
69 if ((event.len == 0) ||
70 (index + iEventSize + event.len > bytesTransferred))
71 {
72 index += (iEventSize + event.len);
73 continue;
74 }
75
76 std::string fileName(&readBuffer[index + iEventSize]);
77 if (fileName != "redfish")
78 {
79 index += (iEventSize + event.len);
80 continue;
81 }
82
83 BMCWEB_LOG_DEBUG(
84 "Redfish log file created/deleted. event.name: {}",
85 fileName);
86 if (event.mask == IN_CREATE)
87 {
88 if (fileWatchDesc != -1)
89 {
90 BMCWEB_LOG_DEBUG(
91 "Remove and Add inotify watcher on "
92 "redfish event log file");
93 // Remove existing inotify watcher and add
94 // with new redfish event log file.
95 inotify_rm_watch(inotifyFd, fileWatchDesc);
96 fileWatchDesc = -1;
97 }
98
99 fileWatchDesc = inotify_add_watch(
100 inotifyFd, redfishEventLogFile, IN_MODIFY);
101 if (fileWatchDesc == -1)
102 {
103 BMCWEB_LOG_ERROR("inotify_add_watch failed for "
104 "redfish log file.");
105 return;
106 }
107
108 EventServiceManager::getInstance()
109 .resetRedfishFilePosition();
110 EventServiceManager::getInstance()
111 .readEventLogsFromFile();
112 }
113 else if ((event.mask == IN_DELETE) ||
114 (event.mask == IN_MOVED_TO))
115 {
116 if (fileWatchDesc != -1)
117 {
118 inotify_rm_watch(inotifyFd, fileWatchDesc);
119 fileWatchDesc = -1;
120 }
121 }
122 }
123 else if (event.wd == fileWatchDesc)
124 {
125 if (event.mask == IN_MODIFY)
126 {
127 EventServiceManager::getInstance()
128 .readEventLogsFromFile();
129 }
130 }
131 index += (iEventSize + event.len);
132 }
133
134 watchRedfishEventLogFile();
135 });
136 }
137
startEventLogMonitor(boost::asio::io_context & ioc)138 int startEventLogMonitor(boost::asio::io_context& ioc)
139 {
140 BMCWEB_LOG_DEBUG("starting Event Log Monitor");
141
142 inotifyConn.emplace(ioc);
143 inotifyFd = inotify_init1(IN_NONBLOCK);
144 if (inotifyFd == -1)
145 {
146 BMCWEB_LOG_ERROR("inotify_init1 failed.");
147 return -1;
148 }
149
150 // Add watch on directory to handle redfish event log file
151 // create/delete.
152 dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
153 IN_CREATE | IN_MOVED_TO | IN_DELETE);
154 if (dirWatchDesc == -1)
155 {
156 BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
157 return -1;
158 }
159
160 // Watch redfish event log file for modifications.
161 fileWatchDesc =
162 inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
163 if (fileWatchDesc == -1)
164 {
165 BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
166 // Don't return error if file not exist.
167 // Watch on directory will handle create/delete of file.
168 }
169
170 // monitor redfish event log file
171 inotifyConn->assign(inotifyFd);
172 watchRedfishEventLogFile();
173
174 return 0;
175 }
176
stopEventLogMonitor()177 void stopEventLogMonitor()
178 {
179 inotifyConn.reset();
180 }
181 } // namespace redfish
182