blob: 4c68facbbe39e289679c066e5e656021024ca946 [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>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070024#include <experimental/filesystem>
25#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070026#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
30namespace openbmc_mapper
31{
Ed Tanousba9f9a62017-10-11 16:40:35 -070032
Matt Spinler3ae4ba72018-12-05 14:01:22 -060033using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Matt Spinler2ae60092018-12-06 10:35:36 -060037const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060038const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060039const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060040const std::string forbiddenMsg = "403 Forbidden";
Matt Spinler6db06242018-12-11 11:21:22 -060041
Matt Spinler2ae60092018-12-06 10:35:36 -060042const std::string notFoundDesc =
43 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060044const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060045const std::string noJsonDesc = "No JSON object could be decoded";
46const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060047const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060048const std::string forbiddenPropDesc =
49 "The specified property cannot be created";
50const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060051
52void setErrorResponse(crow::Response &res, boost::beast::http::status result,
53 const std::string &desc, const std::string &msg)
54{
55 res.result(result);
56 res.jsonValue = {{"data", {{"description", desc}}},
57 {"message", msg},
58 {"status", "error"}};
59}
60
Ed Tanouse3cb5a32018-08-08 14:16:49 -070061void introspectObjects(const std::string &processName,
62 const std::string &objectPath,
63 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070064{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070065 if (transaction->res.jsonValue.is_null())
66 {
67 transaction->res.jsonValue = {{"status", "ok"},
68 {"bus_name", processName},
69 {"objects", nlohmann::json::array()}};
70 }
71
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070073 [transaction, processName{std::string(processName)},
74 objectPath{std::string(objectPath)}](
75 const boost::system::error_code ec,
76 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 if (ec)
78 {
79 BMCWEB_LOG_ERROR
80 << "Introspect call failed with error: " << ec.message()
81 << " on process: " << processName << " path: " << objectPath
82 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070083 return;
84 }
85 transaction->res.jsonValue["objects"].push_back(
86 {{"path", objectPath}});
87
88 tinyxml2::XMLDocument doc;
89
90 doc.Parse(introspect_xml.c_str());
91 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
92 if (pRoot == nullptr)
93 {
94 BMCWEB_LOG_ERROR << "XML document failed to parse "
95 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070096 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 else
98 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070099 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
100 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 const char *childPath = node->Attribute("name");
103 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 std::string newpath;
106 if (objectPath != "/")
107 {
108 newpath += objectPath;
109 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700110 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700112 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700114
115 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 }
117 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700119 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700121}
Ed Tanous64530012018-02-06 17:08:16 -0800122
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600123void getPropertiesForEnumerate(const std::string &objectPath,
124 const std::string &service,
125 const std::string &interface,
126 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
127{
128 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
129 << service << " " << interface;
130
131 crow::connections::systemBus->async_method_call(
132 [asyncResp, objectPath, service,
133 interface](const boost::system::error_code ec,
134 const std::vector<
135 std::pair<std::string, dbus::utility::DbusVariantType>>
136 &propertiesList) {
137 if (ec)
138 {
139 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
140 << interface << " service " << service
141 << " failed with code " << ec;
142 return;
143 }
144
145 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
146 nlohmann::json &objectJson = dataJson[objectPath];
147 if (objectJson.is_null())
148 {
149 objectJson = nlohmann::json::object();
150 }
151
152 for (const auto &[name, value] : propertiesList)
153 {
154 nlohmann::json &propertyJson = objectJson[name];
155 sdbusplus::message::variant_ns::visit(
156 [&propertyJson](auto &&val) { propertyJson = val; }, value);
157 }
158 },
159 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
160 interface);
161}
162
163// Find any results that weren't picked up by ObjectManagers, to be
164// called after all ObjectManagers are searched for and called.
165void findRemainingObjectsForEnumerate(
166 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
167 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
168{
169 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
170 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
171
172 for (const auto &[path, interface_map] : *subtree)
173 {
174 if (path == objectPath)
175 {
176 // An enumerate does not return the target path's properties
177 continue;
178 }
179 if (dataJson.find(path) == dataJson.end())
180 {
181 for (const auto &[service, interfaces] : interface_map)
182 {
183 for (const auto &interface : interfaces)
184 {
185 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
186 {
187 getPropertiesForEnumerate(path, service, interface,
188 asyncResp);
189 }
190 }
191 }
192 }
193 }
194}
195
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600196struct InProgressEnumerateData
197{
198 InProgressEnumerateData(const std::string &objectPath,
199 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
200 objectPath(objectPath),
201 asyncResp(asyncResp)
202 {
203 }
204
205 ~InProgressEnumerateData()
206 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600207 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600208 }
209
210 const std::string objectPath;
211 std::shared_ptr<GetSubTreeType> subtree;
212 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
213};
214
215void getManagedObjectsForEnumerate(
216 const std::string &object_name, const std::string &object_manager_path,
217 const std::string &connection_name,
218 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219{
Ed Tanous049a0512018-11-01 13:58:42 -0700220 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
221 << " object_manager_path " << object_manager_path
222 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600224 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700225 connection_name](const boost::system::error_code ec,
226 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227 if (ec)
228 {
Ed Tanous049a0512018-11-01 13:58:42 -0700229 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600230 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700231 << " failed with code " << ec;
232 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700233 }
Ed Tanous64530012018-02-06 17:08:16 -0800234
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600235 nlohmann::json &dataJson =
236 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700237
238 for (const auto &objectPath : objects)
239 {
240 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 {
Ed Tanous049a0512018-11-01 13:58:42 -0700242 BMCWEB_LOG_DEBUG << "Reading object "
243 << objectPath.first.str;
244 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 if (objectJson.is_null())
246 {
247 objectJson = nlohmann::json::object();
248 }
249 for (const auto &interface : objectPath.second)
250 {
251 for (const auto &property : interface.second)
252 {
253 nlohmann::json &propertyJson =
254 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700255 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 [&propertyJson](auto &&val) {
257 propertyJson = val;
258 },
259 property.second);
260 }
261 }
262 }
Ed Tanous049a0512018-11-01 13:58:42 -0700263 for (const auto &interface : objectPath.second)
264 {
265 if (interface.first == "org.freedesktop.DBus.ObjectManager")
266 {
267 getManagedObjectsForEnumerate(
268 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600269 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700270 }
271 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 }
273 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700274 connection_name, object_manager_path,
275 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
276}
277
278void findObjectManagerPathForEnumerate(
279 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600280 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700281{
Ed Tanous049a0512018-11-01 13:58:42 -0700282 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
283 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600285 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700286 const boost::system::error_code ec,
287 const boost::container::flat_map<
288 std::string, boost::container::flat_map<
289 std::string, std::vector<std::string>>>
290 &objects) {
291 if (ec)
292 {
Ed Tanous049a0512018-11-01 13:58:42 -0700293 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
294 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700295 return;
296 }
297
Ed Tanousf254ba72018-10-12 13:40:35 -0700298 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700299 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700300 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700301 {
302 if (connectionGroup.first == connection_name)
303 {
304 // Found the object manager path for this resource.
305 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700306 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600307 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700308 return;
309 }
310 }
311 }
312 },
313 "xyz.openbmc_project.ObjectMapper",
314 "/xyz/openbmc_project/object_mapper",
315 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
316 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700317}
Ed Tanous64530012018-02-06 17:08:16 -0800318
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600319// Uses GetObject to add the object info about the target /enumerate path to the
320// results of GetSubTree, as GetSubTree will not return info for the
321// target path, and then continues on enumerating the rest of the tree.
322void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
323{
324 using GetObjectType =
325 std::vector<std::pair<std::string, std::vector<std::string>>>;
326
327 crow::connections::systemBus->async_method_call(
328 [transaction](const boost::system::error_code ec,
329 const GetObjectType &objects) {
330 if (ec)
331 {
332 BMCWEB_LOG_ERROR << "GetObject for path "
333 << transaction->objectPath
334 << " failed with code " << ec;
335 return;
336 }
337
338 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
339 << " has " << objects.size() << " entries";
340 if (!objects.empty())
341 {
342 transaction->subtree->emplace_back(transaction->objectPath,
343 objects);
344 }
345
346 // Map indicating connection name, and the path where the object
347 // manager exists
348 boost::container::flat_map<std::string, std::string> connections;
349
350 for (const auto &object : *(transaction->subtree))
351 {
352 for (const auto &connection : object.second)
353 {
354 std::string &objectManagerPath =
355 connections[connection.first];
356 for (const auto &interface : connection.second)
357 {
358 BMCWEB_LOG_DEBUG << connection.first
359 << " has interface " << interface;
360 if (interface == "org.freedesktop.DBus.ObjectManager")
361 {
362 BMCWEB_LOG_DEBUG << "found object manager path "
363 << object.first;
364 objectManagerPath = object.first;
365 }
366 }
367 }
368 }
369 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
370
371 for (const auto &connection : connections)
372 {
373 // If we already know where the object manager is, we don't need
374 // to search for it, we can call directly in to
375 // getManagedObjects
376 if (!connection.second.empty())
377 {
378 getManagedObjectsForEnumerate(
379 transaction->objectPath, connection.second,
380 connection.first, transaction);
381 }
382 else
383 {
384 // otherwise we need to find the object manager path before
385 // we can continue
386 findObjectManagerPathForEnumerate(
387 transaction->objectPath, connection.first, transaction);
388 }
389 }
390 },
391 "xyz.openbmc_project.ObjectMapper",
392 "/xyz/openbmc_project/object_mapper",
393 "xyz.openbmc_project.ObjectMapper", "GetObject",
394 transaction->objectPath, std::array<const char *, 0>());
395}
Ed Tanous64530012018-02-06 17:08:16 -0800396
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700397// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398struct InProgressActionData
399{
400 InProgressActionData(crow::Response &res) : res(res){};
401 ~InProgressActionData()
402 {
Matt Spinler6db06242018-12-11 11:21:22 -0600403 // If still no JSON filled in, then we never found the method.
404 if (res.jsonValue.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 {
Matt Spinler6db06242018-12-11 11:21:22 -0600406 setErrorResponse(res, boost::beast::http::status::not_found,
407 methodNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700408 }
409 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700410 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700411
Matt Spinler6db06242018-12-11 11:21:22 -0600412 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 {
Matt Spinler6db06242018-12-11 11:21:22 -0600414 setErrorResponse(res, boost::beast::http::status::internal_server_error,
415 desc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 }
417 crow::Response &res;
418 std::string path;
419 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600420 std::string interfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700421 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700422};
423
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424std::vector<std::string> dbusArgSplit(const std::string &string)
425{
426 std::vector<std::string> ret;
427 if (string.empty())
428 {
429 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700430 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700431 ret.push_back("");
432 int containerDepth = 0;
433
434 for (std::string::const_iterator character = string.begin();
435 character != string.end(); character++)
436 {
437 ret.back() += *character;
438 switch (*character)
439 {
440 case ('a'):
441 break;
442 case ('('):
443 case ('{'):
444 containerDepth++;
445 break;
446 case ('}'):
447 case (')'):
448 containerDepth--;
449 if (containerDepth == 0)
450 {
451 if (character + 1 != string.end())
452 {
453 ret.push_back("");
454 }
455 }
456 break;
457 default:
458 if (containerDepth == 0)
459 {
460 if (character + 1 != string.end())
461 {
462 ret.push_back("");
463 }
464 }
465 break;
466 }
467 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700468}
469
Ed Tanousd76323e2018-08-07 14:35:40 -0700470int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471 const nlohmann::json &input_json)
472{
473 int r = 0;
474 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
475 << " to type: " << arg_type;
476 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700477
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 // Assume a single object for now.
479 const nlohmann::json *j = &input_json;
480 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700481
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700482 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700483 {
484 // If we are decoding multiple objects, grab the pointer to the
485 // iterator, and increment it for the next loop
486 if (argTypes.size() > 1)
487 {
488 if (jIt == input_json.end())
489 {
490 return -2;
491 }
492 j = &*jIt;
493 jIt++;
494 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700495 const int64_t *intValue = j->get_ptr<const int64_t *>();
496 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
497 const std::string *stringValue = j->get_ptr<const std::string *>();
498 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 const bool *b = j->get_ptr<const bool *>();
500 int64_t v = 0;
501 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700502
Ed Tanous1abe55e2018-09-05 08:30:59 -0700503 // Do some basic type conversions that make sense. uint can be
504 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700505 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700507 v = static_cast<int64_t>(*uintValue);
508 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700510 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700512 d = static_cast<double>(*uintValue);
513 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700514 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700515 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700517 d = static_cast<double>(*intValue);
518 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700519 }
520
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700521 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700523 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 {
525 return -1;
526 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700527 r = sd_bus_message_append_basic(m, argCode[0],
528 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700529 if (r < 0)
530 {
531 return r;
532 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700533 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700534 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700535 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700536 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700537 {
538 return -1;
539 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700540 int32_t i = static_cast<int32_t>(*intValue);
541 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700542 if (r < 0)
543 {
544 return r;
545 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700546 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700547 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700548 {
549 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700550 int boolInt = false;
551 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700553 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700554 }
555 else if (b != nullptr)
556 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700557 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700559 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 }
563 else
564 {
565 return -1;
566 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700567 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 if (r < 0)
569 {
570 return r;
571 }
572 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700573 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700575 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 {
577 return -1;
578 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700579 int16_t n = static_cast<int16_t>(*intValue);
580 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 if (r < 0)
582 {
583 return r;
584 }
585 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700586 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700588 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 {
590 return -1;
591 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700592 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593 if (r < 0)
594 {
595 return r;
596 }
597 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700598 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 {
602 return -1;
603 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700604 uint8_t y = static_cast<uint8_t>(*uintValue);
605 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700607 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700609 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 {
611 return -1;
612 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700613 uint16_t q = static_cast<uint16_t>(*uintValue);
614 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700616 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700618 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 {
620 return -1;
621 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700622 uint32_t u = static_cast<uint32_t>(*uintValue);
623 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700625 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700627 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 {
629 return -1;
630 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700631 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700633 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700635 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700636 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700641 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 if (r < 0)
643 {
644 return r;
645 }
646
647 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
648 ++it)
649 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700650 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700651 if (r < 0)
652 {
653 return r;
654 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 }
656 sd_bus_message_close_container(m);
657 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700658 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 std::string containedType = argCode.substr(1);
661 BMCWEB_LOG_DEBUG << "variant type: " << argCode
662 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700664 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 if (r < 0)
666 {
667 return r;
668 }
669
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700670 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 if (r < 0)
672 {
673 return r;
674 }
675
676 r = sd_bus_message_close_container(m);
677 if (r < 0)
678 {
679 return r;
680 }
681 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 else if (boost::starts_with(argCode, "(") &&
683 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700685 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700687 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 {
691 if (it == j->end())
692 {
693 return -1;
694 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700695 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 if (r < 0)
697 {
698 return r;
699 }
700 it++;
701 }
702 r = sd_bus_message_close_container(m);
703 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 else if (boost::starts_with(argCode, "{") &&
705 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700707 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700709 containedType.c_str());
710 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 if (codes.size() != 2)
712 {
713 return -1;
714 }
715 const std::string &key_type = codes[0];
716 const std::string &value_type = codes[1];
717 for (auto it : j->items())
718 {
719 r = convertJsonToDbus(m, key_type, it.key());
720 if (r < 0)
721 {
722 return r;
723 }
724
725 r = convertJsonToDbus(m, value_type, it.value());
726 if (r < 0)
727 {
728 return r;
729 }
730 }
731 r = sd_bus_message_close_container(m);
732 }
733 else
734 {
735 return -2;
736 }
737 if (r < 0)
738 {
739 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700740 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700741
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 if (argTypes.size() > 1)
743 {
744 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700745 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700746 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700747}
748
Ed Tanousd76323e2018-08-07 14:35:40 -0700749void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 const std::string &connectionName)
751{
752 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
753 << connectionName;
754 crow::connections::systemBus->async_method_call(
755 [transaction, connectionName{std::string(connectionName)}](
756 const boost::system::error_code ec,
757 const std::string &introspect_xml) {
758 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
759 if (ec)
760 {
761 BMCWEB_LOG_ERROR
762 << "Introspect call failed with error: " << ec.message()
763 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700764 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700765 else
766 {
767 tinyxml2::XMLDocument doc;
768
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700769 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
771 if (pRoot == nullptr)
772 {
773 BMCWEB_LOG_ERROR << "XML document failed to parse "
774 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700775 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700777 tinyxml2::XMLElement *interfaceNode =
778 pRoot->FirstChildElement("interface");
779 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700780 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700781 const char *thisInterfaceName =
782 interfaceNode->Attribute("name");
783 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700784 {
Matt Spinlerde818812018-12-11 16:39:20 -0600785 if (!transaction->interfaceName.empty() &&
786 (transaction->interfaceName != thisInterfaceName))
787 {
788 interfaceNode =
789 interfaceNode->NextSiblingElement("interface");
790 continue;
791 }
792
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700793 tinyxml2::XMLElement *methodNode =
794 interfaceNode->FirstChildElement("method");
795 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700797 const char *thisMethodName =
798 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700800 << thisMethodName;
801 if (thisMethodName != nullptr &&
802 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700803 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700804 BMCWEB_LOG_DEBUG
805 << "Found method named " << thisMethodName
806 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 sdbusplus::message::message m =
808 crow::connections::systemBus
809 ->new_method_call(
810 connectionName.c_str(),
811 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700812 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 transaction->methodName.c_str());
814
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700815 tinyxml2::XMLElement *argumentNode =
816 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700818 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 transaction->arguments.begin();
820
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700821 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700822 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700823 const char *argDirection =
824 argumentNode->Attribute("direction");
825 const char *argType =
826 argumentNode->Attribute("type");
827 if (argDirection != nullptr &&
828 argType != nullptr &&
829 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700831 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 transaction->arguments.end())
833 {
Matt Spinler6db06242018-12-11 11:21:22 -0600834 transaction->setErrorStatus(
835 "Invalid method args");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700836 return;
837 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700838 if (convertJsonToDbus(
839 m.get(), std::string(argType),
840 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 {
Matt Spinler6db06242018-12-11 11:21:22 -0600842 transaction->setErrorStatus(
843 "Invalid method arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700844 return;
845 }
846
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700847 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700848 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700849 argumentNode =
AppaRao Puli4d72dcc2018-12-26 20:26:22 +0530850 argumentNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700852
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 crow::connections::systemBus->async_send(
854 m, [transaction](
855 boost::system::error_code ec,
856 sdbusplus::message::message &m) {
857 if (ec)
858 {
Matt Spinler6db06242018-12-11 11:21:22 -0600859 transaction->setErrorStatus(
860 "Method call failed");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 return;
862 }
863 transaction->res.jsonValue = {
864 {"status", "ok"},
865 {"message", "200 OK"},
866 {"data", nullptr}};
867 });
868 break;
869 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700870 methodNode =
871 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700873 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700874 interfaceNode =
875 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 }
877 }
878 },
879 connectionName, transaction->path,
880 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700881}
882
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700883void handleAction(const crow::Request &req, crow::Response &res,
884 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700886 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
887 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888 nlohmann::json requestDbusData =
889 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700890
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891 if (requestDbusData.is_discarded())
892 {
Matt Spinler6db06242018-12-11 11:21:22 -0600893 setErrorResponse(res, boost::beast::http::status::bad_request,
894 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700895 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700896 return;
897 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700898 nlohmann::json::iterator data = requestDbusData.find("data");
899 if (data == requestDbusData.end())
900 {
Matt Spinler6db06242018-12-11 11:21:22 -0600901 setErrorResponse(res, boost::beast::http::status::bad_request,
902 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700903 res.end();
904 return;
905 }
906
907 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 {
Matt Spinler6db06242018-12-11 11:21:22 -0600909 setErrorResponse(res, boost::beast::http::status::bad_request,
910 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700911 res.end();
912 return;
913 }
914 auto transaction = std::make_shared<InProgressActionData>(res);
915
916 transaction->path = objectPath;
917 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700918 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700919 crow::connections::systemBus->async_method_call(
920 [transaction](
921 const boost::system::error_code ec,
922 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700923 &interfaceNames) {
924 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700926 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -0600927 setErrorResponse(transaction->res,
928 boost::beast::http::status::not_found,
929 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700930 return;
931 }
932
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700933 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
934 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700935
936 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700937 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700938 {
939 findActionOnInterface(transaction, object.first);
940 }
941 },
942 "xyz.openbmc_project.ObjectMapper",
943 "/xyz/openbmc_project/object_mapper",
944 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
945 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700946}
947
Matt Spinlerde818812018-12-11 16:39:20 -0600948void handleDelete(const crow::Request &req, crow::Response &res,
949 const std::string &objectPath)
950{
951 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
952
953 crow::connections::systemBus->async_method_call(
954 [&res, objectPath](
955 const boost::system::error_code ec,
956 const std::vector<std::pair<std::string, std::vector<std::string>>>
957 &interfaceNames) {
958 if (ec || interfaceNames.size() <= 0)
959 {
960 BMCWEB_LOG_ERROR << "Can't find object";
961 setErrorResponse(res, boost::beast::http::status::not_found,
962 notFoundDesc, notFoundMsg);
963 res.end();
964 return;
965 }
966
967 auto transaction = std::make_shared<InProgressActionData>(res);
968 transaction->path = objectPath;
969 transaction->methodName = "Delete";
970 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
971
972 for (const std::pair<std::string, std::vector<std::string>>
973 &object : interfaceNames)
974 {
975 findActionOnInterface(transaction, object.first);
976 }
977 },
978 "xyz.openbmc_project.ObjectMapper",
979 "/xyz/openbmc_project/object_mapper",
980 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
981 std::array<const char *, 0>());
982}
983
Ed Tanousf839dfe2018-11-12 11:11:15 -0800984void handleList(crow::Response &res, const std::string &objectPath,
985 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700986{
987 crow::connections::systemBus->async_method_call(
988 [&res](const boost::system::error_code ec,
989 std::vector<std::string> &objectPaths) {
990 if (ec)
991 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -0600992 setErrorResponse(res, boost::beast::http::status::not_found,
993 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 }
995 else
996 {
997 res.jsonValue = {{"status", "ok"},
998 {"message", "200 OK"},
999 {"data", std::move(objectPaths)}};
1000 }
1001 res.end();
1002 },
1003 "xyz.openbmc_project.ObjectMapper",
1004 "/xyz/openbmc_project/object_mapper",
1005 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001006 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001007}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001008
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001009void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001010{
Ed Tanous049a0512018-11-01 13:58:42 -07001011 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1012 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1013
1014 asyncResp->res.jsonValue = {{"message", "200 OK"},
1015 {"status", "ok"},
1016 {"data", nlohmann::json::object()}};
1017
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001019 [objectPath, asyncResp](const boost::system::error_code ec,
1020 GetSubTreeType &object_names) {
1021 auto transaction = std::make_shared<InProgressEnumerateData>(
1022 objectPath, asyncResp);
1023
1024 transaction->subtree =
1025 std::make_shared<GetSubTreeType>(std::move(object_names));
1026
Ed Tanous1abe55e2018-09-05 08:30:59 -07001027 if (ec)
1028 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001029 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1030 << transaction->objectPath;
1031 setErrorResponse(transaction->asyncResp->res,
1032 boost::beast::http::status::not_found,
1033 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001034 return;
1035 }
Ed Tanous64530012018-02-06 17:08:16 -08001036
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001037 // Add the data for the path passed in to the results
1038 // as if GetSubTree returned it, and continue on enumerating
1039 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040 },
1041 "xyz.openbmc_project.ObjectMapper",
1042 "/xyz/openbmc_project/object_mapper",
1043 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001044 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001045}
Ed Tanous911ac312017-08-15 09:37:42 -07001046
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001047void handleGet(crow::Response &res, std::string &objectPath,
1048 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001049{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001050 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1051 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001052 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001053
Ed Tanous1abe55e2018-09-05 08:30:59 -07001054 std::shared_ptr<std::string> path =
1055 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001056
Ed Tanous1abe55e2018-09-05 08:30:59 -07001057 using GetObjectType =
1058 std::vector<std::pair<std::string, std::vector<std::string>>>;
1059 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001060 [&res, path, propertyName](const boost::system::error_code ec,
1061 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001062 if (ec || object_names.size() <= 0)
1063 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001064 setErrorResponse(res, boost::beast::http::status::not_found,
1065 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001066 res.end();
1067 return;
1068 }
1069 std::shared_ptr<nlohmann::json> response =
1070 std::make_shared<nlohmann::json>(nlohmann::json::object());
1071 // The mapper should never give us an empty interface names list,
1072 // but check anyway
1073 for (const std::pair<std::string, std::vector<std::string>>
1074 connection : object_names)
1075 {
1076 const std::vector<std::string> &interfaceNames =
1077 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001078
Ed Tanous1abe55e2018-09-05 08:30:59 -07001079 if (interfaceNames.size() <= 0)
1080 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001081 setErrorResponse(res, boost::beast::http::status::not_found,
1082 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001083 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084 return;
1085 }
1086
1087 for (const std::string &interface : interfaceNames)
1088 {
1089 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001090 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001091 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001092 const std::vector<std::pair<
1093 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001094 &properties) {
1095 if (ec)
1096 {
1097 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1098 << ec;
1099 }
1100 else
1101 {
James Feist5b4aa862018-08-16 14:07:01 -07001102 for (const std::pair<
1103 std::string,
1104 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001105 &property : properties)
1106 {
1107 // if property name is empty, or matches our
1108 // search query, add it to the response json
1109
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001110 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001111 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001112 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001113 [&response, &property](auto &&val) {
1114 (*response)[property.first] =
1115 val;
1116 },
1117 property.second);
1118 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001119 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001120 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001121 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001122 [&response](auto &&val) {
1123 (*response) = val;
1124 },
1125 property.second);
1126 }
1127 }
1128 }
1129 if (response.use_count() == 1)
1130 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001131 if (!propertyName->empty() && response->empty())
1132 {
1133 setErrorResponse(
1134 res,
1135 boost::beast::http::status::not_found,
1136 propNotFoundDesc, notFoundMsg);
1137 }
1138 else
1139 {
1140 res.jsonValue = {{"status", "ok"},
1141 {"message", "200 OK"},
1142 {"data", *response}};
1143 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001144 res.end();
1145 }
1146 },
1147 connection.first, *path,
1148 "org.freedesktop.DBus.Properties", "GetAll", interface);
1149 }
1150 }
1151 },
1152 "xyz.openbmc_project.ObjectMapper",
1153 "/xyz/openbmc_project/object_mapper",
1154 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1155 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001156}
1157
Ed Tanous1abe55e2018-09-05 08:30:59 -07001158struct AsyncPutRequest
1159{
1160 AsyncPutRequest(crow::Response &res) : res(res)
1161 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001162 }
1163 ~AsyncPutRequest()
1164 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001165 if (res.jsonValue.empty())
1166 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001167 setErrorResponse(res, boost::beast::http::status::forbidden,
1168 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001169 }
1170
1171 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001172 }
1173
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001174 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001175 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001176 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1177 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001178 }
1179
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 crow::Response &res;
1181 std::string objectPath;
1182 std::string propertyName;
1183 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001184};
1185
Ed Tanousd76323e2018-08-07 14:35:40 -07001186void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001187 const std::string &objectPath, const std::string &destProperty)
1188{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001189 if (destProperty.empty())
1190 {
1191 setErrorResponse(res, boost::beast::http::status::forbidden,
1192 forbiddenResDesc, forbiddenMsg);
1193 res.end();
1194 return;
1195 }
1196
Ed Tanous1abe55e2018-09-05 08:30:59 -07001197 nlohmann::json requestDbusData =
1198 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001199
Ed Tanous1abe55e2018-09-05 08:30:59 -07001200 if (requestDbusData.is_discarded())
1201 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001202 setErrorResponse(res, boost::beast::http::status::bad_request,
1203 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001204 res.end();
1205 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001206 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001207
Ed Tanous1abe55e2018-09-05 08:30:59 -07001208 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1209 if (propertyIt == requestDbusData.end())
1210 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001211 setErrorResponse(res, boost::beast::http::status::bad_request,
1212 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001213 res.end();
1214 return;
1215 }
1216 const nlohmann::json &propertySetValue = *propertyIt;
1217 auto transaction = std::make_shared<AsyncPutRequest>(res);
1218 transaction->objectPath = objectPath;
1219 transaction->propertyName = destProperty;
1220 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001221
Ed Tanous1abe55e2018-09-05 08:30:59 -07001222 using GetObjectType =
1223 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001224
Ed Tanous1abe55e2018-09-05 08:30:59 -07001225 crow::connections::systemBus->async_method_call(
1226 [transaction](const boost::system::error_code ec,
1227 const GetObjectType &object_names) {
1228 if (!ec && object_names.size() <= 0)
1229 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001230 setErrorResponse(transaction->res,
1231 boost::beast::http::status::not_found,
1232 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001233 return;
1234 }
Ed Tanous911ac312017-08-15 09:37:42 -07001235
Ed Tanous1abe55e2018-09-05 08:30:59 -07001236 for (const std::pair<std::string, std::vector<std::string>>
1237 connection : object_names)
1238 {
1239 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001240
Ed Tanous1abe55e2018-09-05 08:30:59 -07001241 crow::connections::systemBus->async_method_call(
1242 [connectionName{std::string(connectionName)},
1243 transaction](const boost::system::error_code ec,
1244 const std::string &introspectXml) {
1245 if (ec)
1246 {
1247 BMCWEB_LOG_ERROR
1248 << "Introspect call failed with error: "
1249 << ec.message()
1250 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001251 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001252 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001253 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001254 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001255
Ed Tanous1abe55e2018-09-05 08:30:59 -07001256 doc.Parse(introspectXml.c_str());
1257 tinyxml2::XMLNode *pRoot =
1258 doc.FirstChildElement("node");
1259 if (pRoot == nullptr)
1260 {
1261 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1262 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001263 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001264 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001265 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001266 tinyxml2::XMLElement *ifaceNode =
1267 pRoot->FirstChildElement("interface");
1268 while (ifaceNode != nullptr)
1269 {
1270 const char *interfaceName =
1271 ifaceNode->Attribute("name");
1272 BMCWEB_LOG_DEBUG << "found interface "
1273 << interfaceName;
1274 tinyxml2::XMLElement *propNode =
1275 ifaceNode->FirstChildElement("property");
1276 while (propNode != nullptr)
1277 {
1278 const char *propertyName =
1279 propNode->Attribute("name");
1280 BMCWEB_LOG_DEBUG << "Found property "
1281 << propertyName;
1282 if (propertyName == transaction->propertyName)
1283 {
1284 const char *argType =
1285 propNode->Attribute("type");
1286 if (argType != nullptr)
1287 {
1288 sdbusplus::message::message m =
1289 crow::connections::systemBus
1290 ->new_method_call(
1291 connectionName.c_str(),
1292 transaction->objectPath
1293 .c_str(),
1294 "org.freedesktop.DBus."
1295 "Properties",
1296 "Set");
1297 m.append(interfaceName,
1298 transaction->propertyName);
1299 int r = sd_bus_message_open_container(
1300 m.get(), SD_BUS_TYPE_VARIANT,
1301 argType);
1302 if (r < 0)
1303 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001304 transaction->setErrorStatus(
1305 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001306 return;
1307 }
1308 r = convertJsonToDbus(
1309 m.get(), argType,
1310 transaction->propertyValue);
1311 if (r < 0)
1312 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001313 transaction->setErrorStatus(
1314 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001315 return;
1316 }
1317 r = sd_bus_message_close_container(
1318 m.get());
1319 if (r < 0)
1320 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001321 transaction->setErrorStatus(
1322 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001323 return;
1324 }
Ed Tanous911ac312017-08-15 09:37:42 -07001325
Ed Tanous1abe55e2018-09-05 08:30:59 -07001326 crow::connections::systemBus
1327 ->async_send(
1328 m,
1329 [transaction](
1330 boost::system::error_code
1331 ec,
1332 sdbusplus::message::message
1333 &m) {
1334 BMCWEB_LOG_DEBUG << "sent";
1335 if (ec)
1336 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001337 setErrorResponse(
1338 transaction->res,
1339 boost::beast::http::
1340 status::
1341 forbidden,
1342 forbiddenPropDesc,
1343 ec.message());
1344 }
1345 else
1346 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001347 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001348 .jsonValue = {
1349 {"status", "ok"},
1350 {"message",
1351 "200 OK"},
1352 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001353 }
1354 });
1355 }
1356 }
1357 propNode =
1358 propNode->NextSiblingElement("property");
1359 }
1360 ifaceNode =
1361 ifaceNode->NextSiblingElement("interface");
1362 }
1363 },
1364 connectionName, transaction->objectPath,
1365 "org.freedesktop.DBus.Introspectable", "Introspect");
1366 }
1367 },
1368 "xyz.openbmc_project.ObjectMapper",
1369 "/xyz/openbmc_project/object_mapper",
1370 "xyz.openbmc_project.ObjectMapper", "GetObject",
1371 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001372}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001373
Ed Tanous049a0512018-11-01 13:58:42 -07001374inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1375 std::string &objectPath)
1376{
Ed Tanous049a0512018-11-01 13:58:42 -07001377
1378 // If accessing a single attribute, fill in and update objectPath,
1379 // otherwise leave destProperty blank
1380 std::string destProperty = "";
1381 const char *attrSeperator = "/attr/";
1382 size_t attrPosition = objectPath.find(attrSeperator);
1383 if (attrPosition != objectPath.npos)
1384 {
1385 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1386 objectPath.length());
1387 objectPath = objectPath.substr(0, attrPosition);
1388 }
1389
1390 if (req.method() == "POST"_method)
1391 {
1392 constexpr const char *actionSeperator = "/action/";
1393 size_t actionPosition = objectPath.find(actionSeperator);
1394 if (actionPosition != objectPath.npos)
1395 {
1396 std::string postProperty =
1397 objectPath.substr((actionPosition + strlen(actionSeperator)),
1398 objectPath.length());
1399 objectPath = objectPath.substr(0, actionPosition);
1400 handleAction(req, res, objectPath, postProperty);
1401 return;
1402 }
1403 }
1404 else if (req.method() == "GET"_method)
1405 {
1406 if (boost::ends_with(objectPath, "/enumerate"))
1407 {
1408 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1409 objectPath.end());
1410 handleEnumerate(res, objectPath);
1411 }
1412 else if (boost::ends_with(objectPath, "/list"))
1413 {
1414 objectPath.erase(objectPath.end() - sizeof("list"),
1415 objectPath.end());
1416 handleList(res, objectPath);
1417 }
1418 else
1419 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001420 // Trim any trailing "/" at the end
1421 if (boost::ends_with(objectPath, "/"))
1422 {
1423 objectPath.pop_back();
1424 handleList(res, objectPath, 1);
1425 }
1426 else
1427 {
1428 handleGet(res, objectPath, destProperty);
1429 }
Ed Tanous049a0512018-11-01 13:58:42 -07001430 }
1431 return;
1432 }
1433 else if (req.method() == "PUT"_method)
1434 {
1435 handlePut(req, res, objectPath, destProperty);
1436 return;
1437 }
Matt Spinlerde818812018-12-11 16:39:20 -06001438 else if (req.method() == "DELETE"_method)
1439 {
1440 handleDelete(req, res, objectPath);
1441 return;
1442 }
Ed Tanous049a0512018-11-01 13:58:42 -07001443
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001444 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1445 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001446 res.end();
1447}
1448
Ed Tanous1abe55e2018-09-05 08:30:59 -07001449template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1450{
1451 BMCWEB_ROUTE(app, "/bus/")
1452 .methods("GET"_method)(
1453 [](const crow::Request &req, crow::Response &res) {
1454 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1455 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001456 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001457 });
1458
1459 BMCWEB_ROUTE(app, "/bus/system/")
1460 .methods("GET"_method)(
1461 [](const crow::Request &req, crow::Response &res) {
1462 auto myCallback = [&res](const boost::system::error_code ec,
1463 std::vector<std::string> &names) {
1464 if (ec)
1465 {
1466 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1467 res.result(
1468 boost::beast::http::status::internal_server_error);
1469 }
1470 else
1471 {
1472 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001473 res.jsonValue = {{"status", "ok"}};
1474 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001475 for (auto &name : names)
1476 {
1477 objectsSub.push_back({{"name", name}});
1478 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001479 }
1480 res.end();
1481 };
1482 crow::connections::systemBus->async_method_call(
1483 std::move(myCallback), "org.freedesktop.DBus", "/",
1484 "org.freedesktop.DBus", "ListNames");
1485 });
1486
1487 BMCWEB_ROUTE(app, "/list/")
1488 .methods("GET"_method)(
1489 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001490 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001491 });
1492
1493 BMCWEB_ROUTE(app, "/xyz/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06001494 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07001495 [](const crow::Request &req, crow::Response &res,
1496 const std::string &path) {
1497 std::string objectPath = "/xyz/" + path;
1498 handleDBusUrl(req, res, objectPath);
1499 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001500
Ed Tanous049a0512018-11-01 13:58:42 -07001501 BMCWEB_ROUTE(app, "/org/<path>")
Matt Spinlerde818812018-12-11 16:39:20 -06001502 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07001503 [](const crow::Request &req, crow::Response &res,
1504 const std::string &path) {
1505 std::string objectPath = "/org/" + path;
1506 handleDBusUrl(req, res, objectPath);
1507 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001508
Ed Tanous1abe55e2018-09-05 08:30:59 -07001509 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1510 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1511 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08001512 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001513 if (!std::regex_match(dumpId, validFilename))
1514 {
Ed Tanousad18f072018-11-14 14:07:48 -08001515 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001516 res.end();
1517 return;
1518 }
1519 std::experimental::filesystem::path loc(
1520 "/var/lib/phosphor-debug-collector/dumps");
1521
Ed Tanousad18f072018-11-14 14:07:48 -08001522 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001523
1524 if (!std::experimental::filesystem::exists(loc) ||
1525 !std::experimental::filesystem::is_directory(loc))
1526 {
Ed Tanousad18f072018-11-14 14:07:48 -08001527 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001528 res.result(boost::beast::http::status::not_found);
1529 res.end();
1530 return;
1531 }
1532 std::experimental::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08001533
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534 for (auto &file : files)
1535 {
1536 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08001537 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001538 {
1539 continue;
1540 }
1541 res.addHeader("Content-Type", "application/octet-stream");
1542 res.body() = {std::istreambuf_iterator<char>(readFile),
1543 std::istreambuf_iterator<char>()};
1544 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08001545 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001546 }
1547 res.result(boost::beast::http::status::not_found);
1548 res.end();
1549 return;
1550 });
1551
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001552 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001553 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001554 const std::string &Connection) {
1555 introspectObjects(Connection, "/",
1556 std::make_shared<bmcweb::AsyncResp>(res));
1557 });
1558
1559 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1560 .methods("GET"_method,
1561 "POST"_method)([](const crow::Request &req,
1562 crow::Response &res,
1563 const std::string &processName,
1564 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001565 std::vector<std::string> strs;
1566 boost::split(strs, requestedPath, boost::is_any_of("/"));
1567 std::string objectPath;
1568 std::string interfaceName;
1569 std::string methodName;
1570 auto it = strs.begin();
1571 if (it == strs.end())
1572 {
1573 objectPath = "/";
1574 }
1575 while (it != strs.end())
1576 {
1577 // Check if segment contains ".". If it does, it must be an
1578 // interface
1579 if (it->find(".") != std::string::npos)
1580 {
1581 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001582 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001583 // as part of our <path> specifier above, which causes the
1584 // normal trailing backslash redirector to fail.
1585 }
1586 else if (!it->empty())
1587 {
1588 objectPath += "/" + *it;
1589 }
1590 it++;
1591 }
1592 if (it != strs.end())
1593 {
1594 interfaceName = *it;
1595 it++;
1596
1597 // after interface, we might have a method name
1598 if (it != strs.end())
1599 {
1600 methodName = *it;
1601 it++;
1602 }
1603 }
1604 if (it != strs.end())
1605 {
1606 // if there is more levels past the method name, something went
1607 // wrong, return not found
1608 res.result(boost::beast::http::status::not_found);
1609 res.end();
1610 return;
1611 }
1612 if (interfaceName.empty())
1613 {
1614 crow::connections::systemBus->async_method_call(
1615 [&, processName,
1616 objectPath](const boost::system::error_code ec,
1617 const std::string &introspect_xml) {
1618 if (ec)
1619 {
1620 BMCWEB_LOG_ERROR
1621 << "Introspect call failed with error: "
1622 << ec.message()
1623 << " on process: " << processName
1624 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001625 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001626 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001627 tinyxml2::XMLDocument doc;
1628
1629 doc.Parse(introspect_xml.c_str());
1630 tinyxml2::XMLNode *pRoot =
1631 doc.FirstChildElement("node");
1632 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001633 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001634 BMCWEB_LOG_ERROR << "XML document failed to parse "
1635 << processName << " " << objectPath
1636 << "\n";
1637 res.jsonValue = {{"status", "XML parse error"}};
1638 res.result(boost::beast::http::status::
1639 internal_server_error);
1640 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001641 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001642
1643 BMCWEB_LOG_DEBUG << introspect_xml;
1644 res.jsonValue = {{"status", "ok"},
1645 {"bus_name", processName},
1646 {"object_path", objectPath}};
1647 nlohmann::json &interfacesArray =
1648 res.jsonValue["interfaces"];
1649 interfacesArray = nlohmann::json::array();
1650 tinyxml2::XMLElement *interface =
1651 pRoot->FirstChildElement("interface");
1652
1653 while (interface != nullptr)
1654 {
1655 const char *ifaceName =
1656 interface->Attribute("name");
1657 if (ifaceName != nullptr)
1658 {
1659 interfacesArray.push_back(
1660 {{"name", ifaceName}});
1661 }
1662
1663 interface =
1664 interface->NextSiblingElement("interface");
1665 }
1666
Ed Tanous1abe55e2018-09-05 08:30:59 -07001667 res.end();
1668 },
1669 processName, objectPath,
1670 "org.freedesktop.DBus.Introspectable", "Introspect");
1671 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001672 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001673 {
1674 crow::connections::systemBus->async_method_call(
1675 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001676 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001677 const boost::system::error_code ec,
1678 const std::string &introspect_xml) {
1679 if (ec)
1680 {
1681 BMCWEB_LOG_ERROR
1682 << "Introspect call failed with error: "
1683 << ec.message()
1684 << " on process: " << processName
1685 << " path: " << objectPath << "\n";
1686 }
1687 else
1688 {
1689 tinyxml2::XMLDocument doc;
1690
1691 doc.Parse(introspect_xml.c_str());
1692 tinyxml2::XMLNode *pRoot =
1693 doc.FirstChildElement("node");
1694 if (pRoot == nullptr)
1695 {
1696 BMCWEB_LOG_ERROR
1697 << "XML document failed to parse "
1698 << processName << " " << objectPath << "\n";
1699 res.result(boost::beast::http::status::
1700 internal_server_error);
1701 }
1702 else
1703 {
1704 tinyxml2::XMLElement *node =
1705 pRoot->FirstChildElement("node");
1706
1707 // if we know we're the only call, build the
1708 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001709 tinyxml2::XMLElement *interface =
1710 pRoot->FirstChildElement("interface");
1711
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001712 res.jsonValue = {
1713 {"status", "ok"},
1714 {"bus_name", processName},
1715 {"interface", interfaceName},
1716 {"object_path", objectPath},
1717 {"properties", nlohmann::json::object()}};
1718
1719 nlohmann::json &methodsArray =
1720 res.jsonValue["methods"];
1721 methodsArray = nlohmann::json::array();
1722
1723 nlohmann::json &signalsArray =
1724 res.jsonValue["signals"];
1725 signalsArray = nlohmann::json::array();
1726
Ed Tanous1abe55e2018-09-05 08:30:59 -07001727 while (interface != nullptr)
1728 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001729 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001730 interface->Attribute("name");
1731
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001732 if (ifaceName != nullptr &&
1733 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 {
1735 tinyxml2::XMLElement *methods =
1736 interface->FirstChildElement(
1737 "method");
1738 while (methods != nullptr)
1739 {
1740 nlohmann::json argsArray =
1741 nlohmann::json::array();
1742 tinyxml2::XMLElement *arg =
1743 methods->FirstChildElement(
1744 "arg");
1745 while (arg != nullptr)
1746 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001747 nlohmann::json thisArg;
1748 for (const char *fieldName :
1749 std::array<const char *,
1750 3>{"name",
1751 "direction",
1752 "type"})
1753 {
1754 const char *fieldValue =
1755 arg->Attribute(
1756 fieldName);
1757 if (fieldValue != nullptr)
1758 {
1759 thisArg[fieldName] =
1760 fieldValue;
1761 }
1762 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001763 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001764 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001765 arg = arg->NextSiblingElement(
1766 "arg");
1767 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001768
1769 const char *name =
1770 methods->Attribute("name");
1771 if (name != nullptr)
1772 {
1773 methodsArray.push_back(
1774 {{"name", name},
1775 {"uri", "/bus/system/" +
1776 processName +
1777 objectPath +
1778 "/" +
1779 interfaceName +
1780 "/" + name},
1781 {"args", argsArray}});
1782 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001783 methods =
1784 methods->NextSiblingElement(
1785 "method");
1786 }
1787 tinyxml2::XMLElement *signals =
1788 interface->FirstChildElement(
1789 "signal");
1790 while (signals != nullptr)
1791 {
1792 nlohmann::json argsArray =
1793 nlohmann::json::array();
1794
1795 tinyxml2::XMLElement *arg =
1796 signals->FirstChildElement(
1797 "arg");
1798 while (arg != nullptr)
1799 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001800 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001801 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001802 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001803 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001804 if (name != nullptr &&
1805 type != nullptr)
1806 {
1807 argsArray.push_back({
1808 {"name", name},
1809 {"type", type},
1810 });
1811 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001812 arg = arg->NextSiblingElement(
1813 "arg");
1814 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001815 const char *name =
1816 signals->Attribute("name");
1817 if (name != nullptr)
1818 {
1819 signalsArray.push_back(
1820 {{"name", name},
1821 {"args", argsArray}});
1822 }
1823
Ed Tanous1abe55e2018-09-05 08:30:59 -07001824 signals =
1825 signals->NextSiblingElement(
1826 "signal");
1827 }
1828
Ed Tanous1abe55e2018-09-05 08:30:59 -07001829 break;
1830 }
1831
1832 interface = interface->NextSiblingElement(
1833 "interface");
1834 }
1835 if (interface == nullptr)
1836 {
1837 // if we got to the end of the list and
1838 // never found a match, throw 404
1839 res.result(
1840 boost::beast::http::status::not_found);
1841 }
1842 }
1843 }
1844 res.end();
1845 },
1846 processName, objectPath,
1847 "org.freedesktop.DBus.Introspectable", "Introspect");
1848 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001849 else
1850 {
1851 if (req.method() != "POST"_method)
1852 {
1853 res.result(boost::beast::http::status::not_found);
1854 res.end();
1855 return;
1856 }
1857
1858 nlohmann::json requestDbusData =
1859 nlohmann::json::parse(req.body, nullptr, false);
1860
1861 if (requestDbusData.is_discarded())
1862 {
1863 res.result(boost::beast::http::status::bad_request);
1864 res.end();
1865 return;
1866 }
1867 if (!requestDbusData.is_array())
1868 {
1869 res.result(boost::beast::http::status::bad_request);
1870 res.end();
1871 return;
1872 }
1873 auto transaction = std::make_shared<InProgressActionData>(res);
1874
1875 transaction->path = objectPath;
1876 transaction->methodName = methodName;
1877 transaction->arguments = std::move(requestDbusData);
1878
1879 findActionOnInterface(transaction, processName);
1880 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001881 });
1882}
1883} // namespace openbmc_mapper
1884} // namespace crow