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