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