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 "dbus_utility.hpp"
17 #include "http_request.hpp"
18 #include "http_response.hpp"
19 #include "logging.hpp"
20 #include "routing.hpp"
21 
22 #include <systemd/sd-bus-protocol.h>
23 #include <systemd/sd-bus.h>
24 #include <tinyxml2.h>
25 
26 #include <app.hpp>
27 #include <async_resp.hpp>
28 #include <boost/algorithm/string/classification.hpp>
29 #include <boost/algorithm/string/predicate.hpp>
30 #include <boost/algorithm/string/split.hpp>
31 #include <boost/beast/http/status.hpp>
32 #include <boost/beast/http/verb.hpp>
33 #include <boost/container/flat_map.hpp>
34 #include <boost/container/vector.hpp>
35 #include <boost/iterator/iterator_facade.hpp>
36 #include <dbus_singleton.hpp>
37 #include <dbus_utility.hpp>
38 #include <nlohmann/json.hpp>
39 #include <sdbusplus/asio/connection.hpp>
40 #include <sdbusplus/asio/property.hpp>
41 #include <sdbusplus/exception.hpp>
42 #include <sdbusplus/message.hpp>
43 #include <sdbusplus/message/native_types.hpp>
44 
45 #include <algorithm>
46 #include <array>
47 #include <cerrno>
48 #include <cstdint>
49 #include <cstring>
50 #include <filesystem>
51 #include <fstream>
52 #include <functional>
53 #include <initializer_list>
54 #include <iterator>
55 #include <limits>
56 #include <map>
57 #include <memory>
58 #include <regex>
59 #include <string>
60 #include <string_view>
61 #include <type_traits>
62 #include <utility>
63 #include <variant>
64 #include <vector>
65 
66 // IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp>
67 // IWYU pragma: no_include <boost/system/detail/error_code.hpp>
68 // IWYU pragma: no_include <boost/system/detail/error_category.hpp>
69 // IWYU pragma: no_include <errno.h>
70 // IWYU pragma: no_include <string.h>
71 // IWYU pragma: no_include <ext/alloc_traits.h>
72 // IWYU pragma: no_include <exception>
73 // IWYU pragma: no_include <boost/type_index/type_index_facade.hpp>
74 
75 namespace crow
76 {
77 namespace openbmc_mapper
78 {
79 const constexpr char* notFoundMsg = "404 Not Found";
80 const constexpr char* badReqMsg = "400 Bad Request";
81 const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
82 const constexpr char* forbiddenMsg = "403 Forbidden";
83 const constexpr char* methodFailedMsg = "500 Method Call Failed";
84 const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
85 const constexpr char* notFoundDesc =
86     "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
87 const constexpr char* propNotFoundDesc =
88     "The specified property cannot be found";
89 const constexpr char* noJsonDesc = "No JSON object could be decoded";
90 const constexpr char* methodNotFoundDesc =
91     "The specified method cannot be found";
92 const constexpr char* methodNotAllowedDesc = "Method not allowed";
93 const constexpr char* forbiddenPropDesc =
94     "The specified property cannot be created";
95 const constexpr char* forbiddenResDesc =
96     "The specified resource cannot be created";
97 
98 inline bool validateFilename(const std::string& filename)
99 {
100     std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
101 
102     return std::regex_match(filename, validFilename);
103 }
104 
105 inline void setErrorResponse(crow::Response& res,
106                              boost::beast::http::status result,
107                              const std::string& desc,
108                              const std::string_view msg)
109 {
110     res.result(result);
111     res.jsonValue["data"]["description"] = desc;
112     res.jsonValue["message"] = msg;
113     res.jsonValue["status"] = "error";
114 }
115 
116 inline void
117     introspectObjects(const std::string& processName,
118                       const std::string& objectPath,
119                       const std::shared_ptr<bmcweb::AsyncResp>& transaction)
120 {
121     if (transaction->res.jsonValue.is_null())
122     {
123         transaction->res.jsonValue["status"] = "ok";
124         transaction->res.jsonValue["bus_name"] = processName;
125         transaction->res.jsonValue["objects"] = nlohmann::json::array();
126     }
127 
128     crow::connections::systemBus->async_method_call(
129         [transaction, processName{std::string(processName)},
130          objectPath{std::string(objectPath)}](
131             const boost::system::error_code ec,
132             const std::string& introspectXml) {
133         if (ec)
134         {
135             BMCWEB_LOG_ERROR
136                 << "Introspect call failed with error: " << ec.message()
137                 << " on process: " << processName << " path: " << objectPath
138                 << "\n";
139             return;
140         }
141         nlohmann::json::object_t object;
142         object["path"] = objectPath;
143 
144         transaction->res.jsonValue["objects"].push_back(std::move(object));
145 
146         tinyxml2::XMLDocument doc;
147 
148         doc.Parse(introspectXml.c_str());
149         tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
150         if (pRoot == nullptr)
151         {
152             BMCWEB_LOG_ERROR << "XML document failed to parse " << processName
153                              << " " << objectPath << "\n";
154         }
155         else
156         {
157             tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
158             while (node != nullptr)
159             {
160                 const char* childPath = node->Attribute("name");
161                 if (childPath != nullptr)
162                 {
163                     std::string newpath;
164                     if (objectPath != "/")
165                     {
166                         newpath += objectPath;
167                     }
168                     newpath += std::string("/") + childPath;
169                     // introspect the subobjects as well
170                     introspectObjects(processName, newpath, transaction);
171                 }
172 
173                 node = node->NextSiblingElement("node");
174             }
175         }
176         },
177         processName, objectPath, "org.freedesktop.DBus.Introspectable",
178         "Introspect");
179 }
180 
181 inline void getPropertiesForEnumerate(
182     const std::string& objectPath, const std::string& service,
183     const std::string& interface,
184     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
185 {
186     BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
187                      << service << " " << interface;
188 
189     sdbusplus::asio::getAllProperties(
190         *crow::connections::systemBus, service, objectPath, interface,
191         [asyncResp, objectPath, service,
192          interface](const boost::system::error_code ec,
193                     const dbus::utility::DBusPropertiesMap& propertiesList) {
194         if (ec)
195         {
196             BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
197                              << interface << " service " << service
198                              << " failed with code " << ec;
199             return;
200         }
201 
202         nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
203         nlohmann::json& objectJson = dataJson[objectPath];
204         if (objectJson.is_null())
205         {
206             objectJson = nlohmann::json::object();
207         }
208 
209         for (const auto& [name, value] : propertiesList)
210         {
211             nlohmann::json& propertyJson = objectJson[name];
212             std::visit(
213                 [&propertyJson](auto&& val) {
214                 if constexpr (std::is_same_v<std::decay_t<decltype(val)>,
215                                              sdbusplus::message::unix_fd>)
216                 {
217                     propertyJson = val.fd;
218                 }
219                 else
220                 {
221 
222                     propertyJson = val;
223                 }
224                 },
225                 value);
226         }
227         });
228 }
229 
230 // Find any results that weren't picked up by ObjectManagers, to be
231 // called after all ObjectManagers are searched for and called.
232 inline void findRemainingObjectsForEnumerate(
233     const std::string& objectPath,
234     const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
235     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
236 {
237     BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
238     const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
239 
240     for (const auto& [path, interface_map] : *subtree)
241     {
242         if (path == objectPath)
243         {
244             // An enumerate does not return the target path's properties
245             continue;
246         }
247         if (dataJson.find(path) == dataJson.end())
248         {
249             for (const auto& [service, interfaces] : interface_map)
250             {
251                 for (const auto& interface : interfaces)
252                 {
253                     if (!interface.starts_with("org.freedesktop.DBus"))
254                     {
255                         getPropertiesForEnumerate(path, service, interface,
256                                                   asyncResp);
257                     }
258                 }
259             }
260         }
261     }
262 }
263 
264 struct InProgressEnumerateData
265 {
266     InProgressEnumerateData(
267         const std::string& objectPathIn,
268         const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
269         objectPath(objectPathIn),
270         asyncResp(asyncRespIn)
271     {}
272 
273     ~InProgressEnumerateData()
274     {
275         try
276         {
277             findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
278         }
279         catch (...)
280         {
281             BMCWEB_LOG_CRITICAL
282                 << "findRemainingObjectsForEnumerate threw exception";
283         }
284     }
285 
286     InProgressEnumerateData(const InProgressEnumerateData&) = delete;
287     InProgressEnumerateData(InProgressEnumerateData&&) = delete;
288     InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
289     InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
290     const std::string objectPath;
291     std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
292     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
293 };
294 
295 inline void getManagedObjectsForEnumerate(
296     const std::string& objectName, const std::string& objectManagerPath,
297     const std::string& connectionName,
298     const std::shared_ptr<InProgressEnumerateData>& transaction)
299 {
300     BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName
301                      << " object_manager_path " << objectManagerPath
302                      << " connection_name " << connectionName;
303     crow::connections::systemBus->async_method_call(
304         [transaction, objectName,
305          connectionName](const boost::system::error_code ec,
306                          const dbus::utility::ManagedObjectType& objects) {
307         if (ec)
308         {
309             BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName
310                              << " on connection " << connectionName
311                              << " failed with code " << ec;
312             return;
313         }
314 
315         nlohmann::json& dataJson =
316             transaction->asyncResp->res.jsonValue["data"];
317 
318         for (const auto& objectPath : objects)
319         {
320             if (objectPath.first.str.starts_with(objectName))
321             {
322                 BMCWEB_LOG_DEBUG << "Reading object " << objectPath.first.str;
323                 nlohmann::json& objectJson = dataJson[objectPath.first.str];
324                 if (objectJson.is_null())
325                 {
326                     objectJson = nlohmann::json::object();
327                 }
328                 for (const auto& interface : objectPath.second)
329                 {
330                     for (const auto& property : interface.second)
331                     {
332                         nlohmann::json& propertyJson =
333                             objectJson[property.first];
334                         std::visit(
335                             [&propertyJson](auto&& val) {
336                             if constexpr (std::is_same_v<
337                                               std::decay_t<decltype(val)>,
338                                               sdbusplus::message::unix_fd>)
339                             {
340                                 propertyJson = val.fd;
341                             }
342                             else
343                             {
344 
345                                 propertyJson = val;
346                             }
347                             },
348                             property.second);
349                     }
350                 }
351             }
352             for (const auto& interface : objectPath.second)
353             {
354                 if (interface.first == "org.freedesktop.DBus.ObjectManager")
355                 {
356                     getManagedObjectsForEnumerate(objectPath.first.str,
357                                                   objectPath.first.str,
358                                                   connectionName, transaction);
359                 }
360             }
361         }
362         },
363         connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager",
364         "GetManagedObjects");
365 }
366 
367 inline void findObjectManagerPathForEnumerate(
368     const std::string& objectName, const std::string& connectionName,
369     const std::shared_ptr<InProgressEnumerateData>& transaction)
370 {
371     BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName
372                      << " on connection:" << connectionName;
373     crow::connections::systemBus->async_method_call(
374         [transaction, objectName, connectionName](
375             const boost::system::error_code ec,
376             const dbus::utility::MapperGetAncestorsResponse& objects) {
377         if (ec)
378         {
379             BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName
380                              << " failed with code " << ec;
381             return;
382         }
383 
384         for (const auto& pathGroup : objects)
385         {
386             for (const auto& connectionGroup : pathGroup.second)
387             {
388                 if (connectionGroup.first == connectionName)
389                 {
390                     // Found the object manager path for this resource.
391                     getManagedObjectsForEnumerate(objectName, pathGroup.first,
392                                                   connectionName, transaction);
393                     return;
394                 }
395             }
396         }
397         },
398         "xyz.openbmc_project.ObjectMapper",
399         "/xyz/openbmc_project/object_mapper",
400         "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
401         std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
402 }
403 
404 // Uses GetObject to add the object info about the target /enumerate path to
405 // the results of GetSubTree, as GetSubTree will not return info for the
406 // target path, and then continues on enumerating the rest of the tree.
407 inline void getObjectAndEnumerate(
408     const std::shared_ptr<InProgressEnumerateData>& transaction)
409 {
410     crow::connections::systemBus->async_method_call(
411         [transaction](const boost::system::error_code ec,
412                       const dbus::utility::MapperGetObject& objects) {
413         if (ec)
414         {
415             BMCWEB_LOG_ERROR << "GetObject for path " << transaction->objectPath
416                              << " failed with code " << ec;
417             return;
418         }
419 
420         BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
421                          << " has " << objects.size() << " entries";
422         if (!objects.empty())
423         {
424             transaction->subtree->emplace_back(transaction->objectPath,
425                                                objects);
426         }
427 
428         // Map indicating connection name, and the path where the object
429         // manager exists
430         boost::container::flat_map<std::string, std::string> connections;
431 
432         for (const auto& object : *(transaction->subtree))
433         {
434             for (const auto& connection : object.second)
435             {
436                 for (const auto& interface : connection.second)
437                 {
438                     BMCWEB_LOG_DEBUG << connection.first << " has interface "
439                                      << interface;
440                     if (interface == "org.freedesktop.DBus.ObjectManager")
441                     {
442                         BMCWEB_LOG_DEBUG << "found object manager path "
443                                          << object.first;
444                         connections[connection.first] = object.first;
445                     }
446                 }
447             }
448         }
449         BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
450 
451         for (const auto& connection : connections)
452         {
453             // If we already know where the object manager is, we don't
454             // need to search for it, we can call directly in to
455             // getManagedObjects
456             if (!connection.second.empty())
457             {
458                 getManagedObjectsForEnumerate(transaction->objectPath,
459                                               connection.second,
460                                               connection.first, transaction);
461             }
462             else
463             {
464                 // otherwise we need to find the object manager path
465                 // before we can continue
466                 findObjectManagerPathForEnumerate(
467                     transaction->objectPath, connection.first, transaction);
468             }
469         }
470         },
471         "xyz.openbmc_project.ObjectMapper",
472         "/xyz/openbmc_project/object_mapper",
473         "xyz.openbmc_project.ObjectMapper", "GetObject",
474         transaction->objectPath, std::array<const char*, 0>());
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         nlohmann::json::parse(req.body, nullptr, false);
1546 
1547     if (requestDbusData.is_discarded())
1548     {
1549         setErrorResponse(asyncResp->res,
1550                          boost::beast::http::status::bad_request, noJsonDesc,
1551                          badReqMsg);
1552         return;
1553     }
1554     nlohmann::json::iterator data = requestDbusData.find("data");
1555     if (data == requestDbusData.end())
1556     {
1557         setErrorResponse(asyncResp->res,
1558                          boost::beast::http::status::bad_request, noJsonDesc,
1559                          badReqMsg);
1560         return;
1561     }
1562 
1563     if (!data->is_array())
1564     {
1565         setErrorResponse(asyncResp->res,
1566                          boost::beast::http::status::bad_request, noJsonDesc,
1567                          badReqMsg);
1568         return;
1569     }
1570     auto transaction = std::make_shared<InProgressActionData>(asyncResp->res);
1571 
1572     transaction->path = objectPath;
1573     transaction->methodName = methodName;
1574     transaction->arguments = std::move(*data);
1575     crow::connections::systemBus->async_method_call(
1576         [transaction](
1577             const boost::system::error_code ec,
1578             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1579                 interfaceNames) {
1580         if (ec || interfaceNames.empty())
1581         {
1582             BMCWEB_LOG_ERROR << "Can't find object";
1583             setErrorResponse(transaction->res,
1584                              boost::beast::http::status::not_found,
1585                              notFoundDesc, notFoundMsg);
1586             return;
1587         }
1588 
1589         BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1590                          << " object(s)";
1591 
1592         for (const std::pair<std::string, std::vector<std::string>>& object :
1593              interfaceNames)
1594         {
1595             findActionOnInterface(transaction, object.first);
1596         }
1597         },
1598         "xyz.openbmc_project.ObjectMapper",
1599         "/xyz/openbmc_project/object_mapper",
1600         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1601         std::array<std::string, 0>());
1602 }
1603 
1604 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1605                          const std::string& objectPath)
1606 {
1607     BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1608 
1609     crow::connections::systemBus->async_method_call(
1610         [asyncResp, objectPath](
1611             const boost::system::error_code ec,
1612             const std::vector<std::pair<std::string, std::vector<std::string>>>&
1613                 interfaceNames) {
1614         if (ec || interfaceNames.empty())
1615         {
1616             BMCWEB_LOG_ERROR << "Can't find object";
1617             setErrorResponse(asyncResp->res,
1618                              boost::beast::http::status::method_not_allowed,
1619                              methodNotAllowedDesc, methodNotAllowedMsg);
1620             return;
1621         }
1622 
1623         auto transaction =
1624             std::make_shared<InProgressActionData>(asyncResp->res);
1625         transaction->path = objectPath;
1626         transaction->methodName = "Delete";
1627         transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1628 
1629         for (const std::pair<std::string, std::vector<std::string>>& object :
1630              interfaceNames)
1631         {
1632             findActionOnInterface(transaction, object.first);
1633         }
1634         },
1635         "xyz.openbmc_project.ObjectMapper",
1636         "/xyz/openbmc_project/object_mapper",
1637         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1638         std::array<const char*, 0>());
1639 }
1640 
1641 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1642                        const std::string& objectPath, int32_t depth = 0)
1643 {
1644     dbus::utility::getSubTreePaths(
1645         objectPath, depth, {},
1646         [asyncResp](
1647             const boost::system::error_code& ec,
1648             const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
1649         if (ec)
1650         {
1651             setErrorResponse(asyncResp->res,
1652                              boost::beast::http::status::not_found,
1653                              notFoundDesc, notFoundMsg);
1654         }
1655         else
1656         {
1657             asyncResp->res.jsonValue["status"] = "ok";
1658             asyncResp->res.jsonValue["message"] = "200 OK";
1659             asyncResp->res.jsonValue["data"] = objectPaths;
1660         }
1661         });
1662 }
1663 
1664 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1665                             const std::string& objectPath)
1666 {
1667     BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1668 
1669     asyncResp->res.jsonValue["message"] = "200 OK";
1670     asyncResp->res.jsonValue["status"] = "ok";
1671     asyncResp->res.jsonValue["data"] = nlohmann::json::object();
1672 
1673     crow::connections::systemBus->async_method_call(
1674         [objectPath, asyncResp](
1675             const boost::system::error_code ec,
1676             const dbus::utility::MapperGetSubTreeResponse& objectNames) {
1677         auto transaction =
1678             std::make_shared<InProgressEnumerateData>(objectPath, asyncResp);
1679 
1680         transaction->subtree =
1681             std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1682                 objectNames);
1683 
1684         if (ec)
1685         {
1686             BMCWEB_LOG_ERROR << "GetSubTree failed on "
1687                              << transaction->objectPath;
1688             setErrorResponse(transaction->asyncResp->res,
1689                              boost::beast::http::status::not_found,
1690                              notFoundDesc, notFoundMsg);
1691             return;
1692         }
1693 
1694         // Add the data for the path passed in to the results
1695         // as if GetSubTree returned it, and continue on enumerating
1696         getObjectAndEnumerate(transaction);
1697         },
1698         "xyz.openbmc_project.ObjectMapper",
1699         "/xyz/openbmc_project/object_mapper",
1700         "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
1701         std::array<const char*, 0>());
1702 }
1703 
1704 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1705                       std::string& objectPath, std::string& destProperty)
1706 {
1707     BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1708     std::shared_ptr<std::string> propertyName =
1709         std::make_shared<std::string>(std::move(destProperty));
1710 
1711     std::shared_ptr<std::string> path =
1712         std::make_shared<std::string>(std::move(objectPath));
1713 
1714     crow::connections::systemBus->async_method_call(
1715         [asyncResp, path,
1716          propertyName](const boost::system::error_code ec,
1717                        const dbus::utility::MapperGetObject& objectNames) {
1718         if (ec || objectNames.empty())
1719         {
1720             setErrorResponse(asyncResp->res,
1721                              boost::beast::http::status::not_found,
1722                              notFoundDesc, notFoundMsg);
1723             return;
1724         }
1725         std::shared_ptr<nlohmann::json> response =
1726             std::make_shared<nlohmann::json>(nlohmann::json::object());
1727         // The mapper should never give us an empty interface names
1728         // list, but check anyway
1729         for (const std::pair<std::string, std::vector<std::string>>&
1730                  connection : objectNames)
1731         {
1732             const std::vector<std::string>& interfaceNames = connection.second;
1733 
1734             if (interfaceNames.empty())
1735             {
1736                 setErrorResponse(asyncResp->res,
1737                                  boost::beast::http::status::not_found,
1738                                  notFoundDesc, notFoundMsg);
1739                 return;
1740             }
1741 
1742             for (const std::string& interface : interfaceNames)
1743             {
1744                 sdbusplus::message_t m =
1745                     crow::connections::systemBus->new_method_call(
1746                         connection.first.c_str(), path->c_str(),
1747                         "org.freedesktop.DBus.Properties", "GetAll");
1748                 m.append(interface);
1749                 crow::connections::systemBus->async_send(
1750                     m, [asyncResp, response,
1751                         propertyName](const boost::system::error_code ec2,
1752                                       sdbusplus::message_t& msg) {
1753                         if (ec2)
1754                         {
1755                             BMCWEB_LOG_ERROR << "Bad dbus request error: "
1756                                              << ec2;
1757                         }
1758                         else
1759                         {
1760                             nlohmann::json properties;
1761                             int r = convertDBusToJSON("a{sv}", msg, properties);
1762                             if (r < 0)
1763                             {
1764                                 BMCWEB_LOG_ERROR << "convertDBusToJSON failed";
1765                             }
1766                             else
1767                             {
1768                                 for (const auto& prop : properties.items())
1769                                 {
1770                                     // if property name is empty, or
1771                                     // matches our search query, add it
1772                                     // to the response json
1773 
1774                                     if (propertyName->empty())
1775                                     {
1776                                         (*response)[prop.key()] =
1777                                             std::move(prop.value());
1778                                     }
1779                                     else if (prop.key() == *propertyName)
1780                                     {
1781                                         *response = std::move(prop.value());
1782                                     }
1783                                 }
1784                             }
1785                         }
1786                         if (response.use_count() == 1)
1787                         {
1788                             if (!propertyName->empty() && response->empty())
1789                             {
1790                                 setErrorResponse(
1791                                     asyncResp->res,
1792                                     boost::beast::http::status::not_found,
1793                                     propNotFoundDesc, notFoundMsg);
1794                             }
1795                             else
1796                             {
1797                                 asyncResp->res.jsonValue["status"] = "ok";
1798                                 asyncResp->res.jsonValue["message"] = "200 OK";
1799                                 asyncResp->res.jsonValue["data"] = *response;
1800                             }
1801                         }
1802                     });
1803             }
1804         }
1805         },
1806         "xyz.openbmc_project.ObjectMapper",
1807         "/xyz/openbmc_project/object_mapper",
1808         "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1809         std::array<std::string, 0>());
1810 }
1811 
1812 struct AsyncPutRequest
1813 {
1814     explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
1815         asyncResp(resIn)
1816     {}
1817     ~AsyncPutRequest()
1818     {
1819         if (asyncResp->res.jsonValue.empty())
1820         {
1821             setErrorResponse(asyncResp->res,
1822                              boost::beast::http::status::forbidden,
1823                              forbiddenMsg, forbiddenPropDesc);
1824         }
1825     }
1826 
1827     AsyncPutRequest(const AsyncPutRequest&) = delete;
1828     AsyncPutRequest(AsyncPutRequest&&) = delete;
1829     AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1830     AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1831 
1832     void setErrorStatus(const std::string& desc)
1833     {
1834         setErrorResponse(asyncResp->res,
1835                          boost::beast::http::status::internal_server_error,
1836                          desc, badReqMsg);
1837     }
1838 
1839     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1840     std::string objectPath;
1841     std::string propertyName;
1842     nlohmann::json propertyValue;
1843 };
1844 
1845 inline void handlePut(const crow::Request& req,
1846                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1847                       const std::string& objectPath,
1848                       const std::string& destProperty)
1849 {
1850     if (destProperty.empty())
1851     {
1852         setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
1853                          forbiddenResDesc, forbiddenMsg);
1854         return;
1855     }
1856 
1857     nlohmann::json requestDbusData =
1858         nlohmann::json::parse(req.body, nullptr, false);
1859 
1860     if (requestDbusData.is_discarded())
1861     {
1862         setErrorResponse(asyncResp->res,
1863                          boost::beast::http::status::bad_request, noJsonDesc,
1864                          badReqMsg);
1865         return;
1866     }
1867 
1868     auto propertyIt = requestDbusData.find("data");
1869     if (propertyIt == requestDbusData.end())
1870     {
1871         setErrorResponse(asyncResp->res,
1872                          boost::beast::http::status::bad_request, noJsonDesc,
1873                          badReqMsg);
1874         return;
1875     }
1876     const nlohmann::json& propertySetValue = *propertyIt;
1877     auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
1878     transaction->objectPath = objectPath;
1879     transaction->propertyName = destProperty;
1880     transaction->propertyValue = propertySetValue;
1881 
1882     crow::connections::systemBus->async_method_call(
1883         [transaction](const boost::system::error_code ec2,
1884                       const dbus::utility::MapperGetObject& objectNames) {
1885         if (!ec2 && objectNames.empty())
1886         {
1887             setErrorResponse(transaction->asyncResp->res,
1888                              boost::beast::http::status::not_found,
1889                              propNotFoundDesc, notFoundMsg);
1890             return;
1891         }
1892 
1893         for (const std::pair<std::string, std::vector<std::string>>&
1894                  connection : objectNames)
1895         {
1896             const std::string& connectionName = connection.first;
1897 
1898             crow::connections::systemBus->async_method_call(
1899                 [connectionName{std::string(connectionName)},
1900                  transaction](const boost::system::error_code ec3,
1901                               const std::string& introspectXml) {
1902                 if (ec3)
1903                 {
1904                     BMCWEB_LOG_ERROR << "Introspect call failed with error: "
1905                                      << ec3.message()
1906                                      << " on process: " << connectionName;
1907                     transaction->setErrorStatus("Unexpected Error");
1908                     return;
1909                 }
1910                 tinyxml2::XMLDocument doc;
1911 
1912                 doc.Parse(introspectXml.c_str());
1913                 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1914                 if (pRoot == nullptr)
1915                 {
1916                     BMCWEB_LOG_ERROR << "XML document failed to parse: "
1917                                      << introspectXml;
1918                     transaction->setErrorStatus("Unexpected Error");
1919                     return;
1920                 }
1921                 tinyxml2::XMLElement* ifaceNode =
1922                     pRoot->FirstChildElement("interface");
1923                 while (ifaceNode != nullptr)
1924                 {
1925                     const char* interfaceName = ifaceNode->Attribute("name");
1926                     BMCWEB_LOG_DEBUG << "found interface " << interfaceName;
1927                     tinyxml2::XMLElement* propNode =
1928                         ifaceNode->FirstChildElement("property");
1929                     while (propNode != nullptr)
1930                     {
1931                         const char* propertyName = propNode->Attribute("name");
1932                         BMCWEB_LOG_DEBUG << "Found property " << propertyName;
1933                         if (propertyName == transaction->propertyName)
1934                         {
1935                             const char* argType = propNode->Attribute("type");
1936                             if (argType != nullptr)
1937                             {
1938                                 sdbusplus::message_t m =
1939                                     crow::connections::systemBus
1940                                         ->new_method_call(
1941                                             connectionName.c_str(),
1942                                             transaction->objectPath.c_str(),
1943                                             "org.freedesktop.DBus."
1944                                             "Properties",
1945                                             "Set");
1946                                 m.append(interfaceName,
1947                                          transaction->propertyName);
1948                                 int r = sd_bus_message_open_container(
1949                                     m.get(), SD_BUS_TYPE_VARIANT, argType);
1950                                 if (r < 0)
1951                                 {
1952                                     transaction->setErrorStatus(
1953                                         "Unexpected Error");
1954                                     return;
1955                                 }
1956                                 r = convertJsonToDbus(
1957                                     m.get(), argType,
1958                                     transaction->propertyValue);
1959                                 if (r < 0)
1960                                 {
1961                                     if (r == -ERANGE)
1962                                     {
1963                                         transaction->setErrorStatus(
1964                                             "Provided property value "
1965                                             "is out of range for the "
1966                                             "property type");
1967                                     }
1968                                     else
1969                                     {
1970                                         transaction->setErrorStatus(
1971                                             "Invalid arg type");
1972                                     }
1973                                     return;
1974                                 }
1975                                 r = sd_bus_message_close_container(m.get());
1976                                 if (r < 0)
1977                                 {
1978                                     transaction->setErrorStatus(
1979                                         "Unexpected Error");
1980                                     return;
1981                                 }
1982                                 crow::connections::systemBus->async_send(
1983                                     m,
1984                                     [transaction](boost::system::error_code ec,
1985                                                   sdbusplus::message_t& m2) {
1986                                     BMCWEB_LOG_DEBUG << "sent";
1987                                     if (ec)
1988                                     {
1989                                         const sd_bus_error* e = m2.get_error();
1990                                         setErrorResponse(
1991                                             transaction->asyncResp->res,
1992                                             boost::beast::http::status::
1993                                                 forbidden,
1994                                             (e) != nullptr
1995                                                 ? e->name
1996                                                 : ec.category().name(),
1997                                             (e) != nullptr ? e->message
1998                                                            : ec.message());
1999                                     }
2000                                     else
2001                                     {
2002                                         transaction->asyncResp->res
2003                                             .jsonValue["status"] = "ok";
2004                                         transaction->asyncResp->res
2005                                             .jsonValue["message"] = "200 OK";
2006                                         transaction->asyncResp->res
2007                                             .jsonValue["data"] = nullptr;
2008                                     }
2009                                     });
2010                             }
2011                         }
2012                         propNode = propNode->NextSiblingElement("property");
2013                     }
2014                     ifaceNode = ifaceNode->NextSiblingElement("interface");
2015                 }
2016                 },
2017                 connectionName, transaction->objectPath,
2018                 "org.freedesktop.DBus.Introspectable", "Introspect");
2019         }
2020         },
2021         "xyz.openbmc_project.ObjectMapper",
2022         "/xyz/openbmc_project/object_mapper",
2023         "xyz.openbmc_project.ObjectMapper", "GetObject",
2024         transaction->objectPath, std::array<std::string, 0>());
2025 }
2026 
2027 inline void handleDBusUrl(const crow::Request& req,
2028                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2029                           std::string& objectPath)
2030 {
2031 
2032     // If accessing a single attribute, fill in and update objectPath,
2033     // otherwise leave destProperty blank
2034     std::string destProperty;
2035     const char* attrSeperator = "/attr/";
2036     size_t attrPosition = objectPath.find(attrSeperator);
2037     if (attrPosition != std::string::npos)
2038     {
2039         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2040                                          objectPath.length());
2041         objectPath.resize(attrPosition);
2042     }
2043 
2044     if (req.method() == boost::beast::http::verb::post)
2045     {
2046         constexpr const char* actionSeperator = "/action/";
2047         size_t actionPosition = objectPath.find(actionSeperator);
2048         if (actionPosition != std::string::npos)
2049         {
2050             std::string postProperty =
2051                 objectPath.substr((actionPosition + strlen(actionSeperator)),
2052                                   objectPath.length());
2053             objectPath.resize(actionPosition);
2054             handleAction(req, asyncResp, objectPath, postProperty);
2055             return;
2056         }
2057     }
2058     else if (req.method() == boost::beast::http::verb::get)
2059     {
2060         if (objectPath.ends_with("/enumerate"))
2061         {
2062             objectPath.erase(objectPath.end() - sizeof("enumerate"),
2063                              objectPath.end());
2064             handleEnumerate(asyncResp, objectPath);
2065         }
2066         else if (objectPath.ends_with("/list"))
2067         {
2068             objectPath.erase(objectPath.end() - sizeof("list"),
2069                              objectPath.end());
2070             handleList(asyncResp, objectPath);
2071         }
2072         else
2073         {
2074             // Trim any trailing "/" at the end
2075             if (objectPath.ends_with("/"))
2076             {
2077                 objectPath.pop_back();
2078                 handleList(asyncResp, objectPath, 1);
2079             }
2080             else
2081             {
2082                 handleGet(asyncResp, objectPath, destProperty);
2083             }
2084         }
2085         return;
2086     }
2087     else if (req.method() == boost::beast::http::verb::put)
2088     {
2089         handlePut(req, asyncResp, objectPath, destProperty);
2090         return;
2091     }
2092     else if (req.method() == boost::beast::http::verb::delete_)
2093     {
2094         handleDelete(asyncResp, objectPath);
2095         return;
2096     }
2097 
2098     setErrorResponse(asyncResp->res,
2099                      boost::beast::http::status::method_not_allowed,
2100                      methodNotAllowedDesc, methodNotAllowedMsg);
2101 }
2102 
2103 inline void
2104     handleBusSystemPost(const crow::Request& req,
2105                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2106                         const std::string& processName,
2107                         const std::string& requestedPath)
2108 {
2109     std::vector<std::string> strs;
2110     boost::split(strs, requestedPath, boost::is_any_of("/"));
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             nlohmann::json::parse(req.body, nullptr, false);
2398 
2399         if (requestDbusData.is_discarded())
2400         {
2401             asyncResp->res.result(boost::beast::http::status::bad_request);
2402             return;
2403         }
2404         if (!requestDbusData.is_array())
2405         {
2406             asyncResp->res.result(boost::beast::http::status::bad_request);
2407             return;
2408         }
2409         auto transaction =
2410             std::make_shared<InProgressActionData>(asyncResp->res);
2411 
2412         transaction->path = objectPath;
2413         transaction->methodName = methodName;
2414         transaction->arguments = std::move(requestDbusData);
2415 
2416         findActionOnInterface(transaction, processName);
2417     }
2418 }
2419 
2420 inline void requestRoutes(App& app)
2421 {
2422     BMCWEB_ROUTE(app, "/bus/")
2423         .privileges({{"Login"}})
2424         .methods(boost::beast::http::verb::get)(
2425             [](const crow::Request&,
2426                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2427         nlohmann::json::array_t buses;
2428         nlohmann::json& bus = buses.emplace_back();
2429         bus["name"] = "system";
2430         asyncResp->res.jsonValue["busses"] = std::move(buses);
2431         asyncResp->res.jsonValue["status"] = "ok";
2432         });
2433 
2434     BMCWEB_ROUTE(app, "/bus/system/")
2435         .privileges({{"Login"}})
2436         .methods(boost::beast::http::verb::get)(
2437             [](const crow::Request&,
2438                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2439         auto myCallback = [asyncResp](const boost::system::error_code ec,
2440                                       std::vector<std::string>& names) {
2441             if (ec)
2442             {
2443                 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2444                 asyncResp->res.result(
2445                     boost::beast::http::status::internal_server_error);
2446             }
2447             else
2448             {
2449                 std::sort(names.begin(), names.end());
2450                 asyncResp->res.jsonValue["status"] = "ok";
2451                 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2452                 for (const auto& name : names)
2453                 {
2454                     nlohmann::json::object_t object;
2455                     object["name"] = name;
2456                     objectsSub.push_back(std::move(object));
2457                 }
2458             }
2459         };
2460         crow::connections::systemBus->async_method_call(
2461             std::move(myCallback), "org.freedesktop.DBus", "/",
2462             "org.freedesktop.DBus", "ListNames");
2463         });
2464 
2465     BMCWEB_ROUTE(app, "/list/")
2466         .privileges({{"Login"}})
2467         .methods(boost::beast::http::verb::get)(
2468             [](const crow::Request&,
2469                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2470         handleList(asyncResp, "/");
2471         });
2472 
2473     BMCWEB_ROUTE(app, "/xyz/<path>")
2474         .privileges({{"Login"}})
2475         .methods(boost::beast::http::verb::get)(
2476             [](const crow::Request& req,
2477                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2478                const std::string& path) {
2479         std::string objectPath = "/xyz/" + path;
2480         handleDBusUrl(req, asyncResp, objectPath);
2481         });
2482 
2483     BMCWEB_ROUTE(app, "/xyz/<path>")
2484         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2485         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2486                  boost::beast::http::verb::delete_)(
2487             [](const crow::Request& req,
2488                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2489                const std::string& path) {
2490         std::string objectPath = "/xyz/" + path;
2491         handleDBusUrl(req, asyncResp, objectPath);
2492         });
2493 
2494     BMCWEB_ROUTE(app, "/org/<path>")
2495         .privileges({{"Login"}})
2496         .methods(boost::beast::http::verb::get)(
2497             [](const crow::Request& req,
2498                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2499                const std::string& path) {
2500         std::string objectPath = "/org/" + path;
2501         handleDBusUrl(req, asyncResp, objectPath);
2502         });
2503 
2504     BMCWEB_ROUTE(app, "/org/<path>")
2505         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2506         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2507                  boost::beast::http::verb::delete_)(
2508             [](const crow::Request& req,
2509                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2510                const std::string& path) {
2511         std::string objectPath = "/org/" + path;
2512         handleDBusUrl(req, asyncResp, objectPath);
2513         });
2514 
2515     BMCWEB_ROUTE(app, "/download/dump/<str>/")
2516         .privileges({{"ConfigureManager"}})
2517         .methods(boost::beast::http::verb::get)(
2518             [](const crow::Request&,
2519                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2520                const std::string& dumpId) {
2521         if (!validateFilename(dumpId))
2522         {
2523             asyncResp->res.result(boost::beast::http::status::bad_request);
2524             return;
2525         }
2526         std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps");
2527 
2528         loc /= dumpId;
2529 
2530         if (!std::filesystem::exists(loc) ||
2531             !std::filesystem::is_directory(loc))
2532         {
2533             BMCWEB_LOG_ERROR << loc.string() << "Not found";
2534             asyncResp->res.result(boost::beast::http::status::not_found);
2535             return;
2536         }
2537         std::filesystem::directory_iterator files(loc);
2538 
2539         for (const auto& file : files)
2540         {
2541             std::ifstream readFile(file.path());
2542             if (!readFile.good())
2543             {
2544                 continue;
2545             }
2546 
2547             asyncResp->res.addHeader(boost::beast::http::field::content_type,
2548                                      "application/octet-stream");
2549 
2550             // Assuming only one dump file will be present in the dump
2551             // id directory
2552             std::string dumpFileName = file.path().filename().string();
2553 
2554             // Filename should be in alphanumeric, dot and underscore
2555             // Its based on phosphor-debug-collector application
2556             // dumpfile format
2557             std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2558             if (!std::regex_match(dumpFileName, dumpFileRegex))
2559             {
2560                 BMCWEB_LOG_ERROR << "Invalid dump filename " << dumpFileName;
2561                 asyncResp->res.result(boost::beast::http::status::not_found);
2562                 return;
2563             }
2564             std::string contentDispositionParam =
2565                 "attachment; filename=\"" + dumpFileName + "\"";
2566 
2567             asyncResp->res.addHeader(
2568                 boost::beast::http::field::content_disposition,
2569                 contentDispositionParam);
2570 
2571             asyncResp->res.body() = {std::istreambuf_iterator<char>(readFile),
2572                                      std::istreambuf_iterator<char>()};
2573             return;
2574         }
2575         asyncResp->res.result(boost::beast::http::status::not_found);
2576         return;
2577         });
2578 
2579     BMCWEB_ROUTE(app, "/bus/system/<str>/")
2580         .privileges({{"Login"}})
2581 
2582         .methods(boost::beast::http::verb::get)(
2583             [](const crow::Request&,
2584                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2585                const std::string& connection) {
2586         introspectObjects(connection, "/", asyncResp);
2587         });
2588 
2589     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2590         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2591         .methods(boost::beast::http::verb::get,
2592                  boost::beast::http::verb::post)(handleBusSystemPost);
2593 }
2594 } // namespace openbmc_mapper
2595 } // namespace crow
2596