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