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