1b52664e2SAppaRao Puli /* 2b52664e2SAppaRao Puli // Copyright (c) 2020 Intel Corporation 3b52664e2SAppaRao Puli // 4b52664e2SAppaRao Puli // Licensed under the Apache License, Version 2.0 (the "License"); 5b52664e2SAppaRao Puli // you may not use this file except in compliance with the License. 6b52664e2SAppaRao Puli // You may obtain a copy of the License at 7b52664e2SAppaRao Puli // 8b52664e2SAppaRao Puli // http://www.apache.org/licenses/LICENSE-2.0 9b52664e2SAppaRao Puli // 10b52664e2SAppaRao Puli // Unless required by applicable law or agreed to in writing, software 11b52664e2SAppaRao Puli // distributed under the License is distributed on an "AS IS" BASIS, 12b52664e2SAppaRao Puli // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b52664e2SAppaRao Puli // See the License for the specific language governing permissions and 14b52664e2SAppaRao Puli // limitations under the License. 15b52664e2SAppaRao Puli */ 16b52664e2SAppaRao Puli #pragma once 17b52664e2SAppaRao Puli #include "node.hpp" 18*e9a14131SAppaRao Puli #include "registries.hpp" 19*e9a14131SAppaRao Puli #include "registries/base_message_registry.hpp" 20*e9a14131SAppaRao Puli #include "registries/openbmc_message_registry.hpp" 21*e9a14131SAppaRao Puli 22*e9a14131SAppaRao Puli #include <sys/inotify.h> 23b52664e2SAppaRao Puli 24b52664e2SAppaRao Puli #include <boost/container/flat_map.hpp> 25b52664e2SAppaRao Puli #include <cstdlib> 26b52664e2SAppaRao Puli #include <ctime> 27b52664e2SAppaRao Puli #include <error_messages.hpp> 28b52664e2SAppaRao Puli #include <http_client.hpp> 29b52664e2SAppaRao Puli #include <memory> 30b52664e2SAppaRao Puli #include <utils/json_utils.hpp> 31b52664e2SAppaRao Puli #include <variant> 32b52664e2SAppaRao Puli 33b52664e2SAppaRao Puli namespace redfish 34b52664e2SAppaRao Puli { 35*e9a14131SAppaRao Puli #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 36*e9a14131SAppaRao Puli constexpr const char* redfishEventLogFile = "/var/log/redfish"; 37*e9a14131SAppaRao Puli constexpr const uint32_t inotifyFileAction = IN_MODIFY; 38*e9a14131SAppaRao Puli std::shared_ptr<boost::asio::posix::stream_descriptor> inotifyConn = nullptr; 39*e9a14131SAppaRao Puli 40*e9a14131SAppaRao Puli // <ID, timestamp, RedfishLogId, registryPrefix, MessageId, MessageArgs> 41*e9a14131SAppaRao Puli using EventLogObjectsType = 42*e9a14131SAppaRao Puli std::tuple<std::string, std::string, std::string, std::string, std::string, 43*e9a14131SAppaRao Puli boost::beast::span<std::string>>; 44*e9a14131SAppaRao Puli 45*e9a14131SAppaRao Puli namespace message_registries 46*e9a14131SAppaRao Puli { 47*e9a14131SAppaRao Puli static const Message* 48*e9a14131SAppaRao Puli getMsgFromRegistry(const std::string& messageKey, 49*e9a14131SAppaRao Puli const boost::beast::span<const MessageEntry>& registry) 50*e9a14131SAppaRao Puli { 51*e9a14131SAppaRao Puli boost::beast::span<const MessageEntry>::const_iterator messageIt = 52*e9a14131SAppaRao Puli std::find_if(registry.cbegin(), registry.cend(), 53*e9a14131SAppaRao Puli [&messageKey](const MessageEntry& messageEntry) { 54*e9a14131SAppaRao Puli return !messageKey.compare(messageEntry.first); 55*e9a14131SAppaRao Puli }); 56*e9a14131SAppaRao Puli if (messageIt != registry.cend()) 57*e9a14131SAppaRao Puli { 58*e9a14131SAppaRao Puli return &messageIt->second; 59*e9a14131SAppaRao Puli } 60*e9a14131SAppaRao Puli 61*e9a14131SAppaRao Puli return nullptr; 62*e9a14131SAppaRao Puli } 63*e9a14131SAppaRao Puli 64*e9a14131SAppaRao Puli static const Message* formatMessage(const std::string_view& messageID) 65*e9a14131SAppaRao Puli { 66*e9a14131SAppaRao Puli // Redfish MessageIds are in the form 67*e9a14131SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 68*e9a14131SAppaRao Puli // the right Message 69*e9a14131SAppaRao Puli std::vector<std::string> fields; 70*e9a14131SAppaRao Puli fields.reserve(4); 71*e9a14131SAppaRao Puli boost::split(fields, messageID, boost::is_any_of(".")); 72*e9a14131SAppaRao Puli if (fields.size() != 4) 73*e9a14131SAppaRao Puli { 74*e9a14131SAppaRao Puli return nullptr; 75*e9a14131SAppaRao Puli } 76*e9a14131SAppaRao Puli std::string& registryName = fields[0]; 77*e9a14131SAppaRao Puli std::string& messageKey = fields[3]; 78*e9a14131SAppaRao Puli 79*e9a14131SAppaRao Puli // Find the right registry and check it for the MessageKey 80*e9a14131SAppaRao Puli if (std::string(base::header.registryPrefix) == registryName) 81*e9a14131SAppaRao Puli { 82*e9a14131SAppaRao Puli return getMsgFromRegistry( 83*e9a14131SAppaRao Puli messageKey, boost::beast::span<const MessageEntry>(base::registry)); 84*e9a14131SAppaRao Puli } 85*e9a14131SAppaRao Puli if (std::string(openbmc::header.registryPrefix) == registryName) 86*e9a14131SAppaRao Puli { 87*e9a14131SAppaRao Puli return getMsgFromRegistry( 88*e9a14131SAppaRao Puli messageKey, 89*e9a14131SAppaRao Puli boost::beast::span<const MessageEntry>(openbmc::registry)); 90*e9a14131SAppaRao Puli } 91*e9a14131SAppaRao Puli return nullptr; 92*e9a14131SAppaRao Puli } 93*e9a14131SAppaRao Puli } // namespace message_registries 94*e9a14131SAppaRao Puli 95*e9a14131SAppaRao Puli namespace event_log 96*e9a14131SAppaRao Puli { 97*e9a14131SAppaRao Puli bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, 98*e9a14131SAppaRao Puli const bool firstEntry = true) 99*e9a14131SAppaRao Puli { 100*e9a14131SAppaRao Puli static time_t prevTs = 0; 101*e9a14131SAppaRao Puli static int index = 0; 102*e9a14131SAppaRao Puli if (firstEntry) 103*e9a14131SAppaRao Puli { 104*e9a14131SAppaRao Puli prevTs = 0; 105*e9a14131SAppaRao Puli } 106*e9a14131SAppaRao Puli 107*e9a14131SAppaRao Puli // Get the entry timestamp 108*e9a14131SAppaRao Puli std::time_t curTs = 0; 109*e9a14131SAppaRao Puli std::tm timeStruct = {}; 110*e9a14131SAppaRao Puli std::istringstream entryStream(logEntry); 111*e9a14131SAppaRao Puli if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 112*e9a14131SAppaRao Puli { 113*e9a14131SAppaRao Puli curTs = std::mktime(&timeStruct); 114*e9a14131SAppaRao Puli if (curTs == -1) 115*e9a14131SAppaRao Puli { 116*e9a14131SAppaRao Puli return false; 117*e9a14131SAppaRao Puli } 118*e9a14131SAppaRao Puli } 119*e9a14131SAppaRao Puli // If the timestamp isn't unique, increment the index 120*e9a14131SAppaRao Puli index = (curTs == prevTs) ? index + 1 : 0; 121*e9a14131SAppaRao Puli 122*e9a14131SAppaRao Puli // Save the timestamp 123*e9a14131SAppaRao Puli prevTs = curTs; 124*e9a14131SAppaRao Puli 125*e9a14131SAppaRao Puli entryID = std::to_string(curTs); 126*e9a14131SAppaRao Puli if (index > 0) 127*e9a14131SAppaRao Puli { 128*e9a14131SAppaRao Puli entryID += "_" + std::to_string(index); 129*e9a14131SAppaRao Puli } 130*e9a14131SAppaRao Puli return true; 131*e9a14131SAppaRao Puli } 132*e9a14131SAppaRao Puli 133*e9a14131SAppaRao Puli int getEventLogParams(const std::string& logEntry, std::string& timestamp, 134*e9a14131SAppaRao Puli std::string& messageID, 135*e9a14131SAppaRao Puli boost::beast::span<std::string>& messageArgs) 136*e9a14131SAppaRao Puli { 137*e9a14131SAppaRao Puli // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 138*e9a14131SAppaRao Puli // First get the Timestamp 139*e9a14131SAppaRao Puli size_t space = logEntry.find_first_of(" "); 140*e9a14131SAppaRao Puli if (space == std::string::npos) 141*e9a14131SAppaRao Puli { 142*e9a14131SAppaRao Puli return -EINVAL; 143*e9a14131SAppaRao Puli } 144*e9a14131SAppaRao Puli timestamp = logEntry.substr(0, space); 145*e9a14131SAppaRao Puli // Then get the log contents 146*e9a14131SAppaRao Puli size_t entryStart = logEntry.find_first_not_of(" ", space); 147*e9a14131SAppaRao Puli if (entryStart == std::string::npos) 148*e9a14131SAppaRao Puli { 149*e9a14131SAppaRao Puli return -EINVAL; 150*e9a14131SAppaRao Puli } 151*e9a14131SAppaRao Puli std::string_view entry(logEntry); 152*e9a14131SAppaRao Puli entry.remove_prefix(entryStart); 153*e9a14131SAppaRao Puli // Use split to separate the entry into its fields 154*e9a14131SAppaRao Puli std::vector<std::string> logEntryFields; 155*e9a14131SAppaRao Puli boost::split(logEntryFields, entry, boost::is_any_of(","), 156*e9a14131SAppaRao Puli boost::token_compress_on); 157*e9a14131SAppaRao Puli // We need at least a MessageId to be valid 158*e9a14131SAppaRao Puli if (logEntryFields.size() < 1) 159*e9a14131SAppaRao Puli { 160*e9a14131SAppaRao Puli return -EINVAL; 161*e9a14131SAppaRao Puli } 162*e9a14131SAppaRao Puli messageID = logEntryFields[0]; 163*e9a14131SAppaRao Puli 164*e9a14131SAppaRao Puli // Get the MessageArgs from the log if there are any 165*e9a14131SAppaRao Puli if (logEntryFields.size() > 1) 166*e9a14131SAppaRao Puli { 167*e9a14131SAppaRao Puli std::string& messageArgsStart = logEntryFields[1]; 168*e9a14131SAppaRao Puli // If the first string is empty, assume there are no MessageArgs 169*e9a14131SAppaRao Puli std::size_t messageArgsSize = 0; 170*e9a14131SAppaRao Puli if (!messageArgsStart.empty()) 171*e9a14131SAppaRao Puli { 172*e9a14131SAppaRao Puli messageArgsSize = logEntryFields.size() - 1; 173*e9a14131SAppaRao Puli } 174*e9a14131SAppaRao Puli 175*e9a14131SAppaRao Puli messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize); 176*e9a14131SAppaRao Puli } 177*e9a14131SAppaRao Puli 178*e9a14131SAppaRao Puli return 0; 179*e9a14131SAppaRao Puli } 180*e9a14131SAppaRao Puli 181*e9a14131SAppaRao Puli void getRegistryAndMessageKey(const std::string& messageID, 182*e9a14131SAppaRao Puli std::string& registryName, 183*e9a14131SAppaRao Puli std::string& messageKey) 184*e9a14131SAppaRao Puli { 185*e9a14131SAppaRao Puli // Redfish MessageIds are in the form 186*e9a14131SAppaRao Puli // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 187*e9a14131SAppaRao Puli // the right Message 188*e9a14131SAppaRao Puli std::vector<std::string> fields; 189*e9a14131SAppaRao Puli fields.reserve(4); 190*e9a14131SAppaRao Puli boost::split(fields, messageID, boost::is_any_of(".")); 191*e9a14131SAppaRao Puli if (fields.size() == 4) 192*e9a14131SAppaRao Puli { 193*e9a14131SAppaRao Puli registryName = fields[0]; 194*e9a14131SAppaRao Puli messageKey = fields[3]; 195*e9a14131SAppaRao Puli } 196*e9a14131SAppaRao Puli } 197*e9a14131SAppaRao Puli 198*e9a14131SAppaRao Puli int formatEventLogEntry(const std::string& logEntryID, 199*e9a14131SAppaRao Puli const std::string& messageID, 200*e9a14131SAppaRao Puli const boost::beast::span<std::string>& messageArgs, 201*e9a14131SAppaRao Puli std::string timestamp, const std::string customText, 202*e9a14131SAppaRao Puli nlohmann::json& logEntryJson) 203*e9a14131SAppaRao Puli { 204*e9a14131SAppaRao Puli // Get the Message from the MessageRegistry 205*e9a14131SAppaRao Puli const message_registries::Message* message = 206*e9a14131SAppaRao Puli message_registries::formatMessage(messageID); 207*e9a14131SAppaRao Puli 208*e9a14131SAppaRao Puli std::string msg; 209*e9a14131SAppaRao Puli std::string severity; 210*e9a14131SAppaRao Puli if (message != nullptr) 211*e9a14131SAppaRao Puli { 212*e9a14131SAppaRao Puli msg = message->message; 213*e9a14131SAppaRao Puli severity = message->severity; 214*e9a14131SAppaRao Puli } 215*e9a14131SAppaRao Puli 216*e9a14131SAppaRao Puli // Fill the MessageArgs into the Message 217*e9a14131SAppaRao Puli int i = 0; 218*e9a14131SAppaRao Puli for (const std::string& messageArg : messageArgs) 219*e9a14131SAppaRao Puli { 220*e9a14131SAppaRao Puli std::string argStr = "%" + std::to_string(++i); 221*e9a14131SAppaRao Puli size_t argPos = msg.find(argStr); 222*e9a14131SAppaRao Puli if (argPos != std::string::npos) 223*e9a14131SAppaRao Puli { 224*e9a14131SAppaRao Puli msg.replace(argPos, argStr.length(), messageArg); 225*e9a14131SAppaRao Puli } 226*e9a14131SAppaRao Puli } 227*e9a14131SAppaRao Puli 228*e9a14131SAppaRao Puli // Get the Created time from the timestamp. The log timestamp is in 229*e9a14131SAppaRao Puli // RFC3339 format which matches the Redfish format except for the 230*e9a14131SAppaRao Puli // fractional seconds between the '.' and the '+', so just remove them. 231*e9a14131SAppaRao Puli std::size_t dot = timestamp.find_first_of("."); 232*e9a14131SAppaRao Puli std::size_t plus = timestamp.find_first_of("+"); 233*e9a14131SAppaRao Puli if (dot != std::string::npos && plus != std::string::npos) 234*e9a14131SAppaRao Puli { 235*e9a14131SAppaRao Puli timestamp.erase(dot, plus - dot); 236*e9a14131SAppaRao Puli } 237*e9a14131SAppaRao Puli 238*e9a14131SAppaRao Puli // Fill in the log entry with the gathered data 239*e9a14131SAppaRao Puli logEntryJson = {{"EventId", logEntryID}, 240*e9a14131SAppaRao Puli {"EventType", "Event"}, 241*e9a14131SAppaRao Puli {"Severity", std::move(severity)}, 242*e9a14131SAppaRao Puli {"Message", std::move(msg)}, 243*e9a14131SAppaRao Puli {"MessageId", std::move(messageID)}, 244*e9a14131SAppaRao Puli {"MessageArgs", std::move(messageArgs)}, 245*e9a14131SAppaRao Puli {"EventTimestamp", std::move(timestamp)}, 246*e9a14131SAppaRao Puli {"Context", customText}}; 247*e9a14131SAppaRao Puli return 0; 248*e9a14131SAppaRao Puli } 249*e9a14131SAppaRao Puli #endif 250*e9a14131SAppaRao Puli 251*e9a14131SAppaRao Puli } // namespace event_log 252*e9a14131SAppaRao Puli 253b52664e2SAppaRao Puli class Subscription 254b52664e2SAppaRao Puli { 255b52664e2SAppaRao Puli public: 256b52664e2SAppaRao Puli std::string id; 257b52664e2SAppaRao Puli std::string destinationUrl; 258b52664e2SAppaRao Puli std::string protocol; 259b52664e2SAppaRao Puli std::string retryPolicy; 260b52664e2SAppaRao Puli std::string customText; 261b52664e2SAppaRao Puli std::string eventFormatType; 262b52664e2SAppaRao Puli std::string subscriptionType; 263b52664e2SAppaRao Puli std::vector<std::string> registryMsgIds; 264b52664e2SAppaRao Puli std::vector<std::string> registryPrefixes; 265b52664e2SAppaRao Puli std::vector<nlohmann::json> httpHeaders; // key-value pair 266b52664e2SAppaRao Puli 267b52664e2SAppaRao Puli Subscription(const Subscription&) = delete; 268b52664e2SAppaRao Puli Subscription& operator=(const Subscription&) = delete; 269b52664e2SAppaRao Puli Subscription(Subscription&&) = delete; 270b52664e2SAppaRao Puli Subscription& operator=(Subscription&&) = delete; 271b52664e2SAppaRao Puli 272b52664e2SAppaRao Puli Subscription(const std::string& inHost, const std::string& inPort, 273b52664e2SAppaRao Puli const std::string& inPath, const std::string& inUriProto) : 2740b4bdd93SAppaRao Puli eventSeqNum(1), 2750b4bdd93SAppaRao Puli host(inHost), port(inPort), path(inPath), uriProto(inUriProto) 276b52664e2SAppaRao Puli { 277b52664e2SAppaRao Puli conn = std::make_shared<crow::HttpClient>( 2782a5689a7SAppaRao Puli crow::connections::systemBus->get_io_context(), host, port, path); 279b52664e2SAppaRao Puli } 280b52664e2SAppaRao Puli ~Subscription() 281b52664e2SAppaRao Puli { 282b52664e2SAppaRao Puli } 283b52664e2SAppaRao Puli 284b52664e2SAppaRao Puli void sendEvent(const std::string& msg) 285b52664e2SAppaRao Puli { 286b52664e2SAppaRao Puli std::vector<std::pair<std::string, std::string>> reqHeaders; 287b52664e2SAppaRao Puli for (const auto& header : httpHeaders) 288b52664e2SAppaRao Puli { 289b52664e2SAppaRao Puli for (const auto& item : header.items()) 290b52664e2SAppaRao Puli { 291b52664e2SAppaRao Puli std::string key = item.key(); 292b52664e2SAppaRao Puli std::string val = item.value(); 293b52664e2SAppaRao Puli reqHeaders.emplace_back(std::pair(key, val)); 294b52664e2SAppaRao Puli } 295b52664e2SAppaRao Puli } 296b52664e2SAppaRao Puli conn->setHeaders(reqHeaders); 2972a5689a7SAppaRao Puli conn->sendData(msg); 298b52664e2SAppaRao Puli } 299b52664e2SAppaRao Puli 3000b4bdd93SAppaRao Puli void sendTestEventLog() 3010b4bdd93SAppaRao Puli { 3020b4bdd93SAppaRao Puli nlohmann::json logEntryArray; 3030b4bdd93SAppaRao Puli logEntryArray.push_back({}); 3040b4bdd93SAppaRao Puli nlohmann::json& logEntryJson = logEntryArray.back(); 3050b4bdd93SAppaRao Puli 3060b4bdd93SAppaRao Puli logEntryJson = {{"EventId", "TestID"}, 3070b4bdd93SAppaRao Puli {"EventType", "Event"}, 3080b4bdd93SAppaRao Puli {"Severity", "OK"}, 3090b4bdd93SAppaRao Puli {"Message", "Generated test event"}, 3100b4bdd93SAppaRao Puli {"MessageId", "OpenBMC.0.1.TestEventLog"}, 3110b4bdd93SAppaRao Puli {"MessageArgs", nlohmann::json::array()}, 3120b4bdd93SAppaRao Puli {"EventTimestamp", crow::utility::dateTimeNow()}, 3130b4bdd93SAppaRao Puli {"Context", customText}}; 3140b4bdd93SAppaRao Puli 3150b4bdd93SAppaRao Puli nlohmann::json msg = {{"@odata.type", "#Event.v1_4_0.Event"}, 3160b4bdd93SAppaRao Puli {"Id", std::to_string(eventSeqNum)}, 3170b4bdd93SAppaRao Puli {"Name", "Event Log"}, 3180b4bdd93SAppaRao Puli {"Events", logEntryArray}}; 3190b4bdd93SAppaRao Puli 3200b4bdd93SAppaRao Puli this->sendEvent(msg.dump()); 3210b4bdd93SAppaRao Puli this->eventSeqNum++; 3220b4bdd93SAppaRao Puli } 3230b4bdd93SAppaRao Puli 324*e9a14131SAppaRao Puli #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 325*e9a14131SAppaRao Puli void filterAndSendEventLogs( 326*e9a14131SAppaRao Puli const std::vector<EventLogObjectsType>& eventRecords) 327*e9a14131SAppaRao Puli { 328*e9a14131SAppaRao Puli nlohmann::json logEntryArray; 329*e9a14131SAppaRao Puli for (const EventLogObjectsType& logEntry : eventRecords) 330*e9a14131SAppaRao Puli { 331*e9a14131SAppaRao Puli const std::string& idStr = std::get<0>(logEntry); 332*e9a14131SAppaRao Puli const std::string& timestamp = std::get<1>(logEntry); 333*e9a14131SAppaRao Puli const std::string& messageID = std::get<2>(logEntry); 334*e9a14131SAppaRao Puli const std::string& registryName = std::get<3>(logEntry); 335*e9a14131SAppaRao Puli const std::string& messageKey = std::get<4>(logEntry); 336*e9a14131SAppaRao Puli const boost::beast::span<std::string>& messageArgs = 337*e9a14131SAppaRao Puli std::get<5>(logEntry); 338*e9a14131SAppaRao Puli 339*e9a14131SAppaRao Puli // If registryPrefixes list is empty, don't filter events 340*e9a14131SAppaRao Puli // send everything. 341*e9a14131SAppaRao Puli if (registryPrefixes.size()) 342*e9a14131SAppaRao Puli { 343*e9a14131SAppaRao Puli auto obj = std::find(registryPrefixes.begin(), 344*e9a14131SAppaRao Puli registryPrefixes.end(), registryName); 345*e9a14131SAppaRao Puli if (obj == registryPrefixes.end()) 346*e9a14131SAppaRao Puli { 347*e9a14131SAppaRao Puli continue; 348*e9a14131SAppaRao Puli } 349*e9a14131SAppaRao Puli } 350*e9a14131SAppaRao Puli 351*e9a14131SAppaRao Puli // If registryMsgIds list is empty, don't filter events 352*e9a14131SAppaRao Puli // send everything. 353*e9a14131SAppaRao Puli if (registryMsgIds.size()) 354*e9a14131SAppaRao Puli { 355*e9a14131SAppaRao Puli auto obj = std::find(registryMsgIds.begin(), 356*e9a14131SAppaRao Puli registryMsgIds.end(), messageKey); 357*e9a14131SAppaRao Puli if (obj == registryMsgIds.end()) 358*e9a14131SAppaRao Puli { 359*e9a14131SAppaRao Puli continue; 360*e9a14131SAppaRao Puli } 361*e9a14131SAppaRao Puli } 362*e9a14131SAppaRao Puli 363*e9a14131SAppaRao Puli logEntryArray.push_back({}); 364*e9a14131SAppaRao Puli nlohmann::json& bmcLogEntry = logEntryArray.back(); 365*e9a14131SAppaRao Puli if (event_log::formatEventLogEntry(idStr, messageID, messageArgs, 366*e9a14131SAppaRao Puli timestamp, customText, 367*e9a14131SAppaRao Puli bmcLogEntry) != 0) 368*e9a14131SAppaRao Puli { 369*e9a14131SAppaRao Puli BMCWEB_LOG_DEBUG << "Read eventLog entry failed"; 370*e9a14131SAppaRao Puli continue; 371*e9a14131SAppaRao Puli } 372*e9a14131SAppaRao Puli } 373*e9a14131SAppaRao Puli 374*e9a14131SAppaRao Puli if (logEntryArray.size() < 1) 375*e9a14131SAppaRao Puli { 376*e9a14131SAppaRao Puli BMCWEB_LOG_DEBUG << "No log entries available to be transferred."; 377*e9a14131SAppaRao Puli return; 378*e9a14131SAppaRao Puli } 379*e9a14131SAppaRao Puli 380*e9a14131SAppaRao Puli nlohmann::json msg = {{"@odata.type", "#Event.v1_4_0.Event"}, 381*e9a14131SAppaRao Puli {"Id", std::to_string(eventSeqNum)}, 382*e9a14131SAppaRao Puli {"Name", "Event Log"}, 383*e9a14131SAppaRao Puli {"Events", logEntryArray}}; 384*e9a14131SAppaRao Puli 385*e9a14131SAppaRao Puli this->sendEvent(msg.dump()); 386*e9a14131SAppaRao Puli this->eventSeqNum++; 387*e9a14131SAppaRao Puli } 388*e9a14131SAppaRao Puli #endif 389*e9a14131SAppaRao Puli 390b52664e2SAppaRao Puli private: 3910b4bdd93SAppaRao Puli uint64_t eventSeqNum; 392b52664e2SAppaRao Puli std::string host; 393b52664e2SAppaRao Puli std::string port; 394b52664e2SAppaRao Puli std::string path; 395b52664e2SAppaRao Puli std::string uriProto; 396b52664e2SAppaRao Puli std::shared_ptr<crow::HttpClient> conn; 397b52664e2SAppaRao Puli }; 398b52664e2SAppaRao Puli 399b52664e2SAppaRao Puli class EventServiceManager 400b52664e2SAppaRao Puli { 401b52664e2SAppaRao Puli private: 402b52664e2SAppaRao Puli EventServiceManager(const EventServiceManager&) = delete; 403b52664e2SAppaRao Puli EventServiceManager& operator=(const EventServiceManager&) = delete; 404b52664e2SAppaRao Puli EventServiceManager(EventServiceManager&&) = delete; 405b52664e2SAppaRao Puli EventServiceManager& operator=(EventServiceManager&&) = delete; 406b52664e2SAppaRao Puli 407b52664e2SAppaRao Puli EventServiceManager() 408b52664e2SAppaRao Puli { 409b52664e2SAppaRao Puli // TODO: Read the persistent data from store and populate. 410b52664e2SAppaRao Puli // Populating with default. 411b52664e2SAppaRao Puli enabled = true; 412b52664e2SAppaRao Puli retryAttempts = 3; 413b52664e2SAppaRao Puli retryTimeoutInterval = 30; // seconds 414b52664e2SAppaRao Puli } 415b52664e2SAppaRao Puli 416*e9a14131SAppaRao Puli std::string lastEventTStr; 417b52664e2SAppaRao Puli boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 418b52664e2SAppaRao Puli subscriptionsMap; 419b52664e2SAppaRao Puli 420b52664e2SAppaRao Puli public: 421b52664e2SAppaRao Puli bool enabled; 422b52664e2SAppaRao Puli uint32_t retryAttempts; 423b52664e2SAppaRao Puli uint32_t retryTimeoutInterval; 424b52664e2SAppaRao Puli 425b52664e2SAppaRao Puli static EventServiceManager& getInstance() 426b52664e2SAppaRao Puli { 427b52664e2SAppaRao Puli static EventServiceManager handler; 428b52664e2SAppaRao Puli return handler; 429b52664e2SAppaRao Puli } 430b52664e2SAppaRao Puli 431b52664e2SAppaRao Puli void updateSubscriptionData() 432b52664e2SAppaRao Puli { 433b52664e2SAppaRao Puli // Persist the config and subscription data. 434b52664e2SAppaRao Puli // TODO: subscriptionsMap & configData need to be 435b52664e2SAppaRao Puli // written to Persist store. 436b52664e2SAppaRao Puli return; 437b52664e2SAppaRao Puli } 438b52664e2SAppaRao Puli 439b52664e2SAppaRao Puli std::shared_ptr<Subscription> getSubscription(const std::string& id) 440b52664e2SAppaRao Puli { 441b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 442b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 443b52664e2SAppaRao Puli { 444b52664e2SAppaRao Puli BMCWEB_LOG_ERROR << "No subscription exist with ID:" << id; 445b52664e2SAppaRao Puli return nullptr; 446b52664e2SAppaRao Puli } 447b52664e2SAppaRao Puli std::shared_ptr<Subscription> subValue = obj->second; 448b52664e2SAppaRao Puli return subValue; 449b52664e2SAppaRao Puli } 450b52664e2SAppaRao Puli 451b52664e2SAppaRao Puli std::string addSubscription(const std::shared_ptr<Subscription> subValue) 452b52664e2SAppaRao Puli { 453b52664e2SAppaRao Puli std::srand(static_cast<uint32_t>(std::time(0))); 454b52664e2SAppaRao Puli std::string id; 455b52664e2SAppaRao Puli 456b52664e2SAppaRao Puli int retry = 3; 457b52664e2SAppaRao Puli while (retry) 458b52664e2SAppaRao Puli { 459b52664e2SAppaRao Puli id = std::to_string(std::rand()); 460b52664e2SAppaRao Puli auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 461b52664e2SAppaRao Puli if (inserted.second) 462b52664e2SAppaRao Puli { 463b52664e2SAppaRao Puli break; 464b52664e2SAppaRao Puli } 465b52664e2SAppaRao Puli --retry; 466b52664e2SAppaRao Puli }; 467b52664e2SAppaRao Puli 468b52664e2SAppaRao Puli if (retry <= 0) 469b52664e2SAppaRao Puli { 470b52664e2SAppaRao Puli BMCWEB_LOG_ERROR << "Failed to generate random number"; 471b52664e2SAppaRao Puli return std::string(""); 472b52664e2SAppaRao Puli } 473b52664e2SAppaRao Puli 474b52664e2SAppaRao Puli updateSubscriptionData(); 475*e9a14131SAppaRao Puli 476*e9a14131SAppaRao Puli #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 477*e9a14131SAppaRao Puli if (lastEventTStr.empty()) 478*e9a14131SAppaRao Puli { 479*e9a14131SAppaRao Puli cacheLastEventTimestamp(); 480*e9a14131SAppaRao Puli } 481*e9a14131SAppaRao Puli #endif 482b52664e2SAppaRao Puli return id; 483b52664e2SAppaRao Puli } 484b52664e2SAppaRao Puli 485b52664e2SAppaRao Puli bool isSubscriptionExist(const std::string& id) 486b52664e2SAppaRao Puli { 487b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 488b52664e2SAppaRao Puli if (obj == subscriptionsMap.end()) 489b52664e2SAppaRao Puli { 490b52664e2SAppaRao Puli return false; 491b52664e2SAppaRao Puli } 492b52664e2SAppaRao Puli return true; 493b52664e2SAppaRao Puli } 494b52664e2SAppaRao Puli 495b52664e2SAppaRao Puli void deleteSubscription(const std::string& id) 496b52664e2SAppaRao Puli { 497b52664e2SAppaRao Puli auto obj = subscriptionsMap.find(id); 498b52664e2SAppaRao Puli if (obj != subscriptionsMap.end()) 499b52664e2SAppaRao Puli { 500b52664e2SAppaRao Puli subscriptionsMap.erase(obj); 501b52664e2SAppaRao Puli updateSubscriptionData(); 502b52664e2SAppaRao Puli } 503b52664e2SAppaRao Puli } 504b52664e2SAppaRao Puli 505b52664e2SAppaRao Puli size_t getNumberOfSubscriptions() 506b52664e2SAppaRao Puli { 507b52664e2SAppaRao Puli return subscriptionsMap.size(); 508b52664e2SAppaRao Puli } 509b52664e2SAppaRao Puli 510b52664e2SAppaRao Puli std::vector<std::string> getAllIDs() 511b52664e2SAppaRao Puli { 512b52664e2SAppaRao Puli std::vector<std::string> idList; 513b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 514b52664e2SAppaRao Puli { 515b52664e2SAppaRao Puli idList.emplace_back(it.first); 516b52664e2SAppaRao Puli } 517b52664e2SAppaRao Puli return idList; 518b52664e2SAppaRao Puli } 519b52664e2SAppaRao Puli 520b52664e2SAppaRao Puli bool isDestinationExist(const std::string& destUrl) 521b52664e2SAppaRao Puli { 522b52664e2SAppaRao Puli for (const auto& it : subscriptionsMap) 523b52664e2SAppaRao Puli { 524b52664e2SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 525b52664e2SAppaRao Puli if (entry->destinationUrl == destUrl) 526b52664e2SAppaRao Puli { 527b52664e2SAppaRao Puli BMCWEB_LOG_ERROR << "Destination exist already" << destUrl; 528b52664e2SAppaRao Puli return true; 529b52664e2SAppaRao Puli } 530b52664e2SAppaRao Puli } 531b52664e2SAppaRao Puli return false; 532b52664e2SAppaRao Puli } 5330b4bdd93SAppaRao Puli 5340b4bdd93SAppaRao Puli void sendTestEventLog() 5350b4bdd93SAppaRao Puli { 5360b4bdd93SAppaRao Puli for (const auto& it : this->subscriptionsMap) 5370b4bdd93SAppaRao Puli { 5380b4bdd93SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 5390b4bdd93SAppaRao Puli entry->sendTestEventLog(); 5400b4bdd93SAppaRao Puli } 5410b4bdd93SAppaRao Puli } 542*e9a14131SAppaRao Puli 543*e9a14131SAppaRao Puli #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 544*e9a14131SAppaRao Puli void cacheLastEventTimestamp() 545*e9a14131SAppaRao Puli { 546*e9a14131SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 547*e9a14131SAppaRao Puli if (!logStream.good()) 548*e9a14131SAppaRao Puli { 549*e9a14131SAppaRao Puli BMCWEB_LOG_ERROR << " Redfish log file open failed \n"; 550*e9a14131SAppaRao Puli return; 551*e9a14131SAppaRao Puli } 552*e9a14131SAppaRao Puli std::string logEntry; 553*e9a14131SAppaRao Puli while (std::getline(logStream, logEntry)) 554*e9a14131SAppaRao Puli { 555*e9a14131SAppaRao Puli size_t space = logEntry.find_first_of(" "); 556*e9a14131SAppaRao Puli if (space == std::string::npos) 557*e9a14131SAppaRao Puli { 558*e9a14131SAppaRao Puli // Shouldn't enter here but lets skip it. 559*e9a14131SAppaRao Puli BMCWEB_LOG_DEBUG << "Invalid log entry found."; 560*e9a14131SAppaRao Puli continue; 561*e9a14131SAppaRao Puli } 562*e9a14131SAppaRao Puli lastEventTStr = logEntry.substr(0, space); 563*e9a14131SAppaRao Puli } 564*e9a14131SAppaRao Puli BMCWEB_LOG_DEBUG << "Last Event time stamp set: " << lastEventTStr; 565*e9a14131SAppaRao Puli } 566*e9a14131SAppaRao Puli 567*e9a14131SAppaRao Puli void readEventLogsFromFile() 568*e9a14131SAppaRao Puli { 569*e9a14131SAppaRao Puli if (!getNumberOfSubscriptions()) 570*e9a14131SAppaRao Puli { 571*e9a14131SAppaRao Puli // no subscriptions. Just return. 572*e9a14131SAppaRao Puli BMCWEB_LOG_DEBUG << "No Subscriptions\n"; 573*e9a14131SAppaRao Puli return; 574*e9a14131SAppaRao Puli } 575*e9a14131SAppaRao Puli std::ifstream logStream(redfishEventLogFile); 576*e9a14131SAppaRao Puli if (!logStream.good()) 577*e9a14131SAppaRao Puli { 578*e9a14131SAppaRao Puli BMCWEB_LOG_ERROR << " Redfish log file open failed"; 579*e9a14131SAppaRao Puli return; 580*e9a14131SAppaRao Puli } 581*e9a14131SAppaRao Puli 582*e9a14131SAppaRao Puli std::vector<EventLogObjectsType> eventRecords; 583*e9a14131SAppaRao Puli 584*e9a14131SAppaRao Puli bool startLogCollection = false; 585*e9a14131SAppaRao Puli bool firstEntry = true; 586*e9a14131SAppaRao Puli 587*e9a14131SAppaRao Puli std::string logEntry; 588*e9a14131SAppaRao Puli while (std::getline(logStream, logEntry)) 589*e9a14131SAppaRao Puli { 590*e9a14131SAppaRao Puli if (!startLogCollection) 591*e9a14131SAppaRao Puli { 592*e9a14131SAppaRao Puli if (boost::starts_with(logEntry, lastEventTStr)) 593*e9a14131SAppaRao Puli { 594*e9a14131SAppaRao Puli startLogCollection = true; 595*e9a14131SAppaRao Puli } 596*e9a14131SAppaRao Puli continue; 597*e9a14131SAppaRao Puli } 598*e9a14131SAppaRao Puli 599*e9a14131SAppaRao Puli std::string idStr; 600*e9a14131SAppaRao Puli if (!event_log::getUniqueEntryID(logEntry, idStr, firstEntry)) 601*e9a14131SAppaRao Puli { 602*e9a14131SAppaRao Puli continue; 603*e9a14131SAppaRao Puli } 604*e9a14131SAppaRao Puli firstEntry = false; 605*e9a14131SAppaRao Puli 606*e9a14131SAppaRao Puli std::string timestamp; 607*e9a14131SAppaRao Puli std::string messageID; 608*e9a14131SAppaRao Puli boost::beast::span<std::string> messageArgs; 609*e9a14131SAppaRao Puli if (event_log::getEventLogParams(logEntry, timestamp, messageID, 610*e9a14131SAppaRao Puli messageArgs) != 0) 611*e9a14131SAppaRao Puli { 612*e9a14131SAppaRao Puli BMCWEB_LOG_DEBUG << "Read eventLog entry params failed"; 613*e9a14131SAppaRao Puli continue; 614*e9a14131SAppaRao Puli } 615*e9a14131SAppaRao Puli 616*e9a14131SAppaRao Puli std::string registryName; 617*e9a14131SAppaRao Puli std::string messageKey; 618*e9a14131SAppaRao Puli event_log::getRegistryAndMessageKey(messageID, registryName, 619*e9a14131SAppaRao Puli messageKey); 620*e9a14131SAppaRao Puli if (registryName.empty() || messageKey.empty()) 621*e9a14131SAppaRao Puli { 622*e9a14131SAppaRao Puli continue; 623*e9a14131SAppaRao Puli } 624*e9a14131SAppaRao Puli 625*e9a14131SAppaRao Puli lastEventTStr = timestamp; 626*e9a14131SAppaRao Puli eventRecords.emplace_back(idStr, timestamp, messageID, registryName, 627*e9a14131SAppaRao Puli messageKey, messageArgs); 628*e9a14131SAppaRao Puli } 629*e9a14131SAppaRao Puli 630*e9a14131SAppaRao Puli for (const auto& it : this->subscriptionsMap) 631*e9a14131SAppaRao Puli { 632*e9a14131SAppaRao Puli std::shared_ptr<Subscription> entry = it.second; 633*e9a14131SAppaRao Puli if (entry->eventFormatType == "Event") 634*e9a14131SAppaRao Puli { 635*e9a14131SAppaRao Puli entry->filterAndSendEventLogs(eventRecords); 636*e9a14131SAppaRao Puli } 637*e9a14131SAppaRao Puli } 638*e9a14131SAppaRao Puli } 639*e9a14131SAppaRao Puli 640*e9a14131SAppaRao Puli static void watchRedfishEventLogFile() 641*e9a14131SAppaRao Puli { 642*e9a14131SAppaRao Puli if (inotifyConn == nullptr) 643*e9a14131SAppaRao Puli { 644*e9a14131SAppaRao Puli return; 645*e9a14131SAppaRao Puli } 646*e9a14131SAppaRao Puli 647*e9a14131SAppaRao Puli static std::array<char, 1024> readBuffer; 648*e9a14131SAppaRao Puli 649*e9a14131SAppaRao Puli inotifyConn->async_read_some( 650*e9a14131SAppaRao Puli boost::asio::buffer(readBuffer), 651*e9a14131SAppaRao Puli [&](const boost::system::error_code& ec, 652*e9a14131SAppaRao Puli const std::size_t& bytesTransferred) { 653*e9a14131SAppaRao Puli if (ec) 654*e9a14131SAppaRao Puli { 655*e9a14131SAppaRao Puli BMCWEB_LOG_ERROR << "Callback Error: " << ec.message(); 656*e9a14131SAppaRao Puli return; 657*e9a14131SAppaRao Puli } 658*e9a14131SAppaRao Puli std::size_t index = 0; 659*e9a14131SAppaRao Puli while ((index + sizeof(inotify_event)) <= bytesTransferred) 660*e9a14131SAppaRao Puli { 661*e9a14131SAppaRao Puli struct inotify_event event; 662*e9a14131SAppaRao Puli std::memcpy(&event, &readBuffer[index], 663*e9a14131SAppaRao Puli sizeof(inotify_event)); 664*e9a14131SAppaRao Puli if (event.mask == inotifyFileAction) 665*e9a14131SAppaRao Puli { 666*e9a14131SAppaRao Puli EventServiceManager::getInstance() 667*e9a14131SAppaRao Puli .readEventLogsFromFile(); 668*e9a14131SAppaRao Puli } 669*e9a14131SAppaRao Puli index += (sizeof(inotify_event) + event.len); 670*e9a14131SAppaRao Puli } 671*e9a14131SAppaRao Puli 672*e9a14131SAppaRao Puli watchRedfishEventLogFile(); 673*e9a14131SAppaRao Puli }); 674*e9a14131SAppaRao Puli } 675*e9a14131SAppaRao Puli 676*e9a14131SAppaRao Puli static int startEventLogMonitor(boost::asio::io_service& ioc) 677*e9a14131SAppaRao Puli { 678*e9a14131SAppaRao Puli inotifyConn = 679*e9a14131SAppaRao Puli std::make_shared<boost::asio::posix::stream_descriptor>(ioc); 680*e9a14131SAppaRao Puli int fd = inotify_init1(IN_NONBLOCK); 681*e9a14131SAppaRao Puli if (fd == -1) 682*e9a14131SAppaRao Puli { 683*e9a14131SAppaRao Puli BMCWEB_LOG_ERROR << "inotify_init1 failed."; 684*e9a14131SAppaRao Puli return -1; 685*e9a14131SAppaRao Puli } 686*e9a14131SAppaRao Puli auto wd = inotify_add_watch(fd, redfishEventLogFile, inotifyFileAction); 687*e9a14131SAppaRao Puli if (wd == -1) 688*e9a14131SAppaRao Puli { 689*e9a14131SAppaRao Puli BMCWEB_LOG_ERROR 690*e9a14131SAppaRao Puli << "inotify_add_watch failed for redfish log file."; 691*e9a14131SAppaRao Puli return -1; 692*e9a14131SAppaRao Puli } 693*e9a14131SAppaRao Puli 694*e9a14131SAppaRao Puli // monitor redfish event log file 695*e9a14131SAppaRao Puli inotifyConn->assign(fd); 696*e9a14131SAppaRao Puli watchRedfishEventLogFile(); 697*e9a14131SAppaRao Puli 698*e9a14131SAppaRao Puli return 0; 699*e9a14131SAppaRao Puli } 700*e9a14131SAppaRao Puli 701*e9a14131SAppaRao Puli #endif 702b52664e2SAppaRao Puli }; 703b52664e2SAppaRao Puli 704b52664e2SAppaRao Puli } // namespace redfish 705