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