blob: 749e89952b977a73458c3fdea222d46d1f5eff1b [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 *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700536 const std::string *stringValue = j->get_ptr<const std::string *>();
537 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 const bool *b = j->get_ptr<const bool *>();
539 int64_t v = 0;
540 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700541
Ed Tanous1abe55e2018-09-05 08:30:59 -0700542 // Do some basic type conversions that make sense. uint can be
543 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700544 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 {
Ed Tanous66664f22019-10-11 13:05:49 -0700546 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
547 if (uintValue != nullptr)
548 {
549 v = static_cast<int64_t>(*uintValue);
550 intValue = &v;
551 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 }
Ed Tanous66664f22019-10-11 13:05:49 -0700553 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 {
Ed Tanous66664f22019-10-11 13:05:49 -0700555 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
556 if (uintValue != nullptr)
557 {
558 d = static_cast<double>(*uintValue);
559 doubleValue = &d;
560 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 }
Ed Tanous66664f22019-10-11 13:05:49 -0700562 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 {
Ed Tanous66664f22019-10-11 13:05:49 -0700564 if (intValue != nullptr)
565 {
566 d = static_cast<double>(*intValue);
567 doubleValue = &d;
568 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700569 }
570
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700571 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700573 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 {
575 return -1;
576 }
Ed Tanous271584a2019-07-09 16:24:22 -0700577 r = sd_bus_message_append_basic(
578 m, argCode[0], static_cast<const void *>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 if (r < 0)
580 {
581 return r;
582 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700583 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700584 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700585 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700586 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 {
588 return -1;
589 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500590 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
591 (*intValue > std::numeric_limits<int32_t>::max()))
592 {
593 return -ERANGE;
594 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700595 int32_t i = static_cast<int32_t>(*intValue);
596 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 if (r < 0)
598 {
599 return r;
600 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700601 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700602 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 {
604 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700605 int boolInt = false;
606 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500608 if (*intValue == 1)
609 {
610 boolInt = true;
611 }
612 else if (*intValue == 0)
613 {
614 boolInt = false;
615 }
616 else
617 {
618 return -ERANGE;
619 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 }
621 else if (b != nullptr)
622 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600623 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700625 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700627 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 }
629 else
630 {
631 return -1;
632 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700633 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 if (r < 0)
635 {
636 return r;
637 }
638 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700641 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 {
643 return -1;
644 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500645 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
646 (*intValue > std::numeric_limits<int16_t>::max()))
647 {
648 return -ERANGE;
649 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700650 int16_t n = static_cast<int16_t>(*intValue);
651 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 if (r < 0)
653 {
654 return r;
655 }
656 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700657 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700659 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 {
661 return -1;
662 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500663 if ((*intValue < std::numeric_limits<int64_t>::lowest()) ||
664 (*intValue > std::numeric_limits<int64_t>::max()))
665 {
666 return -ERANGE;
667 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700668 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 if (r < 0)
670 {
671 return r;
672 }
673 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700674 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
Ed Tanous66664f22019-10-11 13:05:49 -0700676 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700677 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 {
679 return -1;
680 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500681 if ((*uintValue < std::numeric_limits<uint8_t>::lowest()) ||
682 (*uintValue > std::numeric_limits<uint8_t>::max()))
683 {
684 return -ERANGE;
685 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 uint8_t y = static_cast<uint8_t>(*uintValue);
687 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 {
Ed Tanous66664f22019-10-11 13:05:49 -0700691 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700692 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 {
694 return -1;
695 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500696 if ((*uintValue < std::numeric_limits<uint16_t>::lowest()) ||
697 (*uintValue > std::numeric_limits<uint16_t>::max()))
698 {
699 return -ERANGE;
700 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700701 uint16_t q = static_cast<uint16_t>(*uintValue);
702 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 {
Ed Tanous66664f22019-10-11 13:05:49 -0700706 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700707 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 {
709 return -1;
710 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500711 if ((*uintValue < std::numeric_limits<uint32_t>::lowest()) ||
712 (*uintValue > std::numeric_limits<uint32_t>::max()))
713 {
714 return -ERANGE;
715 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700716 uint32_t u = static_cast<uint32_t>(*uintValue);
717 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700719 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700720 {
Ed Tanous66664f22019-10-11 13:05:49 -0700721 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700722 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 {
724 return -1;
725 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500726 if ((*uintValue < std::numeric_limits<uint64_t>::lowest()) ||
727 (*uintValue > std::numeric_limits<uint64_t>::max()))
728 {
729 return -ERANGE;
730 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700731 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700733 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500735 if (doubleValue == nullptr)
736 {
737 return -1;
738 }
739 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
740 (*doubleValue > std::numeric_limits<double>::max()))
741 {
742 return -ERANGE;
743 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700744 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700746 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700748 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700750 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751 if (r < 0)
752 {
753 return r;
754 }
755
756 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
757 ++it)
758 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 if (r < 0)
761 {
762 return r;
763 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 }
765 sd_bus_message_close_container(m);
766 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700767 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700769 std::string containedType = argCode.substr(1);
770 BMCWEB_LOG_DEBUG << "variant type: " << argCode
771 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700773 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700774 if (r < 0)
775 {
776 return r;
777 }
778
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700779 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700780 if (r < 0)
781 {
782 return r;
783 }
784
785 r = sd_bus_message_close_container(m);
786 if (r < 0)
787 {
788 return r;
789 }
790 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700791 else if (boost::starts_with(argCode, "(") &&
792 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700794 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700796 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800797 if (r < 0)
798 {
799 return r;
800 }
801
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000803 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 {
805 if (it == j->end())
806 {
807 return -1;
808 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000809 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700810 if (r < 0)
811 {
812 return r;
813 }
814 it++;
815 }
816 r = sd_bus_message_close_container(m);
817 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700818 else if (boost::starts_with(argCode, "{") &&
819 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700821 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700822 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700823 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800824 if (r < 0)
825 {
826 return r;
827 }
828
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700829 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 if (codes.size() != 2)
831 {
832 return -1;
833 }
834 const std::string &key_type = codes[0];
835 const std::string &value_type = codes[1];
836 for (auto it : j->items())
837 {
838 r = convertJsonToDbus(m, key_type, it.key());
839 if (r < 0)
840 {
841 return r;
842 }
843
844 r = convertJsonToDbus(m, value_type, it.value());
845 if (r < 0)
846 {
847 return r;
848 }
849 }
850 r = sd_bus_message_close_container(m);
851 }
852 else
853 {
854 return -2;
855 }
856 if (r < 0)
857 {
858 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700859 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700860
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 if (argTypes.size() > 1)
862 {
863 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700864 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700865 }
Matt Spinler127ea542019-01-14 11:04:28 -0600866
867 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700868}
869
Matt Spinlerd22a7132019-01-14 12:14:30 -0600870template <typename T>
871int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
872 nlohmann::json &data)
873{
874 T value;
875
876 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
877 if (r < 0)
878 {
879 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
880 << " failed!";
881 return r;
882 }
883
884 data = value;
885 return 0;
886}
887
Matt Spinler16caaee2019-01-15 11:40:34 -0600888int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600889 sdbusplus::message::message &m, nlohmann::json &response);
890
891int readDictEntryFromMessage(const std::string &typeCode,
892 sdbusplus::message::message &m,
893 nlohmann::json &object)
894{
895 std::vector<std::string> types = dbusArgSplit(typeCode);
896 if (types.size() != 2)
897 {
898 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
899 << types.size();
900 return -1;
901 }
902
903 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
904 typeCode.c_str());
905 if (r < 0)
906 {
907 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
908 return r;
909 }
910
911 nlohmann::json key;
912 r = convertDBusToJSON(types[0], m, key);
913 if (r < 0)
914 {
915 return r;
916 }
917
918 const std::string *keyPtr = key.get_ptr<const std::string *>();
919 if (keyPtr == nullptr)
920 {
921 // json doesn't support non-string keys. If we hit this condition,
922 // convert the result to a string so we can proceed
923 key = key.dump();
924 keyPtr = key.get_ptr<const std::string *>();
Ed Tanous7c091622019-05-23 11:42:36 -0700925 // in theory this can't fail now, but lets be paranoid about it
926 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600927 if (keyPtr == nullptr)
928 {
929 return -1;
930 }
931 }
932 nlohmann::json &value = object[*keyPtr];
933
934 r = convertDBusToJSON(types[1], m, value);
935 if (r < 0)
936 {
937 return r;
938 }
939
940 r = sd_bus_message_exit_container(m.get());
941 if (r < 0)
942 {
943 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
944 return r;
945 }
946
947 return 0;
948}
949
950int readArrayFromMessage(const std::string &typeCode,
951 sdbusplus::message::message &m, nlohmann::json &data)
952{
953 if (typeCode.size() < 2)
954 {
955 BMCWEB_LOG_ERROR << "Type code " << typeCode
956 << " too small for an array";
957 return -1;
958 }
959
960 std::string containedType = typeCode.substr(1);
961
962 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
963 containedType.c_str());
964 if (r < 0)
965 {
966 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
967 << r;
968 return r;
969 }
970
971 bool dict = boost::starts_with(containedType, "{") &&
972 boost::ends_with(containedType, "}");
973
974 if (dict)
975 {
976 // Remove the { }
977 containedType = containedType.substr(1, containedType.size() - 2);
978 data = nlohmann::json::object();
979 }
980 else
981 {
982 data = nlohmann::json::array();
983 }
984
985 while (true)
986 {
987 r = sd_bus_message_at_end(m.get(), false);
988 if (r < 0)
989 {
990 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
991 return r;
992 }
993
994 if (r > 0)
995 {
996 break;
997 }
998
999 // Dictionaries are only ever seen in an array
1000 if (dict)
1001 {
1002 r = readDictEntryFromMessage(containedType, m, data);
1003 if (r < 0)
1004 {
1005 return r;
1006 }
1007 }
1008 else
1009 {
1010 data.push_back(nlohmann::json());
1011
1012 r = convertDBusToJSON(containedType, m, data.back());
1013 if (r < 0)
1014 {
1015 return r;
1016 }
1017 }
1018 }
1019
1020 r = sd_bus_message_exit_container(m.get());
1021 if (r < 0)
1022 {
1023 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1024 return r;
1025 }
1026
1027 return 0;
1028}
1029
Matt Spinler75c6c672019-01-14 13:01:46 -06001030int readStructFromMessage(const std::string &typeCode,
1031 sdbusplus::message::message &m, nlohmann::json &data)
1032{
1033 if (typeCode.size() < 3)
1034 {
1035 BMCWEB_LOG_ERROR << "Type code " << typeCode
1036 << " too small for a struct";
1037 return -1;
1038 }
1039
1040 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1041 std::vector<std::string> types = dbusArgSplit(containedTypes);
1042
1043 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1044 containedTypes.c_str());
1045 if (r < 0)
1046 {
1047 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1048 << r;
1049 return r;
1050 }
1051
1052 for (const std::string &type : types)
1053 {
1054 data.push_back(nlohmann::json());
1055 r = convertDBusToJSON(type, m, data.back());
1056 if (r < 0)
1057 {
1058 return r;
1059 }
1060 }
1061
1062 r = sd_bus_message_exit_container(m.get());
1063 if (r < 0)
1064 {
1065 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1066 return r;
1067 }
1068 return 0;
1069}
1070
Matt Spinler89c19702019-01-14 13:13:00 -06001071int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1072{
1073 const char *containerType;
1074 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1075 if (r < 0)
1076 {
1077 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1078 return r;
1079 }
1080
1081 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1082 containerType);
1083 if (r < 0)
1084 {
1085 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1086 << r;
1087 return r;
1088 }
1089
1090 r = convertDBusToJSON(containerType, m, data);
1091 if (r < 0)
1092 {
1093 return r;
1094 }
1095
1096 r = sd_bus_message_exit_container(m.get());
1097 if (r < 0)
1098 {
1099 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1100 return r;
1101 }
1102
1103 return 0;
1104}
1105
Matt Spinler6df8f992019-01-14 12:47:47 -06001106int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001107 sdbusplus::message::message &m, nlohmann::json &response)
1108{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001109 int r = 0;
1110 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1111
Matt Spinlerd22a7132019-01-14 12:14:30 -06001112 for (const std::string &typeCode : returnTypes)
1113 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001114 nlohmann::json *thisElement = &response;
1115 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001116 {
1117 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001118 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001119 }
1120
1121 if (typeCode == "s")
1122 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001123 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001124 if (r < 0)
1125 {
1126 return r;
1127 }
1128 }
1129 else if (typeCode == "g")
1130 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001131 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001132 if (r < 0)
1133 {
1134 return r;
1135 }
1136 }
1137 else if (typeCode == "o")
1138 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001139 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001140 if (r < 0)
1141 {
1142 return r;
1143 }
1144 }
1145 else if (typeCode == "b")
1146 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001147 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001148 if (r < 0)
1149 {
1150 return r;
1151 }
1152
Matt Spinlerf39420c2019-01-30 12:57:18 -06001153 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001154 }
1155 else if (typeCode == "u")
1156 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001157 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001158 if (r < 0)
1159 {
1160 return r;
1161 }
1162 }
1163 else if (typeCode == "i")
1164 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001165 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001166 if (r < 0)
1167 {
1168 return r;
1169 }
1170 }
1171 else if (typeCode == "x")
1172 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001173 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001174 if (r < 0)
1175 {
1176 return r;
1177 }
1178 }
1179 else if (typeCode == "t")
1180 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001181 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001182 if (r < 0)
1183 {
1184 return r;
1185 }
1186 }
1187 else if (typeCode == "n")
1188 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001189 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001190 if (r < 0)
1191 {
1192 return r;
1193 }
1194 }
1195 else if (typeCode == "q")
1196 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001197 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001198 if (r < 0)
1199 {
1200 return r;
1201 }
1202 }
1203 else if (typeCode == "y")
1204 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001205 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001206 if (r < 0)
1207 {
1208 return r;
1209 }
1210 }
1211 else if (typeCode == "d")
1212 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001213 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001214 if (r < 0)
1215 {
1216 return r;
1217 }
1218 }
1219 else if (typeCode == "h")
1220 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001221 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001222 if (r < 0)
1223 {
1224 return r;
1225 }
1226 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001227 else if (boost::starts_with(typeCode, "a"))
1228 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001229 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001230 if (r < 0)
1231 {
1232 return r;
1233 }
1234 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001235 else if (boost::starts_with(typeCode, "(") &&
1236 boost::ends_with(typeCode, ")"))
1237 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001238 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001239 if (r < 0)
1240 {
1241 return r;
1242 }
1243 }
Matt Spinler89c19702019-01-14 13:13:00 -06001244 else if (boost::starts_with(typeCode, "v"))
1245 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001246 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001247 if (r < 0)
1248 {
1249 return r;
1250 }
1251 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001252 else
1253 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001254 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1255 return -2;
1256 }
1257 }
1258
Matt Spinler16caaee2019-01-15 11:40:34 -06001259 return 0;
1260}
1261
1262void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1263 sdbusplus::message::message &m,
1264 const std::string &returnType)
1265{
Matt Spinler39a4e392019-01-15 11:53:13 -06001266 nlohmann::json data;
1267
1268 int r = convertDBusToJSON(returnType, m, data);
1269 if (r < 0)
1270 {
1271 transaction->outputFailed = true;
1272 return;
1273 }
1274
1275 if (data.is_null())
1276 {
1277 return;
1278 }
1279
1280 if (transaction->methodResponse.is_null())
1281 {
1282 transaction->methodResponse = std::move(data);
1283 return;
1284 }
1285
1286 // If they're both dictionaries or arrays, merge into one.
1287 // Otherwise, make the results an array with every result
1288 // an entry. Could also just fail in that case, but it
1289 // seems better to get the data back somehow.
1290
1291 if (transaction->methodResponse.is_object() && data.is_object())
1292 {
1293 for (const auto &obj : data.items())
1294 {
1295 // Note: Will overwrite the data for a duplicate key
1296 transaction->methodResponse.emplace(obj.key(),
1297 std::move(obj.value()));
1298 }
1299 return;
1300 }
1301
1302 if (transaction->methodResponse.is_array() && data.is_array())
1303 {
1304 for (auto &obj : data)
1305 {
1306 transaction->methodResponse.push_back(std::move(obj));
1307 }
1308 return;
1309 }
1310
1311 if (!transaction->convertedToArray)
1312 {
1313 // They are different types. May as well turn them into an array
1314 nlohmann::json j = std::move(transaction->methodResponse);
1315 transaction->methodResponse = nlohmann::json::array();
1316 transaction->methodResponse.push_back(std::move(j));
1317 transaction->methodResponse.push_back(std::move(data));
1318 transaction->convertedToArray = true;
1319 }
1320 else
1321 {
1322 transaction->methodResponse.push_back(std::move(data));
1323 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001324}
1325
Ed Tanousd76323e2018-08-07 14:35:40 -07001326void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001327 const std::string &connectionName)
1328{
1329 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1330 << connectionName;
1331 crow::connections::systemBus->async_method_call(
1332 [transaction, connectionName{std::string(connectionName)}](
1333 const boost::system::error_code ec,
1334 const std::string &introspect_xml) {
1335 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1336 if (ec)
1337 {
1338 BMCWEB_LOG_ERROR
1339 << "Introspect call failed with error: " << ec.message()
1340 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001341 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001342 }
Matt Spinler318bd892019-01-15 09:59:20 -06001343 tinyxml2::XMLDocument doc;
1344
1345 doc.Parse(introspect_xml.data(), introspect_xml.size());
1346 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1347 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348 {
Matt Spinler318bd892019-01-15 09:59:20 -06001349 BMCWEB_LOG_ERROR << "XML document failed to parse "
1350 << connectionName << "\n";
1351 return;
1352 }
1353 tinyxml2::XMLElement *interfaceNode =
1354 pRoot->FirstChildElement("interface");
1355 while (interfaceNode != nullptr)
1356 {
1357 const char *thisInterfaceName =
1358 interfaceNode->Attribute("name");
1359 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001360 {
Matt Spinler318bd892019-01-15 09:59:20 -06001361 if (!transaction->interfaceName.empty() &&
1362 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001363 {
Matt Spinler318bd892019-01-15 09:59:20 -06001364 interfaceNode =
1365 interfaceNode->NextSiblingElement("interface");
1366 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001367 }
Matt Spinler318bd892019-01-15 09:59:20 -06001368
1369 tinyxml2::XMLElement *methodNode =
1370 interfaceNode->FirstChildElement("method");
1371 while (methodNode != nullptr)
1372 {
1373 const char *thisMethodName =
1374 methodNode->Attribute("name");
1375 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1376 if (thisMethodName != nullptr &&
1377 thisMethodName == transaction->methodName)
1378 {
1379 BMCWEB_LOG_DEBUG
1380 << "Found method named " << thisMethodName
1381 << " on interface " << thisInterfaceName;
1382 sdbusplus::message::message m =
1383 crow::connections::systemBus->new_method_call(
1384 connectionName.c_str(),
1385 transaction->path.c_str(),
1386 thisInterfaceName,
1387 transaction->methodName.c_str());
1388
1389 tinyxml2::XMLElement *argumentNode =
1390 methodNode->FirstChildElement("arg");
1391
Matt Spinler16caaee2019-01-15 11:40:34 -06001392 std::string returnType;
1393
1394 // Find the output type
1395 while (argumentNode != nullptr)
1396 {
1397 const char *argDirection =
1398 argumentNode->Attribute("direction");
1399 const char *argType =
1400 argumentNode->Attribute("type");
1401 if (argDirection != nullptr &&
1402 argType != nullptr &&
1403 std::string(argDirection) == "out")
1404 {
1405 returnType = argType;
1406 break;
1407 }
1408 argumentNode =
1409 argumentNode->NextSiblingElement("arg");
1410 }
1411
Matt Spinler318bd892019-01-15 09:59:20 -06001412 nlohmann::json::const_iterator argIt =
1413 transaction->arguments.begin();
1414
Matt Spinler16caaee2019-01-15 11:40:34 -06001415 argumentNode = methodNode->FirstChildElement("arg");
1416
Matt Spinler318bd892019-01-15 09:59:20 -06001417 while (argumentNode != nullptr)
1418 {
1419 const char *argDirection =
1420 argumentNode->Attribute("direction");
1421 const char *argType =
1422 argumentNode->Attribute("type");
1423 if (argDirection != nullptr &&
1424 argType != nullptr &&
1425 std::string(argDirection) == "in")
1426 {
1427 if (argIt == transaction->arguments.end())
1428 {
1429 transaction->setErrorStatus(
1430 "Invalid method args");
1431 return;
1432 }
1433 if (convertJsonToDbus(m.get(),
1434 std::string(argType),
1435 *argIt) < 0)
1436 {
1437 transaction->setErrorStatus(
1438 "Invalid method arg type");
1439 return;
1440 }
1441
1442 argIt++;
1443 }
1444 argumentNode =
1445 argumentNode->NextSiblingElement("arg");
1446 }
1447
1448 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001449 m, [transaction, returnType](
1450 boost::system::error_code ec,
1451 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001452 if (ec)
1453 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001454 transaction->methodFailed = true;
Matt Spinler06b1b632019-06-18 16:09:25 -05001455 const sd_bus_error *e = m.get_error();
1456
1457 if (e)
1458 {
1459 setErrorResponse(
1460 transaction->res,
1461 boost::beast::http::status::
1462 bad_request,
1463 e->name, e->message);
1464 }
1465 else
1466 {
1467 setErrorResponse(
1468 transaction->res,
1469 boost::beast::http::status::
1470 bad_request,
1471 "Method call failed",
1472 methodFailedMsg);
1473 }
Matt Spinler318bd892019-01-15 09:59:20 -06001474 return;
1475 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001476 else
1477 {
1478 transaction->methodPassed = true;
1479 }
1480
1481 handleMethodResponse(transaction, m,
1482 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001483 });
1484 break;
1485 }
1486 methodNode = methodNode->NextSiblingElement("method");
1487 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001488 }
Matt Spinler318bd892019-01-15 09:59:20 -06001489 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 }
1491 },
1492 connectionName, transaction->path,
1493 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001494}
1495
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001496void handleAction(const crow::Request &req, crow::Response &res,
1497 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001498{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001499 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1500 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001501 nlohmann::json requestDbusData =
1502 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001503
Ed Tanous1abe55e2018-09-05 08:30:59 -07001504 if (requestDbusData.is_discarded())
1505 {
Matt Spinler6db06242018-12-11 11:21:22 -06001506 setErrorResponse(res, boost::beast::http::status::bad_request,
1507 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001508 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001509 return;
1510 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001511 nlohmann::json::iterator data = requestDbusData.find("data");
1512 if (data == requestDbusData.end())
1513 {
Matt Spinler6db06242018-12-11 11:21:22 -06001514 setErrorResponse(res, boost::beast::http::status::bad_request,
1515 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001516 res.end();
1517 return;
1518 }
1519
1520 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001521 {
Matt Spinler6db06242018-12-11 11:21:22 -06001522 setErrorResponse(res, boost::beast::http::status::bad_request,
1523 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001524 res.end();
1525 return;
1526 }
1527 auto transaction = std::make_shared<InProgressActionData>(res);
1528
1529 transaction->path = objectPath;
1530 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001531 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001532 crow::connections::systemBus->async_method_call(
1533 [transaction](
1534 const boost::system::error_code ec,
1535 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001536 &interfaceNames) {
1537 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001538 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001539 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001540 setErrorResponse(transaction->res,
1541 boost::beast::http::status::not_found,
1542 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001543 return;
1544 }
1545
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001546 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1547 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001548
1549 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001550 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001551 {
1552 findActionOnInterface(transaction, object.first);
1553 }
1554 },
1555 "xyz.openbmc_project.ObjectMapper",
1556 "/xyz/openbmc_project/object_mapper",
1557 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1558 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001559}
1560
Matt Spinlerde818812018-12-11 16:39:20 -06001561void handleDelete(const crow::Request &req, crow::Response &res,
1562 const std::string &objectPath)
1563{
1564 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1565
1566 crow::connections::systemBus->async_method_call(
1567 [&res, objectPath](
1568 const boost::system::error_code ec,
1569 const std::vector<std::pair<std::string, std::vector<std::string>>>
1570 &interfaceNames) {
1571 if (ec || interfaceNames.size() <= 0)
1572 {
1573 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001574 setErrorResponse(res,
1575 boost::beast::http::status::method_not_allowed,
1576 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001577 res.end();
1578 return;
1579 }
1580
1581 auto transaction = std::make_shared<InProgressActionData>(res);
1582 transaction->path = objectPath;
1583 transaction->methodName = "Delete";
1584 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1585
1586 for (const std::pair<std::string, std::vector<std::string>>
1587 &object : interfaceNames)
1588 {
1589 findActionOnInterface(transaction, object.first);
1590 }
1591 },
1592 "xyz.openbmc_project.ObjectMapper",
1593 "/xyz/openbmc_project/object_mapper",
1594 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1595 std::array<const char *, 0>());
1596}
1597
Ed Tanousf839dfe2018-11-12 11:11:15 -08001598void handleList(crow::Response &res, const std::string &objectPath,
1599 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600{
1601 crow::connections::systemBus->async_method_call(
1602 [&res](const boost::system::error_code ec,
1603 std::vector<std::string> &objectPaths) {
1604 if (ec)
1605 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001606 setErrorResponse(res, boost::beast::http::status::not_found,
1607 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001608 }
1609 else
1610 {
1611 res.jsonValue = {{"status", "ok"},
1612 {"message", "200 OK"},
1613 {"data", std::move(objectPaths)}};
1614 }
1615 res.end();
1616 },
1617 "xyz.openbmc_project.ObjectMapper",
1618 "/xyz/openbmc_project/object_mapper",
1619 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001620 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001621}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001622
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001623void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001624{
Ed Tanous049a0512018-11-01 13:58:42 -07001625 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1626 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1627
1628 asyncResp->res.jsonValue = {{"message", "200 OK"},
1629 {"status", "ok"},
1630 {"data", nlohmann::json::object()}};
1631
Ed Tanous1abe55e2018-09-05 08:30:59 -07001632 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001633 [objectPath, asyncResp](const boost::system::error_code ec,
1634 GetSubTreeType &object_names) {
1635 auto transaction = std::make_shared<InProgressEnumerateData>(
1636 objectPath, asyncResp);
1637
1638 transaction->subtree =
1639 std::make_shared<GetSubTreeType>(std::move(object_names));
1640
Ed Tanous1abe55e2018-09-05 08:30:59 -07001641 if (ec)
1642 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001643 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1644 << transaction->objectPath;
1645 setErrorResponse(transaction->asyncResp->res,
1646 boost::beast::http::status::not_found,
1647 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001648 return;
1649 }
Ed Tanous64530012018-02-06 17:08:16 -08001650
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001651 // Add the data for the path passed in to the results
1652 // as if GetSubTree returned it, and continue on enumerating
1653 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001654 },
1655 "xyz.openbmc_project.ObjectMapper",
1656 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -07001657 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
1658 std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001659}
Ed Tanous911ac312017-08-15 09:37:42 -07001660
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001661void handleGet(crow::Response &res, std::string &objectPath,
1662 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001663{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001664 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1665 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001666 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001667
Ed Tanous1abe55e2018-09-05 08:30:59 -07001668 std::shared_ptr<std::string> path =
1669 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001670
Ed Tanous1abe55e2018-09-05 08:30:59 -07001671 using GetObjectType =
1672 std::vector<std::pair<std::string, std::vector<std::string>>>;
1673 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001674 [&res, path, propertyName](const boost::system::error_code ec,
1675 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001676 if (ec || object_names.size() <= 0)
1677 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001678 setErrorResponse(res, boost::beast::http::status::not_found,
1679 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001680 res.end();
1681 return;
1682 }
1683 std::shared_ptr<nlohmann::json> response =
1684 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001685 // The mapper should never give us an empty interface names
1686 // list, but check anyway
Ed Tanous1abe55e2018-09-05 08:30:59 -07001687 for (const std::pair<std::string, std::vector<std::string>>
1688 connection : object_names)
1689 {
1690 const std::vector<std::string> &interfaceNames =
1691 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001692
Ed Tanous1abe55e2018-09-05 08:30:59 -07001693 if (interfaceNames.size() <= 0)
1694 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001695 setErrorResponse(res, boost::beast::http::status::not_found,
1696 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001697 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001698 return;
1699 }
1700
1701 for (const std::string &interface : interfaceNames)
1702 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001703 sdbusplus::message::message m =
1704 crow::connections::systemBus->new_method_call(
1705 connection.first.c_str(), path->c_str(),
1706 "org.freedesktop.DBus.Properties", "GetAll");
1707 m.append(interface);
1708 crow::connections::systemBus->async_send(
1709 m, [&res, response,
1710 propertyName](const boost::system::error_code ec,
1711 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 if (ec)
1713 {
1714 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1715 << ec;
1716 }
1717 else
1718 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001719 nlohmann::json properties;
1720 int r =
1721 convertDBusToJSON("a{sv}", msg, properties);
1722 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001723 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001724 BMCWEB_LOG_ERROR
1725 << "convertDBusToJSON failed";
1726 }
1727 else
1728 {
1729 for (auto &prop : properties.items())
1730 {
Ed Tanous7c091622019-05-23 11:42:36 -07001731 // if property name is empty, or
1732 // matches our search query, add it
1733 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001735 if (propertyName->empty())
1736 {
1737 (*response)[prop.key()] =
1738 std::move(prop.value());
1739 }
1740 else if (prop.key() == *propertyName)
1741 {
1742 *response = std::move(prop.value());
1743 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001744 }
1745 }
1746 }
1747 if (response.use_count() == 1)
1748 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001749 if (!propertyName->empty() && response->empty())
1750 {
1751 setErrorResponse(
1752 res,
1753 boost::beast::http::status::not_found,
1754 propNotFoundDesc, notFoundMsg);
1755 }
1756 else
1757 {
1758 res.jsonValue = {{"status", "ok"},
1759 {"message", "200 OK"},
1760 {"data", *response}};
1761 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001762 res.end();
1763 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001764 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001765 }
1766 }
1767 },
1768 "xyz.openbmc_project.ObjectMapper",
1769 "/xyz/openbmc_project/object_mapper",
1770 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1771 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001772}
1773
Ed Tanous1abe55e2018-09-05 08:30:59 -07001774struct AsyncPutRequest
1775{
1776 AsyncPutRequest(crow::Response &res) : res(res)
1777 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001778 }
1779 ~AsyncPutRequest()
1780 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001781 if (res.jsonValue.empty())
1782 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001783 setErrorResponse(res, boost::beast::http::status::forbidden,
1784 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001785 }
1786
1787 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001788 }
1789
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001790 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001791 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001792 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1793 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001794 }
1795
Ed Tanous1abe55e2018-09-05 08:30:59 -07001796 crow::Response &res;
1797 std::string objectPath;
1798 std::string propertyName;
1799 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001800};
1801
Ed Tanousd76323e2018-08-07 14:35:40 -07001802void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001803 const std::string &objectPath, const std::string &destProperty)
1804{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001805 if (destProperty.empty())
1806 {
1807 setErrorResponse(res, boost::beast::http::status::forbidden,
1808 forbiddenResDesc, forbiddenMsg);
1809 res.end();
1810 return;
1811 }
1812
Ed Tanous1abe55e2018-09-05 08:30:59 -07001813 nlohmann::json requestDbusData =
1814 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001815
Ed Tanous1abe55e2018-09-05 08:30:59 -07001816 if (requestDbusData.is_discarded())
1817 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001818 setErrorResponse(res, boost::beast::http::status::bad_request,
1819 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001820 res.end();
1821 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001822 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001823
Ed Tanous1abe55e2018-09-05 08:30:59 -07001824 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1825 if (propertyIt == requestDbusData.end())
1826 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001827 setErrorResponse(res, boost::beast::http::status::bad_request,
1828 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001829 res.end();
1830 return;
1831 }
1832 const nlohmann::json &propertySetValue = *propertyIt;
1833 auto transaction = std::make_shared<AsyncPutRequest>(res);
1834 transaction->objectPath = objectPath;
1835 transaction->propertyName = destProperty;
1836 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001837
Ed Tanous1abe55e2018-09-05 08:30:59 -07001838 using GetObjectType =
1839 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001840
Ed Tanous1abe55e2018-09-05 08:30:59 -07001841 crow::connections::systemBus->async_method_call(
1842 [transaction](const boost::system::error_code ec,
1843 const GetObjectType &object_names) {
1844 if (!ec && object_names.size() <= 0)
1845 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001846 setErrorResponse(transaction->res,
1847 boost::beast::http::status::not_found,
1848 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001849 return;
1850 }
Ed Tanous911ac312017-08-15 09:37:42 -07001851
Ed Tanous1abe55e2018-09-05 08:30:59 -07001852 for (const std::pair<std::string, std::vector<std::string>>
1853 connection : object_names)
1854 {
1855 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001856
Ed Tanous1abe55e2018-09-05 08:30:59 -07001857 crow::connections::systemBus->async_method_call(
1858 [connectionName{std::string(connectionName)},
1859 transaction](const boost::system::error_code ec,
1860 const std::string &introspectXml) {
1861 if (ec)
1862 {
1863 BMCWEB_LOG_ERROR
1864 << "Introspect call failed with error: "
1865 << ec.message()
1866 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001867 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001868 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001869 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001870 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001871
Ed Tanous1abe55e2018-09-05 08:30:59 -07001872 doc.Parse(introspectXml.c_str());
1873 tinyxml2::XMLNode *pRoot =
1874 doc.FirstChildElement("node");
1875 if (pRoot == nullptr)
1876 {
1877 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1878 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001879 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001880 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001881 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001882 tinyxml2::XMLElement *ifaceNode =
1883 pRoot->FirstChildElement("interface");
1884 while (ifaceNode != nullptr)
1885 {
1886 const char *interfaceName =
1887 ifaceNode->Attribute("name");
1888 BMCWEB_LOG_DEBUG << "found interface "
1889 << interfaceName;
1890 tinyxml2::XMLElement *propNode =
1891 ifaceNode->FirstChildElement("property");
1892 while (propNode != nullptr)
1893 {
1894 const char *propertyName =
1895 propNode->Attribute("name");
1896 BMCWEB_LOG_DEBUG << "Found property "
1897 << propertyName;
1898 if (propertyName == transaction->propertyName)
1899 {
1900 const char *argType =
1901 propNode->Attribute("type");
1902 if (argType != nullptr)
1903 {
1904 sdbusplus::message::message m =
1905 crow::connections::systemBus
1906 ->new_method_call(
1907 connectionName.c_str(),
1908 transaction->objectPath
1909 .c_str(),
1910 "org.freedesktop.DBus."
1911 "Properties",
1912 "Set");
1913 m.append(interfaceName,
1914 transaction->propertyName);
1915 int r = sd_bus_message_open_container(
1916 m.get(), SD_BUS_TYPE_VARIANT,
1917 argType);
1918 if (r < 0)
1919 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001920 transaction->setErrorStatus(
1921 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001922 return;
1923 }
1924 r = convertJsonToDbus(
1925 m.get(), argType,
1926 transaction->propertyValue);
1927 if (r < 0)
1928 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -05001929 if (r == -ERANGE)
1930 {
1931 transaction->setErrorStatus(
1932 "Provided property value "
1933 "is out of range for the "
1934 "property type");
1935 }
1936 else
1937 {
1938 transaction->setErrorStatus(
1939 "Invalid arg type");
1940 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001941 return;
1942 }
1943 r = sd_bus_message_close_container(
1944 m.get());
1945 if (r < 0)
1946 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001947 transaction->setErrorStatus(
1948 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001949 return;
1950 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001951 crow::connections::systemBus
1952 ->async_send(
1953 m,
1954 [transaction](
1955 boost::system::error_code
1956 ec,
1957 sdbusplus::message::message
1958 &m) {
1959 BMCWEB_LOG_DEBUG << "sent";
1960 if (ec)
1961 {
Lei YU97d2a472019-06-11 17:44:27 +08001962 const sd_bus_error *e =
1963 m.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001964 setErrorResponse(
1965 transaction->res,
1966 boost::beast::http::
1967 status::
1968 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001969 (e) ? e->name
1970 : ec.category()
1971 .name(),
1972 (e) ? e->message
1973 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001974 }
1975 else
1976 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001977 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001978 .jsonValue = {
1979 {"status", "ok"},
1980 {"message",
1981 "200 OK"},
1982 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001983 }
1984 });
1985 }
1986 }
1987 propNode =
1988 propNode->NextSiblingElement("property");
1989 }
1990 ifaceNode =
1991 ifaceNode->NextSiblingElement("interface");
1992 }
1993 },
1994 connectionName, transaction->objectPath,
1995 "org.freedesktop.DBus.Introspectable", "Introspect");
1996 }
1997 },
1998 "xyz.openbmc_project.ObjectMapper",
1999 "/xyz/openbmc_project/object_mapper",
2000 "xyz.openbmc_project.ObjectMapper", "GetObject",
2001 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002002}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002003
Ed Tanous049a0512018-11-01 13:58:42 -07002004inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
2005 std::string &objectPath)
2006{
Ed Tanous049a0512018-11-01 13:58:42 -07002007
2008 // If accessing a single attribute, fill in and update objectPath,
2009 // otherwise leave destProperty blank
2010 std::string destProperty = "";
2011 const char *attrSeperator = "/attr/";
2012 size_t attrPosition = objectPath.find(attrSeperator);
2013 if (attrPosition != objectPath.npos)
2014 {
2015 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2016 objectPath.length());
2017 objectPath = objectPath.substr(0, attrPosition);
2018 }
2019
2020 if (req.method() == "POST"_method)
2021 {
2022 constexpr const char *actionSeperator = "/action/";
2023 size_t actionPosition = objectPath.find(actionSeperator);
2024 if (actionPosition != objectPath.npos)
2025 {
2026 std::string postProperty =
2027 objectPath.substr((actionPosition + strlen(actionSeperator)),
2028 objectPath.length());
2029 objectPath = objectPath.substr(0, actionPosition);
2030 handleAction(req, res, objectPath, postProperty);
2031 return;
2032 }
2033 }
2034 else if (req.method() == "GET"_method)
2035 {
2036 if (boost::ends_with(objectPath, "/enumerate"))
2037 {
2038 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2039 objectPath.end());
2040 handleEnumerate(res, objectPath);
2041 }
2042 else if (boost::ends_with(objectPath, "/list"))
2043 {
2044 objectPath.erase(objectPath.end() - sizeof("list"),
2045 objectPath.end());
2046 handleList(res, objectPath);
2047 }
2048 else
2049 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002050 // Trim any trailing "/" at the end
2051 if (boost::ends_with(objectPath, "/"))
2052 {
2053 objectPath.pop_back();
2054 handleList(res, objectPath, 1);
2055 }
2056 else
2057 {
2058 handleGet(res, objectPath, destProperty);
2059 }
Ed Tanous049a0512018-11-01 13:58:42 -07002060 }
2061 return;
2062 }
2063 else if (req.method() == "PUT"_method)
2064 {
2065 handlePut(req, res, objectPath, destProperty);
2066 return;
2067 }
Matt Spinlerde818812018-12-11 16:39:20 -06002068 else if (req.method() == "DELETE"_method)
2069 {
2070 handleDelete(req, res, objectPath);
2071 return;
2072 }
Ed Tanous049a0512018-11-01 13:58:42 -07002073
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002074 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2075 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002076 res.end();
2077}
2078
Ed Tanous1abe55e2018-09-05 08:30:59 -07002079template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
2080{
2081 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03002082 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002083 .methods("GET"_method)(
2084 [](const crow::Request &req, crow::Response &res) {
2085 res.jsonValue = {{"busses", {{{"name", "system"}}}},
2086 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002087 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002088 });
2089
2090 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03002091 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002092 .methods("GET"_method)(
2093 [](const crow::Request &req, crow::Response &res) {
2094 auto myCallback = [&res](const boost::system::error_code ec,
2095 std::vector<std::string> &names) {
2096 if (ec)
2097 {
2098 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2099 res.result(
2100 boost::beast::http::status::internal_server_error);
2101 }
2102 else
2103 {
2104 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002105 res.jsonValue = {{"status", "ok"}};
2106 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002107 for (auto &name : names)
2108 {
2109 objectsSub.push_back({{"name", name}});
2110 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002111 }
2112 res.end();
2113 };
2114 crow::connections::systemBus->async_method_call(
2115 std::move(myCallback), "org.freedesktop.DBus", "/",
2116 "org.freedesktop.DBus", "ListNames");
2117 });
2118
2119 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002120 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002121 .methods("GET"_method)(
2122 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002123 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002124 });
2125
2126 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002127 .requires({"Login"})
2128 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2129 const std::string &path) {
2130 std::string objectPath = "/xyz/" + path;
2131 handleDBusUrl(req, res, objectPath);
2132 });
2133
2134 BMCWEB_ROUTE(app, "/xyz/<path>")
2135 .requires({"ConfigureComponents", "ConfigureManager"})
2136 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002137 [](const crow::Request &req, crow::Response &res,
2138 const std::string &path) {
2139 std::string objectPath = "/xyz/" + path;
2140 handleDBusUrl(req, res, objectPath);
2141 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002142
Ed Tanous049a0512018-11-01 13:58:42 -07002143 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002144 .requires({"Login"})
2145 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2146 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002147 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002148 handleDBusUrl(req, res, objectPath);
2149 });
2150
2151 BMCWEB_ROUTE(app, "/org/<path>")
2152 .requires({"ConfigureComponents", "ConfigureManager"})
2153 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002154 [](const crow::Request &req, crow::Response &res,
2155 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002156 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002157 handleDBusUrl(req, res, objectPath);
2158 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002159
Ed Tanous1abe55e2018-09-05 08:30:59 -07002160 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002161 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002162 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2163 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002164 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002165 if (!std::regex_match(dumpId, validFilename))
2166 {
Ed Tanousad18f072018-11-14 14:07:48 -08002167 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002168 res.end();
2169 return;
2170 }
James Feistf6150402019-01-08 10:36:20 -08002171 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002172 "/var/lib/phosphor-debug-collector/dumps");
2173
Ed Tanousad18f072018-11-14 14:07:48 -08002174 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002175
James Feistf6150402019-01-08 10:36:20 -08002176 if (!std::filesystem::exists(loc) ||
2177 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002178 {
Ed Tanousad18f072018-11-14 14:07:48 -08002179 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002180 res.result(boost::beast::http::status::not_found);
2181 res.end();
2182 return;
2183 }
James Feistf6150402019-01-08 10:36:20 -08002184 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002185
Ed Tanous1abe55e2018-09-05 08:30:59 -07002186 for (auto &file : files)
2187 {
2188 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002189 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002190 {
2191 continue;
2192 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002193
Ed Tanous1abe55e2018-09-05 08:30:59 -07002194 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002195
2196 // Assuming only one dump file will be present in the dump id
2197 // directory
2198 std::string dumpFileName = file.path().filename().string();
2199
2200 // Filename should be in alphanumeric, dot and underscore
2201 // Its based on phosphor-debug-collector application dumpfile
2202 // format
2203 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2204 if (!std::regex_match(dumpFileName, dumpFileRegex))
2205 {
2206 BMCWEB_LOG_ERROR << "Invalid dump filename "
2207 << dumpFileName;
2208 res.result(boost::beast::http::status::not_found);
2209 res.end();
2210 return;
2211 }
2212 std::string contentDispositionParam =
2213 "attachment; filename=\"" + dumpFileName + "\"";
2214
2215 res.addHeader("Content-Disposition", contentDispositionParam);
2216
Ed Tanous1abe55e2018-09-05 08:30:59 -07002217 res.body() = {std::istreambuf_iterator<char>(readFile),
2218 std::istreambuf_iterator<char>()};
2219 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002220 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002221 }
2222 res.result(boost::beast::http::status::not_found);
2223 res.end();
2224 return;
2225 });
2226
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002227 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002228 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002229 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002230 const std::string &Connection) {
2231 introspectObjects(Connection, "/",
2232 std::make_shared<bmcweb::AsyncResp>(res));
2233 });
2234
2235 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2236 .methods("GET"_method,
2237 "POST"_method)([](const crow::Request &req,
2238 crow::Response &res,
2239 const std::string &processName,
2240 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002241 std::vector<std::string> strs;
2242 boost::split(strs, requestedPath, boost::is_any_of("/"));
2243 std::string objectPath;
2244 std::string interfaceName;
2245 std::string methodName;
2246 auto it = strs.begin();
2247 if (it == strs.end())
2248 {
2249 objectPath = "/";
2250 }
2251 while (it != strs.end())
2252 {
2253 // Check if segment contains ".". If it does, it must be an
2254 // interface
2255 if (it->find(".") != std::string::npos)
2256 {
2257 break;
Ed Tanous7c091622019-05-23 11:42:36 -07002258 // This check is neccesary as the trailing slash gets
2259 // parsed as part of our <path> specifier above, which
2260 // causes the normal trailing backslash redirector to
2261 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002262 }
2263 else if (!it->empty())
2264 {
2265 objectPath += "/" + *it;
2266 }
2267 it++;
2268 }
2269 if (it != strs.end())
2270 {
2271 interfaceName = *it;
2272 it++;
2273
2274 // after interface, we might have a method name
2275 if (it != strs.end())
2276 {
2277 methodName = *it;
2278 it++;
2279 }
2280 }
2281 if (it != strs.end())
2282 {
Ed Tanous7c091622019-05-23 11:42:36 -07002283 // if there is more levels past the method name, something
2284 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002285 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002286 return;
2287 }
2288 if (interfaceName.empty())
2289 {
Ed Tanous7c091622019-05-23 11:42:36 -07002290 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2291 std::make_shared<bmcweb::AsyncResp>(res);
2292
Ed Tanous1abe55e2018-09-05 08:30:59 -07002293 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002294 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002295 objectPath](const boost::system::error_code ec,
2296 const std::string &introspect_xml) {
2297 if (ec)
2298 {
2299 BMCWEB_LOG_ERROR
2300 << "Introspect call failed with error: "
2301 << ec.message()
2302 << " on process: " << processName
2303 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002304 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002305 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002306 tinyxml2::XMLDocument doc;
2307
2308 doc.Parse(introspect_xml.c_str());
2309 tinyxml2::XMLNode *pRoot =
2310 doc.FirstChildElement("node");
2311 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002312 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002313 BMCWEB_LOG_ERROR << "XML document failed to parse "
2314 << processName << " " << objectPath
2315 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002316 asyncResp->res.jsonValue = {
2317 {"status", "XML parse error"}};
2318 asyncResp->res.result(boost::beast::http::status::
2319 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002320 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002321 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002322
2323 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002324 asyncResp->res.jsonValue = {
2325 {"status", "ok"},
2326 {"bus_name", processName},
2327 {"object_path", objectPath}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002328 nlohmann::json &interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002329 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002330 interfacesArray = nlohmann::json::array();
2331 tinyxml2::XMLElement *interface =
2332 pRoot->FirstChildElement("interface");
2333
2334 while (interface != nullptr)
2335 {
2336 const char *ifaceName =
2337 interface->Attribute("name");
2338 if (ifaceName != nullptr)
2339 {
2340 interfacesArray.push_back(
2341 {{"name", ifaceName}});
2342 }
2343
2344 interface =
2345 interface->NextSiblingElement("interface");
2346 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002347 },
2348 processName, objectPath,
2349 "org.freedesktop.DBus.Introspectable", "Introspect");
2350 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002351 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002352 {
Ed Tanous7c091622019-05-23 11:42:36 -07002353 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2354 std::make_shared<bmcweb::AsyncResp>(res);
2355
Ed Tanous1abe55e2018-09-05 08:30:59 -07002356 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002357 [asyncResp, processName, objectPath,
2358 interfaceName](const boost::system::error_code ec,
2359 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002360 if (ec)
2361 {
2362 BMCWEB_LOG_ERROR
2363 << "Introspect call failed with error: "
2364 << ec.message()
2365 << " on process: " << processName
2366 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002367 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002368 }
Ed Tanous7c091622019-05-23 11:42:36 -07002369 tinyxml2::XMLDocument doc;
2370
2371 doc.Parse(introspect_xml.data(), introspect_xml.size());
2372 tinyxml2::XMLNode *pRoot =
2373 doc.FirstChildElement("node");
2374 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002375 {
Ed Tanous7c091622019-05-23 11:42:36 -07002376 BMCWEB_LOG_ERROR << "XML document failed to parse "
2377 << processName << " " << objectPath
2378 << "\n";
2379 asyncResp->res.result(boost::beast::http::status::
2380 internal_server_error);
2381 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002382 }
Ed Tanous7c091622019-05-23 11:42:36 -07002383 asyncResp->res.jsonValue = {
2384 {"status", "ok"},
2385 {"bus_name", processName},
2386 {"interface", interfaceName},
2387 {"object_path", objectPath}};
2388
2389 nlohmann::json &methodsArray =
2390 asyncResp->res.jsonValue["methods"];
2391 methodsArray = nlohmann::json::array();
2392
2393 nlohmann::json &signalsArray =
2394 asyncResp->res.jsonValue["signals"];
2395 signalsArray = nlohmann::json::array();
2396
2397 nlohmann::json &propertiesObj =
2398 asyncResp->res.jsonValue["properties"];
2399 propertiesObj = nlohmann::json::object();
2400
2401 // if we know we're the only call, build the
2402 // json directly
2403 tinyxml2::XMLElement *interface =
2404 pRoot->FirstChildElement("interface");
2405 while (interface != nullptr)
2406 {
2407 const char *ifaceName =
2408 interface->Attribute("name");
2409
2410 if (ifaceName != nullptr &&
2411 ifaceName == interfaceName)
2412 {
2413 break;
2414 }
2415
2416 interface =
2417 interface->NextSiblingElement("interface");
2418 }
2419 if (interface == nullptr)
2420 {
2421 // if we got to the end of the list and
2422 // never found a match, throw 404
2423 asyncResp->res.result(
2424 boost::beast::http::status::not_found);
2425 return;
2426 }
2427
2428 tinyxml2::XMLElement *methods =
2429 interface->FirstChildElement("method");
2430 while (methods != nullptr)
2431 {
2432 nlohmann::json argsArray = nlohmann::json::array();
2433 tinyxml2::XMLElement *arg =
2434 methods->FirstChildElement("arg");
2435 while (arg != nullptr)
2436 {
2437 nlohmann::json thisArg;
2438 for (const char *fieldName :
2439 std::array<const char *, 3>{
2440 "name", "direction", "type"})
2441 {
2442 const char *fieldValue =
2443 arg->Attribute(fieldName);
2444 if (fieldValue != nullptr)
2445 {
2446 thisArg[fieldName] = fieldValue;
2447 }
2448 }
2449 argsArray.push_back(std::move(thisArg));
2450 arg = arg->NextSiblingElement("arg");
2451 }
2452
2453 const char *name = methods->Attribute("name");
2454 if (name != nullptr)
2455 {
2456 methodsArray.push_back(
2457 {{"name", name},
2458 {"uri", "/bus/system/" + processName +
2459 objectPath + "/" +
2460 interfaceName + "/" + name},
2461 {"args", argsArray}});
2462 }
2463 methods = methods->NextSiblingElement("method");
2464 }
2465 tinyxml2::XMLElement *signals =
2466 interface->FirstChildElement("signal");
2467 while (signals != nullptr)
2468 {
2469 nlohmann::json argsArray = nlohmann::json::array();
2470
2471 tinyxml2::XMLElement *arg =
2472 signals->FirstChildElement("arg");
2473 while (arg != nullptr)
2474 {
2475 const char *name = arg->Attribute("name");
2476 const char *type = arg->Attribute("type");
2477 if (name != nullptr && type != nullptr)
2478 {
2479 argsArray.push_back({
2480 {"name", name},
2481 {"type", type},
2482 });
2483 }
2484 arg = arg->NextSiblingElement("arg");
2485 }
2486 const char *name = signals->Attribute("name");
2487 if (name != nullptr)
2488 {
2489 signalsArray.push_back(
2490 {{"name", name}, {"args", argsArray}});
2491 }
2492
2493 signals = signals->NextSiblingElement("signal");
2494 }
2495
2496 tinyxml2::XMLElement *property =
2497 interface->FirstChildElement("property");
2498 while (property != nullptr)
2499 {
2500 const char *name = property->Attribute("name");
2501 const char *type = property->Attribute("type");
2502 if (type != nullptr && name != nullptr)
2503 {
2504 sdbusplus::message::message m =
2505 crow::connections::systemBus
2506 ->new_method_call(processName.c_str(),
2507 objectPath.c_str(),
2508 "org.freedesktop."
2509 "DBus."
2510 "Properties",
2511 "Get");
2512 m.append(interfaceName, name);
2513 nlohmann::json &propertyItem =
2514 propertiesObj[name];
2515 crow::connections::systemBus->async_send(
2516 m, [&propertyItem, asyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -07002517 boost::system::error_code &e,
2518 sdbusplus::message::message &msg) {
2519 if (e)
Ed Tanous7c091622019-05-23 11:42:36 -07002520 {
2521 return;
2522 }
2523
Ed Tanous271584a2019-07-09 16:24:22 -07002524 convertDBusToJSON("v", msg,
2525 propertyItem);
Ed Tanous7c091622019-05-23 11:42:36 -07002526 });
2527 }
2528 property = property->NextSiblingElement("property");
2529 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002530 },
2531 processName, objectPath,
2532 "org.freedesktop.DBus.Introspectable", "Introspect");
2533 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002534 else
2535 {
2536 if (req.method() != "POST"_method)
2537 {
2538 res.result(boost::beast::http::status::not_found);
2539 res.end();
2540 return;
2541 }
2542
2543 nlohmann::json requestDbusData =
2544 nlohmann::json::parse(req.body, nullptr, false);
2545
2546 if (requestDbusData.is_discarded())
2547 {
2548 res.result(boost::beast::http::status::bad_request);
2549 res.end();
2550 return;
2551 }
2552 if (!requestDbusData.is_array())
2553 {
2554 res.result(boost::beast::http::status::bad_request);
2555 res.end();
2556 return;
2557 }
2558 auto transaction = std::make_shared<InProgressActionData>(res);
2559
2560 transaction->path = objectPath;
2561 transaction->methodName = methodName;
2562 transaction->arguments = std::move(requestDbusData);
2563
2564 findActionOnInterface(transaction, processName);
2565 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002566 });
2567}
2568} // namespace openbmc_mapper
2569} // namespace crow