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