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