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