xref: /openbmc/bmcweb/features/redfish/src/filesystem_log_watcher.cpp (revision 7b6697234817c8761e353ecae44e2b2515a6fefa)
12185ddeaSEd Tanous #include "filesystem_log_watcher.hpp"
22185ddeaSEd Tanous 
3*7b669723SEd Tanous #include "event_log.hpp"
4*7b669723SEd Tanous #include "event_logs_object_type.hpp"
52185ddeaSEd Tanous #include "event_service_manager.hpp"
62185ddeaSEd Tanous #include "logging.hpp"
72185ddeaSEd Tanous 
82185ddeaSEd Tanous #include <sys/inotify.h>
92185ddeaSEd Tanous 
102185ddeaSEd Tanous #include <boost/asio/buffer.hpp>
112185ddeaSEd Tanous #include <boost/asio/error.hpp>
122185ddeaSEd Tanous #include <boost/asio/io_context.hpp>
132185ddeaSEd Tanous #include <boost/asio/posix/stream_descriptor.hpp>
142185ddeaSEd Tanous 
152185ddeaSEd Tanous #include <array>
162185ddeaSEd Tanous #include <cstddef>
172185ddeaSEd Tanous #include <cstring>
18*7b669723SEd Tanous #include <fstream>
19*7b669723SEd Tanous #include <functional>
202185ddeaSEd Tanous #include <string>
21*7b669723SEd Tanous #include <vector>
222185ddeaSEd Tanous 
232185ddeaSEd Tanous namespace redfish
242185ddeaSEd Tanous {
25*7b669723SEd Tanous void FilesystemLogWatcher::resetRedfishFilePosition()
26*7b669723SEd Tanous {
27*7b669723SEd Tanous     // Control would be here when Redfish file is created.
28*7b669723SEd Tanous     // Reset File Position as new file is created
29*7b669723SEd Tanous     redfishLogFilePosition = 0;
30*7b669723SEd Tanous }
312185ddeaSEd Tanous 
32*7b669723SEd Tanous void FilesystemLogWatcher::cacheRedfishLogFile()
33*7b669723SEd Tanous {
34*7b669723SEd Tanous     // Open the redfish file and read till the last record.
35*7b669723SEd Tanous 
36*7b669723SEd Tanous     std::ifstream logStream(redfishEventLogFile);
37*7b669723SEd Tanous     if (!logStream.good())
38*7b669723SEd Tanous     {
39*7b669723SEd Tanous         BMCWEB_LOG_ERROR(" Redfish log file open failed ");
40*7b669723SEd Tanous         return;
41*7b669723SEd Tanous     }
42*7b669723SEd Tanous     std::string logEntry;
43*7b669723SEd Tanous     while (std::getline(logStream, logEntry))
44*7b669723SEd Tanous     {
45*7b669723SEd Tanous         redfishLogFilePosition = logStream.tellg();
46*7b669723SEd Tanous     }
47*7b669723SEd Tanous }
48*7b669723SEd Tanous 
49*7b669723SEd Tanous void FilesystemLogWatcher::readEventLogsFromFile()
50*7b669723SEd Tanous {
51*7b669723SEd Tanous     std::ifstream logStream(redfishEventLogFile);
52*7b669723SEd Tanous     if (!logStream.good())
53*7b669723SEd Tanous     {
54*7b669723SEd Tanous         BMCWEB_LOG_ERROR(" Redfish log file open failed");
55*7b669723SEd Tanous         return;
56*7b669723SEd Tanous     }
57*7b669723SEd Tanous 
58*7b669723SEd Tanous     std::vector<EventLogObjectsType> eventRecords;
59*7b669723SEd Tanous 
60*7b669723SEd Tanous     std::string logEntry;
61*7b669723SEd Tanous 
62*7b669723SEd Tanous     BMCWEB_LOG_DEBUG("Redfish log file: seek to {}",
63*7b669723SEd Tanous                      static_cast<int>(redfishLogFilePosition));
64*7b669723SEd Tanous 
65*7b669723SEd Tanous     // Get the read pointer to the next log to be read.
66*7b669723SEd Tanous     logStream.seekg(redfishLogFilePosition);
67*7b669723SEd Tanous 
68*7b669723SEd Tanous     while (std::getline(logStream, logEntry))
69*7b669723SEd Tanous     {
70*7b669723SEd Tanous         BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry");
71*7b669723SEd Tanous         // Update Pointer position
72*7b669723SEd Tanous         redfishLogFilePosition = logStream.tellg();
73*7b669723SEd Tanous 
74*7b669723SEd Tanous         std::string idStr;
75*7b669723SEd Tanous         if (!event_log::getUniqueEntryID(logEntry, idStr))
76*7b669723SEd Tanous         {
77*7b669723SEd Tanous             BMCWEB_LOG_DEBUG(
78*7b669723SEd Tanous                 "Redfish log file: could not get unique entry id for {}",
79*7b669723SEd Tanous                 logEntry);
80*7b669723SEd Tanous             continue;
81*7b669723SEd Tanous         }
82*7b669723SEd Tanous 
83*7b669723SEd Tanous         std::string timestamp;
84*7b669723SEd Tanous         std::string messageID;
85*7b669723SEd Tanous         std::vector<std::string> messageArgs;
86*7b669723SEd Tanous         if (event_log::getEventLogParams(logEntry, timestamp, messageID,
87*7b669723SEd Tanous                                          messageArgs) != 0)
88*7b669723SEd Tanous         {
89*7b669723SEd Tanous             BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}",
90*7b669723SEd Tanous                              logEntry);
91*7b669723SEd Tanous             continue;
92*7b669723SEd Tanous         }
93*7b669723SEd Tanous 
94*7b669723SEd Tanous         eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs);
95*7b669723SEd Tanous     }
96*7b669723SEd Tanous 
97*7b669723SEd Tanous     if (eventRecords.empty())
98*7b669723SEd Tanous     {
99*7b669723SEd Tanous         // No Records to send
100*7b669723SEd Tanous         BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
101*7b669723SEd Tanous         return;
102*7b669723SEd Tanous     }
103*7b669723SEd Tanous     EventServiceManager::sendEventsToSubs(eventRecords);
104*7b669723SEd Tanous }
1052185ddeaSEd Tanous 
1062185ddeaSEd Tanous static constexpr const char* redfishEventLogDir = "/var/log";
1072185ddeaSEd Tanous static constexpr const size_t iEventSize = sizeof(inotify_event);
1082185ddeaSEd Tanous 
109*7b669723SEd Tanous void FilesystemLogWatcher::onINotify(const boost::system::error_code& ec,
110ea20bc66SEd Tanous                                      std::size_t bytesTransferred)
111ea20bc66SEd Tanous {
1122185ddeaSEd Tanous     if (ec == boost::asio::error::operation_aborted)
1132185ddeaSEd Tanous     {
1142185ddeaSEd Tanous         BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
1152185ddeaSEd Tanous         return;
1162185ddeaSEd Tanous     }
1172185ddeaSEd Tanous     if (ec)
1182185ddeaSEd Tanous     {
1192185ddeaSEd Tanous         BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
1202185ddeaSEd Tanous         return;
1212185ddeaSEd Tanous     }
1222185ddeaSEd Tanous 
1232185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
1242185ddeaSEd Tanous 
1252185ddeaSEd Tanous     std::size_t index = 0;
1262185ddeaSEd Tanous     while ((index + iEventSize) <= bytesTransferred)
1272185ddeaSEd Tanous     {
1282185ddeaSEd Tanous         struct inotify_event event
1292185ddeaSEd Tanous         {};
1302185ddeaSEd Tanous         std::memcpy(&event, &readBuffer[index], iEventSize);
1312185ddeaSEd Tanous         if (event.wd == dirWatchDesc)
1322185ddeaSEd Tanous         {
1332185ddeaSEd Tanous             if ((event.len == 0) ||
1342185ddeaSEd Tanous                 (index + iEventSize + event.len > bytesTransferred))
1352185ddeaSEd Tanous             {
1362185ddeaSEd Tanous                 index += (iEventSize + event.len);
1372185ddeaSEd Tanous                 continue;
1382185ddeaSEd Tanous             }
1392185ddeaSEd Tanous 
1402185ddeaSEd Tanous             std::string fileName(&readBuffer[index + iEventSize]);
1412185ddeaSEd Tanous             if (fileName != "redfish")
1422185ddeaSEd Tanous             {
1432185ddeaSEd Tanous                 index += (iEventSize + event.len);
1442185ddeaSEd Tanous                 continue;
1452185ddeaSEd Tanous             }
1462185ddeaSEd Tanous 
147ea20bc66SEd Tanous             BMCWEB_LOG_DEBUG("Redfish log file created/deleted. event.name: {}",
1482185ddeaSEd Tanous                              fileName);
1492185ddeaSEd Tanous             if (event.mask == IN_CREATE)
1502185ddeaSEd Tanous             {
1512185ddeaSEd Tanous                 if (fileWatchDesc != -1)
1522185ddeaSEd Tanous                 {
153ea20bc66SEd Tanous                     BMCWEB_LOG_DEBUG("Remove and Add inotify watcher on "
1542185ddeaSEd Tanous                                      "redfish event log file");
1552185ddeaSEd Tanous                     // Remove existing inotify watcher and add
1562185ddeaSEd Tanous                     // with new redfish event log file.
1572185ddeaSEd Tanous                     inotify_rm_watch(inotifyFd, fileWatchDesc);
1582185ddeaSEd Tanous                     fileWatchDesc = -1;
1592185ddeaSEd Tanous                 }
1602185ddeaSEd Tanous 
1612185ddeaSEd Tanous                 fileWatchDesc = inotify_add_watch(
1622185ddeaSEd Tanous                     inotifyFd, redfishEventLogFile, IN_MODIFY);
1632185ddeaSEd Tanous                 if (fileWatchDesc == -1)
1642185ddeaSEd Tanous                 {
1652185ddeaSEd Tanous                     BMCWEB_LOG_ERROR("inotify_add_watch failed for "
1662185ddeaSEd Tanous                                      "redfish log file.");
1672185ddeaSEd Tanous                     return;
1682185ddeaSEd Tanous                 }
1692185ddeaSEd Tanous 
170*7b669723SEd Tanous                 resetRedfishFilePosition();
171*7b669723SEd Tanous                 readEventLogsFromFile();
1722185ddeaSEd Tanous             }
173ea20bc66SEd Tanous             else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
1742185ddeaSEd Tanous             {
1752185ddeaSEd Tanous                 if (fileWatchDesc != -1)
1762185ddeaSEd Tanous                 {
1772185ddeaSEd Tanous                     inotify_rm_watch(inotifyFd, fileWatchDesc);
1782185ddeaSEd Tanous                     fileWatchDesc = -1;
1792185ddeaSEd Tanous                 }
1802185ddeaSEd Tanous             }
1812185ddeaSEd Tanous         }
1822185ddeaSEd Tanous         else if (event.wd == fileWatchDesc)
1832185ddeaSEd Tanous         {
1842185ddeaSEd Tanous             if (event.mask == IN_MODIFY)
1852185ddeaSEd Tanous             {
186*7b669723SEd Tanous                 readEventLogsFromFile();
1872185ddeaSEd Tanous             }
1882185ddeaSEd Tanous         }
1892185ddeaSEd Tanous         index += (iEventSize + event.len);
1902185ddeaSEd Tanous     }
1912185ddeaSEd Tanous 
1922185ddeaSEd Tanous     watchRedfishEventLogFile();
193ea20bc66SEd Tanous }
194ea20bc66SEd Tanous 
195*7b669723SEd Tanous void FilesystemLogWatcher::watchRedfishEventLogFile()
196ea20bc66SEd Tanous {
197*7b669723SEd Tanous     inotifyConn.async_read_some(
198*7b669723SEd Tanous         boost::asio::buffer(readBuffer),
199*7b669723SEd Tanous         std::bind_front(&FilesystemLogWatcher::onINotify, this));
200ea20bc66SEd Tanous }
201ea20bc66SEd Tanous 
202*7b669723SEd Tanous FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) :
203*7b669723SEd Tanous     inotifyFd(inotify_init1(IN_NONBLOCK)), inotifyConn(ioc)
2042185ddeaSEd Tanous {
2052185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
2062185ddeaSEd Tanous 
2072185ddeaSEd Tanous     if (inotifyFd == -1)
2082185ddeaSEd Tanous     {
2092185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_init1 failed.");
210*7b669723SEd Tanous         return;
2112185ddeaSEd Tanous     }
2122185ddeaSEd Tanous 
2132185ddeaSEd Tanous     // Add watch on directory to handle redfish event log file
2142185ddeaSEd Tanous     // create/delete.
2152185ddeaSEd Tanous     dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
2162185ddeaSEd Tanous                                      IN_CREATE | IN_MOVED_TO | IN_DELETE);
2172185ddeaSEd Tanous     if (dirWatchDesc == -1)
2182185ddeaSEd Tanous     {
2192185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
220*7b669723SEd Tanous         return;
2212185ddeaSEd Tanous     }
2222185ddeaSEd Tanous 
2232185ddeaSEd Tanous     // Watch redfish event log file for modifications.
2242185ddeaSEd Tanous     fileWatchDesc =
2252185ddeaSEd Tanous         inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
2262185ddeaSEd Tanous     if (fileWatchDesc == -1)
2272185ddeaSEd Tanous     {
2282185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
2292185ddeaSEd Tanous         // Don't return error if file not exist.
2302185ddeaSEd Tanous         // Watch on directory will handle create/delete of file.
2312185ddeaSEd Tanous     }
2322185ddeaSEd Tanous 
2332185ddeaSEd Tanous     // monitor redfish event log file
234*7b669723SEd Tanous     inotifyConn.assign(inotifyFd);
2352185ddeaSEd Tanous     watchRedfishEventLogFile();
2362185ddeaSEd Tanous 
237*7b669723SEd Tanous     if (redfishLogFilePosition != 0)
2382185ddeaSEd Tanous     {
239*7b669723SEd Tanous         cacheRedfishLogFile();
240*7b669723SEd Tanous     }
2412185ddeaSEd Tanous }
2422185ddeaSEd Tanous } // namespace redfish
243