blob: 38aa630e2eccbb01f79f9efc1bcfb66ff31a5f70 [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanousc94ad492019-10-10 15:39:33 -070016#include <app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
Ed Tanouse3cb5a32018-08-08 14:16:49 -070019#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <boost/algorithm/string.hpp>
21#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070024#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070025#include <fstream>
Ramesh Iyyard9207042019-07-05 08:04:42 -050026#include <regex>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070027#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace openbmc_mapper
32{
Ed Tanousba9f9a62017-10-11 16:40:35 -070033
Matt Spinler3ae4ba72018-12-05 14:01:22 -060034using GetSubTreeType = std::vector<
35 std::pair<std::string,
36 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37
Ed Tanous42c20182019-10-24 10:18:09 -070038const char *notFoundMsg = "404 Not Found";
39const char *badReqMsg = "400 Bad Request";
40const char *methodNotAllowedMsg = "405 Method Not Allowed";
41const char *forbiddenMsg = "403 Forbidden";
42const char *methodFailedMsg = "500 Method Call Failed";
43const char *methodOutputFailedMsg = "500 Method Output Error";
44const char *notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060045 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Ed Tanous42c20182019-10-24 10:18:09 -070046const char *propNotFoundDesc = "The specified property cannot be found";
47const char *noJsonDesc = "No JSON object could be decoded";
48const char *methodNotFoundDesc = "The specified method cannot be found";
49const char *methodNotAllowedDesc = "Method not allowed";
50const char *forbiddenPropDesc = "The specified property cannot be created";
51const char *forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060052
53void setErrorResponse(crow::Response &res, boost::beast::http::status result,
Ed Tanous42c20182019-10-24 10:18:09 -070054 const std::string &desc, const std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -060055{
56 res.result(result);
57 res.jsonValue = {{"data", {{"description", desc}}},
58 {"message", msg},
59 {"status", "error"}};
60}
61
Ed Tanouse3cb5a32018-08-08 14:16:49 -070062void introspectObjects(const std::string &processName,
63 const std::string &objectPath,
64 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070065{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070066 if (transaction->res.jsonValue.is_null())
67 {
68 transaction->res.jsonValue = {{"status", "ok"},
69 {"bus_name", processName},
70 {"objects", nlohmann::json::array()}};
71 }
72
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070074 [transaction, processName{std::string(processName)},
75 objectPath{std::string(objectPath)}](
76 const boost::system::error_code ec,
77 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 if (ec)
79 {
80 BMCWEB_LOG_ERROR
81 << "Introspect call failed with error: " << ec.message()
82 << " on process: " << processName << " path: " << objectPath
83 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070084 return;
85 }
86 transaction->res.jsonValue["objects"].push_back(
87 {{"path", objectPath}});
88
89 tinyxml2::XMLDocument doc;
90
91 doc.Parse(introspect_xml.c_str());
92 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
93 if (pRoot == nullptr)
94 {
95 BMCWEB_LOG_ERROR << "XML document failed to parse "
96 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070097 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070098 else
99 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700100 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
101 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700103 const char *childPath = node->Attribute("name");
104 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 std::string newpath;
107 if (objectPath != "/")
108 {
109 newpath += objectPath;
110 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700111 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700113 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700115
116 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 }
118 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700120 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700122}
Ed Tanous64530012018-02-06 17:08:16 -0800123
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600124void getPropertiesForEnumerate(const std::string &objectPath,
125 const std::string &service,
126 const std::string &interface,
127 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
128{
129 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
130 << service << " " << interface;
131
132 crow::connections::systemBus->async_method_call(
133 [asyncResp, objectPath, service,
134 interface](const boost::system::error_code ec,
135 const std::vector<
136 std::pair<std::string, dbus::utility::DbusVariantType>>
137 &propertiesList) {
138 if (ec)
139 {
140 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
141 << interface << " service " << service
142 << " failed with code " << ec;
143 return;
144 }
145
146 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
147 nlohmann::json &objectJson = dataJson[objectPath];
148 if (objectJson.is_null())
149 {
150 objectJson = nlohmann::json::object();
151 }
152
153 for (const auto &[name, value] : propertiesList)
154 {
155 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800156 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
157 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600158 }
159 },
160 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
161 interface);
162}
163
164// Find any results that weren't picked up by ObjectManagers, to be
165// called after all ObjectManagers are searched for and called.
166void findRemainingObjectsForEnumerate(
167 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
168 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
169{
170 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
171 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
172
173 for (const auto &[path, interface_map] : *subtree)
174 {
175 if (path == objectPath)
176 {
177 // An enumerate does not return the target path's properties
178 continue;
179 }
180 if (dataJson.find(path) == dataJson.end())
181 {
182 for (const auto &[service, interfaces] : interface_map)
183 {
184 for (const auto &interface : interfaces)
185 {
186 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
187 {
188 getPropertiesForEnumerate(path, service, interface,
189 asyncResp);
190 }
191 }
192 }
193 }
194 }
195}
196
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600197struct InProgressEnumerateData
198{
199 InProgressEnumerateData(const std::string &objectPath,
200 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
201 objectPath(objectPath),
202 asyncResp(asyncResp)
203 {
204 }
205
206 ~InProgressEnumerateData()
207 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600208 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600209 }
210
211 const std::string objectPath;
212 std::shared_ptr<GetSubTreeType> subtree;
213 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
214};
215
216void getManagedObjectsForEnumerate(
217 const std::string &object_name, const std::string &object_manager_path,
218 const std::string &connection_name,
219 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220{
Ed Tanous049a0512018-11-01 13:58:42 -0700221 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
222 << " object_manager_path " << object_manager_path
223 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600225 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700226 connection_name](const boost::system::error_code ec,
227 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 if (ec)
229 {
Ed Tanous049a0512018-11-01 13:58:42 -0700230 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600231 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700232 << " failed with code " << ec;
233 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700234 }
Ed Tanous64530012018-02-06 17:08:16 -0800235
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600236 nlohmann::json &dataJson =
237 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700238
239 for (const auto &objectPath : objects)
240 {
241 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 {
Ed Tanous049a0512018-11-01 13:58:42 -0700243 BMCWEB_LOG_DEBUG << "Reading object "
244 << objectPath.first.str;
245 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 if (objectJson.is_null())
247 {
248 objectJson = nlohmann::json::object();
249 }
250 for (const auto &interface : objectPath.second)
251 {
252 for (const auto &property : interface.second)
253 {
254 nlohmann::json &propertyJson =
255 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800256 std::visit([&propertyJson](
257 auto &&val) { propertyJson = val; },
258 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 }
260 }
261 }
Ed Tanous049a0512018-11-01 13:58:42 -0700262 for (const auto &interface : objectPath.second)
263 {
264 if (interface.first == "org.freedesktop.DBus.ObjectManager")
265 {
266 getManagedObjectsForEnumerate(
267 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600268 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700269 }
270 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271 }
272 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700273 connection_name, object_manager_path,
274 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
275}
276
277void findObjectManagerPathForEnumerate(
278 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600279 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700280{
Ed Tanous049a0512018-11-01 13:58:42 -0700281 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
282 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700283 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600284 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700285 const boost::system::error_code ec,
286 const boost::container::flat_map<
287 std::string, boost::container::flat_map<
288 std::string, std::vector<std::string>>>
289 &objects) {
290 if (ec)
291 {
Ed Tanous049a0512018-11-01 13:58:42 -0700292 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
293 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700294 return;
295 }
296
Ed Tanousf254ba72018-10-12 13:40:35 -0700297 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700298 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700299 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700300 {
301 if (connectionGroup.first == connection_name)
302 {
303 // Found the object manager path for this resource.
304 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700305 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600306 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700307 return;
308 }
309 }
310 }
311 },
312 "xyz.openbmc_project.ObjectMapper",
313 "/xyz/openbmc_project/object_mapper",
314 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
315 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700316}
Ed Tanous64530012018-02-06 17:08:16 -0800317
Ed Tanous7c091622019-05-23 11:42:36 -0700318// Uses GetObject to add the object info about the target /enumerate path to
319// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600320// target path, and then continues on enumerating the rest of the tree.
321void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
322{
323 using GetObjectType =
324 std::vector<std::pair<std::string, std::vector<std::string>>>;
325
326 crow::connections::systemBus->async_method_call(
327 [transaction](const boost::system::error_code ec,
328 const GetObjectType &objects) {
329 if (ec)
330 {
331 BMCWEB_LOG_ERROR << "GetObject for path "
332 << transaction->objectPath
333 << " failed with code " << ec;
334 return;
335 }
336
337 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
338 << " has " << objects.size() << " entries";
339 if (!objects.empty())
340 {
341 transaction->subtree->emplace_back(transaction->objectPath,
342 objects);
343 }
344
345 // Map indicating connection name, and the path where the object
346 // manager exists
347 boost::container::flat_map<std::string, std::string> connections;
348
349 for (const auto &object : *(transaction->subtree))
350 {
351 for (const auto &connection : object.second)
352 {
353 std::string &objectManagerPath =
354 connections[connection.first];
355 for (const auto &interface : connection.second)
356 {
357 BMCWEB_LOG_DEBUG << connection.first
358 << " has interface " << interface;
359 if (interface == "org.freedesktop.DBus.ObjectManager")
360 {
361 BMCWEB_LOG_DEBUG << "found object manager path "
362 << object.first;
363 objectManagerPath = object.first;
364 }
365 }
366 }
367 }
368 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
369
370 for (const auto &connection : connections)
371 {
Ed Tanous7c091622019-05-23 11:42:36 -0700372 // If we already know where the object manager is, we don't
373 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600374 // getManagedObjects
375 if (!connection.second.empty())
376 {
377 getManagedObjectsForEnumerate(
378 transaction->objectPath, connection.second,
379 connection.first, transaction);
380 }
381 else
382 {
Ed Tanous7c091622019-05-23 11:42:36 -0700383 // otherwise we need to find the object manager path
384 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600385 findObjectManagerPathForEnumerate(
386 transaction->objectPath, connection.first, transaction);
387 }
388 }
389 },
390 "xyz.openbmc_project.ObjectMapper",
391 "/xyz/openbmc_project/object_mapper",
392 "xyz.openbmc_project.ObjectMapper", "GetObject",
393 transaction->objectPath, std::array<const char *, 0>());
394}
Ed Tanous64530012018-02-06 17:08:16 -0800395
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700396// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397struct InProgressActionData
398{
399 InProgressActionData(crow::Response &res) : res(res){};
400 ~InProgressActionData()
401 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600402 // Methods could have been called across different owners
403 // and interfaces, where some calls failed and some passed.
404 //
405 // The rules for this are:
406 // * if no method was called - error
407 // * if a method failed and none passed - error
408 // (converse: if at least one method passed - OK)
409 // * for the method output:
410 // * if output processing didn't fail, return the data
411
412 // Only deal with method returns if nothing failed earlier
413 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700414 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600415 if (!methodPassed)
416 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500417 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600418 {
419 setErrorResponse(res, boost::beast::http::status::not_found,
420 methodNotFoundDesc, notFoundMsg);
421 }
422 }
423 else
424 {
425 if (outputFailed)
426 {
427 setErrorResponse(
428 res, boost::beast::http::status::internal_server_error,
429 "Method output failure", methodOutputFailedMsg);
430 }
431 else
432 {
433 res.jsonValue = {{"status", "ok"},
434 {"message", "200 OK"},
435 {"data", methodResponse}};
436 }
437 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600439
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700441 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700442
Matt Spinler6db06242018-12-11 11:21:22 -0600443 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700444 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600445 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
446 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 }
448 crow::Response &res;
449 std::string path;
450 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600451 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600452 bool methodPassed = false;
453 bool methodFailed = false;
454 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600455 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600456 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700458};
459
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460std::vector<std::string> dbusArgSplit(const std::string &string)
461{
462 std::vector<std::string> ret;
463 if (string.empty())
464 {
465 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700466 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700467 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 int containerDepth = 0;
469
470 for (std::string::const_iterator character = string.begin();
471 character != string.end(); character++)
472 {
473 ret.back() += *character;
474 switch (*character)
475 {
476 case ('a'):
477 break;
478 case ('('):
479 case ('{'):
480 containerDepth++;
481 break;
482 case ('}'):
483 case (')'):
484 containerDepth--;
485 if (containerDepth == 0)
486 {
487 if (character + 1 != string.end())
488 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700489 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700490 }
491 }
492 break;
493 default:
494 if (containerDepth == 0)
495 {
496 if (character + 1 != string.end())
497 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700498 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 }
500 }
501 break;
502 }
503 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600504
505 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700506}
507
Ed Tanousd76323e2018-08-07 14:35:40 -0700508int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 const nlohmann::json &input_json)
510{
511 int r = 0;
512 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
513 << " to type: " << arg_type;
514 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700515
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 // Assume a single object for now.
517 const nlohmann::json *j = &input_json;
518 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700519
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700520 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 {
522 // If we are decoding multiple objects, grab the pointer to the
523 // iterator, and increment it for the next loop
524 if (argTypes.size() > 1)
525 {
526 if (jIt == input_json.end())
527 {
528 return -2;
529 }
530 j = &*jIt;
531 jIt++;
532 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700533 const int64_t *intValue = j->get_ptr<const int64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700534 const std::string *stringValue = j->get_ptr<const std::string *>();
535 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 const bool *b = j->get_ptr<const bool *>();
537 int64_t v = 0;
538 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700539
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540 // Do some basic type conversions that make sense. uint can be
541 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700542 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 {
Ed Tanous66664f22019-10-11 13:05:49 -0700544 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
545 if (uintValue != nullptr)
546 {
547 v = static_cast<int64_t>(*uintValue);
548 intValue = &v;
549 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 }
Ed Tanous66664f22019-10-11 13:05:49 -0700551 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 {
Ed Tanous66664f22019-10-11 13:05:49 -0700553 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
554 if (uintValue != nullptr)
555 {
556 d = static_cast<double>(*uintValue);
557 doubleValue = &d;
558 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 }
Ed Tanous66664f22019-10-11 13:05:49 -0700560 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 {
Ed Tanous66664f22019-10-11 13:05:49 -0700562 if (intValue != nullptr)
563 {
564 d = static_cast<double>(*intValue);
565 doubleValue = &d;
566 }
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 Tanous271584a2019-07-09 16:24:22 -0700575 r = sd_bus_message_append_basic(
576 m, argCode[0], static_cast<const void *>(stringValue->data()));
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 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500588 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
589 (*intValue > std::numeric_limits<int32_t>::max()))
590 {
591 return -ERANGE;
592 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700593 int32_t i = static_cast<int32_t>(*intValue);
594 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 if (r < 0)
596 {
597 return r;
598 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700599 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 {
602 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700603 int boolInt = false;
604 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500606 if (*intValue == 1)
607 {
608 boolInt = true;
609 }
610 else if (*intValue == 0)
611 {
612 boolInt = false;
613 }
614 else
615 {
616 return -ERANGE;
617 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 }
619 else if (b != nullptr)
620 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600621 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700623 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700625 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 }
627 else
628 {
629 return -1;
630 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700631 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 if (r < 0)
633 {
634 return r;
635 }
636 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 {
641 return -1;
642 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500643 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
644 (*intValue > std::numeric_limits<int16_t>::max()))
645 {
646 return -ERANGE;
647 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700648 int16_t n = static_cast<int16_t>(*intValue);
649 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 if (r < 0)
651 {
652 return r;
653 }
654 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700655 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700657 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 {
659 return -1;
660 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500661 if ((*intValue < std::numeric_limits<int64_t>::lowest()) ||
662 (*intValue > std::numeric_limits<int64_t>::max()))
663 {
664 return -ERANGE;
665 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700666 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 if (r < 0)
668 {
669 return r;
670 }
671 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 {
Ed Tanous66664f22019-10-11 13:05:49 -0700674 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700675 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 {
677 return -1;
678 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500679 if ((*uintValue < std::numeric_limits<uint8_t>::lowest()) ||
680 (*uintValue > std::numeric_limits<uint8_t>::max()))
681 {
682 return -ERANGE;
683 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 uint8_t y = static_cast<uint8_t>(*uintValue);
685 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700687 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 {
Ed Tanous66664f22019-10-11 13:05:49 -0700689 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700690 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 {
692 return -1;
693 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500694 if ((*uintValue < std::numeric_limits<uint16_t>::lowest()) ||
695 (*uintValue > std::numeric_limits<uint16_t>::max()))
696 {
697 return -ERANGE;
698 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700699 uint16_t q = static_cast<uint16_t>(*uintValue);
700 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700701 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700702 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 {
Ed Tanous66664f22019-10-11 13:05:49 -0700704 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700705 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 {
707 return -1;
708 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500709 if ((*uintValue < std::numeric_limits<uint32_t>::lowest()) ||
710 (*uintValue > std::numeric_limits<uint32_t>::max()))
711 {
712 return -ERANGE;
713 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700714 uint32_t u = static_cast<uint32_t>(*uintValue);
715 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700717 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 {
Ed Tanous66664f22019-10-11 13:05:49 -0700719 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700720 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 {
722 return -1;
723 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500724 if ((*uintValue < std::numeric_limits<uint64_t>::lowest()) ||
725 (*uintValue > std::numeric_limits<uint64_t>::max()))
726 {
727 return -ERANGE;
728 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700729 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700731 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500733 if (doubleValue == nullptr)
734 {
735 return -1;
736 }
737 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
738 (*doubleValue > std::numeric_limits<double>::max()))
739 {
740 return -ERANGE;
741 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700742 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700744 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700746 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700748 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 if (r < 0)
750 {
751 return r;
752 }
753
754 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
755 ++it)
756 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700757 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 if (r < 0)
759 {
760 return r;
761 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700762 }
763 sd_bus_message_close_container(m);
764 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700765 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700767 std::string containedType = argCode.substr(1);
768 BMCWEB_LOG_DEBUG << "variant type: " << argCode
769 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700771 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 if (r < 0)
773 {
774 return r;
775 }
776
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700777 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 if (r < 0)
779 {
780 return r;
781 }
782
783 r = sd_bus_message_close_container(m);
784 if (r < 0)
785 {
786 return r;
787 }
788 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700789 else if (boost::starts_with(argCode, "(") &&
790 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700791 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700792 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700794 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800795 if (r < 0)
796 {
797 return r;
798 }
799
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000801 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 {
803 if (it == j->end())
804 {
805 return -1;
806 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000807 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 if (r < 0)
809 {
810 return r;
811 }
812 it++;
813 }
814 r = sd_bus_message_close_container(m);
815 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700816 else if (boost::starts_with(argCode, "{") &&
817 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700819 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700821 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800822 if (r < 0)
823 {
824 return r;
825 }
826
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700827 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 if (codes.size() != 2)
829 {
830 return -1;
831 }
832 const std::string &key_type = codes[0];
833 const std::string &value_type = codes[1];
834 for (auto it : j->items())
835 {
836 r = convertJsonToDbus(m, key_type, it.key());
837 if (r < 0)
838 {
839 return r;
840 }
841
842 r = convertJsonToDbus(m, value_type, it.value());
843 if (r < 0)
844 {
845 return r;
846 }
847 }
848 r = sd_bus_message_close_container(m);
849 }
850 else
851 {
852 return -2;
853 }
854 if (r < 0)
855 {
856 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700857 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700858
Ed Tanous1abe55e2018-09-05 08:30:59 -0700859 if (argTypes.size() > 1)
860 {
861 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700862 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700863 }
Matt Spinler127ea542019-01-14 11:04:28 -0600864
865 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700866}
867
Matt Spinlerd22a7132019-01-14 12:14:30 -0600868template <typename T>
869int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
870 nlohmann::json &data)
871{
872 T value;
873
874 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
875 if (r < 0)
876 {
877 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
878 << " failed!";
879 return r;
880 }
881
882 data = value;
883 return 0;
884}
885
Matt Spinler16caaee2019-01-15 11:40:34 -0600886int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600887 sdbusplus::message::message &m, nlohmann::json &response);
888
889int readDictEntryFromMessage(const std::string &typeCode,
890 sdbusplus::message::message &m,
891 nlohmann::json &object)
892{
893 std::vector<std::string> types = dbusArgSplit(typeCode);
894 if (types.size() != 2)
895 {
896 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
897 << types.size();
898 return -1;
899 }
900
901 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
902 typeCode.c_str());
903 if (r < 0)
904 {
905 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
906 return r;
907 }
908
909 nlohmann::json key;
910 r = convertDBusToJSON(types[0], m, key);
911 if (r < 0)
912 {
913 return r;
914 }
915
916 const std::string *keyPtr = key.get_ptr<const std::string *>();
917 if (keyPtr == nullptr)
918 {
919 // json doesn't support non-string keys. If we hit this condition,
920 // convert the result to a string so we can proceed
921 key = key.dump();
922 keyPtr = key.get_ptr<const std::string *>();
Ed Tanous7c091622019-05-23 11:42:36 -0700923 // in theory this can't fail now, but lets be paranoid about it
924 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600925 if (keyPtr == nullptr)
926 {
927 return -1;
928 }
929 }
930 nlohmann::json &value = object[*keyPtr];
931
932 r = convertDBusToJSON(types[1], m, value);
933 if (r < 0)
934 {
935 return r;
936 }
937
938 r = sd_bus_message_exit_container(m.get());
939 if (r < 0)
940 {
941 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
942 return r;
943 }
944
945 return 0;
946}
947
948int readArrayFromMessage(const std::string &typeCode,
949 sdbusplus::message::message &m, nlohmann::json &data)
950{
951 if (typeCode.size() < 2)
952 {
953 BMCWEB_LOG_ERROR << "Type code " << typeCode
954 << " too small for an array";
955 return -1;
956 }
957
958 std::string containedType = typeCode.substr(1);
959
960 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
961 containedType.c_str());
962 if (r < 0)
963 {
964 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
965 << r;
966 return r;
967 }
968
969 bool dict = boost::starts_with(containedType, "{") &&
970 boost::ends_with(containedType, "}");
971
972 if (dict)
973 {
974 // Remove the { }
975 containedType = containedType.substr(1, containedType.size() - 2);
976 data = nlohmann::json::object();
977 }
978 else
979 {
980 data = nlohmann::json::array();
981 }
982
983 while (true)
984 {
985 r = sd_bus_message_at_end(m.get(), false);
986 if (r < 0)
987 {
988 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
989 return r;
990 }
991
992 if (r > 0)
993 {
994 break;
995 }
996
997 // Dictionaries are only ever seen in an array
998 if (dict)
999 {
1000 r = readDictEntryFromMessage(containedType, m, data);
1001 if (r < 0)
1002 {
1003 return r;
1004 }
1005 }
1006 else
1007 {
1008 data.push_back(nlohmann::json());
1009
1010 r = convertDBusToJSON(containedType, m, data.back());
1011 if (r < 0)
1012 {
1013 return r;
1014 }
1015 }
1016 }
1017
1018 r = sd_bus_message_exit_container(m.get());
1019 if (r < 0)
1020 {
1021 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1022 return r;
1023 }
1024
1025 return 0;
1026}
1027
Matt Spinler75c6c672019-01-14 13:01:46 -06001028int readStructFromMessage(const std::string &typeCode,
1029 sdbusplus::message::message &m, nlohmann::json &data)
1030{
1031 if (typeCode.size() < 3)
1032 {
1033 BMCWEB_LOG_ERROR << "Type code " << typeCode
1034 << " too small for a struct";
1035 return -1;
1036 }
1037
1038 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1039 std::vector<std::string> types = dbusArgSplit(containedTypes);
1040
1041 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1042 containedTypes.c_str());
1043 if (r < 0)
1044 {
1045 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1046 << r;
1047 return r;
1048 }
1049
1050 for (const std::string &type : types)
1051 {
1052 data.push_back(nlohmann::json());
1053 r = convertDBusToJSON(type, m, data.back());
1054 if (r < 0)
1055 {
1056 return r;
1057 }
1058 }
1059
1060 r = sd_bus_message_exit_container(m.get());
1061 if (r < 0)
1062 {
1063 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1064 return r;
1065 }
1066 return 0;
1067}
1068
Matt Spinler89c19702019-01-14 13:13:00 -06001069int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1070{
1071 const char *containerType;
Ed Tanous99131cd2019-10-24 11:12:47 -07001072 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001073 if (r < 0)
1074 {
1075 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1076 return r;
1077 }
1078
1079 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1080 containerType);
1081 if (r < 0)
1082 {
1083 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1084 << r;
1085 return r;
1086 }
1087
1088 r = convertDBusToJSON(containerType, m, data);
1089 if (r < 0)
1090 {
1091 return r;
1092 }
1093
1094 r = sd_bus_message_exit_container(m.get());
1095 if (r < 0)
1096 {
1097 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1098 return r;
1099 }
1100
1101 return 0;
1102}
1103
Matt Spinler6df8f992019-01-14 12:47:47 -06001104int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001105 sdbusplus::message::message &m, nlohmann::json &response)
1106{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001107 int r = 0;
1108 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1109
Matt Spinlerd22a7132019-01-14 12:14:30 -06001110 for (const std::string &typeCode : returnTypes)
1111 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001112 nlohmann::json *thisElement = &response;
1113 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001114 {
1115 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001116 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001117 }
1118
1119 if (typeCode == "s")
1120 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001121 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001122 if (r < 0)
1123 {
1124 return r;
1125 }
1126 }
1127 else if (typeCode == "g")
1128 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001129 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001130 if (r < 0)
1131 {
1132 return r;
1133 }
1134 }
1135 else if (typeCode == "o")
1136 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001137 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001138 if (r < 0)
1139 {
1140 return r;
1141 }
1142 }
1143 else if (typeCode == "b")
1144 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001145 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001146 if (r < 0)
1147 {
1148 return r;
1149 }
1150
Matt Spinlerf39420c2019-01-30 12:57:18 -06001151 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001152 }
1153 else if (typeCode == "u")
1154 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001155 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001156 if (r < 0)
1157 {
1158 return r;
1159 }
1160 }
1161 else if (typeCode == "i")
1162 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001163 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001164 if (r < 0)
1165 {
1166 return r;
1167 }
1168 }
1169 else if (typeCode == "x")
1170 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001171 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001172 if (r < 0)
1173 {
1174 return r;
1175 }
1176 }
1177 else if (typeCode == "t")
1178 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001179 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001180 if (r < 0)
1181 {
1182 return r;
1183 }
1184 }
1185 else if (typeCode == "n")
1186 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001187 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001188 if (r < 0)
1189 {
1190 return r;
1191 }
1192 }
1193 else if (typeCode == "q")
1194 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001195 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001196 if (r < 0)
1197 {
1198 return r;
1199 }
1200 }
1201 else if (typeCode == "y")
1202 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001203 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001204 if (r < 0)
1205 {
1206 return r;
1207 }
1208 }
1209 else if (typeCode == "d")
1210 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001211 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001212 if (r < 0)
1213 {
1214 return r;
1215 }
1216 }
1217 else if (typeCode == "h")
1218 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001219 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001220 if (r < 0)
1221 {
1222 return r;
1223 }
1224 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001225 else if (boost::starts_with(typeCode, "a"))
1226 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001227 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001228 if (r < 0)
1229 {
1230 return r;
1231 }
1232 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001233 else if (boost::starts_with(typeCode, "(") &&
1234 boost::ends_with(typeCode, ")"))
1235 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001236 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001237 if (r < 0)
1238 {
1239 return r;
1240 }
1241 }
Matt Spinler89c19702019-01-14 13:13:00 -06001242 else if (boost::starts_with(typeCode, "v"))
1243 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001244 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001245 if (r < 0)
1246 {
1247 return r;
1248 }
1249 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001250 else
1251 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001252 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1253 return -2;
1254 }
1255 }
1256
Matt Spinler16caaee2019-01-15 11:40:34 -06001257 return 0;
1258}
1259
1260void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1261 sdbusplus::message::message &m,
1262 const std::string &returnType)
1263{
Matt Spinler39a4e392019-01-15 11:53:13 -06001264 nlohmann::json data;
1265
1266 int r = convertDBusToJSON(returnType, m, data);
1267 if (r < 0)
1268 {
1269 transaction->outputFailed = true;
1270 return;
1271 }
1272
1273 if (data.is_null())
1274 {
1275 return;
1276 }
1277
1278 if (transaction->methodResponse.is_null())
1279 {
1280 transaction->methodResponse = std::move(data);
1281 return;
1282 }
1283
1284 // If they're both dictionaries or arrays, merge into one.
1285 // Otherwise, make the results an array with every result
1286 // an entry. Could also just fail in that case, but it
1287 // seems better to get the data back somehow.
1288
1289 if (transaction->methodResponse.is_object() && data.is_object())
1290 {
1291 for (const auto &obj : data.items())
1292 {
1293 // Note: Will overwrite the data for a duplicate key
1294 transaction->methodResponse.emplace(obj.key(),
1295 std::move(obj.value()));
1296 }
1297 return;
1298 }
1299
1300 if (transaction->methodResponse.is_array() && data.is_array())
1301 {
1302 for (auto &obj : data)
1303 {
1304 transaction->methodResponse.push_back(std::move(obj));
1305 }
1306 return;
1307 }
1308
1309 if (!transaction->convertedToArray)
1310 {
1311 // They are different types. May as well turn them into an array
1312 nlohmann::json j = std::move(transaction->methodResponse);
1313 transaction->methodResponse = nlohmann::json::array();
1314 transaction->methodResponse.push_back(std::move(j));
1315 transaction->methodResponse.push_back(std::move(data));
1316 transaction->convertedToArray = true;
1317 }
1318 else
1319 {
1320 transaction->methodResponse.push_back(std::move(data));
1321 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001322}
1323
Ed Tanousd76323e2018-08-07 14:35:40 -07001324void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001325 const std::string &connectionName)
1326{
1327 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1328 << connectionName;
1329 crow::connections::systemBus->async_method_call(
1330 [transaction, connectionName{std::string(connectionName)}](
1331 const boost::system::error_code ec,
1332 const std::string &introspect_xml) {
1333 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1334 if (ec)
1335 {
1336 BMCWEB_LOG_ERROR
1337 << "Introspect call failed with error: " << ec.message()
1338 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001339 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001340 }
Matt Spinler318bd892019-01-15 09:59:20 -06001341 tinyxml2::XMLDocument doc;
1342
1343 doc.Parse(introspect_xml.data(), introspect_xml.size());
1344 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1345 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001346 {
Matt Spinler318bd892019-01-15 09:59:20 -06001347 BMCWEB_LOG_ERROR << "XML document failed to parse "
1348 << connectionName << "\n";
1349 return;
1350 }
1351 tinyxml2::XMLElement *interfaceNode =
1352 pRoot->FirstChildElement("interface");
1353 while (interfaceNode != nullptr)
1354 {
1355 const char *thisInterfaceName =
1356 interfaceNode->Attribute("name");
1357 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001358 {
Matt Spinler318bd892019-01-15 09:59:20 -06001359 if (!transaction->interfaceName.empty() &&
1360 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001361 {
Matt Spinler318bd892019-01-15 09:59:20 -06001362 interfaceNode =
1363 interfaceNode->NextSiblingElement("interface");
1364 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001365 }
Matt Spinler318bd892019-01-15 09:59:20 -06001366
1367 tinyxml2::XMLElement *methodNode =
1368 interfaceNode->FirstChildElement("method");
1369 while (methodNode != nullptr)
1370 {
1371 const char *thisMethodName =
1372 methodNode->Attribute("name");
1373 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1374 if (thisMethodName != nullptr &&
1375 thisMethodName == transaction->methodName)
1376 {
1377 BMCWEB_LOG_DEBUG
1378 << "Found method named " << thisMethodName
1379 << " on interface " << thisInterfaceName;
1380 sdbusplus::message::message m =
1381 crow::connections::systemBus->new_method_call(
1382 connectionName.c_str(),
1383 transaction->path.c_str(),
1384 thisInterfaceName,
1385 transaction->methodName.c_str());
1386
1387 tinyxml2::XMLElement *argumentNode =
1388 methodNode->FirstChildElement("arg");
1389
Matt Spinler16caaee2019-01-15 11:40:34 -06001390 std::string returnType;
1391
1392 // Find the output type
1393 while (argumentNode != nullptr)
1394 {
1395 const char *argDirection =
1396 argumentNode->Attribute("direction");
1397 const char *argType =
1398 argumentNode->Attribute("type");
1399 if (argDirection != nullptr &&
1400 argType != nullptr &&
1401 std::string(argDirection) == "out")
1402 {
1403 returnType = argType;
1404 break;
1405 }
1406 argumentNode =
1407 argumentNode->NextSiblingElement("arg");
1408 }
1409
Matt Spinler318bd892019-01-15 09:59:20 -06001410 nlohmann::json::const_iterator argIt =
1411 transaction->arguments.begin();
1412
Matt Spinler16caaee2019-01-15 11:40:34 -06001413 argumentNode = methodNode->FirstChildElement("arg");
1414
Matt Spinler318bd892019-01-15 09:59:20 -06001415 while (argumentNode != nullptr)
1416 {
1417 const char *argDirection =
1418 argumentNode->Attribute("direction");
1419 const char *argType =
1420 argumentNode->Attribute("type");
1421 if (argDirection != nullptr &&
1422 argType != nullptr &&
1423 std::string(argDirection) == "in")
1424 {
1425 if (argIt == transaction->arguments.end())
1426 {
1427 transaction->setErrorStatus(
1428 "Invalid method args");
1429 return;
1430 }
1431 if (convertJsonToDbus(m.get(),
1432 std::string(argType),
1433 *argIt) < 0)
1434 {
1435 transaction->setErrorStatus(
1436 "Invalid method arg type");
1437 return;
1438 }
1439
1440 argIt++;
1441 }
1442 argumentNode =
1443 argumentNode->NextSiblingElement("arg");
1444 }
1445
1446 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001447 m, [transaction, returnType](
1448 boost::system::error_code ec,
1449 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001450 if (ec)
1451 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001452 transaction->methodFailed = true;
Matt Spinler06b1b632019-06-18 16:09:25 -05001453 const sd_bus_error *e = m.get_error();
1454
1455 if (e)
1456 {
1457 setErrorResponse(
1458 transaction->res,
1459 boost::beast::http::status::
1460 bad_request,
1461 e->name, e->message);
1462 }
1463 else
1464 {
1465 setErrorResponse(
1466 transaction->res,
1467 boost::beast::http::status::
1468 bad_request,
1469 "Method call failed",
1470 methodFailedMsg);
1471 }
Matt Spinler318bd892019-01-15 09:59:20 -06001472 return;
1473 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001474 else
1475 {
1476 transaction->methodPassed = true;
1477 }
1478
1479 handleMethodResponse(transaction, m,
1480 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001481 });
1482 break;
1483 }
1484 methodNode = methodNode->NextSiblingElement("method");
1485 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001486 }
Matt Spinler318bd892019-01-15 09:59:20 -06001487 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001488 }
1489 },
1490 connectionName, transaction->path,
1491 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001492}
1493
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001494void handleAction(const crow::Request &req, crow::Response &res,
1495 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001496{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001497 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1498 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001499 nlohmann::json requestDbusData =
1500 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001501
Ed Tanous1abe55e2018-09-05 08:30:59 -07001502 if (requestDbusData.is_discarded())
1503 {
Matt Spinler6db06242018-12-11 11:21:22 -06001504 setErrorResponse(res, boost::beast::http::status::bad_request,
1505 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001506 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001507 return;
1508 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001509 nlohmann::json::iterator data = requestDbusData.find("data");
1510 if (data == requestDbusData.end())
1511 {
Matt Spinler6db06242018-12-11 11:21:22 -06001512 setErrorResponse(res, boost::beast::http::status::bad_request,
1513 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001514 res.end();
1515 return;
1516 }
1517
1518 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001519 {
Matt Spinler6db06242018-12-11 11:21:22 -06001520 setErrorResponse(res, boost::beast::http::status::bad_request,
1521 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001522 res.end();
1523 return;
1524 }
1525 auto transaction = std::make_shared<InProgressActionData>(res);
1526
1527 transaction->path = objectPath;
1528 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001529 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001530 crow::connections::systemBus->async_method_call(
1531 [transaction](
1532 const boost::system::error_code ec,
1533 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001534 &interfaceNames) {
1535 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001536 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001537 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001538 setErrorResponse(transaction->res,
1539 boost::beast::http::status::not_found,
1540 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001541 return;
1542 }
1543
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001544 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1545 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001546
1547 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001548 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001549 {
1550 findActionOnInterface(transaction, object.first);
1551 }
1552 },
1553 "xyz.openbmc_project.ObjectMapper",
1554 "/xyz/openbmc_project/object_mapper",
1555 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1556 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001557}
1558
Matt Spinlerde818812018-12-11 16:39:20 -06001559void handleDelete(const crow::Request &req, crow::Response &res,
1560 const std::string &objectPath)
1561{
1562 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1563
1564 crow::connections::systemBus->async_method_call(
1565 [&res, objectPath](
1566 const boost::system::error_code ec,
1567 const std::vector<std::pair<std::string, std::vector<std::string>>>
1568 &interfaceNames) {
1569 if (ec || interfaceNames.size() <= 0)
1570 {
1571 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001572 setErrorResponse(res,
1573 boost::beast::http::status::method_not_allowed,
1574 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001575 res.end();
1576 return;
1577 }
1578
1579 auto transaction = std::make_shared<InProgressActionData>(res);
1580 transaction->path = objectPath;
1581 transaction->methodName = "Delete";
1582 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1583
1584 for (const std::pair<std::string, std::vector<std::string>>
1585 &object : interfaceNames)
1586 {
1587 findActionOnInterface(transaction, object.first);
1588 }
1589 },
1590 "xyz.openbmc_project.ObjectMapper",
1591 "/xyz/openbmc_project/object_mapper",
1592 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1593 std::array<const char *, 0>());
1594}
1595
Ed Tanousf839dfe2018-11-12 11:11:15 -08001596void handleList(crow::Response &res, const std::string &objectPath,
1597 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001598{
1599 crow::connections::systemBus->async_method_call(
1600 [&res](const boost::system::error_code ec,
1601 std::vector<std::string> &objectPaths) {
1602 if (ec)
1603 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001604 setErrorResponse(res, boost::beast::http::status::not_found,
1605 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001606 }
1607 else
1608 {
1609 res.jsonValue = {{"status", "ok"},
1610 {"message", "200 OK"},
1611 {"data", std::move(objectPaths)}};
1612 }
1613 res.end();
1614 },
1615 "xyz.openbmc_project.ObjectMapper",
1616 "/xyz/openbmc_project/object_mapper",
1617 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001618 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001619}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001620
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001621void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001622{
Ed Tanous049a0512018-11-01 13:58:42 -07001623 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1624 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1625
1626 asyncResp->res.jsonValue = {{"message", "200 OK"},
1627 {"status", "ok"},
1628 {"data", nlohmann::json::object()}};
1629
Ed Tanous1abe55e2018-09-05 08:30:59 -07001630 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001631 [objectPath, asyncResp](const boost::system::error_code ec,
1632 GetSubTreeType &object_names) {
1633 auto transaction = std::make_shared<InProgressEnumerateData>(
1634 objectPath, asyncResp);
1635
1636 transaction->subtree =
1637 std::make_shared<GetSubTreeType>(std::move(object_names));
1638
Ed Tanous1abe55e2018-09-05 08:30:59 -07001639 if (ec)
1640 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001641 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1642 << transaction->objectPath;
1643 setErrorResponse(transaction->asyncResp->res,
1644 boost::beast::http::status::not_found,
1645 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001646 return;
1647 }
Ed Tanous64530012018-02-06 17:08:16 -08001648
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001649 // Add the data for the path passed in to the results
1650 // as if GetSubTree returned it, and continue on enumerating
1651 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001652 },
1653 "xyz.openbmc_project.ObjectMapper",
1654 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -07001655 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
1656 std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001657}
Ed Tanous911ac312017-08-15 09:37:42 -07001658
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001659void handleGet(crow::Response &res, std::string &objectPath,
1660 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001661{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001662 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1663 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001664 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001665
Ed Tanous1abe55e2018-09-05 08:30:59 -07001666 std::shared_ptr<std::string> path =
1667 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001668
Ed Tanous1abe55e2018-09-05 08:30:59 -07001669 using GetObjectType =
1670 std::vector<std::pair<std::string, std::vector<std::string>>>;
1671 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001672 [&res, path, propertyName](const boost::system::error_code ec,
1673 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001674 if (ec || object_names.size() <= 0)
1675 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001676 setErrorResponse(res, boost::beast::http::status::not_found,
1677 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001678 res.end();
1679 return;
1680 }
1681 std::shared_ptr<nlohmann::json> response =
1682 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001683 // The mapper should never give us an empty interface names
1684 // list, but check anyway
Ed Tanous1abe55e2018-09-05 08:30:59 -07001685 for (const std::pair<std::string, std::vector<std::string>>
1686 connection : object_names)
1687 {
1688 const std::vector<std::string> &interfaceNames =
1689 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001690
Ed Tanous1abe55e2018-09-05 08:30:59 -07001691 if (interfaceNames.size() <= 0)
1692 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001693 setErrorResponse(res, boost::beast::http::status::not_found,
1694 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001695 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001696 return;
1697 }
1698
1699 for (const std::string &interface : interfaceNames)
1700 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001701 sdbusplus::message::message m =
1702 crow::connections::systemBus->new_method_call(
1703 connection.first.c_str(), path->c_str(),
1704 "org.freedesktop.DBus.Properties", "GetAll");
1705 m.append(interface);
1706 crow::connections::systemBus->async_send(
1707 m, [&res, response,
1708 propertyName](const boost::system::error_code ec,
1709 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001710 if (ec)
1711 {
1712 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1713 << ec;
1714 }
1715 else
1716 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001717 nlohmann::json properties;
1718 int r =
1719 convertDBusToJSON("a{sv}", msg, properties);
1720 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001721 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001722 BMCWEB_LOG_ERROR
1723 << "convertDBusToJSON failed";
1724 }
1725 else
1726 {
1727 for (auto &prop : properties.items())
1728 {
Ed Tanous7c091622019-05-23 11:42:36 -07001729 // if property name is empty, or
1730 // matches our search query, add it
1731 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001732
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001733 if (propertyName->empty())
1734 {
1735 (*response)[prop.key()] =
1736 std::move(prop.value());
1737 }
1738 else if (prop.key() == *propertyName)
1739 {
1740 *response = std::move(prop.value());
1741 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001742 }
1743 }
1744 }
1745 if (response.use_count() == 1)
1746 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001747 if (!propertyName->empty() && response->empty())
1748 {
1749 setErrorResponse(
1750 res,
1751 boost::beast::http::status::not_found,
1752 propNotFoundDesc, notFoundMsg);
1753 }
1754 else
1755 {
1756 res.jsonValue = {{"status", "ok"},
1757 {"message", "200 OK"},
1758 {"data", *response}};
1759 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001760 res.end();
1761 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001762 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001763 }
1764 }
1765 },
1766 "xyz.openbmc_project.ObjectMapper",
1767 "/xyz/openbmc_project/object_mapper",
1768 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1769 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001770}
1771
Ed Tanous1abe55e2018-09-05 08:30:59 -07001772struct AsyncPutRequest
1773{
1774 AsyncPutRequest(crow::Response &res) : res(res)
1775 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001776 }
1777 ~AsyncPutRequest()
1778 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001779 if (res.jsonValue.empty())
1780 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001781 setErrorResponse(res, boost::beast::http::status::forbidden,
1782 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001783 }
1784
1785 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001786 }
1787
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001788 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001789 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001790 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1791 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001792 }
1793
Ed Tanous1abe55e2018-09-05 08:30:59 -07001794 crow::Response &res;
1795 std::string objectPath;
1796 std::string propertyName;
1797 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001798};
1799
Ed Tanousd76323e2018-08-07 14:35:40 -07001800void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001801 const std::string &objectPath, const std::string &destProperty)
1802{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001803 if (destProperty.empty())
1804 {
1805 setErrorResponse(res, boost::beast::http::status::forbidden,
1806 forbiddenResDesc, forbiddenMsg);
1807 res.end();
1808 return;
1809 }
1810
Ed Tanous1abe55e2018-09-05 08:30:59 -07001811 nlohmann::json requestDbusData =
1812 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001813
Ed Tanous1abe55e2018-09-05 08:30:59 -07001814 if (requestDbusData.is_discarded())
1815 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001816 setErrorResponse(res, boost::beast::http::status::bad_request,
1817 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001818 res.end();
1819 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001820 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001821
Ed Tanous1abe55e2018-09-05 08:30:59 -07001822 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1823 if (propertyIt == requestDbusData.end())
1824 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001825 setErrorResponse(res, boost::beast::http::status::bad_request,
1826 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001827 res.end();
1828 return;
1829 }
1830 const nlohmann::json &propertySetValue = *propertyIt;
1831 auto transaction = std::make_shared<AsyncPutRequest>(res);
1832 transaction->objectPath = objectPath;
1833 transaction->propertyName = destProperty;
1834 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001835
Ed Tanous1abe55e2018-09-05 08:30:59 -07001836 using GetObjectType =
1837 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001838
Ed Tanous1abe55e2018-09-05 08:30:59 -07001839 crow::connections::systemBus->async_method_call(
1840 [transaction](const boost::system::error_code ec,
1841 const GetObjectType &object_names) {
1842 if (!ec && object_names.size() <= 0)
1843 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001844 setErrorResponse(transaction->res,
1845 boost::beast::http::status::not_found,
1846 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001847 return;
1848 }
Ed Tanous911ac312017-08-15 09:37:42 -07001849
Ed Tanous1abe55e2018-09-05 08:30:59 -07001850 for (const std::pair<std::string, std::vector<std::string>>
1851 connection : object_names)
1852 {
1853 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001854
Ed Tanous1abe55e2018-09-05 08:30:59 -07001855 crow::connections::systemBus->async_method_call(
1856 [connectionName{std::string(connectionName)},
1857 transaction](const boost::system::error_code ec,
1858 const std::string &introspectXml) {
1859 if (ec)
1860 {
1861 BMCWEB_LOG_ERROR
1862 << "Introspect call failed with error: "
1863 << ec.message()
1864 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001865 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001866 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001867 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001868 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001869
Ed Tanous1abe55e2018-09-05 08:30:59 -07001870 doc.Parse(introspectXml.c_str());
1871 tinyxml2::XMLNode *pRoot =
1872 doc.FirstChildElement("node");
1873 if (pRoot == nullptr)
1874 {
1875 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1876 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001877 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001878 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001879 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001880 tinyxml2::XMLElement *ifaceNode =
1881 pRoot->FirstChildElement("interface");
1882 while (ifaceNode != nullptr)
1883 {
1884 const char *interfaceName =
1885 ifaceNode->Attribute("name");
1886 BMCWEB_LOG_DEBUG << "found interface "
1887 << interfaceName;
1888 tinyxml2::XMLElement *propNode =
1889 ifaceNode->FirstChildElement("property");
1890 while (propNode != nullptr)
1891 {
1892 const char *propertyName =
1893 propNode->Attribute("name");
1894 BMCWEB_LOG_DEBUG << "Found property "
1895 << propertyName;
1896 if (propertyName == transaction->propertyName)
1897 {
1898 const char *argType =
1899 propNode->Attribute("type");
1900 if (argType != nullptr)
1901 {
1902 sdbusplus::message::message m =
1903 crow::connections::systemBus
1904 ->new_method_call(
1905 connectionName.c_str(),
1906 transaction->objectPath
1907 .c_str(),
1908 "org.freedesktop.DBus."
1909 "Properties",
1910 "Set");
1911 m.append(interfaceName,
1912 transaction->propertyName);
1913 int r = sd_bus_message_open_container(
1914 m.get(), SD_BUS_TYPE_VARIANT,
1915 argType);
1916 if (r < 0)
1917 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001918 transaction->setErrorStatus(
1919 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001920 return;
1921 }
1922 r = convertJsonToDbus(
1923 m.get(), argType,
1924 transaction->propertyValue);
1925 if (r < 0)
1926 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -05001927 if (r == -ERANGE)
1928 {
1929 transaction->setErrorStatus(
1930 "Provided property value "
1931 "is out of range for the "
1932 "property type");
1933 }
1934 else
1935 {
1936 transaction->setErrorStatus(
1937 "Invalid arg type");
1938 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001939 return;
1940 }
1941 r = sd_bus_message_close_container(
1942 m.get());
1943 if (r < 0)
1944 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001945 transaction->setErrorStatus(
1946 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001947 return;
1948 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001949 crow::connections::systemBus
1950 ->async_send(
1951 m,
1952 [transaction](
1953 boost::system::error_code
1954 ec,
1955 sdbusplus::message::message
1956 &m) {
1957 BMCWEB_LOG_DEBUG << "sent";
1958 if (ec)
1959 {
Lei YU97d2a472019-06-11 17:44:27 +08001960 const sd_bus_error *e =
1961 m.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001962 setErrorResponse(
1963 transaction->res,
1964 boost::beast::http::
1965 status::
1966 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001967 (e) ? e->name
1968 : ec.category()
1969 .name(),
1970 (e) ? e->message
1971 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001972 }
1973 else
1974 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001975 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001976 .jsonValue = {
1977 {"status", "ok"},
1978 {"message",
1979 "200 OK"},
1980 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001981 }
1982 });
1983 }
1984 }
1985 propNode =
1986 propNode->NextSiblingElement("property");
1987 }
1988 ifaceNode =
1989 ifaceNode->NextSiblingElement("interface");
1990 }
1991 },
1992 connectionName, transaction->objectPath,
1993 "org.freedesktop.DBus.Introspectable", "Introspect");
1994 }
1995 },
1996 "xyz.openbmc_project.ObjectMapper",
1997 "/xyz/openbmc_project/object_mapper",
1998 "xyz.openbmc_project.ObjectMapper", "GetObject",
1999 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002000}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002001
Ed Tanous049a0512018-11-01 13:58:42 -07002002inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
2003 std::string &objectPath)
2004{
Ed Tanous049a0512018-11-01 13:58:42 -07002005
2006 // If accessing a single attribute, fill in and update objectPath,
2007 // otherwise leave destProperty blank
2008 std::string destProperty = "";
2009 const char *attrSeperator = "/attr/";
2010 size_t attrPosition = objectPath.find(attrSeperator);
2011 if (attrPosition != objectPath.npos)
2012 {
2013 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2014 objectPath.length());
2015 objectPath = objectPath.substr(0, attrPosition);
2016 }
2017
2018 if (req.method() == "POST"_method)
2019 {
2020 constexpr const char *actionSeperator = "/action/";
2021 size_t actionPosition = objectPath.find(actionSeperator);
2022 if (actionPosition != objectPath.npos)
2023 {
2024 std::string postProperty =
2025 objectPath.substr((actionPosition + strlen(actionSeperator)),
2026 objectPath.length());
2027 objectPath = objectPath.substr(0, actionPosition);
2028 handleAction(req, res, objectPath, postProperty);
2029 return;
2030 }
2031 }
2032 else if (req.method() == "GET"_method)
2033 {
2034 if (boost::ends_with(objectPath, "/enumerate"))
2035 {
2036 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2037 objectPath.end());
2038 handleEnumerate(res, objectPath);
2039 }
2040 else if (boost::ends_with(objectPath, "/list"))
2041 {
2042 objectPath.erase(objectPath.end() - sizeof("list"),
2043 objectPath.end());
2044 handleList(res, objectPath);
2045 }
2046 else
2047 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002048 // Trim any trailing "/" at the end
2049 if (boost::ends_with(objectPath, "/"))
2050 {
2051 objectPath.pop_back();
2052 handleList(res, objectPath, 1);
2053 }
2054 else
2055 {
2056 handleGet(res, objectPath, destProperty);
2057 }
Ed Tanous049a0512018-11-01 13:58:42 -07002058 }
2059 return;
2060 }
2061 else if (req.method() == "PUT"_method)
2062 {
2063 handlePut(req, res, objectPath, destProperty);
2064 return;
2065 }
Matt Spinlerde818812018-12-11 16:39:20 -06002066 else if (req.method() == "DELETE"_method)
2067 {
2068 handleDelete(req, res, objectPath);
2069 return;
2070 }
Ed Tanous049a0512018-11-01 13:58:42 -07002071
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002072 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2073 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002074 res.end();
2075}
2076
Ed Tanous1abe55e2018-09-05 08:30:59 -07002077template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
2078{
2079 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03002080 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002081 .methods("GET"_method)(
2082 [](const crow::Request &req, crow::Response &res) {
2083 res.jsonValue = {{"busses", {{{"name", "system"}}}},
2084 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002085 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002086 });
2087
2088 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03002089 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002090 .methods("GET"_method)(
2091 [](const crow::Request &req, crow::Response &res) {
2092 auto myCallback = [&res](const boost::system::error_code ec,
2093 std::vector<std::string> &names) {
2094 if (ec)
2095 {
2096 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2097 res.result(
2098 boost::beast::http::status::internal_server_error);
2099 }
2100 else
2101 {
2102 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002103 res.jsonValue = {{"status", "ok"}};
2104 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002105 for (auto &name : names)
2106 {
2107 objectsSub.push_back({{"name", name}});
2108 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002109 }
2110 res.end();
2111 };
2112 crow::connections::systemBus->async_method_call(
2113 std::move(myCallback), "org.freedesktop.DBus", "/",
2114 "org.freedesktop.DBus", "ListNames");
2115 });
2116
2117 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002118 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002119 .methods("GET"_method)(
2120 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002121 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002122 });
2123
2124 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002125 .requires({"Login"})
2126 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2127 const std::string &path) {
2128 std::string objectPath = "/xyz/" + path;
2129 handleDBusUrl(req, res, objectPath);
2130 });
2131
2132 BMCWEB_ROUTE(app, "/xyz/<path>")
2133 .requires({"ConfigureComponents", "ConfigureManager"})
2134 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002135 [](const crow::Request &req, crow::Response &res,
2136 const std::string &path) {
2137 std::string objectPath = "/xyz/" + path;
2138 handleDBusUrl(req, res, objectPath);
2139 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002140
Ed Tanous049a0512018-11-01 13:58:42 -07002141 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002142 .requires({"Login"})
2143 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2144 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002145 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002146 handleDBusUrl(req, res, objectPath);
2147 });
2148
2149 BMCWEB_ROUTE(app, "/org/<path>")
2150 .requires({"ConfigureComponents", "ConfigureManager"})
2151 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002152 [](const crow::Request &req, crow::Response &res,
2153 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002154 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002155 handleDBusUrl(req, res, objectPath);
2156 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002157
Ed Tanous1abe55e2018-09-05 08:30:59 -07002158 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002159 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002160 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2161 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002162 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002163 if (!std::regex_match(dumpId, validFilename))
2164 {
Ed Tanousad18f072018-11-14 14:07:48 -08002165 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002166 res.end();
2167 return;
2168 }
James Feistf6150402019-01-08 10:36:20 -08002169 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002170 "/var/lib/phosphor-debug-collector/dumps");
2171
Ed Tanousad18f072018-11-14 14:07:48 -08002172 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002173
James Feistf6150402019-01-08 10:36:20 -08002174 if (!std::filesystem::exists(loc) ||
2175 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002176 {
Ed Tanousad18f072018-11-14 14:07:48 -08002177 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002178 res.result(boost::beast::http::status::not_found);
2179 res.end();
2180 return;
2181 }
James Feistf6150402019-01-08 10:36:20 -08002182 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002183
Ed Tanous1abe55e2018-09-05 08:30:59 -07002184 for (auto &file : files)
2185 {
2186 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002187 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002188 {
2189 continue;
2190 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002191
Ed Tanous1abe55e2018-09-05 08:30:59 -07002192 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002193
2194 // Assuming only one dump file will be present in the dump id
2195 // directory
2196 std::string dumpFileName = file.path().filename().string();
2197
2198 // Filename should be in alphanumeric, dot and underscore
2199 // Its based on phosphor-debug-collector application dumpfile
2200 // format
2201 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2202 if (!std::regex_match(dumpFileName, dumpFileRegex))
2203 {
2204 BMCWEB_LOG_ERROR << "Invalid dump filename "
2205 << dumpFileName;
2206 res.result(boost::beast::http::status::not_found);
2207 res.end();
2208 return;
2209 }
2210 std::string contentDispositionParam =
2211 "attachment; filename=\"" + dumpFileName + "\"";
2212
2213 res.addHeader("Content-Disposition", contentDispositionParam);
2214
Ed Tanous1abe55e2018-09-05 08:30:59 -07002215 res.body() = {std::istreambuf_iterator<char>(readFile),
2216 std::istreambuf_iterator<char>()};
2217 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002218 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002219 }
2220 res.result(boost::beast::http::status::not_found);
2221 res.end();
2222 return;
2223 });
2224
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002225 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002226 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002227 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002228 const std::string &Connection) {
2229 introspectObjects(Connection, "/",
2230 std::make_shared<bmcweb::AsyncResp>(res));
2231 });
2232
2233 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous8251ffe2019-10-10 14:33:54 -07002234 .requires({"ConfigureComponents", "ConfigureManager"})
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002235 .methods("GET"_method,
2236 "POST"_method)([](const crow::Request &req,
2237 crow::Response &res,
2238 const std::string &processName,
2239 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002240 std::vector<std::string> strs;
2241 boost::split(strs, requestedPath, boost::is_any_of("/"));
2242 std::string objectPath;
2243 std::string interfaceName;
2244 std::string methodName;
2245 auto it = strs.begin();
2246 if (it == strs.end())
2247 {
2248 objectPath = "/";
2249 }
2250 while (it != strs.end())
2251 {
2252 // Check if segment contains ".". If it does, it must be an
2253 // interface
2254 if (it->find(".") != std::string::npos)
2255 {
2256 break;
Ed Tanous7c091622019-05-23 11:42:36 -07002257 // This check is neccesary as the trailing slash gets
2258 // parsed as part of our <path> specifier above, which
2259 // causes the normal trailing backslash redirector to
2260 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002261 }
2262 else if (!it->empty())
2263 {
2264 objectPath += "/" + *it;
2265 }
2266 it++;
2267 }
2268 if (it != strs.end())
2269 {
2270 interfaceName = *it;
2271 it++;
2272
2273 // after interface, we might have a method name
2274 if (it != strs.end())
2275 {
2276 methodName = *it;
2277 it++;
2278 }
2279 }
2280 if (it != strs.end())
2281 {
Ed Tanous7c091622019-05-23 11:42:36 -07002282 // if there is more levels past the method name, something
2283 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002284 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002285 return;
2286 }
2287 if (interfaceName.empty())
2288 {
Ed Tanous7c091622019-05-23 11:42:36 -07002289 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2290 std::make_shared<bmcweb::AsyncResp>(res);
2291
Ed Tanous1abe55e2018-09-05 08:30:59 -07002292 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002293 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002294 objectPath](const boost::system::error_code ec,
2295 const std::string &introspect_xml) {
2296 if (ec)
2297 {
2298 BMCWEB_LOG_ERROR
2299 << "Introspect call failed with error: "
2300 << ec.message()
2301 << " on process: " << processName
2302 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002303 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002304 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002305 tinyxml2::XMLDocument doc;
2306
2307 doc.Parse(introspect_xml.c_str());
2308 tinyxml2::XMLNode *pRoot =
2309 doc.FirstChildElement("node");
2310 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002311 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002312 BMCWEB_LOG_ERROR << "XML document failed to parse "
2313 << processName << " " << objectPath
2314 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002315 asyncResp->res.jsonValue = {
2316 {"status", "XML parse error"}};
2317 asyncResp->res.result(boost::beast::http::status::
2318 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002319 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002320 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002321
2322 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002323 asyncResp->res.jsonValue = {
2324 {"status", "ok"},
2325 {"bus_name", processName},
2326 {"object_path", objectPath}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002327 nlohmann::json &interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002328 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002329 interfacesArray = nlohmann::json::array();
2330 tinyxml2::XMLElement *interface =
2331 pRoot->FirstChildElement("interface");
2332
2333 while (interface != nullptr)
2334 {
2335 const char *ifaceName =
2336 interface->Attribute("name");
2337 if (ifaceName != nullptr)
2338 {
2339 interfacesArray.push_back(
2340 {{"name", ifaceName}});
2341 }
2342
2343 interface =
2344 interface->NextSiblingElement("interface");
2345 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002346 },
2347 processName, objectPath,
2348 "org.freedesktop.DBus.Introspectable", "Introspect");
2349 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002350 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002351 {
Ed Tanous7c091622019-05-23 11:42:36 -07002352 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2353 std::make_shared<bmcweb::AsyncResp>(res);
2354
Ed Tanous1abe55e2018-09-05 08:30:59 -07002355 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002356 [asyncResp, processName, objectPath,
2357 interfaceName](const boost::system::error_code ec,
2358 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002359 if (ec)
2360 {
2361 BMCWEB_LOG_ERROR
2362 << "Introspect call failed with error: "
2363 << ec.message()
2364 << " on process: " << processName
2365 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002366 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002367 }
Ed Tanous7c091622019-05-23 11:42:36 -07002368 tinyxml2::XMLDocument doc;
2369
2370 doc.Parse(introspect_xml.data(), introspect_xml.size());
2371 tinyxml2::XMLNode *pRoot =
2372 doc.FirstChildElement("node");
2373 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002374 {
Ed Tanous7c091622019-05-23 11:42:36 -07002375 BMCWEB_LOG_ERROR << "XML document failed to parse "
2376 << processName << " " << objectPath
2377 << "\n";
2378 asyncResp->res.result(boost::beast::http::status::
2379 internal_server_error);
2380 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002381 }
Ed Tanous7c091622019-05-23 11:42:36 -07002382 asyncResp->res.jsonValue = {
2383 {"status", "ok"},
2384 {"bus_name", processName},
2385 {"interface", interfaceName},
2386 {"object_path", objectPath}};
2387
2388 nlohmann::json &methodsArray =
2389 asyncResp->res.jsonValue["methods"];
2390 methodsArray = nlohmann::json::array();
2391
2392 nlohmann::json &signalsArray =
2393 asyncResp->res.jsonValue["signals"];
2394 signalsArray = nlohmann::json::array();
2395
2396 nlohmann::json &propertiesObj =
2397 asyncResp->res.jsonValue["properties"];
2398 propertiesObj = nlohmann::json::object();
2399
2400 // if we know we're the only call, build the
2401 // json directly
2402 tinyxml2::XMLElement *interface =
2403 pRoot->FirstChildElement("interface");
2404 while (interface != nullptr)
2405 {
2406 const char *ifaceName =
2407 interface->Attribute("name");
2408
2409 if (ifaceName != nullptr &&
2410 ifaceName == interfaceName)
2411 {
2412 break;
2413 }
2414
2415 interface =
2416 interface->NextSiblingElement("interface");
2417 }
2418 if (interface == nullptr)
2419 {
2420 // if we got to the end of the list and
2421 // never found a match, throw 404
2422 asyncResp->res.result(
2423 boost::beast::http::status::not_found);
2424 return;
2425 }
2426
2427 tinyxml2::XMLElement *methods =
2428 interface->FirstChildElement("method");
2429 while (methods != nullptr)
2430 {
2431 nlohmann::json argsArray = nlohmann::json::array();
2432 tinyxml2::XMLElement *arg =
2433 methods->FirstChildElement("arg");
2434 while (arg != nullptr)
2435 {
2436 nlohmann::json thisArg;
2437 for (const char *fieldName :
2438 std::array<const char *, 3>{
2439 "name", "direction", "type"})
2440 {
2441 const char *fieldValue =
2442 arg->Attribute(fieldName);
2443 if (fieldValue != nullptr)
2444 {
2445 thisArg[fieldName] = fieldValue;
2446 }
2447 }
2448 argsArray.push_back(std::move(thisArg));
2449 arg = arg->NextSiblingElement("arg");
2450 }
2451
2452 const char *name = methods->Attribute("name");
2453 if (name != nullptr)
2454 {
2455 methodsArray.push_back(
2456 {{"name", name},
2457 {"uri", "/bus/system/" + processName +
2458 objectPath + "/" +
2459 interfaceName + "/" + name},
2460 {"args", argsArray}});
2461 }
2462 methods = methods->NextSiblingElement("method");
2463 }
2464 tinyxml2::XMLElement *signals =
2465 interface->FirstChildElement("signal");
2466 while (signals != nullptr)
2467 {
2468 nlohmann::json argsArray = nlohmann::json::array();
2469
2470 tinyxml2::XMLElement *arg =
2471 signals->FirstChildElement("arg");
2472 while (arg != nullptr)
2473 {
2474 const char *name = arg->Attribute("name");
2475 const char *type = arg->Attribute("type");
2476 if (name != nullptr && type != nullptr)
2477 {
2478 argsArray.push_back({
2479 {"name", name},
2480 {"type", type},
2481 });
2482 }
2483 arg = arg->NextSiblingElement("arg");
2484 }
2485 const char *name = signals->Attribute("name");
2486 if (name != nullptr)
2487 {
2488 signalsArray.push_back(
2489 {{"name", name}, {"args", argsArray}});
2490 }
2491
2492 signals = signals->NextSiblingElement("signal");
2493 }
2494
2495 tinyxml2::XMLElement *property =
2496 interface->FirstChildElement("property");
2497 while (property != nullptr)
2498 {
2499 const char *name = property->Attribute("name");
2500 const char *type = property->Attribute("type");
2501 if (type != nullptr && name != nullptr)
2502 {
2503 sdbusplus::message::message m =
2504 crow::connections::systemBus
2505 ->new_method_call(processName.c_str(),
2506 objectPath.c_str(),
2507 "org.freedesktop."
2508 "DBus."
2509 "Properties",
2510 "Get");
2511 m.append(interfaceName, name);
2512 nlohmann::json &propertyItem =
2513 propertiesObj[name];
2514 crow::connections::systemBus->async_send(
2515 m, [&propertyItem, asyncResp](
Ed Tanous271584a2019-07-09 16:24:22 -07002516 boost::system::error_code &e,
2517 sdbusplus::message::message &msg) {
2518 if (e)
Ed Tanous7c091622019-05-23 11:42:36 -07002519 {
2520 return;
2521 }
2522
Ed Tanous271584a2019-07-09 16:24:22 -07002523 convertDBusToJSON("v", msg,
2524 propertyItem);
Ed Tanous7c091622019-05-23 11:42:36 -07002525 });
2526 }
2527 property = property->NextSiblingElement("property");
2528 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002529 },
2530 processName, objectPath,
2531 "org.freedesktop.DBus.Introspectable", "Introspect");
2532 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002533 else
2534 {
2535 if (req.method() != "POST"_method)
2536 {
2537 res.result(boost::beast::http::status::not_found);
2538 res.end();
2539 return;
2540 }
2541
2542 nlohmann::json requestDbusData =
2543 nlohmann::json::parse(req.body, nullptr, false);
2544
2545 if (requestDbusData.is_discarded())
2546 {
2547 res.result(boost::beast::http::status::bad_request);
2548 res.end();
2549 return;
2550 }
2551 if (!requestDbusData.is_array())
2552 {
2553 res.result(boost::beast::http::status::bad_request);
2554 res.end();
2555 return;
2556 }
2557 auto transaction = std::make_shared<InProgressActionData>(res);
2558
2559 transaction->path = objectPath;
2560 transaction->methodName = methodName;
2561 transaction->arguments = std::move(requestDbusData);
2562
2563 findActionOnInterface(transaction, processName);
2564 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002565 });
2566}
2567} // namespace openbmc_mapper
2568} // namespace crow