xref: /openbmc/bmcweb/features/redfish/src/filesystem_log_watcher.cpp (revision fda37f9b6d12ff0852cba4dcdecfd633436e6152)
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     {
1280f441f09SEd 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.
156*fda37f9bSEd Tanous                     inotify_rm_watch(inotifyConn.native_handle(),
157*fda37f9bSEd Tanous                                      fileWatchDesc);
1582185ddeaSEd Tanous                     fileWatchDesc = -1;
1592185ddeaSEd Tanous                 }
1602185ddeaSEd Tanous 
161*fda37f9bSEd Tanous                 fileWatchDesc =
162*fda37f9bSEd Tanous                     inotify_add_watch(inotifyConn.native_handle(),
163*fda37f9bSEd Tanous                                       redfishEventLogFile, IN_MODIFY);
1642185ddeaSEd Tanous                 if (fileWatchDesc == -1)
1652185ddeaSEd Tanous                 {
1662185ddeaSEd Tanous                     BMCWEB_LOG_ERROR("inotify_add_watch failed for "
1672185ddeaSEd Tanous                                      "redfish log file.");
1682185ddeaSEd Tanous                     return;
1692185ddeaSEd Tanous                 }
1702185ddeaSEd Tanous 
1717b669723SEd Tanous                 resetRedfishFilePosition();
1727b669723SEd Tanous                 readEventLogsFromFile();
1732185ddeaSEd Tanous             }
174ea20bc66SEd Tanous             else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
1752185ddeaSEd Tanous             {
1762185ddeaSEd Tanous                 if (fileWatchDesc != -1)
1772185ddeaSEd Tanous                 {
178*fda37f9bSEd Tanous                     inotify_rm_watch(inotifyConn.native_handle(),
179*fda37f9bSEd Tanous                                      fileWatchDesc);
1802185ddeaSEd Tanous                     fileWatchDesc = -1;
1812185ddeaSEd Tanous                 }
1822185ddeaSEd Tanous             }
1832185ddeaSEd Tanous         }
1842185ddeaSEd Tanous         else if (event.wd == fileWatchDesc)
1852185ddeaSEd Tanous         {
1862185ddeaSEd Tanous             if (event.mask == IN_MODIFY)
1872185ddeaSEd Tanous             {
1887b669723SEd Tanous                 readEventLogsFromFile();
1892185ddeaSEd Tanous             }
1902185ddeaSEd Tanous         }
1912185ddeaSEd Tanous         index += (iEventSize + event.len);
1922185ddeaSEd Tanous     }
1932185ddeaSEd Tanous 
1942185ddeaSEd Tanous     watchRedfishEventLogFile();
195ea20bc66SEd Tanous }
196ea20bc66SEd Tanous 
1977b669723SEd Tanous void FilesystemLogWatcher::watchRedfishEventLogFile()
198ea20bc66SEd Tanous {
1997b669723SEd Tanous     inotifyConn.async_read_some(
2007b669723SEd Tanous         boost::asio::buffer(readBuffer),
2017b669723SEd Tanous         std::bind_front(&FilesystemLogWatcher::onINotify, this));
202ea20bc66SEd Tanous }
203ea20bc66SEd Tanous 
2047b669723SEd Tanous FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) :
205*fda37f9bSEd Tanous     inotifyConn(ioc)
2062185ddeaSEd Tanous {
2072185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
2082185ddeaSEd Tanous 
209*fda37f9bSEd Tanous     int inotifyFd = inotify_init1(IN_NONBLOCK);
210*fda37f9bSEd Tanous     if (inotifyFd < 0)
2112185ddeaSEd Tanous     {
2122185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_init1 failed.");
2137b669723SEd Tanous         return;
2142185ddeaSEd Tanous     }
215*fda37f9bSEd Tanous     boost::system::error_code ec;
216*fda37f9bSEd Tanous     inotifyConn.assign(inotifyFd, ec);
217*fda37f9bSEd Tanous     if (ec)
218*fda37f9bSEd Tanous     {
219*fda37f9bSEd Tanous         BMCWEB_LOG_ERROR("Failed to assign fd {}", ec.message());
220*fda37f9bSEd Tanous         return;
221*fda37f9bSEd Tanous     }
2222185ddeaSEd Tanous 
2232185ddeaSEd Tanous     // Add watch on directory to handle redfish event log file
2242185ddeaSEd Tanous     // create/delete.
225*fda37f9bSEd Tanous     dirWatchDesc =
226*fda37f9bSEd Tanous         inotify_add_watch(inotifyConn.native_handle(), redfishEventLogDir,
2272185ddeaSEd Tanous                           IN_CREATE | IN_MOVED_TO | IN_DELETE);
228*fda37f9bSEd Tanous     if (dirWatchDesc < 0)
2292185ddeaSEd Tanous     {
2302185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
2317b669723SEd Tanous         return;
2322185ddeaSEd Tanous     }
2332185ddeaSEd Tanous 
2342185ddeaSEd Tanous     // Watch redfish event log file for modifications.
235*fda37f9bSEd Tanous     fileWatchDesc = inotify_add_watch(inotifyConn.native_handle(),
236*fda37f9bSEd Tanous                                       redfishEventLogFile, IN_MODIFY);
237*fda37f9bSEd Tanous     if (fileWatchDesc < 0)
2382185ddeaSEd Tanous     {
2392185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
2402185ddeaSEd Tanous         // Don't return error if file not exist.
2412185ddeaSEd Tanous         // Watch on directory will handle create/delete of file.
2422185ddeaSEd Tanous     }
2432185ddeaSEd Tanous 
2442185ddeaSEd Tanous     // monitor redfish event log file
2452185ddeaSEd Tanous     watchRedfishEventLogFile();
2462185ddeaSEd Tanous 
2477b669723SEd Tanous     if (redfishLogFilePosition != 0)
2482185ddeaSEd Tanous     {
2497b669723SEd Tanous         cacheRedfishLogFile();
2507b669723SEd Tanous     }
2512185ddeaSEd Tanous }
2522185ddeaSEd Tanous } // namespace redfish
253