1 #include "filesystem_log_watcher.hpp" 2 3 #include "event_log.hpp" 4 #include "event_logs_object_type.hpp" 5 #include "event_service_manager.hpp" 6 #include "logging.hpp" 7 8 #include <sys/inotify.h> 9 10 #include <boost/asio/buffer.hpp> 11 #include <boost/asio/error.hpp> 12 #include <boost/asio/io_context.hpp> 13 #include <boost/asio/posix/stream_descriptor.hpp> 14 15 #include <array> 16 #include <cstddef> 17 #include <cstring> 18 #include <fstream> 19 #include <functional> 20 #include <string> 21 #include <vector> 22 23 namespace redfish 24 { 25 void FilesystemLogWatcher::resetRedfishFilePosition() 26 { 27 // Control would be here when Redfish file is created. 28 // Reset File Position as new file is created 29 redfishLogFilePosition = 0; 30 } 31 32 void FilesystemLogWatcher::cacheRedfishLogFile() 33 { 34 // Open the redfish file and read till the last record. 35 36 std::ifstream logStream(redfishEventLogFile); 37 if (!logStream.good()) 38 { 39 BMCWEB_LOG_ERROR(" Redfish log file open failed "); 40 return; 41 } 42 std::string logEntry; 43 while (std::getline(logStream, logEntry)) 44 { 45 redfishLogFilePosition = logStream.tellg(); 46 } 47 } 48 49 void FilesystemLogWatcher::readEventLogsFromFile() 50 { 51 std::ifstream logStream(redfishEventLogFile); 52 if (!logStream.good()) 53 { 54 BMCWEB_LOG_ERROR(" Redfish log file open failed"); 55 return; 56 } 57 58 std::vector<EventLogObjectsType> eventRecords; 59 60 std::string logEntry; 61 62 BMCWEB_LOG_DEBUG("Redfish log file: seek to {}", 63 static_cast<int>(redfishLogFilePosition)); 64 65 // Get the read pointer to the next log to be read. 66 logStream.seekg(redfishLogFilePosition); 67 68 while (std::getline(logStream, logEntry)) 69 { 70 BMCWEB_LOG_DEBUG("Redfish log file: found new event log entry"); 71 // Update Pointer position 72 redfishLogFilePosition = logStream.tellg(); 73 74 std::string idStr; 75 if (!event_log::getUniqueEntryID(logEntry, idStr)) 76 { 77 BMCWEB_LOG_DEBUG( 78 "Redfish log file: could not get unique entry id for {}", 79 logEntry); 80 continue; 81 } 82 83 std::string timestamp; 84 std::string messageID; 85 std::vector<std::string> messageArgs; 86 if (event_log::getEventLogParams(logEntry, timestamp, messageID, 87 messageArgs) != 0) 88 { 89 BMCWEB_LOG_DEBUG("Read eventLog entry params failed for {}", 90 logEntry); 91 continue; 92 } 93 94 eventRecords.emplace_back(idStr, timestamp, messageID, messageArgs); 95 } 96 97 if (eventRecords.empty()) 98 { 99 // No Records to send 100 BMCWEB_LOG_DEBUG("No log entries available to be transferred."); 101 return; 102 } 103 EventServiceManager::sendEventsToSubs(eventRecords); 104 } 105 106 static constexpr const char* redfishEventLogDir = "/var/log"; 107 static constexpr const size_t iEventSize = sizeof(inotify_event); 108 109 void FilesystemLogWatcher::onINotify(const boost::system::error_code& ec, 110 std::size_t bytesTransferred) 111 { 112 if (ec == boost::asio::error::operation_aborted) 113 { 114 BMCWEB_LOG_DEBUG("Inotify was canceled (shutdown?)"); 115 return; 116 } 117 if (ec) 118 { 119 BMCWEB_LOG_ERROR("Callback Error: {}", ec.message()); 120 return; 121 } 122 123 BMCWEB_LOG_DEBUG("reading {} via inotify", bytesTransferred); 124 125 std::size_t index = 0; 126 while ((index + iEventSize) <= bytesTransferred) 127 { 128 struct inotify_event event{}; 129 std::memcpy(&event, &readBuffer[index], iEventSize); 130 if (event.wd == dirWatchDesc) 131 { 132 if ((event.len == 0) || 133 (index + iEventSize + event.len > bytesTransferred)) 134 { 135 index += (iEventSize + event.len); 136 continue; 137 } 138 139 std::string fileName(&readBuffer[index + iEventSize]); 140 if (fileName != "redfish") 141 { 142 index += (iEventSize + event.len); 143 continue; 144 } 145 146 BMCWEB_LOG_DEBUG("Redfish log file created/deleted. event.name: {}", 147 fileName); 148 if (event.mask == IN_CREATE) 149 { 150 if (fileWatchDesc != -1) 151 { 152 BMCWEB_LOG_DEBUG("Remove and Add inotify watcher on " 153 "redfish event log file"); 154 // Remove existing inotify watcher and add 155 // with new redfish event log file. 156 inotify_rm_watch(inotifyFd, fileWatchDesc); 157 fileWatchDesc = -1; 158 } 159 160 fileWatchDesc = inotify_add_watch( 161 inotifyFd, redfishEventLogFile, IN_MODIFY); 162 if (fileWatchDesc == -1) 163 { 164 BMCWEB_LOG_ERROR("inotify_add_watch failed for " 165 "redfish log file."); 166 return; 167 } 168 169 resetRedfishFilePosition(); 170 readEventLogsFromFile(); 171 } 172 else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO)) 173 { 174 if (fileWatchDesc != -1) 175 { 176 inotify_rm_watch(inotifyFd, fileWatchDesc); 177 fileWatchDesc = -1; 178 } 179 } 180 } 181 else if (event.wd == fileWatchDesc) 182 { 183 if (event.mask == IN_MODIFY) 184 { 185 readEventLogsFromFile(); 186 } 187 } 188 index += (iEventSize + event.len); 189 } 190 191 watchRedfishEventLogFile(); 192 } 193 194 void FilesystemLogWatcher::watchRedfishEventLogFile() 195 { 196 inotifyConn.async_read_some( 197 boost::asio::buffer(readBuffer), 198 std::bind_front(&FilesystemLogWatcher::onINotify, this)); 199 } 200 201 FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) : 202 inotifyFd(inotify_init1(IN_NONBLOCK)), inotifyConn(ioc) 203 { 204 BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 205 206 if (inotifyFd == -1) 207 { 208 BMCWEB_LOG_ERROR("inotify_init1 failed."); 209 return; 210 } 211 212 // Add watch on directory to handle redfish event log file 213 // create/delete. 214 dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 215 IN_CREATE | IN_MOVED_TO | IN_DELETE); 216 if (dirWatchDesc == -1) 217 { 218 BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory."); 219 return; 220 } 221 222 // Watch redfish event log file for modifications. 223 fileWatchDesc = 224 inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 225 if (fileWatchDesc == -1) 226 { 227 BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 228 // Don't return error if file not exist. 229 // Watch on directory will handle create/delete of file. 230 } 231 232 // monitor redfish event log file 233 inotifyConn.assign(inotifyFd); 234 watchRedfishEventLogFile(); 235 236 if (redfishLogFilePosition != 0) 237 { 238 cacheRedfishLogFile(); 239 } 240 } 241 } // namespace redfish 242