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