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