blob: c8811a6e4efca97501a8ed9a5ec1fc64b8425703 [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 Spinler6db06242018-12-11 11:21:22 -060040
Matt Spinler2ae60092018-12-06 10:35:36 -060041const std::string notFoundDesc =
42 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060043const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060044const std::string noJsonDesc = "No JSON object could be decoded";
45const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060046const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinler2ae60092018-12-06 10:35:36 -060047
48void setErrorResponse(crow::Response &res, boost::beast::http::status result,
49 const std::string &desc, const std::string &msg)
50{
51 res.result(result);
52 res.jsonValue = {{"data", {{"description", desc}}},
53 {"message", msg},
54 {"status", "error"}};
55}
56
Ed Tanouse3cb5a32018-08-08 14:16:49 -070057void introspectObjects(const std::string &processName,
58 const std::string &objectPath,
59 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070060{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070061 if (transaction->res.jsonValue.is_null())
62 {
63 transaction->res.jsonValue = {{"status", "ok"},
64 {"bus_name", processName},
65 {"objects", nlohmann::json::array()}};
66 }
67
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070069 [transaction, processName{std::string(processName)},
70 objectPath{std::string(objectPath)}](
71 const boost::system::error_code ec,
72 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 if (ec)
74 {
75 BMCWEB_LOG_ERROR
76 << "Introspect call failed with error: " << ec.message()
77 << " on process: " << processName << " path: " << objectPath
78 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070079 return;
80 }
81 transaction->res.jsonValue["objects"].push_back(
82 {{"path", objectPath}});
83
84 tinyxml2::XMLDocument doc;
85
86 doc.Parse(introspect_xml.c_str());
87 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
88 if (pRoot == nullptr)
89 {
90 BMCWEB_LOG_ERROR << "XML document failed to parse "
91 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070092 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 else
94 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070095 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
96 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070098 const char *childPath = node->Attribute("name");
99 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 std::string newpath;
102 if (objectPath != "/")
103 {
104 newpath += objectPath;
105 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700106 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700108 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700110
111 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 }
113 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700115 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700117}
Ed Tanous64530012018-02-06 17:08:16 -0800118
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600119void getPropertiesForEnumerate(const std::string &objectPath,
120 const std::string &service,
121 const std::string &interface,
122 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
123{
124 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
125 << service << " " << interface;
126
127 crow::connections::systemBus->async_method_call(
128 [asyncResp, objectPath, service,
129 interface](const boost::system::error_code ec,
130 const std::vector<
131 std::pair<std::string, dbus::utility::DbusVariantType>>
132 &propertiesList) {
133 if (ec)
134 {
135 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
136 << interface << " service " << service
137 << " failed with code " << ec;
138 return;
139 }
140
141 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
142 nlohmann::json &objectJson = dataJson[objectPath];
143 if (objectJson.is_null())
144 {
145 objectJson = nlohmann::json::object();
146 }
147
148 for (const auto &[name, value] : propertiesList)
149 {
150 nlohmann::json &propertyJson = objectJson[name];
151 sdbusplus::message::variant_ns::visit(
152 [&propertyJson](auto &&val) { propertyJson = val; }, value);
153 }
154 },
155 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
156 interface);
157}
158
159// Find any results that weren't picked up by ObjectManagers, to be
160// called after all ObjectManagers are searched for and called.
161void findRemainingObjectsForEnumerate(
162 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
163 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
164{
165 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
166 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
167
168 for (const auto &[path, interface_map] : *subtree)
169 {
170 if (path == objectPath)
171 {
172 // An enumerate does not return the target path's properties
173 continue;
174 }
175 if (dataJson.find(path) == dataJson.end())
176 {
177 for (const auto &[service, interfaces] : interface_map)
178 {
179 for (const auto &interface : interfaces)
180 {
181 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
182 {
183 getPropertiesForEnumerate(path, service, interface,
184 asyncResp);
185 }
186 }
187 }
188 }
189 }
190}
191
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600192struct InProgressEnumerateData
193{
194 InProgressEnumerateData(const std::string &objectPath,
195 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
196 objectPath(objectPath),
197 asyncResp(asyncResp)
198 {
199 }
200
201 ~InProgressEnumerateData()
202 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600203 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600204 }
205
206 const std::string objectPath;
207 std::shared_ptr<GetSubTreeType> subtree;
208 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
209};
210
211void getManagedObjectsForEnumerate(
212 const std::string &object_name, const std::string &object_manager_path,
213 const std::string &connection_name,
214 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215{
Ed Tanous049a0512018-11-01 13:58:42 -0700216 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
217 << " object_manager_path " << object_manager_path
218 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600220 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700221 connection_name](const boost::system::error_code ec,
222 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223 if (ec)
224 {
Ed Tanous049a0512018-11-01 13:58:42 -0700225 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600226 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700227 << " failed with code " << ec;
228 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700229 }
Ed Tanous64530012018-02-06 17:08:16 -0800230
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600231 nlohmann::json &dataJson =
232 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700233
234 for (const auto &objectPath : objects)
235 {
236 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700237 {
Ed Tanous049a0512018-11-01 13:58:42 -0700238 BMCWEB_LOG_DEBUG << "Reading object "
239 << objectPath.first.str;
240 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 if (objectJson.is_null())
242 {
243 objectJson = nlohmann::json::object();
244 }
245 for (const auto &interface : objectPath.second)
246 {
247 for (const auto &property : interface.second)
248 {
249 nlohmann::json &propertyJson =
250 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700251 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 [&propertyJson](auto &&val) {
253 propertyJson = val;
254 },
255 property.second);
256 }
257 }
258 }
Ed Tanous049a0512018-11-01 13:58:42 -0700259 for (const auto &interface : objectPath.second)
260 {
261 if (interface.first == "org.freedesktop.DBus.ObjectManager")
262 {
263 getManagedObjectsForEnumerate(
264 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600265 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700266 }
267 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700268 }
269 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700270 connection_name, object_manager_path,
271 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
272}
273
274void findObjectManagerPathForEnumerate(
275 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600276 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700277{
Ed Tanous049a0512018-11-01 13:58:42 -0700278 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
279 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700280 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600281 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700282 const boost::system::error_code ec,
283 const boost::container::flat_map<
284 std::string, boost::container::flat_map<
285 std::string, std::vector<std::string>>>
286 &objects) {
287 if (ec)
288 {
Ed Tanous049a0512018-11-01 13:58:42 -0700289 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
290 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700291 return;
292 }
293
Ed Tanousf254ba72018-10-12 13:40:35 -0700294 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700295 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700296 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700297 {
298 if (connectionGroup.first == connection_name)
299 {
300 // Found the object manager path for this resource.
301 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700302 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600303 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700304 return;
305 }
306 }
307 }
308 },
309 "xyz.openbmc_project.ObjectMapper",
310 "/xyz/openbmc_project/object_mapper",
311 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
312 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700313}
Ed Tanous64530012018-02-06 17:08:16 -0800314
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600315// Uses GetObject to add the object info about the target /enumerate path to the
316// results of GetSubTree, as GetSubTree will not return info for the
317// target path, and then continues on enumerating the rest of the tree.
318void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
319{
320 using GetObjectType =
321 std::vector<std::pair<std::string, std::vector<std::string>>>;
322
323 crow::connections::systemBus->async_method_call(
324 [transaction](const boost::system::error_code ec,
325 const GetObjectType &objects) {
326 if (ec)
327 {
328 BMCWEB_LOG_ERROR << "GetObject for path "
329 << transaction->objectPath
330 << " failed with code " << ec;
331 return;
332 }
333
334 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
335 << " has " << objects.size() << " entries";
336 if (!objects.empty())
337 {
338 transaction->subtree->emplace_back(transaction->objectPath,
339 objects);
340 }
341
342 // Map indicating connection name, and the path where the object
343 // manager exists
344 boost::container::flat_map<std::string, std::string> connections;
345
346 for (const auto &object : *(transaction->subtree))
347 {
348 for (const auto &connection : object.second)
349 {
350 std::string &objectManagerPath =
351 connections[connection.first];
352 for (const auto &interface : connection.second)
353 {
354 BMCWEB_LOG_DEBUG << connection.first
355 << " has interface " << interface;
356 if (interface == "org.freedesktop.DBus.ObjectManager")
357 {
358 BMCWEB_LOG_DEBUG << "found object manager path "
359 << object.first;
360 objectManagerPath = object.first;
361 }
362 }
363 }
364 }
365 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
366
367 for (const auto &connection : connections)
368 {
369 // If we already know where the object manager is, we don't need
370 // to search for it, we can call directly in to
371 // getManagedObjects
372 if (!connection.second.empty())
373 {
374 getManagedObjectsForEnumerate(
375 transaction->objectPath, connection.second,
376 connection.first, transaction);
377 }
378 else
379 {
380 // otherwise we need to find the object manager path before
381 // we can continue
382 findObjectManagerPathForEnumerate(
383 transaction->objectPath, connection.first, transaction);
384 }
385 }
386 },
387 "xyz.openbmc_project.ObjectMapper",
388 "/xyz/openbmc_project/object_mapper",
389 "xyz.openbmc_project.ObjectMapper", "GetObject",
390 transaction->objectPath, std::array<const char *, 0>());
391}
Ed Tanous64530012018-02-06 17:08:16 -0800392
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700393// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700394struct InProgressActionData
395{
396 InProgressActionData(crow::Response &res) : res(res){};
397 ~InProgressActionData()
398 {
Matt Spinler6db06242018-12-11 11:21:22 -0600399 // If still no JSON filled in, then we never found the method.
400 if (res.jsonValue.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700401 {
Matt Spinler6db06242018-12-11 11:21:22 -0600402 setErrorResponse(res, boost::beast::http::status::not_found,
403 methodNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 }
405 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700406 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700407
Matt Spinler6db06242018-12-11 11:21:22 -0600408 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700409 {
Matt Spinler6db06242018-12-11 11:21:22 -0600410 setErrorResponse(res, boost::beast::http::status::internal_server_error,
411 desc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 }
413 crow::Response &res;
414 std::string path;
415 std::string methodName;
416 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700417};
418
Ed Tanous1abe55e2018-09-05 08:30:59 -0700419std::vector<std::string> dbusArgSplit(const std::string &string)
420{
421 std::vector<std::string> ret;
422 if (string.empty())
423 {
424 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700425 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700426 ret.push_back("");
427 int containerDepth = 0;
428
429 for (std::string::const_iterator character = string.begin();
430 character != string.end(); character++)
431 {
432 ret.back() += *character;
433 switch (*character)
434 {
435 case ('a'):
436 break;
437 case ('('):
438 case ('{'):
439 containerDepth++;
440 break;
441 case ('}'):
442 case (')'):
443 containerDepth--;
444 if (containerDepth == 0)
445 {
446 if (character + 1 != string.end())
447 {
448 ret.push_back("");
449 }
450 }
451 break;
452 default:
453 if (containerDepth == 0)
454 {
455 if (character + 1 != string.end())
456 {
457 ret.push_back("");
458 }
459 }
460 break;
461 }
462 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700463}
464
Ed Tanousd76323e2018-08-07 14:35:40 -0700465int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 const nlohmann::json &input_json)
467{
468 int r = 0;
469 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
470 << " to type: " << arg_type;
471 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700472
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473 // Assume a single object for now.
474 const nlohmann::json *j = &input_json;
475 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700476
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700477 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 {
479 // If we are decoding multiple objects, grab the pointer to the
480 // iterator, and increment it for the next loop
481 if (argTypes.size() > 1)
482 {
483 if (jIt == input_json.end())
484 {
485 return -2;
486 }
487 j = &*jIt;
488 jIt++;
489 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700490 const int64_t *intValue = j->get_ptr<const int64_t *>();
491 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
492 const std::string *stringValue = j->get_ptr<const std::string *>();
493 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 const bool *b = j->get_ptr<const bool *>();
495 int64_t v = 0;
496 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700497
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498 // Do some basic type conversions that make sense. uint can be
499 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700500 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700502 v = static_cast<int64_t>(*uintValue);
503 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700504 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700505 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700507 d = static_cast<double>(*uintValue);
508 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700510 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700511 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700512 d = static_cast<double>(*intValue);
513 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700514 }
515
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700516 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700518 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700519 {
520 return -1;
521 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700522 r = sd_bus_message_append_basic(m, argCode[0],
523 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 if (r < 0)
525 {
526 return r;
527 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700528 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700529 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700531 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700532 {
533 return -1;
534 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700535 int32_t i = static_cast<int32_t>(*intValue);
536 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700537 if (r < 0)
538 {
539 return r;
540 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700541 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700542 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 {
544 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700545 int boolInt = false;
546 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700548 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 }
550 else if (b != nullptr)
551 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700554 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700556 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 }
558 else
559 {
560 return -1;
561 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700562 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 if (r < 0)
564 {
565 return r;
566 }
567 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700570 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571 {
572 return -1;
573 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700574 int16_t n = static_cast<int16_t>(*intValue);
575 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 if (r < 0)
577 {
578 return r;
579 }
580 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700583 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 {
585 return -1;
586 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700587 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588 if (r < 0)
589 {
590 return r;
591 }
592 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700593 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700595 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 {
597 return -1;
598 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700599 uint8_t y = static_cast<uint8_t>(*uintValue);
600 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700602 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700604 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 {
606 return -1;
607 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700608 uint16_t q = static_cast<uint16_t>(*uintValue);
609 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700611 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700613 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 {
615 return -1;
616 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700617 uint32_t u = static_cast<uint32_t>(*uintValue);
618 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700620 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700622 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 {
624 return -1;
625 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700628 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700630 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700632 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700634 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700636 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 if (r < 0)
638 {
639 return r;
640 }
641
642 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
643 ++it)
644 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700645 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700646 if (r < 0)
647 {
648 return r;
649 }
650
651 it++;
652 }
653 sd_bus_message_close_container(m);
654 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700655 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700657 std::string containedType = argCode.substr(1);
658 BMCWEB_LOG_DEBUG << "variant type: " << argCode
659 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700661 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 if (r < 0)
663 {
664 return r;
665 }
666
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700667 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 if (r < 0)
669 {
670 return r;
671 }
672
673 r = sd_bus_message_close_container(m);
674 if (r < 0)
675 {
676 return r;
677 }
678 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700679 else if (boost::starts_with(argCode, "(") &&
680 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 {
688 if (it == j->end())
689 {
690 return -1;
691 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700692 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 if (r < 0)
694 {
695 return r;
696 }
697 it++;
698 }
699 r = sd_bus_message_close_container(m);
700 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700701 else if (boost::starts_with(argCode, "{") &&
702 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700706 containedType.c_str());
707 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 if (codes.size() != 2)
709 {
710 return -1;
711 }
712 const std::string &key_type = codes[0];
713 const std::string &value_type = codes[1];
714 for (auto it : j->items())
715 {
716 r = convertJsonToDbus(m, key_type, it.key());
717 if (r < 0)
718 {
719 return r;
720 }
721
722 r = convertJsonToDbus(m, value_type, it.value());
723 if (r < 0)
724 {
725 return r;
726 }
727 }
728 r = sd_bus_message_close_container(m);
729 }
730 else
731 {
732 return -2;
733 }
734 if (r < 0)
735 {
736 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700737 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700738
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 if (argTypes.size() > 1)
740 {
741 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700742 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700743 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700744}
745
Ed Tanousd76323e2018-08-07 14:35:40 -0700746void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 const std::string &connectionName)
748{
749 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
750 << connectionName;
751 crow::connections::systemBus->async_method_call(
752 [transaction, connectionName{std::string(connectionName)}](
753 const boost::system::error_code ec,
754 const std::string &introspect_xml) {
755 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
756 if (ec)
757 {
758 BMCWEB_LOG_ERROR
759 << "Introspect call failed with error: " << ec.message()
760 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700761 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700762 else
763 {
764 tinyxml2::XMLDocument doc;
765
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700766 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
768 if (pRoot == nullptr)
769 {
770 BMCWEB_LOG_ERROR << "XML document failed to parse "
771 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700772 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700774 tinyxml2::XMLElement *interfaceNode =
775 pRoot->FirstChildElement("interface");
776 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700777 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700778 const char *thisInterfaceName =
779 interfaceNode->Attribute("name");
780 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700782 tinyxml2::XMLElement *methodNode =
783 interfaceNode->FirstChildElement("method");
784 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700786 const char *thisMethodName =
787 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700788 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700789 << thisMethodName;
790 if (thisMethodName != nullptr &&
791 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700793 BMCWEB_LOG_DEBUG
794 << "Found method named " << thisMethodName
795 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 sdbusplus::message::message m =
797 crow::connections::systemBus
798 ->new_method_call(
799 connectionName.c_str(),
800 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700801 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 transaction->methodName.c_str());
803
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700804 tinyxml2::XMLElement *argumentNode =
805 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700807 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 transaction->arguments.begin();
809
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700810 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700812 const char *argDirection =
813 argumentNode->Attribute("direction");
814 const char *argType =
815 argumentNode->Attribute("type");
816 if (argDirection != nullptr &&
817 argType != nullptr &&
818 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700820
821 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700822 transaction->arguments.end())
823 {
Matt Spinler6db06242018-12-11 11:21:22 -0600824 transaction->setErrorStatus(
825 "Invalid method args");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700826 return;
827 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700828 if (convertJsonToDbus(
829 m.get(), std::string(argType),
830 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700831 {
Matt Spinler6db06242018-12-11 11:21:22 -0600832 transaction->setErrorStatus(
833 "Invalid method arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 return;
835 }
836
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700837 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700839 argumentNode =
840 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700842
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 crow::connections::systemBus->async_send(
844 m, [transaction](
845 boost::system::error_code ec,
846 sdbusplus::message::message &m) {
847 if (ec)
848 {
Matt Spinler6db06242018-12-11 11:21:22 -0600849 transaction->setErrorStatus(
850 "Method call failed");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 return;
852 }
853 transaction->res.jsonValue = {
854 {"status", "ok"},
855 {"message", "200 OK"},
856 {"data", nullptr}};
857 });
858 break;
859 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700860 methodNode =
861 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700864 interfaceNode =
865 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 }
867 }
868 },
869 connectionName, transaction->path,
870 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700871}
872
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700873void handleAction(const crow::Request &req, crow::Response &res,
874 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700876 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
877 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 nlohmann::json requestDbusData =
879 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700880
Ed Tanous1abe55e2018-09-05 08:30:59 -0700881 if (requestDbusData.is_discarded())
882 {
Matt Spinler6db06242018-12-11 11:21:22 -0600883 setErrorResponse(res, boost::beast::http::status::bad_request,
884 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700885 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886 return;
887 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700888 nlohmann::json::iterator data = requestDbusData.find("data");
889 if (data == requestDbusData.end())
890 {
Matt Spinler6db06242018-12-11 11:21:22 -0600891 setErrorResponse(res, boost::beast::http::status::bad_request,
892 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700893 res.end();
894 return;
895 }
896
897 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700898 {
Matt Spinler6db06242018-12-11 11:21:22 -0600899 setErrorResponse(res, boost::beast::http::status::bad_request,
900 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700901 res.end();
902 return;
903 }
904 auto transaction = std::make_shared<InProgressActionData>(res);
905
906 transaction->path = objectPath;
907 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700908 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700909 crow::connections::systemBus->async_method_call(
910 [transaction](
911 const boost::system::error_code ec,
912 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700913 &interfaceNames) {
914 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700915 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700916 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -0600917 setErrorResponse(transaction->res,
918 boost::beast::http::status::not_found,
919 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 return;
921 }
922
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700923 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
924 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925
926 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700927 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700928 {
929 findActionOnInterface(transaction, object.first);
930 }
931 },
932 "xyz.openbmc_project.ObjectMapper",
933 "/xyz/openbmc_project/object_mapper",
934 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
935 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700936}
937
Ed Tanousf839dfe2018-11-12 11:11:15 -0800938void handleList(crow::Response &res, const std::string &objectPath,
939 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700940{
941 crow::connections::systemBus->async_method_call(
942 [&res](const boost::system::error_code ec,
943 std::vector<std::string> &objectPaths) {
944 if (ec)
945 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -0600946 setErrorResponse(res, boost::beast::http::status::not_found,
947 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700948 }
949 else
950 {
951 res.jsonValue = {{"status", "ok"},
952 {"message", "200 OK"},
953 {"data", std::move(objectPaths)}};
954 }
955 res.end();
956 },
957 "xyz.openbmc_project.ObjectMapper",
958 "/xyz/openbmc_project/object_mapper",
959 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -0800960 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700961}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700962
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700963void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700964{
Ed Tanous049a0512018-11-01 13:58:42 -0700965 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
966 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
967
968 asyncResp->res.jsonValue = {{"message", "200 OK"},
969 {"status", "ok"},
970 {"data", nlohmann::json::object()}};
971
Ed Tanous1abe55e2018-09-05 08:30:59 -0700972 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600973 [objectPath, asyncResp](const boost::system::error_code ec,
974 GetSubTreeType &object_names) {
975 auto transaction = std::make_shared<InProgressEnumerateData>(
976 objectPath, asyncResp);
977
978 transaction->subtree =
979 std::make_shared<GetSubTreeType>(std::move(object_names));
980
Ed Tanous1abe55e2018-09-05 08:30:59 -0700981 if (ec)
982 {
Matt Spinler2ae60092018-12-06 10:35:36 -0600983 BMCWEB_LOG_ERROR << "GetSubTree failed on "
984 << transaction->objectPath;
985 setErrorResponse(transaction->asyncResp->res,
986 boost::beast::http::status::not_found,
987 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700988 return;
989 }
Ed Tanous64530012018-02-06 17:08:16 -0800990
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600991 // Add the data for the path passed in to the results
992 // as if GetSubTree returned it, and continue on enumerating
993 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 },
995 "xyz.openbmc_project.ObjectMapper",
996 "/xyz/openbmc_project/object_mapper",
997 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -0700998 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800999}
Ed Tanous911ac312017-08-15 09:37:42 -07001000
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001001void handleGet(crow::Response &res, std::string &objectPath,
1002 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001003{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001004 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1005 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001007
Ed Tanous1abe55e2018-09-05 08:30:59 -07001008 std::shared_ptr<std::string> path =
1009 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001010
Ed Tanous1abe55e2018-09-05 08:30:59 -07001011 using GetObjectType =
1012 std::vector<std::pair<std::string, std::vector<std::string>>>;
1013 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001014 [&res, path, propertyName](const boost::system::error_code ec,
1015 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016 if (ec || object_names.size() <= 0)
1017 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001018 setErrorResponse(res, boost::beast::http::status::not_found,
1019 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001020 res.end();
1021 return;
1022 }
1023 std::shared_ptr<nlohmann::json> response =
1024 std::make_shared<nlohmann::json>(nlohmann::json::object());
1025 // The mapper should never give us an empty interface names list,
1026 // but check anyway
1027 for (const std::pair<std::string, std::vector<std::string>>
1028 connection : object_names)
1029 {
1030 const std::vector<std::string> &interfaceNames =
1031 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001032
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 if (interfaceNames.size() <= 0)
1034 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001035 setErrorResponse(res, boost::beast::http::status::not_found,
1036 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001037 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001038 return;
1039 }
1040
1041 for (const std::string &interface : interfaceNames)
1042 {
1043 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001044 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001045 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001046 const std::vector<std::pair<
1047 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001048 &properties) {
1049 if (ec)
1050 {
1051 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1052 << ec;
1053 }
1054 else
1055 {
James Feist5b4aa862018-08-16 14:07:01 -07001056 for (const std::pair<
1057 std::string,
1058 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001059 &property : properties)
1060 {
1061 // if property name is empty, or matches our
1062 // search query, add it to the response json
1063
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001064 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001065 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001066 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001067 [&response, &property](auto &&val) {
1068 (*response)[property.first] =
1069 val;
1070 },
1071 property.second);
1072 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001073 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001074 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001075 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001076 [&response](auto &&val) {
1077 (*response) = val;
1078 },
1079 property.second);
1080 }
1081 }
1082 }
1083 if (response.use_count() == 1)
1084 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001085 if (!propertyName->empty() && response->empty())
1086 {
1087 setErrorResponse(
1088 res,
1089 boost::beast::http::status::not_found,
1090 propNotFoundDesc, notFoundMsg);
1091 }
1092 else
1093 {
1094 res.jsonValue = {{"status", "ok"},
1095 {"message", "200 OK"},
1096 {"data", *response}};
1097 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001098 res.end();
1099 }
1100 },
1101 connection.first, *path,
1102 "org.freedesktop.DBus.Properties", "GetAll", interface);
1103 }
1104 }
1105 },
1106 "xyz.openbmc_project.ObjectMapper",
1107 "/xyz/openbmc_project/object_mapper",
1108 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1109 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001110}
1111
Ed Tanous1abe55e2018-09-05 08:30:59 -07001112struct AsyncPutRequest
1113{
1114 AsyncPutRequest(crow::Response &res) : res(res)
1115 {
1116 res.jsonValue = {
1117 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
1118 }
1119 ~AsyncPutRequest()
1120 {
1121 if (res.result() == boost::beast::http::status::internal_server_error)
1122 {
1123 // Reset the json object to clear out any data that made it in
1124 // before the error happened todo(ed) handle error condition with
1125 // proper code
1126 res.jsonValue = nlohmann::json::object();
1127 }
1128
1129 if (res.jsonValue.empty())
1130 {
1131 res.result(boost::beast::http::status::forbidden);
1132 res.jsonValue = {
1133 {"status", "error"},
1134 {"message", "403 Forbidden"},
1135 {"data",
1136 {{"message", "The specified property cannot be created: " +
1137 propertyName}}}};
1138 }
1139
1140 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001141 }
1142
Ed Tanous1abe55e2018-09-05 08:30:59 -07001143 void setErrorStatus()
1144 {
1145 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001146 }
1147
Ed Tanous1abe55e2018-09-05 08:30:59 -07001148 crow::Response &res;
1149 std::string objectPath;
1150 std::string propertyName;
1151 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001152};
1153
Ed Tanousd76323e2018-08-07 14:35:40 -07001154void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001155 const std::string &objectPath, const std::string &destProperty)
1156{
1157 nlohmann::json requestDbusData =
1158 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001159
Ed Tanous1abe55e2018-09-05 08:30:59 -07001160 if (requestDbusData.is_discarded())
1161 {
1162 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001163 res.end();
1164 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001165 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001166
Ed Tanous1abe55e2018-09-05 08:30:59 -07001167 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1168 if (propertyIt == requestDbusData.end())
1169 {
1170 res.result(boost::beast::http::status::bad_request);
1171 res.end();
1172 return;
1173 }
1174 const nlohmann::json &propertySetValue = *propertyIt;
1175 auto transaction = std::make_shared<AsyncPutRequest>(res);
1176 transaction->objectPath = objectPath;
1177 transaction->propertyName = destProperty;
1178 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001179
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 using GetObjectType =
1181 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001182
Ed Tanous1abe55e2018-09-05 08:30:59 -07001183 crow::connections::systemBus->async_method_call(
1184 [transaction](const boost::system::error_code ec,
1185 const GetObjectType &object_names) {
1186 if (!ec && object_names.size() <= 0)
1187 {
1188 transaction->res.result(boost::beast::http::status::not_found);
1189 return;
1190 }
Ed Tanous911ac312017-08-15 09:37:42 -07001191
Ed Tanous1abe55e2018-09-05 08:30:59 -07001192 for (const std::pair<std::string, std::vector<std::string>>
1193 connection : object_names)
1194 {
1195 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001196
Ed Tanous1abe55e2018-09-05 08:30:59 -07001197 crow::connections::systemBus->async_method_call(
1198 [connectionName{std::string(connectionName)},
1199 transaction](const boost::system::error_code ec,
1200 const std::string &introspectXml) {
1201 if (ec)
1202 {
1203 BMCWEB_LOG_ERROR
1204 << "Introspect call failed with error: "
1205 << ec.message()
1206 << " on process: " << connectionName;
1207 transaction->setErrorStatus();
1208 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001209 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001210 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001211
Ed Tanous1abe55e2018-09-05 08:30:59 -07001212 doc.Parse(introspectXml.c_str());
1213 tinyxml2::XMLNode *pRoot =
1214 doc.FirstChildElement("node");
1215 if (pRoot == nullptr)
1216 {
1217 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1218 << introspectXml;
1219 transaction->setErrorStatus();
1220 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001221 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001222 tinyxml2::XMLElement *ifaceNode =
1223 pRoot->FirstChildElement("interface");
1224 while (ifaceNode != nullptr)
1225 {
1226 const char *interfaceName =
1227 ifaceNode->Attribute("name");
1228 BMCWEB_LOG_DEBUG << "found interface "
1229 << interfaceName;
1230 tinyxml2::XMLElement *propNode =
1231 ifaceNode->FirstChildElement("property");
1232 while (propNode != nullptr)
1233 {
1234 const char *propertyName =
1235 propNode->Attribute("name");
1236 BMCWEB_LOG_DEBUG << "Found property "
1237 << propertyName;
1238 if (propertyName == transaction->propertyName)
1239 {
1240 const char *argType =
1241 propNode->Attribute("type");
1242 if (argType != nullptr)
1243 {
1244 sdbusplus::message::message m =
1245 crow::connections::systemBus
1246 ->new_method_call(
1247 connectionName.c_str(),
1248 transaction->objectPath
1249 .c_str(),
1250 "org.freedesktop.DBus."
1251 "Properties",
1252 "Set");
1253 m.append(interfaceName,
1254 transaction->propertyName);
1255 int r = sd_bus_message_open_container(
1256 m.get(), SD_BUS_TYPE_VARIANT,
1257 argType);
1258 if (r < 0)
1259 {
1260 transaction->setErrorStatus();
1261 return;
1262 }
1263 r = convertJsonToDbus(
1264 m.get(), argType,
1265 transaction->propertyValue);
1266 if (r < 0)
1267 {
1268 transaction->setErrorStatus();
1269 return;
1270 }
1271 r = sd_bus_message_close_container(
1272 m.get());
1273 if (r < 0)
1274 {
1275 transaction->setErrorStatus();
1276 return;
1277 }
Ed Tanous911ac312017-08-15 09:37:42 -07001278
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 crow::connections::systemBus
1280 ->async_send(
1281 m,
1282 [transaction](
1283 boost::system::error_code
1284 ec,
1285 sdbusplus::message::message
1286 &m) {
1287 BMCWEB_LOG_DEBUG << "sent";
1288 if (ec)
1289 {
1290 transaction->res
1291 .jsonValue
1292 ["status"] =
1293 "error";
1294 transaction->res
1295 .jsonValue
1296 ["message"] =
1297 ec.message();
1298 }
1299 });
1300 }
1301 }
1302 propNode =
1303 propNode->NextSiblingElement("property");
1304 }
1305 ifaceNode =
1306 ifaceNode->NextSiblingElement("interface");
1307 }
1308 },
1309 connectionName, transaction->objectPath,
1310 "org.freedesktop.DBus.Introspectable", "Introspect");
1311 }
1312 },
1313 "xyz.openbmc_project.ObjectMapper",
1314 "/xyz/openbmc_project/object_mapper",
1315 "xyz.openbmc_project.ObjectMapper", "GetObject",
1316 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001317}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001318
Ed Tanous049a0512018-11-01 13:58:42 -07001319inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1320 std::string &objectPath)
1321{
Ed Tanous049a0512018-11-01 13:58:42 -07001322
1323 // If accessing a single attribute, fill in and update objectPath,
1324 // otherwise leave destProperty blank
1325 std::string destProperty = "";
1326 const char *attrSeperator = "/attr/";
1327 size_t attrPosition = objectPath.find(attrSeperator);
1328 if (attrPosition != objectPath.npos)
1329 {
1330 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1331 objectPath.length());
1332 objectPath = objectPath.substr(0, attrPosition);
1333 }
1334
1335 if (req.method() == "POST"_method)
1336 {
1337 constexpr const char *actionSeperator = "/action/";
1338 size_t actionPosition = objectPath.find(actionSeperator);
1339 if (actionPosition != objectPath.npos)
1340 {
1341 std::string postProperty =
1342 objectPath.substr((actionPosition + strlen(actionSeperator)),
1343 objectPath.length());
1344 objectPath = objectPath.substr(0, actionPosition);
1345 handleAction(req, res, objectPath, postProperty);
1346 return;
1347 }
1348 }
1349 else if (req.method() == "GET"_method)
1350 {
1351 if (boost::ends_with(objectPath, "/enumerate"))
1352 {
1353 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1354 objectPath.end());
1355 handleEnumerate(res, objectPath);
1356 }
1357 else if (boost::ends_with(objectPath, "/list"))
1358 {
1359 objectPath.erase(objectPath.end() - sizeof("list"),
1360 objectPath.end());
1361 handleList(res, objectPath);
1362 }
1363 else
1364 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001365 // Trim any trailing "/" at the end
1366 if (boost::ends_with(objectPath, "/"))
1367 {
1368 objectPath.pop_back();
1369 handleList(res, objectPath, 1);
1370 }
1371 else
1372 {
1373 handleGet(res, objectPath, destProperty);
1374 }
Ed Tanous049a0512018-11-01 13:58:42 -07001375 }
1376 return;
1377 }
1378 else if (req.method() == "PUT"_method)
1379 {
1380 handlePut(req, res, objectPath, destProperty);
1381 return;
1382 }
1383
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001384 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1385 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001386 res.end();
1387}
1388
Ed Tanous1abe55e2018-09-05 08:30:59 -07001389template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1390{
1391 BMCWEB_ROUTE(app, "/bus/")
1392 .methods("GET"_method)(
1393 [](const crow::Request &req, crow::Response &res) {
1394 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1395 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001396 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001397 });
1398
1399 BMCWEB_ROUTE(app, "/bus/system/")
1400 .methods("GET"_method)(
1401 [](const crow::Request &req, crow::Response &res) {
1402 auto myCallback = [&res](const boost::system::error_code ec,
1403 std::vector<std::string> &names) {
1404 if (ec)
1405 {
1406 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1407 res.result(
1408 boost::beast::http::status::internal_server_error);
1409 }
1410 else
1411 {
1412 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001413 res.jsonValue = {{"status", "ok"}};
1414 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001415 for (auto &name : names)
1416 {
1417 objectsSub.push_back({{"name", name}});
1418 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001419 }
1420 res.end();
1421 };
1422 crow::connections::systemBus->async_method_call(
1423 std::move(myCallback), "org.freedesktop.DBus", "/",
1424 "org.freedesktop.DBus", "ListNames");
1425 });
1426
1427 BMCWEB_ROUTE(app, "/list/")
1428 .methods("GET"_method)(
1429 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001430 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001431 });
1432
1433 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous049a0512018-11-01 13:58:42 -07001434 .methods("GET"_method, "PUT"_method, "POST"_method)(
1435 [](const crow::Request &req, crow::Response &res,
1436 const std::string &path) {
1437 std::string objectPath = "/xyz/" + path;
1438 handleDBusUrl(req, res, objectPath);
1439 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001440
Ed Tanous049a0512018-11-01 13:58:42 -07001441 BMCWEB_ROUTE(app, "/org/<path>")
1442 .methods("GET"_method, "PUT"_method, "POST"_method)(
1443 [](const crow::Request &req, crow::Response &res,
1444 const std::string &path) {
1445 std::string objectPath = "/org/" + path;
1446 handleDBusUrl(req, res, objectPath);
1447 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001448
Ed Tanous1abe55e2018-09-05 08:30:59 -07001449 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1450 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1451 const std::string &dumpId) {
1452 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1453 if (!std::regex_match(dumpId, validFilename))
1454 {
1455 res.result(boost::beast::http::status::not_found);
1456 res.end();
1457 return;
1458 }
1459 std::experimental::filesystem::path loc(
1460 "/var/lib/phosphor-debug-collector/dumps");
1461
1462 loc += dumpId;
1463
1464 if (!std::experimental::filesystem::exists(loc) ||
1465 !std::experimental::filesystem::is_directory(loc))
1466 {
1467 res.result(boost::beast::http::status::not_found);
1468 res.end();
1469 return;
1470 }
1471 std::experimental::filesystem::directory_iterator files(loc);
1472 for (auto &file : files)
1473 {
1474 std::ifstream readFile(file.path());
1475 if (readFile.good())
1476 {
1477 continue;
1478 }
1479 res.addHeader("Content-Type", "application/octet-stream");
1480 res.body() = {std::istreambuf_iterator<char>(readFile),
1481 std::istreambuf_iterator<char>()};
1482 res.end();
1483 }
1484 res.result(boost::beast::http::status::not_found);
1485 res.end();
1486 return;
1487 });
1488
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001489 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001491 const std::string &Connection) {
1492 introspectObjects(Connection, "/",
1493 std::make_shared<bmcweb::AsyncResp>(res));
1494 });
1495
1496 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1497 .methods("GET"_method,
1498 "POST"_method)([](const crow::Request &req,
1499 crow::Response &res,
1500 const std::string &processName,
1501 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001502 std::vector<std::string> strs;
1503 boost::split(strs, requestedPath, boost::is_any_of("/"));
1504 std::string objectPath;
1505 std::string interfaceName;
1506 std::string methodName;
1507 auto it = strs.begin();
1508 if (it == strs.end())
1509 {
1510 objectPath = "/";
1511 }
1512 while (it != strs.end())
1513 {
1514 // Check if segment contains ".". If it does, it must be an
1515 // interface
1516 if (it->find(".") != std::string::npos)
1517 {
1518 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001519 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001520 // as part of our <path> specifier above, which causes the
1521 // normal trailing backslash redirector to fail.
1522 }
1523 else if (!it->empty())
1524 {
1525 objectPath += "/" + *it;
1526 }
1527 it++;
1528 }
1529 if (it != strs.end())
1530 {
1531 interfaceName = *it;
1532 it++;
1533
1534 // after interface, we might have a method name
1535 if (it != strs.end())
1536 {
1537 methodName = *it;
1538 it++;
1539 }
1540 }
1541 if (it != strs.end())
1542 {
1543 // if there is more levels past the method name, something went
1544 // wrong, return not found
1545 res.result(boost::beast::http::status::not_found);
1546 res.end();
1547 return;
1548 }
1549 if (interfaceName.empty())
1550 {
1551 crow::connections::systemBus->async_method_call(
1552 [&, processName,
1553 objectPath](const boost::system::error_code ec,
1554 const std::string &introspect_xml) {
1555 if (ec)
1556 {
1557 BMCWEB_LOG_ERROR
1558 << "Introspect call failed with error: "
1559 << ec.message()
1560 << " on process: " << processName
1561 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001562 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001563 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001564 tinyxml2::XMLDocument doc;
1565
1566 doc.Parse(introspect_xml.c_str());
1567 tinyxml2::XMLNode *pRoot =
1568 doc.FirstChildElement("node");
1569 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001570 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001571 BMCWEB_LOG_ERROR << "XML document failed to parse "
1572 << processName << " " << objectPath
1573 << "\n";
1574 res.jsonValue = {{"status", "XML parse error"}};
1575 res.result(boost::beast::http::status::
1576 internal_server_error);
1577 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001578 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001579
1580 BMCWEB_LOG_DEBUG << introspect_xml;
1581 res.jsonValue = {{"status", "ok"},
1582 {"bus_name", processName},
1583 {"object_path", objectPath}};
1584 nlohmann::json &interfacesArray =
1585 res.jsonValue["interfaces"];
1586 interfacesArray = nlohmann::json::array();
1587 tinyxml2::XMLElement *interface =
1588 pRoot->FirstChildElement("interface");
1589
1590 while (interface != nullptr)
1591 {
1592 const char *ifaceName =
1593 interface->Attribute("name");
1594 if (ifaceName != nullptr)
1595 {
1596 interfacesArray.push_back(
1597 {{"name", ifaceName}});
1598 }
1599
1600 interface =
1601 interface->NextSiblingElement("interface");
1602 }
1603
Ed Tanous1abe55e2018-09-05 08:30:59 -07001604 res.end();
1605 },
1606 processName, objectPath,
1607 "org.freedesktop.DBus.Introspectable", "Introspect");
1608 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001609 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001610 {
1611 crow::connections::systemBus->async_method_call(
1612 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001613 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001614 const boost::system::error_code ec,
1615 const std::string &introspect_xml) {
1616 if (ec)
1617 {
1618 BMCWEB_LOG_ERROR
1619 << "Introspect call failed with error: "
1620 << ec.message()
1621 << " on process: " << processName
1622 << " path: " << objectPath << "\n";
1623 }
1624 else
1625 {
1626 tinyxml2::XMLDocument doc;
1627
1628 doc.Parse(introspect_xml.c_str());
1629 tinyxml2::XMLNode *pRoot =
1630 doc.FirstChildElement("node");
1631 if (pRoot == nullptr)
1632 {
1633 BMCWEB_LOG_ERROR
1634 << "XML document failed to parse "
1635 << processName << " " << objectPath << "\n";
1636 res.result(boost::beast::http::status::
1637 internal_server_error);
1638 }
1639 else
1640 {
1641 tinyxml2::XMLElement *node =
1642 pRoot->FirstChildElement("node");
1643
1644 // if we know we're the only call, build the
1645 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001646 tinyxml2::XMLElement *interface =
1647 pRoot->FirstChildElement("interface");
1648
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001649 res.jsonValue = {
1650 {"status", "ok"},
1651 {"bus_name", processName},
1652 {"interface", interfaceName},
1653 {"object_path", objectPath},
1654 {"properties", nlohmann::json::object()}};
1655
1656 nlohmann::json &methodsArray =
1657 res.jsonValue["methods"];
1658 methodsArray = nlohmann::json::array();
1659
1660 nlohmann::json &signalsArray =
1661 res.jsonValue["signals"];
1662 signalsArray = nlohmann::json::array();
1663
Ed Tanous1abe55e2018-09-05 08:30:59 -07001664 while (interface != nullptr)
1665 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001666 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001667 interface->Attribute("name");
1668
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001669 if (ifaceName != nullptr &&
1670 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001671 {
1672 tinyxml2::XMLElement *methods =
1673 interface->FirstChildElement(
1674 "method");
1675 while (methods != nullptr)
1676 {
1677 nlohmann::json argsArray =
1678 nlohmann::json::array();
1679 tinyxml2::XMLElement *arg =
1680 methods->FirstChildElement(
1681 "arg");
1682 while (arg != nullptr)
1683 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001684 nlohmann::json thisArg;
1685 for (const char *fieldName :
1686 std::array<const char *,
1687 3>{"name",
1688 "direction",
1689 "type"})
1690 {
1691 const char *fieldValue =
1692 arg->Attribute(
1693 fieldName);
1694 if (fieldValue != nullptr)
1695 {
1696 thisArg[fieldName] =
1697 fieldValue;
1698 }
1699 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001700 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001701 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001702 arg = arg->NextSiblingElement(
1703 "arg");
1704 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001705
1706 const char *name =
1707 methods->Attribute("name");
1708 if (name != nullptr)
1709 {
1710 methodsArray.push_back(
1711 {{"name", name},
1712 {"uri", "/bus/system/" +
1713 processName +
1714 objectPath +
1715 "/" +
1716 interfaceName +
1717 "/" + name},
1718 {"args", argsArray}});
1719 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001720 methods =
1721 methods->NextSiblingElement(
1722 "method");
1723 }
1724 tinyxml2::XMLElement *signals =
1725 interface->FirstChildElement(
1726 "signal");
1727 while (signals != nullptr)
1728 {
1729 nlohmann::json argsArray =
1730 nlohmann::json::array();
1731
1732 tinyxml2::XMLElement *arg =
1733 signals->FirstChildElement(
1734 "arg");
1735 while (arg != nullptr)
1736 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001737 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001738 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001739 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001741 if (name != nullptr &&
1742 type != nullptr)
1743 {
1744 argsArray.push_back({
1745 {"name", name},
1746 {"type", type},
1747 });
1748 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001749 arg = arg->NextSiblingElement(
1750 "arg");
1751 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001752 const char *name =
1753 signals->Attribute("name");
1754 if (name != nullptr)
1755 {
1756 signalsArray.push_back(
1757 {{"name", name},
1758 {"args", argsArray}});
1759 }
1760
Ed Tanous1abe55e2018-09-05 08:30:59 -07001761 signals =
1762 signals->NextSiblingElement(
1763 "signal");
1764 }
1765
Ed Tanous1abe55e2018-09-05 08:30:59 -07001766 break;
1767 }
1768
1769 interface = interface->NextSiblingElement(
1770 "interface");
1771 }
1772 if (interface == nullptr)
1773 {
1774 // if we got to the end of the list and
1775 // never found a match, throw 404
1776 res.result(
1777 boost::beast::http::status::not_found);
1778 }
1779 }
1780 }
1781 res.end();
1782 },
1783 processName, objectPath,
1784 "org.freedesktop.DBus.Introspectable", "Introspect");
1785 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001786 else
1787 {
1788 if (req.method() != "POST"_method)
1789 {
1790 res.result(boost::beast::http::status::not_found);
1791 res.end();
1792 return;
1793 }
1794
1795 nlohmann::json requestDbusData =
1796 nlohmann::json::parse(req.body, nullptr, false);
1797
1798 if (requestDbusData.is_discarded())
1799 {
1800 res.result(boost::beast::http::status::bad_request);
1801 res.end();
1802 return;
1803 }
1804 if (!requestDbusData.is_array())
1805 {
1806 res.result(boost::beast::http::status::bad_request);
1807 res.end();
1808 return;
1809 }
1810 auto transaction = std::make_shared<InProgressActionData>(res);
1811
1812 transaction->path = objectPath;
1813 transaction->methodName = methodName;
1814 transaction->arguments = std::move(requestDbusData);
1815
1816 findActionOnInterface(transaction, processName);
1817 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001818 });
1819}
1820} // namespace openbmc_mapper
1821} // namespace crow