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