blob: 5b9b7382d26299f4c94c2198837ccc71d0de4444 [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous911ac312017-08-15 09:37:42 -070016#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
Ed Tanouse3cb5a32018-08-08 14:16:49 -070019#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <boost/algorithm/string.hpp>
21#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070024#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070025#include <fstream>
Ramesh Iyyard9207042019-07-05 08:04:42 -050026#include <regex>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070027#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace openbmc_mapper
32{
Ed Tanousba9f9a62017-10-11 16:40:35 -070033
Matt Spinler3ae4ba72018-12-05 14:01:22 -060034using GetSubTreeType = std::vector<
35 std::pair<std::string,
36 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37
Matt Spinler2ae60092018-12-06 10:35:36 -060038const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060039const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060040const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060041const std::string forbiddenMsg = "403 Forbidden";
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -060042const std::string methodFailedMsg = "500 Method Call Failed";
Matt Spinler16caaee2019-01-15 11:40:34 -060043const std::string methodOutputFailedMsg = "500 Method Output Error";
Matt Spinler6db06242018-12-11 11:21:22 -060044
Matt Spinler2ae60092018-12-06 10:35:36 -060045const std::string notFoundDesc =
46 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060047const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060048const std::string noJsonDesc = "No JSON object could be decoded";
49const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060050const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060051const std::string forbiddenPropDesc =
52 "The specified property cannot be created";
53const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060054
55void setErrorResponse(crow::Response &res, boost::beast::http::status result,
56 const std::string &desc, const std::string &msg)
57{
58 res.result(result);
59 res.jsonValue = {{"data", {{"description", desc}}},
60 {"message", msg},
61 {"status", "error"}};
62}
63
Ed Tanouse3cb5a32018-08-08 14:16:49 -070064void introspectObjects(const std::string &processName,
65 const std::string &objectPath,
66 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070067{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070068 if (transaction->res.jsonValue.is_null())
69 {
70 transaction->res.jsonValue = {{"status", "ok"},
71 {"bus_name", processName},
72 {"objects", nlohmann::json::array()}};
73 }
74
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070076 [transaction, processName{std::string(processName)},
77 objectPath{std::string(objectPath)}](
78 const boost::system::error_code ec,
79 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 if (ec)
81 {
82 BMCWEB_LOG_ERROR
83 << "Introspect call failed with error: " << ec.message()
84 << " on process: " << processName << " path: " << objectPath
85 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070086 return;
87 }
88 transaction->res.jsonValue["objects"].push_back(
89 {{"path", objectPath}});
90
91 tinyxml2::XMLDocument doc;
92
93 doc.Parse(introspect_xml.c_str());
94 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
95 if (pRoot == nullptr)
96 {
97 BMCWEB_LOG_ERROR << "XML document failed to parse "
98 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070099 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 else
101 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
103 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700105 const char *childPath = node->Attribute("name");
106 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 std::string newpath;
109 if (objectPath != "/")
110 {
111 newpath += objectPath;
112 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700113 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700115 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700117
118 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 }
120 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700122 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700124}
Ed Tanous64530012018-02-06 17:08:16 -0800125
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600126void getPropertiesForEnumerate(const std::string &objectPath,
127 const std::string &service,
128 const std::string &interface,
129 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
130{
131 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
132 << service << " " << interface;
133
134 crow::connections::systemBus->async_method_call(
135 [asyncResp, objectPath, service,
136 interface](const boost::system::error_code ec,
137 const std::vector<
138 std::pair<std::string, dbus::utility::DbusVariantType>>
139 &propertiesList) {
140 if (ec)
141 {
142 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
143 << interface << " service " << service
144 << " failed with code " << ec;
145 return;
146 }
147
148 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
149 nlohmann::json &objectJson = dataJson[objectPath];
150 if (objectJson.is_null())
151 {
152 objectJson = nlohmann::json::object();
153 }
154
155 for (const auto &[name, value] : propertiesList)
156 {
157 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800158 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
159 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600160 }
161 },
162 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
163 interface);
164}
165
166// Find any results that weren't picked up by ObjectManagers, to be
167// called after all ObjectManagers are searched for and called.
168void findRemainingObjectsForEnumerate(
169 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
170 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
171{
172 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
173 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
174
175 for (const auto &[path, interface_map] : *subtree)
176 {
177 if (path == objectPath)
178 {
179 // An enumerate does not return the target path's properties
180 continue;
181 }
182 if (dataJson.find(path) == dataJson.end())
183 {
184 for (const auto &[service, interfaces] : interface_map)
185 {
186 for (const auto &interface : interfaces)
187 {
188 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
189 {
190 getPropertiesForEnumerate(path, service, interface,
191 asyncResp);
192 }
193 }
194 }
195 }
196 }
197}
198
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600199struct InProgressEnumerateData
200{
201 InProgressEnumerateData(const std::string &objectPath,
202 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
203 objectPath(objectPath),
204 asyncResp(asyncResp)
205 {
206 }
207
208 ~InProgressEnumerateData()
209 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600210 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600211 }
212
213 const std::string objectPath;
214 std::shared_ptr<GetSubTreeType> subtree;
215 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
216};
217
218void getManagedObjectsForEnumerate(
219 const std::string &object_name, const std::string &object_manager_path,
220 const std::string &connection_name,
221 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700222{
Ed Tanous049a0512018-11-01 13:58:42 -0700223 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
224 << " object_manager_path " << object_manager_path
225 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600227 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700228 connection_name](const boost::system::error_code ec,
229 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 if (ec)
231 {
Ed Tanous049a0512018-11-01 13:58:42 -0700232 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600233 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700234 << " failed with code " << ec;
235 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700236 }
Ed Tanous64530012018-02-06 17:08:16 -0800237
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600238 nlohmann::json &dataJson =
239 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700240
241 for (const auto &objectPath : objects)
242 {
243 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 {
Ed Tanous049a0512018-11-01 13:58:42 -0700245 BMCWEB_LOG_DEBUG << "Reading object "
246 << objectPath.first.str;
247 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 if (objectJson.is_null())
249 {
250 objectJson = nlohmann::json::object();
251 }
252 for (const auto &interface : objectPath.second)
253 {
254 for (const auto &property : interface.second)
255 {
256 nlohmann::json &propertyJson =
257 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800258 std::visit([&propertyJson](
259 auto &&val) { propertyJson = val; },
260 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 }
262 }
263 }
Ed Tanous049a0512018-11-01 13:58:42 -0700264 for (const auto &interface : objectPath.second)
265 {
266 if (interface.first == "org.freedesktop.DBus.ObjectManager")
267 {
268 getManagedObjectsForEnumerate(
269 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600270 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700271 }
272 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 }
274 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700275 connection_name, object_manager_path,
276 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
277}
278
279void findObjectManagerPathForEnumerate(
280 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600281 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700282{
Ed Tanous049a0512018-11-01 13:58:42 -0700283 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
284 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700285 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600286 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700287 const boost::system::error_code ec,
288 const boost::container::flat_map<
289 std::string, boost::container::flat_map<
290 std::string, std::vector<std::string>>>
291 &objects) {
292 if (ec)
293 {
Ed Tanous049a0512018-11-01 13:58:42 -0700294 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
295 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700296 return;
297 }
298
Ed Tanousf254ba72018-10-12 13:40:35 -0700299 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700300 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700301 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 {
303 if (connectionGroup.first == connection_name)
304 {
305 // Found the object manager path for this resource.
306 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700307 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600308 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700309 return;
310 }
311 }
312 }
313 },
314 "xyz.openbmc_project.ObjectMapper",
315 "/xyz/openbmc_project/object_mapper",
316 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
317 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700318}
Ed Tanous64530012018-02-06 17:08:16 -0800319
Ed Tanous7c091622019-05-23 11:42:36 -0700320// Uses GetObject to add the object info about the target /enumerate path to
321// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600322// target path, and then continues on enumerating the rest of the tree.
323void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
324{
325 using GetObjectType =
326 std::vector<std::pair<std::string, std::vector<std::string>>>;
327
328 crow::connections::systemBus->async_method_call(
329 [transaction](const boost::system::error_code ec,
330 const GetObjectType &objects) {
331 if (ec)
332 {
333 BMCWEB_LOG_ERROR << "GetObject for path "
334 << transaction->objectPath
335 << " failed with code " << ec;
336 return;
337 }
338
339 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
340 << " has " << objects.size() << " entries";
341 if (!objects.empty())
342 {
343 transaction->subtree->emplace_back(transaction->objectPath,
344 objects);
345 }
346
347 // Map indicating connection name, and the path where the object
348 // manager exists
349 boost::container::flat_map<std::string, std::string> connections;
350
351 for (const auto &object : *(transaction->subtree))
352 {
353 for (const auto &connection : object.second)
354 {
355 std::string &objectManagerPath =
356 connections[connection.first];
357 for (const auto &interface : connection.second)
358 {
359 BMCWEB_LOG_DEBUG << connection.first
360 << " has interface " << interface;
361 if (interface == "org.freedesktop.DBus.ObjectManager")
362 {
363 BMCWEB_LOG_DEBUG << "found object manager path "
364 << object.first;
365 objectManagerPath = object.first;
366 }
367 }
368 }
369 }
370 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
371
372 for (const auto &connection : connections)
373 {
Ed Tanous7c091622019-05-23 11:42:36 -0700374 // If we already know where the object manager is, we don't
375 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600376 // getManagedObjects
377 if (!connection.second.empty())
378 {
379 getManagedObjectsForEnumerate(
380 transaction->objectPath, connection.second,
381 connection.first, transaction);
382 }
383 else
384 {
Ed Tanous7c091622019-05-23 11:42:36 -0700385 // otherwise we need to find the object manager path
386 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600387 findObjectManagerPathForEnumerate(
388 transaction->objectPath, connection.first, transaction);
389 }
390 }
391 },
392 "xyz.openbmc_project.ObjectMapper",
393 "/xyz/openbmc_project/object_mapper",
394 "xyz.openbmc_project.ObjectMapper", "GetObject",
395 transaction->objectPath, std::array<const char *, 0>());
396}
Ed Tanous64530012018-02-06 17:08:16 -0800397
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700398// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399struct InProgressActionData
400{
401 InProgressActionData(crow::Response &res) : res(res){};
402 ~InProgressActionData()
403 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600404 // Methods could have been called across different owners
405 // and interfaces, where some calls failed and some passed.
406 //
407 // The rules for this are:
408 // * if no method was called - error
409 // * if a method failed and none passed - error
410 // (converse: if at least one method passed - OK)
411 // * for the method output:
412 // * if output processing didn't fail, return the data
413
414 // Only deal with method returns if nothing failed earlier
415 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600417 if (!methodPassed)
418 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500419 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600420 {
421 setErrorResponse(res, boost::beast::http::status::not_found,
422 methodNotFoundDesc, notFoundMsg);
423 }
424 }
425 else
426 {
427 if (outputFailed)
428 {
429 setErrorResponse(
430 res, boost::beast::http::status::internal_server_error,
431 "Method output failure", methodOutputFailedMsg);
432 }
433 else
434 {
435 res.jsonValue = {{"status", "ok"},
436 {"message", "200 OK"},
437 {"data", methodResponse}};
438 }
439 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600441
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700443 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700444
Matt Spinler6db06242018-12-11 11:21:22 -0600445 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600447 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
448 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 }
450 crow::Response &res;
451 std::string path;
452 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600453 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600454 bool methodPassed = false;
455 bool methodFailed = false;
456 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600457 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600458 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700460};
461
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462std::vector<std::string> dbusArgSplit(const std::string &string)
463{
464 std::vector<std::string> ret;
465 if (string.empty())
466 {
467 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700468 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469 ret.push_back("");
470 int containerDepth = 0;
471
472 for (std::string::const_iterator character = string.begin();
473 character != string.end(); character++)
474 {
475 ret.back() += *character;
476 switch (*character)
477 {
478 case ('a'):
479 break;
480 case ('('):
481 case ('{'):
482 containerDepth++;
483 break;
484 case ('}'):
485 case (')'):
486 containerDepth--;
487 if (containerDepth == 0)
488 {
489 if (character + 1 != string.end())
490 {
491 ret.push_back("");
492 }
493 }
494 break;
495 default:
496 if (containerDepth == 0)
497 {
498 if (character + 1 != string.end())
499 {
500 ret.push_back("");
501 }
502 }
503 break;
504 }
505 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600506
507 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700508}
509
Ed Tanousd76323e2018-08-07 14:35:40 -0700510int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 const nlohmann::json &input_json)
512{
513 int r = 0;
514 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
515 << " to type: " << arg_type;
516 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700517
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 // Assume a single object for now.
519 const nlohmann::json *j = &input_json;
520 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700521
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700522 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 {
524 // If we are decoding multiple objects, grab the pointer to the
525 // iterator, and increment it for the next loop
526 if (argTypes.size() > 1)
527 {
528 if (jIt == input_json.end())
529 {
530 return -2;
531 }
532 j = &*jIt;
533 jIt++;
534 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700535 const int64_t *intValue = j->get_ptr<const int64_t *>();
536 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
537 const std::string *stringValue = j->get_ptr<const std::string *>();
538 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 const bool *b = j->get_ptr<const bool *>();
540 int64_t v = 0;
541 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700542
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 // Do some basic type conversions that make sense. uint can be
544 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700545 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700547 v = static_cast<int64_t>(*uintValue);
548 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700550 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 d = static_cast<double>(*uintValue);
553 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700555 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700557 d = static_cast<double>(*intValue);
558 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700559 }
560
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700563 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 {
565 return -1;
566 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000567 r = sd_bus_message_append_basic(m, argCode[0],
568 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 if (r < 0)
570 {
571 return r;
572 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700573 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700574 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700576 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 {
578 return -1;
579 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700580 int32_t i = static_cast<int32_t>(*intValue);
581 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 if (r < 0)
583 {
584 return r;
585 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700586 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700587 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588 {
589 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700590 int boolInt = false;
591 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700593 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 }
595 else if (b != nullptr)
596 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600597 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700599 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700601 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700602 }
603 else
604 {
605 return -1;
606 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700607 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 if (r < 0)
609 {
610 return r;
611 }
612 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700613 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
617 return -1;
618 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700619 int16_t n = static_cast<int16_t>(*intValue);
620 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 if (r < 0)
622 {
623 return r;
624 }
625 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700628 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 {
630 return -1;
631 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700632 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 if (r < 0)
634 {
635 return r;
636 }
637 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700638 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700640 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 {
642 return -1;
643 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700644 uint8_t y = static_cast<uint8_t>(*uintValue);
645 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700646 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700647 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700649 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
651 return -1;
652 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700653 uint16_t q = static_cast<uint16_t>(*uintValue);
654 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700656 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700658 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 {
660 return -1;
661 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700662 uint32_t u = static_cast<uint32_t>(*uintValue);
663 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700665 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700667 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 {
669 return -1;
670 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700671 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700675 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700677 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700679 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700681 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700682 if (r < 0)
683 {
684 return r;
685 }
686
687 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
688 ++it)
689 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700690 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 if (r < 0)
692 {
693 return r;
694 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700695 }
696 sd_bus_message_close_container(m);
697 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700698 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700699 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700700 std::string containedType = argCode.substr(1);
701 BMCWEB_LOG_DEBUG << "variant type: " << argCode
702 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 if (r < 0)
706 {
707 return r;
708 }
709
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700710 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 if (r < 0)
712 {
713 return r;
714 }
715
716 r = sd_bus_message_close_container(m);
717 if (r < 0)
718 {
719 return r;
720 }
721 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700722 else if (boost::starts_with(argCode, "(") &&
723 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700724 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700725 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700726 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700727 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800728 if (r < 0)
729 {
730 return r;
731 }
732
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000734 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 {
736 if (it == j->end())
737 {
738 return -1;
739 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000740 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741 if (r < 0)
742 {
743 return r;
744 }
745 it++;
746 }
747 r = sd_bus_message_close_container(m);
748 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700749 else if (boost::starts_with(argCode, "{") &&
750 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700752 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700753 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700754 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800755 if (r < 0)
756 {
757 return r;
758 }
759
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700760 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700761 if (codes.size() != 2)
762 {
763 return -1;
764 }
765 const std::string &key_type = codes[0];
766 const std::string &value_type = codes[1];
767 for (auto it : j->items())
768 {
769 r = convertJsonToDbus(m, key_type, it.key());
770 if (r < 0)
771 {
772 return r;
773 }
774
775 r = convertJsonToDbus(m, value_type, it.value());
776 if (r < 0)
777 {
778 return r;
779 }
780 }
781 r = sd_bus_message_close_container(m);
782 }
783 else
784 {
785 return -2;
786 }
787 if (r < 0)
788 {
789 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700790 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700791
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 if (argTypes.size() > 1)
793 {
794 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700795 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700796 }
Matt Spinler127ea542019-01-14 11:04:28 -0600797
798 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700799}
800
Matt Spinlerd22a7132019-01-14 12:14:30 -0600801template <typename T>
802int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
803 nlohmann::json &data)
804{
805 T value;
806
807 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
808 if (r < 0)
809 {
810 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
811 << " failed!";
812 return r;
813 }
814
815 data = value;
816 return 0;
817}
818
Matt Spinler16caaee2019-01-15 11:40:34 -0600819int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600820 sdbusplus::message::message &m, nlohmann::json &response);
821
822int readDictEntryFromMessage(const std::string &typeCode,
823 sdbusplus::message::message &m,
824 nlohmann::json &object)
825{
826 std::vector<std::string> types = dbusArgSplit(typeCode);
827 if (types.size() != 2)
828 {
829 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
830 << types.size();
831 return -1;
832 }
833
834 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
835 typeCode.c_str());
836 if (r < 0)
837 {
838 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
839 return r;
840 }
841
842 nlohmann::json key;
843 r = convertDBusToJSON(types[0], m, key);
844 if (r < 0)
845 {
846 return r;
847 }
848
849 const std::string *keyPtr = key.get_ptr<const std::string *>();
850 if (keyPtr == nullptr)
851 {
852 // json doesn't support non-string keys. If we hit this condition,
853 // convert the result to a string so we can proceed
854 key = key.dump();
855 keyPtr = key.get_ptr<const std::string *>();
Ed Tanous7c091622019-05-23 11:42:36 -0700856 // in theory this can't fail now, but lets be paranoid about it
857 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600858 if (keyPtr == nullptr)
859 {
860 return -1;
861 }
862 }
863 nlohmann::json &value = object[*keyPtr];
864
865 r = convertDBusToJSON(types[1], m, value);
866 if (r < 0)
867 {
868 return r;
869 }
870
871 r = sd_bus_message_exit_container(m.get());
872 if (r < 0)
873 {
874 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
875 return r;
876 }
877
878 return 0;
879}
880
881int readArrayFromMessage(const std::string &typeCode,
882 sdbusplus::message::message &m, nlohmann::json &data)
883{
884 if (typeCode.size() < 2)
885 {
886 BMCWEB_LOG_ERROR << "Type code " << typeCode
887 << " too small for an array";
888 return -1;
889 }
890
891 std::string containedType = typeCode.substr(1);
892
893 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
894 containedType.c_str());
895 if (r < 0)
896 {
897 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
898 << r;
899 return r;
900 }
901
902 bool dict = boost::starts_with(containedType, "{") &&
903 boost::ends_with(containedType, "}");
904
905 if (dict)
906 {
907 // Remove the { }
908 containedType = containedType.substr(1, containedType.size() - 2);
909 data = nlohmann::json::object();
910 }
911 else
912 {
913 data = nlohmann::json::array();
914 }
915
916 while (true)
917 {
918 r = sd_bus_message_at_end(m.get(), false);
919 if (r < 0)
920 {
921 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
922 return r;
923 }
924
925 if (r > 0)
926 {
927 break;
928 }
929
930 // Dictionaries are only ever seen in an array
931 if (dict)
932 {
933 r = readDictEntryFromMessage(containedType, m, data);
934 if (r < 0)
935 {
936 return r;
937 }
938 }
939 else
940 {
941 data.push_back(nlohmann::json());
942
943 r = convertDBusToJSON(containedType, m, data.back());
944 if (r < 0)
945 {
946 return r;
947 }
948 }
949 }
950
951 r = sd_bus_message_exit_container(m.get());
952 if (r < 0)
953 {
954 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
955 return r;
956 }
957
958 return 0;
959}
960
Matt Spinler75c6c672019-01-14 13:01:46 -0600961int readStructFromMessage(const std::string &typeCode,
962 sdbusplus::message::message &m, nlohmann::json &data)
963{
964 if (typeCode.size() < 3)
965 {
966 BMCWEB_LOG_ERROR << "Type code " << typeCode
967 << " too small for a struct";
968 return -1;
969 }
970
971 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
972 std::vector<std::string> types = dbusArgSplit(containedTypes);
973
974 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
975 containedTypes.c_str());
976 if (r < 0)
977 {
978 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
979 << r;
980 return r;
981 }
982
983 for (const std::string &type : types)
984 {
985 data.push_back(nlohmann::json());
986 r = convertDBusToJSON(type, m, data.back());
987 if (r < 0)
988 {
989 return r;
990 }
991 }
992
993 r = sd_bus_message_exit_container(m.get());
994 if (r < 0)
995 {
996 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
997 return r;
998 }
999 return 0;
1000}
1001
Matt Spinler89c19702019-01-14 13:13:00 -06001002int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1003{
1004 const char *containerType;
1005 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1006 if (r < 0)
1007 {
1008 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1009 return r;
1010 }
1011
1012 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1013 containerType);
1014 if (r < 0)
1015 {
1016 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1017 << r;
1018 return r;
1019 }
1020
1021 r = convertDBusToJSON(containerType, m, data);
1022 if (r < 0)
1023 {
1024 return r;
1025 }
1026
1027 r = sd_bus_message_exit_container(m.get());
1028 if (r < 0)
1029 {
1030 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1031 return r;
1032 }
1033
1034 return 0;
1035}
1036
Matt Spinler6df8f992019-01-14 12:47:47 -06001037int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001038 sdbusplus::message::message &m, nlohmann::json &response)
1039{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001040 int r = 0;
1041 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1042
Matt Spinlerd22a7132019-01-14 12:14:30 -06001043 for (const std::string &typeCode : returnTypes)
1044 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001045 nlohmann::json *thisElement = &response;
1046 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001047 {
1048 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001049 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001050 }
1051
1052 if (typeCode == "s")
1053 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001054 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001055 if (r < 0)
1056 {
1057 return r;
1058 }
1059 }
1060 else if (typeCode == "g")
1061 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001062 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001063 if (r < 0)
1064 {
1065 return r;
1066 }
1067 }
1068 else if (typeCode == "o")
1069 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001070 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001071 if (r < 0)
1072 {
1073 return r;
1074 }
1075 }
1076 else if (typeCode == "b")
1077 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001078 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001079 if (r < 0)
1080 {
1081 return r;
1082 }
1083
Matt Spinlerf39420c2019-01-30 12:57:18 -06001084 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001085 }
1086 else if (typeCode == "u")
1087 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001088 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001089 if (r < 0)
1090 {
1091 return r;
1092 }
1093 }
1094 else if (typeCode == "i")
1095 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001096 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001097 if (r < 0)
1098 {
1099 return r;
1100 }
1101 }
1102 else if (typeCode == "x")
1103 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001104 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001105 if (r < 0)
1106 {
1107 return r;
1108 }
1109 }
1110 else if (typeCode == "t")
1111 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001112 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001113 if (r < 0)
1114 {
1115 return r;
1116 }
1117 }
1118 else if (typeCode == "n")
1119 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001120 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001121 if (r < 0)
1122 {
1123 return r;
1124 }
1125 }
1126 else if (typeCode == "q")
1127 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001128 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001129 if (r < 0)
1130 {
1131 return r;
1132 }
1133 }
1134 else if (typeCode == "y")
1135 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001136 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001137 if (r < 0)
1138 {
1139 return r;
1140 }
1141 }
1142 else if (typeCode == "d")
1143 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001144 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001145 if (r < 0)
1146 {
1147 return r;
1148 }
1149 }
1150 else if (typeCode == "h")
1151 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001152 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001153 if (r < 0)
1154 {
1155 return r;
1156 }
1157 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001158 else if (boost::starts_with(typeCode, "a"))
1159 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001160 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001161 if (r < 0)
1162 {
1163 return r;
1164 }
1165 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001166 else if (boost::starts_with(typeCode, "(") &&
1167 boost::ends_with(typeCode, ")"))
1168 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001169 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001170 if (r < 0)
1171 {
1172 return r;
1173 }
1174 }
Matt Spinler89c19702019-01-14 13:13:00 -06001175 else if (boost::starts_with(typeCode, "v"))
1176 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001177 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001178 if (r < 0)
1179 {
1180 return r;
1181 }
1182 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001183 else
1184 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001185 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1186 return -2;
1187 }
1188 }
1189
Matt Spinler16caaee2019-01-15 11:40:34 -06001190 return 0;
1191}
1192
1193void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1194 sdbusplus::message::message &m,
1195 const std::string &returnType)
1196{
Matt Spinler39a4e392019-01-15 11:53:13 -06001197 nlohmann::json data;
1198
1199 int r = convertDBusToJSON(returnType, m, data);
1200 if (r < 0)
1201 {
1202 transaction->outputFailed = true;
1203 return;
1204 }
1205
1206 if (data.is_null())
1207 {
1208 return;
1209 }
1210
1211 if (transaction->methodResponse.is_null())
1212 {
1213 transaction->methodResponse = std::move(data);
1214 return;
1215 }
1216
1217 // If they're both dictionaries or arrays, merge into one.
1218 // Otherwise, make the results an array with every result
1219 // an entry. Could also just fail in that case, but it
1220 // seems better to get the data back somehow.
1221
1222 if (transaction->methodResponse.is_object() && data.is_object())
1223 {
1224 for (const auto &obj : data.items())
1225 {
1226 // Note: Will overwrite the data for a duplicate key
1227 transaction->methodResponse.emplace(obj.key(),
1228 std::move(obj.value()));
1229 }
1230 return;
1231 }
1232
1233 if (transaction->methodResponse.is_array() && data.is_array())
1234 {
1235 for (auto &obj : data)
1236 {
1237 transaction->methodResponse.push_back(std::move(obj));
1238 }
1239 return;
1240 }
1241
1242 if (!transaction->convertedToArray)
1243 {
1244 // They are different types. May as well turn them into an array
1245 nlohmann::json j = std::move(transaction->methodResponse);
1246 transaction->methodResponse = nlohmann::json::array();
1247 transaction->methodResponse.push_back(std::move(j));
1248 transaction->methodResponse.push_back(std::move(data));
1249 transaction->convertedToArray = true;
1250 }
1251 else
1252 {
1253 transaction->methodResponse.push_back(std::move(data));
1254 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001255}
1256
Ed Tanousd76323e2018-08-07 14:35:40 -07001257void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001258 const std::string &connectionName)
1259{
1260 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1261 << connectionName;
1262 crow::connections::systemBus->async_method_call(
1263 [transaction, connectionName{std::string(connectionName)}](
1264 const boost::system::error_code ec,
1265 const std::string &introspect_xml) {
1266 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1267 if (ec)
1268 {
1269 BMCWEB_LOG_ERROR
1270 << "Introspect call failed with error: " << ec.message()
1271 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001272 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001273 }
Matt Spinler318bd892019-01-15 09:59:20 -06001274 tinyxml2::XMLDocument doc;
1275
1276 doc.Parse(introspect_xml.data(), introspect_xml.size());
1277 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1278 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 {
Matt Spinler318bd892019-01-15 09:59:20 -06001280 BMCWEB_LOG_ERROR << "XML document failed to parse "
1281 << connectionName << "\n";
1282 return;
1283 }
1284 tinyxml2::XMLElement *interfaceNode =
1285 pRoot->FirstChildElement("interface");
1286 while (interfaceNode != nullptr)
1287 {
1288 const char *thisInterfaceName =
1289 interfaceNode->Attribute("name");
1290 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001291 {
Matt Spinler318bd892019-01-15 09:59:20 -06001292 if (!transaction->interfaceName.empty() &&
1293 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001294 {
Matt Spinler318bd892019-01-15 09:59:20 -06001295 interfaceNode =
1296 interfaceNode->NextSiblingElement("interface");
1297 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001298 }
Matt Spinler318bd892019-01-15 09:59:20 -06001299
1300 tinyxml2::XMLElement *methodNode =
1301 interfaceNode->FirstChildElement("method");
1302 while (methodNode != nullptr)
1303 {
1304 const char *thisMethodName =
1305 methodNode->Attribute("name");
1306 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1307 if (thisMethodName != nullptr &&
1308 thisMethodName == transaction->methodName)
1309 {
1310 BMCWEB_LOG_DEBUG
1311 << "Found method named " << thisMethodName
1312 << " on interface " << thisInterfaceName;
1313 sdbusplus::message::message m =
1314 crow::connections::systemBus->new_method_call(
1315 connectionName.c_str(),
1316 transaction->path.c_str(),
1317 thisInterfaceName,
1318 transaction->methodName.c_str());
1319
1320 tinyxml2::XMLElement *argumentNode =
1321 methodNode->FirstChildElement("arg");
1322
Matt Spinler16caaee2019-01-15 11:40:34 -06001323 std::string returnType;
1324
1325 // Find the output type
1326 while (argumentNode != nullptr)
1327 {
1328 const char *argDirection =
1329 argumentNode->Attribute("direction");
1330 const char *argType =
1331 argumentNode->Attribute("type");
1332 if (argDirection != nullptr &&
1333 argType != nullptr &&
1334 std::string(argDirection) == "out")
1335 {
1336 returnType = argType;
1337 break;
1338 }
1339 argumentNode =
1340 argumentNode->NextSiblingElement("arg");
1341 }
1342
Matt Spinler318bd892019-01-15 09:59:20 -06001343 nlohmann::json::const_iterator argIt =
1344 transaction->arguments.begin();
1345
Matt Spinler16caaee2019-01-15 11:40:34 -06001346 argumentNode = methodNode->FirstChildElement("arg");
1347
Matt Spinler318bd892019-01-15 09:59:20 -06001348 while (argumentNode != nullptr)
1349 {
1350 const char *argDirection =
1351 argumentNode->Attribute("direction");
1352 const char *argType =
1353 argumentNode->Attribute("type");
1354 if (argDirection != nullptr &&
1355 argType != nullptr &&
1356 std::string(argDirection) == "in")
1357 {
1358 if (argIt == transaction->arguments.end())
1359 {
1360 transaction->setErrorStatus(
1361 "Invalid method args");
1362 return;
1363 }
1364 if (convertJsonToDbus(m.get(),
1365 std::string(argType),
1366 *argIt) < 0)
1367 {
1368 transaction->setErrorStatus(
1369 "Invalid method arg type");
1370 return;
1371 }
1372
1373 argIt++;
1374 }
1375 argumentNode =
1376 argumentNode->NextSiblingElement("arg");
1377 }
1378
1379 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001380 m, [transaction, returnType](
1381 boost::system::error_code ec,
1382 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001383 if (ec)
1384 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001385 transaction->methodFailed = true;
Matt Spinler06b1b632019-06-18 16:09:25 -05001386 const sd_bus_error *e = m.get_error();
1387
1388 if (e)
1389 {
1390 setErrorResponse(
1391 transaction->res,
1392 boost::beast::http::status::
1393 bad_request,
1394 e->name, e->message);
1395 }
1396 else
1397 {
1398 setErrorResponse(
1399 transaction->res,
1400 boost::beast::http::status::
1401 bad_request,
1402 "Method call failed",
1403 methodFailedMsg);
1404 }
Matt Spinler318bd892019-01-15 09:59:20 -06001405 return;
1406 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001407 else
1408 {
1409 transaction->methodPassed = true;
1410 }
1411
1412 handleMethodResponse(transaction, m,
1413 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001414 });
1415 break;
1416 }
1417 methodNode = methodNode->NextSiblingElement("method");
1418 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001419 }
Matt Spinler318bd892019-01-15 09:59:20 -06001420 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001421 }
1422 },
1423 connectionName, transaction->path,
1424 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001425}
1426
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001427void handleAction(const crow::Request &req, crow::Response &res,
1428 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001429{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001430 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1431 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001432 nlohmann::json requestDbusData =
1433 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001434
Ed Tanous1abe55e2018-09-05 08:30:59 -07001435 if (requestDbusData.is_discarded())
1436 {
Matt Spinler6db06242018-12-11 11:21:22 -06001437 setErrorResponse(res, boost::beast::http::status::bad_request,
1438 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001439 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001440 return;
1441 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001442 nlohmann::json::iterator data = requestDbusData.find("data");
1443 if (data == requestDbusData.end())
1444 {
Matt Spinler6db06242018-12-11 11:21:22 -06001445 setErrorResponse(res, boost::beast::http::status::bad_request,
1446 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001447 res.end();
1448 return;
1449 }
1450
1451 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001452 {
Matt Spinler6db06242018-12-11 11:21:22 -06001453 setErrorResponse(res, boost::beast::http::status::bad_request,
1454 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001455 res.end();
1456 return;
1457 }
1458 auto transaction = std::make_shared<InProgressActionData>(res);
1459
1460 transaction->path = objectPath;
1461 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001462 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001463 crow::connections::systemBus->async_method_call(
1464 [transaction](
1465 const boost::system::error_code ec,
1466 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001467 &interfaceNames) {
1468 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001469 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001470 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001471 setErrorResponse(transaction->res,
1472 boost::beast::http::status::not_found,
1473 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001474 return;
1475 }
1476
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001477 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1478 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001479
1480 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001481 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001482 {
1483 findActionOnInterface(transaction, object.first);
1484 }
1485 },
1486 "xyz.openbmc_project.ObjectMapper",
1487 "/xyz/openbmc_project/object_mapper",
1488 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1489 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001490}
1491
Matt Spinlerde818812018-12-11 16:39:20 -06001492void handleDelete(const crow::Request &req, crow::Response &res,
1493 const std::string &objectPath)
1494{
1495 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1496
1497 crow::connections::systemBus->async_method_call(
1498 [&res, objectPath](
1499 const boost::system::error_code ec,
1500 const std::vector<std::pair<std::string, std::vector<std::string>>>
1501 &interfaceNames) {
1502 if (ec || interfaceNames.size() <= 0)
1503 {
1504 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001505 setErrorResponse(res,
1506 boost::beast::http::status::method_not_allowed,
1507 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001508 res.end();
1509 return;
1510 }
1511
1512 auto transaction = std::make_shared<InProgressActionData>(res);
1513 transaction->path = objectPath;
1514 transaction->methodName = "Delete";
1515 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1516
1517 for (const std::pair<std::string, std::vector<std::string>>
1518 &object : interfaceNames)
1519 {
1520 findActionOnInterface(transaction, object.first);
1521 }
1522 },
1523 "xyz.openbmc_project.ObjectMapper",
1524 "/xyz/openbmc_project/object_mapper",
1525 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1526 std::array<const char *, 0>());
1527}
1528
Ed Tanousf839dfe2018-11-12 11:11:15 -08001529void handleList(crow::Response &res, const std::string &objectPath,
1530 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001531{
1532 crow::connections::systemBus->async_method_call(
1533 [&res](const boost::system::error_code ec,
1534 std::vector<std::string> &objectPaths) {
1535 if (ec)
1536 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001537 setErrorResponse(res, boost::beast::http::status::not_found,
1538 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001539 }
1540 else
1541 {
1542 res.jsonValue = {{"status", "ok"},
1543 {"message", "200 OK"},
1544 {"data", std::move(objectPaths)}};
1545 }
1546 res.end();
1547 },
1548 "xyz.openbmc_project.ObjectMapper",
1549 "/xyz/openbmc_project/object_mapper",
1550 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001551 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001552}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001553
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001554void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001555{
Ed Tanous049a0512018-11-01 13:58:42 -07001556 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1557 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1558
1559 asyncResp->res.jsonValue = {{"message", "200 OK"},
1560 {"status", "ok"},
1561 {"data", nlohmann::json::object()}};
1562
Ed Tanous1abe55e2018-09-05 08:30:59 -07001563 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001564 [objectPath, asyncResp](const boost::system::error_code ec,
1565 GetSubTreeType &object_names) {
1566 auto transaction = std::make_shared<InProgressEnumerateData>(
1567 objectPath, asyncResp);
1568
1569 transaction->subtree =
1570 std::make_shared<GetSubTreeType>(std::move(object_names));
1571
Ed Tanous1abe55e2018-09-05 08:30:59 -07001572 if (ec)
1573 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001574 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1575 << transaction->objectPath;
1576 setErrorResponse(transaction->asyncResp->res,
1577 boost::beast::http::status::not_found,
1578 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001579 return;
1580 }
Ed Tanous64530012018-02-06 17:08:16 -08001581
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001582 // Add the data for the path passed in to the results
1583 // as if GetSubTree returned it, and continue on enumerating
1584 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001585 },
1586 "xyz.openbmc_project.ObjectMapper",
1587 "/xyz/openbmc_project/object_mapper",
1588 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001589 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001590}
Ed Tanous911ac312017-08-15 09:37:42 -07001591
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001592void handleGet(crow::Response &res, std::string &objectPath,
1593 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001594{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001595 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1596 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001597 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001598
Ed Tanous1abe55e2018-09-05 08:30:59 -07001599 std::shared_ptr<std::string> path =
1600 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001601
Ed Tanous1abe55e2018-09-05 08:30:59 -07001602 using GetObjectType =
1603 std::vector<std::pair<std::string, std::vector<std::string>>>;
1604 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001605 [&res, path, propertyName](const boost::system::error_code ec,
1606 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001607 if (ec || object_names.size() <= 0)
1608 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001609 setErrorResponse(res, boost::beast::http::status::not_found,
1610 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001611 res.end();
1612 return;
1613 }
1614 std::shared_ptr<nlohmann::json> response =
1615 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001616 // The mapper should never give us an empty interface names
1617 // list, but check anyway
Ed Tanous1abe55e2018-09-05 08:30:59 -07001618 for (const std::pair<std::string, std::vector<std::string>>
1619 connection : object_names)
1620 {
1621 const std::vector<std::string> &interfaceNames =
1622 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001623
Ed Tanous1abe55e2018-09-05 08:30:59 -07001624 if (interfaceNames.size() <= 0)
1625 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001626 setErrorResponse(res, boost::beast::http::status::not_found,
1627 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001628 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001629 return;
1630 }
1631
1632 for (const std::string &interface : interfaceNames)
1633 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001634 sdbusplus::message::message m =
1635 crow::connections::systemBus->new_method_call(
1636 connection.first.c_str(), path->c_str(),
1637 "org.freedesktop.DBus.Properties", "GetAll");
1638 m.append(interface);
1639 crow::connections::systemBus->async_send(
1640 m, [&res, response,
1641 propertyName](const boost::system::error_code ec,
1642 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001643 if (ec)
1644 {
1645 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1646 << ec;
1647 }
1648 else
1649 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001650 nlohmann::json properties;
1651 int r =
1652 convertDBusToJSON("a{sv}", msg, properties);
1653 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001654 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001655 BMCWEB_LOG_ERROR
1656 << "convertDBusToJSON failed";
1657 }
1658 else
1659 {
1660 for (auto &prop : properties.items())
1661 {
Ed Tanous7c091622019-05-23 11:42:36 -07001662 // if property name is empty, or
1663 // matches our search query, add it
1664 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001665
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001666 if (propertyName->empty())
1667 {
1668 (*response)[prop.key()] =
1669 std::move(prop.value());
1670 }
1671 else if (prop.key() == *propertyName)
1672 {
1673 *response = std::move(prop.value());
1674 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001675 }
1676 }
1677 }
1678 if (response.use_count() == 1)
1679 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001680 if (!propertyName->empty() && response->empty())
1681 {
1682 setErrorResponse(
1683 res,
1684 boost::beast::http::status::not_found,
1685 propNotFoundDesc, notFoundMsg);
1686 }
1687 else
1688 {
1689 res.jsonValue = {{"status", "ok"},
1690 {"message", "200 OK"},
1691 {"data", *response}};
1692 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001693 res.end();
1694 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001695 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001696 }
1697 }
1698 },
1699 "xyz.openbmc_project.ObjectMapper",
1700 "/xyz/openbmc_project/object_mapper",
1701 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1702 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001703}
1704
Ed Tanous1abe55e2018-09-05 08:30:59 -07001705struct AsyncPutRequest
1706{
1707 AsyncPutRequest(crow::Response &res) : res(res)
1708 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001709 }
1710 ~AsyncPutRequest()
1711 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 if (res.jsonValue.empty())
1713 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001714 setErrorResponse(res, boost::beast::http::status::forbidden,
1715 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001716 }
1717
1718 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001719 }
1720
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001721 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001722 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001723 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1724 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001725 }
1726
Ed Tanous1abe55e2018-09-05 08:30:59 -07001727 crow::Response &res;
1728 std::string objectPath;
1729 std::string propertyName;
1730 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001731};
1732
Ed Tanousd76323e2018-08-07 14:35:40 -07001733void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 const std::string &objectPath, const std::string &destProperty)
1735{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001736 if (destProperty.empty())
1737 {
1738 setErrorResponse(res, boost::beast::http::status::forbidden,
1739 forbiddenResDesc, forbiddenMsg);
1740 res.end();
1741 return;
1742 }
1743
Ed Tanous1abe55e2018-09-05 08:30:59 -07001744 nlohmann::json requestDbusData =
1745 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001746
Ed Tanous1abe55e2018-09-05 08:30:59 -07001747 if (requestDbusData.is_discarded())
1748 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001749 setErrorResponse(res, boost::beast::http::status::bad_request,
1750 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001751 res.end();
1752 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001753 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001754
Ed Tanous1abe55e2018-09-05 08:30:59 -07001755 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1756 if (propertyIt == requestDbusData.end())
1757 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001758 setErrorResponse(res, boost::beast::http::status::bad_request,
1759 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001760 res.end();
1761 return;
1762 }
1763 const nlohmann::json &propertySetValue = *propertyIt;
1764 auto transaction = std::make_shared<AsyncPutRequest>(res);
1765 transaction->objectPath = objectPath;
1766 transaction->propertyName = destProperty;
1767 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001768
Ed Tanous1abe55e2018-09-05 08:30:59 -07001769 using GetObjectType =
1770 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001771
Ed Tanous1abe55e2018-09-05 08:30:59 -07001772 crow::connections::systemBus->async_method_call(
1773 [transaction](const boost::system::error_code ec,
1774 const GetObjectType &object_names) {
1775 if (!ec && object_names.size() <= 0)
1776 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001777 setErrorResponse(transaction->res,
1778 boost::beast::http::status::not_found,
1779 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001780 return;
1781 }
Ed Tanous911ac312017-08-15 09:37:42 -07001782
Ed Tanous1abe55e2018-09-05 08:30:59 -07001783 for (const std::pair<std::string, std::vector<std::string>>
1784 connection : object_names)
1785 {
1786 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001787
Ed Tanous1abe55e2018-09-05 08:30:59 -07001788 crow::connections::systemBus->async_method_call(
1789 [connectionName{std::string(connectionName)},
1790 transaction](const boost::system::error_code ec,
1791 const std::string &introspectXml) {
1792 if (ec)
1793 {
1794 BMCWEB_LOG_ERROR
1795 << "Introspect call failed with error: "
1796 << ec.message()
1797 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001798 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001799 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001800 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001801 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001802
Ed Tanous1abe55e2018-09-05 08:30:59 -07001803 doc.Parse(introspectXml.c_str());
1804 tinyxml2::XMLNode *pRoot =
1805 doc.FirstChildElement("node");
1806 if (pRoot == nullptr)
1807 {
1808 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1809 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001810 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001811 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001812 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001813 tinyxml2::XMLElement *ifaceNode =
1814 pRoot->FirstChildElement("interface");
1815 while (ifaceNode != nullptr)
1816 {
1817 const char *interfaceName =
1818 ifaceNode->Attribute("name");
1819 BMCWEB_LOG_DEBUG << "found interface "
1820 << interfaceName;
1821 tinyxml2::XMLElement *propNode =
1822 ifaceNode->FirstChildElement("property");
1823 while (propNode != nullptr)
1824 {
1825 const char *propertyName =
1826 propNode->Attribute("name");
1827 BMCWEB_LOG_DEBUG << "Found property "
1828 << propertyName;
1829 if (propertyName == transaction->propertyName)
1830 {
1831 const char *argType =
1832 propNode->Attribute("type");
1833 if (argType != nullptr)
1834 {
1835 sdbusplus::message::message m =
1836 crow::connections::systemBus
1837 ->new_method_call(
1838 connectionName.c_str(),
1839 transaction->objectPath
1840 .c_str(),
1841 "org.freedesktop.DBus."
1842 "Properties",
1843 "Set");
1844 m.append(interfaceName,
1845 transaction->propertyName);
1846 int r = sd_bus_message_open_container(
1847 m.get(), SD_BUS_TYPE_VARIANT,
1848 argType);
1849 if (r < 0)
1850 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001851 transaction->setErrorStatus(
1852 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001853 return;
1854 }
1855 r = convertJsonToDbus(
1856 m.get(), argType,
1857 transaction->propertyValue);
1858 if (r < 0)
1859 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001860 transaction->setErrorStatus(
1861 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001862 return;
1863 }
1864 r = sd_bus_message_close_container(
1865 m.get());
1866 if (r < 0)
1867 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001868 transaction->setErrorStatus(
1869 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001870 return;
1871 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001872 crow::connections::systemBus
1873 ->async_send(
1874 m,
1875 [transaction](
1876 boost::system::error_code
1877 ec,
1878 sdbusplus::message::message
1879 &m) {
1880 BMCWEB_LOG_DEBUG << "sent";
1881 if (ec)
1882 {
Lei YU97d2a472019-06-11 17:44:27 +08001883 const sd_bus_error *e =
1884 m.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001885 setErrorResponse(
1886 transaction->res,
1887 boost::beast::http::
1888 status::
1889 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001890 (e) ? e->name
1891 : ec.category()
1892 .name(),
1893 (e) ? e->message
1894 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001895 }
1896 else
1897 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001898 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001899 .jsonValue = {
1900 {"status", "ok"},
1901 {"message",
1902 "200 OK"},
1903 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001904 }
1905 });
1906 }
1907 }
1908 propNode =
1909 propNode->NextSiblingElement("property");
1910 }
1911 ifaceNode =
1912 ifaceNode->NextSiblingElement("interface");
1913 }
1914 },
1915 connectionName, transaction->objectPath,
1916 "org.freedesktop.DBus.Introspectable", "Introspect");
1917 }
1918 },
1919 "xyz.openbmc_project.ObjectMapper",
1920 "/xyz/openbmc_project/object_mapper",
1921 "xyz.openbmc_project.ObjectMapper", "GetObject",
1922 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001923}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001924
Ed Tanous049a0512018-11-01 13:58:42 -07001925inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1926 std::string &objectPath)
1927{
Ed Tanous049a0512018-11-01 13:58:42 -07001928
1929 // If accessing a single attribute, fill in and update objectPath,
1930 // otherwise leave destProperty blank
1931 std::string destProperty = "";
1932 const char *attrSeperator = "/attr/";
1933 size_t attrPosition = objectPath.find(attrSeperator);
1934 if (attrPosition != objectPath.npos)
1935 {
1936 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1937 objectPath.length());
1938 objectPath = objectPath.substr(0, attrPosition);
1939 }
1940
1941 if (req.method() == "POST"_method)
1942 {
1943 constexpr const char *actionSeperator = "/action/";
1944 size_t actionPosition = objectPath.find(actionSeperator);
1945 if (actionPosition != objectPath.npos)
1946 {
1947 std::string postProperty =
1948 objectPath.substr((actionPosition + strlen(actionSeperator)),
1949 objectPath.length());
1950 objectPath = objectPath.substr(0, actionPosition);
1951 handleAction(req, res, objectPath, postProperty);
1952 return;
1953 }
1954 }
1955 else if (req.method() == "GET"_method)
1956 {
1957 if (boost::ends_with(objectPath, "/enumerate"))
1958 {
1959 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1960 objectPath.end());
1961 handleEnumerate(res, objectPath);
1962 }
1963 else if (boost::ends_with(objectPath, "/list"))
1964 {
1965 objectPath.erase(objectPath.end() - sizeof("list"),
1966 objectPath.end());
1967 handleList(res, objectPath);
1968 }
1969 else
1970 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001971 // Trim any trailing "/" at the end
1972 if (boost::ends_with(objectPath, "/"))
1973 {
1974 objectPath.pop_back();
1975 handleList(res, objectPath, 1);
1976 }
1977 else
1978 {
1979 handleGet(res, objectPath, destProperty);
1980 }
Ed Tanous049a0512018-11-01 13:58:42 -07001981 }
1982 return;
1983 }
1984 else if (req.method() == "PUT"_method)
1985 {
1986 handlePut(req, res, objectPath, destProperty);
1987 return;
1988 }
Matt Spinlerde818812018-12-11 16:39:20 -06001989 else if (req.method() == "DELETE"_method)
1990 {
1991 handleDelete(req, res, objectPath);
1992 return;
1993 }
Ed Tanous049a0512018-11-01 13:58:42 -07001994
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001995 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1996 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001997 res.end();
1998}
1999
Ed Tanous1abe55e2018-09-05 08:30:59 -07002000template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
2001{
2002 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03002003 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002004 .methods("GET"_method)(
2005 [](const crow::Request &req, crow::Response &res) {
2006 res.jsonValue = {{"busses", {{{"name", "system"}}}},
2007 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002008 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002009 });
2010
2011 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03002012 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002013 .methods("GET"_method)(
2014 [](const crow::Request &req, crow::Response &res) {
2015 auto myCallback = [&res](const boost::system::error_code ec,
2016 std::vector<std::string> &names) {
2017 if (ec)
2018 {
2019 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2020 res.result(
2021 boost::beast::http::status::internal_server_error);
2022 }
2023 else
2024 {
2025 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002026 res.jsonValue = {{"status", "ok"}};
2027 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002028 for (auto &name : names)
2029 {
2030 objectsSub.push_back({{"name", name}});
2031 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002032 }
2033 res.end();
2034 };
2035 crow::connections::systemBus->async_method_call(
2036 std::move(myCallback), "org.freedesktop.DBus", "/",
2037 "org.freedesktop.DBus", "ListNames");
2038 });
2039
2040 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002041 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002042 .methods("GET"_method)(
2043 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002044 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002045 });
2046
2047 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002048 .requires({"Login"})
2049 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2050 const std::string &path) {
2051 std::string objectPath = "/xyz/" + path;
2052 handleDBusUrl(req, res, objectPath);
2053 });
2054
2055 BMCWEB_ROUTE(app, "/xyz/<path>")
2056 .requires({"ConfigureComponents", "ConfigureManager"})
2057 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002058 [](const crow::Request &req, crow::Response &res,
2059 const std::string &path) {
2060 std::string objectPath = "/xyz/" + path;
2061 handleDBusUrl(req, res, objectPath);
2062 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002063
Ed Tanous049a0512018-11-01 13:58:42 -07002064 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002065 .requires({"Login"})
2066 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2067 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002068 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002069 handleDBusUrl(req, res, objectPath);
2070 });
2071
2072 BMCWEB_ROUTE(app, "/org/<path>")
2073 .requires({"ConfigureComponents", "ConfigureManager"})
2074 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002075 [](const crow::Request &req, crow::Response &res,
2076 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002077 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002078 handleDBusUrl(req, res, objectPath);
2079 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002080
Ed Tanous1abe55e2018-09-05 08:30:59 -07002081 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002082 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002083 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2084 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002085 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002086 if (!std::regex_match(dumpId, validFilename))
2087 {
Ed Tanousad18f072018-11-14 14:07:48 -08002088 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002089 res.end();
2090 return;
2091 }
James Feistf6150402019-01-08 10:36:20 -08002092 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002093 "/var/lib/phosphor-debug-collector/dumps");
2094
Ed Tanousad18f072018-11-14 14:07:48 -08002095 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002096
James Feistf6150402019-01-08 10:36:20 -08002097 if (!std::filesystem::exists(loc) ||
2098 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002099 {
Ed Tanousad18f072018-11-14 14:07:48 -08002100 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002101 res.result(boost::beast::http::status::not_found);
2102 res.end();
2103 return;
2104 }
James Feistf6150402019-01-08 10:36:20 -08002105 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002106
Ed Tanous1abe55e2018-09-05 08:30:59 -07002107 for (auto &file : files)
2108 {
2109 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002110 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002111 {
2112 continue;
2113 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002114
Ed Tanous1abe55e2018-09-05 08:30:59 -07002115 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002116
2117 // Assuming only one dump file will be present in the dump id
2118 // directory
2119 std::string dumpFileName = file.path().filename().string();
2120
2121 // Filename should be in alphanumeric, dot and underscore
2122 // Its based on phosphor-debug-collector application dumpfile
2123 // format
2124 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2125 if (!std::regex_match(dumpFileName, dumpFileRegex))
2126 {
2127 BMCWEB_LOG_ERROR << "Invalid dump filename "
2128 << dumpFileName;
2129 res.result(boost::beast::http::status::not_found);
2130 res.end();
2131 return;
2132 }
2133 std::string contentDispositionParam =
2134 "attachment; filename=\"" + dumpFileName + "\"";
2135
2136 res.addHeader("Content-Disposition", contentDispositionParam);
2137
Ed Tanous1abe55e2018-09-05 08:30:59 -07002138 res.body() = {std::istreambuf_iterator<char>(readFile),
2139 std::istreambuf_iterator<char>()};
2140 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002141 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002142 }
2143 res.result(boost::beast::http::status::not_found);
2144 res.end();
2145 return;
2146 });
2147
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002148 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002149 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002150 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002151 const std::string &Connection) {
2152 introspectObjects(Connection, "/",
2153 std::make_shared<bmcweb::AsyncResp>(res));
2154 });
2155
2156 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2157 .methods("GET"_method,
2158 "POST"_method)([](const crow::Request &req,
2159 crow::Response &res,
2160 const std::string &processName,
2161 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002162 std::vector<std::string> strs;
2163 boost::split(strs, requestedPath, boost::is_any_of("/"));
2164 std::string objectPath;
2165 std::string interfaceName;
2166 std::string methodName;
2167 auto it = strs.begin();
2168 if (it == strs.end())
2169 {
2170 objectPath = "/";
2171 }
2172 while (it != strs.end())
2173 {
2174 // Check if segment contains ".". If it does, it must be an
2175 // interface
2176 if (it->find(".") != std::string::npos)
2177 {
2178 break;
Ed Tanous7c091622019-05-23 11:42:36 -07002179 // This check is neccesary as the trailing slash gets
2180 // parsed as part of our <path> specifier above, which
2181 // causes the normal trailing backslash redirector to
2182 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002183 }
2184 else if (!it->empty())
2185 {
2186 objectPath += "/" + *it;
2187 }
2188 it++;
2189 }
2190 if (it != strs.end())
2191 {
2192 interfaceName = *it;
2193 it++;
2194
2195 // after interface, we might have a method name
2196 if (it != strs.end())
2197 {
2198 methodName = *it;
2199 it++;
2200 }
2201 }
2202 if (it != strs.end())
2203 {
Ed Tanous7c091622019-05-23 11:42:36 -07002204 // if there is more levels past the method name, something
2205 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002206 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002207 return;
2208 }
2209 if (interfaceName.empty())
2210 {
Ed Tanous7c091622019-05-23 11:42:36 -07002211 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2212 std::make_shared<bmcweb::AsyncResp>(res);
2213
Ed Tanous1abe55e2018-09-05 08:30:59 -07002214 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002215 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002216 objectPath](const boost::system::error_code ec,
2217 const std::string &introspect_xml) {
2218 if (ec)
2219 {
2220 BMCWEB_LOG_ERROR
2221 << "Introspect call failed with error: "
2222 << ec.message()
2223 << " on process: " << processName
2224 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002225 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002226 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002227 tinyxml2::XMLDocument doc;
2228
2229 doc.Parse(introspect_xml.c_str());
2230 tinyxml2::XMLNode *pRoot =
2231 doc.FirstChildElement("node");
2232 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002233 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002234 BMCWEB_LOG_ERROR << "XML document failed to parse "
2235 << processName << " " << objectPath
2236 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002237 asyncResp->res.jsonValue = {
2238 {"status", "XML parse error"}};
2239 asyncResp->res.result(boost::beast::http::status::
2240 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002241 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002242 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002243
2244 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002245 asyncResp->res.jsonValue = {
2246 {"status", "ok"},
2247 {"bus_name", processName},
2248 {"object_path", objectPath}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002249 nlohmann::json &interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002250 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002251 interfacesArray = nlohmann::json::array();
2252 tinyxml2::XMLElement *interface =
2253 pRoot->FirstChildElement("interface");
2254
2255 while (interface != nullptr)
2256 {
2257 const char *ifaceName =
2258 interface->Attribute("name");
2259 if (ifaceName != nullptr)
2260 {
2261 interfacesArray.push_back(
2262 {{"name", ifaceName}});
2263 }
2264
2265 interface =
2266 interface->NextSiblingElement("interface");
2267 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002268 },
2269 processName, objectPath,
2270 "org.freedesktop.DBus.Introspectable", "Introspect");
2271 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002272 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002273 {
Ed Tanous7c091622019-05-23 11:42:36 -07002274 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2275 std::make_shared<bmcweb::AsyncResp>(res);
2276
Ed Tanous1abe55e2018-09-05 08:30:59 -07002277 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002278 [asyncResp, processName, objectPath,
2279 interfaceName](const boost::system::error_code ec,
2280 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002281 if (ec)
2282 {
2283 BMCWEB_LOG_ERROR
2284 << "Introspect call failed with error: "
2285 << ec.message()
2286 << " on process: " << processName
2287 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002288 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002289 }
Ed Tanous7c091622019-05-23 11:42:36 -07002290 tinyxml2::XMLDocument doc;
2291
2292 doc.Parse(introspect_xml.data(), introspect_xml.size());
2293 tinyxml2::XMLNode *pRoot =
2294 doc.FirstChildElement("node");
2295 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002296 {
Ed Tanous7c091622019-05-23 11:42:36 -07002297 BMCWEB_LOG_ERROR << "XML document failed to parse "
2298 << processName << " " << objectPath
2299 << "\n";
2300 asyncResp->res.result(boost::beast::http::status::
2301 internal_server_error);
2302 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002303 }
Ed Tanous7c091622019-05-23 11:42:36 -07002304 asyncResp->res.jsonValue = {
2305 {"status", "ok"},
2306 {"bus_name", processName},
2307 {"interface", interfaceName},
2308 {"object_path", objectPath}};
2309
2310 nlohmann::json &methodsArray =
2311 asyncResp->res.jsonValue["methods"];
2312 methodsArray = nlohmann::json::array();
2313
2314 nlohmann::json &signalsArray =
2315 asyncResp->res.jsonValue["signals"];
2316 signalsArray = nlohmann::json::array();
2317
2318 nlohmann::json &propertiesObj =
2319 asyncResp->res.jsonValue["properties"];
2320 propertiesObj = nlohmann::json::object();
2321
2322 // if we know we're the only call, build the
2323 // json directly
2324 tinyxml2::XMLElement *interface =
2325 pRoot->FirstChildElement("interface");
2326 while (interface != nullptr)
2327 {
2328 const char *ifaceName =
2329 interface->Attribute("name");
2330
2331 if (ifaceName != nullptr &&
2332 ifaceName == interfaceName)
2333 {
2334 break;
2335 }
2336
2337 interface =
2338 interface->NextSiblingElement("interface");
2339 }
2340 if (interface == nullptr)
2341 {
2342 // if we got to the end of the list and
2343 // never found a match, throw 404
2344 asyncResp->res.result(
2345 boost::beast::http::status::not_found);
2346 return;
2347 }
2348
2349 tinyxml2::XMLElement *methods =
2350 interface->FirstChildElement("method");
2351 while (methods != nullptr)
2352 {
2353 nlohmann::json argsArray = nlohmann::json::array();
2354 tinyxml2::XMLElement *arg =
2355 methods->FirstChildElement("arg");
2356 while (arg != nullptr)
2357 {
2358 nlohmann::json thisArg;
2359 for (const char *fieldName :
2360 std::array<const char *, 3>{
2361 "name", "direction", "type"})
2362 {
2363 const char *fieldValue =
2364 arg->Attribute(fieldName);
2365 if (fieldValue != nullptr)
2366 {
2367 thisArg[fieldName] = fieldValue;
2368 }
2369 }
2370 argsArray.push_back(std::move(thisArg));
2371 arg = arg->NextSiblingElement("arg");
2372 }
2373
2374 const char *name = methods->Attribute("name");
2375 if (name != nullptr)
2376 {
2377 methodsArray.push_back(
2378 {{"name", name},
2379 {"uri", "/bus/system/" + processName +
2380 objectPath + "/" +
2381 interfaceName + "/" + name},
2382 {"args", argsArray}});
2383 }
2384 methods = methods->NextSiblingElement("method");
2385 }
2386 tinyxml2::XMLElement *signals =
2387 interface->FirstChildElement("signal");
2388 while (signals != nullptr)
2389 {
2390 nlohmann::json argsArray = nlohmann::json::array();
2391
2392 tinyxml2::XMLElement *arg =
2393 signals->FirstChildElement("arg");
2394 while (arg != nullptr)
2395 {
2396 const char *name = arg->Attribute("name");
2397 const char *type = arg->Attribute("type");
2398 if (name != nullptr && type != nullptr)
2399 {
2400 argsArray.push_back({
2401 {"name", name},
2402 {"type", type},
2403 });
2404 }
2405 arg = arg->NextSiblingElement("arg");
2406 }
2407 const char *name = signals->Attribute("name");
2408 if (name != nullptr)
2409 {
2410 signalsArray.push_back(
2411 {{"name", name}, {"args", argsArray}});
2412 }
2413
2414 signals = signals->NextSiblingElement("signal");
2415 }
2416
2417 tinyxml2::XMLElement *property =
2418 interface->FirstChildElement("property");
2419 while (property != nullptr)
2420 {
2421 const char *name = property->Attribute("name");
2422 const char *type = property->Attribute("type");
2423 if (type != nullptr && name != nullptr)
2424 {
2425 sdbusplus::message::message m =
2426 crow::connections::systemBus
2427 ->new_method_call(processName.c_str(),
2428 objectPath.c_str(),
2429 "org.freedesktop."
2430 "DBus."
2431 "Properties",
2432 "Get");
2433 m.append(interfaceName, name);
2434 nlohmann::json &propertyItem =
2435 propertiesObj[name];
2436 crow::connections::systemBus->async_send(
2437 m, [&propertyItem, asyncResp](
2438 boost::system::error_code &ec,
2439 sdbusplus::message::message &m) {
2440 if (ec)
2441 {
2442 return;
2443 }
2444
2445 convertDBusToJSON("v", m, propertyItem);
2446 });
2447 }
2448 property = property->NextSiblingElement("property");
2449 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002450 },
2451 processName, objectPath,
2452 "org.freedesktop.DBus.Introspectable", "Introspect");
2453 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002454 else
2455 {
2456 if (req.method() != "POST"_method)
2457 {
2458 res.result(boost::beast::http::status::not_found);
2459 res.end();
2460 return;
2461 }
2462
2463 nlohmann::json requestDbusData =
2464 nlohmann::json::parse(req.body, nullptr, false);
2465
2466 if (requestDbusData.is_discarded())
2467 {
2468 res.result(boost::beast::http::status::bad_request);
2469 res.end();
2470 return;
2471 }
2472 if (!requestDbusData.is_array())
2473 {
2474 res.result(boost::beast::http::status::bad_request);
2475 res.end();
2476 return;
2477 }
2478 auto transaction = std::make_shared<InProgressActionData>(res);
2479
2480 transaction->path = objectPath;
2481 transaction->methodName = methodName;
2482 transaction->arguments = std::move(requestDbusData);
2483
2484 findActionOnInterface(transaction, processName);
2485 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002486 });
2487}
2488} // namespace openbmc_mapper
2489} // namespace crow