xref: /openbmc/bmcweb/redfish-core/include/utils/eventlog_utils.hpp (revision 42d4e63ba242d4cd63fdbe8708d38c6d755dc23c)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "async_resp.hpp"
7 #include "dbus_utility.hpp"
8 #include "error_messages.hpp"
9 #include "generated/enums/log_service.hpp"
10 #include "http_response.hpp"
11 #include "logging.hpp"
12 #include "registries.hpp"
13 #include "str_utility.hpp"
14 #include "utils/dbus_event_log_entry.hpp"
15 #include "utils/etag_utils.hpp"
16 #include "utils/log_services_utils.hpp"
17 #include "utils/query_param.hpp"
18 #include "utils/time_utils.hpp"
19 
20 #include <boost/beast/http/field.hpp>
21 #include <boost/beast/http/status.hpp>
22 #include <boost/beast/http/verb.hpp>
23 #include <boost/system/linux_error.hpp>
24 #include <boost/url/format.hpp>
25 #include <boost/url/url.hpp>
26 #include <sdbusplus/message.hpp>
27 #include <sdbusplus/message/native_types.hpp>
28 #include <sdbusplus/unpack_properties.hpp>
29 
30 #include <algorithm>
31 #include <cstddef>
32 #include <cstdint>
33 #include <cstdio>
34 #include <ctime>
35 #include <fstream>
36 #include <iomanip>
37 #include <memory>
38 #include <optional>
39 #include <sstream>
40 #include <string>
41 #include <string_view>
42 #include <utility>
43 
44 namespace redfish
45 {
46 namespace eventlog_utils
47 {
48 
49 constexpr const char* rfSystemsStr = "Systems";
50 constexpr const char* rfManagersStr = "Managers";
51 
52 enum class LogServiceParent
53 {
54     Systems,
55     Managers
56 };
57 
logServiceParentToString(LogServiceParent parent)58 inline std::string logServiceParentToString(LogServiceParent parent)
59 {
60     std::string parentStr;
61     switch (parent)
62     {
63         case LogServiceParent::Managers:
64             parentStr = rfManagersStr;
65             break;
66         case LogServiceParent::Systems:
67             parentStr = rfSystemsStr;
68             break;
69         default:
70             BMCWEB_LOG_ERROR("Unable to stringify bmcweb eventlog location");
71             break;
72     }
73     return parentStr;
74 }
75 
getChildIdFromParent(LogServiceParent parent)76 inline std::string_view getChildIdFromParent(LogServiceParent parent)
77 {
78     std::string_view childId;
79 
80     switch (parent)
81     {
82         case LogServiceParent::Managers:
83             childId = BMCWEB_REDFISH_MANAGER_URI_NAME;
84             break;
85         case LogServiceParent::Systems:
86             childId = BMCWEB_REDFISH_SYSTEM_URI_NAME;
87             break;
88         default:
89             BMCWEB_LOG_ERROR(
90                 "Unable to stringify bmcweb eventlog location childId");
91             break;
92     }
93     return childId;
94 }
95 
getLogEntryDescriptor(LogServiceParent parent)96 inline std::string getLogEntryDescriptor(LogServiceParent parent)
97 {
98     std::string descriptor;
99     switch (parent)
100     {
101         case LogServiceParent::Managers:
102             descriptor = "Manager";
103             break;
104         case LogServiceParent::Systems:
105             descriptor = "System";
106             break;
107         default:
108             BMCWEB_LOG_ERROR("Unable to get Log Entry descriptor");
109             break;
110     }
111     return descriptor;
112 }
113 
handleSystemsAndManagersEventLogServiceGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,LogServiceParent parent)114 inline void handleSystemsAndManagersEventLogServiceGet(
115     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
116     LogServiceParent parent)
117 {
118     const std::string parentStr = logServiceParentToString(parent);
119     const std::string_view childId = getChildIdFromParent(parent);
120     const std::string logEntryDescriptor = getLogEntryDescriptor(parent);
121 
122     if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty())
123     {
124         messages::internalError(asyncResp->res);
125         return;
126     }
127 
128     asyncResp->res.jsonValue["@odata.id"] = std::format(
129         "/redfish/v1/{}/{}/LogServices/EventLog", parentStr, childId);
130     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
131     asyncResp->res.jsonValue["Name"] = "Event Log Service";
132     asyncResp->res.jsonValue["Description"] =
133         std::format("{} Event Log Service", logEntryDescriptor);
134     asyncResp->res.jsonValue["Id"] = "EventLog";
135     asyncResp->res.jsonValue["OverWritePolicy"] =
136         log_service::OverWritePolicy::WrapsWhenFull;
137 
138     std::pair<std::string, std::string> redfishDateTimeOffset =
139         redfish::time_utils::getDateTimeOffsetNow();
140 
141     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
142     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
143         redfishDateTimeOffset.second;
144 
145     asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
146         "/redfish/v1/{}/{}/LogServices/EventLog/Entries", parentStr, childId);
147     asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"]
148 
149         = std::format(
150             "/redfish/v1/{}/{}/LogServices/EventLog/Actions/LogService.ClearLog",
151             parentStr, childId);
152     etag_utils::setEtagOmitDateTimeHandler(asyncResp);
153 }
154 
155 /*
156  * Journal EventLog utilities
157  * */
158 
getRedfishLogFiles(std::vector<std::filesystem::path> & redfishLogFiles)159 inline bool getRedfishLogFiles(
160     std::vector<std::filesystem::path>& redfishLogFiles)
161 {
162     static const std::filesystem::path redfishLogDir = "/var/log";
163     static const std::string redfishLogFilename = "redfish";
164 
165     // Loop through the directory looking for redfish log files
166     for (const std::filesystem::directory_entry& dirEnt :
167          std::filesystem::directory_iterator(redfishLogDir))
168     {
169         // If we find a redfish log file, save the path
170         std::string filename = dirEnt.path().filename();
171         if (filename.starts_with(redfishLogFilename))
172         {
173             redfishLogFiles.emplace_back(redfishLogDir / filename);
174         }
175     }
176     // As the log files rotate, they are appended with a ".#" that is higher for
177     // the older logs. Since we don't expect more than 10 log files, we
178     // can just sort the list to get them in order from newest to oldest
179     std::ranges::sort(redfishLogFiles);
180 
181     return !redfishLogFiles.empty();
182 }
183 
getUniqueEntryID(const std::string & logEntry,std::string & entryID,const bool firstEntry=true)184 inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
185                              const bool firstEntry = true)
186 {
187     static time_t prevTs = 0;
188     static int index = 0;
189     if (firstEntry)
190     {
191         prevTs = 0;
192     }
193 
194     // Get the entry timestamp
195     std::time_t curTs = 0;
196     std::tm timeStruct = {};
197     std::istringstream entryStream(logEntry);
198     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
199     {
200         curTs = std::mktime(&timeStruct);
201     }
202     // If the timestamp isn't unique, increment the index
203     if (curTs == prevTs)
204     {
205         index++;
206     }
207     else
208     {
209         // Otherwise, reset it
210         index = 0;
211     }
212     // Save the timestamp
213     prevTs = curTs;
214 
215     entryID = std::to_string(curTs);
216     if (index > 0)
217     {
218         entryID += "_" + std::to_string(index);
219     }
220     return true;
221 }
222 
223 enum class LogParseError
224 {
225     success,
226     parseFailed,
227     messageIdNotInRegistry,
228 };
229 
fillEventLogEntryJson(const std::string & logEntryID,const std::string & logEntry,nlohmann::json::object_t & logEntryJson,const std::string & parentStr,const std::string_view childId,const std::string & logEntryDescriptor)230 static LogParseError fillEventLogEntryJson(
231     const std::string& logEntryID, const std::string& logEntry,
232     nlohmann::json::object_t& logEntryJson, const std::string& parentStr,
233     const std::string_view childId, const std::string& logEntryDescriptor)
234 {
235     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
236     // First get the Timestamp
237     size_t space = logEntry.find_first_of(' ');
238     if (space == std::string::npos)
239     {
240         return LogParseError::parseFailed;
241     }
242     std::string timestamp = logEntry.substr(0, space);
243     // Then get the log contents
244     size_t entryStart = logEntry.find_first_not_of(' ', space);
245     if (entryStart == std::string::npos)
246     {
247         return LogParseError::parseFailed;
248     }
249     std::string_view entry(logEntry);
250     entry.remove_prefix(entryStart);
251     // Use split to separate the entry into its fields
252     std::vector<std::string> logEntryFields;
253     bmcweb::split(logEntryFields, entry, ',');
254     // We need at least a MessageId to be valid
255     auto logEntryIter = logEntryFields.begin();
256     if (logEntryIter == logEntryFields.end())
257     {
258         return LogParseError::parseFailed;
259     }
260     std::string& messageID = *logEntryIter;
261 
262     std::optional<registries::MessageId> msgComponents =
263         registries::getMessageComponents(messageID);
264     if (!msgComponents)
265     {
266         return LogParseError::parseFailed;
267     }
268 
269     std::optional<registries::RegistryEntryRef> registry =
270         registries::getRegistryFromPrefix(msgComponents->registryName);
271     if (!registry)
272     {
273         return LogParseError::messageIdNotInRegistry;
274     }
275 
276     // Get the Message from the MessageKey and RegistryEntries
277     const registries::Message* message = registries::getMessageFromRegistry(
278         msgComponents->messageKey, registry->get().entries);
279 
280     logEntryIter++;
281     if (message == nullptr)
282     {
283         BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry);
284         return LogParseError::messageIdNotInRegistry;
285     }
286 
287     const unsigned int& versionMajor = registry->get().header.versionMajor;
288     const unsigned int& versionMinor = registry->get().header.versionMinor;
289 
290     std::vector<std::string_view> messageArgs(logEntryIter,
291                                               logEntryFields.end());
292     messageArgs.resize(message->numberOfArgs);
293 
294     std::string msg =
295         redfish::registries::fillMessageArgs(messageArgs, message->message);
296     if (msg.empty())
297     {
298         return LogParseError::parseFailed;
299     }
300 
301     // Get the Created time from the timestamp. The log timestamp is in RFC3339
302     // format which matches the Redfish format except for the
303     // fractional seconds between the '.' and the '+', so just remove them.
304     std::size_t dot = timestamp.find_first_of('.');
305     std::size_t plus = timestamp.find_first_of('+');
306     if (dot != std::string::npos && plus != std::string::npos)
307     {
308         timestamp.erase(dot, plus - dot);
309     }
310 
311     // Fill in the log entry with the gathered data
312     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
313     logEntryJson["@odata.id"] =
314         boost::urls::format("/redfish/v1/{}/{}/LogServices/EventLog/Entries/{}",
315                             parentStr, childId, logEntryID);
316     logEntryJson["Name"] =
317         std::format("{} Event Log Entry", logEntryDescriptor);
318     logEntryJson["Id"] = logEntryID;
319     logEntryJson["Message"] = std::move(msg);
320     logEntryJson["MessageId"] =
321         std::format("{}.{}.{}.{}", msgComponents->registryName, versionMajor,
322                     versionMinor, msgComponents->messageKey);
323     logEntryJson["MessageArgs"] = messageArgs;
324     logEntryJson["EntryType"] = "Event";
325     logEntryJson["Severity"] = message->messageSeverity;
326     logEntryJson["Created"] = std::move(timestamp);
327     return LogParseError::success;
328 }
329 
handleSystemsAndManagersLogServiceEventLogLogEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,query_param::Query & delegatedQuery,LogServiceParent parent)330 inline void handleSystemsAndManagersLogServiceEventLogLogEntryCollection(
331     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
332     query_param::Query& delegatedQuery, LogServiceParent parent)
333 {
334     size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
335     size_t skip = delegatedQuery.skip.value_or(0);
336 
337     const std::string parentStr = logServiceParentToString(parent);
338     const std::string_view childId = getChildIdFromParent(parent);
339     const std::string logEntryDescriptor = getLogEntryDescriptor(parent);
340 
341     if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty())
342     {
343         messages::internalError(asyncResp->res);
344         return;
345     }
346 
347     // Collections don't include the static data added by SubRoute
348     // because it has a duplicate entry for members
349     asyncResp->res.jsonValue["@odata.type"] =
350         "#LogEntryCollection.LogEntryCollection";
351     asyncResp->res.jsonValue["@odata.id"] = std::format(
352         "/redfish/v1/{}/{}/LogServices/EventLog/Entries", parentStr, childId);
353     asyncResp->res.jsonValue["Name"] =
354         std::format("{} Event Log Entries", logEntryDescriptor);
355     asyncResp->res.jsonValue["Description"] =
356         std::format("Collection of {} Event Log Entries", logEntryDescriptor);
357 
358     nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
359     logEntryArray = nlohmann::json::array();
360     // Go through the log files and create a unique ID for each
361     // entry
362     std::vector<std::filesystem::path> redfishLogFiles;
363     getRedfishLogFiles(redfishLogFiles);
364     uint64_t entryCount = 0;
365     std::string logEntry;
366 
367     // Oldest logs are in the last file, so start there and loop
368     // backwards
369     for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
370     {
371         std::ifstream logStream(*it);
372         if (!logStream.is_open())
373         {
374             continue;
375         }
376 
377         // Reset the unique ID on the first entry
378         bool firstEntry = true;
379         while (std::getline(logStream, logEntry))
380         {
381             std::string idStr;
382             if (!getUniqueEntryID(logEntry, idStr, firstEntry))
383             {
384                 continue;
385             }
386             firstEntry = false;
387 
388             nlohmann::json::object_t bmcLogEntry;
389             LogParseError status =
390                 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry, parentStr,
391                                       childId, logEntryDescriptor);
392             if (status == LogParseError::messageIdNotInRegistry)
393             {
394                 continue;
395             }
396             if (status != LogParseError::success)
397             {
398                 messages::internalError(asyncResp->res);
399                 return;
400             }
401 
402             entryCount++;
403             // Handle paging using skip (number of entries to skip from the
404             // start) and top (number of entries to display)
405             if (entryCount <= skip || entryCount > skip + top)
406             {
407                 continue;
408             }
409 
410             logEntryArray.emplace_back(std::move(bmcLogEntry));
411         }
412     }
413     asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
414     if (skip + top < entryCount)
415     {
416         asyncResp->res.jsonValue["Members@odata.nextLink"] =
417             boost::urls::format(
418                 "/redfish/v1/{}/{}/LogServices/EventLog/Entries?$skip={}",
419                 parentStr, childId, std::to_string(skip + top));
420     }
421 }
422 
handleSystemsAndManagersLogServiceEventLogEntriesGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & param,LogServiceParent parent)423 inline void handleSystemsAndManagersLogServiceEventLogEntriesGet(
424     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
425     const std::string& param, LogServiceParent parent)
426 {
427     const std::string& targetID = param;
428 
429     const std::string parentStr = logServiceParentToString(parent);
430     const std::string_view childId = getChildIdFromParent(parent);
431     const std::string logEntryDescriptor = getLogEntryDescriptor(parent);
432 
433     if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty())
434     {
435         messages::internalError(asyncResp->res);
436         return;
437     }
438 
439     // Go through the log files and check the unique ID for each
440     // entry to find the target entry
441     std::vector<std::filesystem::path> redfishLogFiles;
442     getRedfishLogFiles(redfishLogFiles);
443     std::string logEntry;
444 
445     // Oldest logs are in the last file, so start there and loop
446     // backwards
447     for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
448     {
449         std::ifstream logStream(*it);
450         if (!logStream.is_open())
451         {
452             continue;
453         }
454 
455         // Reset the unique ID on the first entry
456         bool firstEntry = true;
457         while (std::getline(logStream, logEntry))
458         {
459             std::string idStr;
460             if (!getUniqueEntryID(logEntry, idStr, firstEntry))
461             {
462                 continue;
463             }
464             firstEntry = false;
465 
466             if (idStr == targetID)
467             {
468                 nlohmann::json::object_t bmcLogEntry;
469                 LogParseError status = fillEventLogEntryJson(
470                     idStr, logEntry, bmcLogEntry, parentStr, childId,
471                     logEntryDescriptor);
472                 if (status != LogParseError::success)
473                 {
474                     messages::internalError(asyncResp->res);
475                     return;
476                 }
477                 asyncResp->res.jsonValue.update(bmcLogEntry);
478                 return;
479             }
480         }
481     }
482     // Requested ID was not found
483     messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
484 }
485 
handleSystemsAndManagersLogServicesEventLogActionsClearPost(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)486 inline void handleSystemsAndManagersLogServicesEventLogActionsClearPost(
487     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
488 {
489     // Clear the EventLog by deleting the log files
490     std::vector<std::filesystem::path> redfishLogFiles;
491     if (getRedfishLogFiles(redfishLogFiles))
492     {
493         for (const std::filesystem::path& file : redfishLogFiles)
494         {
495             std::error_code ec;
496             std::filesystem::remove(file, ec);
497         }
498     }
499 
500     // Reload rsyslog so it knows to start new log files
501     dbus::utility::async_method_call(
502         asyncResp,
503         [asyncResp](const boost::system::error_code& ec) {
504             if (ec)
505             {
506                 BMCWEB_LOG_ERROR("Failed to reload rsyslog: {}", ec);
507                 messages::internalError(asyncResp->res);
508                 return;
509             }
510 
511             messages::success(asyncResp->res);
512         },
513         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
514         "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
515         "replace");
516 }
517 
518 /*
519  * DBus EventLog utilities
520  * */
521 
getProviderNotifyAction(const std::string & notify)522 inline std::optional<bool> getProviderNotifyAction(const std::string& notify)
523 {
524     std::optional<bool> notifyAction;
525     if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Notify")
526     {
527         notifyAction = true;
528     }
529     else if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Inhibit")
530     {
531         notifyAction = false;
532     }
533 
534     return notifyAction;
535 }
536 
translateSeverityDbusToRedfish(const std::string & s)537 inline std::string translateSeverityDbusToRedfish(const std::string& s)
538 {
539     if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
540         (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
541         (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
542         (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
543     {
544         return "Critical";
545     }
546     if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
547         (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
548         (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
549     {
550         return "OK";
551     }
552     if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
553     {
554         return "Warning";
555     }
556     return "";
557 }
558 
fillEventLogLogEntryFromDbusLogEntry(const DbusEventLogEntry & entry,nlohmann::json & objectToFillOut,const std::string & parentStr,const std::string_view childId,const std::string & logEntryDescriptor)559 inline void fillEventLogLogEntryFromDbusLogEntry(
560     const DbusEventLogEntry& entry, nlohmann::json& objectToFillOut,
561     const std::string& parentStr, const std::string_view childId,
562     const std::string& logEntryDescriptor)
563 {
564     objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
565     objectToFillOut["@odata.id"] =
566         boost::urls::format("/redfish/v1/{}/{}/LogServices/EventLog/Entries/{}",
567                             parentStr, childId, std::to_string(entry.Id));
568     objectToFillOut["Name"] =
569         std::format("{} Event Log Entry", logEntryDescriptor);
570     objectToFillOut["Id"] = std::to_string(entry.Id);
571     objectToFillOut["Message"] = entry.Message;
572     objectToFillOut["Resolved"] = entry.Resolved;
573     std::optional<bool> notifyAction =
574         getProviderNotifyAction(entry.ServiceProviderNotify);
575     if (notifyAction)
576     {
577         objectToFillOut["ServiceProviderNotified"] = *notifyAction;
578     }
579     if ((entry.Resolution != nullptr) && !entry.Resolution->empty())
580     {
581         objectToFillOut["Resolution"] = *entry.Resolution;
582     }
583     objectToFillOut["EntryType"] = "Event";
584     objectToFillOut["Severity"] =
585         translateSeverityDbusToRedfish(entry.Severity);
586     objectToFillOut["Created"] =
587         redfish::time_utils::getDateTimeUintMs(entry.Timestamp);
588     objectToFillOut["Modified"] =
589         redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp);
590     if (entry.Path != nullptr)
591     {
592         objectToFillOut["AdditionalDataURI"] = boost::urls::format(
593             "/redfish/v1/{}/{}/LogServices/EventLog/Entries/{}/attachment",
594             parentStr, childId, std::to_string(entry.Id));
595     }
596 }
597 
afterLogEntriesGetManagedObjects(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & parentStr,const std::string_view childId,const std::string & logEntryDescriptor,const boost::system::error_code & ec,const dbus::utility::ManagedObjectType & resp)598 inline void afterLogEntriesGetManagedObjects(
599     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
600     const std::string& parentStr, const std::string_view childId,
601     const std::string& logEntryDescriptor, const boost::system::error_code& ec,
602     const dbus::utility::ManagedObjectType& resp)
603 {
604     if (ec)
605     {
606         // TODO Handle for specific error code
607         BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}",
608                          ec);
609         messages::internalError(asyncResp->res);
610         return;
611     }
612     nlohmann::json::array_t entriesArray;
613     for (const auto& objectPath : resp)
614     {
615         dbus::utility::DBusPropertiesMap propsFlattened;
616         auto isEntry =
617             std::ranges::find_if(objectPath.second, [](const auto& object) {
618                 return object.first == "xyz.openbmc_project.Logging.Entry";
619             });
620         if (isEntry == objectPath.second.end())
621         {
622             continue;
623         }
624 
625         for (const auto& interfaceMap : objectPath.second)
626         {
627             for (const auto& propertyMap : interfaceMap.second)
628             {
629                 propsFlattened.emplace_back(propertyMap.first,
630                                             propertyMap.second);
631             }
632         }
633         std::optional<DbusEventLogEntry> optEntry =
634             fillDbusEventLogEntryFromPropertyMap(propsFlattened);
635 
636         if (!optEntry.has_value())
637         {
638             messages::internalError(asyncResp->res);
639             return;
640         }
641         fillEventLogLogEntryFromDbusLogEntry(
642             *optEntry, entriesArray.emplace_back(), parentStr, childId,
643             logEntryDescriptor);
644     }
645 
646     redfish::json_util::sortJsonArrayByKey(entriesArray, "Id");
647     asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
648     asyncResp->res.jsonValue["Members"] = std::move(entriesArray);
649 }
650 
dBusEventLogEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,LogServiceParent parent)651 inline void dBusEventLogEntryCollection(
652     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
653     LogServiceParent parent)
654 {
655     const std::string_view childId = getChildIdFromParent(parent);
656     const std::string parentStr = logServiceParentToString(parent);
657     const std::string logEntryDescriptor = getLogEntryDescriptor(parent);
658 
659     if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty())
660     {
661         messages::internalError(asyncResp->res);
662         return;
663     }
664 
665     // Collections don't include the static data added by SubRoute
666     // because it has a duplicate entry for members
667     asyncResp->res.jsonValue["@odata.type"] =
668         "#LogEntryCollection.LogEntryCollection";
669     asyncResp->res.jsonValue["@odata.id"] = std::format(
670         "/redfish/v1/{}/{}/LogServices/EventLog/Entries", parentStr, childId);
671     asyncResp->res.jsonValue["Name"] =
672         std::format("{} Event Log Entries", logEntryDescriptor);
673     asyncResp->res.jsonValue["Description"] =
674         std::format("Collection of {} Event Log Entries", logEntryDescriptor);
675 
676     // DBus implementation of EventLog/Entries
677     // Make call to Logging Service to find all log entry objects
678     sdbusplus::message::object_path path("/xyz/openbmc_project/logging");
679     dbus::utility::getManagedObjects(
680         "xyz.openbmc_project.Logging", path,
681         [asyncResp, parentStr, childId,
682          logEntryDescriptor](const boost::system::error_code& ec,
683                              const dbus::utility::ManagedObjectType& resp) {
684             afterLogEntriesGetManagedObjects(asyncResp, parentStr, childId,
685                                              logEntryDescriptor, ec, resp);
686         });
687 }
688 
afterDBusEventLogEntryGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & parentStr,const std::string_view childId,const std::string & logEntryDescriptor,const std::string & entryID,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & resp)689 inline void afterDBusEventLogEntryGet(
690     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
691     const std::string& parentStr, const std::string_view childId,
692     const std::string& logEntryDescriptor, const std::string& entryID,
693     const boost::system::error_code& ec,
694     const dbus::utility::DBusPropertiesMap& resp)
695 {
696     if (ec.value() == EBADR)
697     {
698         messages::resourceNotFound(asyncResp->res, "EventLogEntry", entryID);
699         return;
700     }
701     if (ec)
702     {
703         BMCWEB_LOG_ERROR("EventLogEntry (DBus) resp_handler got error {}", ec);
704         messages::internalError(asyncResp->res);
705         return;
706     }
707 
708     std::optional<DbusEventLogEntry> optEntry =
709         fillDbusEventLogEntryFromPropertyMap(resp);
710 
711     if (!optEntry.has_value())
712     {
713         messages::internalError(asyncResp->res);
714         return;
715     }
716 
717     fillEventLogLogEntryFromDbusLogEntry(
718         *optEntry, asyncResp->res.jsonValue, parentStr, childId,
719         logEntryDescriptor);
720 }
721 
dBusEventLogEntryGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,LogServiceParent parent,std::string entryID)722 inline void dBusEventLogEntryGet(
723     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
724     LogServiceParent parent, std::string entryID)
725 {
726     const std::string parentStr = logServiceParentToString(parent);
727     const std::string_view childId = getChildIdFromParent(parent);
728     const std::string logEntryDescriptor = getLogEntryDescriptor(parent);
729 
730     if (parentStr.empty() || childId.empty() || logEntryDescriptor.empty())
731     {
732         messages::internalError(asyncResp->res);
733         return;
734     }
735 
736     dbus::utility::escapePathForDbus(entryID);
737 
738     // DBus implementation of EventLog/Entries
739     // Make call to Logging Service to find all log entry objects
740     dbus::utility::getAllProperties(
741         "xyz.openbmc_project.Logging",
742         "/xyz/openbmc_project/logging/entry/" + entryID, "",
743         std::bind_front(afterDBusEventLogEntryGet, asyncResp, parentStr,
744                         childId, logEntryDescriptor, entryID));
745 }
746 
dBusEventLogEntryPatch(const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryId)747 inline void dBusEventLogEntryPatch(
748     const crow::Request& req,
749     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
750     const std::string& entryId)
751 {
752     std::optional<bool> resolved;
753 
754     if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved))
755     {
756         return;
757     }
758     BMCWEB_LOG_DEBUG("Set Resolved");
759 
760     setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging",
761                     "/xyz/openbmc_project/logging/entry/" + entryId,
762                     "xyz.openbmc_project.Logging.Entry", "Resolved",
763                     resolved.value_or(false));
764 }
765 
dBusEventLogEntryDelete(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string entryID)766 inline void dBusEventLogEntryDelete(
767     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
768 {
769     BMCWEB_LOG_DEBUG("Do delete single event entries.");
770 
771     dbus::utility::escapePathForDbus(entryID);
772 
773     // Process response from Logging service.
774     auto respHandler = [asyncResp,
775                         entryID](const boost::system::error_code& ec) {
776         BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done");
777         if (ec)
778         {
779             if (ec.value() == EBADR)
780             {
781                 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
782                 return;
783             }
784             // TODO Handle for specific error code
785             BMCWEB_LOG_ERROR(
786                 "EventLogEntry (DBus) doDelete respHandler got error {}", ec);
787             asyncResp->res.result(
788                 boost::beast::http::status::internal_server_error);
789             return;
790         }
791 
792         messages::success(asyncResp->res);
793     };
794 
795     // Make call to Logging service to request Delete Log
796     dbus::utility::async_method_call(
797         asyncResp, respHandler, "xyz.openbmc_project.Logging",
798         "/xyz/openbmc_project/logging/entry/" + entryID,
799         "xyz.openbmc_project.Object.Delete", "Delete");
800 }
801 
dBusLogServiceActionsClear(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)802 inline void dBusLogServiceActionsClear(
803     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
804 {
805     BMCWEB_LOG_DEBUG("Do delete all entries.");
806 
807     // Process response from Logging service.
808     auto respHandler = [asyncResp](const boost::system::error_code& ec) {
809         BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done");
810         if (ec)
811         {
812             // TODO Handle for specific error code
813             BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec);
814             asyncResp->res.result(
815                 boost::beast::http::status::internal_server_error);
816             return;
817         }
818 
819         messages::success(asyncResp->res);
820     };
821 
822     // Make call to Logging service to request Clear Log
823     dbus::utility::async_method_call(
824         asyncResp, respHandler, "xyz.openbmc_project.Logging",
825         "/xyz/openbmc_project/logging",
826         "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
827 }
828 
downloadEventLogEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & downloadEntryType)829 inline void downloadEventLogEntry(
830     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
831     const std::string& entryID, const std::string& downloadEntryType)
832 {
833     std::string entryPath =
834         sdbusplus::message::object_path("/xyz/openbmc_project/logging/entry") /
835         entryID;
836 
837     auto downloadEventLogEntryHandler =
838         [asyncResp, entryID,
839          downloadEntryType](const boost::system::error_code& ec,
840                             const sdbusplus::message::unix_fd& unixfd) {
841             log_services_utils::downloadEntryCallback(
842                 asyncResp, entryID, downloadEntryType, ec, unixfd);
843         };
844 
845     dbus::utility::async_method_call(
846         asyncResp, std::move(downloadEventLogEntryHandler),
847         "xyz.openbmc_project.Logging", entryPath,
848         "xyz.openbmc_project.Logging.Entry", "GetEntry");
849 }
850 } // namespace eventlog_utils
851 } // namespace redfish
852