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