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