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