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