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