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