blob: e59692020b397c415a3e8ce7cbb52358dc573093 [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
James Feistf6150402019-01-08 10:36:20 -080016#include "filesystem.hpp"
17
Ed Tanous911ac312017-08-15 09:37:42 -070018#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070019#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020
Ed Tanouse3cb5a32018-08-08 14:16:49 -070021#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070022#include <boost/algorithm/string.hpp>
23#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070024#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070025#include <dbus_utility.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070026#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070027#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace openbmc_mapper
32{
Ed Tanousba9f9a62017-10-11 16:40:35 -070033
Matt Spinler3ae4ba72018-12-05 14:01:22 -060034using GetSubTreeType = std::vector<
35 std::pair<std::string,
36 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37
Matt Spinler2ae60092018-12-06 10:35:36 -060038const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060039const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060040const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060041const std::string forbiddenMsg = "403 Forbidden";
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -060042const std::string methodFailedMsg = "500 Method Call Failed";
Matt Spinler16caaee2019-01-15 11:40:34 -060043const std::string methodOutputFailedMsg = "500 Method Output Error";
Matt Spinler6db06242018-12-11 11:21:22 -060044
Matt Spinler2ae60092018-12-06 10:35:36 -060045const std::string notFoundDesc =
46 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060047const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060048const std::string noJsonDesc = "No JSON object could be decoded";
49const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060050const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060051const std::string forbiddenPropDesc =
52 "The specified property cannot be created";
53const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060054
55void setErrorResponse(crow::Response &res, boost::beast::http::status result,
56 const std::string &desc, const std::string &msg)
57{
58 res.result(result);
59 res.jsonValue = {{"data", {{"description", desc}}},
60 {"message", msg},
61 {"status", "error"}};
62}
63
Ed Tanouse3cb5a32018-08-08 14:16:49 -070064void introspectObjects(const std::string &processName,
65 const std::string &objectPath,
66 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070067{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070068 if (transaction->res.jsonValue.is_null())
69 {
70 transaction->res.jsonValue = {{"status", "ok"},
71 {"bus_name", processName},
72 {"objects", nlohmann::json::array()}};
73 }
74
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070076 [transaction, processName{std::string(processName)},
77 objectPath{std::string(objectPath)}](
78 const boost::system::error_code ec,
79 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 if (ec)
81 {
82 BMCWEB_LOG_ERROR
83 << "Introspect call failed with error: " << ec.message()
84 << " on process: " << processName << " path: " << objectPath
85 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070086 return;
87 }
88 transaction->res.jsonValue["objects"].push_back(
89 {{"path", objectPath}});
90
91 tinyxml2::XMLDocument doc;
92
93 doc.Parse(introspect_xml.c_str());
94 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
95 if (pRoot == nullptr)
96 {
97 BMCWEB_LOG_ERROR << "XML document failed to parse "
98 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070099 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 else
101 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
103 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700105 const char *childPath = node->Attribute("name");
106 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 std::string newpath;
109 if (objectPath != "/")
110 {
111 newpath += objectPath;
112 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700113 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700115 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700117
118 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 }
120 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700122 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700124}
Ed Tanous64530012018-02-06 17:08:16 -0800125
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600126void getPropertiesForEnumerate(const std::string &objectPath,
127 const std::string &service,
128 const std::string &interface,
129 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
130{
131 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
132 << service << " " << interface;
133
134 crow::connections::systemBus->async_method_call(
135 [asyncResp, objectPath, service,
136 interface](const boost::system::error_code ec,
137 const std::vector<
138 std::pair<std::string, dbus::utility::DbusVariantType>>
139 &propertiesList) {
140 if (ec)
141 {
142 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
143 << interface << " service " << service
144 << " failed with code " << ec;
145 return;
146 }
147
148 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
149 nlohmann::json &objectJson = dataJson[objectPath];
150 if (objectJson.is_null())
151 {
152 objectJson = nlohmann::json::object();
153 }
154
155 for (const auto &[name, value] : propertiesList)
156 {
157 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800158 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
159 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600160 }
161 },
162 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
163 interface);
164}
165
166// Find any results that weren't picked up by ObjectManagers, to be
167// called after all ObjectManagers are searched for and called.
168void findRemainingObjectsForEnumerate(
169 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
170 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
171{
172 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
173 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
174
175 for (const auto &[path, interface_map] : *subtree)
176 {
177 if (path == objectPath)
178 {
179 // An enumerate does not return the target path's properties
180 continue;
181 }
182 if (dataJson.find(path) == dataJson.end())
183 {
184 for (const auto &[service, interfaces] : interface_map)
185 {
186 for (const auto &interface : interfaces)
187 {
188 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
189 {
190 getPropertiesForEnumerate(path, service, interface,
191 asyncResp);
192 }
193 }
194 }
195 }
196 }
197}
198
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600199struct InProgressEnumerateData
200{
201 InProgressEnumerateData(const std::string &objectPath,
202 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
203 objectPath(objectPath),
204 asyncResp(asyncResp)
205 {
206 }
207
208 ~InProgressEnumerateData()
209 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600210 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600211 }
212
213 const std::string objectPath;
214 std::shared_ptr<GetSubTreeType> subtree;
215 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
216};
217
218void getManagedObjectsForEnumerate(
219 const std::string &object_name, const std::string &object_manager_path,
220 const std::string &connection_name,
221 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222{
Ed Tanous049a0512018-11-01 13:58:42 -0700223 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
224 << " object_manager_path " << object_manager_path
225 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600227 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700228 connection_name](const boost::system::error_code ec,
229 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 if (ec)
231 {
Ed Tanous049a0512018-11-01 13:58:42 -0700232 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600233 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700234 << " failed with code " << ec;
235 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700236 }
Ed Tanous64530012018-02-06 17:08:16 -0800237
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600238 nlohmann::json &dataJson =
239 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700240
241 for (const auto &objectPath : objects)
242 {
243 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 {
Ed Tanous049a0512018-11-01 13:58:42 -0700245 BMCWEB_LOG_DEBUG << "Reading object "
246 << objectPath.first.str;
247 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 if (objectJson.is_null())
249 {
250 objectJson = nlohmann::json::object();
251 }
252 for (const auto &interface : objectPath.second)
253 {
254 for (const auto &property : interface.second)
255 {
256 nlohmann::json &propertyJson =
257 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800258 std::visit([&propertyJson](
259 auto &&val) { propertyJson = val; },
260 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 }
262 }
263 }
Ed Tanous049a0512018-11-01 13:58:42 -0700264 for (const auto &interface : objectPath.second)
265 {
266 if (interface.first == "org.freedesktop.DBus.ObjectManager")
267 {
268 getManagedObjectsForEnumerate(
269 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600270 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700271 }
272 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 }
274 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700275 connection_name, object_manager_path,
276 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
277}
278
279void findObjectManagerPathForEnumerate(
280 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600281 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700282{
Ed Tanous049a0512018-11-01 13:58:42 -0700283 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
284 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700285 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600286 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700287 const boost::system::error_code ec,
288 const boost::container::flat_map<
289 std::string, boost::container::flat_map<
290 std::string, std::vector<std::string>>>
291 &objects) {
292 if (ec)
293 {
Ed Tanous049a0512018-11-01 13:58:42 -0700294 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
295 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700296 return;
297 }
298
Ed Tanousf254ba72018-10-12 13:40:35 -0700299 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700300 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700301 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 {
303 if (connectionGroup.first == connection_name)
304 {
305 // Found the object manager path for this resource.
306 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700307 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600308 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700309 return;
310 }
311 }
312 }
313 },
314 "xyz.openbmc_project.ObjectMapper",
315 "/xyz/openbmc_project/object_mapper",
316 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
317 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700318}
Ed Tanous64530012018-02-06 17:08:16 -0800319
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600320// Uses GetObject to add the object info about the target /enumerate path to the
321// results of GetSubTree, as GetSubTree will not return info for the
322// target path, and then continues on enumerating the rest of the tree.
323void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
324{
325 using GetObjectType =
326 std::vector<std::pair<std::string, std::vector<std::string>>>;
327
328 crow::connections::systemBus->async_method_call(
329 [transaction](const boost::system::error_code ec,
330 const GetObjectType &objects) {
331 if (ec)
332 {
333 BMCWEB_LOG_ERROR << "GetObject for path "
334 << transaction->objectPath
335 << " failed with code " << ec;
336 return;
337 }
338
339 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
340 << " has " << objects.size() << " entries";
341 if (!objects.empty())
342 {
343 transaction->subtree->emplace_back(transaction->objectPath,
344 objects);
345 }
346
347 // Map indicating connection name, and the path where the object
348 // manager exists
349 boost::container::flat_map<std::string, std::string> connections;
350
351 for (const auto &object : *(transaction->subtree))
352 {
353 for (const auto &connection : object.second)
354 {
355 std::string &objectManagerPath =
356 connections[connection.first];
357 for (const auto &interface : connection.second)
358 {
359 BMCWEB_LOG_DEBUG << connection.first
360 << " has interface " << interface;
361 if (interface == "org.freedesktop.DBus.ObjectManager")
362 {
363 BMCWEB_LOG_DEBUG << "found object manager path "
364 << object.first;
365 objectManagerPath = object.first;
366 }
367 }
368 }
369 }
370 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
371
372 for (const auto &connection : connections)
373 {
374 // If we already know where the object manager is, we don't need
375 // to search for it, we can call directly in to
376 // getManagedObjects
377 if (!connection.second.empty())
378 {
379 getManagedObjectsForEnumerate(
380 transaction->objectPath, connection.second,
381 connection.first, transaction);
382 }
383 else
384 {
385 // otherwise we need to find the object manager path before
386 // we can continue
387 findObjectManagerPathForEnumerate(
388 transaction->objectPath, connection.first, transaction);
389 }
390 }
391 },
392 "xyz.openbmc_project.ObjectMapper",
393 "/xyz/openbmc_project/object_mapper",
394 "xyz.openbmc_project.ObjectMapper", "GetObject",
395 transaction->objectPath, std::array<const char *, 0>());
396}
Ed Tanous64530012018-02-06 17:08:16 -0800397
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700398// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399struct InProgressActionData
400{
401 InProgressActionData(crow::Response &res) : res(res){};
402 ~InProgressActionData()
403 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600404 // Methods could have been called across different owners
405 // and interfaces, where some calls failed and some passed.
406 //
407 // The rules for this are:
408 // * if no method was called - error
409 // * if a method failed and none passed - error
410 // (converse: if at least one method passed - OK)
411 // * for the method output:
412 // * if output processing didn't fail, return the data
413
414 // Only deal with method returns if nothing failed earlier
415 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600417 if (!methodPassed)
418 {
419 if (methodFailed)
420 {
421 setErrorResponse(res,
422 boost::beast::http::status::bad_request,
423 "Method call failed", methodFailedMsg);
424 }
425 else
426 {
427 setErrorResponse(res, boost::beast::http::status::not_found,
428 methodNotFoundDesc, notFoundMsg);
429 }
430 }
431 else
432 {
433 if (outputFailed)
434 {
435 setErrorResponse(
436 res, boost::beast::http::status::internal_server_error,
437 "Method output failure", methodOutputFailedMsg);
438 }
439 else
440 {
441 res.jsonValue = {{"status", "ok"},
442 {"message", "200 OK"},
443 {"data", methodResponse}};
444 }
445 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600447
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700449 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700450
Matt Spinler6db06242018-12-11 11:21:22 -0600451 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600453 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
454 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 }
456 crow::Response &res;
457 std::string path;
458 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600459 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600460 bool methodPassed = false;
461 bool methodFailed = false;
462 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600463 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600464 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700466};
467
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468std::vector<std::string> dbusArgSplit(const std::string &string)
469{
470 std::vector<std::string> ret;
471 if (string.empty())
472 {
473 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700474 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700475 ret.push_back("");
476 int containerDepth = 0;
477
478 for (std::string::const_iterator character = string.begin();
479 character != string.end(); character++)
480 {
481 ret.back() += *character;
482 switch (*character)
483 {
484 case ('a'):
485 break;
486 case ('('):
487 case ('{'):
488 containerDepth++;
489 break;
490 case ('}'):
491 case (')'):
492 containerDepth--;
493 if (containerDepth == 0)
494 {
495 if (character + 1 != string.end())
496 {
497 ret.push_back("");
498 }
499 }
500 break;
501 default:
502 if (containerDepth == 0)
503 {
504 if (character + 1 != string.end())
505 {
506 ret.push_back("");
507 }
508 }
509 break;
510 }
511 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600512
513 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700514}
515
Ed Tanousd76323e2018-08-07 14:35:40 -0700516int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 const nlohmann::json &input_json)
518{
519 int r = 0;
520 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
521 << " to type: " << arg_type;
522 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700523
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 // Assume a single object for now.
525 const nlohmann::json *j = &input_json;
526 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700527
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700528 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700529 {
530 // If we are decoding multiple objects, grab the pointer to the
531 // iterator, and increment it for the next loop
532 if (argTypes.size() > 1)
533 {
534 if (jIt == input_json.end())
535 {
536 return -2;
537 }
538 j = &*jIt;
539 jIt++;
540 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700541 const int64_t *intValue = j->get_ptr<const int64_t *>();
542 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
543 const std::string *stringValue = j->get_ptr<const std::string *>();
544 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 const bool *b = j->get_ptr<const bool *>();
546 int64_t v = 0;
547 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700548
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 // Do some basic type conversions that make sense. uint can be
550 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700551 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700553 v = static_cast<int64_t>(*uintValue);
554 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700556 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700558 d = static_cast<double>(*uintValue);
559 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700563 d = static_cast<double>(*intValue);
564 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700565 }
566
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700567 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700569 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 {
571 return -1;
572 }
Ed Tanous6ea007a2019-02-13 22:48:25 -0800573 r = sd_bus_message_append_basic(
574 m, argCode[0],
575 reinterpret_cast<const void *>(stringValue->c_str()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 if (r < 0)
577 {
578 return r;
579 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700580 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700583 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 {
585 return -1;
586 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700587 int32_t i = static_cast<int32_t>(*intValue);
588 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 if (r < 0)
590 {
591 return r;
592 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700593 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700594 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 {
596 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700597 int boolInt = false;
598 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 }
602 else if (b != nullptr)
603 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600604 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700608 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 }
610 else
611 {
612 return -1;
613 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700614 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 if (r < 0)
616 {
617 return r;
618 }
619 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700620 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700622 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 {
624 return -1;
625 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 int16_t n = static_cast<int16_t>(*intValue);
627 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 if (r < 0)
629 {
630 return r;
631 }
632 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700633 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700635 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700636 {
637 return -1;
638 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 if (r < 0)
641 {
642 return r;
643 }
644 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700645 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700646 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700647 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 {
649 return -1;
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 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 uint16_t q = static_cast<uint16_t>(*uintValue);
661 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700663 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700665 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 {
667 return -1;
668 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700669 uint32_t u = static_cast<uint32_t>(*uintValue);
670 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700674 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
676 return -1;
677 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700678 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700688 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689 if (r < 0)
690 {
691 return r;
692 }
693
694 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
695 ++it)
696 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700697 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 if (r < 0)
699 {
700 return r;
701 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 }
703 sd_bus_message_close_container(m);
704 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700705 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700707 std::string containedType = argCode.substr(1);
708 BMCWEB_LOG_DEBUG << "variant type: " << argCode
709 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700711 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712 if (r < 0)
713 {
714 return r;
715 }
716
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700717 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 if (r < 0)
719 {
720 return r;
721 }
722
723 r = sd_bus_message_close_container(m);
724 if (r < 0)
725 {
726 return r;
727 }
728 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700729 else if (boost::starts_with(argCode, "(") &&
730 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700732 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700734 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800735 if (r < 0)
736 {
737 return r;
738 }
739
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 nlohmann::json::const_iterator it = j->begin();
Ed Tanous6ea007a2019-02-13 22:48:25 -0800741 for (const std::string &argCode2 : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 {
743 if (it == j->end())
744 {
745 return -1;
746 }
Ed Tanous6ea007a2019-02-13 22:48:25 -0800747 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 if (r < 0)
749 {
750 return r;
751 }
752 it++;
753 }
754 r = sd_bus_message_close_container(m);
755 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700756 else if (boost::starts_with(argCode, "{") &&
757 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700761 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800762 if (r < 0)
763 {
764 return r;
765 }
766
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700767 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 if (codes.size() != 2)
769 {
770 return -1;
771 }
772 const std::string &key_type = codes[0];
773 const std::string &value_type = codes[1];
774 for (auto it : j->items())
775 {
776 r = convertJsonToDbus(m, key_type, it.key());
777 if (r < 0)
778 {
779 return r;
780 }
781
782 r = convertJsonToDbus(m, value_type, it.value());
783 if (r < 0)
784 {
785 return r;
786 }
787 }
788 r = sd_bus_message_close_container(m);
789 }
790 else
791 {
792 return -2;
793 }
794 if (r < 0)
795 {
796 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700797 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700798
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799 if (argTypes.size() > 1)
800 {
801 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700802 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700803 }
Matt Spinler127ea542019-01-14 11:04:28 -0600804
805 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700806}
807
Matt Spinlerd22a7132019-01-14 12:14:30 -0600808template <typename T>
809int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
810 nlohmann::json &data)
811{
812 T value;
813
814 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
815 if (r < 0)
816 {
817 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
818 << " failed!";
819 return r;
820 }
821
822 data = value;
823 return 0;
824}
825
Matt Spinler16caaee2019-01-15 11:40:34 -0600826int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600827 sdbusplus::message::message &m, nlohmann::json &response);
828
829int readDictEntryFromMessage(const std::string &typeCode,
830 sdbusplus::message::message &m,
831 nlohmann::json &object)
832{
833 std::vector<std::string> types = dbusArgSplit(typeCode);
834 if (types.size() != 2)
835 {
836 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
837 << types.size();
838 return -1;
839 }
840
841 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
842 typeCode.c_str());
843 if (r < 0)
844 {
845 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
846 return r;
847 }
848
849 nlohmann::json key;
850 r = convertDBusToJSON(types[0], m, key);
851 if (r < 0)
852 {
853 return r;
854 }
855
856 const std::string *keyPtr = key.get_ptr<const std::string *>();
857 if (keyPtr == nullptr)
858 {
859 // json doesn't support non-string keys. If we hit this condition,
860 // convert the result to a string so we can proceed
861 key = key.dump();
862 keyPtr = key.get_ptr<const std::string *>();
863 // in theory this can't fail now, but lets be paranoid about it anyway
864 if (keyPtr == nullptr)
865 {
866 return -1;
867 }
868 }
869 nlohmann::json &value = object[*keyPtr];
870
871 r = convertDBusToJSON(types[1], m, value);
872 if (r < 0)
873 {
874 return r;
875 }
876
877 r = sd_bus_message_exit_container(m.get());
878 if (r < 0)
879 {
880 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
881 return r;
882 }
883
884 return 0;
885}
886
887int readArrayFromMessage(const std::string &typeCode,
888 sdbusplus::message::message &m, nlohmann::json &data)
889{
890 if (typeCode.size() < 2)
891 {
892 BMCWEB_LOG_ERROR << "Type code " << typeCode
893 << " too small for an array";
894 return -1;
895 }
896
897 std::string containedType = typeCode.substr(1);
898
899 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
900 containedType.c_str());
901 if (r < 0)
902 {
903 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
904 << r;
905 return r;
906 }
907
908 bool dict = boost::starts_with(containedType, "{") &&
909 boost::ends_with(containedType, "}");
910
911 if (dict)
912 {
913 // Remove the { }
914 containedType = containedType.substr(1, containedType.size() - 2);
915 data = nlohmann::json::object();
916 }
917 else
918 {
919 data = nlohmann::json::array();
920 }
921
922 while (true)
923 {
924 r = sd_bus_message_at_end(m.get(), false);
925 if (r < 0)
926 {
927 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
928 return r;
929 }
930
931 if (r > 0)
932 {
933 break;
934 }
935
936 // Dictionaries are only ever seen in an array
937 if (dict)
938 {
939 r = readDictEntryFromMessage(containedType, m, data);
940 if (r < 0)
941 {
942 return r;
943 }
944 }
945 else
946 {
947 data.push_back(nlohmann::json());
948
949 r = convertDBusToJSON(containedType, m, data.back());
950 if (r < 0)
951 {
952 return r;
953 }
954 }
955 }
956
957 r = sd_bus_message_exit_container(m.get());
958 if (r < 0)
959 {
960 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
961 return r;
962 }
963
964 return 0;
965}
966
Matt Spinler75c6c672019-01-14 13:01:46 -0600967int readStructFromMessage(const std::string &typeCode,
968 sdbusplus::message::message &m, nlohmann::json &data)
969{
970 if (typeCode.size() < 3)
971 {
972 BMCWEB_LOG_ERROR << "Type code " << typeCode
973 << " too small for a struct";
974 return -1;
975 }
976
977 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
978 std::vector<std::string> types = dbusArgSplit(containedTypes);
979
980 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
981 containedTypes.c_str());
982 if (r < 0)
983 {
984 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
985 << r;
986 return r;
987 }
988
989 for (const std::string &type : types)
990 {
991 data.push_back(nlohmann::json());
992 r = convertDBusToJSON(type, m, data.back());
993 if (r < 0)
994 {
995 return r;
996 }
997 }
998
999 r = sd_bus_message_exit_container(m.get());
1000 if (r < 0)
1001 {
1002 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1003 return r;
1004 }
1005 return 0;
1006}
1007
Matt Spinler89c19702019-01-14 13:13:00 -06001008int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1009{
1010 const char *containerType;
1011 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1012 if (r < 0)
1013 {
1014 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1015 return r;
1016 }
1017
1018 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1019 containerType);
1020 if (r < 0)
1021 {
1022 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1023 << r;
1024 return r;
1025 }
1026
1027 r = convertDBusToJSON(containerType, m, data);
1028 if (r < 0)
1029 {
1030 return r;
1031 }
1032
1033 r = sd_bus_message_exit_container(m.get());
1034 if (r < 0)
1035 {
1036 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1037 return r;
1038 }
1039
1040 return 0;
1041}
1042
Matt Spinler6df8f992019-01-14 12:47:47 -06001043int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001044 sdbusplus::message::message &m, nlohmann::json &response)
1045{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001046 int r = 0;
1047 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1048
Matt Spinlerd22a7132019-01-14 12:14:30 -06001049 for (const std::string &typeCode : returnTypes)
1050 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001051 nlohmann::json *thisElement = &response;
1052 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001053 {
1054 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001055 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001056 }
1057
1058 if (typeCode == "s")
1059 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001060 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001061 if (r < 0)
1062 {
1063 return r;
1064 }
1065 }
1066 else if (typeCode == "g")
1067 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001068 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001069 if (r < 0)
1070 {
1071 return r;
1072 }
1073 }
1074 else if (typeCode == "o")
1075 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001076 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001077 if (r < 0)
1078 {
1079 return r;
1080 }
1081 }
1082 else if (typeCode == "b")
1083 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001084 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001085 if (r < 0)
1086 {
1087 return r;
1088 }
1089
Matt Spinlerf39420c2019-01-30 12:57:18 -06001090 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001091 }
1092 else if (typeCode == "u")
1093 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001094 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001095 if (r < 0)
1096 {
1097 return r;
1098 }
1099 }
1100 else if (typeCode == "i")
1101 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001102 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001103 if (r < 0)
1104 {
1105 return r;
1106 }
1107 }
1108 else if (typeCode == "x")
1109 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001110 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001111 if (r < 0)
1112 {
1113 return r;
1114 }
1115 }
1116 else if (typeCode == "t")
1117 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001118 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001119 if (r < 0)
1120 {
1121 return r;
1122 }
1123 }
1124 else if (typeCode == "n")
1125 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001126 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001127 if (r < 0)
1128 {
1129 return r;
1130 }
1131 }
1132 else if (typeCode == "q")
1133 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001134 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001135 if (r < 0)
1136 {
1137 return r;
1138 }
1139 }
1140 else if (typeCode == "y")
1141 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001142 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001143 if (r < 0)
1144 {
1145 return r;
1146 }
1147 }
1148 else if (typeCode == "d")
1149 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001150 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001151 if (r < 0)
1152 {
1153 return r;
1154 }
1155 }
1156 else if (typeCode == "h")
1157 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001158 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001159 if (r < 0)
1160 {
1161 return r;
1162 }
1163 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001164 else if (boost::starts_with(typeCode, "a"))
1165 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001166 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001167 if (r < 0)
1168 {
1169 return r;
1170 }
1171 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001172 else if (boost::starts_with(typeCode, "(") &&
1173 boost::ends_with(typeCode, ")"))
1174 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001175 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001176 if (r < 0)
1177 {
1178 return r;
1179 }
1180 }
Matt Spinler89c19702019-01-14 13:13:00 -06001181 else if (boost::starts_with(typeCode, "v"))
1182 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001183 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001184 if (r < 0)
1185 {
1186 return r;
1187 }
1188 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001189 else
1190 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001191 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1192 return -2;
1193 }
1194 }
1195
Matt Spinler16caaee2019-01-15 11:40:34 -06001196 return 0;
1197}
1198
1199void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1200 sdbusplus::message::message &m,
1201 const std::string &returnType)
1202{
Matt Spinler39a4e392019-01-15 11:53:13 -06001203 nlohmann::json data;
1204
1205 int r = convertDBusToJSON(returnType, m, data);
1206 if (r < 0)
1207 {
1208 transaction->outputFailed = true;
1209 return;
1210 }
1211
1212 if (data.is_null())
1213 {
1214 return;
1215 }
1216
1217 if (transaction->methodResponse.is_null())
1218 {
1219 transaction->methodResponse = std::move(data);
1220 return;
1221 }
1222
1223 // If they're both dictionaries or arrays, merge into one.
1224 // Otherwise, make the results an array with every result
1225 // an entry. Could also just fail in that case, but it
1226 // seems better to get the data back somehow.
1227
1228 if (transaction->methodResponse.is_object() && data.is_object())
1229 {
1230 for (const auto &obj : data.items())
1231 {
1232 // Note: Will overwrite the data for a duplicate key
1233 transaction->methodResponse.emplace(obj.key(),
1234 std::move(obj.value()));
1235 }
1236 return;
1237 }
1238
1239 if (transaction->methodResponse.is_array() && data.is_array())
1240 {
1241 for (auto &obj : data)
1242 {
1243 transaction->methodResponse.push_back(std::move(obj));
1244 }
1245 return;
1246 }
1247
1248 if (!transaction->convertedToArray)
1249 {
1250 // They are different types. May as well turn them into an array
1251 nlohmann::json j = std::move(transaction->methodResponse);
1252 transaction->methodResponse = nlohmann::json::array();
1253 transaction->methodResponse.push_back(std::move(j));
1254 transaction->methodResponse.push_back(std::move(data));
1255 transaction->convertedToArray = true;
1256 }
1257 else
1258 {
1259 transaction->methodResponse.push_back(std::move(data));
1260 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001261}
1262
Ed Tanousd76323e2018-08-07 14:35:40 -07001263void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001264 const std::string &connectionName)
1265{
1266 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1267 << connectionName;
1268 crow::connections::systemBus->async_method_call(
1269 [transaction, connectionName{std::string(connectionName)}](
1270 const boost::system::error_code ec,
1271 const std::string &introspect_xml) {
1272 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1273 if (ec)
1274 {
1275 BMCWEB_LOG_ERROR
1276 << "Introspect call failed with error: " << ec.message()
1277 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001278 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001279 }
Matt Spinler318bd892019-01-15 09:59:20 -06001280 tinyxml2::XMLDocument doc;
1281
1282 doc.Parse(introspect_xml.data(), introspect_xml.size());
1283 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1284 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001285 {
Matt Spinler318bd892019-01-15 09:59:20 -06001286 BMCWEB_LOG_ERROR << "XML document failed to parse "
1287 << connectionName << "\n";
1288 return;
1289 }
1290 tinyxml2::XMLElement *interfaceNode =
1291 pRoot->FirstChildElement("interface");
1292 while (interfaceNode != nullptr)
1293 {
1294 const char *thisInterfaceName =
1295 interfaceNode->Attribute("name");
1296 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001297 {
Matt Spinler318bd892019-01-15 09:59:20 -06001298 if (!transaction->interfaceName.empty() &&
1299 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001300 {
Matt Spinler318bd892019-01-15 09:59:20 -06001301 interfaceNode =
1302 interfaceNode->NextSiblingElement("interface");
1303 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001304 }
Matt Spinler318bd892019-01-15 09:59:20 -06001305
1306 tinyxml2::XMLElement *methodNode =
1307 interfaceNode->FirstChildElement("method");
1308 while (methodNode != nullptr)
1309 {
1310 const char *thisMethodName =
1311 methodNode->Attribute("name");
1312 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1313 if (thisMethodName != nullptr &&
1314 thisMethodName == transaction->methodName)
1315 {
1316 BMCWEB_LOG_DEBUG
1317 << "Found method named " << thisMethodName
1318 << " on interface " << thisInterfaceName;
1319 sdbusplus::message::message m =
1320 crow::connections::systemBus->new_method_call(
1321 connectionName.c_str(),
1322 transaction->path.c_str(),
1323 thisInterfaceName,
1324 transaction->methodName.c_str());
1325
1326 tinyxml2::XMLElement *argumentNode =
1327 methodNode->FirstChildElement("arg");
1328
Matt Spinler16caaee2019-01-15 11:40:34 -06001329 std::string returnType;
1330
1331 // Find the output type
1332 while (argumentNode != nullptr)
1333 {
1334 const char *argDirection =
1335 argumentNode->Attribute("direction");
1336 const char *argType =
1337 argumentNode->Attribute("type");
1338 if (argDirection != nullptr &&
1339 argType != nullptr &&
1340 std::string(argDirection) == "out")
1341 {
1342 returnType = argType;
1343 break;
1344 }
1345 argumentNode =
1346 argumentNode->NextSiblingElement("arg");
1347 }
1348
Matt Spinler318bd892019-01-15 09:59:20 -06001349 nlohmann::json::const_iterator argIt =
1350 transaction->arguments.begin();
1351
Matt Spinler16caaee2019-01-15 11:40:34 -06001352 argumentNode = methodNode->FirstChildElement("arg");
1353
Matt Spinler318bd892019-01-15 09:59:20 -06001354 while (argumentNode != nullptr)
1355 {
1356 const char *argDirection =
1357 argumentNode->Attribute("direction");
1358 const char *argType =
1359 argumentNode->Attribute("type");
1360 if (argDirection != nullptr &&
1361 argType != nullptr &&
1362 std::string(argDirection) == "in")
1363 {
1364 if (argIt == transaction->arguments.end())
1365 {
1366 transaction->setErrorStatus(
1367 "Invalid method args");
1368 return;
1369 }
1370 if (convertJsonToDbus(m.get(),
1371 std::string(argType),
1372 *argIt) < 0)
1373 {
1374 transaction->setErrorStatus(
1375 "Invalid method arg type");
1376 return;
1377 }
1378
1379 argIt++;
1380 }
1381 argumentNode =
1382 argumentNode->NextSiblingElement("arg");
1383 }
1384
1385 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001386 m, [transaction, returnType](
1387 boost::system::error_code ec,
1388 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001389 if (ec)
1390 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001391 transaction->methodFailed = true;
Matt Spinler318bd892019-01-15 09:59:20 -06001392 return;
1393 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001394 else
1395 {
1396 transaction->methodPassed = true;
1397 }
1398
1399 handleMethodResponse(transaction, m,
1400 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001401 });
1402 break;
1403 }
1404 methodNode = methodNode->NextSiblingElement("method");
1405 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001406 }
Matt Spinler318bd892019-01-15 09:59:20 -06001407 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001408 }
1409 },
1410 connectionName, transaction->path,
1411 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001412}
1413
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001414void handleAction(const crow::Request &req, crow::Response &res,
1415 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001416{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001417 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1418 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001419 nlohmann::json requestDbusData =
1420 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001421
Ed Tanous1abe55e2018-09-05 08:30:59 -07001422 if (requestDbusData.is_discarded())
1423 {
Matt Spinler6db06242018-12-11 11:21:22 -06001424 setErrorResponse(res, boost::beast::http::status::bad_request,
1425 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001426 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001427 return;
1428 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001429 nlohmann::json::iterator data = requestDbusData.find("data");
1430 if (data == requestDbusData.end())
1431 {
Matt Spinler6db06242018-12-11 11:21:22 -06001432 setErrorResponse(res, boost::beast::http::status::bad_request,
1433 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001434 res.end();
1435 return;
1436 }
1437
1438 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001439 {
Matt Spinler6db06242018-12-11 11:21:22 -06001440 setErrorResponse(res, boost::beast::http::status::bad_request,
1441 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001442 res.end();
1443 return;
1444 }
1445 auto transaction = std::make_shared<InProgressActionData>(res);
1446
1447 transaction->path = objectPath;
1448 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001449 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001450 crow::connections::systemBus->async_method_call(
1451 [transaction](
1452 const boost::system::error_code ec,
1453 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001454 &interfaceNames) {
1455 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001456 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001457 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001458 setErrorResponse(transaction->res,
1459 boost::beast::http::status::not_found,
1460 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001461 return;
1462 }
1463
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001464 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1465 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001466
1467 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001468 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001469 {
1470 findActionOnInterface(transaction, object.first);
1471 }
1472 },
1473 "xyz.openbmc_project.ObjectMapper",
1474 "/xyz/openbmc_project/object_mapper",
1475 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1476 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001477}
1478
Matt Spinlerde818812018-12-11 16:39:20 -06001479void handleDelete(const crow::Request &req, crow::Response &res,
1480 const std::string &objectPath)
1481{
1482 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1483
1484 crow::connections::systemBus->async_method_call(
1485 [&res, objectPath](
1486 const boost::system::error_code ec,
1487 const std::vector<std::pair<std::string, std::vector<std::string>>>
1488 &interfaceNames) {
1489 if (ec || interfaceNames.size() <= 0)
1490 {
1491 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001492 setErrorResponse(res,
1493 boost::beast::http::status::method_not_allowed,
1494 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001495 res.end();
1496 return;
1497 }
1498
1499 auto transaction = std::make_shared<InProgressActionData>(res);
1500 transaction->path = objectPath;
1501 transaction->methodName = "Delete";
1502 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1503
1504 for (const std::pair<std::string, std::vector<std::string>>
1505 &object : interfaceNames)
1506 {
1507 findActionOnInterface(transaction, object.first);
1508 }
1509 },
1510 "xyz.openbmc_project.ObjectMapper",
1511 "/xyz/openbmc_project/object_mapper",
1512 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1513 std::array<const char *, 0>());
1514}
1515
Ed Tanousf839dfe2018-11-12 11:11:15 -08001516void handleList(crow::Response &res, const std::string &objectPath,
1517 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001518{
1519 crow::connections::systemBus->async_method_call(
1520 [&res](const boost::system::error_code ec,
1521 std::vector<std::string> &objectPaths) {
1522 if (ec)
1523 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001524 setErrorResponse(res, boost::beast::http::status::not_found,
1525 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001526 }
1527 else
1528 {
1529 res.jsonValue = {{"status", "ok"},
1530 {"message", "200 OK"},
1531 {"data", std::move(objectPaths)}};
1532 }
1533 res.end();
1534 },
1535 "xyz.openbmc_project.ObjectMapper",
1536 "/xyz/openbmc_project/object_mapper",
1537 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001538 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001539}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001540
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001541void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001542{
Ed Tanous049a0512018-11-01 13:58:42 -07001543 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1544 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1545
1546 asyncResp->res.jsonValue = {{"message", "200 OK"},
1547 {"status", "ok"},
1548 {"data", nlohmann::json::object()}};
1549
Ed Tanous1abe55e2018-09-05 08:30:59 -07001550 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001551 [objectPath, asyncResp](const boost::system::error_code ec,
1552 GetSubTreeType &object_names) {
1553 auto transaction = std::make_shared<InProgressEnumerateData>(
1554 objectPath, asyncResp);
1555
1556 transaction->subtree =
1557 std::make_shared<GetSubTreeType>(std::move(object_names));
1558
Ed Tanous1abe55e2018-09-05 08:30:59 -07001559 if (ec)
1560 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001561 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1562 << transaction->objectPath;
1563 setErrorResponse(transaction->asyncResp->res,
1564 boost::beast::http::status::not_found,
1565 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001566 return;
1567 }
Ed Tanous64530012018-02-06 17:08:16 -08001568
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001569 // Add the data for the path passed in to the results
1570 // as if GetSubTree returned it, and continue on enumerating
1571 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001572 },
1573 "xyz.openbmc_project.ObjectMapper",
1574 "/xyz/openbmc_project/object_mapper",
1575 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001576 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001577}
Ed Tanous911ac312017-08-15 09:37:42 -07001578
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001579void handleGet(crow::Response &res, std::string &objectPath,
1580 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001581{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001582 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1583 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001584 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001585
Ed Tanous1abe55e2018-09-05 08:30:59 -07001586 std::shared_ptr<std::string> path =
1587 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001588
Ed Tanous1abe55e2018-09-05 08:30:59 -07001589 using GetObjectType =
1590 std::vector<std::pair<std::string, std::vector<std::string>>>;
1591 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001592 [&res, path, propertyName](const boost::system::error_code ec,
1593 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001594 if (ec || object_names.size() <= 0)
1595 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001596 setErrorResponse(res, boost::beast::http::status::not_found,
1597 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001598 res.end();
1599 return;
1600 }
1601 std::shared_ptr<nlohmann::json> response =
1602 std::make_shared<nlohmann::json>(nlohmann::json::object());
1603 // The mapper should never give us an empty interface names list,
1604 // but check anyway
1605 for (const std::pair<std::string, std::vector<std::string>>
1606 connection : object_names)
1607 {
1608 const std::vector<std::string> &interfaceNames =
1609 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001610
Ed Tanous1abe55e2018-09-05 08:30:59 -07001611 if (interfaceNames.size() <= 0)
1612 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001613 setErrorResponse(res, boost::beast::http::status::not_found,
1614 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001615 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001616 return;
1617 }
1618
1619 for (const std::string &interface : interfaceNames)
1620 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001621 sdbusplus::message::message m =
1622 crow::connections::systemBus->new_method_call(
1623 connection.first.c_str(), path->c_str(),
1624 "org.freedesktop.DBus.Properties", "GetAll");
1625 m.append(interface);
1626 crow::connections::systemBus->async_send(
1627 m, [&res, response,
1628 propertyName](const boost::system::error_code ec,
1629 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001630 if (ec)
1631 {
1632 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1633 << ec;
1634 }
1635 else
1636 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001637 nlohmann::json properties;
1638 int r =
1639 convertDBusToJSON("a{sv}", msg, properties);
1640 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001641 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001642 BMCWEB_LOG_ERROR
1643 << "convertDBusToJSON failed";
1644 }
1645 else
1646 {
1647 for (auto &prop : properties.items())
1648 {
1649 // if property name is empty, or matches
1650 // our search query, add it to the
1651 // response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001652
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001653 if (propertyName->empty())
1654 {
1655 (*response)[prop.key()] =
1656 std::move(prop.value());
1657 }
1658 else if (prop.key() == *propertyName)
1659 {
1660 *response = std::move(prop.value());
1661 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001662 }
1663 }
1664 }
1665 if (response.use_count() == 1)
1666 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001667 if (!propertyName->empty() && response->empty())
1668 {
1669 setErrorResponse(
1670 res,
1671 boost::beast::http::status::not_found,
1672 propNotFoundDesc, notFoundMsg);
1673 }
1674 else
1675 {
1676 res.jsonValue = {{"status", "ok"},
1677 {"message", "200 OK"},
1678 {"data", *response}};
1679 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001680 res.end();
1681 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001682 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001683 }
1684 }
1685 },
1686 "xyz.openbmc_project.ObjectMapper",
1687 "/xyz/openbmc_project/object_mapper",
1688 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1689 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001690}
1691
Ed Tanous1abe55e2018-09-05 08:30:59 -07001692struct AsyncPutRequest
1693{
1694 AsyncPutRequest(crow::Response &res) : res(res)
1695 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001696 }
1697 ~AsyncPutRequest()
1698 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001699 if (res.jsonValue.empty())
1700 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001701 setErrorResponse(res, boost::beast::http::status::forbidden,
1702 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001703 }
1704
1705 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001706 }
1707
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001708 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001709 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001710 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1711 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001712 }
1713
Ed Tanous1abe55e2018-09-05 08:30:59 -07001714 crow::Response &res;
1715 std::string objectPath;
1716 std::string propertyName;
1717 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001718};
1719
Ed Tanousd76323e2018-08-07 14:35:40 -07001720void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001721 const std::string &objectPath, const std::string &destProperty)
1722{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001723 if (destProperty.empty())
1724 {
1725 setErrorResponse(res, boost::beast::http::status::forbidden,
1726 forbiddenResDesc, forbiddenMsg);
1727 res.end();
1728 return;
1729 }
1730
Ed Tanous1abe55e2018-09-05 08:30:59 -07001731 nlohmann::json requestDbusData =
1732 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001733
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 if (requestDbusData.is_discarded())
1735 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001736 setErrorResponse(res, boost::beast::http::status::bad_request,
1737 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001738 res.end();
1739 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001741
Ed Tanous1abe55e2018-09-05 08:30:59 -07001742 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1743 if (propertyIt == requestDbusData.end())
1744 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001745 setErrorResponse(res, boost::beast::http::status::bad_request,
1746 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001747 res.end();
1748 return;
1749 }
1750 const nlohmann::json &propertySetValue = *propertyIt;
1751 auto transaction = std::make_shared<AsyncPutRequest>(res);
1752 transaction->objectPath = objectPath;
1753 transaction->propertyName = destProperty;
1754 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001755
Ed Tanous1abe55e2018-09-05 08:30:59 -07001756 using GetObjectType =
1757 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001758
Ed Tanous1abe55e2018-09-05 08:30:59 -07001759 crow::connections::systemBus->async_method_call(
1760 [transaction](const boost::system::error_code ec,
1761 const GetObjectType &object_names) {
1762 if (!ec && object_names.size() <= 0)
1763 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001764 setErrorResponse(transaction->res,
1765 boost::beast::http::status::not_found,
1766 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001767 return;
1768 }
Ed Tanous911ac312017-08-15 09:37:42 -07001769
Ed Tanous1abe55e2018-09-05 08:30:59 -07001770 for (const std::pair<std::string, std::vector<std::string>>
1771 connection : object_names)
1772 {
1773 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001774
Ed Tanous1abe55e2018-09-05 08:30:59 -07001775 crow::connections::systemBus->async_method_call(
1776 [connectionName{std::string(connectionName)},
1777 transaction](const boost::system::error_code ec,
1778 const std::string &introspectXml) {
1779 if (ec)
1780 {
1781 BMCWEB_LOG_ERROR
1782 << "Introspect call failed with error: "
1783 << ec.message()
1784 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001785 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001786 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001787 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001788 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001789
Ed Tanous1abe55e2018-09-05 08:30:59 -07001790 doc.Parse(introspectXml.c_str());
1791 tinyxml2::XMLNode *pRoot =
1792 doc.FirstChildElement("node");
1793 if (pRoot == nullptr)
1794 {
1795 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1796 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001797 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001798 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001799 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001800 tinyxml2::XMLElement *ifaceNode =
1801 pRoot->FirstChildElement("interface");
1802 while (ifaceNode != nullptr)
1803 {
1804 const char *interfaceName =
1805 ifaceNode->Attribute("name");
1806 BMCWEB_LOG_DEBUG << "found interface "
1807 << interfaceName;
1808 tinyxml2::XMLElement *propNode =
1809 ifaceNode->FirstChildElement("property");
1810 while (propNode != nullptr)
1811 {
1812 const char *propertyName =
1813 propNode->Attribute("name");
1814 BMCWEB_LOG_DEBUG << "Found property "
1815 << propertyName;
1816 if (propertyName == transaction->propertyName)
1817 {
1818 const char *argType =
1819 propNode->Attribute("type");
1820 if (argType != nullptr)
1821 {
1822 sdbusplus::message::message m =
1823 crow::connections::systemBus
1824 ->new_method_call(
1825 connectionName.c_str(),
1826 transaction->objectPath
1827 .c_str(),
1828 "org.freedesktop.DBus."
1829 "Properties",
1830 "Set");
1831 m.append(interfaceName,
1832 transaction->propertyName);
1833 int r = sd_bus_message_open_container(
1834 m.get(), SD_BUS_TYPE_VARIANT,
1835 argType);
1836 if (r < 0)
1837 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001838 transaction->setErrorStatus(
1839 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001840 return;
1841 }
1842 r = convertJsonToDbus(
1843 m.get(), argType,
1844 transaction->propertyValue);
1845 if (r < 0)
1846 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001847 transaction->setErrorStatus(
1848 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001849 return;
1850 }
1851 r = sd_bus_message_close_container(
1852 m.get());
1853 if (r < 0)
1854 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001855 transaction->setErrorStatus(
1856 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001857 return;
1858 }
Ed Tanous911ac312017-08-15 09:37:42 -07001859
Ed Tanous1abe55e2018-09-05 08:30:59 -07001860 crow::connections::systemBus
1861 ->async_send(
1862 m,
1863 [transaction](
1864 boost::system::error_code
1865 ec,
1866 sdbusplus::message::message
1867 &m) {
1868 BMCWEB_LOG_DEBUG << "sent";
1869 if (ec)
1870 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001871 setErrorResponse(
1872 transaction->res,
1873 boost::beast::http::
1874 status::
1875 forbidden,
1876 forbiddenPropDesc,
1877 ec.message());
1878 }
1879 else
1880 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001881 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001882 .jsonValue = {
1883 {"status", "ok"},
1884 {"message",
1885 "200 OK"},
1886 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001887 }
1888 });
1889 }
1890 }
1891 propNode =
1892 propNode->NextSiblingElement("property");
1893 }
1894 ifaceNode =
1895 ifaceNode->NextSiblingElement("interface");
1896 }
1897 },
1898 connectionName, transaction->objectPath,
1899 "org.freedesktop.DBus.Introspectable", "Introspect");
1900 }
1901 },
1902 "xyz.openbmc_project.ObjectMapper",
1903 "/xyz/openbmc_project/object_mapper",
1904 "xyz.openbmc_project.ObjectMapper", "GetObject",
1905 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001906}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001907
Ed Tanous049a0512018-11-01 13:58:42 -07001908inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1909 std::string &objectPath)
1910{
Ed Tanous049a0512018-11-01 13:58:42 -07001911
1912 // If accessing a single attribute, fill in and update objectPath,
1913 // otherwise leave destProperty blank
1914 std::string destProperty = "";
1915 const char *attrSeperator = "/attr/";
1916 size_t attrPosition = objectPath.find(attrSeperator);
1917 if (attrPosition != objectPath.npos)
1918 {
1919 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1920 objectPath.length());
1921 objectPath = objectPath.substr(0, attrPosition);
1922 }
1923
1924 if (req.method() == "POST"_method)
1925 {
1926 constexpr const char *actionSeperator = "/action/";
1927 size_t actionPosition = objectPath.find(actionSeperator);
1928 if (actionPosition != objectPath.npos)
1929 {
1930 std::string postProperty =
1931 objectPath.substr((actionPosition + strlen(actionSeperator)),
1932 objectPath.length());
1933 objectPath = objectPath.substr(0, actionPosition);
1934 handleAction(req, res, objectPath, postProperty);
1935 return;
1936 }
1937 }
1938 else if (req.method() == "GET"_method)
1939 {
1940 if (boost::ends_with(objectPath, "/enumerate"))
1941 {
1942 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1943 objectPath.end());
1944 handleEnumerate(res, objectPath);
1945 }
1946 else if (boost::ends_with(objectPath, "/list"))
1947 {
1948 objectPath.erase(objectPath.end() - sizeof("list"),
1949 objectPath.end());
1950 handleList(res, objectPath);
1951 }
1952 else
1953 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001954 // Trim any trailing "/" at the end
1955 if (boost::ends_with(objectPath, "/"))
1956 {
1957 objectPath.pop_back();
1958 handleList(res, objectPath, 1);
1959 }
1960 else
1961 {
1962 handleGet(res, objectPath, destProperty);
1963 }
Ed Tanous049a0512018-11-01 13:58:42 -07001964 }
1965 return;
1966 }
1967 else if (req.method() == "PUT"_method)
1968 {
1969 handlePut(req, res, objectPath, destProperty);
1970 return;
1971 }
Matt Spinlerde818812018-12-11 16:39:20 -06001972 else if (req.method() == "DELETE"_method)
1973 {
1974 handleDelete(req, res, objectPath);
1975 return;
1976 }
Ed Tanous049a0512018-11-01 13:58:42 -07001977
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001978 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1979 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001980 res.end();
1981}
1982
Ed Tanous1abe55e2018-09-05 08:30:59 -07001983template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1984{
1985 BMCWEB_ROUTE(app, "/bus/")
1986 .methods("GET"_method)(
1987 [](const crow::Request &req, crow::Response &res) {
1988 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1989 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001990 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001991 });
1992
1993 BMCWEB_ROUTE(app, "/bus/system/")
1994 .methods("GET"_method)(
1995 [](const crow::Request &req, crow::Response &res) {
1996 auto myCallback = [&res](const boost::system::error_code ec,
1997 std::vector<std::string> &names) {
1998 if (ec)
1999 {
2000 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2001 res.result(
2002 boost::beast::http::status::internal_server_error);
2003 }
2004 else
2005 {
2006 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002007 res.jsonValue = {{"status", "ok"}};
2008 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002009 for (auto &name : names)
2010 {
2011 objectsSub.push_back({{"name", name}});
2012 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002013 }
2014 res.end();
2015 };
2016 crow::connections::systemBus->async_method_call(
2017 std::move(myCallback), "org.freedesktop.DBus", "/",
2018 "org.freedesktop.DBus", "ListNames");
2019 });
2020
2021 BMCWEB_ROUTE(app, "/list/")
2022 .methods("GET"_method)(
2023 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002024 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002025 });
2026
2027 BMCWEB_ROUTE(app, "/xyz/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06002028 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002029 [](const crow::Request &req, crow::Response &res,
2030 const std::string &path) {
2031 std::string objectPath = "/xyz/" + path;
2032 handleDBusUrl(req, res, objectPath);
2033 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002034
Ed Tanous049a0512018-11-01 13:58:42 -07002035 BMCWEB_ROUTE(app, "/org/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06002036 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002037 [](const crow::Request &req, crow::Response &res,
2038 const std::string &path) {
2039 std::string objectPath = "/org/" + path;
2040 handleDBusUrl(req, res, objectPath);
2041 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002042
Ed Tanous1abe55e2018-09-05 08:30:59 -07002043 BMCWEB_ROUTE(app, "/download/dump/<str>/")
2044 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2045 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002046 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002047 if (!std::regex_match(dumpId, validFilename))
2048 {
Ed Tanousad18f072018-11-14 14:07:48 -08002049 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002050 res.end();
2051 return;
2052 }
James Feistf6150402019-01-08 10:36:20 -08002053 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002054 "/var/lib/phosphor-debug-collector/dumps");
2055
Ed Tanousad18f072018-11-14 14:07:48 -08002056 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002057
James Feistf6150402019-01-08 10:36:20 -08002058 if (!std::filesystem::exists(loc) ||
2059 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002060 {
Ed Tanousad18f072018-11-14 14:07:48 -08002061 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002062 res.result(boost::beast::http::status::not_found);
2063 res.end();
2064 return;
2065 }
James Feistf6150402019-01-08 10:36:20 -08002066 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002067
Ed Tanous1abe55e2018-09-05 08:30:59 -07002068 for (auto &file : files)
2069 {
2070 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002071 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002072 {
2073 continue;
2074 }
2075 res.addHeader("Content-Type", "application/octet-stream");
2076 res.body() = {std::istreambuf_iterator<char>(readFile),
2077 std::istreambuf_iterator<char>()};
2078 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002079 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002080 }
2081 res.result(boost::beast::http::status::not_found);
2082 res.end();
2083 return;
2084 });
2085
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002086 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07002087 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002088 const std::string &Connection) {
2089 introspectObjects(Connection, "/",
2090 std::make_shared<bmcweb::AsyncResp>(res));
2091 });
2092
2093 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2094 .methods("GET"_method,
2095 "POST"_method)([](const crow::Request &req,
2096 crow::Response &res,
2097 const std::string &processName,
2098 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002099 std::vector<std::string> strs;
2100 boost::split(strs, requestedPath, boost::is_any_of("/"));
2101 std::string objectPath;
2102 std::string interfaceName;
2103 std::string methodName;
2104 auto it = strs.begin();
2105 if (it == strs.end())
2106 {
2107 objectPath = "/";
2108 }
2109 while (it != strs.end())
2110 {
2111 // Check if segment contains ".". If it does, it must be an
2112 // interface
2113 if (it->find(".") != std::string::npos)
2114 {
2115 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002116 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07002117 // as part of our <path> specifier above, which causes the
2118 // normal trailing backslash redirector to fail.
2119 }
2120 else if (!it->empty())
2121 {
2122 objectPath += "/" + *it;
2123 }
2124 it++;
2125 }
2126 if (it != strs.end())
2127 {
2128 interfaceName = *it;
2129 it++;
2130
2131 // after interface, we might have a method name
2132 if (it != strs.end())
2133 {
2134 methodName = *it;
2135 it++;
2136 }
2137 }
2138 if (it != strs.end())
2139 {
2140 // if there is more levels past the method name, something went
2141 // wrong, return not found
2142 res.result(boost::beast::http::status::not_found);
2143 res.end();
2144 return;
2145 }
2146 if (interfaceName.empty())
2147 {
2148 crow::connections::systemBus->async_method_call(
2149 [&, processName,
2150 objectPath](const boost::system::error_code ec,
2151 const std::string &introspect_xml) {
2152 if (ec)
2153 {
2154 BMCWEB_LOG_ERROR
2155 << "Introspect call failed with error: "
2156 << ec.message()
2157 << " on process: " << processName
2158 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002159 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002160 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002161 tinyxml2::XMLDocument doc;
2162
2163 doc.Parse(introspect_xml.c_str());
2164 tinyxml2::XMLNode *pRoot =
2165 doc.FirstChildElement("node");
2166 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002167 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002168 BMCWEB_LOG_ERROR << "XML document failed to parse "
2169 << processName << " " << objectPath
2170 << "\n";
2171 res.jsonValue = {{"status", "XML parse error"}};
2172 res.result(boost::beast::http::status::
2173 internal_server_error);
2174 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002175 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002176
2177 BMCWEB_LOG_DEBUG << introspect_xml;
2178 res.jsonValue = {{"status", "ok"},
2179 {"bus_name", processName},
2180 {"object_path", objectPath}};
2181 nlohmann::json &interfacesArray =
2182 res.jsonValue["interfaces"];
2183 interfacesArray = nlohmann::json::array();
2184 tinyxml2::XMLElement *interface =
2185 pRoot->FirstChildElement("interface");
2186
2187 while (interface != nullptr)
2188 {
2189 const char *ifaceName =
2190 interface->Attribute("name");
2191 if (ifaceName != nullptr)
2192 {
2193 interfacesArray.push_back(
2194 {{"name", ifaceName}});
2195 }
2196
2197 interface =
2198 interface->NextSiblingElement("interface");
2199 }
2200
Ed Tanous1abe55e2018-09-05 08:30:59 -07002201 res.end();
2202 },
2203 processName, objectPath,
2204 "org.freedesktop.DBus.Introspectable", "Introspect");
2205 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002206 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002207 {
2208 crow::connections::systemBus->async_method_call(
2209 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002210 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07002211 const boost::system::error_code ec,
2212 const std::string &introspect_xml) {
2213 if (ec)
2214 {
2215 BMCWEB_LOG_ERROR
2216 << "Introspect call failed with error: "
2217 << ec.message()
2218 << " on process: " << processName
2219 << " path: " << objectPath << "\n";
2220 }
2221 else
2222 {
2223 tinyxml2::XMLDocument doc;
2224
2225 doc.Parse(introspect_xml.c_str());
2226 tinyxml2::XMLNode *pRoot =
2227 doc.FirstChildElement("node");
2228 if (pRoot == nullptr)
2229 {
2230 BMCWEB_LOG_ERROR
2231 << "XML document failed to parse "
2232 << processName << " " << objectPath << "\n";
2233 res.result(boost::beast::http::status::
2234 internal_server_error);
2235 }
2236 else
2237 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002238 // if we know we're the only call, build the
2239 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07002240 tinyxml2::XMLElement *interface =
2241 pRoot->FirstChildElement("interface");
2242
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002243 res.jsonValue = {
2244 {"status", "ok"},
2245 {"bus_name", processName},
2246 {"interface", interfaceName},
2247 {"object_path", objectPath},
2248 {"properties", nlohmann::json::object()}};
2249
2250 nlohmann::json &methodsArray =
2251 res.jsonValue["methods"];
2252 methodsArray = nlohmann::json::array();
2253
2254 nlohmann::json &signalsArray =
2255 res.jsonValue["signals"];
2256 signalsArray = nlohmann::json::array();
2257
Ed Tanous1abe55e2018-09-05 08:30:59 -07002258 while (interface != nullptr)
2259 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002260 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002261 interface->Attribute("name");
2262
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002263 if (ifaceName != nullptr &&
2264 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002265 {
2266 tinyxml2::XMLElement *methods =
2267 interface->FirstChildElement(
2268 "method");
2269 while (methods != nullptr)
2270 {
2271 nlohmann::json argsArray =
2272 nlohmann::json::array();
2273 tinyxml2::XMLElement *arg =
2274 methods->FirstChildElement(
2275 "arg");
2276 while (arg != nullptr)
2277 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002278 nlohmann::json thisArg;
2279 for (const char *fieldName :
2280 std::array<const char *,
2281 3>{"name",
2282 "direction",
2283 "type"})
2284 {
2285 const char *fieldValue =
2286 arg->Attribute(
2287 fieldName);
2288 if (fieldValue != nullptr)
2289 {
2290 thisArg[fieldName] =
2291 fieldValue;
2292 }
2293 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002294 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002295 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07002296 arg = arg->NextSiblingElement(
2297 "arg");
2298 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002299
2300 const char *name =
2301 methods->Attribute("name");
2302 if (name != nullptr)
2303 {
2304 methodsArray.push_back(
2305 {{"name", name},
2306 {"uri", "/bus/system/" +
2307 processName +
2308 objectPath +
2309 "/" +
2310 interfaceName +
2311 "/" + name},
2312 {"args", argsArray}});
2313 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002314 methods =
2315 methods->NextSiblingElement(
2316 "method");
2317 }
2318 tinyxml2::XMLElement *signals =
2319 interface->FirstChildElement(
2320 "signal");
2321 while (signals != nullptr)
2322 {
2323 nlohmann::json argsArray =
2324 nlohmann::json::array();
2325
2326 tinyxml2::XMLElement *arg =
2327 signals->FirstChildElement(
2328 "arg");
2329 while (arg != nullptr)
2330 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002331 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002332 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002333 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002334 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002335 if (name != nullptr &&
2336 type != nullptr)
2337 {
2338 argsArray.push_back({
2339 {"name", name},
2340 {"type", type},
2341 });
2342 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002343 arg = arg->NextSiblingElement(
2344 "arg");
2345 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002346 const char *name =
2347 signals->Attribute("name");
2348 if (name != nullptr)
2349 {
2350 signalsArray.push_back(
2351 {{"name", name},
2352 {"args", argsArray}});
2353 }
2354
Ed Tanous1abe55e2018-09-05 08:30:59 -07002355 signals =
2356 signals->NextSiblingElement(
2357 "signal");
2358 }
2359
Ed Tanous1abe55e2018-09-05 08:30:59 -07002360 break;
2361 }
2362
2363 interface = interface->NextSiblingElement(
2364 "interface");
2365 }
2366 if (interface == nullptr)
2367 {
2368 // if we got to the end of the list and
2369 // never found a match, throw 404
2370 res.result(
2371 boost::beast::http::status::not_found);
2372 }
2373 }
2374 }
2375 res.end();
2376 },
2377 processName, objectPath,
2378 "org.freedesktop.DBus.Introspectable", "Introspect");
2379 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002380 else
2381 {
2382 if (req.method() != "POST"_method)
2383 {
2384 res.result(boost::beast::http::status::not_found);
2385 res.end();
2386 return;
2387 }
2388
2389 nlohmann::json requestDbusData =
2390 nlohmann::json::parse(req.body, nullptr, false);
2391
2392 if (requestDbusData.is_discarded())
2393 {
2394 res.result(boost::beast::http::status::bad_request);
2395 res.end();
2396 return;
2397 }
2398 if (!requestDbusData.is_array())
2399 {
2400 res.result(boost::beast::http::status::bad_request);
2401 res.end();
2402 return;
2403 }
2404 auto transaction = std::make_shared<InProgressActionData>(res);
2405
2406 transaction->path = objectPath;
2407 transaction->methodName = methodName;
2408 transaction->arguments = std::move(requestDbusData);
2409
2410 findActionOnInterface(transaction, processName);
2411 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002412 });
2413}
2414} // namespace openbmc_mapper
2415} // namespace crow