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 <tinyxml2.h>
17 
18 #include <app.hpp>
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 <sdbusplus/message/types.hpp>
25 
26 #include <filesystem>
27 #include <fstream>
28 #include <regex>
29 #include <utility>
30 
31 namespace crow
32 {
33 namespace openbmc_mapper
34 {
35 
36 using GetSubTreeType = std::vector<
37     std::pair<std::string,
38               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
39 
40 const constexpr char* notFoundMsg = "404 Not Found";
41 const constexpr char* badReqMsg = "400 Bad Request";
42 const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
43 const constexpr char* forbiddenMsg = "403 Forbidden";
44 const constexpr char* methodFailedMsg = "500 Method Call Failed";
45 const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
46 const constexpr char* notFoundDesc =
47     "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
48 const constexpr char* propNotFoundDesc =
49     "The specified property cannot be found";
50 const constexpr char* noJsonDesc = "No JSON object could be decoded";
51 const constexpr char* methodNotFoundDesc =
52     "The specified method cannot be found";
53 const constexpr char* methodNotAllowedDesc = "Method not allowed";
54 const constexpr char* forbiddenPropDesc =
55     "The specified property cannot be created";
56 const constexpr char* forbiddenResDesc =
57     "The specified resource cannot be created";
58 
59 inline void setErrorResponse(crow::Response& res,
60                              boost::beast::http::status result,
61                              const std::string& desc,
62                              const std::string_view msg)
63 {
64     res.result(result);
65     res.jsonValue = {{"data", {{"description", desc}}},
66                      {"message", msg},
67                      {"status", "error"}};
68 }
69 
70 inline void
71     introspectObjects(const std::string& processName,
72                       const std::string& objectPath,
73                       const std::shared_ptr<bmcweb::AsyncResp>& transaction)
74 {
75     if (transaction->res.jsonValue.is_null())
76     {
77         transaction->res.jsonValue = {{"status", "ok"},
78                                       {"bus_name", processName},
79                                       {"objects", nlohmann::json::array()}};
80     }
81 
82     crow::connections::systemBus->async_method_call(
83         [transaction, processName{std::string(processName)},
84          objectPath{std::string(objectPath)}](
85             const boost::system::error_code ec,
86             const std::string& introspectXml) {
87             if (ec)
88             {
89                 BMCWEB_LOG_ERROR
90                     << "Introspect call failed with error: " << ec.message()
91                     << " on process: " << processName << " path: " << objectPath
92                     << "\n";
93                 return;
94             }
95             transaction->res.jsonValue["objects"].push_back(
96                 {{"path", objectPath}});
97 
98             tinyxml2::XMLDocument doc;
99 
100             doc.Parse(introspectXml.c_str());
101             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
102             if (pRoot == nullptr)
103             {
104                 BMCWEB_LOG_ERROR << "XML document failed to parse "
105                                  << processName << " " << objectPath << "\n";
106             }
107             else
108             {
109                 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
110                 while (node != nullptr)
111                 {
112                     const char* childPath = node->Attribute("name");
113                     if (childPath != nullptr)
114                     {
115                         std::string newpath;
116                         if (objectPath != "/")
117                         {
118                             newpath += objectPath;
119                         }
120                         newpath += std::string("/") + childPath;
121                         // introspect the subobjects as well
122                         introspectObjects(processName, newpath, transaction);
123                     }
124 
125                     node = node->NextSiblingElement("node");
126                 }
127             }
128         },
129         processName, objectPath, "org.freedesktop.DBus.Introspectable",
130         "Introspect");
131 }
132 
133 inline void getPropertiesForEnumerate(
134     const std::string& objectPath, const std::string& service,
135     const std::string& interface,
136     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
137 {
138     BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
139                      << service << " " << interface;
140 
141     crow::connections::systemBus->async_method_call(
142         [asyncResp, objectPath, service, interface](
143             const boost::system::error_code ec,
144             const std::vector<std::pair<
145                 std::string, dbus::utility::DbusVariantType>>& propertiesList) {
146             if (ec)
147             {
148                 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
149                                  << interface << " service " << service
150                                  << " failed with code " << ec;
151                 return;
152             }
153 
154             nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
155             nlohmann::json& objectJson = dataJson[objectPath];
156             if (objectJson.is_null())
157             {
158                 objectJson = nlohmann::json::object();
159             }
160 
161             for (const auto& [name, value] : propertiesList)
162             {
163                 nlohmann::json& propertyJson = objectJson[name];
164                 std::visit(
165                     [&propertyJson](auto&& val) {
166                         if constexpr (std::is_same_v<
167                                           std::decay_t<decltype(val)>,
168                                           sdbusplus::message::unix_fd>)
169                         {
170                             propertyJson = val.fd;
171                         }
172                         else
173                         {
174 
175                             propertyJson = val;
176                         }
177                     },
178                     value);
179             }
180         },
181         service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
182         interface);
183 }
184 
185 // Find any results that weren't picked up by ObjectManagers, to be
186 // called after all ObjectManagers are searched for and called.
187 inline void findRemainingObjectsForEnumerate(
188     const std::string& objectPath,
189     const std::shared_ptr<GetSubTreeType>& subtree,
190     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
191 {
192     BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
193     const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
194 
195     for (const auto& [path, interface_map] : *subtree)
196     {
197         if (path == objectPath)
198         {
199             // An enumerate does not return the target path's properties
200             continue;
201         }
202         if (dataJson.find(path) == dataJson.end())
203         {
204             for (const auto& [service, interfaces] : interface_map)
205             {
206                 for (const auto& interface : interfaces)
207                 {
208                     if (!boost::starts_with(interface, "org.freedesktop.DBus"))
209                     {
210                         getPropertiesForEnumerate(path, service, interface,
211                                                   asyncResp);
212                     }
213                 }
214             }
215         }
216     }
217 }
218 
219 struct InProgressEnumerateData
220 {
221     InProgressEnumerateData(
222         const std::string& objectPathIn,
223         const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
224         objectPath(objectPathIn),
225         asyncResp(asyncRespIn)
226     {}
227 
228     ~InProgressEnumerateData()
229     {
230         try
231         {
232             findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
233         }
234         catch (...)
235         {
236             BMCWEB_LOG_CRITICAL
237                 << "findRemainingObjectsForEnumerate threw exception";
238         }
239     }
240 
241     InProgressEnumerateData(const InProgressEnumerateData&) = delete;
242     InProgressEnumerateData(InProgressEnumerateData&&) = delete;
243     InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
244     InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
245 
246     const std::string objectPath;
247     std::shared_ptr<GetSubTreeType> subtree;
248     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
249 };
250 
251 inline void getManagedObjectsForEnumerate(
252     const std::string& objectName, const std::string& objectManagerPath,
253     const std::string& connectionName,
254     const std::shared_ptr<InProgressEnumerateData>& transaction)
255 {
256     BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName
257                      << " object_manager_path " << objectManagerPath
258                      << " connection_name " << connectionName;
259     crow::connections::systemBus->async_method_call(
260         [transaction, objectName,
261          connectionName](const boost::system::error_code ec,
262                          const dbus::utility::ManagedObjectType& objects) {
263             if (ec)
264             {
265                 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName
266                                  << " on connection " << connectionName
267                                  << " failed with code " << ec;
268                 return;
269             }
270 
271             nlohmann::json& dataJson =
272                 transaction->asyncResp->res.jsonValue["data"];
273 
274             for (const auto& objectPath : objects)
275             {
276                 if (boost::starts_with(objectPath.first.str, objectName))
277                 {
278                     BMCWEB_LOG_DEBUG << "Reading object "
279                                      << objectPath.first.str;
280                     nlohmann::json& objectJson = dataJson[objectPath.first.str];
281                     if (objectJson.is_null())
282                     {
283                         objectJson = nlohmann::json::object();
284                     }
285                     for (const auto& interface : objectPath.second)
286                     {
287                         for (const auto& property : interface.second)
288                         {
289                             nlohmann::json& propertyJson =
290                                 objectJson[property.first];
291                             std::visit(
292                                 [&propertyJson](auto&& val) {
293                                     if constexpr (
294                                         std::is_same_v<
295                                             std::decay_t<decltype(val)>,
296                                             sdbusplus::message::unix_fd>)
297                                     {
298                                         propertyJson = val.fd;
299                                     }
300                                     else
301                                     {
302 
303                                         propertyJson = val;
304                                     }
305                                 },
306                                 property.second);
307                         }
308                     }
309                 }
310                 for (const auto& interface : objectPath.second)
311                 {
312                     if (interface.first == "org.freedesktop.DBus.ObjectManager")
313                     {
314                         getManagedObjectsForEnumerate(
315                             objectPath.first.str, objectPath.first.str,
316                             connectionName, transaction);
317                     }
318                 }
319             }
320         },
321         connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager",
322         "GetManagedObjects");
323 }
324 
325 inline void findObjectManagerPathForEnumerate(
326     const std::string& objectName, const std::string& connectionName,
327     const std::shared_ptr<InProgressEnumerateData>& transaction)
328 {
329     BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName
330                      << " on connection:" << connectionName;
331     crow::connections::systemBus->async_method_call(
332         [transaction, objectName, connectionName](
333             const boost::system::error_code ec,
334             const boost::container::flat_map<
335                 std::string, boost::container::flat_map<
336                                  std::string, std::vector<std::string>>>&
337                 objects) {
338             if (ec)
339             {
340                 BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName
341                                  << " failed with code " << ec;
342                 return;
343             }
344 
345             for (const auto& pathGroup : objects)
346             {
347                 for (const auto& connectionGroup : pathGroup.second)
348                 {
349                     if (connectionGroup.first == connectionName)
350                     {
351                         // Found the object manager path for this resource.
352                         getManagedObjectsForEnumerate(
353                             objectName, pathGroup.first, connectionName,
354                             transaction);
355                         return;
356                     }
357                 }
358             }
359         },
360         "xyz.openbmc_project.ObjectMapper",
361         "/xyz/openbmc_project/object_mapper",
362         "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
363         std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
364 }
365 
366 // Uses GetObject to add the object info about the target /enumerate path to
367 // the results of GetSubTree, as GetSubTree will not return info for the
368 // target path, and then continues on enumerating the rest of the tree.
369 inline void getObjectAndEnumerate(
370     const std::shared_ptr<InProgressEnumerateData>& transaction)
371 {
372     using GetObjectType =
373         std::vector<std::pair<std::string, std::vector<std::string>>>;
374 
375     crow::connections::systemBus->async_method_call(
376         [transaction](const boost::system::error_code ec,
377                       const GetObjectType& objects) {
378             if (ec)
379             {
380                 BMCWEB_LOG_ERROR << "GetObject for path "
381                                  << transaction->objectPath
382                                  << " failed with code " << ec;
383                 return;
384             }
385 
386             BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
387                              << " has " << objects.size() << " entries";
388             if (!objects.empty())
389             {
390                 transaction->subtree->emplace_back(transaction->objectPath,
391                                                    objects);
392             }
393 
394             // Map indicating connection name, and the path where the object
395             // manager exists
396             boost::container::flat_map<std::string, std::string> connections;
397 
398             for (const auto& object : *(transaction->subtree))
399             {
400                 for (const auto& connection : object.second)
401                 {
402                     std::string& objectManagerPath =
403                         connections[connection.first];
404                     for (const auto& interface : connection.second)
405                     {
406                         BMCWEB_LOG_DEBUG << connection.first
407                                          << " has interface " << interface;
408                         if (interface == "org.freedesktop.DBus.ObjectManager")
409                         {
410                             BMCWEB_LOG_DEBUG << "found object manager path "
411                                              << object.first;
412                             objectManagerPath = object.first;
413                         }
414                     }
415                 }
416             }
417             BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
418 
419             for (const auto& connection : connections)
420             {
421                 // If we already know where the object manager is, we don't
422                 // need to search for it, we can call directly in to
423                 // getManagedObjects
424                 if (!connection.second.empty())
425                 {
426                     getManagedObjectsForEnumerate(
427                         transaction->objectPath, connection.second,
428                         connection.first, transaction);
429                 }
430                 else
431                 {
432                     // otherwise we need to find the object manager path
433                     // before we can continue
434                     findObjectManagerPathForEnumerate(
435                         transaction->objectPath, connection.first, transaction);
436                 }
437             }
438         },
439         "xyz.openbmc_project.ObjectMapper",
440         "/xyz/openbmc_project/object_mapper",
441         "xyz.openbmc_project.ObjectMapper", "GetObject",
442         transaction->objectPath, std::array<const char*, 0>());
443 }
444 
445 // Structure for storing data on an in progress action
446 struct InProgressActionData
447 {
448     InProgressActionData(crow::Response& resIn) : res(resIn)
449     {}
450     ~InProgressActionData()
451     {
452         // Methods could have been called across different owners
453         // and interfaces, where some calls failed and some passed.
454         //
455         // The rules for this are:
456         // * if no method was called - error
457         // * if a method failed and none passed - error
458         //   (converse: if at least one method passed - OK)
459         // * for the method output:
460         //   * if output processing didn't fail, return the data
461 
462         // Only deal with method returns if nothing failed earlier
463         if (res.result() == boost::beast::http::status::ok)
464         {
465             if (!methodPassed)
466             {
467                 if (!methodFailed)
468                 {
469                     setErrorResponse(res, boost::beast::http::status::not_found,
470                                      methodNotFoundDesc, notFoundMsg);
471                 }
472             }
473             else
474             {
475                 if (outputFailed)
476                 {
477                     setErrorResponse(
478                         res, boost::beast::http::status::internal_server_error,
479                         "Method output failure", methodOutputFailedMsg);
480                 }
481                 else
482                 {
483                     res.jsonValue = {{"status", "ok"},
484                                      {"message", "200 OK"},
485                                      {"data", methodResponse}};
486                 }
487             }
488         }
489 
490         res.end();
491     }
492     InProgressActionData(const InProgressActionData&) = delete;
493     InProgressActionData(InProgressActionData&&) = delete;
494     InProgressActionData& operator=(const InProgressActionData&) = delete;
495     InProgressActionData& operator=(InProgressActionData&&) = delete;
496 
497     void setErrorStatus(const std::string& desc)
498     {
499         setErrorResponse(res, boost::beast::http::status::bad_request, desc,
500                          badReqMsg);
501     }
502     crow::Response& res;
503     std::string path;
504     std::string methodName;
505     std::string interfaceName;
506     bool methodPassed = false;
507     bool methodFailed = false;
508     bool outputFailed = false;
509     bool convertedToArray = false;
510     nlohmann::json methodResponse;
511     nlohmann::json arguments;
512 };
513 
514 inline std::vector<std::string> dbusArgSplit(const std::string& string)
515 {
516     std::vector<std::string> ret;
517     if (string.empty())
518     {
519         return ret;
520     }
521     ret.emplace_back("");
522     int containerDepth = 0;
523 
524     for (std::string::const_iterator character = string.begin();
525          character != string.end(); character++)
526     {
527         ret.back() += *character;
528         switch (*character)
529         {
530             case ('a'):
531                 break;
532             case ('('):
533             case ('{'):
534                 containerDepth++;
535                 break;
536             case ('}'):
537             case (')'):
538                 containerDepth--;
539                 if (containerDepth == 0)
540                 {
541                     if (character + 1 != string.end())
542                     {
543                         ret.emplace_back("");
544                     }
545                 }
546                 break;
547             default:
548                 if (containerDepth == 0)
549                 {
550                     if (character + 1 != string.end())
551                     {
552                         ret.emplace_back("");
553                     }
554                 }
555                 break;
556         }
557     }
558 
559     return ret;
560 }
561 
562 inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
563                              const nlohmann::json& inputJson)
564 {
565     int r = 0;
566     BMCWEB_LOG_DEBUG << "Converting "
567                      << inputJson.dump(2, ' ', true,
568                                        nlohmann::json::error_handler_t::replace)
569                      << " to type: " << argType;
570     const std::vector<std::string> argTypes = dbusArgSplit(argType);
571 
572     // Assume a single object for now.
573     const nlohmann::json* j = &inputJson;
574     nlohmann::json::const_iterator jIt = inputJson.begin();
575 
576     for (const std::string& argCode : argTypes)
577     {
578         // If we are decoding multiple objects, grab the pointer to the
579         // iterator, and increment it for the next loop
580         if (argTypes.size() > 1)
581         {
582             if (jIt == inputJson.end())
583             {
584                 return -2;
585             }
586             j = &*jIt;
587             jIt++;
588         }
589         const int64_t* intValue = j->get_ptr<const int64_t*>();
590         const std::string* stringValue = j->get_ptr<const std::string*>();
591         const double* doubleValue = j->get_ptr<const double*>();
592         const bool* b = j->get_ptr<const bool*>();
593         int64_t v = 0;
594         double d = 0.0;
595 
596         // Do some basic type conversions that make sense.  uint can be
597         // converted to int.  int and uint can be converted to double
598         if (intValue == nullptr)
599         {
600             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
601             if (uintValue != nullptr)
602             {
603                 v = static_cast<int64_t>(*uintValue);
604                 intValue = &v;
605             }
606         }
607         if (doubleValue == nullptr)
608         {
609             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
610             if (uintValue != nullptr)
611             {
612                 d = static_cast<double>(*uintValue);
613                 doubleValue = &d;
614             }
615         }
616         if (doubleValue == nullptr)
617         {
618             if (intValue != nullptr)
619             {
620                 d = static_cast<double>(*intValue);
621                 doubleValue = &d;
622             }
623         }
624 
625         if (argCode == "s")
626         {
627             if (stringValue == nullptr)
628             {
629                 return -1;
630             }
631             r = sd_bus_message_append_basic(
632                 m, argCode[0], static_cast<const void*>(stringValue->data()));
633             if (r < 0)
634             {
635                 return r;
636             }
637         }
638         else if (argCode == "i")
639         {
640             if (intValue == nullptr)
641             {
642                 return -1;
643             }
644             if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
645                 (*intValue > std::numeric_limits<int32_t>::max()))
646             {
647                 return -ERANGE;
648             }
649             int32_t i = static_cast<int32_t>(*intValue);
650             r = sd_bus_message_append_basic(m, argCode[0], &i);
651             if (r < 0)
652             {
653                 return r;
654             }
655         }
656         else if (argCode == "b")
657         {
658             // lots of ways bool could be represented here.  Try them all
659             int boolInt = false;
660             if (intValue != nullptr)
661             {
662                 if (*intValue == 1)
663                 {
664                     boolInt = true;
665                 }
666                 else if (*intValue == 0)
667                 {
668                     boolInt = false;
669                 }
670                 else
671                 {
672                     return -ERANGE;
673                 }
674             }
675             else if (b != nullptr)
676             {
677                 boolInt = *b ? 1 : 0;
678             }
679             else if (stringValue != nullptr)
680             {
681                 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
682             }
683             else
684             {
685                 return -1;
686             }
687             r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
688             if (r < 0)
689             {
690                 return r;
691             }
692         }
693         else if (argCode == "n")
694         {
695             if (intValue == nullptr)
696             {
697                 return -1;
698             }
699             if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
700                 (*intValue > std::numeric_limits<int16_t>::max()))
701             {
702                 return -ERANGE;
703             }
704             int16_t n = static_cast<int16_t>(*intValue);
705             r = sd_bus_message_append_basic(m, argCode[0], &n);
706             if (r < 0)
707             {
708                 return r;
709             }
710         }
711         else if (argCode == "x")
712         {
713             if (intValue == nullptr)
714             {
715                 return -1;
716             }
717             r = sd_bus_message_append_basic(m, argCode[0], intValue);
718             if (r < 0)
719             {
720                 return r;
721             }
722         }
723         else if (argCode == "y")
724         {
725             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
726             if (uintValue == nullptr)
727             {
728                 return -1;
729             }
730             if (*uintValue > std::numeric_limits<uint8_t>::max())
731             {
732                 return -ERANGE;
733             }
734             uint8_t y = static_cast<uint8_t>(*uintValue);
735             r = sd_bus_message_append_basic(m, argCode[0], &y);
736         }
737         else if (argCode == "q")
738         {
739             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
740             if (uintValue == nullptr)
741             {
742                 return -1;
743             }
744             if (*uintValue > std::numeric_limits<uint16_t>::max())
745             {
746                 return -ERANGE;
747             }
748             uint16_t q = static_cast<uint16_t>(*uintValue);
749             r = sd_bus_message_append_basic(m, argCode[0], &q);
750         }
751         else if (argCode == "u")
752         {
753             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
754             if (uintValue == nullptr)
755             {
756                 return -1;
757             }
758             if (*uintValue > std::numeric_limits<uint32_t>::max())
759             {
760                 return -ERANGE;
761             }
762             uint32_t u = static_cast<uint32_t>(*uintValue);
763             r = sd_bus_message_append_basic(m, argCode[0], &u);
764         }
765         else if (argCode == "t")
766         {
767             const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
768             if (uintValue == nullptr)
769             {
770                 return -1;
771             }
772             r = sd_bus_message_append_basic(m, argCode[0], uintValue);
773         }
774         else if (argCode == "d")
775         {
776             if (doubleValue == nullptr)
777             {
778                 return -1;
779             }
780             if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
781                 (*doubleValue > std::numeric_limits<double>::max()))
782             {
783                 return -ERANGE;
784             }
785             sd_bus_message_append_basic(m, argCode[0], doubleValue);
786         }
787         else if (boost::starts_with(argCode, "a"))
788         {
789             std::string containedType = argCode.substr(1);
790             r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
791                                               containedType.c_str());
792             if (r < 0)
793             {
794                 return r;
795             }
796 
797             for (const auto& it : *j)
798             {
799                 r = convertJsonToDbus(m, containedType, it);
800                 if (r < 0)
801                 {
802                     return r;
803                 }
804             }
805             sd_bus_message_close_container(m);
806         }
807         else if (boost::starts_with(argCode, "v"))
808         {
809             std::string containedType = argCode.substr(1);
810             BMCWEB_LOG_DEBUG << "variant type: " << argCode
811                              << " appending variant of type: " << containedType;
812             r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
813                                               containedType.c_str());
814             if (r < 0)
815             {
816                 return r;
817             }
818 
819             r = convertJsonToDbus(m, containedType, inputJson);
820             if (r < 0)
821             {
822                 return r;
823             }
824 
825             r = sd_bus_message_close_container(m);
826             if (r < 0)
827             {
828                 return r;
829             }
830         }
831         else if (boost::starts_with(argCode, "(") &&
832                  boost::ends_with(argCode, ")"))
833         {
834             std::string containedType = argCode.substr(1, argCode.size() - 1);
835             r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
836                                               containedType.c_str());
837             if (r < 0)
838             {
839                 return r;
840             }
841 
842             nlohmann::json::const_iterator it = j->begin();
843             for (const std::string& argCode2 : dbusArgSplit(argType))
844             {
845                 if (it == j->end())
846                 {
847                     return -1;
848                 }
849                 r = convertJsonToDbus(m, argCode2, *it);
850                 if (r < 0)
851                 {
852                     return r;
853                 }
854                 it++;
855             }
856             r = sd_bus_message_close_container(m);
857         }
858         else if (boost::starts_with(argCode, "{") &&
859                  boost::ends_with(argCode, "}"))
860         {
861             std::string containedType = argCode.substr(1, argCode.size() - 1);
862             r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
863                                               containedType.c_str());
864             if (r < 0)
865             {
866                 return r;
867             }
868 
869             std::vector<std::string> codes = dbusArgSplit(containedType);
870             if (codes.size() != 2)
871             {
872                 return -1;
873             }
874             const std::string& keyType = codes[0];
875             const std::string& valueType = codes[1];
876             for (const auto& it : j->items())
877             {
878                 r = convertJsonToDbus(m, keyType, it.key());
879                 if (r < 0)
880                 {
881                     return r;
882                 }
883 
884                 r = convertJsonToDbus(m, valueType, it.value());
885                 if (r < 0)
886                 {
887                     return r;
888                 }
889             }
890             r = sd_bus_message_close_container(m);
891         }
892         else
893         {
894             return -2;
895         }
896         if (r < 0)
897         {
898             return r;
899         }
900 
901         if (argTypes.size() > 1)
902         {
903             jIt++;
904         }
905     }
906 
907     return r;
908 }
909 
910 template <typename T>
911 int readMessageItem(const std::string& typeCode, sdbusplus::message::message& m,
912                     nlohmann::json& data)
913 {
914     T value;
915 
916     int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
917     if (r < 0)
918     {
919         BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
920                          << " failed!";
921         return r;
922     }
923 
924     data = value;
925     return 0;
926 }
927 
928 int convertDBusToJSON(const std::string& returnType,
929                       sdbusplus::message::message& m, nlohmann::json& response);
930 
931 inline int readDictEntryFromMessage(const std::string& typeCode,
932                                     sdbusplus::message::message& m,
933                                     nlohmann::json& object)
934 {
935     std::vector<std::string> types = dbusArgSplit(typeCode);
936     if (types.size() != 2)
937     {
938         BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
939                          << types.size();
940         return -1;
941     }
942 
943     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
944                                            typeCode.c_str());
945     if (r < 0)
946     {
947         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
948         return r;
949     }
950 
951     nlohmann::json key;
952     r = convertDBusToJSON(types[0], m, key);
953     if (r < 0)
954     {
955         return r;
956     }
957 
958     const std::string* keyPtr = key.get_ptr<const std::string*>();
959     if (keyPtr == nullptr)
960     {
961         // json doesn't support non-string keys.  If we hit this condition,
962         // convert the result to a string so we can proceed
963         key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
964         keyPtr = key.get_ptr<const std::string*>();
965         // in theory this can't fail now, but lets be paranoid about it
966         // anyway
967         if (keyPtr == nullptr)
968         {
969             return -1;
970         }
971     }
972     nlohmann::json& value = object[*keyPtr];
973 
974     r = convertDBusToJSON(types[1], m, value);
975     if (r < 0)
976     {
977         return r;
978     }
979 
980     r = sd_bus_message_exit_container(m.get());
981     if (r < 0)
982     {
983         BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
984         return r;
985     }
986 
987     return 0;
988 }
989 
990 inline int readArrayFromMessage(const std::string& typeCode,
991                                 sdbusplus::message::message& m,
992                                 nlohmann::json& data)
993 {
994     if (typeCode.size() < 2)
995     {
996         BMCWEB_LOG_ERROR << "Type code " << typeCode
997                          << " too small for an array";
998         return -1;
999     }
1000 
1001     std::string containedType = typeCode.substr(1);
1002 
1003     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
1004                                            containedType.c_str());
1005     if (r < 0)
1006     {
1007         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1008                          << r;
1009         return r;
1010     }
1011 
1012     bool dict = boost::starts_with(containedType, "{") &&
1013                 boost::ends_with(containedType, "}");
1014 
1015     if (dict)
1016     {
1017         // Remove the { }
1018         containedType = containedType.substr(1, containedType.size() - 2);
1019         data = nlohmann::json::object();
1020     }
1021     else
1022     {
1023         data = nlohmann::json::array();
1024     }
1025 
1026     while (true)
1027     {
1028         r = sd_bus_message_at_end(m.get(), false);
1029         if (r < 0)
1030         {
1031             BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
1032             return r;
1033         }
1034 
1035         if (r > 0)
1036         {
1037             break;
1038         }
1039 
1040         // Dictionaries are only ever seen in an array
1041         if (dict)
1042         {
1043             r = readDictEntryFromMessage(containedType, m, data);
1044             if (r < 0)
1045             {
1046                 return r;
1047             }
1048         }
1049         else
1050         {
1051             data.push_back(nlohmann::json());
1052 
1053             r = convertDBusToJSON(containedType, m, data.back());
1054             if (r < 0)
1055             {
1056                 return r;
1057             }
1058         }
1059     }
1060 
1061     r = sd_bus_message_exit_container(m.get());
1062     if (r < 0)
1063     {
1064         BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1065         return r;
1066     }
1067 
1068     return 0;
1069 }
1070 
1071 inline int readStructFromMessage(const std::string& typeCode,
1072                                  sdbusplus::message::message& m,
1073                                  nlohmann::json& data)
1074 {
1075     if (typeCode.size() < 3)
1076     {
1077         BMCWEB_LOG_ERROR << "Type code " << typeCode
1078                          << " too small for a struct";
1079         return -1;
1080     }
1081 
1082     std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1083     std::vector<std::string> types = dbusArgSplit(containedTypes);
1084 
1085     int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1086                                            containedTypes.c_str());
1087     if (r < 0)
1088     {
1089         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1090                          << r;
1091         return r;
1092     }
1093 
1094     for (const std::string& type : types)
1095     {
1096         data.push_back(nlohmann::json());
1097         r = convertDBusToJSON(type, m, data.back());
1098         if (r < 0)
1099         {
1100             return r;
1101         }
1102     }
1103 
1104     r = sd_bus_message_exit_container(m.get());
1105     if (r < 0)
1106     {
1107         BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1108         return r;
1109     }
1110     return 0;
1111 }
1112 
1113 inline int readVariantFromMessage(sdbusplus::message::message& m,
1114                                   nlohmann::json& data)
1115 {
1116     const char* containerType = nullptr;
1117     int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
1118     if (r < 0)
1119     {
1120         BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1121         return r;
1122     }
1123 
1124     r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1125                                        containerType);
1126     if (r < 0)
1127     {
1128         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1129                          << r;
1130         return r;
1131     }
1132 
1133     r = convertDBusToJSON(containerType, m, data);
1134     if (r < 0)
1135     {
1136         return r;
1137     }
1138 
1139     r = sd_bus_message_exit_container(m.get());
1140     if (r < 0)
1141     {
1142         BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1143         return r;
1144     }
1145 
1146     return 0;
1147 }
1148 
1149 inline int convertDBusToJSON(const std::string& returnType,
1150                              sdbusplus::message::message& m,
1151                              nlohmann::json& response)
1152 {
1153     int r = 0;
1154     const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1155 
1156     for (const std::string& typeCode : returnTypes)
1157     {
1158         nlohmann::json* thisElement = &response;
1159         if (returnTypes.size() > 1)
1160         {
1161             response.push_back(nlohmann::json{});
1162             thisElement = &response.back();
1163         }
1164 
1165         if (typeCode == "s" || typeCode == "g" || typeCode == "o")
1166         {
1167             r = readMessageItem<char*>(typeCode, m, *thisElement);
1168             if (r < 0)
1169             {
1170                 return r;
1171             }
1172         }
1173         else if (typeCode == "b")
1174         {
1175             r = readMessageItem<int>(typeCode, m, *thisElement);
1176             if (r < 0)
1177             {
1178                 return r;
1179             }
1180 
1181             *thisElement = static_cast<bool>(thisElement->get<int>());
1182         }
1183         else if (typeCode == "u")
1184         {
1185             r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
1186             if (r < 0)
1187             {
1188                 return r;
1189             }
1190         }
1191         else if (typeCode == "i")
1192         {
1193             r = readMessageItem<int32_t>(typeCode, m, *thisElement);
1194             if (r < 0)
1195             {
1196                 return r;
1197             }
1198         }
1199         else if (typeCode == "x")
1200         {
1201             r = readMessageItem<int64_t>(typeCode, m, *thisElement);
1202             if (r < 0)
1203             {
1204                 return r;
1205             }
1206         }
1207         else if (typeCode == "t")
1208         {
1209             r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
1210             if (r < 0)
1211             {
1212                 return r;
1213             }
1214         }
1215         else if (typeCode == "n")
1216         {
1217             r = readMessageItem<int16_t>(typeCode, m, *thisElement);
1218             if (r < 0)
1219             {
1220                 return r;
1221             }
1222         }
1223         else if (typeCode == "q")
1224         {
1225             r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
1226             if (r < 0)
1227             {
1228                 return r;
1229             }
1230         }
1231         else if (typeCode == "y")
1232         {
1233             r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
1234             if (r < 0)
1235             {
1236                 return r;
1237             }
1238         }
1239         else if (typeCode == "d")
1240         {
1241             r = readMessageItem<double>(typeCode, m, *thisElement);
1242             if (r < 0)
1243             {
1244                 return r;
1245             }
1246         }
1247         else if (typeCode == "h")
1248         {
1249             r = readMessageItem<int>(typeCode, m, *thisElement);
1250             if (r < 0)
1251             {
1252                 return r;
1253             }
1254         }
1255         else if (boost::starts_with(typeCode, "a"))
1256         {
1257             r = readArrayFromMessage(typeCode, m, *thisElement);
1258             if (r < 0)
1259             {
1260                 return r;
1261             }
1262         }
1263         else if (boost::starts_with(typeCode, "(") &&
1264                  boost::ends_with(typeCode, ")"))
1265         {
1266             r = readStructFromMessage(typeCode, m, *thisElement);
1267             if (r < 0)
1268             {
1269                 return r;
1270             }
1271         }
1272         else if (boost::starts_with(typeCode, "v"))
1273         {
1274             r = readVariantFromMessage(m, *thisElement);
1275             if (r < 0)
1276             {
1277                 return r;
1278             }
1279         }
1280         else
1281         {
1282             BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1283             return -2;
1284         }
1285     }
1286 
1287     return 0;
1288 }
1289 
1290 inline void handleMethodResponse(
1291     const std::shared_ptr<InProgressActionData>& transaction,
1292     sdbusplus::message::message& m, const std::string& returnType)
1293 {
1294     nlohmann::json data;
1295 
1296     int r = convertDBusToJSON(returnType, m, data);
1297     if (r < 0)
1298     {
1299         transaction->outputFailed = true;
1300         return;
1301     }
1302 
1303     if (data.is_null())
1304     {
1305         return;
1306     }
1307 
1308     if (transaction->methodResponse.is_null())
1309     {
1310         transaction->methodResponse = std::move(data);
1311         return;
1312     }
1313 
1314     // If they're both dictionaries or arrays, merge into one.
1315     // Otherwise, make the results an array with every result
1316     // an entry.  Could also just fail in that case, but it
1317     // seems better to get the data back somehow.
1318 
1319     if (transaction->methodResponse.is_object() && data.is_object())
1320     {
1321         for (const auto& obj : data.items())
1322         {
1323             // Note: Will overwrite the data for a duplicate key
1324             transaction->methodResponse.emplace(obj.key(),
1325                                                 std::move(obj.value()));
1326         }
1327         return;
1328     }
1329 
1330     if (transaction->methodResponse.is_array() && data.is_array())
1331     {
1332         for (auto& obj : data)
1333         {
1334             transaction->methodResponse.push_back(std::move(obj));
1335         }
1336         return;
1337     }
1338 
1339     if (!transaction->convertedToArray)
1340     {
1341         // They are different types. May as well turn them into an array
1342         nlohmann::json j = std::move(transaction->methodResponse);
1343         transaction->methodResponse = nlohmann::json::array();
1344         transaction->methodResponse.push_back(std::move(j));
1345         transaction->methodResponse.push_back(std::move(data));
1346         transaction->convertedToArray = true;
1347     }
1348     else
1349     {
1350         transaction->methodResponse.push_back(std::move(data));
1351     }
1352 }
1353 
1354 inline void findActionOnInterface(
1355     const std::shared_ptr<InProgressActionData>& transaction,
1356     const std::string& connectionName)
1357 {
1358     BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1359                      << connectionName;
1360     crow::connections::systemBus->async_method_call(
1361         [transaction, connectionName{std::string(connectionName)}](
1362             const boost::system::error_code ec,
1363             const std::string& introspectXml) {
1364             BMCWEB_LOG_DEBUG << "got xml:\n " << introspectXml;
1365             if (ec)
1366             {
1367                 BMCWEB_LOG_ERROR
1368                     << "Introspect call failed with error: " << ec.message()
1369                     << " on process: " << connectionName << "\n";
1370                 return;
1371             }
1372             tinyxml2::XMLDocument doc;
1373 
1374             doc.Parse(introspectXml.data(), introspectXml.size());
1375             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1376             if (pRoot == nullptr)
1377             {
1378                 BMCWEB_LOG_ERROR << "XML document failed to parse "
1379                                  << connectionName << "\n";
1380                 return;
1381             }
1382             tinyxml2::XMLElement* interfaceNode =
1383                 pRoot->FirstChildElement("interface");
1384             while (interfaceNode != nullptr)
1385             {
1386                 const char* thisInterfaceName =
1387                     interfaceNode->Attribute("name");
1388                 if (thisInterfaceName != nullptr)
1389                 {
1390                     if (!transaction->interfaceName.empty() &&
1391                         (transaction->interfaceName != thisInterfaceName))
1392                     {
1393                         interfaceNode =
1394                             interfaceNode->NextSiblingElement("interface");
1395                         continue;
1396                     }
1397 
1398                     tinyxml2::XMLElement* methodNode =
1399                         interfaceNode->FirstChildElement("method");
1400                     while (methodNode != nullptr)
1401                     {
1402                         const char* thisMethodName =
1403                             methodNode->Attribute("name");
1404                         BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1405                         if (thisMethodName != nullptr &&
1406                             thisMethodName == transaction->methodName)
1407                         {
1408                             BMCWEB_LOG_DEBUG
1409                                 << "Found method named " << thisMethodName
1410                                 << " on interface " << thisInterfaceName;
1411                             sdbusplus::message::message m =
1412                                 crow::connections::systemBus->new_method_call(
1413                                     connectionName.c_str(),
1414                                     transaction->path.c_str(),
1415                                     thisInterfaceName,
1416                                     transaction->methodName.c_str());
1417 
1418                             tinyxml2::XMLElement* argumentNode =
1419                                 methodNode->FirstChildElement("arg");
1420 
1421                             std::string returnType;
1422 
1423                             // Find the output type
1424                             while (argumentNode != nullptr)
1425                             {
1426                                 const char* argDirection =
1427                                     argumentNode->Attribute("direction");
1428                                 const char* argType =
1429                                     argumentNode->Attribute("type");
1430                                 if (argDirection != nullptr &&
1431                                     argType != nullptr &&
1432                                     std::string(argDirection) == "out")
1433                                 {
1434                                     returnType = argType;
1435                                     break;
1436                                 }
1437                                 argumentNode =
1438                                     argumentNode->NextSiblingElement("arg");
1439                             }
1440 
1441                             nlohmann::json::const_iterator argIt =
1442                                 transaction->arguments.begin();
1443 
1444                             argumentNode = methodNode->FirstChildElement("arg");
1445 
1446                             while (argumentNode != nullptr)
1447                             {
1448                                 const char* argDirection =
1449                                     argumentNode->Attribute("direction");
1450                                 const char* argType =
1451                                     argumentNode->Attribute("type");
1452                                 if (argDirection != nullptr &&
1453                                     argType != nullptr &&
1454                                     std::string(argDirection) == "in")
1455                                 {
1456                                     if (argIt == transaction->arguments.end())
1457                                     {
1458                                         transaction->setErrorStatus(
1459                                             "Invalid method args");
1460                                         return;
1461                                     }
1462                                     if (convertJsonToDbus(m.get(),
1463                                                           std::string(argType),
1464                                                           *argIt) < 0)
1465                                     {
1466                                         transaction->setErrorStatus(
1467                                             "Invalid method arg type");
1468                                         return;
1469                                     }
1470 
1471                                     argIt++;
1472                                 }
1473                                 argumentNode =
1474                                     argumentNode->NextSiblingElement("arg");
1475                             }
1476 
1477                             crow::connections::systemBus->async_send(
1478                                 m, [transaction, returnType](
1479                                        boost::system::error_code ec2,
1480                                        sdbusplus::message::message& m2) {
1481                                     if (ec2)
1482                                     {
1483                                         transaction->methodFailed = true;
1484                                         const sd_bus_error* e = m2.get_error();
1485 
1486                                         if (e)
1487                                         {
1488                                             setErrorResponse(
1489                                                 transaction->res,
1490                                                 boost::beast::http::status::
1491                                                     bad_request,
1492                                                 e->name, e->message);
1493                                         }
1494                                         else
1495                                         {
1496                                             setErrorResponse(
1497                                                 transaction->res,
1498                                                 boost::beast::http::status::
1499                                                     bad_request,
1500                                                 "Method call failed",
1501                                                 methodFailedMsg);
1502                                         }
1503                                         return;
1504                                     }
1505                                     transaction->methodPassed = true;
1506 
1507                                     handleMethodResponse(transaction, m2,
1508                                                          returnType);
1509                                 });
1510                             break;
1511                         }
1512                         methodNode = methodNode->NextSiblingElement("method");
1513                     }
1514                 }
1515                 interfaceNode = interfaceNode->NextSiblingElement("interface");
1516             }
1517         },
1518         connectionName, transaction->path,
1519         "org.freedesktop.DBus.Introspectable", "Introspect");
1520 }
1521 
1522 inline void handleAction(const crow::Request& req,
1523                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1524                          const std::string& objectPath,
1525                          const std::string& methodName)
1526 {
1527     BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1528                      << methodName;
1529     nlohmann::json requestDbusData =
1530         nlohmann::json::parse(req.body, nullptr, false);
1531 
1532     if (requestDbusData.is_discarded())
1533     {
1534         setErrorResponse(asyncResp->res,
1535                          boost::beast::http::status::bad_request, noJsonDesc,
1536                          badReqMsg);
1537         return;
1538     }
1539     nlohmann::json::iterator data = requestDbusData.find("data");
1540     if (data == requestDbusData.end())
1541     {
1542         setErrorResponse(asyncResp->res,
1543                          boost::beast::http::status::bad_request, noJsonDesc,
1544                          badReqMsg);
1545         return;
1546     }
1547 
1548     if (!data->is_array())
1549     {
1550         setErrorResponse(asyncResp->res,
1551                          boost::beast::http::status::bad_request, noJsonDesc,
1552                          badReqMsg);
1553         return;
1554     }
1555     auto transaction = std::make_shared<InProgressActionData>(asyncResp->res);
1556 
1557     transaction->path = objectPath;
1558     transaction->methodName = methodName;
1559     transaction->arguments = std::move(*data);
1560     crow::connections::systemBus->async_method_call(
1561         [transaction](
1562             const boost::system::error_code ec,
1563             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1564                 interfaceNames) {
1565             if (ec || interfaceNames.empty())
1566             {
1567                 BMCWEB_LOG_ERROR << "Can't find object";
1568                 setErrorResponse(transaction->res,
1569                                  boost::beast::http::status::not_found,
1570                                  notFoundDesc, notFoundMsg);
1571                 return;
1572             }
1573 
1574             BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1575                              << " object(s)";
1576 
1577             for (const std::pair<std::string, std::vector<std::string>>&
1578                      object : interfaceNames)
1579             {
1580                 findActionOnInterface(transaction, object.first);
1581             }
1582         },
1583         "xyz.openbmc_project.ObjectMapper",
1584         "/xyz/openbmc_project/object_mapper",
1585         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1586         std::array<std::string, 0>());
1587 }
1588 
1589 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1590                          const std::string& objectPath)
1591 {
1592     BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1593 
1594     crow::connections::systemBus->async_method_call(
1595         [asyncResp, objectPath](
1596             const boost::system::error_code ec,
1597             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1598                 interfaceNames) {
1599             if (ec || interfaceNames.empty())
1600             {
1601                 BMCWEB_LOG_ERROR << "Can't find object";
1602                 setErrorResponse(asyncResp->res,
1603                                  boost::beast::http::status::method_not_allowed,
1604                                  methodNotAllowedDesc, methodNotAllowedMsg);
1605                 return;
1606             }
1607 
1608             auto transaction =
1609                 std::make_shared<InProgressActionData>(asyncResp->res);
1610             transaction->path = objectPath;
1611             transaction->methodName = "Delete";
1612             transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1613 
1614             for (const std::pair<std::string, std::vector<std::string>>&
1615                      object : interfaceNames)
1616             {
1617                 findActionOnInterface(transaction, object.first);
1618             }
1619         },
1620         "xyz.openbmc_project.ObjectMapper",
1621         "/xyz/openbmc_project/object_mapper",
1622         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1623         std::array<const char*, 0>());
1624 }
1625 
1626 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1627                        const std::string& objectPath, int32_t depth = 0)
1628 {
1629     crow::connections::systemBus->async_method_call(
1630         [asyncResp](const boost::system::error_code ec,
1631                     const std::vector<std::string>& objectPaths) {
1632             if (ec)
1633             {
1634                 setErrorResponse(asyncResp->res,
1635                                  boost::beast::http::status::not_found,
1636                                  notFoundDesc, notFoundMsg);
1637             }
1638             else
1639             {
1640                 asyncResp->res.jsonValue = {{"status", "ok"},
1641                                             {"message", "200 OK"},
1642                                             {"data", objectPaths}};
1643             }
1644         },
1645         "xyz.openbmc_project.ObjectMapper",
1646         "/xyz/openbmc_project/object_mapper",
1647         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
1648         depth, std::array<std::string, 0>());
1649 }
1650 
1651 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1652                             const std::string& objectPath)
1653 {
1654     BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1655 
1656     asyncResp->res.jsonValue = {{"message", "200 OK"},
1657                                 {"status", "ok"},
1658                                 {"data", nlohmann::json::object()}};
1659 
1660     crow::connections::systemBus->async_method_call(
1661         [objectPath, asyncResp](const boost::system::error_code ec,
1662                                 const GetSubTreeType& objectNames) {
1663             auto transaction = std::make_shared<InProgressEnumerateData>(
1664                 objectPath, asyncResp);
1665 
1666             transaction->subtree =
1667                 std::make_shared<GetSubTreeType>(objectNames);
1668 
1669             if (ec)
1670             {
1671                 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1672                                  << transaction->objectPath;
1673                 setErrorResponse(transaction->asyncResp->res,
1674                                  boost::beast::http::status::not_found,
1675                                  notFoundDesc, notFoundMsg);
1676                 return;
1677             }
1678 
1679             // Add the data for the path passed in to the results
1680             // as if GetSubTree returned it, and continue on enumerating
1681             getObjectAndEnumerate(transaction);
1682         },
1683         "xyz.openbmc_project.ObjectMapper",
1684         "/xyz/openbmc_project/object_mapper",
1685         "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
1686         std::array<const char*, 0>());
1687 }
1688 
1689 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1690                       std::string& objectPath, std::string& destProperty)
1691 {
1692     BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1693     std::shared_ptr<std::string> propertyName =
1694         std::make_shared<std::string>(std::move(destProperty));
1695 
1696     std::shared_ptr<std::string> path =
1697         std::make_shared<std::string>(std::move(objectPath));
1698 
1699     using GetObjectType =
1700         std::vector<std::pair<std::string, std::vector<std::string>>>;
1701     crow::connections::systemBus->async_method_call(
1702         [asyncResp, path, propertyName](const boost::system::error_code ec,
1703                                         const GetObjectType& objectNames) {
1704             if (ec || objectNames.empty())
1705             {
1706                 setErrorResponse(asyncResp->res,
1707                                  boost::beast::http::status::not_found,
1708                                  notFoundDesc, notFoundMsg);
1709                 return;
1710             }
1711             std::shared_ptr<nlohmann::json> response =
1712                 std::make_shared<nlohmann::json>(nlohmann::json::object());
1713             // The mapper should never give us an empty interface names
1714             // list, but check anyway
1715             for (const std::pair<std::string, std::vector<std::string>>&
1716                      connection : objectNames)
1717             {
1718                 const std::vector<std::string>& interfaceNames =
1719                     connection.second;
1720 
1721                 if (interfaceNames.empty())
1722                 {
1723                     setErrorResponse(asyncResp->res,
1724                                      boost::beast::http::status::not_found,
1725                                      notFoundDesc, notFoundMsg);
1726                     return;
1727                 }
1728 
1729                 for (const std::string& interface : interfaceNames)
1730                 {
1731                     sdbusplus::message::message m =
1732                         crow::connections::systemBus->new_method_call(
1733                             connection.first.c_str(), path->c_str(),
1734                             "org.freedesktop.DBus.Properties", "GetAll");
1735                     m.append(interface);
1736                     crow::connections::systemBus->async_send(
1737                         m, [asyncResp, response,
1738                             propertyName](const boost::system::error_code ec2,
1739                                           sdbusplus::message::message& msg) {
1740                             if (ec2)
1741                             {
1742                                 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1743                                                  << ec2;
1744                             }
1745                             else
1746                             {
1747                                 nlohmann::json properties;
1748                                 int r =
1749                                     convertDBusToJSON("a{sv}", msg, properties);
1750                                 if (r < 0)
1751                                 {
1752                                     BMCWEB_LOG_ERROR
1753                                         << "convertDBusToJSON failed";
1754                                 }
1755                                 else
1756                                 {
1757                                     for (auto& prop : properties.items())
1758                                     {
1759                                         // if property name is empty, or
1760                                         // matches our search query, add it
1761                                         // to the response json
1762 
1763                                         if (propertyName->empty())
1764                                         {
1765                                             (*response)[prop.key()] =
1766                                                 std::move(prop.value());
1767                                         }
1768                                         else if (prop.key() == *propertyName)
1769                                         {
1770                                             *response = std::move(prop.value());
1771                                         }
1772                                     }
1773                                 }
1774                             }
1775                             if (response.use_count() == 1)
1776                             {
1777                                 if (!propertyName->empty() && response->empty())
1778                                 {
1779                                     setErrorResponse(
1780                                         asyncResp->res,
1781                                         boost::beast::http::status::not_found,
1782                                         propNotFoundDesc, notFoundMsg);
1783                                 }
1784                                 else
1785                                 {
1786                                     asyncResp->res.jsonValue = {
1787                                         {"status", "ok"},
1788                                         {"message", "200 OK"},
1789                                         {"data", *response}};
1790                                 }
1791                             }
1792                         });
1793                 }
1794             }
1795         },
1796         "xyz.openbmc_project.ObjectMapper",
1797         "/xyz/openbmc_project/object_mapper",
1798         "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1799         std::array<std::string, 0>());
1800 }
1801 
1802 struct AsyncPutRequest
1803 {
1804     AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
1805         asyncResp(resIn)
1806     {}
1807     ~AsyncPutRequest()
1808     {
1809         if (asyncResp->res.jsonValue.empty())
1810         {
1811             setErrorResponse(asyncResp->res,
1812                              boost::beast::http::status::forbidden,
1813                              forbiddenMsg, forbiddenPropDesc);
1814         }
1815     }
1816 
1817     AsyncPutRequest(const AsyncPutRequest&) = delete;
1818     AsyncPutRequest(AsyncPutRequest&&) = delete;
1819     AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1820     AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1821 
1822     void setErrorStatus(const std::string& desc)
1823     {
1824         setErrorResponse(asyncResp->res,
1825                          boost::beast::http::status::internal_server_error,
1826                          desc, badReqMsg);
1827     }
1828 
1829     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1830     std::string objectPath;
1831     std::string propertyName;
1832     nlohmann::json propertyValue;
1833 };
1834 
1835 inline void handlePut(const crow::Request& req,
1836                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1837                       const std::string& objectPath,
1838                       const std::string& destProperty)
1839 {
1840     if (destProperty.empty())
1841     {
1842         setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
1843                          forbiddenResDesc, forbiddenMsg);
1844         return;
1845     }
1846 
1847     nlohmann::json requestDbusData =
1848         nlohmann::json::parse(req.body, nullptr, false);
1849 
1850     if (requestDbusData.is_discarded())
1851     {
1852         setErrorResponse(asyncResp->res,
1853                          boost::beast::http::status::bad_request, noJsonDesc,
1854                          badReqMsg);
1855         return;
1856     }
1857 
1858     nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1859     if (propertyIt == requestDbusData.end())
1860     {
1861         setErrorResponse(asyncResp->res,
1862                          boost::beast::http::status::bad_request, noJsonDesc,
1863                          badReqMsg);
1864         return;
1865     }
1866     const nlohmann::json& propertySetValue = *propertyIt;
1867     auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
1868     transaction->objectPath = objectPath;
1869     transaction->propertyName = destProperty;
1870     transaction->propertyValue = propertySetValue;
1871 
1872     using GetObjectType =
1873         std::vector<std::pair<std::string, std::vector<std::string>>>;
1874 
1875     crow::connections::systemBus->async_method_call(
1876         [transaction](const boost::system::error_code ec2,
1877                       const GetObjectType& objectNames) {
1878             if (!ec2 && objectNames.empty())
1879             {
1880                 setErrorResponse(transaction->asyncResp->res,
1881                                  boost::beast::http::status::not_found,
1882                                  propNotFoundDesc, notFoundMsg);
1883                 return;
1884             }
1885 
1886             for (const std::pair<std::string, std::vector<std::string>>&
1887                      connection : objectNames)
1888             {
1889                 const std::string& connectionName = connection.first;
1890 
1891                 crow::connections::systemBus->async_method_call(
1892                     [connectionName{std::string(connectionName)},
1893                      transaction](const boost::system::error_code ec3,
1894                                   const std::string& introspectXml) {
1895                         if (ec3)
1896                         {
1897                             BMCWEB_LOG_ERROR
1898                                 << "Introspect call failed with error: "
1899                                 << ec3.message()
1900                                 << " on process: " << connectionName;
1901                             transaction->setErrorStatus("Unexpected Error");
1902                             return;
1903                         }
1904                         tinyxml2::XMLDocument doc;
1905 
1906                         doc.Parse(introspectXml.c_str());
1907                         tinyxml2::XMLNode* pRoot =
1908                             doc.FirstChildElement("node");
1909                         if (pRoot == nullptr)
1910                         {
1911                             BMCWEB_LOG_ERROR << "XML document failed to parse: "
1912                                              << introspectXml;
1913                             transaction->setErrorStatus("Unexpected Error");
1914                             return;
1915                         }
1916                         tinyxml2::XMLElement* ifaceNode =
1917                             pRoot->FirstChildElement("interface");
1918                         while (ifaceNode != nullptr)
1919                         {
1920                             const char* interfaceName =
1921                                 ifaceNode->Attribute("name");
1922                             BMCWEB_LOG_DEBUG << "found interface "
1923                                              << interfaceName;
1924                             tinyxml2::XMLElement* propNode =
1925                                 ifaceNode->FirstChildElement("property");
1926                             while (propNode != nullptr)
1927                             {
1928                                 const char* propertyName =
1929                                     propNode->Attribute("name");
1930                                 BMCWEB_LOG_DEBUG << "Found property "
1931                                                  << propertyName;
1932                                 if (propertyName == transaction->propertyName)
1933                                 {
1934                                     const char* argType =
1935                                         propNode->Attribute("type");
1936                                     if (argType != nullptr)
1937                                     {
1938                                         sdbusplus::message::message m =
1939                                             crow::connections::systemBus
1940                                                 ->new_method_call(
1941                                                     connectionName.c_str(),
1942                                                     transaction->objectPath
1943                                                         .c_str(),
1944                                                     "org.freedesktop.DBus."
1945                                                     "Properties",
1946                                                     "Set");
1947                                         m.append(interfaceName,
1948                                                  transaction->propertyName);
1949                                         int r = sd_bus_message_open_container(
1950                                             m.get(), SD_BUS_TYPE_VARIANT,
1951                                             argType);
1952                                         if (r < 0)
1953                                         {
1954                                             transaction->setErrorStatus(
1955                                                 "Unexpected Error");
1956                                             return;
1957                                         }
1958                                         r = convertJsonToDbus(
1959                                             m.get(), argType,
1960                                             transaction->propertyValue);
1961                                         if (r < 0)
1962                                         {
1963                                             if (r == -ERANGE)
1964                                             {
1965                                                 transaction->setErrorStatus(
1966                                                     "Provided property value "
1967                                                     "is out of range for the "
1968                                                     "property type");
1969                                             }
1970                                             else
1971                                             {
1972                                                 transaction->setErrorStatus(
1973                                                     "Invalid arg type");
1974                                             }
1975                                             return;
1976                                         }
1977                                         r = sd_bus_message_close_container(
1978                                             m.get());
1979                                         if (r < 0)
1980                                         {
1981                                             transaction->setErrorStatus(
1982                                                 "Unexpected Error");
1983                                             return;
1984                                         }
1985                                         crow::connections::systemBus
1986                                             ->async_send(
1987                                                 m,
1988                                                 [transaction](
1989                                                     boost::system::error_code
1990                                                         ec,
1991                                                     sdbusplus::message::message&
1992                                                         m2) {
1993                                                     BMCWEB_LOG_DEBUG << "sent";
1994                                                     if (ec)
1995                                                     {
1996                                                         const sd_bus_error* e =
1997                                                             m2.get_error();
1998                                                         setErrorResponse(
1999                                                             transaction
2000                                                                 ->asyncResp
2001                                                                 ->res,
2002                                                             boost::beast::http::
2003                                                                 status::
2004                                                                     forbidden,
2005                                                             (e) ? e->name
2006                                                                 : ec.category()
2007                                                                       .name(),
2008                                                             (e) ? e->message
2009                                                                 : ec.message());
2010                                                     }
2011                                                     else
2012                                                     {
2013                                                         transaction->asyncResp
2014                                                             ->res.jsonValue = {
2015                                                             {"status", "ok"},
2016                                                             {"message",
2017                                                              "200 OK"},
2018                                                             {"data", nullptr}};
2019                                                     }
2020                                                 });
2021                                     }
2022                                 }
2023                                 propNode =
2024                                     propNode->NextSiblingElement("property");
2025                             }
2026                             ifaceNode =
2027                                 ifaceNode->NextSiblingElement("interface");
2028                         }
2029                     },
2030                     connectionName, transaction->objectPath,
2031                     "org.freedesktop.DBus.Introspectable", "Introspect");
2032             }
2033         },
2034         "xyz.openbmc_project.ObjectMapper",
2035         "/xyz/openbmc_project/object_mapper",
2036         "xyz.openbmc_project.ObjectMapper", "GetObject",
2037         transaction->objectPath, std::array<std::string, 0>());
2038 }
2039 
2040 inline void handleDBusUrl(const crow::Request& req,
2041                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2042                           std::string& objectPath)
2043 {
2044 
2045     // If accessing a single attribute, fill in and update objectPath,
2046     // otherwise leave destProperty blank
2047     std::string destProperty = "";
2048     const char* attrSeperator = "/attr/";
2049     size_t attrPosition = objectPath.find(attrSeperator);
2050     if (attrPosition != objectPath.npos)
2051     {
2052         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2053                                          objectPath.length());
2054         objectPath = objectPath.substr(0, attrPosition);
2055     }
2056 
2057     if (req.method() == boost::beast::http::verb::post)
2058     {
2059         constexpr const char* actionSeperator = "/action/";
2060         size_t actionPosition = objectPath.find(actionSeperator);
2061         if (actionPosition != objectPath.npos)
2062         {
2063             std::string postProperty =
2064                 objectPath.substr((actionPosition + strlen(actionSeperator)),
2065                                   objectPath.length());
2066             objectPath = objectPath.substr(0, actionPosition);
2067             handleAction(req, asyncResp, objectPath, postProperty);
2068             return;
2069         }
2070     }
2071     else if (req.method() == boost::beast::http::verb::get)
2072     {
2073         if (boost::ends_with(objectPath, "/enumerate"))
2074         {
2075             objectPath.erase(objectPath.end() - sizeof("enumerate"),
2076                              objectPath.end());
2077             handleEnumerate(asyncResp, objectPath);
2078         }
2079         else if (boost::ends_with(objectPath, "/list"))
2080         {
2081             objectPath.erase(objectPath.end() - sizeof("list"),
2082                              objectPath.end());
2083             handleList(asyncResp, objectPath);
2084         }
2085         else
2086         {
2087             // Trim any trailing "/" at the end
2088             if (boost::ends_with(objectPath, "/"))
2089             {
2090                 objectPath.pop_back();
2091                 handleList(asyncResp, objectPath, 1);
2092             }
2093             else
2094             {
2095                 handleGet(asyncResp, objectPath, destProperty);
2096             }
2097         }
2098         return;
2099     }
2100     else if (req.method() == boost::beast::http::verb::put)
2101     {
2102         handlePut(req, asyncResp, objectPath, destProperty);
2103         return;
2104     }
2105     else if (req.method() == boost::beast::http::verb::delete_)
2106     {
2107         handleDelete(asyncResp, objectPath);
2108         return;
2109     }
2110 
2111     setErrorResponse(asyncResp->res,
2112                      boost::beast::http::status::method_not_allowed,
2113                      methodNotAllowedDesc, methodNotAllowedMsg);
2114 }
2115 
2116 inline void requestRoutes(App& app)
2117 {
2118     BMCWEB_ROUTE(app, "/bus/")
2119         .privileges({{"Login"}})
2120         .methods(boost::beast::http::verb::get)(
2121             [](const crow::Request&,
2122                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2123                 asyncResp->res.jsonValue = {{"buses", {{{"name", "system"}}}},
2124                                             {"status", "ok"}};
2125             });
2126 
2127     BMCWEB_ROUTE(app, "/bus/system/")
2128         .privileges({{"Login"}})
2129         .methods(boost::beast::http::verb::get)(
2130             [](const crow::Request&,
2131                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2132                 auto myCallback = [asyncResp](
2133                                       const boost::system::error_code ec,
2134                                       std::vector<std::string>& names) {
2135                     if (ec)
2136                     {
2137                         BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2138                         asyncResp->res.result(
2139                             boost::beast::http::status::internal_server_error);
2140                     }
2141                     else
2142                     {
2143                         std::sort(names.begin(), names.end());
2144                         asyncResp->res.jsonValue = {{"status", "ok"}};
2145                         auto& objectsSub = asyncResp->res.jsonValue["objects"];
2146                         for (auto& name : names)
2147                         {
2148                             objectsSub.push_back({{"name", name}});
2149                         }
2150                     }
2151                 };
2152                 crow::connections::systemBus->async_method_call(
2153                     std::move(myCallback), "org.freedesktop.DBus", "/",
2154                     "org.freedesktop.DBus", "ListNames");
2155             });
2156 
2157     BMCWEB_ROUTE(app, "/list/")
2158         .privileges({{"Login"}})
2159         .methods(boost::beast::http::verb::get)(
2160             [](const crow::Request&,
2161                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2162                 handleList(asyncResp, "/");
2163             });
2164 
2165     BMCWEB_ROUTE(app, "/xyz/<path>")
2166         .privileges({{"Login"}})
2167         .methods(boost::beast::http::verb::get)(
2168             [](const crow::Request& req,
2169                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2170                const std::string& path) {
2171                 std::string objectPath = "/xyz/" + path;
2172                 handleDBusUrl(req, asyncResp, objectPath);
2173             });
2174 
2175     BMCWEB_ROUTE(app, "/xyz/<path>")
2176         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2177         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2178                  boost::beast::http::verb::delete_)(
2179             [](const crow::Request& req,
2180                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2181                const std::string& path) {
2182                 std::string objectPath = "/xyz/" + path;
2183                 handleDBusUrl(req, asyncResp, objectPath);
2184             });
2185 
2186     BMCWEB_ROUTE(app, "/org/<path>")
2187         .privileges({{"Login"}})
2188         .methods(boost::beast::http::verb::get)(
2189             [](const crow::Request& req,
2190                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2191                const std::string& path) {
2192                 std::string objectPath = "/org/" + path;
2193                 handleDBusUrl(req, asyncResp, objectPath);
2194             });
2195 
2196     BMCWEB_ROUTE(app, "/org/<path>")
2197         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2198         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2199                  boost::beast::http::verb::delete_)(
2200             [](const crow::Request& req,
2201                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2202                const std::string& path) {
2203                 std::string objectPath = "/org/" + path;
2204                 handleDBusUrl(req, asyncResp, objectPath);
2205             });
2206 
2207     BMCWEB_ROUTE(app, "/download/dump/<str>/")
2208         .privileges({{"ConfigureManager"}})
2209         .methods(boost::beast::http::verb::get)(
2210             [](const crow::Request&,
2211                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2212                const std::string& dumpId) {
2213                 std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
2214                 if (!std::regex_match(dumpId, validFilename))
2215                 {
2216                     asyncResp->res.result(
2217                         boost::beast::http::status::bad_request);
2218                     return;
2219                 }
2220                 std::filesystem::path loc(
2221                     "/var/lib/phosphor-debug-collector/dumps");
2222 
2223                 loc /= dumpId;
2224 
2225                 if (!std::filesystem::exists(loc) ||
2226                     !std::filesystem::is_directory(loc))
2227                 {
2228                     BMCWEB_LOG_ERROR << loc << "Not found";
2229                     asyncResp->res.result(
2230                         boost::beast::http::status::not_found);
2231                     return;
2232                 }
2233                 std::filesystem::directory_iterator files(loc);
2234 
2235                 for (const auto& file : files)
2236                 {
2237                     std::ifstream readFile(file.path());
2238                     if (!readFile.good())
2239                     {
2240                         continue;
2241                     }
2242 
2243                     asyncResp->res.addHeader("Content-Type",
2244                                              "application/octet-stream");
2245 
2246                     // Assuming only one dump file will be present in the dump
2247                     // id directory
2248                     std::string dumpFileName = file.path().filename().string();
2249 
2250                     // Filename should be in alphanumeric, dot and underscore
2251                     // Its based on phosphor-debug-collector application
2252                     // dumpfile format
2253                     std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2254                     if (!std::regex_match(dumpFileName, dumpFileRegex))
2255                     {
2256                         BMCWEB_LOG_ERROR << "Invalid dump filename "
2257                                          << dumpFileName;
2258                         asyncResp->res.result(
2259                             boost::beast::http::status::not_found);
2260                         return;
2261                     }
2262                     std::string contentDispositionParam =
2263                         "attachment; filename=\"" + dumpFileName + "\"";
2264 
2265                     asyncResp->res.addHeader("Content-Disposition",
2266                                              contentDispositionParam);
2267 
2268                     asyncResp->res.body() = {
2269                         std::istreambuf_iterator<char>(readFile),
2270                         std::istreambuf_iterator<char>()};
2271                     return;
2272                 }
2273                 asyncResp->res.result(boost::beast::http::status::not_found);
2274                 return;
2275             });
2276 
2277     BMCWEB_ROUTE(app, "/bus/system/<str>/")
2278         .privileges({{"Login"}})
2279 
2280         .methods(boost::beast::http::verb::get)(
2281             [](const crow::Request&,
2282                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2283                const std::string& connection) {
2284                 introspectObjects(
2285                     connection, "/",
2286                     std::make_shared<bmcweb::AsyncResp>(asyncResp->res));
2287             });
2288 
2289     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2290         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2291         .methods(boost::beast::http::verb::get, boost::beast::http::verb::post)(
2292             [](const crow::Request& req,
2293                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2294                const std::string& processName,
2295                const std::string& requestedPath) {
2296                 std::vector<std::string> strs;
2297                 boost::split(strs, requestedPath, boost::is_any_of("/"));
2298                 std::string objectPath;
2299                 std::string interfaceName;
2300                 std::string methodName;
2301                 auto it = strs.begin();
2302                 if (it == strs.end())
2303                 {
2304                     objectPath = "/";
2305                 }
2306                 while (it != strs.end())
2307                 {
2308                     // Check if segment contains ".".  If it does, it must be an
2309                     // interface
2310                     if (it->find(".") != std::string::npos)
2311                     {
2312                         break;
2313                         // This check is necessary as the trailing slash gets
2314                         // parsed as part of our <path> specifier above, which
2315                         // causes the normal trailing backslash redirector to
2316                         // fail.
2317                     }
2318                     if (!it->empty())
2319                     {
2320                         objectPath += "/" + *it;
2321                     }
2322                     it++;
2323                 }
2324                 if (it != strs.end())
2325                 {
2326                     interfaceName = *it;
2327                     it++;
2328 
2329                     // after interface, we might have a method name
2330                     if (it != strs.end())
2331                     {
2332                         methodName = *it;
2333                         it++;
2334                     }
2335                 }
2336                 if (it != strs.end())
2337                 {
2338                     // if there is more levels past the method name, something
2339                     // went wrong, return not found
2340                     asyncResp->res.result(
2341                         boost::beast::http::status::not_found);
2342                     return;
2343                 }
2344                 if (interfaceName.empty())
2345                 {
2346                     crow::connections::systemBus->async_method_call(
2347                         [asyncResp, processName,
2348                          objectPath](const boost::system::error_code ec,
2349                                      const std::string& introspectXml) {
2350                             if (ec)
2351                             {
2352                                 BMCWEB_LOG_ERROR
2353                                     << "Introspect call failed with error: "
2354                                     << ec.message()
2355                                     << " on process: " << processName
2356                                     << " path: " << objectPath << "\n";
2357                                 return;
2358                             }
2359                             tinyxml2::XMLDocument doc;
2360 
2361                             doc.Parse(introspectXml.c_str());
2362                             tinyxml2::XMLNode* pRoot =
2363                                 doc.FirstChildElement("node");
2364                             if (pRoot == nullptr)
2365                             {
2366                                 BMCWEB_LOG_ERROR
2367                                     << "XML document failed to parse "
2368                                     << processName << " " << objectPath << "\n";
2369                                 asyncResp->res.jsonValue = {
2370                                     {"status", "XML parse error"}};
2371                                 asyncResp->res.result(
2372                                     boost::beast::http::status::
2373                                         internal_server_error);
2374                                 return;
2375                             }
2376 
2377                             BMCWEB_LOG_DEBUG << introspectXml;
2378                             asyncResp->res.jsonValue = {
2379                                 {"status", "ok"},
2380                                 {"bus_name", processName},
2381                                 {"object_path", objectPath}};
2382                             nlohmann::json& interfacesArray =
2383                                 asyncResp->res.jsonValue["interfaces"];
2384                             interfacesArray = nlohmann::json::array();
2385                             tinyxml2::XMLElement* interface =
2386                                 pRoot->FirstChildElement("interface");
2387 
2388                             while (interface != nullptr)
2389                             {
2390                                 const char* ifaceName =
2391                                     interface->Attribute("name");
2392                                 if (ifaceName != nullptr)
2393                                 {
2394                                     interfacesArray.push_back(
2395                                         {{"name", ifaceName}});
2396                                 }
2397 
2398                                 interface =
2399                                     interface->NextSiblingElement("interface");
2400                             }
2401                         },
2402                         processName, objectPath,
2403                         "org.freedesktop.DBus.Introspectable", "Introspect");
2404                 }
2405                 else if (methodName.empty())
2406                 {
2407                     crow::connections::systemBus->async_method_call(
2408                         [asyncResp, processName, objectPath,
2409                          interfaceName](const boost::system::error_code ec,
2410                                         const std::string& introspectXml) {
2411                             if (ec)
2412                             {
2413                                 BMCWEB_LOG_ERROR
2414                                     << "Introspect call failed with error: "
2415                                     << ec.message()
2416                                     << " on process: " << processName
2417                                     << " path: " << objectPath << "\n";
2418                                 return;
2419                             }
2420                             tinyxml2::XMLDocument doc;
2421 
2422                             doc.Parse(introspectXml.data(),
2423                                       introspectXml.size());
2424                             tinyxml2::XMLNode* pRoot =
2425                                 doc.FirstChildElement("node");
2426                             if (pRoot == nullptr)
2427                             {
2428                                 BMCWEB_LOG_ERROR
2429                                     << "XML document failed to parse "
2430                                     << processName << " " << objectPath << "\n";
2431                                 asyncResp->res.result(
2432                                     boost::beast::http::status::
2433                                         internal_server_error);
2434                                 return;
2435                             }
2436                             asyncResp->res.jsonValue = {
2437                                 {"status", "ok"},
2438                                 {"bus_name", processName},
2439                                 {"interface", interfaceName},
2440                                 {"object_path", objectPath}};
2441 
2442                             nlohmann::json& methodsArray =
2443                                 asyncResp->res.jsonValue["methods"];
2444                             methodsArray = nlohmann::json::array();
2445 
2446                             nlohmann::json& signalsArray =
2447                                 asyncResp->res.jsonValue["signals"];
2448                             signalsArray = nlohmann::json::array();
2449 
2450                             nlohmann::json& propertiesObj =
2451                                 asyncResp->res.jsonValue["properties"];
2452                             propertiesObj = nlohmann::json::object();
2453 
2454                             // if we know we're the only call, build the
2455                             // json directly
2456                             tinyxml2::XMLElement* interface =
2457                                 pRoot->FirstChildElement("interface");
2458                             while (interface != nullptr)
2459                             {
2460                                 const char* ifaceName =
2461                                     interface->Attribute("name");
2462 
2463                                 if (ifaceName != nullptr &&
2464                                     ifaceName == interfaceName)
2465                                 {
2466                                     break;
2467                                 }
2468 
2469                                 interface =
2470                                     interface->NextSiblingElement("interface");
2471                             }
2472                             if (interface == nullptr)
2473                             {
2474                                 // if we got to the end of the list and
2475                                 // never found a match, throw 404
2476                                 asyncResp->res.result(
2477                                     boost::beast::http::status::not_found);
2478                                 return;
2479                             }
2480 
2481                             tinyxml2::XMLElement* methods =
2482                                 interface->FirstChildElement("method");
2483                             while (methods != nullptr)
2484                             {
2485                                 nlohmann::json argsArray =
2486                                     nlohmann::json::array();
2487                                 tinyxml2::XMLElement* arg =
2488                                     methods->FirstChildElement("arg");
2489                                 while (arg != nullptr)
2490                                 {
2491                                     nlohmann::json thisArg;
2492                                     for (const char* fieldName :
2493                                          std::array<const char*, 3>{
2494                                              "name", "direction", "type"})
2495                                     {
2496                                         const char* fieldValue =
2497                                             arg->Attribute(fieldName);
2498                                         if (fieldValue != nullptr)
2499                                         {
2500                                             thisArg[fieldName] = fieldValue;
2501                                         }
2502                                     }
2503                                     argsArray.push_back(std::move(thisArg));
2504                                     arg = arg->NextSiblingElement("arg");
2505                                 }
2506 
2507                                 const char* name = methods->Attribute("name");
2508                                 if (name != nullptr)
2509                                 {
2510                                     std::string uri;
2511                                     uri.reserve(14 + processName.size() +
2512                                                 objectPath.size() +
2513                                                 interfaceName.size() +
2514                                                 strlen(name));
2515                                     uri += "/bus/system/";
2516                                     uri += processName;
2517                                     uri += objectPath;
2518                                     uri += "/";
2519                                     uri += interfaceName;
2520                                     uri += "/";
2521                                     uri += name;
2522                                     methodsArray.push_back(
2523                                         {{"name", name},
2524                                          {"uri", std::move(uri)},
2525                                          {"args", argsArray}});
2526                                 }
2527                                 methods = methods->NextSiblingElement("method");
2528                             }
2529                             tinyxml2::XMLElement* signals =
2530                                 interface->FirstChildElement("signal");
2531                             while (signals != nullptr)
2532                             {
2533                                 nlohmann::json argsArray =
2534                                     nlohmann::json::array();
2535 
2536                                 tinyxml2::XMLElement* arg =
2537                                     signals->FirstChildElement("arg");
2538                                 while (arg != nullptr)
2539                                 {
2540                                     const char* name = arg->Attribute("name");
2541                                     const char* type = arg->Attribute("type");
2542                                     if (name != nullptr && type != nullptr)
2543                                     {
2544                                         argsArray.push_back({
2545                                             {"name", name},
2546                                             {"type", type},
2547                                         });
2548                                     }
2549                                     arg = arg->NextSiblingElement("arg");
2550                                 }
2551                                 const char* name = signals->Attribute("name");
2552                                 if (name != nullptr)
2553                                 {
2554                                     signalsArray.push_back(
2555                                         {{"name", name}, {"args", argsArray}});
2556                                 }
2557 
2558                                 signals = signals->NextSiblingElement("signal");
2559                             }
2560 
2561                             tinyxml2::XMLElement* property =
2562                                 interface->FirstChildElement("property");
2563                             while (property != nullptr)
2564                             {
2565                                 const char* name = property->Attribute("name");
2566                                 const char* type = property->Attribute("type");
2567                                 if (type != nullptr && name != nullptr)
2568                                 {
2569                                     sdbusplus::message::message m =
2570                                         crow::connections::systemBus
2571                                             ->new_method_call(
2572                                                 processName.c_str(),
2573                                                 objectPath.c_str(),
2574                                                 "org.freedesktop."
2575                                                 "DBus."
2576                                                 "Properties",
2577                                                 "Get");
2578                                     m.append(interfaceName, name);
2579                                     nlohmann::json& propertyItem =
2580                                         propertiesObj[name];
2581                                     crow::connections::systemBus->async_send(
2582                                         m,
2583                                         [&propertyItem, asyncResp](
2584                                             boost::system::error_code& e,
2585                                             sdbusplus::message::message& msg) {
2586                                             if (e)
2587                                             {
2588                                                 return;
2589                                             }
2590 
2591                                             convertDBusToJSON("v", msg,
2592                                                               propertyItem);
2593                                         });
2594                                 }
2595                                 property =
2596                                     property->NextSiblingElement("property");
2597                             }
2598                         },
2599                         processName, objectPath,
2600                         "org.freedesktop.DBus.Introspectable", "Introspect");
2601                 }
2602                 else
2603                 {
2604                     if (req.method() != boost::beast::http::verb::post)
2605                     {
2606                         asyncResp->res.result(
2607                             boost::beast::http::status::not_found);
2608                         return;
2609                     }
2610 
2611                     nlohmann::json requestDbusData =
2612                         nlohmann::json::parse(req.body, nullptr, false);
2613 
2614                     if (requestDbusData.is_discarded())
2615                     {
2616                         asyncResp->res.result(
2617                             boost::beast::http::status::bad_request);
2618                         return;
2619                     }
2620                     if (!requestDbusData.is_array())
2621                     {
2622                         asyncResp->res.result(
2623                             boost::beast::http::status::bad_request);
2624                         return;
2625                     }
2626                     auto transaction =
2627                         std::make_shared<InProgressActionData>(asyncResp->res);
2628 
2629                     transaction->path = objectPath;
2630                     transaction->methodName = methodName;
2631                     transaction->arguments = std::move(requestDbusData);
2632 
2633                     findActionOnInterface(transaction, processName);
2634                 }
2635             });
2636 }
2637 } // namespace openbmc_mapper
2638 } // namespace crow
2639