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