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