1 // Copyright (c) 2018 Intel Corporation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 #include <crow/app.h>
17 #include <tinyxml2.h>
18 
19 #include <async_resp.hpp>
20 #include <boost/algorithm/string.hpp>
21 #include <boost/container/flat_set.hpp>
22 #include <dbus_singleton.hpp>
23 #include <dbus_utility.hpp>
24 #include <experimental/filesystem>
25 #include <fstream>
26 #include <sdbusplus/message/types.hpp>
27 
28 namespace crow
29 {
30 namespace openbmc_mapper
31 {
32 
33 using GetSubTreeType = std::vector<
34     std::pair<std::string,
35               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36 
37 const std::string notFoundMsg = "404 Not Found";
38 const std::string badReqMsg = "400 Bad Request";
39 const std::string methodNotAllowedMsg = "405 Method Not Allowed";
40 const std::string forbiddenMsg = "403 Forbidden";
41 
42 const std::string notFoundDesc =
43     "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
44 const std::string propNotFoundDesc = "The specified property cannot be found";
45 const std::string noJsonDesc = "No JSON object could be decoded";
46 const std::string methodNotFoundDesc = "The specified method cannot be found";
47 const std::string methodNotAllowedDesc = "Method not allowed";
48 const std::string forbiddenPropDesc =
49     "The specified property cannot be created";
50 const std::string forbiddenResDesc = "The specified resource cannot be created";
51 
52 void setErrorResponse(crow::Response &res, boost::beast::http::status result,
53                       const std::string &desc, const std::string &msg)
54 {
55     res.result(result);
56     res.jsonValue = {{"data", {{"description", desc}}},
57                      {"message", msg},
58                      {"status", "error"}};
59 }
60 
61 void introspectObjects(const std::string &processName,
62                        const std::string &objectPath,
63                        std::shared_ptr<bmcweb::AsyncResp> transaction)
64 {
65     if (transaction->res.jsonValue.is_null())
66     {
67         transaction->res.jsonValue = {{"status", "ok"},
68                                       {"bus_name", processName},
69                                       {"objects", nlohmann::json::array()}};
70     }
71 
72     crow::connections::systemBus->async_method_call(
73         [transaction, processName{std::string(processName)},
74          objectPath{std::string(objectPath)}](
75             const boost::system::error_code ec,
76             const std::string &introspect_xml) {
77             if (ec)
78             {
79                 BMCWEB_LOG_ERROR
80                     << "Introspect call failed with error: " << ec.message()
81                     << " on process: " << processName << " path: " << objectPath
82                     << "\n";
83                 return;
84             }
85             transaction->res.jsonValue["objects"].push_back(
86                 {{"path", objectPath}});
87 
88             tinyxml2::XMLDocument doc;
89 
90             doc.Parse(introspect_xml.c_str());
91             tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
92             if (pRoot == nullptr)
93             {
94                 BMCWEB_LOG_ERROR << "XML document failed to parse "
95                                  << processName << " " << objectPath << "\n";
96             }
97             else
98             {
99                 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
100                 while (node != nullptr)
101                 {
102                     const char *childPath = node->Attribute("name");
103                     if (childPath != nullptr)
104                     {
105                         std::string newpath;
106                         if (objectPath != "/")
107                         {
108                             newpath += objectPath;
109                         }
110                         newpath += std::string("/") + childPath;
111                         // introspect the subobjects as well
112                         introspectObjects(processName, newpath, transaction);
113                     }
114 
115                     node = node->NextSiblingElement("node");
116                 }
117             }
118         },
119         processName, objectPath, "org.freedesktop.DBus.Introspectable",
120         "Introspect");
121 }
122 
123 void getPropertiesForEnumerate(const std::string &objectPath,
124                                const std::string &service,
125                                const std::string &interface,
126                                std::shared_ptr<bmcweb::AsyncResp> asyncResp)
127 {
128     BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
129                      << service << " " << interface;
130 
131     crow::connections::systemBus->async_method_call(
132         [asyncResp, objectPath, service,
133          interface](const boost::system::error_code ec,
134                     const std::vector<
135                         std::pair<std::string, dbus::utility::DbusVariantType>>
136                         &propertiesList) {
137             if (ec)
138             {
139                 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
140                                  << interface << " service " << service
141                                  << " failed with code " << ec;
142                 return;
143             }
144 
145             nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
146             nlohmann::json &objectJson = dataJson[objectPath];
147             if (objectJson.is_null())
148             {
149                 objectJson = nlohmann::json::object();
150             }
151 
152             for (const auto &[name, value] : propertiesList)
153             {
154                 nlohmann::json &propertyJson = objectJson[name];
155                 sdbusplus::message::variant_ns::visit(
156                     [&propertyJson](auto &&val) { propertyJson = val; }, value);
157             }
158         },
159         service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
160         interface);
161 }
162 
163 // Find any results that weren't picked up by ObjectManagers, to be
164 // called after all ObjectManagers are searched for and called.
165 void findRemainingObjectsForEnumerate(
166     const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
167     std::shared_ptr<bmcweb::AsyncResp> asyncResp)
168 {
169     BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
170     const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
171 
172     for (const auto &[path, interface_map] : *subtree)
173     {
174         if (path == objectPath)
175         {
176             // An enumerate does not return the target path's properties
177             continue;
178         }
179         if (dataJson.find(path) == dataJson.end())
180         {
181             for (const auto &[service, interfaces] : interface_map)
182             {
183                 for (const auto &interface : interfaces)
184                 {
185                     if (!boost::starts_with(interface, "org.freedesktop.DBus"))
186                     {
187                         getPropertiesForEnumerate(path, service, interface,
188                                                   asyncResp);
189                     }
190                 }
191             }
192         }
193     }
194 }
195 
196 struct InProgressEnumerateData
197 {
198     InProgressEnumerateData(const std::string &objectPath,
199                             std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
200         objectPath(objectPath),
201         asyncResp(asyncResp)
202     {
203     }
204 
205     ~InProgressEnumerateData()
206     {
207         findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
208     }
209 
210     const std::string objectPath;
211     std::shared_ptr<GetSubTreeType> subtree;
212     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
213 };
214 
215 void getManagedObjectsForEnumerate(
216     const std::string &object_name, const std::string &object_manager_path,
217     const std::string &connection_name,
218     std::shared_ptr<InProgressEnumerateData> transaction)
219 {
220     BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
221                      << " object_manager_path " << object_manager_path
222                      << " connection_name " << connection_name;
223     crow::connections::systemBus->async_method_call(
224         [transaction, object_name,
225          connection_name](const boost::system::error_code ec,
226                           const dbus::utility::ManagedObjectType &objects) {
227             if (ec)
228             {
229                 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
230                                  << " on connection " << connection_name
231                                  << " failed with code " << ec;
232                 return;
233             }
234 
235             nlohmann::json &dataJson =
236                 transaction->asyncResp->res.jsonValue["data"];
237 
238             for (const auto &objectPath : objects)
239             {
240                 if (boost::starts_with(objectPath.first.str, object_name))
241                 {
242                     BMCWEB_LOG_DEBUG << "Reading object "
243                                      << objectPath.first.str;
244                     nlohmann::json &objectJson = dataJson[objectPath.first.str];
245                     if (objectJson.is_null())
246                     {
247                         objectJson = nlohmann::json::object();
248                     }
249                     for (const auto &interface : objectPath.second)
250                     {
251                         for (const auto &property : interface.second)
252                         {
253                             nlohmann::json &propertyJson =
254                                 objectJson[property.first];
255                             sdbusplus::message::variant_ns::visit(
256                                 [&propertyJson](auto &&val) {
257                                     propertyJson = val;
258                                 },
259                                 property.second);
260                         }
261                     }
262                 }
263                 for (const auto &interface : objectPath.second)
264                 {
265                     if (interface.first == "org.freedesktop.DBus.ObjectManager")
266                     {
267                         getManagedObjectsForEnumerate(
268                             objectPath.first.str, objectPath.first.str,
269                             connection_name, transaction);
270                     }
271                 }
272             }
273         },
274         connection_name, object_manager_path,
275         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
276 }
277 
278 void findObjectManagerPathForEnumerate(
279     const std::string &object_name, const std::string &connection_name,
280     std::shared_ptr<InProgressEnumerateData> transaction)
281 {
282     BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
283                      << " on connection:" << connection_name;
284     crow::connections::systemBus->async_method_call(
285         [transaction, object_name, connection_name](
286             const boost::system::error_code ec,
287             const boost::container::flat_map<
288                 std::string, boost::container::flat_map<
289                                  std::string, std::vector<std::string>>>
290                 &objects) {
291             if (ec)
292             {
293                 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
294                                  << " failed with code " << ec;
295                 return;
296             }
297 
298             for (const auto &pathGroup : objects)
299             {
300                 for (const auto &connectionGroup : pathGroup.second)
301                 {
302                     if (connectionGroup.first == connection_name)
303                     {
304                         // Found the object manager path for this resource.
305                         getManagedObjectsForEnumerate(
306                             object_name, pathGroup.first, connection_name,
307                             transaction);
308                         return;
309                     }
310                 }
311             }
312         },
313         "xyz.openbmc_project.ObjectMapper",
314         "/xyz/openbmc_project/object_mapper",
315         "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
316         std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
317 }
318 
319 // Uses GetObject to add the object info about the target /enumerate path to the
320 // results of GetSubTree, as GetSubTree will not return info for the
321 // target path, and then continues on enumerating the rest of the tree.
322 void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
323 {
324     using GetObjectType =
325         std::vector<std::pair<std::string, std::vector<std::string>>>;
326 
327     crow::connections::systemBus->async_method_call(
328         [transaction](const boost::system::error_code ec,
329                       const GetObjectType &objects) {
330             if (ec)
331             {
332                 BMCWEB_LOG_ERROR << "GetObject for path "
333                                  << transaction->objectPath
334                                  << " failed with code " << ec;
335                 return;
336             }
337 
338             BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
339                              << " has " << objects.size() << " entries";
340             if (!objects.empty())
341             {
342                 transaction->subtree->emplace_back(transaction->objectPath,
343                                                    objects);
344             }
345 
346             // Map indicating connection name, and the path where the object
347             // manager exists
348             boost::container::flat_map<std::string, std::string> connections;
349 
350             for (const auto &object : *(transaction->subtree))
351             {
352                 for (const auto &connection : object.second)
353                 {
354                     std::string &objectManagerPath =
355                         connections[connection.first];
356                     for (const auto &interface : connection.second)
357                     {
358                         BMCWEB_LOG_DEBUG << connection.first
359                                          << " has interface " << interface;
360                         if (interface == "org.freedesktop.DBus.ObjectManager")
361                         {
362                             BMCWEB_LOG_DEBUG << "found object manager path "
363                                              << object.first;
364                             objectManagerPath = object.first;
365                         }
366                     }
367                 }
368             }
369             BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
370 
371             for (const auto &connection : connections)
372             {
373                 // If we already know where the object manager is, we don't need
374                 // to search for it, we can call directly in to
375                 // getManagedObjects
376                 if (!connection.second.empty())
377                 {
378                     getManagedObjectsForEnumerate(
379                         transaction->objectPath, connection.second,
380                         connection.first, transaction);
381                 }
382                 else
383                 {
384                     // otherwise we need to find the object manager path before
385                     // we can continue
386                     findObjectManagerPathForEnumerate(
387                         transaction->objectPath, connection.first, transaction);
388                 }
389             }
390         },
391         "xyz.openbmc_project.ObjectMapper",
392         "/xyz/openbmc_project/object_mapper",
393         "xyz.openbmc_project.ObjectMapper", "GetObject",
394         transaction->objectPath, std::array<const char *, 0>());
395 }
396 
397 // Structure for storing data on an in progress action
398 struct InProgressActionData
399 {
400     InProgressActionData(crow::Response &res) : res(res){};
401     ~InProgressActionData()
402     {
403         // If still no JSON filled in, then we never found the method.
404         if (res.jsonValue.is_null())
405         {
406             setErrorResponse(res, boost::beast::http::status::not_found,
407                              methodNotFoundDesc, notFoundMsg);
408         }
409         res.end();
410     }
411 
412     void setErrorStatus(const std::string &desc)
413     {
414         setErrorResponse(res, boost::beast::http::status::internal_server_error,
415                          desc, badReqMsg);
416     }
417     crow::Response &res;
418     std::string path;
419     std::string methodName;
420     nlohmann::json arguments;
421 };
422 
423 std::vector<std::string> dbusArgSplit(const std::string &string)
424 {
425     std::vector<std::string> ret;
426     if (string.empty())
427     {
428         return ret;
429     }
430     ret.push_back("");
431     int containerDepth = 0;
432 
433     for (std::string::const_iterator character = string.begin();
434          character != string.end(); character++)
435     {
436         ret.back() += *character;
437         switch (*character)
438         {
439             case ('a'):
440                 break;
441             case ('('):
442             case ('{'):
443                 containerDepth++;
444                 break;
445             case ('}'):
446             case (')'):
447                 containerDepth--;
448                 if (containerDepth == 0)
449                 {
450                     if (character + 1 != string.end())
451                     {
452                         ret.push_back("");
453                     }
454                 }
455                 break;
456             default:
457                 if (containerDepth == 0)
458                 {
459                     if (character + 1 != string.end())
460                     {
461                         ret.push_back("");
462                     }
463                 }
464                 break;
465         }
466     }
467 }
468 
469 int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
470                       const nlohmann::json &input_json)
471 {
472     int r = 0;
473     BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
474                      << " to type: " << arg_type;
475     const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
476 
477     // Assume a single object for now.
478     const nlohmann::json *j = &input_json;
479     nlohmann::json::const_iterator jIt = input_json.begin();
480 
481     for (const std::string &argCode : argTypes)
482     {
483         // If we are decoding multiple objects, grab the pointer to the
484         // iterator, and increment it for the next loop
485         if (argTypes.size() > 1)
486         {
487             if (jIt == input_json.end())
488             {
489                 return -2;
490             }
491             j = &*jIt;
492             jIt++;
493         }
494         const int64_t *intValue = j->get_ptr<const int64_t *>();
495         const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
496         const std::string *stringValue = j->get_ptr<const std::string *>();
497         const double *doubleValue = j->get_ptr<const double *>();
498         const bool *b = j->get_ptr<const bool *>();
499         int64_t v = 0;
500         double d = 0.0;
501 
502         // Do some basic type conversions that make sense.  uint can be
503         // converted to int.  int and uint can be converted to double
504         if (uintValue != nullptr && intValue == nullptr)
505         {
506             v = static_cast<int64_t>(*uintValue);
507             intValue = &v;
508         }
509         if (uintValue != nullptr && doubleValue == nullptr)
510         {
511             d = static_cast<double>(*uintValue);
512             doubleValue = &d;
513         }
514         if (intValue != nullptr && doubleValue == nullptr)
515         {
516             d = static_cast<double>(*intValue);
517             doubleValue = &d;
518         }
519 
520         if (argCode == "s")
521         {
522             if (stringValue == nullptr)
523             {
524                 return -1;
525             }
526             r = sd_bus_message_append_basic(m, argCode[0],
527                                             (void *)stringValue->c_str());
528             if (r < 0)
529             {
530                 return r;
531             }
532         }
533         else if (argCode == "i")
534         {
535             if (intValue == nullptr)
536             {
537                 return -1;
538             }
539             int32_t i = static_cast<int32_t>(*intValue);
540             r = sd_bus_message_append_basic(m, argCode[0], &i);
541             if (r < 0)
542             {
543                 return r;
544             }
545         }
546         else if (argCode == "b")
547         {
548             // lots of ways bool could be represented here.  Try them all
549             int boolInt = false;
550             if (intValue != nullptr)
551             {
552                 boolInt = *intValue > 0 ? 1 : 0;
553             }
554             else if (b != nullptr)
555             {
556                 boolInt = b ? 1 : 0;
557             }
558             else if (stringValue != nullptr)
559             {
560                 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
561             }
562             else
563             {
564                 return -1;
565             }
566             r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
567             if (r < 0)
568             {
569                 return r;
570             }
571         }
572         else if (argCode == "n")
573         {
574             if (intValue == nullptr)
575             {
576                 return -1;
577             }
578             int16_t n = static_cast<int16_t>(*intValue);
579             r = sd_bus_message_append_basic(m, argCode[0], &n);
580             if (r < 0)
581             {
582                 return r;
583             }
584         }
585         else if (argCode == "x")
586         {
587             if (intValue == nullptr)
588             {
589                 return -1;
590             }
591             r = sd_bus_message_append_basic(m, argCode[0], intValue);
592             if (r < 0)
593             {
594                 return r;
595             }
596         }
597         else if (argCode == "y")
598         {
599             if (uintValue == nullptr)
600             {
601                 return -1;
602             }
603             uint8_t y = static_cast<uint8_t>(*uintValue);
604             r = sd_bus_message_append_basic(m, argCode[0], &y);
605         }
606         else if (argCode == "q")
607         {
608             if (uintValue == nullptr)
609             {
610                 return -1;
611             }
612             uint16_t q = static_cast<uint16_t>(*uintValue);
613             r = sd_bus_message_append_basic(m, argCode[0], &q);
614         }
615         else if (argCode == "u")
616         {
617             if (uintValue == nullptr)
618             {
619                 return -1;
620             }
621             uint32_t u = static_cast<uint32_t>(*uintValue);
622             r = sd_bus_message_append_basic(m, argCode[0], &u);
623         }
624         else if (argCode == "t")
625         {
626             if (uintValue == nullptr)
627             {
628                 return -1;
629             }
630             r = sd_bus_message_append_basic(m, argCode[0], uintValue);
631         }
632         else if (argCode == "d")
633         {
634             sd_bus_message_append_basic(m, argCode[0], doubleValue);
635         }
636         else if (boost::starts_with(argCode, "a"))
637         {
638             std::string containedType = argCode.substr(1);
639             r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
640                                               containedType.c_str());
641             if (r < 0)
642             {
643                 return r;
644             }
645 
646             for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
647                  ++it)
648             {
649                 r = convertJsonToDbus(m, containedType, *it);
650                 if (r < 0)
651                 {
652                     return r;
653                 }
654 
655                 it++;
656             }
657             sd_bus_message_close_container(m);
658         }
659         else if (boost::starts_with(argCode, "v"))
660         {
661             std::string containedType = argCode.substr(1);
662             BMCWEB_LOG_DEBUG << "variant type: " << argCode
663                              << " appending variant of type: " << containedType;
664             r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
665                                               containedType.c_str());
666             if (r < 0)
667             {
668                 return r;
669             }
670 
671             r = convertJsonToDbus(m, containedType, input_json);
672             if (r < 0)
673             {
674                 return r;
675             }
676 
677             r = sd_bus_message_close_container(m);
678             if (r < 0)
679             {
680                 return r;
681             }
682         }
683         else if (boost::starts_with(argCode, "(") &&
684                  boost::ends_with(argCode, ")"))
685         {
686             std::string containedType = argCode.substr(1, argCode.size() - 1);
687             r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
688                                               containedType.c_str());
689             nlohmann::json::const_iterator it = j->begin();
690             for (const std::string &argCode : dbusArgSplit(arg_type))
691             {
692                 if (it == j->end())
693                 {
694                     return -1;
695                 }
696                 r = convertJsonToDbus(m, argCode, *it);
697                 if (r < 0)
698                 {
699                     return r;
700                 }
701                 it++;
702             }
703             r = sd_bus_message_close_container(m);
704         }
705         else if (boost::starts_with(argCode, "{") &&
706                  boost::ends_with(argCode, "}"))
707         {
708             std::string containedType = argCode.substr(1, argCode.size() - 1);
709             r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
710                                               containedType.c_str());
711             std::vector<std::string> codes = dbusArgSplit(containedType);
712             if (codes.size() != 2)
713             {
714                 return -1;
715             }
716             const std::string &key_type = codes[0];
717             const std::string &value_type = codes[1];
718             for (auto it : j->items())
719             {
720                 r = convertJsonToDbus(m, key_type, it.key());
721                 if (r < 0)
722                 {
723                     return r;
724                 }
725 
726                 r = convertJsonToDbus(m, value_type, it.value());
727                 if (r < 0)
728                 {
729                     return r;
730                 }
731             }
732             r = sd_bus_message_close_container(m);
733         }
734         else
735         {
736             return -2;
737         }
738         if (r < 0)
739         {
740             return r;
741         }
742 
743         if (argTypes.size() > 1)
744         {
745             jIt++;
746         }
747     }
748 }
749 
750 void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
751                            const std::string &connectionName)
752 {
753     BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
754                      << connectionName;
755     crow::connections::systemBus->async_method_call(
756         [transaction, connectionName{std::string(connectionName)}](
757             const boost::system::error_code ec,
758             const std::string &introspect_xml) {
759             BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
760             if (ec)
761             {
762                 BMCWEB_LOG_ERROR
763                     << "Introspect call failed with error: " << ec.message()
764                     << " on process: " << connectionName << "\n";
765             }
766             else
767             {
768                 tinyxml2::XMLDocument doc;
769 
770                 doc.Parse(introspect_xml.data(), introspect_xml.size());
771                 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
772                 if (pRoot == nullptr)
773                 {
774                     BMCWEB_LOG_ERROR << "XML document failed to parse "
775                                      << connectionName << "\n";
776                     return;
777                 }
778                 tinyxml2::XMLElement *interfaceNode =
779                     pRoot->FirstChildElement("interface");
780                 while (interfaceNode != nullptr)
781                 {
782                     const char *thisInterfaceName =
783                         interfaceNode->Attribute("name");
784                     if (thisInterfaceName != nullptr)
785                     {
786                         tinyxml2::XMLElement *methodNode =
787                             interfaceNode->FirstChildElement("method");
788                         while (methodNode != nullptr)
789                         {
790                             const char *thisMethodName =
791                                 methodNode->Attribute("name");
792                             BMCWEB_LOG_DEBUG << "Found method: "
793                                              << thisMethodName;
794                             if (thisMethodName != nullptr &&
795                                 thisMethodName == transaction->methodName)
796                             {
797                                 BMCWEB_LOG_DEBUG
798                                     << "Found method named " << thisMethodName
799                                     << " on interface " << thisInterfaceName;
800                                 sdbusplus::message::message m =
801                                     crow::connections::systemBus
802                                         ->new_method_call(
803                                             connectionName.c_str(),
804                                             transaction->path.c_str(),
805                                             thisInterfaceName,
806                                             transaction->methodName.c_str());
807 
808                                 tinyxml2::XMLElement *argumentNode =
809                                     methodNode->FirstChildElement("arg");
810 
811                                 nlohmann::json::const_iterator argIt =
812                                     transaction->arguments.begin();
813 
814                                 while (argumentNode != nullptr)
815                                 {
816                                     const char *argDirection =
817                                         argumentNode->Attribute("direction");
818                                     const char *argType =
819                                         argumentNode->Attribute("type");
820                                     if (argDirection != nullptr &&
821                                         argType != nullptr &&
822                                         std::string(argDirection) == "in")
823                                     {
824 
825                                         if (argIt ==
826                                             transaction->arguments.end())
827                                         {
828                                             transaction->setErrorStatus(
829                                                 "Invalid method args");
830                                             return;
831                                         }
832                                         if (convertJsonToDbus(
833                                                 m.get(), std::string(argType),
834                                                 *argIt) < 0)
835                                         {
836                                             transaction->setErrorStatus(
837                                                 "Invalid method arg type");
838                                             return;
839                                         }
840 
841                                         argIt++;
842                                     }
843                                     argumentNode =
844                                         methodNode->NextSiblingElement("arg");
845                                 }
846 
847                                 crow::connections::systemBus->async_send(
848                                     m, [transaction](
849                                            boost::system::error_code ec,
850                                            sdbusplus::message::message &m) {
851                                         if (ec)
852                                         {
853                                             transaction->setErrorStatus(
854                                                 "Method call failed");
855                                             return;
856                                         }
857                                         transaction->res.jsonValue = {
858                                             {"status", "ok"},
859                                             {"message", "200 OK"},
860                                             {"data", nullptr}};
861                                     });
862                                 break;
863                             }
864                             methodNode =
865                                 methodNode->NextSiblingElement("method");
866                         }
867                     }
868                     interfaceNode =
869                         interfaceNode->NextSiblingElement("interface");
870                 }
871             }
872         },
873         connectionName, transaction->path,
874         "org.freedesktop.DBus.Introspectable", "Introspect");
875 }
876 
877 void handleAction(const crow::Request &req, crow::Response &res,
878                   const std::string &objectPath, const std::string &methodName)
879 {
880     BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
881                      << methodName;
882     nlohmann::json requestDbusData =
883         nlohmann::json::parse(req.body, nullptr, false);
884 
885     if (requestDbusData.is_discarded())
886     {
887         setErrorResponse(res, boost::beast::http::status::bad_request,
888                          noJsonDesc, badReqMsg);
889         res.end();
890         return;
891     }
892     nlohmann::json::iterator data = requestDbusData.find("data");
893     if (data == requestDbusData.end())
894     {
895         setErrorResponse(res, boost::beast::http::status::bad_request,
896                          noJsonDesc, badReqMsg);
897         res.end();
898         return;
899     }
900 
901     if (!data->is_array())
902     {
903         setErrorResponse(res, boost::beast::http::status::bad_request,
904                          noJsonDesc, badReqMsg);
905         res.end();
906         return;
907     }
908     auto transaction = std::make_shared<InProgressActionData>(res);
909 
910     transaction->path = objectPath;
911     transaction->methodName = methodName;
912     transaction->arguments = std::move(*data);
913     crow::connections::systemBus->async_method_call(
914         [transaction](
915             const boost::system::error_code ec,
916             const std::vector<std::pair<std::string, std::vector<std::string>>>
917                 &interfaceNames) {
918             if (ec || interfaceNames.size() <= 0)
919             {
920                 BMCWEB_LOG_ERROR << "Can't find object";
921                 setErrorResponse(transaction->res,
922                                  boost::beast::http::status::not_found,
923                                  notFoundDesc, notFoundMsg);
924                 return;
925             }
926 
927             BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
928                              << " object(s)";
929 
930             for (const std::pair<std::string, std::vector<std::string>>
931                      &object : interfaceNames)
932             {
933                 findActionOnInterface(transaction, object.first);
934             }
935         },
936         "xyz.openbmc_project.ObjectMapper",
937         "/xyz/openbmc_project/object_mapper",
938         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
939         std::array<std::string, 0>());
940 }
941 
942 void handleList(crow::Response &res, const std::string &objectPath,
943                 int32_t depth = 0)
944 {
945     crow::connections::systemBus->async_method_call(
946         [&res](const boost::system::error_code ec,
947                std::vector<std::string> &objectPaths) {
948             if (ec)
949             {
950                 setErrorResponse(res, boost::beast::http::status::not_found,
951                                  notFoundDesc, notFoundMsg);
952             }
953             else
954             {
955                 res.jsonValue = {{"status", "ok"},
956                                  {"message", "200 OK"},
957                                  {"data", std::move(objectPaths)}};
958             }
959             res.end();
960         },
961         "xyz.openbmc_project.ObjectMapper",
962         "/xyz/openbmc_project/object_mapper",
963         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
964         depth, std::array<std::string, 0>());
965 }
966 
967 void handleEnumerate(crow::Response &res, const std::string &objectPath)
968 {
969     BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
970     auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
971 
972     asyncResp->res.jsonValue = {{"message", "200 OK"},
973                                 {"status", "ok"},
974                                 {"data", nlohmann::json::object()}};
975 
976     crow::connections::systemBus->async_method_call(
977         [objectPath, asyncResp](const boost::system::error_code ec,
978                                 GetSubTreeType &object_names) {
979             auto transaction = std::make_shared<InProgressEnumerateData>(
980                 objectPath, asyncResp);
981 
982             transaction->subtree =
983                 std::make_shared<GetSubTreeType>(std::move(object_names));
984 
985             if (ec)
986             {
987                 BMCWEB_LOG_ERROR << "GetSubTree failed on "
988                                  << transaction->objectPath;
989                 setErrorResponse(transaction->asyncResp->res,
990                                  boost::beast::http::status::not_found,
991                                  notFoundDesc, notFoundMsg);
992                 return;
993             }
994 
995             // Add the data for the path passed in to the results
996             // as if GetSubTree returned it, and continue on enumerating
997             getObjectAndEnumerate(transaction);
998         },
999         "xyz.openbmc_project.ObjectMapper",
1000         "/xyz/openbmc_project/object_mapper",
1001         "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
1002         static_cast<int32_t>(0), std::array<const char *, 0>());
1003 }
1004 
1005 void handleGet(crow::Response &res, std::string &objectPath,
1006                std::string &destProperty)
1007 {
1008     BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1009     std::shared_ptr<std::string> propertyName =
1010         std::make_shared<std::string>(std::move(destProperty));
1011 
1012     std::shared_ptr<std::string> path =
1013         std::make_shared<std::string>(std::move(objectPath));
1014 
1015     using GetObjectType =
1016         std::vector<std::pair<std::string, std::vector<std::string>>>;
1017     crow::connections::systemBus->async_method_call(
1018         [&res, path, propertyName](const boost::system::error_code ec,
1019                                    const GetObjectType &object_names) {
1020             if (ec || object_names.size() <= 0)
1021             {
1022                 setErrorResponse(res, boost::beast::http::status::not_found,
1023                                  notFoundDesc, notFoundMsg);
1024                 res.end();
1025                 return;
1026             }
1027             std::shared_ptr<nlohmann::json> response =
1028                 std::make_shared<nlohmann::json>(nlohmann::json::object());
1029             // The mapper should never give us an empty interface names list,
1030             // but check anyway
1031             for (const std::pair<std::string, std::vector<std::string>>
1032                      connection : object_names)
1033             {
1034                 const std::vector<std::string> &interfaceNames =
1035                     connection.second;
1036 
1037                 if (interfaceNames.size() <= 0)
1038                 {
1039                     setErrorResponse(res, boost::beast::http::status::not_found,
1040                                      notFoundDesc, notFoundMsg);
1041                     res.end();
1042                     return;
1043                 }
1044 
1045                 for (const std::string &interface : interfaceNames)
1046                 {
1047                     crow::connections::systemBus->async_method_call(
1048                         [&res, response, propertyName](
1049                             const boost::system::error_code ec,
1050                             const std::vector<std::pair<
1051                                 std::string, dbus::utility::DbusVariantType>>
1052                                 &properties) {
1053                             if (ec)
1054                             {
1055                                 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1056                                                  << ec;
1057                             }
1058                             else
1059                             {
1060                                 for (const std::pair<
1061                                          std::string,
1062                                          dbus::utility::DbusVariantType>
1063                                          &property : properties)
1064                                 {
1065                                     // if property name is empty, or matches our
1066                                     // search query, add it to the response json
1067 
1068                                     if (propertyName->empty())
1069                                     {
1070                                         sdbusplus::message::variant_ns::visit(
1071                                             [&response, &property](auto &&val) {
1072                                                 (*response)[property.first] =
1073                                                     val;
1074                                             },
1075                                             property.second);
1076                                     }
1077                                     else if (property.first == *propertyName)
1078                                     {
1079                                         sdbusplus::message::variant_ns::visit(
1080                                             [&response](auto &&val) {
1081                                                 (*response) = val;
1082                                             },
1083                                             property.second);
1084                                     }
1085                                 }
1086                             }
1087                             if (response.use_count() == 1)
1088                             {
1089                                 if (!propertyName->empty() && response->empty())
1090                                 {
1091                                     setErrorResponse(
1092                                         res,
1093                                         boost::beast::http::status::not_found,
1094                                         propNotFoundDesc, notFoundMsg);
1095                                 }
1096                                 else
1097                                 {
1098                                     res.jsonValue = {{"status", "ok"},
1099                                                      {"message", "200 OK"},
1100                                                      {"data", *response}};
1101                                 }
1102                                 res.end();
1103                             }
1104                         },
1105                         connection.first, *path,
1106                         "org.freedesktop.DBus.Properties", "GetAll", interface);
1107                 }
1108             }
1109         },
1110         "xyz.openbmc_project.ObjectMapper",
1111         "/xyz/openbmc_project/object_mapper",
1112         "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1113         std::array<std::string, 0>());
1114 }
1115 
1116 struct AsyncPutRequest
1117 {
1118     AsyncPutRequest(crow::Response &res) : res(res)
1119     {
1120     }
1121     ~AsyncPutRequest()
1122     {
1123         if (res.jsonValue.empty())
1124         {
1125             setErrorResponse(res, boost::beast::http::status::forbidden,
1126                              forbiddenMsg, forbiddenPropDesc);
1127         }
1128 
1129         res.end();
1130     }
1131 
1132     void setErrorStatus(const std::string &desc)
1133     {
1134         setErrorResponse(res, boost::beast::http::status::internal_server_error,
1135                          desc, badReqMsg);
1136     }
1137 
1138     crow::Response &res;
1139     std::string objectPath;
1140     std::string propertyName;
1141     nlohmann::json propertyValue;
1142 };
1143 
1144 void handlePut(const crow::Request &req, crow::Response &res,
1145                const std::string &objectPath, const std::string &destProperty)
1146 {
1147     if (destProperty.empty())
1148     {
1149         setErrorResponse(res, boost::beast::http::status::forbidden,
1150                          forbiddenResDesc, forbiddenMsg);
1151         res.end();
1152         return;
1153     }
1154 
1155     nlohmann::json requestDbusData =
1156         nlohmann::json::parse(req.body, nullptr, false);
1157 
1158     if (requestDbusData.is_discarded())
1159     {
1160         setErrorResponse(res, boost::beast::http::status::bad_request,
1161                          noJsonDesc, badReqMsg);
1162         res.end();
1163         return;
1164     }
1165 
1166     nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1167     if (propertyIt == requestDbusData.end())
1168     {
1169         setErrorResponse(res, boost::beast::http::status::bad_request,
1170                          noJsonDesc, badReqMsg);
1171         res.end();
1172         return;
1173     }
1174     const nlohmann::json &propertySetValue = *propertyIt;
1175     auto transaction = std::make_shared<AsyncPutRequest>(res);
1176     transaction->objectPath = objectPath;
1177     transaction->propertyName = destProperty;
1178     transaction->propertyValue = propertySetValue;
1179 
1180     using GetObjectType =
1181         std::vector<std::pair<std::string, std::vector<std::string>>>;
1182 
1183     crow::connections::systemBus->async_method_call(
1184         [transaction](const boost::system::error_code ec,
1185                       const GetObjectType &object_names) {
1186             if (!ec && object_names.size() <= 0)
1187             {
1188                 setErrorResponse(transaction->res,
1189                                  boost::beast::http::status::not_found,
1190                                  propNotFoundDesc, notFoundMsg);
1191                 return;
1192             }
1193 
1194             for (const std::pair<std::string, std::vector<std::string>>
1195                      connection : object_names)
1196             {
1197                 const std::string &connectionName = connection.first;
1198 
1199                 crow::connections::systemBus->async_method_call(
1200                     [connectionName{std::string(connectionName)},
1201                      transaction](const boost::system::error_code ec,
1202                                   const std::string &introspectXml) {
1203                         if (ec)
1204                         {
1205                             BMCWEB_LOG_ERROR
1206                                 << "Introspect call failed with error: "
1207                                 << ec.message()
1208                                 << " on process: " << connectionName;
1209                             transaction->setErrorStatus("Unexpected Error");
1210                             return;
1211                         }
1212                         tinyxml2::XMLDocument doc;
1213 
1214                         doc.Parse(introspectXml.c_str());
1215                         tinyxml2::XMLNode *pRoot =
1216                             doc.FirstChildElement("node");
1217                         if (pRoot == nullptr)
1218                         {
1219                             BMCWEB_LOG_ERROR << "XML document failed to parse: "
1220                                              << introspectXml;
1221                             transaction->setErrorStatus("Unexpected Error");
1222                             return;
1223                         }
1224                         tinyxml2::XMLElement *ifaceNode =
1225                             pRoot->FirstChildElement("interface");
1226                         while (ifaceNode != nullptr)
1227                         {
1228                             const char *interfaceName =
1229                                 ifaceNode->Attribute("name");
1230                             BMCWEB_LOG_DEBUG << "found interface "
1231                                              << interfaceName;
1232                             tinyxml2::XMLElement *propNode =
1233                                 ifaceNode->FirstChildElement("property");
1234                             while (propNode != nullptr)
1235                             {
1236                                 const char *propertyName =
1237                                     propNode->Attribute("name");
1238                                 BMCWEB_LOG_DEBUG << "Found property "
1239                                                  << propertyName;
1240                                 if (propertyName == transaction->propertyName)
1241                                 {
1242                                     const char *argType =
1243                                         propNode->Attribute("type");
1244                                     if (argType != nullptr)
1245                                     {
1246                                         sdbusplus::message::message m =
1247                                             crow::connections::systemBus
1248                                                 ->new_method_call(
1249                                                     connectionName.c_str(),
1250                                                     transaction->objectPath
1251                                                         .c_str(),
1252                                                     "org.freedesktop.DBus."
1253                                                     "Properties",
1254                                                     "Set");
1255                                         m.append(interfaceName,
1256                                                  transaction->propertyName);
1257                                         int r = sd_bus_message_open_container(
1258                                             m.get(), SD_BUS_TYPE_VARIANT,
1259                                             argType);
1260                                         if (r < 0)
1261                                         {
1262                                             transaction->setErrorStatus(
1263                                                 "Unexpected Error");
1264                                             return;
1265                                         }
1266                                         r = convertJsonToDbus(
1267                                             m.get(), argType,
1268                                             transaction->propertyValue);
1269                                         if (r < 0)
1270                                         {
1271                                             transaction->setErrorStatus(
1272                                                 "Invalid arg type");
1273                                             return;
1274                                         }
1275                                         r = sd_bus_message_close_container(
1276                                             m.get());
1277                                         if (r < 0)
1278                                         {
1279                                             transaction->setErrorStatus(
1280                                                 "Unexpected Error");
1281                                             return;
1282                                         }
1283 
1284                                         crow::connections::systemBus
1285                                             ->async_send(
1286                                                 m,
1287                                                 [transaction](
1288                                                     boost::system::error_code
1289                                                         ec,
1290                                                     sdbusplus::message::message
1291                                                         &m) {
1292                                                     BMCWEB_LOG_DEBUG << "sent";
1293                                                     if (ec)
1294                                                     {
1295                                                         setErrorResponse(
1296                                                             transaction->res,
1297                                                             boost::beast::http::
1298                                                                 status::
1299                                                                     forbidden,
1300                                                             forbiddenPropDesc,
1301                                                             ec.message());
1302                                                     }
1303                                                     else
1304                                                     {
1305                                                         transaction->res
1306                                                             .jsonValue = {
1307                                                             {"status", "ok"},
1308                                                             {"message",
1309                                                              "200 OK"},
1310                                                             {"data", nullptr}};
1311                                                     }
1312                                                 });
1313                                     }
1314                                 }
1315                                 propNode =
1316                                     propNode->NextSiblingElement("property");
1317                             }
1318                             ifaceNode =
1319                                 ifaceNode->NextSiblingElement("interface");
1320                         }
1321                     },
1322                     connectionName, transaction->objectPath,
1323                     "org.freedesktop.DBus.Introspectable", "Introspect");
1324             }
1325         },
1326         "xyz.openbmc_project.ObjectMapper",
1327         "/xyz/openbmc_project/object_mapper",
1328         "xyz.openbmc_project.ObjectMapper", "GetObject",
1329         transaction->objectPath, std::array<std::string, 0>());
1330 }
1331 
1332 inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1333                           std::string &objectPath)
1334 {
1335 
1336     // If accessing a single attribute, fill in and update objectPath,
1337     // otherwise leave destProperty blank
1338     std::string destProperty = "";
1339     const char *attrSeperator = "/attr/";
1340     size_t attrPosition = objectPath.find(attrSeperator);
1341     if (attrPosition != objectPath.npos)
1342     {
1343         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1344                                          objectPath.length());
1345         objectPath = objectPath.substr(0, attrPosition);
1346     }
1347 
1348     if (req.method() == "POST"_method)
1349     {
1350         constexpr const char *actionSeperator = "/action/";
1351         size_t actionPosition = objectPath.find(actionSeperator);
1352         if (actionPosition != objectPath.npos)
1353         {
1354             std::string postProperty =
1355                 objectPath.substr((actionPosition + strlen(actionSeperator)),
1356                                   objectPath.length());
1357             objectPath = objectPath.substr(0, actionPosition);
1358             handleAction(req, res, objectPath, postProperty);
1359             return;
1360         }
1361     }
1362     else if (req.method() == "GET"_method)
1363     {
1364         if (boost::ends_with(objectPath, "/enumerate"))
1365         {
1366             objectPath.erase(objectPath.end() - sizeof("enumerate"),
1367                              objectPath.end());
1368             handleEnumerate(res, objectPath);
1369         }
1370         else if (boost::ends_with(objectPath, "/list"))
1371         {
1372             objectPath.erase(objectPath.end() - sizeof("list"),
1373                              objectPath.end());
1374             handleList(res, objectPath);
1375         }
1376         else
1377         {
1378             // Trim any trailing "/" at the end
1379             if (boost::ends_with(objectPath, "/"))
1380             {
1381                 objectPath.pop_back();
1382                 handleList(res, objectPath, 1);
1383             }
1384             else
1385             {
1386                 handleGet(res, objectPath, destProperty);
1387             }
1388         }
1389         return;
1390     }
1391     else if (req.method() == "PUT"_method)
1392     {
1393         handlePut(req, res, objectPath, destProperty);
1394         return;
1395     }
1396 
1397     setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1398                      methodNotAllowedDesc, methodNotAllowedMsg);
1399     res.end();
1400 }
1401 
1402 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1403 {
1404     BMCWEB_ROUTE(app, "/bus/")
1405         .methods("GET"_method)(
1406             [](const crow::Request &req, crow::Response &res) {
1407                 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1408                                  {"status", "ok"}};
1409                 res.end();
1410             });
1411 
1412     BMCWEB_ROUTE(app, "/bus/system/")
1413         .methods("GET"_method)(
1414             [](const crow::Request &req, crow::Response &res) {
1415                 auto myCallback = [&res](const boost::system::error_code ec,
1416                                          std::vector<std::string> &names) {
1417                     if (ec)
1418                     {
1419                         BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1420                         res.result(
1421                             boost::beast::http::status::internal_server_error);
1422                     }
1423                     else
1424                     {
1425                         std::sort(names.begin(), names.end());
1426                         res.jsonValue = {{"status", "ok"}};
1427                         auto &objectsSub = res.jsonValue["objects"];
1428                         for (auto &name : names)
1429                         {
1430                             objectsSub.push_back({{"name", name}});
1431                         }
1432                     }
1433                     res.end();
1434                 };
1435                 crow::connections::systemBus->async_method_call(
1436                     std::move(myCallback), "org.freedesktop.DBus", "/",
1437                     "org.freedesktop.DBus", "ListNames");
1438             });
1439 
1440     BMCWEB_ROUTE(app, "/list/")
1441         .methods("GET"_method)(
1442             [](const crow::Request &req, crow::Response &res) {
1443                 handleList(res, "/");
1444             });
1445 
1446     BMCWEB_ROUTE(app, "/xyz/<path>")
1447         .methods("GET"_method, "PUT"_method, "POST"_method)(
1448             [](const crow::Request &req, crow::Response &res,
1449                const std::string &path) {
1450                 std::string objectPath = "/xyz/" + path;
1451                 handleDBusUrl(req, res, objectPath);
1452             });
1453 
1454     BMCWEB_ROUTE(app, "/org/<path>")
1455         .methods("GET"_method, "PUT"_method, "POST"_method)(
1456             [](const crow::Request &req, crow::Response &res,
1457                const std::string &path) {
1458                 std::string objectPath = "/org/" + path;
1459                 handleDBusUrl(req, res, objectPath);
1460             });
1461 
1462     BMCWEB_ROUTE(app, "/download/dump/<str>/")
1463         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1464                                   const std::string &dumpId) {
1465             std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1466             if (!std::regex_match(dumpId, validFilename))
1467             {
1468                 res.result(boost::beast::http::status::not_found);
1469                 res.end();
1470                 return;
1471             }
1472             std::experimental::filesystem::path loc(
1473                 "/var/lib/phosphor-debug-collector/dumps");
1474 
1475             loc += dumpId;
1476 
1477             if (!std::experimental::filesystem::exists(loc) ||
1478                 !std::experimental::filesystem::is_directory(loc))
1479             {
1480                 res.result(boost::beast::http::status::not_found);
1481                 res.end();
1482                 return;
1483             }
1484             std::experimental::filesystem::directory_iterator files(loc);
1485             for (auto &file : files)
1486             {
1487                 std::ifstream readFile(file.path());
1488                 if (readFile.good())
1489                 {
1490                     continue;
1491                 }
1492                 res.addHeader("Content-Type", "application/octet-stream");
1493                 res.body() = {std::istreambuf_iterator<char>(readFile),
1494                               std::istreambuf_iterator<char>()};
1495                 res.end();
1496             }
1497             res.result(boost::beast::http::status::not_found);
1498             res.end();
1499             return;
1500         });
1501 
1502     BMCWEB_ROUTE(app, "/bus/system/<str>/")
1503         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1504                                   const std::string &Connection) {
1505             introspectObjects(Connection, "/",
1506                               std::make_shared<bmcweb::AsyncResp>(res));
1507         });
1508 
1509     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1510         .methods("GET"_method,
1511                  "POST"_method)([](const crow::Request &req,
1512                                    crow::Response &res,
1513                                    const std::string &processName,
1514                                    const std::string &requestedPath) {
1515             std::vector<std::string> strs;
1516             boost::split(strs, requestedPath, boost::is_any_of("/"));
1517             std::string objectPath;
1518             std::string interfaceName;
1519             std::string methodName;
1520             auto it = strs.begin();
1521             if (it == strs.end())
1522             {
1523                 objectPath = "/";
1524             }
1525             while (it != strs.end())
1526             {
1527                 // Check if segment contains ".".  If it does, it must be an
1528                 // interface
1529                 if (it->find(".") != std::string::npos)
1530                 {
1531                     break;
1532                     // This check is neccesary as the trailing slash gets parsed
1533                     // as part of our <path> specifier above, which causes the
1534                     // normal trailing backslash redirector to fail.
1535                 }
1536                 else if (!it->empty())
1537                 {
1538                     objectPath += "/" + *it;
1539                 }
1540                 it++;
1541             }
1542             if (it != strs.end())
1543             {
1544                 interfaceName = *it;
1545                 it++;
1546 
1547                 // after interface, we might have a method name
1548                 if (it != strs.end())
1549                 {
1550                     methodName = *it;
1551                     it++;
1552                 }
1553             }
1554             if (it != strs.end())
1555             {
1556                 // if there is more levels past the method name, something went
1557                 // wrong, return not found
1558                 res.result(boost::beast::http::status::not_found);
1559                 res.end();
1560                 return;
1561             }
1562             if (interfaceName.empty())
1563             {
1564                 crow::connections::systemBus->async_method_call(
1565                     [&, processName,
1566                      objectPath](const boost::system::error_code ec,
1567                                  const std::string &introspect_xml) {
1568                         if (ec)
1569                         {
1570                             BMCWEB_LOG_ERROR
1571                                 << "Introspect call failed with error: "
1572                                 << ec.message()
1573                                 << " on process: " << processName
1574                                 << " path: " << objectPath << "\n";
1575                             return;
1576                         }
1577                         tinyxml2::XMLDocument doc;
1578 
1579                         doc.Parse(introspect_xml.c_str());
1580                         tinyxml2::XMLNode *pRoot =
1581                             doc.FirstChildElement("node");
1582                         if (pRoot == nullptr)
1583                         {
1584                             BMCWEB_LOG_ERROR << "XML document failed to parse "
1585                                              << processName << " " << objectPath
1586                                              << "\n";
1587                             res.jsonValue = {{"status", "XML parse error"}};
1588                             res.result(boost::beast::http::status::
1589                                            internal_server_error);
1590                             return;
1591                         }
1592 
1593                         BMCWEB_LOG_DEBUG << introspect_xml;
1594                         res.jsonValue = {{"status", "ok"},
1595                                          {"bus_name", processName},
1596                                          {"object_path", objectPath}};
1597                         nlohmann::json &interfacesArray =
1598                             res.jsonValue["interfaces"];
1599                         interfacesArray = nlohmann::json::array();
1600                         tinyxml2::XMLElement *interface =
1601                             pRoot->FirstChildElement("interface");
1602 
1603                         while (interface != nullptr)
1604                         {
1605                             const char *ifaceName =
1606                                 interface->Attribute("name");
1607                             if (ifaceName != nullptr)
1608                             {
1609                                 interfacesArray.push_back(
1610                                     {{"name", ifaceName}});
1611                             }
1612 
1613                             interface =
1614                                 interface->NextSiblingElement("interface");
1615                         }
1616 
1617                         res.end();
1618                     },
1619                     processName, objectPath,
1620                     "org.freedesktop.DBus.Introspectable", "Introspect");
1621             }
1622             else if (methodName.empty())
1623             {
1624                 crow::connections::systemBus->async_method_call(
1625                     [&, processName, objectPath,
1626                      interfaceName{std::move(interfaceName)}](
1627                         const boost::system::error_code ec,
1628                         const std::string &introspect_xml) {
1629                         if (ec)
1630                         {
1631                             BMCWEB_LOG_ERROR
1632                                 << "Introspect call failed with error: "
1633                                 << ec.message()
1634                                 << " on process: " << processName
1635                                 << " path: " << objectPath << "\n";
1636                         }
1637                         else
1638                         {
1639                             tinyxml2::XMLDocument doc;
1640 
1641                             doc.Parse(introspect_xml.c_str());
1642                             tinyxml2::XMLNode *pRoot =
1643                                 doc.FirstChildElement("node");
1644                             if (pRoot == nullptr)
1645                             {
1646                                 BMCWEB_LOG_ERROR
1647                                     << "XML document failed to parse "
1648                                     << processName << " " << objectPath << "\n";
1649                                 res.result(boost::beast::http::status::
1650                                                internal_server_error);
1651                             }
1652                             else
1653                             {
1654                                 tinyxml2::XMLElement *node =
1655                                     pRoot->FirstChildElement("node");
1656 
1657                                 // if we know we're the only call, build the
1658                                 // json directly
1659                                 tinyxml2::XMLElement *interface =
1660                                     pRoot->FirstChildElement("interface");
1661 
1662                                 res.jsonValue = {
1663                                     {"status", "ok"},
1664                                     {"bus_name", processName},
1665                                     {"interface", interfaceName},
1666                                     {"object_path", objectPath},
1667                                     {"properties", nlohmann::json::object()}};
1668 
1669                                 nlohmann::json &methodsArray =
1670                                     res.jsonValue["methods"];
1671                                 methodsArray = nlohmann::json::array();
1672 
1673                                 nlohmann::json &signalsArray =
1674                                     res.jsonValue["signals"];
1675                                 signalsArray = nlohmann::json::array();
1676 
1677                                 while (interface != nullptr)
1678                                 {
1679                                     const char *ifaceName =
1680                                         interface->Attribute("name");
1681 
1682                                     if (ifaceName != nullptr &&
1683                                         ifaceName == interfaceName)
1684                                     {
1685                                         tinyxml2::XMLElement *methods =
1686                                             interface->FirstChildElement(
1687                                                 "method");
1688                                         while (methods != nullptr)
1689                                         {
1690                                             nlohmann::json argsArray =
1691                                                 nlohmann::json::array();
1692                                             tinyxml2::XMLElement *arg =
1693                                                 methods->FirstChildElement(
1694                                                     "arg");
1695                                             while (arg != nullptr)
1696                                             {
1697                                                 nlohmann::json thisArg;
1698                                                 for (const char *fieldName :
1699                                                      std::array<const char *,
1700                                                                 3>{"name",
1701                                                                    "direction",
1702                                                                    "type"})
1703                                                 {
1704                                                     const char *fieldValue =
1705                                                         arg->Attribute(
1706                                                             fieldName);
1707                                                     if (fieldValue != nullptr)
1708                                                     {
1709                                                         thisArg[fieldName] =
1710                                                             fieldValue;
1711                                                     }
1712                                                 }
1713                                                 argsArray.push_back(
1714                                                     std::move(thisArg));
1715                                                 arg = arg->NextSiblingElement(
1716                                                     "arg");
1717                                             }
1718 
1719                                             const char *name =
1720                                                 methods->Attribute("name");
1721                                             if (name != nullptr)
1722                                             {
1723                                                 methodsArray.push_back(
1724                                                     {{"name", name},
1725                                                      {"uri", "/bus/system/" +
1726                                                                  processName +
1727                                                                  objectPath +
1728                                                                  "/" +
1729                                                                  interfaceName +
1730                                                                  "/" + name},
1731                                                      {"args", argsArray}});
1732                                             }
1733                                             methods =
1734                                                 methods->NextSiblingElement(
1735                                                     "method");
1736                                         }
1737                                         tinyxml2::XMLElement *signals =
1738                                             interface->FirstChildElement(
1739                                                 "signal");
1740                                         while (signals != nullptr)
1741                                         {
1742                                             nlohmann::json argsArray =
1743                                                 nlohmann::json::array();
1744 
1745                                             tinyxml2::XMLElement *arg =
1746                                                 signals->FirstChildElement(
1747                                                     "arg");
1748                                             while (arg != nullptr)
1749                                             {
1750                                                 const char *name =
1751                                                     arg->Attribute("name");
1752                                                 const char *type =
1753                                                     arg->Attribute("type");
1754                                                 if (name != nullptr &&
1755                                                     type != nullptr)
1756                                                 {
1757                                                     argsArray.push_back({
1758                                                         {"name", name},
1759                                                         {"type", type},
1760                                                     });
1761                                                 }
1762                                                 arg = arg->NextSiblingElement(
1763                                                     "arg");
1764                                             }
1765                                             const char *name =
1766                                                 signals->Attribute("name");
1767                                             if (name != nullptr)
1768                                             {
1769                                                 signalsArray.push_back(
1770                                                     {{"name", name},
1771                                                      {"args", argsArray}});
1772                                             }
1773 
1774                                             signals =
1775                                                 signals->NextSiblingElement(
1776                                                     "signal");
1777                                         }
1778 
1779                                         break;
1780                                     }
1781 
1782                                     interface = interface->NextSiblingElement(
1783                                         "interface");
1784                                 }
1785                                 if (interface == nullptr)
1786                                 {
1787                                     // if we got to the end of the list and
1788                                     // never found a match, throw 404
1789                                     res.result(
1790                                         boost::beast::http::status::not_found);
1791                                 }
1792                             }
1793                         }
1794                         res.end();
1795                     },
1796                     processName, objectPath,
1797                     "org.freedesktop.DBus.Introspectable", "Introspect");
1798             }
1799             else
1800             {
1801                 if (req.method() != "POST"_method)
1802                 {
1803                     res.result(boost::beast::http::status::not_found);
1804                     res.end();
1805                     return;
1806                 }
1807 
1808                 nlohmann::json requestDbusData =
1809                     nlohmann::json::parse(req.body, nullptr, false);
1810 
1811                 if (requestDbusData.is_discarded())
1812                 {
1813                     res.result(boost::beast::http::status::bad_request);
1814                     res.end();
1815                     return;
1816                 }
1817                 if (!requestDbusData.is_array())
1818                 {
1819                     res.result(boost::beast::http::status::bad_request);
1820                     res.end();
1821                     return;
1822                 }
1823                 auto transaction = std::make_shared<InProgressActionData>(res);
1824 
1825                 transaction->path = objectPath;
1826                 transaction->methodName = methodName;
1827                 transaction->arguments = std::move(requestDbusData);
1828 
1829                 findActionOnInterface(transaction, processName);
1830             }
1831         });
1832 }
1833 } // namespace openbmc_mapper
1834 } // namespace crow
1835