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" 2246229577SJames Feist #include "task.hpp" 231da66f75SEd Tanous 24e1f26343SJason M. Bills #include <systemd/sd-journal.h> 25e1f26343SJason M. Bills 264851d45dSJason M. Bills #include <boost/algorithm/string/split.hpp> 274851d45dSJason M. Bills #include <boost/beast/core/span.hpp> 281da66f75SEd Tanous #include <boost/container/flat_map.hpp> 291ddcf01aSJason M. Bills #include <boost/system/linux_error.hpp> 30cb92c03bSAndrew Geissler #include <error_messages.hpp> 314418c7f0SJames Feist #include <filesystem> 32cd225da8SJason M. Bills #include <string_view> 33abf2add6SEd Tanous #include <variant> 341da66f75SEd Tanous 351da66f75SEd Tanous namespace redfish 361da66f75SEd Tanous { 371da66f75SEd Tanous 385b61b5e8SJason M. Bills constexpr char const *crashdumpObject = "com.intel.crashdump"; 395b61b5e8SJason M. Bills constexpr char const *crashdumpPath = "/com/intel/crashdump"; 405b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand"; 415b61b5e8SJason M. Bills constexpr char const *crashdumpInterface = "com.intel.crashdump"; 425b61b5e8SJason M. Bills constexpr char const *deleteAllInterface = 435b61b5e8SJason M. Bills "xyz.openbmc_project.Collection.DeleteAll"; 445b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandInterface = 45424c4176SJason M. Bills "com.intel.crashdump.OnDemand"; 465b61b5e8SJason M. Bills constexpr char const *crashdumpRawPECIInterface = 47424c4176SJason M. Bills "com.intel.crashdump.SendRawPeci"; 481da66f75SEd Tanous 494851d45dSJason M. Bills namespace message_registries 504851d45dSJason M. Bills { 514851d45dSJason M. Bills static const Message *getMessageFromRegistry( 524851d45dSJason M. Bills const std::string &messageKey, 534851d45dSJason M. Bills const boost::beast::span<const MessageEntry> registry) 544851d45dSJason M. Bills { 554851d45dSJason M. Bills boost::beast::span<const MessageEntry>::const_iterator messageIt = 564851d45dSJason M. Bills std::find_if(registry.cbegin(), registry.cend(), 574851d45dSJason M. Bills [&messageKey](const MessageEntry &messageEntry) { 584851d45dSJason M. Bills return !std::strcmp(messageEntry.first, 594851d45dSJason M. Bills messageKey.c_str()); 604851d45dSJason M. Bills }); 614851d45dSJason M. Bills if (messageIt != registry.cend()) 624851d45dSJason M. Bills { 634851d45dSJason M. Bills return &messageIt->second; 644851d45dSJason M. Bills } 654851d45dSJason M. Bills 664851d45dSJason M. Bills return nullptr; 674851d45dSJason M. Bills } 684851d45dSJason M. Bills 694851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID) 704851d45dSJason M. Bills { 714851d45dSJason M. Bills // Redfish MessageIds are in the form 724851d45dSJason M. Bills // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 734851d45dSJason M. Bills // the right Message 744851d45dSJason M. Bills std::vector<std::string> fields; 754851d45dSJason M. Bills fields.reserve(4); 764851d45dSJason M. Bills boost::split(fields, messageID, boost::is_any_of(".")); 774851d45dSJason M. Bills std::string ®istryName = fields[0]; 784851d45dSJason M. Bills std::string &messageKey = fields[3]; 794851d45dSJason M. Bills 804851d45dSJason M. Bills // Find the right registry and check it for the MessageKey 814851d45dSJason M. Bills if (std::string(base::header.registryPrefix) == registryName) 824851d45dSJason M. Bills { 834851d45dSJason M. Bills return getMessageFromRegistry( 844851d45dSJason M. Bills messageKey, boost::beast::span<const MessageEntry>(base::registry)); 854851d45dSJason M. Bills } 864851d45dSJason M. Bills if (std::string(openbmc::header.registryPrefix) == registryName) 874851d45dSJason M. Bills { 884851d45dSJason M. Bills return getMessageFromRegistry( 894851d45dSJason M. Bills messageKey, 904851d45dSJason M. Bills boost::beast::span<const MessageEntry>(openbmc::registry)); 914851d45dSJason M. Bills } 924851d45dSJason M. Bills return nullptr; 934851d45dSJason M. Bills } 944851d45dSJason M. Bills } // namespace message_registries 954851d45dSJason M. Bills 96f6150403SJames Feist namespace fs = std::filesystem; 971da66f75SEd Tanous 98cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map< 99cb92c03bSAndrew Geissler std::string, 100cb92c03bSAndrew Geissler sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 101cb92c03bSAndrew Geissler int32_t, uint32_t, int64_t, uint64_t, double>>; 102cb92c03bSAndrew Geissler 103cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map< 104cb92c03bSAndrew Geissler sdbusplus::message::object_path, 105cb92c03bSAndrew Geissler boost::container::flat_map<std::string, GetManagedPropertyType>>; 106cb92c03bSAndrew Geissler 107cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s) 108cb92c03bSAndrew Geissler { 109cb92c03bSAndrew Geissler if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert") 110cb92c03bSAndrew Geissler { 111cb92c03bSAndrew Geissler return "Critical"; 112cb92c03bSAndrew Geissler } 113cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") 114cb92c03bSAndrew Geissler { 115cb92c03bSAndrew Geissler return "Critical"; 116cb92c03bSAndrew Geissler } 117cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug") 118cb92c03bSAndrew Geissler { 119cb92c03bSAndrew Geissler return "OK"; 120cb92c03bSAndrew Geissler } 121cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") 122cb92c03bSAndrew Geissler { 123cb92c03bSAndrew Geissler return "Critical"; 124cb92c03bSAndrew Geissler } 125cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error") 126cb92c03bSAndrew Geissler { 127cb92c03bSAndrew Geissler return "Critical"; 128cb92c03bSAndrew Geissler } 129cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") 130cb92c03bSAndrew Geissler { 131cb92c03bSAndrew Geissler return "OK"; 132cb92c03bSAndrew Geissler } 133cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice") 134cb92c03bSAndrew Geissler { 135cb92c03bSAndrew Geissler return "OK"; 136cb92c03bSAndrew Geissler } 137cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") 138cb92c03bSAndrew Geissler { 139cb92c03bSAndrew Geissler return "Warning"; 140cb92c03bSAndrew Geissler } 141cb92c03bSAndrew Geissler return ""; 142cb92c03bSAndrew Geissler } 143cb92c03bSAndrew Geissler 14416428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 14539e77504SEd Tanous const std::string_view &field, 14639e77504SEd Tanous std::string_view &contents) 14716428a1aSJason M. Bills { 14816428a1aSJason M. Bills const char *data = nullptr; 14916428a1aSJason M. Bills size_t length = 0; 15016428a1aSJason M. Bills int ret = 0; 15116428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 152271584abSEd Tanous ret = sd_journal_get_data(journal, field.data(), 153271584abSEd Tanous reinterpret_cast<const void **>(&data), &length); 15416428a1aSJason M. Bills if (ret < 0) 15516428a1aSJason M. Bills { 15616428a1aSJason M. Bills return ret; 15716428a1aSJason M. Bills } 15839e77504SEd Tanous contents = std::string_view(data, length); 15916428a1aSJason M. Bills // Only use the content after the "=" character. 16016428a1aSJason M. Bills contents.remove_prefix(std::min(contents.find("=") + 1, contents.size())); 16116428a1aSJason M. Bills return ret; 16216428a1aSJason M. Bills } 16316428a1aSJason M. Bills 16416428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 16539e77504SEd Tanous const std::string_view &field, const int &base, 166271584abSEd Tanous long int &contents) 16716428a1aSJason M. Bills { 16816428a1aSJason M. Bills int ret = 0; 16939e77504SEd Tanous std::string_view metadata; 17016428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 17116428a1aSJason M. Bills ret = getJournalMetadata(journal, field, metadata); 17216428a1aSJason M. Bills if (ret < 0) 17316428a1aSJason M. Bills { 17416428a1aSJason M. Bills return ret; 17516428a1aSJason M. Bills } 176b01bf299SEd Tanous contents = strtol(metadata.data(), nullptr, base); 17716428a1aSJason M. Bills return ret; 17816428a1aSJason M. Bills } 17916428a1aSJason M. Bills 18016428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp) 18116428a1aSJason M. Bills { 18216428a1aSJason M. Bills int ret = 0; 18316428a1aSJason M. Bills uint64_t timestamp = 0; 18416428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 18516428a1aSJason M. Bills if (ret < 0) 18616428a1aSJason M. Bills { 18716428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 18816428a1aSJason M. Bills << strerror(-ret); 18916428a1aSJason M. Bills return false; 19016428a1aSJason M. Bills } 19116428a1aSJason M. Bills time_t t = 19216428a1aSJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 19316428a1aSJason M. Bills struct tm *loctime = localtime(&t); 19416428a1aSJason M. Bills char entryTime[64] = {}; 19599131cd0SEd Tanous if (nullptr != loctime) 19616428a1aSJason M. Bills { 19716428a1aSJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 19816428a1aSJason M. Bills } 19916428a1aSJason M. Bills // Insert the ':' into the timezone 20039e77504SEd Tanous std::string_view t1(entryTime); 20139e77504SEd Tanous std::string_view t2(entryTime); 20216428a1aSJason M. Bills if (t1.size() > 2 && t2.size() > 2) 20316428a1aSJason M. Bills { 20416428a1aSJason M. Bills t1.remove_suffix(2); 20516428a1aSJason M. Bills t2.remove_prefix(t2.size() - 2); 20616428a1aSJason M. Bills } 20739e77504SEd Tanous entryTimestamp = std::string(t1) + ":" + std::string(t2); 20816428a1aSJason M. Bills return true; 20916428a1aSJason M. Bills } 21016428a1aSJason M. Bills 21116428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req, 212271584abSEd Tanous uint64_t &skip) 21316428a1aSJason M. Bills { 21416428a1aSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 21516428a1aSJason M. Bills if (skipParam != nullptr) 21616428a1aSJason M. Bills { 21716428a1aSJason M. Bills char *ptr = nullptr; 218271584abSEd Tanous skip = std::strtoul(skipParam, &ptr, 10); 21916428a1aSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 22016428a1aSJason M. Bills { 22116428a1aSJason M. Bills 22216428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(skipParam), 22316428a1aSJason M. Bills "$skip"); 22416428a1aSJason M. Bills return false; 22516428a1aSJason M. Bills } 22616428a1aSJason M. Bills } 22716428a1aSJason M. Bills return true; 22816428a1aSJason M. Bills } 22916428a1aSJason M. Bills 230271584abSEd Tanous static constexpr const uint64_t maxEntriesPerPage = 1000; 23116428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req, 232271584abSEd Tanous uint64_t &top) 23316428a1aSJason M. Bills { 23416428a1aSJason M. Bills char *topParam = req.urlParams.get("$top"); 23516428a1aSJason M. Bills if (topParam != nullptr) 23616428a1aSJason M. Bills { 23716428a1aSJason M. Bills char *ptr = nullptr; 238271584abSEd Tanous top = std::strtoul(topParam, &ptr, 10); 23916428a1aSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 24016428a1aSJason M. Bills { 24116428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(topParam), 24216428a1aSJason M. Bills "$top"); 24316428a1aSJason M. Bills return false; 24416428a1aSJason M. Bills } 245271584abSEd Tanous if (top < 1U || top > maxEntriesPerPage) 24616428a1aSJason M. Bills { 24716428a1aSJason M. Bills 24816428a1aSJason M. Bills messages::queryParameterOutOfRange( 24916428a1aSJason M. Bills res, std::to_string(top), "$top", 25016428a1aSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 25116428a1aSJason M. Bills return false; 25216428a1aSJason M. Bills } 25316428a1aSJason M. Bills } 25416428a1aSJason M. Bills return true; 25516428a1aSJason M. Bills } 25616428a1aSJason M. Bills 257e85d6b16SJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID, 258e85d6b16SJason M. Bills const bool firstEntry = true) 25916428a1aSJason M. Bills { 26016428a1aSJason M. Bills int ret = 0; 26116428a1aSJason M. Bills static uint64_t prevTs = 0; 26216428a1aSJason M. Bills static int index = 0; 263e85d6b16SJason M. Bills if (firstEntry) 264e85d6b16SJason M. Bills { 265e85d6b16SJason M. Bills prevTs = 0; 266e85d6b16SJason M. Bills } 267e85d6b16SJason M. Bills 26816428a1aSJason M. Bills // Get the entry timestamp 26916428a1aSJason M. Bills uint64_t curTs = 0; 27016428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 27116428a1aSJason M. Bills if (ret < 0) 27216428a1aSJason M. Bills { 27316428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 27416428a1aSJason M. Bills << strerror(-ret); 27516428a1aSJason M. Bills return false; 27616428a1aSJason M. Bills } 27716428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 27816428a1aSJason M. Bills if (curTs == prevTs) 27916428a1aSJason M. Bills { 28016428a1aSJason M. Bills index++; 28116428a1aSJason M. Bills } 28216428a1aSJason M. Bills else 28316428a1aSJason M. Bills { 28416428a1aSJason M. Bills // Otherwise, reset it 28516428a1aSJason M. Bills index = 0; 28616428a1aSJason M. Bills } 28716428a1aSJason M. Bills // Save the timestamp 28816428a1aSJason M. Bills prevTs = curTs; 28916428a1aSJason M. Bills 29016428a1aSJason M. Bills entryID = std::to_string(curTs); 29116428a1aSJason M. Bills if (index > 0) 29216428a1aSJason M. Bills { 29316428a1aSJason M. Bills entryID += "_" + std::to_string(index); 29416428a1aSJason M. Bills } 29516428a1aSJason M. Bills return true; 29616428a1aSJason M. Bills } 29716428a1aSJason M. Bills 298e85d6b16SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID, 299e85d6b16SJason M. Bills const bool firstEntry = true) 30095820184SJason M. Bills { 301271584abSEd Tanous static time_t prevTs = 0; 30295820184SJason M. Bills static int index = 0; 303e85d6b16SJason M. Bills if (firstEntry) 304e85d6b16SJason M. Bills { 305e85d6b16SJason M. Bills prevTs = 0; 306e85d6b16SJason M. Bills } 307e85d6b16SJason M. Bills 30895820184SJason M. Bills // Get the entry timestamp 309271584abSEd Tanous std::time_t curTs = 0; 31095820184SJason M. Bills std::tm timeStruct = {}; 31195820184SJason M. Bills std::istringstream entryStream(logEntry); 31295820184SJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 31395820184SJason M. Bills { 31495820184SJason M. Bills curTs = std::mktime(&timeStruct); 31595820184SJason M. Bills } 31695820184SJason M. Bills // If the timestamp isn't unique, increment the index 31795820184SJason M. Bills if (curTs == prevTs) 31895820184SJason M. Bills { 31995820184SJason M. Bills index++; 32095820184SJason M. Bills } 32195820184SJason M. Bills else 32295820184SJason M. Bills { 32395820184SJason M. Bills // Otherwise, reset it 32495820184SJason M. Bills index = 0; 32595820184SJason M. Bills } 32695820184SJason M. Bills // Save the timestamp 32795820184SJason M. Bills prevTs = curTs; 32895820184SJason M. Bills 32995820184SJason M. Bills entryID = std::to_string(curTs); 33095820184SJason M. Bills if (index > 0) 33195820184SJason M. Bills { 33295820184SJason M. Bills entryID += "_" + std::to_string(index); 33395820184SJason M. Bills } 33495820184SJason M. Bills return true; 33595820184SJason M. Bills } 33695820184SJason M. Bills 33716428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 338271584abSEd Tanous uint64_t ×tamp, uint64_t &index) 33916428a1aSJason M. Bills { 34016428a1aSJason M. Bills if (entryID.empty()) 34116428a1aSJason M. Bills { 34216428a1aSJason M. Bills return false; 34316428a1aSJason M. Bills } 34416428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 34539e77504SEd Tanous std::string_view tsStr(entryID); 34616428a1aSJason M. Bills 34716428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 34816428a1aSJason M. Bills if (underscorePos != tsStr.npos) 34916428a1aSJason M. Bills { 35016428a1aSJason M. Bills // Timestamp has an index 35116428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 35239e77504SEd Tanous std::string_view indexStr(entryID); 35316428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 35416428a1aSJason M. Bills std::size_t pos; 35516428a1aSJason M. Bills try 35616428a1aSJason M. Bills { 35739e77504SEd Tanous index = std::stoul(std::string(indexStr), &pos); 35816428a1aSJason M. Bills } 359271584abSEd Tanous catch (std::invalid_argument &) 36016428a1aSJason M. Bills { 36116428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 36216428a1aSJason M. Bills return false; 36316428a1aSJason M. Bills } 364271584abSEd Tanous catch (std::out_of_range &) 36516428a1aSJason M. Bills { 36616428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 36716428a1aSJason M. Bills return false; 36816428a1aSJason M. Bills } 36916428a1aSJason M. Bills if (pos != indexStr.size()) 37016428a1aSJason M. Bills { 37116428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 37216428a1aSJason M. Bills return false; 37316428a1aSJason M. Bills } 37416428a1aSJason M. Bills } 37516428a1aSJason M. Bills // Timestamp has no index 37616428a1aSJason M. Bills std::size_t pos; 37716428a1aSJason M. Bills try 37816428a1aSJason M. Bills { 37939e77504SEd Tanous timestamp = std::stoull(std::string(tsStr), &pos); 38016428a1aSJason M. Bills } 381271584abSEd Tanous catch (std::invalid_argument &) 38216428a1aSJason M. Bills { 38316428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 38416428a1aSJason M. Bills return false; 38516428a1aSJason M. Bills } 386271584abSEd Tanous catch (std::out_of_range &) 38716428a1aSJason M. Bills { 38816428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 38916428a1aSJason M. Bills return false; 39016428a1aSJason M. Bills } 39116428a1aSJason M. Bills if (pos != tsStr.size()) 39216428a1aSJason M. Bills { 39316428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 39416428a1aSJason M. Bills return false; 39516428a1aSJason M. Bills } 39616428a1aSJason M. Bills return true; 39716428a1aSJason M. Bills } 39816428a1aSJason M. Bills 39995820184SJason M. Bills static bool 40095820184SJason M. Bills getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles) 40195820184SJason M. Bills { 40295820184SJason M. Bills static const std::filesystem::path redfishLogDir = "/var/log"; 40395820184SJason M. Bills static const std::string redfishLogFilename = "redfish"; 40495820184SJason M. Bills 40595820184SJason M. Bills // Loop through the directory looking for redfish log files 40695820184SJason M. Bills for (const std::filesystem::directory_entry &dirEnt : 40795820184SJason M. Bills std::filesystem::directory_iterator(redfishLogDir)) 40895820184SJason M. Bills { 40995820184SJason M. Bills // If we find a redfish log file, save the path 41095820184SJason M. Bills std::string filename = dirEnt.path().filename(); 41195820184SJason M. Bills if (boost::starts_with(filename, redfishLogFilename)) 41295820184SJason M. Bills { 41395820184SJason M. Bills redfishLogFiles.emplace_back(redfishLogDir / filename); 41495820184SJason M. Bills } 41595820184SJason M. Bills } 41695820184SJason M. Bills // As the log files rotate, they are appended with a ".#" that is higher for 41795820184SJason M. Bills // the older logs. Since we don't expect more than 10 log files, we 41895820184SJason M. Bills // can just sort the list to get them in order from newest to oldest 41995820184SJason M. Bills std::sort(redfishLogFiles.begin(), redfishLogFiles.end()); 42095820184SJason M. Bills 42195820184SJason M. Bills return !redfishLogFiles.empty(); 42295820184SJason M. Bills } 42395820184SJason M. Bills 424c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node 4251da66f75SEd Tanous { 4261da66f75SEd Tanous public: 4271da66f75SEd Tanous template <typename CrowApp> 428c4bf6374SJason M. Bills SystemLogServiceCollection(CrowApp &app) : 429029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/") 430c4bf6374SJason M. Bills { 431c4bf6374SJason M. Bills entityPrivileges = { 432c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 433c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 434c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 435c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 436c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 437c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 438c4bf6374SJason M. Bills } 439c4bf6374SJason M. Bills 440c4bf6374SJason M. Bills private: 441c4bf6374SJason M. Bills /** 442c4bf6374SJason M. Bills * Functions triggers appropriate requests on DBus 443c4bf6374SJason M. Bills */ 444c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 445c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 446c4bf6374SJason M. Bills { 447c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 448c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 449c4bf6374SJason M. Bills // it has a duplicate entry for members 450c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 451c4bf6374SJason M. Bills "#LogServiceCollection.LogServiceCollection"; 452c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 453029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices"; 454c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 455c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 456c4bf6374SJason M. Bills "Collection of LogServices for this Computer System"; 457c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 458c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 459029573d4SEd Tanous logServiceArray.push_back( 460029573d4SEd Tanous {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); 461d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 462d53dd41fSJason M. Bills logServiceArray.push_back( 463cb92c03bSAndrew Geissler {{"@odata.id", 464424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"}}); 465d53dd41fSJason M. Bills #endif 466c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 467c4bf6374SJason M. Bills logServiceArray.size(); 468c4bf6374SJason M. Bills } 469c4bf6374SJason M. Bills }; 470c4bf6374SJason M. Bills 471c4bf6374SJason M. Bills class EventLogService : public Node 472c4bf6374SJason M. Bills { 473c4bf6374SJason M. Bills public: 474c4bf6374SJason M. Bills template <typename CrowApp> 475c4bf6374SJason M. Bills EventLogService(CrowApp &app) : 476029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/") 477c4bf6374SJason M. Bills { 478c4bf6374SJason M. Bills entityPrivileges = { 479c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 480c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 481c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 482c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 483c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 484c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 485c4bf6374SJason M. Bills } 486c4bf6374SJason M. Bills 487c4bf6374SJason M. Bills private: 488c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 489c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 490c4bf6374SJason M. Bills { 491c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 492c4bf6374SJason M. Bills 493c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 494029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog"; 495c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 496c4bf6374SJason M. Bills "#LogService.v1_1_0.LogService"; 497c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Event Log Service"; 498c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "System Event Log Service"; 499c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "Event Log"; 500c4bf6374SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 501c4bf6374SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 502c4bf6374SJason M. Bills {"@odata.id", 503029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}}; 504e7d6c8b2SGunnar Mills asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = { 505e7d6c8b2SGunnar Mills 506e7d6c8b2SGunnar Mills {"target", "/redfish/v1/Systems/system/LogServices/EventLog/" 507e7d6c8b2SGunnar Mills "Actions/LogService.ClearLog"}}; 508489640c6SJason M. Bills } 509489640c6SJason M. Bills }; 510489640c6SJason M. Bills 5111f56a3a6STim Lee class JournalEventLogClear : public Node 512489640c6SJason M. Bills { 513489640c6SJason M. Bills public: 5141f56a3a6STim Lee JournalEventLogClear(CrowApp &app) : 515489640c6SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 516489640c6SJason M. Bills "LogService.ClearLog/") 517489640c6SJason M. Bills { 518489640c6SJason M. Bills entityPrivileges = { 519489640c6SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 520489640c6SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 521489640c6SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 522489640c6SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 523489640c6SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 524489640c6SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 525489640c6SJason M. Bills } 526489640c6SJason M. Bills 527489640c6SJason M. Bills private: 528489640c6SJason M. Bills void doPost(crow::Response &res, const crow::Request &req, 529489640c6SJason M. Bills const std::vector<std::string> ¶ms) override 530489640c6SJason M. Bills { 531489640c6SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 532489640c6SJason M. Bills 533489640c6SJason M. Bills // Clear the EventLog by deleting the log files 534489640c6SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 535489640c6SJason M. Bills if (getRedfishLogFiles(redfishLogFiles)) 536489640c6SJason M. Bills { 537489640c6SJason M. Bills for (const std::filesystem::path &file : redfishLogFiles) 538489640c6SJason M. Bills { 539489640c6SJason M. Bills std::error_code ec; 540489640c6SJason M. Bills std::filesystem::remove(file, ec); 541489640c6SJason M. Bills } 542489640c6SJason M. Bills } 543489640c6SJason M. Bills 544489640c6SJason M. Bills // Reload rsyslog so it knows to start new log files 545489640c6SJason M. Bills crow::connections::systemBus->async_method_call( 546489640c6SJason M. Bills [asyncResp](const boost::system::error_code ec) { 547489640c6SJason M. Bills if (ec) 548489640c6SJason M. Bills { 549489640c6SJason M. Bills BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec; 550489640c6SJason M. Bills messages::internalError(asyncResp->res); 551489640c6SJason M. Bills return; 552489640c6SJason M. Bills } 553489640c6SJason M. Bills 554489640c6SJason M. Bills messages::success(asyncResp->res); 555489640c6SJason M. Bills }, 556489640c6SJason M. Bills "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 557489640c6SJason M. Bills "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", 558489640c6SJason M. Bills "replace"); 559c4bf6374SJason M. Bills } 560c4bf6374SJason M. Bills }; 561c4bf6374SJason M. Bills 56295820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID, 56395820184SJason M. Bills const std::string logEntry, 56495820184SJason M. Bills nlohmann::json &logEntryJson) 565c4bf6374SJason M. Bills { 56695820184SJason M. Bills // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 567cd225da8SJason M. Bills // First get the Timestamp 568cd225da8SJason M. Bills size_t space = logEntry.find_first_of(" "); 569cd225da8SJason M. Bills if (space == std::string::npos) 57095820184SJason M. Bills { 57195820184SJason M. Bills return 1; 57295820184SJason M. Bills } 573cd225da8SJason M. Bills std::string timestamp = logEntry.substr(0, space); 574cd225da8SJason M. Bills // Then get the log contents 575cd225da8SJason M. Bills size_t entryStart = logEntry.find_first_not_of(" ", space); 576cd225da8SJason M. Bills if (entryStart == std::string::npos) 577cd225da8SJason M. Bills { 578cd225da8SJason M. Bills return 1; 579cd225da8SJason M. Bills } 580cd225da8SJason M. Bills std::string_view entry(logEntry); 581cd225da8SJason M. Bills entry.remove_prefix(entryStart); 582cd225da8SJason M. Bills // Use split to separate the entry into its fields 583cd225da8SJason M. Bills std::vector<std::string> logEntryFields; 584cd225da8SJason M. Bills boost::split(logEntryFields, entry, boost::is_any_of(","), 585cd225da8SJason M. Bills boost::token_compress_on); 586cd225da8SJason M. Bills // We need at least a MessageId to be valid 587cd225da8SJason M. Bills if (logEntryFields.size() < 1) 588cd225da8SJason M. Bills { 589cd225da8SJason M. Bills return 1; 590cd225da8SJason M. Bills } 591cd225da8SJason M. Bills std::string &messageID = logEntryFields[0]; 59295820184SJason M. Bills 5934851d45dSJason M. Bills // Get the Message from the MessageRegistry 5944851d45dSJason M. Bills const message_registries::Message *message = 5954851d45dSJason M. Bills message_registries::getMessage(messageID); 596c4bf6374SJason M. Bills 5974851d45dSJason M. Bills std::string msg; 5984851d45dSJason M. Bills std::string severity; 5994851d45dSJason M. Bills if (message != nullptr) 600c4bf6374SJason M. Bills { 6014851d45dSJason M. Bills msg = message->message; 6024851d45dSJason M. Bills severity = message->severity; 603c4bf6374SJason M. Bills } 604c4bf6374SJason M. Bills 60515a86ff6SJason M. Bills // Get the MessageArgs from the log if there are any 60615a86ff6SJason M. Bills boost::beast::span<std::string> messageArgs; 60715a86ff6SJason M. Bills if (logEntryFields.size() > 1) 60815a86ff6SJason M. Bills { 60915a86ff6SJason M. Bills std::string &messageArgsStart = logEntryFields[1]; 61015a86ff6SJason M. Bills // If the first string is empty, assume there are no MessageArgs 61115a86ff6SJason M. Bills std::size_t messageArgsSize = 0; 61215a86ff6SJason M. Bills if (!messageArgsStart.empty()) 61315a86ff6SJason M. Bills { 61415a86ff6SJason M. Bills messageArgsSize = logEntryFields.size() - 1; 61515a86ff6SJason M. Bills } 61615a86ff6SJason M. Bills 61715a86ff6SJason M. Bills messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize); 618c4bf6374SJason M. Bills 6194851d45dSJason M. Bills // Fill the MessageArgs into the Message 62095820184SJason M. Bills int i = 0; 62195820184SJason M. Bills for (const std::string &messageArg : messageArgs) 6224851d45dSJason M. Bills { 62395820184SJason M. Bills std::string argStr = "%" + std::to_string(++i); 6244851d45dSJason M. Bills size_t argPos = msg.find(argStr); 6254851d45dSJason M. Bills if (argPos != std::string::npos) 6264851d45dSJason M. Bills { 62795820184SJason M. Bills msg.replace(argPos, argStr.length(), messageArg); 6284851d45dSJason M. Bills } 6294851d45dSJason M. Bills } 63015a86ff6SJason M. Bills } 6314851d45dSJason M. Bills 63295820184SJason M. Bills // Get the Created time from the timestamp. The log timestamp is in RFC3339 63395820184SJason M. Bills // format which matches the Redfish format except for the fractional seconds 63495820184SJason M. Bills // between the '.' and the '+', so just remove them. 63595820184SJason M. Bills std::size_t dot = timestamp.find_first_of("."); 63695820184SJason M. Bills std::size_t plus = timestamp.find_first_of("+"); 63795820184SJason M. Bills if (dot != std::string::npos && plus != std::string::npos) 638c4bf6374SJason M. Bills { 63995820184SJason M. Bills timestamp.erase(dot, plus - dot); 640c4bf6374SJason M. Bills } 641c4bf6374SJason M. Bills 642c4bf6374SJason M. Bills // Fill in the log entry with the gathered data 64395820184SJason M. Bills logEntryJson = { 644cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 645029573d4SEd Tanous {"@odata.id", 646897967deSJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + 64795820184SJason M. Bills logEntryID}, 648c4bf6374SJason M. Bills {"Name", "System Event Log Entry"}, 64995820184SJason M. Bills {"Id", logEntryID}, 65095820184SJason M. Bills {"Message", std::move(msg)}, 65195820184SJason M. Bills {"MessageId", std::move(messageID)}, 652c4bf6374SJason M. Bills {"MessageArgs", std::move(messageArgs)}, 653c4bf6374SJason M. Bills {"EntryType", "Event"}, 65495820184SJason M. Bills {"Severity", std::move(severity)}, 65595820184SJason M. Bills {"Created", std::move(timestamp)}}; 656c4bf6374SJason M. Bills return 0; 657c4bf6374SJason M. Bills } 658c4bf6374SJason M. Bills 65927062605SAnthony Wilson class JournalEventLogEntryCollection : public Node 660c4bf6374SJason M. Bills { 661c4bf6374SJason M. Bills public: 662c4bf6374SJason M. Bills template <typename CrowApp> 66327062605SAnthony Wilson JournalEventLogEntryCollection(CrowApp &app) : 664029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 665c4bf6374SJason M. Bills { 666c4bf6374SJason M. Bills entityPrivileges = { 667c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 668c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 669c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 670c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 671c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 672c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 673c4bf6374SJason M. Bills } 674c4bf6374SJason M. Bills 675c4bf6374SJason M. Bills private: 676c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 677c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 678c4bf6374SJason M. Bills { 679c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 680271584abSEd Tanous uint64_t skip = 0; 681271584abSEd Tanous uint64_t top = maxEntriesPerPage; // Show max entries by default 682c4bf6374SJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 683c4bf6374SJason M. Bills { 684c4bf6374SJason M. Bills return; 685c4bf6374SJason M. Bills } 686c4bf6374SJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 687c4bf6374SJason M. Bills { 688c4bf6374SJason M. Bills return; 689c4bf6374SJason M. Bills } 690c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 691c4bf6374SJason M. Bills // it has a duplicate entry for members 692c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 693c4bf6374SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 694c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 695029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 696c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 697c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 698c4bf6374SJason M. Bills "Collection of System Event Log Entries"; 699cb92c03bSAndrew Geissler 700c4bf6374SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 701c4bf6374SJason M. Bills logEntryArray = nlohmann::json::array(); 70295820184SJason M. Bills // Go through the log files and create a unique ID for each entry 70395820184SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 70495820184SJason M. Bills getRedfishLogFiles(redfishLogFiles); 705b01bf299SEd Tanous uint64_t entryCount = 0; 706cd225da8SJason M. Bills std::string logEntry; 70795820184SJason M. Bills 70895820184SJason M. Bills // Oldest logs are in the last file, so start there and loop backwards 709cd225da8SJason M. Bills for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); 710cd225da8SJason M. Bills it++) 711c4bf6374SJason M. Bills { 712cd225da8SJason M. Bills std::ifstream logStream(*it); 71395820184SJason M. Bills if (!logStream.is_open()) 714c4bf6374SJason M. Bills { 715c4bf6374SJason M. Bills continue; 716c4bf6374SJason M. Bills } 717c4bf6374SJason M. Bills 718e85d6b16SJason M. Bills // Reset the unique ID on the first entry 719e85d6b16SJason M. Bills bool firstEntry = true; 72095820184SJason M. Bills while (std::getline(logStream, logEntry)) 72195820184SJason M. Bills { 722c4bf6374SJason M. Bills entryCount++; 723c4bf6374SJason M. Bills // Handle paging using skip (number of entries to skip from the 724c4bf6374SJason M. Bills // start) and top (number of entries to display) 725c4bf6374SJason M. Bills if (entryCount <= skip || entryCount > skip + top) 726c4bf6374SJason M. Bills { 727c4bf6374SJason M. Bills continue; 728c4bf6374SJason M. Bills } 729c4bf6374SJason M. Bills 730c4bf6374SJason M. Bills std::string idStr; 731e85d6b16SJason M. Bills if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 732c4bf6374SJason M. Bills { 733c4bf6374SJason M. Bills continue; 734c4bf6374SJason M. Bills } 735c4bf6374SJason M. Bills 736e85d6b16SJason M. Bills if (firstEntry) 737e85d6b16SJason M. Bills { 738e85d6b16SJason M. Bills firstEntry = false; 739e85d6b16SJason M. Bills } 740e85d6b16SJason M. Bills 741c4bf6374SJason M. Bills logEntryArray.push_back({}); 742c4bf6374SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 74395820184SJason M. Bills if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0) 744c4bf6374SJason M. Bills { 745c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 746c4bf6374SJason M. Bills return; 747c4bf6374SJason M. Bills } 748c4bf6374SJason M. Bills } 74995820184SJason M. Bills } 750c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 751c4bf6374SJason M. Bills if (skip + top < entryCount) 752c4bf6374SJason M. Bills { 753c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 75495820184SJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/" 75595820184SJason M. Bills "Entries?$skip=" + 756c4bf6374SJason M. Bills std::to_string(skip + top); 757c4bf6374SJason M. Bills } 75808a4e4b5SAnthony Wilson } 75908a4e4b5SAnthony Wilson }; 76008a4e4b5SAnthony Wilson 761897967deSJason M. Bills class JournalEventLogEntry : public Node 762897967deSJason M. Bills { 763897967deSJason M. Bills public: 764897967deSJason M. Bills JournalEventLogEntry(CrowApp &app) : 765897967deSJason M. Bills Node(app, 766897967deSJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 767897967deSJason M. Bills std::string()) 768897967deSJason M. Bills { 769897967deSJason M. Bills entityPrivileges = { 770897967deSJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 771897967deSJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 772897967deSJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 773897967deSJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 774897967deSJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 775897967deSJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 776897967deSJason M. Bills } 777897967deSJason M. Bills 778897967deSJason M. Bills private: 779897967deSJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 780897967deSJason M. Bills const std::vector<std::string> ¶ms) override 781897967deSJason M. Bills { 782897967deSJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 783897967deSJason M. Bills if (params.size() != 1) 784897967deSJason M. Bills { 785897967deSJason M. Bills messages::internalError(asyncResp->res); 786897967deSJason M. Bills return; 787897967deSJason M. Bills } 788897967deSJason M. Bills const std::string &targetID = params[0]; 789897967deSJason M. Bills 790897967deSJason M. Bills // Go through the log files and check the unique ID for each entry to 791897967deSJason M. Bills // find the target entry 792897967deSJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 793897967deSJason M. Bills getRedfishLogFiles(redfishLogFiles); 794897967deSJason M. Bills std::string logEntry; 795897967deSJason M. Bills 796897967deSJason M. Bills // Oldest logs are in the last file, so start there and loop backwards 797897967deSJason M. Bills for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); 798897967deSJason M. Bills it++) 799897967deSJason M. Bills { 800897967deSJason M. Bills std::ifstream logStream(*it); 801897967deSJason M. Bills if (!logStream.is_open()) 802897967deSJason M. Bills { 803897967deSJason M. Bills continue; 804897967deSJason M. Bills } 805897967deSJason M. Bills 806897967deSJason M. Bills // Reset the unique ID on the first entry 807897967deSJason M. Bills bool firstEntry = true; 808897967deSJason M. Bills while (std::getline(logStream, logEntry)) 809897967deSJason M. Bills { 810897967deSJason M. Bills std::string idStr; 811897967deSJason M. Bills if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 812897967deSJason M. Bills { 813897967deSJason M. Bills continue; 814897967deSJason M. Bills } 815897967deSJason M. Bills 816897967deSJason M. Bills if (firstEntry) 817897967deSJason M. Bills { 818897967deSJason M. Bills firstEntry = false; 819897967deSJason M. Bills } 820897967deSJason M. Bills 821897967deSJason M. Bills if (idStr == targetID) 822897967deSJason M. Bills { 823897967deSJason M. Bills if (fillEventLogEntryJson(idStr, logEntry, 824897967deSJason M. Bills asyncResp->res.jsonValue) != 0) 825897967deSJason M. Bills { 826897967deSJason M. Bills messages::internalError(asyncResp->res); 827897967deSJason M. Bills return; 828897967deSJason M. Bills } 829897967deSJason M. Bills return; 830897967deSJason M. Bills } 831897967deSJason M. Bills } 832897967deSJason M. Bills } 833897967deSJason M. Bills // Requested ID was not found 834897967deSJason M. Bills messages::resourceMissingAtURI(asyncResp->res, targetID); 835897967deSJason M. Bills } 836897967deSJason M. Bills }; 837897967deSJason M. Bills 83808a4e4b5SAnthony Wilson class DBusEventLogEntryCollection : public Node 83908a4e4b5SAnthony Wilson { 84008a4e4b5SAnthony Wilson public: 84108a4e4b5SAnthony Wilson template <typename CrowApp> 84208a4e4b5SAnthony Wilson DBusEventLogEntryCollection(CrowApp &app) : 84308a4e4b5SAnthony Wilson Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 84408a4e4b5SAnthony Wilson { 84508a4e4b5SAnthony Wilson entityPrivileges = { 84608a4e4b5SAnthony Wilson {boost::beast::http::verb::get, {{"Login"}}}, 84708a4e4b5SAnthony Wilson {boost::beast::http::verb::head, {{"Login"}}}, 84808a4e4b5SAnthony Wilson {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 84908a4e4b5SAnthony Wilson {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 85008a4e4b5SAnthony Wilson {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 85108a4e4b5SAnthony Wilson {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 85208a4e4b5SAnthony Wilson } 85308a4e4b5SAnthony Wilson 85408a4e4b5SAnthony Wilson private: 85508a4e4b5SAnthony Wilson void doGet(crow::Response &res, const crow::Request &req, 85608a4e4b5SAnthony Wilson const std::vector<std::string> ¶ms) override 85708a4e4b5SAnthony Wilson { 85808a4e4b5SAnthony Wilson std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 85908a4e4b5SAnthony Wilson 86008a4e4b5SAnthony Wilson // Collections don't include the static data added by SubRoute because 86108a4e4b5SAnthony Wilson // it has a duplicate entry for members 86208a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.type"] = 86308a4e4b5SAnthony Wilson "#LogEntryCollection.LogEntryCollection"; 86408a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.id"] = 86508a4e4b5SAnthony Wilson "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 86608a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 86708a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Description"] = 86808a4e4b5SAnthony Wilson "Collection of System Event Log Entries"; 86908a4e4b5SAnthony Wilson 870cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 871cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 872cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 873cb92c03bSAndrew Geissler [asyncResp](const boost::system::error_code ec, 874cb92c03bSAndrew Geissler GetManagedObjectsType &resp) { 875cb92c03bSAndrew Geissler if (ec) 876cb92c03bSAndrew Geissler { 877cb92c03bSAndrew Geissler // TODO Handle for specific error code 878cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 879cb92c03bSAndrew Geissler << "getLogEntriesIfaceData resp_handler got error " 880cb92c03bSAndrew Geissler << ec; 881cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 882cb92c03bSAndrew Geissler return; 883cb92c03bSAndrew Geissler } 884cb92c03bSAndrew Geissler nlohmann::json &entriesArray = 885cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members"]; 886cb92c03bSAndrew Geissler entriesArray = nlohmann::json::array(); 887cb92c03bSAndrew Geissler for (auto &objectPath : resp) 888cb92c03bSAndrew Geissler { 889cb92c03bSAndrew Geissler for (auto &interfaceMap : objectPath.second) 890cb92c03bSAndrew Geissler { 891cb92c03bSAndrew Geissler if (interfaceMap.first != 892cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry") 893cb92c03bSAndrew Geissler { 894cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Bailing early on " 895cb92c03bSAndrew Geissler << interfaceMap.first; 896cb92c03bSAndrew Geissler continue; 897cb92c03bSAndrew Geissler } 898cb92c03bSAndrew Geissler entriesArray.push_back({}); 899cb92c03bSAndrew Geissler nlohmann::json &thisEntry = entriesArray.back(); 90066664f25SEd Tanous uint32_t *id = nullptr; 90166664f25SEd Tanous std::time_t timestamp{}; 90266664f25SEd Tanous std::string *severity = nullptr; 90366664f25SEd Tanous std::string *message = nullptr; 904cb92c03bSAndrew Geissler for (auto &propertyMap : interfaceMap.second) 905cb92c03bSAndrew Geissler { 906cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 907cb92c03bSAndrew Geissler { 908cb92c03bSAndrew Geissler id = sdbusplus::message::variant_ns::get_if< 909cb92c03bSAndrew Geissler uint32_t>(&propertyMap.second); 910cb92c03bSAndrew Geissler if (id == nullptr) 911cb92c03bSAndrew Geissler { 912cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 913cb92c03bSAndrew Geissler "Id"); 914cb92c03bSAndrew Geissler } 915cb92c03bSAndrew Geissler } 916cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 917cb92c03bSAndrew Geissler { 918cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 919cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 920cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 921cb92c03bSAndrew Geissler { 922cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 923cb92c03bSAndrew Geissler "Timestamp"); 924271584abSEd Tanous continue; 925cb92c03bSAndrew Geissler } 926cb92c03bSAndrew Geissler // Retrieve Created property with format: 927cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 928cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 929cb92c03bSAndrew Geissler *millisTimeStamp); 930271584abSEd Tanous timestamp = std::chrono::duration_cast< 931271584abSEd Tanous std::chrono::duration<int>>( 932271584abSEd Tanous chronoTimeStamp) 933cb92c03bSAndrew Geissler .count(); 934cb92c03bSAndrew Geissler } 935cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 936cb92c03bSAndrew Geissler { 937cb92c03bSAndrew Geissler severity = std::get_if<std::string>( 938cb92c03bSAndrew Geissler &propertyMap.second); 939cb92c03bSAndrew Geissler if (severity == nullptr) 940cb92c03bSAndrew Geissler { 941cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 942cb92c03bSAndrew Geissler "Severity"); 943cb92c03bSAndrew Geissler } 944cb92c03bSAndrew Geissler } 945cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 946cb92c03bSAndrew Geissler { 947cb92c03bSAndrew Geissler message = std::get_if<std::string>( 948cb92c03bSAndrew Geissler &propertyMap.second); 949cb92c03bSAndrew Geissler if (message == nullptr) 950cb92c03bSAndrew Geissler { 951cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 952cb92c03bSAndrew Geissler "Message"); 953cb92c03bSAndrew Geissler } 954cb92c03bSAndrew Geissler } 955cb92c03bSAndrew Geissler } 956cb92c03bSAndrew Geissler thisEntry = { 957cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 958cb92c03bSAndrew Geissler {"@odata.id", 959cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 960cb92c03bSAndrew Geissler "Entries/" + 961cb92c03bSAndrew Geissler std::to_string(*id)}, 96227062605SAnthony Wilson {"Name", "System Event Log Entry"}, 963cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 964cb92c03bSAndrew Geissler {"Message", *message}, 965cb92c03bSAndrew Geissler {"EntryType", "Event"}, 966cb92c03bSAndrew Geissler {"Severity", 967cb92c03bSAndrew Geissler translateSeverityDbusToRedfish(*severity)}, 968cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 969cb92c03bSAndrew Geissler } 970cb92c03bSAndrew Geissler } 971cb92c03bSAndrew Geissler std::sort(entriesArray.begin(), entriesArray.end(), 972cb92c03bSAndrew Geissler [](const nlohmann::json &left, 973cb92c03bSAndrew Geissler const nlohmann::json &right) { 974cb92c03bSAndrew Geissler return (left["Id"] <= right["Id"]); 975cb92c03bSAndrew Geissler }); 976cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members@odata.count"] = 977cb92c03bSAndrew Geissler entriesArray.size(); 978cb92c03bSAndrew Geissler }, 979cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 980cb92c03bSAndrew Geissler "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 981c4bf6374SJason M. Bills } 982c4bf6374SJason M. Bills }; 983c4bf6374SJason M. Bills 98408a4e4b5SAnthony Wilson class DBusEventLogEntry : public Node 985c4bf6374SJason M. Bills { 986c4bf6374SJason M. Bills public: 98708a4e4b5SAnthony Wilson DBusEventLogEntry(CrowApp &app) : 988c4bf6374SJason M. Bills Node(app, 989029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 990029573d4SEd Tanous std::string()) 991c4bf6374SJason M. Bills { 992c4bf6374SJason M. Bills entityPrivileges = { 993c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 994c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 995c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 996c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 997c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 998c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 999c4bf6374SJason M. Bills } 1000c4bf6374SJason M. Bills 1001c4bf6374SJason M. Bills private: 1002c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1003c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 1004c4bf6374SJason M. Bills { 1005c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1006029573d4SEd Tanous if (params.size() != 1) 1007c4bf6374SJason M. Bills { 1008c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 1009c4bf6374SJason M. Bills return; 1010c4bf6374SJason M. Bills } 1011029573d4SEd Tanous const std::string &entryID = params[0]; 1012cb92c03bSAndrew Geissler 1013cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 1014cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 1015cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 1016cb92c03bSAndrew Geissler [asyncResp, entryID](const boost::system::error_code ec, 1017cb92c03bSAndrew Geissler GetManagedPropertyType &resp) { 1018cb92c03bSAndrew Geissler if (ec) 1019cb92c03bSAndrew Geissler { 1020cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 1021cb92c03bSAndrew Geissler << "EventLogEntry (DBus) resp_handler got error " << ec; 1022cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 1023cb92c03bSAndrew Geissler return; 1024cb92c03bSAndrew Geissler } 102566664f25SEd Tanous uint32_t *id = nullptr; 102666664f25SEd Tanous std::time_t timestamp{}; 102766664f25SEd Tanous std::string *severity = nullptr; 102866664f25SEd Tanous std::string *message = nullptr; 1029cb92c03bSAndrew Geissler for (auto &propertyMap : resp) 1030cb92c03bSAndrew Geissler { 1031cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 1032cb92c03bSAndrew Geissler { 1033cb92c03bSAndrew Geissler id = std::get_if<uint32_t>(&propertyMap.second); 1034cb92c03bSAndrew Geissler if (id == nullptr) 1035cb92c03bSAndrew Geissler { 1036cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, "Id"); 1037cb92c03bSAndrew Geissler } 1038cb92c03bSAndrew Geissler } 1039cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 1040cb92c03bSAndrew Geissler { 1041cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 1042cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 1043cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 1044cb92c03bSAndrew Geissler { 1045cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1046cb92c03bSAndrew Geissler "Timestamp"); 1047271584abSEd Tanous continue; 1048cb92c03bSAndrew Geissler } 1049cb92c03bSAndrew Geissler // Retrieve Created property with format: 1050cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 1051cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 1052cb92c03bSAndrew Geissler *millisTimeStamp); 1053cb92c03bSAndrew Geissler timestamp = 1054271584abSEd Tanous std::chrono::duration_cast< 1055271584abSEd Tanous std::chrono::duration<int>>(chronoTimeStamp) 1056cb92c03bSAndrew Geissler .count(); 1057cb92c03bSAndrew Geissler } 1058cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 1059cb92c03bSAndrew Geissler { 1060cb92c03bSAndrew Geissler severity = 1061cb92c03bSAndrew Geissler std::get_if<std::string>(&propertyMap.second); 1062cb92c03bSAndrew Geissler if (severity == nullptr) 1063cb92c03bSAndrew Geissler { 1064cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1065cb92c03bSAndrew Geissler "Severity"); 1066cb92c03bSAndrew Geissler } 1067cb92c03bSAndrew Geissler } 1068cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 1069cb92c03bSAndrew Geissler { 1070cb92c03bSAndrew Geissler message = std::get_if<std::string>(&propertyMap.second); 1071cb92c03bSAndrew Geissler if (message == nullptr) 1072cb92c03bSAndrew Geissler { 1073cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1074cb92c03bSAndrew Geissler "Message"); 1075cb92c03bSAndrew Geissler } 1076cb92c03bSAndrew Geissler } 1077cb92c03bSAndrew Geissler } 1078271584abSEd Tanous if (id == nullptr || message == nullptr || severity == nullptr) 1079271584abSEd Tanous { 1080271584abSEd Tanous return; 1081271584abSEd Tanous } 1082cb92c03bSAndrew Geissler asyncResp->res.jsonValue = { 1083cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1084cb92c03bSAndrew Geissler {"@odata.id", 1085cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 1086cb92c03bSAndrew Geissler "Entries/" + 1087cb92c03bSAndrew Geissler std::to_string(*id)}, 108827062605SAnthony Wilson {"Name", "System Event Log Entry"}, 1089cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 1090cb92c03bSAndrew Geissler {"Message", *message}, 1091cb92c03bSAndrew Geissler {"EntryType", "Event"}, 1092cb92c03bSAndrew Geissler {"Severity", translateSeverityDbusToRedfish(*severity)}, 109308a4e4b5SAnthony Wilson {"Created", crow::utility::getDateTime(timestamp)}}; 1094cb92c03bSAndrew Geissler }, 1095cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", 1096cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging/entry/" + entryID, 1097cb92c03bSAndrew Geissler "org.freedesktop.DBus.Properties", "GetAll", 1098cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry"); 1099c4bf6374SJason M. Bills } 1100336e96c6SChicago Duan 1101336e96c6SChicago Duan void doDelete(crow::Response &res, const crow::Request &req, 1102336e96c6SChicago Duan const std::vector<std::string> ¶ms) override 1103336e96c6SChicago Duan { 1104336e96c6SChicago Duan 1105336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "Do delete single event entries."; 1106336e96c6SChicago Duan 1107336e96c6SChicago Duan auto asyncResp = std::make_shared<AsyncResp>(res); 1108336e96c6SChicago Duan 1109336e96c6SChicago Duan if (params.size() != 1) 1110336e96c6SChicago Duan { 1111336e96c6SChicago Duan messages::internalError(asyncResp->res); 1112336e96c6SChicago Duan return; 1113336e96c6SChicago Duan } 1114336e96c6SChicago Duan std::string entryID = params[0]; 1115336e96c6SChicago Duan 1116336e96c6SChicago Duan dbus::utility::escapePathForDbus(entryID); 1117336e96c6SChicago Duan 1118336e96c6SChicago Duan // Process response from Logging service. 1119336e96c6SChicago Duan auto respHandler = [asyncResp](const boost::system::error_code ec) { 1120336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done"; 1121336e96c6SChicago Duan if (ec) 1122336e96c6SChicago Duan { 1123336e96c6SChicago Duan // TODO Handle for specific error code 1124336e96c6SChicago Duan BMCWEB_LOG_ERROR 1125336e96c6SChicago Duan << "EventLogEntry (DBus) doDelete respHandler got error " 1126336e96c6SChicago Duan << ec; 1127336e96c6SChicago Duan asyncResp->res.result( 1128336e96c6SChicago Duan boost::beast::http::status::internal_server_error); 1129336e96c6SChicago Duan return; 1130336e96c6SChicago Duan } 1131336e96c6SChicago Duan 1132336e96c6SChicago Duan asyncResp->res.result(boost::beast::http::status::ok); 1133336e96c6SChicago Duan }; 1134336e96c6SChicago Duan 1135336e96c6SChicago Duan // Make call to Logging service to request Delete Log 1136336e96c6SChicago Duan crow::connections::systemBus->async_method_call( 1137336e96c6SChicago Duan respHandler, "xyz.openbmc_project.Logging", 1138336e96c6SChicago Duan "/xyz/openbmc_project/logging/entry/" + entryID, 1139336e96c6SChicago Duan "xyz.openbmc_project.Object.Delete", "Delete"); 1140336e96c6SChicago Duan } 1141c4bf6374SJason M. Bills }; 1142c4bf6374SJason M. Bills 1143c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node 1144c4bf6374SJason M. Bills { 1145c4bf6374SJason M. Bills public: 1146c4bf6374SJason M. Bills template <typename CrowApp> 1147c4bf6374SJason M. Bills BMCLogServiceCollection(CrowApp &app) : 11484ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 11491da66f75SEd Tanous { 11501da66f75SEd Tanous entityPrivileges = { 1151e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1152e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1153e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1154e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1155e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1156e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 11571da66f75SEd Tanous } 11581da66f75SEd Tanous 11591da66f75SEd Tanous private: 11601da66f75SEd Tanous /** 11611da66f75SEd Tanous * Functions triggers appropriate requests on DBus 11621da66f75SEd Tanous */ 11631da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 11641da66f75SEd Tanous const std::vector<std::string> ¶ms) override 11651da66f75SEd Tanous { 1166e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 11671da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 11681da66f75SEd Tanous // it has a duplicate entry for members 1169e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 11701da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 1171e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1172e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 1173e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 1174e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 11751da66f75SEd Tanous "Collection of LogServices for this Manager"; 1176c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 1177c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 1178c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL 1179c4bf6374SJason M. Bills logServiceArray.push_back( 118008a4e4b5SAnthony Wilson {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}}); 1181c4bf6374SJason M. Bills #endif 1182e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1183c4bf6374SJason M. Bills logServiceArray.size(); 11841da66f75SEd Tanous } 11851da66f75SEd Tanous }; 11861da66f75SEd Tanous 1187c4bf6374SJason M. Bills class BMCJournalLogService : public Node 11881da66f75SEd Tanous { 11891da66f75SEd Tanous public: 11901da66f75SEd Tanous template <typename CrowApp> 1191c4bf6374SJason M. Bills BMCJournalLogService(CrowApp &app) : 1192c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") 1193e1f26343SJason M. Bills { 1194e1f26343SJason M. Bills entityPrivileges = { 1195e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1196e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1197e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1198e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1199e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1200e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1201e1f26343SJason M. Bills } 1202e1f26343SJason M. Bills 1203e1f26343SJason M. Bills private: 1204e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1205e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1206e1f26343SJason M. Bills { 1207e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1208e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1209e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 12100f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 12110f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal"; 1212c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 1213c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 1214c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Journal"; 1215e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1216cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1217cd50aa42SJason M. Bills {"@odata.id", 1218086be238SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}}; 1219e1f26343SJason M. Bills } 1220e1f26343SJason M. Bills }; 1221e1f26343SJason M. Bills 1222c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, 1223e1f26343SJason M. Bills sd_journal *journal, 1224c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntryJson) 1225e1f26343SJason M. Bills { 1226e1f26343SJason M. Bills // Get the Log Entry contents 1227e1f26343SJason M. Bills int ret = 0; 1228e1f26343SJason M. Bills 122939e77504SEd Tanous std::string_view msg; 123016428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 1231e1f26343SJason M. Bills if (ret < 0) 1232e1f26343SJason M. Bills { 1233e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 1234e1f26343SJason M. Bills return 1; 1235e1f26343SJason M. Bills } 1236e1f26343SJason M. Bills 1237e1f26343SJason M. Bills // Get the severity from the PRIORITY field 1238271584abSEd Tanous long int severity = 8; // Default to an invalid priority 123916428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 1240e1f26343SJason M. Bills if (ret < 0) 1241e1f26343SJason M. Bills { 1242e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 1243e1f26343SJason M. Bills } 1244e1f26343SJason M. Bills 1245e1f26343SJason M. Bills // Get the Created time from the timestamp 124616428a1aSJason M. Bills std::string entryTimeStr; 124716428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 1248e1f26343SJason M. Bills { 124916428a1aSJason M. Bills return 1; 1250e1f26343SJason M. Bills } 1251e1f26343SJason M. Bills 1252e1f26343SJason M. Bills // Fill in the log entry with the gathered data 1253c4bf6374SJason M. Bills bmcJournalLogEntryJson = { 1254cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1255c4bf6374SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + 1256c4bf6374SJason M. Bills bmcJournalLogEntryID}, 1257e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 1258c4bf6374SJason M. Bills {"Id", bmcJournalLogEntryID}, 125916428a1aSJason M. Bills {"Message", msg}, 1260e1f26343SJason M. Bills {"EntryType", "Oem"}, 1261e1f26343SJason M. Bills {"Severity", 1262b6a61a5eSJason M. Bills severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"}, 1263086be238SEd Tanous {"OemRecordFormat", "BMC Journal Entry"}, 1264e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 1265e1f26343SJason M. Bills return 0; 1266e1f26343SJason M. Bills } 1267e1f26343SJason M. Bills 1268c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node 1269e1f26343SJason M. Bills { 1270e1f26343SJason M. Bills public: 1271e1f26343SJason M. Bills template <typename CrowApp> 1272c4bf6374SJason M. Bills BMCJournalLogEntryCollection(CrowApp &app) : 1273c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/") 1274e1f26343SJason M. Bills { 1275e1f26343SJason M. Bills entityPrivileges = { 1276e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1277e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1278e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1279e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1280e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1281e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1282e1f26343SJason M. Bills } 1283e1f26343SJason M. Bills 1284e1f26343SJason M. Bills private: 1285e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1286e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1287e1f26343SJason M. Bills { 1288e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1289193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 1290271584abSEd Tanous uint64_t skip = 0; 1291271584abSEd Tanous uint64_t top = maxEntriesPerPage; // Show max entries by default 129216428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 1293193ad2faSJason M. Bills { 1294193ad2faSJason M. Bills return; 1295193ad2faSJason M. Bills } 129616428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 1297193ad2faSJason M. Bills { 1298193ad2faSJason M. Bills return; 1299193ad2faSJason M. Bills } 1300e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 1301e1f26343SJason M. Bills // it has a duplicate entry for members 1302e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1303e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 13040f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 13050f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1306e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1307c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1308e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 1309e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1310e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 13110f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 13120f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 1313e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1314e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1315e1f26343SJason M. Bills 1316e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 1317e1f26343SJason M. Bills // for each entry 1318e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1319e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1320e1f26343SJason M. Bills if (ret < 0) 1321e1f26343SJason M. Bills { 1322e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1323f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1324e1f26343SJason M. Bills return; 1325e1f26343SJason M. Bills } 1326e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1327e1f26343SJason M. Bills journalTmp, sd_journal_close); 1328e1f26343SJason M. Bills journalTmp = nullptr; 1329b01bf299SEd Tanous uint64_t entryCount = 0; 1330e85d6b16SJason M. Bills // Reset the unique ID on the first entry 1331e85d6b16SJason M. Bills bool firstEntry = true; 1332e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 1333e1f26343SJason M. Bills { 1334193ad2faSJason M. Bills entryCount++; 1335193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 1336193ad2faSJason M. Bills // start) and top (number of entries to display) 1337193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 1338193ad2faSJason M. Bills { 1339193ad2faSJason M. Bills continue; 1340193ad2faSJason M. Bills } 1341193ad2faSJason M. Bills 134216428a1aSJason M. Bills std::string idStr; 1343e85d6b16SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) 1344e1f26343SJason M. Bills { 1345e1f26343SJason M. Bills continue; 1346e1f26343SJason M. Bills } 1347e1f26343SJason M. Bills 1348e85d6b16SJason M. Bills if (firstEntry) 1349e85d6b16SJason M. Bills { 1350e85d6b16SJason M. Bills firstEntry = false; 1351e85d6b16SJason M. Bills } 1352e85d6b16SJason M. Bills 1353e1f26343SJason M. Bills logEntryArray.push_back({}); 1354c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); 1355c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(idStr, journal.get(), 1356c4bf6374SJason M. Bills bmcJournalLogEntry) != 0) 1357e1f26343SJason M. Bills { 1358f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1359e1f26343SJason M. Bills return; 1360e1f26343SJason M. Bills } 1361e1f26343SJason M. Bills } 1362193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1363193ad2faSJason M. Bills if (skip + top < entryCount) 1364193ad2faSJason M. Bills { 1365193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 1366c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + 1367193ad2faSJason M. Bills std::to_string(skip + top); 1368193ad2faSJason M. Bills } 1369e1f26343SJason M. Bills } 1370e1f26343SJason M. Bills }; 1371e1f26343SJason M. Bills 1372c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node 1373e1f26343SJason M. Bills { 1374e1f26343SJason M. Bills public: 1375c4bf6374SJason M. Bills BMCJournalLogEntry(CrowApp &app) : 1376c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", 1377e1f26343SJason M. Bills std::string()) 1378e1f26343SJason M. Bills { 1379e1f26343SJason M. Bills entityPrivileges = { 1380e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1381e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1382e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1383e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1384e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1385e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1386e1f26343SJason M. Bills } 1387e1f26343SJason M. Bills 1388e1f26343SJason M. Bills private: 1389e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1390e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1391e1f26343SJason M. Bills { 1392e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1393e1f26343SJason M. Bills if (params.size() != 1) 1394e1f26343SJason M. Bills { 1395f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1396e1f26343SJason M. Bills return; 1397e1f26343SJason M. Bills } 139816428a1aSJason M. Bills const std::string &entryID = params[0]; 1399e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 1400e1f26343SJason M. Bills uint64_t ts = 0; 1401271584abSEd Tanous uint64_t index = 0; 140216428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 1403e1f26343SJason M. Bills { 140416428a1aSJason M. Bills return; 1405e1f26343SJason M. Bills } 1406e1f26343SJason M. Bills 1407e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1408e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1409e1f26343SJason M. Bills if (ret < 0) 1410e1f26343SJason M. Bills { 1411e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1412f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1413e1f26343SJason M. Bills return; 1414e1f26343SJason M. Bills } 1415e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1416e1f26343SJason M. Bills journalTmp, sd_journal_close); 1417e1f26343SJason M. Bills journalTmp = nullptr; 1418e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 1419af07e3f5SJason M. Bills // tracking the unique ID 1420af07e3f5SJason M. Bills std::string idStr; 1421af07e3f5SJason M. Bills bool firstEntry = true; 1422e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 1423271584abSEd Tanous for (uint64_t i = 0; i <= index; i++) 1424e1f26343SJason M. Bills { 1425e1f26343SJason M. Bills sd_journal_next(journal.get()); 1426af07e3f5SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) 1427af07e3f5SJason M. Bills { 1428af07e3f5SJason M. Bills messages::internalError(asyncResp->res); 1429af07e3f5SJason M. Bills return; 1430af07e3f5SJason M. Bills } 1431af07e3f5SJason M. Bills if (firstEntry) 1432af07e3f5SJason M. Bills { 1433af07e3f5SJason M. Bills firstEntry = false; 1434af07e3f5SJason M. Bills } 1435e1f26343SJason M. Bills } 1436c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 1437af07e3f5SJason M. Bills if (idStr != entryID) 1438c4bf6374SJason M. Bills { 1439c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 1440c4bf6374SJason M. Bills return; 1441c4bf6374SJason M. Bills } 1442c4bf6374SJason M. Bills 1443c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(entryID, journal.get(), 1444e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 1445e1f26343SJason M. Bills { 1446f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1447e1f26343SJason M. Bills return; 1448e1f26343SJason M. Bills } 1449e1f26343SJason M. Bills } 1450e1f26343SJason M. Bills }; 1451e1f26343SJason M. Bills 1452424c4176SJason M. Bills class CrashdumpService : public Node 1453e1f26343SJason M. Bills { 1454e1f26343SJason M. Bills public: 1455e1f26343SJason M. Bills template <typename CrowApp> 1456424c4176SJason M. Bills CrashdumpService(CrowApp &app) : 1457424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/") 14581da66f75SEd Tanous { 14591da66f75SEd Tanous entityPrivileges = { 1460e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1461e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1462e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1463e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1464e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1465e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 14661da66f75SEd Tanous } 14671da66f75SEd Tanous 14681da66f75SEd Tanous private: 14691da66f75SEd Tanous /** 14701da66f75SEd Tanous * Functions triggers appropriate requests on DBus 14711da66f75SEd Tanous */ 14721da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 14731da66f75SEd Tanous const std::vector<std::string> ¶ms) override 14741da66f75SEd Tanous { 1475e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 14761da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 14770f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1478424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"; 1479e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1480e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 14814f50ae4bSGunnar Mills asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; 14824f50ae4bSGunnar Mills asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; 14834f50ae4bSGunnar Mills asyncResp->res.jsonValue["Id"] = "Oem Crashdump"; 1484e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1485e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 1486cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1487cd50aa42SJason M. Bills {"@odata.id", 1488424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}}; 1489e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 14905b61b5e8SJason M. Bills {"#LogService.ClearLog", 14915b61b5e8SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 14925b61b5e8SJason M. Bills "Actions/LogService.ClearLog"}}}, 14931da66f75SEd Tanous {"Oem", 1494424c4176SJason M. Bills {{"#Crashdump.OnDemand", 1495424c4176SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1496424c4176SJason M. Bills "Actions/Oem/Crashdump.OnDemand"}}}}}}; 14971da66f75SEd Tanous 14981da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 1499e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 1500424c4176SJason M. Bills {"#Crashdump.SendRawPeci", 150108a4e4b5SAnthony Wilson {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1502424c4176SJason M. Bills "Actions/Oem/Crashdump.SendRawPeci"}}}); 15031da66f75SEd Tanous #endif 15041da66f75SEd Tanous } 15051da66f75SEd Tanous }; 15061da66f75SEd Tanous 15075b61b5e8SJason M. Bills class CrashdumpClear : public Node 15085b61b5e8SJason M. Bills { 15095b61b5e8SJason M. Bills public: 15105b61b5e8SJason M. Bills CrashdumpClear(CrowApp &app) : 15115b61b5e8SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/" 15125b61b5e8SJason M. Bills "LogService.ClearLog/") 15135b61b5e8SJason M. Bills { 15145b61b5e8SJason M. Bills entityPrivileges = { 15155b61b5e8SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 15165b61b5e8SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 15175b61b5e8SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 15185b61b5e8SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 15195b61b5e8SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 15205b61b5e8SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 15215b61b5e8SJason M. Bills } 15225b61b5e8SJason M. Bills 15235b61b5e8SJason M. Bills private: 15245b61b5e8SJason M. Bills void doPost(crow::Response &res, const crow::Request &req, 15255b61b5e8SJason M. Bills const std::vector<std::string> ¶ms) override 15265b61b5e8SJason M. Bills { 15275b61b5e8SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 15285b61b5e8SJason M. Bills 15295b61b5e8SJason M. Bills crow::connections::systemBus->async_method_call( 15305b61b5e8SJason M. Bills [asyncResp](const boost::system::error_code ec, 15315b61b5e8SJason M. Bills const std::string &resp) { 15325b61b5e8SJason M. Bills if (ec) 15335b61b5e8SJason M. Bills { 15345b61b5e8SJason M. Bills messages::internalError(asyncResp->res); 15355b61b5e8SJason M. Bills return; 15365b61b5e8SJason M. Bills } 15375b61b5e8SJason M. Bills messages::success(asyncResp->res); 15385b61b5e8SJason M. Bills }, 15395b61b5e8SJason M. Bills crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll"); 15405b61b5e8SJason M. Bills } 15415b61b5e8SJason M. Bills }; 15425b61b5e8SJason M. Bills 1543e855dd28SJason M. Bills std::string getLogCreatedTime(const std::string &crashdump) 1544e855dd28SJason M. Bills { 1545e855dd28SJason M. Bills nlohmann::json crashdumpJson = 1546e855dd28SJason M. Bills nlohmann::json::parse(crashdump, nullptr, false); 1547e855dd28SJason M. Bills if (crashdumpJson.is_discarded()) 1548e855dd28SJason M. Bills { 1549e855dd28SJason M. Bills return std::string(); 1550e855dd28SJason M. Bills } 1551e855dd28SJason M. Bills 1552e855dd28SJason M. Bills nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data"); 1553e855dd28SJason M. Bills if (cdIt == crashdumpJson.end()) 1554e855dd28SJason M. Bills { 1555e855dd28SJason M. Bills return std::string(); 1556e855dd28SJason M. Bills } 1557e855dd28SJason M. Bills 1558e855dd28SJason M. Bills nlohmann::json::const_iterator siIt = cdIt->find("METADATA"); 1559e855dd28SJason M. Bills if (siIt == cdIt->end()) 1560e855dd28SJason M. Bills { 1561e855dd28SJason M. Bills return std::string(); 1562e855dd28SJason M. Bills } 1563e855dd28SJason M. Bills 1564e855dd28SJason M. Bills nlohmann::json::const_iterator tsIt = siIt->find("timestamp"); 1565e855dd28SJason M. Bills if (tsIt == siIt->end()) 1566e855dd28SJason M. Bills { 1567e855dd28SJason M. Bills return std::string(); 1568e855dd28SJason M. Bills } 1569e855dd28SJason M. Bills 1570e855dd28SJason M. Bills const std::string *logTime = tsIt->get_ptr<const std::string *>(); 1571e855dd28SJason M. Bills if (logTime == nullptr) 1572e855dd28SJason M. Bills { 1573e855dd28SJason M. Bills return std::string(); 1574e855dd28SJason M. Bills } 1575e855dd28SJason M. Bills 1576e855dd28SJason M. Bills std::string redfishDateTime = *logTime; 1577e855dd28SJason M. Bills if (redfishDateTime.length() > 2) 1578e855dd28SJason M. Bills { 1579e855dd28SJason M. Bills redfishDateTime.insert(redfishDateTime.end() - 2, ':'); 1580e855dd28SJason M. Bills } 1581e855dd28SJason M. Bills 1582e855dd28SJason M. Bills return redfishDateTime; 1583e855dd28SJason M. Bills } 1584e855dd28SJason M. Bills 1585e855dd28SJason M. Bills std::string getLogFileName(const std::string &logTime) 1586e855dd28SJason M. Bills { 1587e855dd28SJason M. Bills // Set the crashdump file name to "crashdump_<logTime>.json" using the 1588e855dd28SJason M. Bills // created time without the timezone info 1589e855dd28SJason M. Bills std::string fileTime = logTime; 1590e855dd28SJason M. Bills size_t plusPos = fileTime.rfind('+'); 1591e855dd28SJason M. Bills if (plusPos != std::string::npos) 1592e855dd28SJason M. Bills { 1593e855dd28SJason M. Bills fileTime.erase(plusPos); 1594e855dd28SJason M. Bills } 1595e855dd28SJason M. Bills return "crashdump_" + fileTime + ".json"; 1596e855dd28SJason M. Bills } 1597e855dd28SJason M. Bills 1598e855dd28SJason M. Bills static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp, 1599e855dd28SJason M. Bills const std::string &logID, 1600e855dd28SJason M. Bills nlohmann::json &logEntryJson) 1601e855dd28SJason M. Bills { 1602e855dd28SJason M. Bills auto getStoredLogCallback = [asyncResp, logID, &logEntryJson]( 1603e855dd28SJason M. Bills const boost::system::error_code ec, 1604e855dd28SJason M. Bills const std::variant<std::string> &resp) { 1605e855dd28SJason M. Bills if (ec) 1606e855dd28SJason M. Bills { 1607e855dd28SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 16081ddcf01aSJason M. Bills if (ec.value() == 16091ddcf01aSJason M. Bills boost::system::linux_error::bad_request_descriptor) 16101ddcf01aSJason M. Bills { 16111ddcf01aSJason M. Bills messages::resourceNotFound(asyncResp->res, "LogEntry", logID); 16121ddcf01aSJason M. Bills } 16131ddcf01aSJason M. Bills else 16141ddcf01aSJason M. Bills { 1615e855dd28SJason M. Bills messages::internalError(asyncResp->res); 16161ddcf01aSJason M. Bills } 1617e855dd28SJason M. Bills return; 1618e855dd28SJason M. Bills } 1619e855dd28SJason M. Bills const std::string *log = std::get_if<std::string>(&resp); 1620e855dd28SJason M. Bills if (log == nullptr) 1621e855dd28SJason M. Bills { 1622e855dd28SJason M. Bills messages::internalError(asyncResp->res); 1623e855dd28SJason M. Bills return; 1624e855dd28SJason M. Bills } 1625e855dd28SJason M. Bills std::string logTime = getLogCreatedTime(*log); 1626e855dd28SJason M. Bills std::string fileName = getLogFileName(logTime); 1627e855dd28SJason M. Bills 1628e855dd28SJason M. Bills logEntryJson = { 1629e855dd28SJason M. Bills {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1630e855dd28SJason M. Bills {"@odata.id", 1631e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 1632e855dd28SJason M. Bills logID}, 1633e855dd28SJason M. Bills {"Name", "CPU Crashdump"}, 1634e855dd28SJason M. Bills {"Id", logID}, 1635e855dd28SJason M. Bills {"EntryType", "Oem"}, 1636e855dd28SJason M. Bills {"OemRecordFormat", "Crashdump URI"}, 1637e855dd28SJason M. Bills {"Message", 1638e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 1639e855dd28SJason M. Bills logID + "/" + fileName}, 1640e855dd28SJason M. Bills {"Created", std::move(logTime)}}; 1641e855dd28SJason M. Bills }; 1642e855dd28SJason M. Bills crow::connections::systemBus->async_method_call( 16435b61b5e8SJason M. Bills std::move(getStoredLogCallback), crashdumpObject, 16445b61b5e8SJason M. Bills crashdumpPath + std::string("/") + logID, 16455b61b5e8SJason M. Bills "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, "Log"); 1646e855dd28SJason M. Bills } 1647e855dd28SJason M. Bills 1648424c4176SJason M. Bills class CrashdumpEntryCollection : public Node 16491da66f75SEd Tanous { 16501da66f75SEd Tanous public: 16511da66f75SEd Tanous template <typename CrowApp> 1652424c4176SJason M. Bills CrashdumpEntryCollection(CrowApp &app) : 1653424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/") 16541da66f75SEd Tanous { 16551da66f75SEd Tanous entityPrivileges = { 1656e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1657e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1658e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1659e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1660e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1661e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 16621da66f75SEd Tanous } 16631da66f75SEd Tanous 16641da66f75SEd Tanous private: 16651da66f75SEd Tanous /** 16661da66f75SEd Tanous * Functions triggers appropriate requests on DBus 16671da66f75SEd Tanous */ 16681da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 16691da66f75SEd Tanous const std::vector<std::string> ¶ms) override 16701da66f75SEd Tanous { 1671e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 16721da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 16731da66f75SEd Tanous // it has a duplicate entry for members 1674e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 1675e1f26343SJason M. Bills const boost::system::error_code ec, 16761da66f75SEd Tanous const std::vector<std::string> &resp) { 16771da66f75SEd Tanous if (ec) 16781da66f75SEd Tanous { 16791da66f75SEd Tanous if (ec.value() != 16801da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 16811da66f75SEd Tanous { 16821da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 16831da66f75SEd Tanous << ec.message(); 1684f12894f8SJason M. Bills messages::internalError(asyncResp->res); 16851da66f75SEd Tanous return; 16861da66f75SEd Tanous } 16871da66f75SEd Tanous } 1688e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 16891da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 16900f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1691424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"; 1692424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries"; 1693e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1694424c4176SJason M. Bills "Collection of Crashdump Entries"; 1695e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1696e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1697e855dd28SJason M. Bills std::vector<std::string> logIDs; 1698e855dd28SJason M. Bills // Get the list of log entries and build up an empty array big 1699e855dd28SJason M. Bills // enough to hold them 17001da66f75SEd Tanous for (const std::string &objpath : resp) 17011da66f75SEd Tanous { 1702e855dd28SJason M. Bills // Get the log ID 17034ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 1704e855dd28SJason M. Bills if (lastPos == std::string::npos) 17051da66f75SEd Tanous { 1706e855dd28SJason M. Bills continue; 17071da66f75SEd Tanous } 1708e855dd28SJason M. Bills logIDs.emplace_back(objpath.substr(lastPos + 1)); 1709e855dd28SJason M. Bills 1710e855dd28SJason M. Bills // Add a space for the log entry to the array 1711e855dd28SJason M. Bills logEntryArray.push_back({}); 1712e855dd28SJason M. Bills } 1713e855dd28SJason M. Bills // Now go through and set up async calls to fill in the entries 1714e855dd28SJason M. Bills size_t index = 0; 1715e855dd28SJason M. Bills for (const std::string &logID : logIDs) 1716e855dd28SJason M. Bills { 1717e855dd28SJason M. Bills // Add the log entry to the array 1718e855dd28SJason M. Bills logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]); 17191da66f75SEd Tanous } 1720e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1721e1f26343SJason M. Bills logEntryArray.size(); 17221da66f75SEd Tanous }; 17231da66f75SEd Tanous crow::connections::systemBus->async_method_call( 17241da66f75SEd Tanous std::move(getLogEntriesCallback), 17251da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 17261da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 17271da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 17285b61b5e8SJason M. Bills std::array<const char *, 1>{crashdumpInterface}); 17291da66f75SEd Tanous } 17301da66f75SEd Tanous }; 17311da66f75SEd Tanous 1732424c4176SJason M. Bills class CrashdumpEntry : public Node 17331da66f75SEd Tanous { 17341da66f75SEd Tanous public: 1735424c4176SJason M. Bills CrashdumpEntry(CrowApp &app) : 1736d53dd41fSJason M. Bills Node(app, 1737424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/", 17381da66f75SEd Tanous std::string()) 17391da66f75SEd Tanous { 17401da66f75SEd Tanous entityPrivileges = { 1741e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1742e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1743e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1744e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1745e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1746e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 17471da66f75SEd Tanous } 17481da66f75SEd Tanous 17491da66f75SEd Tanous private: 17501da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 17511da66f75SEd Tanous const std::vector<std::string> ¶ms) override 17521da66f75SEd Tanous { 1753e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 17541da66f75SEd Tanous if (params.size() != 1) 17551da66f75SEd Tanous { 1756f12894f8SJason M. Bills messages::internalError(asyncResp->res); 17571da66f75SEd Tanous return; 17581da66f75SEd Tanous } 1759e855dd28SJason M. Bills const std::string &logID = params[0]; 1760e855dd28SJason M. Bills logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); 1761e855dd28SJason M. Bills } 1762e855dd28SJason M. Bills }; 1763e855dd28SJason M. Bills 1764e855dd28SJason M. Bills class CrashdumpFile : public Node 1765e855dd28SJason M. Bills { 1766e855dd28SJason M. Bills public: 1767e855dd28SJason M. Bills CrashdumpFile(CrowApp &app) : 1768e855dd28SJason M. Bills Node(app, 1769e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/" 1770e855dd28SJason M. Bills "<str>/", 1771e855dd28SJason M. Bills std::string(), std::string()) 1772e855dd28SJason M. Bills { 1773e855dd28SJason M. Bills entityPrivileges = { 1774e855dd28SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1775e855dd28SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1776e855dd28SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1777e855dd28SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1778e855dd28SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1779e855dd28SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1780e855dd28SJason M. Bills } 1781e855dd28SJason M. Bills 1782e855dd28SJason M. Bills private: 1783e855dd28SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1784e855dd28SJason M. Bills const std::vector<std::string> ¶ms) override 1785e855dd28SJason M. Bills { 1786e855dd28SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1787e855dd28SJason M. Bills if (params.size() != 2) 1788e855dd28SJason M. Bills { 1789e855dd28SJason M. Bills messages::internalError(asyncResp->res); 1790e855dd28SJason M. Bills return; 1791e855dd28SJason M. Bills } 1792e855dd28SJason M. Bills const std::string &logID = params[0]; 1793e855dd28SJason M. Bills const std::string &fileName = params[1]; 1794e855dd28SJason M. Bills 1795e855dd28SJason M. Bills auto getStoredLogCallback = [asyncResp, logID, fileName]( 1796abf2add6SEd Tanous const boost::system::error_code ec, 1797abf2add6SEd Tanous const std::variant<std::string> &resp) { 17981da66f75SEd Tanous if (ec) 17991da66f75SEd Tanous { 1800abf2add6SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 1801f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18021da66f75SEd Tanous return; 18031da66f75SEd Tanous } 1804abf2add6SEd Tanous const std::string *log = std::get_if<std::string>(&resp); 18051da66f75SEd Tanous if (log == nullptr) 18061da66f75SEd Tanous { 1807f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18081da66f75SEd Tanous return; 18091da66f75SEd Tanous } 1810e855dd28SJason M. Bills 1811e855dd28SJason M. Bills // Verify the file name parameter is correct 1812e855dd28SJason M. Bills if (fileName != getLogFileName(getLogCreatedTime(*log))) 18131da66f75SEd Tanous { 1814e855dd28SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, fileName); 18151da66f75SEd Tanous return; 18161da66f75SEd Tanous } 1817e855dd28SJason M. Bills 1818e855dd28SJason M. Bills // Configure this to be a file download when accessed from a browser 1819e855dd28SJason M. Bills asyncResp->res.addHeader("Content-Disposition", "attachment"); 1820e855dd28SJason M. Bills asyncResp->res.body() = *log; 18211da66f75SEd Tanous }; 18221da66f75SEd Tanous crow::connections::systemBus->async_method_call( 18235b61b5e8SJason M. Bills std::move(getStoredLogCallback), crashdumpObject, 18245b61b5e8SJason M. Bills crashdumpPath + std::string("/") + logID, 18255b61b5e8SJason M. Bills "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, 1826424c4176SJason M. Bills "Log"); 18271da66f75SEd Tanous } 18281da66f75SEd Tanous }; 18291da66f75SEd Tanous 1830424c4176SJason M. Bills class OnDemandCrashdump : public Node 18311da66f75SEd Tanous { 18321da66f75SEd Tanous public: 1833424c4176SJason M. Bills OnDemandCrashdump(CrowApp &app) : 1834424c4176SJason M. Bills Node(app, 1835424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1836424c4176SJason M. Bills "Crashdump.OnDemand/") 18371da66f75SEd Tanous { 18381da66f75SEd Tanous entityPrivileges = { 1839e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1840e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1841e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1842e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1843e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1844e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 18451da66f75SEd Tanous } 18461da66f75SEd Tanous 18471da66f75SEd Tanous private: 18481da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 18491da66f75SEd Tanous const std::vector<std::string> ¶ms) override 18501da66f75SEd Tanous { 1851e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 18521da66f75SEd Tanous 185346229577SJames Feist auto generateonDemandLogCallback = [asyncResp]( 185446229577SJames Feist const boost::system::error_code 185546229577SJames Feist ec, 18561da66f75SEd Tanous const std::string &resp) { 18571da66f75SEd Tanous if (ec) 18581da66f75SEd Tanous { 185946229577SJames Feist if (ec.value() == boost::system::errc::operation_not_supported) 18601da66f75SEd Tanous { 1861f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 18621da66f75SEd Tanous } 18634363d3b2SJason M. Bills else if (ec.value() == 18644363d3b2SJason M. Bills boost::system::errc::device_or_resource_busy) 18654363d3b2SJason M. Bills { 18664363d3b2SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, 18674363d3b2SJason M. Bills "60"); 18684363d3b2SJason M. Bills } 18691da66f75SEd Tanous else 18701da66f75SEd Tanous { 1871f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18721da66f75SEd Tanous } 18731da66f75SEd Tanous return; 18741da66f75SEd Tanous } 187546229577SJames Feist std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 187666afe4faSJames Feist [](boost::system::error_code err, sdbusplus::message::message &, 187766afe4faSJames Feist const std::shared_ptr<task::TaskData> &taskData) { 187866afe4faSJames Feist if (!err) 187966afe4faSJames Feist { 188066afe4faSJames Feist taskData->messages.emplace_back(messages::success()); 1881*32898ceaSJames Feist taskData->status = "Completed"; 188266afe4faSJames Feist } 1883*32898ceaSJames Feist return task::completed; 188466afe4faSJames Feist }, 188546229577SJames Feist "type='signal',interface='org.freedesktop.DBus.Properties'," 188646229577SJames Feist "member='PropertiesChanged',arg0namespace='com.intel." 188746229577SJames Feist "crashdump'"); 188846229577SJames Feist task->startTimer(std::chrono::minutes(5)); 188946229577SJames Feist task->populateResp(asyncResp->res); 18901da66f75SEd Tanous }; 18911da66f75SEd Tanous crow::connections::systemBus->async_method_call( 18925b61b5e8SJason M. Bills std::move(generateonDemandLogCallback), crashdumpObject, 18935b61b5e8SJason M. Bills crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog"); 18941da66f75SEd Tanous } 18951da66f75SEd Tanous }; 18961da66f75SEd Tanous 1897e1f26343SJason M. Bills class SendRawPECI : public Node 18981da66f75SEd Tanous { 18991da66f75SEd Tanous public: 1900e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 1901424c4176SJason M. Bills Node(app, 1902424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1903424c4176SJason M. Bills "Crashdump.SendRawPeci/") 19041da66f75SEd Tanous { 19051da66f75SEd Tanous entityPrivileges = { 19061da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 19071da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 19081da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 19091da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 19101da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 19111da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 19121da66f75SEd Tanous } 19131da66f75SEd Tanous 19141da66f75SEd Tanous private: 19151da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 19161da66f75SEd Tanous const std::vector<std::string> ¶ms) override 19171da66f75SEd Tanous { 1918e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 19198724c297SKarthick Sundarrajan std::vector<std::vector<uint8_t>> peciCommands; 19208724c297SKarthick Sundarrajan 19218724c297SKarthick Sundarrajan nlohmann::json reqJson = 19228724c297SKarthick Sundarrajan nlohmann::json::parse(req.body, nullptr, false); 19238724c297SKarthick Sundarrajan if (reqJson.find("PECICommands") != reqJson.end()) 19248724c297SKarthick Sundarrajan { 19258724c297SKarthick Sundarrajan if (!json_util::readJson(req, res, "PECICommands", peciCommands)) 19268724c297SKarthick Sundarrajan { 19278724c297SKarthick Sundarrajan return; 19288724c297SKarthick Sundarrajan } 19298724c297SKarthick Sundarrajan uint32_t idx = 0; 19308724c297SKarthick Sundarrajan for (auto const &cmd : peciCommands) 19318724c297SKarthick Sundarrajan { 19328724c297SKarthick Sundarrajan if (cmd.size() < 3) 19338724c297SKarthick Sundarrajan { 19348724c297SKarthick Sundarrajan std::string s("["); 19358724c297SKarthick Sundarrajan for (auto const &val : cmd) 19368724c297SKarthick Sundarrajan { 19378724c297SKarthick Sundarrajan if (val != *cmd.begin()) 19388724c297SKarthick Sundarrajan { 19398724c297SKarthick Sundarrajan s += ","; 19408724c297SKarthick Sundarrajan } 19418724c297SKarthick Sundarrajan s += std::to_string(val); 19428724c297SKarthick Sundarrajan } 19438724c297SKarthick Sundarrajan s += "]"; 19448724c297SKarthick Sundarrajan messages::actionParameterValueFormatError( 19458724c297SKarthick Sundarrajan res, s, "PECICommands[" + std::to_string(idx) + "]", 19468724c297SKarthick Sundarrajan "SendRawPeci"); 19478724c297SKarthick Sundarrajan return; 19488724c297SKarthick Sundarrajan } 19498724c297SKarthick Sundarrajan idx++; 19508724c297SKarthick Sundarrajan } 19518724c297SKarthick Sundarrajan } 19528724c297SKarthick Sundarrajan else 19538724c297SKarthick Sundarrajan { 19548724c297SKarthick Sundarrajan /* This interface is deprecated */ 1955b1556427SEd Tanous uint8_t clientAddress = 0; 1956b1556427SEd Tanous uint8_t readLength = 0; 19571da66f75SEd Tanous std::vector<uint8_t> peciCommand; 1958b1556427SEd Tanous if (!json_util::readJson(req, res, "ClientAddress", clientAddress, 1959b1556427SEd Tanous "ReadLength", readLength, "PECICommand", 1960b1556427SEd Tanous peciCommand)) 19611da66f75SEd Tanous { 19621da66f75SEd Tanous return; 19631da66f75SEd Tanous } 19648724c297SKarthick Sundarrajan peciCommands.push_back({clientAddress, 0, readLength}); 19658724c297SKarthick Sundarrajan peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(), 19668724c297SKarthick Sundarrajan peciCommand.end()); 19678724c297SKarthick Sundarrajan } 19681da66f75SEd Tanous // Callback to return the Raw PECI response 1969e1f26343SJason M. Bills auto sendRawPECICallback = 1970e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 19718724c297SKarthick Sundarrajan const std::vector<std::vector<uint8_t>> &resp) { 19721da66f75SEd Tanous if (ec) 19731da66f75SEd Tanous { 19748724c297SKarthick Sundarrajan BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: " 19751da66f75SEd Tanous << ec.message(); 1976f12894f8SJason M. Bills messages::internalError(asyncResp->res); 19771da66f75SEd Tanous return; 19781da66f75SEd Tanous } 1979e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 19801da66f75SEd Tanous {"PECIResponse", resp}}; 19811da66f75SEd Tanous }; 19821da66f75SEd Tanous // Call the SendRawPECI command with the provided data 19831da66f75SEd Tanous crow::connections::systemBus->async_method_call( 19845b61b5e8SJason M. Bills std::move(sendRawPECICallback), crashdumpObject, crashdumpPath, 19858724c297SKarthick Sundarrajan crashdumpRawPECIInterface, "SendRawPeci", peciCommands); 19861da66f75SEd Tanous } 19871da66f75SEd Tanous }; 19881da66f75SEd Tanous 1989cb92c03bSAndrew Geissler /** 1990cb92c03bSAndrew Geissler * DBusLogServiceActionsClear class supports POST method for ClearLog action. 1991cb92c03bSAndrew Geissler */ 1992cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node 1993cb92c03bSAndrew Geissler { 1994cb92c03bSAndrew Geissler public: 1995cb92c03bSAndrew Geissler DBusLogServiceActionsClear(CrowApp &app) : 1996cb92c03bSAndrew Geissler Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 19971f56a3a6STim Lee "LogService.ClearLog") 1998cb92c03bSAndrew Geissler { 1999cb92c03bSAndrew Geissler entityPrivileges = { 2000cb92c03bSAndrew Geissler {boost::beast::http::verb::get, {{"Login"}}}, 2001cb92c03bSAndrew Geissler {boost::beast::http::verb::head, {{"Login"}}}, 2002cb92c03bSAndrew Geissler {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 2003cb92c03bSAndrew Geissler {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 2004cb92c03bSAndrew Geissler {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 2005cb92c03bSAndrew Geissler {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2006cb92c03bSAndrew Geissler } 2007cb92c03bSAndrew Geissler 2008cb92c03bSAndrew Geissler private: 2009cb92c03bSAndrew Geissler /** 2010cb92c03bSAndrew Geissler * Function handles POST method request. 2011cb92c03bSAndrew Geissler * The Clear Log actions does not require any parameter.The action deletes 2012cb92c03bSAndrew Geissler * all entries found in the Entries collection for this Log Service. 2013cb92c03bSAndrew Geissler */ 2014cb92c03bSAndrew Geissler void doPost(crow::Response &res, const crow::Request &req, 2015cb92c03bSAndrew Geissler const std::vector<std::string> ¶ms) override 2016cb92c03bSAndrew Geissler { 2017cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Do delete all entries."; 2018cb92c03bSAndrew Geissler 2019cb92c03bSAndrew Geissler auto asyncResp = std::make_shared<AsyncResp>(res); 2020cb92c03bSAndrew Geissler // Process response from Logging service. 2021cb92c03bSAndrew Geissler auto resp_handler = [asyncResp](const boost::system::error_code ec) { 2022cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done"; 2023cb92c03bSAndrew Geissler if (ec) 2024cb92c03bSAndrew Geissler { 2025cb92c03bSAndrew Geissler // TODO Handle for specific error code 2026cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec; 2027cb92c03bSAndrew Geissler asyncResp->res.result( 2028cb92c03bSAndrew Geissler boost::beast::http::status::internal_server_error); 2029cb92c03bSAndrew Geissler return; 2030cb92c03bSAndrew Geissler } 2031cb92c03bSAndrew Geissler 2032cb92c03bSAndrew Geissler asyncResp->res.result(boost::beast::http::status::no_content); 2033cb92c03bSAndrew Geissler }; 2034cb92c03bSAndrew Geissler 2035cb92c03bSAndrew Geissler // Make call to Logging service to request Clear Log 2036cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 2037cb92c03bSAndrew Geissler resp_handler, "xyz.openbmc_project.Logging", 2038cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging", 2039cb92c03bSAndrew Geissler "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 2040cb92c03bSAndrew Geissler } 2041cb92c03bSAndrew Geissler }; 20421da66f75SEd Tanous } // namespace redfish 2043