xref: /openbmc/bmcweb/features/redfish/src/filesystem_log_watcher.cpp (revision bb456a6d35c1e99b882dd286d689a6b711d9af87)
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>
16*bb456a6dSEd Tanous #include <bit>
172185ddeaSEd Tanous #include <cstddef>
182185ddeaSEd Tanous #include <cstring>
197b669723SEd Tanous #include <fstream>
207b669723SEd Tanous #include <functional>
212185ddeaSEd Tanous #include <string>
227b669723SEd Tanous #include <vector>
232185ddeaSEd Tanous 
242185ddeaSEd Tanous namespace redfish
252185ddeaSEd Tanous {
267b669723SEd Tanous void FilesystemLogWatcher::resetRedfishFilePosition()
277b669723SEd Tanous {
287b669723SEd Tanous     // Control would be here when Redfish file is created.
297b669723SEd Tanous     // Reset File Position as new file is created
307b669723SEd Tanous     redfishLogFilePosition = 0;
317b669723SEd Tanous }
322185ddeaSEd Tanous 
337b669723SEd Tanous void FilesystemLogWatcher::cacheRedfishLogFile()
347b669723SEd Tanous {
357b669723SEd Tanous     // Open the redfish file and read till the last record.
367b669723SEd Tanous 
377b669723SEd Tanous     std::ifstream logStream(redfishEventLogFile);
387b669723SEd Tanous     if (!logStream.good())
397b669723SEd Tanous     {
407b669723SEd Tanous         BMCWEB_LOG_ERROR(" Redfish log file open failed ");
417b669723SEd Tanous         return;
427b669723SEd Tanous     }
437b669723SEd Tanous     std::string logEntry;
447b669723SEd Tanous     while (std::getline(logStream, logEntry))
457b669723SEd Tanous     {
467b669723SEd Tanous         redfishLogFilePosition = logStream.tellg();
477b669723SEd Tanous     }
487b669723SEd Tanous }
497b669723SEd Tanous 
507b669723SEd Tanous void FilesystemLogWatcher::readEventLogsFromFile()
517b669723SEd Tanous {
527b669723SEd Tanous     std::ifstream logStream(redfishEventLogFile);
537b669723SEd Tanous     if (!logStream.good())
547b669723SEd Tanous     {
557b669723SEd Tanous         BMCWEB_LOG_ERROR(" Redfish log file open failed");
567b669723SEd Tanous         return;
577b669723SEd Tanous     }
587b669723SEd Tanous 
597b669723SEd Tanous     std::vector<EventLogObjectsType> eventRecords;
607b669723SEd Tanous 
617b669723SEd Tanous     std::string logEntry;
627b669723SEd Tanous 
637b669723SEd Tanous     BMCWEB_LOG_DEBUG("Redfish log file: seek to {}",
647b669723SEd Tanous                      static_cast<int>(redfishLogFilePosition));
657b669723SEd Tanous 
667b669723SEd Tanous     // Get the read pointer to the next log to be read.
677b669723SEd Tanous     logStream.seekg(redfishLogFilePosition);
687b669723SEd Tanous 
697b669723SEd Tanous     while (std::getline(logStream, logEntry))
707b669723SEd Tanous     {
717b669723SEd Tanous         BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry");
727b669723SEd Tanous         // Update Pointer position
737b669723SEd Tanous         redfishLogFilePosition = logStream.tellg();
747b669723SEd Tanous 
757b669723SEd Tanous         std::string idStr;
767b669723SEd Tanous         if (!event_log::getUniqueEntryID(logEntry, idStr))
777b669723SEd Tanous         {
787b669723SEd Tanous             BMCWEB_LOG_DEBUG(
797b669723SEd Tanous                 "Redfish log file: could not get unique entry id for {}",
807b669723SEd Tanous                 logEntry);
817b669723SEd Tanous             continue;
827b669723SEd Tanous         }
837b669723SEd Tanous 
847b669723SEd Tanous         std::string timestamp;
857b669723SEd Tanous         std::string messageID;
867b669723SEd Tanous         std::vector<std::string> messageArgs;
877b669723SEd Tanous         if (event_log::getEventLogParams(logEntry, timestamp, messageID,
887b669723SEd Tanous                                          messageArgs) != 0)
897b669723SEd Tanous         {
907b669723SEd Tanous             BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}",
917b669723SEd Tanous                              logEntry);
927b669723SEd Tanous             continue;
937b669723SEd Tanous         }
947b669723SEd Tanous 
957b669723SEd Tanous         eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs);
967b669723SEd Tanous     }
977b669723SEd Tanous 
987b669723SEd Tanous     if (eventRecords.empty())
997b669723SEd Tanous     {
1007b669723SEd Tanous         // No Records to send
1017b669723SEd Tanous         BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
1027b669723SEd Tanous         return;
1037b669723SEd Tanous     }
1047b669723SEd Tanous     EventServiceManager::sendEventsToSubs(eventRecords);
1057b669723SEd Tanous }
1062185ddeaSEd Tanous 
1072185ddeaSEd Tanous static constexpr const char* redfishEventLogDir = "/var/log";
1082185ddeaSEd Tanous static constexpr const size_t iEventSize = sizeof(inotify_event);
1092185ddeaSEd Tanous 
1107b669723SEd Tanous void FilesystemLogWatcher::onINotify(const boost::system::error_code& ec,
111ea20bc66SEd Tanous                                      std::size_t bytesTransferred)
112ea20bc66SEd Tanous {
1132185ddeaSEd Tanous     if (ec == boost::asio::error::operation_aborted)
1142185ddeaSEd Tanous     {
1152185ddeaSEd Tanous         BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)");
1162185ddeaSEd Tanous         return;
1172185ddeaSEd Tanous     }
1182185ddeaSEd Tanous     if (ec)
1192185ddeaSEd Tanous     {
1202185ddeaSEd Tanous         BMCWEB_LOG_ERROR("Callback Error: {}", ec.message());
1212185ddeaSEd Tanous         return;
1222185ddeaSEd Tanous     }
1232185ddeaSEd Tanous 
1242185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred);
1252185ddeaSEd Tanous 
1262185ddeaSEd Tanous     std::size_t index = 0;
1272185ddeaSEd Tanous     while ((index + iEventSize) <= bytesTransferred)
1282185ddeaSEd Tanous     {
129*bb456a6dSEd Tanous         struct inotify_event& event =
130*bb456a6dSEd Tanous             *std::bit_cast<struct inotify_event*>(&readBuffer[index]);
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.
157fda37f9bSEd Tanous                     inotify_rm_watch(inotifyConn.native_handle(),
158fda37f9bSEd Tanous                                      fileWatchDesc);
1592185ddeaSEd Tanous                     fileWatchDesc = -1;
1602185ddeaSEd Tanous                 }
1612185ddeaSEd Tanous 
162fda37f9bSEd Tanous                 fileWatchDesc =
163fda37f9bSEd Tanous                     inotify_add_watch(inotifyConn.native_handle(),
164fda37f9bSEd Tanous                                       redfishEventLogFile, IN_MODIFY);
1652185ddeaSEd Tanous                 if (fileWatchDesc == -1)
1662185ddeaSEd Tanous                 {
1672185ddeaSEd Tanous                     BMCWEB_LOG_ERROR("inotify_add_watch failed for "
1682185ddeaSEd Tanous                                      "redfish log file.");
1692185ddeaSEd Tanous                     return;
1702185ddeaSEd Tanous                 }
1712185ddeaSEd Tanous 
1727b669723SEd Tanous                 resetRedfishFilePosition();
1737b669723SEd Tanous                 readEventLogsFromFile();
1742185ddeaSEd Tanous             }
175ea20bc66SEd Tanous             else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO))
1762185ddeaSEd Tanous             {
1772185ddeaSEd Tanous                 if (fileWatchDesc != -1)
1782185ddeaSEd Tanous                 {
179fda37f9bSEd Tanous                     inotify_rm_watch(inotifyConn.native_handle(),
180fda37f9bSEd Tanous                                      fileWatchDesc);
1812185ddeaSEd Tanous                     fileWatchDesc = -1;
1822185ddeaSEd Tanous                 }
1832185ddeaSEd Tanous             }
1842185ddeaSEd Tanous         }
1852185ddeaSEd Tanous         else if (event.wd == fileWatchDesc)
1862185ddeaSEd Tanous         {
1872185ddeaSEd Tanous             if (event.mask == IN_MODIFY)
1882185ddeaSEd Tanous             {
1897b669723SEd Tanous                 readEventLogsFromFile();
1902185ddeaSEd Tanous             }
1912185ddeaSEd Tanous         }
1922185ddeaSEd Tanous         index += (iEventSize + event.len);
1932185ddeaSEd Tanous     }
1942185ddeaSEd Tanous 
1952185ddeaSEd Tanous     watchRedfishEventLogFile();
196ea20bc66SEd Tanous }
197ea20bc66SEd Tanous 
1987b669723SEd Tanous void FilesystemLogWatcher::watchRedfishEventLogFile()
199ea20bc66SEd Tanous {
2007b669723SEd Tanous     inotifyConn.async_read_some(
2017b669723SEd Tanous         boost::asio::buffer(readBuffer),
2027b669723SEd Tanous         std::bind_front(&FilesystemLogWatcher::onINotify, this));
203ea20bc66SEd Tanous }
204ea20bc66SEd Tanous 
2057b669723SEd Tanous FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) :
206fda37f9bSEd Tanous     inotifyConn(ioc)
2072185ddeaSEd Tanous {
2082185ddeaSEd Tanous     BMCWEB_LOG_DEBUG("starting Event Log Monitor");
2092185ddeaSEd Tanous 
210fda37f9bSEd Tanous     int inotifyFd = inotify_init1(IN_NONBLOCK);
211fda37f9bSEd Tanous     if (inotifyFd < 0)
2122185ddeaSEd Tanous     {
2132185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_init1 failed.");
2147b669723SEd Tanous         return;
2152185ddeaSEd Tanous     }
216fda37f9bSEd Tanous     boost::system::error_code ec;
217fda37f9bSEd Tanous     inotifyConn.assign(inotifyFd, ec);
218fda37f9bSEd Tanous     if (ec)
219fda37f9bSEd Tanous     {
220fda37f9bSEd Tanous         BMCWEB_LOG_ERROR("Failed to assign fd {}", ec.message());
221fda37f9bSEd Tanous         return;
222fda37f9bSEd Tanous     }
2232185ddeaSEd Tanous 
2242185ddeaSEd Tanous     // Add watch on directory to handle redfish event log file
2252185ddeaSEd Tanous     // create/delete.
226fda37f9bSEd Tanous     dirWatchDesc =
227fda37f9bSEd Tanous         inotify_add_watch(inotifyConn.native_handle(), redfishEventLogDir,
2282185ddeaSEd Tanous                           IN_CREATE | IN_MOVED_TO | IN_DELETE);
229fda37f9bSEd Tanous     if (dirWatchDesc < 0)
2302185ddeaSEd Tanous     {
2312185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory.");
2327b669723SEd Tanous         return;
2332185ddeaSEd Tanous     }
2342185ddeaSEd Tanous 
2352185ddeaSEd Tanous     // Watch redfish event log file for modifications.
236fda37f9bSEd Tanous     fileWatchDesc = inotify_add_watch(inotifyConn.native_handle(),
237fda37f9bSEd Tanous                                       redfishEventLogFile, IN_MODIFY);
238fda37f9bSEd Tanous     if (fileWatchDesc < 0)
2392185ddeaSEd Tanous     {
2402185ddeaSEd Tanous         BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file.");
2412185ddeaSEd Tanous         // Don't return error if file not exist.
2422185ddeaSEd Tanous         // Watch on directory will handle create/delete of file.
2432185ddeaSEd Tanous     }
2442185ddeaSEd Tanous 
2452185ddeaSEd Tanous     // monitor redfish event log file
2462185ddeaSEd Tanous     watchRedfishEventLogFile();
2472185ddeaSEd Tanous 
2487b669723SEd Tanous     if (redfishLogFilePosition != 0)
2492185ddeaSEd Tanous     {
2507b669723SEd Tanous         cacheRedfishLogFile();
2517b669723SEd Tanous     }
2522185ddeaSEd Tanous }
2532185ddeaSEd Tanous } // namespace redfish
254