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