1 // Copyright (c) 2018 Intel Corporation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 #include "app.hpp"
17 #include "async_resp.hpp"
18 #include "dbus_singleton.hpp"
19 #include "dbus_utility.hpp"
20 #include "http_request.hpp"
21 #include "http_response.hpp"
22 #include "logging.hpp"
23 #include "parsing.hpp"
24 #include "routing.hpp"
25 #include "str_utility.hpp"
26 
27 #include <systemd/sd-bus-protocol.h>
28 #include <systemd/sd-bus.h>
29 #include <tinyxml2.h>
30 
31 #include <boost/algorithm/string/classification.hpp>
32 #include <boost/algorithm/string/predicate.hpp>
33 #include <boost/beast/http/status.hpp>
34 #include <boost/beast/http/verb.hpp>
35 #include <boost/container/flat_map.hpp>
36 #include <boost/container/vector.hpp>
37 #include <boost/iterator/iterator_facade.hpp>
38 #include <boost/system/error_code.hpp>
39 #include <nlohmann/json.hpp>
40 #include <sdbusplus/asio/connection.hpp>
41 #include <sdbusplus/asio/property.hpp>
42 #include <sdbusplus/exception.hpp>
43 #include <sdbusplus/message.hpp>
44 #include <sdbusplus/message/native_types.hpp>
45 
46 #include <algorithm>
47 #include <array>
48 #include <cerrno>
49 #include <cstdint>
50 #include <cstring>
51 #include <filesystem>
52 #include <fstream>
53 #include <functional>
54 #include <initializer_list>
55 #include <iterator>
56 #include <limits>
57 #include <map>
58 #include <memory>
59 #include <regex>
60 #include <string>
61 #include <string_view>
62 #include <type_traits>
63 #include <utility>
64 #include <variant>
65 #include <vector>
66 
67 // IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp>
68 // IWYU pragma: no_include <boost/system/detail/error_code.hpp>
69 // IWYU pragma: no_include <boost/system/detail/error_category.hpp>
70 // IWYU pragma: no_include <errno.h>
71 // IWYU pragma: no_include <string.h>
72 // IWYU pragma: no_include <ext/alloc_traits.h>
73 // IWYU pragma: no_include <exception>
74 // IWYU pragma: no_include <boost/type_index/type_index_facade.hpp>
75 
76 namespace crow
77 {
78 namespace openbmc_mapper
79 {
80 const constexpr char* notFoundMsg = "404 Not Found";
81 const constexpr char* badReqMsg = "400 Bad Request";
82 const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
83 const constexpr char* forbiddenMsg = "403 Forbidden";
84 const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
85 const constexpr char* methodFailedMsg = "500 Method Call Failed";
86 const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
87 const constexpr char* notFoundDesc =
88     "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
89 const constexpr char* propNotFoundDesc =
90     "The specified property cannot be found";
91 const constexpr char* noJsonDesc = "No JSON object could be decoded";
92 const constexpr char* invalidContentType =
93     "Content-type header is missing or invalid";
94 const constexpr char* methodNotFoundDesc =
95     "The specified method cannot be found";
96 const constexpr char* methodNotAllowedDesc = "Method not allowed";
97 const constexpr char* forbiddenPropDesc =
98     "The specified property cannot be created";
99 const constexpr char* forbiddenResDesc =
100     "The specified resource cannot be created";
101 
102 inline bool validateFilename(const std::string& filename)
103 {
104     std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
105 
106     return std::regex_match(filename, validFilename);
107 }
108 
109 inline void setErrorResponse(crow::Response& res,
110                              boost::beast::http::status result,
111                              const std::string& desc, std::string_view msg)
112 {
113     res.result(result);
114     res.jsonValue["data"]["description"] = desc;
115     res.jsonValue["message"] = msg;
116     res.jsonValue["status"] = "error";
117 }
118 
119 inline void
120     introspectObjects(const std::string& processName,
121                       const std::string& objectPath,
122                       const std::shared_ptr<bmcweb::AsyncResp>& transaction)
123 {
124     if (transaction->res.jsonValue.is_null())
125     {
126         transaction->res.jsonValue["status"] = "ok";
127         transaction->res.jsonValue["bus_name"] = processName;
128         transaction->res.jsonValue["objects"] = nlohmann::json::array();
129     }
130 
131     crow::connections::systemBus->async_method_call(
132         [transaction, processName{std::string(processName)},
133          objectPath{std::string(objectPath)}](
134             const boost::system::error_code& ec,
135             const std::string& introspectXml) {
136         if (ec)
137         {
138             BMCWEB_LOG_ERROR
139                 << "Introspect call failed with error: " << ec.message()
140                 << " on process: " << processName << " path: " << objectPath
141                 << "\n";
142             return;
143         }
144         nlohmann::json::object_t object;
145         object["path"] = objectPath;
146 
147         transaction->res.jsonValue["objects"].emplace_back(std::move(object));
148 
149         tinyxml2::XMLDocument doc;
150 
151         doc.Parse(introspectXml.c_str());
152         tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
153         if (pRoot == nullptr)
154         {
155             BMCWEB_LOG_ERROR << "XML document failed to parse " << processName
156                              << " " << objectPath << "\n";
157         }
158         else
159         {
160             tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
161             while (node != nullptr)
162             {
163                 const char* childPath = node->Attribute("name");
164                 if (childPath != nullptr)
165                 {
166                     std::string newpath;
167                     if (objectPath != "/")
168                     {
169                         newpath += objectPath;
170                     }
171                     newpath += std::string("/") + childPath;
172                     // introspect the subobjects as well
173                     introspectObjects(processName, newpath, transaction);
174                 }
175 
176                 node = node->NextSiblingElement("node");
177             }
178         }
179         },
180         processName, objectPath, "org.freedesktop.DBus.Introspectable",
181         "Introspect");
182 }
183 
184 inline void getPropertiesForEnumerate(
185     const std::string& objectPath, const std::string& service,
186     const std::string& interface,
187     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
188 {
189     BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
190                      << service << " " << interface;
191 
192     sdbusplus::asio::getAllProperties(
193         *crow::connections::systemBus, service, objectPath, interface,
194         [asyncResp, objectPath, service,
195          interface](const boost::system::error_code& ec,
196                     const dbus::utility::DBusPropertiesMap& propertiesList) {
197         if (ec)
198         {
199             BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
200                              << interface << " service " << service
201                              << " failed with code " << ec;
202             return;
203         }
204 
205         nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
206         nlohmann::json& objectJson = dataJson[objectPath];
207         if (objectJson.is_null())
208         {
209             objectJson = nlohmann::json::object();
210         }
211 
212         for (const auto& [name, value] : propertiesList)
213         {
214             nlohmann::json& propertyJson = objectJson[name];
215             std::visit(
216                 [&propertyJson](auto&& val) {
217                 if constexpr (std::is_same_v<std::decay_t<decltype(val)>,
218                                              sdbusplus::message::unix_fd>)
219                 {
220                     propertyJson = val.fd;
221                 }
222                 else
223                 {
224                     propertyJson = val;
225                 }
226                 },
227                 value);
228         }
229         });
230 }
231 
232 // Find any results that weren't picked up by ObjectManagers, to be
233 // called after all ObjectManagers are searched for and called.
234 inline void findRemainingObjectsForEnumerate(
235     const std::string& objectPath,
236     const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
237     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
238 {
239     BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
240     const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
241 
242     for (const auto& [path, interface_map] : *subtree)
243     {
244         if (path == objectPath)
245         {
246             // An enumerate does not return the target path's properties
247             continue;
248         }
249         if (dataJson.find(path) == dataJson.end())
250         {
251             for (const auto& [service, interfaces] : interface_map)
252             {
253                 for (const auto& interface : interfaces)
254                 {
255                     if (!interface.starts_with("org.freedesktop.DBus"))
256                     {
257                         getPropertiesForEnumerate(path, service, interface,
258                                                   asyncResp);
259                     }
260                 }
261             }
262         }
263     }
264 }
265 
266 struct InProgressEnumerateData
267 {
268     InProgressEnumerateData(
269         const std::string& objectPathIn,
270         const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
271         objectPath(objectPathIn),
272         asyncResp(asyncRespIn)
273     {}
274 
275     ~InProgressEnumerateData()
276     {
277         try
278         {
279             findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
280         }
281         catch (...)
282         {
283             BMCWEB_LOG_CRITICAL
284                 << "findRemainingObjectsForEnumerate threw exception";
285         }
286     }
287 
288     InProgressEnumerateData(const InProgressEnumerateData&) = delete;
289     InProgressEnumerateData(InProgressEnumerateData&&) = delete;
290     InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
291     InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
292     const std::string objectPath;
293     std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
294     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
295 };
296 
297 inline void getManagedObjectsForEnumerate(
298     const std::string& objectName, const std::string& objectManagerPath,
299     const std::string& connectionName,
300     const std::shared_ptr<InProgressEnumerateData>& transaction)
301 {
302     BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName
303                      << " object_manager_path " << objectManagerPath
304                      << " connection_name " << connectionName;
305     crow::connections::systemBus->async_method_call(
306         [transaction, objectName,
307          connectionName](const boost::system::error_code& ec,
308                          const dbus::utility::ManagedObjectType& objects) {
309         if (ec)
310         {
311             BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName
312                              << " on connection " << connectionName
313                              << " failed with code " << ec;
314             return;
315         }
316 
317         nlohmann::json& dataJson =
318             transaction->asyncResp->res.jsonValue["data"];
319 
320         for (const auto& objectPath : objects)
321         {
322             if (objectPath.first.str.starts_with(objectName))
323             {
324                 BMCWEB_LOG_DEBUG << "Reading object " << objectPath.first.str;
325                 nlohmann::json& objectJson = dataJson[objectPath.first.str];
326                 if (objectJson.is_null())
327                 {
328                     objectJson = nlohmann::json::object();
329                 }
330                 for (const auto& interface : objectPath.second)
331                 {
332                     for (const auto& property : interface.second)
333                     {
334                         nlohmann::json& propertyJson =
335                             objectJson[property.first];
336                         std::visit(
337                             [&propertyJson](auto&& val) {
338                             if constexpr (std::is_same_v<
339                                               std::decay_t<decltype(val)>,
340                                               sdbusplus::message::unix_fd>)
341                             {
342                                 propertyJson = val.fd;
343                             }
344                             else
345                             {
346                                 propertyJson = val;
347                             }
348                             },
349                             property.second);
350                     }
351                 }
352             }
353             for (const auto& interface : objectPath.second)
354             {
355                 if (interface.first == "org.freedesktop.DBus.ObjectManager")
356                 {
357                     getManagedObjectsForEnumerate(objectPath.first.str,
358                                                   objectPath.first.str,
359                                                   connectionName, transaction);
360                 }
361             }
362         }
363         },
364         connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager",
365         "GetManagedObjects");
366 }
367 
368 inline void findObjectManagerPathForEnumerate(
369     const std::string& objectName, const std::string& connectionName,
370     const std::shared_ptr<InProgressEnumerateData>& transaction)
371 {
372     BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName
373                      << " on connection:" << connectionName;
374     crow::connections::systemBus->async_method_call(
375         [transaction, objectName, connectionName](
376             const boost::system::error_code& ec,
377             const dbus::utility::MapperGetAncestorsResponse& objects) {
378         if (ec)
379         {
380             BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName
381                              << " failed with code " << ec;
382             return;
383         }
384 
385         for (const auto& pathGroup : objects)
386         {
387             for (const auto& connectionGroup : pathGroup.second)
388             {
389                 if (connectionGroup.first == connectionName)
390                 {
391                     // Found the object manager path for this resource.
392                     getManagedObjectsForEnumerate(objectName, pathGroup.first,
393                                                   connectionName, transaction);
394                     return;
395                 }
396             }
397         }
398         },
399         "xyz.openbmc_project.ObjectMapper",
400         "/xyz/openbmc_project/object_mapper",
401         "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
402         std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
403 }
404 
405 // Uses GetObject to add the object info about the target /enumerate path to
406 // the results of GetSubTree, as GetSubTree will not return info for the
407 // target path, and then continues on enumerating the rest of the tree.
408 inline void getObjectAndEnumerate(
409     const std::shared_ptr<InProgressEnumerateData>& transaction)
410 {
411     dbus::utility::getDbusObject(
412         transaction->objectPath, {},
413         [transaction](const boost::system::error_code& ec,
414                       const dbus::utility::MapperGetObject& objects) {
415         if (ec)
416         {
417             BMCWEB_LOG_ERROR << "GetObject for path " << transaction->objectPath
418                              << " failed with code " << ec;
419             return;
420         }
421 
422         BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
423                          << " has " << objects.size() << " entries";
424         if (!objects.empty())
425         {
426             transaction->subtree->emplace_back(transaction->objectPath,
427                                                objects);
428         }
429 
430         // Map indicating connection name, and the path where the object
431         // manager exists
432         boost::container::flat_map<std::string, std::string> connections;
433 
434         for (const auto& object : *(transaction->subtree))
435         {
436             for (const auto& connection : object.second)
437             {
438                 for (const auto& interface : connection.second)
439                 {
440                     BMCWEB_LOG_DEBUG << connection.first << " has interface "
441                                      << interface;
442                     if (interface == "org.freedesktop.DBus.ObjectManager")
443                     {
444                         BMCWEB_LOG_DEBUG << "found object manager path "
445                                          << object.first;
446                         connections[connection.first] = object.first;
447                     }
448                 }
449             }
450         }
451         BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
452 
453         for (const auto& connection : connections)
454         {
455             // If we already know where the object manager is, we don't
456             // need to search for it, we can call directly in to
457             // getManagedObjects
458             if (!connection.second.empty())
459             {
460                 getManagedObjectsForEnumerate(transaction->objectPath,
461                                               connection.second,
462                                               connection.first, transaction);
463             }
464             else
465             {
466                 // otherwise we need to find the object manager path
467                 // before we can continue
468                 findObjectManagerPathForEnumerate(
469                     transaction->objectPath, connection.first, transaction);
470             }
471         }
472         });
473 }
474 
475 // Structure for storing data on an in progress action
476 struct InProgressActionData
477 {
478     explicit InProgressActionData(
479         const std::shared_ptr<bmcweb::AsyncResp>& res) :
480         asyncResp(res)
481     {}
482     ~InProgressActionData()
483     {
484         // Methods could have been called across different owners
485         // and interfaces, where some calls failed and some passed.
486         //
487         // The rules for this are:
488         // * if no method was called - error
489         // * if a method failed and none passed - error
490         //   (converse: if at least one method passed - OK)
491         // * for the method output:
492         //   * if output processing didn't fail, return the data
493 
494         // Only deal with method returns if nothing failed earlier
495         if (asyncResp->res.result() == boost::beast::http::status::ok)
496         {
497             if (!methodPassed)
498             {
499                 if (!methodFailed)
500                 {
501                     setErrorResponse(asyncResp->res,
502                                      boost::beast::http::status::not_found,
503                                      methodNotFoundDesc, notFoundMsg);
504                 }
505             }
506             else
507             {
508                 if (outputFailed)
509                 {
510                     setErrorResponse(
511                         asyncResp->res,
512                         boost::beast::http::status::internal_server_error,
513                         "Method output failure", methodOutputFailedMsg);
514                 }
515                 else
516                 {
517                     asyncResp->res.jsonValue["status"] = "ok";
518                     asyncResp->res.jsonValue["message"] = "200 OK";
519                     asyncResp->res.jsonValue["data"] = methodResponse;
520                 }
521             }
522         }
523     }
524     InProgressActionData(const InProgressActionData&) = delete;
525     InProgressActionData(InProgressActionData&&) = delete;
526     InProgressActionData& operator=(const InProgressActionData&) = delete;
527     InProgressActionData& operator=(InProgressActionData&&) = delete;
528 
529     void setErrorStatus(const std::string& desc)
530     {
531         setErrorResponse(asyncResp->res,
532                          boost::beast::http::status::bad_request, desc,
533                          badReqMsg);
534     }
535     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
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.emplace_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.emplace_back(std::move(j));
1370         transaction->methodResponse.emplace_back(std::move(data));
1371         transaction->convertedToArray = true;
1372     }
1373     else
1374     {
1375         transaction->methodResponse.emplace_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](const 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->asyncResp->res,
1510                                         boost::beast::http::status::bad_request,
1511                                         e->name, e->message);
1512                                 }
1513                                 else
1514                                 {
1515                                     setErrorResponse(
1516                                         transaction->asyncResp->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);
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->asyncResp->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 = std::make_shared<InProgressActionData>(asyncResp);
1630         transaction->path = objectPath;
1631         transaction->methodName = "Delete";
1632         transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1633 
1634         for (const std::pair<std::string, std::vector<std::string>>& object :
1635              interfaceNames)
1636         {
1637             findActionOnInterface(transaction, object.first);
1638         }
1639         });
1640 }
1641 
1642 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1643                        const std::string& objectPath, int32_t depth = 0)
1644 {
1645     dbus::utility::getSubTreePaths(
1646         objectPath, depth, {},
1647         [asyncResp](
1648             const boost::system::error_code& ec,
1649             const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
1650         if (ec)
1651         {
1652             setErrorResponse(asyncResp->res,
1653                              boost::beast::http::status::not_found,
1654                              notFoundDesc, notFoundMsg);
1655         }
1656         else
1657         {
1658             asyncResp->res.jsonValue["status"] = "ok";
1659             asyncResp->res.jsonValue["message"] = "200 OK";
1660             asyncResp->res.jsonValue["data"] = objectPaths;
1661         }
1662         });
1663 }
1664 
1665 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1666                             const std::string& objectPath)
1667 {
1668     BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1669 
1670     asyncResp->res.jsonValue["message"] = "200 OK";
1671     asyncResp->res.jsonValue["status"] = "ok";
1672     asyncResp->res.jsonValue["data"] = nlohmann::json::object();
1673 
1674     dbus::utility::getSubTree(
1675         objectPath, 0, {},
1676         [objectPath, asyncResp](
1677             const boost::system::error_code& ec,
1678             const dbus::utility::MapperGetSubTreeResponse& objectNames) {
1679         auto transaction = std::make_shared<InProgressEnumerateData>(objectPath,
1680                                                                      asyncResp);
1681 
1682         transaction->subtree =
1683             std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1684                 objectNames);
1685 
1686         if (ec)
1687         {
1688             BMCWEB_LOG_ERROR << "GetSubTree failed on "
1689                              << transaction->objectPath;
1690             setErrorResponse(transaction->asyncResp->res,
1691                              boost::beast::http::status::not_found,
1692                              notFoundDesc, notFoundMsg);
1693             return;
1694         }
1695 
1696         // Add the data for the path passed in to the results
1697         // as if GetSubTree returned it, and continue on enumerating
1698         getObjectAndEnumerate(transaction);
1699         });
1700 }
1701 
1702 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1703                       std::string& objectPath, std::string& destProperty)
1704 {
1705     BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1706     std::shared_ptr<std::string> propertyName =
1707         std::make_shared<std::string>(std::move(destProperty));
1708 
1709     std::shared_ptr<std::string> path =
1710         std::make_shared<std::string>(std::move(objectPath));
1711 
1712     dbus::utility::getDbusObject(
1713         *path, {},
1714         [asyncResp, path,
1715          propertyName](const boost::system::error_code& ec,
1716                        const dbus::utility::MapperGetObject& objectNames) {
1717         if (ec || objectNames.empty())
1718         {
1719             setErrorResponse(asyncResp->res,
1720                              boost::beast::http::status::not_found,
1721                              notFoundDesc, notFoundMsg);
1722             return;
1723         }
1724         std::shared_ptr<nlohmann::json> response =
1725             std::make_shared<nlohmann::json>(nlohmann::json::object());
1726         // The mapper should never give us an empty interface names
1727         // list, but check anyway
1728         for (const std::pair<std::string, std::vector<std::string>>&
1729                  connection : objectNames)
1730         {
1731             const std::vector<std::string>& interfaceNames = connection.second;
1732 
1733             if (interfaceNames.empty())
1734             {
1735                 setErrorResponse(asyncResp->res,
1736                                  boost::beast::http::status::not_found,
1737                                  notFoundDesc, notFoundMsg);
1738                 return;
1739             }
1740 
1741             for (const std::string& interface : interfaceNames)
1742             {
1743                 sdbusplus::message_t m =
1744                     crow::connections::systemBus->new_method_call(
1745                         connection.first.c_str(), path->c_str(),
1746                         "org.freedesktop.DBus.Properties", "GetAll");
1747                 m.append(interface);
1748                 crow::connections::systemBus->async_send(
1749                     m, [asyncResp, response,
1750                         propertyName](const boost::system::error_code& ec2,
1751                                       sdbusplus::message_t& msg) {
1752                         if (ec2)
1753                         {
1754                             BMCWEB_LOG_ERROR << "Bad dbus request error: "
1755                                              << ec2;
1756                         }
1757                         else
1758                         {
1759                             nlohmann::json properties;
1760                             int r = convertDBusToJSON("a{sv}", msg, properties);
1761                             if (r < 0)
1762                             {
1763                                 BMCWEB_LOG_ERROR << "convertDBusToJSON failed";
1764                             }
1765                             else
1766                             {
1767                                 for (const auto& prop : properties.items())
1768                                 {
1769                                     // if property name is empty, or
1770                                     // matches our search query, add it
1771                                     // to the response json
1772 
1773                                     if (propertyName->empty())
1774                                     {
1775                                         (*response)[prop.key()] =
1776                                             std::move(prop.value());
1777                                     }
1778                                     else if (prop.key() == *propertyName)
1779                                     {
1780                                         *response = std::move(prop.value());
1781                                     }
1782                                 }
1783                             }
1784                         }
1785                         if (response.use_count() == 1)
1786                         {
1787                             if (!propertyName->empty() && response->empty())
1788                             {
1789                                 setErrorResponse(
1790                                     asyncResp->res,
1791                                     boost::beast::http::status::not_found,
1792                                     propNotFoundDesc, notFoundMsg);
1793                             }
1794                             else
1795                             {
1796                                 asyncResp->res.jsonValue["status"] = "ok";
1797                                 asyncResp->res.jsonValue["message"] = "200 OK";
1798                                 asyncResp->res.jsonValue["data"] = *response;
1799                             }
1800                         }
1801                     });
1802             }
1803         }
1804         });
1805 }
1806 
1807 struct AsyncPutRequest
1808 {
1809     explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
1810         asyncResp(resIn)
1811     {}
1812     ~AsyncPutRequest()
1813     {
1814         if (asyncResp->res.jsonValue.empty())
1815         {
1816             setErrorResponse(asyncResp->res,
1817                              boost::beast::http::status::forbidden,
1818                              forbiddenMsg, forbiddenPropDesc);
1819         }
1820     }
1821 
1822     AsyncPutRequest(const AsyncPutRequest&) = delete;
1823     AsyncPutRequest(AsyncPutRequest&&) = delete;
1824     AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1825     AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1826 
1827     void setErrorStatus(const std::string& desc)
1828     {
1829         setErrorResponse(asyncResp->res,
1830                          boost::beast::http::status::internal_server_error,
1831                          desc, badReqMsg);
1832     }
1833 
1834     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1835     std::string objectPath;
1836     std::string propertyName;
1837     nlohmann::json propertyValue;
1838 };
1839 
1840 inline void handlePut(const crow::Request& req,
1841                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1842                       const std::string& objectPath,
1843                       const std::string& destProperty)
1844 {
1845     if (destProperty.empty())
1846     {
1847         setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
1848                          forbiddenResDesc, forbiddenMsg);
1849         return;
1850     }
1851     nlohmann::json requestDbusData;
1852 
1853     JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1854     if (ret == JsonParseResult::BadContentType)
1855     {
1856         setErrorResponse(asyncResp->res,
1857                          boost::beast::http::status::unsupported_media_type,
1858                          invalidContentType, unsupportedMediaMsg);
1859         return;
1860     }
1861 
1862     if (ret != JsonParseResult::Success)
1863     {
1864         setErrorResponse(asyncResp->res,
1865                          boost::beast::http::status::bad_request, noJsonDesc,
1866                          badReqMsg);
1867         return;
1868     }
1869 
1870     auto propertyIt = requestDbusData.find("data");
1871     if (propertyIt == requestDbusData.end())
1872     {
1873         setErrorResponse(asyncResp->res,
1874                          boost::beast::http::status::bad_request, noJsonDesc,
1875                          badReqMsg);
1876         return;
1877     }
1878     const nlohmann::json& propertySetValue = *propertyIt;
1879     auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
1880     transaction->objectPath = objectPath;
1881     transaction->propertyName = destProperty;
1882     transaction->propertyValue = propertySetValue;
1883 
1884     dbus::utility::getDbusObject(
1885         transaction->objectPath, {},
1886         [transaction](const boost::system::error_code& ec2,
1887                       const dbus::utility::MapperGetObject& objectNames) {
1888         if (!ec2 && objectNames.empty())
1889         {
1890             setErrorResponse(transaction->asyncResp->res,
1891                              boost::beast::http::status::not_found,
1892                              propNotFoundDesc, notFoundMsg);
1893             return;
1894         }
1895 
1896         for (const std::pair<std::string, std::vector<std::string>>&
1897                  connection : objectNames)
1898         {
1899             const std::string& connectionName = connection.first;
1900 
1901             crow::connections::systemBus->async_method_call(
1902                 [connectionName{std::string(connectionName)},
1903                  transaction](const boost::system::error_code& ec3,
1904                               const std::string& introspectXml) {
1905                 if (ec3)
1906                 {
1907                     BMCWEB_LOG_ERROR << "Introspect call failed with error: "
1908                                      << ec3.message()
1909                                      << " on process: " << connectionName;
1910                     transaction->setErrorStatus("Unexpected Error");
1911                     return;
1912                 }
1913                 tinyxml2::XMLDocument doc;
1914 
1915                 doc.Parse(introspectXml.c_str());
1916                 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1917                 if (pRoot == nullptr)
1918                 {
1919                     BMCWEB_LOG_ERROR << "XML document failed to parse: "
1920                                      << introspectXml;
1921                     transaction->setErrorStatus("Unexpected Error");
1922                     return;
1923                 }
1924                 tinyxml2::XMLElement* ifaceNode =
1925                     pRoot->FirstChildElement("interface");
1926                 while (ifaceNode != nullptr)
1927                 {
1928                     const char* interfaceName = ifaceNode->Attribute("name");
1929                     BMCWEB_LOG_DEBUG << "found interface " << interfaceName;
1930                     tinyxml2::XMLElement* propNode =
1931                         ifaceNode->FirstChildElement("property");
1932                     while (propNode != nullptr)
1933                     {
1934                         const char* propertyName = propNode->Attribute("name");
1935                         BMCWEB_LOG_DEBUG << "Found property " << propertyName;
1936                         if (propertyName == transaction->propertyName)
1937                         {
1938                             const char* argType = propNode->Attribute("type");
1939                             if (argType != nullptr)
1940                             {
1941                                 sdbusplus::message_t m =
1942                                     crow::connections::systemBus
1943                                         ->new_method_call(
1944                                             connectionName.c_str(),
1945                                             transaction->objectPath.c_str(),
1946                                             "org.freedesktop.DBus."
1947                                             "Properties",
1948                                             "Set");
1949                                 m.append(interfaceName,
1950                                          transaction->propertyName);
1951                                 int r = sd_bus_message_open_container(
1952                                     m.get(), SD_BUS_TYPE_VARIANT, argType);
1953                                 if (r < 0)
1954                                 {
1955                                     transaction->setErrorStatus(
1956                                         "Unexpected Error");
1957                                     return;
1958                                 }
1959                                 r = convertJsonToDbus(
1960                                     m.get(), argType,
1961                                     transaction->propertyValue);
1962                                 if (r < 0)
1963                                 {
1964                                     if (r == -ERANGE)
1965                                     {
1966                                         transaction->setErrorStatus(
1967                                             "Provided property value "
1968                                             "is out of range for the "
1969                                             "property type");
1970                                     }
1971                                     else
1972                                     {
1973                                         transaction->setErrorStatus(
1974                                             "Invalid arg type");
1975                                     }
1976                                     return;
1977                                 }
1978                                 r = sd_bus_message_close_container(m.get());
1979                                 if (r < 0)
1980                                 {
1981                                     transaction->setErrorStatus(
1982                                         "Unexpected Error");
1983                                     return;
1984                                 }
1985                                 crow::connections::systemBus->async_send(
1986                                     m,
1987                                     [transaction](
1988                                         const 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     // If accessing a single attribute, fill in and update objectPath,
2032     // otherwise leave destProperty blank
2033     std::string destProperty;
2034     const char* attrSeperator = "/attr/";
2035     size_t attrPosition = objectPath.find(attrSeperator);
2036     if (attrPosition != std::string::npos)
2037     {
2038         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2039                                          objectPath.length());
2040         objectPath.resize(attrPosition);
2041     }
2042 
2043     if (req.method() == boost::beast::http::verb::post)
2044     {
2045         constexpr const char* actionSeperator = "/action/";
2046         size_t actionPosition = objectPath.find(actionSeperator);
2047         if (actionPosition != std::string::npos)
2048         {
2049             std::string postProperty =
2050                 objectPath.substr((actionPosition + strlen(actionSeperator)),
2051                                   objectPath.length());
2052             objectPath.resize(actionPosition);
2053             handleAction(req, asyncResp, objectPath, postProperty);
2054             return;
2055         }
2056     }
2057     else if (req.method() == boost::beast::http::verb::get)
2058     {
2059         if (objectPath.ends_with("/enumerate"))
2060         {
2061             objectPath.erase(objectPath.end() - sizeof("enumerate"),
2062                              objectPath.end());
2063             handleEnumerate(asyncResp, objectPath);
2064         }
2065         else if (objectPath.ends_with("/list"))
2066         {
2067             objectPath.erase(objectPath.end() - sizeof("list"),
2068                              objectPath.end());
2069             handleList(asyncResp, objectPath);
2070         }
2071         else
2072         {
2073             // Trim any trailing "/" at the end
2074             if (objectPath.ends_with("/"))
2075             {
2076                 objectPath.pop_back();
2077                 handleList(asyncResp, objectPath, 1);
2078             }
2079             else
2080             {
2081                 handleGet(asyncResp, objectPath, destProperty);
2082             }
2083         }
2084         return;
2085     }
2086     else if (req.method() == boost::beast::http::verb::put)
2087     {
2088         handlePut(req, asyncResp, objectPath, destProperty);
2089         return;
2090     }
2091     else if (req.method() == boost::beast::http::verb::delete_)
2092     {
2093         handleDelete(asyncResp, objectPath);
2094         return;
2095     }
2096 
2097     setErrorResponse(asyncResp->res,
2098                      boost::beast::http::status::method_not_allowed,
2099                      methodNotAllowedDesc, methodNotAllowedMsg);
2100 }
2101 
2102 inline void
2103     handleBusSystemPost(const crow::Request& req,
2104                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2105                         const std::string& processName,
2106                         const std::string& requestedPath)
2107 {
2108     std::vector<std::string> strs;
2109 
2110     bmcweb::split(strs, requestedPath, '/');
2111     std::string objectPath;
2112     std::string interfaceName;
2113     std::string methodName;
2114     auto it = strs.begin();
2115     if (it == strs.end())
2116     {
2117         objectPath = "/";
2118     }
2119     while (it != strs.end())
2120     {
2121         // Check if segment contains ".".  If it does, it must be an
2122         // interface
2123         if (it->find(".") != std::string::npos)
2124         {
2125             break;
2126             // This check is necessary as the trailing slash gets
2127             // parsed as part of our <path> specifier above, which
2128             // causes the normal trailing backslash redirector to
2129             // fail.
2130         }
2131         if (!it->empty())
2132         {
2133             objectPath += "/" + *it;
2134         }
2135         it++;
2136     }
2137     if (it != strs.end())
2138     {
2139         interfaceName = *it;
2140         it++;
2141 
2142         // after interface, we might have a method name
2143         if (it != strs.end())
2144         {
2145             methodName = *it;
2146             it++;
2147         }
2148     }
2149     if (it != strs.end())
2150     {
2151         // if there is more levels past the method name, something
2152         // went wrong, return not found
2153         asyncResp->res.result(boost::beast::http::status::not_found);
2154         return;
2155     }
2156     if (interfaceName.empty())
2157     {
2158         crow::connections::systemBus->async_method_call(
2159             [asyncResp, processName,
2160              objectPath](const boost::system::error_code& ec,
2161                          const std::string& introspectXml) {
2162             if (ec)
2163             {
2164                 BMCWEB_LOG_ERROR
2165                     << "Introspect call failed with error: " << ec.message()
2166                     << " on process: " << processName << " path: " << objectPath
2167                     << "\n";
2168                 return;
2169             }
2170             tinyxml2::XMLDocument doc;
2171 
2172             doc.Parse(introspectXml.c_str());
2173             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2174             if (pRoot == nullptr)
2175             {
2176                 BMCWEB_LOG_ERROR << "XML document failed to parse "
2177                                  << processName << " " << objectPath << "\n";
2178                 asyncResp->res.jsonValue["status"] = "XML parse error";
2179                 asyncResp->res.result(
2180                     boost::beast::http::status::internal_server_error);
2181                 return;
2182             }
2183 
2184             BMCWEB_LOG_DEBUG << introspectXml;
2185             asyncResp->res.jsonValue["status"] = "ok";
2186             asyncResp->res.jsonValue["bus_name"] = processName;
2187             asyncResp->res.jsonValue["object_path"] = objectPath;
2188 
2189             nlohmann::json& interfacesArray =
2190                 asyncResp->res.jsonValue["interfaces"];
2191             interfacesArray = nlohmann::json::array();
2192             tinyxml2::XMLElement* interface =
2193                 pRoot->FirstChildElement("interface");
2194 
2195             while (interface != nullptr)
2196             {
2197                 const char* ifaceName = interface->Attribute("name");
2198                 if (ifaceName != nullptr)
2199                 {
2200                     nlohmann::json::object_t interfaceObj;
2201                     interfaceObj["name"] = ifaceName;
2202                     interfacesArray.emplace_back(std::move(interfaceObj));
2203                 }
2204 
2205                 interface = interface->NextSiblingElement("interface");
2206             }
2207             },
2208             processName, objectPath, "org.freedesktop.DBus.Introspectable",
2209             "Introspect");
2210     }
2211     else if (methodName.empty())
2212     {
2213         crow::connections::systemBus->async_method_call(
2214             [asyncResp, processName, objectPath,
2215              interfaceName](const boost::system::error_code& ec,
2216                             const std::string& introspectXml) {
2217             if (ec)
2218             {
2219                 BMCWEB_LOG_ERROR
2220                     << "Introspect call failed with error: " << ec.message()
2221                     << " on process: " << processName << " path: " << objectPath
2222                     << "\n";
2223                 return;
2224             }
2225             tinyxml2::XMLDocument doc;
2226 
2227             doc.Parse(introspectXml.data(), introspectXml.size());
2228             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2229             if (pRoot == nullptr)
2230             {
2231                 BMCWEB_LOG_ERROR << "XML document failed to parse "
2232                                  << processName << " " << objectPath << "\n";
2233                 asyncResp->res.result(
2234                     boost::beast::http::status::internal_server_error);
2235                 return;
2236             }
2237 
2238             asyncResp->res.jsonValue["status"] = "ok";
2239             asyncResp->res.jsonValue["bus_name"] = processName;
2240             asyncResp->res.jsonValue["interface"] = interfaceName;
2241             asyncResp->res.jsonValue["object_path"] = objectPath;
2242 
2243             nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"];
2244             methodsArray = nlohmann::json::array();
2245 
2246             nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"];
2247             signalsArray = nlohmann::json::array();
2248 
2249             nlohmann::json& propertiesObj =
2250                 asyncResp->res.jsonValue["properties"];
2251             propertiesObj = nlohmann::json::object();
2252 
2253             // if we know we're the only call, build the
2254             // json directly
2255             tinyxml2::XMLElement* interface =
2256                 pRoot->FirstChildElement("interface");
2257             while (interface != nullptr)
2258             {
2259                 const char* ifaceName = interface->Attribute("name");
2260 
2261                 if (ifaceName != nullptr && ifaceName == interfaceName)
2262                 {
2263                     break;
2264                 }
2265 
2266                 interface = interface->NextSiblingElement("interface");
2267             }
2268             if (interface == nullptr)
2269             {
2270                 // if we got to the end of the list and
2271                 // never found a match, throw 404
2272                 asyncResp->res.result(boost::beast::http::status::not_found);
2273                 return;
2274             }
2275 
2276             tinyxml2::XMLElement* methods =
2277                 interface->FirstChildElement("method");
2278             while (methods != nullptr)
2279             {
2280                 nlohmann::json argsArray = nlohmann::json::array();
2281                 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg");
2282                 while (arg != nullptr)
2283                 {
2284                     nlohmann::json thisArg;
2285                     for (const char* fieldName : std::array<const char*, 3>{
2286                              "name", "direction", "type"})
2287                     {
2288                         const char* fieldValue = arg->Attribute(fieldName);
2289                         if (fieldValue != nullptr)
2290                         {
2291                             thisArg[fieldName] = fieldValue;
2292                         }
2293                     }
2294                     argsArray.emplace_back(std::move(thisArg));
2295                     arg = arg->NextSiblingElement("arg");
2296                 }
2297 
2298                 const char* name = methods->Attribute("name");
2299                 if (name != nullptr)
2300                 {
2301                     std::string uri;
2302                     uri.reserve(14 + processName.size() + objectPath.size() +
2303                                 interfaceName.size() + strlen(name));
2304                     uri += "/bus/system/";
2305                     uri += processName;
2306                     uri += objectPath;
2307                     uri += "/";
2308                     uri += interfaceName;
2309                     uri += "/";
2310                     uri += name;
2311 
2312                     nlohmann::json::object_t object;
2313                     object["name"] = name;
2314                     object["uri"] = std::move(uri);
2315                     object["args"] = argsArray;
2316 
2317                     methodsArray.emplace_back(std::move(object));
2318                 }
2319                 methods = methods->NextSiblingElement("method");
2320             }
2321             tinyxml2::XMLElement* signals =
2322                 interface->FirstChildElement("signal");
2323             while (signals != nullptr)
2324             {
2325                 nlohmann::json argsArray = nlohmann::json::array();
2326 
2327                 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg");
2328                 while (arg != nullptr)
2329                 {
2330                     const char* name = arg->Attribute("name");
2331                     const char* type = arg->Attribute("type");
2332                     if (name != nullptr && type != nullptr)
2333                     {
2334                         argsArray.push_back({
2335                             {"name", name},
2336                             {"type", type},
2337                         });
2338                     }
2339                     arg = arg->NextSiblingElement("arg");
2340                 }
2341                 const char* name = signals->Attribute("name");
2342                 if (name != nullptr)
2343                 {
2344                     nlohmann::json::object_t object;
2345                     object["name"] = name;
2346                     object["args"] = argsArray;
2347                     signalsArray.emplace_back(std::move(object));
2348                 }
2349 
2350                 signals = signals->NextSiblingElement("signal");
2351             }
2352 
2353             tinyxml2::XMLElement* property =
2354                 interface->FirstChildElement("property");
2355             while (property != nullptr)
2356             {
2357                 const char* name = property->Attribute("name");
2358                 const char* type = property->Attribute("type");
2359                 if (type != nullptr && name != nullptr)
2360                 {
2361                     sdbusplus::message_t m =
2362                         crow::connections::systemBus->new_method_call(
2363                             processName.c_str(), objectPath.c_str(),
2364                             "org.freedesktop."
2365                             "DBus."
2366                             "Properties",
2367                             "Get");
2368                     m.append(interfaceName, name);
2369                     nlohmann::json& propertyItem = propertiesObj[name];
2370                     crow::connections::systemBus->async_send(
2371                         m, [&propertyItem,
2372                             asyncResp](const boost::system::error_code& e,
2373                                        sdbusplus::message_t& msg) {
2374                             if (e)
2375                             {
2376                                 return;
2377                             }
2378 
2379                             convertDBusToJSON("v", msg, propertyItem);
2380                         });
2381                 }
2382                 property = property->NextSiblingElement("property");
2383             }
2384             },
2385             processName, objectPath, "org.freedesktop.DBus.Introspectable",
2386             "Introspect");
2387     }
2388     else
2389     {
2390         if (req.method() != boost::beast::http::verb::post)
2391         {
2392             asyncResp->res.result(boost::beast::http::status::not_found);
2393             return;
2394         }
2395 
2396         nlohmann::json requestDbusData;
2397         JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2398         if (ret == JsonParseResult::BadContentType)
2399         {
2400             setErrorResponse(asyncResp->res,
2401                              boost::beast::http::status::unsupported_media_type,
2402                              invalidContentType, unsupportedMediaMsg);
2403             return;
2404         }
2405         if (ret != JsonParseResult::Success)
2406         {
2407             setErrorResponse(asyncResp->res,
2408                              boost::beast::http::status::bad_request,
2409                              noJsonDesc, badReqMsg);
2410             return;
2411         }
2412 
2413         if (!requestDbusData.is_array())
2414         {
2415             asyncResp->res.result(boost::beast::http::status::bad_request);
2416             return;
2417         }
2418         auto transaction = std::make_shared<InProgressActionData>(asyncResp);
2419 
2420         transaction->path = objectPath;
2421         transaction->methodName = methodName;
2422         transaction->arguments = std::move(requestDbusData);
2423 
2424         findActionOnInterface(transaction, processName);
2425     }
2426 }
2427 
2428 inline void requestRoutes(App& app)
2429 {
2430     BMCWEB_ROUTE(app, "/bus/")
2431         .privileges({{"Login"}})
2432         .methods(boost::beast::http::verb::get)(
2433             [](const crow::Request&,
2434                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2435         nlohmann::json::array_t buses;
2436         nlohmann::json& bus = buses.emplace_back();
2437         bus["name"] = "system";
2438         asyncResp->res.jsonValue["busses"] = std::move(buses);
2439         asyncResp->res.jsonValue["status"] = "ok";
2440         });
2441 
2442     BMCWEB_ROUTE(app, "/bus/system/")
2443         .privileges({{"Login"}})
2444         .methods(boost::beast::http::verb::get)(
2445             [](const crow::Request&,
2446                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2447         auto myCallback = [asyncResp](const boost::system::error_code& ec,
2448                                       std::vector<std::string>& names) {
2449             if (ec)
2450             {
2451                 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2452                 asyncResp->res.result(
2453                     boost::beast::http::status::internal_server_error);
2454             }
2455             else
2456             {
2457                 std::sort(names.begin(), names.end());
2458                 asyncResp->res.jsonValue["status"] = "ok";
2459                 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2460                 for (const auto& name : names)
2461                 {
2462                     nlohmann::json::object_t object;
2463                     object["name"] = name;
2464                     objectsSub.emplace_back(std::move(object));
2465                 }
2466             }
2467         };
2468         crow::connections::systemBus->async_method_call(
2469             std::move(myCallback), "org.freedesktop.DBus", "/",
2470             "org.freedesktop.DBus", "ListNames");
2471         });
2472 
2473     BMCWEB_ROUTE(app, "/list/")
2474         .privileges({{"Login"}})
2475         .methods(boost::beast::http::verb::get)(
2476             [](const crow::Request&,
2477                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2478         handleList(asyncResp, "/");
2479         });
2480 
2481     BMCWEB_ROUTE(app, "/xyz/<path>")
2482         .privileges({{"Login"}})
2483         .methods(boost::beast::http::verb::get)(
2484             [](const crow::Request& req,
2485                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2486                const std::string& path) {
2487         std::string objectPath = "/xyz/" + path;
2488         handleDBusUrl(req, asyncResp, objectPath);
2489         });
2490 
2491     BMCWEB_ROUTE(app, "/xyz/<path>")
2492         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2493         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2494                  boost::beast::http::verb::delete_)(
2495             [](const crow::Request& req,
2496                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2497                const std::string& path) {
2498         std::string objectPath = "/xyz/" + path;
2499         handleDBusUrl(req, asyncResp, objectPath);
2500         });
2501 
2502     BMCWEB_ROUTE(app, "/org/<path>")
2503         .privileges({{"Login"}})
2504         .methods(boost::beast::http::verb::get)(
2505             [](const crow::Request& req,
2506                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2507                const std::string& path) {
2508         std::string objectPath = "/org/" + path;
2509         handleDBusUrl(req, asyncResp, objectPath);
2510         });
2511 
2512     BMCWEB_ROUTE(app, "/org/<path>")
2513         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2514         .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2515                  boost::beast::http::verb::delete_)(
2516             [](const crow::Request& req,
2517                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2518                const std::string& path) {
2519         std::string objectPath = "/org/" + path;
2520         handleDBusUrl(req, asyncResp, objectPath);
2521         });
2522 
2523     BMCWEB_ROUTE(app, "/download/dump/<str>/")
2524         .privileges({{"ConfigureManager"}})
2525         .methods(boost::beast::http::verb::get)(
2526             [](const crow::Request&,
2527                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2528                const std::string& dumpId) {
2529         if (!validateFilename(dumpId))
2530         {
2531             asyncResp->res.result(boost::beast::http::status::bad_request);
2532             return;
2533         }
2534         std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps");
2535 
2536         loc /= dumpId;
2537 
2538         if (!std::filesystem::exists(loc) ||
2539             !std::filesystem::is_directory(loc))
2540         {
2541             BMCWEB_LOG_ERROR << loc.string() << "Not found";
2542             asyncResp->res.result(boost::beast::http::status::not_found);
2543             return;
2544         }
2545         std::filesystem::directory_iterator files(loc);
2546 
2547         for (const auto& file : files)
2548         {
2549             std::ifstream readFile(file.path());
2550             if (!readFile.good())
2551             {
2552                 continue;
2553             }
2554 
2555             asyncResp->res.addHeader(boost::beast::http::field::content_type,
2556                                      "application/octet-stream");
2557 
2558             // Assuming only one dump file will be present in the dump
2559             // id directory
2560             std::string dumpFileName = file.path().filename().string();
2561 
2562             // Filename should be in alphanumeric, dot and underscore
2563             // Its based on phosphor-debug-collector application
2564             // dumpfile format
2565             std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2566             if (!std::regex_match(dumpFileName, dumpFileRegex))
2567             {
2568                 BMCWEB_LOG_ERROR << "Invalid dump filename " << dumpFileName;
2569                 asyncResp->res.result(boost::beast::http::status::not_found);
2570                 return;
2571             }
2572             std::string contentDispositionParam = "attachment; filename=\"" +
2573                                                   dumpFileName + "\"";
2574 
2575             asyncResp->res.addHeader(
2576                 boost::beast::http::field::content_disposition,
2577                 contentDispositionParam);
2578 
2579             asyncResp->res.body() = {std::istreambuf_iterator<char>(readFile),
2580                                      std::istreambuf_iterator<char>()};
2581             return;
2582         }
2583         asyncResp->res.result(boost::beast::http::status::not_found);
2584         return;
2585         });
2586 
2587     BMCWEB_ROUTE(app, "/bus/system/<str>/")
2588         .privileges({{"Login"}})
2589 
2590         .methods(boost::beast::http::verb::get)(
2591             [](const crow::Request&,
2592                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2593                const std::string& connection) {
2594         introspectObjects(connection, "/", asyncResp);
2595         });
2596 
2597     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2598         .privileges({{"ConfigureComponents", "ConfigureManager"}})
2599         .methods(boost::beast::http::verb::get,
2600                  boost::beast::http::verb::post)(handleBusSystemPost);
2601 }
2602 } // namespace openbmc_mapper
2603 } // namespace crow
2604