// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright OpenBMC Authors // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation #pragma once #include "bmcweb_config.h" #include "app.hpp" #include "async_resp.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" #include "http_body.hpp" #include "http_request.hpp" #include "http_response.hpp" #include "http_utility.hpp" #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/dbus_event_log_entry.hpp" #include "utils/dbus_utils.hpp" #include "utils/json_utils.hpp" #include "utils/log_services_utils.hpp" #include "utils/time_utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace redfish { inline std::optional getProviderNotifyAction(const std::string& notify) { std::optional notifyAction; if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Notify") { notifyAction = true; } else if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Inhibit") { notifyAction = false; } return notifyAction; } inline std::string translateSeverityDbusToRedfish(const std::string& s) { if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") || (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") || (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") || (s == "xyz.openbmc_project.Logging.Entry.Level.Error")) { return "Critical"; } if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") || (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") || (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")) { return "OK"; } if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") { return "Warning"; } return ""; } inline void fillEventLogLogEntryFromDbusLogEntry( const DbusEventLogEntry& entry, nlohmann::json& objectToFillOut) { objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; objectToFillOut["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); objectToFillOut["Name"] = "System Event Log Entry"; objectToFillOut["Id"] = std::to_string(entry.Id); objectToFillOut["Message"] = entry.Message; objectToFillOut["Resolved"] = entry.Resolved; std::optional notifyAction = getProviderNotifyAction(entry.ServiceProviderNotify); if (notifyAction) { objectToFillOut["ServiceProviderNotified"] = *notifyAction; } if ((entry.Resolution != nullptr) && !entry.Resolution->empty()) { objectToFillOut["Resolution"] = *entry.Resolution; } objectToFillOut["EntryType"] = "Event"; objectToFillOut["Severity"] = translateSeverityDbusToRedfish(entry.Severity); objectToFillOut["Created"] = redfish::time_utils::getDateTimeUintMs(entry.Timestamp); objectToFillOut["Modified"] = redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp); if (entry.Path != nullptr) { objectToFillOut["AdditionalDataURI"] = boost::urls::format( "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment", BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); } } inline void afterLogEntriesGetManagedObjects( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& resp) { if (ec) { // TODO Handle for specific error code BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}", ec); messages::internalError(asyncResp->res); return; } nlohmann::json::array_t entriesArray; for (const auto& objectPath : resp) { dbus::utility::DBusPropertiesMap propsFlattened; auto isEntry = std::ranges::find_if(objectPath.second, [](const auto& object) { return object.first == "xyz.openbmc_project.Logging.Entry"; }); if (isEntry == objectPath.second.end()) { continue; } for (const auto& interfaceMap : objectPath.second) { for (const auto& propertyMap : interfaceMap.second) { propsFlattened.emplace_back(propertyMap.first, propertyMap.second); } } std::optional optEntry = fillDbusEventLogEntryFromPropertyMap(propsFlattened); if (!optEntry.has_value()) { messages::internalError(asyncResp->res); return; } fillEventLogLogEntryFromDbusLogEntry(*optEntry, entriesArray.emplace_back()); } redfish::json_util::sortJsonArrayByKey(entriesArray, "Id"); asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size(); asyncResp->res.jsonValue["Members"] = std::move(entriesArray); } inline void dBusEventLogEntryCollection( const std::shared_ptr& asyncResp) { // Collections don't include the static data added by SubRoute // because it has a duplicate entry for members asyncResp->res.jsonValue["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; asyncResp->res.jsonValue["@odata.id"] = std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; asyncResp->res.jsonValue["Description"] = "Collection of System Event Log Entries"; // DBus implementation of EventLog/Entries // Make call to Logging Service to find all log entry objects sdbusplus::message::object_path path("/xyz/openbmc_project/logging"); dbus::utility::getManagedObjects( "xyz.openbmc_project.Logging", path, [asyncResp](const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& resp) { afterLogEntriesGetManagedObjects(asyncResp, ec, resp); }); } inline void afterDBusEventLogEntryGet( const std::shared_ptr& asyncResp, const std::string& entryID, const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& resp) { if (ec.value() == EBADR) { messages::resourceNotFound(asyncResp->res, "EventLogEntry", entryID); return; } if (ec) { BMCWEB_LOG_ERROR("EventLogEntry (DBus) resp_handler got error {}", ec); messages::internalError(asyncResp->res); return; } std::optional optEntry = fillDbusEventLogEntryFromPropertyMap(resp); if (!optEntry.has_value()) { messages::internalError(asyncResp->res); return; } fillEventLogLogEntryFromDbusLogEntry(*optEntry, asyncResp->res.jsonValue); } inline void dBusEventLogEntryGet( const std::shared_ptr& asyncResp, std::string entryID) { dbus::utility::escapePathForDbus(entryID); // DBus implementation of EventLog/Entries // Make call to Logging Service to find all log entry objects dbus::utility::getAllProperties( "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging/entry/" + entryID, "", std::bind_front(afterDBusEventLogEntryGet, asyncResp, entryID)); } inline void dBusEventLogEntryPatch( const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& entryId) { std::optional resolved; if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved)) { return; } BMCWEB_LOG_DEBUG("Set Resolved"); setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging/entry/" + entryId, "xyz.openbmc_project.Logging.Entry", "Resolved", resolved.value_or(false)); } inline void dBusEventLogEntryDelete( const std::shared_ptr& asyncResp, std::string entryID) { BMCWEB_LOG_DEBUG("Do delete single event entries."); dbus::utility::escapePathForDbus(entryID); // Process response from Logging service. auto respHandler = [asyncResp, entryID](const boost::system::error_code& ec) { BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done"); if (ec) { if (ec.value() == EBADR) { messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); return; } // TODO Handle for specific error code BMCWEB_LOG_ERROR( "EventLogEntry (DBus) doDelete respHandler got error {}", ec); asyncResp->res.result( boost::beast::http::status::internal_server_error); return; } messages::success(asyncResp->res); }; // Make call to Logging service to request Delete Log dbus::utility::async_method_call( asyncResp, respHandler, "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging/entry/" + entryID, "xyz.openbmc_project.Object.Delete", "Delete"); } inline void dBusLogServiceActionsClear( const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Do delete all entries."); // Process response from Logging service. auto respHandler = [asyncResp](const boost::system::error_code& ec) { BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done"); if (ec) { // TODO Handle for specific error code BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec); asyncResp->res.result( boost::beast::http::status::internal_server_error); return; } messages::success(asyncResp->res); }; // Make call to Logging service to request Clear Log dbus::utility::async_method_call( asyncResp, respHandler, "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); } inline void downloadEventLogEntry( const std::shared_ptr& asyncResp, const std::string& systemName, const std::string& entryID, const std::string& dumpType) { if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } std::string entryPath = sdbusplus::message::object_path("/xyz/openbmc_project/logging/entry") / entryID; auto downloadEventLogEntryHandler = [asyncResp, entryID, dumpType](const boost::system::error_code& ec, const sdbusplus::message::unix_fd& unixfd) { log_services_utils::downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); }; dbus::utility::async_method_call( asyncResp, std::move(downloadEventLogEntryHandler), "xyz.openbmc_project.Logging", entryPath, "xyz.openbmc_project.Logging.Entry", "GetEntry"); } inline void handleDBusEventLogEntryDownloadGet( crow::App& app, const std::string& dumpType, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName, const std::string& entryID) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if (!http_helpers::isContentTypeAllowed( req.getHeaderValue("Accept"), http_helpers::ContentType::OctetStream, true)) { asyncResp->res.result(boost::beast::http::status::bad_request); return; } downloadEventLogEntry(asyncResp, systemName, entryID, dumpType); } inline void requestRoutesDBusEventLogEntryCollection(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Systems//LogServices/EventLog/Entries/") .privileges(redfish::privileges::getLogEntryCollection) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } dBusEventLogEntryCollection(asyncResp); }); } inline void requestRoutesDBusEventLogEntry(App& app) { BMCWEB_ROUTE( app, "/redfish/v1/Systems//LogServices/EventLog/Entries//") .privileges(redfish::privileges::getLogEntry) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName, const std::string& entryId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } dBusEventLogEntryGet(asyncResp, entryId); }); BMCWEB_ROUTE( app, "/redfish/v1/Systems//LogServices/EventLog/Entries//") .privileges(redfish::privileges::patchLogEntry) .methods(boost::beast::http::verb::patch)( [&app](const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName, const std::string& entryId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } dBusEventLogEntryPatch(req, asyncResp, entryId); }); BMCWEB_ROUTE( app, "/redfish/v1/Systems//LogServices/EventLog/Entries//") .privileges( redfish::privileges:: deleteLogEntrySubOverComputerSystemLogServiceCollectionLogServiceLogEntryCollection) .methods(boost::beast::http::verb::delete_)( [&app](const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName, const std::string& param) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } dBusEventLogEntryDelete(asyncResp, param); }); } /** * DBusLogServiceActionsClear class supports POST method for ClearLog action. */ inline void requestRoutesDBusLogServiceActionsClear(App& app) { /** * Function handles POST method request. * The Clear Log actions does not require any parameter.The action deletes * all entries found in the Entries collection for this Log Service. */ BMCWEB_ROUTE( app, "/redfish/v1/Systems//LogServices/EventLog/Actions/LogService.ClearLog/") .privileges(redfish::privileges:: postLogServiceSubOverComputerSystemLogServiceCollection) .methods(boost::beast::http::verb::post)( [&app](const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } dBusLogServiceActionsClear(asyncResp); }); } inline void requestRoutesDBusEventLogEntryDownload(App& app) { BMCWEB_ROUTE( app, "/redfish/v1/Systems//LogServices/EventLog/Entries//attachment/") .privileges(redfish::privileges::getLogEntry) .methods(boost::beast::http::verb::get)(std::bind_front( handleDBusEventLogEntryDownloadGet, std::ref(app), "System")); } } // namespace redfish