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