blob: b063fa6e967341009c7e626ca4d79472e5339fb3 [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>
Joseph Reynolds8fd315a2019-09-12 12:02:33 -050026#include <json_msg_utility.hpp>
Ramesh Iyyard9207042019-07-05 08:04:42 -050027#include <regex>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070028#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace crow
31{
32namespace openbmc_mapper
33{
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
Ed Tanouse3cb5a32018-08-08 14:16:49 -070038void introspectObjects(const std::string &processName,
39 const std::string &objectPath,
40 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070041{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070042 if (transaction->res.jsonValue.is_null())
43 {
44 transaction->res.jsonValue = {{"status", "ok"},
45 {"bus_name", processName},
46 {"objects", nlohmann::json::array()}};
47 }
48
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070050 [transaction, processName{std::string(processName)},
51 objectPath{std::string(objectPath)}](
52 const boost::system::error_code ec,
53 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 if (ec)
55 {
56 BMCWEB_LOG_ERROR
57 << "Introspect call failed with error: " << ec.message()
58 << " on process: " << processName << " path: " << objectPath
59 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070060 return;
61 }
62 transaction->res.jsonValue["objects"].push_back(
63 {{"path", objectPath}});
64
65 tinyxml2::XMLDocument doc;
66
67 doc.Parse(introspect_xml.c_str());
68 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
69 if (pRoot == nullptr)
70 {
71 BMCWEB_LOG_ERROR << "XML document failed to parse "
72 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070073 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 else
75 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070076 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
77 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070079 const char *childPath = node->Attribute("name");
80 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 std::string newpath;
83 if (objectPath != "/")
84 {
85 newpath += objectPath;
86 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070087 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -070089 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070091
92 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 }
94 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -070096 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070098}
Ed Tanous64530012018-02-06 17:08:16 -080099
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600100void getPropertiesForEnumerate(const std::string &objectPath,
101 const std::string &service,
102 const std::string &interface,
103 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
104{
105 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
106 << service << " " << interface;
107
108 crow::connections::systemBus->async_method_call(
109 [asyncResp, objectPath, service,
110 interface](const boost::system::error_code ec,
111 const std::vector<
112 std::pair<std::string, dbus::utility::DbusVariantType>>
113 &propertiesList) {
114 if (ec)
115 {
116 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
117 << interface << " service " << service
118 << " failed with code " << ec;
119 return;
120 }
121
122 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
123 nlohmann::json &objectJson = dataJson[objectPath];
124 if (objectJson.is_null())
125 {
126 objectJson = nlohmann::json::object();
127 }
128
129 for (const auto &[name, value] : propertiesList)
130 {
131 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800132 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
133 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600134 }
135 },
136 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
137 interface);
138}
139
140// Find any results that weren't picked up by ObjectManagers, to be
141// called after all ObjectManagers are searched for and called.
142void findRemainingObjectsForEnumerate(
143 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
144 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
145{
146 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
147 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
148
149 for (const auto &[path, interface_map] : *subtree)
150 {
151 if (path == objectPath)
152 {
153 // An enumerate does not return the target path's properties
154 continue;
155 }
156 if (dataJson.find(path) == dataJson.end())
157 {
158 for (const auto &[service, interfaces] : interface_map)
159 {
160 for (const auto &interface : interfaces)
161 {
162 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
163 {
164 getPropertiesForEnumerate(path, service, interface,
165 asyncResp);
166 }
167 }
168 }
169 }
170 }
171}
172
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600173struct InProgressEnumerateData
174{
175 InProgressEnumerateData(const std::string &objectPath,
176 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
177 objectPath(objectPath),
178 asyncResp(asyncResp)
179 {
180 }
181
182 ~InProgressEnumerateData()
183 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600184 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600185 }
186
187 const std::string objectPath;
188 std::shared_ptr<GetSubTreeType> subtree;
189 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
190};
191
192void getManagedObjectsForEnumerate(
193 const std::string &object_name, const std::string &object_manager_path,
194 const std::string &connection_name,
195 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700196{
Ed Tanous049a0512018-11-01 13:58:42 -0700197 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
198 << " object_manager_path " << object_manager_path
199 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600201 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700202 connection_name](const boost::system::error_code ec,
203 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700204 if (ec)
205 {
Ed Tanous049a0512018-11-01 13:58:42 -0700206 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600207 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700208 << " failed with code " << ec;
209 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700210 }
Ed Tanous64530012018-02-06 17:08:16 -0800211
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600212 nlohmann::json &dataJson =
213 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700214
215 for (const auto &objectPath : objects)
216 {
217 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700218 {
Ed Tanous049a0512018-11-01 13:58:42 -0700219 BMCWEB_LOG_DEBUG << "Reading object "
220 << objectPath.first.str;
221 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222 if (objectJson.is_null())
223 {
224 objectJson = nlohmann::json::object();
225 }
226 for (const auto &interface : objectPath.second)
227 {
228 for (const auto &property : interface.second)
229 {
230 nlohmann::json &propertyJson =
231 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800232 std::visit([&propertyJson](
233 auto &&val) { propertyJson = val; },
234 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 }
236 }
237 }
Ed Tanous049a0512018-11-01 13:58:42 -0700238 for (const auto &interface : objectPath.second)
239 {
240 if (interface.first == "org.freedesktop.DBus.ObjectManager")
241 {
242 getManagedObjectsForEnumerate(
243 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600244 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700245 }
246 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 }
248 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700249 connection_name, object_manager_path,
250 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
251}
252
253void findObjectManagerPathForEnumerate(
254 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600255 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700256{
Ed Tanous049a0512018-11-01 13:58:42 -0700257 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
258 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700259 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600260 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700261 const boost::system::error_code ec,
262 const boost::container::flat_map<
263 std::string, boost::container::flat_map<
264 std::string, std::vector<std::string>>>
265 &objects) {
266 if (ec)
267 {
Ed Tanous049a0512018-11-01 13:58:42 -0700268 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
269 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700270 return;
271 }
272
Ed Tanousf254ba72018-10-12 13:40:35 -0700273 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700274 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700275 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700276 {
277 if (connectionGroup.first == connection_name)
278 {
279 // Found the object manager path for this resource.
280 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700281 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600282 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700283 return;
284 }
285 }
286 }
287 },
288 "xyz.openbmc_project.ObjectMapper",
289 "/xyz/openbmc_project/object_mapper",
290 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
291 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700292}
Ed Tanous64530012018-02-06 17:08:16 -0800293
Ed Tanous7c091622019-05-23 11:42:36 -0700294// Uses GetObject to add the object info about the target /enumerate path to
295// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600296// target path, and then continues on enumerating the rest of the tree.
297void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
298{
299 using GetObjectType =
300 std::vector<std::pair<std::string, std::vector<std::string>>>;
301
302 crow::connections::systemBus->async_method_call(
303 [transaction](const boost::system::error_code ec,
304 const GetObjectType &objects) {
305 if (ec)
306 {
307 BMCWEB_LOG_ERROR << "GetObject for path "
308 << transaction->objectPath
309 << " failed with code " << ec;
310 return;
311 }
312
313 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
314 << " has " << objects.size() << " entries";
315 if (!objects.empty())
316 {
317 transaction->subtree->emplace_back(transaction->objectPath,
318 objects);
319 }
320
321 // Map indicating connection name, and the path where the object
322 // manager exists
323 boost::container::flat_map<std::string, std::string> connections;
324
325 for (const auto &object : *(transaction->subtree))
326 {
327 for (const auto &connection : object.second)
328 {
329 std::string &objectManagerPath =
330 connections[connection.first];
331 for (const auto &interface : connection.second)
332 {
333 BMCWEB_LOG_DEBUG << connection.first
334 << " has interface " << interface;
335 if (interface == "org.freedesktop.DBus.ObjectManager")
336 {
337 BMCWEB_LOG_DEBUG << "found object manager path "
338 << object.first;
339 objectManagerPath = object.first;
340 }
341 }
342 }
343 }
344 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
345
346 for (const auto &connection : connections)
347 {
Ed Tanous7c091622019-05-23 11:42:36 -0700348 // If we already know where the object manager is, we don't
349 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600350 // getManagedObjects
351 if (!connection.second.empty())
352 {
353 getManagedObjectsForEnumerate(
354 transaction->objectPath, connection.second,
355 connection.first, transaction);
356 }
357 else
358 {
Ed Tanous7c091622019-05-23 11:42:36 -0700359 // otherwise we need to find the object manager path
360 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600361 findObjectManagerPathForEnumerate(
362 transaction->objectPath, connection.first, transaction);
363 }
364 }
365 },
366 "xyz.openbmc_project.ObjectMapper",
367 "/xyz/openbmc_project/object_mapper",
368 "xyz.openbmc_project.ObjectMapper", "GetObject",
369 transaction->objectPath, std::array<const char *, 0>());
370}
Ed Tanous64530012018-02-06 17:08:16 -0800371
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700372// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700373struct InProgressActionData
374{
375 InProgressActionData(crow::Response &res) : res(res){};
376 ~InProgressActionData()
377 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600378 // Methods could have been called across different owners
379 // and interfaces, where some calls failed and some passed.
380 //
381 // The rules for this are:
382 // * if no method was called - error
383 // * if a method failed and none passed - error
384 // (converse: if at least one method passed - OK)
385 // * for the method output:
386 // * if output processing didn't fail, return the data
387
388 // Only deal with method returns if nothing failed earlier
389 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700390 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600391 if (!methodPassed)
392 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500393 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600394 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -0500395 crow::setErrorResponse(
396 res, boost::beast::http::status::not_found,
397 crow::msg::methodNotFoundDesc, crow::msg::notFoundMsg);
Matt Spinler16caaee2019-01-15 11:40:34 -0600398 }
399 }
400 else
401 {
402 if (outputFailed)
403 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -0500404 crow::setErrorResponse(
Matt Spinler16caaee2019-01-15 11:40:34 -0600405 res, boost::beast::http::status::internal_server_error,
Joseph Reynolds8fd315a2019-09-12 12:02:33 -0500406 "Method output failure",
407 crow::msg::methodOutputFailedMsg);
Matt Spinler16caaee2019-01-15 11:40:34 -0600408 }
409 else
410 {
411 res.jsonValue = {{"status", "ok"},
412 {"message", "200 OK"},
413 {"data", methodResponse}};
414 }
415 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600417
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700419 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700420
Matt Spinler6db06242018-12-11 11:21:22 -0600421 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -0500423 crow::setErrorResponse(res, boost::beast::http::status::bad_request,
424 desc, crow::msg::badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425 }
426 crow::Response &res;
427 std::string path;
428 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600429 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600430 bool methodPassed = false;
431 bool methodFailed = false;
432 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600433 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600434 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700436};
437
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438std::vector<std::string> dbusArgSplit(const std::string &string)
439{
440 std::vector<std::string> ret;
441 if (string.empty())
442 {
443 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700444 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 ret.push_back("");
446 int containerDepth = 0;
447
448 for (std::string::const_iterator character = string.begin();
449 character != string.end(); character++)
450 {
451 ret.back() += *character;
452 switch (*character)
453 {
454 case ('a'):
455 break;
456 case ('('):
457 case ('{'):
458 containerDepth++;
459 break;
460 case ('}'):
461 case (')'):
462 containerDepth--;
463 if (containerDepth == 0)
464 {
465 if (character + 1 != string.end())
466 {
467 ret.push_back("");
468 }
469 }
470 break;
471 default:
472 if (containerDepth == 0)
473 {
474 if (character + 1 != string.end())
475 {
476 ret.push_back("");
477 }
478 }
479 break;
480 }
481 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600482
483 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700484}
485
Ed Tanousd76323e2018-08-07 14:35:40 -0700486int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700487 const nlohmann::json &input_json)
488{
489 int r = 0;
490 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
491 << " to type: " << arg_type;
492 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700493
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 // Assume a single object for now.
495 const nlohmann::json *j = &input_json;
496 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700497
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700498 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 {
500 // If we are decoding multiple objects, grab the pointer to the
501 // iterator, and increment it for the next loop
502 if (argTypes.size() > 1)
503 {
504 if (jIt == input_json.end())
505 {
506 return -2;
507 }
508 j = &*jIt;
509 jIt++;
510 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700511 const int64_t *intValue = j->get_ptr<const int64_t *>();
512 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
513 const std::string *stringValue = j->get_ptr<const std::string *>();
514 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 const bool *b = j->get_ptr<const bool *>();
516 int64_t v = 0;
517 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700518
Ed Tanous1abe55e2018-09-05 08:30:59 -0700519 // Do some basic type conversions that make sense. uint can be
520 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700521 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700523 v = static_cast<int64_t>(*uintValue);
524 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700525 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700526 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700528 d = static_cast<double>(*uintValue);
529 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700531 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700532 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700533 d = static_cast<double>(*intValue);
534 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700535 }
536
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700537 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700539 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540 {
541 return -1;
542 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000543 r = sd_bus_message_append_basic(m, argCode[0],
544 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 if (r < 0)
546 {
547 return r;
548 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700549 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700550 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 {
554 return -1;
555 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500556 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
557 (*intValue > std::numeric_limits<int32_t>::max()))
558 {
559 return -ERANGE;
560 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 int32_t i = static_cast<int32_t>(*intValue);
562 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 if (r < 0)
564 {
565 return r;
566 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700567 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
570 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700571 int boolInt = false;
572 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500574 if (*intValue == 1)
575 {
576 boolInt = true;
577 }
578 else if (*intValue == 0)
579 {
580 boolInt = false;
581 }
582 else
583 {
584 return -ERANGE;
585 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 }
587 else if (b != nullptr)
588 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600589 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700590 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700591 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700593 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 }
595 else
596 {
597 return -1;
598 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700599 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600 if (r < 0)
601 {
602 return r;
603 }
604 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700605 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700607 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 {
609 return -1;
610 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500611 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
612 (*intValue > std::numeric_limits<int16_t>::max()))
613 {
614 return -ERANGE;
615 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700616 int16_t n = static_cast<int16_t>(*intValue);
617 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 if (r < 0)
619 {
620 return r;
621 }
622 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700623 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700625 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 {
627 return -1;
628 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500629 if ((*intValue < std::numeric_limits<int64_t>::lowest()) ||
630 (*intValue > std::numeric_limits<int64_t>::max()))
631 {
632 return -ERANGE;
633 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700634 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 if (r < 0)
636 {
637 return r;
638 }
639 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700640 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700642 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700643 {
644 return -1;
645 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500646 if ((*uintValue < std::numeric_limits<uint8_t>::lowest()) ||
647 (*uintValue > std::numeric_limits<uint8_t>::max()))
648 {
649 return -ERANGE;
650 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700651 uint8_t y = static_cast<uint8_t>(*uintValue);
652 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700654 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700656 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 {
658 return -1;
659 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500660 if ((*uintValue < std::numeric_limits<uint16_t>::lowest()) ||
661 (*uintValue > std::numeric_limits<uint16_t>::max()))
662 {
663 return -ERANGE;
664 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700665 uint16_t q = static_cast<uint16_t>(*uintValue);
666 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700668 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700670 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 {
672 return -1;
673 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500674 if ((*uintValue < std::numeric_limits<uint32_t>::lowest()) ||
675 (*uintValue > std::numeric_limits<uint32_t>::max()))
676 {
677 return -ERANGE;
678 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700679 uint32_t u = static_cast<uint32_t>(*uintValue);
680 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 {
686 return -1;
687 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500688 if ((*uintValue < std::numeric_limits<uint64_t>::lowest()) ||
689 (*uintValue > std::numeric_limits<uint64_t>::max()))
690 {
691 return -ERANGE;
692 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700693 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700695 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500697 if (doubleValue == nullptr)
698 {
699 return -1;
700 }
701 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
702 (*doubleValue > std::numeric_limits<double>::max()))
703 {
704 return -ERANGE;
705 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700706 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700708 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700710 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700712 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 if (r < 0)
714 {
715 return r;
716 }
717
718 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
719 ++it)
720 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700721 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 if (r < 0)
723 {
724 return r;
725 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 }
727 sd_bus_message_close_container(m);
728 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700729 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700731 std::string containedType = argCode.substr(1);
732 BMCWEB_LOG_DEBUG << "variant type: " << argCode
733 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700735 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 if (r < 0)
737 {
738 return r;
739 }
740
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700741 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 if (r < 0)
743 {
744 return r;
745 }
746
747 r = sd_bus_message_close_container(m);
748 if (r < 0)
749 {
750 return r;
751 }
752 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700753 else if (boost::starts_with(argCode, "(") &&
754 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700756 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700758 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800759 if (r < 0)
760 {
761 return r;
762 }
763
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000765 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 {
767 if (it == j->end())
768 {
769 return -1;
770 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000771 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 if (r < 0)
773 {
774 return r;
775 }
776 it++;
777 }
778 r = sd_bus_message_close_container(m);
779 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700780 else if (boost::starts_with(argCode, "{") &&
781 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700782 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700783 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700784 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700785 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800786 if (r < 0)
787 {
788 return r;
789 }
790
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700791 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 if (codes.size() != 2)
793 {
794 return -1;
795 }
796 const std::string &key_type = codes[0];
797 const std::string &value_type = codes[1];
798 for (auto it : j->items())
799 {
800 r = convertJsonToDbus(m, key_type, it.key());
801 if (r < 0)
802 {
803 return r;
804 }
805
806 r = convertJsonToDbus(m, value_type, it.value());
807 if (r < 0)
808 {
809 return r;
810 }
811 }
812 r = sd_bus_message_close_container(m);
813 }
814 else
815 {
816 return -2;
817 }
818 if (r < 0)
819 {
820 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700821 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700822
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 if (argTypes.size() > 1)
824 {
825 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700826 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700827 }
Matt Spinler127ea542019-01-14 11:04:28 -0600828
829 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700830}
831
Matt Spinlerd22a7132019-01-14 12:14:30 -0600832template <typename T>
833int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
834 nlohmann::json &data)
835{
836 T value;
837
838 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
839 if (r < 0)
840 {
841 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
842 << " failed!";
843 return r;
844 }
845
846 data = value;
847 return 0;
848}
849
Matt Spinler16caaee2019-01-15 11:40:34 -0600850int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600851 sdbusplus::message::message &m, nlohmann::json &response);
852
853int readDictEntryFromMessage(const std::string &typeCode,
854 sdbusplus::message::message &m,
855 nlohmann::json &object)
856{
857 std::vector<std::string> types = dbusArgSplit(typeCode);
858 if (types.size() != 2)
859 {
860 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
861 << types.size();
862 return -1;
863 }
864
865 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
866 typeCode.c_str());
867 if (r < 0)
868 {
869 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
870 return r;
871 }
872
873 nlohmann::json key;
874 r = convertDBusToJSON(types[0], m, key);
875 if (r < 0)
876 {
877 return r;
878 }
879
880 const std::string *keyPtr = key.get_ptr<const std::string *>();
881 if (keyPtr == nullptr)
882 {
883 // json doesn't support non-string keys. If we hit this condition,
884 // convert the result to a string so we can proceed
885 key = key.dump();
886 keyPtr = key.get_ptr<const std::string *>();
Ed Tanous7c091622019-05-23 11:42:36 -0700887 // in theory this can't fail now, but lets be paranoid about it
888 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600889 if (keyPtr == nullptr)
890 {
891 return -1;
892 }
893 }
894 nlohmann::json &value = object[*keyPtr];
895
896 r = convertDBusToJSON(types[1], m, value);
897 if (r < 0)
898 {
899 return r;
900 }
901
902 r = sd_bus_message_exit_container(m.get());
903 if (r < 0)
904 {
905 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
906 return r;
907 }
908
909 return 0;
910}
911
912int readArrayFromMessage(const std::string &typeCode,
913 sdbusplus::message::message &m, nlohmann::json &data)
914{
915 if (typeCode.size() < 2)
916 {
917 BMCWEB_LOG_ERROR << "Type code " << typeCode
918 << " too small for an array";
919 return -1;
920 }
921
922 std::string containedType = typeCode.substr(1);
923
924 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
925 containedType.c_str());
926 if (r < 0)
927 {
928 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
929 << r;
930 return r;
931 }
932
933 bool dict = boost::starts_with(containedType, "{") &&
934 boost::ends_with(containedType, "}");
935
936 if (dict)
937 {
938 // Remove the { }
939 containedType = containedType.substr(1, containedType.size() - 2);
940 data = nlohmann::json::object();
941 }
942 else
943 {
944 data = nlohmann::json::array();
945 }
946
947 while (true)
948 {
949 r = sd_bus_message_at_end(m.get(), false);
950 if (r < 0)
951 {
952 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
953 return r;
954 }
955
956 if (r > 0)
957 {
958 break;
959 }
960
961 // Dictionaries are only ever seen in an array
962 if (dict)
963 {
964 r = readDictEntryFromMessage(containedType, m, data);
965 if (r < 0)
966 {
967 return r;
968 }
969 }
970 else
971 {
972 data.push_back(nlohmann::json());
973
974 r = convertDBusToJSON(containedType, m, data.back());
975 if (r < 0)
976 {
977 return r;
978 }
979 }
980 }
981
982 r = sd_bus_message_exit_container(m.get());
983 if (r < 0)
984 {
985 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
986 return r;
987 }
988
989 return 0;
990}
991
Matt Spinler75c6c672019-01-14 13:01:46 -0600992int readStructFromMessage(const std::string &typeCode,
993 sdbusplus::message::message &m, nlohmann::json &data)
994{
995 if (typeCode.size() < 3)
996 {
997 BMCWEB_LOG_ERROR << "Type code " << typeCode
998 << " too small for a struct";
999 return -1;
1000 }
1001
1002 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1003 std::vector<std::string> types = dbusArgSplit(containedTypes);
1004
1005 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1006 containedTypes.c_str());
1007 if (r < 0)
1008 {
1009 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1010 << r;
1011 return r;
1012 }
1013
1014 for (const std::string &type : types)
1015 {
1016 data.push_back(nlohmann::json());
1017 r = convertDBusToJSON(type, m, data.back());
1018 if (r < 0)
1019 {
1020 return r;
1021 }
1022 }
1023
1024 r = sd_bus_message_exit_container(m.get());
1025 if (r < 0)
1026 {
1027 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1028 return r;
1029 }
1030 return 0;
1031}
1032
Matt Spinler89c19702019-01-14 13:13:00 -06001033int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1034{
1035 const char *containerType;
1036 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1037 if (r < 0)
1038 {
1039 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1040 return r;
1041 }
1042
1043 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1044 containerType);
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 r = convertDBusToJSON(containerType, m, data);
1053 if (r < 0)
1054 {
1055 return r;
1056 }
1057
1058 r = sd_bus_message_exit_container(m.get());
1059 if (r < 0)
1060 {
1061 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1062 return r;
1063 }
1064
1065 return 0;
1066}
1067
Matt Spinler6df8f992019-01-14 12:47:47 -06001068int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001069 sdbusplus::message::message &m, nlohmann::json &response)
1070{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001071 int r = 0;
1072 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1073
Matt Spinlerd22a7132019-01-14 12:14:30 -06001074 for (const std::string &typeCode : returnTypes)
1075 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001076 nlohmann::json *thisElement = &response;
1077 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001078 {
1079 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001080 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001081 }
1082
1083 if (typeCode == "s")
1084 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001085 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001086 if (r < 0)
1087 {
1088 return r;
1089 }
1090 }
1091 else if (typeCode == "g")
1092 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001093 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001094 if (r < 0)
1095 {
1096 return r;
1097 }
1098 }
1099 else if (typeCode == "o")
1100 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001101 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001102 if (r < 0)
1103 {
1104 return r;
1105 }
1106 }
1107 else if (typeCode == "b")
1108 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001109 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001110 if (r < 0)
1111 {
1112 return r;
1113 }
1114
Matt Spinlerf39420c2019-01-30 12:57:18 -06001115 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001116 }
1117 else if (typeCode == "u")
1118 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001119 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001120 if (r < 0)
1121 {
1122 return r;
1123 }
1124 }
1125 else if (typeCode == "i")
1126 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001127 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001128 if (r < 0)
1129 {
1130 return r;
1131 }
1132 }
1133 else if (typeCode == "x")
1134 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001135 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001136 if (r < 0)
1137 {
1138 return r;
1139 }
1140 }
1141 else if (typeCode == "t")
1142 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001143 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001144 if (r < 0)
1145 {
1146 return r;
1147 }
1148 }
1149 else if (typeCode == "n")
1150 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001151 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001152 if (r < 0)
1153 {
1154 return r;
1155 }
1156 }
1157 else if (typeCode == "q")
1158 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001159 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001160 if (r < 0)
1161 {
1162 return r;
1163 }
1164 }
1165 else if (typeCode == "y")
1166 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001167 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001168 if (r < 0)
1169 {
1170 return r;
1171 }
1172 }
1173 else if (typeCode == "d")
1174 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001175 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001176 if (r < 0)
1177 {
1178 return r;
1179 }
1180 }
1181 else if (typeCode == "h")
1182 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001183 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001184 if (r < 0)
1185 {
1186 return r;
1187 }
1188 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001189 else if (boost::starts_with(typeCode, "a"))
1190 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001191 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001192 if (r < 0)
1193 {
1194 return r;
1195 }
1196 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001197 else if (boost::starts_with(typeCode, "(") &&
1198 boost::ends_with(typeCode, ")"))
1199 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001200 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001201 if (r < 0)
1202 {
1203 return r;
1204 }
1205 }
Matt Spinler89c19702019-01-14 13:13:00 -06001206 else if (boost::starts_with(typeCode, "v"))
1207 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001208 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001209 if (r < 0)
1210 {
1211 return r;
1212 }
1213 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001214 else
1215 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001216 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1217 return -2;
1218 }
1219 }
1220
Matt Spinler16caaee2019-01-15 11:40:34 -06001221 return 0;
1222}
1223
1224void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1225 sdbusplus::message::message &m,
1226 const std::string &returnType)
1227{
Matt Spinler39a4e392019-01-15 11:53:13 -06001228 nlohmann::json data;
1229
1230 int r = convertDBusToJSON(returnType, m, data);
1231 if (r < 0)
1232 {
1233 transaction->outputFailed = true;
1234 return;
1235 }
1236
1237 if (data.is_null())
1238 {
1239 return;
1240 }
1241
1242 if (transaction->methodResponse.is_null())
1243 {
1244 transaction->methodResponse = std::move(data);
1245 return;
1246 }
1247
1248 // If they're both dictionaries or arrays, merge into one.
1249 // Otherwise, make the results an array with every result
1250 // an entry. Could also just fail in that case, but it
1251 // seems better to get the data back somehow.
1252
1253 if (transaction->methodResponse.is_object() && data.is_object())
1254 {
1255 for (const auto &obj : data.items())
1256 {
1257 // Note: Will overwrite the data for a duplicate key
1258 transaction->methodResponse.emplace(obj.key(),
1259 std::move(obj.value()));
1260 }
1261 return;
1262 }
1263
1264 if (transaction->methodResponse.is_array() && data.is_array())
1265 {
1266 for (auto &obj : data)
1267 {
1268 transaction->methodResponse.push_back(std::move(obj));
1269 }
1270 return;
1271 }
1272
1273 if (!transaction->convertedToArray)
1274 {
1275 // They are different types. May as well turn them into an array
1276 nlohmann::json j = std::move(transaction->methodResponse);
1277 transaction->methodResponse = nlohmann::json::array();
1278 transaction->methodResponse.push_back(std::move(j));
1279 transaction->methodResponse.push_back(std::move(data));
1280 transaction->convertedToArray = true;
1281 }
1282 else
1283 {
1284 transaction->methodResponse.push_back(std::move(data));
1285 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001286}
1287
Ed Tanousd76323e2018-08-07 14:35:40 -07001288void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001289 const std::string &connectionName)
1290{
1291 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1292 << connectionName;
1293 crow::connections::systemBus->async_method_call(
1294 [transaction, connectionName{std::string(connectionName)}](
1295 const boost::system::error_code ec,
1296 const std::string &introspect_xml) {
1297 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1298 if (ec)
1299 {
1300 BMCWEB_LOG_ERROR
1301 << "Introspect call failed with error: " << ec.message()
1302 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001303 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001304 }
Matt Spinler318bd892019-01-15 09:59:20 -06001305 tinyxml2::XMLDocument doc;
1306
1307 doc.Parse(introspect_xml.data(), introspect_xml.size());
1308 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1309 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001310 {
Matt Spinler318bd892019-01-15 09:59:20 -06001311 BMCWEB_LOG_ERROR << "XML document failed to parse "
1312 << connectionName << "\n";
1313 return;
1314 }
1315 tinyxml2::XMLElement *interfaceNode =
1316 pRoot->FirstChildElement("interface");
1317 while (interfaceNode != nullptr)
1318 {
1319 const char *thisInterfaceName =
1320 interfaceNode->Attribute("name");
1321 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001322 {
Matt Spinler318bd892019-01-15 09:59:20 -06001323 if (!transaction->interfaceName.empty() &&
1324 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001325 {
Matt Spinler318bd892019-01-15 09:59:20 -06001326 interfaceNode =
1327 interfaceNode->NextSiblingElement("interface");
1328 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001329 }
Matt Spinler318bd892019-01-15 09:59:20 -06001330
1331 tinyxml2::XMLElement *methodNode =
1332 interfaceNode->FirstChildElement("method");
1333 while (methodNode != nullptr)
1334 {
1335 const char *thisMethodName =
1336 methodNode->Attribute("name");
1337 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1338 if (thisMethodName != nullptr &&
1339 thisMethodName == transaction->methodName)
1340 {
1341 BMCWEB_LOG_DEBUG
1342 << "Found method named " << thisMethodName
1343 << " on interface " << thisInterfaceName;
1344 sdbusplus::message::message m =
1345 crow::connections::systemBus->new_method_call(
1346 connectionName.c_str(),
1347 transaction->path.c_str(),
1348 thisInterfaceName,
1349 transaction->methodName.c_str());
1350
1351 tinyxml2::XMLElement *argumentNode =
1352 methodNode->FirstChildElement("arg");
1353
Matt Spinler16caaee2019-01-15 11:40:34 -06001354 std::string returnType;
1355
1356 // Find the output type
1357 while (argumentNode != nullptr)
1358 {
1359 const char *argDirection =
1360 argumentNode->Attribute("direction");
1361 const char *argType =
1362 argumentNode->Attribute("type");
1363 if (argDirection != nullptr &&
1364 argType != nullptr &&
1365 std::string(argDirection) == "out")
1366 {
1367 returnType = argType;
1368 break;
1369 }
1370 argumentNode =
1371 argumentNode->NextSiblingElement("arg");
1372 }
1373
Matt Spinler318bd892019-01-15 09:59:20 -06001374 nlohmann::json::const_iterator argIt =
1375 transaction->arguments.begin();
1376
Matt Spinler16caaee2019-01-15 11:40:34 -06001377 argumentNode = methodNode->FirstChildElement("arg");
1378
Matt Spinler318bd892019-01-15 09:59:20 -06001379 while (argumentNode != nullptr)
1380 {
1381 const char *argDirection =
1382 argumentNode->Attribute("direction");
1383 const char *argType =
1384 argumentNode->Attribute("type");
1385 if (argDirection != nullptr &&
1386 argType != nullptr &&
1387 std::string(argDirection) == "in")
1388 {
1389 if (argIt == transaction->arguments.end())
1390 {
1391 transaction->setErrorStatus(
1392 "Invalid method args");
1393 return;
1394 }
1395 if (convertJsonToDbus(m.get(),
1396 std::string(argType),
1397 *argIt) < 0)
1398 {
1399 transaction->setErrorStatus(
1400 "Invalid method arg type");
1401 return;
1402 }
1403
1404 argIt++;
1405 }
1406 argumentNode =
1407 argumentNode->NextSiblingElement("arg");
1408 }
1409
1410 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001411 m, [transaction, returnType](
1412 boost::system::error_code ec,
1413 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001414 if (ec)
1415 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001416 transaction->methodFailed = true;
Matt Spinler06b1b632019-06-18 16:09:25 -05001417 const sd_bus_error *e = m.get_error();
1418
1419 if (e)
1420 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001421 crow::setErrorResponse(
Matt Spinler06b1b632019-06-18 16:09:25 -05001422 transaction->res,
1423 boost::beast::http::status::
1424 bad_request,
1425 e->name, e->message);
1426 }
1427 else
1428 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001429 crow::setErrorResponse(
Matt Spinler06b1b632019-06-18 16:09:25 -05001430 transaction->res,
1431 boost::beast::http::status::
1432 bad_request,
1433 "Method call failed",
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001434 crow::msg::methodFailedMsg);
Matt Spinler06b1b632019-06-18 16:09:25 -05001435 }
Matt Spinler318bd892019-01-15 09:59:20 -06001436 return;
1437 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001438 else
1439 {
1440 transaction->methodPassed = true;
1441 }
1442
1443 handleMethodResponse(transaction, m,
1444 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001445 });
1446 break;
1447 }
1448 methodNode = methodNode->NextSiblingElement("method");
1449 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001450 }
Matt Spinler318bd892019-01-15 09:59:20 -06001451 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001452 }
1453 },
1454 connectionName, transaction->path,
1455 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001456}
1457
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001458void handleAction(const crow::Request &req, crow::Response &res,
1459 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001460{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001461 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1462 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001463 nlohmann::json requestDbusData =
1464 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001465
Ed Tanous1abe55e2018-09-05 08:30:59 -07001466 if (requestDbusData.is_discarded())
1467 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001468 crow::setErrorResponse(res, boost::beast::http::status::bad_request,
1469 crow::msg::noJsonDesc, crow::msg::badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001470 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001471 return;
1472 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001473 nlohmann::json::iterator data = requestDbusData.find("data");
1474 if (data == requestDbusData.end())
1475 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001476 crow::setErrorResponse(res, boost::beast::http::status::bad_request,
1477 crow::msg::noJsonDesc, crow::msg::badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001478 res.end();
1479 return;
1480 }
1481
1482 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001483 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001484 crow::setErrorResponse(res, boost::beast::http::status::bad_request,
1485 crow::msg::noJsonDesc, crow::msg::badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001486 res.end();
1487 return;
1488 }
1489 auto transaction = std::make_shared<InProgressActionData>(res);
1490
1491 transaction->path = objectPath;
1492 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001493 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001494 crow::connections::systemBus->async_method_call(
1495 [transaction](
1496 const boost::system::error_code ec,
1497 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001498 &interfaceNames) {
1499 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001500 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001501 BMCWEB_LOG_ERROR << "Can't find object";
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001502 crow::setErrorResponse(
1503 transaction->res, boost::beast::http::status::not_found,
1504 crow::msg::notFoundDesc, crow::msg::notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001505 return;
1506 }
1507
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001508 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1509 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001510
1511 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001512 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001513 {
1514 findActionOnInterface(transaction, object.first);
1515 }
1516 },
1517 "xyz.openbmc_project.ObjectMapper",
1518 "/xyz/openbmc_project/object_mapper",
1519 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1520 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001521}
1522
Matt Spinlerde818812018-12-11 16:39:20 -06001523void handleDelete(const crow::Request &req, crow::Response &res,
1524 const std::string &objectPath)
1525{
1526 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1527
1528 crow::connections::systemBus->async_method_call(
1529 [&res, objectPath](
1530 const boost::system::error_code ec,
1531 const std::vector<std::pair<std::string, std::vector<std::string>>>
1532 &interfaceNames) {
1533 if (ec || interfaceNames.size() <= 0)
1534 {
1535 BMCWEB_LOG_ERROR << "Can't find object";
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001536 crow::setErrorResponse(
1537 res, boost::beast::http::status::method_not_allowed,
1538 crow::msg::methodNotAllowedDesc,
1539 crow::msg::methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001540 res.end();
1541 return;
1542 }
1543
1544 auto transaction = std::make_shared<InProgressActionData>(res);
1545 transaction->path = objectPath;
1546 transaction->methodName = "Delete";
1547 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1548
1549 for (const std::pair<std::string, std::vector<std::string>>
1550 &object : interfaceNames)
1551 {
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<const char *, 0>());
1559}
1560
Ed Tanousf839dfe2018-11-12 11:11:15 -08001561void handleList(crow::Response &res, const std::string &objectPath,
1562 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001563{
1564 crow::connections::systemBus->async_method_call(
1565 [&res](const boost::system::error_code ec,
1566 std::vector<std::string> &objectPaths) {
1567 if (ec)
1568 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001569 crow::setErrorResponse(
1570 res, boost::beast::http::status::not_found,
1571 crow::msg::notFoundDesc, crow::msg::notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001572 }
1573 else
1574 {
1575 res.jsonValue = {{"status", "ok"},
1576 {"message", "200 OK"},
1577 {"data", std::move(objectPaths)}};
1578 }
1579 res.end();
1580 },
1581 "xyz.openbmc_project.ObjectMapper",
1582 "/xyz/openbmc_project/object_mapper",
1583 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001584 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001585}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001586
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001587void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001588{
Ed Tanous049a0512018-11-01 13:58:42 -07001589 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1590 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1591
1592 asyncResp->res.jsonValue = {{"message", "200 OK"},
1593 {"status", "ok"},
1594 {"data", nlohmann::json::object()}};
1595
Ed Tanous1abe55e2018-09-05 08:30:59 -07001596 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001597 [objectPath, asyncResp](const boost::system::error_code ec,
1598 GetSubTreeType &object_names) {
1599 auto transaction = std::make_shared<InProgressEnumerateData>(
1600 objectPath, asyncResp);
1601
1602 transaction->subtree =
1603 std::make_shared<GetSubTreeType>(std::move(object_names));
1604
Ed Tanous1abe55e2018-09-05 08:30:59 -07001605 if (ec)
1606 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001607 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1608 << transaction->objectPath;
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001609 crow::setErrorResponse(transaction->asyncResp->res,
1610 boost::beast::http::status::not_found,
1611 crow::msg::notFoundDesc,
1612 crow::msg::notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001613 return;
1614 }
Ed Tanous64530012018-02-06 17:08:16 -08001615
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001616 // Add the data for the path passed in to the results
1617 // as if GetSubTree returned it, and continue on enumerating
1618 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001619 },
1620 "xyz.openbmc_project.ObjectMapper",
1621 "/xyz/openbmc_project/object_mapper",
1622 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001623 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001624}
Ed Tanous911ac312017-08-15 09:37:42 -07001625
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001626void handleGet(crow::Response &res, std::string &objectPath,
1627 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001628{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001629 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1630 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001631 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001632
Ed Tanous1abe55e2018-09-05 08:30:59 -07001633 std::shared_ptr<std::string> path =
1634 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001635
Ed Tanous1abe55e2018-09-05 08:30:59 -07001636 using GetObjectType =
1637 std::vector<std::pair<std::string, std::vector<std::string>>>;
1638 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001639 [&res, path, propertyName](const boost::system::error_code ec,
1640 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001641 if (ec || object_names.size() <= 0)
1642 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001643 crow::setErrorResponse(
1644 res, boost::beast::http::status::not_found,
1645 crow::msg::notFoundDesc, crow::msg::notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001646 res.end();
1647 return;
1648 }
1649 std::shared_ptr<nlohmann::json> response =
1650 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001651 // The mapper should never give us an empty interface names
1652 // list, but check anyway
Ed Tanous1abe55e2018-09-05 08:30:59 -07001653 for (const std::pair<std::string, std::vector<std::string>>
1654 connection : object_names)
1655 {
1656 const std::vector<std::string> &interfaceNames =
1657 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001658
Ed Tanous1abe55e2018-09-05 08:30:59 -07001659 if (interfaceNames.size() <= 0)
1660 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001661 crow::setErrorResponse(
1662 res, boost::beast::http::status::not_found,
1663 crow::msg::notFoundDesc, crow::msg::notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001664 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001665 return;
1666 }
1667
1668 for (const std::string &interface : interfaceNames)
1669 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001670 sdbusplus::message::message m =
1671 crow::connections::systemBus->new_method_call(
1672 connection.first.c_str(), path->c_str(),
1673 "org.freedesktop.DBus.Properties", "GetAll");
1674 m.append(interface);
1675 crow::connections::systemBus->async_send(
1676 m, [&res, response,
1677 propertyName](const boost::system::error_code ec,
1678 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001679 if (ec)
1680 {
1681 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1682 << ec;
1683 }
1684 else
1685 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001686 nlohmann::json properties;
1687 int r =
1688 convertDBusToJSON("a{sv}", msg, properties);
1689 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001690 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001691 BMCWEB_LOG_ERROR
1692 << "convertDBusToJSON failed";
1693 }
1694 else
1695 {
1696 for (auto &prop : properties.items())
1697 {
Ed Tanous7c091622019-05-23 11:42:36 -07001698 // if property name is empty, or
1699 // matches our search query, add it
1700 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001701
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001702 if (propertyName->empty())
1703 {
1704 (*response)[prop.key()] =
1705 std::move(prop.value());
1706 }
1707 else if (prop.key() == *propertyName)
1708 {
1709 *response = std::move(prop.value());
1710 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001711 }
1712 }
1713 }
1714 if (response.use_count() == 1)
1715 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001716 if (!propertyName->empty() && response->empty())
1717 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001718 crow::setErrorResponse(
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001719 res,
1720 boost::beast::http::status::not_found,
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001721 crow::msg::propNotFoundDesc,
1722 crow::msg::notFoundMsg);
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001723 }
1724 else
1725 {
1726 res.jsonValue = {{"status", "ok"},
1727 {"message", "200 OK"},
1728 {"data", *response}};
1729 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001730 res.end();
1731 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001732 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733 }
1734 }
1735 },
1736 "xyz.openbmc_project.ObjectMapper",
1737 "/xyz/openbmc_project/object_mapper",
1738 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1739 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001740}
1741
Ed Tanous1abe55e2018-09-05 08:30:59 -07001742struct AsyncPutRequest
1743{
1744 AsyncPutRequest(crow::Response &res) : res(res)
1745 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 }
1747 ~AsyncPutRequest()
1748 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001749 if (res.jsonValue.empty())
1750 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001751 crow::setErrorResponse(res, boost::beast::http::status::forbidden,
1752 crow::msg::forbiddenMsg,
1753 crow::msg::forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001754 }
1755
1756 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001757 }
1758
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001759 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001760 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001761 crow::setErrorResponse(
1762 res, boost::beast::http::status::internal_server_error, desc,
1763 crow::msg::badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001764 }
1765
Ed Tanous1abe55e2018-09-05 08:30:59 -07001766 crow::Response &res;
1767 std::string objectPath;
1768 std::string propertyName;
1769 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001770};
1771
Ed Tanousd76323e2018-08-07 14:35:40 -07001772void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001773 const std::string &objectPath, const std::string &destProperty)
1774{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001775 if (destProperty.empty())
1776 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001777 crow::setErrorResponse(res, boost::beast::http::status::forbidden,
1778 crow::msg::forbiddenResDesc,
1779 crow::msg::forbiddenMsg);
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001780 res.end();
1781 return;
1782 }
1783
Ed Tanous1abe55e2018-09-05 08:30:59 -07001784 nlohmann::json requestDbusData =
1785 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001786
Ed Tanous1abe55e2018-09-05 08:30:59 -07001787 if (requestDbusData.is_discarded())
1788 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001789 crow::setErrorResponse(res, boost::beast::http::status::bad_request,
1790 crow::msg::noJsonDesc, crow::msg::badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001791 res.end();
1792 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001793 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001794
Ed Tanous1abe55e2018-09-05 08:30:59 -07001795 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1796 if (propertyIt == requestDbusData.end())
1797 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001798 crow::setErrorResponse(res, boost::beast::http::status::bad_request,
1799 crow::msg::noJsonDesc, crow::msg::badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001800 res.end();
1801 return;
1802 }
1803 const nlohmann::json &propertySetValue = *propertyIt;
1804 auto transaction = std::make_shared<AsyncPutRequest>(res);
1805 transaction->objectPath = objectPath;
1806 transaction->propertyName = destProperty;
1807 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001808
Ed Tanous1abe55e2018-09-05 08:30:59 -07001809 using GetObjectType =
1810 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001811
Ed Tanous1abe55e2018-09-05 08:30:59 -07001812 crow::connections::systemBus->async_method_call(
1813 [transaction](const boost::system::error_code ec,
1814 const GetObjectType &object_names) {
1815 if (!ec && object_names.size() <= 0)
1816 {
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001817 crow::setErrorResponse(
1818 transaction->res, boost::beast::http::status::not_found,
1819 crow::msg::propNotFoundDesc, crow::msg::notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001820 return;
1821 }
Ed Tanous911ac312017-08-15 09:37:42 -07001822
Ed Tanous1abe55e2018-09-05 08:30:59 -07001823 for (const std::pair<std::string, std::vector<std::string>>
1824 connection : object_names)
1825 {
1826 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001827
Ed Tanous1abe55e2018-09-05 08:30:59 -07001828 crow::connections::systemBus->async_method_call(
1829 [connectionName{std::string(connectionName)},
1830 transaction](const boost::system::error_code ec,
1831 const std::string &introspectXml) {
1832 if (ec)
1833 {
1834 BMCWEB_LOG_ERROR
1835 << "Introspect call failed with error: "
1836 << ec.message()
1837 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001838 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001839 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001840 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001841 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001842
Ed Tanous1abe55e2018-09-05 08:30:59 -07001843 doc.Parse(introspectXml.c_str());
1844 tinyxml2::XMLNode *pRoot =
1845 doc.FirstChildElement("node");
1846 if (pRoot == nullptr)
1847 {
1848 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1849 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001850 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001851 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001852 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001853 tinyxml2::XMLElement *ifaceNode =
1854 pRoot->FirstChildElement("interface");
1855 while (ifaceNode != nullptr)
1856 {
1857 const char *interfaceName =
1858 ifaceNode->Attribute("name");
1859 BMCWEB_LOG_DEBUG << "found interface "
1860 << interfaceName;
1861 tinyxml2::XMLElement *propNode =
1862 ifaceNode->FirstChildElement("property");
1863 while (propNode != nullptr)
1864 {
1865 const char *propertyName =
1866 propNode->Attribute("name");
1867 BMCWEB_LOG_DEBUG << "Found property "
1868 << propertyName;
1869 if (propertyName == transaction->propertyName)
1870 {
1871 const char *argType =
1872 propNode->Attribute("type");
1873 if (argType != nullptr)
1874 {
1875 sdbusplus::message::message m =
1876 crow::connections::systemBus
1877 ->new_method_call(
1878 connectionName.c_str(),
1879 transaction->objectPath
1880 .c_str(),
1881 "org.freedesktop.DBus."
1882 "Properties",
1883 "Set");
1884 m.append(interfaceName,
1885 transaction->propertyName);
1886 int r = sd_bus_message_open_container(
1887 m.get(), SD_BUS_TYPE_VARIANT,
1888 argType);
1889 if (r < 0)
1890 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001891 transaction->setErrorStatus(
1892 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001893 return;
1894 }
1895 r = convertJsonToDbus(
1896 m.get(), argType,
1897 transaction->propertyValue);
1898 if (r < 0)
1899 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -05001900 if (r == -ERANGE)
1901 {
1902 transaction->setErrorStatus(
1903 "Provided property value "
1904 "is out of range for the "
1905 "property type");
1906 }
1907 else
1908 {
1909 transaction->setErrorStatus(
1910 "Invalid arg type");
1911 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001912 return;
1913 }
1914 r = sd_bus_message_close_container(
1915 m.get());
1916 if (r < 0)
1917 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001918 transaction->setErrorStatus(
1919 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001920 return;
1921 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001922 crow::connections::systemBus
1923 ->async_send(
1924 m,
1925 [transaction](
1926 boost::system::error_code
1927 ec,
1928 sdbusplus::message::message
1929 &m) {
1930 BMCWEB_LOG_DEBUG << "sent";
1931 if (ec)
1932 {
Lei YU97d2a472019-06-11 17:44:27 +08001933 const sd_bus_error *e =
1934 m.get_error();
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05001935 crow::setErrorResponse(
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001936 transaction->res,
1937 boost::beast::http::
1938 status::
1939 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001940 (e) ? e->name
1941 : ec.category()
1942 .name(),
1943 (e) ? e->message
1944 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001945 }
1946 else
1947 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001948 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001949 .jsonValue = {
1950 {"status", "ok"},
1951 {"message",
1952 "200 OK"},
1953 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001954 }
1955 });
1956 }
1957 }
1958 propNode =
1959 propNode->NextSiblingElement("property");
1960 }
1961 ifaceNode =
1962 ifaceNode->NextSiblingElement("interface");
1963 }
1964 },
1965 connectionName, transaction->objectPath,
1966 "org.freedesktop.DBus.Introspectable", "Introspect");
1967 }
1968 },
1969 "xyz.openbmc_project.ObjectMapper",
1970 "/xyz/openbmc_project/object_mapper",
1971 "xyz.openbmc_project.ObjectMapper", "GetObject",
1972 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001973}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001974
Ed Tanous049a0512018-11-01 13:58:42 -07001975inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1976 std::string &objectPath)
1977{
Ed Tanous049a0512018-11-01 13:58:42 -07001978
1979 // If accessing a single attribute, fill in and update objectPath,
1980 // otherwise leave destProperty blank
1981 std::string destProperty = "";
1982 const char *attrSeperator = "/attr/";
1983 size_t attrPosition = objectPath.find(attrSeperator);
1984 if (attrPosition != objectPath.npos)
1985 {
1986 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1987 objectPath.length());
1988 objectPath = objectPath.substr(0, attrPosition);
1989 }
1990
1991 if (req.method() == "POST"_method)
1992 {
1993 constexpr const char *actionSeperator = "/action/";
1994 size_t actionPosition = objectPath.find(actionSeperator);
1995 if (actionPosition != objectPath.npos)
1996 {
1997 std::string postProperty =
1998 objectPath.substr((actionPosition + strlen(actionSeperator)),
1999 objectPath.length());
2000 objectPath = objectPath.substr(0, actionPosition);
2001 handleAction(req, res, objectPath, postProperty);
2002 return;
2003 }
2004 }
2005 else if (req.method() == "GET"_method)
2006 {
2007 if (boost::ends_with(objectPath, "/enumerate"))
2008 {
2009 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2010 objectPath.end());
2011 handleEnumerate(res, objectPath);
2012 }
2013 else if (boost::ends_with(objectPath, "/list"))
2014 {
2015 objectPath.erase(objectPath.end() - sizeof("list"),
2016 objectPath.end());
2017 handleList(res, objectPath);
2018 }
2019 else
2020 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002021 // Trim any trailing "/" at the end
2022 if (boost::ends_with(objectPath, "/"))
2023 {
2024 objectPath.pop_back();
2025 handleList(res, objectPath, 1);
2026 }
2027 else
2028 {
2029 handleGet(res, objectPath, destProperty);
2030 }
Ed Tanous049a0512018-11-01 13:58:42 -07002031 }
2032 return;
2033 }
2034 else if (req.method() == "PUT"_method)
2035 {
2036 handlePut(req, res, objectPath, destProperty);
2037 return;
2038 }
Matt Spinlerde818812018-12-11 16:39:20 -06002039 else if (req.method() == "DELETE"_method)
2040 {
2041 handleDelete(req, res, objectPath);
2042 return;
2043 }
Ed Tanous049a0512018-11-01 13:58:42 -07002044
Joseph Reynolds8fd315a2019-09-12 12:02:33 -05002045 crow::setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2046 crow::msg::methodNotAllowedDesc,
2047 crow::msg::methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002048 res.end();
2049}
2050
Ed Tanous1abe55e2018-09-05 08:30:59 -07002051template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
2052{
2053 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03002054 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002055 .methods("GET"_method)(
2056 [](const crow::Request &req, crow::Response &res) {
2057 res.jsonValue = {{"busses", {{{"name", "system"}}}},
2058 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002059 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002060 });
2061
2062 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03002063 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002064 .methods("GET"_method)(
2065 [](const crow::Request &req, crow::Response &res) {
2066 auto myCallback = [&res](const boost::system::error_code ec,
2067 std::vector<std::string> &names) {
2068 if (ec)
2069 {
2070 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2071 res.result(
2072 boost::beast::http::status::internal_server_error);
2073 }
2074 else
2075 {
2076 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002077 res.jsonValue = {{"status", "ok"}};
2078 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002079 for (auto &name : names)
2080 {
2081 objectsSub.push_back({{"name", name}});
2082 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002083 }
2084 res.end();
2085 };
2086 crow::connections::systemBus->async_method_call(
2087 std::move(myCallback), "org.freedesktop.DBus", "/",
2088 "org.freedesktop.DBus", "ListNames");
2089 });
2090
2091 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002092 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002093 .methods("GET"_method)(
2094 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002095 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002096 });
2097
2098 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002099 .requires({"Login"})
2100 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2101 const std::string &path) {
2102 std::string objectPath = "/xyz/" + path;
2103 handleDBusUrl(req, res, objectPath);
2104 });
2105
2106 BMCWEB_ROUTE(app, "/xyz/<path>")
2107 .requires({"ConfigureComponents", "ConfigureManager"})
2108 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002109 [](const crow::Request &req, crow::Response &res,
2110 const std::string &path) {
2111 std::string objectPath = "/xyz/" + path;
2112 handleDBusUrl(req, res, objectPath);
2113 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002114
Ed Tanous049a0512018-11-01 13:58:42 -07002115 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002116 .requires({"Login"})
2117 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2118 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002119 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002120 handleDBusUrl(req, res, objectPath);
2121 });
2122
2123 BMCWEB_ROUTE(app, "/org/<path>")
2124 .requires({"ConfigureComponents", "ConfigureManager"})
2125 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002126 [](const crow::Request &req, crow::Response &res,
2127 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002128 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002129 handleDBusUrl(req, res, objectPath);
2130 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002131
Ed Tanous1abe55e2018-09-05 08:30:59 -07002132 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002133 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002134 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2135 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002136 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002137 if (!std::regex_match(dumpId, validFilename))
2138 {
Ed Tanousad18f072018-11-14 14:07:48 -08002139 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002140 res.end();
2141 return;
2142 }
James Feistf6150402019-01-08 10:36:20 -08002143 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002144 "/var/lib/phosphor-debug-collector/dumps");
2145
Ed Tanousad18f072018-11-14 14:07:48 -08002146 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002147
James Feistf6150402019-01-08 10:36:20 -08002148 if (!std::filesystem::exists(loc) ||
2149 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002150 {
Ed Tanousad18f072018-11-14 14:07:48 -08002151 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002152 res.result(boost::beast::http::status::not_found);
2153 res.end();
2154 return;
2155 }
James Feistf6150402019-01-08 10:36:20 -08002156 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002157
Ed Tanous1abe55e2018-09-05 08:30:59 -07002158 for (auto &file : files)
2159 {
2160 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002161 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002162 {
2163 continue;
2164 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002165
Ed Tanous1abe55e2018-09-05 08:30:59 -07002166 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002167
2168 // Assuming only one dump file will be present in the dump id
2169 // directory
2170 std::string dumpFileName = file.path().filename().string();
2171
2172 // Filename should be in alphanumeric, dot and underscore
2173 // Its based on phosphor-debug-collector application dumpfile
2174 // format
2175 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2176 if (!std::regex_match(dumpFileName, dumpFileRegex))
2177 {
2178 BMCWEB_LOG_ERROR << "Invalid dump filename "
2179 << dumpFileName;
2180 res.result(boost::beast::http::status::not_found);
2181 res.end();
2182 return;
2183 }
2184 std::string contentDispositionParam =
2185 "attachment; filename=\"" + dumpFileName + "\"";
2186
2187 res.addHeader("Content-Disposition", contentDispositionParam);
2188
Ed Tanous1abe55e2018-09-05 08:30:59 -07002189 res.body() = {std::istreambuf_iterator<char>(readFile),
2190 std::istreambuf_iterator<char>()};
2191 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002192 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002193 }
2194 res.result(boost::beast::http::status::not_found);
2195 res.end();
2196 return;
2197 });
2198
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002199 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002200 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002201 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002202 const std::string &Connection) {
2203 introspectObjects(Connection, "/",
2204 std::make_shared<bmcweb::AsyncResp>(res));
2205 });
2206
2207 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2208 .methods("GET"_method,
2209 "POST"_method)([](const crow::Request &req,
2210 crow::Response &res,
2211 const std::string &processName,
2212 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002213 std::vector<std::string> strs;
2214 boost::split(strs, requestedPath, boost::is_any_of("/"));
2215 std::string objectPath;
2216 std::string interfaceName;
2217 std::string methodName;
2218 auto it = strs.begin();
2219 if (it == strs.end())
2220 {
2221 objectPath = "/";
2222 }
2223 while (it != strs.end())
2224 {
2225 // Check if segment contains ".". If it does, it must be an
2226 // interface
2227 if (it->find(".") != std::string::npos)
2228 {
2229 break;
Ed Tanous7c091622019-05-23 11:42:36 -07002230 // This check is neccesary as the trailing slash gets
2231 // parsed as part of our <path> specifier above, which
2232 // causes the normal trailing backslash redirector to
2233 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002234 }
2235 else if (!it->empty())
2236 {
2237 objectPath += "/" + *it;
2238 }
2239 it++;
2240 }
2241 if (it != strs.end())
2242 {
2243 interfaceName = *it;
2244 it++;
2245
2246 // after interface, we might have a method name
2247 if (it != strs.end())
2248 {
2249 methodName = *it;
2250 it++;
2251 }
2252 }
2253 if (it != strs.end())
2254 {
Ed Tanous7c091622019-05-23 11:42:36 -07002255 // if there is more levels past the method name, something
2256 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002257 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002258 return;
2259 }
2260 if (interfaceName.empty())
2261 {
Ed Tanous7c091622019-05-23 11:42:36 -07002262 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2263 std::make_shared<bmcweb::AsyncResp>(res);
2264
Ed Tanous1abe55e2018-09-05 08:30:59 -07002265 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002266 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002267 objectPath](const boost::system::error_code ec,
2268 const std::string &introspect_xml) {
2269 if (ec)
2270 {
2271 BMCWEB_LOG_ERROR
2272 << "Introspect call failed with error: "
2273 << ec.message()
2274 << " on process: " << processName
2275 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002276 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002277 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002278 tinyxml2::XMLDocument doc;
2279
2280 doc.Parse(introspect_xml.c_str());
2281 tinyxml2::XMLNode *pRoot =
2282 doc.FirstChildElement("node");
2283 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002284 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002285 BMCWEB_LOG_ERROR << "XML document failed to parse "
2286 << processName << " " << objectPath
2287 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002288 asyncResp->res.jsonValue = {
2289 {"status", "XML parse error"}};
2290 asyncResp->res.result(boost::beast::http::status::
2291 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002292 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002293 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002294
2295 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002296 asyncResp->res.jsonValue = {
2297 {"status", "ok"},
2298 {"bus_name", processName},
2299 {"object_path", objectPath}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002300 nlohmann::json &interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002301 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002302 interfacesArray = nlohmann::json::array();
2303 tinyxml2::XMLElement *interface =
2304 pRoot->FirstChildElement("interface");
2305
2306 while (interface != nullptr)
2307 {
2308 const char *ifaceName =
2309 interface->Attribute("name");
2310 if (ifaceName != nullptr)
2311 {
2312 interfacesArray.push_back(
2313 {{"name", ifaceName}});
2314 }
2315
2316 interface =
2317 interface->NextSiblingElement("interface");
2318 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002319 },
2320 processName, objectPath,
2321 "org.freedesktop.DBus.Introspectable", "Introspect");
2322 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002323 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002324 {
Ed Tanous7c091622019-05-23 11:42:36 -07002325 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2326 std::make_shared<bmcweb::AsyncResp>(res);
2327
Ed Tanous1abe55e2018-09-05 08:30:59 -07002328 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002329 [asyncResp, processName, objectPath,
2330 interfaceName](const boost::system::error_code ec,
2331 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002332 if (ec)
2333 {
2334 BMCWEB_LOG_ERROR
2335 << "Introspect call failed with error: "
2336 << ec.message()
2337 << " on process: " << processName
2338 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002339 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002340 }
Ed Tanous7c091622019-05-23 11:42:36 -07002341 tinyxml2::XMLDocument doc;
2342
2343 doc.Parse(introspect_xml.data(), introspect_xml.size());
2344 tinyxml2::XMLNode *pRoot =
2345 doc.FirstChildElement("node");
2346 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002347 {
Ed Tanous7c091622019-05-23 11:42:36 -07002348 BMCWEB_LOG_ERROR << "XML document failed to parse "
2349 << processName << " " << objectPath
2350 << "\n";
2351 asyncResp->res.result(boost::beast::http::status::
2352 internal_server_error);
2353 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002354 }
Ed Tanous7c091622019-05-23 11:42:36 -07002355 asyncResp->res.jsonValue = {
2356 {"status", "ok"},
2357 {"bus_name", processName},
2358 {"interface", interfaceName},
2359 {"object_path", objectPath}};
2360
2361 nlohmann::json &methodsArray =
2362 asyncResp->res.jsonValue["methods"];
2363 methodsArray = nlohmann::json::array();
2364
2365 nlohmann::json &signalsArray =
2366 asyncResp->res.jsonValue["signals"];
2367 signalsArray = nlohmann::json::array();
2368
2369 nlohmann::json &propertiesObj =
2370 asyncResp->res.jsonValue["properties"];
2371 propertiesObj = nlohmann::json::object();
2372
2373 // if we know we're the only call, build the
2374 // json directly
2375 tinyxml2::XMLElement *interface =
2376 pRoot->FirstChildElement("interface");
2377 while (interface != nullptr)
2378 {
2379 const char *ifaceName =
2380 interface->Attribute("name");
2381
2382 if (ifaceName != nullptr &&
2383 ifaceName == interfaceName)
2384 {
2385 break;
2386 }
2387
2388 interface =
2389 interface->NextSiblingElement("interface");
2390 }
2391 if (interface == nullptr)
2392 {
2393 // if we got to the end of the list and
2394 // never found a match, throw 404
2395 asyncResp->res.result(
2396 boost::beast::http::status::not_found);
2397 return;
2398 }
2399
2400 tinyxml2::XMLElement *methods =
2401 interface->FirstChildElement("method");
2402 while (methods != nullptr)
2403 {
2404 nlohmann::json argsArray = nlohmann::json::array();
2405 tinyxml2::XMLElement *arg =
2406 methods->FirstChildElement("arg");
2407 while (arg != nullptr)
2408 {
2409 nlohmann::json thisArg;
2410 for (const char *fieldName :
2411 std::array<const char *, 3>{
2412 "name", "direction", "type"})
2413 {
2414 const char *fieldValue =
2415 arg->Attribute(fieldName);
2416 if (fieldValue != nullptr)
2417 {
2418 thisArg[fieldName] = fieldValue;
2419 }
2420 }
2421 argsArray.push_back(std::move(thisArg));
2422 arg = arg->NextSiblingElement("arg");
2423 }
2424
2425 const char *name = methods->Attribute("name");
2426 if (name != nullptr)
2427 {
2428 methodsArray.push_back(
2429 {{"name", name},
2430 {"uri", "/bus/system/" + processName +
2431 objectPath + "/" +
2432 interfaceName + "/" + name},
2433 {"args", argsArray}});
2434 }
2435 methods = methods->NextSiblingElement("method");
2436 }
2437 tinyxml2::XMLElement *signals =
2438 interface->FirstChildElement("signal");
2439 while (signals != nullptr)
2440 {
2441 nlohmann::json argsArray = nlohmann::json::array();
2442
2443 tinyxml2::XMLElement *arg =
2444 signals->FirstChildElement("arg");
2445 while (arg != nullptr)
2446 {
2447 const char *name = arg->Attribute("name");
2448 const char *type = arg->Attribute("type");
2449 if (name != nullptr && type != nullptr)
2450 {
2451 argsArray.push_back({
2452 {"name", name},
2453 {"type", type},
2454 });
2455 }
2456 arg = arg->NextSiblingElement("arg");
2457 }
2458 const char *name = signals->Attribute("name");
2459 if (name != nullptr)
2460 {
2461 signalsArray.push_back(
2462 {{"name", name}, {"args", argsArray}});
2463 }
2464
2465 signals = signals->NextSiblingElement("signal");
2466 }
2467
2468 tinyxml2::XMLElement *property =
2469 interface->FirstChildElement("property");
2470 while (property != nullptr)
2471 {
2472 const char *name = property->Attribute("name");
2473 const char *type = property->Attribute("type");
2474 if (type != nullptr && name != nullptr)
2475 {
2476 sdbusplus::message::message m =
2477 crow::connections::systemBus
2478 ->new_method_call(processName.c_str(),
2479 objectPath.c_str(),
2480 "org.freedesktop."
2481 "DBus."
2482 "Properties",
2483 "Get");
2484 m.append(interfaceName, name);
2485 nlohmann::json &propertyItem =
2486 propertiesObj[name];
2487 crow::connections::systemBus->async_send(
2488 m, [&propertyItem, asyncResp](
2489 boost::system::error_code &ec,
2490 sdbusplus::message::message &m) {
2491 if (ec)
2492 {
2493 return;
2494 }
2495
2496 convertDBusToJSON("v", m, propertyItem);
2497 });
2498 }
2499 property = property->NextSiblingElement("property");
2500 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002501 },
2502 processName, objectPath,
2503 "org.freedesktop.DBus.Introspectable", "Introspect");
2504 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002505 else
2506 {
2507 if (req.method() != "POST"_method)
2508 {
2509 res.result(boost::beast::http::status::not_found);
2510 res.end();
2511 return;
2512 }
2513
2514 nlohmann::json requestDbusData =
2515 nlohmann::json::parse(req.body, nullptr, false);
2516
2517 if (requestDbusData.is_discarded())
2518 {
2519 res.result(boost::beast::http::status::bad_request);
2520 res.end();
2521 return;
2522 }
2523 if (!requestDbusData.is_array())
2524 {
2525 res.result(boost::beast::http::status::bad_request);
2526 res.end();
2527 return;
2528 }
2529 auto transaction = std::make_shared<InProgressActionData>(res);
2530
2531 transaction->path = objectPath;
2532 transaction->methodName = methodName;
2533 transaction->arguments = std::move(requestDbusData);
2534
2535 findActionOnInterface(transaction, processName);
2536 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002537 });
2538}
2539} // namespace openbmc_mapper
2540} // namespace crow