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 (const auto& it : *j)
748             {
749                 r = convertJsonToDbus(m, containedType, it);
750                 if (r < 0)
751                 {
752                     return r;
753                 }
754             }
755             sd_bus_message_close_container(m);
756         }
757         else if (boost::starts_with(argCode, "v"))
758         {
759             std::string containedType = argCode.substr(1);
760             BMCWEB_LOG_DEBUG << "variant type: " << argCode
761                              << " appending variant of type: " << containedType;
762             r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
763                                               containedType.c_str());
764             if (r < 0)
765             {
766                 return r;
767             }
768 
769             r = convertJsonToDbus(m, containedType, input_json);
770             if (r < 0)
771             {
772                 return r;
773             }
774 
775             r = sd_bus_message_close_container(m);
776             if (r < 0)
777             {
778                 return r;
779             }
780         }
781         else if (boost::starts_with(argCode, "(") &&
782                  boost::ends_with(argCode, ")"))
783         {
784             std::string containedType = argCode.substr(1, argCode.size() - 1);
785             r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
786                                               containedType.c_str());
787             if (r < 0)
788             {
789                 return r;
790             }
791 
792             nlohmann::json::const_iterator it = j->begin();
793             for (const std::string& argCode2 : dbusArgSplit(arg_type))
794             {
795                 if (it == j->end())
796                 {
797                     return -1;
798                 }
799                 r = convertJsonToDbus(m, argCode2, *it);
800                 if (r < 0)
801                 {
802                     return r;
803                 }
804                 it++;
805             }
806             r = sd_bus_message_close_container(m);
807         }
808         else if (boost::starts_with(argCode, "{") &&
809                  boost::ends_with(argCode, "}"))
810         {
811             std::string containedType = argCode.substr(1, argCode.size() - 1);
812             r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
813                                               containedType.c_str());
814             if (r < 0)
815             {
816                 return r;
817             }
818 
819             std::vector<std::string> codes = dbusArgSplit(containedType);
820             if (codes.size() != 2)
821             {
822                 return -1;
823             }
824             const std::string& key_type = codes[0];
825             const std::string& value_type = codes[1];
826             for (auto it : j->items())
827             {
828                 r = convertJsonToDbus(m, key_type, it.key());
829                 if (r < 0)
830                 {
831                     return r;
832                 }
833 
834                 r = convertJsonToDbus(m, value_type, it.value());
835                 if (r < 0)
836                 {
837                     return r;
838                 }
839             }
840             r = sd_bus_message_close_container(m);
841         }
842         else
843         {
844             return -2;
845         }
846         if (r < 0)
847         {
848             return r;
849         }
850 
851         if (argTypes.size() > 1)
852         {
853             jIt++;
854         }
855     }
856 
857     return r;
858 }
859 
860 template <typename T>
861 int readMessageItem(const std::string& typeCode, sdbusplus::message::message& m,
862                     nlohmann::json& data)
863 {
864     T value;
865 
866     int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
867     if (r < 0)
868     {
869         BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
870                          << " failed!";
871         return r;
872     }
873 
874     data = value;
875     return 0;
876 }
877 
878 int convertDBusToJSON(const std::string& returnType,
879                       sdbusplus::message::message& m, nlohmann::json& response);
880 
881 inline int readDictEntryFromMessage(const std::string& typeCode,
882                                     sdbusplus::message::message& m,
883                                     nlohmann::json& object)
884 {
885     std::vector<std::string> types = dbusArgSplit(typeCode);
886     if (types.size() != 2)
887     {
888         BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
889                          << types.size();
890         return -1;
891     }
892 
893     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
894                                            typeCode.c_str());
895     if (r < 0)
896     {
897         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
898         return r;
899     }
900 
901     nlohmann::json key;
902     r = convertDBusToJSON(types[0], m, key);
903     if (r < 0)
904     {
905         return r;
906     }
907 
908     const std::string* keyPtr = key.get_ptr<const std::string*>();
909     if (keyPtr == nullptr)
910     {
911         // json doesn't support non-string keys.  If we hit this condition,
912         // convert the result to a string so we can proceed
913         key = key.dump();
914         keyPtr = key.get_ptr<const std::string*>();
915         // in theory this can't fail now, but lets be paranoid about it
916         // anyway
917         if (keyPtr == nullptr)
918         {
919             return -1;
920         }
921     }
922     nlohmann::json& value = object[*keyPtr];
923 
924     r = convertDBusToJSON(types[1], m, value);
925     if (r < 0)
926     {
927         return r;
928     }
929 
930     r = sd_bus_message_exit_container(m.get());
931     if (r < 0)
932     {
933         BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
934         return r;
935     }
936 
937     return 0;
938 }
939 
940 inline int readArrayFromMessage(const std::string& typeCode,
941                                 sdbusplus::message::message& m,
942                                 nlohmann::json& data)
943 {
944     if (typeCode.size() < 2)
945     {
946         BMCWEB_LOG_ERROR << "Type code " << typeCode
947                          << " too small for an array";
948         return -1;
949     }
950 
951     std::string containedType = typeCode.substr(1);
952 
953     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
954                                            containedType.c_str());
955     if (r < 0)
956     {
957         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
958                          << r;
959         return r;
960     }
961 
962     bool dict = boost::starts_with(containedType, "{") &&
963                 boost::ends_with(containedType, "}");
964 
965     if (dict)
966     {
967         // Remove the { }
968         containedType = containedType.substr(1, containedType.size() - 2);
969         data = nlohmann::json::object();
970     }
971     else
972     {
973         data = nlohmann::json::array();
974     }
975 
976     while (true)
977     {
978         r = sd_bus_message_at_end(m.get(), false);
979         if (r < 0)
980         {
981             BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
982             return r;
983         }
984 
985         if (r > 0)
986         {
987             break;
988         }
989 
990         // Dictionaries are only ever seen in an array
991         if (dict)
992         {
993             r = readDictEntryFromMessage(containedType, m, data);
994             if (r < 0)
995             {
996                 return r;
997             }
998         }
999         else
1000         {
1001             data.push_back(nlohmann::json());
1002 
1003             r = convertDBusToJSON(containedType, m, data.back());
1004             if (r < 0)
1005             {
1006                 return r;
1007             }
1008         }
1009     }
1010 
1011     r = sd_bus_message_exit_container(m.get());
1012     if (r < 0)
1013     {
1014         BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1015         return r;
1016     }
1017 
1018     return 0;
1019 }
1020 
1021 inline int readStructFromMessage(const std::string& typeCode,
1022                                  sdbusplus::message::message& m,
1023                                  nlohmann::json& data)
1024 {
1025     if (typeCode.size() < 3)
1026     {
1027         BMCWEB_LOG_ERROR << "Type code " << typeCode
1028                          << " too small for a struct";
1029         return -1;
1030     }
1031 
1032     std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1033     std::vector<std::string> types = dbusArgSplit(containedTypes);
1034 
1035     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1036                                            containedTypes.c_str());
1037     if (r < 0)
1038     {
1039         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1040                          << r;
1041         return r;
1042     }
1043 
1044     for (const std::string& type : types)
1045     {
1046         data.push_back(nlohmann::json());
1047         r = convertDBusToJSON(type, m, data.back());
1048         if (r < 0)
1049         {
1050             return r;
1051         }
1052     }
1053 
1054     r = sd_bus_message_exit_container(m.get());
1055     if (r < 0)
1056     {
1057         BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1058         return r;
1059     }
1060     return 0;
1061 }
1062 
1063 inline int readVariantFromMessage(sdbusplus::message::message& m,
1064                                   nlohmann::json& data)
1065 {
1066     const char* containerType;
1067     int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
1068     if (r < 0)
1069     {
1070         BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1071         return r;
1072     }
1073 
1074     r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1075                                        containerType);
1076     if (r < 0)
1077     {
1078         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1079                          << r;
1080         return r;
1081     }
1082 
1083     r = convertDBusToJSON(containerType, m, data);
1084     if (r < 0)
1085     {
1086         return r;
1087     }
1088 
1089     r = sd_bus_message_exit_container(m.get());
1090     if (r < 0)
1091     {
1092         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1093         return r;
1094     }
1095 
1096     return 0;
1097 }
1098 
1099 inline int convertDBusToJSON(const std::string& returnType,
1100                              sdbusplus::message::message& m,
1101                              nlohmann::json& response)
1102 {
1103     int r = 0;
1104     const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1105 
1106     for (const std::string& typeCode : returnTypes)
1107     {
1108         nlohmann::json* thisElement = &response;
1109         if (returnTypes.size() > 1)
1110         {
1111             response.push_back(nlohmann::json{});
1112             thisElement = &response.back();
1113         }
1114 
1115         if (typeCode == "s")
1116         {
1117             r = readMessageItem<char*>(typeCode, m, *thisElement);
1118             if (r < 0)
1119             {
1120                 return r;
1121             }
1122         }
1123         else if (typeCode == "g")
1124         {
1125             r = readMessageItem<char*>(typeCode, m, *thisElement);
1126             if (r < 0)
1127             {
1128                 return r;
1129             }
1130         }
1131         else if (typeCode == "o")
1132         {
1133             r = readMessageItem<char*>(typeCode, m, *thisElement);
1134             if (r < 0)
1135             {
1136                 return r;
1137             }
1138         }
1139         else if (typeCode == "b")
1140         {
1141             r = readMessageItem<int>(typeCode, m, *thisElement);
1142             if (r < 0)
1143             {
1144                 return r;
1145             }
1146 
1147             *thisElement = static_cast<bool>(thisElement->get<int>());
1148         }
1149         else if (typeCode == "u")
1150         {
1151             r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
1152             if (r < 0)
1153             {
1154                 return r;
1155             }
1156         }
1157         else if (typeCode == "i")
1158         {
1159             r = readMessageItem<int32_t>(typeCode, m, *thisElement);
1160             if (r < 0)
1161             {
1162                 return r;
1163             }
1164         }
1165         else if (typeCode == "x")
1166         {
1167             r = readMessageItem<int64_t>(typeCode, m, *thisElement);
1168             if (r < 0)
1169             {
1170                 return r;
1171             }
1172         }
1173         else if (typeCode == "t")
1174         {
1175             r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
1176             if (r < 0)
1177             {
1178                 return r;
1179             }
1180         }
1181         else if (typeCode == "n")
1182         {
1183             r = readMessageItem<int16_t>(typeCode, m, *thisElement);
1184             if (r < 0)
1185             {
1186                 return r;
1187             }
1188         }
1189         else if (typeCode == "q")
1190         {
1191             r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
1192             if (r < 0)
1193             {
1194                 return r;
1195             }
1196         }
1197         else if (typeCode == "y")
1198         {
1199             r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
1200             if (r < 0)
1201             {
1202                 return r;
1203             }
1204         }
1205         else if (typeCode == "d")
1206         {
1207             r = readMessageItem<double>(typeCode, m, *thisElement);
1208             if (r < 0)
1209             {
1210                 return r;
1211             }
1212         }
1213         else if (typeCode == "h")
1214         {
1215             r = readMessageItem<int>(typeCode, m, *thisElement);
1216             if (r < 0)
1217             {
1218                 return r;
1219             }
1220         }
1221         else if (boost::starts_with(typeCode, "a"))
1222         {
1223             r = readArrayFromMessage(typeCode, m, *thisElement);
1224             if (r < 0)
1225             {
1226                 return r;
1227             }
1228         }
1229         else if (boost::starts_with(typeCode, "(") &&
1230                  boost::ends_with(typeCode, ")"))
1231         {
1232             r = readStructFromMessage(typeCode, m, *thisElement);
1233             if (r < 0)
1234             {
1235                 return r;
1236             }
1237         }
1238         else if (boost::starts_with(typeCode, "v"))
1239         {
1240             r = readVariantFromMessage(m, *thisElement);
1241             if (r < 0)
1242             {
1243                 return r;
1244             }
1245         }
1246         else
1247         {
1248             BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1249             return -2;
1250         }
1251     }
1252 
1253     return 0;
1254 }
1255 
1256 inline void
1257     handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1258                          sdbusplus::message::message& m,
1259                          const std::string& returnType)
1260 {
1261     nlohmann::json data;
1262 
1263     int r = convertDBusToJSON(returnType, m, data);
1264     if (r < 0)
1265     {
1266         transaction->outputFailed = true;
1267         return;
1268     }
1269 
1270     if (data.is_null())
1271     {
1272         return;
1273     }
1274 
1275     if (transaction->methodResponse.is_null())
1276     {
1277         transaction->methodResponse = std::move(data);
1278         return;
1279     }
1280 
1281     // If they're both dictionaries or arrays, merge into one.
1282     // Otherwise, make the results an array with every result
1283     // an entry.  Could also just fail in that case, but it
1284     // seems better to get the data back somehow.
1285 
1286     if (transaction->methodResponse.is_object() && data.is_object())
1287     {
1288         for (const auto& obj : data.items())
1289         {
1290             // Note: Will overwrite the data for a duplicate key
1291             transaction->methodResponse.emplace(obj.key(),
1292                                                 std::move(obj.value()));
1293         }
1294         return;
1295     }
1296 
1297     if (transaction->methodResponse.is_array() && data.is_array())
1298     {
1299         for (auto& obj : data)
1300         {
1301             transaction->methodResponse.push_back(std::move(obj));
1302         }
1303         return;
1304     }
1305 
1306     if (!transaction->convertedToArray)
1307     {
1308         // They are different types. May as well turn them into an array
1309         nlohmann::json j = std::move(transaction->methodResponse);
1310         transaction->methodResponse = nlohmann::json::array();
1311         transaction->methodResponse.push_back(std::move(j));
1312         transaction->methodResponse.push_back(std::move(data));
1313         transaction->convertedToArray = true;
1314     }
1315     else
1316     {
1317         transaction->methodResponse.push_back(std::move(data));
1318     }
1319 }
1320 
1321 inline void
1322     findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
1323                           const std::string& connectionName)
1324 {
1325     BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1326                      << connectionName;
1327     crow::connections::systemBus->async_method_call(
1328         [transaction, connectionName{std::string(connectionName)}](
1329             const boost::system::error_code ec,
1330             const std::string& introspect_xml) {
1331             BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1332             if (ec)
1333             {
1334                 BMCWEB_LOG_ERROR
1335                     << "Introspect call failed with error: " << ec.message()
1336                     << " on process: " << connectionName << "\n";
1337                 return;
1338             }
1339             tinyxml2::XMLDocument doc;
1340 
1341             doc.Parse(introspect_xml.data(), introspect_xml.size());
1342             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1343             if (pRoot == nullptr)
1344             {
1345                 BMCWEB_LOG_ERROR << "XML document failed to parse "
1346                                  << connectionName << "\n";
1347                 return;
1348             }
1349             tinyxml2::XMLElement* interfaceNode =
1350                 pRoot->FirstChildElement("interface");
1351             while (interfaceNode != nullptr)
1352             {
1353                 const char* thisInterfaceName =
1354                     interfaceNode->Attribute("name");
1355                 if (thisInterfaceName != nullptr)
1356                 {
1357                     if (!transaction->interfaceName.empty() &&
1358                         (transaction->interfaceName != thisInterfaceName))
1359                     {
1360                         interfaceNode =
1361                             interfaceNode->NextSiblingElement("interface");
1362                         continue;
1363                     }
1364 
1365                     tinyxml2::XMLElement* methodNode =
1366                         interfaceNode->FirstChildElement("method");
1367                     while (methodNode != nullptr)
1368                     {
1369                         const char* thisMethodName =
1370                             methodNode->Attribute("name");
1371                         BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1372                         if (thisMethodName != nullptr &&
1373                             thisMethodName == transaction->methodName)
1374                         {
1375                             BMCWEB_LOG_DEBUG
1376                                 << "Found method named " << thisMethodName
1377                                 << " on interface " << thisInterfaceName;
1378                             sdbusplus::message::message m =
1379                                 crow::connections::systemBus->new_method_call(
1380                                     connectionName.c_str(),
1381                                     transaction->path.c_str(),
1382                                     thisInterfaceName,
1383                                     transaction->methodName.c_str());
1384 
1385                             tinyxml2::XMLElement* argumentNode =
1386                                 methodNode->FirstChildElement("arg");
1387 
1388                             std::string returnType;
1389 
1390                             // Find the output type
1391                             while (argumentNode != nullptr)
1392                             {
1393                                 const char* argDirection =
1394                                     argumentNode->Attribute("direction");
1395                                 const char* argType =
1396                                     argumentNode->Attribute("type");
1397                                 if (argDirection != nullptr &&
1398                                     argType != nullptr &&
1399                                     std::string(argDirection) == "out")
1400                                 {
1401                                     returnType = argType;
1402                                     break;
1403                                 }
1404                                 argumentNode =
1405                                     argumentNode->NextSiblingElement("arg");
1406                             }
1407 
1408                             nlohmann::json::const_iterator argIt =
1409                                 transaction->arguments.begin();
1410 
1411                             argumentNode = methodNode->FirstChildElement("arg");
1412 
1413                             while (argumentNode != nullptr)
1414                             {
1415                                 const char* argDirection =
1416                                     argumentNode->Attribute("direction");
1417                                 const char* argType =
1418                                     argumentNode->Attribute("type");
1419                                 if (argDirection != nullptr &&
1420                                     argType != nullptr &&
1421                                     std::string(argDirection) == "in")
1422                                 {
1423                                     if (argIt == transaction->arguments.end())
1424                                     {
1425                                         transaction->setErrorStatus(
1426                                             "Invalid method args");
1427                                         return;
1428                                     }
1429                                     if (convertJsonToDbus(m.get(),
1430                                                           std::string(argType),
1431                                                           *argIt) < 0)
1432                                     {
1433                                         transaction->setErrorStatus(
1434                                             "Invalid method arg type");
1435                                         return;
1436                                     }
1437 
1438                                     argIt++;
1439                                 }
1440                                 argumentNode =
1441                                     argumentNode->NextSiblingElement("arg");
1442                             }
1443 
1444                             crow::connections::systemBus->async_send(
1445                                 m, [transaction, returnType](
1446                                        boost::system::error_code ec2,
1447                                        sdbusplus::message::message& m2) {
1448                                     if (ec2)
1449                                     {
1450                                         transaction->methodFailed = true;
1451                                         const sd_bus_error* e = m2.get_error();
1452 
1453                                         if (e)
1454                                         {
1455                                             setErrorResponse(
1456                                                 transaction->res,
1457                                                 boost::beast::http::status::
1458                                                     bad_request,
1459                                                 e->name, e->message);
1460                                         }
1461                                         else
1462                                         {
1463                                             setErrorResponse(
1464                                                 transaction->res,
1465                                                 boost::beast::http::status::
1466                                                     bad_request,
1467                                                 "Method call failed",
1468                                                 methodFailedMsg);
1469                                         }
1470                                         return;
1471                                     }
1472                                     else
1473                                     {
1474                                         transaction->methodPassed = true;
1475                                     }
1476 
1477                                     handleMethodResponse(transaction, m2,
1478                                                          returnType);
1479                                 });
1480                             break;
1481                         }
1482                         methodNode = methodNode->NextSiblingElement("method");
1483                     }
1484                 }
1485                 interfaceNode = interfaceNode->NextSiblingElement("interface");
1486             }
1487         },
1488         connectionName, transaction->path,
1489         "org.freedesktop.DBus.Introspectable", "Introspect");
1490 }
1491 
1492 inline void handleAction(const crow::Request& req, crow::Response& res,
1493                          const std::string& objectPath,
1494                          const std::string& methodName)
1495 {
1496     BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1497                      << methodName;
1498     nlohmann::json requestDbusData =
1499         nlohmann::json::parse(req.body, nullptr, false);
1500 
1501     if (requestDbusData.is_discarded())
1502     {
1503         setErrorResponse(res, boost::beast::http::status::bad_request,
1504                          noJsonDesc, badReqMsg);
1505         res.end();
1506         return;
1507     }
1508     nlohmann::json::iterator data = requestDbusData.find("data");
1509     if (data == requestDbusData.end())
1510     {
1511         setErrorResponse(res, boost::beast::http::status::bad_request,
1512                          noJsonDesc, badReqMsg);
1513         res.end();
1514         return;
1515     }
1516 
1517     if (!data->is_array())
1518     {
1519         setErrorResponse(res, boost::beast::http::status::bad_request,
1520                          noJsonDesc, badReqMsg);
1521         res.end();
1522         return;
1523     }
1524     auto transaction = std::make_shared<InProgressActionData>(res);
1525 
1526     transaction->path = objectPath;
1527     transaction->methodName = methodName;
1528     transaction->arguments = std::move(*data);
1529     crow::connections::systemBus->async_method_call(
1530         [transaction](
1531             const boost::system::error_code ec,
1532             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1533                 interfaceNames) {
1534             if (ec || interfaceNames.size() <= 0)
1535             {
1536                 BMCWEB_LOG_ERROR << "Can't find object";
1537                 setErrorResponse(transaction->res,
1538                                  boost::beast::http::status::not_found,
1539                                  notFoundDesc, notFoundMsg);
1540                 return;
1541             }
1542 
1543             BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1544                              << " object(s)";
1545 
1546             for (const std::pair<std::string, std::vector<std::string>>&
1547                      object : interfaceNames)
1548             {
1549                 findActionOnInterface(transaction, object.first);
1550             }
1551         },
1552         "xyz.openbmc_project.ObjectMapper",
1553         "/xyz/openbmc_project/object_mapper",
1554         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1555         std::array<std::string, 0>());
1556 }
1557 
1558 inline void handleDelete(crow::Response& res, const std::string& objectPath)
1559 {
1560     BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1561 
1562     crow::connections::systemBus->async_method_call(
1563         [&res, objectPath](
1564             const boost::system::error_code ec,
1565             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1566                 interfaceNames) {
1567             if (ec || interfaceNames.size() <= 0)
1568             {
1569                 BMCWEB_LOG_ERROR << "Can't find object";
1570                 setErrorResponse(res,
1571                                  boost::beast::http::status::method_not_allowed,
1572                                  methodNotAllowedDesc, methodNotAllowedMsg);
1573                 res.end();
1574                 return;
1575             }
1576 
1577             auto transaction = std::make_shared<InProgressActionData>(res);
1578             transaction->path = objectPath;
1579             transaction->methodName = "Delete";
1580             transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1581 
1582             for (const std::pair<std::string, std::vector<std::string>>&
1583                      object : interfaceNames)
1584             {
1585                 findActionOnInterface(transaction, object.first);
1586             }
1587         },
1588         "xyz.openbmc_project.ObjectMapper",
1589         "/xyz/openbmc_project/object_mapper",
1590         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1591         std::array<const char*, 0>());
1592 }
1593 
1594 inline void handleList(crow::Response& res, const std::string& objectPath,
1595                        int32_t depth = 0)
1596 {
1597     crow::connections::systemBus->async_method_call(
1598         [&res](const boost::system::error_code ec,
1599                std::vector<std::string>& objectPaths) {
1600             if (ec)
1601             {
1602                 setErrorResponse(res, boost::beast::http::status::not_found,
1603                                  notFoundDesc, notFoundMsg);
1604             }
1605             else
1606             {
1607                 res.jsonValue = {{"status", "ok"},
1608                                  {"message", "200 OK"},
1609                                  {"data", std::move(objectPaths)}};
1610             }
1611             res.end();
1612         },
1613         "xyz.openbmc_project.ObjectMapper",
1614         "/xyz/openbmc_project/object_mapper",
1615         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
1616         depth, std::array<std::string, 0>());
1617 }
1618 
1619 inline void handleEnumerate(crow::Response& res, const std::string& objectPath)
1620 {
1621     BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1622     auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1623 
1624     asyncResp->res.jsonValue = {{"message", "200 OK"},
1625                                 {"status", "ok"},
1626                                 {"data", nlohmann::json::object()}};
1627 
1628     crow::connections::systemBus->async_method_call(
1629         [objectPath, asyncResp](const boost::system::error_code ec,
1630                                 GetSubTreeType& object_names) {
1631             auto transaction = std::make_shared<InProgressEnumerateData>(
1632                 objectPath, asyncResp);
1633 
1634             transaction->subtree =
1635                 std::make_shared<GetSubTreeType>(std::move(object_names));
1636 
1637             if (ec)
1638             {
1639                 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1640                                  << transaction->objectPath;
1641                 setErrorResponse(transaction->asyncResp->res,
1642                                  boost::beast::http::status::not_found,
1643                                  notFoundDesc, notFoundMsg);
1644                 return;
1645             }
1646 
1647             // Add the data for the path passed in to the results
1648             // as if GetSubTree returned it, and continue on enumerating
1649             getObjectAndEnumerate(transaction);
1650         },
1651         "xyz.openbmc_project.ObjectMapper",
1652         "/xyz/openbmc_project/object_mapper",
1653         "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
1654         std::array<const char*, 0>());
1655 }
1656 
1657 inline void handleGet(crow::Response& res, std::string& objectPath,
1658                       std::string& destProperty)
1659 {
1660     BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1661     std::shared_ptr<std::string> propertyName =
1662         std::make_shared<std::string>(std::move(destProperty));
1663 
1664     std::shared_ptr<std::string> path =
1665         std::make_shared<std::string>(std::move(objectPath));
1666 
1667     using GetObjectType =
1668         std::vector<std::pair<std::string, std::vector<std::string>>>;
1669     crow::connections::systemBus->async_method_call(
1670         [&res, path, propertyName](const boost::system::error_code ec,
1671                                    const GetObjectType& object_names) {
1672             if (ec || object_names.size() <= 0)
1673             {
1674                 setErrorResponse(res, boost::beast::http::status::not_found,
1675                                  notFoundDesc, notFoundMsg);
1676                 res.end();
1677                 return;
1678             }
1679             std::shared_ptr<nlohmann::json> response =
1680                 std::make_shared<nlohmann::json>(nlohmann::json::object());
1681             // The mapper should never give us an empty interface names
1682             // list, but check anyway
1683             for (const std::pair<std::string, std::vector<std::string>>&
1684                      connection : object_names)
1685             {
1686                 const std::vector<std::string>& interfaceNames =
1687                     connection.second;
1688 
1689                 if (interfaceNames.size() <= 0)
1690                 {
1691                     setErrorResponse(res, boost::beast::http::status::not_found,
1692                                      notFoundDesc, notFoundMsg);
1693                     res.end();
1694                     return;
1695                 }
1696 
1697                 for (const std::string& interface : interfaceNames)
1698                 {
1699                     sdbusplus::message::message m =
1700                         crow::connections::systemBus->new_method_call(
1701                             connection.first.c_str(), path->c_str(),
1702                             "org.freedesktop.DBus.Properties", "GetAll");
1703                     m.append(interface);
1704                     crow::connections::systemBus->async_send(
1705                         m, [&res, response,
1706                             propertyName](const boost::system::error_code ec2,
1707                                           sdbusplus::message::message& msg) {
1708                             if (ec2)
1709                             {
1710                                 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1711                                                  << ec2;
1712                             }
1713                             else
1714                             {
1715                                 nlohmann::json properties;
1716                                 int r =
1717                                     convertDBusToJSON("a{sv}", msg, properties);
1718                                 if (r < 0)
1719                                 {
1720                                     BMCWEB_LOG_ERROR
1721                                         << "convertDBusToJSON failed";
1722                                 }
1723                                 else
1724                                 {
1725                                     for (auto& prop : properties.items())
1726                                     {
1727                                         // if property name is empty, or
1728                                         // matches our search query, add it
1729                                         // to the response json
1730 
1731                                         if (propertyName->empty())
1732                                         {
1733                                             (*response)[prop.key()] =
1734                                                 std::move(prop.value());
1735                                         }
1736                                         else if (prop.key() == *propertyName)
1737                                         {
1738                                             *response = std::move(prop.value());
1739                                         }
1740                                     }
1741                                 }
1742                             }
1743                             if (response.use_count() == 1)
1744                             {
1745                                 if (!propertyName->empty() && response->empty())
1746                                 {
1747                                     setErrorResponse(
1748                                         res,
1749                                         boost::beast::http::status::not_found,
1750                                         propNotFoundDesc, notFoundMsg);
1751                                 }
1752                                 else
1753                                 {
1754                                     res.jsonValue = {{"status", "ok"},
1755                                                      {"message", "200 OK"},
1756                                                      {"data", *response}};
1757                                 }
1758                                 res.end();
1759                             }
1760                         });
1761                 }
1762             }
1763         },
1764         "xyz.openbmc_project.ObjectMapper",
1765         "/xyz/openbmc_project/object_mapper",
1766         "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1767         std::array<std::string, 0>());
1768 }
1769 
1770 struct AsyncPutRequest
1771 {
1772     AsyncPutRequest(crow::Response& resIn) : res(resIn)
1773     {}
1774     ~AsyncPutRequest()
1775     {
1776         if (res.jsonValue.empty())
1777         {
1778             setErrorResponse(res, boost::beast::http::status::forbidden,
1779                              forbiddenMsg, forbiddenPropDesc);
1780         }
1781 
1782         res.end();
1783     }
1784 
1785     void setErrorStatus(const std::string& desc)
1786     {
1787         setErrorResponse(res, boost::beast::http::status::internal_server_error,
1788                          desc, badReqMsg);
1789     }
1790 
1791     crow::Response& res;
1792     std::string objectPath;
1793     std::string propertyName;
1794     nlohmann::json propertyValue;
1795 };
1796 
1797 inline void handlePut(const crow::Request& req, crow::Response& res,
1798                       const std::string& objectPath,
1799                       const std::string& destProperty)
1800 {
1801     if (destProperty.empty())
1802     {
1803         setErrorResponse(res, boost::beast::http::status::forbidden,
1804                          forbiddenResDesc, forbiddenMsg);
1805         res.end();
1806         return;
1807     }
1808 
1809     nlohmann::json requestDbusData =
1810         nlohmann::json::parse(req.body, nullptr, false);
1811 
1812     if (requestDbusData.is_discarded())
1813     {
1814         setErrorResponse(res, boost::beast::http::status::bad_request,
1815                          noJsonDesc, badReqMsg);
1816         res.end();
1817         return;
1818     }
1819 
1820     nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1821     if (propertyIt == requestDbusData.end())
1822     {
1823         setErrorResponse(res, boost::beast::http::status::bad_request,
1824                          noJsonDesc, badReqMsg);
1825         res.end();
1826         return;
1827     }
1828     const nlohmann::json& propertySetValue = *propertyIt;
1829     auto transaction = std::make_shared<AsyncPutRequest>(res);
1830     transaction->objectPath = objectPath;
1831     transaction->propertyName = destProperty;
1832     transaction->propertyValue = propertySetValue;
1833 
1834     using GetObjectType =
1835         std::vector<std::pair<std::string, std::vector<std::string>>>;
1836 
1837     crow::connections::systemBus->async_method_call(
1838         [transaction](const boost::system::error_code ec2,
1839                       const GetObjectType& object_names) {
1840             if (!ec2 && object_names.size() <= 0)
1841             {
1842                 setErrorResponse(transaction->res,
1843                                  boost::beast::http::status::not_found,
1844                                  propNotFoundDesc, notFoundMsg);
1845                 return;
1846             }
1847 
1848             for (const std::pair<std::string, std::vector<std::string>>&
1849                      connection : object_names)
1850             {
1851                 const std::string& connectionName = connection.first;
1852 
1853                 crow::connections::systemBus->async_method_call(
1854                     [connectionName{std::string(connectionName)},
1855                      transaction](const boost::system::error_code ec3,
1856                                   const std::string& introspectXml) {
1857                         if (ec3)
1858                         {
1859                             BMCWEB_LOG_ERROR
1860                                 << "Introspect call failed with error: "
1861                                 << ec3.message()
1862                                 << " on process: " << connectionName;
1863                             transaction->setErrorStatus("Unexpected Error");
1864                             return;
1865                         }
1866                         tinyxml2::XMLDocument doc;
1867 
1868                         doc.Parse(introspectXml.c_str());
1869                         tinyxml2::XMLNode* pRoot =
1870                             doc.FirstChildElement("node");
1871                         if (pRoot == nullptr)
1872                         {
1873                             BMCWEB_LOG_ERROR << "XML document failed to parse: "
1874                                              << introspectXml;
1875                             transaction->setErrorStatus("Unexpected Error");
1876                             return;
1877                         }
1878                         tinyxml2::XMLElement* ifaceNode =
1879                             pRoot->FirstChildElement("interface");
1880                         while (ifaceNode != nullptr)
1881                         {
1882                             const char* interfaceName =
1883                                 ifaceNode->Attribute("name");
1884                             BMCWEB_LOG_DEBUG << "found interface "
1885                                              << interfaceName;
1886                             tinyxml2::XMLElement* propNode =
1887                                 ifaceNode->FirstChildElement("property");
1888                             while (propNode != nullptr)
1889                             {
1890                                 const char* propertyName =
1891                                     propNode->Attribute("name");
1892                                 BMCWEB_LOG_DEBUG << "Found property "
1893                                                  << propertyName;
1894                                 if (propertyName == transaction->propertyName)
1895                                 {
1896                                     const char* argType =
1897                                         propNode->Attribute("type");
1898                                     if (argType != nullptr)
1899                                     {
1900                                         sdbusplus::message::message m =
1901                                             crow::connections::systemBus
1902                                                 ->new_method_call(
1903                                                     connectionName.c_str(),
1904                                                     transaction->objectPath
1905                                                         .c_str(),
1906                                                     "org.freedesktop.DBus."
1907                                                     "Properties",
1908                                                     "Set");
1909                                         m.append(interfaceName,
1910                                                  transaction->propertyName);
1911                                         int r = sd_bus_message_open_container(
1912                                             m.get(), SD_BUS_TYPE_VARIANT,
1913                                             argType);
1914                                         if (r < 0)
1915                                         {
1916                                             transaction->setErrorStatus(
1917                                                 "Unexpected Error");
1918                                             return;
1919                                         }
1920                                         r = convertJsonToDbus(
1921                                             m.get(), argType,
1922                                             transaction->propertyValue);
1923                                         if (r < 0)
1924                                         {
1925                                             if (r == -ERANGE)
1926                                             {
1927                                                 transaction->setErrorStatus(
1928                                                     "Provided property value "
1929                                                     "is out of range for the "
1930                                                     "property type");
1931                                             }
1932                                             else
1933                                             {
1934                                                 transaction->setErrorStatus(
1935                                                     "Invalid arg type");
1936                                             }
1937                                             return;
1938                                         }
1939                                         r = sd_bus_message_close_container(
1940                                             m.get());
1941                                         if (r < 0)
1942                                         {
1943                                             transaction->setErrorStatus(
1944                                                 "Unexpected Error");
1945                                             return;
1946                                         }
1947                                         crow::connections::systemBus
1948                                             ->async_send(
1949                                                 m,
1950                                                 [transaction](
1951                                                     boost::system::error_code
1952                                                         ec,
1953                                                     sdbusplus::message::message&
1954                                                         m2) {
1955                                                     BMCWEB_LOG_DEBUG << "sent";
1956                                                     if (ec)
1957                                                     {
1958                                                         const sd_bus_error* e =
1959                                                             m2.get_error();
1960                                                         setErrorResponse(
1961                                                             transaction->res,
1962                                                             boost::beast::http::
1963                                                                 status::
1964                                                                     forbidden,
1965                                                             (e) ? e->name
1966                                                                 : ec.category()
1967                                                                       .name(),
1968                                                             (e) ? e->message
1969                                                                 : ec.message());
1970                                                     }
1971                                                     else
1972                                                     {
1973                                                         transaction->res
1974                                                             .jsonValue = {
1975                                                             {"status", "ok"},
1976                                                             {"message",
1977                                                              "200 OK"},
1978                                                             {"data", nullptr}};
1979                                                     }
1980                                                 });
1981                                     }
1982                                 }
1983                                 propNode =
1984                                     propNode->NextSiblingElement("property");
1985                             }
1986                             ifaceNode =
1987                                 ifaceNode->NextSiblingElement("interface");
1988                         }
1989                     },
1990                     connectionName, transaction->objectPath,
1991                     "org.freedesktop.DBus.Introspectable", "Introspect");
1992             }
1993         },
1994         "xyz.openbmc_project.ObjectMapper",
1995         "/xyz/openbmc_project/object_mapper",
1996         "xyz.openbmc_project.ObjectMapper", "GetObject",
1997         transaction->objectPath, std::array<std::string, 0>());
1998 }
1999 
2000 inline void handleDBusUrl(const crow::Request& req, crow::Response& res,
2001                           std::string& objectPath)
2002 {
2003 
2004     // If accessing a single attribute, fill in and update objectPath,
2005     // otherwise leave destProperty blank
2006     std::string destProperty = "";
2007     const char* attrSeperator = "/attr/";
2008     size_t attrPosition = objectPath.find(attrSeperator);
2009     if (attrPosition != objectPath.npos)
2010     {
2011         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2012                                          objectPath.length());
2013         objectPath = objectPath.substr(0, attrPosition);
2014     }
2015 
2016     if (req.method() == boost::beast::http::verb::post)
2017     {
2018         constexpr const char* actionSeperator = "/action/";
2019         size_t actionPosition = objectPath.find(actionSeperator);
2020         if (actionPosition != objectPath.npos)
2021         {
2022             std::string postProperty =
2023                 objectPath.substr((actionPosition + strlen(actionSeperator)),
2024                                   objectPath.length());
2025             objectPath = objectPath.substr(0, actionPosition);
2026             handleAction(req, res, objectPath, postProperty);
2027             return;
2028         }
2029     }
2030     else if (req.method() == boost::beast::http::verb::get)
2031     {
2032         if (boost::ends_with(objectPath, "/enumerate"))
2033         {
2034             objectPath.erase(objectPath.end() - sizeof("enumerate"),
2035                              objectPath.end());
2036             handleEnumerate(res, objectPath);
2037         }
2038         else if (boost::ends_with(objectPath, "/list"))
2039         {
2040             objectPath.erase(objectPath.end() - sizeof("list"),
2041                              objectPath.end());
2042             handleList(res, objectPath);
2043         }
2044         else
2045         {
2046             // Trim any trailing "/" at the end
2047             if (boost::ends_with(objectPath, "/"))
2048             {
2049                 objectPath.pop_back();
2050                 handleList(res, objectPath, 1);
2051             }
2052             else
2053             {
2054                 handleGet(res, objectPath, destProperty);
2055             }
2056         }
2057         return;
2058     }
2059     else if (req.method() == boost::beast::http::verb::put)
2060     {
2061         handlePut(req, res, objectPath, destProperty);
2062         return;
2063     }
2064     else if (req.method() == boost::beast::http::verb::delete_)
2065     {
2066         handleDelete(res, objectPath);
2067         return;
2068     }
2069 
2070     setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2071                      methodNotAllowedDesc, methodNotAllowedMsg);
2072     res.end();
2073 }
2074 
2075 inline void requestRoutes(App& app)
2076 {
2077     BMCWEB_ROUTE(app, "/bus/")
2078         .privileges({"Login"})
2079         .methods(boost::beast::http::verb::get)(
2080             [](const crow::Request&, crow::Response& res) {
2081                 res.jsonValue = {{"buses", {{{"name", "system"}}}},
2082                                  {"status", "ok"}};
2083                 res.end();
2084             });
2085 
2086     BMCWEB_ROUTE(app, "/bus/system/")
2087         .privileges({"Login"})
2088         .methods(boost::beast::http::verb::get)(
2089             [](const crow::Request&, crow::Response& res) {
2090                 auto myCallback = [&res](const boost::system::error_code ec,
2091                                          std::vector<std::string>& names) {
2092                     if (ec)
2093                     {
2094                         BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2095                         res.result(
2096                             boost::beast::http::status::internal_server_error);
2097                     }
2098                     else
2099                     {
2100                         std::sort(names.begin(), names.end());
2101                         res.jsonValue = {{"status", "ok"}};
2102                         auto& objectsSub = res.jsonValue["objects"];
2103                         for (auto& name : names)
2104                         {
2105                             objectsSub.push_back({{"name", name}});
2106                         }
2107                     }
2108                     res.end();
2109                 };
2110                 crow::connections::systemBus->async_method_call(
2111                     std::move(myCallback), "org.freedesktop.DBus", "/",
2112                     "org.freedesktop.DBus", "ListNames");
2113             });
2114 
2115     BMCWEB_ROUTE(app, "/list/")
2116         .privileges({"Login"})
2117         .methods(boost::beast::http::verb::get)(
2118             [](const crow::Request&, crow::Response& res) {
2119                 handleList(res, "/");
2120             });
2121 
2122     BMCWEB_ROUTE(app, "/xyz/<path>")
2123         .privileges({"Login"})
2124         .methods(boost::beast::http::verb::get)([](const crow::Request& req,
2125                                                    crow::Response& res,
2126                                                    const std::string& path) {
2127             std::string objectPath = "/xyz/" + path;
2128             handleDBusUrl(req, res, objectPath);
2129         });
2130 
2131     BMCWEB_ROUTE(app, "/xyz/<path>")
2132         .privileges({"ConfigureComponents", "ConfigureManager"})
2133         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2134                  boost::beast::http::verb::delete_)(
2135             [](const crow::Request& req, crow::Response& res,
2136                const std::string& path) {
2137                 std::string objectPath = "/xyz/" + path;
2138                 handleDBusUrl(req, res, objectPath);
2139             });
2140 
2141     BMCWEB_ROUTE(app, "/org/<path>")
2142         .privileges({"Login"})
2143         .methods(boost::beast::http::verb::get)([](const crow::Request& req,
2144                                                    crow::Response& res,
2145                                                    const std::string& path) {
2146             std::string objectPath = "/org/" + path;
2147             handleDBusUrl(req, res, objectPath);
2148         });
2149 
2150     BMCWEB_ROUTE(app, "/org/<path>")
2151         .privileges({"ConfigureComponents", "ConfigureManager"})
2152         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2153                  boost::beast::http::verb::delete_)(
2154             [](const crow::Request& req, crow::Response& res,
2155                const std::string& path) {
2156                 std::string objectPath = "/org/" + path;
2157                 handleDBusUrl(req, res, objectPath);
2158             });
2159 
2160     BMCWEB_ROUTE(app, "/download/dump/<str>/")
2161         .privileges({"ConfigureManager"})
2162         .methods(boost::beast::http::verb::get)([](const crow::Request&,
2163                                                    crow::Response& res,
2164                                                    const std::string& dumpId) {
2165             std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
2166             if (!std::regex_match(dumpId, validFilename))
2167             {
2168                 res.result(boost::beast::http::status::bad_request);
2169                 res.end();
2170                 return;
2171             }
2172             std::filesystem::path loc(
2173                 "/var/lib/phosphor-debug-collector/dumps");
2174 
2175             loc /= dumpId;
2176 
2177             if (!std::filesystem::exists(loc) ||
2178                 !std::filesystem::is_directory(loc))
2179             {
2180                 BMCWEB_LOG_ERROR << loc << "Not found";
2181                 res.result(boost::beast::http::status::not_found);
2182                 res.end();
2183                 return;
2184             }
2185             std::filesystem::directory_iterator files(loc);
2186 
2187             for (auto& file : files)
2188             {
2189                 std::ifstream readFile(file.path());
2190                 if (!readFile.good())
2191                 {
2192                     continue;
2193                 }
2194 
2195                 res.addHeader("Content-Type", "application/octet-stream");
2196 
2197                 // Assuming only one dump file will be present in the dump id
2198                 // directory
2199                 std::string dumpFileName = file.path().filename().string();
2200 
2201                 // Filename should be in alphanumeric, dot and underscore
2202                 // Its based on phosphor-debug-collector application dumpfile
2203                 // format
2204                 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2205                 if (!std::regex_match(dumpFileName, dumpFileRegex))
2206                 {
2207                     BMCWEB_LOG_ERROR << "Invalid dump filename "
2208                                      << dumpFileName;
2209                     res.result(boost::beast::http::status::not_found);
2210                     res.end();
2211                     return;
2212                 }
2213                 std::string contentDispositionParam =
2214                     "attachment; filename=\"" + dumpFileName + "\"";
2215 
2216                 res.addHeader("Content-Disposition", contentDispositionParam);
2217 
2218                 res.body() = {std::istreambuf_iterator<char>(readFile),
2219                               std::istreambuf_iterator<char>()};
2220                 res.end();
2221                 return;
2222             }
2223             res.result(boost::beast::http::status::not_found);
2224             res.end();
2225             return;
2226         });
2227 
2228     BMCWEB_ROUTE(app, "/bus/system/<str>/")
2229         .privileges({"Login"})
2230 
2231         .methods(boost::beast::http::verb::get)(
2232             [](const crow::Request&, crow::Response& res,
2233                const std::string& Connection) {
2234                 introspectObjects(Connection, "/",
2235                                   std::make_shared<bmcweb::AsyncResp>(res));
2236             });
2237 
2238     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2239         .privileges({"ConfigureComponents", "ConfigureManager"})
2240         .methods(
2241             boost::beast::http::verb::get,
2242             boost::beast::http::verb::post)([](const crow::Request& req,
2243                                                crow::Response& res,
2244                                                const std::string& processName,
2245                                                const std::string&
2246                                                    requestedPath) {
2247             std::vector<std::string> strs;
2248             boost::split(strs, requestedPath, boost::is_any_of("/"));
2249             std::string objectPath;
2250             std::string interfaceName;
2251             std::string methodName;
2252             auto it = strs.begin();
2253             if (it == strs.end())
2254             {
2255                 objectPath = "/";
2256             }
2257             while (it != strs.end())
2258             {
2259                 // Check if segment contains ".".  If it does, it must be an
2260                 // interface
2261                 if (it->find(".") != std::string::npos)
2262                 {
2263                     break;
2264                     // This check is necessary as the trailing slash gets
2265                     // parsed as part of our <path> specifier above, which
2266                     // causes the normal trailing backslash redirector to
2267                     // fail.
2268                 }
2269                 else if (!it->empty())
2270                 {
2271                     objectPath += "/" + *it;
2272                 }
2273                 it++;
2274             }
2275             if (it != strs.end())
2276             {
2277                 interfaceName = *it;
2278                 it++;
2279 
2280                 // after interface, we might have a method name
2281                 if (it != strs.end())
2282                 {
2283                     methodName = *it;
2284                     it++;
2285                 }
2286             }
2287             if (it != strs.end())
2288             {
2289                 // if there is more levels past the method name, something
2290                 // went wrong, return not found
2291                 res.result(boost::beast::http::status::not_found);
2292                 res.end();
2293                 return;
2294             }
2295             if (interfaceName.empty())
2296             {
2297                 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2298                     std::make_shared<bmcweb::AsyncResp>(res);
2299 
2300                 crow::connections::systemBus->async_method_call(
2301                     [asyncResp, processName,
2302                      objectPath](const boost::system::error_code ec,
2303                                  const std::string& introspect_xml) {
2304                         if (ec)
2305                         {
2306                             BMCWEB_LOG_ERROR
2307                                 << "Introspect call failed with error: "
2308                                 << ec.message()
2309                                 << " on process: " << processName
2310                                 << " path: " << objectPath << "\n";
2311                             return;
2312                         }
2313                         tinyxml2::XMLDocument doc;
2314 
2315                         doc.Parse(introspect_xml.c_str());
2316                         tinyxml2::XMLNode* pRoot =
2317                             doc.FirstChildElement("node");
2318                         if (pRoot == nullptr)
2319                         {
2320                             BMCWEB_LOG_ERROR << "XML document failed to parse "
2321                                              << processName << " " << objectPath
2322                                              << "\n";
2323                             asyncResp->res.jsonValue = {
2324                                 {"status", "XML parse error"}};
2325                             asyncResp->res.result(boost::beast::http::status::
2326                                                       internal_server_error);
2327                             return;
2328                         }
2329 
2330                         BMCWEB_LOG_DEBUG << introspect_xml;
2331                         asyncResp->res.jsonValue = {
2332                             {"status", "ok"},
2333                             {"bus_name", processName},
2334                             {"object_path", objectPath}};
2335                         nlohmann::json& interfacesArray =
2336                             asyncResp->res.jsonValue["interfaces"];
2337                         interfacesArray = nlohmann::json::array();
2338                         tinyxml2::XMLElement* interface =
2339                             pRoot->FirstChildElement("interface");
2340 
2341                         while (interface != nullptr)
2342                         {
2343                             const char* ifaceName =
2344                                 interface->Attribute("name");
2345                             if (ifaceName != nullptr)
2346                             {
2347                                 interfacesArray.push_back(
2348                                     {{"name", ifaceName}});
2349                             }
2350 
2351                             interface =
2352                                 interface->NextSiblingElement("interface");
2353                         }
2354                     },
2355                     processName, objectPath,
2356                     "org.freedesktop.DBus.Introspectable", "Introspect");
2357             }
2358             else if (methodName.empty())
2359             {
2360                 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2361                     std::make_shared<bmcweb::AsyncResp>(res);
2362 
2363                 crow::connections::systemBus->async_method_call(
2364                     [asyncResp, processName, objectPath,
2365                      interfaceName](const boost::system::error_code ec,
2366                                     const std::string& introspect_xml) {
2367                         if (ec)
2368                         {
2369                             BMCWEB_LOG_ERROR
2370                                 << "Introspect call failed with error: "
2371                                 << ec.message()
2372                                 << " on process: " << processName
2373                                 << " path: " << objectPath << "\n";
2374                             return;
2375                         }
2376                         tinyxml2::XMLDocument doc;
2377 
2378                         doc.Parse(introspect_xml.data(), introspect_xml.size());
2379                         tinyxml2::XMLNode* pRoot =
2380                             doc.FirstChildElement("node");
2381                         if (pRoot == nullptr)
2382                         {
2383                             BMCWEB_LOG_ERROR << "XML document failed to parse "
2384                                              << processName << " " << objectPath
2385                                              << "\n";
2386                             asyncResp->res.result(boost::beast::http::status::
2387                                                       internal_server_error);
2388                             return;
2389                         }
2390                         asyncResp->res.jsonValue = {
2391                             {"status", "ok"},
2392                             {"bus_name", processName},
2393                             {"interface", interfaceName},
2394                             {"object_path", objectPath}};
2395 
2396                         nlohmann::json& methodsArray =
2397                             asyncResp->res.jsonValue["methods"];
2398                         methodsArray = nlohmann::json::array();
2399 
2400                         nlohmann::json& signalsArray =
2401                             asyncResp->res.jsonValue["signals"];
2402                         signalsArray = nlohmann::json::array();
2403 
2404                         nlohmann::json& propertiesObj =
2405                             asyncResp->res.jsonValue["properties"];
2406                         propertiesObj = nlohmann::json::object();
2407 
2408                         // if we know we're the only call, build the
2409                         // json directly
2410                         tinyxml2::XMLElement* interface =
2411                             pRoot->FirstChildElement("interface");
2412                         while (interface != nullptr)
2413                         {
2414                             const char* ifaceName =
2415                                 interface->Attribute("name");
2416 
2417                             if (ifaceName != nullptr &&
2418                                 ifaceName == interfaceName)
2419                             {
2420                                 break;
2421                             }
2422 
2423                             interface =
2424                                 interface->NextSiblingElement("interface");
2425                         }
2426                         if (interface == nullptr)
2427                         {
2428                             // if we got to the end of the list and
2429                             // never found a match, throw 404
2430                             asyncResp->res.result(
2431                                 boost::beast::http::status::not_found);
2432                             return;
2433                         }
2434 
2435                         tinyxml2::XMLElement* methods =
2436                             interface->FirstChildElement("method");
2437                         while (methods != nullptr)
2438                         {
2439                             nlohmann::json argsArray = nlohmann::json::array();
2440                             tinyxml2::XMLElement* arg =
2441                                 methods->FirstChildElement("arg");
2442                             while (arg != nullptr)
2443                             {
2444                                 nlohmann::json thisArg;
2445                                 for (const char* fieldName :
2446                                      std::array<const char*, 3>{
2447                                          "name", "direction", "type"})
2448                                 {
2449                                     const char* fieldValue =
2450                                         arg->Attribute(fieldName);
2451                                     if (fieldValue != nullptr)
2452                                     {
2453                                         thisArg[fieldName] = fieldValue;
2454                                     }
2455                                 }
2456                                 argsArray.push_back(std::move(thisArg));
2457                                 arg = arg->NextSiblingElement("arg");
2458                             }
2459 
2460                             const char* name = methods->Attribute("name");
2461                             if (name != nullptr)
2462                             {
2463                                 methodsArray.push_back(
2464                                     {{"name", name},
2465                                      {"uri", "/bus/system/" + processName +
2466                                                  objectPath + "/" +
2467                                                  interfaceName + "/" + name},
2468                                      {"args", argsArray}});
2469                             }
2470                             methods = methods->NextSiblingElement("method");
2471                         }
2472                         tinyxml2::XMLElement* signals =
2473                             interface->FirstChildElement("signal");
2474                         while (signals != nullptr)
2475                         {
2476                             nlohmann::json argsArray = nlohmann::json::array();
2477 
2478                             tinyxml2::XMLElement* arg =
2479                                 signals->FirstChildElement("arg");
2480                             while (arg != nullptr)
2481                             {
2482                                 const char* name = arg->Attribute("name");
2483                                 const char* type = arg->Attribute("type");
2484                                 if (name != nullptr && type != nullptr)
2485                                 {
2486                                     argsArray.push_back({
2487                                         {"name", name},
2488                                         {"type", type},
2489                                     });
2490                                 }
2491                                 arg = arg->NextSiblingElement("arg");
2492                             }
2493                             const char* name = signals->Attribute("name");
2494                             if (name != nullptr)
2495                             {
2496                                 signalsArray.push_back(
2497                                     {{"name", name}, {"args", argsArray}});
2498                             }
2499 
2500                             signals = signals->NextSiblingElement("signal");
2501                         }
2502 
2503                         tinyxml2::XMLElement* property =
2504                             interface->FirstChildElement("property");
2505                         while (property != nullptr)
2506                         {
2507                             const char* name = property->Attribute("name");
2508                             const char* type = property->Attribute("type");
2509                             if (type != nullptr && name != nullptr)
2510                             {
2511                                 sdbusplus::message::message m =
2512                                     crow::connections::systemBus
2513                                         ->new_method_call(processName.c_str(),
2514                                                           objectPath.c_str(),
2515                                                           "org.freedesktop."
2516                                                           "DBus."
2517                                                           "Properties",
2518                                                           "Get");
2519                                 m.append(interfaceName, name);
2520                                 nlohmann::json& propertyItem =
2521                                     propertiesObj[name];
2522                                 crow::connections::systemBus->async_send(
2523                                     m, [&propertyItem, asyncResp](
2524                                            boost::system::error_code& e,
2525                                            sdbusplus::message::message& msg) {
2526                                         if (e)
2527                                         {
2528                                             return;
2529                                         }
2530 
2531                                         convertDBusToJSON("v", msg,
2532                                                           propertyItem);
2533                                     });
2534                             }
2535                             property = property->NextSiblingElement("property");
2536                         }
2537                     },
2538                     processName, objectPath,
2539                     "org.freedesktop.DBus.Introspectable", "Introspect");
2540             }
2541             else
2542             {
2543                 if (req.method() != boost::beast::http::verb::post)
2544                 {
2545                     res.result(boost::beast::http::status::not_found);
2546                     res.end();
2547                     return;
2548                 }
2549 
2550                 nlohmann::json requestDbusData =
2551                     nlohmann::json::parse(req.body, nullptr, false);
2552 
2553                 if (requestDbusData.is_discarded())
2554                 {
2555                     res.result(boost::beast::http::status::bad_request);
2556                     res.end();
2557                     return;
2558                 }
2559                 if (!requestDbusData.is_array())
2560                 {
2561                     res.result(boost::beast::http::status::bad_request);
2562                     res.end();
2563                     return;
2564                 }
2565                 auto transaction = std::make_shared<InProgressActionData>(res);
2566 
2567                 transaction->path = objectPath;
2568                 transaction->methodName = methodName;
2569                 transaction->arguments = std::move(requestDbusData);
2570 
2571                 findActionOnInterface(transaction, processName);
2572             }
2573         });
2574 }
2575 } // namespace openbmc_mapper
2576 } // namespace crow
2577