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                     sdbusplus::message::message m =
1613                         crow::connections::systemBus->new_method_call(
1614                             connection.first.c_str(), path->c_str(),
1615                             "org.freedesktop.DBus.Properties", "GetAll");
1616                     m.append(interface);
1617                     crow::connections::systemBus->async_send(
1618                         m, [&res, response,
1619                             propertyName](const boost::system::error_code ec,
1620                                           sdbusplus::message::message &msg) {
1621                             if (ec)
1622                             {
1623                                 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1624                                                  << ec;
1625                             }
1626                             else
1627                             {
1628                                 nlohmann::json properties;
1629                                 int r =
1630                                     convertDBusToJSON("a{sv}", msg, properties);
1631                                 if (r < 0)
1632                                 {
1633                                     BMCWEB_LOG_ERROR
1634                                         << "convertDBusToJSON failed";
1635                                 }
1636                                 else
1637                                 {
1638                                     for (auto &prop : properties.items())
1639                                     {
1640                                         // if property name is empty, or matches
1641                                         // our search query, add it to the
1642                                         // response json
1643 
1644                                         if (propertyName->empty())
1645                                         {
1646                                             (*response)[prop.key()] =
1647                                                 std::move(prop.value());
1648                                         }
1649                                         else if (prop.key() == *propertyName)
1650                                         {
1651                                             *response = std::move(prop.value());
1652                                         }
1653                                     }
1654                                 }
1655                             }
1656                             if (response.use_count() == 1)
1657                             {
1658                                 if (!propertyName->empty() && response->empty())
1659                                 {
1660                                     setErrorResponse(
1661                                         res,
1662                                         boost::beast::http::status::not_found,
1663                                         propNotFoundDesc, notFoundMsg);
1664                                 }
1665                                 else
1666                                 {
1667                                     res.jsonValue = {{"status", "ok"},
1668                                                      {"message", "200 OK"},
1669                                                      {"data", *response}};
1670                                 }
1671                                 res.end();
1672                             }
1673                         });
1674                 }
1675             }
1676         },
1677         "xyz.openbmc_project.ObjectMapper",
1678         "/xyz/openbmc_project/object_mapper",
1679         "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1680         std::array<std::string, 0>());
1681 }
1682 
1683 struct AsyncPutRequest
1684 {
1685     AsyncPutRequest(crow::Response &res) : res(res)
1686     {
1687     }
1688     ~AsyncPutRequest()
1689     {
1690         if (res.jsonValue.empty())
1691         {
1692             setErrorResponse(res, boost::beast::http::status::forbidden,
1693                              forbiddenMsg, forbiddenPropDesc);
1694         }
1695 
1696         res.end();
1697     }
1698 
1699     void setErrorStatus(const std::string &desc)
1700     {
1701         setErrorResponse(res, boost::beast::http::status::internal_server_error,
1702                          desc, badReqMsg);
1703     }
1704 
1705     crow::Response &res;
1706     std::string objectPath;
1707     std::string propertyName;
1708     nlohmann::json propertyValue;
1709 };
1710 
1711 void handlePut(const crow::Request &req, crow::Response &res,
1712                const std::string &objectPath, const std::string &destProperty)
1713 {
1714     if (destProperty.empty())
1715     {
1716         setErrorResponse(res, boost::beast::http::status::forbidden,
1717                          forbiddenResDesc, forbiddenMsg);
1718         res.end();
1719         return;
1720     }
1721 
1722     nlohmann::json requestDbusData =
1723         nlohmann::json::parse(req.body, nullptr, false);
1724 
1725     if (requestDbusData.is_discarded())
1726     {
1727         setErrorResponse(res, boost::beast::http::status::bad_request,
1728                          noJsonDesc, badReqMsg);
1729         res.end();
1730         return;
1731     }
1732 
1733     nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1734     if (propertyIt == requestDbusData.end())
1735     {
1736         setErrorResponse(res, boost::beast::http::status::bad_request,
1737                          noJsonDesc, badReqMsg);
1738         res.end();
1739         return;
1740     }
1741     const nlohmann::json &propertySetValue = *propertyIt;
1742     auto transaction = std::make_shared<AsyncPutRequest>(res);
1743     transaction->objectPath = objectPath;
1744     transaction->propertyName = destProperty;
1745     transaction->propertyValue = propertySetValue;
1746 
1747     using GetObjectType =
1748         std::vector<std::pair<std::string, std::vector<std::string>>>;
1749 
1750     crow::connections::systemBus->async_method_call(
1751         [transaction](const boost::system::error_code ec,
1752                       const GetObjectType &object_names) {
1753             if (!ec && object_names.size() <= 0)
1754             {
1755                 setErrorResponse(transaction->res,
1756                                  boost::beast::http::status::not_found,
1757                                  propNotFoundDesc, notFoundMsg);
1758                 return;
1759             }
1760 
1761             for (const std::pair<std::string, std::vector<std::string>>
1762                      connection : object_names)
1763             {
1764                 const std::string &connectionName = connection.first;
1765 
1766                 crow::connections::systemBus->async_method_call(
1767                     [connectionName{std::string(connectionName)},
1768                      transaction](const boost::system::error_code ec,
1769                                   const std::string &introspectXml) {
1770                         if (ec)
1771                         {
1772                             BMCWEB_LOG_ERROR
1773                                 << "Introspect call failed with error: "
1774                                 << ec.message()
1775                                 << " on process: " << connectionName;
1776                             transaction->setErrorStatus("Unexpected Error");
1777                             return;
1778                         }
1779                         tinyxml2::XMLDocument doc;
1780 
1781                         doc.Parse(introspectXml.c_str());
1782                         tinyxml2::XMLNode *pRoot =
1783                             doc.FirstChildElement("node");
1784                         if (pRoot == nullptr)
1785                         {
1786                             BMCWEB_LOG_ERROR << "XML document failed to parse: "
1787                                              << introspectXml;
1788                             transaction->setErrorStatus("Unexpected Error");
1789                             return;
1790                         }
1791                         tinyxml2::XMLElement *ifaceNode =
1792                             pRoot->FirstChildElement("interface");
1793                         while (ifaceNode != nullptr)
1794                         {
1795                             const char *interfaceName =
1796                                 ifaceNode->Attribute("name");
1797                             BMCWEB_LOG_DEBUG << "found interface "
1798                                              << interfaceName;
1799                             tinyxml2::XMLElement *propNode =
1800                                 ifaceNode->FirstChildElement("property");
1801                             while (propNode != nullptr)
1802                             {
1803                                 const char *propertyName =
1804                                     propNode->Attribute("name");
1805                                 BMCWEB_LOG_DEBUG << "Found property "
1806                                                  << propertyName;
1807                                 if (propertyName == transaction->propertyName)
1808                                 {
1809                                     const char *argType =
1810                                         propNode->Attribute("type");
1811                                     if (argType != nullptr)
1812                                     {
1813                                         sdbusplus::message::message m =
1814                                             crow::connections::systemBus
1815                                                 ->new_method_call(
1816                                                     connectionName.c_str(),
1817                                                     transaction->objectPath
1818                                                         .c_str(),
1819                                                     "org.freedesktop.DBus."
1820                                                     "Properties",
1821                                                     "Set");
1822                                         m.append(interfaceName,
1823                                                  transaction->propertyName);
1824                                         int r = sd_bus_message_open_container(
1825                                             m.get(), SD_BUS_TYPE_VARIANT,
1826                                             argType);
1827                                         if (r < 0)
1828                                         {
1829                                             transaction->setErrorStatus(
1830                                                 "Unexpected Error");
1831                                             return;
1832                                         }
1833                                         r = convertJsonToDbus(
1834                                             m.get(), argType,
1835                                             transaction->propertyValue);
1836                                         if (r < 0)
1837                                         {
1838                                             transaction->setErrorStatus(
1839                                                 "Invalid arg type");
1840                                             return;
1841                                         }
1842                                         r = sd_bus_message_close_container(
1843                                             m.get());
1844                                         if (r < 0)
1845                                         {
1846                                             transaction->setErrorStatus(
1847                                                 "Unexpected Error");
1848                                             return;
1849                                         }
1850 
1851                                         crow::connections::systemBus
1852                                             ->async_send(
1853                                                 m,
1854                                                 [transaction](
1855                                                     boost::system::error_code
1856                                                         ec,
1857                                                     sdbusplus::message::message
1858                                                         &m) {
1859                                                     BMCWEB_LOG_DEBUG << "sent";
1860                                                     if (ec)
1861                                                     {
1862                                                         setErrorResponse(
1863                                                             transaction->res,
1864                                                             boost::beast::http::
1865                                                                 status::
1866                                                                     forbidden,
1867                                                             forbiddenPropDesc,
1868                                                             ec.message());
1869                                                     }
1870                                                     else
1871                                                     {
1872                                                         transaction->res
1873                                                             .jsonValue = {
1874                                                             {"status", "ok"},
1875                                                             {"message",
1876                                                              "200 OK"},
1877                                                             {"data", nullptr}};
1878                                                     }
1879                                                 });
1880                                     }
1881                                 }
1882                                 propNode =
1883                                     propNode->NextSiblingElement("property");
1884                             }
1885                             ifaceNode =
1886                                 ifaceNode->NextSiblingElement("interface");
1887                         }
1888                     },
1889                     connectionName, transaction->objectPath,
1890                     "org.freedesktop.DBus.Introspectable", "Introspect");
1891             }
1892         },
1893         "xyz.openbmc_project.ObjectMapper",
1894         "/xyz/openbmc_project/object_mapper",
1895         "xyz.openbmc_project.ObjectMapper", "GetObject",
1896         transaction->objectPath, std::array<std::string, 0>());
1897 }
1898 
1899 inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1900                           std::string &objectPath)
1901 {
1902 
1903     // If accessing a single attribute, fill in and update objectPath,
1904     // otherwise leave destProperty blank
1905     std::string destProperty = "";
1906     const char *attrSeperator = "/attr/";
1907     size_t attrPosition = objectPath.find(attrSeperator);
1908     if (attrPosition != objectPath.npos)
1909     {
1910         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1911                                          objectPath.length());
1912         objectPath = objectPath.substr(0, attrPosition);
1913     }
1914 
1915     if (req.method() == "POST"_method)
1916     {
1917         constexpr const char *actionSeperator = "/action/";
1918         size_t actionPosition = objectPath.find(actionSeperator);
1919         if (actionPosition != objectPath.npos)
1920         {
1921             std::string postProperty =
1922                 objectPath.substr((actionPosition + strlen(actionSeperator)),
1923                                   objectPath.length());
1924             objectPath = objectPath.substr(0, actionPosition);
1925             handleAction(req, res, objectPath, postProperty);
1926             return;
1927         }
1928     }
1929     else if (req.method() == "GET"_method)
1930     {
1931         if (boost::ends_with(objectPath, "/enumerate"))
1932         {
1933             objectPath.erase(objectPath.end() - sizeof("enumerate"),
1934                              objectPath.end());
1935             handleEnumerate(res, objectPath);
1936         }
1937         else if (boost::ends_with(objectPath, "/list"))
1938         {
1939             objectPath.erase(objectPath.end() - sizeof("list"),
1940                              objectPath.end());
1941             handleList(res, objectPath);
1942         }
1943         else
1944         {
1945             // Trim any trailing "/" at the end
1946             if (boost::ends_with(objectPath, "/"))
1947             {
1948                 objectPath.pop_back();
1949                 handleList(res, objectPath, 1);
1950             }
1951             else
1952             {
1953                 handleGet(res, objectPath, destProperty);
1954             }
1955         }
1956         return;
1957     }
1958     else if (req.method() == "PUT"_method)
1959     {
1960         handlePut(req, res, objectPath, destProperty);
1961         return;
1962     }
1963     else if (req.method() == "DELETE"_method)
1964     {
1965         handleDelete(req, res, objectPath);
1966         return;
1967     }
1968 
1969     setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1970                      methodNotAllowedDesc, methodNotAllowedMsg);
1971     res.end();
1972 }
1973 
1974 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1975 {
1976     BMCWEB_ROUTE(app, "/bus/")
1977         .methods("GET"_method)(
1978             [](const crow::Request &req, crow::Response &res) {
1979                 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1980                                  {"status", "ok"}};
1981                 res.end();
1982             });
1983 
1984     BMCWEB_ROUTE(app, "/bus/system/")
1985         .methods("GET"_method)(
1986             [](const crow::Request &req, crow::Response &res) {
1987                 auto myCallback = [&res](const boost::system::error_code ec,
1988                                          std::vector<std::string> &names) {
1989                     if (ec)
1990                     {
1991                         BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1992                         res.result(
1993                             boost::beast::http::status::internal_server_error);
1994                     }
1995                     else
1996                     {
1997                         std::sort(names.begin(), names.end());
1998                         res.jsonValue = {{"status", "ok"}};
1999                         auto &objectsSub = res.jsonValue["objects"];
2000                         for (auto &name : names)
2001                         {
2002                             objectsSub.push_back({{"name", name}});
2003                         }
2004                     }
2005                     res.end();
2006                 };
2007                 crow::connections::systemBus->async_method_call(
2008                     std::move(myCallback), "org.freedesktop.DBus", "/",
2009                     "org.freedesktop.DBus", "ListNames");
2010             });
2011 
2012     BMCWEB_ROUTE(app, "/list/")
2013         .methods("GET"_method)(
2014             [](const crow::Request &req, crow::Response &res) {
2015                 handleList(res, "/");
2016             });
2017 
2018     BMCWEB_ROUTE(app, "/xyz/<path>")
2019         .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
2020             [](const crow::Request &req, crow::Response &res,
2021                const std::string &path) {
2022                 std::string objectPath = "/xyz/" + path;
2023                 handleDBusUrl(req, res, objectPath);
2024             });
2025 
2026     BMCWEB_ROUTE(app, "/org/<path>")
2027         .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
2028             [](const crow::Request &req, crow::Response &res,
2029                const std::string &path) {
2030                 std::string objectPath = "/org/" + path;
2031                 handleDBusUrl(req, res, objectPath);
2032             });
2033 
2034     BMCWEB_ROUTE(app, "/download/dump/<str>/")
2035         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2036                                   const std::string &dumpId) {
2037             std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
2038             if (!std::regex_match(dumpId, validFilename))
2039             {
2040                 res.result(boost::beast::http::status::bad_request);
2041                 res.end();
2042                 return;
2043             }
2044             std::filesystem::path loc(
2045                 "/var/lib/phosphor-debug-collector/dumps");
2046 
2047             loc /= dumpId;
2048 
2049             if (!std::filesystem::exists(loc) ||
2050                 !std::filesystem::is_directory(loc))
2051             {
2052                 BMCWEB_LOG_ERROR << loc << "Not found";
2053                 res.result(boost::beast::http::status::not_found);
2054                 res.end();
2055                 return;
2056             }
2057             std::filesystem::directory_iterator files(loc);
2058 
2059             for (auto &file : files)
2060             {
2061                 std::ifstream readFile(file.path());
2062                 if (!readFile.good())
2063                 {
2064                     continue;
2065                 }
2066                 res.addHeader("Content-Type", "application/octet-stream");
2067                 res.body() = {std::istreambuf_iterator<char>(readFile),
2068                               std::istreambuf_iterator<char>()};
2069                 res.end();
2070                 return;
2071             }
2072             res.result(boost::beast::http::status::not_found);
2073             res.end();
2074             return;
2075         });
2076 
2077     BMCWEB_ROUTE(app, "/bus/system/<str>/")
2078         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2079                                   const std::string &Connection) {
2080             introspectObjects(Connection, "/",
2081                               std::make_shared<bmcweb::AsyncResp>(res));
2082         });
2083 
2084     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2085         .methods("GET"_method,
2086                  "POST"_method)([](const crow::Request &req,
2087                                    crow::Response &res,
2088                                    const std::string &processName,
2089                                    const std::string &requestedPath) {
2090             std::vector<std::string> strs;
2091             boost::split(strs, requestedPath, boost::is_any_of("/"));
2092             std::string objectPath;
2093             std::string interfaceName;
2094             std::string methodName;
2095             auto it = strs.begin();
2096             if (it == strs.end())
2097             {
2098                 objectPath = "/";
2099             }
2100             while (it != strs.end())
2101             {
2102                 // Check if segment contains ".".  If it does, it must be an
2103                 // interface
2104                 if (it->find(".") != std::string::npos)
2105                 {
2106                     break;
2107                     // This check is neccesary as the trailing slash gets parsed
2108                     // as part of our <path> specifier above, which causes the
2109                     // normal trailing backslash redirector to fail.
2110                 }
2111                 else if (!it->empty())
2112                 {
2113                     objectPath += "/" + *it;
2114                 }
2115                 it++;
2116             }
2117             if (it != strs.end())
2118             {
2119                 interfaceName = *it;
2120                 it++;
2121 
2122                 // after interface, we might have a method name
2123                 if (it != strs.end())
2124                 {
2125                     methodName = *it;
2126                     it++;
2127                 }
2128             }
2129             if (it != strs.end())
2130             {
2131                 // if there is more levels past the method name, something went
2132                 // wrong, return not found
2133                 res.result(boost::beast::http::status::not_found);
2134                 res.end();
2135                 return;
2136             }
2137             if (interfaceName.empty())
2138             {
2139                 crow::connections::systemBus->async_method_call(
2140                     [&, processName,
2141                      objectPath](const boost::system::error_code ec,
2142                                  const std::string &introspect_xml) {
2143                         if (ec)
2144                         {
2145                             BMCWEB_LOG_ERROR
2146                                 << "Introspect call failed with error: "
2147                                 << ec.message()
2148                                 << " on process: " << processName
2149                                 << " path: " << objectPath << "\n";
2150                             return;
2151                         }
2152                         tinyxml2::XMLDocument doc;
2153 
2154                         doc.Parse(introspect_xml.c_str());
2155                         tinyxml2::XMLNode *pRoot =
2156                             doc.FirstChildElement("node");
2157                         if (pRoot == nullptr)
2158                         {
2159                             BMCWEB_LOG_ERROR << "XML document failed to parse "
2160                                              << processName << " " << objectPath
2161                                              << "\n";
2162                             res.jsonValue = {{"status", "XML parse error"}};
2163                             res.result(boost::beast::http::status::
2164                                            internal_server_error);
2165                             return;
2166                         }
2167 
2168                         BMCWEB_LOG_DEBUG << introspect_xml;
2169                         res.jsonValue = {{"status", "ok"},
2170                                          {"bus_name", processName},
2171                                          {"object_path", objectPath}};
2172                         nlohmann::json &interfacesArray =
2173                             res.jsonValue["interfaces"];
2174                         interfacesArray = nlohmann::json::array();
2175                         tinyxml2::XMLElement *interface =
2176                             pRoot->FirstChildElement("interface");
2177 
2178                         while (interface != nullptr)
2179                         {
2180                             const char *ifaceName =
2181                                 interface->Attribute("name");
2182                             if (ifaceName != nullptr)
2183                             {
2184                                 interfacesArray.push_back(
2185                                     {{"name", ifaceName}});
2186                             }
2187 
2188                             interface =
2189                                 interface->NextSiblingElement("interface");
2190                         }
2191 
2192                         res.end();
2193                     },
2194                     processName, objectPath,
2195                     "org.freedesktop.DBus.Introspectable", "Introspect");
2196             }
2197             else if (methodName.empty())
2198             {
2199                 crow::connections::systemBus->async_method_call(
2200                     [&, processName, objectPath,
2201                      interfaceName{std::move(interfaceName)}](
2202                         const boost::system::error_code ec,
2203                         const std::string &introspect_xml) {
2204                         if (ec)
2205                         {
2206                             BMCWEB_LOG_ERROR
2207                                 << "Introspect call failed with error: "
2208                                 << ec.message()
2209                                 << " on process: " << processName
2210                                 << " path: " << objectPath << "\n";
2211                         }
2212                         else
2213                         {
2214                             tinyxml2::XMLDocument doc;
2215 
2216                             doc.Parse(introspect_xml.c_str());
2217                             tinyxml2::XMLNode *pRoot =
2218                                 doc.FirstChildElement("node");
2219                             if (pRoot == nullptr)
2220                             {
2221                                 BMCWEB_LOG_ERROR
2222                                     << "XML document failed to parse "
2223                                     << processName << " " << objectPath << "\n";
2224                                 res.result(boost::beast::http::status::
2225                                                internal_server_error);
2226                             }
2227                             else
2228                             {
2229                                 tinyxml2::XMLElement *node =
2230                                     pRoot->FirstChildElement("node");
2231 
2232                                 // if we know we're the only call, build the
2233                                 // json directly
2234                                 tinyxml2::XMLElement *interface =
2235                                     pRoot->FirstChildElement("interface");
2236 
2237                                 res.jsonValue = {
2238                                     {"status", "ok"},
2239                                     {"bus_name", processName},
2240                                     {"interface", interfaceName},
2241                                     {"object_path", objectPath},
2242                                     {"properties", nlohmann::json::object()}};
2243 
2244                                 nlohmann::json &methodsArray =
2245                                     res.jsonValue["methods"];
2246                                 methodsArray = nlohmann::json::array();
2247 
2248                                 nlohmann::json &signalsArray =
2249                                     res.jsonValue["signals"];
2250                                 signalsArray = nlohmann::json::array();
2251 
2252                                 while (interface != nullptr)
2253                                 {
2254                                     const char *ifaceName =
2255                                         interface->Attribute("name");
2256 
2257                                     if (ifaceName != nullptr &&
2258                                         ifaceName == interfaceName)
2259                                     {
2260                                         tinyxml2::XMLElement *methods =
2261                                             interface->FirstChildElement(
2262                                                 "method");
2263                                         while (methods != nullptr)
2264                                         {
2265                                             nlohmann::json argsArray =
2266                                                 nlohmann::json::array();
2267                                             tinyxml2::XMLElement *arg =
2268                                                 methods->FirstChildElement(
2269                                                     "arg");
2270                                             while (arg != nullptr)
2271                                             {
2272                                                 nlohmann::json thisArg;
2273                                                 for (const char *fieldName :
2274                                                      std::array<const char *,
2275                                                                 3>{"name",
2276                                                                    "direction",
2277                                                                    "type"})
2278                                                 {
2279                                                     const char *fieldValue =
2280                                                         arg->Attribute(
2281                                                             fieldName);
2282                                                     if (fieldValue != nullptr)
2283                                                     {
2284                                                         thisArg[fieldName] =
2285                                                             fieldValue;
2286                                                     }
2287                                                 }
2288                                                 argsArray.push_back(
2289                                                     std::move(thisArg));
2290                                                 arg = arg->NextSiblingElement(
2291                                                     "arg");
2292                                             }
2293 
2294                                             const char *name =
2295                                                 methods->Attribute("name");
2296                                             if (name != nullptr)
2297                                             {
2298                                                 methodsArray.push_back(
2299                                                     {{"name", name},
2300                                                      {"uri", "/bus/system/" +
2301                                                                  processName +
2302                                                                  objectPath +
2303                                                                  "/" +
2304                                                                  interfaceName +
2305                                                                  "/" + name},
2306                                                      {"args", argsArray}});
2307                                             }
2308                                             methods =
2309                                                 methods->NextSiblingElement(
2310                                                     "method");
2311                                         }
2312                                         tinyxml2::XMLElement *signals =
2313                                             interface->FirstChildElement(
2314                                                 "signal");
2315                                         while (signals != nullptr)
2316                                         {
2317                                             nlohmann::json argsArray =
2318                                                 nlohmann::json::array();
2319 
2320                                             tinyxml2::XMLElement *arg =
2321                                                 signals->FirstChildElement(
2322                                                     "arg");
2323                                             while (arg != nullptr)
2324                                             {
2325                                                 const char *name =
2326                                                     arg->Attribute("name");
2327                                                 const char *type =
2328                                                     arg->Attribute("type");
2329                                                 if (name != nullptr &&
2330                                                     type != nullptr)
2331                                                 {
2332                                                     argsArray.push_back({
2333                                                         {"name", name},
2334                                                         {"type", type},
2335                                                     });
2336                                                 }
2337                                                 arg = arg->NextSiblingElement(
2338                                                     "arg");
2339                                             }
2340                                             const char *name =
2341                                                 signals->Attribute("name");
2342                                             if (name != nullptr)
2343                                             {
2344                                                 signalsArray.push_back(
2345                                                     {{"name", name},
2346                                                      {"args", argsArray}});
2347                                             }
2348 
2349                                             signals =
2350                                                 signals->NextSiblingElement(
2351                                                     "signal");
2352                                         }
2353 
2354                                         break;
2355                                     }
2356 
2357                                     interface = interface->NextSiblingElement(
2358                                         "interface");
2359                                 }
2360                                 if (interface == nullptr)
2361                                 {
2362                                     // if we got to the end of the list and
2363                                     // never found a match, throw 404
2364                                     res.result(
2365                                         boost::beast::http::status::not_found);
2366                                 }
2367                             }
2368                         }
2369                         res.end();
2370                     },
2371                     processName, objectPath,
2372                     "org.freedesktop.DBus.Introspectable", "Introspect");
2373             }
2374             else
2375             {
2376                 if (req.method() != "POST"_method)
2377                 {
2378                     res.result(boost::beast::http::status::not_found);
2379                     res.end();
2380                     return;
2381                 }
2382 
2383                 nlohmann::json requestDbusData =
2384                     nlohmann::json::parse(req.body, nullptr, false);
2385 
2386                 if (requestDbusData.is_discarded())
2387                 {
2388                     res.result(boost::beast::http::status::bad_request);
2389                     res.end();
2390                     return;
2391                 }
2392                 if (!requestDbusData.is_array())
2393                 {
2394                     res.result(boost::beast::http::status::bad_request);
2395                     res.end();
2396                     return;
2397                 }
2398                 auto transaction = std::make_shared<InProgressActionData>(res);
2399 
2400                 transaction->path = objectPath;
2401                 transaction->methodName = methodName;
2402                 transaction->arguments = std::move(requestDbusData);
2403 
2404                 findActionOnInterface(transaction, processName);
2405             }
2406         });
2407 }
2408 } // namespace openbmc_mapper
2409 } // namespace crow
2410