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