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 <crow/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 <experimental/filesystem>
25 #include <fstream>
26 #include <sdbusplus/message/types.hpp>
27 
28 namespace crow
29 {
30 namespace openbmc_mapper
31 {
32 
33 using GetSubTreeType = std::vector<
34     std::pair<std::string,
35               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36 
37 void introspectObjects(const std::string &processName,
38                        const std::string &objectPath,
39                        std::shared_ptr<bmcweb::AsyncResp> transaction)
40 {
41     if (transaction->res.jsonValue.is_null())
42     {
43         transaction->res.jsonValue = {{"status", "ok"},
44                                       {"bus_name", processName},
45                                       {"objects", nlohmann::json::array()}};
46     }
47 
48     crow::connections::systemBus->async_method_call(
49         [transaction, processName{std::string(processName)},
50          objectPath{std::string(objectPath)}](
51             const boost::system::error_code ec,
52             const std::string &introspect_xml) {
53             if (ec)
54             {
55                 BMCWEB_LOG_ERROR
56                     << "Introspect call failed with error: " << ec.message()
57                     << " on process: " << processName << " path: " << objectPath
58                     << "\n";
59                 return;
60             }
61             transaction->res.jsonValue["objects"].push_back(
62                 {{"path", objectPath}});
63 
64             tinyxml2::XMLDocument doc;
65 
66             doc.Parse(introspect_xml.c_str());
67             tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
68             if (pRoot == nullptr)
69             {
70                 BMCWEB_LOG_ERROR << "XML document failed to parse "
71                                  << processName << " " << objectPath << "\n";
72             }
73             else
74             {
75                 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
76                 while (node != nullptr)
77                 {
78                     const char *childPath = node->Attribute("name");
79                     if (childPath != nullptr)
80                     {
81                         std::string newpath;
82                         if (objectPath != "/")
83                         {
84                             newpath += objectPath;
85                         }
86                         newpath += std::string("/") + childPath;
87                         // introspect the subobjects as well
88                         introspectObjects(processName, newpath, transaction);
89                     }
90 
91                     node = node->NextSiblingElement("node");
92                 }
93             }
94         },
95         processName, objectPath, "org.freedesktop.DBus.Introspectable",
96         "Introspect");
97 }
98 
99 struct InProgressEnumerateData
100 {
101     InProgressEnumerateData(const std::string &objectPath,
102                             std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
103         objectPath(objectPath),
104         asyncResp(asyncResp)
105     {
106     }
107 
108     ~InProgressEnumerateData()
109     {
110         // TODO
111     }
112 
113     const std::string objectPath;
114     std::shared_ptr<GetSubTreeType> subtree;
115     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
116 };
117 
118 void getManagedObjectsForEnumerate(
119     const std::string &object_name, const std::string &object_manager_path,
120     const std::string &connection_name,
121     std::shared_ptr<InProgressEnumerateData> transaction)
122 {
123     BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
124                      << " object_manager_path " << object_manager_path
125                      << " connection_name " << connection_name;
126     crow::connections::systemBus->async_method_call(
127         [transaction, object_name,
128          connection_name](const boost::system::error_code ec,
129                           const dbus::utility::ManagedObjectType &objects) {
130             if (ec)
131             {
132                 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
133                                  << " on connection " << connection_name
134                                  << " failed with code " << ec;
135                 return;
136             }
137 
138             nlohmann::json &dataJson =
139                 transaction->asyncResp->res.jsonValue["data"];
140 
141             for (const auto &objectPath : objects)
142             {
143                 if (boost::starts_with(objectPath.first.str, object_name))
144                 {
145                     BMCWEB_LOG_DEBUG << "Reading object "
146                                      << objectPath.first.str;
147                     nlohmann::json &objectJson = dataJson[objectPath.first.str];
148                     if (objectJson.is_null())
149                     {
150                         objectJson = nlohmann::json::object();
151                     }
152                     for (const auto &interface : objectPath.second)
153                     {
154                         for (const auto &property : interface.second)
155                         {
156                             nlohmann::json &propertyJson =
157                                 objectJson[property.first];
158                             sdbusplus::message::variant_ns::visit(
159                                 [&propertyJson](auto &&val) {
160                                     propertyJson = val;
161                                 },
162                                 property.second);
163                         }
164                     }
165                 }
166                 for (const auto &interface : objectPath.second)
167                 {
168                     if (interface.first == "org.freedesktop.DBus.ObjectManager")
169                     {
170                         getManagedObjectsForEnumerate(
171                             objectPath.first.str, objectPath.first.str,
172                             connection_name, transaction);
173                     }
174                 }
175             }
176         },
177         connection_name, object_manager_path,
178         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
179 }
180 
181 void findObjectManagerPathForEnumerate(
182     const std::string &object_name, const std::string &connection_name,
183     std::shared_ptr<InProgressEnumerateData> transaction)
184 {
185     BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
186                      << " on connection:" << connection_name;
187     crow::connections::systemBus->async_method_call(
188         [transaction, object_name, connection_name](
189             const boost::system::error_code ec,
190             const boost::container::flat_map<
191                 std::string, boost::container::flat_map<
192                                  std::string, std::vector<std::string>>>
193                 &objects) {
194             if (ec)
195             {
196                 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
197                                  << " failed with code " << ec;
198                 return;
199             }
200 
201             for (const auto &pathGroup : objects)
202             {
203                 for (const auto &connectionGroup : pathGroup.second)
204                 {
205                     if (connectionGroup.first == connection_name)
206                     {
207                         // Found the object manager path for this resource.
208                         getManagedObjectsForEnumerate(
209                             object_name, pathGroup.first, connection_name,
210                             transaction);
211                         return;
212                     }
213                 }
214             }
215         },
216         "xyz.openbmc_project.ObjectMapper",
217         "/xyz/openbmc_project/object_mapper",
218         "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
219         std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
220 }
221 
222 // Uses GetObject to add the object info about the target /enumerate path to the
223 // results of GetSubTree, as GetSubTree will not return info for the
224 // target path, and then continues on enumerating the rest of the tree.
225 void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
226 {
227     using GetObjectType =
228         std::vector<std::pair<std::string, std::vector<std::string>>>;
229 
230     crow::connections::systemBus->async_method_call(
231         [transaction](const boost::system::error_code ec,
232                       const GetObjectType &objects) {
233             if (ec)
234             {
235                 BMCWEB_LOG_ERROR << "GetObject for path "
236                                  << transaction->objectPath
237                                  << " failed with code " << ec;
238                 return;
239             }
240 
241             BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
242                              << " has " << objects.size() << " entries";
243             if (!objects.empty())
244             {
245                 transaction->subtree->emplace_back(transaction->objectPath,
246                                                    objects);
247             }
248 
249             // Map indicating connection name, and the path where the object
250             // manager exists
251             boost::container::flat_map<std::string, std::string> connections;
252 
253             for (const auto &object : *(transaction->subtree))
254             {
255                 for (const auto &connection : object.second)
256                 {
257                     std::string &objectManagerPath =
258                         connections[connection.first];
259                     for (const auto &interface : connection.second)
260                     {
261                         BMCWEB_LOG_DEBUG << connection.first
262                                          << " has interface " << interface;
263                         if (interface == "org.freedesktop.DBus.ObjectManager")
264                         {
265                             BMCWEB_LOG_DEBUG << "found object manager path "
266                                              << object.first;
267                             objectManagerPath = object.first;
268                         }
269                     }
270                 }
271             }
272             BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
273 
274             for (const auto &connection : connections)
275             {
276                 // If we already know where the object manager is, we don't need
277                 // to search for it, we can call directly in to
278                 // getManagedObjects
279                 if (!connection.second.empty())
280                 {
281                     getManagedObjectsForEnumerate(
282                         transaction->objectPath, connection.second,
283                         connection.first, transaction);
284                 }
285                 else
286                 {
287                     // otherwise we need to find the object manager path before
288                     // we can continue
289                     findObjectManagerPathForEnumerate(
290                         transaction->objectPath, connection.first, transaction);
291                 }
292             }
293         },
294         "xyz.openbmc_project.ObjectMapper",
295         "/xyz/openbmc_project/object_mapper",
296         "xyz.openbmc_project.ObjectMapper", "GetObject",
297         transaction->objectPath, std::array<const char *, 0>());
298 }
299 
300 // Structure for storing data on an in progress action
301 struct InProgressActionData
302 {
303     InProgressActionData(crow::Response &res) : res(res){};
304     ~InProgressActionData()
305     {
306         if (res.result() == boost::beast::http::status::internal_server_error)
307         {
308             // Reset the json object to clear out any data that made it in
309             // before the error happened todo(ed) handle error condition with
310             // proper code
311             res.jsonValue = nlohmann::json::object();
312         }
313         res.end();
314     }
315 
316     void setErrorStatus()
317     {
318         res.result(boost::beast::http::status::internal_server_error);
319     }
320     crow::Response &res;
321     std::string path;
322     std::string methodName;
323     nlohmann::json arguments;
324 };
325 
326 std::vector<std::string> dbusArgSplit(const std::string &string)
327 {
328     std::vector<std::string> ret;
329     if (string.empty())
330     {
331         return ret;
332     }
333     ret.push_back("");
334     int containerDepth = 0;
335 
336     for (std::string::const_iterator character = string.begin();
337          character != string.end(); character++)
338     {
339         ret.back() += *character;
340         switch (*character)
341         {
342             case ('a'):
343                 break;
344             case ('('):
345             case ('{'):
346                 containerDepth++;
347                 break;
348             case ('}'):
349             case (')'):
350                 containerDepth--;
351                 if (containerDepth == 0)
352                 {
353                     if (character + 1 != string.end())
354                     {
355                         ret.push_back("");
356                     }
357                 }
358                 break;
359             default:
360                 if (containerDepth == 0)
361                 {
362                     if (character + 1 != string.end())
363                     {
364                         ret.push_back("");
365                     }
366                 }
367                 break;
368         }
369     }
370 }
371 
372 int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
373                       const nlohmann::json &input_json)
374 {
375     int r = 0;
376     BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
377                      << " to type: " << arg_type;
378     const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
379 
380     // Assume a single object for now.
381     const nlohmann::json *j = &input_json;
382     nlohmann::json::const_iterator jIt = input_json.begin();
383 
384     for (const std::string &argCode : argTypes)
385     {
386         // If we are decoding multiple objects, grab the pointer to the
387         // iterator, and increment it for the next loop
388         if (argTypes.size() > 1)
389         {
390             if (jIt == input_json.end())
391             {
392                 return -2;
393             }
394             j = &*jIt;
395             jIt++;
396         }
397         const int64_t *intValue = j->get_ptr<const int64_t *>();
398         const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
399         const std::string *stringValue = j->get_ptr<const std::string *>();
400         const double *doubleValue = j->get_ptr<const double *>();
401         const bool *b = j->get_ptr<const bool *>();
402         int64_t v = 0;
403         double d = 0.0;
404 
405         // Do some basic type conversions that make sense.  uint can be
406         // converted to int.  int and uint can be converted to double
407         if (uintValue != nullptr && intValue == nullptr)
408         {
409             v = static_cast<int64_t>(*uintValue);
410             intValue = &v;
411         }
412         if (uintValue != nullptr && doubleValue == nullptr)
413         {
414             d = static_cast<double>(*uintValue);
415             doubleValue = &d;
416         }
417         if (intValue != nullptr && doubleValue == nullptr)
418         {
419             d = static_cast<double>(*intValue);
420             doubleValue = &d;
421         }
422 
423         if (argCode == "s")
424         {
425             if (stringValue == nullptr)
426             {
427                 return -1;
428             }
429             r = sd_bus_message_append_basic(m, argCode[0],
430                                             (void *)stringValue->c_str());
431             if (r < 0)
432             {
433                 return r;
434             }
435         }
436         else if (argCode == "i")
437         {
438             if (intValue == nullptr)
439             {
440                 return -1;
441             }
442             int32_t i = static_cast<int32_t>(*intValue);
443             r = sd_bus_message_append_basic(m, argCode[0], &i);
444             if (r < 0)
445             {
446                 return r;
447             }
448         }
449         else if (argCode == "b")
450         {
451             // lots of ways bool could be represented here.  Try them all
452             int boolInt = false;
453             if (intValue != nullptr)
454             {
455                 boolInt = *intValue > 0 ? 1 : 0;
456             }
457             else if (b != nullptr)
458             {
459                 boolInt = b ? 1 : 0;
460             }
461             else if (stringValue != nullptr)
462             {
463                 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
464             }
465             else
466             {
467                 return -1;
468             }
469             r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
470             if (r < 0)
471             {
472                 return r;
473             }
474         }
475         else if (argCode == "n")
476         {
477             if (intValue == nullptr)
478             {
479                 return -1;
480             }
481             int16_t n = static_cast<int16_t>(*intValue);
482             r = sd_bus_message_append_basic(m, argCode[0], &n);
483             if (r < 0)
484             {
485                 return r;
486             }
487         }
488         else if (argCode == "x")
489         {
490             if (intValue == nullptr)
491             {
492                 return -1;
493             }
494             r = sd_bus_message_append_basic(m, argCode[0], intValue);
495             if (r < 0)
496             {
497                 return r;
498             }
499         }
500         else if (argCode == "y")
501         {
502             if (uintValue == nullptr)
503             {
504                 return -1;
505             }
506             uint8_t y = static_cast<uint8_t>(*uintValue);
507             r = sd_bus_message_append_basic(m, argCode[0], &y);
508         }
509         else if (argCode == "q")
510         {
511             if (uintValue == nullptr)
512             {
513                 return -1;
514             }
515             uint16_t q = static_cast<uint16_t>(*uintValue);
516             r = sd_bus_message_append_basic(m, argCode[0], &q);
517         }
518         else if (argCode == "u")
519         {
520             if (uintValue == nullptr)
521             {
522                 return -1;
523             }
524             uint32_t u = static_cast<uint32_t>(*uintValue);
525             r = sd_bus_message_append_basic(m, argCode[0], &u);
526         }
527         else if (argCode == "t")
528         {
529             if (uintValue == nullptr)
530             {
531                 return -1;
532             }
533             r = sd_bus_message_append_basic(m, argCode[0], uintValue);
534         }
535         else if (argCode == "d")
536         {
537             sd_bus_message_append_basic(m, argCode[0], doubleValue);
538         }
539         else if (boost::starts_with(argCode, "a"))
540         {
541             std::string containedType = argCode.substr(1);
542             r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
543                                               containedType.c_str());
544             if (r < 0)
545             {
546                 return r;
547             }
548 
549             for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
550                  ++it)
551             {
552                 r = convertJsonToDbus(m, containedType, *it);
553                 if (r < 0)
554                 {
555                     return r;
556                 }
557 
558                 it++;
559             }
560             sd_bus_message_close_container(m);
561         }
562         else if (boost::starts_with(argCode, "v"))
563         {
564             std::string containedType = argCode.substr(1);
565             BMCWEB_LOG_DEBUG << "variant type: " << argCode
566                              << " appending variant of type: " << containedType;
567             r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
568                                               containedType.c_str());
569             if (r < 0)
570             {
571                 return r;
572             }
573 
574             r = convertJsonToDbus(m, containedType, input_json);
575             if (r < 0)
576             {
577                 return r;
578             }
579 
580             r = sd_bus_message_close_container(m);
581             if (r < 0)
582             {
583                 return r;
584             }
585         }
586         else if (boost::starts_with(argCode, "(") &&
587                  boost::ends_with(argCode, ")"))
588         {
589             std::string containedType = argCode.substr(1, argCode.size() - 1);
590             r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
591                                               containedType.c_str());
592             nlohmann::json::const_iterator it = j->begin();
593             for (const std::string &argCode : dbusArgSplit(arg_type))
594             {
595                 if (it == j->end())
596                 {
597                     return -1;
598                 }
599                 r = convertJsonToDbus(m, argCode, *it);
600                 if (r < 0)
601                 {
602                     return r;
603                 }
604                 it++;
605             }
606             r = sd_bus_message_close_container(m);
607         }
608         else if (boost::starts_with(argCode, "{") &&
609                  boost::ends_with(argCode, "}"))
610         {
611             std::string containedType = argCode.substr(1, argCode.size() - 1);
612             r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
613                                               containedType.c_str());
614             std::vector<std::string> codes = dbusArgSplit(containedType);
615             if (codes.size() != 2)
616             {
617                 return -1;
618             }
619             const std::string &key_type = codes[0];
620             const std::string &value_type = codes[1];
621             for (auto it : j->items())
622             {
623                 r = convertJsonToDbus(m, key_type, it.key());
624                 if (r < 0)
625                 {
626                     return r;
627                 }
628 
629                 r = convertJsonToDbus(m, value_type, it.value());
630                 if (r < 0)
631                 {
632                     return r;
633                 }
634             }
635             r = sd_bus_message_close_container(m);
636         }
637         else
638         {
639             return -2;
640         }
641         if (r < 0)
642         {
643             return r;
644         }
645 
646         if (argTypes.size() > 1)
647         {
648             jIt++;
649         }
650     }
651 }
652 
653 void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
654                            const std::string &connectionName)
655 {
656     BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
657                      << connectionName;
658     crow::connections::systemBus->async_method_call(
659         [transaction, connectionName{std::string(connectionName)}](
660             const boost::system::error_code ec,
661             const std::string &introspect_xml) {
662             BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
663             if (ec)
664             {
665                 BMCWEB_LOG_ERROR
666                     << "Introspect call failed with error: " << ec.message()
667                     << " on process: " << connectionName << "\n";
668             }
669             else
670             {
671                 tinyxml2::XMLDocument doc;
672 
673                 doc.Parse(introspect_xml.data(), introspect_xml.size());
674                 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
675                 if (pRoot == nullptr)
676                 {
677                     BMCWEB_LOG_ERROR << "XML document failed to parse "
678                                      << connectionName << "\n";
679                     return;
680                 }
681                 tinyxml2::XMLElement *interfaceNode =
682                     pRoot->FirstChildElement("interface");
683                 while (interfaceNode != nullptr)
684                 {
685                     const char *thisInterfaceName =
686                         interfaceNode->Attribute("name");
687                     if (thisInterfaceName != nullptr)
688                     {
689                         tinyxml2::XMLElement *methodNode =
690                             interfaceNode->FirstChildElement("method");
691                         while (methodNode != nullptr)
692                         {
693                             const char *thisMethodName =
694                                 methodNode->Attribute("name");
695                             BMCWEB_LOG_DEBUG << "Found method: "
696                                              << thisMethodName;
697                             if (thisMethodName != nullptr &&
698                                 thisMethodName == transaction->methodName)
699                             {
700                                 BMCWEB_LOG_DEBUG
701                                     << "Found method named " << thisMethodName
702                                     << " on interface " << thisInterfaceName;
703                                 sdbusplus::message::message m =
704                                     crow::connections::systemBus
705                                         ->new_method_call(
706                                             connectionName.c_str(),
707                                             transaction->path.c_str(),
708                                             thisInterfaceName,
709                                             transaction->methodName.c_str());
710 
711                                 tinyxml2::XMLElement *argumentNode =
712                                     methodNode->FirstChildElement("arg");
713 
714                                 nlohmann::json::const_iterator argIt =
715                                     transaction->arguments.begin();
716 
717                                 while (argumentNode != nullptr)
718                                 {
719                                     const char *argDirection =
720                                         argumentNode->Attribute("direction");
721                                     const char *argType =
722                                         argumentNode->Attribute("type");
723                                     if (argDirection != nullptr &&
724                                         argType != nullptr &&
725                                         std::string(argDirection) == "in")
726                                     {
727 
728                                         if (argIt ==
729                                             transaction->arguments.end())
730                                         {
731                                             transaction->setErrorStatus();
732                                             return;
733                                         }
734                                         if (convertJsonToDbus(
735                                                 m.get(), std::string(argType),
736                                                 *argIt) < 0)
737                                         {
738                                             transaction->setErrorStatus();
739                                             return;
740                                         }
741 
742                                         argIt++;
743                                     }
744                                     argumentNode =
745                                         methodNode->NextSiblingElement("arg");
746                                 }
747 
748                                 crow::connections::systemBus->async_send(
749                                     m, [transaction](
750                                            boost::system::error_code ec,
751                                            sdbusplus::message::message &m) {
752                                         if (ec)
753                                         {
754                                             transaction->setErrorStatus();
755                                             return;
756                                         }
757                                         transaction->res.jsonValue = {
758                                             {"status", "ok"},
759                                             {"message", "200 OK"},
760                                             {"data", nullptr}};
761                                     });
762                                 break;
763                             }
764                             methodNode =
765                                 methodNode->NextSiblingElement("method");
766                         }
767                     }
768                     interfaceNode =
769                         interfaceNode->NextSiblingElement("interface");
770                 }
771             }
772         },
773         connectionName, transaction->path,
774         "org.freedesktop.DBus.Introspectable", "Introspect");
775 }
776 
777 void handleAction(const crow::Request &req, crow::Response &res,
778                   const std::string &objectPath, const std::string &methodName)
779 {
780     BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
781                      << methodName;
782     nlohmann::json requestDbusData =
783         nlohmann::json::parse(req.body, nullptr, false);
784 
785     if (requestDbusData.is_discarded())
786     {
787         res.result(boost::beast::http::status::bad_request);
788         res.end();
789         return;
790     }
791     nlohmann::json::iterator data = requestDbusData.find("data");
792     if (data == requestDbusData.end())
793     {
794         res.result(boost::beast::http::status::bad_request);
795         res.end();
796         return;
797     }
798 
799     if (!data->is_array())
800     {
801         res.result(boost::beast::http::status::bad_request);
802         res.end();
803         return;
804     }
805     auto transaction = std::make_shared<InProgressActionData>(res);
806 
807     transaction->path = objectPath;
808     transaction->methodName = methodName;
809     transaction->arguments = std::move(*data);
810     crow::connections::systemBus->async_method_call(
811         [transaction](
812             const boost::system::error_code ec,
813             const std::vector<std::pair<std::string, std::vector<std::string>>>
814                 &interfaceNames) {
815             if (ec || interfaceNames.size() <= 0)
816             {
817                 BMCWEB_LOG_ERROR << "Can't find object";
818                 transaction->setErrorStatus();
819                 return;
820             }
821 
822             BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
823                              << " object(s)";
824 
825             for (const std::pair<std::string, std::vector<std::string>>
826                      &object : interfaceNames)
827             {
828                 findActionOnInterface(transaction, object.first);
829             }
830         },
831         "xyz.openbmc_project.ObjectMapper",
832         "/xyz/openbmc_project/object_mapper",
833         "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
834         std::array<std::string, 0>());
835 }
836 
837 void handleList(crow::Response &res, const std::string &objectPath,
838                 int32_t depth = 0)
839 {
840     crow::connections::systemBus->async_method_call(
841         [&res](const boost::system::error_code ec,
842                std::vector<std::string> &objectPaths) {
843             if (ec)
844             {
845                 res.result(boost::beast::http::status::internal_server_error);
846             }
847             else
848             {
849                 res.jsonValue = {{"status", "ok"},
850                                  {"message", "200 OK"},
851                                  {"data", std::move(objectPaths)}};
852             }
853             res.end();
854         },
855         "xyz.openbmc_project.ObjectMapper",
856         "/xyz/openbmc_project/object_mapper",
857         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
858         depth, std::array<std::string, 0>());
859 }
860 
861 void handleEnumerate(crow::Response &res, const std::string &objectPath)
862 {
863     BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
864     auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
865 
866     asyncResp->res.jsonValue = {{"message", "200 OK"},
867                                 {"status", "ok"},
868                                 {"data", nlohmann::json::object()}};
869 
870     crow::connections::systemBus->async_method_call(
871         [objectPath, asyncResp](const boost::system::error_code ec,
872                                 GetSubTreeType &object_names) {
873             auto transaction = std::make_shared<InProgressEnumerateData>(
874                 objectPath, asyncResp);
875 
876             transaction->subtree =
877                 std::make_shared<GetSubTreeType>(std::move(object_names));
878 
879             if (ec)
880             {
881                 return;
882             }
883 
884             // Add the data for the path passed in to the results
885             // as if GetSubTree returned it, and continue on enumerating
886             getObjectAndEnumerate(transaction);
887         },
888         "xyz.openbmc_project.ObjectMapper",
889         "/xyz/openbmc_project/object_mapper",
890         "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
891         static_cast<int32_t>(0), std::array<const char *, 0>());
892 }
893 
894 void handleGet(crow::Response &res, std::string &objectPath,
895                std::string &destProperty)
896 {
897     BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
898     std::shared_ptr<std::string> propertyName =
899         std::make_shared<std::string>(std::move(destProperty));
900 
901     std::shared_ptr<std::string> path =
902         std::make_shared<std::string>(std::move(objectPath));
903 
904     using GetObjectType =
905         std::vector<std::pair<std::string, std::vector<std::string>>>;
906     crow::connections::systemBus->async_method_call(
907         [&res, path, propertyName](const boost::system::error_code ec,
908                                    const GetObjectType &object_names) {
909             if (ec || object_names.size() <= 0)
910             {
911                 res.result(boost::beast::http::status::not_found);
912                 res.end();
913                 return;
914             }
915             std::shared_ptr<nlohmann::json> response =
916                 std::make_shared<nlohmann::json>(nlohmann::json::object());
917             // The mapper should never give us an empty interface names list,
918             // but check anyway
919             for (const std::pair<std::string, std::vector<std::string>>
920                      connection : object_names)
921             {
922                 const std::vector<std::string> &interfaceNames =
923                     connection.second;
924 
925                 if (interfaceNames.size() <= 0)
926                 {
927                     res.result(boost::beast::http::status::not_found);
928                     res.end();
929                     return;
930                 }
931 
932                 for (const std::string &interface : interfaceNames)
933                 {
934                     crow::connections::systemBus->async_method_call(
935                         [&res, response, propertyName](
936                             const boost::system::error_code ec,
937                             const std::vector<std::pair<
938                                 std::string, dbus::utility::DbusVariantType>>
939                                 &properties) {
940                             if (ec)
941                             {
942                                 BMCWEB_LOG_ERROR << "Bad dbus request error: "
943                                                  << ec;
944                             }
945                             else
946                             {
947                                 for (const std::pair<
948                                          std::string,
949                                          dbus::utility::DbusVariantType>
950                                          &property : properties)
951                                 {
952                                     // if property name is empty, or matches our
953                                     // search query, add it to the response json
954 
955                                     if (propertyName->empty())
956                                     {
957                                         sdbusplus::message::variant_ns::visit(
958                                             [&response, &property](auto &&val) {
959                                                 (*response)[property.first] =
960                                                     val;
961                                             },
962                                             property.second);
963                                     }
964                                     else if (property.first == *propertyName)
965                                     {
966                                         sdbusplus::message::variant_ns::visit(
967                                             [&response](auto &&val) {
968                                                 (*response) = val;
969                                             },
970                                             property.second);
971                                     }
972                                 }
973                             }
974                             if (response.use_count() == 1)
975                             {
976                                 res.jsonValue = {{"status", "ok"},
977                                                  {"message", "200 OK"},
978                                                  {"data", *response}};
979 
980                                 res.end();
981                             }
982                         },
983                         connection.first, *path,
984                         "org.freedesktop.DBus.Properties", "GetAll", interface);
985                 }
986             }
987         },
988         "xyz.openbmc_project.ObjectMapper",
989         "/xyz/openbmc_project/object_mapper",
990         "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
991         std::array<std::string, 0>());
992 }
993 
994 struct AsyncPutRequest
995 {
996     AsyncPutRequest(crow::Response &res) : res(res)
997     {
998         res.jsonValue = {
999             {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
1000     }
1001     ~AsyncPutRequest()
1002     {
1003         if (res.result() == boost::beast::http::status::internal_server_error)
1004         {
1005             // Reset the json object to clear out any data that made it in
1006             // before the error happened todo(ed) handle error condition with
1007             // proper code
1008             res.jsonValue = nlohmann::json::object();
1009         }
1010 
1011         if (res.jsonValue.empty())
1012         {
1013             res.result(boost::beast::http::status::forbidden);
1014             res.jsonValue = {
1015                 {"status", "error"},
1016                 {"message", "403 Forbidden"},
1017                 {"data",
1018                  {{"message", "The specified property cannot be created: " +
1019                                   propertyName}}}};
1020         }
1021 
1022         res.end();
1023     }
1024 
1025     void setErrorStatus()
1026     {
1027         res.result(boost::beast::http::status::internal_server_error);
1028     }
1029 
1030     crow::Response &res;
1031     std::string objectPath;
1032     std::string propertyName;
1033     nlohmann::json propertyValue;
1034 };
1035 
1036 void handlePut(const crow::Request &req, crow::Response &res,
1037                const std::string &objectPath, const std::string &destProperty)
1038 {
1039     nlohmann::json requestDbusData =
1040         nlohmann::json::parse(req.body, nullptr, false);
1041 
1042     if (requestDbusData.is_discarded())
1043     {
1044         res.result(boost::beast::http::status::bad_request);
1045         res.end();
1046         return;
1047     }
1048 
1049     nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1050     if (propertyIt == requestDbusData.end())
1051     {
1052         res.result(boost::beast::http::status::bad_request);
1053         res.end();
1054         return;
1055     }
1056     const nlohmann::json &propertySetValue = *propertyIt;
1057     auto transaction = std::make_shared<AsyncPutRequest>(res);
1058     transaction->objectPath = objectPath;
1059     transaction->propertyName = destProperty;
1060     transaction->propertyValue = propertySetValue;
1061 
1062     using GetObjectType =
1063         std::vector<std::pair<std::string, std::vector<std::string>>>;
1064 
1065     crow::connections::systemBus->async_method_call(
1066         [transaction](const boost::system::error_code ec,
1067                       const GetObjectType &object_names) {
1068             if (!ec && object_names.size() <= 0)
1069             {
1070                 transaction->res.result(boost::beast::http::status::not_found);
1071                 return;
1072             }
1073 
1074             for (const std::pair<std::string, std::vector<std::string>>
1075                      connection : object_names)
1076             {
1077                 const std::string &connectionName = connection.first;
1078 
1079                 crow::connections::systemBus->async_method_call(
1080                     [connectionName{std::string(connectionName)},
1081                      transaction](const boost::system::error_code ec,
1082                                   const std::string &introspectXml) {
1083                         if (ec)
1084                         {
1085                             BMCWEB_LOG_ERROR
1086                                 << "Introspect call failed with error: "
1087                                 << ec.message()
1088                                 << " on process: " << connectionName;
1089                             transaction->setErrorStatus();
1090                             return;
1091                         }
1092                         tinyxml2::XMLDocument doc;
1093 
1094                         doc.Parse(introspectXml.c_str());
1095                         tinyxml2::XMLNode *pRoot =
1096                             doc.FirstChildElement("node");
1097                         if (pRoot == nullptr)
1098                         {
1099                             BMCWEB_LOG_ERROR << "XML document failed to parse: "
1100                                              << introspectXml;
1101                             transaction->setErrorStatus();
1102                             return;
1103                         }
1104                         tinyxml2::XMLElement *ifaceNode =
1105                             pRoot->FirstChildElement("interface");
1106                         while (ifaceNode != nullptr)
1107                         {
1108                             const char *interfaceName =
1109                                 ifaceNode->Attribute("name");
1110                             BMCWEB_LOG_DEBUG << "found interface "
1111                                              << interfaceName;
1112                             tinyxml2::XMLElement *propNode =
1113                                 ifaceNode->FirstChildElement("property");
1114                             while (propNode != nullptr)
1115                             {
1116                                 const char *propertyName =
1117                                     propNode->Attribute("name");
1118                                 BMCWEB_LOG_DEBUG << "Found property "
1119                                                  << propertyName;
1120                                 if (propertyName == transaction->propertyName)
1121                                 {
1122                                     const char *argType =
1123                                         propNode->Attribute("type");
1124                                     if (argType != nullptr)
1125                                     {
1126                                         sdbusplus::message::message m =
1127                                             crow::connections::systemBus
1128                                                 ->new_method_call(
1129                                                     connectionName.c_str(),
1130                                                     transaction->objectPath
1131                                                         .c_str(),
1132                                                     "org.freedesktop.DBus."
1133                                                     "Properties",
1134                                                     "Set");
1135                                         m.append(interfaceName,
1136                                                  transaction->propertyName);
1137                                         int r = sd_bus_message_open_container(
1138                                             m.get(), SD_BUS_TYPE_VARIANT,
1139                                             argType);
1140                                         if (r < 0)
1141                                         {
1142                                             transaction->setErrorStatus();
1143                                             return;
1144                                         }
1145                                         r = convertJsonToDbus(
1146                                             m.get(), argType,
1147                                             transaction->propertyValue);
1148                                         if (r < 0)
1149                                         {
1150                                             transaction->setErrorStatus();
1151                                             return;
1152                                         }
1153                                         r = sd_bus_message_close_container(
1154                                             m.get());
1155                                         if (r < 0)
1156                                         {
1157                                             transaction->setErrorStatus();
1158                                             return;
1159                                         }
1160 
1161                                         crow::connections::systemBus
1162                                             ->async_send(
1163                                                 m,
1164                                                 [transaction](
1165                                                     boost::system::error_code
1166                                                         ec,
1167                                                     sdbusplus::message::message
1168                                                         &m) {
1169                                                     BMCWEB_LOG_DEBUG << "sent";
1170                                                     if (ec)
1171                                                     {
1172                                                         transaction->res
1173                                                             .jsonValue
1174                                                                 ["status"] =
1175                                                             "error";
1176                                                         transaction->res
1177                                                             .jsonValue
1178                                                                 ["message"] =
1179                                                             ec.message();
1180                                                     }
1181                                                 });
1182                                     }
1183                                 }
1184                                 propNode =
1185                                     propNode->NextSiblingElement("property");
1186                             }
1187                             ifaceNode =
1188                                 ifaceNode->NextSiblingElement("interface");
1189                         }
1190                     },
1191                     connectionName, transaction->objectPath,
1192                     "org.freedesktop.DBus.Introspectable", "Introspect");
1193             }
1194         },
1195         "xyz.openbmc_project.ObjectMapper",
1196         "/xyz/openbmc_project/object_mapper",
1197         "xyz.openbmc_project.ObjectMapper", "GetObject",
1198         transaction->objectPath, std::array<std::string, 0>());
1199 }
1200 
1201 inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1202                           std::string &objectPath)
1203 {
1204 
1205     // If accessing a single attribute, fill in and update objectPath,
1206     // otherwise leave destProperty blank
1207     std::string destProperty = "";
1208     const char *attrSeperator = "/attr/";
1209     size_t attrPosition = objectPath.find(attrSeperator);
1210     if (attrPosition != objectPath.npos)
1211     {
1212         destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1213                                          objectPath.length());
1214         objectPath = objectPath.substr(0, attrPosition);
1215     }
1216 
1217     if (req.method() == "POST"_method)
1218     {
1219         constexpr const char *actionSeperator = "/action/";
1220         size_t actionPosition = objectPath.find(actionSeperator);
1221         if (actionPosition != objectPath.npos)
1222         {
1223             std::string postProperty =
1224                 objectPath.substr((actionPosition + strlen(actionSeperator)),
1225                                   objectPath.length());
1226             objectPath = objectPath.substr(0, actionPosition);
1227             handleAction(req, res, objectPath, postProperty);
1228             return;
1229         }
1230     }
1231     else if (req.method() == "GET"_method)
1232     {
1233         if (boost::ends_with(objectPath, "/enumerate"))
1234         {
1235             objectPath.erase(objectPath.end() - sizeof("enumerate"),
1236                              objectPath.end());
1237             handleEnumerate(res, objectPath);
1238         }
1239         else if (boost::ends_with(objectPath, "/list"))
1240         {
1241             objectPath.erase(objectPath.end() - sizeof("list"),
1242                              objectPath.end());
1243             handleList(res, objectPath);
1244         }
1245         else
1246         {
1247             // Trim any trailing "/" at the end
1248             if (boost::ends_with(objectPath, "/"))
1249             {
1250                 objectPath.pop_back();
1251                 handleList(res, objectPath, 1);
1252             }
1253             else
1254             {
1255                 handleGet(res, objectPath, destProperty);
1256             }
1257         }
1258         return;
1259     }
1260     else if (req.method() == "PUT"_method)
1261     {
1262         handlePut(req, res, objectPath, destProperty);
1263         return;
1264     }
1265 
1266     res.result(boost::beast::http::status::method_not_allowed);
1267     res.end();
1268 }
1269 
1270 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1271 {
1272     BMCWEB_ROUTE(app, "/bus/")
1273         .methods("GET"_method)(
1274             [](const crow::Request &req, crow::Response &res) {
1275                 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1276                                  {"status", "ok"}};
1277                 res.end();
1278             });
1279 
1280     BMCWEB_ROUTE(app, "/bus/system/")
1281         .methods("GET"_method)(
1282             [](const crow::Request &req, crow::Response &res) {
1283                 auto myCallback = [&res](const boost::system::error_code ec,
1284                                          std::vector<std::string> &names) {
1285                     if (ec)
1286                     {
1287                         BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1288                         res.result(
1289                             boost::beast::http::status::internal_server_error);
1290                     }
1291                     else
1292                     {
1293                         std::sort(names.begin(), names.end());
1294                         res.jsonValue = {{"status", "ok"}};
1295                         auto &objectsSub = res.jsonValue["objects"];
1296                         for (auto &name : names)
1297                         {
1298                             objectsSub.push_back({{"name", name}});
1299                         }
1300                     }
1301                     res.end();
1302                 };
1303                 crow::connections::systemBus->async_method_call(
1304                     std::move(myCallback), "org.freedesktop.DBus", "/",
1305                     "org.freedesktop.DBus", "ListNames");
1306             });
1307 
1308     BMCWEB_ROUTE(app, "/list/")
1309         .methods("GET"_method)(
1310             [](const crow::Request &req, crow::Response &res) {
1311                 handleList(res, "/");
1312             });
1313 
1314     BMCWEB_ROUTE(app, "/xyz/<path>")
1315         .methods("GET"_method, "PUT"_method, "POST"_method)(
1316             [](const crow::Request &req, crow::Response &res,
1317                const std::string &path) {
1318                 std::string objectPath = "/xyz/" + path;
1319                 handleDBusUrl(req, res, objectPath);
1320             });
1321 
1322     BMCWEB_ROUTE(app, "/org/<path>")
1323         .methods("GET"_method, "PUT"_method, "POST"_method)(
1324             [](const crow::Request &req, crow::Response &res,
1325                const std::string &path) {
1326                 std::string objectPath = "/org/" + path;
1327                 handleDBusUrl(req, res, objectPath);
1328             });
1329 
1330     BMCWEB_ROUTE(app, "/download/dump/<str>/")
1331         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1332                                   const std::string &dumpId) {
1333             std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1334             if (!std::regex_match(dumpId, validFilename))
1335             {
1336                 res.result(boost::beast::http::status::not_found);
1337                 res.end();
1338                 return;
1339             }
1340             std::experimental::filesystem::path loc(
1341                 "/var/lib/phosphor-debug-collector/dumps");
1342 
1343             loc += dumpId;
1344 
1345             if (!std::experimental::filesystem::exists(loc) ||
1346                 !std::experimental::filesystem::is_directory(loc))
1347             {
1348                 res.result(boost::beast::http::status::not_found);
1349                 res.end();
1350                 return;
1351             }
1352             std::experimental::filesystem::directory_iterator files(loc);
1353             for (auto &file : files)
1354             {
1355                 std::ifstream readFile(file.path());
1356                 if (readFile.good())
1357                 {
1358                     continue;
1359                 }
1360                 res.addHeader("Content-Type", "application/octet-stream");
1361                 res.body() = {std::istreambuf_iterator<char>(readFile),
1362                               std::istreambuf_iterator<char>()};
1363                 res.end();
1364             }
1365             res.result(boost::beast::http::status::not_found);
1366             res.end();
1367             return;
1368         });
1369 
1370     BMCWEB_ROUTE(app, "/bus/system/<str>/")
1371         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1372                                   const std::string &Connection) {
1373             introspectObjects(Connection, "/",
1374                               std::make_shared<bmcweb::AsyncResp>(res));
1375         });
1376 
1377     BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1378         .methods("GET"_method,
1379                  "POST"_method)([](const crow::Request &req,
1380                                    crow::Response &res,
1381                                    const std::string &processName,
1382                                    const std::string &requestedPath) {
1383             std::vector<std::string> strs;
1384             boost::split(strs, requestedPath, boost::is_any_of("/"));
1385             std::string objectPath;
1386             std::string interfaceName;
1387             std::string methodName;
1388             auto it = strs.begin();
1389             if (it == strs.end())
1390             {
1391                 objectPath = "/";
1392             }
1393             while (it != strs.end())
1394             {
1395                 // Check if segment contains ".".  If it does, it must be an
1396                 // interface
1397                 if (it->find(".") != std::string::npos)
1398                 {
1399                     break;
1400                     // This check is neccesary as the trailing slash gets parsed
1401                     // as part of our <path> specifier above, which causes the
1402                     // normal trailing backslash redirector to fail.
1403                 }
1404                 else if (!it->empty())
1405                 {
1406                     objectPath += "/" + *it;
1407                 }
1408                 it++;
1409             }
1410             if (it != strs.end())
1411             {
1412                 interfaceName = *it;
1413                 it++;
1414 
1415                 // after interface, we might have a method name
1416                 if (it != strs.end())
1417                 {
1418                     methodName = *it;
1419                     it++;
1420                 }
1421             }
1422             if (it != strs.end())
1423             {
1424                 // if there is more levels past the method name, something went
1425                 // wrong, return not found
1426                 res.result(boost::beast::http::status::not_found);
1427                 res.end();
1428                 return;
1429             }
1430             if (interfaceName.empty())
1431             {
1432                 crow::connections::systemBus->async_method_call(
1433                     [&, processName,
1434                      objectPath](const boost::system::error_code ec,
1435                                  const std::string &introspect_xml) {
1436                         if (ec)
1437                         {
1438                             BMCWEB_LOG_ERROR
1439                                 << "Introspect call failed with error: "
1440                                 << ec.message()
1441                                 << " on process: " << processName
1442                                 << " path: " << objectPath << "\n";
1443                             return;
1444                         }
1445                         tinyxml2::XMLDocument doc;
1446 
1447                         doc.Parse(introspect_xml.c_str());
1448                         tinyxml2::XMLNode *pRoot =
1449                             doc.FirstChildElement("node");
1450                         if (pRoot == nullptr)
1451                         {
1452                             BMCWEB_LOG_ERROR << "XML document failed to parse "
1453                                              << processName << " " << objectPath
1454                                              << "\n";
1455                             res.jsonValue = {{"status", "XML parse error"}};
1456                             res.result(boost::beast::http::status::
1457                                            internal_server_error);
1458                             return;
1459                         }
1460 
1461                         BMCWEB_LOG_DEBUG << introspect_xml;
1462                         res.jsonValue = {{"status", "ok"},
1463                                          {"bus_name", processName},
1464                                          {"object_path", objectPath}};
1465                         nlohmann::json &interfacesArray =
1466                             res.jsonValue["interfaces"];
1467                         interfacesArray = nlohmann::json::array();
1468                         tinyxml2::XMLElement *interface =
1469                             pRoot->FirstChildElement("interface");
1470 
1471                         while (interface != nullptr)
1472                         {
1473                             const char *ifaceName =
1474                                 interface->Attribute("name");
1475                             if (ifaceName != nullptr)
1476                             {
1477                                 interfacesArray.push_back(
1478                                     {{"name", ifaceName}});
1479                             }
1480 
1481                             interface =
1482                                 interface->NextSiblingElement("interface");
1483                         }
1484 
1485                         res.end();
1486                     },
1487                     processName, objectPath,
1488                     "org.freedesktop.DBus.Introspectable", "Introspect");
1489             }
1490             else if (methodName.empty())
1491             {
1492                 crow::connections::systemBus->async_method_call(
1493                     [&, processName, objectPath,
1494                      interfaceName{std::move(interfaceName)}](
1495                         const boost::system::error_code ec,
1496                         const std::string &introspect_xml) {
1497                         if (ec)
1498                         {
1499                             BMCWEB_LOG_ERROR
1500                                 << "Introspect call failed with error: "
1501                                 << ec.message()
1502                                 << " on process: " << processName
1503                                 << " path: " << objectPath << "\n";
1504                         }
1505                         else
1506                         {
1507                             tinyxml2::XMLDocument doc;
1508 
1509                             doc.Parse(introspect_xml.c_str());
1510                             tinyxml2::XMLNode *pRoot =
1511                                 doc.FirstChildElement("node");
1512                             if (pRoot == nullptr)
1513                             {
1514                                 BMCWEB_LOG_ERROR
1515                                     << "XML document failed to parse "
1516                                     << processName << " " << objectPath << "\n";
1517                                 res.result(boost::beast::http::status::
1518                                                internal_server_error);
1519                             }
1520                             else
1521                             {
1522                                 tinyxml2::XMLElement *node =
1523                                     pRoot->FirstChildElement("node");
1524 
1525                                 // if we know we're the only call, build the
1526                                 // json directly
1527                                 tinyxml2::XMLElement *interface =
1528                                     pRoot->FirstChildElement("interface");
1529 
1530                                 res.jsonValue = {
1531                                     {"status", "ok"},
1532                                     {"bus_name", processName},
1533                                     {"interface", interfaceName},
1534                                     {"object_path", objectPath},
1535                                     {"properties", nlohmann::json::object()}};
1536 
1537                                 nlohmann::json &methodsArray =
1538                                     res.jsonValue["methods"];
1539                                 methodsArray = nlohmann::json::array();
1540 
1541                                 nlohmann::json &signalsArray =
1542                                     res.jsonValue["signals"];
1543                                 signalsArray = nlohmann::json::array();
1544 
1545                                 while (interface != nullptr)
1546                                 {
1547                                     const char *ifaceName =
1548                                         interface->Attribute("name");
1549 
1550                                     if (ifaceName != nullptr &&
1551                                         ifaceName == interfaceName)
1552                                     {
1553                                         tinyxml2::XMLElement *methods =
1554                                             interface->FirstChildElement(
1555                                                 "method");
1556                                         while (methods != nullptr)
1557                                         {
1558                                             nlohmann::json argsArray =
1559                                                 nlohmann::json::array();
1560                                             tinyxml2::XMLElement *arg =
1561                                                 methods->FirstChildElement(
1562                                                     "arg");
1563                                             while (arg != nullptr)
1564                                             {
1565                                                 nlohmann::json thisArg;
1566                                                 for (const char *fieldName :
1567                                                      std::array<const char *,
1568                                                                 3>{"name",
1569                                                                    "direction",
1570                                                                    "type"})
1571                                                 {
1572                                                     const char *fieldValue =
1573                                                         arg->Attribute(
1574                                                             fieldName);
1575                                                     if (fieldValue != nullptr)
1576                                                     {
1577                                                         thisArg[fieldName] =
1578                                                             fieldValue;
1579                                                     }
1580                                                 }
1581                                                 argsArray.push_back(
1582                                                     std::move(thisArg));
1583                                                 arg = arg->NextSiblingElement(
1584                                                     "arg");
1585                                             }
1586 
1587                                             const char *name =
1588                                                 methods->Attribute("name");
1589                                             if (name != nullptr)
1590                                             {
1591                                                 methodsArray.push_back(
1592                                                     {{"name", name},
1593                                                      {"uri", "/bus/system/" +
1594                                                                  processName +
1595                                                                  objectPath +
1596                                                                  "/" +
1597                                                                  interfaceName +
1598                                                                  "/" + name},
1599                                                      {"args", argsArray}});
1600                                             }
1601                                             methods =
1602                                                 methods->NextSiblingElement(
1603                                                     "method");
1604                                         }
1605                                         tinyxml2::XMLElement *signals =
1606                                             interface->FirstChildElement(
1607                                                 "signal");
1608                                         while (signals != nullptr)
1609                                         {
1610                                             nlohmann::json argsArray =
1611                                                 nlohmann::json::array();
1612 
1613                                             tinyxml2::XMLElement *arg =
1614                                                 signals->FirstChildElement(
1615                                                     "arg");
1616                                             while (arg != nullptr)
1617                                             {
1618                                                 const char *name =
1619                                                     arg->Attribute("name");
1620                                                 const char *type =
1621                                                     arg->Attribute("type");
1622                                                 if (name != nullptr &&
1623                                                     type != nullptr)
1624                                                 {
1625                                                     argsArray.push_back({
1626                                                         {"name", name},
1627                                                         {"type", type},
1628                                                     });
1629                                                 }
1630                                                 arg = arg->NextSiblingElement(
1631                                                     "arg");
1632                                             }
1633                                             const char *name =
1634                                                 signals->Attribute("name");
1635                                             if (name != nullptr)
1636                                             {
1637                                                 signalsArray.push_back(
1638                                                     {{"name", name},
1639                                                      {"args", argsArray}});
1640                                             }
1641 
1642                                             signals =
1643                                                 signals->NextSiblingElement(
1644                                                     "signal");
1645                                         }
1646 
1647                                         break;
1648                                     }
1649 
1650                                     interface = interface->NextSiblingElement(
1651                                         "interface");
1652                                 }
1653                                 if (interface == nullptr)
1654                                 {
1655                                     // if we got to the end of the list and
1656                                     // never found a match, throw 404
1657                                     res.result(
1658                                         boost::beast::http::status::not_found);
1659                                 }
1660                             }
1661                         }
1662                         res.end();
1663                     },
1664                     processName, objectPath,
1665                     "org.freedesktop.DBus.Introspectable", "Introspect");
1666             }
1667             else
1668             {
1669                 if (req.method() != "POST"_method)
1670                 {
1671                     res.result(boost::beast::http::status::not_found);
1672                     res.end();
1673                     return;
1674                 }
1675 
1676                 nlohmann::json requestDbusData =
1677                     nlohmann::json::parse(req.body, nullptr, false);
1678 
1679                 if (requestDbusData.is_discarded())
1680                 {
1681                     res.result(boost::beast::http::status::bad_request);
1682                     res.end();
1683                     return;
1684                 }
1685                 if (!requestDbusData.is_array())
1686                 {
1687                     res.result(boost::beast::http::status::bad_request);
1688                     res.end();
1689                     return;
1690                 }
1691                 auto transaction = std::make_shared<InProgressActionData>(res);
1692 
1693                 transaction->path = objectPath;
1694                 transaction->methodName = methodName;
1695                 transaction->arguments = std::move(requestDbusData);
1696 
1697                 findActionOnInterface(transaction, processName);
1698             }
1699         });
1700 }
1701 } // namespace openbmc_mapper
1702 } // namespace crow
1703