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