blob: da59d2597b382431034871edb8b0d3aec3b8b284 [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
James Feistf6150402019-01-08 10:36:20 -080016#include "filesystem.hpp"
17
Ed Tanous911ac312017-08-15 09:37:42 -070018#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070019#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020
Ed Tanouse3cb5a32018-08-08 14:16:49 -070021#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070022#include <boost/algorithm/string.hpp>
23#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070024#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070025#include <dbus_utility.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070026#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070027#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace openbmc_mapper
32{
Ed Tanousba9f9a62017-10-11 16:40:35 -070033
Matt Spinler3ae4ba72018-12-05 14:01:22 -060034using GetSubTreeType = std::vector<
35 std::pair<std::string,
36 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37
Matt Spinler2ae60092018-12-06 10:35:36 -060038const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060039const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060040const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060041const std::string forbiddenMsg = "403 Forbidden";
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -060042const std::string methodFailedMsg = "500 Method Call Failed";
Matt Spinler6db06242018-12-11 11:21:22 -060043
Matt Spinler2ae60092018-12-06 10:35:36 -060044const std::string notFoundDesc =
45 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060046const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060047const std::string noJsonDesc = "No JSON object could be decoded";
48const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060049const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060050const std::string forbiddenPropDesc =
51 "The specified property cannot be created";
52const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060053
54void setErrorResponse(crow::Response &res, boost::beast::http::status result,
55 const std::string &desc, const std::string &msg)
56{
57 res.result(result);
58 res.jsonValue = {{"data", {{"description", desc}}},
59 {"message", msg},
60 {"status", "error"}};
61}
62
Ed Tanouse3cb5a32018-08-08 14:16:49 -070063void introspectObjects(const std::string &processName,
64 const std::string &objectPath,
65 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070066{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070067 if (transaction->res.jsonValue.is_null())
68 {
69 transaction->res.jsonValue = {{"status", "ok"},
70 {"bus_name", processName},
71 {"objects", nlohmann::json::array()}};
72 }
73
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070075 [transaction, processName{std::string(processName)},
76 objectPath{std::string(objectPath)}](
77 const boost::system::error_code ec,
78 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070079 if (ec)
80 {
81 BMCWEB_LOG_ERROR
82 << "Introspect call failed with error: " << ec.message()
83 << " on process: " << processName << " path: " << objectPath
84 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070085 return;
86 }
87 transaction->res.jsonValue["objects"].push_back(
88 {{"path", objectPath}});
89
90 tinyxml2::XMLDocument doc;
91
92 doc.Parse(introspect_xml.c_str());
93 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
94 if (pRoot == nullptr)
95 {
96 BMCWEB_LOG_ERROR << "XML document failed to parse "
97 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070098 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 else
100 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700101 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
102 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700104 const char *childPath = node->Attribute("name");
105 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 std::string newpath;
108 if (objectPath != "/")
109 {
110 newpath += objectPath;
111 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700112 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700114 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700116
117 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 }
119 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700121 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700123}
Ed Tanous64530012018-02-06 17:08:16 -0800124
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600125void getPropertiesForEnumerate(const std::string &objectPath,
126 const std::string &service,
127 const std::string &interface,
128 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
129{
130 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
131 << service << " " << interface;
132
133 crow::connections::systemBus->async_method_call(
134 [asyncResp, objectPath, service,
135 interface](const boost::system::error_code ec,
136 const std::vector<
137 std::pair<std::string, dbus::utility::DbusVariantType>>
138 &propertiesList) {
139 if (ec)
140 {
141 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
142 << interface << " service " << service
143 << " failed with code " << ec;
144 return;
145 }
146
147 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
148 nlohmann::json &objectJson = dataJson[objectPath];
149 if (objectJson.is_null())
150 {
151 objectJson = nlohmann::json::object();
152 }
153
154 for (const auto &[name, value] : propertiesList)
155 {
156 nlohmann::json &propertyJson = objectJson[name];
157 sdbusplus::message::variant_ns::visit(
158 [&propertyJson](auto &&val) { propertyJson = val; }, value);
159 }
160 },
161 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
162 interface);
163}
164
165// Find any results that weren't picked up by ObjectManagers, to be
166// called after all ObjectManagers are searched for and called.
167void findRemainingObjectsForEnumerate(
168 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
169 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
170{
171 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
172 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
173
174 for (const auto &[path, interface_map] : *subtree)
175 {
176 if (path == objectPath)
177 {
178 // An enumerate does not return the target path's properties
179 continue;
180 }
181 if (dataJson.find(path) == dataJson.end())
182 {
183 for (const auto &[service, interfaces] : interface_map)
184 {
185 for (const auto &interface : interfaces)
186 {
187 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
188 {
189 getPropertiesForEnumerate(path, service, interface,
190 asyncResp);
191 }
192 }
193 }
194 }
195 }
196}
197
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600198struct InProgressEnumerateData
199{
200 InProgressEnumerateData(const std::string &objectPath,
201 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
202 objectPath(objectPath),
203 asyncResp(asyncResp)
204 {
205 }
206
207 ~InProgressEnumerateData()
208 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600209 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600210 }
211
212 const std::string objectPath;
213 std::shared_ptr<GetSubTreeType> subtree;
214 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
215};
216
217void getManagedObjectsForEnumerate(
218 const std::string &object_name, const std::string &object_manager_path,
219 const std::string &connection_name,
220 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221{
Ed Tanous049a0512018-11-01 13:58:42 -0700222 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
223 << " object_manager_path " << object_manager_path
224 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600226 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700227 connection_name](const boost::system::error_code ec,
228 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 if (ec)
230 {
Ed Tanous049a0512018-11-01 13:58:42 -0700231 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600232 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700233 << " failed with code " << ec;
234 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700235 }
Ed Tanous64530012018-02-06 17:08:16 -0800236
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600237 nlohmann::json &dataJson =
238 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700239
240 for (const auto &objectPath : objects)
241 {
242 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243 {
Ed Tanous049a0512018-11-01 13:58:42 -0700244 BMCWEB_LOG_DEBUG << "Reading object "
245 << objectPath.first.str;
246 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 if (objectJson.is_null())
248 {
249 objectJson = nlohmann::json::object();
250 }
251 for (const auto &interface : objectPath.second)
252 {
253 for (const auto &property : interface.second)
254 {
255 nlohmann::json &propertyJson =
256 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700257 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 [&propertyJson](auto &&val) {
259 propertyJson = val;
260 },
261 property.second);
262 }
263 }
264 }
Ed Tanous049a0512018-11-01 13:58:42 -0700265 for (const auto &interface : objectPath.second)
266 {
267 if (interface.first == "org.freedesktop.DBus.ObjectManager")
268 {
269 getManagedObjectsForEnumerate(
270 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600271 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700272 }
273 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 }
275 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700276 connection_name, object_manager_path,
277 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
278}
279
280void findObjectManagerPathForEnumerate(
281 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600282 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700283{
Ed Tanous049a0512018-11-01 13:58:42 -0700284 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
285 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700286 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600287 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700288 const boost::system::error_code ec,
289 const boost::container::flat_map<
290 std::string, boost::container::flat_map<
291 std::string, std::vector<std::string>>>
292 &objects) {
293 if (ec)
294 {
Ed Tanous049a0512018-11-01 13:58:42 -0700295 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
296 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700297 return;
298 }
299
Ed Tanousf254ba72018-10-12 13:40:35 -0700300 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700301 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700302 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700303 {
304 if (connectionGroup.first == connection_name)
305 {
306 // Found the object manager path for this resource.
307 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700308 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600309 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700310 return;
311 }
312 }
313 }
314 },
315 "xyz.openbmc_project.ObjectMapper",
316 "/xyz/openbmc_project/object_mapper",
317 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
318 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700319}
Ed Tanous64530012018-02-06 17:08:16 -0800320
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600321// Uses GetObject to add the object info about the target /enumerate path to the
322// results of GetSubTree, as GetSubTree will not return info for the
323// target path, and then continues on enumerating the rest of the tree.
324void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
325{
326 using GetObjectType =
327 std::vector<std::pair<std::string, std::vector<std::string>>>;
328
329 crow::connections::systemBus->async_method_call(
330 [transaction](const boost::system::error_code ec,
331 const GetObjectType &objects) {
332 if (ec)
333 {
334 BMCWEB_LOG_ERROR << "GetObject for path "
335 << transaction->objectPath
336 << " failed with code " << ec;
337 return;
338 }
339
340 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
341 << " has " << objects.size() << " entries";
342 if (!objects.empty())
343 {
344 transaction->subtree->emplace_back(transaction->objectPath,
345 objects);
346 }
347
348 // Map indicating connection name, and the path where the object
349 // manager exists
350 boost::container::flat_map<std::string, std::string> connections;
351
352 for (const auto &object : *(transaction->subtree))
353 {
354 for (const auto &connection : object.second)
355 {
356 std::string &objectManagerPath =
357 connections[connection.first];
358 for (const auto &interface : connection.second)
359 {
360 BMCWEB_LOG_DEBUG << connection.first
361 << " has interface " << interface;
362 if (interface == "org.freedesktop.DBus.ObjectManager")
363 {
364 BMCWEB_LOG_DEBUG << "found object manager path "
365 << object.first;
366 objectManagerPath = object.first;
367 }
368 }
369 }
370 }
371 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
372
373 for (const auto &connection : connections)
374 {
375 // If we already know where the object manager is, we don't need
376 // to search for it, we can call directly in to
377 // getManagedObjects
378 if (!connection.second.empty())
379 {
380 getManagedObjectsForEnumerate(
381 transaction->objectPath, connection.second,
382 connection.first, transaction);
383 }
384 else
385 {
386 // otherwise we need to find the object manager path before
387 // we can continue
388 findObjectManagerPathForEnumerate(
389 transaction->objectPath, connection.first, transaction);
390 }
391 }
392 },
393 "xyz.openbmc_project.ObjectMapper",
394 "/xyz/openbmc_project/object_mapper",
395 "xyz.openbmc_project.ObjectMapper", "GetObject",
396 transaction->objectPath, std::array<const char *, 0>());
397}
Ed Tanous64530012018-02-06 17:08:16 -0800398
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700399// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700400struct InProgressActionData
401{
402 InProgressActionData(crow::Response &res) : res(res){};
403 ~InProgressActionData()
404 {
Matt Spinler6db06242018-12-11 11:21:22 -0600405 // If still no JSON filled in, then we never found the method.
406 if (res.jsonValue.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 {
Matt Spinler6db06242018-12-11 11:21:22 -0600408 setErrorResponse(res, boost::beast::http::status::not_found,
409 methodNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 }
411 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700412 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700413
Matt Spinler6db06242018-12-11 11:21:22 -0600414 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700415 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600416 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
417 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 }
419 crow::Response &res;
420 std::string path;
421 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600422 std::string interfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700424};
425
Ed Tanous1abe55e2018-09-05 08:30:59 -0700426std::vector<std::string> dbusArgSplit(const std::string &string)
427{
428 std::vector<std::string> ret;
429 if (string.empty())
430 {
431 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700432 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700433 ret.push_back("");
434 int containerDepth = 0;
435
436 for (std::string::const_iterator character = string.begin();
437 character != string.end(); character++)
438 {
439 ret.back() += *character;
440 switch (*character)
441 {
442 case ('a'):
443 break;
444 case ('('):
445 case ('{'):
446 containerDepth++;
447 break;
448 case ('}'):
449 case (')'):
450 containerDepth--;
451 if (containerDepth == 0)
452 {
453 if (character + 1 != string.end())
454 {
455 ret.push_back("");
456 }
457 }
458 break;
459 default:
460 if (containerDepth == 0)
461 {
462 if (character + 1 != string.end())
463 {
464 ret.push_back("");
465 }
466 }
467 break;
468 }
469 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700470}
471
Ed Tanousd76323e2018-08-07 14:35:40 -0700472int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 const nlohmann::json &input_json)
474{
475 int r = 0;
476 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
477 << " to type: " << arg_type;
478 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700479
Ed Tanous1abe55e2018-09-05 08:30:59 -0700480 // Assume a single object for now.
481 const nlohmann::json *j = &input_json;
482 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700483
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700484 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700485 {
486 // If we are decoding multiple objects, grab the pointer to the
487 // iterator, and increment it for the next loop
488 if (argTypes.size() > 1)
489 {
490 if (jIt == input_json.end())
491 {
492 return -2;
493 }
494 j = &*jIt;
495 jIt++;
496 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700497 const int64_t *intValue = j->get_ptr<const int64_t *>();
498 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
499 const std::string *stringValue = j->get_ptr<const std::string *>();
500 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501 const bool *b = j->get_ptr<const bool *>();
502 int64_t v = 0;
503 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700504
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505 // Do some basic type conversions that make sense. uint can be
506 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700507 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700509 v = static_cast<int64_t>(*uintValue);
510 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700512 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700514 d = static_cast<double>(*uintValue);
515 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700517 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700519 d = static_cast<double>(*intValue);
520 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700521 }
522
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700523 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700525 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700526 {
527 return -1;
528 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700529 r = sd_bus_message_append_basic(m, argCode[0],
530 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 if (r < 0)
532 {
533 return r;
534 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700535 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700536 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700537 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700538 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 {
540 return -1;
541 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700542 int32_t i = static_cast<int32_t>(*intValue);
543 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544 if (r < 0)
545 {
546 return r;
547 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700548 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700549 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 {
551 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 int boolInt = false;
553 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700555 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 }
557 else if (b != nullptr)
558 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700559 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700563 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 }
565 else
566 {
567 return -1;
568 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700569 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 if (r < 0)
571 {
572 return r;
573 }
574 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700575 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700577 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
579 return -1;
580 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 int16_t n = static_cast<int16_t>(*intValue);
582 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 if (r < 0)
584 {
585 return r;
586 }
587 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700588 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700590 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591 {
592 return -1;
593 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700594 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 if (r < 0)
596 {
597 return r;
598 }
599 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700602 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 {
604 return -1;
605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 uint8_t y = static_cast<uint8_t>(*uintValue);
607 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700609 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700611 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 {
613 return -1;
614 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 uint16_t q = static_cast<uint16_t>(*uintValue);
616 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700618 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700620 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
622 return -1;
623 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700624 uint32_t u = static_cast<uint32_t>(*uintValue);
625 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700627 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700629 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 {
631 return -1;
632 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700633 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700635 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700636 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700641 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700643 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 if (r < 0)
645 {
646 return r;
647 }
648
649 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
650 ++it)
651 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700652 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 if (r < 0)
654 {
655 return r;
656 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 }
658 sd_bus_message_close_container(m);
659 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700662 std::string containedType = argCode.substr(1);
663 BMCWEB_LOG_DEBUG << "variant type: " << argCode
664 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700666 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 if (r < 0)
668 {
669 return r;
670 }
671
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 if (r < 0)
674 {
675 return r;
676 }
677
678 r = sd_bus_message_close_container(m);
679 if (r < 0)
680 {
681 return r;
682 }
683 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 else if (boost::starts_with(argCode, "(") &&
685 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700687 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700691 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700692 {
693 if (it == j->end())
694 {
695 return -1;
696 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700697 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 if (r < 0)
699 {
700 return r;
701 }
702 it++;
703 }
704 r = sd_bus_message_close_container(m);
705 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700706 else if (boost::starts_with(argCode, "{") &&
707 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700709 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700711 containedType.c_str());
712 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 if (codes.size() != 2)
714 {
715 return -1;
716 }
717 const std::string &key_type = codes[0];
718 const std::string &value_type = codes[1];
719 for (auto it : j->items())
720 {
721 r = convertJsonToDbus(m, key_type, it.key());
722 if (r < 0)
723 {
724 return r;
725 }
726
727 r = convertJsonToDbus(m, value_type, it.value());
728 if (r < 0)
729 {
730 return r;
731 }
732 }
733 r = sd_bus_message_close_container(m);
734 }
735 else
736 {
737 return -2;
738 }
739 if (r < 0)
740 {
741 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700742 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700743
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 if (argTypes.size() > 1)
745 {
746 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700747 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700748 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700749}
750
Ed Tanousd76323e2018-08-07 14:35:40 -0700751void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752 const std::string &connectionName)
753{
754 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
755 << connectionName;
756 crow::connections::systemBus->async_method_call(
757 [transaction, connectionName{std::string(connectionName)}](
758 const boost::system::error_code ec,
759 const std::string &introspect_xml) {
760 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
761 if (ec)
762 {
763 BMCWEB_LOG_ERROR
764 << "Introspect call failed with error: " << ec.message()
765 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700766 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 else
768 {
769 tinyxml2::XMLDocument doc;
770
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700771 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
773 if (pRoot == nullptr)
774 {
775 BMCWEB_LOG_ERROR << "XML document failed to parse "
776 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700777 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700779 tinyxml2::XMLElement *interfaceNode =
780 pRoot->FirstChildElement("interface");
781 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700782 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700783 const char *thisInterfaceName =
784 interfaceNode->Attribute("name");
785 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 {
Matt Spinlerde818812018-12-11 16:39:20 -0600787 if (!transaction->interfaceName.empty() &&
788 (transaction->interfaceName != thisInterfaceName))
789 {
790 interfaceNode =
791 interfaceNode->NextSiblingElement("interface");
792 continue;
793 }
794
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700795 tinyxml2::XMLElement *methodNode =
796 interfaceNode->FirstChildElement("method");
797 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700799 const char *thisMethodName =
800 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700802 << thisMethodName;
803 if (thisMethodName != nullptr &&
804 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700806 BMCWEB_LOG_DEBUG
807 << "Found method named " << thisMethodName
808 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809 sdbusplus::message::message m =
810 crow::connections::systemBus
811 ->new_method_call(
812 connectionName.c_str(),
813 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700814 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 transaction->methodName.c_str());
816
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700817 tinyxml2::XMLElement *argumentNode =
818 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700820 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 transaction->arguments.begin();
822
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700823 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700825 const char *argDirection =
826 argumentNode->Attribute("direction");
827 const char *argType =
828 argumentNode->Attribute("type");
829 if (argDirection != nullptr &&
830 argType != nullptr &&
831 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700833 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 transaction->arguments.end())
835 {
Matt Spinler6db06242018-12-11 11:21:22 -0600836 transaction->setErrorStatus(
837 "Invalid method args");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 return;
839 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700840 if (convertJsonToDbus(
841 m.get(), std::string(argType),
842 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 {
Matt Spinler6db06242018-12-11 11:21:22 -0600844 transaction->setErrorStatus(
845 "Invalid method arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 return;
847 }
848
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700849 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700850 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700851 argumentNode =
AppaRao Puli4d72dcc2018-12-26 20:26:22 +0530852 argumentNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700854
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 crow::connections::systemBus->async_send(
856 m, [transaction](
857 boost::system::error_code ec,
858 sdbusplus::message::message &m) {
859 if (ec)
860 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600861 setErrorResponse(
862 transaction->res,
863 boost::beast::http::status::
864 internal_server_error,
865 "Method call failed",
866 methodFailedMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 return;
868 }
869 transaction->res.jsonValue = {
870 {"status", "ok"},
871 {"message", "200 OK"},
872 {"data", nullptr}};
873 });
874 break;
875 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700876 methodNode =
877 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700879 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700880 interfaceNode =
881 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 }
883 }
884 },
885 connectionName, transaction->path,
886 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700887}
888
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700889void handleAction(const crow::Request &req, crow::Response &res,
890 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700892 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
893 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 nlohmann::json requestDbusData =
895 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700896
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897 if (requestDbusData.is_discarded())
898 {
Matt Spinler6db06242018-12-11 11:21:22 -0600899 setErrorResponse(res, boost::beast::http::status::bad_request,
900 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700901 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 return;
903 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700904 nlohmann::json::iterator data = requestDbusData.find("data");
905 if (data == requestDbusData.end())
906 {
Matt Spinler6db06242018-12-11 11:21:22 -0600907 setErrorResponse(res, boost::beast::http::status::bad_request,
908 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700909 res.end();
910 return;
911 }
912
913 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
Matt Spinler6db06242018-12-11 11:21:22 -0600915 setErrorResponse(res, boost::beast::http::status::bad_request,
916 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700917 res.end();
918 return;
919 }
920 auto transaction = std::make_shared<InProgressActionData>(res);
921
922 transaction->path = objectPath;
923 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700924 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 crow::connections::systemBus->async_method_call(
926 [transaction](
927 const boost::system::error_code ec,
928 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700929 &interfaceNames) {
930 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700931 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700932 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -0600933 setErrorResponse(transaction->res,
934 boost::beast::http::status::not_found,
935 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700936 return;
937 }
938
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700939 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
940 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700941
942 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700943 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700944 {
945 findActionOnInterface(transaction, object.first);
946 }
947 },
948 "xyz.openbmc_project.ObjectMapper",
949 "/xyz/openbmc_project/object_mapper",
950 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
951 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700952}
953
Matt Spinlerde818812018-12-11 16:39:20 -0600954void handleDelete(const crow::Request &req, crow::Response &res,
955 const std::string &objectPath)
956{
957 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
958
959 crow::connections::systemBus->async_method_call(
960 [&res, objectPath](
961 const boost::system::error_code ec,
962 const std::vector<std::pair<std::string, std::vector<std::string>>>
963 &interfaceNames) {
964 if (ec || interfaceNames.size() <= 0)
965 {
966 BMCWEB_LOG_ERROR << "Can't find object";
967 setErrorResponse(res, boost::beast::http::status::not_found,
968 notFoundDesc, notFoundMsg);
969 res.end();
970 return;
971 }
972
973 auto transaction = std::make_shared<InProgressActionData>(res);
974 transaction->path = objectPath;
975 transaction->methodName = "Delete";
976 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
977
978 for (const std::pair<std::string, std::vector<std::string>>
979 &object : interfaceNames)
980 {
981 findActionOnInterface(transaction, object.first);
982 }
983 },
984 "xyz.openbmc_project.ObjectMapper",
985 "/xyz/openbmc_project/object_mapper",
986 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
987 std::array<const char *, 0>());
988}
989
Ed Tanousf839dfe2018-11-12 11:11:15 -0800990void handleList(crow::Response &res, const std::string &objectPath,
991 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992{
993 crow::connections::systemBus->async_method_call(
994 [&res](const boost::system::error_code ec,
995 std::vector<std::string> &objectPaths) {
996 if (ec)
997 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -0600998 setErrorResponse(res, boost::beast::http::status::not_found,
999 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001000 }
1001 else
1002 {
1003 res.jsonValue = {{"status", "ok"},
1004 {"message", "200 OK"},
1005 {"data", std::move(objectPaths)}};
1006 }
1007 res.end();
1008 },
1009 "xyz.openbmc_project.ObjectMapper",
1010 "/xyz/openbmc_project/object_mapper",
1011 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001012 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001013}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001014
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001015void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016{
Ed Tanous049a0512018-11-01 13:58:42 -07001017 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1018 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1019
1020 asyncResp->res.jsonValue = {{"message", "200 OK"},
1021 {"status", "ok"},
1022 {"data", nlohmann::json::object()}};
1023
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001025 [objectPath, asyncResp](const boost::system::error_code ec,
1026 GetSubTreeType &object_names) {
1027 auto transaction = std::make_shared<InProgressEnumerateData>(
1028 objectPath, asyncResp);
1029
1030 transaction->subtree =
1031 std::make_shared<GetSubTreeType>(std::move(object_names));
1032
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 if (ec)
1034 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001035 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1036 << transaction->objectPath;
1037 setErrorResponse(transaction->asyncResp->res,
1038 boost::beast::http::status::not_found,
1039 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040 return;
1041 }
Ed Tanous64530012018-02-06 17:08:16 -08001042
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001043 // Add the data for the path passed in to the results
1044 // as if GetSubTree returned it, and continue on enumerating
1045 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001046 },
1047 "xyz.openbmc_project.ObjectMapper",
1048 "/xyz/openbmc_project/object_mapper",
1049 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001050 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001051}
Ed Tanous911ac312017-08-15 09:37:42 -07001052
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001053void handleGet(crow::Response &res, std::string &objectPath,
1054 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001055{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001056 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1057 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001058 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001059
Ed Tanous1abe55e2018-09-05 08:30:59 -07001060 std::shared_ptr<std::string> path =
1061 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001062
Ed Tanous1abe55e2018-09-05 08:30:59 -07001063 using GetObjectType =
1064 std::vector<std::pair<std::string, std::vector<std::string>>>;
1065 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001066 [&res, path, propertyName](const boost::system::error_code ec,
1067 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001068 if (ec || object_names.size() <= 0)
1069 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001070 setErrorResponse(res, boost::beast::http::status::not_found,
1071 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001072 res.end();
1073 return;
1074 }
1075 std::shared_ptr<nlohmann::json> response =
1076 std::make_shared<nlohmann::json>(nlohmann::json::object());
1077 // The mapper should never give us an empty interface names list,
1078 // but check anyway
1079 for (const std::pair<std::string, std::vector<std::string>>
1080 connection : object_names)
1081 {
1082 const std::vector<std::string> &interfaceNames =
1083 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001084
Ed Tanous1abe55e2018-09-05 08:30:59 -07001085 if (interfaceNames.size() <= 0)
1086 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001087 setErrorResponse(res, boost::beast::http::status::not_found,
1088 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001089 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001090 return;
1091 }
1092
1093 for (const std::string &interface : interfaceNames)
1094 {
1095 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001096 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001097 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001098 const std::vector<std::pair<
1099 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001100 &properties) {
1101 if (ec)
1102 {
1103 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1104 << ec;
1105 }
1106 else
1107 {
James Feist5b4aa862018-08-16 14:07:01 -07001108 for (const std::pair<
1109 std::string,
1110 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001111 &property : properties)
1112 {
1113 // if property name is empty, or matches our
1114 // search query, add it to the response json
1115
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001116 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001117 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001118 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001119 [&response, &property](auto &&val) {
1120 (*response)[property.first] =
1121 val;
1122 },
1123 property.second);
1124 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001125 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001126 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001127 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001128 [&response](auto &&val) {
1129 (*response) = val;
1130 },
1131 property.second);
1132 }
1133 }
1134 }
1135 if (response.use_count() == 1)
1136 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001137 if (!propertyName->empty() && response->empty())
1138 {
1139 setErrorResponse(
1140 res,
1141 boost::beast::http::status::not_found,
1142 propNotFoundDesc, notFoundMsg);
1143 }
1144 else
1145 {
1146 res.jsonValue = {{"status", "ok"},
1147 {"message", "200 OK"},
1148 {"data", *response}};
1149 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 res.end();
1151 }
1152 },
1153 connection.first, *path,
1154 "org.freedesktop.DBus.Properties", "GetAll", interface);
1155 }
1156 }
1157 },
1158 "xyz.openbmc_project.ObjectMapper",
1159 "/xyz/openbmc_project/object_mapper",
1160 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1161 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001162}
1163
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164struct AsyncPutRequest
1165{
1166 AsyncPutRequest(crow::Response &res) : res(res)
1167 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001168 }
1169 ~AsyncPutRequest()
1170 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001171 if (res.jsonValue.empty())
1172 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001173 setErrorResponse(res, boost::beast::http::status::forbidden,
1174 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001175 }
1176
1177 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001178 }
1179
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001180 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001181 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001182 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1183 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001184 }
1185
Ed Tanous1abe55e2018-09-05 08:30:59 -07001186 crow::Response &res;
1187 std::string objectPath;
1188 std::string propertyName;
1189 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001190};
1191
Ed Tanousd76323e2018-08-07 14:35:40 -07001192void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001193 const std::string &objectPath, const std::string &destProperty)
1194{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001195 if (destProperty.empty())
1196 {
1197 setErrorResponse(res, boost::beast::http::status::forbidden,
1198 forbiddenResDesc, forbiddenMsg);
1199 res.end();
1200 return;
1201 }
1202
Ed Tanous1abe55e2018-09-05 08:30:59 -07001203 nlohmann::json requestDbusData =
1204 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001205
Ed Tanous1abe55e2018-09-05 08:30:59 -07001206 if (requestDbusData.is_discarded())
1207 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001208 setErrorResponse(res, boost::beast::http::status::bad_request,
1209 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001210 res.end();
1211 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001212 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001213
Ed Tanous1abe55e2018-09-05 08:30:59 -07001214 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1215 if (propertyIt == requestDbusData.end())
1216 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001217 setErrorResponse(res, boost::beast::http::status::bad_request,
1218 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001219 res.end();
1220 return;
1221 }
1222 const nlohmann::json &propertySetValue = *propertyIt;
1223 auto transaction = std::make_shared<AsyncPutRequest>(res);
1224 transaction->objectPath = objectPath;
1225 transaction->propertyName = destProperty;
1226 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001227
Ed Tanous1abe55e2018-09-05 08:30:59 -07001228 using GetObjectType =
1229 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001230
Ed Tanous1abe55e2018-09-05 08:30:59 -07001231 crow::connections::systemBus->async_method_call(
1232 [transaction](const boost::system::error_code ec,
1233 const GetObjectType &object_names) {
1234 if (!ec && object_names.size() <= 0)
1235 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001236 setErrorResponse(transaction->res,
1237 boost::beast::http::status::not_found,
1238 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001239 return;
1240 }
Ed Tanous911ac312017-08-15 09:37:42 -07001241
Ed Tanous1abe55e2018-09-05 08:30:59 -07001242 for (const std::pair<std::string, std::vector<std::string>>
1243 connection : object_names)
1244 {
1245 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001246
Ed Tanous1abe55e2018-09-05 08:30:59 -07001247 crow::connections::systemBus->async_method_call(
1248 [connectionName{std::string(connectionName)},
1249 transaction](const boost::system::error_code ec,
1250 const std::string &introspectXml) {
1251 if (ec)
1252 {
1253 BMCWEB_LOG_ERROR
1254 << "Introspect call failed with error: "
1255 << ec.message()
1256 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001257 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001258 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001259 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001261
Ed Tanous1abe55e2018-09-05 08:30:59 -07001262 doc.Parse(introspectXml.c_str());
1263 tinyxml2::XMLNode *pRoot =
1264 doc.FirstChildElement("node");
1265 if (pRoot == nullptr)
1266 {
1267 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1268 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001269 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001270 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001271 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001272 tinyxml2::XMLElement *ifaceNode =
1273 pRoot->FirstChildElement("interface");
1274 while (ifaceNode != nullptr)
1275 {
1276 const char *interfaceName =
1277 ifaceNode->Attribute("name");
1278 BMCWEB_LOG_DEBUG << "found interface "
1279 << interfaceName;
1280 tinyxml2::XMLElement *propNode =
1281 ifaceNode->FirstChildElement("property");
1282 while (propNode != nullptr)
1283 {
1284 const char *propertyName =
1285 propNode->Attribute("name");
1286 BMCWEB_LOG_DEBUG << "Found property "
1287 << propertyName;
1288 if (propertyName == transaction->propertyName)
1289 {
1290 const char *argType =
1291 propNode->Attribute("type");
1292 if (argType != nullptr)
1293 {
1294 sdbusplus::message::message m =
1295 crow::connections::systemBus
1296 ->new_method_call(
1297 connectionName.c_str(),
1298 transaction->objectPath
1299 .c_str(),
1300 "org.freedesktop.DBus."
1301 "Properties",
1302 "Set");
1303 m.append(interfaceName,
1304 transaction->propertyName);
1305 int r = sd_bus_message_open_container(
1306 m.get(), SD_BUS_TYPE_VARIANT,
1307 argType);
1308 if (r < 0)
1309 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001310 transaction->setErrorStatus(
1311 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001312 return;
1313 }
1314 r = convertJsonToDbus(
1315 m.get(), argType,
1316 transaction->propertyValue);
1317 if (r < 0)
1318 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001319 transaction->setErrorStatus(
1320 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001321 return;
1322 }
1323 r = sd_bus_message_close_container(
1324 m.get());
1325 if (r < 0)
1326 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001327 transaction->setErrorStatus(
1328 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001329 return;
1330 }
Ed Tanous911ac312017-08-15 09:37:42 -07001331
Ed Tanous1abe55e2018-09-05 08:30:59 -07001332 crow::connections::systemBus
1333 ->async_send(
1334 m,
1335 [transaction](
1336 boost::system::error_code
1337 ec,
1338 sdbusplus::message::message
1339 &m) {
1340 BMCWEB_LOG_DEBUG << "sent";
1341 if (ec)
1342 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001343 setErrorResponse(
1344 transaction->res,
1345 boost::beast::http::
1346 status::
1347 forbidden,
1348 forbiddenPropDesc,
1349 ec.message());
1350 }
1351 else
1352 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001353 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001354 .jsonValue = {
1355 {"status", "ok"},
1356 {"message",
1357 "200 OK"},
1358 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001359 }
1360 });
1361 }
1362 }
1363 propNode =
1364 propNode->NextSiblingElement("property");
1365 }
1366 ifaceNode =
1367 ifaceNode->NextSiblingElement("interface");
1368 }
1369 },
1370 connectionName, transaction->objectPath,
1371 "org.freedesktop.DBus.Introspectable", "Introspect");
1372 }
1373 },
1374 "xyz.openbmc_project.ObjectMapper",
1375 "/xyz/openbmc_project/object_mapper",
1376 "xyz.openbmc_project.ObjectMapper", "GetObject",
1377 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001378}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001379
Ed Tanous049a0512018-11-01 13:58:42 -07001380inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1381 std::string &objectPath)
1382{
Ed Tanous049a0512018-11-01 13:58:42 -07001383
1384 // If accessing a single attribute, fill in and update objectPath,
1385 // otherwise leave destProperty blank
1386 std::string destProperty = "";
1387 const char *attrSeperator = "/attr/";
1388 size_t attrPosition = objectPath.find(attrSeperator);
1389 if (attrPosition != objectPath.npos)
1390 {
1391 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1392 objectPath.length());
1393 objectPath = objectPath.substr(0, attrPosition);
1394 }
1395
1396 if (req.method() == "POST"_method)
1397 {
1398 constexpr const char *actionSeperator = "/action/";
1399 size_t actionPosition = objectPath.find(actionSeperator);
1400 if (actionPosition != objectPath.npos)
1401 {
1402 std::string postProperty =
1403 objectPath.substr((actionPosition + strlen(actionSeperator)),
1404 objectPath.length());
1405 objectPath = objectPath.substr(0, actionPosition);
1406 handleAction(req, res, objectPath, postProperty);
1407 return;
1408 }
1409 }
1410 else if (req.method() == "GET"_method)
1411 {
1412 if (boost::ends_with(objectPath, "/enumerate"))
1413 {
1414 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1415 objectPath.end());
1416 handleEnumerate(res, objectPath);
1417 }
1418 else if (boost::ends_with(objectPath, "/list"))
1419 {
1420 objectPath.erase(objectPath.end() - sizeof("list"),
1421 objectPath.end());
1422 handleList(res, objectPath);
1423 }
1424 else
1425 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001426 // Trim any trailing "/" at the end
1427 if (boost::ends_with(objectPath, "/"))
1428 {
1429 objectPath.pop_back();
1430 handleList(res, objectPath, 1);
1431 }
1432 else
1433 {
1434 handleGet(res, objectPath, destProperty);
1435 }
Ed Tanous049a0512018-11-01 13:58:42 -07001436 }
1437 return;
1438 }
1439 else if (req.method() == "PUT"_method)
1440 {
1441 handlePut(req, res, objectPath, destProperty);
1442 return;
1443 }
Matt Spinlerde818812018-12-11 16:39:20 -06001444 else if (req.method() == "DELETE"_method)
1445 {
1446 handleDelete(req, res, objectPath);
1447 return;
1448 }
Ed Tanous049a0512018-11-01 13:58:42 -07001449
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001450 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1451 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001452 res.end();
1453}
1454
Ed Tanous1abe55e2018-09-05 08:30:59 -07001455template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1456{
1457 BMCWEB_ROUTE(app, "/bus/")
1458 .methods("GET"_method)(
1459 [](const crow::Request &req, crow::Response &res) {
1460 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1461 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001462 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001463 });
1464
1465 BMCWEB_ROUTE(app, "/bus/system/")
1466 .methods("GET"_method)(
1467 [](const crow::Request &req, crow::Response &res) {
1468 auto myCallback = [&res](const boost::system::error_code ec,
1469 std::vector<std::string> &names) {
1470 if (ec)
1471 {
1472 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1473 res.result(
1474 boost::beast::http::status::internal_server_error);
1475 }
1476 else
1477 {
1478 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001479 res.jsonValue = {{"status", "ok"}};
1480 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001481 for (auto &name : names)
1482 {
1483 objectsSub.push_back({{"name", name}});
1484 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001485 }
1486 res.end();
1487 };
1488 crow::connections::systemBus->async_method_call(
1489 std::move(myCallback), "org.freedesktop.DBus", "/",
1490 "org.freedesktop.DBus", "ListNames");
1491 });
1492
1493 BMCWEB_ROUTE(app, "/list/")
1494 .methods("GET"_method)(
1495 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001496 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001497 });
1498
1499 BMCWEB_ROUTE(app, "/xyz/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06001500 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07001501 [](const crow::Request &req, crow::Response &res,
1502 const std::string &path) {
1503 std::string objectPath = "/xyz/" + path;
1504 handleDBusUrl(req, res, objectPath);
1505 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001506
Ed Tanous049a0512018-11-01 13:58:42 -07001507 BMCWEB_ROUTE(app, "/org/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06001508 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07001509 [](const crow::Request &req, crow::Response &res,
1510 const std::string &path) {
1511 std::string objectPath = "/org/" + path;
1512 handleDBusUrl(req, res, objectPath);
1513 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001514
Ed Tanous1abe55e2018-09-05 08:30:59 -07001515 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1516 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1517 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08001518 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001519 if (!std::regex_match(dumpId, validFilename))
1520 {
Ed Tanousad18f072018-11-14 14:07:48 -08001521 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001522 res.end();
1523 return;
1524 }
James Feistf6150402019-01-08 10:36:20 -08001525 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001526 "/var/lib/phosphor-debug-collector/dumps");
1527
Ed Tanousad18f072018-11-14 14:07:48 -08001528 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529
James Feistf6150402019-01-08 10:36:20 -08001530 if (!std::filesystem::exists(loc) ||
1531 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001532 {
Ed Tanousad18f072018-11-14 14:07:48 -08001533 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534 res.result(boost::beast::http::status::not_found);
1535 res.end();
1536 return;
1537 }
James Feistf6150402019-01-08 10:36:20 -08001538 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08001539
Ed Tanous1abe55e2018-09-05 08:30:59 -07001540 for (auto &file : files)
1541 {
1542 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08001543 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001544 {
1545 continue;
1546 }
1547 res.addHeader("Content-Type", "application/octet-stream");
1548 res.body() = {std::istreambuf_iterator<char>(readFile),
1549 std::istreambuf_iterator<char>()};
1550 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08001551 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001552 }
1553 res.result(boost::beast::http::status::not_found);
1554 res.end();
1555 return;
1556 });
1557
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001558 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001559 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001560 const std::string &Connection) {
1561 introspectObjects(Connection, "/",
1562 std::make_shared<bmcweb::AsyncResp>(res));
1563 });
1564
1565 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1566 .methods("GET"_method,
1567 "POST"_method)([](const crow::Request &req,
1568 crow::Response &res,
1569 const std::string &processName,
1570 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001571 std::vector<std::string> strs;
1572 boost::split(strs, requestedPath, boost::is_any_of("/"));
1573 std::string objectPath;
1574 std::string interfaceName;
1575 std::string methodName;
1576 auto it = strs.begin();
1577 if (it == strs.end())
1578 {
1579 objectPath = "/";
1580 }
1581 while (it != strs.end())
1582 {
1583 // Check if segment contains ".". If it does, it must be an
1584 // interface
1585 if (it->find(".") != std::string::npos)
1586 {
1587 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001588 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001589 // as part of our <path> specifier above, which causes the
1590 // normal trailing backslash redirector to fail.
1591 }
1592 else if (!it->empty())
1593 {
1594 objectPath += "/" + *it;
1595 }
1596 it++;
1597 }
1598 if (it != strs.end())
1599 {
1600 interfaceName = *it;
1601 it++;
1602
1603 // after interface, we might have a method name
1604 if (it != strs.end())
1605 {
1606 methodName = *it;
1607 it++;
1608 }
1609 }
1610 if (it != strs.end())
1611 {
1612 // if there is more levels past the method name, something went
1613 // wrong, return not found
1614 res.result(boost::beast::http::status::not_found);
1615 res.end();
1616 return;
1617 }
1618 if (interfaceName.empty())
1619 {
1620 crow::connections::systemBus->async_method_call(
1621 [&, processName,
1622 objectPath](const boost::system::error_code ec,
1623 const std::string &introspect_xml) {
1624 if (ec)
1625 {
1626 BMCWEB_LOG_ERROR
1627 << "Introspect call failed with error: "
1628 << ec.message()
1629 << " on process: " << processName
1630 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001631 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001632 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001633 tinyxml2::XMLDocument doc;
1634
1635 doc.Parse(introspect_xml.c_str());
1636 tinyxml2::XMLNode *pRoot =
1637 doc.FirstChildElement("node");
1638 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001639 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001640 BMCWEB_LOG_ERROR << "XML document failed to parse "
1641 << processName << " " << objectPath
1642 << "\n";
1643 res.jsonValue = {{"status", "XML parse error"}};
1644 res.result(boost::beast::http::status::
1645 internal_server_error);
1646 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001647 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001648
1649 BMCWEB_LOG_DEBUG << introspect_xml;
1650 res.jsonValue = {{"status", "ok"},
1651 {"bus_name", processName},
1652 {"object_path", objectPath}};
1653 nlohmann::json &interfacesArray =
1654 res.jsonValue["interfaces"];
1655 interfacesArray = nlohmann::json::array();
1656 tinyxml2::XMLElement *interface =
1657 pRoot->FirstChildElement("interface");
1658
1659 while (interface != nullptr)
1660 {
1661 const char *ifaceName =
1662 interface->Attribute("name");
1663 if (ifaceName != nullptr)
1664 {
1665 interfacesArray.push_back(
1666 {{"name", ifaceName}});
1667 }
1668
1669 interface =
1670 interface->NextSiblingElement("interface");
1671 }
1672
Ed Tanous1abe55e2018-09-05 08:30:59 -07001673 res.end();
1674 },
1675 processName, objectPath,
1676 "org.freedesktop.DBus.Introspectable", "Introspect");
1677 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001678 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001679 {
1680 crow::connections::systemBus->async_method_call(
1681 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001682 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001683 const boost::system::error_code ec,
1684 const std::string &introspect_xml) {
1685 if (ec)
1686 {
1687 BMCWEB_LOG_ERROR
1688 << "Introspect call failed with error: "
1689 << ec.message()
1690 << " on process: " << processName
1691 << " path: " << objectPath << "\n";
1692 }
1693 else
1694 {
1695 tinyxml2::XMLDocument doc;
1696
1697 doc.Parse(introspect_xml.c_str());
1698 tinyxml2::XMLNode *pRoot =
1699 doc.FirstChildElement("node");
1700 if (pRoot == nullptr)
1701 {
1702 BMCWEB_LOG_ERROR
1703 << "XML document failed to parse "
1704 << processName << " " << objectPath << "\n";
1705 res.result(boost::beast::http::status::
1706 internal_server_error);
1707 }
1708 else
1709 {
1710 tinyxml2::XMLElement *node =
1711 pRoot->FirstChildElement("node");
1712
1713 // if we know we're the only call, build the
1714 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001715 tinyxml2::XMLElement *interface =
1716 pRoot->FirstChildElement("interface");
1717
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001718 res.jsonValue = {
1719 {"status", "ok"},
1720 {"bus_name", processName},
1721 {"interface", interfaceName},
1722 {"object_path", objectPath},
1723 {"properties", nlohmann::json::object()}};
1724
1725 nlohmann::json &methodsArray =
1726 res.jsonValue["methods"];
1727 methodsArray = nlohmann::json::array();
1728
1729 nlohmann::json &signalsArray =
1730 res.jsonValue["signals"];
1731 signalsArray = nlohmann::json::array();
1732
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733 while (interface != nullptr)
1734 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001735 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001736 interface->Attribute("name");
1737
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001738 if (ifaceName != nullptr &&
1739 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 {
1741 tinyxml2::XMLElement *methods =
1742 interface->FirstChildElement(
1743 "method");
1744 while (methods != nullptr)
1745 {
1746 nlohmann::json argsArray =
1747 nlohmann::json::array();
1748 tinyxml2::XMLElement *arg =
1749 methods->FirstChildElement(
1750 "arg");
1751 while (arg != nullptr)
1752 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001753 nlohmann::json thisArg;
1754 for (const char *fieldName :
1755 std::array<const char *,
1756 3>{"name",
1757 "direction",
1758 "type"})
1759 {
1760 const char *fieldValue =
1761 arg->Attribute(
1762 fieldName);
1763 if (fieldValue != nullptr)
1764 {
1765 thisArg[fieldName] =
1766 fieldValue;
1767 }
1768 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001769 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001770 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001771 arg = arg->NextSiblingElement(
1772 "arg");
1773 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001774
1775 const char *name =
1776 methods->Attribute("name");
1777 if (name != nullptr)
1778 {
1779 methodsArray.push_back(
1780 {{"name", name},
1781 {"uri", "/bus/system/" +
1782 processName +
1783 objectPath +
1784 "/" +
1785 interfaceName +
1786 "/" + name},
1787 {"args", argsArray}});
1788 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001789 methods =
1790 methods->NextSiblingElement(
1791 "method");
1792 }
1793 tinyxml2::XMLElement *signals =
1794 interface->FirstChildElement(
1795 "signal");
1796 while (signals != nullptr)
1797 {
1798 nlohmann::json argsArray =
1799 nlohmann::json::array();
1800
1801 tinyxml2::XMLElement *arg =
1802 signals->FirstChildElement(
1803 "arg");
1804 while (arg != nullptr)
1805 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001806 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001807 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001808 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001809 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001810 if (name != nullptr &&
1811 type != nullptr)
1812 {
1813 argsArray.push_back({
1814 {"name", name},
1815 {"type", type},
1816 });
1817 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001818 arg = arg->NextSiblingElement(
1819 "arg");
1820 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001821 const char *name =
1822 signals->Attribute("name");
1823 if (name != nullptr)
1824 {
1825 signalsArray.push_back(
1826 {{"name", name},
1827 {"args", argsArray}});
1828 }
1829
Ed Tanous1abe55e2018-09-05 08:30:59 -07001830 signals =
1831 signals->NextSiblingElement(
1832 "signal");
1833 }
1834
Ed Tanous1abe55e2018-09-05 08:30:59 -07001835 break;
1836 }
1837
1838 interface = interface->NextSiblingElement(
1839 "interface");
1840 }
1841 if (interface == nullptr)
1842 {
1843 // if we got to the end of the list and
1844 // never found a match, throw 404
1845 res.result(
1846 boost::beast::http::status::not_found);
1847 }
1848 }
1849 }
1850 res.end();
1851 },
1852 processName, objectPath,
1853 "org.freedesktop.DBus.Introspectable", "Introspect");
1854 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001855 else
1856 {
1857 if (req.method() != "POST"_method)
1858 {
1859 res.result(boost::beast::http::status::not_found);
1860 res.end();
1861 return;
1862 }
1863
1864 nlohmann::json requestDbusData =
1865 nlohmann::json::parse(req.body, nullptr, false);
1866
1867 if (requestDbusData.is_discarded())
1868 {
1869 res.result(boost::beast::http::status::bad_request);
1870 res.end();
1871 return;
1872 }
1873 if (!requestDbusData.is_array())
1874 {
1875 res.result(boost::beast::http::status::bad_request);
1876 res.end();
1877 return;
1878 }
1879 auto transaction = std::make_shared<InProgressActionData>(res);
1880
1881 transaction->path = objectPath;
1882 transaction->methodName = methodName;
1883 transaction->arguments = std::move(requestDbusData);
1884
1885 findActionOnInterface(transaction, processName);
1886 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001887 });
1888}
1889} // namespace openbmc_mapper
1890} // namespace crow