blob: 950319113f6f6b38cb430c57f2bb1a8c17972b82 [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];
158 sdbusplus::message::variant_ns::visit(
159 [&propertyJson](auto &&val) { propertyJson = val; }, value);
160 }
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];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700258 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 [&propertyJson](auto &&val) {
260 propertyJson = val;
261 },
262 property.second);
263 }
264 }
265 }
Ed Tanous049a0512018-11-01 13:58:42 -0700266 for (const auto &interface : objectPath.second)
267 {
268 if (interface.first == "org.freedesktop.DBus.ObjectManager")
269 {
270 getManagedObjectsForEnumerate(
271 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600272 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700273 }
274 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 }
276 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700277 connection_name, object_manager_path,
278 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
279}
280
281void findObjectManagerPathForEnumerate(
282 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600283 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284{
Ed Tanous049a0512018-11-01 13:58:42 -0700285 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
286 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700287 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600288 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700289 const boost::system::error_code ec,
290 const boost::container::flat_map<
291 std::string, boost::container::flat_map<
292 std::string, std::vector<std::string>>>
293 &objects) {
294 if (ec)
295 {
Ed Tanous049a0512018-11-01 13:58:42 -0700296 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
297 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700298 return;
299 }
300
Ed Tanousf254ba72018-10-12 13:40:35 -0700301 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700303 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700304 {
305 if (connectionGroup.first == connection_name)
306 {
307 // Found the object manager path for this resource.
308 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700309 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600310 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700311 return;
312 }
313 }
314 }
315 },
316 "xyz.openbmc_project.ObjectMapper",
317 "/xyz/openbmc_project/object_mapper",
318 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
319 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700320}
Ed Tanous64530012018-02-06 17:08:16 -0800321
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600322// Uses GetObject to add the object info about the target /enumerate path to the
323// results of GetSubTree, as GetSubTree will not return info for the
324// target path, and then continues on enumerating the rest of the tree.
325void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
326{
327 using GetObjectType =
328 std::vector<std::pair<std::string, std::vector<std::string>>>;
329
330 crow::connections::systemBus->async_method_call(
331 [transaction](const boost::system::error_code ec,
332 const GetObjectType &objects) {
333 if (ec)
334 {
335 BMCWEB_LOG_ERROR << "GetObject for path "
336 << transaction->objectPath
337 << " failed with code " << ec;
338 return;
339 }
340
341 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
342 << " has " << objects.size() << " entries";
343 if (!objects.empty())
344 {
345 transaction->subtree->emplace_back(transaction->objectPath,
346 objects);
347 }
348
349 // Map indicating connection name, and the path where the object
350 // manager exists
351 boost::container::flat_map<std::string, std::string> connections;
352
353 for (const auto &object : *(transaction->subtree))
354 {
355 for (const auto &connection : object.second)
356 {
357 std::string &objectManagerPath =
358 connections[connection.first];
359 for (const auto &interface : connection.second)
360 {
361 BMCWEB_LOG_DEBUG << connection.first
362 << " has interface " << interface;
363 if (interface == "org.freedesktop.DBus.ObjectManager")
364 {
365 BMCWEB_LOG_DEBUG << "found object manager path "
366 << object.first;
367 objectManagerPath = object.first;
368 }
369 }
370 }
371 }
372 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
373
374 for (const auto &connection : connections)
375 {
376 // If we already know where the object manager is, we don't need
377 // to search for it, we can call directly in to
378 // getManagedObjects
379 if (!connection.second.empty())
380 {
381 getManagedObjectsForEnumerate(
382 transaction->objectPath, connection.second,
383 connection.first, transaction);
384 }
385 else
386 {
387 // otherwise we need to find the object manager path before
388 // we can continue
389 findObjectManagerPathForEnumerate(
390 transaction->objectPath, connection.first, transaction);
391 }
392 }
393 },
394 "xyz.openbmc_project.ObjectMapper",
395 "/xyz/openbmc_project/object_mapper",
396 "xyz.openbmc_project.ObjectMapper", "GetObject",
397 transaction->objectPath, std::array<const char *, 0>());
398}
Ed Tanous64530012018-02-06 17:08:16 -0800399
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700400// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700401struct InProgressActionData
402{
403 InProgressActionData(crow::Response &res) : res(res){};
404 ~InProgressActionData()
405 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600406 // Methods could have been called across different owners
407 // and interfaces, where some calls failed and some passed.
408 //
409 // The rules for this are:
410 // * if no method was called - error
411 // * if a method failed and none passed - error
412 // (converse: if at least one method passed - OK)
413 // * for the method output:
414 // * if output processing didn't fail, return the data
415
416 // Only deal with method returns if nothing failed earlier
417 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600419 if (!methodPassed)
420 {
421 if (methodFailed)
422 {
423 setErrorResponse(res,
424 boost::beast::http::status::bad_request,
425 "Method call failed", methodFailedMsg);
426 }
427 else
428 {
429 setErrorResponse(res, boost::beast::http::status::not_found,
430 methodNotFoundDesc, notFoundMsg);
431 }
432 }
433 else
434 {
435 if (outputFailed)
436 {
437 setErrorResponse(
438 res, boost::beast::http::status::internal_server_error,
439 "Method output failure", methodOutputFailedMsg);
440 }
441 else
442 {
443 res.jsonValue = {{"status", "ok"},
444 {"message", "200 OK"},
445 {"data", methodResponse}};
446 }
447 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600449
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700451 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700452
Matt Spinler6db06242018-12-11 11:21:22 -0600453 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600455 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
456 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 }
458 crow::Response &res;
459 std::string path;
460 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600461 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600462 bool methodPassed = false;
463 bool methodFailed = false;
464 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600465 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600466 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700468};
469
Ed Tanous1abe55e2018-09-05 08:30:59 -0700470std::vector<std::string> dbusArgSplit(const std::string &string)
471{
472 std::vector<std::string> ret;
473 if (string.empty())
474 {
475 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700476 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700477 ret.push_back("");
478 int containerDepth = 0;
479
480 for (std::string::const_iterator character = string.begin();
481 character != string.end(); character++)
482 {
483 ret.back() += *character;
484 switch (*character)
485 {
486 case ('a'):
487 break;
488 case ('('):
489 case ('{'):
490 containerDepth++;
491 break;
492 case ('}'):
493 case (')'):
494 containerDepth--;
495 if (containerDepth == 0)
496 {
497 if (character + 1 != string.end())
498 {
499 ret.push_back("");
500 }
501 }
502 break;
503 default:
504 if (containerDepth == 0)
505 {
506 if (character + 1 != string.end())
507 {
508 ret.push_back("");
509 }
510 }
511 break;
512 }
513 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600514
515 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700516}
517
Ed Tanousd76323e2018-08-07 14:35:40 -0700518int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700519 const nlohmann::json &input_json)
520{
521 int r = 0;
522 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
523 << " to type: " << arg_type;
524 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700525
Ed Tanous1abe55e2018-09-05 08:30:59 -0700526 // Assume a single object for now.
527 const nlohmann::json *j = &input_json;
528 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700529
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700530 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 {
532 // If we are decoding multiple objects, grab the pointer to the
533 // iterator, and increment it for the next loop
534 if (argTypes.size() > 1)
535 {
536 if (jIt == input_json.end())
537 {
538 return -2;
539 }
540 j = &*jIt;
541 jIt++;
542 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700543 const int64_t *intValue = j->get_ptr<const int64_t *>();
544 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
545 const std::string *stringValue = j->get_ptr<const std::string *>();
546 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 const bool *b = j->get_ptr<const bool *>();
548 int64_t v = 0;
549 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700550
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 // Do some basic type conversions that make sense. uint can be
552 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700553 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700555 v = static_cast<int64_t>(*uintValue);
556 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700558 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700560 d = static_cast<double>(*uintValue);
561 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700563 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700565 d = static_cast<double>(*intValue);
566 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700567 }
568
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700569 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700571 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 {
573 return -1;
574 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700575 r = sd_bus_message_append_basic(m, argCode[0],
576 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 if (r < 0)
578 {
579 return r;
580 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700581 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700582 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700584 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700585 {
586 return -1;
587 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700588 int32_t i = static_cast<int32_t>(*intValue);
589 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700590 if (r < 0)
591 {
592 return r;
593 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700594 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700595 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 {
597 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700598 int boolInt = false;
599 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700601 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700602 }
603 else if (b != nullptr)
604 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600605 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700607 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700609 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 }
611 else
612 {
613 return -1;
614 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 if (r < 0)
617 {
618 return r;
619 }
620 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700621 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700623 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 {
625 return -1;
626 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700627 int16_t n = static_cast<int16_t>(*intValue);
628 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 if (r < 0)
630 {
631 return r;
632 }
633 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700634 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700636 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 {
638 return -1;
639 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700640 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 if (r < 0)
642 {
643 return r;
644 }
645 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700646 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700648 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649 {
650 return -1;
651 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700652 uint8_t y = static_cast<uint8_t>(*uintValue);
653 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700655 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700657 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 {
659 return -1;
660 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700661 uint16_t q = static_cast<uint16_t>(*uintValue);
662 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700664 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700666 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 {
668 return -1;
669 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700670 uint32_t u = static_cast<uint32_t>(*uintValue);
671 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700675 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 {
677 return -1;
678 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700679 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700681 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700683 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700685 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700687 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 if (r < 0)
691 {
692 return r;
693 }
694
695 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
696 ++it)
697 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700698 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700699 if (r < 0)
700 {
701 return r;
702 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 }
704 sd_bus_message_close_container(m);
705 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700706 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700708 std::string containedType = argCode.substr(1);
709 BMCWEB_LOG_DEBUG << "variant type: " << argCode
710 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
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
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700718 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 if (r < 0)
720 {
721 return r;
722 }
723
724 r = sd_bus_message_close_container(m);
725 if (r < 0)
726 {
727 return r;
728 }
729 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700730 else if (boost::starts_with(argCode, "(") &&
731 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700733 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700735 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700737 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 {
739 if (it == j->end())
740 {
741 return -1;
742 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700743 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 if (r < 0)
745 {
746 return r;
747 }
748 it++;
749 }
750 r = sd_bus_message_close_container(m);
751 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700752 else if (boost::starts_with(argCode, "{") &&
753 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700755 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700757 containedType.c_str());
758 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759 if (codes.size() != 2)
760 {
761 return -1;
762 }
763 const std::string &key_type = codes[0];
764 const std::string &value_type = codes[1];
765 for (auto it : j->items())
766 {
767 r = convertJsonToDbus(m, key_type, it.key());
768 if (r < 0)
769 {
770 return r;
771 }
772
773 r = convertJsonToDbus(m, value_type, it.value());
774 if (r < 0)
775 {
776 return r;
777 }
778 }
779 r = sd_bus_message_close_container(m);
780 }
781 else
782 {
783 return -2;
784 }
785 if (r < 0)
786 {
787 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700788 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700789
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 if (argTypes.size() > 1)
791 {
792 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700793 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700794 }
Matt Spinler127ea542019-01-14 11:04:28 -0600795
796 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700797}
798
Matt Spinlerd22a7132019-01-14 12:14:30 -0600799template <typename T>
800int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
801 nlohmann::json &data)
802{
803 T value;
804
805 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
806 if (r < 0)
807 {
808 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
809 << " failed!";
810 return r;
811 }
812
813 data = value;
814 return 0;
815}
816
Matt Spinler16caaee2019-01-15 11:40:34 -0600817int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600818 sdbusplus::message::message &m, nlohmann::json &response);
819
820int readDictEntryFromMessage(const std::string &typeCode,
821 sdbusplus::message::message &m,
822 nlohmann::json &object)
823{
824 std::vector<std::string> types = dbusArgSplit(typeCode);
825 if (types.size() != 2)
826 {
827 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
828 << types.size();
829 return -1;
830 }
831
832 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
833 typeCode.c_str());
834 if (r < 0)
835 {
836 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
837 return r;
838 }
839
840 nlohmann::json key;
841 r = convertDBusToJSON(types[0], m, key);
842 if (r < 0)
843 {
844 return r;
845 }
846
847 const std::string *keyPtr = key.get_ptr<const std::string *>();
848 if (keyPtr == nullptr)
849 {
850 // json doesn't support non-string keys. If we hit this condition,
851 // convert the result to a string so we can proceed
852 key = key.dump();
853 keyPtr = key.get_ptr<const std::string *>();
854 // in theory this can't fail now, but lets be paranoid about it anyway
855 if (keyPtr == nullptr)
856 {
857 return -1;
858 }
859 }
860 nlohmann::json &value = object[*keyPtr];
861
862 r = convertDBusToJSON(types[1], m, value);
863 if (r < 0)
864 {
865 return r;
866 }
867
868 r = sd_bus_message_exit_container(m.get());
869 if (r < 0)
870 {
871 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
872 return r;
873 }
874
875 return 0;
876}
877
878int readArrayFromMessage(const std::string &typeCode,
879 sdbusplus::message::message &m, nlohmann::json &data)
880{
881 if (typeCode.size() < 2)
882 {
883 BMCWEB_LOG_ERROR << "Type code " << typeCode
884 << " too small for an array";
885 return -1;
886 }
887
888 std::string containedType = typeCode.substr(1);
889
890 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
891 containedType.c_str());
892 if (r < 0)
893 {
894 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
895 << r;
896 return r;
897 }
898
899 bool dict = boost::starts_with(containedType, "{") &&
900 boost::ends_with(containedType, "}");
901
902 if (dict)
903 {
904 // Remove the { }
905 containedType = containedType.substr(1, containedType.size() - 2);
906 data = nlohmann::json::object();
907 }
908 else
909 {
910 data = nlohmann::json::array();
911 }
912
913 while (true)
914 {
915 r = sd_bus_message_at_end(m.get(), false);
916 if (r < 0)
917 {
918 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
919 return r;
920 }
921
922 if (r > 0)
923 {
924 break;
925 }
926
927 // Dictionaries are only ever seen in an array
928 if (dict)
929 {
930 r = readDictEntryFromMessage(containedType, m, data);
931 if (r < 0)
932 {
933 return r;
934 }
935 }
936 else
937 {
938 data.push_back(nlohmann::json());
939
940 r = convertDBusToJSON(containedType, m, data.back());
941 if (r < 0)
942 {
943 return r;
944 }
945 }
946 }
947
948 r = sd_bus_message_exit_container(m.get());
949 if (r < 0)
950 {
951 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
952 return r;
953 }
954
955 return 0;
956}
957
Matt Spinler75c6c672019-01-14 13:01:46 -0600958int readStructFromMessage(const std::string &typeCode,
959 sdbusplus::message::message &m, nlohmann::json &data)
960{
961 if (typeCode.size() < 3)
962 {
963 BMCWEB_LOG_ERROR << "Type code " << typeCode
964 << " too small for a struct";
965 return -1;
966 }
967
968 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
969 std::vector<std::string> types = dbusArgSplit(containedTypes);
970
971 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
972 containedTypes.c_str());
973 if (r < 0)
974 {
975 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
976 << r;
977 return r;
978 }
979
980 for (const std::string &type : types)
981 {
982 data.push_back(nlohmann::json());
983 r = convertDBusToJSON(type, m, data.back());
984 if (r < 0)
985 {
986 return r;
987 }
988 }
989
990 r = sd_bus_message_exit_container(m.get());
991 if (r < 0)
992 {
993 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
994 return r;
995 }
996 return 0;
997}
998
Matt Spinler89c19702019-01-14 13:13:00 -0600999int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1000{
1001 const char *containerType;
1002 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1003 if (r < 0)
1004 {
1005 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1006 return r;
1007 }
1008
1009 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1010 containerType);
1011 if (r < 0)
1012 {
1013 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1014 << r;
1015 return r;
1016 }
1017
1018 r = convertDBusToJSON(containerType, m, data);
1019 if (r < 0)
1020 {
1021 return r;
1022 }
1023
1024 r = sd_bus_message_exit_container(m.get());
1025 if (r < 0)
1026 {
1027 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1028 return r;
1029 }
1030
1031 return 0;
1032}
1033
Matt Spinler6df8f992019-01-14 12:47:47 -06001034int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001035 sdbusplus::message::message &m, nlohmann::json &response)
1036{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001037 int r = 0;
1038 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1039
Matt Spinlerd22a7132019-01-14 12:14:30 -06001040 for (const std::string &typeCode : returnTypes)
1041 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001042 nlohmann::json *thisElement = &response;
1043 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001044 {
1045 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001046 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001047 }
1048
1049 if (typeCode == "s")
1050 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001051 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001052 if (r < 0)
1053 {
1054 return r;
1055 }
1056 }
1057 else if (typeCode == "g")
1058 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001059 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001060 if (r < 0)
1061 {
1062 return r;
1063 }
1064 }
1065 else if (typeCode == "o")
1066 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001067 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001068 if (r < 0)
1069 {
1070 return r;
1071 }
1072 }
1073 else if (typeCode == "b")
1074 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001075 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001076 if (r < 0)
1077 {
1078 return r;
1079 }
1080
Matt Spinlerf39420c2019-01-30 12:57:18 -06001081 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001082 }
1083 else if (typeCode == "u")
1084 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001085 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001086 if (r < 0)
1087 {
1088 return r;
1089 }
1090 }
1091 else if (typeCode == "i")
1092 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001093 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001094 if (r < 0)
1095 {
1096 return r;
1097 }
1098 }
1099 else if (typeCode == "x")
1100 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001101 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001102 if (r < 0)
1103 {
1104 return r;
1105 }
1106 }
1107 else if (typeCode == "t")
1108 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001109 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001110 if (r < 0)
1111 {
1112 return r;
1113 }
1114 }
1115 else if (typeCode == "n")
1116 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001117 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001118 if (r < 0)
1119 {
1120 return r;
1121 }
1122 }
1123 else if (typeCode == "q")
1124 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001125 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001126 if (r < 0)
1127 {
1128 return r;
1129 }
1130 }
1131 else if (typeCode == "y")
1132 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001133 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001134 if (r < 0)
1135 {
1136 return r;
1137 }
1138 }
1139 else if (typeCode == "d")
1140 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001141 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001142 if (r < 0)
1143 {
1144 return r;
1145 }
1146 }
1147 else if (typeCode == "h")
1148 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001149 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001150 if (r < 0)
1151 {
1152 return r;
1153 }
1154 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001155 else if (boost::starts_with(typeCode, "a"))
1156 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001157 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001158 if (r < 0)
1159 {
1160 return r;
1161 }
1162 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001163 else if (boost::starts_with(typeCode, "(") &&
1164 boost::ends_with(typeCode, ")"))
1165 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001166 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001167 if (r < 0)
1168 {
1169 return r;
1170 }
1171 }
Matt Spinler89c19702019-01-14 13:13:00 -06001172 else if (boost::starts_with(typeCode, "v"))
1173 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001174 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001175 if (r < 0)
1176 {
1177 return r;
1178 }
1179 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001180 else
1181 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001182 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1183 return -2;
1184 }
1185 }
1186
Matt Spinler16caaee2019-01-15 11:40:34 -06001187 return 0;
1188}
1189
1190void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1191 sdbusplus::message::message &m,
1192 const std::string &returnType)
1193{
Matt Spinler39a4e392019-01-15 11:53:13 -06001194 nlohmann::json data;
1195
1196 int r = convertDBusToJSON(returnType, m, data);
1197 if (r < 0)
1198 {
1199 transaction->outputFailed = true;
1200 return;
1201 }
1202
1203 if (data.is_null())
1204 {
1205 return;
1206 }
1207
1208 if (transaction->methodResponse.is_null())
1209 {
1210 transaction->methodResponse = std::move(data);
1211 return;
1212 }
1213
1214 // If they're both dictionaries or arrays, merge into one.
1215 // Otherwise, make the results an array with every result
1216 // an entry. Could also just fail in that case, but it
1217 // seems better to get the data back somehow.
1218
1219 if (transaction->methodResponse.is_object() && data.is_object())
1220 {
1221 for (const auto &obj : data.items())
1222 {
1223 // Note: Will overwrite the data for a duplicate key
1224 transaction->methodResponse.emplace(obj.key(),
1225 std::move(obj.value()));
1226 }
1227 return;
1228 }
1229
1230 if (transaction->methodResponse.is_array() && data.is_array())
1231 {
1232 for (auto &obj : data)
1233 {
1234 transaction->methodResponse.push_back(std::move(obj));
1235 }
1236 return;
1237 }
1238
1239 if (!transaction->convertedToArray)
1240 {
1241 // They are different types. May as well turn them into an array
1242 nlohmann::json j = std::move(transaction->methodResponse);
1243 transaction->methodResponse = nlohmann::json::array();
1244 transaction->methodResponse.push_back(std::move(j));
1245 transaction->methodResponse.push_back(std::move(data));
1246 transaction->convertedToArray = true;
1247 }
1248 else
1249 {
1250 transaction->methodResponse.push_back(std::move(data));
1251 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001252}
1253
Ed Tanousd76323e2018-08-07 14:35:40 -07001254void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001255 const std::string &connectionName)
1256{
1257 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1258 << connectionName;
1259 crow::connections::systemBus->async_method_call(
1260 [transaction, connectionName{std::string(connectionName)}](
1261 const boost::system::error_code ec,
1262 const std::string &introspect_xml) {
1263 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1264 if (ec)
1265 {
1266 BMCWEB_LOG_ERROR
1267 << "Introspect call failed with error: " << ec.message()
1268 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001269 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001270 }
Matt Spinler318bd892019-01-15 09:59:20 -06001271 tinyxml2::XMLDocument doc;
1272
1273 doc.Parse(introspect_xml.data(), introspect_xml.size());
1274 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1275 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001276 {
Matt Spinler318bd892019-01-15 09:59:20 -06001277 BMCWEB_LOG_ERROR << "XML document failed to parse "
1278 << connectionName << "\n";
1279 return;
1280 }
1281 tinyxml2::XMLElement *interfaceNode =
1282 pRoot->FirstChildElement("interface");
1283 while (interfaceNode != nullptr)
1284 {
1285 const char *thisInterfaceName =
1286 interfaceNode->Attribute("name");
1287 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001288 {
Matt Spinler318bd892019-01-15 09:59:20 -06001289 if (!transaction->interfaceName.empty() &&
1290 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001291 {
Matt Spinler318bd892019-01-15 09:59:20 -06001292 interfaceNode =
1293 interfaceNode->NextSiblingElement("interface");
1294 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001295 }
Matt Spinler318bd892019-01-15 09:59:20 -06001296
1297 tinyxml2::XMLElement *methodNode =
1298 interfaceNode->FirstChildElement("method");
1299 while (methodNode != nullptr)
1300 {
1301 const char *thisMethodName =
1302 methodNode->Attribute("name");
1303 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1304 if (thisMethodName != nullptr &&
1305 thisMethodName == transaction->methodName)
1306 {
1307 BMCWEB_LOG_DEBUG
1308 << "Found method named " << thisMethodName
1309 << " on interface " << thisInterfaceName;
1310 sdbusplus::message::message m =
1311 crow::connections::systemBus->new_method_call(
1312 connectionName.c_str(),
1313 transaction->path.c_str(),
1314 thisInterfaceName,
1315 transaction->methodName.c_str());
1316
1317 tinyxml2::XMLElement *argumentNode =
1318 methodNode->FirstChildElement("arg");
1319
Matt Spinler16caaee2019-01-15 11:40:34 -06001320 std::string returnType;
1321
1322 // Find the output type
1323 while (argumentNode != nullptr)
1324 {
1325 const char *argDirection =
1326 argumentNode->Attribute("direction");
1327 const char *argType =
1328 argumentNode->Attribute("type");
1329 if (argDirection != nullptr &&
1330 argType != nullptr &&
1331 std::string(argDirection) == "out")
1332 {
1333 returnType = argType;
1334 break;
1335 }
1336 argumentNode =
1337 argumentNode->NextSiblingElement("arg");
1338 }
1339
Matt Spinler318bd892019-01-15 09:59:20 -06001340 nlohmann::json::const_iterator argIt =
1341 transaction->arguments.begin();
1342
Matt Spinler16caaee2019-01-15 11:40:34 -06001343 argumentNode = methodNode->FirstChildElement("arg");
1344
Matt Spinler318bd892019-01-15 09:59:20 -06001345 while (argumentNode != nullptr)
1346 {
1347 const char *argDirection =
1348 argumentNode->Attribute("direction");
1349 const char *argType =
1350 argumentNode->Attribute("type");
1351 if (argDirection != nullptr &&
1352 argType != nullptr &&
1353 std::string(argDirection) == "in")
1354 {
1355 if (argIt == transaction->arguments.end())
1356 {
1357 transaction->setErrorStatus(
1358 "Invalid method args");
1359 return;
1360 }
1361 if (convertJsonToDbus(m.get(),
1362 std::string(argType),
1363 *argIt) < 0)
1364 {
1365 transaction->setErrorStatus(
1366 "Invalid method arg type");
1367 return;
1368 }
1369
1370 argIt++;
1371 }
1372 argumentNode =
1373 argumentNode->NextSiblingElement("arg");
1374 }
1375
1376 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001377 m, [transaction, returnType](
1378 boost::system::error_code ec,
1379 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001380 if (ec)
1381 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001382 transaction->methodFailed = true;
Matt Spinler318bd892019-01-15 09:59:20 -06001383 return;
1384 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001385 else
1386 {
1387 transaction->methodPassed = true;
1388 }
1389
1390 handleMethodResponse(transaction, m,
1391 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001392 });
1393 break;
1394 }
1395 methodNode = methodNode->NextSiblingElement("method");
1396 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001397 }
Matt Spinler318bd892019-01-15 09:59:20 -06001398 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001399 }
1400 },
1401 connectionName, transaction->path,
1402 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001403}
1404
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001405void handleAction(const crow::Request &req, crow::Response &res,
1406 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001407{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001408 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1409 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001410 nlohmann::json requestDbusData =
1411 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001412
Ed Tanous1abe55e2018-09-05 08:30:59 -07001413 if (requestDbusData.is_discarded())
1414 {
Matt Spinler6db06242018-12-11 11:21:22 -06001415 setErrorResponse(res, boost::beast::http::status::bad_request,
1416 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001417 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001418 return;
1419 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001420 nlohmann::json::iterator data = requestDbusData.find("data");
1421 if (data == requestDbusData.end())
1422 {
Matt Spinler6db06242018-12-11 11:21:22 -06001423 setErrorResponse(res, boost::beast::http::status::bad_request,
1424 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001425 res.end();
1426 return;
1427 }
1428
1429 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001430 {
Matt Spinler6db06242018-12-11 11:21:22 -06001431 setErrorResponse(res, boost::beast::http::status::bad_request,
1432 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001433 res.end();
1434 return;
1435 }
1436 auto transaction = std::make_shared<InProgressActionData>(res);
1437
1438 transaction->path = objectPath;
1439 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001440 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001441 crow::connections::systemBus->async_method_call(
1442 [transaction](
1443 const boost::system::error_code ec,
1444 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001445 &interfaceNames) {
1446 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001447 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001448 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001449 setErrorResponse(transaction->res,
1450 boost::beast::http::status::not_found,
1451 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001452 return;
1453 }
1454
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001455 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1456 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001457
1458 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001459 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001460 {
1461 findActionOnInterface(transaction, object.first);
1462 }
1463 },
1464 "xyz.openbmc_project.ObjectMapper",
1465 "/xyz/openbmc_project/object_mapper",
1466 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1467 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001468}
1469
Matt Spinlerde818812018-12-11 16:39:20 -06001470void handleDelete(const crow::Request &req, crow::Response &res,
1471 const std::string &objectPath)
1472{
1473 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1474
1475 crow::connections::systemBus->async_method_call(
1476 [&res, objectPath](
1477 const boost::system::error_code ec,
1478 const std::vector<std::pair<std::string, std::vector<std::string>>>
1479 &interfaceNames) {
1480 if (ec || interfaceNames.size() <= 0)
1481 {
1482 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001483 setErrorResponse(res,
1484 boost::beast::http::status::method_not_allowed,
1485 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001486 res.end();
1487 return;
1488 }
1489
1490 auto transaction = std::make_shared<InProgressActionData>(res);
1491 transaction->path = objectPath;
1492 transaction->methodName = "Delete";
1493 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1494
1495 for (const std::pair<std::string, std::vector<std::string>>
1496 &object : interfaceNames)
1497 {
1498 findActionOnInterface(transaction, object.first);
1499 }
1500 },
1501 "xyz.openbmc_project.ObjectMapper",
1502 "/xyz/openbmc_project/object_mapper",
1503 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1504 std::array<const char *, 0>());
1505}
1506
Ed Tanousf839dfe2018-11-12 11:11:15 -08001507void handleList(crow::Response &res, const std::string &objectPath,
1508 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001509{
1510 crow::connections::systemBus->async_method_call(
1511 [&res](const boost::system::error_code ec,
1512 std::vector<std::string> &objectPaths) {
1513 if (ec)
1514 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001515 setErrorResponse(res, boost::beast::http::status::not_found,
1516 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517 }
1518 else
1519 {
1520 res.jsonValue = {{"status", "ok"},
1521 {"message", "200 OK"},
1522 {"data", std::move(objectPaths)}};
1523 }
1524 res.end();
1525 },
1526 "xyz.openbmc_project.ObjectMapper",
1527 "/xyz/openbmc_project/object_mapper",
1528 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001529 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001530}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001531
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001532void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001533{
Ed Tanous049a0512018-11-01 13:58:42 -07001534 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1535 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1536
1537 asyncResp->res.jsonValue = {{"message", "200 OK"},
1538 {"status", "ok"},
1539 {"data", nlohmann::json::object()}};
1540
Ed Tanous1abe55e2018-09-05 08:30:59 -07001541 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001542 [objectPath, asyncResp](const boost::system::error_code ec,
1543 GetSubTreeType &object_names) {
1544 auto transaction = std::make_shared<InProgressEnumerateData>(
1545 objectPath, asyncResp);
1546
1547 transaction->subtree =
1548 std::make_shared<GetSubTreeType>(std::move(object_names));
1549
Ed Tanous1abe55e2018-09-05 08:30:59 -07001550 if (ec)
1551 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001552 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1553 << transaction->objectPath;
1554 setErrorResponse(transaction->asyncResp->res,
1555 boost::beast::http::status::not_found,
1556 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001557 return;
1558 }
Ed Tanous64530012018-02-06 17:08:16 -08001559
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001560 // Add the data for the path passed in to the results
1561 // as if GetSubTree returned it, and continue on enumerating
1562 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001563 },
1564 "xyz.openbmc_project.ObjectMapper",
1565 "/xyz/openbmc_project/object_mapper",
1566 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001567 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001568}
Ed Tanous911ac312017-08-15 09:37:42 -07001569
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001570void handleGet(crow::Response &res, std::string &objectPath,
1571 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001572{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001573 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1574 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001575 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001576
Ed Tanous1abe55e2018-09-05 08:30:59 -07001577 std::shared_ptr<std::string> path =
1578 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001579
Ed Tanous1abe55e2018-09-05 08:30:59 -07001580 using GetObjectType =
1581 std::vector<std::pair<std::string, std::vector<std::string>>>;
1582 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001583 [&res, path, propertyName](const boost::system::error_code ec,
1584 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001585 if (ec || object_names.size() <= 0)
1586 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001587 setErrorResponse(res, boost::beast::http::status::not_found,
1588 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001589 res.end();
1590 return;
1591 }
1592 std::shared_ptr<nlohmann::json> response =
1593 std::make_shared<nlohmann::json>(nlohmann::json::object());
1594 // The mapper should never give us an empty interface names list,
1595 // but check anyway
1596 for (const std::pair<std::string, std::vector<std::string>>
1597 connection : object_names)
1598 {
1599 const std::vector<std::string> &interfaceNames =
1600 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001601
Ed Tanous1abe55e2018-09-05 08:30:59 -07001602 if (interfaceNames.size() <= 0)
1603 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001604 setErrorResponse(res, boost::beast::http::status::not_found,
1605 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001606 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001607 return;
1608 }
1609
1610 for (const std::string &interface : interfaceNames)
1611 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001612 sdbusplus::message::message m =
1613 crow::connections::systemBus->new_method_call(
1614 connection.first.c_str(), path->c_str(),
1615 "org.freedesktop.DBus.Properties", "GetAll");
1616 m.append(interface);
1617 crow::connections::systemBus->async_send(
1618 m, [&res, response,
1619 propertyName](const boost::system::error_code ec,
1620 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001621 if (ec)
1622 {
1623 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1624 << ec;
1625 }
1626 else
1627 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001628 nlohmann::json properties;
1629 int r =
1630 convertDBusToJSON("a{sv}", msg, properties);
1631 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001632 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001633 BMCWEB_LOG_ERROR
1634 << "convertDBusToJSON failed";
1635 }
1636 else
1637 {
1638 for (auto &prop : properties.items())
1639 {
1640 // if property name is empty, or matches
1641 // our search query, add it to the
1642 // response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001643
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001644 if (propertyName->empty())
1645 {
1646 (*response)[prop.key()] =
1647 std::move(prop.value());
1648 }
1649 else if (prop.key() == *propertyName)
1650 {
1651 *response = std::move(prop.value());
1652 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001653 }
1654 }
1655 }
1656 if (response.use_count() == 1)
1657 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001658 if (!propertyName->empty() && response->empty())
1659 {
1660 setErrorResponse(
1661 res,
1662 boost::beast::http::status::not_found,
1663 propNotFoundDesc, notFoundMsg);
1664 }
1665 else
1666 {
1667 res.jsonValue = {{"status", "ok"},
1668 {"message", "200 OK"},
1669 {"data", *response}};
1670 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001671 res.end();
1672 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001673 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001674 }
1675 }
1676 },
1677 "xyz.openbmc_project.ObjectMapper",
1678 "/xyz/openbmc_project/object_mapper",
1679 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1680 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001681}
1682
Ed Tanous1abe55e2018-09-05 08:30:59 -07001683struct AsyncPutRequest
1684{
1685 AsyncPutRequest(crow::Response &res) : res(res)
1686 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001687 }
1688 ~AsyncPutRequest()
1689 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001690 if (res.jsonValue.empty())
1691 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001692 setErrorResponse(res, boost::beast::http::status::forbidden,
1693 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001694 }
1695
1696 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001697 }
1698
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001699 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001700 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001701 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1702 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001703 }
1704
Ed Tanous1abe55e2018-09-05 08:30:59 -07001705 crow::Response &res;
1706 std::string objectPath;
1707 std::string propertyName;
1708 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001709};
1710
Ed Tanousd76323e2018-08-07 14:35:40 -07001711void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 const std::string &objectPath, const std::string &destProperty)
1713{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001714 if (destProperty.empty())
1715 {
1716 setErrorResponse(res, boost::beast::http::status::forbidden,
1717 forbiddenResDesc, forbiddenMsg);
1718 res.end();
1719 return;
1720 }
1721
Ed Tanous1abe55e2018-09-05 08:30:59 -07001722 nlohmann::json requestDbusData =
1723 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001724
Ed Tanous1abe55e2018-09-05 08:30:59 -07001725 if (requestDbusData.is_discarded())
1726 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001727 setErrorResponse(res, boost::beast::http::status::bad_request,
1728 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001729 res.end();
1730 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001731 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001732
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1734 if (propertyIt == requestDbusData.end())
1735 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001736 setErrorResponse(res, boost::beast::http::status::bad_request,
1737 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001738 res.end();
1739 return;
1740 }
1741 const nlohmann::json &propertySetValue = *propertyIt;
1742 auto transaction = std::make_shared<AsyncPutRequest>(res);
1743 transaction->objectPath = objectPath;
1744 transaction->propertyName = destProperty;
1745 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001746
Ed Tanous1abe55e2018-09-05 08:30:59 -07001747 using GetObjectType =
1748 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001749
Ed Tanous1abe55e2018-09-05 08:30:59 -07001750 crow::connections::systemBus->async_method_call(
1751 [transaction](const boost::system::error_code ec,
1752 const GetObjectType &object_names) {
1753 if (!ec && object_names.size() <= 0)
1754 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001755 setErrorResponse(transaction->res,
1756 boost::beast::http::status::not_found,
1757 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001758 return;
1759 }
Ed Tanous911ac312017-08-15 09:37:42 -07001760
Ed Tanous1abe55e2018-09-05 08:30:59 -07001761 for (const std::pair<std::string, std::vector<std::string>>
1762 connection : object_names)
1763 {
1764 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001765
Ed Tanous1abe55e2018-09-05 08:30:59 -07001766 crow::connections::systemBus->async_method_call(
1767 [connectionName{std::string(connectionName)},
1768 transaction](const boost::system::error_code ec,
1769 const std::string &introspectXml) {
1770 if (ec)
1771 {
1772 BMCWEB_LOG_ERROR
1773 << "Introspect call failed with error: "
1774 << ec.message()
1775 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001776 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001777 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001778 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001779 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001780
Ed Tanous1abe55e2018-09-05 08:30:59 -07001781 doc.Parse(introspectXml.c_str());
1782 tinyxml2::XMLNode *pRoot =
1783 doc.FirstChildElement("node");
1784 if (pRoot == nullptr)
1785 {
1786 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1787 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001788 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001789 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001790 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001791 tinyxml2::XMLElement *ifaceNode =
1792 pRoot->FirstChildElement("interface");
1793 while (ifaceNode != nullptr)
1794 {
1795 const char *interfaceName =
1796 ifaceNode->Attribute("name");
1797 BMCWEB_LOG_DEBUG << "found interface "
1798 << interfaceName;
1799 tinyxml2::XMLElement *propNode =
1800 ifaceNode->FirstChildElement("property");
1801 while (propNode != nullptr)
1802 {
1803 const char *propertyName =
1804 propNode->Attribute("name");
1805 BMCWEB_LOG_DEBUG << "Found property "
1806 << propertyName;
1807 if (propertyName == transaction->propertyName)
1808 {
1809 const char *argType =
1810 propNode->Attribute("type");
1811 if (argType != nullptr)
1812 {
1813 sdbusplus::message::message m =
1814 crow::connections::systemBus
1815 ->new_method_call(
1816 connectionName.c_str(),
1817 transaction->objectPath
1818 .c_str(),
1819 "org.freedesktop.DBus."
1820 "Properties",
1821 "Set");
1822 m.append(interfaceName,
1823 transaction->propertyName);
1824 int r = sd_bus_message_open_container(
1825 m.get(), SD_BUS_TYPE_VARIANT,
1826 argType);
1827 if (r < 0)
1828 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001829 transaction->setErrorStatus(
1830 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001831 return;
1832 }
1833 r = convertJsonToDbus(
1834 m.get(), argType,
1835 transaction->propertyValue);
1836 if (r < 0)
1837 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001838 transaction->setErrorStatus(
1839 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001840 return;
1841 }
1842 r = sd_bus_message_close_container(
1843 m.get());
1844 if (r < 0)
1845 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001846 transaction->setErrorStatus(
1847 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001848 return;
1849 }
Ed Tanous911ac312017-08-15 09:37:42 -07001850
Ed Tanous1abe55e2018-09-05 08:30:59 -07001851 crow::connections::systemBus
1852 ->async_send(
1853 m,
1854 [transaction](
1855 boost::system::error_code
1856 ec,
1857 sdbusplus::message::message
1858 &m) {
1859 BMCWEB_LOG_DEBUG << "sent";
1860 if (ec)
1861 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001862 setErrorResponse(
1863 transaction->res,
1864 boost::beast::http::
1865 status::
1866 forbidden,
1867 forbiddenPropDesc,
1868 ec.message());
1869 }
1870 else
1871 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001872 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001873 .jsonValue = {
1874 {"status", "ok"},
1875 {"message",
1876 "200 OK"},
1877 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001878 }
1879 });
1880 }
1881 }
1882 propNode =
1883 propNode->NextSiblingElement("property");
1884 }
1885 ifaceNode =
1886 ifaceNode->NextSiblingElement("interface");
1887 }
1888 },
1889 connectionName, transaction->objectPath,
1890 "org.freedesktop.DBus.Introspectable", "Introspect");
1891 }
1892 },
1893 "xyz.openbmc_project.ObjectMapper",
1894 "/xyz/openbmc_project/object_mapper",
1895 "xyz.openbmc_project.ObjectMapper", "GetObject",
1896 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001897}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001898
Ed Tanous049a0512018-11-01 13:58:42 -07001899inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1900 std::string &objectPath)
1901{
Ed Tanous049a0512018-11-01 13:58:42 -07001902
1903 // If accessing a single attribute, fill in and update objectPath,
1904 // otherwise leave destProperty blank
1905 std::string destProperty = "";
1906 const char *attrSeperator = "/attr/";
1907 size_t attrPosition = objectPath.find(attrSeperator);
1908 if (attrPosition != objectPath.npos)
1909 {
1910 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1911 objectPath.length());
1912 objectPath = objectPath.substr(0, attrPosition);
1913 }
1914
1915 if (req.method() == "POST"_method)
1916 {
1917 constexpr const char *actionSeperator = "/action/";
1918 size_t actionPosition = objectPath.find(actionSeperator);
1919 if (actionPosition != objectPath.npos)
1920 {
1921 std::string postProperty =
1922 objectPath.substr((actionPosition + strlen(actionSeperator)),
1923 objectPath.length());
1924 objectPath = objectPath.substr(0, actionPosition);
1925 handleAction(req, res, objectPath, postProperty);
1926 return;
1927 }
1928 }
1929 else if (req.method() == "GET"_method)
1930 {
1931 if (boost::ends_with(objectPath, "/enumerate"))
1932 {
1933 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1934 objectPath.end());
1935 handleEnumerate(res, objectPath);
1936 }
1937 else if (boost::ends_with(objectPath, "/list"))
1938 {
1939 objectPath.erase(objectPath.end() - sizeof("list"),
1940 objectPath.end());
1941 handleList(res, objectPath);
1942 }
1943 else
1944 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001945 // Trim any trailing "/" at the end
1946 if (boost::ends_with(objectPath, "/"))
1947 {
1948 objectPath.pop_back();
1949 handleList(res, objectPath, 1);
1950 }
1951 else
1952 {
1953 handleGet(res, objectPath, destProperty);
1954 }
Ed Tanous049a0512018-11-01 13:58:42 -07001955 }
1956 return;
1957 }
1958 else if (req.method() == "PUT"_method)
1959 {
1960 handlePut(req, res, objectPath, destProperty);
1961 return;
1962 }
Matt Spinlerde818812018-12-11 16:39:20 -06001963 else if (req.method() == "DELETE"_method)
1964 {
1965 handleDelete(req, res, objectPath);
1966 return;
1967 }
Ed Tanous049a0512018-11-01 13:58:42 -07001968
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001969 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1970 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001971 res.end();
1972}
1973
Ed Tanous1abe55e2018-09-05 08:30:59 -07001974template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1975{
1976 BMCWEB_ROUTE(app, "/bus/")
1977 .methods("GET"_method)(
1978 [](const crow::Request &req, crow::Response &res) {
1979 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1980 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001981 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001982 });
1983
1984 BMCWEB_ROUTE(app, "/bus/system/")
1985 .methods("GET"_method)(
1986 [](const crow::Request &req, crow::Response &res) {
1987 auto myCallback = [&res](const boost::system::error_code ec,
1988 std::vector<std::string> &names) {
1989 if (ec)
1990 {
1991 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1992 res.result(
1993 boost::beast::http::status::internal_server_error);
1994 }
1995 else
1996 {
1997 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001998 res.jsonValue = {{"status", "ok"}};
1999 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002000 for (auto &name : names)
2001 {
2002 objectsSub.push_back({{"name", name}});
2003 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002004 }
2005 res.end();
2006 };
2007 crow::connections::systemBus->async_method_call(
2008 std::move(myCallback), "org.freedesktop.DBus", "/",
2009 "org.freedesktop.DBus", "ListNames");
2010 });
2011
2012 BMCWEB_ROUTE(app, "/list/")
2013 .methods("GET"_method)(
2014 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002015 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002016 });
2017
2018 BMCWEB_ROUTE(app, "/xyz/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06002019 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002020 [](const crow::Request &req, crow::Response &res,
2021 const std::string &path) {
2022 std::string objectPath = "/xyz/" + path;
2023 handleDBusUrl(req, res, objectPath);
2024 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002025
Ed Tanous049a0512018-11-01 13:58:42 -07002026 BMCWEB_ROUTE(app, "/org/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06002027 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002028 [](const crow::Request &req, crow::Response &res,
2029 const std::string &path) {
2030 std::string objectPath = "/org/" + path;
2031 handleDBusUrl(req, res, objectPath);
2032 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002033
Ed Tanous1abe55e2018-09-05 08:30:59 -07002034 BMCWEB_ROUTE(app, "/download/dump/<str>/")
2035 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2036 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002037 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002038 if (!std::regex_match(dumpId, validFilename))
2039 {
Ed Tanousad18f072018-11-14 14:07:48 -08002040 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002041 res.end();
2042 return;
2043 }
James Feistf6150402019-01-08 10:36:20 -08002044 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002045 "/var/lib/phosphor-debug-collector/dumps");
2046
Ed Tanousad18f072018-11-14 14:07:48 -08002047 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002048
James Feistf6150402019-01-08 10:36:20 -08002049 if (!std::filesystem::exists(loc) ||
2050 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002051 {
Ed Tanousad18f072018-11-14 14:07:48 -08002052 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002053 res.result(boost::beast::http::status::not_found);
2054 res.end();
2055 return;
2056 }
James Feistf6150402019-01-08 10:36:20 -08002057 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002058
Ed Tanous1abe55e2018-09-05 08:30:59 -07002059 for (auto &file : files)
2060 {
2061 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002062 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002063 {
2064 continue;
2065 }
2066 res.addHeader("Content-Type", "application/octet-stream");
2067 res.body() = {std::istreambuf_iterator<char>(readFile),
2068 std::istreambuf_iterator<char>()};
2069 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002070 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002071 }
2072 res.result(boost::beast::http::status::not_found);
2073 res.end();
2074 return;
2075 });
2076
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002077 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07002078 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002079 const std::string &Connection) {
2080 introspectObjects(Connection, "/",
2081 std::make_shared<bmcweb::AsyncResp>(res));
2082 });
2083
2084 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2085 .methods("GET"_method,
2086 "POST"_method)([](const crow::Request &req,
2087 crow::Response &res,
2088 const std::string &processName,
2089 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002090 std::vector<std::string> strs;
2091 boost::split(strs, requestedPath, boost::is_any_of("/"));
2092 std::string objectPath;
2093 std::string interfaceName;
2094 std::string methodName;
2095 auto it = strs.begin();
2096 if (it == strs.end())
2097 {
2098 objectPath = "/";
2099 }
2100 while (it != strs.end())
2101 {
2102 // Check if segment contains ".". If it does, it must be an
2103 // interface
2104 if (it->find(".") != std::string::npos)
2105 {
2106 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002107 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07002108 // as part of our <path> specifier above, which causes the
2109 // normal trailing backslash redirector to fail.
2110 }
2111 else if (!it->empty())
2112 {
2113 objectPath += "/" + *it;
2114 }
2115 it++;
2116 }
2117 if (it != strs.end())
2118 {
2119 interfaceName = *it;
2120 it++;
2121
2122 // after interface, we might have a method name
2123 if (it != strs.end())
2124 {
2125 methodName = *it;
2126 it++;
2127 }
2128 }
2129 if (it != strs.end())
2130 {
2131 // if there is more levels past the method name, something went
2132 // wrong, return not found
2133 res.result(boost::beast::http::status::not_found);
2134 res.end();
2135 return;
2136 }
2137 if (interfaceName.empty())
2138 {
2139 crow::connections::systemBus->async_method_call(
2140 [&, processName,
2141 objectPath](const boost::system::error_code ec,
2142 const std::string &introspect_xml) {
2143 if (ec)
2144 {
2145 BMCWEB_LOG_ERROR
2146 << "Introspect call failed with error: "
2147 << ec.message()
2148 << " on process: " << processName
2149 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002150 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002151 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002152 tinyxml2::XMLDocument doc;
2153
2154 doc.Parse(introspect_xml.c_str());
2155 tinyxml2::XMLNode *pRoot =
2156 doc.FirstChildElement("node");
2157 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002158 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002159 BMCWEB_LOG_ERROR << "XML document failed to parse "
2160 << processName << " " << objectPath
2161 << "\n";
2162 res.jsonValue = {{"status", "XML parse error"}};
2163 res.result(boost::beast::http::status::
2164 internal_server_error);
2165 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002166 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002167
2168 BMCWEB_LOG_DEBUG << introspect_xml;
2169 res.jsonValue = {{"status", "ok"},
2170 {"bus_name", processName},
2171 {"object_path", objectPath}};
2172 nlohmann::json &interfacesArray =
2173 res.jsonValue["interfaces"];
2174 interfacesArray = nlohmann::json::array();
2175 tinyxml2::XMLElement *interface =
2176 pRoot->FirstChildElement("interface");
2177
2178 while (interface != nullptr)
2179 {
2180 const char *ifaceName =
2181 interface->Attribute("name");
2182 if (ifaceName != nullptr)
2183 {
2184 interfacesArray.push_back(
2185 {{"name", ifaceName}});
2186 }
2187
2188 interface =
2189 interface->NextSiblingElement("interface");
2190 }
2191
Ed Tanous1abe55e2018-09-05 08:30:59 -07002192 res.end();
2193 },
2194 processName, objectPath,
2195 "org.freedesktop.DBus.Introspectable", "Introspect");
2196 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002197 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002198 {
2199 crow::connections::systemBus->async_method_call(
2200 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002201 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07002202 const boost::system::error_code ec,
2203 const std::string &introspect_xml) {
2204 if (ec)
2205 {
2206 BMCWEB_LOG_ERROR
2207 << "Introspect call failed with error: "
2208 << ec.message()
2209 << " on process: " << processName
2210 << " path: " << objectPath << "\n";
2211 }
2212 else
2213 {
2214 tinyxml2::XMLDocument doc;
2215
2216 doc.Parse(introspect_xml.c_str());
2217 tinyxml2::XMLNode *pRoot =
2218 doc.FirstChildElement("node");
2219 if (pRoot == nullptr)
2220 {
2221 BMCWEB_LOG_ERROR
2222 << "XML document failed to parse "
2223 << processName << " " << objectPath << "\n";
2224 res.result(boost::beast::http::status::
2225 internal_server_error);
2226 }
2227 else
2228 {
2229 tinyxml2::XMLElement *node =
2230 pRoot->FirstChildElement("node");
2231
2232 // if we know we're the only call, build the
2233 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07002234 tinyxml2::XMLElement *interface =
2235 pRoot->FirstChildElement("interface");
2236
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002237 res.jsonValue = {
2238 {"status", "ok"},
2239 {"bus_name", processName},
2240 {"interface", interfaceName},
2241 {"object_path", objectPath},
2242 {"properties", nlohmann::json::object()}};
2243
2244 nlohmann::json &methodsArray =
2245 res.jsonValue["methods"];
2246 methodsArray = nlohmann::json::array();
2247
2248 nlohmann::json &signalsArray =
2249 res.jsonValue["signals"];
2250 signalsArray = nlohmann::json::array();
2251
Ed Tanous1abe55e2018-09-05 08:30:59 -07002252 while (interface != nullptr)
2253 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002254 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002255 interface->Attribute("name");
2256
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002257 if (ifaceName != nullptr &&
2258 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002259 {
2260 tinyxml2::XMLElement *methods =
2261 interface->FirstChildElement(
2262 "method");
2263 while (methods != nullptr)
2264 {
2265 nlohmann::json argsArray =
2266 nlohmann::json::array();
2267 tinyxml2::XMLElement *arg =
2268 methods->FirstChildElement(
2269 "arg");
2270 while (arg != nullptr)
2271 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002272 nlohmann::json thisArg;
2273 for (const char *fieldName :
2274 std::array<const char *,
2275 3>{"name",
2276 "direction",
2277 "type"})
2278 {
2279 const char *fieldValue =
2280 arg->Attribute(
2281 fieldName);
2282 if (fieldValue != nullptr)
2283 {
2284 thisArg[fieldName] =
2285 fieldValue;
2286 }
2287 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002288 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002289 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07002290 arg = arg->NextSiblingElement(
2291 "arg");
2292 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002293
2294 const char *name =
2295 methods->Attribute("name");
2296 if (name != nullptr)
2297 {
2298 methodsArray.push_back(
2299 {{"name", name},
2300 {"uri", "/bus/system/" +
2301 processName +
2302 objectPath +
2303 "/" +
2304 interfaceName +
2305 "/" + name},
2306 {"args", argsArray}});
2307 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002308 methods =
2309 methods->NextSiblingElement(
2310 "method");
2311 }
2312 tinyxml2::XMLElement *signals =
2313 interface->FirstChildElement(
2314 "signal");
2315 while (signals != nullptr)
2316 {
2317 nlohmann::json argsArray =
2318 nlohmann::json::array();
2319
2320 tinyxml2::XMLElement *arg =
2321 signals->FirstChildElement(
2322 "arg");
2323 while (arg != nullptr)
2324 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002325 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002326 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002327 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07002328 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002329 if (name != nullptr &&
2330 type != nullptr)
2331 {
2332 argsArray.push_back({
2333 {"name", name},
2334 {"type", type},
2335 });
2336 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002337 arg = arg->NextSiblingElement(
2338 "arg");
2339 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002340 const char *name =
2341 signals->Attribute("name");
2342 if (name != nullptr)
2343 {
2344 signalsArray.push_back(
2345 {{"name", name},
2346 {"args", argsArray}});
2347 }
2348
Ed Tanous1abe55e2018-09-05 08:30:59 -07002349 signals =
2350 signals->NextSiblingElement(
2351 "signal");
2352 }
2353
Ed Tanous1abe55e2018-09-05 08:30:59 -07002354 break;
2355 }
2356
2357 interface = interface->NextSiblingElement(
2358 "interface");
2359 }
2360 if (interface == nullptr)
2361 {
2362 // if we got to the end of the list and
2363 // never found a match, throw 404
2364 res.result(
2365 boost::beast::http::status::not_found);
2366 }
2367 }
2368 }
2369 res.end();
2370 },
2371 processName, objectPath,
2372 "org.freedesktop.DBus.Introspectable", "Introspect");
2373 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002374 else
2375 {
2376 if (req.method() != "POST"_method)
2377 {
2378 res.result(boost::beast::http::status::not_found);
2379 res.end();
2380 return;
2381 }
2382
2383 nlohmann::json requestDbusData =
2384 nlohmann::json::parse(req.body, nullptr, false);
2385
2386 if (requestDbusData.is_discarded())
2387 {
2388 res.result(boost::beast::http::status::bad_request);
2389 res.end();
2390 return;
2391 }
2392 if (!requestDbusData.is_array())
2393 {
2394 res.result(boost::beast::http::status::bad_request);
2395 res.end();
2396 return;
2397 }
2398 auto transaction = std::make_shared<InProgressActionData>(res);
2399
2400 transaction->path = objectPath;
2401 transaction->methodName = methodName;
2402 transaction->arguments = std::move(requestDbusData);
2403
2404 findActionOnInterface(transaction, processName);
2405 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002406 });
2407}
2408} // namespace openbmc_mapper
2409} // namespace crow