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 ®istryName = 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, ×tamp); 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 260*e85d6b16SJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID, 261*e85d6b16SJason M. Bills const bool firstEntry = true) 26216428a1aSJason M. Bills { 26316428a1aSJason M. Bills int ret = 0; 26416428a1aSJason M. Bills static uint64_t prevTs = 0; 26516428a1aSJason M. Bills static int index = 0; 266*e85d6b16SJason M. Bills if (firstEntry) 267*e85d6b16SJason M. Bills { 268*e85d6b16SJason M. Bills prevTs = 0; 269*e85d6b16SJason M. Bills } 270*e85d6b16SJason M. Bills 27116428a1aSJason M. Bills // Get the entry timestamp 27216428a1aSJason M. Bills uint64_t curTs = 0; 27316428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 27416428a1aSJason M. Bills if (ret < 0) 27516428a1aSJason M. Bills { 27616428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 27716428a1aSJason M. Bills << strerror(-ret); 27816428a1aSJason M. Bills return false; 27916428a1aSJason M. Bills } 28016428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 28116428a1aSJason M. Bills if (curTs == prevTs) 28216428a1aSJason M. Bills { 28316428a1aSJason M. Bills index++; 28416428a1aSJason M. Bills } 28516428a1aSJason M. Bills else 28616428a1aSJason M. Bills { 28716428a1aSJason M. Bills // Otherwise, reset it 28816428a1aSJason M. Bills index = 0; 28916428a1aSJason M. Bills } 29016428a1aSJason M. Bills // Save the timestamp 29116428a1aSJason M. Bills prevTs = curTs; 29216428a1aSJason M. Bills 29316428a1aSJason M. Bills entryID = std::to_string(curTs); 29416428a1aSJason M. Bills if (index > 0) 29516428a1aSJason M. Bills { 29616428a1aSJason M. Bills entryID += "_" + std::to_string(index); 29716428a1aSJason M. Bills } 29816428a1aSJason M. Bills return true; 29916428a1aSJason M. Bills } 30016428a1aSJason M. Bills 301*e85d6b16SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID, 302*e85d6b16SJason M. Bills const bool firstEntry = true) 30395820184SJason M. Bills { 30495820184SJason M. Bills static uint64_t prevTs = 0; 30595820184SJason M. Bills static int index = 0; 306*e85d6b16SJason M. Bills if (firstEntry) 307*e85d6b16SJason M. Bills { 308*e85d6b16SJason M. Bills prevTs = 0; 309*e85d6b16SJason M. Bills } 310*e85d6b16SJason M. Bills 31195820184SJason M. Bills // Get the entry timestamp 31295820184SJason M. Bills uint64_t curTs = 0; 31395820184SJason M. Bills std::tm timeStruct = {}; 31495820184SJason M. Bills std::istringstream entryStream(logEntry); 31595820184SJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 31695820184SJason M. Bills { 31795820184SJason M. Bills curTs = std::mktime(&timeStruct); 31895820184SJason M. Bills } 31995820184SJason M. Bills // If the timestamp isn't unique, increment the index 32095820184SJason M. Bills if (curTs == prevTs) 32195820184SJason M. Bills { 32295820184SJason M. Bills index++; 32395820184SJason M. Bills } 32495820184SJason M. Bills else 32595820184SJason M. Bills { 32695820184SJason M. Bills // Otherwise, reset it 32795820184SJason M. Bills index = 0; 32895820184SJason M. Bills } 32995820184SJason M. Bills // Save the timestamp 33095820184SJason M. Bills prevTs = curTs; 33195820184SJason M. Bills 33295820184SJason M. Bills entryID = std::to_string(curTs); 33395820184SJason M. Bills if (index > 0) 33495820184SJason M. Bills { 33595820184SJason M. Bills entryID += "_" + std::to_string(index); 33695820184SJason M. Bills } 33795820184SJason M. Bills return true; 33895820184SJason M. Bills } 33995820184SJason M. Bills 34016428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 34116428a1aSJason M. Bills uint64_t ×tamp, uint16_t &index) 34216428a1aSJason M. Bills { 34316428a1aSJason M. Bills if (entryID.empty()) 34416428a1aSJason M. Bills { 34516428a1aSJason M. Bills return false; 34616428a1aSJason M. Bills } 34716428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 34839e77504SEd Tanous std::string_view tsStr(entryID); 34916428a1aSJason M. Bills 35016428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 35116428a1aSJason M. Bills if (underscorePos != tsStr.npos) 35216428a1aSJason M. Bills { 35316428a1aSJason M. Bills // Timestamp has an index 35416428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 35539e77504SEd Tanous std::string_view indexStr(entryID); 35616428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 35716428a1aSJason M. Bills std::size_t pos; 35816428a1aSJason M. Bills try 35916428a1aSJason M. Bills { 36039e77504SEd Tanous index = std::stoul(std::string(indexStr), &pos); 36116428a1aSJason M. Bills } 362b01bf299SEd Tanous catch (std::invalid_argument) 36316428a1aSJason M. Bills { 36416428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 36516428a1aSJason M. Bills return false; 36616428a1aSJason M. Bills } 367b01bf299SEd Tanous catch (std::out_of_range) 36816428a1aSJason M. Bills { 36916428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 37016428a1aSJason M. Bills return false; 37116428a1aSJason M. Bills } 37216428a1aSJason M. Bills if (pos != indexStr.size()) 37316428a1aSJason M. Bills { 37416428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 37516428a1aSJason M. Bills return false; 37616428a1aSJason M. Bills } 37716428a1aSJason M. Bills } 37816428a1aSJason M. Bills // Timestamp has no index 37916428a1aSJason M. Bills std::size_t pos; 38016428a1aSJason M. Bills try 38116428a1aSJason M. Bills { 38239e77504SEd Tanous timestamp = std::stoull(std::string(tsStr), &pos); 38316428a1aSJason M. Bills } 384b01bf299SEd Tanous catch (std::invalid_argument) 38516428a1aSJason M. Bills { 38616428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 38716428a1aSJason M. Bills return false; 38816428a1aSJason M. Bills } 389b01bf299SEd Tanous catch (std::out_of_range) 39016428a1aSJason M. Bills { 39116428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 39216428a1aSJason M. Bills return false; 39316428a1aSJason M. Bills } 39416428a1aSJason M. Bills if (pos != tsStr.size()) 39516428a1aSJason M. Bills { 39616428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 39716428a1aSJason M. Bills return false; 39816428a1aSJason M. Bills } 39916428a1aSJason M. Bills return true; 40016428a1aSJason M. Bills } 40116428a1aSJason M. Bills 40295820184SJason M. Bills static bool 40395820184SJason M. Bills getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles) 40495820184SJason M. Bills { 40595820184SJason M. Bills static const std::filesystem::path redfishLogDir = "/var/log"; 40695820184SJason M. Bills static const std::string redfishLogFilename = "redfish"; 40795820184SJason M. Bills 40895820184SJason M. Bills // Loop through the directory looking for redfish log files 40995820184SJason M. Bills for (const std::filesystem::directory_entry &dirEnt : 41095820184SJason M. Bills std::filesystem::directory_iterator(redfishLogDir)) 41195820184SJason M. Bills { 41295820184SJason M. Bills // If we find a redfish log file, save the path 41395820184SJason M. Bills std::string filename = dirEnt.path().filename(); 41495820184SJason M. Bills if (boost::starts_with(filename, redfishLogFilename)) 41595820184SJason M. Bills { 41695820184SJason M. Bills redfishLogFiles.emplace_back(redfishLogDir / filename); 41795820184SJason M. Bills } 41895820184SJason M. Bills } 41995820184SJason M. Bills // As the log files rotate, they are appended with a ".#" that is higher for 42095820184SJason M. Bills // the older logs. Since we don't expect more than 10 log files, we 42195820184SJason M. Bills // can just sort the list to get them in order from newest to oldest 42295820184SJason M. Bills std::sort(redfishLogFiles.begin(), redfishLogFiles.end()); 42395820184SJason M. Bills 42495820184SJason M. Bills return !redfishLogFiles.empty(); 42595820184SJason M. Bills } 42695820184SJason M. Bills 427c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node 4281da66f75SEd Tanous { 4291da66f75SEd Tanous public: 4301da66f75SEd Tanous template <typename CrowApp> 431c4bf6374SJason M. Bills SystemLogServiceCollection(CrowApp &app) : 432029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/") 433c4bf6374SJason M. Bills { 434c4bf6374SJason M. Bills entityPrivileges = { 435c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 436c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 437c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 438c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 439c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 440c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 441c4bf6374SJason M. Bills } 442c4bf6374SJason M. Bills 443c4bf6374SJason M. Bills private: 444c4bf6374SJason M. Bills /** 445c4bf6374SJason M. Bills * Functions triggers appropriate requests on DBus 446c4bf6374SJason M. Bills */ 447c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 448c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 449c4bf6374SJason M. Bills { 450c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 451c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 452c4bf6374SJason M. Bills // it has a duplicate entry for members 453c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 454c4bf6374SJason M. Bills "#LogServiceCollection.LogServiceCollection"; 455c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 456c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 457c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 458029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices"; 459c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 460c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 461c4bf6374SJason M. Bills "Collection of LogServices for this Computer System"; 462c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 463c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 464029573d4SEd Tanous logServiceArray.push_back( 465029573d4SEd Tanous {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); 466d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 467d53dd41fSJason M. Bills logServiceArray.push_back( 468cb92c03bSAndrew Geissler {{"@odata.id", 469424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"}}); 470d53dd41fSJason M. Bills #endif 471c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 472c4bf6374SJason M. Bills logServiceArray.size(); 473c4bf6374SJason M. Bills } 474c4bf6374SJason M. Bills }; 475c4bf6374SJason M. Bills 476c4bf6374SJason M. Bills class EventLogService : public Node 477c4bf6374SJason M. Bills { 478c4bf6374SJason M. Bills public: 479c4bf6374SJason M. Bills template <typename CrowApp> 480c4bf6374SJason M. Bills EventLogService(CrowApp &app) : 481029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/") 482c4bf6374SJason M. Bills { 483c4bf6374SJason M. Bills entityPrivileges = { 484c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 485c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 486c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 487c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 488c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 489c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 490c4bf6374SJason M. Bills } 491c4bf6374SJason M. Bills 492c4bf6374SJason M. Bills private: 493c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 494c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 495c4bf6374SJason M. Bills { 496c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 497c4bf6374SJason M. Bills 498c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 499029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog"; 500c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 501c4bf6374SJason M. Bills "#LogService.v1_1_0.LogService"; 502c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 503c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 504c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Event Log Service"; 505c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "System Event Log Service"; 506c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "Event Log"; 507c4bf6374SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 508c4bf6374SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 509c4bf6374SJason M. Bills {"@odata.id", 510029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}}; 511e7d6c8b2SGunnar Mills asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = { 512e7d6c8b2SGunnar Mills 513e7d6c8b2SGunnar Mills {"target", "/redfish/v1/Systems/system/LogServices/EventLog/" 514e7d6c8b2SGunnar Mills "Actions/LogService.ClearLog"}}; 515489640c6SJason M. Bills } 516489640c6SJason M. Bills }; 517489640c6SJason M. Bills 518489640c6SJason M. Bills class EventLogClear : public Node 519489640c6SJason M. Bills { 520489640c6SJason M. Bills public: 521489640c6SJason M. Bills EventLogClear(CrowApp &app) : 522489640c6SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 523489640c6SJason M. Bills "LogService.ClearLog/") 524489640c6SJason M. Bills { 525489640c6SJason M. Bills entityPrivileges = { 526489640c6SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 527489640c6SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 528489640c6SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 529489640c6SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 530489640c6SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 531489640c6SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 532489640c6SJason M. Bills } 533489640c6SJason M. Bills 534489640c6SJason M. Bills private: 535489640c6SJason M. Bills void doPost(crow::Response &res, const crow::Request &req, 536489640c6SJason M. Bills const std::vector<std::string> ¶ms) override 537489640c6SJason M. Bills { 538489640c6SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 539489640c6SJason M. Bills 540489640c6SJason M. Bills // Clear the EventLog by deleting the log files 541489640c6SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 542489640c6SJason M. Bills if (getRedfishLogFiles(redfishLogFiles)) 543489640c6SJason M. Bills { 544489640c6SJason M. Bills for (const std::filesystem::path &file : redfishLogFiles) 545489640c6SJason M. Bills { 546489640c6SJason M. Bills std::error_code ec; 547489640c6SJason M. Bills std::filesystem::remove(file, ec); 548489640c6SJason M. Bills } 549489640c6SJason M. Bills } 550489640c6SJason M. Bills 551489640c6SJason M. Bills // Reload rsyslog so it knows to start new log files 552489640c6SJason M. Bills crow::connections::systemBus->async_method_call( 553489640c6SJason M. Bills [asyncResp](const boost::system::error_code ec) { 554489640c6SJason M. Bills if (ec) 555489640c6SJason M. Bills { 556489640c6SJason M. Bills BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec; 557489640c6SJason M. Bills messages::internalError(asyncResp->res); 558489640c6SJason M. Bills return; 559489640c6SJason M. Bills } 560489640c6SJason M. Bills 561489640c6SJason M. Bills messages::success(asyncResp->res); 562489640c6SJason M. Bills }, 563489640c6SJason M. Bills "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 564489640c6SJason M. Bills "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", 565489640c6SJason M. Bills "replace"); 566c4bf6374SJason M. Bills } 567c4bf6374SJason M. Bills }; 568c4bf6374SJason M. Bills 56995820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID, 57095820184SJason M. Bills const std::string logEntry, 57195820184SJason M. Bills nlohmann::json &logEntryJson) 572c4bf6374SJason M. Bills { 57395820184SJason M. Bills // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 574cd225da8SJason M. Bills // First get the Timestamp 575cd225da8SJason M. Bills size_t space = logEntry.find_first_of(" "); 576cd225da8SJason M. Bills if (space == std::string::npos) 57795820184SJason M. Bills { 57895820184SJason M. Bills return 1; 57995820184SJason M. Bills } 580cd225da8SJason M. Bills std::string timestamp = logEntry.substr(0, space); 581cd225da8SJason M. Bills // Then get the log contents 582cd225da8SJason M. Bills size_t entryStart = logEntry.find_first_not_of(" ", space); 583cd225da8SJason M. Bills if (entryStart == std::string::npos) 584cd225da8SJason M. Bills { 585cd225da8SJason M. Bills return 1; 586cd225da8SJason M. Bills } 587cd225da8SJason M. Bills std::string_view entry(logEntry); 588cd225da8SJason M. Bills entry.remove_prefix(entryStart); 589cd225da8SJason M. Bills // Use split to separate the entry into its fields 590cd225da8SJason M. Bills std::vector<std::string> logEntryFields; 591cd225da8SJason M. Bills boost::split(logEntryFields, entry, boost::is_any_of(","), 592cd225da8SJason M. Bills boost::token_compress_on); 593cd225da8SJason M. Bills // We need at least a MessageId to be valid 594cd225da8SJason M. Bills if (logEntryFields.size() < 1) 595cd225da8SJason M. Bills { 596cd225da8SJason M. Bills return 1; 597cd225da8SJason M. Bills } 598cd225da8SJason M. Bills std::string &messageID = logEntryFields[0]; 59995820184SJason M. Bills 6004851d45dSJason M. Bills // Get the Message from the MessageRegistry 6014851d45dSJason M. Bills const message_registries::Message *message = 6024851d45dSJason M. Bills message_registries::getMessage(messageID); 603c4bf6374SJason M. Bills 6044851d45dSJason M. Bills std::string msg; 6054851d45dSJason M. Bills std::string severity; 6064851d45dSJason M. Bills if (message != nullptr) 607c4bf6374SJason M. Bills { 6084851d45dSJason M. Bills msg = message->message; 6094851d45dSJason M. Bills severity = message->severity; 610c4bf6374SJason M. Bills } 611c4bf6374SJason M. Bills 61215a86ff6SJason M. Bills // Get the MessageArgs from the log if there are any 61315a86ff6SJason M. Bills boost::beast::span<std::string> messageArgs; 61415a86ff6SJason M. Bills if (logEntryFields.size() > 1) 61515a86ff6SJason M. Bills { 61615a86ff6SJason M. Bills std::string &messageArgsStart = logEntryFields[1]; 61715a86ff6SJason M. Bills // If the first string is empty, assume there are no MessageArgs 61815a86ff6SJason M. Bills std::size_t messageArgsSize = 0; 61915a86ff6SJason M. Bills if (!messageArgsStart.empty()) 62015a86ff6SJason M. Bills { 62115a86ff6SJason M. Bills messageArgsSize = logEntryFields.size() - 1; 62215a86ff6SJason M. Bills } 62315a86ff6SJason M. Bills 62415a86ff6SJason M. Bills messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize); 625c4bf6374SJason M. Bills 6264851d45dSJason M. Bills // Fill the MessageArgs into the Message 62795820184SJason M. Bills int i = 0; 62895820184SJason M. Bills for (const std::string &messageArg : messageArgs) 6294851d45dSJason M. Bills { 63095820184SJason M. Bills std::string argStr = "%" + std::to_string(++i); 6314851d45dSJason M. Bills size_t argPos = msg.find(argStr); 6324851d45dSJason M. Bills if (argPos != std::string::npos) 6334851d45dSJason M. Bills { 63495820184SJason M. Bills msg.replace(argPos, argStr.length(), messageArg); 6354851d45dSJason M. Bills } 6364851d45dSJason M. Bills } 63715a86ff6SJason M. Bills } 6384851d45dSJason M. Bills 63995820184SJason M. Bills // Get the Created time from the timestamp. The log timestamp is in RFC3339 64095820184SJason M. Bills // format which matches the Redfish format except for the fractional seconds 64195820184SJason M. Bills // between the '.' and the '+', so just remove them. 64295820184SJason M. Bills std::size_t dot = timestamp.find_first_of("."); 64395820184SJason M. Bills std::size_t plus = timestamp.find_first_of("+"); 64495820184SJason M. Bills if (dot != std::string::npos && plus != std::string::npos) 645c4bf6374SJason M. Bills { 64695820184SJason M. Bills timestamp.erase(dot, plus - dot); 647c4bf6374SJason M. Bills } 648c4bf6374SJason M. Bills 649c4bf6374SJason M. Bills // Fill in the log entry with the gathered data 65095820184SJason M. Bills logEntryJson = { 651cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 652c4bf6374SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 653029573d4SEd Tanous {"@odata.id", 65495820184SJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/Entries/#" + 65595820184SJason M. Bills logEntryID}, 656c4bf6374SJason M. Bills {"Name", "System Event Log Entry"}, 65795820184SJason M. Bills {"Id", logEntryID}, 65895820184SJason M. Bills {"Message", std::move(msg)}, 65995820184SJason M. Bills {"MessageId", std::move(messageID)}, 660c4bf6374SJason M. Bills {"MessageArgs", std::move(messageArgs)}, 661c4bf6374SJason M. Bills {"EntryType", "Event"}, 66295820184SJason M. Bills {"Severity", std::move(severity)}, 66395820184SJason M. Bills {"Created", std::move(timestamp)}}; 664c4bf6374SJason M. Bills return 0; 665c4bf6374SJason M. Bills } 666c4bf6374SJason M. Bills 66727062605SAnthony Wilson class JournalEventLogEntryCollection : public Node 668c4bf6374SJason M. Bills { 669c4bf6374SJason M. Bills public: 670c4bf6374SJason M. Bills template <typename CrowApp> 67127062605SAnthony Wilson JournalEventLogEntryCollection(CrowApp &app) : 672029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 673c4bf6374SJason M. Bills { 674c4bf6374SJason M. Bills entityPrivileges = { 675c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 676c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 677c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 678c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 679c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 680c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 681c4bf6374SJason M. Bills } 682c4bf6374SJason M. Bills 683c4bf6374SJason M. Bills private: 684c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 685c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 686c4bf6374SJason M. Bills { 687c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 688c4bf6374SJason M. Bills long skip = 0; 689c4bf6374SJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 690c4bf6374SJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 691c4bf6374SJason M. Bills { 692c4bf6374SJason M. Bills return; 693c4bf6374SJason M. Bills } 694c4bf6374SJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 695c4bf6374SJason M. Bills { 696c4bf6374SJason M. Bills return; 697c4bf6374SJason M. Bills } 698c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 699c4bf6374SJason M. Bills // it has a duplicate entry for members 700c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 701c4bf6374SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 702c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 703c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 704c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 705029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 706c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 707c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 708c4bf6374SJason M. Bills "Collection of System Event Log Entries"; 709cb92c03bSAndrew Geissler 710c4bf6374SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 711c4bf6374SJason M. Bills logEntryArray = nlohmann::json::array(); 71295820184SJason M. Bills // Go through the log files and create a unique ID for each entry 71395820184SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 71495820184SJason M. Bills getRedfishLogFiles(redfishLogFiles); 715b01bf299SEd Tanous uint64_t entryCount = 0; 716cd225da8SJason M. Bills std::string logEntry; 71795820184SJason M. Bills 71895820184SJason M. Bills // Oldest logs are in the last file, so start there and loop backwards 719cd225da8SJason M. Bills for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); 720cd225da8SJason M. Bills it++) 721c4bf6374SJason M. Bills { 722cd225da8SJason M. Bills std::ifstream logStream(*it); 72395820184SJason M. Bills if (!logStream.is_open()) 724c4bf6374SJason M. Bills { 725c4bf6374SJason M. Bills continue; 726c4bf6374SJason M. Bills } 727c4bf6374SJason M. Bills 728*e85d6b16SJason M. Bills // Reset the unique ID on the first entry 729*e85d6b16SJason M. Bills bool firstEntry = true; 73095820184SJason M. Bills while (std::getline(logStream, logEntry)) 73195820184SJason M. Bills { 732c4bf6374SJason M. Bills entryCount++; 733c4bf6374SJason M. Bills // Handle paging using skip (number of entries to skip from the 734c4bf6374SJason M. Bills // start) and top (number of entries to display) 735c4bf6374SJason M. Bills if (entryCount <= skip || entryCount > skip + top) 736c4bf6374SJason M. Bills { 737c4bf6374SJason M. Bills continue; 738c4bf6374SJason M. Bills } 739c4bf6374SJason M. Bills 740c4bf6374SJason M. Bills std::string idStr; 741*e85d6b16SJason M. Bills if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 742c4bf6374SJason M. Bills { 743c4bf6374SJason M. Bills continue; 744c4bf6374SJason M. Bills } 745c4bf6374SJason M. Bills 746*e85d6b16SJason M. Bills if (firstEntry) 747*e85d6b16SJason M. Bills { 748*e85d6b16SJason M. Bills firstEntry = false; 749*e85d6b16SJason M. Bills } 750*e85d6b16SJason M. Bills 751c4bf6374SJason M. Bills logEntryArray.push_back({}); 752c4bf6374SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 75395820184SJason M. Bills if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0) 754c4bf6374SJason M. Bills { 755c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 756c4bf6374SJason M. Bills return; 757c4bf6374SJason M. Bills } 758c4bf6374SJason M. Bills } 75995820184SJason M. Bills } 760c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 761c4bf6374SJason M. Bills if (skip + top < entryCount) 762c4bf6374SJason M. Bills { 763c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 76495820184SJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/" 76595820184SJason M. Bills "Entries?$skip=" + 766c4bf6374SJason M. Bills std::to_string(skip + top); 767c4bf6374SJason M. Bills } 76808a4e4b5SAnthony Wilson } 76908a4e4b5SAnthony Wilson }; 77008a4e4b5SAnthony Wilson 77108a4e4b5SAnthony Wilson class DBusEventLogEntryCollection : public Node 77208a4e4b5SAnthony Wilson { 77308a4e4b5SAnthony Wilson public: 77408a4e4b5SAnthony Wilson template <typename CrowApp> 77508a4e4b5SAnthony Wilson DBusEventLogEntryCollection(CrowApp &app) : 77608a4e4b5SAnthony Wilson Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 77708a4e4b5SAnthony Wilson { 77808a4e4b5SAnthony Wilson entityPrivileges = { 77908a4e4b5SAnthony Wilson {boost::beast::http::verb::get, {{"Login"}}}, 78008a4e4b5SAnthony Wilson {boost::beast::http::verb::head, {{"Login"}}}, 78108a4e4b5SAnthony Wilson {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 78208a4e4b5SAnthony Wilson {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 78308a4e4b5SAnthony Wilson {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 78408a4e4b5SAnthony Wilson {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 78508a4e4b5SAnthony Wilson } 78608a4e4b5SAnthony Wilson 78708a4e4b5SAnthony Wilson private: 78808a4e4b5SAnthony Wilson void doGet(crow::Response &res, const crow::Request &req, 78908a4e4b5SAnthony Wilson const std::vector<std::string> ¶ms) override 79008a4e4b5SAnthony Wilson { 79108a4e4b5SAnthony Wilson std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 79208a4e4b5SAnthony Wilson 79308a4e4b5SAnthony Wilson // Collections don't include the static data added by SubRoute because 79408a4e4b5SAnthony Wilson // it has a duplicate entry for members 79508a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.type"] = 79608a4e4b5SAnthony Wilson "#LogEntryCollection.LogEntryCollection"; 79708a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.context"] = 79808a4e4b5SAnthony Wilson "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 79908a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.id"] = 80008a4e4b5SAnthony Wilson "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 80108a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 80208a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Description"] = 80308a4e4b5SAnthony Wilson "Collection of System Event Log Entries"; 80408a4e4b5SAnthony Wilson 805cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 806cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 807cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 808cb92c03bSAndrew Geissler [asyncResp](const boost::system::error_code ec, 809cb92c03bSAndrew Geissler GetManagedObjectsType &resp) { 810cb92c03bSAndrew Geissler if (ec) 811cb92c03bSAndrew Geissler { 812cb92c03bSAndrew Geissler // TODO Handle for specific error code 813cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 814cb92c03bSAndrew Geissler << "getLogEntriesIfaceData resp_handler got error " 815cb92c03bSAndrew Geissler << ec; 816cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 817cb92c03bSAndrew Geissler return; 818cb92c03bSAndrew Geissler } 819cb92c03bSAndrew Geissler nlohmann::json &entriesArray = 820cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members"]; 821cb92c03bSAndrew Geissler entriesArray = nlohmann::json::array(); 822cb92c03bSAndrew Geissler for (auto &objectPath : resp) 823cb92c03bSAndrew Geissler { 824cb92c03bSAndrew Geissler for (auto &interfaceMap : objectPath.second) 825cb92c03bSAndrew Geissler { 826cb92c03bSAndrew Geissler if (interfaceMap.first != 827cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry") 828cb92c03bSAndrew Geissler { 829cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Bailing early on " 830cb92c03bSAndrew Geissler << interfaceMap.first; 831cb92c03bSAndrew Geissler continue; 832cb92c03bSAndrew Geissler } 833cb92c03bSAndrew Geissler entriesArray.push_back({}); 834cb92c03bSAndrew Geissler nlohmann::json &thisEntry = entriesArray.back(); 835cb92c03bSAndrew Geissler uint32_t *id; 836cb92c03bSAndrew Geissler std::time_t timestamp; 837cb92c03bSAndrew Geissler std::string *severity, *message; 838cb92c03bSAndrew Geissler bool *resolved; 839cb92c03bSAndrew Geissler for (auto &propertyMap : interfaceMap.second) 840cb92c03bSAndrew Geissler { 841cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 842cb92c03bSAndrew Geissler { 843cb92c03bSAndrew Geissler id = sdbusplus::message::variant_ns::get_if< 844cb92c03bSAndrew Geissler uint32_t>(&propertyMap.second); 845cb92c03bSAndrew Geissler if (id == nullptr) 846cb92c03bSAndrew Geissler { 847cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 848cb92c03bSAndrew Geissler "Id"); 849cb92c03bSAndrew Geissler } 850cb92c03bSAndrew Geissler } 851cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 852cb92c03bSAndrew Geissler { 853cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 854cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 855cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 856cb92c03bSAndrew Geissler { 857cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 858cb92c03bSAndrew Geissler "Timestamp"); 859cb92c03bSAndrew Geissler } 860cb92c03bSAndrew Geissler // Retrieve Created property with format: 861cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 862cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 863cb92c03bSAndrew Geissler *millisTimeStamp); 864cb92c03bSAndrew Geissler timestamp = 865cb92c03bSAndrew Geissler std::chrono::duration_cast< 866cb92c03bSAndrew Geissler std::chrono::seconds>(chronoTimeStamp) 867cb92c03bSAndrew Geissler .count(); 868cb92c03bSAndrew Geissler } 869cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 870cb92c03bSAndrew Geissler { 871cb92c03bSAndrew Geissler severity = std::get_if<std::string>( 872cb92c03bSAndrew Geissler &propertyMap.second); 873cb92c03bSAndrew Geissler if (severity == nullptr) 874cb92c03bSAndrew Geissler { 875cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 876cb92c03bSAndrew Geissler "Severity"); 877cb92c03bSAndrew Geissler } 878cb92c03bSAndrew Geissler } 879cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 880cb92c03bSAndrew Geissler { 881cb92c03bSAndrew Geissler message = std::get_if<std::string>( 882cb92c03bSAndrew Geissler &propertyMap.second); 883cb92c03bSAndrew Geissler if (message == nullptr) 884cb92c03bSAndrew Geissler { 885cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 886cb92c03bSAndrew Geissler "Message"); 887cb92c03bSAndrew Geissler } 888cb92c03bSAndrew Geissler } 889cb92c03bSAndrew Geissler } 890cb92c03bSAndrew Geissler thisEntry = { 891cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 892cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 893cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 894cb92c03bSAndrew Geissler {"@odata.id", 895cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 896cb92c03bSAndrew Geissler "Entries/" + 897cb92c03bSAndrew Geissler std::to_string(*id)}, 89827062605SAnthony Wilson {"Name", "System Event Log Entry"}, 899cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 900cb92c03bSAndrew Geissler {"Message", *message}, 901cb92c03bSAndrew Geissler {"EntryType", "Event"}, 902cb92c03bSAndrew Geissler {"Severity", 903cb92c03bSAndrew Geissler translateSeverityDbusToRedfish(*severity)}, 904cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 905cb92c03bSAndrew Geissler } 906cb92c03bSAndrew Geissler } 907cb92c03bSAndrew Geissler std::sort(entriesArray.begin(), entriesArray.end(), 908cb92c03bSAndrew Geissler [](const nlohmann::json &left, 909cb92c03bSAndrew Geissler const nlohmann::json &right) { 910cb92c03bSAndrew Geissler return (left["Id"] <= right["Id"]); 911cb92c03bSAndrew Geissler }); 912cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members@odata.count"] = 913cb92c03bSAndrew Geissler entriesArray.size(); 914cb92c03bSAndrew Geissler }, 915cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 916cb92c03bSAndrew Geissler "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 917c4bf6374SJason M. Bills } 918c4bf6374SJason M. Bills }; 919c4bf6374SJason M. Bills 92008a4e4b5SAnthony Wilson class DBusEventLogEntry : public Node 921c4bf6374SJason M. Bills { 922c4bf6374SJason M. Bills public: 92308a4e4b5SAnthony Wilson DBusEventLogEntry(CrowApp &app) : 924c4bf6374SJason M. Bills Node(app, 925029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 926029573d4SEd Tanous std::string()) 927c4bf6374SJason M. Bills { 928c4bf6374SJason M. Bills entityPrivileges = { 929c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 930c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 931c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 932c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 933c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 934c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 935c4bf6374SJason M. Bills } 936c4bf6374SJason M. Bills 937c4bf6374SJason M. Bills private: 938c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 939c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 940c4bf6374SJason M. Bills { 941c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 942029573d4SEd Tanous if (params.size() != 1) 943c4bf6374SJason M. Bills { 944c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 945c4bf6374SJason M. Bills return; 946c4bf6374SJason M. Bills } 947029573d4SEd Tanous const std::string &entryID = params[0]; 948cb92c03bSAndrew Geissler 949cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 950cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 951cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 952cb92c03bSAndrew Geissler [asyncResp, entryID](const boost::system::error_code ec, 953cb92c03bSAndrew Geissler GetManagedPropertyType &resp) { 954cb92c03bSAndrew Geissler if (ec) 955cb92c03bSAndrew Geissler { 956cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 957cb92c03bSAndrew Geissler << "EventLogEntry (DBus) resp_handler got error " << ec; 958cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 959cb92c03bSAndrew Geissler return; 960cb92c03bSAndrew Geissler } 961cb92c03bSAndrew Geissler uint32_t *id; 962cb92c03bSAndrew Geissler std::time_t timestamp; 963cb92c03bSAndrew Geissler std::string *severity, *message; 964cb92c03bSAndrew Geissler bool *resolved; 965cb92c03bSAndrew Geissler for (auto &propertyMap : resp) 966cb92c03bSAndrew Geissler { 967cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 968cb92c03bSAndrew Geissler { 969cb92c03bSAndrew Geissler id = std::get_if<uint32_t>(&propertyMap.second); 970cb92c03bSAndrew Geissler if (id == nullptr) 971cb92c03bSAndrew Geissler { 972cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, "Id"); 973cb92c03bSAndrew Geissler } 974cb92c03bSAndrew Geissler } 975cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 976cb92c03bSAndrew Geissler { 977cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 978cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 979cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 980cb92c03bSAndrew Geissler { 981cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 982cb92c03bSAndrew Geissler "Timestamp"); 983cb92c03bSAndrew Geissler } 984cb92c03bSAndrew Geissler // Retrieve Created property with format: 985cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 986cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 987cb92c03bSAndrew Geissler *millisTimeStamp); 988cb92c03bSAndrew Geissler timestamp = 989cb92c03bSAndrew Geissler std::chrono::duration_cast<std::chrono::seconds>( 990cb92c03bSAndrew Geissler chronoTimeStamp) 991cb92c03bSAndrew Geissler .count(); 992cb92c03bSAndrew Geissler } 993cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 994cb92c03bSAndrew Geissler { 995cb92c03bSAndrew Geissler severity = 996cb92c03bSAndrew Geissler std::get_if<std::string>(&propertyMap.second); 997cb92c03bSAndrew Geissler if (severity == nullptr) 998cb92c03bSAndrew Geissler { 999cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1000cb92c03bSAndrew Geissler "Severity"); 1001cb92c03bSAndrew Geissler } 1002cb92c03bSAndrew Geissler } 1003cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 1004cb92c03bSAndrew Geissler { 1005cb92c03bSAndrew Geissler message = std::get_if<std::string>(&propertyMap.second); 1006cb92c03bSAndrew Geissler if (message == nullptr) 1007cb92c03bSAndrew Geissler { 1008cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1009cb92c03bSAndrew Geissler "Message"); 1010cb92c03bSAndrew Geissler } 1011cb92c03bSAndrew Geissler } 1012cb92c03bSAndrew Geissler } 1013cb92c03bSAndrew Geissler asyncResp->res.jsonValue = { 1014cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1015cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 1016cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 1017cb92c03bSAndrew Geissler {"@odata.id", 1018cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 1019cb92c03bSAndrew Geissler "Entries/" + 1020cb92c03bSAndrew Geissler std::to_string(*id)}, 102127062605SAnthony Wilson {"Name", "System Event Log Entry"}, 1022cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 1023cb92c03bSAndrew Geissler {"Message", *message}, 1024cb92c03bSAndrew Geissler {"EntryType", "Event"}, 1025cb92c03bSAndrew Geissler {"Severity", translateSeverityDbusToRedfish(*severity)}, 102608a4e4b5SAnthony Wilson {"Created", crow::utility::getDateTime(timestamp)}}; 1027cb92c03bSAndrew Geissler }, 1028cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", 1029cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging/entry/" + entryID, 1030cb92c03bSAndrew Geissler "org.freedesktop.DBus.Properties", "GetAll", 1031cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry"); 1032c4bf6374SJason M. Bills } 1033336e96c6SChicago Duan 1034336e96c6SChicago Duan void doDelete(crow::Response &res, const crow::Request &req, 1035336e96c6SChicago Duan const std::vector<std::string> ¶ms) override 1036336e96c6SChicago Duan { 1037336e96c6SChicago Duan 1038336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "Do delete single event entries."; 1039336e96c6SChicago Duan 1040336e96c6SChicago Duan auto asyncResp = std::make_shared<AsyncResp>(res); 1041336e96c6SChicago Duan 1042336e96c6SChicago Duan if (params.size() != 1) 1043336e96c6SChicago Duan { 1044336e96c6SChicago Duan messages::internalError(asyncResp->res); 1045336e96c6SChicago Duan return; 1046336e96c6SChicago Duan } 1047336e96c6SChicago Duan std::string entryID = params[0]; 1048336e96c6SChicago Duan 1049336e96c6SChicago Duan dbus::utility::escapePathForDbus(entryID); 1050336e96c6SChicago Duan 1051336e96c6SChicago Duan // Process response from Logging service. 1052336e96c6SChicago Duan auto respHandler = [asyncResp](const boost::system::error_code ec) { 1053336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done"; 1054336e96c6SChicago Duan if (ec) 1055336e96c6SChicago Duan { 1056336e96c6SChicago Duan // TODO Handle for specific error code 1057336e96c6SChicago Duan BMCWEB_LOG_ERROR 1058336e96c6SChicago Duan << "EventLogEntry (DBus) doDelete respHandler got error " 1059336e96c6SChicago Duan << ec; 1060336e96c6SChicago Duan asyncResp->res.result( 1061336e96c6SChicago Duan boost::beast::http::status::internal_server_error); 1062336e96c6SChicago Duan return; 1063336e96c6SChicago Duan } 1064336e96c6SChicago Duan 1065336e96c6SChicago Duan asyncResp->res.result(boost::beast::http::status::ok); 1066336e96c6SChicago Duan }; 1067336e96c6SChicago Duan 1068336e96c6SChicago Duan // Make call to Logging service to request Delete Log 1069336e96c6SChicago Duan crow::connections::systemBus->async_method_call( 1070336e96c6SChicago Duan respHandler, "xyz.openbmc_project.Logging", 1071336e96c6SChicago Duan "/xyz/openbmc_project/logging/entry/" + entryID, 1072336e96c6SChicago Duan "xyz.openbmc_project.Object.Delete", "Delete"); 1073336e96c6SChicago Duan } 1074c4bf6374SJason M. Bills }; 1075c4bf6374SJason M. Bills 1076c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node 1077c4bf6374SJason M. Bills { 1078c4bf6374SJason M. Bills public: 1079c4bf6374SJason M. Bills template <typename CrowApp> 1080c4bf6374SJason M. Bills BMCLogServiceCollection(CrowApp &app) : 10814ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 10821da66f75SEd Tanous { 10831da66f75SEd Tanous entityPrivileges = { 1084e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1085e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1086e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1087e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1088e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1089e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 10901da66f75SEd Tanous } 10911da66f75SEd Tanous 10921da66f75SEd Tanous private: 10931da66f75SEd Tanous /** 10941da66f75SEd Tanous * Functions triggers appropriate requests on DBus 10951da66f75SEd Tanous */ 10961da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 10971da66f75SEd Tanous const std::vector<std::string> ¶ms) override 10981da66f75SEd Tanous { 1099e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 11001da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 11011da66f75SEd Tanous // it has a duplicate entry for members 1102e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 11031da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 1104e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1105c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 1106e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1107e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 1108e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 1109e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 11101da66f75SEd Tanous "Collection of LogServices for this Manager"; 1111c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 1112c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 1113c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL 1114c4bf6374SJason M. Bills logServiceArray.push_back( 111508a4e4b5SAnthony Wilson {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}}); 1116c4bf6374SJason M. Bills #endif 1117e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1118c4bf6374SJason M. Bills logServiceArray.size(); 11191da66f75SEd Tanous } 11201da66f75SEd Tanous }; 11211da66f75SEd Tanous 1122c4bf6374SJason M. Bills class BMCJournalLogService : public Node 11231da66f75SEd Tanous { 11241da66f75SEd Tanous public: 11251da66f75SEd Tanous template <typename CrowApp> 1126c4bf6374SJason M. Bills BMCJournalLogService(CrowApp &app) : 1127c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") 1128e1f26343SJason M. Bills { 1129e1f26343SJason M. Bills entityPrivileges = { 1130e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1131e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1132e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1133e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1134e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1135e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1136e1f26343SJason M. Bills } 1137e1f26343SJason M. Bills 1138e1f26343SJason M. Bills private: 1139e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1140e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1141e1f26343SJason M. Bills { 1142e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1143e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1144e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 11450f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 11460f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal"; 1147e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1148e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 1149c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 1150c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 1151c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Journal"; 1152e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1153cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1154cd50aa42SJason M. Bills {"@odata.id", 1155086be238SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}}; 1156e1f26343SJason M. Bills } 1157e1f26343SJason M. Bills }; 1158e1f26343SJason M. Bills 1159c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, 1160e1f26343SJason M. Bills sd_journal *journal, 1161c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntryJson) 1162e1f26343SJason M. Bills { 1163e1f26343SJason M. Bills // Get the Log Entry contents 1164e1f26343SJason M. Bills int ret = 0; 1165e1f26343SJason M. Bills 116639e77504SEd Tanous std::string_view msg; 116716428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 1168e1f26343SJason M. Bills if (ret < 0) 1169e1f26343SJason M. Bills { 1170e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 1171e1f26343SJason M. Bills return 1; 1172e1f26343SJason M. Bills } 1173e1f26343SJason M. Bills 1174e1f26343SJason M. Bills // Get the severity from the PRIORITY field 1175e1f26343SJason M. Bills int severity = 8; // Default to an invalid priority 117616428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 1177e1f26343SJason M. Bills if (ret < 0) 1178e1f26343SJason M. Bills { 1179e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 1180e1f26343SJason M. Bills return 1; 1181e1f26343SJason M. Bills } 1182e1f26343SJason M. Bills 1183e1f26343SJason M. Bills // Get the Created time from the timestamp 118416428a1aSJason M. Bills std::string entryTimeStr; 118516428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 1186e1f26343SJason M. Bills { 118716428a1aSJason M. Bills return 1; 1188e1f26343SJason M. Bills } 1189e1f26343SJason M. Bills 1190e1f26343SJason M. Bills // Fill in the log entry with the gathered data 1191c4bf6374SJason M. Bills bmcJournalLogEntryJson = { 1192cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1193e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 1194c4bf6374SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + 1195c4bf6374SJason M. Bills bmcJournalLogEntryID}, 1196e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 1197c4bf6374SJason M. Bills {"Id", bmcJournalLogEntryID}, 119816428a1aSJason M. Bills {"Message", msg}, 1199e1f26343SJason M. Bills {"EntryType", "Oem"}, 1200e1f26343SJason M. Bills {"Severity", 1201e1f26343SJason M. Bills severity <= 2 ? "Critical" 1202e1f26343SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 1203086be238SEd Tanous {"OemRecordFormat", "BMC Journal Entry"}, 1204e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 1205e1f26343SJason M. Bills return 0; 1206e1f26343SJason M. Bills } 1207e1f26343SJason M. Bills 1208c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node 1209e1f26343SJason M. Bills { 1210e1f26343SJason M. Bills public: 1211e1f26343SJason M. Bills template <typename CrowApp> 1212c4bf6374SJason M. Bills BMCJournalLogEntryCollection(CrowApp &app) : 1213c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/") 1214e1f26343SJason M. Bills { 1215e1f26343SJason M. Bills entityPrivileges = { 1216e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1217e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1218e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1219e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1220e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1221e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1222e1f26343SJason M. Bills } 1223e1f26343SJason M. Bills 1224e1f26343SJason M. Bills private: 1225e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1226e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1227e1f26343SJason M. Bills { 1228e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1229193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 1230193ad2faSJason M. Bills long skip = 0; 1231193ad2faSJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 123216428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 1233193ad2faSJason M. Bills { 1234193ad2faSJason M. Bills return; 1235193ad2faSJason M. Bills } 123616428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 1237193ad2faSJason M. Bills { 1238193ad2faSJason M. Bills return; 1239193ad2faSJason M. Bills } 1240e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 1241e1f26343SJason M. Bills // it has a duplicate entry for members 1242e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1243e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 12440f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 12450f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1246e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1247c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 1248e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1249c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1250e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 1251e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1252e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 12530f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 12540f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 1255e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1256e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1257e1f26343SJason M. Bills 1258e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 1259e1f26343SJason M. Bills // for each entry 1260e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1261e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1262e1f26343SJason M. Bills if (ret < 0) 1263e1f26343SJason M. Bills { 1264e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1265f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1266e1f26343SJason M. Bills return; 1267e1f26343SJason M. Bills } 1268e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1269e1f26343SJason M. Bills journalTmp, sd_journal_close); 1270e1f26343SJason M. Bills journalTmp = nullptr; 1271b01bf299SEd Tanous uint64_t entryCount = 0; 1272*e85d6b16SJason M. Bills // Reset the unique ID on the first entry 1273*e85d6b16SJason M. Bills bool firstEntry = true; 1274e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 1275e1f26343SJason M. Bills { 1276193ad2faSJason M. Bills entryCount++; 1277193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 1278193ad2faSJason M. Bills // start) and top (number of entries to display) 1279193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 1280193ad2faSJason M. Bills { 1281193ad2faSJason M. Bills continue; 1282193ad2faSJason M. Bills } 1283193ad2faSJason M. Bills 128416428a1aSJason M. Bills std::string idStr; 1285*e85d6b16SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) 1286e1f26343SJason M. Bills { 1287e1f26343SJason M. Bills continue; 1288e1f26343SJason M. Bills } 1289e1f26343SJason M. Bills 1290*e85d6b16SJason M. Bills if (firstEntry) 1291*e85d6b16SJason M. Bills { 1292*e85d6b16SJason M. Bills firstEntry = false; 1293*e85d6b16SJason M. Bills } 1294*e85d6b16SJason M. Bills 1295e1f26343SJason M. Bills logEntryArray.push_back({}); 1296c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); 1297c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(idStr, journal.get(), 1298c4bf6374SJason M. Bills bmcJournalLogEntry) != 0) 1299e1f26343SJason M. Bills { 1300f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1301e1f26343SJason M. Bills return; 1302e1f26343SJason M. Bills } 1303e1f26343SJason M. Bills } 1304193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1305193ad2faSJason M. Bills if (skip + top < entryCount) 1306193ad2faSJason M. Bills { 1307193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 1308c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + 1309193ad2faSJason M. Bills std::to_string(skip + top); 1310193ad2faSJason M. Bills } 1311e1f26343SJason M. Bills } 1312e1f26343SJason M. Bills }; 1313e1f26343SJason M. Bills 1314c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node 1315e1f26343SJason M. Bills { 1316e1f26343SJason M. Bills public: 1317c4bf6374SJason M. Bills BMCJournalLogEntry(CrowApp &app) : 1318c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", 1319e1f26343SJason M. Bills std::string()) 1320e1f26343SJason M. Bills { 1321e1f26343SJason M. Bills entityPrivileges = { 1322e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1323e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1324e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1325e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1326e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1327e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1328e1f26343SJason M. Bills } 1329e1f26343SJason M. Bills 1330e1f26343SJason M. Bills private: 1331e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1332e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1333e1f26343SJason M. Bills { 1334e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1335e1f26343SJason M. Bills if (params.size() != 1) 1336e1f26343SJason M. Bills { 1337f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1338e1f26343SJason M. Bills return; 1339e1f26343SJason M. Bills } 134016428a1aSJason M. Bills const std::string &entryID = params[0]; 1341e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 1342e1f26343SJason M. Bills uint64_t ts = 0; 1343e1f26343SJason M. Bills uint16_t index = 0; 134416428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 1345e1f26343SJason M. Bills { 134616428a1aSJason M. Bills return; 1347e1f26343SJason M. Bills } 1348e1f26343SJason M. Bills 1349e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1350e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1351e1f26343SJason M. Bills if (ret < 0) 1352e1f26343SJason M. Bills { 1353e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1354f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1355e1f26343SJason M. Bills return; 1356e1f26343SJason M. Bills } 1357e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1358e1f26343SJason M. Bills journalTmp, sd_journal_close); 1359e1f26343SJason M. Bills journalTmp = nullptr; 1360e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 1361e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 1362e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 1363e1f26343SJason M. Bills { 1364e1f26343SJason M. Bills sd_journal_next(journal.get()); 1365e1f26343SJason M. Bills } 1366c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 1367c4bf6374SJason M. Bills std::string idStr; 1368c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) 1369c4bf6374SJason M. Bills { 1370c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 1371c4bf6374SJason M. Bills return; 1372c4bf6374SJason M. Bills } 1373c4bf6374SJason M. Bills 1374c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(entryID, journal.get(), 1375e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 1376e1f26343SJason M. Bills { 1377f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1378e1f26343SJason M. Bills return; 1379e1f26343SJason M. Bills } 1380e1f26343SJason M. Bills } 1381e1f26343SJason M. Bills }; 1382e1f26343SJason M. Bills 1383424c4176SJason M. Bills class CrashdumpService : public Node 1384e1f26343SJason M. Bills { 1385e1f26343SJason M. Bills public: 1386e1f26343SJason M. Bills template <typename CrowApp> 1387424c4176SJason M. Bills CrashdumpService(CrowApp &app) : 1388424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/") 13891da66f75SEd Tanous { 13901da66f75SEd Tanous entityPrivileges = { 1391e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1392e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1393e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1394e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1395e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1396e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 13971da66f75SEd Tanous } 13981da66f75SEd Tanous 13991da66f75SEd Tanous private: 14001da66f75SEd Tanous /** 14011da66f75SEd Tanous * Functions triggers appropriate requests on DBus 14021da66f75SEd Tanous */ 14031da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 14041da66f75SEd Tanous const std::vector<std::string> ¶ms) override 14051da66f75SEd Tanous { 1406e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 14071da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 14080f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1409424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"; 1410e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1411e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 1412e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1413c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 1414424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service"; 1415424c4176SJason M. Bills asyncResp->res.jsonValue["Description"] = "Crashdump Service"; 1416424c4176SJason M. Bills asyncResp->res.jsonValue["Id"] = "Crashdump"; 1417e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1418e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 1419cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1420cd50aa42SJason M. Bills {"@odata.id", 1421424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}}; 1422e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 14231da66f75SEd Tanous {"Oem", 1424424c4176SJason M. Bills {{"#Crashdump.OnDemand", 1425424c4176SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1426424c4176SJason M. Bills "Actions/Oem/Crashdump.OnDemand"}}}}}}; 14271da66f75SEd Tanous 14281da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 1429e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 1430424c4176SJason M. Bills {"#Crashdump.SendRawPeci", 143108a4e4b5SAnthony Wilson {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1432424c4176SJason M. Bills "Actions/Oem/Crashdump.SendRawPeci"}}}); 14331da66f75SEd Tanous #endif 14341da66f75SEd Tanous } 14351da66f75SEd Tanous }; 14361da66f75SEd Tanous 1437424c4176SJason M. Bills class CrashdumpEntryCollection : public Node 14381da66f75SEd Tanous { 14391da66f75SEd Tanous public: 14401da66f75SEd Tanous template <typename CrowApp> 1441424c4176SJason M. Bills CrashdumpEntryCollection(CrowApp &app) : 1442424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/") 14431da66f75SEd Tanous { 14441da66f75SEd Tanous entityPrivileges = { 1445e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1446e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1447e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1448e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1449e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1450e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 14511da66f75SEd Tanous } 14521da66f75SEd Tanous 14531da66f75SEd Tanous private: 14541da66f75SEd Tanous /** 14551da66f75SEd Tanous * Functions triggers appropriate requests on DBus 14561da66f75SEd Tanous */ 14571da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 14581da66f75SEd Tanous const std::vector<std::string> ¶ms) override 14591da66f75SEd Tanous { 1460e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 14611da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 14621da66f75SEd Tanous // it has a duplicate entry for members 1463e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 1464e1f26343SJason M. Bills const boost::system::error_code ec, 14651da66f75SEd Tanous const std::vector<std::string> &resp) { 14661da66f75SEd Tanous if (ec) 14671da66f75SEd Tanous { 14681da66f75SEd Tanous if (ec.value() != 14691da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 14701da66f75SEd Tanous { 14711da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 14721da66f75SEd Tanous << ec.message(); 1473f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14741da66f75SEd Tanous return; 14751da66f75SEd Tanous } 14761da66f75SEd Tanous } 1477e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 14781da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 14790f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1480424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"; 1481e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1482d53dd41fSJason M. Bills "/redfish/v1/" 1483d53dd41fSJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 1484424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries"; 1485e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1486424c4176SJason M. Bills "Collection of Crashdump Entries"; 1487e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1488e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 14891da66f75SEd Tanous for (const std::string &objpath : resp) 14901da66f75SEd Tanous { 149148e4639eSJason M. Bills // Don't list the on-demand log 1492424c4176SJason M. Bills if (objpath.compare(CrashdumpOnDemandPath) == 0) 14931da66f75SEd Tanous { 14941da66f75SEd Tanous continue; 14951da66f75SEd Tanous } 14964ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 14974ed77cd5SEd Tanous if (lastPos != std::string::npos) 14981da66f75SEd Tanous { 1499e1f26343SJason M. Bills logEntryArray.push_back( 1500d53dd41fSJason M. Bills {{"@odata.id", "/redfish/v1/Systems/system/LogServices/" 1501424c4176SJason M. Bills "Crashdump/Entries/" + 15024ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 15031da66f75SEd Tanous } 15041da66f75SEd Tanous } 1505e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1506e1f26343SJason M. Bills logEntryArray.size(); 15071da66f75SEd Tanous }; 15081da66f75SEd Tanous crow::connections::systemBus->async_method_call( 15091da66f75SEd Tanous std::move(getLogEntriesCallback), 15101da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 15111da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 15121da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 1513424c4176SJason M. Bills std::array<const char *, 1>{CrashdumpInterface}); 15141da66f75SEd Tanous } 15151da66f75SEd Tanous }; 15161da66f75SEd Tanous 1517424c4176SJason M. Bills std::string getLogCreatedTime(const nlohmann::json &Crashdump) 15181da66f75SEd Tanous { 1519424c4176SJason M. Bills nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data"); 1520424c4176SJason M. Bills if (cdIt != Crashdump.end()) 15211da66f75SEd Tanous { 1522c4d00437SJason M. Bills nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO"); 1523c4d00437SJason M. Bills if (siIt != cdIt->end()) 15241da66f75SEd Tanous { 1525c4d00437SJason M. Bills nlohmann::json::const_iterator tsIt = siIt->find("timestamp"); 1526c4d00437SJason M. Bills if (tsIt != siIt->end()) 1527c4d00437SJason M. Bills { 1528c4d00437SJason M. Bills const std::string *logTime = 1529c4d00437SJason M. Bills tsIt->get_ptr<const std::string *>(); 15301da66f75SEd Tanous if (logTime != nullptr) 15311da66f75SEd Tanous { 15321da66f75SEd Tanous return *logTime; 15331da66f75SEd Tanous } 15341da66f75SEd Tanous } 15351da66f75SEd Tanous } 1536c4d00437SJason M. Bills } 15371da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 15381da66f75SEd Tanous 15391da66f75SEd Tanous return std::string(); 15401da66f75SEd Tanous } 15411da66f75SEd Tanous 1542424c4176SJason M. Bills class CrashdumpEntry : public Node 15431da66f75SEd Tanous { 15441da66f75SEd Tanous public: 1545424c4176SJason M. Bills CrashdumpEntry(CrowApp &app) : 1546d53dd41fSJason M. Bills Node(app, 1547424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/", 15481da66f75SEd Tanous std::string()) 15491da66f75SEd Tanous { 15501da66f75SEd Tanous entityPrivileges = { 1551e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1552e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1553e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1554e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1555e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1556e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 15571da66f75SEd Tanous } 15581da66f75SEd Tanous 15591da66f75SEd Tanous private: 15601da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 15611da66f75SEd Tanous const std::vector<std::string> ¶ms) override 15621da66f75SEd Tanous { 1563e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 15641da66f75SEd Tanous if (params.size() != 1) 15651da66f75SEd Tanous { 1566f12894f8SJason M. Bills messages::internalError(asyncResp->res); 15671da66f75SEd Tanous return; 15681da66f75SEd Tanous } 1569b01bf299SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 1570abf2add6SEd Tanous auto getStoredLogCallback = [asyncResp, logId]( 1571abf2add6SEd Tanous const boost::system::error_code ec, 1572abf2add6SEd Tanous const std::variant<std::string> &resp) { 15731da66f75SEd Tanous if (ec) 15741da66f75SEd Tanous { 1575abf2add6SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 1576f12894f8SJason M. Bills messages::internalError(asyncResp->res); 15771da66f75SEd Tanous return; 15781da66f75SEd Tanous } 1579abf2add6SEd Tanous const std::string *log = std::get_if<std::string>(&resp); 15801da66f75SEd Tanous if (log == nullptr) 15811da66f75SEd Tanous { 1582f12894f8SJason M. Bills messages::internalError(asyncResp->res); 15831da66f75SEd Tanous return; 15841da66f75SEd Tanous } 15851da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 15861da66f75SEd Tanous if (j.is_discarded()) 15871da66f75SEd Tanous { 1588f12894f8SJason M. Bills messages::internalError(asyncResp->res); 15891da66f75SEd Tanous return; 15901da66f75SEd Tanous } 15911da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1592e1f26343SJason M. Bills asyncResp->res.jsonValue = { 1593cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1594abf2add6SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 15951da66f75SEd Tanous {"@odata.id", 1596424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 15974ed77cd5SEd Tanous std::to_string(logId)}, 1598424c4176SJason M. Bills {"Name", "CPU Crashdump"}, 15994ed77cd5SEd Tanous {"Id", logId}, 16001da66f75SEd Tanous {"EntryType", "Oem"}, 1601424c4176SJason M. Bills {"OemRecordFormat", "Intel Crashdump"}, 16021da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 16031da66f75SEd Tanous {"Created", std::move(t)}}; 16041da66f75SEd Tanous }; 16051da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1606424c4176SJason M. Bills std::move(getStoredLogCallback), CrashdumpObject, 1607424c4176SJason M. Bills CrashdumpPath + std::string("/") + std::to_string(logId), 1608424c4176SJason M. Bills "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface, 1609424c4176SJason M. Bills "Log"); 16101da66f75SEd Tanous } 16111da66f75SEd Tanous }; 16121da66f75SEd Tanous 1613424c4176SJason M. Bills class OnDemandCrashdump : public Node 16141da66f75SEd Tanous { 16151da66f75SEd Tanous public: 1616424c4176SJason M. Bills OnDemandCrashdump(CrowApp &app) : 1617424c4176SJason M. Bills Node(app, 1618424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1619424c4176SJason M. Bills "Crashdump.OnDemand/") 16201da66f75SEd Tanous { 16211da66f75SEd Tanous entityPrivileges = { 1622e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1623e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1624e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1625e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1626e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1627e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 16281da66f75SEd Tanous } 16291da66f75SEd Tanous 16301da66f75SEd Tanous private: 16311da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 16321da66f75SEd Tanous const std::vector<std::string> ¶ms) override 16331da66f75SEd Tanous { 1634e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 163548e4639eSJason M. Bills static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher; 16361da66f75SEd Tanous 163748e4639eSJason M. Bills // Only allow one OnDemand Log request at a time 163848e4639eSJason M. Bills if (onDemandLogMatcher != nullptr) 16391da66f75SEd Tanous { 1640e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 1641f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 16421da66f75SEd Tanous return; 16431da66f75SEd Tanous } 16441da66f75SEd Tanous // Make this static so it survives outside this method 16451da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 16461da66f75SEd Tanous 16471da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 1648e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 164948e4639eSJason M. Bills onDemandLogMatcher = nullptr; 16501da66f75SEd Tanous if (ec) 16511da66f75SEd Tanous { 16521da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 16531da66f75SEd Tanous // completion. 16541da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 16551da66f75SEd Tanous { 16561da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 16571da66f75SEd Tanous } 16581da66f75SEd Tanous return; 16591da66f75SEd Tanous } 166048e4639eSJason M. Bills BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log"; 16611da66f75SEd Tanous 1662f12894f8SJason M. Bills messages::internalError(asyncResp->res); 16631da66f75SEd Tanous }); 16641da66f75SEd Tanous 166548e4639eSJason M. Bills auto onDemandLogMatcherCallback = [asyncResp]( 16661da66f75SEd Tanous sdbusplus::message::message &m) { 166748e4639eSJason M. Bills BMCWEB_LOG_DEBUG << "OnDemand log available match fired"; 16681da66f75SEd Tanous boost::system::error_code ec; 16691da66f75SEd Tanous timeout.cancel(ec); 16701da66f75SEd Tanous if (ec) 16711da66f75SEd Tanous { 16721da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 16731da66f75SEd Tanous } 16744ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 16751da66f75SEd Tanous boost::container::flat_map< 1676abf2add6SEd Tanous std::string, boost::container::flat_map< 1677abf2add6SEd Tanous std::string, std::variant<std::string>>> 16784ed77cd5SEd Tanous interfacesAdded; 16794ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 1680abf2add6SEd Tanous const std::string *log = std::get_if<std::string>( 1681424c4176SJason M. Bills &interfacesAdded[CrashdumpInterface]["Log"]); 16821da66f75SEd Tanous if (log == nullptr) 16831da66f75SEd Tanous { 1684f12894f8SJason M. Bills messages::internalError(asyncResp->res); 168548e4639eSJason M. Bills // Careful with onDemandLogMatcher. It is a unique_ptr to the 16861da66f75SEd Tanous // match object inside which this lambda is executing. Once it 16871da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 16881da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 16891da66f75SEd Tanous // be the last thing done. 169048e4639eSJason M. Bills onDemandLogMatcher = nullptr; 16911da66f75SEd Tanous return; 16921da66f75SEd Tanous } 16931da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 16941da66f75SEd Tanous if (j.is_discarded()) 16951da66f75SEd Tanous { 1696f12894f8SJason M. Bills messages::internalError(asyncResp->res); 169748e4639eSJason M. Bills // Careful with onDemandLogMatcher. It is a unique_ptr to the 16981da66f75SEd Tanous // match object inside which this lambda is executing. Once it 16991da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 17001da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 17011da66f75SEd Tanous // be the last thing done. 170248e4639eSJason M. Bills onDemandLogMatcher = nullptr; 17031da66f75SEd Tanous return; 17041da66f75SEd Tanous } 17051da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1706e1f26343SJason M. Bills asyncResp->res.jsonValue = { 1707cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 17081da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 1709424c4176SJason M. Bills {"Name", "CPU Crashdump"}, 17101da66f75SEd Tanous {"EntryType", "Oem"}, 1711424c4176SJason M. Bills {"OemRecordFormat", "Intel Crashdump"}, 17121da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 17131da66f75SEd Tanous {"Created", std::move(t)}}; 171448e4639eSJason M. Bills // Careful with onDemandLogMatcher. It is a unique_ptr to the 17151da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 17161da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 17171da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 17181da66f75SEd Tanous // thing done. 171948e4639eSJason M. Bills onDemandLogMatcher = nullptr; 17201da66f75SEd Tanous }; 172148e4639eSJason M. Bills onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 17221da66f75SEd Tanous *crow::connections::systemBus, 17231da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 1724424c4176SJason M. Bills sdbusplus::bus::match::rules::argNpath(0, 1725424c4176SJason M. Bills CrashdumpOnDemandPath), 172648e4639eSJason M. Bills std::move(onDemandLogMatcherCallback)); 17271da66f75SEd Tanous 172848e4639eSJason M. Bills auto generateonDemandLogCallback = 1729e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 17301da66f75SEd Tanous const std::string &resp) { 17311da66f75SEd Tanous if (ec) 17321da66f75SEd Tanous { 17331da66f75SEd Tanous if (ec.value() == 17341da66f75SEd Tanous boost::system::errc::operation_not_supported) 17351da66f75SEd Tanous { 1736f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 17371da66f75SEd Tanous } 17381da66f75SEd Tanous else 17391da66f75SEd Tanous { 1740f12894f8SJason M. Bills messages::internalError(asyncResp->res); 17411da66f75SEd Tanous } 17421da66f75SEd Tanous boost::system::error_code timeoutec; 17431da66f75SEd Tanous timeout.cancel(timeoutec); 17441da66f75SEd Tanous if (timeoutec) 17451da66f75SEd Tanous { 17461da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 17471da66f75SEd Tanous << timeoutec; 17481da66f75SEd Tanous } 174948e4639eSJason M. Bills onDemandLogMatcher = nullptr; 17501da66f75SEd Tanous return; 17511da66f75SEd Tanous } 17521da66f75SEd Tanous }; 17531da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1754424c4176SJason M. Bills std::move(generateonDemandLogCallback), CrashdumpObject, 1755424c4176SJason M. Bills CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog"); 17561da66f75SEd Tanous } 17571da66f75SEd Tanous }; 17581da66f75SEd Tanous 1759e1f26343SJason M. Bills class SendRawPECI : public Node 17601da66f75SEd Tanous { 17611da66f75SEd Tanous public: 1762e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 1763424c4176SJason M. Bills Node(app, 1764424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1765424c4176SJason M. Bills "Crashdump.SendRawPeci/") 17661da66f75SEd Tanous { 17671da66f75SEd Tanous entityPrivileges = { 17681da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 17691da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 17701da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 17711da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 17721da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 17731da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 17741da66f75SEd Tanous } 17751da66f75SEd Tanous 17761da66f75SEd Tanous private: 17771da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 17781da66f75SEd Tanous const std::vector<std::string> ¶ms) override 17791da66f75SEd Tanous { 1780e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1781b1556427SEd Tanous uint8_t clientAddress = 0; 1782b1556427SEd Tanous uint8_t readLength = 0; 17831da66f75SEd Tanous std::vector<uint8_t> peciCommand; 1784b1556427SEd Tanous if (!json_util::readJson(req, res, "ClientAddress", clientAddress, 1785b1556427SEd Tanous "ReadLength", readLength, "PECICommand", 1786b1556427SEd Tanous peciCommand)) 17871da66f75SEd Tanous { 17881da66f75SEd Tanous return; 17891da66f75SEd Tanous } 1790b1556427SEd Tanous 17911da66f75SEd Tanous // Callback to return the Raw PECI response 1792e1f26343SJason M. Bills auto sendRawPECICallback = 1793e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 17941da66f75SEd Tanous const std::vector<uint8_t> &resp) { 17951da66f75SEd Tanous if (ec) 17961da66f75SEd Tanous { 17971da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 17981da66f75SEd Tanous << ec.message(); 1799f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18001da66f75SEd Tanous return; 18011da66f75SEd Tanous } 1802e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 18031da66f75SEd Tanous {"PECIResponse", resp}}; 18041da66f75SEd Tanous }; 18051da66f75SEd Tanous // Call the SendRawPECI command with the provided data 18061da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1807424c4176SJason M. Bills std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath, 1808424c4176SJason M. Bills CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength, 18094ed77cd5SEd Tanous peciCommand); 18101da66f75SEd Tanous } 18111da66f75SEd Tanous }; 18121da66f75SEd Tanous 1813cb92c03bSAndrew Geissler /** 1814cb92c03bSAndrew Geissler * DBusLogServiceActionsClear class supports POST method for ClearLog action. 1815cb92c03bSAndrew Geissler */ 1816cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node 1817cb92c03bSAndrew Geissler { 1818cb92c03bSAndrew Geissler public: 1819cb92c03bSAndrew Geissler DBusLogServiceActionsClear(CrowApp &app) : 1820cb92c03bSAndrew Geissler Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 1821cb92c03bSAndrew Geissler "LogService.Reset") 1822cb92c03bSAndrew Geissler { 1823cb92c03bSAndrew Geissler entityPrivileges = { 1824cb92c03bSAndrew Geissler {boost::beast::http::verb::get, {{"Login"}}}, 1825cb92c03bSAndrew Geissler {boost::beast::http::verb::head, {{"Login"}}}, 1826cb92c03bSAndrew Geissler {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1827cb92c03bSAndrew Geissler {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1828cb92c03bSAndrew Geissler {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1829cb92c03bSAndrew Geissler {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1830cb92c03bSAndrew Geissler } 1831cb92c03bSAndrew Geissler 1832cb92c03bSAndrew Geissler private: 1833cb92c03bSAndrew Geissler /** 1834cb92c03bSAndrew Geissler * Function handles POST method request. 1835cb92c03bSAndrew Geissler * The Clear Log actions does not require any parameter.The action deletes 1836cb92c03bSAndrew Geissler * all entries found in the Entries collection for this Log Service. 1837cb92c03bSAndrew Geissler */ 1838cb92c03bSAndrew Geissler void doPost(crow::Response &res, const crow::Request &req, 1839cb92c03bSAndrew Geissler const std::vector<std::string> ¶ms) override 1840cb92c03bSAndrew Geissler { 1841cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Do delete all entries."; 1842cb92c03bSAndrew Geissler 1843cb92c03bSAndrew Geissler auto asyncResp = std::make_shared<AsyncResp>(res); 1844cb92c03bSAndrew Geissler // Process response from Logging service. 1845cb92c03bSAndrew Geissler auto resp_handler = [asyncResp](const boost::system::error_code ec) { 1846cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done"; 1847cb92c03bSAndrew Geissler if (ec) 1848cb92c03bSAndrew Geissler { 1849cb92c03bSAndrew Geissler // TODO Handle for specific error code 1850cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec; 1851cb92c03bSAndrew Geissler asyncResp->res.result( 1852cb92c03bSAndrew Geissler boost::beast::http::status::internal_server_error); 1853cb92c03bSAndrew Geissler return; 1854cb92c03bSAndrew Geissler } 1855cb92c03bSAndrew Geissler 1856cb92c03bSAndrew Geissler asyncResp->res.result(boost::beast::http::status::no_content); 1857cb92c03bSAndrew Geissler }; 1858cb92c03bSAndrew Geissler 1859cb92c03bSAndrew Geissler // Make call to Logging service to request Clear Log 1860cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 1861cb92c03bSAndrew Geissler resp_handler, "xyz.openbmc_project.Logging", 1862cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging", 1863cb92c03bSAndrew Geissler "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 1864cb92c03bSAndrew Geissler } 1865cb92c03bSAndrew Geissler }; 18661da66f75SEd Tanous } // namespace redfish 1867