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