xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision 15a86ff65a0ff588adabbeaac7c0e2fe51932357)
11da66f75SEd Tanous /*
21da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation
31da66f75SEd Tanous //
41da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License");
51da66f75SEd Tanous // you may not use this file except in compliance with the License.
61da66f75SEd Tanous // You may obtain a copy of the License at
71da66f75SEd Tanous //
81da66f75SEd Tanous //      http://www.apache.org/licenses/LICENSE-2.0
91da66f75SEd Tanous //
101da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software
111da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS,
121da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131da66f75SEd Tanous // See the License for the specific language governing permissions and
141da66f75SEd Tanous // limitations under the License.
151da66f75SEd Tanous */
161da66f75SEd Tanous #pragma once
171da66f75SEd Tanous 
181da66f75SEd Tanous #include "node.hpp"
194851d45dSJason M. Bills #include "registries.hpp"
204851d45dSJason M. Bills #include "registries/base_message_registry.hpp"
214851d45dSJason M. Bills #include "registries/openbmc_message_registry.hpp"
221da66f75SEd Tanous 
23e1f26343SJason M. Bills #include <systemd/sd-journal.h>
24e1f26343SJason M. Bills 
254851d45dSJason M. Bills #include <boost/algorithm/string/split.hpp>
264851d45dSJason M. Bills #include <boost/beast/core/span.hpp>
271da66f75SEd Tanous #include <boost/container/flat_map.hpp>
28cb92c03bSAndrew Geissler #include <error_messages.hpp>
294418c7f0SJames Feist #include <filesystem>
30cd225da8SJason M. Bills #include <string_view>
31abf2add6SEd Tanous #include <variant>
321da66f75SEd Tanous 
331da66f75SEd Tanous namespace redfish
341da66f75SEd Tanous {
351da66f75SEd Tanous 
36424c4176SJason M. Bills constexpr char const *CrashdumpObject = "com.intel.crashdump";
37424c4176SJason M. Bills constexpr char const *CrashdumpPath = "/com/intel/crashdump";
38424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
39424c4176SJason M. Bills constexpr char const *CrashdumpInterface = "com.intel.crashdump";
40424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandInterface =
41424c4176SJason M. Bills     "com.intel.crashdump.OnDemand";
42424c4176SJason M. Bills constexpr char const *CrashdumpRawPECIInterface =
43424c4176SJason M. Bills     "com.intel.crashdump.SendRawPeci";
441da66f75SEd Tanous 
454851d45dSJason M. Bills namespace message_registries
464851d45dSJason M. Bills {
474851d45dSJason M. Bills static const Message *getMessageFromRegistry(
484851d45dSJason M. Bills     const std::string &messageKey,
494851d45dSJason M. Bills     const boost::beast::span<const MessageEntry> registry)
504851d45dSJason M. Bills {
514851d45dSJason M. Bills     boost::beast::span<const MessageEntry>::const_iterator messageIt =
524851d45dSJason M. Bills         std::find_if(registry.cbegin(), registry.cend(),
534851d45dSJason M. Bills                      [&messageKey](const MessageEntry &messageEntry) {
544851d45dSJason M. Bills                          return !std::strcmp(messageEntry.first,
554851d45dSJason M. Bills                                              messageKey.c_str());
564851d45dSJason M. Bills                      });
574851d45dSJason M. Bills     if (messageIt != registry.cend())
584851d45dSJason M. Bills     {
594851d45dSJason M. Bills         return &messageIt->second;
604851d45dSJason M. Bills     }
614851d45dSJason M. Bills 
624851d45dSJason M. Bills     return nullptr;
634851d45dSJason M. Bills }
644851d45dSJason M. Bills 
654851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID)
664851d45dSJason M. Bills {
674851d45dSJason M. Bills     // Redfish MessageIds are in the form
684851d45dSJason M. Bills     // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
694851d45dSJason M. Bills     // the right Message
704851d45dSJason M. Bills     std::vector<std::string> fields;
714851d45dSJason M. Bills     fields.reserve(4);
724851d45dSJason M. Bills     boost::split(fields, messageID, boost::is_any_of("."));
734851d45dSJason M. Bills     std::string &registryName = fields[0];
744851d45dSJason M. Bills     std::string &messageKey = fields[3];
754851d45dSJason M. Bills 
764851d45dSJason M. Bills     // Find the right registry and check it for the MessageKey
774851d45dSJason M. Bills     if (std::string(base::header.registryPrefix) == registryName)
784851d45dSJason M. Bills     {
794851d45dSJason M. Bills         return getMessageFromRegistry(
804851d45dSJason M. Bills             messageKey, boost::beast::span<const MessageEntry>(base::registry));
814851d45dSJason M. Bills     }
824851d45dSJason M. Bills     if (std::string(openbmc::header.registryPrefix) == registryName)
834851d45dSJason M. Bills     {
844851d45dSJason M. Bills         return getMessageFromRegistry(
854851d45dSJason M. Bills             messageKey,
864851d45dSJason M. Bills             boost::beast::span<const MessageEntry>(openbmc::registry));
874851d45dSJason M. Bills     }
884851d45dSJason M. Bills     return nullptr;
894851d45dSJason M. Bills }
904851d45dSJason M. Bills } // namespace message_registries
914851d45dSJason M. Bills 
92f6150403SJames Feist namespace fs = std::filesystem;
931da66f75SEd Tanous 
94cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map<
95cb92c03bSAndrew Geissler     std::string,
96cb92c03bSAndrew Geissler     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
97cb92c03bSAndrew Geissler                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
98cb92c03bSAndrew Geissler 
99cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map<
100cb92c03bSAndrew Geissler     sdbusplus::message::object_path,
101cb92c03bSAndrew Geissler     boost::container::flat_map<std::string, GetManagedPropertyType>>;
102cb92c03bSAndrew Geissler 
103cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s)
104cb92c03bSAndrew Geissler {
105cb92c03bSAndrew Geissler     if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
106cb92c03bSAndrew Geissler     {
107cb92c03bSAndrew Geissler         return "Critical";
108cb92c03bSAndrew Geissler     }
109cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
110cb92c03bSAndrew Geissler     {
111cb92c03bSAndrew Geissler         return "Critical";
112cb92c03bSAndrew Geissler     }
113cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
114cb92c03bSAndrew Geissler     {
115cb92c03bSAndrew Geissler         return "OK";
116cb92c03bSAndrew Geissler     }
117cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
118cb92c03bSAndrew Geissler     {
119cb92c03bSAndrew Geissler         return "Critical";
120cb92c03bSAndrew Geissler     }
121cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
122cb92c03bSAndrew Geissler     {
123cb92c03bSAndrew Geissler         return "Critical";
124cb92c03bSAndrew Geissler     }
125cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
126cb92c03bSAndrew Geissler     {
127cb92c03bSAndrew Geissler         return "OK";
128cb92c03bSAndrew Geissler     }
129cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
130cb92c03bSAndrew Geissler     {
131cb92c03bSAndrew Geissler         return "OK";
132cb92c03bSAndrew Geissler     }
133cb92c03bSAndrew Geissler     else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
134cb92c03bSAndrew Geissler     {
135cb92c03bSAndrew Geissler         return "Warning";
136cb92c03bSAndrew Geissler     }
137cb92c03bSAndrew Geissler     return "";
138cb92c03bSAndrew Geissler }
139cb92c03bSAndrew Geissler 
14016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
14139e77504SEd Tanous                               const std::string_view &field,
14239e77504SEd Tanous                               std::string_view &contents)
14316428a1aSJason M. Bills {
14416428a1aSJason M. Bills     const char *data = nullptr;
14516428a1aSJason M. Bills     size_t length = 0;
14616428a1aSJason M. Bills     int ret = 0;
14716428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
148b01bf299SEd Tanous     ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
149b01bf299SEd Tanous                               &length);
15016428a1aSJason M. Bills     if (ret < 0)
15116428a1aSJason M. Bills     {
15216428a1aSJason M. Bills         return ret;
15316428a1aSJason M. Bills     }
15439e77504SEd Tanous     contents = std::string_view(data, length);
15516428a1aSJason M. Bills     // Only use the content after the "=" character.
15616428a1aSJason M. Bills     contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
15716428a1aSJason M. Bills     return ret;
15816428a1aSJason M. Bills }
15916428a1aSJason M. Bills 
16016428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal,
16139e77504SEd Tanous                               const std::string_view &field, const int &base,
16216428a1aSJason M. Bills                               int &contents)
16316428a1aSJason M. Bills {
16416428a1aSJason M. Bills     int ret = 0;
16539e77504SEd Tanous     std::string_view metadata;
16616428a1aSJason M. Bills     // Get the metadata from the requested field of the journal entry
16716428a1aSJason M. Bills     ret = getJournalMetadata(journal, field, metadata);
16816428a1aSJason M. Bills     if (ret < 0)
16916428a1aSJason M. Bills     {
17016428a1aSJason M. Bills         return ret;
17116428a1aSJason M. Bills     }
172b01bf299SEd Tanous     contents = strtol(metadata.data(), nullptr, base);
17316428a1aSJason M. Bills     return ret;
17416428a1aSJason M. Bills }
17516428a1aSJason M. Bills 
17616428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
17716428a1aSJason M. Bills {
17816428a1aSJason M. Bills     int ret = 0;
17916428a1aSJason M. Bills     uint64_t timestamp = 0;
18016428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &timestamp);
18116428a1aSJason M. Bills     if (ret < 0)
18216428a1aSJason M. Bills     {
18316428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
18416428a1aSJason M. Bills                          << strerror(-ret);
18516428a1aSJason M. Bills         return false;
18616428a1aSJason M. Bills     }
18716428a1aSJason M. Bills     time_t t =
18816428a1aSJason M. Bills         static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
18916428a1aSJason M. Bills     struct tm *loctime = localtime(&t);
19016428a1aSJason M. Bills     char entryTime[64] = {};
19116428a1aSJason M. Bills     if (NULL != loctime)
19216428a1aSJason M. Bills     {
19316428a1aSJason M. Bills         strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
19416428a1aSJason M. Bills     }
19516428a1aSJason M. Bills     // Insert the ':' into the timezone
19639e77504SEd Tanous     std::string_view t1(entryTime);
19739e77504SEd Tanous     std::string_view t2(entryTime);
19816428a1aSJason M. Bills     if (t1.size() > 2 && t2.size() > 2)
19916428a1aSJason M. Bills     {
20016428a1aSJason M. Bills         t1.remove_suffix(2);
20116428a1aSJason M. Bills         t2.remove_prefix(t2.size() - 2);
20216428a1aSJason M. Bills     }
20339e77504SEd Tanous     entryTimestamp = std::string(t1) + ":" + std::string(t2);
20416428a1aSJason M. Bills     return true;
20516428a1aSJason M. Bills }
20616428a1aSJason M. Bills 
20716428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req,
20816428a1aSJason M. Bills                          long &skip)
20916428a1aSJason M. Bills {
21016428a1aSJason M. Bills     char *skipParam = req.urlParams.get("$skip");
21116428a1aSJason M. Bills     if (skipParam != nullptr)
21216428a1aSJason M. Bills     {
21316428a1aSJason M. Bills         char *ptr = nullptr;
21416428a1aSJason M. Bills         skip = std::strtol(skipParam, &ptr, 10);
21516428a1aSJason M. Bills         if (*skipParam == '\0' || *ptr != '\0')
21616428a1aSJason M. Bills         {
21716428a1aSJason M. Bills 
21816428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(skipParam),
21916428a1aSJason M. Bills                                                    "$skip");
22016428a1aSJason M. Bills             return false;
22116428a1aSJason M. Bills         }
22216428a1aSJason M. Bills         if (skip < 0)
22316428a1aSJason M. Bills         {
22416428a1aSJason M. Bills 
22516428a1aSJason M. Bills             messages::queryParameterOutOfRange(res, std::to_string(skip),
22616428a1aSJason M. Bills                                                "$skip", "greater than 0");
22716428a1aSJason M. Bills             return false;
22816428a1aSJason M. Bills         }
22916428a1aSJason M. Bills     }
23016428a1aSJason M. Bills     return true;
23116428a1aSJason M. Bills }
23216428a1aSJason M. Bills 
23316428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000;
23416428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req,
23516428a1aSJason M. Bills                         long &top)
23616428a1aSJason M. Bills {
23716428a1aSJason M. Bills     char *topParam = req.urlParams.get("$top");
23816428a1aSJason M. Bills     if (topParam != nullptr)
23916428a1aSJason M. Bills     {
24016428a1aSJason M. Bills         char *ptr = nullptr;
24116428a1aSJason M. Bills         top = std::strtol(topParam, &ptr, 10);
24216428a1aSJason M. Bills         if (*topParam == '\0' || *ptr != '\0')
24316428a1aSJason M. Bills         {
24416428a1aSJason M. Bills             messages::queryParameterValueTypeError(res, std::string(topParam),
24516428a1aSJason M. Bills                                                    "$top");
24616428a1aSJason M. Bills             return false;
24716428a1aSJason M. Bills         }
24816428a1aSJason M. Bills         if (top < 1 || top > maxEntriesPerPage)
24916428a1aSJason M. Bills         {
25016428a1aSJason M. Bills 
25116428a1aSJason M. Bills             messages::queryParameterOutOfRange(
25216428a1aSJason M. Bills                 res, std::to_string(top), "$top",
25316428a1aSJason M. Bills                 "1-" + std::to_string(maxEntriesPerPage));
25416428a1aSJason M. Bills             return false;
25516428a1aSJason M. Bills         }
25616428a1aSJason M. Bills     }
25716428a1aSJason M. Bills     return true;
25816428a1aSJason M. Bills }
25916428a1aSJason M. Bills 
26016428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
26116428a1aSJason M. Bills {
26216428a1aSJason M. Bills     int ret = 0;
26316428a1aSJason M. Bills     static uint64_t prevTs = 0;
26416428a1aSJason M. Bills     static int index = 0;
26516428a1aSJason M. Bills     // Get the entry timestamp
26616428a1aSJason M. Bills     uint64_t curTs = 0;
26716428a1aSJason M. Bills     ret = sd_journal_get_realtime_usec(journal, &curTs);
26816428a1aSJason M. Bills     if (ret < 0)
26916428a1aSJason M. Bills     {
27016428a1aSJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
27116428a1aSJason M. Bills                          << strerror(-ret);
27216428a1aSJason M. Bills         return false;
27316428a1aSJason M. Bills     }
27416428a1aSJason M. Bills     // If the timestamp isn't unique, increment the index
27516428a1aSJason M. Bills     if (curTs == prevTs)
27616428a1aSJason M. Bills     {
27716428a1aSJason M. Bills         index++;
27816428a1aSJason M. Bills     }
27916428a1aSJason M. Bills     else
28016428a1aSJason M. Bills     {
28116428a1aSJason M. Bills         // Otherwise, reset it
28216428a1aSJason M. Bills         index = 0;
28316428a1aSJason M. Bills     }
28416428a1aSJason M. Bills     // Save the timestamp
28516428a1aSJason M. Bills     prevTs = curTs;
28616428a1aSJason M. Bills 
28716428a1aSJason M. Bills     entryID = std::to_string(curTs);
28816428a1aSJason M. Bills     if (index > 0)
28916428a1aSJason M. Bills     {
29016428a1aSJason M. Bills         entryID += "_" + std::to_string(index);
29116428a1aSJason M. Bills     }
29216428a1aSJason M. Bills     return true;
29316428a1aSJason M. Bills }
29416428a1aSJason M. Bills 
29595820184SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID)
29695820184SJason M. Bills {
29795820184SJason M. Bills     static uint64_t prevTs = 0;
29895820184SJason M. Bills     static int index = 0;
29995820184SJason M. Bills     // Get the entry timestamp
30095820184SJason M. Bills     uint64_t curTs = 0;
30195820184SJason M. Bills     std::tm timeStruct = {};
30295820184SJason M. Bills     std::istringstream entryStream(logEntry);
30395820184SJason M. Bills     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
30495820184SJason M. Bills     {
30595820184SJason M. Bills         curTs = std::mktime(&timeStruct);
30695820184SJason M. Bills     }
30795820184SJason M. Bills     // If the timestamp isn't unique, increment the index
30895820184SJason M. Bills     if (curTs == prevTs)
30995820184SJason M. Bills     {
31095820184SJason M. Bills         index++;
31195820184SJason M. Bills     }
31295820184SJason M. Bills     else
31395820184SJason M. Bills     {
31495820184SJason M. Bills         // Otherwise, reset it
31595820184SJason M. Bills         index = 0;
31695820184SJason M. Bills     }
31795820184SJason M. Bills     // Save the timestamp
31895820184SJason M. Bills     prevTs = curTs;
31995820184SJason M. Bills 
32095820184SJason M. Bills     entryID = std::to_string(curTs);
32195820184SJason M. Bills     if (index > 0)
32295820184SJason M. Bills     {
32395820184SJason M. Bills         entryID += "_" + std::to_string(index);
32495820184SJason M. Bills     }
32595820184SJason M. Bills     return true;
32695820184SJason M. Bills }
32795820184SJason M. Bills 
32816428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
32916428a1aSJason M. Bills                                uint64_t &timestamp, uint16_t &index)
33016428a1aSJason M. Bills {
33116428a1aSJason M. Bills     if (entryID.empty())
33216428a1aSJason M. Bills     {
33316428a1aSJason M. Bills         return false;
33416428a1aSJason M. Bills     }
33516428a1aSJason M. Bills     // Convert the unique ID back to a timestamp to find the entry
33639e77504SEd Tanous     std::string_view tsStr(entryID);
33716428a1aSJason M. Bills 
33816428a1aSJason M. Bills     auto underscorePos = tsStr.find("_");
33916428a1aSJason M. Bills     if (underscorePos != tsStr.npos)
34016428a1aSJason M. Bills     {
34116428a1aSJason M. Bills         // Timestamp has an index
34216428a1aSJason M. Bills         tsStr.remove_suffix(tsStr.size() - underscorePos);
34339e77504SEd Tanous         std::string_view indexStr(entryID);
34416428a1aSJason M. Bills         indexStr.remove_prefix(underscorePos + 1);
34516428a1aSJason M. Bills         std::size_t pos;
34616428a1aSJason M. Bills         try
34716428a1aSJason M. Bills         {
34839e77504SEd Tanous             index = std::stoul(std::string(indexStr), &pos);
34916428a1aSJason M. Bills         }
350b01bf299SEd Tanous         catch (std::invalid_argument)
35116428a1aSJason M. Bills         {
35216428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
35316428a1aSJason M. Bills             return false;
35416428a1aSJason M. Bills         }
355b01bf299SEd Tanous         catch (std::out_of_range)
35616428a1aSJason M. Bills         {
35716428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
35816428a1aSJason M. Bills             return false;
35916428a1aSJason M. Bills         }
36016428a1aSJason M. Bills         if (pos != indexStr.size())
36116428a1aSJason M. Bills         {
36216428a1aSJason M. Bills             messages::resourceMissingAtURI(res, entryID);
36316428a1aSJason M. Bills             return false;
36416428a1aSJason M. Bills         }
36516428a1aSJason M. Bills     }
36616428a1aSJason M. Bills     // Timestamp has no index
36716428a1aSJason M. Bills     std::size_t pos;
36816428a1aSJason M. Bills     try
36916428a1aSJason M. Bills     {
37039e77504SEd Tanous         timestamp = std::stoull(std::string(tsStr), &pos);
37116428a1aSJason M. Bills     }
372b01bf299SEd Tanous     catch (std::invalid_argument)
37316428a1aSJason M. Bills     {
37416428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
37516428a1aSJason M. Bills         return false;
37616428a1aSJason M. Bills     }
377b01bf299SEd Tanous     catch (std::out_of_range)
37816428a1aSJason M. Bills     {
37916428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
38016428a1aSJason M. Bills         return false;
38116428a1aSJason M. Bills     }
38216428a1aSJason M. Bills     if (pos != tsStr.size())
38316428a1aSJason M. Bills     {
38416428a1aSJason M. Bills         messages::resourceMissingAtURI(res, entryID);
38516428a1aSJason M. Bills         return false;
38616428a1aSJason M. Bills     }
38716428a1aSJason M. Bills     return true;
38816428a1aSJason M. Bills }
38916428a1aSJason M. Bills 
39095820184SJason M. Bills static bool
39195820184SJason M. Bills     getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
39295820184SJason M. Bills {
39395820184SJason M. Bills     static const std::filesystem::path redfishLogDir = "/var/log";
39495820184SJason M. Bills     static const std::string redfishLogFilename = "redfish";
39595820184SJason M. Bills 
39695820184SJason M. Bills     // Loop through the directory looking for redfish log files
39795820184SJason M. Bills     for (const std::filesystem::directory_entry &dirEnt :
39895820184SJason M. Bills          std::filesystem::directory_iterator(redfishLogDir))
39995820184SJason M. Bills     {
40095820184SJason M. Bills         // If we find a redfish log file, save the path
40195820184SJason M. Bills         std::string filename = dirEnt.path().filename();
40295820184SJason M. Bills         if (boost::starts_with(filename, redfishLogFilename))
40395820184SJason M. Bills         {
40495820184SJason M. Bills             redfishLogFiles.emplace_back(redfishLogDir / filename);
40595820184SJason M. Bills         }
40695820184SJason M. Bills     }
40795820184SJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
40895820184SJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
40995820184SJason M. Bills     // can just sort the list to get them in order from newest to oldest
41095820184SJason M. Bills     std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
41195820184SJason M. Bills 
41295820184SJason M. Bills     return !redfishLogFiles.empty();
41395820184SJason M. Bills }
41495820184SJason M. Bills 
415c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node
4161da66f75SEd Tanous {
4171da66f75SEd Tanous   public:
4181da66f75SEd Tanous     template <typename CrowApp>
419c4bf6374SJason M. Bills     SystemLogServiceCollection(CrowApp &app) :
420029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/")
421c4bf6374SJason M. Bills     {
422c4bf6374SJason M. Bills         entityPrivileges = {
423c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
424c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
425c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
426c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
427c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
428c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
429c4bf6374SJason M. Bills     }
430c4bf6374SJason M. Bills 
431c4bf6374SJason M. Bills   private:
432c4bf6374SJason M. Bills     /**
433c4bf6374SJason M. Bills      * Functions triggers appropriate requests on DBus
434c4bf6374SJason M. Bills      */
435c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
436c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
437c4bf6374SJason M. Bills     {
438c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
439c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
440c4bf6374SJason M. Bills         // it has a duplicate entry for members
441c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
442c4bf6374SJason M. Bills             "#LogServiceCollection.LogServiceCollection";
443c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
444c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
445c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
446029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices";
447c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
448c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
449c4bf6374SJason M. Bills             "Collection of LogServices for this Computer System";
450c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
451c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
452029573d4SEd Tanous         logServiceArray.push_back(
453029573d4SEd Tanous             {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
454d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
455d53dd41fSJason M. Bills         logServiceArray.push_back(
456cb92c03bSAndrew Geissler             {{ "@odata.id",
457424c4176SJason M. Bills                "/redfish/v1/Systems/system/LogServices/Crashdump" }});
458d53dd41fSJason M. Bills #endif
459c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
460c4bf6374SJason M. Bills             logServiceArray.size();
461c4bf6374SJason M. Bills     }
462c4bf6374SJason M. Bills };
463c4bf6374SJason M. Bills 
464c4bf6374SJason M. Bills class EventLogService : public Node
465c4bf6374SJason M. Bills {
466c4bf6374SJason M. Bills   public:
467c4bf6374SJason M. Bills     template <typename CrowApp>
468c4bf6374SJason M. Bills     EventLogService(CrowApp &app) :
469029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
470c4bf6374SJason M. Bills     {
471c4bf6374SJason M. Bills         entityPrivileges = {
472c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
473c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
474c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
475c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
476c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
477c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
478c4bf6374SJason M. Bills     }
479c4bf6374SJason M. Bills 
480c4bf6374SJason M. Bills   private:
481c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
482c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
483c4bf6374SJason M. Bills     {
484c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
485c4bf6374SJason M. Bills 
486c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
487029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog";
488c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
489c4bf6374SJason M. Bills             "#LogService.v1_1_0.LogService";
490c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
491c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
492c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Event Log Service";
493c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "System Event Log Service";
494c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Event Log";
495c4bf6374SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
496c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
497c4bf6374SJason M. Bills             {"@odata.id",
498029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
499489640c6SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
500489640c6SJason M. Bills             {{"#LogService.ClearLog",
501489640c6SJason M. Bills               {{"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
502489640c6SJason M. Bills                           "Actions/LogService.ClearLog"}}}}};
503489640c6SJason M. Bills     }
504489640c6SJason M. Bills };
505489640c6SJason M. Bills 
506489640c6SJason M. Bills class EventLogClear : public Node
507489640c6SJason M. Bills {
508489640c6SJason M. Bills   public:
509489640c6SJason M. Bills     EventLogClear(CrowApp &app) :
510489640c6SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
511489640c6SJason M. Bills                   "LogService.ClearLog/")
512489640c6SJason M. Bills     {
513489640c6SJason M. Bills         entityPrivileges = {
514489640c6SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
515489640c6SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
516489640c6SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
517489640c6SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
518489640c6SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
519489640c6SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
520489640c6SJason M. Bills     }
521489640c6SJason M. Bills 
522489640c6SJason M. Bills   private:
523489640c6SJason M. Bills     void doPost(crow::Response &res, const crow::Request &req,
524489640c6SJason M. Bills                 const std::vector<std::string> &params) override
525489640c6SJason M. Bills     {
526489640c6SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
527489640c6SJason M. Bills 
528489640c6SJason M. Bills         // Clear the EventLog by deleting the log files
529489640c6SJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
530489640c6SJason M. Bills         if (getRedfishLogFiles(redfishLogFiles))
531489640c6SJason M. Bills         {
532489640c6SJason M. Bills             for (const std::filesystem::path &file : redfishLogFiles)
533489640c6SJason M. Bills             {
534489640c6SJason M. Bills                 std::error_code ec;
535489640c6SJason M. Bills                 std::filesystem::remove(file, ec);
536489640c6SJason M. Bills             }
537489640c6SJason M. Bills         }
538489640c6SJason M. Bills 
539489640c6SJason M. Bills         // Reload rsyslog so it knows to start new log files
540489640c6SJason M. Bills         crow::connections::systemBus->async_method_call(
541489640c6SJason M. Bills             [asyncResp](const boost::system::error_code ec) {
542489640c6SJason M. Bills                 if (ec)
543489640c6SJason M. Bills                 {
544489640c6SJason M. Bills                     BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
545489640c6SJason M. Bills                     messages::internalError(asyncResp->res);
546489640c6SJason M. Bills                     return;
547489640c6SJason M. Bills                 }
548489640c6SJason M. Bills 
549489640c6SJason M. Bills                 messages::success(asyncResp->res);
550489640c6SJason M. Bills             },
551489640c6SJason M. Bills             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
552489640c6SJason M. Bills             "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
553489640c6SJason M. Bills             "replace");
554c4bf6374SJason M. Bills     }
555c4bf6374SJason M. Bills };
556c4bf6374SJason M. Bills 
55795820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID,
55895820184SJason M. Bills                                  const std::string logEntry,
55995820184SJason M. Bills                                  nlohmann::json &logEntryJson)
560c4bf6374SJason M. Bills {
56195820184SJason M. Bills     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
562cd225da8SJason M. Bills     // First get the Timestamp
563cd225da8SJason M. Bills     size_t space = logEntry.find_first_of(" ");
564cd225da8SJason M. Bills     if (space == std::string::npos)
56595820184SJason M. Bills     {
56695820184SJason M. Bills         return 1;
56795820184SJason M. Bills     }
568cd225da8SJason M. Bills     std::string timestamp = logEntry.substr(0, space);
569cd225da8SJason M. Bills     // Then get the log contents
570cd225da8SJason M. Bills     size_t entryStart = logEntry.find_first_not_of(" ", space);
571cd225da8SJason M. Bills     if (entryStart == std::string::npos)
572cd225da8SJason M. Bills     {
573cd225da8SJason M. Bills         return 1;
574cd225da8SJason M. Bills     }
575cd225da8SJason M. Bills     std::string_view entry(logEntry);
576cd225da8SJason M. Bills     entry.remove_prefix(entryStart);
577cd225da8SJason M. Bills     // Use split to separate the entry into its fields
578cd225da8SJason M. Bills     std::vector<std::string> logEntryFields;
579cd225da8SJason M. Bills     boost::split(logEntryFields, entry, boost::is_any_of(","),
580cd225da8SJason M. Bills                  boost::token_compress_on);
581cd225da8SJason M. Bills     // We need at least a MessageId to be valid
582cd225da8SJason M. Bills     if (logEntryFields.size() < 1)
583cd225da8SJason M. Bills     {
584cd225da8SJason M. Bills         return 1;
585cd225da8SJason M. Bills     }
586cd225da8SJason M. Bills     std::string &messageID = logEntryFields[0];
58795820184SJason M. Bills 
5884851d45dSJason M. Bills     // Get the Message from the MessageRegistry
5894851d45dSJason M. Bills     const message_registries::Message *message =
5904851d45dSJason M. Bills         message_registries::getMessage(messageID);
591c4bf6374SJason M. Bills 
5924851d45dSJason M. Bills     std::string msg;
5934851d45dSJason M. Bills     std::string severity;
5944851d45dSJason M. Bills     if (message != nullptr)
595c4bf6374SJason M. Bills     {
5964851d45dSJason M. Bills         msg = message->message;
5974851d45dSJason M. Bills         severity = message->severity;
598c4bf6374SJason M. Bills     }
599c4bf6374SJason M. Bills 
600*15a86ff6SJason M. Bills     // Get the MessageArgs from the log if there are any
601*15a86ff6SJason M. Bills     boost::beast::span<std::string> messageArgs;
602*15a86ff6SJason M. Bills     if (logEntryFields.size() > 1)
603*15a86ff6SJason M. Bills     {
604*15a86ff6SJason M. Bills         std::string &messageArgsStart = logEntryFields[1];
605*15a86ff6SJason M. Bills         // If the first string is empty, assume there are no MessageArgs
606*15a86ff6SJason M. Bills         std::size_t messageArgsSize = 0;
607*15a86ff6SJason M. Bills         if (!messageArgsStart.empty())
608*15a86ff6SJason M. Bills         {
609*15a86ff6SJason M. Bills             messageArgsSize = logEntryFields.size() - 1;
610*15a86ff6SJason M. Bills         }
611*15a86ff6SJason M. Bills 
612*15a86ff6SJason M. Bills         messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
613c4bf6374SJason M. Bills 
6144851d45dSJason M. Bills         // Fill the MessageArgs into the Message
61595820184SJason M. Bills         int i = 0;
61695820184SJason M. Bills         for (const std::string &messageArg : messageArgs)
6174851d45dSJason M. Bills         {
61895820184SJason M. Bills             std::string argStr = "%" + std::to_string(++i);
6194851d45dSJason M. Bills             size_t argPos = msg.find(argStr);
6204851d45dSJason M. Bills             if (argPos != std::string::npos)
6214851d45dSJason M. Bills             {
62295820184SJason M. Bills                 msg.replace(argPos, argStr.length(), messageArg);
6234851d45dSJason M. Bills             }
6244851d45dSJason M. Bills         }
625*15a86ff6SJason M. Bills     }
6264851d45dSJason M. Bills 
62795820184SJason M. Bills     // Get the Created time from the timestamp. The log timestamp is in RFC3339
62895820184SJason M. Bills     // format which matches the Redfish format except for the fractional seconds
62995820184SJason M. Bills     // between the '.' and the '+', so just remove them.
63095820184SJason M. Bills     std::size_t dot = timestamp.find_first_of(".");
63195820184SJason M. Bills     std::size_t plus = timestamp.find_first_of("+");
63295820184SJason M. Bills     if (dot != std::string::npos && plus != std::string::npos)
633c4bf6374SJason M. Bills     {
63495820184SJason M. Bills         timestamp.erase(dot, plus - dot);
635c4bf6374SJason M. Bills     }
636c4bf6374SJason M. Bills 
637c4bf6374SJason M. Bills     // Fill in the log entry with the gathered data
63895820184SJason M. Bills     logEntryJson = {
639cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
640c4bf6374SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
641029573d4SEd Tanous         {"@odata.id",
64295820184SJason M. Bills          "/redfish/v1/Systems/system/LogServices/EventLog/Entries/#" +
64395820184SJason M. Bills              logEntryID},
644c4bf6374SJason M. Bills         {"Name", "System Event Log Entry"},
64595820184SJason M. Bills         {"Id", logEntryID},
64695820184SJason M. Bills         {"Message", std::move(msg)},
64795820184SJason M. Bills         {"MessageId", std::move(messageID)},
648c4bf6374SJason M. Bills         {"MessageArgs", std::move(messageArgs)},
649c4bf6374SJason M. Bills         {"EntryType", "Event"},
65095820184SJason M. Bills         {"Severity", std::move(severity)},
65195820184SJason M. Bills         {"Created", std::move(timestamp)}};
652c4bf6374SJason M. Bills     return 0;
653c4bf6374SJason M. Bills }
654c4bf6374SJason M. Bills 
655c4bf6374SJason M. Bills class EventLogEntryCollection : public Node
656c4bf6374SJason M. Bills {
657c4bf6374SJason M. Bills   public:
658c4bf6374SJason M. Bills     template <typename CrowApp>
659c4bf6374SJason M. Bills     EventLogEntryCollection(CrowApp &app) :
660029573d4SEd Tanous         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
661c4bf6374SJason M. Bills     {
662c4bf6374SJason M. Bills         entityPrivileges = {
663c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
664c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
665c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
666c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
667c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
668c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
669c4bf6374SJason M. Bills     }
670c4bf6374SJason M. Bills 
671c4bf6374SJason M. Bills   private:
672c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
673c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
674c4bf6374SJason M. Bills     {
675c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
676c4bf6374SJason M. Bills         long skip = 0;
677c4bf6374SJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
678c4bf6374SJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
679c4bf6374SJason M. Bills         {
680c4bf6374SJason M. Bills             return;
681c4bf6374SJason M. Bills         }
682c4bf6374SJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
683c4bf6374SJason M. Bills         {
684c4bf6374SJason M. Bills             return;
685c4bf6374SJason M. Bills         }
686c4bf6374SJason M. Bills         // Collections don't include the static data added by SubRoute because
687c4bf6374SJason M. Bills         // it has a duplicate entry for members
688c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
689c4bf6374SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
690c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
691c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
692c4bf6374SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
693029573d4SEd Tanous             "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
694c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
695c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] =
696c4bf6374SJason M. Bills             "Collection of System Event Log Entries";
697cb92c03bSAndrew Geissler 
698cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
699c4bf6374SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
700c4bf6374SJason M. Bills         logEntryArray = nlohmann::json::array();
70195820184SJason M. Bills         // Go through the log files and create a unique ID for each entry
70295820184SJason M. Bills         std::vector<std::filesystem::path> redfishLogFiles;
70395820184SJason M. Bills         getRedfishLogFiles(redfishLogFiles);
704b01bf299SEd Tanous         uint64_t entryCount = 0;
705cd225da8SJason M. Bills         std::string logEntry;
70695820184SJason M. Bills 
70795820184SJason M. Bills         // Oldest logs are in the last file, so start there and loop backwards
708cd225da8SJason M. Bills         for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
709cd225da8SJason M. Bills              it++)
710c4bf6374SJason M. Bills         {
711cd225da8SJason M. Bills             std::ifstream logStream(*it);
71295820184SJason M. Bills             if (!logStream.is_open())
713c4bf6374SJason M. Bills             {
714c4bf6374SJason M. Bills                 continue;
715c4bf6374SJason M. Bills             }
716c4bf6374SJason M. Bills 
71795820184SJason M. Bills             while (std::getline(logStream, logEntry))
71895820184SJason M. Bills             {
719c4bf6374SJason M. Bills                 entryCount++;
720c4bf6374SJason M. Bills                 // Handle paging using skip (number of entries to skip from the
721c4bf6374SJason M. Bills                 // start) and top (number of entries to display)
722c4bf6374SJason M. Bills                 if (entryCount <= skip || entryCount > skip + top)
723c4bf6374SJason M. Bills                 {
724c4bf6374SJason M. Bills                     continue;
725c4bf6374SJason M. Bills                 }
726c4bf6374SJason M. Bills 
727c4bf6374SJason M. Bills                 std::string idStr;
72895820184SJason M. Bills                 if (!getUniqueEntryID(logEntry, idStr))
729c4bf6374SJason M. Bills                 {
730c4bf6374SJason M. Bills                     continue;
731c4bf6374SJason M. Bills                 }
732c4bf6374SJason M. Bills 
733c4bf6374SJason M. Bills                 logEntryArray.push_back({});
734c4bf6374SJason M. Bills                 nlohmann::json &bmcLogEntry = logEntryArray.back();
73595820184SJason M. Bills                 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
736c4bf6374SJason M. Bills                 {
737c4bf6374SJason M. Bills                     messages::internalError(asyncResp->res);
738c4bf6374SJason M. Bills                     return;
739c4bf6374SJason M. Bills                 }
740c4bf6374SJason M. Bills             }
74195820184SJason M. Bills         }
742c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
743c4bf6374SJason M. Bills         if (skip + top < entryCount)
744c4bf6374SJason M. Bills         {
745c4bf6374SJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
74695820184SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/EventLog/"
74795820184SJason M. Bills                 "Entries?$skip=" +
748c4bf6374SJason M. Bills                 std::to_string(skip + top);
749c4bf6374SJason M. Bills         }
750cb92c03bSAndrew Geissler #else
751cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
752cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
753cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
754cb92c03bSAndrew Geissler             [asyncResp](const boost::system::error_code ec,
755cb92c03bSAndrew Geissler                         GetManagedObjectsType &resp) {
756cb92c03bSAndrew Geissler                 if (ec)
757cb92c03bSAndrew Geissler                 {
758cb92c03bSAndrew Geissler                     // TODO Handle for specific error code
759cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
760cb92c03bSAndrew Geissler                         << "getLogEntriesIfaceData resp_handler got error "
761cb92c03bSAndrew Geissler                         << ec;
762cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
763cb92c03bSAndrew Geissler                     return;
764cb92c03bSAndrew Geissler                 }
765cb92c03bSAndrew Geissler                 nlohmann::json &entriesArray =
766cb92c03bSAndrew Geissler                     asyncResp->res.jsonValue["Members"];
767cb92c03bSAndrew Geissler                 entriesArray = nlohmann::json::array();
768cb92c03bSAndrew Geissler                 for (auto &objectPath : resp)
769cb92c03bSAndrew Geissler                 {
770cb92c03bSAndrew Geissler                     for (auto &interfaceMap : objectPath.second)
771cb92c03bSAndrew Geissler                     {
772cb92c03bSAndrew Geissler                         if (interfaceMap.first !=
773cb92c03bSAndrew Geissler                             "xyz.openbmc_project.Logging.Entry")
774cb92c03bSAndrew Geissler                         {
775cb92c03bSAndrew Geissler                             BMCWEB_LOG_DEBUG << "Bailing early on "
776cb92c03bSAndrew Geissler                                              << interfaceMap.first;
777cb92c03bSAndrew Geissler                             continue;
778cb92c03bSAndrew Geissler                         }
779cb92c03bSAndrew Geissler                         entriesArray.push_back({});
780cb92c03bSAndrew Geissler                         nlohmann::json &thisEntry = entriesArray.back();
781cb92c03bSAndrew Geissler                         uint32_t *id;
782cb92c03bSAndrew Geissler                         std::time_t timestamp;
783cb92c03bSAndrew Geissler                         std::string *severity, *message;
784cb92c03bSAndrew Geissler                         bool *resolved;
785cb92c03bSAndrew Geissler                         for (auto &propertyMap : interfaceMap.second)
786cb92c03bSAndrew Geissler                         {
787cb92c03bSAndrew Geissler                             if (propertyMap.first == "Id")
788cb92c03bSAndrew Geissler                             {
789cb92c03bSAndrew Geissler                                 id = sdbusplus::message::variant_ns::get_if<
790cb92c03bSAndrew Geissler                                     uint32_t>(&propertyMap.second);
791cb92c03bSAndrew Geissler                                 if (id == nullptr)
792cb92c03bSAndrew Geissler                                 {
793cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
794cb92c03bSAndrew Geissler                                                               "Id");
795cb92c03bSAndrew Geissler                                 }
796cb92c03bSAndrew Geissler                             }
797cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Timestamp")
798cb92c03bSAndrew Geissler                             {
799cb92c03bSAndrew Geissler                                 const uint64_t *millisTimeStamp =
800cb92c03bSAndrew Geissler                                     std::get_if<uint64_t>(&propertyMap.second);
801cb92c03bSAndrew Geissler                                 if (millisTimeStamp == nullptr)
802cb92c03bSAndrew Geissler                                 {
803cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
804cb92c03bSAndrew Geissler                                                               "Timestamp");
805cb92c03bSAndrew Geissler                                 }
806cb92c03bSAndrew Geissler                                 // Retrieve Created property with format:
807cb92c03bSAndrew Geissler                                 // yyyy-mm-ddThh:mm:ss
808cb92c03bSAndrew Geissler                                 std::chrono::milliseconds chronoTimeStamp(
809cb92c03bSAndrew Geissler                                     *millisTimeStamp);
810cb92c03bSAndrew Geissler                                 timestamp =
811cb92c03bSAndrew Geissler                                     std::chrono::duration_cast<
812cb92c03bSAndrew Geissler                                         std::chrono::seconds>(chronoTimeStamp)
813cb92c03bSAndrew Geissler                                         .count();
814cb92c03bSAndrew Geissler                             }
815cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Severity")
816cb92c03bSAndrew Geissler                             {
817cb92c03bSAndrew Geissler                                 severity = std::get_if<std::string>(
818cb92c03bSAndrew Geissler                                     &propertyMap.second);
819cb92c03bSAndrew Geissler                                 if (severity == nullptr)
820cb92c03bSAndrew Geissler                                 {
821cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
822cb92c03bSAndrew Geissler                                                               "Severity");
823cb92c03bSAndrew Geissler                                 }
824cb92c03bSAndrew Geissler                             }
825cb92c03bSAndrew Geissler                             else if (propertyMap.first == "Message")
826cb92c03bSAndrew Geissler                             {
827cb92c03bSAndrew Geissler                                 message = std::get_if<std::string>(
828cb92c03bSAndrew Geissler                                     &propertyMap.second);
829cb92c03bSAndrew Geissler                                 if (message == nullptr)
830cb92c03bSAndrew Geissler                                 {
831cb92c03bSAndrew Geissler                                     messages::propertyMissing(asyncResp->res,
832cb92c03bSAndrew Geissler                                                               "Message");
833cb92c03bSAndrew Geissler                                 }
834cb92c03bSAndrew Geissler                             }
835cb92c03bSAndrew Geissler                         }
836cb92c03bSAndrew Geissler                         thisEntry = {
837cb92c03bSAndrew Geissler                             {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
838cb92c03bSAndrew Geissler                             {"@odata.context", "/redfish/v1/"
839cb92c03bSAndrew Geissler                                                "$metadata#LogEntry.LogEntry"},
840cb92c03bSAndrew Geissler                             {"@odata.id",
841cb92c03bSAndrew Geissler                              "/redfish/v1/Systems/system/LogServices/EventLog/"
842cb92c03bSAndrew Geissler                              "Entries/" +
843cb92c03bSAndrew Geissler                                  std::to_string(*id)},
844cb92c03bSAndrew Geissler                             {"Name", "System DBus Event Log Entry"},
845cb92c03bSAndrew Geissler                             {"Id", std::to_string(*id)},
846cb92c03bSAndrew Geissler                             {"Message", *message},
847cb92c03bSAndrew Geissler                             {"EntryType", "Event"},
848cb92c03bSAndrew Geissler                             {"Severity",
849cb92c03bSAndrew Geissler                              translateSeverityDbusToRedfish(*severity)},
850cb92c03bSAndrew Geissler                             {"Created", crow::utility::getDateTime(timestamp)}};
851cb92c03bSAndrew Geissler                     }
852cb92c03bSAndrew Geissler                 }
853cb92c03bSAndrew Geissler                 std::sort(entriesArray.begin(), entriesArray.end(),
854cb92c03bSAndrew Geissler                           [](const nlohmann::json &left,
855cb92c03bSAndrew Geissler                              const nlohmann::json &right) {
856cb92c03bSAndrew Geissler                               return (left["Id"] <= right["Id"]);
857cb92c03bSAndrew Geissler                           });
858cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue["Members@odata.count"] =
859cb92c03bSAndrew Geissler                     entriesArray.size();
860cb92c03bSAndrew Geissler             },
861cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
862cb92c03bSAndrew Geissler             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
863cb92c03bSAndrew Geissler #endif
864c4bf6374SJason M. Bills     }
865c4bf6374SJason M. Bills };
866c4bf6374SJason M. Bills 
867c4bf6374SJason M. Bills class EventLogEntry : public Node
868c4bf6374SJason M. Bills {
869c4bf6374SJason M. Bills   public:
870c4bf6374SJason M. Bills     EventLogEntry(CrowApp &app) :
871c4bf6374SJason M. Bills         Node(app,
872029573d4SEd Tanous              "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
873029573d4SEd Tanous              std::string())
874c4bf6374SJason M. Bills     {
875c4bf6374SJason M. Bills         entityPrivileges = {
876c4bf6374SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
877c4bf6374SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
878c4bf6374SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
879c4bf6374SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
880c4bf6374SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
881c4bf6374SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
882c4bf6374SJason M. Bills     }
883c4bf6374SJason M. Bills 
884c4bf6374SJason M. Bills   private:
885c4bf6374SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
886c4bf6374SJason M. Bills                const std::vector<std::string> &params) override
887c4bf6374SJason M. Bills     {
888c4bf6374SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
889029573d4SEd Tanous         if (params.size() != 1)
890c4bf6374SJason M. Bills         {
891c4bf6374SJason M. Bills             messages::internalError(asyncResp->res);
892c4bf6374SJason M. Bills             return;
893c4bf6374SJason M. Bills         }
894029573d4SEd Tanous         const std::string &entryID = params[0];
895cb92c03bSAndrew Geissler 
89695820184SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
897cb92c03bSAndrew Geissler         // DBus implementation of EventLog/Entries
898cb92c03bSAndrew Geissler         // Make call to Logging Service to find all log entry objects
899cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
900cb92c03bSAndrew Geissler             [asyncResp, entryID](const boost::system::error_code ec,
901cb92c03bSAndrew Geissler                                  GetManagedPropertyType &resp) {
902cb92c03bSAndrew Geissler                 if (ec)
903cb92c03bSAndrew Geissler                 {
904cb92c03bSAndrew Geissler                     BMCWEB_LOG_ERROR
905cb92c03bSAndrew Geissler                         << "EventLogEntry (DBus) resp_handler got error " << ec;
906cb92c03bSAndrew Geissler                     messages::internalError(asyncResp->res);
907cb92c03bSAndrew Geissler                     return;
908cb92c03bSAndrew Geissler                 }
909cb92c03bSAndrew Geissler                 uint32_t *id;
910cb92c03bSAndrew Geissler                 std::time_t timestamp;
911cb92c03bSAndrew Geissler                 std::string *severity, *message;
912cb92c03bSAndrew Geissler                 bool *resolved;
913cb92c03bSAndrew Geissler                 for (auto &propertyMap : resp)
914cb92c03bSAndrew Geissler                 {
915cb92c03bSAndrew Geissler                     if (propertyMap.first == "Id")
916cb92c03bSAndrew Geissler                     {
917cb92c03bSAndrew Geissler                         id = std::get_if<uint32_t>(&propertyMap.second);
918cb92c03bSAndrew Geissler                         if (id == nullptr)
919cb92c03bSAndrew Geissler                         {
920cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res, "Id");
921cb92c03bSAndrew Geissler                         }
922cb92c03bSAndrew Geissler                     }
923cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Timestamp")
924cb92c03bSAndrew Geissler                     {
925cb92c03bSAndrew Geissler                         const uint64_t *millisTimeStamp =
926cb92c03bSAndrew Geissler                             std::get_if<uint64_t>(&propertyMap.second);
927cb92c03bSAndrew Geissler                         if (millisTimeStamp == nullptr)
928cb92c03bSAndrew Geissler                         {
929cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
930cb92c03bSAndrew Geissler                                                       "Timestamp");
931cb92c03bSAndrew Geissler                         }
932cb92c03bSAndrew Geissler                         // Retrieve Created property with format:
933cb92c03bSAndrew Geissler                         // yyyy-mm-ddThh:mm:ss
934cb92c03bSAndrew Geissler                         std::chrono::milliseconds chronoTimeStamp(
935cb92c03bSAndrew Geissler                             *millisTimeStamp);
936cb92c03bSAndrew Geissler                         timestamp =
937cb92c03bSAndrew Geissler                             std::chrono::duration_cast<std::chrono::seconds>(
938cb92c03bSAndrew Geissler                                 chronoTimeStamp)
939cb92c03bSAndrew Geissler                                 .count();
940cb92c03bSAndrew Geissler                     }
941cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Severity")
942cb92c03bSAndrew Geissler                     {
943cb92c03bSAndrew Geissler                         severity =
944cb92c03bSAndrew Geissler                             std::get_if<std::string>(&propertyMap.second);
945cb92c03bSAndrew Geissler                         if (severity == nullptr)
946cb92c03bSAndrew Geissler                         {
947cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
948cb92c03bSAndrew Geissler                                                       "Severity");
949cb92c03bSAndrew Geissler                         }
950cb92c03bSAndrew Geissler                     }
951cb92c03bSAndrew Geissler                     else if (propertyMap.first == "Message")
952cb92c03bSAndrew Geissler                     {
953cb92c03bSAndrew Geissler                         message = std::get_if<std::string>(&propertyMap.second);
954cb92c03bSAndrew Geissler                         if (message == nullptr)
955cb92c03bSAndrew Geissler                         {
956cb92c03bSAndrew Geissler                             messages::propertyMissing(asyncResp->res,
957cb92c03bSAndrew Geissler                                                       "Message");
958cb92c03bSAndrew Geissler                         }
959cb92c03bSAndrew Geissler                     }
960cb92c03bSAndrew Geissler                 }
961cb92c03bSAndrew Geissler                 asyncResp->res.jsonValue = {
962cb92c03bSAndrew Geissler                     {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
963cb92c03bSAndrew Geissler                     {"@odata.context", "/redfish/v1/"
964cb92c03bSAndrew Geissler                                        "$metadata#LogEntry.LogEntry"},
965cb92c03bSAndrew Geissler                     {"@odata.id",
966cb92c03bSAndrew Geissler                      "/redfish/v1/Systems/system/LogServices/EventLog/"
967cb92c03bSAndrew Geissler                      "Entries/" +
968cb92c03bSAndrew Geissler                          std::to_string(*id)},
969cb92c03bSAndrew Geissler                     {"Name", "System DBus Event Log Entry"},
970cb92c03bSAndrew Geissler                     {"Id", std::to_string(*id)},
971cb92c03bSAndrew Geissler                     {"Message", *message},
972cb92c03bSAndrew Geissler                     {"EntryType", "Event"},
973cb92c03bSAndrew Geissler                     {"Severity", translateSeverityDbusToRedfish(*severity)},
97495820184SJason M. Bills                     { "Created",
97595820184SJason M. Bills                       crow::utility::getDateTime(timestamp) }};
976cb92c03bSAndrew Geissler             },
977cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging",
978cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging/entry/" + entryID,
979cb92c03bSAndrew Geissler             "org.freedesktop.DBus.Properties", "GetAll",
980cb92c03bSAndrew Geissler             "xyz.openbmc_project.Logging.Entry");
981cb92c03bSAndrew Geissler #endif
982c4bf6374SJason M. Bills     }
983c4bf6374SJason M. Bills };
984c4bf6374SJason M. Bills 
985c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node
986c4bf6374SJason M. Bills {
987c4bf6374SJason M. Bills   public:
988c4bf6374SJason M. Bills     template <typename CrowApp>
989c4bf6374SJason M. Bills     BMCLogServiceCollection(CrowApp &app) :
9904ed77cd5SEd Tanous         Node(app, "/redfish/v1/Managers/bmc/LogServices/")
9911da66f75SEd Tanous     {
9921da66f75SEd Tanous         entityPrivileges = {
993e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
994e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
995e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
996e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
997e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
998e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
9991da66f75SEd Tanous     }
10001da66f75SEd Tanous 
10011da66f75SEd Tanous   private:
10021da66f75SEd Tanous     /**
10031da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
10041da66f75SEd Tanous      */
10051da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
10061da66f75SEd Tanous                const std::vector<std::string> &params) override
10071da66f75SEd Tanous     {
1008e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
10091da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
10101da66f75SEd Tanous         // it has a duplicate entry for members
1011e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
10121da66f75SEd Tanous             "#LogServiceCollection.LogServiceCollection";
1013e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1014c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
1015e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1016e1f26343SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices";
1017e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1018e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
10191da66f75SEd Tanous             "Collection of LogServices for this Manager";
1020c4bf6374SJason M. Bills         nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1021c4bf6374SJason M. Bills         logServiceArray = nlohmann::json::array();
1022c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1023c4bf6374SJason M. Bills         logServiceArray.push_back(
1024cb92c03bSAndrew Geissler             {{ "@odata.id",
1025cb92c03bSAndrew Geissler                "/redfish/v1/Managers/bmc/LogServices/Journal" }});
1026c4bf6374SJason M. Bills #endif
1027e1f26343SJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] =
1028c4bf6374SJason M. Bills             logServiceArray.size();
10291da66f75SEd Tanous     }
10301da66f75SEd Tanous };
10311da66f75SEd Tanous 
1032c4bf6374SJason M. Bills class BMCJournalLogService : public Node
10331da66f75SEd Tanous {
10341da66f75SEd Tanous   public:
10351da66f75SEd Tanous     template <typename CrowApp>
1036c4bf6374SJason M. Bills     BMCJournalLogService(CrowApp &app) :
1037c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
1038e1f26343SJason M. Bills     {
1039e1f26343SJason M. Bills         entityPrivileges = {
1040e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1041e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1042e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1043e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1044e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1045e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1046e1f26343SJason M. Bills     }
1047e1f26343SJason M. Bills 
1048e1f26343SJason M. Bills   private:
1049e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1050e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1051e1f26343SJason M. Bills     {
1052e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1053e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1054e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
10550f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
10560f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal";
1057e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1058e1f26343SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1059c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1060c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1061c4bf6374SJason M. Bills         asyncResp->res.jsonValue["Id"] = "BMC Journal";
1062e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1063cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1064cd50aa42SJason M. Bills             {"@odata.id",
1065086be238SEd Tanous              "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1066e1f26343SJason M. Bills     }
1067e1f26343SJason M. Bills };
1068e1f26343SJason M. Bills 
1069c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1070e1f26343SJason M. Bills                                       sd_journal *journal,
1071c4bf6374SJason M. Bills                                       nlohmann::json &bmcJournalLogEntryJson)
1072e1f26343SJason M. Bills {
1073e1f26343SJason M. Bills     // Get the Log Entry contents
1074e1f26343SJason M. Bills     int ret = 0;
1075e1f26343SJason M. Bills 
107639e77504SEd Tanous     std::string_view msg;
107716428a1aSJason M. Bills     ret = getJournalMetadata(journal, "MESSAGE", msg);
1078e1f26343SJason M. Bills     if (ret < 0)
1079e1f26343SJason M. Bills     {
1080e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1081e1f26343SJason M. Bills         return 1;
1082e1f26343SJason M. Bills     }
1083e1f26343SJason M. Bills 
1084e1f26343SJason M. Bills     // Get the severity from the PRIORITY field
1085e1f26343SJason M. Bills     int severity = 8; // Default to an invalid priority
108616428a1aSJason M. Bills     ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
1087e1f26343SJason M. Bills     if (ret < 0)
1088e1f26343SJason M. Bills     {
1089e1f26343SJason M. Bills         BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1090e1f26343SJason M. Bills         return 1;
1091e1f26343SJason M. Bills     }
1092e1f26343SJason M. Bills 
1093e1f26343SJason M. Bills     // Get the Created time from the timestamp
109416428a1aSJason M. Bills     std::string entryTimeStr;
109516428a1aSJason M. Bills     if (!getEntryTimestamp(journal, entryTimeStr))
1096e1f26343SJason M. Bills     {
109716428a1aSJason M. Bills         return 1;
1098e1f26343SJason M. Bills     }
1099e1f26343SJason M. Bills 
1100e1f26343SJason M. Bills     // Fill in the log entry with the gathered data
1101c4bf6374SJason M. Bills     bmcJournalLogEntryJson = {
1102cb92c03bSAndrew Geissler         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1103e1f26343SJason M. Bills         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1104c4bf6374SJason M. Bills         {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1105c4bf6374SJason M. Bills                           bmcJournalLogEntryID},
1106e1f26343SJason M. Bills         {"Name", "BMC Journal Entry"},
1107c4bf6374SJason M. Bills         {"Id", bmcJournalLogEntryID},
110816428a1aSJason M. Bills         {"Message", msg},
1109e1f26343SJason M. Bills         {"EntryType", "Oem"},
1110e1f26343SJason M. Bills         {"Severity",
1111e1f26343SJason M. Bills          severity <= 2 ? "Critical"
1112e1f26343SJason M. Bills                        : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
1113086be238SEd Tanous         {"OemRecordFormat", "BMC Journal Entry"},
1114e1f26343SJason M. Bills         {"Created", std::move(entryTimeStr)}};
1115e1f26343SJason M. Bills     return 0;
1116e1f26343SJason M. Bills }
1117e1f26343SJason M. Bills 
1118c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node
1119e1f26343SJason M. Bills {
1120e1f26343SJason M. Bills   public:
1121e1f26343SJason M. Bills     template <typename CrowApp>
1122c4bf6374SJason M. Bills     BMCJournalLogEntryCollection(CrowApp &app) :
1123c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
1124e1f26343SJason M. Bills     {
1125e1f26343SJason M. Bills         entityPrivileges = {
1126e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1127e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1128e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1129e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1130e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1131e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1132e1f26343SJason M. Bills     }
1133e1f26343SJason M. Bills 
1134e1f26343SJason M. Bills   private:
1135e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1136e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1137e1f26343SJason M. Bills     {
1138e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1139193ad2faSJason M. Bills         static constexpr const long maxEntriesPerPage = 1000;
1140193ad2faSJason M. Bills         long skip = 0;
1141193ad2faSJason M. Bills         long top = maxEntriesPerPage; // Show max entries by default
114216428a1aSJason M. Bills         if (!getSkipParam(asyncResp->res, req, skip))
1143193ad2faSJason M. Bills         {
1144193ad2faSJason M. Bills             return;
1145193ad2faSJason M. Bills         }
114616428a1aSJason M. Bills         if (!getTopParam(asyncResp->res, req, top))
1147193ad2faSJason M. Bills         {
1148193ad2faSJason M. Bills             return;
1149193ad2faSJason M. Bills         }
1150e1f26343SJason M. Bills         // Collections don't include the static data added by SubRoute because
1151e1f26343SJason M. Bills         // it has a duplicate entry for members
1152e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1153e1f26343SJason M. Bills             "#LogEntryCollection.LogEntryCollection";
11540f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
11550f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1156e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1157c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
1158e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.id"] =
1159c4bf6374SJason M. Bills             "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1160e1f26343SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1161e1f26343SJason M. Bills         asyncResp->res.jsonValue["Description"] =
1162e1f26343SJason M. Bills             "Collection of BMC Journal Entries";
11630f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
11640f74e643SEd Tanous             "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
1165e1f26343SJason M. Bills         nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1166e1f26343SJason M. Bills         logEntryArray = nlohmann::json::array();
1167e1f26343SJason M. Bills 
1168e1f26343SJason M. Bills         // Go through the journal and use the timestamp to create a unique ID
1169e1f26343SJason M. Bills         // for each entry
1170e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1171e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1172e1f26343SJason M. Bills         if (ret < 0)
1173e1f26343SJason M. Bills         {
1174e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1175f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1176e1f26343SJason M. Bills             return;
1177e1f26343SJason M. Bills         }
1178e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1179e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1180e1f26343SJason M. Bills         journalTmp = nullptr;
1181b01bf299SEd Tanous         uint64_t entryCount = 0;
1182e1f26343SJason M. Bills         SD_JOURNAL_FOREACH(journal.get())
1183e1f26343SJason M. Bills         {
1184193ad2faSJason M. Bills             entryCount++;
1185193ad2faSJason M. Bills             // Handle paging using skip (number of entries to skip from the
1186193ad2faSJason M. Bills             // start) and top (number of entries to display)
1187193ad2faSJason M. Bills             if (entryCount <= skip || entryCount > skip + top)
1188193ad2faSJason M. Bills             {
1189193ad2faSJason M. Bills                 continue;
1190193ad2faSJason M. Bills             }
1191193ad2faSJason M. Bills 
119216428a1aSJason M. Bills             std::string idStr;
119316428a1aSJason M. Bills             if (!getUniqueEntryID(journal.get(), idStr))
1194e1f26343SJason M. Bills             {
1195e1f26343SJason M. Bills                 continue;
1196e1f26343SJason M. Bills             }
1197e1f26343SJason M. Bills 
1198e1f26343SJason M. Bills             logEntryArray.push_back({});
1199c4bf6374SJason M. Bills             nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1200c4bf6374SJason M. Bills             if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1201c4bf6374SJason M. Bills                                            bmcJournalLogEntry) != 0)
1202e1f26343SJason M. Bills             {
1203f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
1204e1f26343SJason M. Bills                 return;
1205e1f26343SJason M. Bills             }
1206e1f26343SJason M. Bills         }
1207193ad2faSJason M. Bills         asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1208193ad2faSJason M. Bills         if (skip + top < entryCount)
1209193ad2faSJason M. Bills         {
1210193ad2faSJason M. Bills             asyncResp->res.jsonValue["Members@odata.nextLink"] =
1211c4bf6374SJason M. Bills                 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
1212193ad2faSJason M. Bills                 std::to_string(skip + top);
1213193ad2faSJason M. Bills         }
1214e1f26343SJason M. Bills     }
1215e1f26343SJason M. Bills };
1216e1f26343SJason M. Bills 
1217c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node
1218e1f26343SJason M. Bills {
1219e1f26343SJason M. Bills   public:
1220c4bf6374SJason M. Bills     BMCJournalLogEntry(CrowApp &app) :
1221c4bf6374SJason M. Bills         Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
1222e1f26343SJason M. Bills              std::string())
1223e1f26343SJason M. Bills     {
1224e1f26343SJason M. Bills         entityPrivileges = {
1225e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1226e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1227e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1228e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1229e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1230e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1231e1f26343SJason M. Bills     }
1232e1f26343SJason M. Bills 
1233e1f26343SJason M. Bills   private:
1234e1f26343SJason M. Bills     void doGet(crow::Response &res, const crow::Request &req,
1235e1f26343SJason M. Bills                const std::vector<std::string> &params) override
1236e1f26343SJason M. Bills     {
1237e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1238e1f26343SJason M. Bills         if (params.size() != 1)
1239e1f26343SJason M. Bills         {
1240f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1241e1f26343SJason M. Bills             return;
1242e1f26343SJason M. Bills         }
124316428a1aSJason M. Bills         const std::string &entryID = params[0];
1244e1f26343SJason M. Bills         // Convert the unique ID back to a timestamp to find the entry
1245e1f26343SJason M. Bills         uint64_t ts = 0;
1246e1f26343SJason M. Bills         uint16_t index = 0;
124716428a1aSJason M. Bills         if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
1248e1f26343SJason M. Bills         {
124916428a1aSJason M. Bills             return;
1250e1f26343SJason M. Bills         }
1251e1f26343SJason M. Bills 
1252e1f26343SJason M. Bills         sd_journal *journalTmp = nullptr;
1253e1f26343SJason M. Bills         int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1254e1f26343SJason M. Bills         if (ret < 0)
1255e1f26343SJason M. Bills         {
1256e1f26343SJason M. Bills             BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
1257f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1258e1f26343SJason M. Bills             return;
1259e1f26343SJason M. Bills         }
1260e1f26343SJason M. Bills         std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1261e1f26343SJason M. Bills             journalTmp, sd_journal_close);
1262e1f26343SJason M. Bills         journalTmp = nullptr;
1263e1f26343SJason M. Bills         // Go to the timestamp in the log and move to the entry at the index
1264e1f26343SJason M. Bills         ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1265e1f26343SJason M. Bills         for (int i = 0; i <= index; i++)
1266e1f26343SJason M. Bills         {
1267e1f26343SJason M. Bills             sd_journal_next(journal.get());
1268e1f26343SJason M. Bills         }
1269c4bf6374SJason M. Bills         // Confirm that the entry ID matches what was requested
1270c4bf6374SJason M. Bills         std::string idStr;
1271c4bf6374SJason M. Bills         if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
1272c4bf6374SJason M. Bills         {
1273c4bf6374SJason M. Bills             messages::resourceMissingAtURI(asyncResp->res, entryID);
1274c4bf6374SJason M. Bills             return;
1275c4bf6374SJason M. Bills         }
1276c4bf6374SJason M. Bills 
1277c4bf6374SJason M. Bills         if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1278e1f26343SJason M. Bills                                        asyncResp->res.jsonValue) != 0)
1279e1f26343SJason M. Bills         {
1280f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
1281e1f26343SJason M. Bills             return;
1282e1f26343SJason M. Bills         }
1283e1f26343SJason M. Bills     }
1284e1f26343SJason M. Bills };
1285e1f26343SJason M. Bills 
1286424c4176SJason M. Bills class CrashdumpService : public Node
1287e1f26343SJason M. Bills {
1288e1f26343SJason M. Bills   public:
1289e1f26343SJason M. Bills     template <typename CrowApp>
1290424c4176SJason M. Bills     CrashdumpService(CrowApp &app) :
1291424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
12921da66f75SEd Tanous     {
12931da66f75SEd Tanous         entityPrivileges = {
1294e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1295e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1296e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1297e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1298e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1299e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
13001da66f75SEd Tanous     }
13011da66f75SEd Tanous 
13021da66f75SEd Tanous   private:
13031da66f75SEd Tanous     /**
13041da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
13051da66f75SEd Tanous      */
13061da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
13071da66f75SEd Tanous                const std::vector<std::string> &params) override
13081da66f75SEd Tanous     {
1309e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
13101da66f75SEd Tanous         // Copy over the static data to include the entries added by SubRoute
13110f74e643SEd Tanous         asyncResp->res.jsonValue["@odata.id"] =
1312424c4176SJason M. Bills             "/redfish/v1/Systems/system/LogServices/Crashdump";
1313e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.type"] =
1314e1f26343SJason M. Bills             "#LogService.v1_1_0.LogService";
1315e1f26343SJason M. Bills         asyncResp->res.jsonValue["@odata.context"] =
1316c4bf6374SJason M. Bills             "/redfish/v1/$metadata#LogService.LogService";
1317424c4176SJason M. Bills         asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1318424c4176SJason M. Bills         asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1319424c4176SJason M. Bills         asyncResp->res.jsonValue["Id"] = "Crashdump";
1320e1f26343SJason M. Bills         asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1321e1f26343SJason M. Bills         asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
1322cd50aa42SJason M. Bills         asyncResp->res.jsonValue["Entries"] = {
1323cd50aa42SJason M. Bills             {"@odata.id",
1324424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
1325e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"] = {
13261da66f75SEd Tanous             {"Oem",
1327424c4176SJason M. Bills              {{"#Crashdump.OnDemand",
1328424c4176SJason M. Bills                {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1329424c4176SJason M. Bills                            "Actions/Oem/Crashdump.OnDemand"}}}}}};
13301da66f75SEd Tanous 
13311da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
1332e1f26343SJason M. Bills         asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
1333424c4176SJason M. Bills             {"#Crashdump.SendRawPeci",
1334cb92c03bSAndrew Geissler              { { "target",
1335424c4176SJason M. Bills                  "/redfish/v1/Systems/system/LogServices/Crashdump/"
1336424c4176SJason M. Bills                  "Actions/Oem/Crashdump.SendRawPeci" } }});
13371da66f75SEd Tanous #endif
13381da66f75SEd Tanous     }
13391da66f75SEd Tanous };
13401da66f75SEd Tanous 
1341424c4176SJason M. Bills class CrashdumpEntryCollection : public Node
13421da66f75SEd Tanous {
13431da66f75SEd Tanous   public:
13441da66f75SEd Tanous     template <typename CrowApp>
1345424c4176SJason M. Bills     CrashdumpEntryCollection(CrowApp &app) :
1346424c4176SJason M. Bills         Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
13471da66f75SEd Tanous     {
13481da66f75SEd Tanous         entityPrivileges = {
1349e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1350e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1351e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1352e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1353e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1354e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
13551da66f75SEd Tanous     }
13561da66f75SEd Tanous 
13571da66f75SEd Tanous   private:
13581da66f75SEd Tanous     /**
13591da66f75SEd Tanous      * Functions triggers appropriate requests on DBus
13601da66f75SEd Tanous      */
13611da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
13621da66f75SEd Tanous                const std::vector<std::string> &params) override
13631da66f75SEd Tanous     {
1364e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
13651da66f75SEd Tanous         // Collections don't include the static data added by SubRoute because
13661da66f75SEd Tanous         // it has a duplicate entry for members
1367e1f26343SJason M. Bills         auto getLogEntriesCallback = [asyncResp](
1368e1f26343SJason M. Bills                                          const boost::system::error_code ec,
13691da66f75SEd Tanous                                          const std::vector<std::string> &resp) {
13701da66f75SEd Tanous             if (ec)
13711da66f75SEd Tanous             {
13721da66f75SEd Tanous                 if (ec.value() !=
13731da66f75SEd Tanous                     boost::system::errc::no_such_file_or_directory)
13741da66f75SEd Tanous                 {
13751da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to get entries ec: "
13761da66f75SEd Tanous                                      << ec.message();
1377f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
13781da66f75SEd Tanous                     return;
13791da66f75SEd Tanous                 }
13801da66f75SEd Tanous             }
1381e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.type"] =
13821da66f75SEd Tanous                 "#LogEntryCollection.LogEntryCollection";
13830f74e643SEd Tanous             asyncResp->res.jsonValue["@odata.id"] =
1384424c4176SJason M. Bills                 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
1385e1f26343SJason M. Bills             asyncResp->res.jsonValue["@odata.context"] =
1386d53dd41fSJason M. Bills                 "/redfish/v1/"
1387d53dd41fSJason M. Bills                 "$metadata#LogEntryCollection.LogEntryCollection";
1388424c4176SJason M. Bills             asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
1389e1f26343SJason M. Bills             asyncResp->res.jsonValue["Description"] =
1390424c4176SJason M. Bills                 "Collection of Crashdump Entries";
1391e1f26343SJason M. Bills             nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1392e1f26343SJason M. Bills             logEntryArray = nlohmann::json::array();
13931da66f75SEd Tanous             for (const std::string &objpath : resp)
13941da66f75SEd Tanous             {
139548e4639eSJason M. Bills                 // Don't list the on-demand log
1396424c4176SJason M. Bills                 if (objpath.compare(CrashdumpOnDemandPath) == 0)
13971da66f75SEd Tanous                 {
13981da66f75SEd Tanous                     continue;
13991da66f75SEd Tanous                 }
14004ed77cd5SEd Tanous                 std::size_t lastPos = objpath.rfind("/");
14014ed77cd5SEd Tanous                 if (lastPos != std::string::npos)
14021da66f75SEd Tanous                 {
1403e1f26343SJason M. Bills                     logEntryArray.push_back(
1404d53dd41fSJason M. Bills                         {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1405424c4176SJason M. Bills                                        "Crashdump/Entries/" +
14064ed77cd5SEd Tanous                                            objpath.substr(lastPos + 1)}});
14071da66f75SEd Tanous                 }
14081da66f75SEd Tanous             }
1409e1f26343SJason M. Bills             asyncResp->res.jsonValue["Members@odata.count"] =
1410e1f26343SJason M. Bills                 logEntryArray.size();
14111da66f75SEd Tanous         };
14121da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
14131da66f75SEd Tanous             std::move(getLogEntriesCallback),
14141da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper",
14151da66f75SEd Tanous             "/xyz/openbmc_project/object_mapper",
14161da66f75SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
1417424c4176SJason M. Bills             std::array<const char *, 1>{CrashdumpInterface});
14181da66f75SEd Tanous     }
14191da66f75SEd Tanous };
14201da66f75SEd Tanous 
1421424c4176SJason M. Bills std::string getLogCreatedTime(const nlohmann::json &Crashdump)
14221da66f75SEd Tanous {
1423424c4176SJason M. Bills     nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1424424c4176SJason M. Bills     if (cdIt != Crashdump.end())
14251da66f75SEd Tanous     {
1426c4d00437SJason M. Bills         nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1427c4d00437SJason M. Bills         if (siIt != cdIt->end())
14281da66f75SEd Tanous         {
1429c4d00437SJason M. Bills             nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1430c4d00437SJason M. Bills             if (tsIt != siIt->end())
1431c4d00437SJason M. Bills             {
1432c4d00437SJason M. Bills                 const std::string *logTime =
1433c4d00437SJason M. Bills                     tsIt->get_ptr<const std::string *>();
14341da66f75SEd Tanous                 if (logTime != nullptr)
14351da66f75SEd Tanous                 {
14361da66f75SEd Tanous                     return *logTime;
14371da66f75SEd Tanous                 }
14381da66f75SEd Tanous             }
14391da66f75SEd Tanous         }
1440c4d00437SJason M. Bills     }
14411da66f75SEd Tanous     BMCWEB_LOG_DEBUG << "failed to find log timestamp";
14421da66f75SEd Tanous 
14431da66f75SEd Tanous     return std::string();
14441da66f75SEd Tanous }
14451da66f75SEd Tanous 
1446424c4176SJason M. Bills class CrashdumpEntry : public Node
14471da66f75SEd Tanous {
14481da66f75SEd Tanous   public:
1449424c4176SJason M. Bills     CrashdumpEntry(CrowApp &app) :
1450d53dd41fSJason M. Bills         Node(app,
1451424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
14521da66f75SEd Tanous              std::string())
14531da66f75SEd Tanous     {
14541da66f75SEd Tanous         entityPrivileges = {
1455e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1456e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1457e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1458e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1459e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1460e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
14611da66f75SEd Tanous     }
14621da66f75SEd Tanous 
14631da66f75SEd Tanous   private:
14641da66f75SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
14651da66f75SEd Tanous                const std::vector<std::string> &params) override
14661da66f75SEd Tanous     {
1467e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
14681da66f75SEd Tanous         if (params.size() != 1)
14691da66f75SEd Tanous         {
1470f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
14711da66f75SEd Tanous             return;
14721da66f75SEd Tanous         }
1473b01bf299SEd Tanous         const uint8_t logId = std::atoi(params[0].c_str());
1474abf2add6SEd Tanous         auto getStoredLogCallback = [asyncResp, logId](
1475abf2add6SEd Tanous                                         const boost::system::error_code ec,
1476abf2add6SEd Tanous                                         const std::variant<std::string> &resp) {
14771da66f75SEd Tanous             if (ec)
14781da66f75SEd Tanous             {
1479abf2add6SEd Tanous                 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1480f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14811da66f75SEd Tanous                 return;
14821da66f75SEd Tanous             }
1483abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(&resp);
14841da66f75SEd Tanous             if (log == nullptr)
14851da66f75SEd Tanous             {
1486f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14871da66f75SEd Tanous                 return;
14881da66f75SEd Tanous             }
14891da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
14901da66f75SEd Tanous             if (j.is_discarded())
14911da66f75SEd Tanous             {
1492f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
14931da66f75SEd Tanous                 return;
14941da66f75SEd Tanous             }
14951da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1496e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1497cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1498abf2add6SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
14991da66f75SEd Tanous                 {"@odata.id",
1500424c4176SJason M. Bills                  "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
15014ed77cd5SEd Tanous                      std::to_string(logId)},
1502424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
15034ed77cd5SEd Tanous                 {"Id", logId},
15041da66f75SEd Tanous                 {"EntryType", "Oem"},
1505424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
15061da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
15071da66f75SEd Tanous                 {"Created", std::move(t)}};
15081da66f75SEd Tanous         };
15091da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1510424c4176SJason M. Bills             std::move(getStoredLogCallback), CrashdumpObject,
1511424c4176SJason M. Bills             CrashdumpPath + std::string("/") + std::to_string(logId),
1512424c4176SJason M. Bills             "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1513424c4176SJason M. Bills             "Log");
15141da66f75SEd Tanous     }
15151da66f75SEd Tanous };
15161da66f75SEd Tanous 
1517424c4176SJason M. Bills class OnDemandCrashdump : public Node
15181da66f75SEd Tanous {
15191da66f75SEd Tanous   public:
1520424c4176SJason M. Bills     OnDemandCrashdump(CrowApp &app) :
1521424c4176SJason M. Bills         Node(app,
1522424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1523424c4176SJason M. Bills              "Crashdump.OnDemand/")
15241da66f75SEd Tanous     {
15251da66f75SEd Tanous         entityPrivileges = {
1526e1f26343SJason M. Bills             {boost::beast::http::verb::get, {{"Login"}}},
1527e1f26343SJason M. Bills             {boost::beast::http::verb::head, {{"Login"}}},
1528e1f26343SJason M. Bills             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1529e1f26343SJason M. Bills             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1530e1f26343SJason M. Bills             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1531e1f26343SJason M. Bills             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
15321da66f75SEd Tanous     }
15331da66f75SEd Tanous 
15341da66f75SEd Tanous   private:
15351da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
15361da66f75SEd Tanous                 const std::vector<std::string> &params) override
15371da66f75SEd Tanous     {
1538e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
153948e4639eSJason M. Bills         static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
15401da66f75SEd Tanous 
154148e4639eSJason M. Bills         // Only allow one OnDemand Log request at a time
154248e4639eSJason M. Bills         if (onDemandLogMatcher != nullptr)
15431da66f75SEd Tanous         {
1544e1f26343SJason M. Bills             asyncResp->res.addHeader("Retry-After", "30");
1545f12894f8SJason M. Bills             messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
15461da66f75SEd Tanous             return;
15471da66f75SEd Tanous         }
15481da66f75SEd Tanous         // Make this static so it survives outside this method
15491da66f75SEd Tanous         static boost::asio::deadline_timer timeout(*req.ioService);
15501da66f75SEd Tanous 
15511da66f75SEd Tanous         timeout.expires_from_now(boost::posix_time::seconds(30));
1552e1f26343SJason M. Bills         timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
155348e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
15541da66f75SEd Tanous             if (ec)
15551da66f75SEd Tanous             {
15561da66f75SEd Tanous                 // operation_aborted is expected if timer is canceled before
15571da66f75SEd Tanous                 // completion.
15581da66f75SEd Tanous                 if (ec != boost::asio::error::operation_aborted)
15591da66f75SEd Tanous                 {
15601da66f75SEd Tanous                     BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
15611da66f75SEd Tanous                 }
15621da66f75SEd Tanous                 return;
15631da66f75SEd Tanous             }
156448e4639eSJason M. Bills             BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
15651da66f75SEd Tanous 
1566f12894f8SJason M. Bills             messages::internalError(asyncResp->res);
15671da66f75SEd Tanous         });
15681da66f75SEd Tanous 
156948e4639eSJason M. Bills         auto onDemandLogMatcherCallback = [asyncResp](
15701da66f75SEd Tanous                                               sdbusplus::message::message &m) {
157148e4639eSJason M. Bills             BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
15721da66f75SEd Tanous             boost::system::error_code ec;
15731da66f75SEd Tanous             timeout.cancel(ec);
15741da66f75SEd Tanous             if (ec)
15751da66f75SEd Tanous             {
15761da66f75SEd Tanous                 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
15771da66f75SEd Tanous             }
15784ed77cd5SEd Tanous             sdbusplus::message::object_path objPath;
15791da66f75SEd Tanous             boost::container::flat_map<
1580abf2add6SEd Tanous                 std::string, boost::container::flat_map<
1581abf2add6SEd Tanous                                  std::string, std::variant<std::string>>>
15824ed77cd5SEd Tanous                 interfacesAdded;
15834ed77cd5SEd Tanous             m.read(objPath, interfacesAdded);
1584abf2add6SEd Tanous             const std::string *log = std::get_if<std::string>(
1585424c4176SJason M. Bills                 &interfacesAdded[CrashdumpInterface]["Log"]);
15861da66f75SEd Tanous             if (log == nullptr)
15871da66f75SEd Tanous             {
1588f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
158948e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
15901da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
15911da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
15921da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
15931da66f75SEd Tanous                 // be the last thing done.
159448e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
15951da66f75SEd Tanous                 return;
15961da66f75SEd Tanous             }
15971da66f75SEd Tanous             nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
15981da66f75SEd Tanous             if (j.is_discarded())
15991da66f75SEd Tanous             {
1600f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
160148e4639eSJason M. Bills                 // Careful with onDemandLogMatcher.  It is a unique_ptr to the
16021da66f75SEd Tanous                 // match object inside which this lambda is executing.  Once it
16031da66f75SEd Tanous                 // is set to nullptr, the match object will be destroyed and the
16041da66f75SEd Tanous                 // lambda will lose its context, including res, so it needs to
16051da66f75SEd Tanous                 // be the last thing done.
160648e4639eSJason M. Bills                 onDemandLogMatcher = nullptr;
16071da66f75SEd Tanous                 return;
16081da66f75SEd Tanous             }
16091da66f75SEd Tanous             std::string t = getLogCreatedTime(j);
1610e1f26343SJason M. Bills             asyncResp->res.jsonValue = {
1611cb92c03bSAndrew Geissler                 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
16121da66f75SEd Tanous                 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1613424c4176SJason M. Bills                 {"Name", "CPU Crashdump"},
16141da66f75SEd Tanous                 {"EntryType", "Oem"},
1615424c4176SJason M. Bills                 {"OemRecordFormat", "Intel Crashdump"},
16161da66f75SEd Tanous                 {"Oem", {{"Intel", std::move(j)}}},
16171da66f75SEd Tanous                 {"Created", std::move(t)}};
161848e4639eSJason M. Bills             // Careful with onDemandLogMatcher.  It is a unique_ptr to the
16191da66f75SEd Tanous             // match object inside which this lambda is executing.  Once it is
16201da66f75SEd Tanous             // set to nullptr, the match object will be destroyed and the lambda
16211da66f75SEd Tanous             // will lose its context, including res, so it needs to be the last
16221da66f75SEd Tanous             // thing done.
162348e4639eSJason M. Bills             onDemandLogMatcher = nullptr;
16241da66f75SEd Tanous         };
162548e4639eSJason M. Bills         onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
16261da66f75SEd Tanous             *crow::connections::systemBus,
16271da66f75SEd Tanous             sdbusplus::bus::match::rules::interfacesAdded() +
1628424c4176SJason M. Bills                 sdbusplus::bus::match::rules::argNpath(0,
1629424c4176SJason M. Bills                                                        CrashdumpOnDemandPath),
163048e4639eSJason M. Bills             std::move(onDemandLogMatcherCallback));
16311da66f75SEd Tanous 
163248e4639eSJason M. Bills         auto generateonDemandLogCallback =
1633e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
16341da66f75SEd Tanous                         const std::string &resp) {
16351da66f75SEd Tanous                 if (ec)
16361da66f75SEd Tanous                 {
16371da66f75SEd Tanous                     if (ec.value() ==
16381da66f75SEd Tanous                         boost::system::errc::operation_not_supported)
16391da66f75SEd Tanous                     {
1640f12894f8SJason M. Bills                         messages::resourceInStandby(asyncResp->res);
16411da66f75SEd Tanous                     }
16421da66f75SEd Tanous                     else
16431da66f75SEd Tanous                     {
1644f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
16451da66f75SEd Tanous                     }
16461da66f75SEd Tanous                     boost::system::error_code timeoutec;
16471da66f75SEd Tanous                     timeout.cancel(timeoutec);
16481da66f75SEd Tanous                     if (timeoutec)
16491da66f75SEd Tanous                     {
16501da66f75SEd Tanous                         BMCWEB_LOG_ERROR << "error canceling timer "
16511da66f75SEd Tanous                                          << timeoutec;
16521da66f75SEd Tanous                     }
165348e4639eSJason M. Bills                     onDemandLogMatcher = nullptr;
16541da66f75SEd Tanous                     return;
16551da66f75SEd Tanous                 }
16561da66f75SEd Tanous             };
16571da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1658424c4176SJason M. Bills             std::move(generateonDemandLogCallback), CrashdumpObject,
1659424c4176SJason M. Bills             CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
16601da66f75SEd Tanous     }
16611da66f75SEd Tanous };
16621da66f75SEd Tanous 
1663e1f26343SJason M. Bills class SendRawPECI : public Node
16641da66f75SEd Tanous {
16651da66f75SEd Tanous   public:
1666e1f26343SJason M. Bills     SendRawPECI(CrowApp &app) :
1667424c4176SJason M. Bills         Node(app,
1668424c4176SJason M. Bills              "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1669424c4176SJason M. Bills              "Crashdump.SendRawPeci/")
16701da66f75SEd Tanous     {
16711da66f75SEd Tanous         entityPrivileges = {
16721da66f75SEd Tanous             {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
16731da66f75SEd Tanous             {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
16741da66f75SEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
16751da66f75SEd Tanous             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
16761da66f75SEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
16771da66f75SEd Tanous             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
16781da66f75SEd Tanous     }
16791da66f75SEd Tanous 
16801da66f75SEd Tanous   private:
16811da66f75SEd Tanous     void doPost(crow::Response &res, const crow::Request &req,
16821da66f75SEd Tanous                 const std::vector<std::string> &params) override
16831da66f75SEd Tanous     {
1684e1f26343SJason M. Bills         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1685b1556427SEd Tanous         uint8_t clientAddress = 0;
1686b1556427SEd Tanous         uint8_t readLength = 0;
16871da66f75SEd Tanous         std::vector<uint8_t> peciCommand;
1688b1556427SEd Tanous         if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1689b1556427SEd Tanous                                  "ReadLength", readLength, "PECICommand",
1690b1556427SEd Tanous                                  peciCommand))
16911da66f75SEd Tanous         {
16921da66f75SEd Tanous             return;
16931da66f75SEd Tanous         }
1694b1556427SEd Tanous 
16951da66f75SEd Tanous         // Callback to return the Raw PECI response
1696e1f26343SJason M. Bills         auto sendRawPECICallback =
1697e1f26343SJason M. Bills             [asyncResp](const boost::system::error_code ec,
16981da66f75SEd Tanous                         const std::vector<uint8_t> &resp) {
16991da66f75SEd Tanous                 if (ec)
17001da66f75SEd Tanous                 {
17011da66f75SEd Tanous                     BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
17021da66f75SEd Tanous                                      << ec.message();
1703f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
17041da66f75SEd Tanous                     return;
17051da66f75SEd Tanous                 }
1706e1f26343SJason M. Bills                 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
17071da66f75SEd Tanous                                             {"PECIResponse", resp}};
17081da66f75SEd Tanous             };
17091da66f75SEd Tanous         // Call the SendRawPECI command with the provided data
17101da66f75SEd Tanous         crow::connections::systemBus->async_method_call(
1711424c4176SJason M. Bills             std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1712424c4176SJason M. Bills             CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
17134ed77cd5SEd Tanous             peciCommand);
17141da66f75SEd Tanous     }
17151da66f75SEd Tanous };
17161da66f75SEd Tanous 
1717cb92c03bSAndrew Geissler /**
1718cb92c03bSAndrew Geissler  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1719cb92c03bSAndrew Geissler  */
1720cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node
1721cb92c03bSAndrew Geissler {
1722cb92c03bSAndrew Geissler   public:
1723cb92c03bSAndrew Geissler     DBusLogServiceActionsClear(CrowApp &app) :
1724cb92c03bSAndrew Geissler         Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1725cb92c03bSAndrew Geissler                   "LogService.Reset")
1726cb92c03bSAndrew Geissler     {
1727cb92c03bSAndrew Geissler         entityPrivileges = {
1728cb92c03bSAndrew Geissler             {boost::beast::http::verb::get, {{"Login"}}},
1729cb92c03bSAndrew Geissler             {boost::beast::http::verb::head, {{"Login"}}},
1730cb92c03bSAndrew Geissler             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1731cb92c03bSAndrew Geissler             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1732cb92c03bSAndrew Geissler             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1733cb92c03bSAndrew Geissler             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1734cb92c03bSAndrew Geissler     }
1735cb92c03bSAndrew Geissler 
1736cb92c03bSAndrew Geissler   private:
1737cb92c03bSAndrew Geissler     /**
1738cb92c03bSAndrew Geissler      * Function handles POST method request.
1739cb92c03bSAndrew Geissler      * The Clear Log actions does not require any parameter.The action deletes
1740cb92c03bSAndrew Geissler      * all entries found in the Entries collection for this Log Service.
1741cb92c03bSAndrew Geissler      */
1742cb92c03bSAndrew Geissler     void doPost(crow::Response &res, const crow::Request &req,
1743cb92c03bSAndrew Geissler                 const std::vector<std::string> &params) override
1744cb92c03bSAndrew Geissler     {
1745cb92c03bSAndrew Geissler         BMCWEB_LOG_DEBUG << "Do delete all entries.";
1746cb92c03bSAndrew Geissler 
1747cb92c03bSAndrew Geissler         auto asyncResp = std::make_shared<AsyncResp>(res);
1748cb92c03bSAndrew Geissler         // Process response from Logging service.
1749cb92c03bSAndrew Geissler         auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1750cb92c03bSAndrew Geissler             BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1751cb92c03bSAndrew Geissler             if (ec)
1752cb92c03bSAndrew Geissler             {
1753cb92c03bSAndrew Geissler                 // TODO Handle for specific error code
1754cb92c03bSAndrew Geissler                 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1755cb92c03bSAndrew Geissler                 asyncResp->res.result(
1756cb92c03bSAndrew Geissler                     boost::beast::http::status::internal_server_error);
1757cb92c03bSAndrew Geissler                 return;
1758cb92c03bSAndrew Geissler             }
1759cb92c03bSAndrew Geissler 
1760cb92c03bSAndrew Geissler             asyncResp->res.result(boost::beast::http::status::no_content);
1761cb92c03bSAndrew Geissler         };
1762cb92c03bSAndrew Geissler 
1763cb92c03bSAndrew Geissler         // Make call to Logging service to request Clear Log
1764cb92c03bSAndrew Geissler         crow::connections::systemBus->async_method_call(
1765cb92c03bSAndrew Geissler             resp_handler, "xyz.openbmc_project.Logging",
1766cb92c03bSAndrew Geissler             "/xyz/openbmc_project/logging",
1767cb92c03bSAndrew Geissler             "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1768cb92c03bSAndrew Geissler     }
1769cb92c03bSAndrew Geissler };
17701da66f75SEd Tanous } // namespace redfish
1771