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