blob: a142426c3ca0b37dc265ea3adb975d0f29429219 [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";
38const std::string notFoundDesc =
39 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060040const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler2ae60092018-12-06 10:35:36 -060041
42void setErrorResponse(crow::Response &res, boost::beast::http::status result,
43 const std::string &desc, const std::string &msg)
44{
45 res.result(result);
46 res.jsonValue = {{"data", {{"description", desc}}},
47 {"message", msg},
48 {"status", "error"}};
49}
50
Ed Tanouse3cb5a32018-08-08 14:16:49 -070051void introspectObjects(const std::string &processName,
52 const std::string &objectPath,
53 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070054{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070055 if (transaction->res.jsonValue.is_null())
56 {
57 transaction->res.jsonValue = {{"status", "ok"},
58 {"bus_name", processName},
59 {"objects", nlohmann::json::array()}};
60 }
61
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070063 [transaction, processName{std::string(processName)},
64 objectPath{std::string(objectPath)}](
65 const boost::system::error_code ec,
66 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 if (ec)
68 {
69 BMCWEB_LOG_ERROR
70 << "Introspect call failed with error: " << ec.message()
71 << " on process: " << processName << " path: " << objectPath
72 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070073 return;
74 }
75 transaction->res.jsonValue["objects"].push_back(
76 {{"path", objectPath}});
77
78 tinyxml2::XMLDocument doc;
79
80 doc.Parse(introspect_xml.c_str());
81 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
82 if (pRoot == nullptr)
83 {
84 BMCWEB_LOG_ERROR << "XML document failed to parse "
85 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070086 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 else
88 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070089 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
90 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070091 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070092 const char *childPath = node->Attribute("name");
93 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 std::string newpath;
96 if (objectPath != "/")
97 {
98 newpath += objectPath;
99 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700100 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700104
105 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 }
107 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700109 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700111}
Ed Tanous64530012018-02-06 17:08:16 -0800112
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600113void getPropertiesForEnumerate(const std::string &objectPath,
114 const std::string &service,
115 const std::string &interface,
116 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
117{
118 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
119 << service << " " << interface;
120
121 crow::connections::systemBus->async_method_call(
122 [asyncResp, objectPath, service,
123 interface](const boost::system::error_code ec,
124 const std::vector<
125 std::pair<std::string, dbus::utility::DbusVariantType>>
126 &propertiesList) {
127 if (ec)
128 {
129 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
130 << interface << " service " << service
131 << " failed with code " << ec;
132 return;
133 }
134
135 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
136 nlohmann::json &objectJson = dataJson[objectPath];
137 if (objectJson.is_null())
138 {
139 objectJson = nlohmann::json::object();
140 }
141
142 for (const auto &[name, value] : propertiesList)
143 {
144 nlohmann::json &propertyJson = objectJson[name];
145 sdbusplus::message::variant_ns::visit(
146 [&propertyJson](auto &&val) { propertyJson = val; }, value);
147 }
148 },
149 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
150 interface);
151}
152
153// Find any results that weren't picked up by ObjectManagers, to be
154// called after all ObjectManagers are searched for and called.
155void findRemainingObjectsForEnumerate(
156 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
157 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
158{
159 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
160 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
161
162 for (const auto &[path, interface_map] : *subtree)
163 {
164 if (path == objectPath)
165 {
166 // An enumerate does not return the target path's properties
167 continue;
168 }
169 if (dataJson.find(path) == dataJson.end())
170 {
171 for (const auto &[service, interfaces] : interface_map)
172 {
173 for (const auto &interface : interfaces)
174 {
175 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
176 {
177 getPropertiesForEnumerate(path, service, interface,
178 asyncResp);
179 }
180 }
181 }
182 }
183 }
184}
185
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600186struct InProgressEnumerateData
187{
188 InProgressEnumerateData(const std::string &objectPath,
189 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
190 objectPath(objectPath),
191 asyncResp(asyncResp)
192 {
193 }
194
195 ~InProgressEnumerateData()
196 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600197 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600198 }
199
200 const std::string objectPath;
201 std::shared_ptr<GetSubTreeType> subtree;
202 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
203};
204
205void getManagedObjectsForEnumerate(
206 const std::string &object_name, const std::string &object_manager_path,
207 const std::string &connection_name,
208 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700209{
Ed Tanous049a0512018-11-01 13:58:42 -0700210 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
211 << " object_manager_path " << object_manager_path
212 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600214 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700215 connection_name](const boost::system::error_code ec,
216 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 if (ec)
218 {
Ed Tanous049a0512018-11-01 13:58:42 -0700219 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600220 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700221 << " failed with code " << ec;
222 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700223 }
Ed Tanous64530012018-02-06 17:08:16 -0800224
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600225 nlohmann::json &dataJson =
226 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700227
228 for (const auto &objectPath : objects)
229 {
230 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 {
Ed Tanous049a0512018-11-01 13:58:42 -0700232 BMCWEB_LOG_DEBUG << "Reading object "
233 << objectPath.first.str;
234 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 if (objectJson.is_null())
236 {
237 objectJson = nlohmann::json::object();
238 }
239 for (const auto &interface : objectPath.second)
240 {
241 for (const auto &property : interface.second)
242 {
243 nlohmann::json &propertyJson =
244 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700245 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 [&propertyJson](auto &&val) {
247 propertyJson = val;
248 },
249 property.second);
250 }
251 }
252 }
Ed Tanous049a0512018-11-01 13:58:42 -0700253 for (const auto &interface : objectPath.second)
254 {
255 if (interface.first == "org.freedesktop.DBus.ObjectManager")
256 {
257 getManagedObjectsForEnumerate(
258 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600259 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700260 }
261 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700262 }
263 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700264 connection_name, object_manager_path,
265 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
266}
267
268void findObjectManagerPathForEnumerate(
269 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600270 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700271{
Ed Tanous049a0512018-11-01 13:58:42 -0700272 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
273 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700274 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600275 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700276 const boost::system::error_code ec,
277 const boost::container::flat_map<
278 std::string, boost::container::flat_map<
279 std::string, std::vector<std::string>>>
280 &objects) {
281 if (ec)
282 {
Ed Tanous049a0512018-11-01 13:58:42 -0700283 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
284 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700285 return;
286 }
287
Ed Tanousf254ba72018-10-12 13:40:35 -0700288 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700289 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700290 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700291 {
292 if (connectionGroup.first == connection_name)
293 {
294 // Found the object manager path for this resource.
295 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700296 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600297 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700298 return;
299 }
300 }
301 }
302 },
303 "xyz.openbmc_project.ObjectMapper",
304 "/xyz/openbmc_project/object_mapper",
305 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
306 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700307}
Ed Tanous64530012018-02-06 17:08:16 -0800308
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600309// Uses GetObject to add the object info about the target /enumerate path to the
310// results of GetSubTree, as GetSubTree will not return info for the
311// target path, and then continues on enumerating the rest of the tree.
312void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
313{
314 using GetObjectType =
315 std::vector<std::pair<std::string, std::vector<std::string>>>;
316
317 crow::connections::systemBus->async_method_call(
318 [transaction](const boost::system::error_code ec,
319 const GetObjectType &objects) {
320 if (ec)
321 {
322 BMCWEB_LOG_ERROR << "GetObject for path "
323 << transaction->objectPath
324 << " failed with code " << ec;
325 return;
326 }
327
328 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
329 << " has " << objects.size() << " entries";
330 if (!objects.empty())
331 {
332 transaction->subtree->emplace_back(transaction->objectPath,
333 objects);
334 }
335
336 // Map indicating connection name, and the path where the object
337 // manager exists
338 boost::container::flat_map<std::string, std::string> connections;
339
340 for (const auto &object : *(transaction->subtree))
341 {
342 for (const auto &connection : object.second)
343 {
344 std::string &objectManagerPath =
345 connections[connection.first];
346 for (const auto &interface : connection.second)
347 {
348 BMCWEB_LOG_DEBUG << connection.first
349 << " has interface " << interface;
350 if (interface == "org.freedesktop.DBus.ObjectManager")
351 {
352 BMCWEB_LOG_DEBUG << "found object manager path "
353 << object.first;
354 objectManagerPath = object.first;
355 }
356 }
357 }
358 }
359 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
360
361 for (const auto &connection : connections)
362 {
363 // If we already know where the object manager is, we don't need
364 // to search for it, we can call directly in to
365 // getManagedObjects
366 if (!connection.second.empty())
367 {
368 getManagedObjectsForEnumerate(
369 transaction->objectPath, connection.second,
370 connection.first, transaction);
371 }
372 else
373 {
374 // otherwise we need to find the object manager path before
375 // we can continue
376 findObjectManagerPathForEnumerate(
377 transaction->objectPath, connection.first, transaction);
378 }
379 }
380 },
381 "xyz.openbmc_project.ObjectMapper",
382 "/xyz/openbmc_project/object_mapper",
383 "xyz.openbmc_project.ObjectMapper", "GetObject",
384 transaction->objectPath, std::array<const char *, 0>());
385}
Ed Tanous64530012018-02-06 17:08:16 -0800386
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700387// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700388struct InProgressActionData
389{
390 InProgressActionData(crow::Response &res) : res(res){};
391 ~InProgressActionData()
392 {
393 if (res.result() == boost::beast::http::status::internal_server_error)
394 {
395 // Reset the json object to clear out any data that made it in
396 // before the error happened todo(ed) handle error condition with
397 // proper code
398 res.jsonValue = nlohmann::json::object();
399 }
400 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700401 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700402
Ed Tanous1abe55e2018-09-05 08:30:59 -0700403 void setErrorStatus()
404 {
405 res.result(boost::beast::http::status::internal_server_error);
406 }
407 crow::Response &res;
408 std::string path;
409 std::string methodName;
410 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700411};
412
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413std::vector<std::string> dbusArgSplit(const std::string &string)
414{
415 std::vector<std::string> ret;
416 if (string.empty())
417 {
418 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700419 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 ret.push_back("");
421 int containerDepth = 0;
422
423 for (std::string::const_iterator character = string.begin();
424 character != string.end(); character++)
425 {
426 ret.back() += *character;
427 switch (*character)
428 {
429 case ('a'):
430 break;
431 case ('('):
432 case ('{'):
433 containerDepth++;
434 break;
435 case ('}'):
436 case (')'):
437 containerDepth--;
438 if (containerDepth == 0)
439 {
440 if (character + 1 != string.end())
441 {
442 ret.push_back("");
443 }
444 }
445 break;
446 default:
447 if (containerDepth == 0)
448 {
449 if (character + 1 != string.end())
450 {
451 ret.push_back("");
452 }
453 }
454 break;
455 }
456 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700457}
458
Ed Tanousd76323e2018-08-07 14:35:40 -0700459int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460 const nlohmann::json &input_json)
461{
462 int r = 0;
463 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
464 << " to type: " << arg_type;
465 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700466
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 // Assume a single object for now.
468 const nlohmann::json *j = &input_json;
469 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700470
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700471 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700472 {
473 // If we are decoding multiple objects, grab the pointer to the
474 // iterator, and increment it for the next loop
475 if (argTypes.size() > 1)
476 {
477 if (jIt == input_json.end())
478 {
479 return -2;
480 }
481 j = &*jIt;
482 jIt++;
483 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700484 const int64_t *intValue = j->get_ptr<const int64_t *>();
485 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
486 const std::string *stringValue = j->get_ptr<const std::string *>();
487 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700488 const bool *b = j->get_ptr<const bool *>();
489 int64_t v = 0;
490 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700491
Ed Tanous1abe55e2018-09-05 08:30:59 -0700492 // Do some basic type conversions that make sense. uint can be
493 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700494 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700495 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700496 v = static_cast<int64_t>(*uintValue);
497 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700499 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700500 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700501 d = static_cast<double>(*uintValue);
502 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700503 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700504 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700506 d = static_cast<double>(*intValue);
507 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700508 }
509
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700510 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700512 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513 {
514 return -1;
515 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700516 r = sd_bus_message_append_basic(m, argCode[0],
517 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 if (r < 0)
519 {
520 return r;
521 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700522 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700523 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700525 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700526 {
527 return -1;
528 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700529 int32_t i = static_cast<int32_t>(*intValue);
530 r = sd_bus_message_append_basic(m, argCode[0], &i);
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 == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700537 {
538 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700539 int boolInt = false;
540 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700541 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700542 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 }
544 else if (b != nullptr)
545 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700546 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700548 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700550 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 }
552 else
553 {
554 return -1;
555 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700556 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 if (r < 0)
558 {
559 return r;
560 }
561 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700562 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700564 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700565 {
566 return -1;
567 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 int16_t n = static_cast<int16_t>(*intValue);
569 r = sd_bus_message_append_basic(m, argCode[0], &n);
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 == "x")
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 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 if (r < 0)
583 {
584 return r;
585 }
586 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700587 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700589 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700590 {
591 return -1;
592 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700593 uint8_t y = static_cast<uint8_t>(*uintValue);
594 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700596 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700598 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 {
600 return -1;
601 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700602 uint16_t q = static_cast<uint16_t>(*uintValue);
603 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700605 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700607 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 {
609 return -1;
610 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700611 uint32_t u = static_cast<uint32_t>(*uintValue);
612 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700614 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700616 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 {
618 return -1;
619 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700620 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700622 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700624 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700628 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700630 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 if (r < 0)
632 {
633 return r;
634 }
635
636 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
637 ++it)
638 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 if (r < 0)
641 {
642 return r;
643 }
644
645 it++;
646 }
647 sd_bus_message_close_container(m);
648 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700649 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700651 std::string containedType = argCode.substr(1);
652 BMCWEB_LOG_DEBUG << "variant type: " << argCode
653 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700655 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 if (r < 0)
657 {
658 return r;
659 }
660
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700661 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 if (r < 0)
663 {
664 return r;
665 }
666
667 r = sd_bus_message_close_container(m);
668 if (r < 0)
669 {
670 return r;
671 }
672 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 else if (boost::starts_with(argCode, "(") &&
674 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700676 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700678 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 {
682 if (it == j->end())
683 {
684 return -1;
685 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 if (r < 0)
688 {
689 return r;
690 }
691 it++;
692 }
693 r = sd_bus_message_close_container(m);
694 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700695 else if (boost::starts_with(argCode, "{") &&
696 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700698 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700699 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700700 containedType.c_str());
701 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 if (codes.size() != 2)
703 {
704 return -1;
705 }
706 const std::string &key_type = codes[0];
707 const std::string &value_type = codes[1];
708 for (auto it : j->items())
709 {
710 r = convertJsonToDbus(m, key_type, it.key());
711 if (r < 0)
712 {
713 return r;
714 }
715
716 r = convertJsonToDbus(m, value_type, it.value());
717 if (r < 0)
718 {
719 return r;
720 }
721 }
722 r = sd_bus_message_close_container(m);
723 }
724 else
725 {
726 return -2;
727 }
728 if (r < 0)
729 {
730 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700731 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700732
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 if (argTypes.size() > 1)
734 {
735 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700736 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700737 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700738}
739
Ed Tanousd76323e2018-08-07 14:35:40 -0700740void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741 const std::string &connectionName)
742{
743 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
744 << connectionName;
745 crow::connections::systemBus->async_method_call(
746 [transaction, connectionName{std::string(connectionName)}](
747 const boost::system::error_code ec,
748 const std::string &introspect_xml) {
749 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
750 if (ec)
751 {
752 BMCWEB_LOG_ERROR
753 << "Introspect call failed with error: " << ec.message()
754 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700755 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756 else
757 {
758 tinyxml2::XMLDocument doc;
759
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700760 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700761 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
762 if (pRoot == nullptr)
763 {
764 BMCWEB_LOG_ERROR << "XML document failed to parse "
765 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700766 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700768 tinyxml2::XMLElement *interfaceNode =
769 pRoot->FirstChildElement("interface");
770 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700772 const char *thisInterfaceName =
773 interfaceNode->Attribute("name");
774 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700776 tinyxml2::XMLElement *methodNode =
777 interfaceNode->FirstChildElement("method");
778 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700780 const char *thisMethodName =
781 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700782 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700783 << thisMethodName;
784 if (thisMethodName != nullptr &&
785 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700787 BMCWEB_LOG_DEBUG
788 << "Found method named " << thisMethodName
789 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 sdbusplus::message::message m =
791 crow::connections::systemBus
792 ->new_method_call(
793 connectionName.c_str(),
794 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700795 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 transaction->methodName.c_str());
797
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700798 tinyxml2::XMLElement *argumentNode =
799 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700801 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 transaction->arguments.begin();
803
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700804 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700806 const char *argDirection =
807 argumentNode->Attribute("direction");
808 const char *argType =
809 argumentNode->Attribute("type");
810 if (argDirection != nullptr &&
811 argType != nullptr &&
812 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700814
815 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 transaction->arguments.end())
817 {
818 transaction->setErrorStatus();
819 return;
820 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700821 if (convertJsonToDbus(
822 m.get(), std::string(argType),
823 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 {
825 transaction->setErrorStatus();
826 return;
827 }
828
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700829 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700831 argumentNode =
832 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700834
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 crow::connections::systemBus->async_send(
836 m, [transaction](
837 boost::system::error_code ec,
838 sdbusplus::message::message &m) {
839 if (ec)
840 {
841 transaction->setErrorStatus();
842 return;
843 }
844 transaction->res.jsonValue = {
845 {"status", "ok"},
846 {"message", "200 OK"},
847 {"data", nullptr}};
848 });
849 break;
850 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700851 methodNode =
852 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700854 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700855 interfaceNode =
856 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 }
858 }
859 },
860 connectionName, transaction->path,
861 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700862}
863
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700864void handleAction(const crow::Request &req, crow::Response &res,
865 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700867 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
868 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 nlohmann::json requestDbusData =
870 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700871
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 if (requestDbusData.is_discarded())
873 {
874 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700875 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 return;
877 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700878 nlohmann::json::iterator data = requestDbusData.find("data");
879 if (data == requestDbusData.end())
880 {
881 res.result(boost::beast::http::status::bad_request);
882 res.end();
883 return;
884 }
885
886 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700887 {
888 res.result(boost::beast::http::status::bad_request);
889 res.end();
890 return;
891 }
892 auto transaction = std::make_shared<InProgressActionData>(res);
893
894 transaction->path = objectPath;
895 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700896 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897 crow::connections::systemBus->async_method_call(
898 [transaction](
899 const boost::system::error_code ec,
900 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700901 &interfaceNames) {
902 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700903 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700904 BMCWEB_LOG_ERROR << "Can't find object";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700905 transaction->setErrorStatus();
906 return;
907 }
908
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700909 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
910 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700911
912 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700913 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
915 findActionOnInterface(transaction, object.first);
916 }
917 },
918 "xyz.openbmc_project.ObjectMapper",
919 "/xyz/openbmc_project/object_mapper",
920 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
921 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700922}
923
Ed Tanousf839dfe2018-11-12 11:11:15 -0800924void handleList(crow::Response &res, const std::string &objectPath,
925 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926{
927 crow::connections::systemBus->async_method_call(
928 [&res](const boost::system::error_code ec,
929 std::vector<std::string> &objectPaths) {
930 if (ec)
931 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -0600932 setErrorResponse(res, boost::beast::http::status::not_found,
933 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700934 }
935 else
936 {
937 res.jsonValue = {{"status", "ok"},
938 {"message", "200 OK"},
939 {"data", std::move(objectPaths)}};
940 }
941 res.end();
942 },
943 "xyz.openbmc_project.ObjectMapper",
944 "/xyz/openbmc_project/object_mapper",
945 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -0800946 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700947}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700948
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700949void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700950{
Ed Tanous049a0512018-11-01 13:58:42 -0700951 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
952 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
953
954 asyncResp->res.jsonValue = {{"message", "200 OK"},
955 {"status", "ok"},
956 {"data", nlohmann::json::object()}};
957
Ed Tanous1abe55e2018-09-05 08:30:59 -0700958 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600959 [objectPath, asyncResp](const boost::system::error_code ec,
960 GetSubTreeType &object_names) {
961 auto transaction = std::make_shared<InProgressEnumerateData>(
962 objectPath, asyncResp);
963
964 transaction->subtree =
965 std::make_shared<GetSubTreeType>(std::move(object_names));
966
Ed Tanous1abe55e2018-09-05 08:30:59 -0700967 if (ec)
968 {
Matt Spinler2ae60092018-12-06 10:35:36 -0600969 BMCWEB_LOG_ERROR << "GetSubTree failed on "
970 << transaction->objectPath;
971 setErrorResponse(transaction->asyncResp->res,
972 boost::beast::http::status::not_found,
973 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700974 return;
975 }
Ed Tanous64530012018-02-06 17:08:16 -0800976
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600977 // Add the data for the path passed in to the results
978 // as if GetSubTree returned it, and continue on enumerating
979 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700980 },
981 "xyz.openbmc_project.ObjectMapper",
982 "/xyz/openbmc_project/object_mapper",
983 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -0700984 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800985}
Ed Tanous911ac312017-08-15 09:37:42 -0700986
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700987void handleGet(crow::Response &res, std::string &objectPath,
988 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700989{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700990 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
991 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -0700993
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 std::shared_ptr<std::string> path =
995 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -0700996
Ed Tanous1abe55e2018-09-05 08:30:59 -0700997 using GetObjectType =
998 std::vector<std::pair<std::string, std::vector<std::string>>>;
999 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001000 [&res, path, propertyName](const boost::system::error_code ec,
1001 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001002 if (ec || object_names.size() <= 0)
1003 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001004 setErrorResponse(res, boost::beast::http::status::not_found,
1005 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 res.end();
1007 return;
1008 }
1009 std::shared_ptr<nlohmann::json> response =
1010 std::make_shared<nlohmann::json>(nlohmann::json::object());
1011 // The mapper should never give us an empty interface names list,
1012 // but check anyway
1013 for (const std::pair<std::string, std::vector<std::string>>
1014 connection : object_names)
1015 {
1016 const std::vector<std::string> &interfaceNames =
1017 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001018
Ed Tanous1abe55e2018-09-05 08:30:59 -07001019 if (interfaceNames.size() <= 0)
1020 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001021 setErrorResponse(res, boost::beast::http::status::not_found,
1022 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001023 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024 return;
1025 }
1026
1027 for (const std::string &interface : interfaceNames)
1028 {
1029 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001030 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001031 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001032 const std::vector<std::pair<
1033 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001034 &properties) {
1035 if (ec)
1036 {
1037 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1038 << ec;
1039 }
1040 else
1041 {
James Feist5b4aa862018-08-16 14:07:01 -07001042 for (const std::pair<
1043 std::string,
1044 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001045 &property : properties)
1046 {
1047 // if property name is empty, or matches our
1048 // search query, add it to the response json
1049
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001050 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001051 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001052 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001053 [&response, &property](auto &&val) {
1054 (*response)[property.first] =
1055 val;
1056 },
1057 property.second);
1058 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001059 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001060 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001061 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001062 [&response](auto &&val) {
1063 (*response) = val;
1064 },
1065 property.second);
1066 }
1067 }
1068 }
1069 if (response.use_count() == 1)
1070 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001071 if (!propertyName->empty() && response->empty())
1072 {
1073 setErrorResponse(
1074 res,
1075 boost::beast::http::status::not_found,
1076 propNotFoundDesc, notFoundMsg);
1077 }
1078 else
1079 {
1080 res.jsonValue = {{"status", "ok"},
1081 {"message", "200 OK"},
1082 {"data", *response}};
1083 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084 res.end();
1085 }
1086 },
1087 connection.first, *path,
1088 "org.freedesktop.DBus.Properties", "GetAll", interface);
1089 }
1090 }
1091 },
1092 "xyz.openbmc_project.ObjectMapper",
1093 "/xyz/openbmc_project/object_mapper",
1094 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1095 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001096}
1097
Ed Tanous1abe55e2018-09-05 08:30:59 -07001098struct AsyncPutRequest
1099{
1100 AsyncPutRequest(crow::Response &res) : res(res)
1101 {
1102 res.jsonValue = {
1103 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
1104 }
1105 ~AsyncPutRequest()
1106 {
1107 if (res.result() == boost::beast::http::status::internal_server_error)
1108 {
1109 // Reset the json object to clear out any data that made it in
1110 // before the error happened todo(ed) handle error condition with
1111 // proper code
1112 res.jsonValue = nlohmann::json::object();
1113 }
1114
1115 if (res.jsonValue.empty())
1116 {
1117 res.result(boost::beast::http::status::forbidden);
1118 res.jsonValue = {
1119 {"status", "error"},
1120 {"message", "403 Forbidden"},
1121 {"data",
1122 {{"message", "The specified property cannot be created: " +
1123 propertyName}}}};
1124 }
1125
1126 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001127 }
1128
Ed Tanous1abe55e2018-09-05 08:30:59 -07001129 void setErrorStatus()
1130 {
1131 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001132 }
1133
Ed Tanous1abe55e2018-09-05 08:30:59 -07001134 crow::Response &res;
1135 std::string objectPath;
1136 std::string propertyName;
1137 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001138};
1139
Ed Tanousd76323e2018-08-07 14:35:40 -07001140void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001141 const std::string &objectPath, const std::string &destProperty)
1142{
1143 nlohmann::json requestDbusData =
1144 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001145
Ed Tanous1abe55e2018-09-05 08:30:59 -07001146 if (requestDbusData.is_discarded())
1147 {
1148 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001149 res.end();
1150 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001151 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001152
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1154 if (propertyIt == requestDbusData.end())
1155 {
1156 res.result(boost::beast::http::status::bad_request);
1157 res.end();
1158 return;
1159 }
1160 const nlohmann::json &propertySetValue = *propertyIt;
1161 auto transaction = std::make_shared<AsyncPutRequest>(res);
1162 transaction->objectPath = objectPath;
1163 transaction->propertyName = destProperty;
1164 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001165
Ed Tanous1abe55e2018-09-05 08:30:59 -07001166 using GetObjectType =
1167 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001168
Ed Tanous1abe55e2018-09-05 08:30:59 -07001169 crow::connections::systemBus->async_method_call(
1170 [transaction](const boost::system::error_code ec,
1171 const GetObjectType &object_names) {
1172 if (!ec && object_names.size() <= 0)
1173 {
1174 transaction->res.result(boost::beast::http::status::not_found);
1175 return;
1176 }
Ed Tanous911ac312017-08-15 09:37:42 -07001177
Ed Tanous1abe55e2018-09-05 08:30:59 -07001178 for (const std::pair<std::string, std::vector<std::string>>
1179 connection : object_names)
1180 {
1181 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001182
Ed Tanous1abe55e2018-09-05 08:30:59 -07001183 crow::connections::systemBus->async_method_call(
1184 [connectionName{std::string(connectionName)},
1185 transaction](const boost::system::error_code ec,
1186 const std::string &introspectXml) {
1187 if (ec)
1188 {
1189 BMCWEB_LOG_ERROR
1190 << "Introspect call failed with error: "
1191 << ec.message()
1192 << " on process: " << connectionName;
1193 transaction->setErrorStatus();
1194 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001195 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001196 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001197
Ed Tanous1abe55e2018-09-05 08:30:59 -07001198 doc.Parse(introspectXml.c_str());
1199 tinyxml2::XMLNode *pRoot =
1200 doc.FirstChildElement("node");
1201 if (pRoot == nullptr)
1202 {
1203 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1204 << introspectXml;
1205 transaction->setErrorStatus();
1206 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001207 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001208 tinyxml2::XMLElement *ifaceNode =
1209 pRoot->FirstChildElement("interface");
1210 while (ifaceNode != nullptr)
1211 {
1212 const char *interfaceName =
1213 ifaceNode->Attribute("name");
1214 BMCWEB_LOG_DEBUG << "found interface "
1215 << interfaceName;
1216 tinyxml2::XMLElement *propNode =
1217 ifaceNode->FirstChildElement("property");
1218 while (propNode != nullptr)
1219 {
1220 const char *propertyName =
1221 propNode->Attribute("name");
1222 BMCWEB_LOG_DEBUG << "Found property "
1223 << propertyName;
1224 if (propertyName == transaction->propertyName)
1225 {
1226 const char *argType =
1227 propNode->Attribute("type");
1228 if (argType != nullptr)
1229 {
1230 sdbusplus::message::message m =
1231 crow::connections::systemBus
1232 ->new_method_call(
1233 connectionName.c_str(),
1234 transaction->objectPath
1235 .c_str(),
1236 "org.freedesktop.DBus."
1237 "Properties",
1238 "Set");
1239 m.append(interfaceName,
1240 transaction->propertyName);
1241 int r = sd_bus_message_open_container(
1242 m.get(), SD_BUS_TYPE_VARIANT,
1243 argType);
1244 if (r < 0)
1245 {
1246 transaction->setErrorStatus();
1247 return;
1248 }
1249 r = convertJsonToDbus(
1250 m.get(), argType,
1251 transaction->propertyValue);
1252 if (r < 0)
1253 {
1254 transaction->setErrorStatus();
1255 return;
1256 }
1257 r = sd_bus_message_close_container(
1258 m.get());
1259 if (r < 0)
1260 {
1261 transaction->setErrorStatus();
1262 return;
1263 }
Ed Tanous911ac312017-08-15 09:37:42 -07001264
Ed Tanous1abe55e2018-09-05 08:30:59 -07001265 crow::connections::systemBus
1266 ->async_send(
1267 m,
1268 [transaction](
1269 boost::system::error_code
1270 ec,
1271 sdbusplus::message::message
1272 &m) {
1273 BMCWEB_LOG_DEBUG << "sent";
1274 if (ec)
1275 {
1276 transaction->res
1277 .jsonValue
1278 ["status"] =
1279 "error";
1280 transaction->res
1281 .jsonValue
1282 ["message"] =
1283 ec.message();
1284 }
1285 });
1286 }
1287 }
1288 propNode =
1289 propNode->NextSiblingElement("property");
1290 }
1291 ifaceNode =
1292 ifaceNode->NextSiblingElement("interface");
1293 }
1294 },
1295 connectionName, transaction->objectPath,
1296 "org.freedesktop.DBus.Introspectable", "Introspect");
1297 }
1298 },
1299 "xyz.openbmc_project.ObjectMapper",
1300 "/xyz/openbmc_project/object_mapper",
1301 "xyz.openbmc_project.ObjectMapper", "GetObject",
1302 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001303}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001304
Ed Tanous049a0512018-11-01 13:58:42 -07001305inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1306 std::string &objectPath)
1307{
Ed Tanous049a0512018-11-01 13:58:42 -07001308
1309 // If accessing a single attribute, fill in and update objectPath,
1310 // otherwise leave destProperty blank
1311 std::string destProperty = "";
1312 const char *attrSeperator = "/attr/";
1313 size_t attrPosition = objectPath.find(attrSeperator);
1314 if (attrPosition != objectPath.npos)
1315 {
1316 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1317 objectPath.length());
1318 objectPath = objectPath.substr(0, attrPosition);
1319 }
1320
1321 if (req.method() == "POST"_method)
1322 {
1323 constexpr const char *actionSeperator = "/action/";
1324 size_t actionPosition = objectPath.find(actionSeperator);
1325 if (actionPosition != objectPath.npos)
1326 {
1327 std::string postProperty =
1328 objectPath.substr((actionPosition + strlen(actionSeperator)),
1329 objectPath.length());
1330 objectPath = objectPath.substr(0, actionPosition);
1331 handleAction(req, res, objectPath, postProperty);
1332 return;
1333 }
1334 }
1335 else if (req.method() == "GET"_method)
1336 {
1337 if (boost::ends_with(objectPath, "/enumerate"))
1338 {
1339 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1340 objectPath.end());
1341 handleEnumerate(res, objectPath);
1342 }
1343 else if (boost::ends_with(objectPath, "/list"))
1344 {
1345 objectPath.erase(objectPath.end() - sizeof("list"),
1346 objectPath.end());
1347 handleList(res, objectPath);
1348 }
1349 else
1350 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001351 // Trim any trailing "/" at the end
1352 if (boost::ends_with(objectPath, "/"))
1353 {
1354 objectPath.pop_back();
1355 handleList(res, objectPath, 1);
1356 }
1357 else
1358 {
1359 handleGet(res, objectPath, destProperty);
1360 }
Ed Tanous049a0512018-11-01 13:58:42 -07001361 }
1362 return;
1363 }
1364 else if (req.method() == "PUT"_method)
1365 {
1366 handlePut(req, res, objectPath, destProperty);
1367 return;
1368 }
1369
1370 res.result(boost::beast::http::status::method_not_allowed);
1371 res.end();
1372}
1373
Ed Tanous1abe55e2018-09-05 08:30:59 -07001374template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1375{
1376 BMCWEB_ROUTE(app, "/bus/")
1377 .methods("GET"_method)(
1378 [](const crow::Request &req, crow::Response &res) {
1379 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1380 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001381 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001382 });
1383
1384 BMCWEB_ROUTE(app, "/bus/system/")
1385 .methods("GET"_method)(
1386 [](const crow::Request &req, crow::Response &res) {
1387 auto myCallback = [&res](const boost::system::error_code ec,
1388 std::vector<std::string> &names) {
1389 if (ec)
1390 {
1391 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1392 res.result(
1393 boost::beast::http::status::internal_server_error);
1394 }
1395 else
1396 {
1397 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001398 res.jsonValue = {{"status", "ok"}};
1399 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001400 for (auto &name : names)
1401 {
1402 objectsSub.push_back({{"name", name}});
1403 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001404 }
1405 res.end();
1406 };
1407 crow::connections::systemBus->async_method_call(
1408 std::move(myCallback), "org.freedesktop.DBus", "/",
1409 "org.freedesktop.DBus", "ListNames");
1410 });
1411
1412 BMCWEB_ROUTE(app, "/list/")
1413 .methods("GET"_method)(
1414 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001415 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001416 });
1417
1418 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous049a0512018-11-01 13:58:42 -07001419 .methods("GET"_method, "PUT"_method, "POST"_method)(
1420 [](const crow::Request &req, crow::Response &res,
1421 const std::string &path) {
1422 std::string objectPath = "/xyz/" + path;
1423 handleDBusUrl(req, res, objectPath);
1424 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001425
Ed Tanous049a0512018-11-01 13:58:42 -07001426 BMCWEB_ROUTE(app, "/org/<path>")
1427 .methods("GET"_method, "PUT"_method, "POST"_method)(
1428 [](const crow::Request &req, crow::Response &res,
1429 const std::string &path) {
1430 std::string objectPath = "/org/" + path;
1431 handleDBusUrl(req, res, objectPath);
1432 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001433
Ed Tanous1abe55e2018-09-05 08:30:59 -07001434 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1435 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1436 const std::string &dumpId) {
1437 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1438 if (!std::regex_match(dumpId, validFilename))
1439 {
1440 res.result(boost::beast::http::status::not_found);
1441 res.end();
1442 return;
1443 }
1444 std::experimental::filesystem::path loc(
1445 "/var/lib/phosphor-debug-collector/dumps");
1446
1447 loc += dumpId;
1448
1449 if (!std::experimental::filesystem::exists(loc) ||
1450 !std::experimental::filesystem::is_directory(loc))
1451 {
1452 res.result(boost::beast::http::status::not_found);
1453 res.end();
1454 return;
1455 }
1456 std::experimental::filesystem::directory_iterator files(loc);
1457 for (auto &file : files)
1458 {
1459 std::ifstream readFile(file.path());
1460 if (readFile.good())
1461 {
1462 continue;
1463 }
1464 res.addHeader("Content-Type", "application/octet-stream");
1465 res.body() = {std::istreambuf_iterator<char>(readFile),
1466 std::istreambuf_iterator<char>()};
1467 res.end();
1468 }
1469 res.result(boost::beast::http::status::not_found);
1470 res.end();
1471 return;
1472 });
1473
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001474 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001475 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001476 const std::string &Connection) {
1477 introspectObjects(Connection, "/",
1478 std::make_shared<bmcweb::AsyncResp>(res));
1479 });
1480
1481 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1482 .methods("GET"_method,
1483 "POST"_method)([](const crow::Request &req,
1484 crow::Response &res,
1485 const std::string &processName,
1486 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001487 std::vector<std::string> strs;
1488 boost::split(strs, requestedPath, boost::is_any_of("/"));
1489 std::string objectPath;
1490 std::string interfaceName;
1491 std::string methodName;
1492 auto it = strs.begin();
1493 if (it == strs.end())
1494 {
1495 objectPath = "/";
1496 }
1497 while (it != strs.end())
1498 {
1499 // Check if segment contains ".". If it does, it must be an
1500 // interface
1501 if (it->find(".") != std::string::npos)
1502 {
1503 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001504 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001505 // as part of our <path> specifier above, which causes the
1506 // normal trailing backslash redirector to fail.
1507 }
1508 else if (!it->empty())
1509 {
1510 objectPath += "/" + *it;
1511 }
1512 it++;
1513 }
1514 if (it != strs.end())
1515 {
1516 interfaceName = *it;
1517 it++;
1518
1519 // after interface, we might have a method name
1520 if (it != strs.end())
1521 {
1522 methodName = *it;
1523 it++;
1524 }
1525 }
1526 if (it != strs.end())
1527 {
1528 // if there is more levels past the method name, something went
1529 // wrong, return not found
1530 res.result(boost::beast::http::status::not_found);
1531 res.end();
1532 return;
1533 }
1534 if (interfaceName.empty())
1535 {
1536 crow::connections::systemBus->async_method_call(
1537 [&, processName,
1538 objectPath](const boost::system::error_code ec,
1539 const std::string &introspect_xml) {
1540 if (ec)
1541 {
1542 BMCWEB_LOG_ERROR
1543 << "Introspect call failed with error: "
1544 << ec.message()
1545 << " on process: " << processName
1546 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001547 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001548 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001549 tinyxml2::XMLDocument doc;
1550
1551 doc.Parse(introspect_xml.c_str());
1552 tinyxml2::XMLNode *pRoot =
1553 doc.FirstChildElement("node");
1554 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001555 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001556 BMCWEB_LOG_ERROR << "XML document failed to parse "
1557 << processName << " " << objectPath
1558 << "\n";
1559 res.jsonValue = {{"status", "XML parse error"}};
1560 res.result(boost::beast::http::status::
1561 internal_server_error);
1562 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001563 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001564
1565 BMCWEB_LOG_DEBUG << introspect_xml;
1566 res.jsonValue = {{"status", "ok"},
1567 {"bus_name", processName},
1568 {"object_path", objectPath}};
1569 nlohmann::json &interfacesArray =
1570 res.jsonValue["interfaces"];
1571 interfacesArray = nlohmann::json::array();
1572 tinyxml2::XMLElement *interface =
1573 pRoot->FirstChildElement("interface");
1574
1575 while (interface != nullptr)
1576 {
1577 const char *ifaceName =
1578 interface->Attribute("name");
1579 if (ifaceName != nullptr)
1580 {
1581 interfacesArray.push_back(
1582 {{"name", ifaceName}});
1583 }
1584
1585 interface =
1586 interface->NextSiblingElement("interface");
1587 }
1588
Ed Tanous1abe55e2018-09-05 08:30:59 -07001589 res.end();
1590 },
1591 processName, objectPath,
1592 "org.freedesktop.DBus.Introspectable", "Introspect");
1593 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001594 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001595 {
1596 crow::connections::systemBus->async_method_call(
1597 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001598 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001599 const boost::system::error_code ec,
1600 const std::string &introspect_xml) {
1601 if (ec)
1602 {
1603 BMCWEB_LOG_ERROR
1604 << "Introspect call failed with error: "
1605 << ec.message()
1606 << " on process: " << processName
1607 << " path: " << objectPath << "\n";
1608 }
1609 else
1610 {
1611 tinyxml2::XMLDocument doc;
1612
1613 doc.Parse(introspect_xml.c_str());
1614 tinyxml2::XMLNode *pRoot =
1615 doc.FirstChildElement("node");
1616 if (pRoot == nullptr)
1617 {
1618 BMCWEB_LOG_ERROR
1619 << "XML document failed to parse "
1620 << processName << " " << objectPath << "\n";
1621 res.result(boost::beast::http::status::
1622 internal_server_error);
1623 }
1624 else
1625 {
1626 tinyxml2::XMLElement *node =
1627 pRoot->FirstChildElement("node");
1628
1629 // if we know we're the only call, build the
1630 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001631 tinyxml2::XMLElement *interface =
1632 pRoot->FirstChildElement("interface");
1633
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001634 res.jsonValue = {
1635 {"status", "ok"},
1636 {"bus_name", processName},
1637 {"interface", interfaceName},
1638 {"object_path", objectPath},
1639 {"properties", nlohmann::json::object()}};
1640
1641 nlohmann::json &methodsArray =
1642 res.jsonValue["methods"];
1643 methodsArray = nlohmann::json::array();
1644
1645 nlohmann::json &signalsArray =
1646 res.jsonValue["signals"];
1647 signalsArray = nlohmann::json::array();
1648
Ed Tanous1abe55e2018-09-05 08:30:59 -07001649 while (interface != nullptr)
1650 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001651 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001652 interface->Attribute("name");
1653
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001654 if (ifaceName != nullptr &&
1655 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001656 {
1657 tinyxml2::XMLElement *methods =
1658 interface->FirstChildElement(
1659 "method");
1660 while (methods != nullptr)
1661 {
1662 nlohmann::json argsArray =
1663 nlohmann::json::array();
1664 tinyxml2::XMLElement *arg =
1665 methods->FirstChildElement(
1666 "arg");
1667 while (arg != nullptr)
1668 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001669 nlohmann::json thisArg;
1670 for (const char *fieldName :
1671 std::array<const char *,
1672 3>{"name",
1673 "direction",
1674 "type"})
1675 {
1676 const char *fieldValue =
1677 arg->Attribute(
1678 fieldName);
1679 if (fieldValue != nullptr)
1680 {
1681 thisArg[fieldName] =
1682 fieldValue;
1683 }
1684 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001685 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001686 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001687 arg = arg->NextSiblingElement(
1688 "arg");
1689 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001690
1691 const char *name =
1692 methods->Attribute("name");
1693 if (name != nullptr)
1694 {
1695 methodsArray.push_back(
1696 {{"name", name},
1697 {"uri", "/bus/system/" +
1698 processName +
1699 objectPath +
1700 "/" +
1701 interfaceName +
1702 "/" + name},
1703 {"args", argsArray}});
1704 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001705 methods =
1706 methods->NextSiblingElement(
1707 "method");
1708 }
1709 tinyxml2::XMLElement *signals =
1710 interface->FirstChildElement(
1711 "signal");
1712 while (signals != nullptr)
1713 {
1714 nlohmann::json argsArray =
1715 nlohmann::json::array();
1716
1717 tinyxml2::XMLElement *arg =
1718 signals->FirstChildElement(
1719 "arg");
1720 while (arg != nullptr)
1721 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001722 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001723 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001724 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001725 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001726 if (name != nullptr &&
1727 type != nullptr)
1728 {
1729 argsArray.push_back({
1730 {"name", name},
1731 {"type", type},
1732 });
1733 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 arg = arg->NextSiblingElement(
1735 "arg");
1736 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001737 const char *name =
1738 signals->Attribute("name");
1739 if (name != nullptr)
1740 {
1741 signalsArray.push_back(
1742 {{"name", name},
1743 {"args", argsArray}});
1744 }
1745
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 signals =
1747 signals->NextSiblingElement(
1748 "signal");
1749 }
1750
Ed Tanous1abe55e2018-09-05 08:30:59 -07001751 break;
1752 }
1753
1754 interface = interface->NextSiblingElement(
1755 "interface");
1756 }
1757 if (interface == nullptr)
1758 {
1759 // if we got to the end of the list and
1760 // never found a match, throw 404
1761 res.result(
1762 boost::beast::http::status::not_found);
1763 }
1764 }
1765 }
1766 res.end();
1767 },
1768 processName, objectPath,
1769 "org.freedesktop.DBus.Introspectable", "Introspect");
1770 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001771 else
1772 {
1773 if (req.method() != "POST"_method)
1774 {
1775 res.result(boost::beast::http::status::not_found);
1776 res.end();
1777 return;
1778 }
1779
1780 nlohmann::json requestDbusData =
1781 nlohmann::json::parse(req.body, nullptr, false);
1782
1783 if (requestDbusData.is_discarded())
1784 {
1785 res.result(boost::beast::http::status::bad_request);
1786 res.end();
1787 return;
1788 }
1789 if (!requestDbusData.is_array())
1790 {
1791 res.result(boost::beast::http::status::bad_request);
1792 res.end();
1793 return;
1794 }
1795 auto transaction = std::make_shared<InProgressActionData>(res);
1796
1797 transaction->path = objectPath;
1798 transaction->methodName = methodName;
1799 transaction->arguments = std::move(requestDbusData);
1800
1801 findActionOnInterface(transaction, processName);
1802 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001803 });
1804}
1805} // namespace openbmc_mapper
1806} // namespace crow