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