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