12185ddeaSEd Tanous #include "filesystem_log_watcher.hpp" 22185ddeaSEd Tanous 3*7b669723SEd Tanous #include "event_log.hpp" 4*7b669723SEd 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> 18*7b669723SEd Tanous #include <fstream> 19*7b669723SEd Tanous #include <functional> 202185ddeaSEd Tanous #include <string> 21*7b669723SEd Tanous #include <vector> 222185ddeaSEd Tanous 232185ddeaSEd Tanous namespace redfish 242185ddeaSEd Tanous { 25*7b669723SEd Tanous void FilesystemLogWatcher::resetRedfishFilePosition() 26*7b669723SEd Tanous { 27*7b669723SEd Tanous // Control would be here when Redfish file is created. 28*7b669723SEd Tanous // Reset File Position as new file is created 29*7b669723SEd Tanous redfishLogFilePosition = 0; 30*7b669723SEd Tanous } 312185ddeaSEd Tanous 32*7b669723SEd Tanous void FilesystemLogWatcher::cacheRedfishLogFile() 33*7b669723SEd Tanous { 34*7b669723SEd Tanous // Open the redfish file and read till the last record. 35*7b669723SEd Tanous 36*7b669723SEd Tanous std::ifstream logStream(redfishEventLogFile); 37*7b669723SEd Tanous if (!logStream.good()) 38*7b669723SEd Tanous { 39*7b669723SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed "); 40*7b669723SEd Tanous return; 41*7b669723SEd Tanous } 42*7b669723SEd Tanous std::string logEntry; 43*7b669723SEd Tanous while (std::getline(logStream, logEntry)) 44*7b669723SEd Tanous { 45*7b669723SEd Tanous redfishLogFilePosition = logStream.tellg(); 46*7b669723SEd Tanous } 47*7b669723SEd Tanous } 48*7b669723SEd Tanous 49*7b669723SEd Tanous void FilesystemLogWatcher::readEventLogsFromFile() 50*7b669723SEd Tanous { 51*7b669723SEd Tanous std::ifstream logStream(redfishEventLogFile); 52*7b669723SEd Tanous if (!logStream.good()) 53*7b669723SEd Tanous { 54*7b669723SEd Tanous BMCWEB_LOG_ERROR(" Redfish log file open failed"); 55*7b669723SEd Tanous return; 56*7b669723SEd Tanous } 57*7b669723SEd Tanous 58*7b669723SEd Tanous std::vector<EventLogObjectsType> eventRecords; 59*7b669723SEd Tanous 60*7b669723SEd Tanous std::string logEntry; 61*7b669723SEd Tanous 62*7b669723SEd Tanous BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 63*7b669723SEd Tanous static_cast<int>(redfishLogFilePosition)); 64*7b669723SEd Tanous 65*7b669723SEd Tanous // Get the read pointer to the next log to be read. 66*7b669723SEd Tanous logStream.seekg(redfishLogFilePosition); 67*7b669723SEd Tanous 68*7b669723SEd Tanous while (std::getline(logStream, logEntry)) 69*7b669723SEd Tanous { 70*7b669723SEd Tanous BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 71*7b669723SEd Tanous // Update Pointer position 72*7b669723SEd Tanous redfishLogFilePosition = logStream.tellg(); 73*7b669723SEd Tanous 74*7b669723SEd Tanous std::string idStr; 75*7b669723SEd Tanous if (!event_log::getUniqueEntryID(logEntry, idStr)) 76*7b669723SEd Tanous { 77*7b669723SEd Tanous BMCWEB_LOG_DEBUG( 78*7b669723SEd Tanous "Redfish log file: could not get unique entry id for {}", 79*7b669723SEd Tanous logEntry); 80*7b669723SEd Tanous continue; 81*7b669723SEd Tanous } 82*7b669723SEd Tanous 83*7b669723SEd Tanous std::string timestamp; 84*7b669723SEd Tanous std::string messageID; 85*7b669723SEd Tanous std::vector<std::string> messageArgs; 86*7b669723SEd Tanous if (event_log::getEventLogParams(logEntry, timestamp, messageID, 87*7b669723SEd Tanous messageArgs) != 0) 88*7b669723SEd Tanous { 89*7b669723SEd Tanous BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 90*7b669723SEd Tanous logEntry); 91*7b669723SEd Tanous continue; 92*7b669723SEd Tanous } 93*7b669723SEd Tanous 94*7b669723SEd Tanous eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 95*7b669723SEd Tanous } 96*7b669723SEd Tanous 97*7b669723SEd Tanous if (eventRecords.empty()) 98*7b669723SEd Tanous { 99*7b669723SEd Tanous // No Records to send 100*7b669723SEd Tanous BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 101*7b669723SEd Tanous return; 102*7b669723SEd Tanous } 103*7b669723SEd Tanous EventServiceManager::sendEventsToSubs(eventRecords); 104*7b669723SEd 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 109*7b669723SEd 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 { 1282185ddeaSEd Tanous struct inotify_event event 1292185ddeaSEd Tanous {}; 1302185ddeaSEd Tanous std::memcpy(&event, &readBuffer[index], iEventSize); 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. 1572185ddeaSEd Tanous inotify_rm_watch(inotifyFd, fileWatchDesc); 1582185ddeaSEd Tanous fileWatchDesc = -1; 1592185ddeaSEd Tanous } 1602185ddeaSEd Tanous 1612185ddeaSEd Tanous fileWatchDesc = inotify_add_watch( 1622185ddeaSEd Tanous inotifyFd, redfishEventLogFile, IN_MODIFY); 1632185ddeaSEd Tanous if (fileWatchDesc == -1) 1642185ddeaSEd Tanous { 1652185ddeaSEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for " 1662185ddeaSEd Tanous "redfish log file."); 1672185ddeaSEd Tanous return; 1682185ddeaSEd Tanous } 1692185ddeaSEd Tanous 170*7b669723SEd Tanous resetRedfishFilePosition(); 171*7b669723SEd Tanous readEventLogsFromFile(); 1722185ddeaSEd Tanous } 173ea20bc66SEd Tanous else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO)) 1742185ddeaSEd Tanous { 1752185ddeaSEd Tanous if (fileWatchDesc != -1) 1762185ddeaSEd Tanous { 1772185ddeaSEd Tanous inotify_rm_watch(inotifyFd, fileWatchDesc); 1782185ddeaSEd Tanous fileWatchDesc = -1; 1792185ddeaSEd Tanous } 1802185ddeaSEd Tanous } 1812185ddeaSEd Tanous } 1822185ddeaSEd Tanous else if (event.wd == fileWatchDesc) 1832185ddeaSEd Tanous { 1842185ddeaSEd Tanous if (event.mask == IN_MODIFY) 1852185ddeaSEd Tanous { 186*7b669723SEd Tanous readEventLogsFromFile(); 1872185ddeaSEd Tanous } 1882185ddeaSEd Tanous } 1892185ddeaSEd Tanous index += (iEventSize + event.len); 1902185ddeaSEd Tanous } 1912185ddeaSEd Tanous 1922185ddeaSEd Tanous watchRedfishEventLogFile(); 193ea20bc66SEd Tanous } 194ea20bc66SEd Tanous 195*7b669723SEd Tanous void FilesystemLogWatcher::watchRedfishEventLogFile() 196ea20bc66SEd Tanous { 197*7b669723SEd Tanous inotifyConn.async_read_some( 198*7b669723SEd Tanous boost::asio::buffer(readBuffer), 199*7b669723SEd Tanous std::bind_front(&FilesystemLogWatcher::onINotify, this)); 200ea20bc66SEd Tanous } 201ea20bc66SEd Tanous 202*7b669723SEd Tanous FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) : 203*7b669723SEd Tanous inotifyFd(inotify_init1(IN_NONBLOCK)), inotifyConn(ioc) 2042185ddeaSEd Tanous { 2052185ddeaSEd Tanous BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 2062185ddeaSEd Tanous 2072185ddeaSEd Tanous if (inotifyFd == -1) 2082185ddeaSEd Tanous { 2092185ddeaSEd Tanous BMCWEB_LOG_ERROR("inotify_init1 failed."); 210*7b669723SEd Tanous return; 2112185ddeaSEd Tanous } 2122185ddeaSEd Tanous 2132185ddeaSEd Tanous // Add watch on directory to handle redfish event log file 2142185ddeaSEd Tanous // create/delete. 2152185ddeaSEd Tanous dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 2162185ddeaSEd Tanous IN_CREATE | IN_MOVED_TO | IN_DELETE); 2172185ddeaSEd Tanous if (dirWatchDesc == -1) 2182185ddeaSEd Tanous { 2192185ddeaSEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory."); 220*7b669723SEd Tanous return; 2212185ddeaSEd Tanous } 2222185ddeaSEd Tanous 2232185ddeaSEd Tanous // Watch redfish event log file for modifications. 2242185ddeaSEd Tanous fileWatchDesc = 2252185ddeaSEd Tanous inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 2262185ddeaSEd Tanous if (fileWatchDesc == -1) 2272185ddeaSEd Tanous { 2282185ddeaSEd Tanous BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 2292185ddeaSEd Tanous // Don't return error if file not exist. 2302185ddeaSEd Tanous // Watch on directory will handle create/delete of file. 2312185ddeaSEd Tanous } 2322185ddeaSEd Tanous 2332185ddeaSEd Tanous // monitor redfish event log file 234*7b669723SEd Tanous inotifyConn.assign(inotifyFd); 2352185ddeaSEd Tanous watchRedfishEventLogFile(); 2362185ddeaSEd Tanous 237*7b669723SEd Tanous if (redfishLogFilePosition != 0) 2382185ddeaSEd Tanous { 239*7b669723SEd Tanous cacheRedfishLogFile(); 240*7b669723SEd Tanous } 2412185ddeaSEd Tanous } 2422185ddeaSEd Tanous } // namespace redfish 243