xref: /openbmc/bmcweb/include/openbmc_dbus_rest.hpp (revision 1516c21b27faf8dcf7c41e9b7253da97025a5f28)
1 // Copyright (c) 2018 Intel Corporation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 #include "app.hpp"
17 #include "async_resp.hpp"
18 #include "boost_formatters.hpp"
19 #include "dbus_singleton.hpp"
20 #include "dbus_utility.hpp"
21 #include "http_request.hpp"
22 #include "http_response.hpp"
23 #include "json_formatters.hpp"
24 #include "logging.hpp"
25 #include "parsing.hpp"
26 #include "routing.hpp"
27 #include "str_utility.hpp"
28 
29 #include <systemd/sd-bus-protocol.h>
30 #include <systemd/sd-bus.h>
31 #include <tinyxml2.h>
32 
33 #include <boost/beast/http/status.hpp>
34 #include <boost/beast/http/verb.hpp>
35 #include <boost/container/flat_map.hpp>
36 #include <boost/container/vector.hpp>
37 #include <boost/system/error_code.hpp>
38 #include <nlohmann/json.hpp>
39 #include <sdbusplus/asio/connection.hpp>
40 #include <sdbusplus/asio/property.hpp>
41 #include <sdbusplus/exception.hpp>
42 #include <sdbusplus/message.hpp>
43 #include <sdbusplus/message/native_types.hpp>
44 
45 #include <algorithm>
46 #include <array>
47 #include <cerrno>
48 #include <cstdint>
49 #include <cstring>
50 #include <filesystem>
51 #include <fstream>
52 #include <functional>
53 #include <initializer_list>
54 #include <iterator>
55 #include <limits>
56 #include <map>
57 #include <memory>
58 #include <ranges>
59 #include <regex>
60 #include <string>
61 #include <string_view>
62 #include <type_traits>
63 #include <utility>
64 #include <variant>
65 #include <vector>
66 
67 // IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp>
68 // IWYU pragma: no_include <boost/system/detail/error_code.hpp>
69 // IWYU pragma: no_include <boost/system/detail/error_category.hpp>
70 // IWYU pragma: no_include <errno.h>
71 // IWYU pragma: no_include <string.h>
72 // IWYU pragma: no_include <ext/alloc_traits.h>
73 // IWYU pragma: no_include <exception>
74 // IWYU pragma: no_include <boost/type_index/type_index_facade.hpp>
75 
76 namespace crow
77 {
78 namespace openbmc_mapper
79 {
80 const constexpr char* notFoundMsg = "404 Not Found";
81 const constexpr char* badReqMsg = "400 Bad Request";
82 const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
83 const constexpr char* forbiddenMsg = "403 Forbidden";
84 const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
85 const constexpr char* methodFailedMsg = "500 Method Call Failed";
86 const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
87 const constexpr char* notFoundDesc =
88     "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
89 const constexpr char* propNotFoundDesc =
90     "The specified property cannot be found";
91 const constexpr char* noJsonDesc = "No JSON object could be decoded";
92 const constexpr char* invalidContentType =
93     "Content-type header is missing or invalid";
94 const constexpr char* methodNotFoundDesc =
95     "The specified method cannot be found";
96 const constexpr char* methodNotAllowedDesc = "Method not allowed";
97 const constexpr char* forbiddenPropDesc =
98     "The specified property cannot be created";
99 const constexpr char* forbiddenResDesc =
100     "The specified resource cannot be created";
101 
102 inline bool validateFilename(const std::string& filename)
103 {
104     static std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
105 
106     return std::regex_match(filename, validFilename);
107 }
108 
109 inline void setErrorResponse(crow::Response& res,
110                              boost::beast::http::status result,
111                              const std::string& desc, std::string_view msg)
112 {
113     res.result(result);
114     res.jsonValue["data"]["description"] = desc;
115     res.jsonValue["message"] = msg;
116     res.jsonValue["status"] = "error";
117 }
118 
119 inline void introspectObjects(
120     const std::string& processName, const std::string& objectPath,
121     const std::shared_ptr<bmcweb::AsyncResp>& transaction)
122 {
123     if (transaction->res.jsonValue.is_null())
124     {
125         transaction->res.jsonValue["status"] = "ok";
126         transaction->res.jsonValue["bus_name"] = processName;
127         transaction->res.jsonValue["objects"] = nlohmann::json::array();
128     }
129 
130     crow::connections::systemBus->async_method_call(
131         [transaction, processName{std::string(processName)},
132          objectPath{std::string(objectPath)}](
133             const boost::system::error_code& ec,
134             const std::string& introspectXml) {
135             if (ec)
136             {
137                 BMCWEB_LOG_ERROR(
138                     "Introspect call failed with error: {} on process: {} path: {}",
139                     ec.message(), processName, objectPath);
140                 return;
141             }
142             nlohmann::json::object_t object;
143             object["path"] = objectPath;
144 
145             transaction->res.jsonValue["objects"].emplace_back(
146                 std::move(object));
147 
148             tinyxml2::XMLDocument doc;
149 
150             doc.Parse(introspectXml.c_str());
151             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
152             if (pRoot == nullptr)
153             {
154                 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
155                                  processName, objectPath);
156             }
157             else
158             {
159                 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
160                 while (node != nullptr)
161                 {
162                     const char* childPath = node->Attribute("name");
163                     if (childPath != nullptr)
164                     {
165                         std::string newpath;
166                         if (objectPath != "/")
167                         {
168                             newpath += objectPath;
169                         }
170                         newpath += std::string("/") + childPath;
171                         // introspect the subobjects as well
172                         introspectObjects(processName, newpath, transaction);
173                     }
174 
175                     node = node->NextSiblingElement("node");
176                 }
177             }
178         },
179         processName, objectPath, "org.freedesktop.DBus.Introspectable",
180         "Introspect");
181 }
182 
183 inline void getPropertiesForEnumerate(
184     const std::string& objectPath, const std::string& service,
185     const std::string& interface,
186     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
187 {
188     BMCWEB_LOG_DEBUG("getPropertiesForEnumerate {} {} {}", objectPath, service,
189                      interface);
190 
191     sdbusplus::asio::getAllProperties(
192         *crow::connections::systemBus, service, objectPath, interface,
193         [asyncResp, objectPath, service,
194          interface](const boost::system::error_code& ec,
195                     const dbus::utility::DBusPropertiesMap& propertiesList) {
196             if (ec)
197             {
198                 BMCWEB_LOG_ERROR(
199                     "GetAll on path {} iface {} service {} failed with code {}",
200                     objectPath, interface, service, ec);
201                 return;
202             }
203 
204             nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
205             nlohmann::json& objectJson = dataJson[objectPath];
206             if (objectJson.is_null())
207             {
208                 objectJson = nlohmann::json::object();
209             }
210 
211             for (const auto& [name, value] : propertiesList)
212             {
213                 nlohmann::json& propertyJson = objectJson[name];
214                 std::visit(
215                     [&propertyJson](auto&& val) {
216                         if constexpr (std::is_same_v<
217                                           std::decay_t<decltype(val)>,
218                                           sdbusplus::message::unix_fd>)
219                         {
220                             propertyJson = val.fd;
221                         }
222                         else
223                         {
224                             propertyJson = val;
225                         }
226                     },
227                     value);
228             }
229         });
230 }
231 
232 // Find any results that weren't picked up by ObjectManagers, to be
233 // called after all ObjectManagers are searched for and called.
234 inline void findRemainingObjectsForEnumerate(
235     const std::string& objectPath,
236     const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
237     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
238 {
239     BMCWEB_LOG_DEBUG("findRemainingObjectsForEnumerate");
240     const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
241 
242     for (const auto& [path, interface_map] : *subtree)
243     {
244         if (path == objectPath)
245         {
246             // An enumerate does not return the target path's properties
247             continue;
248         }
249         if (dataJson.find(path) == dataJson.end())
250         {
251             for (const auto& [service, interfaces] : interface_map)
252             {
253                 for (const auto& interface : interfaces)
254                 {
255                     if (!interface.starts_with("org.freedesktop.DBus"))
256                     {
257                         getPropertiesForEnumerate(path, service, interface,
258                                                   asyncResp);
259                     }
260                 }
261             }
262         }
263     }
264 }
265 
266 struct InProgressEnumerateData
267 {
268     InProgressEnumerateData(
269         const std::string& objectPathIn,
270         const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
271         objectPath(objectPathIn), asyncResp(asyncRespIn)
272     {}
273 
274     ~InProgressEnumerateData()
275     {
276         try
277         {
278             findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
279         }
280         catch (...)
281         {
282             BMCWEB_LOG_CRITICAL(
283                 "findRemainingObjectsForEnumerate threw exception");
284         }
285     }
286 
287     InProgressEnumerateData(const InProgressEnumerateData&) = delete;
288     InProgressEnumerateData(InProgressEnumerateData&&) = delete;
289     InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
290     InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
291     const std::string objectPath;
292     std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
293     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
294 };
295 
296 inline void getManagedObjectsForEnumerate(
297     const std::string& objectName, const std::string& objectManagerPath,
298     const std::string& connectionName,
299     const std::shared_ptr<InProgressEnumerateData>& transaction)
300 {
301     BMCWEB_LOG_DEBUG(
302         "getManagedObjectsForEnumerate {} object_manager_path {} connection_name {}",
303         objectName, objectManagerPath, connectionName);
304     sdbusplus::message::object_path path(objectManagerPath);
305     dbus::utility::getManagedObjects(
306         connectionName, path,
307         [transaction, objectName,
308          connectionName](const boost::system::error_code& ec,
309                          const dbus::utility::ManagedObjectType& objects) {
310             if (ec)
311             {
312                 BMCWEB_LOG_ERROR(
313                     "GetManagedObjects on path {} on connection {} failed with code {}",
314                     objectName, connectionName, ec);
315                 return;
316             }
317 
318             nlohmann::json& dataJson =
319                 transaction->asyncResp->res.jsonValue["data"];
320 
321             for (const auto& objectPath : objects)
322             {
323                 if (objectPath.first.str.starts_with(objectName))
324                 {
325                     BMCWEB_LOG_DEBUG("Reading object {}", objectPath.first.str);
326                     nlohmann::json& objectJson = dataJson[objectPath.first.str];
327                     if (objectJson.is_null())
328                     {
329                         objectJson = nlohmann::json::object();
330                     }
331                     for (const auto& interface : objectPath.second)
332                     {
333                         for (const auto& property : interface.second)
334                         {
335                             nlohmann::json& propertyJson =
336                                 objectJson[property.first];
337                             std::visit(
338                                 [&propertyJson](auto&& val) {
339                                     if constexpr (
340                                         std::is_same_v<
341                                             std::decay_t<decltype(val)>,
342                                             sdbusplus::message::unix_fd>)
343                                     {
344                                         propertyJson = val.fd;
345                                     }
346                                     else
347                                     {
348                                         propertyJson = val;
349                                     }
350                                 },
351                                 property.second);
352                         }
353                     }
354                 }
355                 for (const auto& interface : objectPath.second)
356                 {
357                     if (interface.first == "org.freedesktop.DBus.ObjectManager")
358                     {
359                         getManagedObjectsForEnumerate(
360                             objectPath.first.str, objectPath.first.str,
361                             connectionName, transaction);
362                     }
363                 }
364             }
365         });
366 }
367 
368 inline void findObjectManagerPathForEnumerate(
369     const std::string& objectName, const std::string& connectionName,
370     const std::shared_ptr<InProgressEnumerateData>& transaction)
371 {
372     BMCWEB_LOG_DEBUG("Finding objectmanager for path {} on connection:{}",
373                      objectName, connectionName);
374     crow::connections::systemBus->async_method_call(
375         [transaction, objectName, connectionName](
376             const boost::system::error_code& ec,
377             const dbus::utility::MapperGetAncestorsResponse& objects) {
378             if (ec)
379             {
380                 BMCWEB_LOG_ERROR("GetAncestors on path {} failed with code {}",
381                                  objectName, ec);
382                 return;
383             }
384 
385             for (const auto& pathGroup : objects)
386             {
387                 for (const auto& connectionGroup : pathGroup.second)
388                 {
389                     if (connectionGroup.first == connectionName)
390                     {
391                         // Found the object manager path for this resource.
392                         getManagedObjectsForEnumerate(
393                             objectName, pathGroup.first, connectionName,
394                             transaction);
395                         return;
396                     }
397                 }
398             }
399         },
400         "xyz.openbmc_project.ObjectMapper",
401         "/xyz/openbmc_project/object_mapper",
402         "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
403         std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
404 }
405 
406 // Uses GetObject to add the object info about the target /enumerate path to
407 // the results of GetSubTree, as GetSubTree will not return info for the
408 // target path, and then continues on enumerating the rest of the tree.
409 inline void getObjectAndEnumerate(
410     const std::shared_ptr<InProgressEnumerateData>& transaction)
411 {
412     dbus::utility::getDbusObject(
413         transaction->objectPath, {},
414         [transaction](const boost::system::error_code& ec,
415                       const dbus::utility::MapperGetObject& objects) {
416             if (ec)
417             {
418                 BMCWEB_LOG_ERROR("GetObject for path {} failed with code {}",
419                                  transaction->objectPath, ec);
420                 return;
421             }
422 
423             BMCWEB_LOG_DEBUG("GetObject for {} has {} entries",
424                              transaction->objectPath, objects.size());
425             if (!objects.empty())
426             {
427                 transaction->subtree->emplace_back(transaction->objectPath,
428                                                    objects);
429             }
430 
431             // Map indicating connection name, and the path where the object
432             // manager exists
433             boost::container::flat_map<
434                 std::string, std::string, std::less<>,
435                 std::vector<std::pair<std::string, std::string>>>
436                 connections;
437 
438             for (const auto& object : *(transaction->subtree))
439             {
440                 for (const auto& connection : object.second)
441                 {
442                     for (const auto& interface : connection.second)
443                     {
444                         BMCWEB_LOG_DEBUG("{} has interface {}",
445                                          connection.first, interface);
446                         if (interface == "org.freedesktop.DBus.ObjectManager")
447                         {
448                             BMCWEB_LOG_DEBUG("found object manager path {}",
449                                              object.first);
450                             connections[connection.first] = object.first;
451                         }
452                     }
453                 }
454             }
455             BMCWEB_LOG_DEBUG("Got {} connections", connections.size());
456 
457             for (const auto& connection : connections)
458             {
459                 // If we already know where the object manager is, we don't
460                 // need to search for it, we can call directly in to
461                 // getManagedObjects
462                 if (!connection.second.empty())
463                 {
464                     getManagedObjectsForEnumerate(
465                         transaction->objectPath, connection.second,
466                         connection.first, transaction);
467                 }
468                 else
469                 {
470                     // otherwise we need to find the object manager path
471                     // before we can continue
472                     findObjectManagerPathForEnumerate(
473                         transaction->objectPath, connection.first, transaction);
474                 }
475             }
476         });
477 }
478 
479 // Structure for storing data on an in progress action
480 struct InProgressActionData
481 {
482     explicit InProgressActionData(
483         const std::shared_ptr<bmcweb::AsyncResp>& res) : asyncResp(res)
484     {}
485     ~InProgressActionData()
486     {
487         // Methods could have been called across different owners
488         // and interfaces, where some calls failed and some passed.
489         //
490         // The rules for this are:
491         // * if no method was called - error
492         // * if a method failed and none passed - error
493         //   (converse: if at least one method passed - OK)
494         // * for the method output:
495         //   * if output processing didn't fail, return the data
496 
497         // Only deal with method returns if nothing failed earlier
498         if (asyncResp->res.result() == boost::beast::http::status::ok)
499         {
500             if (!methodPassed)
501             {
502                 if (!methodFailed)
503                 {
504                     setErrorResponse(asyncResp->res,
505                                      boost::beast::http::status::not_found,
506                                      methodNotFoundDesc, notFoundMsg);
507                 }
508             }
509             else
510             {
511                 if (outputFailed)
512                 {
513                     setErrorResponse(
514                         asyncResp->res,
515                         boost::beast::http::status::internal_server_error,
516                         "Method output failure", methodOutputFailedMsg);
517                 }
518                 else
519                 {
520                     asyncResp->res.jsonValue["status"] = "ok";
521                     asyncResp->res.jsonValue["message"] = "200 OK";
522                     asyncResp->res.jsonValue["data"] = methodResponse;
523                 }
524             }
525         }
526     }
527     InProgressActionData(const InProgressActionData&) = delete;
528     InProgressActionData(InProgressActionData&&) = delete;
529     InProgressActionData& operator=(const InProgressActionData&) = delete;
530     InProgressActionData& operator=(InProgressActionData&&) = delete;
531 
532     void setErrorStatus(const std::string& desc)
533     {
534         setErrorResponse(asyncResp->res,
535                          boost::beast::http::status::bad_request, desc,
536                          badReqMsg);
537     }
538     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
539     std::string path;
540     std::string methodName;
541     std::string interfaceName;
542     bool methodPassed = false;
543     bool methodFailed = false;
544     bool outputFailed = false;
545     bool convertedToArray = false;
546     nlohmann::json methodResponse;
547     nlohmann::json arguments;
548 };
549 
550 inline std::vector<std::string> dbusArgSplit(const std::string& string)
551 {
552     std::vector<std::string> ret;
553     if (string.empty())
554     {
555         return ret;
556     }
557     ret.emplace_back("");
558     int containerDepth = 0;
559 
560     for (std::string::const_iterator character = string.begin();
561          character != string.end(); character++)
562     {
563         ret.back() += *character;
564         switch (*character)
565         {
566             case ('a'):
567                 break;
568             case ('('):
569             case ('{'):
570                 containerDepth++;
571                 break;
572             case ('}'):
573             case (')'):
574                 containerDepth--;
575                 if (containerDepth == 0)
576                 {
577                     if (character + 1 != string.end())
578                     {
579                         ret.emplace_back("");
580                     }
581                 }
582                 break;
583             default:
584                 if (containerDepth == 0)
585                 {
586                     if (character + 1 != string.end())
587                     {
588                         ret.emplace_back("");
589                     }
590                 }
591                 break;
592         }
593     }
594 
595     return ret;
596 }
597 
598 inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
599                              const nlohmann::json& inputJson)
600 {
601     int r = 0;
602     BMCWEB_LOG_DEBUG("Converting {} to type: {}", inputJson, argType);
603     const std::vector<std::string> argTypes = dbusArgSplit(argType);
604 
605     // Assume a single object for now.
606     const nlohmann::json* j = &inputJson;
607     nlohmann::json::const_iterator jIt = inputJson.begin();
608 
609     for (const std::string& argCode : argTypes)
610     {
611         // If we are decoding multiple objects, grab the pointer to the
612         // iterator, and increment it for the next loop
613         if (argTypes.size() > 1)
614         {
615             if (jIt == inputJson.end())
616             {
617                 return -2;
618             }
619             j = &*jIt;
620             jIt++;
621         }
622         const int64_t* intValue = j->get_ptr<const int64_t*>();
623         const std::string* stringValue = j->get_ptr<const std::string*>();
624         const double* doubleValue = j->get_ptr<const double*>();
625         const bool* b = j->get_ptr<const bool*>();
626         int64_t v = 0;
627         double d = 0.0;
628 
629         // Do some basic type conversions that make sense.  uint can be
630         // converted to int.  int and uint can be converted to double
631         if (intValue == nullptr)
632         {
633             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
634             if (uintValue != nullptr)
635             {
636                 v = static_cast<int64_t>(*uintValue);
637                 intValue = &v;
638             }
639         }
640         if (doubleValue == nullptr)
641         {
642             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
643             if (uintValue != nullptr)
644             {
645                 d = static_cast<double>(*uintValue);
646                 doubleValue = &d;
647             }
648         }
649         if (doubleValue == nullptr)
650         {
651             if (intValue != nullptr)
652             {
653                 d = static_cast<double>(*intValue);
654                 doubleValue = &d;
655             }
656         }
657 
658         if (argCode == "s")
659         {
660             if (stringValue == nullptr)
661             {
662                 return -1;
663             }
664             r = sd_bus_message_append_basic(
665                 m, argCode[0], static_cast<const void*>(stringValue->data()));
666             if (r < 0)
667             {
668                 return r;
669             }
670         }
671         else if (argCode == "i")
672         {
673             if (intValue == nullptr)
674             {
675                 return -1;
676             }
677             if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
678                 (*intValue > std::numeric_limits<int32_t>::max()))
679             {
680                 return -ERANGE;
681             }
682             int32_t i = static_cast<int32_t>(*intValue);
683             r = sd_bus_message_append_basic(m, argCode[0], &i);
684             if (r < 0)
685             {
686                 return r;
687             }
688         }
689         else if (argCode == "b")
690         {
691             // lots of ways bool could be represented here.  Try them all
692             int boolInt = 0;
693             if (intValue != nullptr)
694             {
695                 if (*intValue == 1)
696                 {
697                     boolInt = 1;
698                 }
699                 else if (*intValue == 0)
700                 {
701                     boolInt = 0;
702                 }
703                 else
704                 {
705                     return -ERANGE;
706                 }
707             }
708             else if (b != nullptr)
709             {
710                 boolInt = *b ? 1 : 0;
711             }
712             else if (stringValue != nullptr)
713             {
714                 if (!stringValue->empty())
715                 {
716                     if (stringValue->front() == 't' ||
717                         stringValue->front() == 'T')
718                     {
719                         boolInt = 1;
720                     }
721                 }
722             }
723             else
724             {
725                 return -1;
726             }
727             r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
728             if (r < 0)
729             {
730                 return r;
731             }
732         }
733         else if (argCode == "n")
734         {
735             if (intValue == nullptr)
736             {
737                 return -1;
738             }
739             if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
740                 (*intValue > std::numeric_limits<int16_t>::max()))
741             {
742                 return -ERANGE;
743             }
744             int16_t n = static_cast<int16_t>(*intValue);
745             r = sd_bus_message_append_basic(m, argCode[0], &n);
746             if (r < 0)
747             {
748                 return r;
749             }
750         }
751         else if (argCode == "x")
752         {
753             if (intValue == nullptr)
754             {
755                 return -1;
756             }
757             r = sd_bus_message_append_basic(m, argCode[0], intValue);
758             if (r < 0)
759             {
760                 return r;
761             }
762         }
763         else if (argCode == "y")
764         {
765             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
766             if (uintValue == nullptr)
767             {
768                 return -1;
769             }
770             if (*uintValue > std::numeric_limits<uint8_t>::max())
771             {
772                 return -ERANGE;
773             }
774             uint8_t y = static_cast<uint8_t>(*uintValue);
775             r = sd_bus_message_append_basic(m, argCode[0], &y);
776         }
777         else if (argCode == "q")
778         {
779             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
780             if (uintValue == nullptr)
781             {
782                 return -1;
783             }
784             if (*uintValue > std::numeric_limits<uint16_t>::max())
785             {
786                 return -ERANGE;
787             }
788             uint16_t q = static_cast<uint16_t>(*uintValue);
789             r = sd_bus_message_append_basic(m, argCode[0], &q);
790         }
791         else if (argCode == "u")
792         {
793             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
794             if (uintValue == nullptr)
795             {
796                 return -1;
797             }
798             if (*uintValue > std::numeric_limits<uint32_t>::max())
799             {
800                 return -ERANGE;
801             }
802             uint32_t u = static_cast<uint32_t>(*uintValue);
803             r = sd_bus_message_append_basic(m, argCode[0], &u);
804         }
805         else if (argCode == "t")
806         {
807             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
808             if (uintValue == nullptr)
809             {
810                 return -1;
811             }
812             r = sd_bus_message_append_basic(m, argCode[0], uintValue);
813         }
814         else if (argCode == "d")
815         {
816             if (doubleValue == nullptr)
817             {
818                 return -1;
819             }
820             if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
821                 (*doubleValue > std::numeric_limits<double>::max()))
822             {
823                 return -ERANGE;
824             }
825             r = sd_bus_message_append_basic(m, argCode[0], doubleValue);
826             if (r < 0)
827             {
828                 return r;
829             }
830         }
831         else if (argCode.starts_with("a"))
832         {
833             std::string containedType = argCode.substr(1);
834             r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
835                                               containedType.c_str());
836             if (r < 0)
837             {
838                 return r;
839             }
840 
841             for (const auto& it : *j)
842             {
843                 r = convertJsonToDbus(m, containedType, it);
844                 if (r < 0)
845                 {
846                     return r;
847                 }
848             }
849             sd_bus_message_close_container(m);
850         }
851         else if (argCode.starts_with("v"))
852         {
853             std::string containedType = argCode.substr(1);
854             BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}",
855                              argCode, containedType);
856             r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
857                                               containedType.c_str());
858             if (r < 0)
859             {
860                 return r;
861             }
862 
863             r = convertJsonToDbus(m, containedType, inputJson);
864             if (r < 0)
865             {
866                 return r;
867             }
868 
869             r = sd_bus_message_close_container(m);
870             if (r < 0)
871             {
872                 return r;
873             }
874         }
875         else if (argCode.starts_with("(") && argCode.ends_with(")"))
876         {
877             std::string containedType = argCode.substr(1, argCode.size() - 2);
878             r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
879                                               containedType.c_str());
880             if (r < 0)
881             {
882                 return r;
883             }
884 
885             nlohmann::json::const_iterator it = j->begin();
886             for (const std::string& argCode2 : dbusArgSplit(containedType))
887             {
888                 if (it == j->end())
889                 {
890                     return -1;
891                 }
892                 r = convertJsonToDbus(m, argCode2, *it);
893                 if (r < 0)
894                 {
895                     return r;
896                 }
897                 it++;
898             }
899             r = sd_bus_message_close_container(m);
900         }
901         else if (argCode.starts_with("{") && argCode.ends_with("}"))
902         {
903             std::string containedType = argCode.substr(1, argCode.size() - 2);
904             r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
905                                               containedType.c_str());
906             if (r < 0)
907             {
908                 return r;
909             }
910 
911             std::vector<std::string> codes = dbusArgSplit(containedType);
912             if (codes.size() != 2)
913             {
914                 return -1;
915             }
916             const std::string& keyType = codes[0];
917             const std::string& valueType = codes[1];
918             const nlohmann::json::object_t* arr =
919                 j->get_ptr<const nlohmann::json::object_t*>();
920             if (arr == nullptr)
921             {
922                 return -1;
923             }
924             for (const auto& it : *arr)
925             {
926                 r = convertJsonToDbus(m, keyType, it.first);
927                 if (r < 0)
928                 {
929                     return r;
930                 }
931 
932                 r = convertJsonToDbus(m, valueType, it.second);
933                 if (r < 0)
934                 {
935                     return r;
936                 }
937             }
938             r = sd_bus_message_close_container(m);
939         }
940         else
941         {
942             return -2;
943         }
944         if (r < 0)
945         {
946             return r;
947         }
948 
949         if (argTypes.size() > 1)
950         {
951             jIt++;
952         }
953     }
954 
955     return r;
956 }
957 
958 template <typename T>
959 int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m,
960                     nlohmann::json& data)
961 {
962     T value;
963     // When T == char*, this warning fires.  Unclear how to resolve
964     // Given that sd-bus takes a void pointer to a char*, and that's
965     // Not something we can fix.
966     // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
967     int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
968     if (r < 0)
969     {
970         BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!",
971                          typeCode);
972         return r;
973     }
974 
975     data = value;
976     return 0;
977 }
978 
979 int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m,
980                       nlohmann::json& response);
981 
982 inline int readDictEntryFromMessage(const std::string& typeCode,
983                                     sdbusplus::message_t& m,
984                                     nlohmann::json& object)
985 {
986     std::vector<std::string> types = dbusArgSplit(typeCode);
987     if (types.size() != 2)
988     {
989         BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}",
990                          types.size());
991         return -1;
992     }
993 
994     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
995                                            typeCode.c_str());
996     if (r < 0)
997     {
998         BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r);
999         return r;
1000     }
1001 
1002     nlohmann::json key;
1003     r = convertDBusToJSON(types[0], m, key);
1004     if (r < 0)
1005     {
1006         return r;
1007     }
1008 
1009     const std::string* keyPtr = key.get_ptr<const std::string*>();
1010     if (keyPtr == nullptr)
1011     {
1012         // json doesn't support non-string keys.  If we hit this condition,
1013         // convert the result to a string so we can proceed
1014         key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
1015         keyPtr = key.get_ptr<const std::string*>();
1016         // in theory this can't fail now, but lets be paranoid about it
1017         // anyway
1018         if (keyPtr == nullptr)
1019         {
1020             return -1;
1021         }
1022     }
1023     nlohmann::json& value = object[*keyPtr];
1024 
1025     r = convertDBusToJSON(types[1], m, value);
1026     if (r < 0)
1027     {
1028         return r;
1029     }
1030 
1031     r = sd_bus_message_exit_container(m.get());
1032     if (r < 0)
1033     {
1034         BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
1035         return r;
1036     }
1037 
1038     return 0;
1039 }
1040 
1041 inline int readArrayFromMessage(const std::string& typeCode,
1042                                 sdbusplus::message_t& m, nlohmann::json& data)
1043 {
1044     if (typeCode.size() < 2)
1045     {
1046         BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode);
1047         return -1;
1048     }
1049 
1050     std::string containedType = typeCode.substr(1);
1051 
1052     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
1053                                            containedType.c_str());
1054     if (r < 0)
1055     {
1056         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
1057         return r;
1058     }
1059 
1060     bool dict = containedType.starts_with("{") && containedType.ends_with("}");
1061 
1062     if (dict)
1063     {
1064         // Remove the { }
1065         containedType = containedType.substr(1, containedType.size() - 2);
1066         data = nlohmann::json::object();
1067     }
1068     else
1069     {
1070         data = nlohmann::json::array();
1071     }
1072 
1073     while (true)
1074     {
1075         r = sd_bus_message_at_end(m.get(), 0);
1076         if (r < 0)
1077         {
1078             BMCWEB_LOG_ERROR("sd_bus_message_at_end failed");
1079             return r;
1080         }
1081 
1082         if (r > 0)
1083         {
1084             break;
1085         }
1086 
1087         // Dictionaries are only ever seen in an array
1088         if (dict)
1089         {
1090             r = readDictEntryFromMessage(containedType, m, data);
1091             if (r < 0)
1092             {
1093                 return r;
1094             }
1095         }
1096         else
1097         {
1098             data.push_back(nlohmann::json());
1099 
1100             r = convertDBusToJSON(containedType, m, data.back());
1101             if (r < 0)
1102             {
1103                 return r;
1104             }
1105         }
1106     }
1107 
1108     r = sd_bus_message_exit_container(m.get());
1109     if (r < 0)
1110     {
1111         BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
1112         return r;
1113     }
1114 
1115     return 0;
1116 }
1117 
1118 inline int readStructFromMessage(const std::string& typeCode,
1119                                  sdbusplus::message_t& m, nlohmann::json& data)
1120 {
1121     if (typeCode.size() < 3)
1122     {
1123         BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode);
1124         return -1;
1125     }
1126 
1127     std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1128     std::vector<std::string> types = dbusArgSplit(containedTypes);
1129 
1130     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1131                                            containedTypes.c_str());
1132     if (r < 0)
1133     {
1134         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
1135         return r;
1136     }
1137 
1138     for (const std::string& type : types)
1139     {
1140         data.push_back(nlohmann::json());
1141         r = convertDBusToJSON(type, m, data.back());
1142         if (r < 0)
1143         {
1144             return r;
1145         }
1146     }
1147 
1148     r = sd_bus_message_exit_container(m.get());
1149     if (r < 0)
1150     {
1151         BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
1152         return r;
1153     }
1154     return 0;
1155 }
1156 
1157 inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data)
1158 {
1159     const char* containerType = nullptr;
1160     int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
1161     if (r < 0)
1162     {
1163         BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed");
1164         return r;
1165     }
1166 
1167     r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1168                                        containerType);
1169     if (r < 0)
1170     {
1171         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
1172         return r;
1173     }
1174 
1175     r = convertDBusToJSON(containerType, m, data);
1176     if (r < 0)
1177     {
1178         return r;
1179     }
1180 
1181     r = sd_bus_message_exit_container(m.get());
1182     if (r < 0)
1183     {
1184         BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed");
1185         return r;
1186     }
1187 
1188     return 0;
1189 }
1190 
1191 inline int convertDBusToJSON(const std::string& returnType,
1192                              sdbusplus::message_t& m, nlohmann::json& response)
1193 {
1194     int r = 0;
1195     const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1196 
1197     for (const std::string& typeCode : returnTypes)
1198     {
1199         nlohmann::json* thisElement = &response;
1200         if (returnTypes.size() > 1)
1201         {
1202             response.push_back(nlohmann::json{});
1203             thisElement = &response.back();
1204         }
1205 
1206         if (typeCode == "s" || typeCode == "g" || typeCode == "o")
1207         {
1208             r = readMessageItem<char*>(typeCode, m, *thisElement);
1209             if (r < 0)
1210             {
1211                 return r;
1212             }
1213         }
1214         else if (typeCode == "b")
1215         {
1216             r = readMessageItem<int>(typeCode, m, *thisElement);
1217             if (r < 0)
1218             {
1219                 return r;
1220             }
1221 
1222             *thisElement = static_cast<bool>(thisElement->get<int>());
1223         }
1224         else if (typeCode == "u")
1225         {
1226             r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
1227             if (r < 0)
1228             {
1229                 return r;
1230             }
1231         }
1232         else if (typeCode == "i")
1233         {
1234             r = readMessageItem<int32_t>(typeCode, m, *thisElement);
1235             if (r < 0)
1236             {
1237                 return r;
1238             }
1239         }
1240         else if (typeCode == "x")
1241         {
1242             r = readMessageItem<int64_t>(typeCode, m, *thisElement);
1243             if (r < 0)
1244             {
1245                 return r;
1246             }
1247         }
1248         else if (typeCode == "t")
1249         {
1250             r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
1251             if (r < 0)
1252             {
1253                 return r;
1254             }
1255         }
1256         else if (typeCode == "n")
1257         {
1258             r = readMessageItem<int16_t>(typeCode, m, *thisElement);
1259             if (r < 0)
1260             {
1261                 return r;
1262             }
1263         }
1264         else if (typeCode == "q")
1265         {
1266             r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
1267             if (r < 0)
1268             {
1269                 return r;
1270             }
1271         }
1272         else if (typeCode == "y")
1273         {
1274             r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
1275             if (r < 0)
1276             {
1277                 return r;
1278             }
1279         }
1280         else if (typeCode == "d")
1281         {
1282             r = readMessageItem<double>(typeCode, m, *thisElement);
1283             if (r < 0)
1284             {
1285                 return r;
1286             }
1287         }
1288         else if (typeCode == "h")
1289         {
1290             r = readMessageItem<int>(typeCode, m, *thisElement);
1291             if (r < 0)
1292             {
1293                 return r;
1294             }
1295         }
1296         else if (typeCode.starts_with("a"))
1297         {
1298             r = readArrayFromMessage(typeCode, m, *thisElement);
1299             if (r < 0)
1300             {
1301                 return r;
1302             }
1303         }
1304         else if (typeCode.starts_with("(") && typeCode.ends_with(")"))
1305         {
1306             r = readStructFromMessage(typeCode, m, *thisElement);
1307             if (r < 0)
1308             {
1309                 return r;
1310             }
1311         }
1312         else if (typeCode.starts_with("v"))
1313         {
1314             r = readVariantFromMessage(m, *thisElement);
1315             if (r < 0)
1316             {
1317                 return r;
1318             }
1319         }
1320         else
1321         {
1322             BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode);
1323             return -2;
1324         }
1325     }
1326 
1327     return 0;
1328 }
1329 
1330 inline void handleMethodResponse(
1331     const std::shared_ptr<InProgressActionData>& transaction,
1332     sdbusplus::message_t& m, const std::string& returnType)
1333 {
1334     nlohmann::json data;
1335 
1336     int r = convertDBusToJSON(returnType, m, data);
1337     if (r < 0)
1338     {
1339         transaction->outputFailed = true;
1340         return;
1341     }
1342 
1343     if (data.is_null())
1344     {
1345         return;
1346     }
1347 
1348     if (transaction->methodResponse.is_null())
1349     {
1350         transaction->methodResponse = std::move(data);
1351         return;
1352     }
1353 
1354     // If they're both dictionaries or arrays, merge into one.
1355     // Otherwise, make the results an array with every result
1356     // an entry.  Could also just fail in that case, but it
1357     // seems better to get the data back somehow.
1358     nlohmann::json::object_t* dataobj =
1359         data.get_ptr<nlohmann::json::object_t*>();
1360     if (transaction->methodResponse.is_object() && dataobj != nullptr)
1361     {
1362         for (auto& obj : *dataobj)
1363         {
1364             // Note: Will overwrite the data for a duplicate key
1365             transaction->methodResponse.emplace(obj.first,
1366                                                 std::move(obj.second));
1367         }
1368         return;
1369     }
1370 
1371     nlohmann::json::array_t* dataarr = data.get_ptr<nlohmann::json::array_t*>();
1372     if (transaction->methodResponse.is_array() && dataarr != nullptr)
1373     {
1374         for (auto& obj : *dataarr)
1375         {
1376             transaction->methodResponse.emplace_back(std::move(obj));
1377         }
1378         return;
1379     }
1380 
1381     if (!transaction->convertedToArray)
1382     {
1383         // They are different types. May as well turn them into an array
1384         nlohmann::json j = std::move(transaction->methodResponse);
1385         transaction->methodResponse = nlohmann::json::array();
1386         transaction->methodResponse.emplace_back(std::move(j));
1387         transaction->methodResponse.emplace_back(std::move(data));
1388         transaction->convertedToArray = true;
1389     }
1390     else
1391     {
1392         transaction->methodResponse.emplace_back(std::move(data));
1393     }
1394 }
1395 
1396 inline void findActionOnInterface(
1397     const std::shared_ptr<InProgressActionData>& transaction,
1398     const std::string& connectionName)
1399 {
1400     BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName);
1401     crow::connections::systemBus->async_method_call(
1402         [transaction, connectionName{std::string(connectionName)}](
1403             const boost::system::error_code& ec,
1404             const std::string& introspectXml) {
1405             BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml);
1406             if (ec)
1407             {
1408                 BMCWEB_LOG_ERROR(
1409                     "Introspect call failed with error: {} on process: {}",
1410                     ec.message(), connectionName);
1411                 return;
1412             }
1413             tinyxml2::XMLDocument doc;
1414 
1415             doc.Parse(introspectXml.data(), introspectXml.size());
1416             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1417             if (pRoot == nullptr)
1418             {
1419                 BMCWEB_LOG_ERROR("XML document failed to parse {}",
1420                                  connectionName);
1421                 return;
1422             }
1423             tinyxml2::XMLElement* interfaceNode =
1424                 pRoot->FirstChildElement("interface");
1425             while (interfaceNode != nullptr)
1426             {
1427                 const char* thisInterfaceName =
1428                     interfaceNode->Attribute("name");
1429                 if (thisInterfaceName != nullptr)
1430                 {
1431                     if (!transaction->interfaceName.empty() &&
1432                         (transaction->interfaceName != thisInterfaceName))
1433                     {
1434                         interfaceNode =
1435                             interfaceNode->NextSiblingElement("interface");
1436                         continue;
1437                     }
1438 
1439                     tinyxml2::XMLElement* methodNode =
1440                         interfaceNode->FirstChildElement("method");
1441                     while (methodNode != nullptr)
1442                     {
1443                         const char* thisMethodName =
1444                             methodNode->Attribute("name");
1445                         BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName);
1446                         if (thisMethodName != nullptr &&
1447                             thisMethodName == transaction->methodName)
1448                         {
1449                             BMCWEB_LOG_DEBUG(
1450                                 "Found method named {} on interface {}",
1451                                 thisMethodName, thisInterfaceName);
1452                             sdbusplus::message_t m =
1453                                 crow::connections::systemBus->new_method_call(
1454                                     connectionName.c_str(),
1455                                     transaction->path.c_str(),
1456                                     thisInterfaceName,
1457                                     transaction->methodName.c_str());
1458 
1459                             tinyxml2::XMLElement* argumentNode =
1460                                 methodNode->FirstChildElement("arg");
1461 
1462                             std::string returnType;
1463 
1464                             // Find the output type
1465                             while (argumentNode != nullptr)
1466                             {
1467                                 const char* argDirection =
1468                                     argumentNode->Attribute("direction");
1469                                 const char* argType =
1470                                     argumentNode->Attribute("type");
1471                                 if (argDirection != nullptr &&
1472                                     argType != nullptr &&
1473                                     std::string(argDirection) == "out")
1474                                 {
1475                                     returnType = argType;
1476                                     break;
1477                                 }
1478                                 argumentNode =
1479                                     argumentNode->NextSiblingElement("arg");
1480                             }
1481 
1482                             auto argIt = transaction->arguments.begin();
1483 
1484                             argumentNode = methodNode->FirstChildElement("arg");
1485 
1486                             while (argumentNode != nullptr)
1487                             {
1488                                 const char* argDirection =
1489                                     argumentNode->Attribute("direction");
1490                                 const char* argType =
1491                                     argumentNode->Attribute("type");
1492                                 if (argDirection != nullptr &&
1493                                     argType != nullptr &&
1494                                     std::string(argDirection) == "in")
1495                                 {
1496                                     if (argIt == transaction->arguments.end())
1497                                     {
1498                                         transaction->setErrorStatus(
1499                                             "Invalid method args");
1500                                         return;
1501                                     }
1502                                     if (convertJsonToDbus(m.get(),
1503                                                           std::string(argType),
1504                                                           *argIt) < 0)
1505                                     {
1506                                         transaction->setErrorStatus(
1507                                             "Invalid method arg type");
1508                                         return;
1509                                     }
1510 
1511                                     argIt++;
1512                                 }
1513                                 argumentNode =
1514                                     argumentNode->NextSiblingElement("arg");
1515                             }
1516 
1517                             crow::connections::systemBus->async_send(
1518                                 m, [transaction, returnType](
1519                                        const boost::system::error_code& ec2,
1520                                        sdbusplus::message_t& m2) {
1521                                     if (ec2)
1522                                     {
1523                                         transaction->methodFailed = true;
1524                                         const sd_bus_error* e = m2.get_error();
1525 
1526                                         if (e != nullptr)
1527                                         {
1528                                             setErrorResponse(
1529                                                 transaction->asyncResp->res,
1530                                                 boost::beast::http::status::
1531                                                     bad_request,
1532                                                 e->name, e->message);
1533                                         }
1534                                         else
1535                                         {
1536                                             setErrorResponse(
1537                                                 transaction->asyncResp->res,
1538                                                 boost::beast::http::status::
1539                                                     bad_request,
1540                                                 "Method call failed",
1541                                                 methodFailedMsg);
1542                                         }
1543                                         return;
1544                                     }
1545                                     transaction->methodPassed = true;
1546 
1547                                     handleMethodResponse(transaction, m2,
1548                                                          returnType);
1549                                 });
1550                             break;
1551                         }
1552                         methodNode = methodNode->NextSiblingElement("method");
1553                     }
1554                 }
1555                 interfaceNode = interfaceNode->NextSiblingElement("interface");
1556             }
1557         },
1558         connectionName, transaction->path,
1559         "org.freedesktop.DBus.Introspectable", "Introspect");
1560 }
1561 
1562 inline void handleAction(const crow::Request& req,
1563                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1564                          const std::string& objectPath,
1565                          const std::string& methodName)
1566 {
1567     BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath,
1568                      methodName);
1569     nlohmann::json requestDbusData;
1570 
1571     JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1572     if (ret == JsonParseResult::BadContentType)
1573     {
1574         setErrorResponse(asyncResp->res,
1575                          boost::beast::http::status::unsupported_media_type,
1576                          invalidContentType, unsupportedMediaMsg);
1577         return;
1578     }
1579     if (ret != JsonParseResult::Success)
1580     {
1581         setErrorResponse(asyncResp->res,
1582                          boost::beast::http::status::bad_request, noJsonDesc,
1583                          badReqMsg);
1584         return;
1585     }
1586     nlohmann::json::iterator data = requestDbusData.find("data");
1587     if (data == requestDbusData.end())
1588     {
1589         setErrorResponse(asyncResp->res,
1590                          boost::beast::http::status::bad_request, noJsonDesc,
1591                          badReqMsg);
1592         return;
1593     }
1594 
1595     if (!data->is_array())
1596     {
1597         setErrorResponse(asyncResp->res,
1598                          boost::beast::http::status::bad_request, noJsonDesc,
1599                          badReqMsg);
1600         return;
1601     }
1602     auto transaction = std::make_shared<InProgressActionData>(asyncResp);
1603 
1604     transaction->path = objectPath;
1605     transaction->methodName = methodName;
1606     transaction->arguments = std::move(*data);
1607     dbus::utility::getDbusObject(
1608         objectPath, {},
1609         [transaction](
1610             const boost::system::error_code& ec,
1611             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1612                 interfaceNames) {
1613             if (ec || interfaceNames.empty())
1614             {
1615                 BMCWEB_LOG_ERROR("Can't find object");
1616                 setErrorResponse(transaction->asyncResp->res,
1617                                  boost::beast::http::status::not_found,
1618                                  notFoundDesc, notFoundMsg);
1619                 return;
1620             }
1621 
1622             BMCWEB_LOG_DEBUG("GetObject returned {} object(s)",
1623                              interfaceNames.size());
1624 
1625             for (const std::pair<std::string, std::vector<std::string>>&
1626                      object : interfaceNames)
1627             {
1628                 findActionOnInterface(transaction, object.first);
1629             }
1630         });
1631 }
1632 
1633 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1634                          const std::string& objectPath)
1635 {
1636     BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath);
1637 
1638     dbus::utility::getDbusObject(
1639         objectPath, {},
1640         [asyncResp, objectPath](
1641             const boost::system::error_code& ec,
1642             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1643                 interfaceNames) {
1644             if (ec || interfaceNames.empty())
1645             {
1646                 BMCWEB_LOG_ERROR("Can't find object");
1647                 setErrorResponse(asyncResp->res,
1648                                  boost::beast::http::status::method_not_allowed,
1649                                  methodNotAllowedDesc, methodNotAllowedMsg);
1650                 return;
1651             }
1652 
1653             auto transaction =
1654                 std::make_shared<InProgressActionData>(asyncResp);
1655             transaction->path = objectPath;
1656             transaction->methodName = "Delete";
1657             transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1658 
1659             for (const std::pair<std::string, std::vector<std::string>>&
1660                      object : interfaceNames)
1661             {
1662                 findActionOnInterface(transaction, object.first);
1663             }
1664         });
1665 }
1666 
1667 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1668                        const std::string& objectPath, int32_t depth = 0)
1669 {
1670     dbus::utility::getSubTreePaths(
1671         objectPath, depth, {},
1672         [asyncResp](
1673             const boost::system::error_code& ec,
1674             const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
1675             if (ec)
1676             {
1677                 setErrorResponse(asyncResp->res,
1678                                  boost::beast::http::status::not_found,
1679                                  notFoundDesc, notFoundMsg);
1680             }
1681             else
1682             {
1683                 asyncResp->res.jsonValue["status"] = "ok";
1684                 asyncResp->res.jsonValue["message"] = "200 OK";
1685                 asyncResp->res.jsonValue["data"] = objectPaths;
1686             }
1687         });
1688 }
1689 
1690 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1691                             const std::string& objectPath)
1692 {
1693     BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath);
1694 
1695     asyncResp->res.jsonValue["message"] = "200 OK";
1696     asyncResp->res.jsonValue["status"] = "ok";
1697     asyncResp->res.jsonValue["data"] = nlohmann::json::object();
1698 
1699     dbus::utility::getSubTree(
1700         objectPath, 0, {},
1701         [objectPath, asyncResp](
1702             const boost::system::error_code& ec,
1703             const dbus::utility::MapperGetSubTreeResponse& objectNames) {
1704             auto transaction = std::make_shared<InProgressEnumerateData>(
1705                 objectPath, asyncResp);
1706 
1707             transaction->subtree =
1708                 std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1709                     objectNames);
1710 
1711             if (ec)
1712             {
1713                 BMCWEB_LOG_ERROR("GetSubTree failed on {}",
1714                                  transaction->objectPath);
1715                 setErrorResponse(transaction->asyncResp->res,
1716                                  boost::beast::http::status::not_found,
1717                                  notFoundDesc, notFoundMsg);
1718                 return;
1719             }
1720 
1721             // Add the data for the path passed in to the results
1722             // as if GetSubTree returned it, and continue on enumerating
1723             getObjectAndEnumerate(transaction);
1724         });
1725 }
1726 
1727 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1728                       std::string& objectPath, std::string& destProperty)
1729 {
1730     BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty);
1731     std::shared_ptr<std::string> propertyName =
1732         std::make_shared<std::string>(std::move(destProperty));
1733 
1734     std::shared_ptr<std::string> path =
1735         std::make_shared<std::string>(std::move(objectPath));
1736 
1737     dbus::utility::getDbusObject(
1738         *path, {},
1739         [asyncResp, path,
1740          propertyName](const boost::system::error_code& ec,
1741                        const dbus::utility::MapperGetObject& objectNames) {
1742             if (ec || objectNames.empty())
1743             {
1744                 setErrorResponse(asyncResp->res,
1745                                  boost::beast::http::status::not_found,
1746                                  notFoundDesc, notFoundMsg);
1747                 return;
1748             }
1749             std::shared_ptr<nlohmann::json> response =
1750                 std::make_shared<nlohmann::json>(nlohmann::json::object());
1751             // The mapper should never give us an empty interface names
1752             // list, but check anyway
1753             for (const std::pair<std::string, std::vector<std::string>>&
1754                      connection : objectNames)
1755             {
1756                 const std::vector<std::string>& interfaceNames =
1757                     connection.second;
1758 
1759                 if (interfaceNames.empty())
1760                 {
1761                     setErrorResponse(asyncResp->res,
1762                                      boost::beast::http::status::not_found,
1763                                      notFoundDesc, notFoundMsg);
1764                     return;
1765                 }
1766 
1767                 for (const std::string& interface : interfaceNames)
1768                 {
1769                     sdbusplus::message_t m =
1770                         crow::connections::systemBus->new_method_call(
1771                             connection.first.c_str(), path->c_str(),
1772                             "org.freedesktop.DBus.Properties", "GetAll");
1773                     m.append(interface);
1774                     crow::connections::systemBus->async_send(
1775                         m, [asyncResp, response,
1776                             propertyName](const boost::system::error_code& ec2,
1777                                           sdbusplus::message_t& msg) {
1778                             if (ec2)
1779                             {
1780                                 BMCWEB_LOG_ERROR("Bad dbus request error: {}",
1781                                                  ec2);
1782                             }
1783                             else
1784                             {
1785                                 nlohmann::json properties;
1786                                 int r =
1787                                     convertDBusToJSON("a{sv}", msg, properties);
1788                                 if (r < 0)
1789                                 {
1790                                     BMCWEB_LOG_ERROR(
1791                                         "convertDBusToJSON failed");
1792                                 }
1793                                 else
1794                                 {
1795                                     nlohmann::json::object_t* obj =
1796                                         properties.get_ptr<
1797                                             nlohmann::json::object_t*>();
1798                                     if (obj == nullptr)
1799                                     {
1800                                         return;
1801                                     }
1802                                     for (auto& prop : *obj)
1803                                     {
1804                                         // if property name is empty, or
1805                                         // matches our search query, add it
1806                                         // to the response json
1807 
1808                                         if (propertyName->empty())
1809                                         {
1810                                             (*response)[prop.first] =
1811                                                 std::move(prop.second);
1812                                         }
1813                                         else if (prop.first == *propertyName)
1814                                         {
1815                                             *response = std::move(prop.second);
1816                                         }
1817                                     }
1818                                 }
1819                             }
1820                             if (response.use_count() == 1)
1821                             {
1822                                 if (!propertyName->empty() && response->empty())
1823                                 {
1824                                     setErrorResponse(
1825                                         asyncResp->res,
1826                                         boost::beast::http::status::not_found,
1827                                         propNotFoundDesc, notFoundMsg);
1828                                 }
1829                                 else
1830                                 {
1831                                     asyncResp->res.jsonValue["status"] = "ok";
1832                                     asyncResp->res.jsonValue["message"] =
1833                                         "200 OK";
1834                                     asyncResp->res.jsonValue["data"] =
1835                                         *response;
1836                                 }
1837                             }
1838                         });
1839                 }
1840             }
1841         });
1842 }
1843 
1844 struct AsyncPutRequest
1845 {
1846     explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
1847         asyncResp(resIn)
1848     {}
1849     ~AsyncPutRequest()
1850     {
1851         if (asyncResp->res.jsonValue.empty())
1852         {
1853             setErrorResponse(asyncResp->res,
1854                              boost::beast::http::status::forbidden,
1855                              forbiddenMsg, forbiddenPropDesc);
1856         }
1857     }
1858 
1859     AsyncPutRequest(const AsyncPutRequest&) = delete;
1860     AsyncPutRequest(AsyncPutRequest&&) = delete;
1861     AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1862     AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1863 
1864     void setErrorStatus(const std::string& desc)
1865     {
1866         setErrorResponse(asyncResp->res,
1867                          boost::beast::http::status::internal_server_error,
1868                          desc, badReqMsg);
1869     }
1870 
1871     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1872     std::string objectPath;
1873     std::string propertyName;
1874     nlohmann::json propertyValue;
1875 };
1876 
1877 inline void handlePut(const crow::Request& req,
1878                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1879                       const std::string& objectPath,
1880                       const std::string& destProperty)
1881 {
1882     if (destProperty.empty())
1883     {
1884         setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
1885                          forbiddenResDesc, forbiddenMsg);
1886         return;
1887     }
1888     nlohmann::json requestDbusData;
1889 
1890     JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1891     if (ret == JsonParseResult::BadContentType)
1892     {
1893         setErrorResponse(asyncResp->res,
1894                          boost::beast::http::status::unsupported_media_type,
1895                          invalidContentType, unsupportedMediaMsg);
1896         return;
1897     }
1898 
1899     if (ret != JsonParseResult::Success)
1900     {
1901         setErrorResponse(asyncResp->res,
1902                          boost::beast::http::status::bad_request, noJsonDesc,
1903                          badReqMsg);
1904         return;
1905     }
1906 
1907     auto propertyIt = requestDbusData.find("data");
1908     if (propertyIt == requestDbusData.end())
1909     {
1910         setErrorResponse(asyncResp->res,
1911                          boost::beast::http::status::bad_request, noJsonDesc,
1912                          badReqMsg);
1913         return;
1914     }
1915     const nlohmann::json& propertySetValue = *propertyIt;
1916     auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
1917     transaction->objectPath = objectPath;
1918     transaction->propertyName = destProperty;
1919     transaction->propertyValue = propertySetValue;
1920 
1921     dbus::utility::getDbusObject(
1922         transaction->objectPath, {},
1923         [transaction](const boost::system::error_code& ec2,
1924                       const dbus::utility::MapperGetObject& objectNames) {
1925             if (!ec2 && objectNames.empty())
1926             {
1927                 setErrorResponse(transaction->asyncResp->res,
1928                                  boost::beast::http::status::not_found,
1929                                  propNotFoundDesc, notFoundMsg);
1930                 return;
1931             }
1932 
1933             for (const std::pair<std::string, std::vector<std::string>>&
1934                      connection : objectNames)
1935             {
1936                 const std::string& connectionName = connection.first;
1937 
1938                 crow::connections::systemBus->async_method_call(
1939                     [connectionName{std::string(connectionName)},
1940                      transaction](const boost::system::error_code& ec3,
1941                                   const std::string& introspectXml) {
1942                         if (ec3)
1943                         {
1944                             BMCWEB_LOG_ERROR(
1945                                 "Introspect call failed with error: {} on process: {}",
1946                                 ec3.message(), connectionName);
1947                             transaction->setErrorStatus("Unexpected Error");
1948                             return;
1949                         }
1950                         tinyxml2::XMLDocument doc;
1951 
1952                         doc.Parse(introspectXml.c_str());
1953                         tinyxml2::XMLNode* pRoot =
1954                             doc.FirstChildElement("node");
1955                         if (pRoot == nullptr)
1956                         {
1957                             BMCWEB_LOG_ERROR("XML document failed to parse: {}",
1958                                              introspectXml);
1959                             transaction->setErrorStatus("Unexpected Error");
1960                             return;
1961                         }
1962                         tinyxml2::XMLElement* ifaceNode =
1963                             pRoot->FirstChildElement("interface");
1964                         while (ifaceNode != nullptr)
1965                         {
1966                             const char* interfaceName =
1967                                 ifaceNode->Attribute("name");
1968                             BMCWEB_LOG_DEBUG("found interface {}",
1969                                              interfaceName);
1970                             tinyxml2::XMLElement* propNode =
1971                                 ifaceNode->FirstChildElement("property");
1972                             while (propNode != nullptr)
1973                             {
1974                                 const char* propertyName =
1975                                     propNode->Attribute("name");
1976                                 if (propertyName == nullptr)
1977                                 {
1978                                     BMCWEB_LOG_DEBUG(
1979                                         "Couldn't find name property");
1980                                     continue;
1981                                 }
1982                                 BMCWEB_LOG_DEBUG("Found property {}",
1983                                                  propertyName);
1984                                 if (propertyName == transaction->propertyName)
1985                                 {
1986                                     const char* argType =
1987                                         propNode->Attribute("type");
1988                                     if (argType != nullptr)
1989                                     {
1990                                         sdbusplus::message_t m =
1991                                             crow::connections::systemBus
1992                                                 ->new_method_call(
1993                                                     connectionName.c_str(),
1994                                                     transaction->objectPath
1995                                                         .c_str(),
1996                                                     "org.freedesktop.DBus."
1997                                                     "Properties",
1998                                                     "Set");
1999                                         m.append(interfaceName,
2000                                                  transaction->propertyName);
2001                                         int r = sd_bus_message_open_container(
2002                                             m.get(), SD_BUS_TYPE_VARIANT,
2003                                             argType);
2004                                         if (r < 0)
2005                                         {
2006                                             transaction->setErrorStatus(
2007                                                 "Unexpected Error");
2008                                             return;
2009                                         }
2010                                         r = convertJsonToDbus(
2011                                             m.get(), argType,
2012                                             transaction->propertyValue);
2013                                         if (r < 0)
2014                                         {
2015                                             if (r == -ERANGE)
2016                                             {
2017                                                 transaction->setErrorStatus(
2018                                                     "Provided property value "
2019                                                     "is out of range for the "
2020                                                     "property type");
2021                                             }
2022                                             else
2023                                             {
2024                                                 transaction->setErrorStatus(
2025                                                     "Invalid arg type");
2026                                             }
2027                                             return;
2028                                         }
2029                                         r = sd_bus_message_close_container(
2030                                             m.get());
2031                                         if (r < 0)
2032                                         {
2033                                             transaction->setErrorStatus(
2034                                                 "Unexpected Error");
2035                                             return;
2036                                         }
2037                                         crow::connections::systemBus
2038                                             ->async_send(
2039                                                 m,
2040                                                 [transaction](
2041                                                     const boost::system::
2042                                                         error_code& ec,
2043                                                     sdbusplus::message_t& m2) {
2044                                                     BMCWEB_LOG_DEBUG("sent");
2045                                                     if (ec)
2046                                                     {
2047                                                         const sd_bus_error* e =
2048                                                             m2.get_error();
2049                                                         setErrorResponse(
2050                                                             transaction
2051                                                                 ->asyncResp
2052                                                                 ->res,
2053                                                             boost::beast::http::
2054                                                                 status::
2055                                                                     forbidden,
2056                                                             (e) != nullptr
2057                                                                 ? e->name
2058                                                                 : ec.category()
2059                                                                       .name(),
2060                                                             (e) != nullptr
2061                                                                 ? e->message
2062                                                                 : ec.message());
2063                                                     }
2064                                                     else
2065                                                     {
2066                                                         transaction->asyncResp
2067                                                             ->res.jsonValue
2068                                                                 ["status"] =
2069                                                             "ok";
2070                                                         transaction->asyncResp
2071                                                             ->res.jsonValue
2072                                                                 ["message"] =
2073                                                             "200 OK";
2074                                                         transaction->asyncResp
2075                                                             ->res
2076                                                             .jsonValue["data"] =
2077                                                             nullptr;
2078                                                     }
2079                                                 });
2080                                     }
2081                                 }
2082                                 propNode =
2083                                     propNode->NextSiblingElement("property");
2084                             }
2085                             ifaceNode =
2086                                 ifaceNode->NextSiblingElement("interface");
2087                         }
2088                     },
2089                     connectionName, transaction->objectPath,
2090                     "org.freedesktop.DBus.Introspectable", "Introspect");
2091             }
2092         });
2093 }
2094 
2095 inline void handleDBusUrl(const crow::Request& req,
2096                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2097                           std::string& objectPath)
2098 {
2099     // If accessing a single attribute, fill in and update objectPath,
2100     // otherwise leave destProperty blank
2101     std::string destProperty;
2102     const char* attrSeperator = "/attr/";
2103     size_t attrPosition = objectPath.find(attrSeperator);
2104     if (attrPosition != std::string::npos)
2105     {
2106         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2107                                          objectPath.length());
2108         objectPath.resize(attrPosition);
2109     }
2110 
2111     if (req.method() == boost::beast::http::verb::post)
2112     {
2113         constexpr const char* actionSeperator = "/action/";
2114         size_t actionPosition = objectPath.find(actionSeperator);
2115         if (actionPosition != std::string::npos)
2116         {
2117             std::string postProperty =
2118                 objectPath.substr((actionPosition + strlen(actionSeperator)),
2119                                   objectPath.length());
2120             objectPath.resize(actionPosition);
2121             handleAction(req, asyncResp, objectPath, postProperty);
2122             return;
2123         }
2124     }
2125     else if (req.method() == boost::beast::http::verb::get)
2126     {
2127         if (objectPath.ends_with("/enumerate"))
2128         {
2129             objectPath.erase(objectPath.end() - sizeof("enumerate"),
2130                              objectPath.end());
2131             handleEnumerate(asyncResp, objectPath);
2132         }
2133         else if (objectPath.ends_with("/list"))
2134         {
2135             objectPath.erase(objectPath.end() - sizeof("list"),
2136                              objectPath.end());
2137             handleList(asyncResp, objectPath);
2138         }
2139         else
2140         {
2141             // Trim any trailing "/" at the end
2142             if (objectPath.ends_with("/"))
2143             {
2144                 objectPath.pop_back();
2145                 handleList(asyncResp, objectPath, 1);
2146             }
2147             else
2148             {
2149                 handleGet(asyncResp, objectPath, destProperty);
2150             }
2151         }
2152         return;
2153     }
2154     else if (req.method() == boost::beast::http::verb::put)
2155     {
2156         handlePut(req, asyncResp, objectPath, destProperty);
2157         return;
2158     }
2159     else if (req.method() == boost::beast::http::verb::delete_)
2160     {
2161         handleDelete(asyncResp, objectPath);
2162         return;
2163     }
2164 
2165     setErrorResponse(asyncResp->res,
2166                      boost::beast::http::status::method_not_allowed,
2167                      methodNotAllowedDesc, methodNotAllowedMsg);
2168 }
2169 
2170 inline void handleBusSystemPost(
2171     const crow::Request& req,
2172     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2173     const std::string& processName, const std::string& requestedPath)
2174 {
2175     std::vector<std::string> strs;
2176 
2177     bmcweb::split(strs, requestedPath, '/');
2178     std::string objectPath;
2179     std::string interfaceName;
2180     std::string methodName;
2181     auto it = strs.begin();
2182     if (it == strs.end())
2183     {
2184         objectPath = "/";
2185     }
2186     while (it != strs.end())
2187     {
2188         // Check if segment contains ".".  If it does, it must be an
2189         // interface
2190         if (it->find(".") != std::string::npos)
2191         {
2192             break;
2193             // This check is necessary as the trailing slash gets
2194             // parsed as part of our <path> specifier above, which
2195             // causes the normal trailing backslash redirector to
2196             // fail.
2197         }
2198         if (!it->empty())
2199         {
2200             objectPath += "/" + *it;
2201         }
2202         it++;
2203     }
2204     if (it != strs.end())
2205     {
2206         interfaceName = *it;
2207         it++;
2208 
2209         // after interface, we might have a method name
2210         if (it != strs.end())
2211         {
2212             methodName = *it;
2213             it++;
2214         }
2215     }
2216     if (it != strs.end())
2217     {
2218         // if there is more levels past the method name, something
2219         // went wrong, return not found
2220         asyncResp->res.result(boost::beast::http::status::not_found);
2221         return;
2222     }
2223     if (interfaceName.empty())
2224     {
2225         crow::connections::systemBus->async_method_call(
2226             [asyncResp, processName,
2227              objectPath](const boost::system::error_code& ec,
2228                          const std::string& introspectXml) {
2229                 if (ec)
2230                 {
2231                     BMCWEB_LOG_ERROR(
2232                         "Introspect call failed with error: {} on process: {} path: {}",
2233                         ec.message(), processName, objectPath);
2234                     return;
2235                 }
2236                 tinyxml2::XMLDocument doc;
2237 
2238                 doc.Parse(introspectXml.c_str());
2239                 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2240                 if (pRoot == nullptr)
2241                 {
2242                     BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2243                                      processName, objectPath);
2244                     asyncResp->res.jsonValue["status"] = "XML parse error";
2245                     asyncResp->res.result(
2246                         boost::beast::http::status::internal_server_error);
2247                     return;
2248                 }
2249 
2250                 BMCWEB_LOG_DEBUG("{}", introspectXml);
2251                 asyncResp->res.jsonValue["status"] = "ok";
2252                 asyncResp->res.jsonValue["bus_name"] = processName;
2253                 asyncResp->res.jsonValue["object_path"] = objectPath;
2254 
2255                 nlohmann::json& interfacesArray =
2256                     asyncResp->res.jsonValue["interfaces"];
2257                 interfacesArray = nlohmann::json::array();
2258                 tinyxml2::XMLElement* interface =
2259                     pRoot->FirstChildElement("interface");
2260 
2261                 while (interface != nullptr)
2262                 {
2263                     const char* ifaceName = interface->Attribute("name");
2264                     if (ifaceName != nullptr)
2265                     {
2266                         nlohmann::json::object_t interfaceObj;
2267                         interfaceObj["name"] = ifaceName;
2268                         interfacesArray.emplace_back(std::move(interfaceObj));
2269                     }
2270 
2271                     interface = interface->NextSiblingElement("interface");
2272                 }
2273             },
2274             processName, objectPath, "org.freedesktop.DBus.Introspectable",
2275             "Introspect");
2276     }
2277     else if (methodName.empty())
2278     {
2279         crow::connections::systemBus->async_method_call(
2280             [asyncResp, processName, objectPath,
2281              interfaceName](const boost::system::error_code& ec,
2282                             const std::string& introspectXml) {
2283                 if (ec)
2284                 {
2285                     BMCWEB_LOG_ERROR(
2286                         "Introspect call failed with error: {} on process: {} path: {}",
2287                         ec.message(), processName, objectPath);
2288                     return;
2289                 }
2290                 tinyxml2::XMLDocument doc;
2291 
2292                 doc.Parse(introspectXml.data(), introspectXml.size());
2293                 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2294                 if (pRoot == nullptr)
2295                 {
2296                     BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2297                                      processName, objectPath);
2298                     asyncResp->res.result(
2299                         boost::beast::http::status::internal_server_error);
2300                     return;
2301                 }
2302 
2303                 asyncResp->res.jsonValue["status"] = "ok";
2304                 asyncResp->res.jsonValue["bus_name"] = processName;
2305                 asyncResp->res.jsonValue["interface"] = interfaceName;
2306                 asyncResp->res.jsonValue["object_path"] = objectPath;
2307 
2308                 nlohmann::json& methodsArray =
2309                     asyncResp->res.jsonValue["methods"];
2310                 methodsArray = nlohmann::json::array();
2311 
2312                 nlohmann::json& signalsArray =
2313                     asyncResp->res.jsonValue["signals"];
2314                 signalsArray = nlohmann::json::array();
2315 
2316                 nlohmann::json& propertiesObj =
2317                     asyncResp->res.jsonValue["properties"];
2318                 propertiesObj = nlohmann::json::object();
2319 
2320                 // if we know we're the only call, build the
2321                 // json directly
2322                 tinyxml2::XMLElement* interface =
2323                     pRoot->FirstChildElement("interface");
2324                 while (interface != nullptr)
2325                 {
2326                     const char* ifaceName = interface->Attribute("name");
2327 
2328                     if (ifaceName != nullptr && ifaceName == interfaceName)
2329                     {
2330                         break;
2331                     }
2332 
2333                     interface = interface->NextSiblingElement("interface");
2334                 }
2335                 if (interface == nullptr)
2336                 {
2337                     // if we got to the end of the list and
2338                     // never found a match, throw 404
2339                     asyncResp->res.result(
2340                         boost::beast::http::status::not_found);
2341                     return;
2342                 }
2343 
2344                 tinyxml2::XMLElement* methods =
2345                     interface->FirstChildElement("method");
2346                 while (methods != nullptr)
2347                 {
2348                     nlohmann::json argsArray = nlohmann::json::array();
2349                     tinyxml2::XMLElement* arg =
2350                         methods->FirstChildElement("arg");
2351                     while (arg != nullptr)
2352                     {
2353                         nlohmann::json thisArg;
2354                         for (const char* fieldName : std::array<const char*, 3>{
2355                                  "name", "direction", "type"})
2356                         {
2357                             const char* fieldValue = arg->Attribute(fieldName);
2358                             if (fieldValue != nullptr)
2359                             {
2360                                 thisArg[fieldName] = fieldValue;
2361                             }
2362                         }
2363                         argsArray.emplace_back(std::move(thisArg));
2364                         arg = arg->NextSiblingElement("arg");
2365                     }
2366 
2367                     const char* name = methods->Attribute("name");
2368                     if (name != nullptr)
2369                     {
2370                         std::string uri;
2371                         uri.reserve(14 + processName.size() +
2372                                     objectPath.size() + interfaceName.size() +
2373                                     strlen(name));
2374                         uri += "/bus/system/";
2375                         uri += processName;
2376                         uri += objectPath;
2377                         uri += "/";
2378                         uri += interfaceName;
2379                         uri += "/";
2380                         uri += name;
2381 
2382                         nlohmann::json::object_t object;
2383                         object["name"] = name;
2384                         object["uri"] = std::move(uri);
2385                         object["args"] = argsArray;
2386 
2387                         methodsArray.emplace_back(std::move(object));
2388                     }
2389                     methods = methods->NextSiblingElement("method");
2390                 }
2391                 tinyxml2::XMLElement* signals =
2392                     interface->FirstChildElement("signal");
2393                 while (signals != nullptr)
2394                 {
2395                     nlohmann::json argsArray = nlohmann::json::array();
2396 
2397                     tinyxml2::XMLElement* arg =
2398                         signals->FirstChildElement("arg");
2399                     while (arg != nullptr)
2400                     {
2401                         const char* name = arg->Attribute("name");
2402                         const char* type = arg->Attribute("type");
2403                         if (name != nullptr && type != nullptr)
2404                         {
2405                             nlohmann::json::object_t params;
2406                             params["name"] = name;
2407                             params["type"] = type;
2408                             argsArray.push_back(std::move(params));
2409                         }
2410                         arg = arg->NextSiblingElement("arg");
2411                     }
2412                     const char* name = signals->Attribute("name");
2413                     if (name != nullptr)
2414                     {
2415                         nlohmann::json::object_t object;
2416                         object["name"] = name;
2417                         object["args"] = argsArray;
2418                         signalsArray.emplace_back(std::move(object));
2419                     }
2420 
2421                     signals = signals->NextSiblingElement("signal");
2422                 }
2423 
2424                 tinyxml2::XMLElement* property =
2425                     interface->FirstChildElement("property");
2426                 while (property != nullptr)
2427                 {
2428                     const char* name = property->Attribute("name");
2429                     const char* type = property->Attribute("type");
2430                     if (type != nullptr && name != nullptr)
2431                     {
2432                         sdbusplus::message_t m =
2433                             crow::connections::systemBus->new_method_call(
2434                                 processName.c_str(), objectPath.c_str(),
2435                                 "org.freedesktop."
2436                                 "DBus."
2437                                 "Properties",
2438                                 "Get");
2439                         m.append(interfaceName, name);
2440                         nlohmann::json& propertyItem = propertiesObj[name];
2441                         crow::connections::systemBus->async_send(
2442                             m, [&propertyItem,
2443                                 asyncResp](const boost::system::error_code& ec2,
2444                                            sdbusplus::message_t& msg) {
2445                                 if (ec2)
2446                                 {
2447                                     return;
2448                                 }
2449 
2450                                 int r =
2451                                     convertDBusToJSON("v", msg, propertyItem);
2452                                 if (r < 0)
2453                                 {
2454                                     BMCWEB_LOG_ERROR(
2455                                         "Couldn't convert vector to json");
2456                                 }
2457                             });
2458                     }
2459                     property = property->NextSiblingElement("property");
2460                 }
2461             },
2462             processName, objectPath, "org.freedesktop.DBus.Introspectable",
2463             "Introspect");
2464     }
2465     else
2466     {
2467         if (req.method() != boost::beast::http::verb::post)
2468         {
2469             asyncResp->res.result(boost::beast::http::status::not_found);
2470             return;
2471         }
2472 
2473         nlohmann::json requestDbusData;
2474         JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2475         if (ret == JsonParseResult::BadContentType)
2476         {
2477             setErrorResponse(asyncResp->res,
2478                              boost::beast::http::status::unsupported_media_type,
2479                              invalidContentType, unsupportedMediaMsg);
2480             return;
2481         }
2482         if (ret != JsonParseResult::Success)
2483         {
2484             setErrorResponse(asyncResp->res,
2485                              boost::beast::http::status::bad_request,
2486                              noJsonDesc, badReqMsg);
2487             return;
2488         }
2489 
2490         if (!requestDbusData.is_array())
2491         {
2492             asyncResp->res.result(boost::beast::http::status::bad_request);
2493             return;
2494         }
2495         auto transaction = std::make_shared<InProgressActionData>(asyncResp);
2496 
2497         transaction->path = objectPath;
2498         transaction->methodName = methodName;
2499         transaction->arguments = std::move(requestDbusData);
2500 
2501         findActionOnInterface(transaction, processName);
2502     }
2503 }
2504 
2505 inline void requestRoutes(App& app)
2506 {
2507     BMCWEB_ROUTE(app, "/bus/")
2508         .privileges({{"Login"}})
2509         .methods(boost::beast::http::verb::get)(
2510             [](const crow::Request&,
2511                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2512                 nlohmann::json::array_t buses;
2513                 nlohmann::json& bus = buses.emplace_back();
2514                 bus["name"] = "system";
2515                 asyncResp->res.jsonValue["busses"] = std::move(buses);
2516                 asyncResp->res.jsonValue["status"] = "ok";
2517             });
2518 
2519     BMCWEB_ROUTE(app, "/bus/system/")
2520         .privileges({{"Login"}})
2521         .methods(boost::beast::http::verb::get)(
2522             [](const crow::Request&,
2523                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2524                 auto myCallback = [asyncResp](
2525                                       const boost::system::error_code& ec,
2526                                       std::vector<std::string>& names) {
2527                     if (ec)
2528                     {
2529                         BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec);
2530                         asyncResp->res.result(
2531                             boost::beast::http::status::internal_server_error);
2532                     }
2533                     else
2534                     {
2535                         std::ranges::sort(names);
2536                         asyncResp->res.jsonValue["status"] = "ok";
2537                         auto& objectsSub = asyncResp->res.jsonValue["objects"];
2538                         for (const auto& name : names)
2539                         {
2540                             nlohmann::json::object_t object;
2541                             object["name"] = name;
2542                             objectsSub.emplace_back(std::move(object));
2543                         }
2544                     }
2545                 };
2546                 crow::connections::systemBus->async_method_call(
2547                     std::move(myCallback), "org.freedesktop.DBus", "/",
2548                     "org.freedesktop.DBus", "ListNames");
2549             });
2550 
2551     BMCWEB_ROUTE(app, "/list/")
2552         .privileges({{"Login"}})
2553         .methods(boost::beast::http::verb::get)(
2554             [](const crow::Request&,
2555                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2556                 handleList(asyncResp, "/");
2557             });
2558 
2559     BMCWEB_ROUTE(app, "/xyz/<path>")
2560         .privileges({{"Login"}})
2561         .methods(boost::beast::http::verb::get)(
2562             [](const crow::Request& req,
2563                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2564                const std::string& path) {
2565                 std::string objectPath = "/xyz/" + path;
2566                 handleDBusUrl(req, asyncResp, objectPath);
2567             });
2568 
2569     BMCWEB_ROUTE(app, "/xyz/<path>")
2570         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2571         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2572                  boost::beast::http::verb::delete_)(
2573             [](const crow::Request& req,
2574                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2575                const std::string& path) {
2576                 std::string objectPath = "/xyz/" + path;
2577                 handleDBusUrl(req, asyncResp, objectPath);
2578             });
2579 
2580     BMCWEB_ROUTE(app, "/org/<path>")
2581         .privileges({{"Login"}})
2582         .methods(boost::beast::http::verb::get)(
2583             [](const crow::Request& req,
2584                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2585                const std::string& path) {
2586                 std::string objectPath = "/org/" + path;
2587                 handleDBusUrl(req, asyncResp, objectPath);
2588             });
2589 
2590     BMCWEB_ROUTE(app, "/org/<path>")
2591         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2592         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2593                  boost::beast::http::verb::delete_)(
2594             [](const crow::Request& req,
2595                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2596                const std::string& path) {
2597                 std::string objectPath = "/org/" + path;
2598                 handleDBusUrl(req, asyncResp, objectPath);
2599             });
2600 
2601     BMCWEB_ROUTE(app, "/download/dump/<str>/")
2602         .privileges({{"ConfigureManager"}})
2603         .methods(boost::beast::http::verb::get)(
2604             [](const crow::Request&,
2605                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2606                const std::string& dumpId) {
2607                 if (!validateFilename(dumpId))
2608                 {
2609                     asyncResp->res.result(
2610                         boost::beast::http::status::bad_request);
2611                     return;
2612                 }
2613                 std::filesystem::path loc(
2614                     "/var/lib/phosphor-debug-collector/dumps");
2615 
2616                 loc /= dumpId;
2617 
2618                 if (!std::filesystem::exists(loc) ||
2619                     !std::filesystem::is_directory(loc))
2620                 {
2621                     BMCWEB_LOG_ERROR("{}Not found", loc.string());
2622                     asyncResp->res.result(
2623                         boost::beast::http::status::not_found);
2624                     return;
2625                 }
2626                 std::filesystem::directory_iterator files(loc);
2627 
2628                 for (const auto& file : files)
2629                 {
2630                     if (!asyncResp->res.openFile(file))
2631                     {
2632                         continue;
2633                     }
2634 
2635                     asyncResp->res.addHeader(
2636                         boost::beast::http::field::content_type,
2637                         "application/octet-stream");
2638 
2639                     // Assuming only one dump file will be present in the dump
2640                     // id directory
2641                     std::string dumpFileName = file.path().filename().string();
2642 
2643                     // Filename should be in alphanumeric, dot and underscore
2644                     // Its based on phosphor-debug-collector application
2645                     // dumpfile format
2646                     static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2647                     if (!std::regex_match(dumpFileName, dumpFileRegex))
2648                     {
2649                         BMCWEB_LOG_ERROR("Invalid dump filename {}",
2650                                          dumpFileName);
2651                         asyncResp->res.result(
2652                             boost::beast::http::status::not_found);
2653                         return;
2654                     }
2655                     std::string contentDispositionParam =
2656                         "attachment; filename=\"" + dumpFileName + "\"";
2657 
2658                     asyncResp->res.addHeader(
2659                         boost::beast::http::field::content_disposition,
2660                         contentDispositionParam);
2661 
2662                     return;
2663                 }
2664                 asyncResp->res.result(boost::beast::http::status::not_found);
2665                 return;
2666             });
2667 
2668     BMCWEB_ROUTE(app, "/bus/system/<str>/")
2669         .privileges({{"Login"}})
2670 
2671         .methods(boost::beast::http::verb::get)(
2672             [](const crow::Request&,
2673                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2674                const std::string& connection) {
2675                 introspectObjects(connection, "/", asyncResp);
2676             });
2677 
2678     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2679         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2680         .methods(boost::beast::http::verb::get,
2681                  boost::beast::http::verb::post)(handleBusSystemPost);
2682 }
2683 } // namespace openbmc_mapper
2684 } // namespace crow
2685