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