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 {}; 130 std::memcpy(&event, &readBuffer[index], iEventSize); 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(inotifyFd, fileWatchDesc); 158 fileWatchDesc = -1; 159 } 160 161 fileWatchDesc = inotify_add_watch( 162 inotifyFd, redfishEventLogFile, IN_MODIFY); 163 if (fileWatchDesc == -1) 164 { 165 BMCWEB_LOG_ERROR("inotify_add_watch failed for " 166 "redfish log file."); 167 return; 168 } 169 170 resetRedfishFilePosition(); 171 readEventLogsFromFile(); 172 } 173 else if ((event.mask == IN_DELETE) || (event.mask == IN_MOVED_TO)) 174 { 175 if (fileWatchDesc != -1) 176 { 177 inotify_rm_watch(inotifyFd, fileWatchDesc); 178 fileWatchDesc = -1; 179 } 180 } 181 } 182 else if (event.wd == fileWatchDesc) 183 { 184 if (event.mask == IN_MODIFY) 185 { 186 readEventLogsFromFile(); 187 } 188 } 189 index += (iEventSize + event.len); 190 } 191 192 watchRedfishEventLogFile(); 193 } 194 195 void FilesystemLogWatcher::watchRedfishEventLogFile() 196 { 197 inotifyConn.async_read_some( 198 boost::asio::buffer(readBuffer), 199 std::bind_front(&FilesystemLogWatcher::onINotify, this)); 200 } 201 202 FilesystemLogWatcher::FilesystemLogWatcher(boost::asio::io_context& ioc) : 203 inotifyFd(inotify_init1(IN_NONBLOCK)), inotifyConn(ioc) 204 { 205 BMCWEB_LOG_DEBUG("starting Event Log Monitor"); 206 207 if (inotifyFd == -1) 208 { 209 BMCWEB_LOG_ERROR("inotify_init1 failed."); 210 return; 211 } 212 213 // Add watch on directory to handle redfish event log file 214 // create/delete. 215 dirWatchDesc = inotify_add_watch(inotifyFd, redfishEventLogDir, 216 IN_CREATE | IN_MOVED_TO | IN_DELETE); 217 if (dirWatchDesc == -1) 218 { 219 BMCWEB_LOG_ERROR("inotify_add_watch failed for event log directory."); 220 return; 221 } 222 223 // Watch redfish event log file for modifications. 224 fileWatchDesc = 225 inotify_add_watch(inotifyFd, redfishEventLogFile, IN_MODIFY); 226 if (fileWatchDesc == -1) 227 { 228 BMCWEB_LOG_ERROR("inotify_add_watch failed for redfish log file."); 229 // Don't return error if file not exist. 230 // Watch on directory will handle create/delete of file. 231 } 232 233 // monitor redfish event log file 234 inotifyConn.assign(inotifyFd); 235 watchRedfishEventLogFile(); 236 237 if (redfishLogFilePosition != 0) 238 { 239 cacheRedfishLogFile(); 240 } 241 } 242 } // namespace redfish 243