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