1 #include "filesystem_log_watcher.hpp"
2
3 #include "event_log.hpp"
4 #include "event_logs_object_type.hpp"
5 #include "event_service_manager.hpp"
6 #include "logging.hpp"
7
8 #include <sys/inotify.h>
9
10 #include <boost/asio/buffer.hpp>
11 #include <boost/asio/error.hpp>
12 #include <boost/asio/io_context.hpp>
13 #include <boost/asio/posix/stream_descriptor.hpp>
14
15 #include <array>
16 #include <bit>
17 #include <cstddef>
18 #include <cstring>
19 #include <fstream>
20 #include <functional>
21 #include <string>
22 #include <vector>
23
24 namespace redfish
25 {
resetRedfishFilePosition()26 void FilesystemLogWatcher::resetRedfishFilePosition()
27 {
28 // Control would be here when Redfish file is created.
29 // Reset File Position as new file is created
30 redfishLogFilePosition = 0;
31 }
32
cacheRedfishLogFile()33 void FilesystemLogWatcher::cacheRedfishLogFile()
34 {
35 // Open the redfish file and read till the last record.
36
37 std::ifstream logStream(redfishEventLogFile);
38 if (!logStream.good())
39 {
40 BMCWEB_LOG_ERROR(" Redfish log file open failed ");
41 return;
42 }
43 std::string logEntry;
44 while (std::getline(logStream, logEntry))
45 {
46 redfishLogFilePosition = logStream.tellg();
47 }
48 }
49
readEventLogsFromFile()50 void FilesystemLogWatcher::readEventLogsFromFile()
51 {
52 std::ifstream logStream(redfishEventLogFile);
53 if (!logStream.good())
54 {
55 BMCWEB_LOG_ERROR(" Redfish log file open failed");
56 return;
57 }
58
59 std::vector<EventLogObjectsType> eventRecords;
60
61 std::string logEntry;
62
63 BMCWEB_LOG_DEBUG("Redfish log file: seek to {}",
64 static_cast<int>(redfishLogFilePosition));
65
66 // Get the read pointer to the next log to be read.
67 logStream.seekg(redfishLogFilePosition);
68
69 while (std::getline(logStream, logEntry))
70 {
71 BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry");
72 // Update Pointer position
73 redfishLogFilePosition = logStream.tellg();
74
75 std::string idStr;
76 if (!event_log::getUniqueEntryID(logEntry, idStr))
77 {
78 BMCWEB_LOG_DEBUG(
79 "Redfish log file: could not get unique entry id for {}",
80 logEntry);
81 continue;
82 }
83
84 std::string timestamp;
85 std::string messageID;
86 std::vector<std::string> messageArgs;
87 if (event_log::getEventLogParams(logEntry, timestamp, messageID,
88 messageArgs) != 0)
89 {
90 BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}",
91 logEntry);
92 continue;
93 }
94
95 eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs);
96 }
97
98 if (eventRecords.empty())
99 {
100 // No Records to send
101 BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
102 return;
103 }
104 EventServiceManager::sendEventsToSubs(eventRecords);
105 }
106
107 static constexpr const char* redfishEventLogDir = "/var/log";
108 static constexpr const size_t iEventSize = sizeof(inotify_event);
109
onINotify(const boost::system::error_code & ec,std::size_t bytesTransferred)110 void FilesystemLogWatcher::onINotify(const boost::system::error_code& ec,
111 std::size_t bytesTransferred)
112 {
113 if (ec == boost::asio::error::operation_aborted)
114 {
115 BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
116 return;
117 }
118 if (ec)
119 {
120 BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
121 return;
122 }
123
124 BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
125
126 std::size_t index = 0;
127 while ((index + iEventSize) <= bytesTransferred)
128 {
129 struct inotify_event& event =
130 *std::bit_cast<struct inotify_event*>(&readBuffer[index]);
131 if (event.wd == dirWatchDesc)
132 {
133 if ((event.len == 0) ||
134 (index + iEventSize + event.len > bytesTransferred))
135 {
136 index += (iEventSize + event.len);
137 continue;
138 }
139
140 std::string fileName(&readBuffer[index + iEventSize]);
141 if (fileName != "redfish")
142 {
143 index += (iEventSize + event.len);
144 continue;
145 }
146
147 BMCWEB_LOG_DEBUG("Redfish log file created/deleted. event.name: {}",
148 fileName);
149 if (event.mask == IN_CREATE)
150 {
151 if (fileWatchDesc != -1)
152 {
153 BMCWEB_LOG_DEBUG("Remove and Add inotify watcher on "
154 "redfish event log file");
155 // Remove existing inotify watcher and add
156 // with new redfish event log file.
157 inotify_rm_watch(inotifyConn.native_handle(),
158 fileWatchDesc);
159 fileWatchDesc = -1;
160 }
161
162 fileWatchDesc =
163 inotify_add_watch(inotifyConn.native_handle(),
164 redfishEventLogFile, IN_MODIFY);
165 if (fileWatchDesc == -1)
166 {
167 BMCWEB_LOG_ERROR("inotify_add_watch failed for "
168 "redfish log file.");
169 return;
170 }
171
172 resetRedfishFilePosition();
173 readEventLogsFromFile();
174 }
175 else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
176 {
177 if (fileWatchDesc != -1)
178 {
179 inotify_rm_watch(inotifyConn.native_handle(),
180 fileWatchDesc);
181 fileWatchDesc = -1;
182 }
183 }
184 }
185 else if (event.wd == fileWatchDesc)
186 {
187 if (event.mask == IN_MODIFY)
188 {
189 readEventLogsFromFile();
190 }
191 }
192 index += (iEventSize + event.len);
193 }
194
195 watchRedfishEventLogFile();
196 }
197
watchRedfishEventLogFile()198 void FilesystemLogWatcher::watchRedfishEventLogFile()
199 {
200 inotifyConn.async_read_some(
201 boost::asio::buffer(readBuffer),
202 std::bind_front(&FilesystemLogWatcher::onINotify, this));
203 }
204
FilesystemLogWatcher(boost::asio::io_context & ioc)205 FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) :
206 inotifyConn(ioc)
207 {
208 BMCWEB_LOG_DEBUG("starting Event Log Monitor");
209
210 int inotifyFd = inotify_init1(IN_NONBLOCK);
211 if (inotifyFd < 0)
212 {
213 BMCWEB_LOG_ERROR("inotify_init1 failed.");
214 return;
215 }
216 boost::system::error_code ec;
217 inotifyConn.assign(inotifyFd, ec);
218 if (ec)
219 {
220 BMCWEB_LOG_ERROR("Failed to assign fd {}", ec.message());
221 return;
222 }
223
224 // Add watch on directory to handle redfish event log file
225 // create/delete.
226 dirWatchDesc =
227 inotify_add_watch(inotifyConn.native_handle(), redfishEventLogDir,
228 IN_CREATE | IN_MOVED_TO | IN_DELETE);
229 if (dirWatchDesc < 0)
230 {
231 BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
232 return;
233 }
234
235 // Watch redfish event log file for modifications.
236 fileWatchDesc = inotify_add_watch(inotifyConn.native_handle(),
237 redfishEventLogFile, IN_MODIFY);
238 if (fileWatchDesc < 0)
239 {
240 BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
241 // Don't return error if file not exist.
242 // Watch on directory will handle create/delete of file.
243 }
244
245 // monitor redfish event log file
246 watchRedfishEventLogFile();
247
248 if (redfishLogFilePosition != 0)
249 {
250 cacheRedfishLogFile();
251 }
252 }
253 } // namespace redfish
254