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