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     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                         nlohmann::json::const_iterator argIt =
1429                             transaction->arguments.begin();
1430 
1431                         argumentNode = methodNode->FirstChildElement("arg");
1432 
1433                         while (argumentNode != nullptr)
1434                         {
1435                             const char* argDirection =
1436                                 argumentNode->Attribute("direction");
1437                             const char* argType =
1438                                 argumentNode->Attribute("type");
1439                             if (argDirection != nullptr && argType != nullptr &&
1440                                 std::string(argDirection) == "in")
1441                             {
1442                                 if (argIt == transaction->arguments.end())
1443                                 {
1444                                     transaction->setErrorStatus(
1445                                         "Invalid method args");
1446                                     return;
1447                                 }
1448                                 if (convertJsonToDbus(m.get(),
1449                                                       std::string(argType),
1450                                                       *argIt) < 0)
1451                                 {
1452                                     transaction->setErrorStatus(
1453                                         "Invalid method arg type");
1454                                     return;
1455                                 }
1456 
1457                                 argIt++;
1458                             }
1459                             argumentNode =
1460                                 argumentNode->NextSiblingElement("arg");
1461                         }
1462 
1463                         crow::connections::systemBus->async_send(
1464                             m,
1465                             [transaction,
1466                              returnType](boost::system::error_code ec2,
1467                                          sdbusplus::message::message& m2) {
1468                             if (ec2)
1469                             {
1470                                 transaction->methodFailed = true;
1471                                 const sd_bus_error* e = m2.get_error();
1472 
1473                                 if (e != nullptr)
1474                                 {
1475                                     setErrorResponse(
1476                                         transaction->res,
1477                                         boost::beast::http::status::bad_request,
1478                                         e->name, e->message);
1479                                 }
1480                                 else
1481                                 {
1482                                     setErrorResponse(
1483                                         transaction->res,
1484                                         boost::beast::http::status::bad_request,
1485                                         "Method call failed", methodFailedMsg);
1486                                 }
1487                                 return;
1488                             }
1489                             transaction->methodPassed = true;
1490 
1491                             handleMethodResponse(transaction, m2, returnType);
1492                             });
1493                         break;
1494                     }
1495                     methodNode = methodNode->NextSiblingElement("method");
1496                 }
1497             }
1498             interfaceNode = interfaceNode->NextSiblingElement("interface");
1499         }
1500         },
1501         connectionName, transaction->path,
1502         "org.freedesktop.DBus.Introspectable", "Introspect");
1503 }
1504 
1505 inline void handleAction(const crow::Request& req,
1506                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1507                          const std::string& objectPath,
1508                          const std::string& methodName)
1509 {
1510     BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1511                      << methodName;
1512     nlohmann::json requestDbusData =
1513         nlohmann::json::parse(req.body, nullptr, false);
1514 
1515     if (requestDbusData.is_discarded())
1516     {
1517         setErrorResponse(asyncResp->res,
1518                          boost::beast::http::status::bad_request, noJsonDesc,
1519                          badReqMsg);
1520         return;
1521     }
1522     nlohmann::json::iterator data = requestDbusData.find("data");
1523     if (data == requestDbusData.end())
1524     {
1525         setErrorResponse(asyncResp->res,
1526                          boost::beast::http::status::bad_request, noJsonDesc,
1527                          badReqMsg);
1528         return;
1529     }
1530 
1531     if (!data->is_array())
1532     {
1533         setErrorResponse(asyncResp->res,
1534                          boost::beast::http::status::bad_request, noJsonDesc,
1535                          badReqMsg);
1536         return;
1537     }
1538     auto transaction = std::make_shared<InProgressActionData>(asyncResp->res);
1539 
1540     transaction->path = objectPath;
1541     transaction->methodName = methodName;
1542     transaction->arguments = std::move(*data);
1543     crow::connections::systemBus->async_method_call(
1544         [transaction](
1545             const boost::system::error_code ec,
1546             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1547                 interfaceNames) {
1548         if (ec || interfaceNames.empty())
1549         {
1550             BMCWEB_LOG_ERROR << "Can't find object";
1551             setErrorResponse(transaction->res,
1552                              boost::beast::http::status::not_found,
1553                              notFoundDesc, notFoundMsg);
1554             return;
1555         }
1556 
1557         BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1558                          << " object(s)";
1559 
1560         for (const std::pair<std::string, std::vector<std::string>>& object :
1561              interfaceNames)
1562         {
1563             findActionOnInterface(transaction, object.first);
1564         }
1565         },
1566         "xyz.openbmc_project.ObjectMapper",
1567         "/xyz/openbmc_project/object_mapper",
1568         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1569         std::array<std::string, 0>());
1570 }
1571 
1572 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1573                          const std::string& objectPath)
1574 {
1575     BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1576 
1577     crow::connections::systemBus->async_method_call(
1578         [asyncResp, objectPath](
1579             const boost::system::error_code ec,
1580             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1581                 interfaceNames) {
1582         if (ec || interfaceNames.empty())
1583         {
1584             BMCWEB_LOG_ERROR << "Can't find object";
1585             setErrorResponse(asyncResp->res,
1586                              boost::beast::http::status::method_not_allowed,
1587                              methodNotAllowedDesc, methodNotAllowedMsg);
1588             return;
1589         }
1590 
1591         auto transaction =
1592             std::make_shared<InProgressActionData>(asyncResp->res);
1593         transaction->path = objectPath;
1594         transaction->methodName = "Delete";
1595         transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1596 
1597         for (const std::pair<std::string, std::vector<std::string>>& object :
1598              interfaceNames)
1599         {
1600             findActionOnInterface(transaction, object.first);
1601         }
1602         },
1603         "xyz.openbmc_project.ObjectMapper",
1604         "/xyz/openbmc_project/object_mapper",
1605         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1606         std::array<const char*, 0>());
1607 }
1608 
1609 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1610                        const std::string& objectPath, int32_t depth = 0)
1611 {
1612     crow::connections::systemBus->async_method_call(
1613         [asyncResp](
1614             const boost::system::error_code ec,
1615             const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
1616         if (ec)
1617         {
1618             setErrorResponse(asyncResp->res,
1619                              boost::beast::http::status::not_found,
1620                              notFoundDesc, notFoundMsg);
1621         }
1622         else
1623         {
1624             asyncResp->res.jsonValue["status"] = "ok";
1625             asyncResp->res.jsonValue["message"] = "200 OK";
1626             asyncResp->res.jsonValue["data"] = objectPaths;
1627         }
1628         },
1629         "xyz.openbmc_project.ObjectMapper",
1630         "/xyz/openbmc_project/object_mapper",
1631         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
1632         depth, std::array<std::string, 0>());
1633 }
1634 
1635 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1636                             const std::string& objectPath)
1637 {
1638     BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1639 
1640     asyncResp->res.jsonValue["message"] = "200 OK";
1641     asyncResp->res.jsonValue["status"] = "ok";
1642     asyncResp->res.jsonValue["data"] = nlohmann::json::object();
1643 
1644     crow::connections::systemBus->async_method_call(
1645         [objectPath, asyncResp](
1646             const boost::system::error_code ec,
1647             const dbus::utility::MapperGetSubTreeResponse& objectNames) {
1648         auto transaction =
1649             std::make_shared<InProgressEnumerateData>(objectPath, asyncResp);
1650 
1651         transaction->subtree =
1652             std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1653                 objectNames);
1654 
1655         if (ec)
1656         {
1657             BMCWEB_LOG_ERROR << "GetSubTree failed on "
1658                              << transaction->objectPath;
1659             setErrorResponse(transaction->asyncResp->res,
1660                              boost::beast::http::status::not_found,
1661                              notFoundDesc, notFoundMsg);
1662             return;
1663         }
1664 
1665         // Add the data for the path passed in to the results
1666         // as if GetSubTree returned it, and continue on enumerating
1667         getObjectAndEnumerate(transaction);
1668         },
1669         "xyz.openbmc_project.ObjectMapper",
1670         "/xyz/openbmc_project/object_mapper",
1671         "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
1672         std::array<const char*, 0>());
1673 }
1674 
1675 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1676                       std::string& objectPath, std::string& destProperty)
1677 {
1678     BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1679     std::shared_ptr<std::string> propertyName =
1680         std::make_shared<std::string>(std::move(destProperty));
1681 
1682     std::shared_ptr<std::string> path =
1683         std::make_shared<std::string>(std::move(objectPath));
1684 
1685     crow::connections::systemBus->async_method_call(
1686         [asyncResp, path,
1687          propertyName](const boost::system::error_code ec,
1688                        const dbus::utility::MapperGetObject& objectNames) {
1689         if (ec || objectNames.empty())
1690         {
1691             setErrorResponse(asyncResp->res,
1692                              boost::beast::http::status::not_found,
1693                              notFoundDesc, notFoundMsg);
1694             return;
1695         }
1696         std::shared_ptr<nlohmann::json> response =
1697             std::make_shared<nlohmann::json>(nlohmann::json::object());
1698         // The mapper should never give us an empty interface names
1699         // list, but check anyway
1700         for (const std::pair<std::string, std::vector<std::string>>&
1701                  connection : objectNames)
1702         {
1703             const std::vector<std::string>& interfaceNames = connection.second;
1704 
1705             if (interfaceNames.empty())
1706             {
1707                 setErrorResponse(asyncResp->res,
1708                                  boost::beast::http::status::not_found,
1709                                  notFoundDesc, notFoundMsg);
1710                 return;
1711             }
1712 
1713             for (const std::string& interface : interfaceNames)
1714             {
1715                 sdbusplus::message::message m =
1716                     crow::connections::systemBus->new_method_call(
1717                         connection.first.c_str(), path->c_str(),
1718                         "org.freedesktop.DBus.Properties", "GetAll");
1719                 m.append(interface);
1720                 crow::connections::systemBus->async_send(
1721                     m, [asyncResp, response,
1722                         propertyName](const boost::system::error_code ec2,
1723                                       sdbusplus::message::message& msg) {
1724                         if (ec2)
1725                         {
1726                             BMCWEB_LOG_ERROR << "Bad dbus request error: "
1727                                              << ec2;
1728                         }
1729                         else
1730                         {
1731                             nlohmann::json properties;
1732                             int r = convertDBusToJSON("a{sv}", msg, properties);
1733                             if (r < 0)
1734                             {
1735                                 BMCWEB_LOG_ERROR << "convertDBusToJSON failed";
1736                             }
1737                             else
1738                             {
1739                                 for (auto& prop : properties.items())
1740                                 {
1741                                     // if property name is empty, or
1742                                     // matches our search query, add it
1743                                     // to the response json
1744 
1745                                     if (propertyName->empty())
1746                                     {
1747                                         (*response)[prop.key()] =
1748                                             std::move(prop.value());
1749                                     }
1750                                     else if (prop.key() == *propertyName)
1751                                     {
1752                                         *response = std::move(prop.value());
1753                                     }
1754                                 }
1755                             }
1756                         }
1757                         if (response.use_count() == 1)
1758                         {
1759                             if (!propertyName->empty() && response->empty())
1760                             {
1761                                 setErrorResponse(
1762                                     asyncResp->res,
1763                                     boost::beast::http::status::not_found,
1764                                     propNotFoundDesc, notFoundMsg);
1765                             }
1766                             else
1767                             {
1768                                 asyncResp->res.jsonValue["status"] = "ok";
1769                                 asyncResp->res.jsonValue["message"] = "200 OK";
1770                                 asyncResp->res.jsonValue["data"] = *response;
1771                             }
1772                         }
1773                     });
1774             }
1775         }
1776         },
1777         "xyz.openbmc_project.ObjectMapper",
1778         "/xyz/openbmc_project/object_mapper",
1779         "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1780         std::array<std::string, 0>());
1781 }
1782 
1783 struct AsyncPutRequest
1784 {
1785     AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
1786         asyncResp(resIn)
1787     {}
1788     ~AsyncPutRequest()
1789     {
1790         if (asyncResp->res.jsonValue.empty())
1791         {
1792             setErrorResponse(asyncResp->res,
1793                              boost::beast::http::status::forbidden,
1794                              forbiddenMsg, forbiddenPropDesc);
1795         }
1796     }
1797 
1798     AsyncPutRequest(const AsyncPutRequest&) = delete;
1799     AsyncPutRequest(AsyncPutRequest&&) = delete;
1800     AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1801     AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1802 
1803     void setErrorStatus(const std::string& desc)
1804     {
1805         setErrorResponse(asyncResp->res,
1806                          boost::beast::http::status::internal_server_error,
1807                          desc, badReqMsg);
1808     }
1809 
1810     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1811     std::string objectPath;
1812     std::string propertyName;
1813     nlohmann::json propertyValue;
1814 };
1815 
1816 inline void handlePut(const crow::Request& req,
1817                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1818                       const std::string& objectPath,
1819                       const std::string& destProperty)
1820 {
1821     if (destProperty.empty())
1822     {
1823         setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
1824                          forbiddenResDesc, forbiddenMsg);
1825         return;
1826     }
1827 
1828     nlohmann::json requestDbusData =
1829         nlohmann::json::parse(req.body, nullptr, false);
1830 
1831     if (requestDbusData.is_discarded())
1832     {
1833         setErrorResponse(asyncResp->res,
1834                          boost::beast::http::status::bad_request, noJsonDesc,
1835                          badReqMsg);
1836         return;
1837     }
1838 
1839     nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1840     if (propertyIt == requestDbusData.end())
1841     {
1842         setErrorResponse(asyncResp->res,
1843                          boost::beast::http::status::bad_request, noJsonDesc,
1844                          badReqMsg);
1845         return;
1846     }
1847     const nlohmann::json& propertySetValue = *propertyIt;
1848     auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
1849     transaction->objectPath = objectPath;
1850     transaction->propertyName = destProperty;
1851     transaction->propertyValue = propertySetValue;
1852 
1853     crow::connections::systemBus->async_method_call(
1854         [transaction](const boost::system::error_code ec2,
1855                       const dbus::utility::MapperGetObject& objectNames) {
1856         if (!ec2 && objectNames.empty())
1857         {
1858             setErrorResponse(transaction->asyncResp->res,
1859                              boost::beast::http::status::not_found,
1860                              propNotFoundDesc, notFoundMsg);
1861             return;
1862         }
1863 
1864         for (const std::pair<std::string, std::vector<std::string>>&
1865                  connection : objectNames)
1866         {
1867             const std::string& connectionName = connection.first;
1868 
1869             crow::connections::systemBus->async_method_call(
1870                 [connectionName{std::string(connectionName)},
1871                  transaction](const boost::system::error_code ec3,
1872                               const std::string& introspectXml) {
1873                 if (ec3)
1874                 {
1875                     BMCWEB_LOG_ERROR << "Introspect call failed with error: "
1876                                      << ec3.message()
1877                                      << " on process: " << connectionName;
1878                     transaction->setErrorStatus("Unexpected Error");
1879                     return;
1880                 }
1881                 tinyxml2::XMLDocument doc;
1882 
1883                 doc.Parse(introspectXml.c_str());
1884                 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1885                 if (pRoot == nullptr)
1886                 {
1887                     BMCWEB_LOG_ERROR << "XML document failed to parse: "
1888                                      << introspectXml;
1889                     transaction->setErrorStatus("Unexpected Error");
1890                     return;
1891                 }
1892                 tinyxml2::XMLElement* ifaceNode =
1893                     pRoot->FirstChildElement("interface");
1894                 while (ifaceNode != nullptr)
1895                 {
1896                     const char* interfaceName = ifaceNode->Attribute("name");
1897                     BMCWEB_LOG_DEBUG << "found interface " << interfaceName;
1898                     tinyxml2::XMLElement* propNode =
1899                         ifaceNode->FirstChildElement("property");
1900                     while (propNode != nullptr)
1901                     {
1902                         const char* propertyName = propNode->Attribute("name");
1903                         BMCWEB_LOG_DEBUG << "Found property " << propertyName;
1904                         if (propertyName == transaction->propertyName)
1905                         {
1906                             const char* argType = propNode->Attribute("type");
1907                             if (argType != nullptr)
1908                             {
1909                                 sdbusplus::message::message m =
1910                                     crow::connections::systemBus
1911                                         ->new_method_call(
1912                                             connectionName.c_str(),
1913                                             transaction->objectPath.c_str(),
1914                                             "org.freedesktop.DBus."
1915                                             "Properties",
1916                                             "Set");
1917                                 m.append(interfaceName,
1918                                          transaction->propertyName);
1919                                 int r = sd_bus_message_open_container(
1920                                     m.get(), SD_BUS_TYPE_VARIANT, argType);
1921                                 if (r < 0)
1922                                 {
1923                                     transaction->setErrorStatus(
1924                                         "Unexpected Error");
1925                                     return;
1926                                 }
1927                                 r = convertJsonToDbus(
1928                                     m.get(), argType,
1929                                     transaction->propertyValue);
1930                                 if (r < 0)
1931                                 {
1932                                     if (r == -ERANGE)
1933                                     {
1934                                         transaction->setErrorStatus(
1935                                             "Provided property value "
1936                                             "is out of range for the "
1937                                             "property type");
1938                                     }
1939                                     else
1940                                     {
1941                                         transaction->setErrorStatus(
1942                                             "Invalid arg type");
1943                                     }
1944                                     return;
1945                                 }
1946                                 r = sd_bus_message_close_container(m.get());
1947                                 if (r < 0)
1948                                 {
1949                                     transaction->setErrorStatus(
1950                                         "Unexpected Error");
1951                                     return;
1952                                 }
1953                                 crow::connections::systemBus->async_send(
1954                                     m,
1955                                     [transaction](
1956                                         boost::system::error_code ec,
1957                                         sdbusplus::message::message& m2) {
1958                                     BMCWEB_LOG_DEBUG << "sent";
1959                                     if (ec)
1960                                     {
1961                                         const sd_bus_error* e = m2.get_error();
1962                                         setErrorResponse(
1963                                             transaction->asyncResp->res,
1964                                             boost::beast::http::status::
1965                                                 forbidden,
1966                                             (e) != nullptr
1967                                                 ? e->name
1968                                                 : ec.category().name(),
1969                                             (e) != nullptr ? e->message
1970                                                            : ec.message());
1971                                     }
1972                                     else
1973                                     {
1974                                         transaction->asyncResp->res
1975                                             .jsonValue["status"] = "ok";
1976                                         transaction->asyncResp->res
1977                                             .jsonValue["message"] = "200 OK";
1978                                         transaction->asyncResp->res
1979                                             .jsonValue["data"] = nullptr;
1980                                     }
1981                                     });
1982                             }
1983                         }
1984                         propNode = propNode->NextSiblingElement("property");
1985                     }
1986                     ifaceNode = ifaceNode->NextSiblingElement("interface");
1987                 }
1988                 },
1989                 connectionName, transaction->objectPath,
1990                 "org.freedesktop.DBus.Introspectable", "Introspect");
1991         }
1992         },
1993         "xyz.openbmc_project.ObjectMapper",
1994         "/xyz/openbmc_project/object_mapper",
1995         "xyz.openbmc_project.ObjectMapper", "GetObject",
1996         transaction->objectPath, std::array<std::string, 0>());
1997 }
1998 
1999 inline void handleDBusUrl(const crow::Request& req,
2000                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2001                           std::string& objectPath)
2002 {
2003 
2004     // If accessing a single attribute, fill in and update objectPath,
2005     // otherwise leave destProperty blank
2006     std::string destProperty;
2007     const char* attrSeperator = "/attr/";
2008     size_t attrPosition = objectPath.find(attrSeperator);
2009     if (attrPosition != std::string::npos)
2010     {
2011         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2012                                          objectPath.length());
2013         objectPath = objectPath.substr(0, attrPosition);
2014     }
2015 
2016     if (req.method() == boost::beast::http::verb::post)
2017     {
2018         constexpr const char* actionSeperator = "/action/";
2019         size_t actionPosition = objectPath.find(actionSeperator);
2020         if (actionPosition != std::string::npos)
2021         {
2022             std::string postProperty =
2023                 objectPath.substr((actionPosition + strlen(actionSeperator)),
2024                                   objectPath.length());
2025             objectPath = objectPath.substr(0, actionPosition);
2026             handleAction(req, asyncResp, objectPath, postProperty);
2027             return;
2028         }
2029     }
2030     else if (req.method() == boost::beast::http::verb::get)
2031     {
2032         if (boost::ends_with(objectPath, "/enumerate"))
2033         {
2034             objectPath.erase(objectPath.end() - sizeof("enumerate"),
2035                              objectPath.end());
2036             handleEnumerate(asyncResp, objectPath);
2037         }
2038         else if (boost::ends_with(objectPath, "/list"))
2039         {
2040             objectPath.erase(objectPath.end() - sizeof("list"),
2041                              objectPath.end());
2042             handleList(asyncResp, objectPath);
2043         }
2044         else
2045         {
2046             // Trim any trailing "/" at the end
2047             if (boost::ends_with(objectPath, "/"))
2048             {
2049                 objectPath.pop_back();
2050                 handleList(asyncResp, objectPath, 1);
2051             }
2052             else
2053             {
2054                 handleGet(asyncResp, objectPath, destProperty);
2055             }
2056         }
2057         return;
2058     }
2059     else if (req.method() == boost::beast::http::verb::put)
2060     {
2061         handlePut(req, asyncResp, objectPath, destProperty);
2062         return;
2063     }
2064     else if (req.method() == boost::beast::http::verb::delete_)
2065     {
2066         handleDelete(asyncResp, objectPath);
2067         return;
2068     }
2069 
2070     setErrorResponse(asyncResp->res,
2071                      boost::beast::http::status::method_not_allowed,
2072                      methodNotAllowedDesc, methodNotAllowedMsg);
2073 }
2074 
2075 inline void
2076     handleBusSystemPost(const crow::Request& req,
2077                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2078                         const std::string& processName,
2079                         const std::string& requestedPath)
2080 {
2081     std::vector<std::string> strs;
2082     boost::split(strs, requestedPath, boost::is_any_of("/"));
2083     std::string objectPath;
2084     std::string interfaceName;
2085     std::string methodName;
2086     auto it = strs.begin();
2087     if (it == strs.end())
2088     {
2089         objectPath = "/";
2090     }
2091     while (it != strs.end())
2092     {
2093         // Check if segment contains ".".  If it does, it must be an
2094         // interface
2095         if (it->find(".") != std::string::npos)
2096         {
2097             break;
2098             // This check is necessary as the trailing slash gets
2099             // parsed as part of our <path> specifier above, which
2100             // causes the normal trailing backslash redirector to
2101             // fail.
2102         }
2103         if (!it->empty())
2104         {
2105             objectPath += "/" + *it;
2106         }
2107         it++;
2108     }
2109     if (it != strs.end())
2110     {
2111         interfaceName = *it;
2112         it++;
2113 
2114         // after interface, we might have a method name
2115         if (it != strs.end())
2116         {
2117             methodName = *it;
2118             it++;
2119         }
2120     }
2121     if (it != strs.end())
2122     {
2123         // if there is more levels past the method name, something
2124         // went wrong, return not found
2125         asyncResp->res.result(boost::beast::http::status::not_found);
2126         return;
2127     }
2128     if (interfaceName.empty())
2129     {
2130         crow::connections::systemBus->async_method_call(
2131             [asyncResp, processName,
2132              objectPath](const boost::system::error_code ec,
2133                          const std::string& introspectXml) {
2134             if (ec)
2135             {
2136                 BMCWEB_LOG_ERROR
2137                     << "Introspect call failed with error: " << ec.message()
2138                     << " on process: " << processName << " path: " << objectPath
2139                     << "\n";
2140                 return;
2141             }
2142             tinyxml2::XMLDocument doc;
2143 
2144             doc.Parse(introspectXml.c_str());
2145             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2146             if (pRoot == nullptr)
2147             {
2148                 BMCWEB_LOG_ERROR << "XML document failed to parse "
2149                                  << processName << " " << objectPath << "\n";
2150                 asyncResp->res.jsonValue["status"] = "XML parse error";
2151                 asyncResp->res.result(
2152                     boost::beast::http::status::internal_server_error);
2153                 return;
2154             }
2155 
2156             BMCWEB_LOG_DEBUG << introspectXml;
2157             asyncResp->res.jsonValue["status"] = "ok";
2158             asyncResp->res.jsonValue["bus_name"] = processName;
2159             asyncResp->res.jsonValue["object_path"] = objectPath;
2160 
2161             nlohmann::json& interfacesArray =
2162                 asyncResp->res.jsonValue["interfaces"];
2163             interfacesArray = nlohmann::json::array();
2164             tinyxml2::XMLElement* interface =
2165                 pRoot->FirstChildElement("interface");
2166 
2167             while (interface != nullptr)
2168             {
2169                 const char* ifaceName = interface->Attribute("name");
2170                 if (ifaceName != nullptr)
2171                 {
2172                     nlohmann::json::object_t interface;
2173                     interface["name"] = ifaceName;
2174                     interfacesArray.push_back(std::move(interface));
2175                 }
2176 
2177                 interface = interface->NextSiblingElement("interface");
2178             }
2179             },
2180             processName, objectPath, "org.freedesktop.DBus.Introspectable",
2181             "Introspect");
2182     }
2183     else if (methodName.empty())
2184     {
2185         crow::connections::systemBus->async_method_call(
2186             [asyncResp, processName, objectPath,
2187              interfaceName](const boost::system::error_code ec,
2188                             const std::string& introspectXml) {
2189             if (ec)
2190             {
2191                 BMCWEB_LOG_ERROR
2192                     << "Introspect call failed with error: " << ec.message()
2193                     << " on process: " << processName << " path: " << objectPath
2194                     << "\n";
2195                 return;
2196             }
2197             tinyxml2::XMLDocument doc;
2198 
2199             doc.Parse(introspectXml.data(), introspectXml.size());
2200             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2201             if (pRoot == nullptr)
2202             {
2203                 BMCWEB_LOG_ERROR << "XML document failed to parse "
2204                                  << processName << " " << objectPath << "\n";
2205                 asyncResp->res.result(
2206                     boost::beast::http::status::internal_server_error);
2207                 return;
2208             }
2209 
2210             asyncResp->res.jsonValue["status"] = "ok";
2211             asyncResp->res.jsonValue["bus_name"] = processName;
2212             asyncResp->res.jsonValue["interface"] = interfaceName;
2213             asyncResp->res.jsonValue["object_path"] = objectPath;
2214 
2215             nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"];
2216             methodsArray = nlohmann::json::array();
2217 
2218             nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"];
2219             signalsArray = nlohmann::json::array();
2220 
2221             nlohmann::json& propertiesObj =
2222                 asyncResp->res.jsonValue["properties"];
2223             propertiesObj = nlohmann::json::object();
2224 
2225             // if we know we're the only call, build the
2226             // json directly
2227             tinyxml2::XMLElement* interface =
2228                 pRoot->FirstChildElement("interface");
2229             while (interface != nullptr)
2230             {
2231                 const char* ifaceName = interface->Attribute("name");
2232 
2233                 if (ifaceName != nullptr && ifaceName == interfaceName)
2234                 {
2235                     break;
2236                 }
2237 
2238                 interface = interface->NextSiblingElement("interface");
2239             }
2240             if (interface == nullptr)
2241             {
2242                 // if we got to the end of the list and
2243                 // never found a match, throw 404
2244                 asyncResp->res.result(boost::beast::http::status::not_found);
2245                 return;
2246             }
2247 
2248             tinyxml2::XMLElement* methods =
2249                 interface->FirstChildElement("method");
2250             while (methods != nullptr)
2251             {
2252                 nlohmann::json argsArray = nlohmann::json::array();
2253                 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg");
2254                 while (arg != nullptr)
2255                 {
2256                     nlohmann::json thisArg;
2257                     for (const char* fieldName : std::array<const char*, 3>{
2258                              "name", "direction", "type"})
2259                     {
2260                         const char* fieldValue = arg->Attribute(fieldName);
2261                         if (fieldValue != nullptr)
2262                         {
2263                             thisArg[fieldName] = fieldValue;
2264                         }
2265                     }
2266                     argsArray.push_back(std::move(thisArg));
2267                     arg = arg->NextSiblingElement("arg");
2268                 }
2269 
2270                 const char* name = methods->Attribute("name");
2271                 if (name != nullptr)
2272                 {
2273                     std::string uri;
2274                     uri.reserve(14 + processName.size() + objectPath.size() +
2275                                 interfaceName.size() + strlen(name));
2276                     uri += "/bus/system/";
2277                     uri += processName;
2278                     uri += objectPath;
2279                     uri += "/";
2280                     uri += interfaceName;
2281                     uri += "/";
2282                     uri += name;
2283 
2284                     nlohmann::json::object_t object;
2285                     object["name"] = name;
2286                     object["uri"] = std::move(uri);
2287                     object["args"] = argsArray;
2288 
2289                     methodsArray.push_back(std::move(object));
2290                 }
2291                 methods = methods->NextSiblingElement("method");
2292             }
2293             tinyxml2::XMLElement* signals =
2294                 interface->FirstChildElement("signal");
2295             while (signals != nullptr)
2296             {
2297                 nlohmann::json argsArray = nlohmann::json::array();
2298 
2299                 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg");
2300                 while (arg != nullptr)
2301                 {
2302                     const char* name = arg->Attribute("name");
2303                     const char* type = arg->Attribute("type");
2304                     if (name != nullptr && type != nullptr)
2305                     {
2306                         argsArray.push_back({
2307                             {"name", name},
2308                             {"type", type},
2309                         });
2310                     }
2311                     arg = arg->NextSiblingElement("arg");
2312                 }
2313                 const char* name = signals->Attribute("name");
2314                 if (name != nullptr)
2315                 {
2316                     nlohmann::json::object_t object;
2317                     object["name"] = name;
2318                     object["args"] = argsArray;
2319                     signalsArray.push_back(std::move(object));
2320                 }
2321 
2322                 signals = signals->NextSiblingElement("signal");
2323             }
2324 
2325             tinyxml2::XMLElement* property =
2326                 interface->FirstChildElement("property");
2327             while (property != nullptr)
2328             {
2329                 const char* name = property->Attribute("name");
2330                 const char* type = property->Attribute("type");
2331                 if (type != nullptr && name != nullptr)
2332                 {
2333                     sdbusplus::message::message m =
2334                         crow::connections::systemBus->new_method_call(
2335                             processName.c_str(), objectPath.c_str(),
2336                             "org.freedesktop."
2337                             "DBus."
2338                             "Properties",
2339                             "Get");
2340                     m.append(interfaceName, name);
2341                     nlohmann::json& propertyItem = propertiesObj[name];
2342                     crow::connections::systemBus->async_send(
2343                         m, [&propertyItem,
2344                             asyncResp](boost::system::error_code& e,
2345                                        sdbusplus::message::message& msg) {
2346                             if (e)
2347                             {
2348                                 return;
2349                             }
2350 
2351                             convertDBusToJSON("v", msg, propertyItem);
2352                         });
2353                 }
2354                 property = property->NextSiblingElement("property");
2355             }
2356             },
2357             processName, objectPath, "org.freedesktop.DBus.Introspectable",
2358             "Introspect");
2359     }
2360     else
2361     {
2362         if (req.method() != boost::beast::http::verb::post)
2363         {
2364             asyncResp->res.result(boost::beast::http::status::not_found);
2365             return;
2366         }
2367 
2368         nlohmann::json requestDbusData =
2369             nlohmann::json::parse(req.body, nullptr, false);
2370 
2371         if (requestDbusData.is_discarded())
2372         {
2373             asyncResp->res.result(boost::beast::http::status::bad_request);
2374             return;
2375         }
2376         if (!requestDbusData.is_array())
2377         {
2378             asyncResp->res.result(boost::beast::http::status::bad_request);
2379             return;
2380         }
2381         auto transaction =
2382             std::make_shared<InProgressActionData>(asyncResp->res);
2383 
2384         transaction->path = objectPath;
2385         transaction->methodName = methodName;
2386         transaction->arguments = std::move(requestDbusData);
2387 
2388         findActionOnInterface(transaction, processName);
2389     }
2390 }
2391 
2392 inline void requestRoutes(App& app)
2393 {
2394     BMCWEB_ROUTE(app, "/bus/")
2395         .privileges({{"Login"}})
2396         .methods(boost::beast::http::verb::get)(
2397             [](const crow::Request&,
2398                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2399         nlohmann::json::array_t buses;
2400         nlohmann::json& bus = buses.emplace_back();
2401         bus["name"] = "system";
2402         asyncResp->res.jsonValue["busses"] = std::move(buses);
2403         asyncResp->res.jsonValue["status"] = "ok";
2404         });
2405 
2406     BMCWEB_ROUTE(app, "/bus/system/")
2407         .privileges({{"Login"}})
2408         .methods(boost::beast::http::verb::get)(
2409             [](const crow::Request&,
2410                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2411         auto myCallback = [asyncResp](const boost::system::error_code ec,
2412                                       std::vector<std::string>& names) {
2413             if (ec)
2414             {
2415                 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2416                 asyncResp->res.result(
2417                     boost::beast::http::status::internal_server_error);
2418             }
2419             else
2420             {
2421                 std::sort(names.begin(), names.end());
2422                 asyncResp->res.jsonValue["status"] = "ok";
2423                 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2424                 for (auto& name : names)
2425                 {
2426                     nlohmann::json::object_t object;
2427                     object["name"] = name;
2428                     objectsSub.push_back(std::move(object));
2429                 }
2430             }
2431         };
2432         crow::connections::systemBus->async_method_call(
2433             std::move(myCallback), "org.freedesktop.DBus", "/",
2434             "org.freedesktop.DBus", "ListNames");
2435         });
2436 
2437     BMCWEB_ROUTE(app, "/list/")
2438         .privileges({{"Login"}})
2439         .methods(boost::beast::http::verb::get)(
2440             [](const crow::Request&,
2441                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2442         handleList(asyncResp, "/");
2443         });
2444 
2445     BMCWEB_ROUTE(app, "/xyz/<path>")
2446         .privileges({{"Login"}})
2447         .methods(boost::beast::http::verb::get)(
2448             [](const crow::Request& req,
2449                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2450                const std::string& path) {
2451         std::string objectPath = "/xyz/" + path;
2452         handleDBusUrl(req, asyncResp, objectPath);
2453         });
2454 
2455     BMCWEB_ROUTE(app, "/xyz/<path>")
2456         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2457         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2458                  boost::beast::http::verb::delete_)(
2459             [](const crow::Request& req,
2460                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2461                const std::string& path) {
2462         std::string objectPath = "/xyz/" + path;
2463         handleDBusUrl(req, asyncResp, objectPath);
2464         });
2465 
2466     BMCWEB_ROUTE(app, "/org/<path>")
2467         .privileges({{"Login"}})
2468         .methods(boost::beast::http::verb::get)(
2469             [](const crow::Request& req,
2470                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2471                const std::string& path) {
2472         std::string objectPath = "/org/" + path;
2473         handleDBusUrl(req, asyncResp, objectPath);
2474         });
2475 
2476     BMCWEB_ROUTE(app, "/org/<path>")
2477         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2478         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2479                  boost::beast::http::verb::delete_)(
2480             [](const crow::Request& req,
2481                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2482                const std::string& path) {
2483         std::string objectPath = "/org/" + path;
2484         handleDBusUrl(req, asyncResp, objectPath);
2485         });
2486 
2487     BMCWEB_ROUTE(app, "/download/dump/<str>/")
2488         .privileges({{"ConfigureManager"}})
2489         .methods(boost::beast::http::verb::get)(
2490             [](const crow::Request&,
2491                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2492                const std::string& dumpId) {
2493         if (!validateFilename(dumpId))
2494         {
2495             asyncResp->res.result(boost::beast::http::status::bad_request);
2496             return;
2497         }
2498         std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps");
2499 
2500         loc /= dumpId;
2501 
2502         if (!std::filesystem::exists(loc) ||
2503             !std::filesystem::is_directory(loc))
2504         {
2505             BMCWEB_LOG_ERROR << loc.string() << "Not found";
2506             asyncResp->res.result(boost::beast::http::status::not_found);
2507             return;
2508         }
2509         std::filesystem::directory_iterator files(loc);
2510 
2511         for (const auto& file : files)
2512         {
2513             std::ifstream readFile(file.path());
2514             if (!readFile.good())
2515             {
2516                 continue;
2517             }
2518 
2519             asyncResp->res.addHeader("Content-Type",
2520                                      "application/octet-stream");
2521 
2522             // Assuming only one dump file will be present in the dump
2523             // id directory
2524             std::string dumpFileName = file.path().filename().string();
2525 
2526             // Filename should be in alphanumeric, dot and underscore
2527             // Its based on phosphor-debug-collector application
2528             // dumpfile format
2529             std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2530             if (!std::regex_match(dumpFileName, dumpFileRegex))
2531             {
2532                 BMCWEB_LOG_ERROR << "Invalid dump filename " << dumpFileName;
2533                 asyncResp->res.result(boost::beast::http::status::not_found);
2534                 return;
2535             }
2536             std::string contentDispositionParam =
2537                 "attachment; filename=\"" + dumpFileName + "\"";
2538 
2539             asyncResp->res.addHeader("Content-Disposition",
2540                                      contentDispositionParam);
2541 
2542             asyncResp->res.body() = {std::istreambuf_iterator<char>(readFile),
2543                                      std::istreambuf_iterator<char>()};
2544             return;
2545         }
2546         asyncResp->res.result(boost::beast::http::status::not_found);
2547         return;
2548         });
2549 
2550     BMCWEB_ROUTE(app, "/bus/system/<str>/")
2551         .privileges({{"Login"}})
2552 
2553         .methods(boost::beast::http::verb::get)(
2554             [](const crow::Request&,
2555                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2556                const std::string& connection) {
2557         introspectObjects(connection, "/", asyncResp);
2558         });
2559 
2560     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2561         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2562         .methods(boost::beast::http::verb::get,
2563                  boost::beast::http::verb::post)(handleBusSystemPost);
2564 }
2565 } // namespace openbmc_mapper
2566 } // namespace crow
2567