xref: /openbmc/bmcweb/redfish-core/src/filesystem_log_watcher.cpp (revision bb456a6d35c1e99b882dd286d689a6b711d9af87)
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