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