blob: 7839e65b9949905e0aca5bb6bd73b0a9433d811e [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous911ac312017-08-15 09:37:42 -070016#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
Ed Tanouse3cb5a32018-08-08 14:16:49 -070019#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <boost/algorithm/string.hpp>
21#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070024#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070025#include <fstream>
Ramesh Iyyard9207042019-07-05 08:04:42 -050026#include <regex>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070027#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace openbmc_mapper
32{
Ed Tanousba9f9a62017-10-11 16:40:35 -070033
Matt Spinler3ae4ba72018-12-05 14:01:22 -060034using GetSubTreeType = std::vector<
35 std::pair<std::string,
36 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37
Matt Spinler2ae60092018-12-06 10:35:36 -060038const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060039const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060040const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060041const std::string forbiddenMsg = "403 Forbidden";
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -060042const std::string methodFailedMsg = "500 Method Call Failed";
Matt Spinler16caaee2019-01-15 11:40:34 -060043const std::string methodOutputFailedMsg = "500 Method Output Error";
Matt Spinler6db06242018-12-11 11:21:22 -060044
Matt Spinler2ae60092018-12-06 10:35:36 -060045const std::string notFoundDesc =
46 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060047const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060048const std::string noJsonDesc = "No JSON object could be decoded";
49const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060050const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060051const std::string forbiddenPropDesc =
52 "The specified property cannot be created";
53const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060054
55void setErrorResponse(crow::Response &res, boost::beast::http::status result,
56 const std::string &desc, const std::string &msg)
57{
58 res.result(result);
59 res.jsonValue = {{"data", {{"description", desc}}},
60 {"message", msg},
61 {"status", "error"}};
62}
63
Ed Tanouse3cb5a32018-08-08 14:16:49 -070064void introspectObjects(const std::string &processName,
65 const std::string &objectPath,
66 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070067{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070068 if (transaction->res.jsonValue.is_null())
69 {
70 transaction->res.jsonValue = {{"status", "ok"},
71 {"bus_name", processName},
72 {"objects", nlohmann::json::array()}};
73 }
74
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070076 [transaction, processName{std::string(processName)},
77 objectPath{std::string(objectPath)}](
78 const boost::system::error_code ec,
79 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 if (ec)
81 {
82 BMCWEB_LOG_ERROR
83 << "Introspect call failed with error: " << ec.message()
84 << " on process: " << processName << " path: " << objectPath
85 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070086 return;
87 }
88 transaction->res.jsonValue["objects"].push_back(
89 {{"path", objectPath}});
90
91 tinyxml2::XMLDocument doc;
92
93 doc.Parse(introspect_xml.c_str());
94 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
95 if (pRoot == nullptr)
96 {
97 BMCWEB_LOG_ERROR << "XML document failed to parse "
98 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070099 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 else
101 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
103 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700105 const char *childPath = node->Attribute("name");
106 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 std::string newpath;
109 if (objectPath != "/")
110 {
111 newpath += objectPath;
112 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700113 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700115 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700117
118 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 }
120 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700122 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700124}
Ed Tanous64530012018-02-06 17:08:16 -0800125
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600126void getPropertiesForEnumerate(const std::string &objectPath,
127 const std::string &service,
128 const std::string &interface,
129 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
130{
131 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
132 << service << " " << interface;
133
134 crow::connections::systemBus->async_method_call(
135 [asyncResp, objectPath, service,
136 interface](const boost::system::error_code ec,
137 const std::vector<
138 std::pair<std::string, dbus::utility::DbusVariantType>>
139 &propertiesList) {
140 if (ec)
141 {
142 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
143 << interface << " service " << service
144 << " failed with code " << ec;
145 return;
146 }
147
148 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
149 nlohmann::json &objectJson = dataJson[objectPath];
150 if (objectJson.is_null())
151 {
152 objectJson = nlohmann::json::object();
153 }
154
155 for (const auto &[name, value] : propertiesList)
156 {
157 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800158 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
159 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600160 }
161 },
162 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
163 interface);
164}
165
166// Find any results that weren't picked up by ObjectManagers, to be
167// called after all ObjectManagers are searched for and called.
168void findRemainingObjectsForEnumerate(
169 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
170 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
171{
172 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
173 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
174
175 for (const auto &[path, interface_map] : *subtree)
176 {
177 if (path == objectPath)
178 {
179 // An enumerate does not return the target path's properties
180 continue;
181 }
182 if (dataJson.find(path) == dataJson.end())
183 {
184 for (const auto &[service, interfaces] : interface_map)
185 {
186 for (const auto &interface : interfaces)
187 {
188 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
189 {
190 getPropertiesForEnumerate(path, service, interface,
191 asyncResp);
192 }
193 }
194 }
195 }
196 }
197}
198
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600199struct InProgressEnumerateData
200{
201 InProgressEnumerateData(const std::string &objectPath,
202 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
203 objectPath(objectPath),
204 asyncResp(asyncResp)
205 {
206 }
207
208 ~InProgressEnumerateData()
209 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600210 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600211 }
212
213 const std::string objectPath;
214 std::shared_ptr<GetSubTreeType> subtree;
215 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
216};
217
218void getManagedObjectsForEnumerate(
219 const std::string &object_name, const std::string &object_manager_path,
220 const std::string &connection_name,
221 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222{
Ed Tanous049a0512018-11-01 13:58:42 -0700223 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
224 << " object_manager_path " << object_manager_path
225 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600227 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700228 connection_name](const boost::system::error_code ec,
229 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 if (ec)
231 {
Ed Tanous049a0512018-11-01 13:58:42 -0700232 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600233 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700234 << " failed with code " << ec;
235 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700236 }
Ed Tanous64530012018-02-06 17:08:16 -0800237
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600238 nlohmann::json &dataJson =
239 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700240
241 for (const auto &objectPath : objects)
242 {
243 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 {
Ed Tanous049a0512018-11-01 13:58:42 -0700245 BMCWEB_LOG_DEBUG << "Reading object "
246 << objectPath.first.str;
247 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 if (objectJson.is_null())
249 {
250 objectJson = nlohmann::json::object();
251 }
252 for (const auto &interface : objectPath.second)
253 {
254 for (const auto &property : interface.second)
255 {
256 nlohmann::json &propertyJson =
257 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800258 std::visit([&propertyJson](
259 auto &&val) { propertyJson = val; },
260 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 }
262 }
263 }
Ed Tanous049a0512018-11-01 13:58:42 -0700264 for (const auto &interface : objectPath.second)
265 {
266 if (interface.first == "org.freedesktop.DBus.ObjectManager")
267 {
268 getManagedObjectsForEnumerate(
269 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600270 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700271 }
272 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 }
274 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700275 connection_name, object_manager_path,
276 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
277}
278
279void findObjectManagerPathForEnumerate(
280 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600281 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700282{
Ed Tanous049a0512018-11-01 13:58:42 -0700283 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
284 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700285 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600286 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700287 const boost::system::error_code ec,
288 const boost::container::flat_map<
289 std::string, boost::container::flat_map<
290 std::string, std::vector<std::string>>>
291 &objects) {
292 if (ec)
293 {
Ed Tanous049a0512018-11-01 13:58:42 -0700294 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
295 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700296 return;
297 }
298
Ed Tanousf254ba72018-10-12 13:40:35 -0700299 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700300 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700301 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 {
303 if (connectionGroup.first == connection_name)
304 {
305 // Found the object manager path for this resource.
306 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700307 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600308 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700309 return;
310 }
311 }
312 }
313 },
314 "xyz.openbmc_project.ObjectMapper",
315 "/xyz/openbmc_project/object_mapper",
316 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
317 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700318}
Ed Tanous64530012018-02-06 17:08:16 -0800319
Ed Tanous7c091622019-05-23 11:42:36 -0700320// Uses GetObject to add the object info about the target /enumerate path to
321// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600322// target path, and then continues on enumerating the rest of the tree.
323void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
324{
325 using GetObjectType =
326 std::vector<std::pair<std::string, std::vector<std::string>>>;
327
328 crow::connections::systemBus->async_method_call(
329 [transaction](const boost::system::error_code ec,
330 const GetObjectType &objects) {
331 if (ec)
332 {
333 BMCWEB_LOG_ERROR << "GetObject for path "
334 << transaction->objectPath
335 << " failed with code " << ec;
336 return;
337 }
338
339 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
340 << " has " << objects.size() << " entries";
341 if (!objects.empty())
342 {
343 transaction->subtree->emplace_back(transaction->objectPath,
344 objects);
345 }
346
347 // Map indicating connection name, and the path where the object
348 // manager exists
349 boost::container::flat_map<std::string, std::string> connections;
350
351 for (const auto &object : *(transaction->subtree))
352 {
353 for (const auto &connection : object.second)
354 {
355 std::string &objectManagerPath =
356 connections[connection.first];
357 for (const auto &interface : connection.second)
358 {
359 BMCWEB_LOG_DEBUG << connection.first
360 << " has interface " << interface;
361 if (interface == "org.freedesktop.DBus.ObjectManager")
362 {
363 BMCWEB_LOG_DEBUG << "found object manager path "
364 << object.first;
365 objectManagerPath = object.first;
366 }
367 }
368 }
369 }
370 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
371
372 for (const auto &connection : connections)
373 {
Ed Tanous7c091622019-05-23 11:42:36 -0700374 // If we already know where the object manager is, we don't
375 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600376 // getManagedObjects
377 if (!connection.second.empty())
378 {
379 getManagedObjectsForEnumerate(
380 transaction->objectPath, connection.second,
381 connection.first, transaction);
382 }
383 else
384 {
Ed Tanous7c091622019-05-23 11:42:36 -0700385 // otherwise we need to find the object manager path
386 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600387 findObjectManagerPathForEnumerate(
388 transaction->objectPath, connection.first, transaction);
389 }
390 }
391 },
392 "xyz.openbmc_project.ObjectMapper",
393 "/xyz/openbmc_project/object_mapper",
394 "xyz.openbmc_project.ObjectMapper", "GetObject",
395 transaction->objectPath, std::array<const char *, 0>());
396}
Ed Tanous64530012018-02-06 17:08:16 -0800397
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700398// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399struct InProgressActionData
400{
401 InProgressActionData(crow::Response &res) : res(res){};
402 ~InProgressActionData()
403 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600404 // Methods could have been called across different owners
405 // and interfaces, where some calls failed and some passed.
406 //
407 // The rules for this are:
408 // * if no method was called - error
409 // * if a method failed and none passed - error
410 // (converse: if at least one method passed - OK)
411 // * for the method output:
412 // * if output processing didn't fail, return the data
413
414 // Only deal with method returns if nothing failed earlier
415 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600417 if (!methodPassed)
418 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500419 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600420 {
421 setErrorResponse(res, boost::beast::http::status::not_found,
422 methodNotFoundDesc, notFoundMsg);
423 }
424 }
425 else
426 {
427 if (outputFailed)
428 {
429 setErrorResponse(
430 res, boost::beast::http::status::internal_server_error,
431 "Method output failure", methodOutputFailedMsg);
432 }
433 else
434 {
435 res.jsonValue = {{"status", "ok"},
436 {"message", "200 OK"},
437 {"data", methodResponse}};
438 }
439 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600441
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700443 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700444
Matt Spinler6db06242018-12-11 11:21:22 -0600445 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600447 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
448 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 }
450 crow::Response &res;
451 std::string path;
452 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600453 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600454 bool methodPassed = false;
455 bool methodFailed = false;
456 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600457 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600458 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700460};
461
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462std::vector<std::string> dbusArgSplit(const std::string &string)
463{
464 std::vector<std::string> ret;
465 if (string.empty())
466 {
467 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700468 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469 ret.push_back("");
470 int containerDepth = 0;
471
472 for (std::string::const_iterator character = string.begin();
473 character != string.end(); character++)
474 {
475 ret.back() += *character;
476 switch (*character)
477 {
478 case ('a'):
479 break;
480 case ('('):
481 case ('{'):
482 containerDepth++;
483 break;
484 case ('}'):
485 case (')'):
486 containerDepth--;
487 if (containerDepth == 0)
488 {
489 if (character + 1 != string.end())
490 {
491 ret.push_back("");
492 }
493 }
494 break;
495 default:
496 if (containerDepth == 0)
497 {
498 if (character + 1 != string.end())
499 {
500 ret.push_back("");
501 }
502 }
503 break;
504 }
505 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600506
507 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700508}
509
Ed Tanousd76323e2018-08-07 14:35:40 -0700510int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 const nlohmann::json &input_json)
512{
513 int r = 0;
514 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
515 << " to type: " << arg_type;
516 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700517
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 // Assume a single object for now.
519 const nlohmann::json *j = &input_json;
520 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700521
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700522 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 {
524 // If we are decoding multiple objects, grab the pointer to the
525 // iterator, and increment it for the next loop
526 if (argTypes.size() > 1)
527 {
528 if (jIt == input_json.end())
529 {
530 return -2;
531 }
532 j = &*jIt;
533 jIt++;
534 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700535 const int64_t *intValue = j->get_ptr<const int64_t *>();
536 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
537 const std::string *stringValue = j->get_ptr<const std::string *>();
538 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 const bool *b = j->get_ptr<const bool *>();
540 int64_t v = 0;
541 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700542
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 // Do some basic type conversions that make sense. uint can be
544 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700545 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700547 v = static_cast<int64_t>(*uintValue);
548 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700550 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 d = static_cast<double>(*uintValue);
553 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700555 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700557 d = static_cast<double>(*intValue);
558 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700559 }
560
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700563 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 {
565 return -1;
566 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000567 r = sd_bus_message_append_basic(m, argCode[0],
568 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 if (r < 0)
570 {
571 return r;
572 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700573 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700574 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700576 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 {
578 return -1;
579 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500580 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
581 (*intValue > std::numeric_limits<int32_t>::max()))
582 {
583 return -ERANGE;
584 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700585 int32_t i = static_cast<int32_t>(*intValue);
586 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 if (r < 0)
588 {
589 return r;
590 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700591 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700592 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593 {
594 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700595 int boolInt = false;
596 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500598 if (*intValue == 1)
599 {
600 boolInt = true;
601 }
602 else if (*intValue == 0)
603 {
604 boolInt = false;
605 }
606 else
607 {
608 return -ERANGE;
609 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 }
611 else if (b != nullptr)
612 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600613 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700617 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 }
619 else
620 {
621 return -1;
622 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700623 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 if (r < 0)
625 {
626 return r;
627 }
628 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700629 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700631 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 {
633 return -1;
634 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500635 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
636 (*intValue > std::numeric_limits<int16_t>::max()))
637 {
638 return -ERANGE;
639 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700640 int16_t n = static_cast<int16_t>(*intValue);
641 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 if (r < 0)
643 {
644 return r;
645 }
646 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700647 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700649 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
651 return -1;
652 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500653 if ((*intValue < std::numeric_limits<int64_t>::lowest()) ||
654 (*intValue > std::numeric_limits<int64_t>::max()))
655 {
656 return -ERANGE;
657 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700658 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 if (r < 0)
660 {
661 return r;
662 }
663 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700664 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700666 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 {
668 return -1;
669 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500670 if ((*uintValue < std::numeric_limits<uint8_t>::lowest()) ||
671 (*uintValue > std::numeric_limits<uint8_t>::max()))
672 {
673 return -ERANGE;
674 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700675 uint8_t y = static_cast<uint8_t>(*uintValue);
676 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700678 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 {
682 return -1;
683 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500684 if ((*uintValue < std::numeric_limits<uint16_t>::lowest()) ||
685 (*uintValue > std::numeric_limits<uint16_t>::max()))
686 {
687 return -ERANGE;
688 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 uint16_t q = static_cast<uint16_t>(*uintValue);
690 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700692 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700694 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700695 {
696 return -1;
697 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500698 if ((*uintValue < std::numeric_limits<uint32_t>::lowest()) ||
699 (*uintValue > std::numeric_limits<uint32_t>::max()))
700 {
701 return -ERANGE;
702 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700703 uint32_t u = static_cast<uint32_t>(*uintValue);
704 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700706 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700708 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 {
710 return -1;
711 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500712 if ((*uintValue < std::numeric_limits<uint64_t>::lowest()) ||
713 (*uintValue > std::numeric_limits<uint64_t>::max()))
714 {
715 return -ERANGE;
716 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700717 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700719 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700720 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500721 if (doubleValue == nullptr)
722 {
723 return -1;
724 }
725 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
726 (*doubleValue > std::numeric_limits<double>::max()))
727 {
728 return -ERANGE;
729 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700730 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700732 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700734 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700736 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 if (r < 0)
738 {
739 return r;
740 }
741
742 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
743 ++it)
744 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700745 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700746 if (r < 0)
747 {
748 return r;
749 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 }
751 sd_bus_message_close_container(m);
752 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700753 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700755 std::string containedType = argCode.substr(1);
756 BMCWEB_LOG_DEBUG << "variant type: " << argCode
757 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 if (r < 0)
761 {
762 return r;
763 }
764
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700765 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 if (r < 0)
767 {
768 return r;
769 }
770
771 r = sd_bus_message_close_container(m);
772 if (r < 0)
773 {
774 return r;
775 }
776 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700777 else if (boost::starts_with(argCode, "(") &&
778 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700780 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700782 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800783 if (r < 0)
784 {
785 return r;
786 }
787
Ed Tanous1abe55e2018-09-05 08:30:59 -0700788 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000789 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 {
791 if (it == j->end())
792 {
793 return -1;
794 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000795 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 if (r < 0)
797 {
798 return r;
799 }
800 it++;
801 }
802 r = sd_bus_message_close_container(m);
803 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700804 else if (boost::starts_with(argCode, "{") &&
805 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700807 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700809 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800810 if (r < 0)
811 {
812 return r;
813 }
814
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700815 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 if (codes.size() != 2)
817 {
818 return -1;
819 }
820 const std::string &key_type = codes[0];
821 const std::string &value_type = codes[1];
822 for (auto it : j->items())
823 {
824 r = convertJsonToDbus(m, key_type, it.key());
825 if (r < 0)
826 {
827 return r;
828 }
829
830 r = convertJsonToDbus(m, value_type, it.value());
831 if (r < 0)
832 {
833 return r;
834 }
835 }
836 r = sd_bus_message_close_container(m);
837 }
838 else
839 {
840 return -2;
841 }
842 if (r < 0)
843 {
844 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700845 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700846
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 if (argTypes.size() > 1)
848 {
849 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700850 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700851 }
Matt Spinler127ea542019-01-14 11:04:28 -0600852
853 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700854}
855
Matt Spinlerd22a7132019-01-14 12:14:30 -0600856template <typename T>
857int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
858 nlohmann::json &data)
859{
860 T value;
861
862 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
863 if (r < 0)
864 {
865 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
866 << " failed!";
867 return r;
868 }
869
870 data = value;
871 return 0;
872}
873
Matt Spinler16caaee2019-01-15 11:40:34 -0600874int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600875 sdbusplus::message::message &m, nlohmann::json &response);
876
877int readDictEntryFromMessage(const std::string &typeCode,
878 sdbusplus::message::message &m,
879 nlohmann::json &object)
880{
881 std::vector<std::string> types = dbusArgSplit(typeCode);
882 if (types.size() != 2)
883 {
884 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
885 << types.size();
886 return -1;
887 }
888
889 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
890 typeCode.c_str());
891 if (r < 0)
892 {
893 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
894 return r;
895 }
896
897 nlohmann::json key;
898 r = convertDBusToJSON(types[0], m, key);
899 if (r < 0)
900 {
901 return r;
902 }
903
904 const std::string *keyPtr = key.get_ptr<const std::string *>();
905 if (keyPtr == nullptr)
906 {
907 // json doesn't support non-string keys. If we hit this condition,
908 // convert the result to a string so we can proceed
909 key = key.dump();
910 keyPtr = key.get_ptr<const std::string *>();
Ed Tanous7c091622019-05-23 11:42:36 -0700911 // in theory this can't fail now, but lets be paranoid about it
912 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600913 if (keyPtr == nullptr)
914 {
915 return -1;
916 }
917 }
918 nlohmann::json &value = object[*keyPtr];
919
920 r = convertDBusToJSON(types[1], m, value);
921 if (r < 0)
922 {
923 return r;
924 }
925
926 r = sd_bus_message_exit_container(m.get());
927 if (r < 0)
928 {
929 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
930 return r;
931 }
932
933 return 0;
934}
935
936int readArrayFromMessage(const std::string &typeCode,
937 sdbusplus::message::message &m, nlohmann::json &data)
938{
939 if (typeCode.size() < 2)
940 {
941 BMCWEB_LOG_ERROR << "Type code " << typeCode
942 << " too small for an array";
943 return -1;
944 }
945
946 std::string containedType = typeCode.substr(1);
947
948 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
949 containedType.c_str());
950 if (r < 0)
951 {
952 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
953 << r;
954 return r;
955 }
956
957 bool dict = boost::starts_with(containedType, "{") &&
958 boost::ends_with(containedType, "}");
959
960 if (dict)
961 {
962 // Remove the { }
963 containedType = containedType.substr(1, containedType.size() - 2);
964 data = nlohmann::json::object();
965 }
966 else
967 {
968 data = nlohmann::json::array();
969 }
970
971 while (true)
972 {
973 r = sd_bus_message_at_end(m.get(), false);
974 if (r < 0)
975 {
976 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
977 return r;
978 }
979
980 if (r > 0)
981 {
982 break;
983 }
984
985 // Dictionaries are only ever seen in an array
986 if (dict)
987 {
988 r = readDictEntryFromMessage(containedType, m, data);
989 if (r < 0)
990 {
991 return r;
992 }
993 }
994 else
995 {
996 data.push_back(nlohmann::json());
997
998 r = convertDBusToJSON(containedType, m, data.back());
999 if (r < 0)
1000 {
1001 return r;
1002 }
1003 }
1004 }
1005
1006 r = sd_bus_message_exit_container(m.get());
1007 if (r < 0)
1008 {
1009 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1010 return r;
1011 }
1012
1013 return 0;
1014}
1015
Matt Spinler75c6c672019-01-14 13:01:46 -06001016int readStructFromMessage(const std::string &typeCode,
1017 sdbusplus::message::message &m, nlohmann::json &data)
1018{
1019 if (typeCode.size() < 3)
1020 {
1021 BMCWEB_LOG_ERROR << "Type code " << typeCode
1022 << " too small for a struct";
1023 return -1;
1024 }
1025
1026 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1027 std::vector<std::string> types = dbusArgSplit(containedTypes);
1028
1029 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1030 containedTypes.c_str());
1031 if (r < 0)
1032 {
1033 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1034 << r;
1035 return r;
1036 }
1037
1038 for (const std::string &type : types)
1039 {
1040 data.push_back(nlohmann::json());
1041 r = convertDBusToJSON(type, m, data.back());
1042 if (r < 0)
1043 {
1044 return r;
1045 }
1046 }
1047
1048 r = sd_bus_message_exit_container(m.get());
1049 if (r < 0)
1050 {
1051 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1052 return r;
1053 }
1054 return 0;
1055}
1056
Matt Spinler89c19702019-01-14 13:13:00 -06001057int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1058{
1059 const char *containerType;
1060 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1061 if (r < 0)
1062 {
1063 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1064 return r;
1065 }
1066
1067 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1068 containerType);
1069 if (r < 0)
1070 {
1071 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1072 << r;
1073 return r;
1074 }
1075
1076 r = convertDBusToJSON(containerType, m, data);
1077 if (r < 0)
1078 {
1079 return r;
1080 }
1081
1082 r = sd_bus_message_exit_container(m.get());
1083 if (r < 0)
1084 {
1085 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1086 return r;
1087 }
1088
1089 return 0;
1090}
1091
Matt Spinler6df8f992019-01-14 12:47:47 -06001092int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001093 sdbusplus::message::message &m, nlohmann::json &response)
1094{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001095 int r = 0;
1096 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1097
Matt Spinlerd22a7132019-01-14 12:14:30 -06001098 for (const std::string &typeCode : returnTypes)
1099 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001100 nlohmann::json *thisElement = &response;
1101 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001102 {
1103 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001104 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001105 }
1106
1107 if (typeCode == "s")
1108 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001109 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001110 if (r < 0)
1111 {
1112 return r;
1113 }
1114 }
1115 else if (typeCode == "g")
1116 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001117 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001118 if (r < 0)
1119 {
1120 return r;
1121 }
1122 }
1123 else if (typeCode == "o")
1124 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001125 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001126 if (r < 0)
1127 {
1128 return r;
1129 }
1130 }
1131 else if (typeCode == "b")
1132 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001133 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001134 if (r < 0)
1135 {
1136 return r;
1137 }
1138
Matt Spinlerf39420c2019-01-30 12:57:18 -06001139 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001140 }
1141 else if (typeCode == "u")
1142 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001143 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001144 if (r < 0)
1145 {
1146 return r;
1147 }
1148 }
1149 else if (typeCode == "i")
1150 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001151 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001152 if (r < 0)
1153 {
1154 return r;
1155 }
1156 }
1157 else if (typeCode == "x")
1158 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001159 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001160 if (r < 0)
1161 {
1162 return r;
1163 }
1164 }
1165 else if (typeCode == "t")
1166 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001167 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001168 if (r < 0)
1169 {
1170 return r;
1171 }
1172 }
1173 else if (typeCode == "n")
1174 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001175 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001176 if (r < 0)
1177 {
1178 return r;
1179 }
1180 }
1181 else if (typeCode == "q")
1182 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001183 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001184 if (r < 0)
1185 {
1186 return r;
1187 }
1188 }
1189 else if (typeCode == "y")
1190 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001191 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001192 if (r < 0)
1193 {
1194 return r;
1195 }
1196 }
1197 else if (typeCode == "d")
1198 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001199 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001200 if (r < 0)
1201 {
1202 return r;
1203 }
1204 }
1205 else if (typeCode == "h")
1206 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001207 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001208 if (r < 0)
1209 {
1210 return r;
1211 }
1212 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001213 else if (boost::starts_with(typeCode, "a"))
1214 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001215 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001216 if (r < 0)
1217 {
1218 return r;
1219 }
1220 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001221 else if (boost::starts_with(typeCode, "(") &&
1222 boost::ends_with(typeCode, ")"))
1223 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001224 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001225 if (r < 0)
1226 {
1227 return r;
1228 }
1229 }
Matt Spinler89c19702019-01-14 13:13:00 -06001230 else if (boost::starts_with(typeCode, "v"))
1231 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001232 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001233 if (r < 0)
1234 {
1235 return r;
1236 }
1237 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001238 else
1239 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001240 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1241 return -2;
1242 }
1243 }
1244
Matt Spinler16caaee2019-01-15 11:40:34 -06001245 return 0;
1246}
1247
1248void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1249 sdbusplus::message::message &m,
1250 const std::string &returnType)
1251{
Matt Spinler39a4e392019-01-15 11:53:13 -06001252 nlohmann::json data;
1253
1254 int r = convertDBusToJSON(returnType, m, data);
1255 if (r < 0)
1256 {
1257 transaction->outputFailed = true;
1258 return;
1259 }
1260
1261 if (data.is_null())
1262 {
1263 return;
1264 }
1265
1266 if (transaction->methodResponse.is_null())
1267 {
1268 transaction->methodResponse = std::move(data);
1269 return;
1270 }
1271
1272 // If they're both dictionaries or arrays, merge into one.
1273 // Otherwise, make the results an array with every result
1274 // an entry. Could also just fail in that case, but it
1275 // seems better to get the data back somehow.
1276
1277 if (transaction->methodResponse.is_object() && data.is_object())
1278 {
1279 for (const auto &obj : data.items())
1280 {
1281 // Note: Will overwrite the data for a duplicate key
1282 transaction->methodResponse.emplace(obj.key(),
1283 std::move(obj.value()));
1284 }
1285 return;
1286 }
1287
1288 if (transaction->methodResponse.is_array() && data.is_array())
1289 {
1290 for (auto &obj : data)
1291 {
1292 transaction->methodResponse.push_back(std::move(obj));
1293 }
1294 return;
1295 }
1296
1297 if (!transaction->convertedToArray)
1298 {
1299 // They are different types. May as well turn them into an array
1300 nlohmann::json j = std::move(transaction->methodResponse);
1301 transaction->methodResponse = nlohmann::json::array();
1302 transaction->methodResponse.push_back(std::move(j));
1303 transaction->methodResponse.push_back(std::move(data));
1304 transaction->convertedToArray = true;
1305 }
1306 else
1307 {
1308 transaction->methodResponse.push_back(std::move(data));
1309 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001310}
1311
Ed Tanousd76323e2018-08-07 14:35:40 -07001312void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001313 const std::string &connectionName)
1314{
1315 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1316 << connectionName;
1317 crow::connections::systemBus->async_method_call(
1318 [transaction, connectionName{std::string(connectionName)}](
1319 const boost::system::error_code ec,
1320 const std::string &introspect_xml) {
1321 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1322 if (ec)
1323 {
1324 BMCWEB_LOG_ERROR
1325 << "Introspect call failed with error: " << ec.message()
1326 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001327 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001328 }
Matt Spinler318bd892019-01-15 09:59:20 -06001329 tinyxml2::XMLDocument doc;
1330
1331 doc.Parse(introspect_xml.data(), introspect_xml.size());
1332 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1333 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001334 {
Matt Spinler318bd892019-01-15 09:59:20 -06001335 BMCWEB_LOG_ERROR << "XML document failed to parse "
1336 << connectionName << "\n";
1337 return;
1338 }
1339 tinyxml2::XMLElement *interfaceNode =
1340 pRoot->FirstChildElement("interface");
1341 while (interfaceNode != nullptr)
1342 {
1343 const char *thisInterfaceName =
1344 interfaceNode->Attribute("name");
1345 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001346 {
Matt Spinler318bd892019-01-15 09:59:20 -06001347 if (!transaction->interfaceName.empty() &&
1348 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001349 {
Matt Spinler318bd892019-01-15 09:59:20 -06001350 interfaceNode =
1351 interfaceNode->NextSiblingElement("interface");
1352 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001353 }
Matt Spinler318bd892019-01-15 09:59:20 -06001354
1355 tinyxml2::XMLElement *methodNode =
1356 interfaceNode->FirstChildElement("method");
1357 while (methodNode != nullptr)
1358 {
1359 const char *thisMethodName =
1360 methodNode->Attribute("name");
1361 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1362 if (thisMethodName != nullptr &&
1363 thisMethodName == transaction->methodName)
1364 {
1365 BMCWEB_LOG_DEBUG
1366 << "Found method named " << thisMethodName
1367 << " on interface " << thisInterfaceName;
1368 sdbusplus::message::message m =
1369 crow::connections::systemBus->new_method_call(
1370 connectionName.c_str(),
1371 transaction->path.c_str(),
1372 thisInterfaceName,
1373 transaction->methodName.c_str());
1374
1375 tinyxml2::XMLElement *argumentNode =
1376 methodNode->FirstChildElement("arg");
1377
Matt Spinler16caaee2019-01-15 11:40:34 -06001378 std::string returnType;
1379
1380 // Find the output type
1381 while (argumentNode != nullptr)
1382 {
1383 const char *argDirection =
1384 argumentNode->Attribute("direction");
1385 const char *argType =
1386 argumentNode->Attribute("type");
1387 if (argDirection != nullptr &&
1388 argType != nullptr &&
1389 std::string(argDirection) == "out")
1390 {
1391 returnType = argType;
1392 break;
1393 }
1394 argumentNode =
1395 argumentNode->NextSiblingElement("arg");
1396 }
1397
Matt Spinler318bd892019-01-15 09:59:20 -06001398 nlohmann::json::const_iterator argIt =
1399 transaction->arguments.begin();
1400
Matt Spinler16caaee2019-01-15 11:40:34 -06001401 argumentNode = methodNode->FirstChildElement("arg");
1402
Matt Spinler318bd892019-01-15 09:59:20 -06001403 while (argumentNode != nullptr)
1404 {
1405 const char *argDirection =
1406 argumentNode->Attribute("direction");
1407 const char *argType =
1408 argumentNode->Attribute("type");
1409 if (argDirection != nullptr &&
1410 argType != nullptr &&
1411 std::string(argDirection) == "in")
1412 {
1413 if (argIt == transaction->arguments.end())
1414 {
1415 transaction->setErrorStatus(
1416 "Invalid method args");
1417 return;
1418 }
1419 if (convertJsonToDbus(m.get(),
1420 std::string(argType),
1421 *argIt) < 0)
1422 {
1423 transaction->setErrorStatus(
1424 "Invalid method arg type");
1425 return;
1426 }
1427
1428 argIt++;
1429 }
1430 argumentNode =
1431 argumentNode->NextSiblingElement("arg");
1432 }
1433
1434 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001435 m, [transaction, returnType](
1436 boost::system::error_code ec,
1437 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001438 if (ec)
1439 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001440 transaction->methodFailed = true;
Matt Spinler06b1b632019-06-18 16:09:25 -05001441 const sd_bus_error *e = m.get_error();
1442
1443 if (e)
1444 {
1445 setErrorResponse(
1446 transaction->res,
1447 boost::beast::http::status::
1448 bad_request,
1449 e->name, e->message);
1450 }
1451 else
1452 {
1453 setErrorResponse(
1454 transaction->res,
1455 boost::beast::http::status::
1456 bad_request,
1457 "Method call failed",
1458 methodFailedMsg);
1459 }
Matt Spinler318bd892019-01-15 09:59:20 -06001460 return;
1461 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001462 else
1463 {
1464 transaction->methodPassed = true;
1465 }
1466
1467 handleMethodResponse(transaction, m,
1468 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001469 });
1470 break;
1471 }
1472 methodNode = methodNode->NextSiblingElement("method");
1473 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001474 }
Matt Spinler318bd892019-01-15 09:59:20 -06001475 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001476 }
1477 },
1478 connectionName, transaction->path,
1479 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001480}
1481
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001482void handleAction(const crow::Request &req, crow::Response &res,
1483 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001484{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001485 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1486 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001487 nlohmann::json requestDbusData =
1488 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001489
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 if (requestDbusData.is_discarded())
1491 {
Matt Spinler6db06242018-12-11 11:21:22 -06001492 setErrorResponse(res, boost::beast::http::status::bad_request,
1493 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001494 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001495 return;
1496 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001497 nlohmann::json::iterator data = requestDbusData.find("data");
1498 if (data == requestDbusData.end())
1499 {
Matt Spinler6db06242018-12-11 11:21:22 -06001500 setErrorResponse(res, boost::beast::http::status::bad_request,
1501 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001502 res.end();
1503 return;
1504 }
1505
1506 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001507 {
Matt Spinler6db06242018-12-11 11:21:22 -06001508 setErrorResponse(res, boost::beast::http::status::bad_request,
1509 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001510 res.end();
1511 return;
1512 }
1513 auto transaction = std::make_shared<InProgressActionData>(res);
1514
1515 transaction->path = objectPath;
1516 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001517 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001518 crow::connections::systemBus->async_method_call(
1519 [transaction](
1520 const boost::system::error_code ec,
1521 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001522 &interfaceNames) {
1523 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001524 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001525 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001526 setErrorResponse(transaction->res,
1527 boost::beast::http::status::not_found,
1528 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529 return;
1530 }
1531
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001532 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1533 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534
1535 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001536 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001537 {
1538 findActionOnInterface(transaction, object.first);
1539 }
1540 },
1541 "xyz.openbmc_project.ObjectMapper",
1542 "/xyz/openbmc_project/object_mapper",
1543 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1544 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001545}
1546
Matt Spinlerde818812018-12-11 16:39:20 -06001547void handleDelete(const crow::Request &req, crow::Response &res,
1548 const std::string &objectPath)
1549{
1550 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1551
1552 crow::connections::systemBus->async_method_call(
1553 [&res, objectPath](
1554 const boost::system::error_code ec,
1555 const std::vector<std::pair<std::string, std::vector<std::string>>>
1556 &interfaceNames) {
1557 if (ec || interfaceNames.size() <= 0)
1558 {
1559 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001560 setErrorResponse(res,
1561 boost::beast::http::status::method_not_allowed,
1562 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001563 res.end();
1564 return;
1565 }
1566
1567 auto transaction = std::make_shared<InProgressActionData>(res);
1568 transaction->path = objectPath;
1569 transaction->methodName = "Delete";
1570 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1571
1572 for (const std::pair<std::string, std::vector<std::string>>
1573 &object : interfaceNames)
1574 {
1575 findActionOnInterface(transaction, object.first);
1576 }
1577 },
1578 "xyz.openbmc_project.ObjectMapper",
1579 "/xyz/openbmc_project/object_mapper",
1580 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1581 std::array<const char *, 0>());
1582}
1583
Ed Tanousf839dfe2018-11-12 11:11:15 -08001584void handleList(crow::Response &res, const std::string &objectPath,
1585 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001586{
1587 crow::connections::systemBus->async_method_call(
1588 [&res](const boost::system::error_code ec,
1589 std::vector<std::string> &objectPaths) {
1590 if (ec)
1591 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001592 setErrorResponse(res, boost::beast::http::status::not_found,
1593 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001594 }
1595 else
1596 {
1597 res.jsonValue = {{"status", "ok"},
1598 {"message", "200 OK"},
1599 {"data", std::move(objectPaths)}};
1600 }
1601 res.end();
1602 },
1603 "xyz.openbmc_project.ObjectMapper",
1604 "/xyz/openbmc_project/object_mapper",
1605 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001606 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001607}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001608
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001609void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001610{
Ed Tanous049a0512018-11-01 13:58:42 -07001611 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1612 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1613
1614 asyncResp->res.jsonValue = {{"message", "200 OK"},
1615 {"status", "ok"},
1616 {"data", nlohmann::json::object()}};
1617
Ed Tanous1abe55e2018-09-05 08:30:59 -07001618 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001619 [objectPath, asyncResp](const boost::system::error_code ec,
1620 GetSubTreeType &object_names) {
1621 auto transaction = std::make_shared<InProgressEnumerateData>(
1622 objectPath, asyncResp);
1623
1624 transaction->subtree =
1625 std::make_shared<GetSubTreeType>(std::move(object_names));
1626
Ed Tanous1abe55e2018-09-05 08:30:59 -07001627 if (ec)
1628 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001629 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1630 << transaction->objectPath;
1631 setErrorResponse(transaction->asyncResp->res,
1632 boost::beast::http::status::not_found,
1633 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001634 return;
1635 }
Ed Tanous64530012018-02-06 17:08:16 -08001636
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001637 // Add the data for the path passed in to the results
1638 // as if GetSubTree returned it, and continue on enumerating
1639 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001640 },
1641 "xyz.openbmc_project.ObjectMapper",
1642 "/xyz/openbmc_project/object_mapper",
1643 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001644 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001645}
Ed Tanous911ac312017-08-15 09:37:42 -07001646
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001647void handleGet(crow::Response &res, std::string &objectPath,
1648 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001649{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001650 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1651 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001652 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001653
Ed Tanous1abe55e2018-09-05 08:30:59 -07001654 std::shared_ptr<std::string> path =
1655 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001656
Ed Tanous1abe55e2018-09-05 08:30:59 -07001657 using GetObjectType =
1658 std::vector<std::pair<std::string, std::vector<std::string>>>;
1659 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001660 [&res, path, propertyName](const boost::system::error_code ec,
1661 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001662 if (ec || object_names.size() <= 0)
1663 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001664 setErrorResponse(res, boost::beast::http::status::not_found,
1665 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001666 res.end();
1667 return;
1668 }
1669 std::shared_ptr<nlohmann::json> response =
1670 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001671 // The mapper should never give us an empty interface names
1672 // list, but check anyway
Ed Tanous1abe55e2018-09-05 08:30:59 -07001673 for (const std::pair<std::string, std::vector<std::string>>
1674 connection : object_names)
1675 {
1676 const std::vector<std::string> &interfaceNames =
1677 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001678
Ed Tanous1abe55e2018-09-05 08:30:59 -07001679 if (interfaceNames.size() <= 0)
1680 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001681 setErrorResponse(res, boost::beast::http::status::not_found,
1682 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001683 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001684 return;
1685 }
1686
1687 for (const std::string &interface : interfaceNames)
1688 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001689 sdbusplus::message::message m =
1690 crow::connections::systemBus->new_method_call(
1691 connection.first.c_str(), path->c_str(),
1692 "org.freedesktop.DBus.Properties", "GetAll");
1693 m.append(interface);
1694 crow::connections::systemBus->async_send(
1695 m, [&res, response,
1696 propertyName](const boost::system::error_code ec,
1697 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001698 if (ec)
1699 {
1700 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1701 << ec;
1702 }
1703 else
1704 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001705 nlohmann::json properties;
1706 int r =
1707 convertDBusToJSON("a{sv}", msg, properties);
1708 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001709 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001710 BMCWEB_LOG_ERROR
1711 << "convertDBusToJSON failed";
1712 }
1713 else
1714 {
1715 for (auto &prop : properties.items())
1716 {
Ed Tanous7c091622019-05-23 11:42:36 -07001717 // if property name is empty, or
1718 // matches our search query, add it
1719 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001720
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001721 if (propertyName->empty())
1722 {
1723 (*response)[prop.key()] =
1724 std::move(prop.value());
1725 }
1726 else if (prop.key() == *propertyName)
1727 {
1728 *response = std::move(prop.value());
1729 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001730 }
1731 }
1732 }
1733 if (response.use_count() == 1)
1734 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001735 if (!propertyName->empty() && response->empty())
1736 {
1737 setErrorResponse(
1738 res,
1739 boost::beast::http::status::not_found,
1740 propNotFoundDesc, notFoundMsg);
1741 }
1742 else
1743 {
1744 res.jsonValue = {{"status", "ok"},
1745 {"message", "200 OK"},
1746 {"data", *response}};
1747 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001748 res.end();
1749 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001750 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001751 }
1752 }
1753 },
1754 "xyz.openbmc_project.ObjectMapper",
1755 "/xyz/openbmc_project/object_mapper",
1756 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1757 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001758}
1759
Ed Tanous1abe55e2018-09-05 08:30:59 -07001760struct AsyncPutRequest
1761{
1762 AsyncPutRequest(crow::Response &res) : res(res)
1763 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001764 }
1765 ~AsyncPutRequest()
1766 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001767 if (res.jsonValue.empty())
1768 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001769 setErrorResponse(res, boost::beast::http::status::forbidden,
1770 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001771 }
1772
1773 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001774 }
1775
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001776 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001777 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001778 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1779 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001780 }
1781
Ed Tanous1abe55e2018-09-05 08:30:59 -07001782 crow::Response &res;
1783 std::string objectPath;
1784 std::string propertyName;
1785 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001786};
1787
Ed Tanousd76323e2018-08-07 14:35:40 -07001788void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001789 const std::string &objectPath, const std::string &destProperty)
1790{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001791 if (destProperty.empty())
1792 {
1793 setErrorResponse(res, boost::beast::http::status::forbidden,
1794 forbiddenResDesc, forbiddenMsg);
1795 res.end();
1796 return;
1797 }
1798
Ed Tanous1abe55e2018-09-05 08:30:59 -07001799 nlohmann::json requestDbusData =
1800 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001801
Ed Tanous1abe55e2018-09-05 08:30:59 -07001802 if (requestDbusData.is_discarded())
1803 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001804 setErrorResponse(res, boost::beast::http::status::bad_request,
1805 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001806 res.end();
1807 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001808 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001809
Ed Tanous1abe55e2018-09-05 08:30:59 -07001810 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1811 if (propertyIt == requestDbusData.end())
1812 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001813 setErrorResponse(res, boost::beast::http::status::bad_request,
1814 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001815 res.end();
1816 return;
1817 }
1818 const nlohmann::json &propertySetValue = *propertyIt;
1819 auto transaction = std::make_shared<AsyncPutRequest>(res);
1820 transaction->objectPath = objectPath;
1821 transaction->propertyName = destProperty;
1822 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001823
Ed Tanous1abe55e2018-09-05 08:30:59 -07001824 using GetObjectType =
1825 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001826
Ed Tanous1abe55e2018-09-05 08:30:59 -07001827 crow::connections::systemBus->async_method_call(
1828 [transaction](const boost::system::error_code ec,
1829 const GetObjectType &object_names) {
1830 if (!ec && object_names.size() <= 0)
1831 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001832 setErrorResponse(transaction->res,
1833 boost::beast::http::status::not_found,
1834 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001835 return;
1836 }
Ed Tanous911ac312017-08-15 09:37:42 -07001837
Ed Tanous1abe55e2018-09-05 08:30:59 -07001838 for (const std::pair<std::string, std::vector<std::string>>
1839 connection : object_names)
1840 {
1841 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001842
Ed Tanous1abe55e2018-09-05 08:30:59 -07001843 crow::connections::systemBus->async_method_call(
1844 [connectionName{std::string(connectionName)},
1845 transaction](const boost::system::error_code ec,
1846 const std::string &introspectXml) {
1847 if (ec)
1848 {
1849 BMCWEB_LOG_ERROR
1850 << "Introspect call failed with error: "
1851 << ec.message()
1852 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001853 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001854 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001855 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001856 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001857
Ed Tanous1abe55e2018-09-05 08:30:59 -07001858 doc.Parse(introspectXml.c_str());
1859 tinyxml2::XMLNode *pRoot =
1860 doc.FirstChildElement("node");
1861 if (pRoot == nullptr)
1862 {
1863 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1864 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001865 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001866 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001867 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001868 tinyxml2::XMLElement *ifaceNode =
1869 pRoot->FirstChildElement("interface");
1870 while (ifaceNode != nullptr)
1871 {
1872 const char *interfaceName =
1873 ifaceNode->Attribute("name");
1874 BMCWEB_LOG_DEBUG << "found interface "
1875 << interfaceName;
1876 tinyxml2::XMLElement *propNode =
1877 ifaceNode->FirstChildElement("property");
1878 while (propNode != nullptr)
1879 {
1880 const char *propertyName =
1881 propNode->Attribute("name");
1882 BMCWEB_LOG_DEBUG << "Found property "
1883 << propertyName;
1884 if (propertyName == transaction->propertyName)
1885 {
1886 const char *argType =
1887 propNode->Attribute("type");
1888 if (argType != nullptr)
1889 {
1890 sdbusplus::message::message m =
1891 crow::connections::systemBus
1892 ->new_method_call(
1893 connectionName.c_str(),
1894 transaction->objectPath
1895 .c_str(),
1896 "org.freedesktop.DBus."
1897 "Properties",
1898 "Set");
1899 m.append(interfaceName,
1900 transaction->propertyName);
1901 int r = sd_bus_message_open_container(
1902 m.get(), SD_BUS_TYPE_VARIANT,
1903 argType);
1904 if (r < 0)
1905 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001906 transaction->setErrorStatus(
1907 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001908 return;
1909 }
1910 r = convertJsonToDbus(
1911 m.get(), argType,
1912 transaction->propertyValue);
1913 if (r < 0)
1914 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -05001915 if (r == -ERANGE)
1916 {
1917 transaction->setErrorStatus(
1918 "Provided property value "
1919 "is out of range for the "
1920 "property type");
1921 }
1922 else
1923 {
1924 transaction->setErrorStatus(
1925 "Invalid arg type");
1926 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001927 return;
1928 }
1929 r = sd_bus_message_close_container(
1930 m.get());
1931 if (r < 0)
1932 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001933 transaction->setErrorStatus(
1934 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001935 return;
1936 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001937 crow::connections::systemBus
1938 ->async_send(
1939 m,
1940 [transaction](
1941 boost::system::error_code
1942 ec,
1943 sdbusplus::message::message
1944 &m) {
1945 BMCWEB_LOG_DEBUG << "sent";
1946 if (ec)
1947 {
Lei YU97d2a472019-06-11 17:44:27 +08001948 const sd_bus_error *e =
1949 m.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001950 setErrorResponse(
1951 transaction->res,
1952 boost::beast::http::
1953 status::
1954 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001955 (e) ? e->name
1956 : ec.category()
1957 .name(),
1958 (e) ? e->message
1959 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001960 }
1961 else
1962 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001963 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001964 .jsonValue = {
1965 {"status", "ok"},
1966 {"message",
1967 "200 OK"},
1968 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001969 }
1970 });
1971 }
1972 }
1973 propNode =
1974 propNode->NextSiblingElement("property");
1975 }
1976 ifaceNode =
1977 ifaceNode->NextSiblingElement("interface");
1978 }
1979 },
1980 connectionName, transaction->objectPath,
1981 "org.freedesktop.DBus.Introspectable", "Introspect");
1982 }
1983 },
1984 "xyz.openbmc_project.ObjectMapper",
1985 "/xyz/openbmc_project/object_mapper",
1986 "xyz.openbmc_project.ObjectMapper", "GetObject",
1987 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001988}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001989
Ed Tanous049a0512018-11-01 13:58:42 -07001990inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1991 std::string &objectPath)
1992{
Ed Tanous049a0512018-11-01 13:58:42 -07001993
1994 // If accessing a single attribute, fill in and update objectPath,
1995 // otherwise leave destProperty blank
1996 std::string destProperty = "";
1997 const char *attrSeperator = "/attr/";
1998 size_t attrPosition = objectPath.find(attrSeperator);
1999 if (attrPosition != objectPath.npos)
2000 {
2001 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2002 objectPath.length());
2003 objectPath = objectPath.substr(0, attrPosition);
2004 }
2005
2006 if (req.method() == "POST"_method)
2007 {
2008 constexpr const char *actionSeperator = "/action/";
2009 size_t actionPosition = objectPath.find(actionSeperator);
2010 if (actionPosition != objectPath.npos)
2011 {
2012 std::string postProperty =
2013 objectPath.substr((actionPosition + strlen(actionSeperator)),
2014 objectPath.length());
2015 objectPath = objectPath.substr(0, actionPosition);
2016 handleAction(req, res, objectPath, postProperty);
2017 return;
2018 }
2019 }
2020 else if (req.method() == "GET"_method)
2021 {
2022 if (boost::ends_with(objectPath, "/enumerate"))
2023 {
2024 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2025 objectPath.end());
2026 handleEnumerate(res, objectPath);
2027 }
2028 else if (boost::ends_with(objectPath, "/list"))
2029 {
2030 objectPath.erase(objectPath.end() - sizeof("list"),
2031 objectPath.end());
2032 handleList(res, objectPath);
2033 }
2034 else
2035 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002036 // Trim any trailing "/" at the end
2037 if (boost::ends_with(objectPath, "/"))
2038 {
2039 objectPath.pop_back();
2040 handleList(res, objectPath, 1);
2041 }
2042 else
2043 {
2044 handleGet(res, objectPath, destProperty);
2045 }
Ed Tanous049a0512018-11-01 13:58:42 -07002046 }
2047 return;
2048 }
2049 else if (req.method() == "PUT"_method)
2050 {
2051 handlePut(req, res, objectPath, destProperty);
2052 return;
2053 }
Matt Spinlerde818812018-12-11 16:39:20 -06002054 else if (req.method() == "DELETE"_method)
2055 {
2056 handleDelete(req, res, objectPath);
2057 return;
2058 }
Ed Tanous049a0512018-11-01 13:58:42 -07002059
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002060 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2061 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002062 res.end();
2063}
2064
Ed Tanous1abe55e2018-09-05 08:30:59 -07002065template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
2066{
2067 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03002068 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002069 .methods("GET"_method)(
2070 [](const crow::Request &req, crow::Response &res) {
2071 res.jsonValue = {{"busses", {{{"name", "system"}}}},
2072 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002073 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002074 });
2075
2076 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03002077 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002078 .methods("GET"_method)(
2079 [](const crow::Request &req, crow::Response &res) {
2080 auto myCallback = [&res](const boost::system::error_code ec,
2081 std::vector<std::string> &names) {
2082 if (ec)
2083 {
2084 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2085 res.result(
2086 boost::beast::http::status::internal_server_error);
2087 }
2088 else
2089 {
2090 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002091 res.jsonValue = {{"status", "ok"}};
2092 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002093 for (auto &name : names)
2094 {
2095 objectsSub.push_back({{"name", name}});
2096 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002097 }
2098 res.end();
2099 };
2100 crow::connections::systemBus->async_method_call(
2101 std::move(myCallback), "org.freedesktop.DBus", "/",
2102 "org.freedesktop.DBus", "ListNames");
2103 });
2104
2105 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002106 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002107 .methods("GET"_method)(
2108 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002109 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002110 });
2111
2112 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002113 .requires({"Login"})
2114 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2115 const std::string &path) {
2116 std::string objectPath = "/xyz/" + path;
2117 handleDBusUrl(req, res, objectPath);
2118 });
2119
2120 BMCWEB_ROUTE(app, "/xyz/<path>")
2121 .requires({"ConfigureComponents", "ConfigureManager"})
2122 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002123 [](const crow::Request &req, crow::Response &res,
2124 const std::string &path) {
2125 std::string objectPath = "/xyz/" + path;
2126 handleDBusUrl(req, res, objectPath);
2127 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002128
Ed Tanous049a0512018-11-01 13:58:42 -07002129 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002130 .requires({"Login"})
2131 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2132 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002133 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002134 handleDBusUrl(req, res, objectPath);
2135 });
2136
2137 BMCWEB_ROUTE(app, "/org/<path>")
2138 .requires({"ConfigureComponents", "ConfigureManager"})
2139 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002140 [](const crow::Request &req, crow::Response &res,
2141 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002142 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002143 handleDBusUrl(req, res, objectPath);
2144 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002145
Ed Tanous1abe55e2018-09-05 08:30:59 -07002146 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002147 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002148 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2149 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002150 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002151 if (!std::regex_match(dumpId, validFilename))
2152 {
Ed Tanousad18f072018-11-14 14:07:48 -08002153 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002154 res.end();
2155 return;
2156 }
James Feistf6150402019-01-08 10:36:20 -08002157 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002158 "/var/lib/phosphor-debug-collector/dumps");
2159
Ed Tanousad18f072018-11-14 14:07:48 -08002160 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002161
James Feistf6150402019-01-08 10:36:20 -08002162 if (!std::filesystem::exists(loc) ||
2163 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002164 {
Ed Tanousad18f072018-11-14 14:07:48 -08002165 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002166 res.result(boost::beast::http::status::not_found);
2167 res.end();
2168 return;
2169 }
James Feistf6150402019-01-08 10:36:20 -08002170 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002171
Ed Tanous1abe55e2018-09-05 08:30:59 -07002172 for (auto &file : files)
2173 {
2174 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002175 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002176 {
2177 continue;
2178 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002179
Ed Tanous1abe55e2018-09-05 08:30:59 -07002180 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002181
2182 // Assuming only one dump file will be present in the dump id
2183 // directory
2184 std::string dumpFileName = file.path().filename().string();
2185
2186 // Filename should be in alphanumeric, dot and underscore
2187 // Its based on phosphor-debug-collector application dumpfile
2188 // format
2189 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2190 if (!std::regex_match(dumpFileName, dumpFileRegex))
2191 {
2192 BMCWEB_LOG_ERROR << "Invalid dump filename "
2193 << dumpFileName;
2194 res.result(boost::beast::http::status::not_found);
2195 res.end();
2196 return;
2197 }
2198 std::string contentDispositionParam =
2199 "attachment; filename=\"" + dumpFileName + "\"";
2200
2201 res.addHeader("Content-Disposition", contentDispositionParam);
2202
Ed Tanous1abe55e2018-09-05 08:30:59 -07002203 res.body() = {std::istreambuf_iterator<char>(readFile),
2204 std::istreambuf_iterator<char>()};
2205 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002206 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002207 }
2208 res.result(boost::beast::http::status::not_found);
2209 res.end();
2210 return;
2211 });
2212
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002213 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002214 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002215 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002216 const std::string &Connection) {
2217 introspectObjects(Connection, "/",
2218 std::make_shared<bmcweb::AsyncResp>(res));
2219 });
2220
2221 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2222 .methods("GET"_method,
2223 "POST"_method)([](const crow::Request &req,
2224 crow::Response &res,
2225 const std::string &processName,
2226 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002227 std::vector<std::string> strs;
2228 boost::split(strs, requestedPath, boost::is_any_of("/"));
2229 std::string objectPath;
2230 std::string interfaceName;
2231 std::string methodName;
2232 auto it = strs.begin();
2233 if (it == strs.end())
2234 {
2235 objectPath = "/";
2236 }
2237 while (it != strs.end())
2238 {
2239 // Check if segment contains ".". If it does, it must be an
2240 // interface
2241 if (it->find(".") != std::string::npos)
2242 {
2243 break;
Ed Tanous7c091622019-05-23 11:42:36 -07002244 // This check is neccesary as the trailing slash gets
2245 // parsed as part of our <path> specifier above, which
2246 // causes the normal trailing backslash redirector to
2247 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002248 }
2249 else if (!it->empty())
2250 {
2251 objectPath += "/" + *it;
2252 }
2253 it++;
2254 }
2255 if (it != strs.end())
2256 {
2257 interfaceName = *it;
2258 it++;
2259
2260 // after interface, we might have a method name
2261 if (it != strs.end())
2262 {
2263 methodName = *it;
2264 it++;
2265 }
2266 }
2267 if (it != strs.end())
2268 {
Ed Tanous7c091622019-05-23 11:42:36 -07002269 // if there is more levels past the method name, something
2270 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002271 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002272 return;
2273 }
2274 if (interfaceName.empty())
2275 {
Ed Tanous7c091622019-05-23 11:42:36 -07002276 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2277 std::make_shared<bmcweb::AsyncResp>(res);
2278
Ed Tanous1abe55e2018-09-05 08:30:59 -07002279 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002280 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002281 objectPath](const boost::system::error_code ec,
2282 const std::string &introspect_xml) {
2283 if (ec)
2284 {
2285 BMCWEB_LOG_ERROR
2286 << "Introspect call failed with error: "
2287 << ec.message()
2288 << " on process: " << processName
2289 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002290 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002291 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002292 tinyxml2::XMLDocument doc;
2293
2294 doc.Parse(introspect_xml.c_str());
2295 tinyxml2::XMLNode *pRoot =
2296 doc.FirstChildElement("node");
2297 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002298 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002299 BMCWEB_LOG_ERROR << "XML document failed to parse "
2300 << processName << " " << objectPath
2301 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002302 asyncResp->res.jsonValue = {
2303 {"status", "XML parse error"}};
2304 asyncResp->res.result(boost::beast::http::status::
2305 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002306 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002307 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002308
2309 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002310 asyncResp->res.jsonValue = {
2311 {"status", "ok"},
2312 {"bus_name", processName},
2313 {"object_path", objectPath}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002314 nlohmann::json &interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002315 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002316 interfacesArray = nlohmann::json::array();
2317 tinyxml2::XMLElement *interface =
2318 pRoot->FirstChildElement("interface");
2319
2320 while (interface != nullptr)
2321 {
2322 const char *ifaceName =
2323 interface->Attribute("name");
2324 if (ifaceName != nullptr)
2325 {
2326 interfacesArray.push_back(
2327 {{"name", ifaceName}});
2328 }
2329
2330 interface =
2331 interface->NextSiblingElement("interface");
2332 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002333 },
2334 processName, objectPath,
2335 "org.freedesktop.DBus.Introspectable", "Introspect");
2336 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002337 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002338 {
Ed Tanous7c091622019-05-23 11:42:36 -07002339 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2340 std::make_shared<bmcweb::AsyncResp>(res);
2341
Ed Tanous1abe55e2018-09-05 08:30:59 -07002342 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002343 [asyncResp, processName, objectPath,
2344 interfaceName](const boost::system::error_code ec,
2345 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002346 if (ec)
2347 {
2348 BMCWEB_LOG_ERROR
2349 << "Introspect call failed with error: "
2350 << ec.message()
2351 << " on process: " << processName
2352 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002353 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002354 }
Ed Tanous7c091622019-05-23 11:42:36 -07002355 tinyxml2::XMLDocument doc;
2356
2357 doc.Parse(introspect_xml.data(), introspect_xml.size());
2358 tinyxml2::XMLNode *pRoot =
2359 doc.FirstChildElement("node");
2360 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002361 {
Ed Tanous7c091622019-05-23 11:42:36 -07002362 BMCWEB_LOG_ERROR << "XML document failed to parse "
2363 << processName << " " << objectPath
2364 << "\n";
2365 asyncResp->res.result(boost::beast::http::status::
2366 internal_server_error);
2367 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002368 }
Ed Tanous7c091622019-05-23 11:42:36 -07002369 asyncResp->res.jsonValue = {
2370 {"status", "ok"},
2371 {"bus_name", processName},
2372 {"interface", interfaceName},
2373 {"object_path", objectPath}};
2374
2375 nlohmann::json &methodsArray =
2376 asyncResp->res.jsonValue["methods"];
2377 methodsArray = nlohmann::json::array();
2378
2379 nlohmann::json &signalsArray =
2380 asyncResp->res.jsonValue["signals"];
2381 signalsArray = nlohmann::json::array();
2382
2383 nlohmann::json &propertiesObj =
2384 asyncResp->res.jsonValue["properties"];
2385 propertiesObj = nlohmann::json::object();
2386
2387 // if we know we're the only call, build the
2388 // json directly
2389 tinyxml2::XMLElement *interface =
2390 pRoot->FirstChildElement("interface");
2391 while (interface != nullptr)
2392 {
2393 const char *ifaceName =
2394 interface->Attribute("name");
2395
2396 if (ifaceName != nullptr &&
2397 ifaceName == interfaceName)
2398 {
2399 break;
2400 }
2401
2402 interface =
2403 interface->NextSiblingElement("interface");
2404 }
2405 if (interface == nullptr)
2406 {
2407 // if we got to the end of the list and
2408 // never found a match, throw 404
2409 asyncResp->res.result(
2410 boost::beast::http::status::not_found);
2411 return;
2412 }
2413
2414 tinyxml2::XMLElement *methods =
2415 interface->FirstChildElement("method");
2416 while (methods != nullptr)
2417 {
2418 nlohmann::json argsArray = nlohmann::json::array();
2419 tinyxml2::XMLElement *arg =
2420 methods->FirstChildElement("arg");
2421 while (arg != nullptr)
2422 {
2423 nlohmann::json thisArg;
2424 for (const char *fieldName :
2425 std::array<const char *, 3>{
2426 "name", "direction", "type"})
2427 {
2428 const char *fieldValue =
2429 arg->Attribute(fieldName);
2430 if (fieldValue != nullptr)
2431 {
2432 thisArg[fieldName] = fieldValue;
2433 }
2434 }
2435 argsArray.push_back(std::move(thisArg));
2436 arg = arg->NextSiblingElement("arg");
2437 }
2438
2439 const char *name = methods->Attribute("name");
2440 if (name != nullptr)
2441 {
2442 methodsArray.push_back(
2443 {{"name", name},
2444 {"uri", "/bus/system/" + processName +
2445 objectPath + "/" +
2446 interfaceName + "/" + name},
2447 {"args", argsArray}});
2448 }
2449 methods = methods->NextSiblingElement("method");
2450 }
2451 tinyxml2::XMLElement *signals =
2452 interface->FirstChildElement("signal");
2453 while (signals != nullptr)
2454 {
2455 nlohmann::json argsArray = nlohmann::json::array();
2456
2457 tinyxml2::XMLElement *arg =
2458 signals->FirstChildElement("arg");
2459 while (arg != nullptr)
2460 {
2461 const char *name = arg->Attribute("name");
2462 const char *type = arg->Attribute("type");
2463 if (name != nullptr && type != nullptr)
2464 {
2465 argsArray.push_back({
2466 {"name", name},
2467 {"type", type},
2468 });
2469 }
2470 arg = arg->NextSiblingElement("arg");
2471 }
2472 const char *name = signals->Attribute("name");
2473 if (name != nullptr)
2474 {
2475 signalsArray.push_back(
2476 {{"name", name}, {"args", argsArray}});
2477 }
2478
2479 signals = signals->NextSiblingElement("signal");
2480 }
2481
2482 tinyxml2::XMLElement *property =
2483 interface->FirstChildElement("property");
2484 while (property != nullptr)
2485 {
2486 const char *name = property->Attribute("name");
2487 const char *type = property->Attribute("type");
2488 if (type != nullptr && name != nullptr)
2489 {
2490 sdbusplus::message::message m =
2491 crow::connections::systemBus
2492 ->new_method_call(processName.c_str(),
2493 objectPath.c_str(),
2494 "org.freedesktop."
2495 "DBus."
2496 "Properties",
2497 "Get");
2498 m.append(interfaceName, name);
2499 nlohmann::json &propertyItem =
2500 propertiesObj[name];
2501 crow::connections::systemBus->async_send(
2502 m, [&propertyItem, asyncResp](
2503 boost::system::error_code &ec,
2504 sdbusplus::message::message &m) {
2505 if (ec)
2506 {
2507 return;
2508 }
2509
2510 convertDBusToJSON("v", m, propertyItem);
2511 });
2512 }
2513 property = property->NextSiblingElement("property");
2514 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002515 },
2516 processName, objectPath,
2517 "org.freedesktop.DBus.Introspectable", "Introspect");
2518 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002519 else
2520 {
2521 if (req.method() != "POST"_method)
2522 {
2523 res.result(boost::beast::http::status::not_found);
2524 res.end();
2525 return;
2526 }
2527
2528 nlohmann::json requestDbusData =
2529 nlohmann::json::parse(req.body, nullptr, false);
2530
2531 if (requestDbusData.is_discarded())
2532 {
2533 res.result(boost::beast::http::status::bad_request);
2534 res.end();
2535 return;
2536 }
2537 if (!requestDbusData.is_array())
2538 {
2539 res.result(boost::beast::http::status::bad_request);
2540 res.end();
2541 return;
2542 }
2543 auto transaction = std::make_shared<InProgressActionData>(res);
2544
2545 transaction->path = objectPath;
2546 transaction->methodName = methodName;
2547 transaction->arguments = std::move(requestDbusData);
2548
2549 findActionOnInterface(transaction, processName);
2550 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002551 });
2552}
2553} // namespace openbmc_mapper
2554} // namespace crow