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