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