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