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