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