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