xref: /openbmc/bmcweb/features/redfish/src/filesystem_log_watcher.cpp (revision 0f441f09144154dbe83747edf2c267cb3e3e7543)
12185ddeaSEd Tanous #include "filesystem_log_watcher.hpp"
22185ddeaSEd Tanous 
37b669723SEd Tanous #include "event_log.hpp"
47b669723SEd 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>
187b669723SEd Tanous #include <fstream>
197b669723SEd Tanous #include <functional>
202185ddeaSEd Tanous #include <string>
217b669723SEd Tanous #include <vector>
222185ddeaSEd Tanous 
232185ddeaSEd Tanous namespace redfish
242185ddeaSEd Tanous {
257b669723SEd Tanous void FilesystemLogWatcher::resetRedfishFilePosition()
267b669723SEd Tanous {
277b669723SEd Tanous     // Control would be here when Redfish file is created.
287b669723SEd Tanous     // Reset File Position as new file is created
297b669723SEd Tanous     redfishLogFilePosition = 0;
307b669723SEd Tanous }
312185ddeaSEd Tanous 
327b669723SEd Tanous void FilesystemLogWatcher::cacheRedfishLogFile()
337b669723SEd Tanous {
347b669723SEd Tanous     // Open the redfish file and read till the last record.
357b669723SEd Tanous 
367b669723SEd Tanous     std::ifstream logStream(redfishEventLogFile);
377b669723SEd Tanous     if (!logStream.good())
387b669723SEd Tanous     {
397b669723SEd Tanous         BMCWEB_LOG_ERROR(" Redfish log file open failed ");
407b669723SEd Tanous         return;
417b669723SEd Tanous     }
427b669723SEd Tanous     std::string logEntry;
437b669723SEd Tanous     while (std::getline(logStream, logEntry))
447b669723SEd Tanous     {
457b669723SEd Tanous         redfishLogFilePosition = logStream.tellg();
467b669723SEd Tanous     }
477b669723SEd Tanous }
487b669723SEd Tanous 
497b669723SEd Tanous void FilesystemLogWatcher::readEventLogsFromFile()
507b669723SEd Tanous {
517b669723SEd Tanous     std::ifstream logStream(redfishEventLogFile);
527b669723SEd Tanous     if (!logStream.good())
537b669723SEd Tanous     {
547b669723SEd Tanous         BMCWEB_LOG_ERROR(" Redfish log file open failed");
557b669723SEd Tanous         return;
567b669723SEd Tanous     }
577b669723SEd Tanous 
587b669723SEd Tanous     std::vector<EventLogObjectsType> eventRecords;
597b669723SEd Tanous 
607b669723SEd Tanous     std::string logEntry;
617b669723SEd Tanous 
627b669723SEd Tanous     BMCWEB_LOG_DEBUG("Redfish log file: seek to {}",
637b669723SEd Tanous                      static_cast<int>(redfishLogFilePosition));
647b669723SEd Tanous 
657b669723SEd Tanous     // Get the read pointer to the next log to be read.
667b669723SEd Tanous     logStream.seekg(redfishLogFilePosition);
677b669723SEd Tanous 
687b669723SEd Tanous     while (std::getline(logStream, logEntry))
697b669723SEd Tanous     {
707b669723SEd Tanous         BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry");
717b669723SEd Tanous         // Update Pointer position
727b669723SEd Tanous         redfishLogFilePosition = logStream.tellg();
737b669723SEd Tanous 
747b669723SEd Tanous         std::string idStr;
757b669723SEd Tanous         if (!event_log::getUniqueEntryID(logEntry, idStr))
767b669723SEd Tanous         {
777b669723SEd Tanous             BMCWEB_LOG_DEBUG(
787b669723SEd Tanous                 "Redfish log file: could not get unique entry id for {}",
797b669723SEd Tanous                 logEntry);
807b669723SEd Tanous             continue;
817b669723SEd Tanous         }
827b669723SEd Tanous 
837b669723SEd Tanous         std::string timestamp;
847b669723SEd Tanous         std::string messageID;
857b669723SEd Tanous         std::vector<std::string> messageArgs;
867b669723SEd Tanous         if (event_log::getEventLogParams(logEntry, timestamp, messageID,
877b669723SEd Tanous                                          messageArgs) != 0)
887b669723SEd Tanous         {
897b669723SEd Tanous             BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}",
907b669723SEd Tanous                              logEntry);
917b669723SEd Tanous             continue;
927b669723SEd Tanous         }
937b669723SEd Tanous 
947b669723SEd Tanous         eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs);
957b669723SEd Tanous     }
967b669723SEd Tanous 
977b669723SEd Tanous     if (eventRecords.empty())
987b669723SEd Tanous     {
997b669723SEd Tanous         // No Records to send
1007b669723SEd Tanous         BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
1017b669723SEd Tanous         return;
1027b669723SEd Tanous     }
1037b669723SEd Tanous     EventServiceManager::sendEventsToSubs(eventRecords);
1047b669723SEd 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 
1097b669723SEd 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     {
128*0f441f09SEd Tanous         struct inotify_event event{};
1292185ddeaSEd Tanous         std::memcpy(&event, &readBuffer[index], iEventSize);
1302185ddeaSEd Tanous         if (event.wd == dirWatchDesc)
1312185ddeaSEd Tanous         {
1322185ddeaSEd Tanous             if ((event.len == 0) ||
1332185ddeaSEd Tanous                 (index + iEventSize + event.len > bytesTransferred))
1342185ddeaSEd Tanous             {
1352185ddeaSEd Tanous                 index += (iEventSize + event.len);
1362185ddeaSEd Tanous                 continue;
1372185ddeaSEd Tanous             }
1382185ddeaSEd Tanous 
1392185ddeaSEd Tanous             std::string fileName(&readBuffer[index + iEventSize]);
1402185ddeaSEd Tanous             if (fileName != "redfish")
1412185ddeaSEd Tanous             {
1422185ddeaSEd Tanous                 index += (iEventSize + event.len);
1432185ddeaSEd Tanous                 continue;
1442185ddeaSEd Tanous             }
1452185ddeaSEd Tanous 
146ea20bc66SEd Tanous             BMCWEB_LOG_DEBUG("Redfish log file created/deleted. event.name: {}",
1472185ddeaSEd Tanous                              fileName);
1482185ddeaSEd Tanous             if (event.mask == IN_CREATE)
1492185ddeaSEd Tanous             {
1502185ddeaSEd Tanous                 if (fileWatchDesc != -1)
1512185ddeaSEd Tanous                 {
152ea20bc66SEd Tanous                     BMCWEB_LOG_DEBUG("Remove and Add inotify watcher on "
1532185ddeaSEd Tanous                                      "redfish event log file");
1542185ddeaSEd Tanous                     // Remove existing inotify watcher and add
1552185ddeaSEd Tanous                     // with new redfish event log file.
1562185ddeaSEd Tanous                     inotify_rm_watch(inotifyFd, fileWatchDesc);
1572185ddeaSEd Tanous                     fileWatchDesc = -1;
1582185ddeaSEd Tanous                 }
1592185ddeaSEd Tanous 
1602185ddeaSEd Tanous                 fileWatchDesc = inotify_add_watch(
1612185ddeaSEd Tanous                     inotifyFd, redfishEventLogFile, IN_MODIFY);
1622185ddeaSEd Tanous                 if (fileWatchDesc == -1)
1632185ddeaSEd Tanous                 {
1642185ddeaSEd Tanous                     BMCWEB_LOG_ERROR("inotify_add_watch failed for "
1652185ddeaSEd Tanous                                      "redfish log file.");
1662185ddeaSEd Tanous                     return;
1672185ddeaSEd Tanous                 }
1682185ddeaSEd Tanous 
1697b669723SEd Tanous                 resetRedfishFilePosition();
1707b669723SEd Tanous                 readEventLogsFromFile();
1712185ddeaSEd Tanous             }
172ea20bc66SEd Tanous             else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
1732185ddeaSEd Tanous             {
1742185ddeaSEd Tanous                 if (fileWatchDesc != -1)
1752185ddeaSEd Tanous                 {
1762185ddeaSEd Tanous                     inotify_rm_watch(inotifyFd, fileWatchDesc);
1772185ddeaSEd Tanous                     fileWatchDesc = -1;
1782185ddeaSEd Tanous                 }
1792185ddeaSEd Tanous             }
1802185ddeaSEd Tanous         }
1812185ddeaSEd Tanous         else if (event.wd == fileWatchDesc)
1822185ddeaSEd Tanous         {
1832185ddeaSEd Tanous             if (event.mask == IN_MODIFY)
1842185ddeaSEd Tanous             {
1857b669723SEd Tanous                 readEventLogsFromFile();
1862185ddeaSEd Tanous             }
1872185ddeaSEd Tanous         }
1882185ddeaSEd Tanous         index += (iEventSize + event.len);
1892185ddeaSEd Tanous     }
1902185ddeaSEd Tanous 
1912185ddeaSEd Tanous     watchRedfishEventLogFile();
192ea20bc66SEd Tanous }
193ea20bc66SEd Tanous 
1947b669723SEd Tanous void FilesystemLogWatcher::watchRedfishEventLogFile()
195ea20bc66SEd Tanous {
1967b669723SEd Tanous     inotifyConn.async_read_some(
1977b669723SEd Tanous         boost::asio::buffer(readBuffer),
1987b669723SEd Tanous         std::bind_front(&FilesystemLogWatcher::onINotify, this));
199ea20bc66SEd Tanous }
200ea20bc66SEd Tanous 
2017b669723SEd Tanous FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) :
2027b669723SEd Tanous     inotifyFd(inotify_init1(IN_NONBLOCK)), inotifyConn(ioc)
2032185ddeaSEd Tanous {
2042185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
2052185ddeaSEd Tanous 
2062185ddeaSEd Tanous     if (inotifyFd == -1)
2072185ddeaSEd Tanous     {
2082185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_init1 failed.");
2097b669723SEd Tanous         return;
2102185ddeaSEd Tanous     }
2112185ddeaSEd Tanous 
2122185ddeaSEd Tanous     // Add watch on directory to handle redfish event log file
2132185ddeaSEd Tanous     // create/delete.
2142185ddeaSEd Tanous     dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir,
2152185ddeaSEd Tanous                                      IN_CREATE | IN_MOVED_TO | IN_DELETE);
2162185ddeaSEd Tanous     if (dirWatchDesc == -1)
2172185ddeaSEd Tanous     {
2182185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
2197b669723SEd Tanous         return;
2202185ddeaSEd Tanous     }
2212185ddeaSEd Tanous 
2222185ddeaSEd Tanous     // Watch redfish event log file for modifications.
2232185ddeaSEd Tanous     fileWatchDesc =
2242185ddeaSEd Tanous         inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY);
2252185ddeaSEd Tanous     if (fileWatchDesc == -1)
2262185ddeaSEd Tanous     {
2272185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
2282185ddeaSEd Tanous         // Don't return error if file not exist.
2292185ddeaSEd Tanous         // Watch on directory will handle create/delete of file.
2302185ddeaSEd Tanous     }
2312185ddeaSEd Tanous 
2322185ddeaSEd Tanous     // monitor redfish event log file
2337b669723SEd Tanous     inotifyConn.assign(inotifyFd);
2342185ddeaSEd Tanous     watchRedfishEventLogFile();
2352185ddeaSEd Tanous 
2367b669723SEd Tanous     if (redfishLogFilePosition != 0)
2372185ddeaSEd Tanous     {
2387b669723SEd Tanous         cacheRedfishLogFile();
2397b669723SEd Tanous     }
2402185ddeaSEd Tanous }
2412185ddeaSEd Tanous } // namespace redfish
242