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