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