xref: /openbmc/bmcweb/redfish-core/src/filesystem_log_watcher.cpp (revision 80e6e25e7d721fa03fcc2b194881d8d8a64fe416)
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(inotifyFd, fileWatchDesc);
157                     fileWatchDesc = -1;
158                 }
159 
160                 fileWatchDesc = inotify_add_watch(
161                     inotifyFd, redfishEventLogFile, IN_MODIFY);
162                 if (fileWatchDesc == -1)
163                 {
164                     BMCWEB_LOG_ERROR("inotify_add_watch failed for "
165                                      "redfish log file.");
166                     return;
167                 }
168 
169                 resetRedfishFilePosition();
170                 readEventLogsFromFile();
171             }
172             else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
173             {
174                 if (fileWatchDesc != -1)
175                 {
176                     inotify_rm_watch(inotifyFd, fileWatchDesc);
177                     fileWatchDesc = -1;
178                 }
179             }
180         }
181         else if (event.wd == fileWatchDesc)
182         {
183             if (event.mask == IN_MODIFY)
184             {
185                 readEventLogsFromFile();
186             }
187         }
188         index += (iEventSize + event.len);
189     }
190 
191     watchRedfishEventLogFile();
192 }
193 
194 void FilesystemLogWatcher::watchRedfishEventLogFile()
195 {
196     inotifyConn.async_read_some(
197         boost::asio::buffer(readBuffer),
198         std::bind_front(&FilesystemLogWatcher::onINotify, this));
199 }
200 
201 FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) :
202     inotifyFd(inotify_init1(IN_NONBLOCK)), inotifyConn(ioc)
203 {
204     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
205 
206     if (inotifyFd == -1)
207     {
208         BMCWEB_LOG_ERROR("inotify_init1 failed.");
209         return;
210     }
211 
212     // Add watch on directory to handle redfish event log file
213     // create/delete.
214     dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
215                                      IN_CREATE | IN_MOVED_TO | IN_DELETE);
216     if (dirWatchDesc == -1)
217     {
218         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
219         return;
220     }
221 
222     // Watch redfish event log file for modifications.
223     fileWatchDesc =
224         inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
225     if (fileWatchDesc == -1)
226     {
227         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
228         // Don't return error if file not exist.
229         // Watch on directory will handle create/delete of file.
230     }
231 
232     // monitor redfish event log file
233     inotifyConn.assign(inotifyFd);
234     watchRedfishEventLogFile();
235 
236     if (redfishLogFilePosition != 0)
237     {
238         cacheRedfishLogFile();
239     }
240 }
241 } // namespace redfish
242