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