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