| // SPDX-License-Identifier: Apache-2.0 | 
 | // SPDX-FileCopyrightText: Copyright OpenBMC Authors | 
 | // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation | 
 |  | 
 | #pragma once | 
 | #include "app.hpp" | 
 | #include "async_resp.hpp" | 
 | #include "boost_formatters.hpp" | 
 | #include "dbus_singleton.hpp" | 
 | #include "dbus_utility.hpp" | 
 | #include "http_request.hpp" | 
 | #include "http_response.hpp" | 
 | #include "json_formatters.hpp" | 
 | #include "logging.hpp" | 
 | #include "parsing.hpp" | 
 | #include "str_utility.hpp" | 
 |  | 
 | #include <systemd/sd-bus-protocol.h> | 
 | #include <systemd/sd-bus.h> | 
 | #include <tinyxml2.h> | 
 |  | 
 | #include <boost/beast/http/field.hpp> | 
 | #include <boost/beast/http/status.hpp> | 
 | #include <boost/beast/http/verb.hpp> | 
 | #include <boost/container/flat_map.hpp> | 
 | #include <boost/system/error_code.hpp> | 
 | #include <nlohmann/json.hpp> | 
 | #include <sdbusplus/asio/connection.hpp> | 
 | #include <sdbusplus/message.hpp> | 
 | #include <sdbusplus/message/native_types.hpp> | 
 |  | 
 | #include <algorithm> | 
 | #include <array> | 
 | #include <cerrno> | 
 | #include <cstdint> | 
 | #include <cstring> | 
 | #include <filesystem> | 
 | #include <functional> | 
 | #include <initializer_list> | 
 | #include <limits> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <ranges> | 
 | #include <regex> | 
 | #include <string> | 
 | #include <string_view> | 
 | #include <type_traits> | 
 | #include <utility> | 
 | #include <variant> | 
 | #include <vector> | 
 |  | 
 | namespace crow | 
 | { | 
 | namespace openbmc_mapper | 
 | { | 
 | const constexpr char* notFoundMsg = "404 Not Found"; | 
 | const constexpr char* badReqMsg = "400 Bad Request"; | 
 | const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed"; | 
 | const constexpr char* forbiddenMsg = "403 Forbidden"; | 
 | const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type"; | 
 | const constexpr char* methodFailedMsg = "500 Method Call Failed"; | 
 | const constexpr char* methodOutputFailedMsg = "500 Method Output Error"; | 
 | const constexpr char* notFoundDesc = | 
 |     "org.freedesktop.DBus.Error.FileNotFound: path or object not found"; | 
 | const constexpr char* propNotFoundDesc = | 
 |     "The specified property cannot be found"; | 
 | const constexpr char* noJsonDesc = "No JSON object could be decoded"; | 
 | const constexpr char* invalidContentType = | 
 |     "Content-type header is missing or invalid"; | 
 | const constexpr char* methodNotFoundDesc = | 
 |     "The specified method cannot be found"; | 
 | const constexpr char* methodNotAllowedDesc = "Method not allowed"; | 
 | const constexpr char* forbiddenPropDesc = | 
 |     "The specified property cannot be created"; | 
 | const constexpr char* forbiddenResDesc = | 
 |     "The specified resource cannot be created"; | 
 |  | 
 | inline bool validateFilename(const std::string& filename) | 
 | { | 
 |     static std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)"); | 
 |  | 
 |     return std::regex_match(filename, validFilename); | 
 | } | 
 |  | 
 | inline void setErrorResponse(crow::Response& res, | 
 |                              boost::beast::http::status result, | 
 |                              const std::string& desc, std::string_view msg) | 
 | { | 
 |     res.result(result); | 
 |     res.jsonValue["data"]["description"] = desc; | 
 |     res.jsonValue["message"] = msg; | 
 |     res.jsonValue["status"] = "error"; | 
 | } | 
 |  | 
 | inline void introspectObjects( | 
 |     const std::string& processName, const std::string& objectPath, | 
 |     const std::shared_ptr<bmcweb::AsyncResp>& transaction) | 
 | { | 
 |     if (transaction->res.jsonValue.is_null()) | 
 |     { | 
 |         transaction->res.jsonValue["status"] = "ok"; | 
 |         transaction->res.jsonValue["bus_name"] = processName; | 
 |         transaction->res.jsonValue["objects"] = nlohmann::json::array(); | 
 |     } | 
 |  | 
 |     dbus::utility::async_method_call( | 
 |         [transaction, processName{std::string(processName)}, | 
 |          objectPath{std::string(objectPath)}]( | 
 |             const boost::system::error_code& ec, | 
 |             const std::string& introspectXml) { | 
 |             if (ec) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR( | 
 |                     "Introspect call failed with error: {} on process: {} path: {}", | 
 |                     ec.message(), processName, objectPath); | 
 |                 return; | 
 |             } | 
 |             nlohmann::json::object_t object; | 
 |             object["path"] = objectPath; | 
 |  | 
 |             transaction->res.jsonValue["objects"].emplace_back( | 
 |                 std::move(object)); | 
 |  | 
 |             tinyxml2::XMLDocument doc; | 
 |  | 
 |             doc.Parse(introspectXml.c_str()); | 
 |             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); | 
 |             if (pRoot == nullptr) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", | 
 |                                  processName, objectPath); | 
 |             } | 
 |             else | 
 |             { | 
 |                 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node"); | 
 |                 while (node != nullptr) | 
 |                 { | 
 |                     const char* childPath = node->Attribute("name"); | 
 |                     if (childPath != nullptr) | 
 |                     { | 
 |                         std::string newpath; | 
 |                         if (objectPath != "/") | 
 |                         { | 
 |                             newpath += objectPath; | 
 |                         } | 
 |                         newpath += std::string("/") + childPath; | 
 |                         // introspect the subobjects as well | 
 |                         introspectObjects(processName, newpath, transaction); | 
 |                     } | 
 |  | 
 |                     node = node->NextSiblingElement("node"); | 
 |                 } | 
 |             } | 
 |         }, | 
 |         processName, objectPath, "org.freedesktop.DBus.Introspectable", | 
 |         "Introspect"); | 
 | } | 
 |  | 
 | inline void getPropertiesForEnumerate( | 
 |     const std::string& objectPath, const std::string& service, | 
 |     const std::string& interface, | 
 |     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("getPropertiesForEnumerate {} {} {}", objectPath, service, | 
 |                      interface); | 
 |  | 
 |     dbus::utility::getAllProperties( | 
 |         service, objectPath, interface, | 
 |         [asyncResp, objectPath, service, | 
 |          interface](const boost::system::error_code& ec, | 
 |                     const dbus::utility::DBusPropertiesMap& propertiesList) { | 
 |             if (ec) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR( | 
 |                     "GetAll on path {} iface {} service {} failed with code {}", | 
 |                     objectPath, interface, service, ec); | 
 |                 return; | 
 |             } | 
 |  | 
 |             nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; | 
 |             nlohmann::json& objectJson = dataJson[objectPath]; | 
 |             if (objectJson.is_null()) | 
 |             { | 
 |                 objectJson = nlohmann::json::object(); | 
 |             } | 
 |  | 
 |             for (const auto& [name, value] : propertiesList) | 
 |             { | 
 |                 nlohmann::json& propertyJson = objectJson[name]; | 
 |                 std::visit([&propertyJson](auto&& val) { propertyJson = val; }, | 
 |                            value); | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | // Find any results that weren't picked up by ObjectManagers, to be | 
 | // called after all ObjectManagers are searched for and called. | 
 | inline void findRemainingObjectsForEnumerate( | 
 |     const std::string& objectPath, | 
 |     const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree, | 
 |     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("findRemainingObjectsForEnumerate"); | 
 |     const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; | 
 |  | 
 |     for (const auto& [path, interface_map] : *subtree) | 
 |     { | 
 |         if (path == objectPath) | 
 |         { | 
 |             // An enumerate does not return the target path's properties | 
 |             continue; | 
 |         } | 
 |         if (!dataJson.contains(path)) | 
 |         { | 
 |             for (const auto& [service, interfaces] : interface_map) | 
 |             { | 
 |                 for (const auto& interface : interfaces) | 
 |                 { | 
 |                     if (!interface.starts_with("org.freedesktop.DBus")) | 
 |                     { | 
 |                         getPropertiesForEnumerate(path, service, interface, | 
 |                                                   asyncResp); | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | struct InProgressEnumerateData | 
 | { | 
 |     InProgressEnumerateData( | 
 |         const std::string& objectPathIn, | 
 |         const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : | 
 |         objectPath(objectPathIn), asyncResp(asyncRespIn) | 
 |     {} | 
 |  | 
 |     ~InProgressEnumerateData() | 
 |     { | 
 |         try | 
 |         { | 
 |             findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp); | 
 |         } | 
 |         catch (...) | 
 |         { | 
 |             BMCWEB_LOG_CRITICAL( | 
 |                 "findRemainingObjectsForEnumerate threw exception"); | 
 |         } | 
 |     } | 
 |  | 
 |     InProgressEnumerateData(const InProgressEnumerateData&) = delete; | 
 |     InProgressEnumerateData(InProgressEnumerateData&&) = delete; | 
 |     InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete; | 
 |     InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete; | 
 |     const std::string objectPath; | 
 |     std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree; | 
 |     std::shared_ptr<bmcweb::AsyncResp> asyncResp; | 
 | }; | 
 |  | 
 | inline void getManagedObjectsForEnumerate( | 
 |     const std::string& objectName, const std::string& objectManagerPath, | 
 |     const std::string& connectionName, | 
 |     const std::shared_ptr<InProgressEnumerateData>& transaction) | 
 | { | 
 |     BMCWEB_LOG_DEBUG( | 
 |         "getManagedObjectsForEnumerate {} object_manager_path {} connection_name {}", | 
 |         objectName, objectManagerPath, connectionName); | 
 |     sdbusplus::message::object_path path(objectManagerPath); | 
 |     dbus::utility::getManagedObjects( | 
 |         connectionName, path, | 
 |         [transaction, objectName, | 
 |          connectionName](const boost::system::error_code& ec, | 
 |                          const dbus::utility::ManagedObjectType& objects) { | 
 |             if (ec) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR( | 
 |                     "GetManagedObjects on path {} on connection {} failed with code {}", | 
 |                     objectName, connectionName, ec); | 
 |                 return; | 
 |             } | 
 |  | 
 |             nlohmann::json& dataJson = | 
 |                 transaction->asyncResp->res.jsonValue["data"]; | 
 |  | 
 |             for (const auto& objectPath : objects) | 
 |             { | 
 |                 if (objectPath.first.str.starts_with(objectName)) | 
 |                 { | 
 |                     BMCWEB_LOG_DEBUG("Reading object {}", objectPath.first.str); | 
 |                     nlohmann::json& objectJson = dataJson[objectPath.first.str]; | 
 |                     if (objectJson.is_null()) | 
 |                     { | 
 |                         objectJson = nlohmann::json::object(); | 
 |                     } | 
 |                     for (const auto& interface : objectPath.second) | 
 |                     { | 
 |                         for (const auto& property : interface.second) | 
 |                         { | 
 |                             nlohmann::json& propertyJson = | 
 |                                 objectJson[property.first]; | 
 |                             std::visit( | 
 |                                 [&propertyJson](auto&& val) { | 
 |                                     if constexpr ( | 
 |                                         std::is_same_v< | 
 |                                             std::decay_t<decltype(val)>, | 
 |                                             sdbusplus::message::unix_fd>) | 
 |                                     { | 
 |                                         propertyJson = val.fd; | 
 |                                     } | 
 |                                     else | 
 |                                     { | 
 |                                         propertyJson = val; | 
 |                                     } | 
 |                                 }, | 
 |                                 property.second); | 
 |                         } | 
 |                     } | 
 |                 } | 
 |                 for (const auto& interface : objectPath.second) | 
 |                 { | 
 |                     if (interface.first == "org.freedesktop.DBus.ObjectManager") | 
 |                     { | 
 |                         getManagedObjectsForEnumerate( | 
 |                             objectPath.first.str, objectPath.first.str, | 
 |                             connectionName, transaction); | 
 |                     } | 
 |                 } | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | inline void findObjectManagerPathForEnumerate( | 
 |     const std::string& objectName, const std::string& connectionName, | 
 |     const std::shared_ptr<InProgressEnumerateData>& transaction) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("Finding objectmanager for path {} on connection:{}", | 
 |                      objectName, connectionName); | 
 |     dbus::utility::async_method_call( | 
 |         [transaction, objectName, connectionName]( | 
 |             const boost::system::error_code& ec, | 
 |             const dbus::utility::MapperGetAncestorsResponse& objects) { | 
 |             if (ec) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR("GetAncestors on path {} failed with code {}", | 
 |                                  objectName, ec); | 
 |                 return; | 
 |             } | 
 |  | 
 |             for (const auto& pathGroup : objects) | 
 |             { | 
 |                 for (const auto& connectionGroup : pathGroup.second) | 
 |                 { | 
 |                     if (connectionGroup.first == connectionName) | 
 |                     { | 
 |                         // Found the object manager path for this resource. | 
 |                         getManagedObjectsForEnumerate( | 
 |                             objectName, pathGroup.first, connectionName, | 
 |                             transaction); | 
 |                         return; | 
 |                     } | 
 |                 } | 
 |             } | 
 |         }, | 
 |         "xyz.openbmc_project.ObjectMapper", | 
 |         "/xyz/openbmc_project/object_mapper", | 
 |         "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName, | 
 |         std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"}); | 
 | } | 
 |  | 
 | // Uses GetObject to add the object info about the target /enumerate path to | 
 | // the results of GetSubTree, as GetSubTree will not return info for the | 
 | // target path, and then continues on enumerating the rest of the tree. | 
 | inline void getObjectAndEnumerate( | 
 |     const std::shared_ptr<InProgressEnumerateData>& transaction) | 
 | { | 
 |     dbus::utility::getDbusObject( | 
 |         transaction->objectPath, {}, | 
 |         [transaction](const boost::system::error_code& ec, | 
 |                       const dbus::utility::MapperGetObject& objects) { | 
 |             if (ec) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR("GetObject for path {} failed with code {}", | 
 |                                  transaction->objectPath, ec); | 
 |                 return; | 
 |             } | 
 |  | 
 |             BMCWEB_LOG_DEBUG("GetObject for {} has {} entries", | 
 |                              transaction->objectPath, objects.size()); | 
 |             if (!objects.empty()) | 
 |             { | 
 |                 transaction->subtree->emplace_back(transaction->objectPath, | 
 |                                                    objects); | 
 |             } | 
 |  | 
 |             // Map indicating connection name, and the path where the object | 
 |             // manager exists | 
 |             boost::container::flat_map< | 
 |                 std::string, std::string, std::less<>, | 
 |                 std::vector<std::pair<std::string, std::string>>> | 
 |                 connections; | 
 |  | 
 |             for (const auto& object : *(transaction->subtree)) | 
 |             { | 
 |                 for (const auto& connection : object.second) | 
 |                 { | 
 |                     for (const auto& interface : connection.second) | 
 |                     { | 
 |                         BMCWEB_LOG_DEBUG("{} has interface {}", | 
 |                                          connection.first, interface); | 
 |                         if (interface == "org.freedesktop.DBus.ObjectManager") | 
 |                         { | 
 |                             BMCWEB_LOG_DEBUG("found object manager path {}", | 
 |                                              object.first); | 
 |                             connections[connection.first] = object.first; | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |             BMCWEB_LOG_DEBUG("Got {} connections", connections.size()); | 
 |  | 
 |             for (const auto& connection : connections) | 
 |             { | 
 |                 // If we already know where the object manager is, we don't | 
 |                 // need to search for it, we can call directly in to | 
 |                 // getManagedObjects | 
 |                 if (!connection.second.empty()) | 
 |                 { | 
 |                     getManagedObjectsForEnumerate( | 
 |                         transaction->objectPath, connection.second, | 
 |                         connection.first, transaction); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     // otherwise we need to find the object manager path | 
 |                     // before we can continue | 
 |                     findObjectManagerPathForEnumerate( | 
 |                         transaction->objectPath, connection.first, transaction); | 
 |                 } | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | // Structure for storing data on an in progress action | 
 | struct InProgressActionData | 
 | { | 
 |     explicit InProgressActionData( | 
 |         const std::shared_ptr<bmcweb::AsyncResp>& res) : asyncResp(res) | 
 |     {} | 
 |     ~InProgressActionData() | 
 |     { | 
 |         // Methods could have been called across different owners | 
 |         // and interfaces, where some calls failed and some passed. | 
 |         // | 
 |         // The rules for this are: | 
 |         // * if no method was called - error | 
 |         // * if a method failed and none passed - error | 
 |         //   (converse: if at least one method passed - OK) | 
 |         // * for the method output: | 
 |         //   * if output processing didn't fail, return the data | 
 |  | 
 |         // Only deal with method returns if nothing failed earlier | 
 |         if (asyncResp->res.result() == boost::beast::http::status::ok) | 
 |         { | 
 |             if (!methodPassed) | 
 |             { | 
 |                 if (!methodFailed) | 
 |                 { | 
 |                     setErrorResponse(asyncResp->res, | 
 |                                      boost::beast::http::status::not_found, | 
 |                                      methodNotFoundDesc, notFoundMsg); | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 if (outputFailed) | 
 |                 { | 
 |                     setErrorResponse( | 
 |                         asyncResp->res, | 
 |                         boost::beast::http::status::internal_server_error, | 
 |                         "Method output failure", methodOutputFailedMsg); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     asyncResp->res.jsonValue["status"] = "ok"; | 
 |                     asyncResp->res.jsonValue["message"] = "200 OK"; | 
 |                     asyncResp->res.jsonValue["data"] = methodResponse; | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |     InProgressActionData(const InProgressActionData&) = delete; | 
 |     InProgressActionData(InProgressActionData&&) = delete; | 
 |     InProgressActionData& operator=(const InProgressActionData&) = delete; | 
 |     InProgressActionData& operator=(InProgressActionData&&) = delete; | 
 |  | 
 |     void setErrorStatus(const std::string& desc) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::bad_request, desc, | 
 |                          badReqMsg); | 
 |     } | 
 |     std::shared_ptr<bmcweb::AsyncResp> asyncResp; | 
 |     std::string path; | 
 |     std::string methodName; | 
 |     std::string interfaceName; | 
 |     bool methodPassed = false; | 
 |     bool methodFailed = false; | 
 |     bool outputFailed = false; | 
 |     bool convertedToArray = false; | 
 |     nlohmann::json methodResponse; | 
 |     nlohmann::json arguments; | 
 | }; | 
 |  | 
 | inline std::vector<std::string> dbusArgSplit(const std::string& string) | 
 | { | 
 |     std::vector<std::string> ret; | 
 |     if (string.empty()) | 
 |     { | 
 |         return ret; | 
 |     } | 
 |     ret.emplace_back(""); | 
 |     int containerDepth = 0; | 
 |  | 
 |     for (std::string::const_iterator character = string.begin(); | 
 |          character != string.end(); character++) | 
 |     { | 
 |         ret.back() += *character; | 
 |         switch (*character) | 
 |         { | 
 |             case ('a'): | 
 |                 break; | 
 |             case ('('): | 
 |             case ('{'): | 
 |                 containerDepth++; | 
 |                 break; | 
 |             case ('}'): | 
 |             case (')'): | 
 |                 containerDepth--; | 
 |                 if (containerDepth == 0) | 
 |                 { | 
 |                     if (character + 1 != string.end()) | 
 |                     { | 
 |                         ret.emplace_back(""); | 
 |                     } | 
 |                 } | 
 |                 break; | 
 |             default: | 
 |                 if (containerDepth == 0) | 
 |                 { | 
 |                     if (character + 1 != string.end()) | 
 |                     { | 
 |                         ret.emplace_back(""); | 
 |                     } | 
 |                 } | 
 |                 break; | 
 |         } | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType, | 
 |                              const nlohmann::json& inputJson) | 
 | { | 
 |     int r = 0; | 
 |     BMCWEB_LOG_DEBUG("Converting {} to type: {}", inputJson, argType); | 
 |     const std::vector<std::string> argTypes = dbusArgSplit(argType); | 
 |  | 
 |     // Assume a single object for now. | 
 |     const nlohmann::json* j = &inputJson; | 
 |     nlohmann::json::const_iterator jIt = inputJson.begin(); | 
 |  | 
 |     for (const std::string& argCode : argTypes) | 
 |     { | 
 |         // If we are decoding multiple objects, grab the pointer to the | 
 |         // iterator, and increment it for the next loop | 
 |         if (argTypes.size() > 1) | 
 |         { | 
 |             if (jIt == inputJson.end()) | 
 |             { | 
 |                 return -2; | 
 |             } | 
 |             j = &*jIt; | 
 |             jIt++; | 
 |         } | 
 |         const int64_t* intValue = j->get_ptr<const int64_t*>(); | 
 |         const std::string* stringValue = j->get_ptr<const std::string*>(); | 
 |         const double* doubleValue = j->get_ptr<const double*>(); | 
 |         const bool* b = j->get_ptr<const bool*>(); | 
 |         int64_t v = 0; | 
 |         double d = 0.0; | 
 |  | 
 |         // Do some basic type conversions that make sense.  uint can be | 
 |         // converted to int.  int and uint can be converted to double | 
 |         if (intValue == nullptr) | 
 |         { | 
 |             const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); | 
 |             if (uintValue != nullptr) | 
 |             { | 
 |                 v = static_cast<int64_t>(*uintValue); | 
 |                 intValue = &v; | 
 |             } | 
 |         } | 
 |         if (doubleValue == nullptr) | 
 |         { | 
 |             const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); | 
 |             if (uintValue != nullptr) | 
 |             { | 
 |                 d = static_cast<double>(*uintValue); | 
 |                 doubleValue = &d; | 
 |             } | 
 |         } | 
 |         if (doubleValue == nullptr) | 
 |         { | 
 |             if (intValue != nullptr) | 
 |             { | 
 |                 d = static_cast<double>(*intValue); | 
 |                 doubleValue = &d; | 
 |             } | 
 |         } | 
 |  | 
 |         if (argCode == "s") | 
 |         { | 
 |             if (stringValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             r = sd_bus_message_append_basic( | 
 |                 m, argCode[0], static_cast<const void*>(stringValue->data())); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (argCode == "i") | 
 |         { | 
 |             if (intValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             if ((*intValue < std::numeric_limits<int32_t>::lowest()) || | 
 |                 (*intValue > std::numeric_limits<int32_t>::max())) | 
 |             { | 
 |                 return -ERANGE; | 
 |             } | 
 |             int32_t i = static_cast<int32_t>(*intValue); | 
 |             r = sd_bus_message_append_basic(m, argCode[0], &i); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (argCode == "b") | 
 |         { | 
 |             // lots of ways bool could be represented here.  Try them all | 
 |             int boolInt = 0; | 
 |             if (intValue != nullptr) | 
 |             { | 
 |                 if (*intValue == 1) | 
 |                 { | 
 |                     boolInt = 1; | 
 |                 } | 
 |                 else if (*intValue == 0) | 
 |                 { | 
 |                     boolInt = 0; | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     return -ERANGE; | 
 |                 } | 
 |             } | 
 |             else if (b != nullptr) | 
 |             { | 
 |                 boolInt = *b ? 1 : 0; | 
 |             } | 
 |             else if (stringValue != nullptr) | 
 |             { | 
 |                 if (!stringValue->empty()) | 
 |                 { | 
 |                     if (stringValue->front() == 't' || | 
 |                         stringValue->front() == 'T') | 
 |                     { | 
 |                         boolInt = 1; | 
 |                     } | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             r = sd_bus_message_append_basic(m, argCode[0], &boolInt); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (argCode == "n") | 
 |         { | 
 |             if (intValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             if ((*intValue < std::numeric_limits<int16_t>::lowest()) || | 
 |                 (*intValue > std::numeric_limits<int16_t>::max())) | 
 |             { | 
 |                 return -ERANGE; | 
 |             } | 
 |             int16_t n = static_cast<int16_t>(*intValue); | 
 |             r = sd_bus_message_append_basic(m, argCode[0], &n); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (argCode == "x") | 
 |         { | 
 |             if (intValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             r = sd_bus_message_append_basic(m, argCode[0], intValue); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (argCode == "y") | 
 |         { | 
 |             const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); | 
 |             if (uintValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             if (*uintValue > std::numeric_limits<uint8_t>::max()) | 
 |             { | 
 |                 return -ERANGE; | 
 |             } | 
 |             uint8_t y = static_cast<uint8_t>(*uintValue); | 
 |             r = sd_bus_message_append_basic(m, argCode[0], &y); | 
 |         } | 
 |         else if (argCode == "q") | 
 |         { | 
 |             const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); | 
 |             if (uintValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             if (*uintValue > std::numeric_limits<uint16_t>::max()) | 
 |             { | 
 |                 return -ERANGE; | 
 |             } | 
 |             uint16_t q = static_cast<uint16_t>(*uintValue); | 
 |             r = sd_bus_message_append_basic(m, argCode[0], &q); | 
 |         } | 
 |         else if (argCode == "u") | 
 |         { | 
 |             const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); | 
 |             if (uintValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             if (*uintValue > std::numeric_limits<uint32_t>::max()) | 
 |             { | 
 |                 return -ERANGE; | 
 |             } | 
 |             uint32_t u = static_cast<uint32_t>(*uintValue); | 
 |             r = sd_bus_message_append_basic(m, argCode[0], &u); | 
 |         } | 
 |         else if (argCode == "t") | 
 |         { | 
 |             const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); | 
 |             if (uintValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             r = sd_bus_message_append_basic(m, argCode[0], uintValue); | 
 |         } | 
 |         else if (argCode == "d") | 
 |         { | 
 |             if (doubleValue == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             if ((*doubleValue < std::numeric_limits<double>::lowest()) || | 
 |                 (*doubleValue > std::numeric_limits<double>::max())) | 
 |             { | 
 |                 return -ERANGE; | 
 |             } | 
 |             r = sd_bus_message_append_basic(m, argCode[0], doubleValue); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (argCode.starts_with("a")) | 
 |         { | 
 |             std::string containedType = argCode.substr(1); | 
 |             r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, | 
 |                                               containedType.c_str()); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |  | 
 |             for (const auto& it : *j) | 
 |             { | 
 |                 r = convertJsonToDbus(m, containedType, it); | 
 |                 if (r < 0) | 
 |                 { | 
 |                     return r; | 
 |                 } | 
 |             } | 
 |             sd_bus_message_close_container(m); | 
 |         } | 
 |         else if (argCode.starts_with("v")) | 
 |         { | 
 |             std::string containedType = argCode.substr(1); | 
 |             BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}", | 
 |                              argCode, containedType); | 
 |             r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, | 
 |                                               containedType.c_str()); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |  | 
 |             r = convertJsonToDbus(m, containedType, inputJson); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |  | 
 |             r = sd_bus_message_close_container(m); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (argCode.starts_with("(") && argCode.ends_with(")")) | 
 |         { | 
 |             std::string containedType = argCode.substr(1, argCode.size() - 2); | 
 |             r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, | 
 |                                               containedType.c_str()); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |  | 
 |             nlohmann::json::const_iterator it = j->begin(); | 
 |             for (const std::string& argCode2 : dbusArgSplit(containedType)) | 
 |             { | 
 |                 if (it == j->end()) | 
 |                 { | 
 |                     return -1; | 
 |                 } | 
 |                 r = convertJsonToDbus(m, argCode2, *it); | 
 |                 if (r < 0) | 
 |                 { | 
 |                     return r; | 
 |                 } | 
 |                 it++; | 
 |             } | 
 |             r = sd_bus_message_close_container(m); | 
 |         } | 
 |         else if (argCode.starts_with("{") && argCode.ends_with("}")) | 
 |         { | 
 |             std::string containedType = argCode.substr(1, argCode.size() - 2); | 
 |             r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, | 
 |                                               containedType.c_str()); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |  | 
 |             std::vector<std::string> codes = dbusArgSplit(containedType); | 
 |             if (codes.size() != 2) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             const std::string& keyType = codes[0]; | 
 |             const std::string& valueType = codes[1]; | 
 |             const nlohmann::json::object_t* arr = | 
 |                 j->get_ptr<const nlohmann::json::object_t*>(); | 
 |             if (arr == nullptr) | 
 |             { | 
 |                 return -1; | 
 |             } | 
 |             for (const auto& it : *arr) | 
 |             { | 
 |                 r = convertJsonToDbus(m, keyType, it.first); | 
 |                 if (r < 0) | 
 |                 { | 
 |                     return r; | 
 |                 } | 
 |  | 
 |                 r = convertJsonToDbus(m, valueType, it.second); | 
 |                 if (r < 0) | 
 |                 { | 
 |                     return r; | 
 |                 } | 
 |             } | 
 |             r = sd_bus_message_close_container(m); | 
 |         } | 
 |         else | 
 |         { | 
 |             return -2; | 
 |         } | 
 |         if (r < 0) | 
 |         { | 
 |             return r; | 
 |         } | 
 |  | 
 |         if (argTypes.size() > 1) | 
 |         { | 
 |             jIt++; | 
 |         } | 
 |     } | 
 |  | 
 |     return r; | 
 | } | 
 |  | 
 | template <typename T> | 
 | int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m, | 
 |                     nlohmann::json& data) | 
 | { | 
 |     T value; | 
 |     // When T == char*, this warning fires.  Unclear how to resolve | 
 |     // Given that sd-bus takes a void pointer to a char*, and that's | 
 |     // Not something we can fix. | 
 |     // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) | 
 |     int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!", | 
 |                          typeCode); | 
 |         return r; | 
 |     } | 
 |  | 
 |     data = value; | 
 |     return 0; | 
 | } | 
 |  | 
 | int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m, | 
 |                       nlohmann::json& response); | 
 |  | 
 | inline int readDictEntryFromMessage(const std::string& typeCode, | 
 |                                     sdbusplus::message_t& m, | 
 |                                     nlohmann::json& object) | 
 | { | 
 |     std::vector<std::string> types = dbusArgSplit(typeCode); | 
 |     if (types.size() != 2) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}", | 
 |                          types.size()); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY, | 
 |                                            typeCode.c_str()); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r); | 
 |         return r; | 
 |     } | 
 |  | 
 |     nlohmann::json key; | 
 |     r = convertDBusToJSON(types[0], m, key); | 
 |     if (r < 0) | 
 |     { | 
 |         return r; | 
 |     } | 
 |  | 
 |     const std::string* keyPtr = key.get_ptr<const std::string*>(); | 
 |     if (keyPtr == nullptr) | 
 |     { | 
 |         // json doesn't support non-string keys.  If we hit this condition, | 
 |         // convert the result to a string so we can proceed | 
 |         key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); | 
 |         keyPtr = key.get_ptr<const std::string*>(); | 
 |         // in theory this can't fail now, but lets be paranoid about it | 
 |         // anyway | 
 |         if (keyPtr == nullptr) | 
 |         { | 
 |             return -1; | 
 |         } | 
 |     } | 
 |     nlohmann::json& value = object[*keyPtr]; | 
 |  | 
 |     r = convertDBusToJSON(types[1], m, value); | 
 |     if (r < 0) | 
 |     { | 
 |         return r; | 
 |     } | 
 |  | 
 |     r = sd_bus_message_exit_container(m.get()); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed"); | 
 |         return r; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | inline int readArrayFromMessage(const std::string& typeCode, | 
 |                                 sdbusplus::message_t& m, nlohmann::json& data) | 
 | { | 
 |     if (typeCode.size() < 2) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     std::string containedType = typeCode.substr(1); | 
 |  | 
 |     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY, | 
 |                                            containedType.c_str()); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r); | 
 |         return r; | 
 |     } | 
 |  | 
 |     bool dict = containedType.starts_with("{") && containedType.ends_with("}"); | 
 |  | 
 |     if (dict) | 
 |     { | 
 |         // Remove the { } | 
 |         containedType = containedType.substr(1, containedType.size() - 2); | 
 |         data = nlohmann::json::object(); | 
 |     } | 
 |     else | 
 |     { | 
 |         data = nlohmann::json::array(); | 
 |     } | 
 |  | 
 |     while (true) | 
 |     { | 
 |         r = sd_bus_message_at_end(m.get(), 0); | 
 |         if (r < 0) | 
 |         { | 
 |             BMCWEB_LOG_ERROR("sd_bus_message_at_end failed"); | 
 |             return r; | 
 |         } | 
 |  | 
 |         if (r > 0) | 
 |         { | 
 |             break; | 
 |         } | 
 |  | 
 |         // Dictionaries are only ever seen in an array | 
 |         if (dict) | 
 |         { | 
 |             r = readDictEntryFromMessage(containedType, m, data); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             data.push_back(nlohmann::json()); | 
 |  | 
 |             r = convertDBusToJSON(containedType, m, data.back()); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     r = sd_bus_message_exit_container(m.get()); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed"); | 
 |         return r; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | inline int readStructFromMessage(const std::string& typeCode, | 
 |                                  sdbusplus::message_t& m, nlohmann::json& data) | 
 | { | 
 |     if (typeCode.size() < 3) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     std::string containedTypes = typeCode.substr(1, typeCode.size() - 2); | 
 |     std::vector<std::string> types = dbusArgSplit(containedTypes); | 
 |  | 
 |     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT, | 
 |                                            containedTypes.c_str()); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r); | 
 |         return r; | 
 |     } | 
 |  | 
 |     for (const std::string& type : types) | 
 |     { | 
 |         data.push_back(nlohmann::json()); | 
 |         r = convertDBusToJSON(type, m, data.back()); | 
 |         if (r < 0) | 
 |         { | 
 |             return r; | 
 |         } | 
 |     } | 
 |  | 
 |     r = sd_bus_message_exit_container(m.get()); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed"); | 
 |         return r; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data) | 
 | { | 
 |     const char* containerType = nullptr; | 
 |     int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed"); | 
 |         return r; | 
 |     } | 
 |  | 
 |     r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT, | 
 |                                        containerType); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r); | 
 |         return r; | 
 |     } | 
 |  | 
 |     r = convertDBusToJSON(containerType, m, data); | 
 |     if (r < 0) | 
 |     { | 
 |         return r; | 
 |     } | 
 |  | 
 |     r = sd_bus_message_exit_container(m.get()); | 
 |     if (r < 0) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed"); | 
 |         return r; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | inline int convertDBusToJSON(const std::string& returnType, | 
 |                              sdbusplus::message_t& m, nlohmann::json& response) | 
 | { | 
 |     int r = 0; | 
 |     const std::vector<std::string> returnTypes = dbusArgSplit(returnType); | 
 |  | 
 |     for (const std::string& typeCode : returnTypes) | 
 |     { | 
 |         nlohmann::json* thisElement = &response; | 
 |         if (returnTypes.size() > 1) | 
 |         { | 
 |             response.push_back(nlohmann::json{}); | 
 |             thisElement = &response.back(); | 
 |         } | 
 |  | 
 |         if (typeCode == "s" || typeCode == "g" || typeCode == "o") | 
 |         { | 
 |             r = readMessageItem<char*>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "b") | 
 |         { | 
 |             r = readMessageItem<int>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |  | 
 |             *thisElement = static_cast<bool>(thisElement->get<int>()); | 
 |         } | 
 |         else if (typeCode == "u") | 
 |         { | 
 |             r = readMessageItem<uint32_t>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "i") | 
 |         { | 
 |             r = readMessageItem<int32_t>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "x") | 
 |         { | 
 |             r = readMessageItem<int64_t>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "t") | 
 |         { | 
 |             r = readMessageItem<uint64_t>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "n") | 
 |         { | 
 |             r = readMessageItem<int16_t>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "q") | 
 |         { | 
 |             r = readMessageItem<uint16_t>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "y") | 
 |         { | 
 |             r = readMessageItem<uint8_t>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "d") | 
 |         { | 
 |             r = readMessageItem<double>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode == "h") | 
 |         { | 
 |             r = readMessageItem<int>(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode.starts_with("a")) | 
 |         { | 
 |             r = readArrayFromMessage(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode.starts_with("(") && typeCode.ends_with(")")) | 
 |         { | 
 |             r = readStructFromMessage(typeCode, m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else if (typeCode.starts_with("v")) | 
 |         { | 
 |             r = readVariantFromMessage(m, *thisElement); | 
 |             if (r < 0) | 
 |             { | 
 |                 return r; | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode); | 
 |             return -2; | 
 |         } | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | inline void handleMethodResponse( | 
 |     const std::shared_ptr<InProgressActionData>& transaction, | 
 |     sdbusplus::message_t& m, const std::string& returnType) | 
 | { | 
 |     nlohmann::json data; | 
 |  | 
 |     int r = convertDBusToJSON(returnType, m, data); | 
 |     if (r < 0) | 
 |     { | 
 |         transaction->outputFailed = true; | 
 |         return; | 
 |     } | 
 |  | 
 |     if (data.is_null()) | 
 |     { | 
 |         return; | 
 |     } | 
 |  | 
 |     if (transaction->methodResponse.is_null()) | 
 |     { | 
 |         transaction->methodResponse = std::move(data); | 
 |         return; | 
 |     } | 
 |  | 
 |     // If they're both dictionaries or arrays, merge into one. | 
 |     // Otherwise, make the results an array with every result | 
 |     // an entry.  Could also just fail in that case, but it | 
 |     // seems better to get the data back somehow. | 
 |     nlohmann::json::object_t* dataobj = | 
 |         data.get_ptr<nlohmann::json::object_t*>(); | 
 |  | 
 |     nlohmann::json::object_t* methodResponseObj = | 
 |         transaction->methodResponse.get_ptr<nlohmann::json::object_t*>(); | 
 |     if (methodResponseObj != nullptr && dataobj != nullptr) | 
 |     { | 
 |         for (auto& obj : *dataobj) | 
 |         { | 
 |             // Note: Will overwrite the data for a duplicate key | 
 |             methodResponseObj->emplace(obj.first, std::move(obj.second)); | 
 |         } | 
 |         return; | 
 |     } | 
 |  | 
 |     nlohmann::json::array_t* dataarr = data.get_ptr<nlohmann::json::array_t*>(); | 
 |     nlohmann::json::array_t* methodResponseArr = | 
 |         transaction->methodResponse.get_ptr<nlohmann::json::array_t*>(); | 
 |     if (methodResponseArr != nullptr && dataarr != nullptr) | 
 |     { | 
 |         for (auto& obj : *dataarr) | 
 |         { | 
 |             methodResponseArr->emplace_back(std::move(obj)); | 
 |         } | 
 |         return; | 
 |     } | 
 |  | 
 |     if (!transaction->convertedToArray) | 
 |     { | 
 |         // They are different types. May as well turn them into an array | 
 |         nlohmann::json j = std::move(transaction->methodResponse); | 
 |         transaction->methodResponse = nlohmann::json::array(); | 
 |         transaction->methodResponse.emplace_back(std::move(j)); | 
 |         transaction->methodResponse.emplace_back(std::move(data)); | 
 |         transaction->convertedToArray = true; | 
 |     } | 
 |     else | 
 |     { | 
 |         transaction->methodResponse.emplace_back(std::move(data)); | 
 |     } | 
 | } | 
 |  | 
 | inline void findActionOnInterface( | 
 |     const std::shared_ptr<InProgressActionData>& transaction, | 
 |     const std::string& connectionName) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName); | 
 |     dbus::utility::async_method_call( | 
 |         [transaction, connectionName{std::string(connectionName)}]( | 
 |             const boost::system::error_code& ec, | 
 |             const std::string& introspectXml) { | 
 |             BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml); | 
 |             if (ec) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR( | 
 |                     "Introspect call failed with error: {} on process: {}", | 
 |                     ec.message(), connectionName); | 
 |                 return; | 
 |             } | 
 |             tinyxml2::XMLDocument doc; | 
 |  | 
 |             doc.Parse(introspectXml.data(), introspectXml.size()); | 
 |             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); | 
 |             if (pRoot == nullptr) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR("XML document failed to parse {}", | 
 |                                  connectionName); | 
 |                 return; | 
 |             } | 
 |             tinyxml2::XMLElement* interfaceNode = | 
 |                 pRoot->FirstChildElement("interface"); | 
 |             while (interfaceNode != nullptr) | 
 |             { | 
 |                 const char* thisInterfaceName = | 
 |                     interfaceNode->Attribute("name"); | 
 |                 if (thisInterfaceName != nullptr) | 
 |                 { | 
 |                     if (!transaction->interfaceName.empty() && | 
 |                         (transaction->interfaceName != thisInterfaceName)) | 
 |                     { | 
 |                         interfaceNode = | 
 |                             interfaceNode->NextSiblingElement("interface"); | 
 |                         continue; | 
 |                     } | 
 |  | 
 |                     tinyxml2::XMLElement* methodNode = | 
 |                         interfaceNode->FirstChildElement("method"); | 
 |                     while (methodNode != nullptr) | 
 |                     { | 
 |                         const char* thisMethodName = | 
 |                             methodNode->Attribute("name"); | 
 |                         BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName); | 
 |                         if (thisMethodName != nullptr && | 
 |                             thisMethodName == transaction->methodName) | 
 |                         { | 
 |                             BMCWEB_LOG_DEBUG( | 
 |                                 "Found method named {} on interface {}", | 
 |                                 thisMethodName, thisInterfaceName); | 
 |                             sdbusplus::message_t m = | 
 |                                 crow::connections::systemBus->new_method_call( | 
 |                                     connectionName.c_str(), | 
 |                                     transaction->path.c_str(), | 
 |                                     thisInterfaceName, | 
 |                                     transaction->methodName.c_str()); | 
 |  | 
 |                             tinyxml2::XMLElement* argumentNode = | 
 |                                 methodNode->FirstChildElement("arg"); | 
 |  | 
 |                             std::string returnType; | 
 |  | 
 |                             // Find the output type | 
 |                             while (argumentNode != nullptr) | 
 |                             { | 
 |                                 const char* argDirection = | 
 |                                     argumentNode->Attribute("direction"); | 
 |                                 const char* argType = | 
 |                                     argumentNode->Attribute("type"); | 
 |                                 if (argDirection != nullptr && | 
 |                                     argType != nullptr && | 
 |                                     std::string(argDirection) == "out") | 
 |                                 { | 
 |                                     returnType = argType; | 
 |                                     break; | 
 |                                 } | 
 |                                 argumentNode = | 
 |                                     argumentNode->NextSiblingElement("arg"); | 
 |                             } | 
 |  | 
 |                             auto argIt = transaction->arguments.begin(); | 
 |  | 
 |                             argumentNode = methodNode->FirstChildElement("arg"); | 
 |  | 
 |                             while (argumentNode != nullptr) | 
 |                             { | 
 |                                 const char* argDirection = | 
 |                                     argumentNode->Attribute("direction"); | 
 |                                 const char* argType = | 
 |                                     argumentNode->Attribute("type"); | 
 |                                 if (argDirection != nullptr && | 
 |                                     argType != nullptr && | 
 |                                     std::string(argDirection) == "in") | 
 |                                 { | 
 |                                     if (argIt == transaction->arguments.end()) | 
 |                                     { | 
 |                                         transaction->setErrorStatus( | 
 |                                             "Invalid method args"); | 
 |                                         return; | 
 |                                     } | 
 |                                     if (convertJsonToDbus(m.get(), | 
 |                                                           std::string(argType), | 
 |                                                           *argIt) < 0) | 
 |                                     { | 
 |                                         transaction->setErrorStatus( | 
 |                                             "Invalid method arg type"); | 
 |                                         return; | 
 |                                     } | 
 |  | 
 |                                     argIt++; | 
 |                                 } | 
 |                                 argumentNode = | 
 |                                     argumentNode->NextSiblingElement("arg"); | 
 |                             } | 
 |  | 
 |                             crow::connections::systemBus->async_send( | 
 |                                 m, [transaction, returnType]( | 
 |                                        const boost::system::error_code& ec2, | 
 |                                        sdbusplus::message_t& m2) { | 
 |                                     if (ec2) | 
 |                                     { | 
 |                                         transaction->methodFailed = true; | 
 |                                         const sd_bus_error* e = m2.get_error(); | 
 |  | 
 |                                         if (e != nullptr) | 
 |                                         { | 
 |                                             setErrorResponse( | 
 |                                                 transaction->asyncResp->res, | 
 |                                                 boost::beast::http::status:: | 
 |                                                     bad_request, | 
 |                                                 e->name, e->message); | 
 |                                         } | 
 |                                         else | 
 |                                         { | 
 |                                             setErrorResponse( | 
 |                                                 transaction->asyncResp->res, | 
 |                                                 boost::beast::http::status:: | 
 |                                                     bad_request, | 
 |                                                 "Method call failed", | 
 |                                                 methodFailedMsg); | 
 |                                         } | 
 |                                         return; | 
 |                                     } | 
 |                                     transaction->methodPassed = true; | 
 |  | 
 |                                     handleMethodResponse(transaction, m2, | 
 |                                                          returnType); | 
 |                                 }); | 
 |                             break; | 
 |                         } | 
 |                         methodNode = methodNode->NextSiblingElement("method"); | 
 |                     } | 
 |                 } | 
 |                 interfaceNode = interfaceNode->NextSiblingElement("interface"); | 
 |             } | 
 |         }, | 
 |         connectionName, transaction->path, | 
 |         "org.freedesktop.DBus.Introspectable", "Introspect"); | 
 | } | 
 |  | 
 | inline void handleAction(const crow::Request& req, | 
 |                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                          const std::string& objectPath, | 
 |                          const std::string& methodName) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath, | 
 |                      methodName); | 
 |     nlohmann::json requestDbusData; | 
 |  | 
 |     JsonParseResult ret = parseRequestAsJson(req, requestDbusData); | 
 |     if (ret == JsonParseResult::BadContentType) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::unsupported_media_type, | 
 |                          invalidContentType, unsupportedMediaMsg); | 
 |         return; | 
 |     } | 
 |     if (ret != JsonParseResult::Success) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::bad_request, noJsonDesc, | 
 |                          badReqMsg); | 
 |         return; | 
 |     } | 
 |     nlohmann::json::iterator data = requestDbusData.find("data"); | 
 |     if (data == requestDbusData.end()) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::bad_request, noJsonDesc, | 
 |                          badReqMsg); | 
 |         return; | 
 |     } | 
 |  | 
 |     if (!data->is_array()) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::bad_request, noJsonDesc, | 
 |                          badReqMsg); | 
 |         return; | 
 |     } | 
 |     auto transaction = std::make_shared<InProgressActionData>(asyncResp); | 
 |  | 
 |     transaction->path = objectPath; | 
 |     transaction->methodName = methodName; | 
 |     transaction->arguments = std::move(*data); | 
 |     dbus::utility::getDbusObject( | 
 |         objectPath, {}, | 
 |         [transaction]( | 
 |             const boost::system::error_code& ec, | 
 |             const std::vector<std::pair<std::string, std::vector<std::string>>>& | 
 |                 interfaceNames) { | 
 |             if (ec || interfaceNames.empty()) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR("Can't find object"); | 
 |                 setErrorResponse(transaction->asyncResp->res, | 
 |                                  boost::beast::http::status::not_found, | 
 |                                  notFoundDesc, notFoundMsg); | 
 |                 return; | 
 |             } | 
 |  | 
 |             BMCWEB_LOG_DEBUG("GetObject returned {} object(s)", | 
 |                              interfaceNames.size()); | 
 |  | 
 |             for (const std::pair<std::string, std::vector<std::string>>& | 
 |                      object : interfaceNames) | 
 |             { | 
 |                 findActionOnInterface(transaction, object.first); | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                          const std::string& objectPath) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath); | 
 |  | 
 |     dbus::utility::getDbusObject( | 
 |         objectPath, {}, | 
 |         [asyncResp, objectPath]( | 
 |             const boost::system::error_code& ec, | 
 |             const std::vector<std::pair<std::string, std::vector<std::string>>>& | 
 |                 interfaceNames) { | 
 |             if (ec || interfaceNames.empty()) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR("Can't find object"); | 
 |                 setErrorResponse(asyncResp->res, | 
 |                                  boost::beast::http::status::method_not_allowed, | 
 |                                  methodNotAllowedDesc, methodNotAllowedMsg); | 
 |                 return; | 
 |             } | 
 |  | 
 |             auto transaction = | 
 |                 std::make_shared<InProgressActionData>(asyncResp); | 
 |             transaction->path = objectPath; | 
 |             transaction->methodName = "Delete"; | 
 |             transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; | 
 |  | 
 |             for (const std::pair<std::string, std::vector<std::string>>& | 
 |                      object : interfaceNames) | 
 |             { | 
 |                 findActionOnInterface(transaction, object.first); | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                        const std::string& objectPath, int32_t depth = 0) | 
 | { | 
 |     dbus::utility::getSubTreePaths( | 
 |         objectPath, depth, {}, | 
 |         [asyncResp]( | 
 |             const boost::system::error_code& ec, | 
 |             const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) { | 
 |             if (ec) | 
 |             { | 
 |                 setErrorResponse(asyncResp->res, | 
 |                                  boost::beast::http::status::not_found, | 
 |                                  notFoundDesc, notFoundMsg); | 
 |             } | 
 |             else | 
 |             { | 
 |                 asyncResp->res.jsonValue["status"] = "ok"; | 
 |                 asyncResp->res.jsonValue["message"] = "200 OK"; | 
 |                 asyncResp->res.jsonValue["data"] = objectPaths; | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                             const std::string& objectPath) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath); | 
 |  | 
 |     asyncResp->res.jsonValue["message"] = "200 OK"; | 
 |     asyncResp->res.jsonValue["status"] = "ok"; | 
 |     asyncResp->res.jsonValue["data"] = nlohmann::json::object(); | 
 |  | 
 |     dbus::utility::getSubTree( | 
 |         objectPath, 0, {}, | 
 |         [objectPath, asyncResp]( | 
 |             const boost::system::error_code& ec, | 
 |             const dbus::utility::MapperGetSubTreeResponse& objectNames) { | 
 |             auto transaction = std::make_shared<InProgressEnumerateData>( | 
 |                 objectPath, asyncResp); | 
 |  | 
 |             transaction->subtree = | 
 |                 std::make_shared<dbus::utility::MapperGetSubTreeResponse>( | 
 |                     objectNames); | 
 |  | 
 |             if (ec) | 
 |             { | 
 |                 BMCWEB_LOG_ERROR("GetSubTree failed on {}", | 
 |                                  transaction->objectPath); | 
 |                 setErrorResponse(transaction->asyncResp->res, | 
 |                                  boost::beast::http::status::not_found, | 
 |                                  notFoundDesc, notFoundMsg); | 
 |                 return; | 
 |             } | 
 |  | 
 |             // Add the data for the path passed in to the results | 
 |             // as if GetSubTree returned it, and continue on enumerating | 
 |             getObjectAndEnumerate(transaction); | 
 |         }); | 
 | } | 
 |  | 
 | inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                       std::string& objectPath, std::string& destProperty) | 
 | { | 
 |     BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty); | 
 |     std::shared_ptr<std::string> propertyName = | 
 |         std::make_shared<std::string>(std::move(destProperty)); | 
 |  | 
 |     std::shared_ptr<std::string> path = | 
 |         std::make_shared<std::string>(std::move(objectPath)); | 
 |  | 
 |     dbus::utility::getDbusObject( | 
 |         *path, {}, | 
 |         [asyncResp, path, | 
 |          propertyName](const boost::system::error_code& ec, | 
 |                        const dbus::utility::MapperGetObject& objectNames) { | 
 |             if (ec || objectNames.empty()) | 
 |             { | 
 |                 setErrorResponse(asyncResp->res, | 
 |                                  boost::beast::http::status::not_found, | 
 |                                  notFoundDesc, notFoundMsg); | 
 |                 return; | 
 |             } | 
 |             std::shared_ptr<nlohmann::json> response = | 
 |                 std::make_shared<nlohmann::json>(nlohmann::json::object()); | 
 |             for (const std::pair<std::string, std::vector<std::string>>& | 
 |                      connection : objectNames) | 
 |             { | 
 |                 const std::vector<std::string>& interfaceNames = | 
 |                     connection.second; | 
 |  | 
 |                 if (interfaceNames.empty()) | 
 |                 { | 
 |                     // mapper allows empty interfaces in case an | 
 |                     // object does not implement any interface. | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 for (const std::string& interface : interfaceNames) | 
 |                 { | 
 |                     sdbusplus::message_t m = | 
 |                         crow::connections::systemBus->new_method_call( | 
 |                             connection.first.c_str(), path->c_str(), | 
 |                             "org.freedesktop.DBus.Properties", "GetAll"); | 
 |                     m.append(interface); | 
 |                     crow::connections::systemBus->async_send( | 
 |                         m, [asyncResp, response, | 
 |                             propertyName](const boost::system::error_code& ec2, | 
 |                                           sdbusplus::message_t& msg) { | 
 |                             if (ec2) | 
 |                             { | 
 |                                 BMCWEB_LOG_ERROR("Bad dbus request error: {}", | 
 |                                                  ec2); | 
 |                             } | 
 |                             else | 
 |                             { | 
 |                                 nlohmann::json properties; | 
 |                                 int r = | 
 |                                     convertDBusToJSON("a{sv}", msg, properties); | 
 |                                 if (r < 0) | 
 |                                 { | 
 |                                     BMCWEB_LOG_ERROR( | 
 |                                         "convertDBusToJSON failed"); | 
 |                                 } | 
 |                                 else | 
 |                                 { | 
 |                                     nlohmann::json::object_t* obj = | 
 |                                         properties.get_ptr< | 
 |                                             nlohmann::json::object_t*>(); | 
 |                                     if (obj == nullptr) | 
 |                                     { | 
 |                                         return; | 
 |                                     } | 
 |                                     for (auto& prop : *obj) | 
 |                                     { | 
 |                                         // if property name is empty, or | 
 |                                         // matches our search query, add it | 
 |                                         // to the response json | 
 |  | 
 |                                         if (propertyName->empty()) | 
 |                                         { | 
 |                                             (*response)[prop.first] = | 
 |                                                 std::move(prop.second); | 
 |                                         } | 
 |                                         else if (prop.first == *propertyName) | 
 |                                         { | 
 |                                             *response = std::move(prop.second); | 
 |                                         } | 
 |                                     } | 
 |                                 } | 
 |                             } | 
 |                             if (response.use_count() == 1) | 
 |                             { | 
 |                                 if (!propertyName->empty() && response->empty()) | 
 |                                 { | 
 |                                     setErrorResponse( | 
 |                                         asyncResp->res, | 
 |                                         boost::beast::http::status::not_found, | 
 |                                         propNotFoundDesc, notFoundMsg); | 
 |                                 } | 
 |                                 else | 
 |                                 { | 
 |                                     asyncResp->res.jsonValue["status"] = "ok"; | 
 |                                     asyncResp->res.jsonValue["message"] = | 
 |                                         "200 OK"; | 
 |                                     asyncResp->res.jsonValue["data"] = | 
 |                                         *response; | 
 |                                 } | 
 |                             } | 
 |                         }); | 
 |                 } | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | struct AsyncPutRequest | 
 | { | 
 |     explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) : | 
 |         asyncResp(resIn) | 
 |     {} | 
 |     ~AsyncPutRequest() | 
 |     { | 
 |         if (asyncResp->res.jsonValue.empty()) | 
 |         { | 
 |             setErrorResponse(asyncResp->res, | 
 |                              boost::beast::http::status::forbidden, | 
 |                              forbiddenMsg, forbiddenPropDesc); | 
 |         } | 
 |     } | 
 |  | 
 |     AsyncPutRequest(const AsyncPutRequest&) = delete; | 
 |     AsyncPutRequest(AsyncPutRequest&&) = delete; | 
 |     AsyncPutRequest& operator=(const AsyncPutRequest&) = delete; | 
 |     AsyncPutRequest& operator=(AsyncPutRequest&&) = delete; | 
 |  | 
 |     void setErrorStatus(const std::string& desc) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::internal_server_error, | 
 |                          desc, badReqMsg); | 
 |     } | 
 |  | 
 |     const std::shared_ptr<bmcweb::AsyncResp> asyncResp; | 
 |     std::string objectPath; | 
 |     std::string propertyName; | 
 |     nlohmann::json propertyValue; | 
 | }; | 
 |  | 
 | inline void handlePut(const crow::Request& req, | 
 |                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                       const std::string& objectPath, | 
 |                       const std::string& destProperty) | 
 | { | 
 |     if (destProperty.empty()) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden, | 
 |                          forbiddenResDesc, forbiddenMsg); | 
 |         return; | 
 |     } | 
 |     nlohmann::json requestDbusData; | 
 |  | 
 |     JsonParseResult ret = parseRequestAsJson(req, requestDbusData); | 
 |     if (ret == JsonParseResult::BadContentType) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::unsupported_media_type, | 
 |                          invalidContentType, unsupportedMediaMsg); | 
 |         return; | 
 |     } | 
 |  | 
 |     if (ret != JsonParseResult::Success) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::bad_request, noJsonDesc, | 
 |                          badReqMsg); | 
 |         return; | 
 |     } | 
 |  | 
 |     auto propertyIt = requestDbusData.find("data"); | 
 |     if (propertyIt == requestDbusData.end()) | 
 |     { | 
 |         setErrorResponse(asyncResp->res, | 
 |                          boost::beast::http::status::bad_request, noJsonDesc, | 
 |                          badReqMsg); | 
 |         return; | 
 |     } | 
 |     const nlohmann::json& propertySetValue = *propertyIt; | 
 |     auto transaction = std::make_shared<AsyncPutRequest>(asyncResp); | 
 |     transaction->objectPath = objectPath; | 
 |     transaction->propertyName = destProperty; | 
 |     transaction->propertyValue = propertySetValue; | 
 |  | 
 |     dbus::utility::getDbusObject( | 
 |         transaction->objectPath, {}, | 
 |         [transaction](const boost::system::error_code& ec2, | 
 |                       const dbus::utility::MapperGetObject& objectNames) { | 
 |             if (!ec2 && objectNames.empty()) | 
 |             { | 
 |                 setErrorResponse(transaction->asyncResp->res, | 
 |                                  boost::beast::http::status::not_found, | 
 |                                  propNotFoundDesc, notFoundMsg); | 
 |                 return; | 
 |             } | 
 |  | 
 |             for (const std::pair<std::string, std::vector<std::string>>& | 
 |                      connection : objectNames) | 
 |             { | 
 |                 const std::string& connectionName = connection.first; | 
 |  | 
 |                 dbus::utility::async_method_call( | 
 |                     [connectionName{std::string(connectionName)}, | 
 |                      transaction](const boost::system::error_code& ec3, | 
 |                                   const std::string& introspectXml) { | 
 |                         if (ec3) | 
 |                         { | 
 |                             BMCWEB_LOG_ERROR( | 
 |                                 "Introspect call failed with error: {} on process: {}", | 
 |                                 ec3.message(), connectionName); | 
 |                             transaction->setErrorStatus("Unexpected Error"); | 
 |                             return; | 
 |                         } | 
 |                         tinyxml2::XMLDocument doc; | 
 |  | 
 |                         doc.Parse(introspectXml.c_str()); | 
 |                         tinyxml2::XMLNode* pRoot = | 
 |                             doc.FirstChildElement("node"); | 
 |                         if (pRoot == nullptr) | 
 |                         { | 
 |                             BMCWEB_LOG_ERROR("XML document failed to parse: {}", | 
 |                                              introspectXml); | 
 |                             transaction->setErrorStatus("Unexpected Error"); | 
 |                             return; | 
 |                         } | 
 |                         tinyxml2::XMLElement* ifaceNode = | 
 |                             pRoot->FirstChildElement("interface"); | 
 |                         while (ifaceNode != nullptr) | 
 |                         { | 
 |                             const char* interfaceName = | 
 |                                 ifaceNode->Attribute("name"); | 
 |                             BMCWEB_LOG_DEBUG("found interface {}", | 
 |                                              interfaceName); | 
 |                             tinyxml2::XMLElement* propNode = | 
 |                                 ifaceNode->FirstChildElement("property"); | 
 |                             while (propNode != nullptr) | 
 |                             { | 
 |                                 const char* propertyName = | 
 |                                     propNode->Attribute("name"); | 
 |                                 if (propertyName == nullptr) | 
 |                                 { | 
 |                                     BMCWEB_LOG_DEBUG( | 
 |                                         "Couldn't find name property"); | 
 |                                     continue; | 
 |                                 } | 
 |                                 BMCWEB_LOG_DEBUG("Found property {}", | 
 |                                                  propertyName); | 
 |                                 if (propertyName == transaction->propertyName) | 
 |                                 { | 
 |                                     const char* argType = | 
 |                                         propNode->Attribute("type"); | 
 |                                     if (argType != nullptr) | 
 |                                     { | 
 |                                         sdbusplus::message_t m = | 
 |                                             crow::connections::systemBus | 
 |                                                 ->new_method_call( | 
 |                                                     connectionName.c_str(), | 
 |                                                     transaction->objectPath | 
 |                                                         .c_str(), | 
 |                                                     "org.freedesktop.DBus." | 
 |                                                     "Properties", | 
 |                                                     "Set"); | 
 |                                         m.append(interfaceName, | 
 |                                                  transaction->propertyName); | 
 |                                         int r = sd_bus_message_open_container( | 
 |                                             m.get(), SD_BUS_TYPE_VARIANT, | 
 |                                             argType); | 
 |                                         if (r < 0) | 
 |                                         { | 
 |                                             transaction->setErrorStatus( | 
 |                                                 "Unexpected Error"); | 
 |                                             return; | 
 |                                         } | 
 |                                         r = convertJsonToDbus( | 
 |                                             m.get(), argType, | 
 |                                             transaction->propertyValue); | 
 |                                         if (r < 0) | 
 |                                         { | 
 |                                             if (r == -ERANGE) | 
 |                                             { | 
 |                                                 transaction->setErrorStatus( | 
 |                                                     "Provided property value " | 
 |                                                     "is out of range for the " | 
 |                                                     "property type"); | 
 |                                             } | 
 |                                             else | 
 |                                             { | 
 |                                                 transaction->setErrorStatus( | 
 |                                                     "Invalid arg type"); | 
 |                                             } | 
 |                                             return; | 
 |                                         } | 
 |                                         r = sd_bus_message_close_container( | 
 |                                             m.get()); | 
 |                                         if (r < 0) | 
 |                                         { | 
 |                                             transaction->setErrorStatus( | 
 |                                                 "Unexpected Error"); | 
 |                                             return; | 
 |                                         } | 
 |                                         crow::connections::systemBus | 
 |                                             ->async_send( | 
 |                                                 m, | 
 |                                                 [transaction]( | 
 |                                                     const boost::system:: | 
 |                                                         error_code& ec, | 
 |                                                     sdbusplus::message_t& m2) { | 
 |                                                     BMCWEB_LOG_DEBUG("sent"); | 
 |                                                     if (ec) | 
 |                                                     { | 
 |                                                         const sd_bus_error* e = | 
 |                                                             m2.get_error(); | 
 |                                                         setErrorResponse( | 
 |                                                             transaction | 
 |                                                                 ->asyncResp | 
 |                                                                 ->res, | 
 |                                                             boost::beast::http:: | 
 |                                                                 status:: | 
 |                                                                     forbidden, | 
 |                                                             (e) != nullptr | 
 |                                                                 ? e->name | 
 |                                                                 : ec.category() | 
 |                                                                       .name(), | 
 |                                                             (e) != nullptr | 
 |                                                                 ? e->message | 
 |                                                                 : ec.message()); | 
 |                                                     } | 
 |                                                     else | 
 |                                                     { | 
 |                                                         transaction->asyncResp | 
 |                                                             ->res.jsonValue | 
 |                                                                 ["status"] = | 
 |                                                             "ok"; | 
 |                                                         transaction->asyncResp | 
 |                                                             ->res.jsonValue | 
 |                                                                 ["message"] = | 
 |                                                             "200 OK"; | 
 |                                                         transaction->asyncResp | 
 |                                                             ->res | 
 |                                                             .jsonValue["data"] = | 
 |                                                             nullptr; | 
 |                                                     } | 
 |                                                 }); | 
 |                                     } | 
 |                                 } | 
 |                                 propNode = | 
 |                                     propNode->NextSiblingElement("property"); | 
 |                             } | 
 |                             ifaceNode = | 
 |                                 ifaceNode->NextSiblingElement("interface"); | 
 |                         } | 
 |                     }, | 
 |                     connectionName, transaction->objectPath, | 
 |                     "org.freedesktop.DBus.Introspectable", "Introspect"); | 
 |             } | 
 |         }); | 
 | } | 
 |  | 
 | inline void handleDBusUrl(const crow::Request& req, | 
 |                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                           std::string& objectPath) | 
 | { | 
 |     // If accessing a single attribute, fill in and update objectPath, | 
 |     // otherwise leave destProperty blank | 
 |     std::string destProperty; | 
 |     const char* attrSeperator = "/attr/"; | 
 |     size_t attrPosition = objectPath.find(attrSeperator); | 
 |     if (attrPosition != std::string::npos) | 
 |     { | 
 |         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), | 
 |                                          objectPath.length()); | 
 |         objectPath.resize(attrPosition); | 
 |     } | 
 |  | 
 |     if (req.method() == boost::beast::http::verb::post) | 
 |     { | 
 |         constexpr const char* actionSeperator = "/action/"; | 
 |         size_t actionPosition = objectPath.find(actionSeperator); | 
 |         if (actionPosition != std::string::npos) | 
 |         { | 
 |             std::string postProperty = | 
 |                 objectPath.substr((actionPosition + strlen(actionSeperator)), | 
 |                                   objectPath.length()); | 
 |             objectPath.resize(actionPosition); | 
 |             handleAction(req, asyncResp, objectPath, postProperty); | 
 |             return; | 
 |         } | 
 |     } | 
 |     else if (req.method() == boost::beast::http::verb::get) | 
 |     { | 
 |         if (objectPath.ends_with("/enumerate")) | 
 |         { | 
 |             objectPath.erase(objectPath.end() - sizeof("enumerate"), | 
 |                              objectPath.end()); | 
 |             handleEnumerate(asyncResp, objectPath); | 
 |         } | 
 |         else if (objectPath.ends_with("/list")) | 
 |         { | 
 |             objectPath.erase(objectPath.end() - sizeof("list"), | 
 |                              objectPath.end()); | 
 |             handleList(asyncResp, objectPath); | 
 |         } | 
 |         else | 
 |         { | 
 |             // Trim any trailing "/" at the end | 
 |             if (objectPath.ends_with("/")) | 
 |             { | 
 |                 objectPath.pop_back(); | 
 |                 handleList(asyncResp, objectPath, 1); | 
 |             } | 
 |             else | 
 |             { | 
 |                 handleGet(asyncResp, objectPath, destProperty); | 
 |             } | 
 |         } | 
 |         return; | 
 |     } | 
 |     else if (req.method() == boost::beast::http::verb::put) | 
 |     { | 
 |         handlePut(req, asyncResp, objectPath, destProperty); | 
 |         return; | 
 |     } | 
 |     else if (req.method() == boost::beast::http::verb::delete_) | 
 |     { | 
 |         handleDelete(asyncResp, objectPath); | 
 |         return; | 
 |     } | 
 |  | 
 |     setErrorResponse(asyncResp->res, | 
 |                      boost::beast::http::status::method_not_allowed, | 
 |                      methodNotAllowedDesc, methodNotAllowedMsg); | 
 | } | 
 |  | 
 | inline void handleBusSystemPost( | 
 |     const crow::Request& req, | 
 |     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |     const std::string& processName, const std::string& requestedPath) | 
 | { | 
 |     std::vector<std::string> strs; | 
 |  | 
 |     bmcweb::split(strs, requestedPath, '/'); | 
 |     std::string objectPath; | 
 |     std::string interfaceName; | 
 |     std::string methodName; | 
 |     auto it = strs.begin(); | 
 |     if (it == strs.end()) | 
 |     { | 
 |         objectPath = "/"; | 
 |     } | 
 |     while (it != strs.end()) | 
 |     { | 
 |         // Check if segment contains ".".  If it does, it must be an | 
 |         // interface | 
 |         if (it->find(".") != std::string::npos) | 
 |         { | 
 |             break; | 
 |             // This check is necessary as the trailing slash gets | 
 |             // parsed as part of our <path> specifier above, which | 
 |             // causes the normal trailing backslash redirector to | 
 |             // fail. | 
 |         } | 
 |         if (!it->empty()) | 
 |         { | 
 |             objectPath += "/" + *it; | 
 |         } | 
 |         it++; | 
 |     } | 
 |     if (it != strs.end()) | 
 |     { | 
 |         interfaceName = *it; | 
 |         it++; | 
 |  | 
 |         // after interface, we might have a method name | 
 |         if (it != strs.end()) | 
 |         { | 
 |             methodName = *it; | 
 |             it++; | 
 |         } | 
 |     } | 
 |     if (it != strs.end()) | 
 |     { | 
 |         // if there is more levels past the method name, something | 
 |         // went wrong, return not found | 
 |         asyncResp->res.result(boost::beast::http::status::not_found); | 
 |         return; | 
 |     } | 
 |     if (interfaceName.empty()) | 
 |     { | 
 |         dbus::utility::async_method_call( | 
 |             [asyncResp, processName, | 
 |              objectPath](const boost::system::error_code& ec, | 
 |                          const std::string& introspectXml) { | 
 |                 if (ec) | 
 |                 { | 
 |                     BMCWEB_LOG_ERROR( | 
 |                         "Introspect call failed with error: {} on process: {} path: {}", | 
 |                         ec.message(), processName, objectPath); | 
 |                     return; | 
 |                 } | 
 |                 tinyxml2::XMLDocument doc; | 
 |  | 
 |                 doc.Parse(introspectXml.c_str()); | 
 |                 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); | 
 |                 if (pRoot == nullptr) | 
 |                 { | 
 |                     BMCWEB_LOG_ERROR("XML document failed to parse {} {}", | 
 |                                      processName, objectPath); | 
 |                     asyncResp->res.jsonValue["status"] = "XML parse error"; | 
 |                     asyncResp->res.result( | 
 |                         boost::beast::http::status::internal_server_error); | 
 |                     return; | 
 |                 } | 
 |  | 
 |                 BMCWEB_LOG_DEBUG("{}", introspectXml); | 
 |                 asyncResp->res.jsonValue["status"] = "ok"; | 
 |                 asyncResp->res.jsonValue["bus_name"] = processName; | 
 |                 asyncResp->res.jsonValue["object_path"] = objectPath; | 
 |  | 
 |                 nlohmann::json& interfacesArray = | 
 |                     asyncResp->res.jsonValue["interfaces"]; | 
 |                 interfacesArray = nlohmann::json::array(); | 
 |                 tinyxml2::XMLElement* interface = | 
 |                     pRoot->FirstChildElement("interface"); | 
 |  | 
 |                 while (interface != nullptr) | 
 |                 { | 
 |                     const char* ifaceName = interface->Attribute("name"); | 
 |                     if (ifaceName != nullptr) | 
 |                     { | 
 |                         nlohmann::json::object_t interfaceObj; | 
 |                         interfaceObj["name"] = ifaceName; | 
 |                         interfacesArray.emplace_back(std::move(interfaceObj)); | 
 |                     } | 
 |  | 
 |                     interface = interface->NextSiblingElement("interface"); | 
 |                 } | 
 |             }, | 
 |             processName, objectPath, "org.freedesktop.DBus.Introspectable", | 
 |             "Introspect"); | 
 |     } | 
 |     else if (methodName.empty()) | 
 |     { | 
 |         dbus::utility::async_method_call( | 
 |             [asyncResp, processName, objectPath, | 
 |              interfaceName](const boost::system::error_code& ec, | 
 |                             const std::string& introspectXml) { | 
 |                 if (ec) | 
 |                 { | 
 |                     BMCWEB_LOG_ERROR( | 
 |                         "Introspect call failed with error: {} on process: {} path: {}", | 
 |                         ec.message(), processName, objectPath); | 
 |                     return; | 
 |                 } | 
 |                 tinyxml2::XMLDocument doc; | 
 |  | 
 |                 doc.Parse(introspectXml.data(), introspectXml.size()); | 
 |                 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); | 
 |                 if (pRoot == nullptr) | 
 |                 { | 
 |                     BMCWEB_LOG_ERROR("XML document failed to parse {} {}", | 
 |                                      processName, objectPath); | 
 |                     asyncResp->res.result( | 
 |                         boost::beast::http::status::internal_server_error); | 
 |                     return; | 
 |                 } | 
 |  | 
 |                 asyncResp->res.jsonValue["status"] = "ok"; | 
 |                 asyncResp->res.jsonValue["bus_name"] = processName; | 
 |                 asyncResp->res.jsonValue["interface"] = interfaceName; | 
 |                 asyncResp->res.jsonValue["object_path"] = objectPath; | 
 |  | 
 |                 nlohmann::json& methodsArray = | 
 |                     asyncResp->res.jsonValue["methods"]; | 
 |                 methodsArray = nlohmann::json::array(); | 
 |  | 
 |                 nlohmann::json& signalsArray = | 
 |                     asyncResp->res.jsonValue["signals"]; | 
 |                 signalsArray = nlohmann::json::array(); | 
 |  | 
 |                 nlohmann::json& propertiesObj = | 
 |                     asyncResp->res.jsonValue["properties"]; | 
 |                 propertiesObj = nlohmann::json::object(); | 
 |  | 
 |                 // if we know we're the only call, build the | 
 |                 // json directly | 
 |                 tinyxml2::XMLElement* interface = | 
 |                     pRoot->FirstChildElement("interface"); | 
 |                 while (interface != nullptr) | 
 |                 { | 
 |                     const char* ifaceName = interface->Attribute("name"); | 
 |  | 
 |                     if (ifaceName != nullptr && ifaceName == interfaceName) | 
 |                     { | 
 |                         break; | 
 |                     } | 
 |  | 
 |                     interface = interface->NextSiblingElement("interface"); | 
 |                 } | 
 |                 if (interface == nullptr) | 
 |                 { | 
 |                     // if we got to the end of the list and | 
 |                     // never found a match, throw 404 | 
 |                     asyncResp->res.result( | 
 |                         boost::beast::http::status::not_found); | 
 |                     return; | 
 |                 } | 
 |  | 
 |                 tinyxml2::XMLElement* methods = | 
 |                     interface->FirstChildElement("method"); | 
 |                 while (methods != nullptr) | 
 |                 { | 
 |                     nlohmann::json argsArray = nlohmann::json::array(); | 
 |                     tinyxml2::XMLElement* arg = | 
 |                         methods->FirstChildElement("arg"); | 
 |                     while (arg != nullptr) | 
 |                     { | 
 |                         nlohmann::json thisArg; | 
 |                         for (const char* fieldName : std::array<const char*, 3>{ | 
 |                                  "name", "direction", "type"}) | 
 |                         { | 
 |                             const char* fieldValue = arg->Attribute(fieldName); | 
 |                             if (fieldValue != nullptr) | 
 |                             { | 
 |                                 thisArg[fieldName] = fieldValue; | 
 |                             } | 
 |                         } | 
 |                         argsArray.emplace_back(std::move(thisArg)); | 
 |                         arg = arg->NextSiblingElement("arg"); | 
 |                     } | 
 |  | 
 |                     const char* name = methods->Attribute("name"); | 
 |                     if (name != nullptr) | 
 |                     { | 
 |                         std::string uri; | 
 |                         uri.reserve(14 + processName.size() + | 
 |                                     objectPath.size() + interfaceName.size() + | 
 |                                     strlen(name)); | 
 |                         uri += "/bus/system/"; | 
 |                         uri += processName; | 
 |                         uri += objectPath; | 
 |                         uri += "/"; | 
 |                         uri += interfaceName; | 
 |                         uri += "/"; | 
 |                         uri += name; | 
 |  | 
 |                         nlohmann::json::object_t object; | 
 |                         object["name"] = name; | 
 |                         object["uri"] = std::move(uri); | 
 |                         object["args"] = argsArray; | 
 |  | 
 |                         methodsArray.emplace_back(std::move(object)); | 
 |                     } | 
 |                     methods = methods->NextSiblingElement("method"); | 
 |                 } | 
 |                 tinyxml2::XMLElement* signals = | 
 |                     interface->FirstChildElement("signal"); | 
 |                 while (signals != nullptr) | 
 |                 { | 
 |                     nlohmann::json argsArray = nlohmann::json::array(); | 
 |  | 
 |                     tinyxml2::XMLElement* arg = | 
 |                         signals->FirstChildElement("arg"); | 
 |                     while (arg != nullptr) | 
 |                     { | 
 |                         const char* name = arg->Attribute("name"); | 
 |                         const char* type = arg->Attribute("type"); | 
 |                         if (name != nullptr && type != nullptr) | 
 |                         { | 
 |                             nlohmann::json::object_t params; | 
 |                             params["name"] = name; | 
 |                             params["type"] = type; | 
 |                             argsArray.push_back(std::move(params)); | 
 |                         } | 
 |                         arg = arg->NextSiblingElement("arg"); | 
 |                     } | 
 |                     const char* name = signals->Attribute("name"); | 
 |                     if (name != nullptr) | 
 |                     { | 
 |                         nlohmann::json::object_t object; | 
 |                         object["name"] = name; | 
 |                         object["args"] = argsArray; | 
 |                         signalsArray.emplace_back(std::move(object)); | 
 |                     } | 
 |  | 
 |                     signals = signals->NextSiblingElement("signal"); | 
 |                 } | 
 |  | 
 |                 tinyxml2::XMLElement* property = | 
 |                     interface->FirstChildElement("property"); | 
 |                 while (property != nullptr) | 
 |                 { | 
 |                     const char* name = property->Attribute("name"); | 
 |                     const char* type = property->Attribute("type"); | 
 |                     if (type != nullptr && name != nullptr) | 
 |                     { | 
 |                         sdbusplus::message_t m = | 
 |                             crow::connections::systemBus->new_method_call( | 
 |                                 processName.c_str(), objectPath.c_str(), | 
 |                                 "org.freedesktop." | 
 |                                 "DBus." | 
 |                                 "Properties", | 
 |                                 "Get"); | 
 |                         m.append(interfaceName, name); | 
 |                         nlohmann::json& propertyItem = propertiesObj[name]; | 
 |                         crow::connections::systemBus->async_send( | 
 |                             m, [&propertyItem, | 
 |                                 asyncResp](const boost::system::error_code& ec2, | 
 |                                            sdbusplus::message_t& msg) { | 
 |                                 if (ec2) | 
 |                                 { | 
 |                                     return; | 
 |                                 } | 
 |  | 
 |                                 int r = | 
 |                                     convertDBusToJSON("v", msg, propertyItem); | 
 |                                 if (r < 0) | 
 |                                 { | 
 |                                     BMCWEB_LOG_ERROR( | 
 |                                         "Couldn't convert vector to json"); | 
 |                                 } | 
 |                             }); | 
 |                     } | 
 |                     property = property->NextSiblingElement("property"); | 
 |                 } | 
 |             }, | 
 |             processName, objectPath, "org.freedesktop.DBus.Introspectable", | 
 |             "Introspect"); | 
 |     } | 
 |     else | 
 |     { | 
 |         if (req.method() != boost::beast::http::verb::post) | 
 |         { | 
 |             asyncResp->res.result(boost::beast::http::status::not_found); | 
 |             return; | 
 |         } | 
 |  | 
 |         nlohmann::json requestDbusData; | 
 |         JsonParseResult ret = parseRequestAsJson(req, requestDbusData); | 
 |         if (ret == JsonParseResult::BadContentType) | 
 |         { | 
 |             setErrorResponse(asyncResp->res, | 
 |                              boost::beast::http::status::unsupported_media_type, | 
 |                              invalidContentType, unsupportedMediaMsg); | 
 |             return; | 
 |         } | 
 |         if (ret != JsonParseResult::Success) | 
 |         { | 
 |             setErrorResponse(asyncResp->res, | 
 |                              boost::beast::http::status::bad_request, | 
 |                              noJsonDesc, badReqMsg); | 
 |             return; | 
 |         } | 
 |  | 
 |         if (!requestDbusData.is_array()) | 
 |         { | 
 |             asyncResp->res.result(boost::beast::http::status::bad_request); | 
 |             return; | 
 |         } | 
 |         auto transaction = std::make_shared<InProgressActionData>(asyncResp); | 
 |  | 
 |         transaction->path = objectPath; | 
 |         transaction->methodName = methodName; | 
 |         transaction->arguments = std::move(requestDbusData); | 
 |  | 
 |         findActionOnInterface(transaction, processName); | 
 |     } | 
 | } | 
 |  | 
 | inline void requestRoutes(App& app) | 
 | { | 
 |     BMCWEB_ROUTE(app, "/bus/") | 
 |         .privileges({{"Login"}}) | 
 |         .methods(boost::beast::http::verb::get)( | 
 |             [](const crow::Request&, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | 
 |                 nlohmann::json::array_t buses; | 
 |                 nlohmann::json& bus = buses.emplace_back(); | 
 |                 bus["name"] = "system"; | 
 |                 asyncResp->res.jsonValue["busses"] = std::move(buses); | 
 |                 asyncResp->res.jsonValue["status"] = "ok"; | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/bus/system/") | 
 |         .privileges({{"Login"}}) | 
 |         .methods(boost::beast::http::verb::get)( | 
 |             [](const crow::Request&, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | 
 |                 auto myCallback = [asyncResp]( | 
 |                                       const boost::system::error_code& ec, | 
 |                                       std::vector<std::string>& names) { | 
 |                     if (ec) | 
 |                     { | 
 |                         BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec); | 
 |                         asyncResp->res.result( | 
 |                             boost::beast::http::status::internal_server_error); | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         std::ranges::sort(names); | 
 |                         asyncResp->res.jsonValue["status"] = "ok"; | 
 |                         auto& objectsSub = asyncResp->res.jsonValue["objects"]; | 
 |                         for (const auto& name : names) | 
 |                         { | 
 |                             nlohmann::json::object_t object; | 
 |                             object["name"] = name; | 
 |                             objectsSub.emplace_back(std::move(object)); | 
 |                         } | 
 |                     } | 
 |                 }; | 
 |                 dbus::utility::async_method_call( | 
 |                     std::move(myCallback), "org.freedesktop.DBus", "/", | 
 |                     "org.freedesktop.DBus", "ListNames"); | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/list/") | 
 |         .privileges({{"Login"}}) | 
 |         .methods(boost::beast::http::verb::get)( | 
 |             [](const crow::Request&, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | 
 |                 handleList(asyncResp, "/"); | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/xyz/<path>") | 
 |         .privileges({{"Login"}}) | 
 |         .methods(boost::beast::http::verb::get)( | 
 |             [](const crow::Request& req, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                const std::string& path) { | 
 |                 std::string objectPath = "/xyz/" + path; | 
 |                 handleDBusUrl(req, asyncResp, objectPath); | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/xyz/<path>") | 
 |         .privileges({{"ConfigureComponents", "ConfigureManager"}}) | 
 |         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, | 
 |                  boost::beast::http::verb::delete_)( | 
 |             [](const crow::Request& req, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                const std::string& path) { | 
 |                 std::string objectPath = "/xyz/" + path; | 
 |                 handleDBusUrl(req, asyncResp, objectPath); | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/org/<path>") | 
 |         .privileges({{"Login"}}) | 
 |         .methods(boost::beast::http::verb::get)( | 
 |             [](const crow::Request& req, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                const std::string& path) { | 
 |                 std::string objectPath = "/org/" + path; | 
 |                 handleDBusUrl(req, asyncResp, objectPath); | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/org/<path>") | 
 |         .privileges({{"ConfigureComponents", "ConfigureManager"}}) | 
 |         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, | 
 |                  boost::beast::http::verb::delete_)( | 
 |             [](const crow::Request& req, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                const std::string& path) { | 
 |                 std::string objectPath = "/org/" + path; | 
 |                 handleDBusUrl(req, asyncResp, objectPath); | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/download/dump/<str>/") | 
 |         .privileges({{"ConfigureManager"}}) | 
 |         .methods(boost::beast::http::verb::get)( | 
 |             [](const crow::Request&, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                const std::string& dumpId) { | 
 |                 if (!validateFilename(dumpId)) | 
 |                 { | 
 |                     asyncResp->res.result( | 
 |                         boost::beast::http::status::bad_request); | 
 |                     return; | 
 |                 } | 
 |                 std::filesystem::path loc( | 
 |                     "/var/lib/phosphor-debug-collector/dumps"); | 
 |  | 
 |                 loc /= dumpId; | 
 |  | 
 |                 if (!std::filesystem::exists(loc) || | 
 |                     !std::filesystem::is_directory(loc)) | 
 |                 { | 
 |                     BMCWEB_LOG_ERROR("{}Not found", loc.string()); | 
 |                     asyncResp->res.result( | 
 |                         boost::beast::http::status::not_found); | 
 |                     return; | 
 |                 } | 
 |                 std::filesystem::directory_iterator files(loc); | 
 |  | 
 |                 for (const auto& file : files) | 
 |                 { | 
 |                     if (asyncResp->res.openFile(file) != | 
 |                         crow::OpenCode::Success) | 
 |                     { | 
 |                         continue; | 
 |                     } | 
 |  | 
 |                     asyncResp->res.addHeader( | 
 |                         boost::beast::http::field::content_type, | 
 |                         "application/octet-stream"); | 
 |  | 
 |                     // Assuming only one dump file will be present in the dump | 
 |                     // id directory | 
 |                     std::string dumpFileName = file.path().filename().string(); | 
 |  | 
 |                     // Filename should be in alphanumeric, dot and underscore | 
 |                     // Its based on phosphor-debug-collector application | 
 |                     // dumpfile format | 
 |                     static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); | 
 |                     if (!std::regex_match(dumpFileName, dumpFileRegex)) | 
 |                     { | 
 |                         BMCWEB_LOG_ERROR("Invalid dump filename {}", | 
 |                                          dumpFileName); | 
 |                         asyncResp->res.result( | 
 |                             boost::beast::http::status::not_found); | 
 |                         return; | 
 |                     } | 
 |                     std::string contentDispositionParam = | 
 |                         "attachment; filename=\"" + dumpFileName + "\""; | 
 |  | 
 |                     asyncResp->res.addHeader( | 
 |                         boost::beast::http::field::content_disposition, | 
 |                         contentDispositionParam); | 
 |  | 
 |                     return; | 
 |                 } | 
 |                 asyncResp->res.result(boost::beast::http::status::not_found); | 
 |                 return; | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/bus/system/<str>/") | 
 |         .privileges({{"Login"}}) | 
 |  | 
 |         .methods(boost::beast::http::verb::get)( | 
 |             [](const crow::Request&, | 
 |                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
 |                const std::string& connection) { | 
 |                 introspectObjects(connection, "/", asyncResp); | 
 |             }); | 
 |  | 
 |     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") | 
 |         .privileges({{"ConfigureComponents", "ConfigureManager"}}) | 
 |         .methods(boost::beast::http::verb::get, | 
 |                  boost::beast::http::verb::post)(handleBusSystemPost); | 
 | } | 
 | } // namespace openbmc_mapper | 
 | } // namespace crow |