blob: 4a34f5c12b70da7bb1046ad715b7ab857670b196 [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous911ac312017-08-15 09:37:42 -070016#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
Ed Tanouse3cb5a32018-08-08 14:16:49 -070019#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <boost/algorithm/string.hpp>
21#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070024#include <experimental/filesystem>
25#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070026#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
30namespace openbmc_mapper
31{
Ed Tanousba9f9a62017-10-11 16:40:35 -070032
Matt Spinler3ae4ba72018-12-05 14:01:22 -060033using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Matt Spinler2ae60092018-12-06 10:35:36 -060037const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060038const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060039const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060040const std::string forbiddenMsg = "403 Forbidden";
Matt Spinler6db06242018-12-11 11:21:22 -060041
Matt Spinler2ae60092018-12-06 10:35:36 -060042const std::string notFoundDesc =
43 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060044const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060045const std::string noJsonDesc = "No JSON object could be decoded";
46const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060047const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060048const std::string forbiddenPropDesc =
49 "The specified property cannot be created";
50const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060051
52void setErrorResponse(crow::Response &res, boost::beast::http::status result,
53 const std::string &desc, const std::string &msg)
54{
55 res.result(result);
56 res.jsonValue = {{"data", {{"description", desc}}},
57 {"message", msg},
58 {"status", "error"}};
59}
60
Ed Tanouse3cb5a32018-08-08 14:16:49 -070061void introspectObjects(const std::string &processName,
62 const std::string &objectPath,
63 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070064{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070065 if (transaction->res.jsonValue.is_null())
66 {
67 transaction->res.jsonValue = {{"status", "ok"},
68 {"bus_name", processName},
69 {"objects", nlohmann::json::array()}};
70 }
71
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070073 [transaction, processName{std::string(processName)},
74 objectPath{std::string(objectPath)}](
75 const boost::system::error_code ec,
76 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 if (ec)
78 {
79 BMCWEB_LOG_ERROR
80 << "Introspect call failed with error: " << ec.message()
81 << " on process: " << processName << " path: " << objectPath
82 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070083 return;
84 }
85 transaction->res.jsonValue["objects"].push_back(
86 {{"path", objectPath}});
87
88 tinyxml2::XMLDocument doc;
89
90 doc.Parse(introspect_xml.c_str());
91 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
92 if (pRoot == nullptr)
93 {
94 BMCWEB_LOG_ERROR << "XML document failed to parse "
95 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070096 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 else
98 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070099 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
100 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 const char *childPath = node->Attribute("name");
103 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 std::string newpath;
106 if (objectPath != "/")
107 {
108 newpath += objectPath;
109 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700110 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700112 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700114
115 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 }
117 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700119 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700121}
Ed Tanous64530012018-02-06 17:08:16 -0800122
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600123void getPropertiesForEnumerate(const std::string &objectPath,
124 const std::string &service,
125 const std::string &interface,
126 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
127{
128 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
129 << service << " " << interface;
130
131 crow::connections::systemBus->async_method_call(
132 [asyncResp, objectPath, service,
133 interface](const boost::system::error_code ec,
134 const std::vector<
135 std::pair<std::string, dbus::utility::DbusVariantType>>
136 &propertiesList) {
137 if (ec)
138 {
139 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
140 << interface << " service " << service
141 << " failed with code " << ec;
142 return;
143 }
144
145 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
146 nlohmann::json &objectJson = dataJson[objectPath];
147 if (objectJson.is_null())
148 {
149 objectJson = nlohmann::json::object();
150 }
151
152 for (const auto &[name, value] : propertiesList)
153 {
154 nlohmann::json &propertyJson = objectJson[name];
155 sdbusplus::message::variant_ns::visit(
156 [&propertyJson](auto &&val) { propertyJson = val; }, value);
157 }
158 },
159 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
160 interface);
161}
162
163// Find any results that weren't picked up by ObjectManagers, to be
164// called after all ObjectManagers are searched for and called.
165void findRemainingObjectsForEnumerate(
166 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
167 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
168{
169 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
170 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
171
172 for (const auto &[path, interface_map] : *subtree)
173 {
174 if (path == objectPath)
175 {
176 // An enumerate does not return the target path's properties
177 continue;
178 }
179 if (dataJson.find(path) == dataJson.end())
180 {
181 for (const auto &[service, interfaces] : interface_map)
182 {
183 for (const auto &interface : interfaces)
184 {
185 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
186 {
187 getPropertiesForEnumerate(path, service, interface,
188 asyncResp);
189 }
190 }
191 }
192 }
193 }
194}
195
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600196struct InProgressEnumerateData
197{
198 InProgressEnumerateData(const std::string &objectPath,
199 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
200 objectPath(objectPath),
201 asyncResp(asyncResp)
202 {
203 }
204
205 ~InProgressEnumerateData()
206 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600207 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600208 }
209
210 const std::string objectPath;
211 std::shared_ptr<GetSubTreeType> subtree;
212 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
213};
214
215void getManagedObjectsForEnumerate(
216 const std::string &object_name, const std::string &object_manager_path,
217 const std::string &connection_name,
218 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219{
Ed Tanous049a0512018-11-01 13:58:42 -0700220 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
221 << " object_manager_path " << object_manager_path
222 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600224 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700225 connection_name](const boost::system::error_code ec,
226 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227 if (ec)
228 {
Ed Tanous049a0512018-11-01 13:58:42 -0700229 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600230 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700231 << " failed with code " << ec;
232 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700233 }
Ed Tanous64530012018-02-06 17:08:16 -0800234
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600235 nlohmann::json &dataJson =
236 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700237
238 for (const auto &objectPath : objects)
239 {
240 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 {
Ed Tanous049a0512018-11-01 13:58:42 -0700242 BMCWEB_LOG_DEBUG << "Reading object "
243 << objectPath.first.str;
244 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 if (objectJson.is_null())
246 {
247 objectJson = nlohmann::json::object();
248 }
249 for (const auto &interface : objectPath.second)
250 {
251 for (const auto &property : interface.second)
252 {
253 nlohmann::json &propertyJson =
254 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700255 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 [&propertyJson](auto &&val) {
257 propertyJson = val;
258 },
259 property.second);
260 }
261 }
262 }
Ed Tanous049a0512018-11-01 13:58:42 -0700263 for (const auto &interface : objectPath.second)
264 {
265 if (interface.first == "org.freedesktop.DBus.ObjectManager")
266 {
267 getManagedObjectsForEnumerate(
268 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600269 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700270 }
271 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 }
273 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700274 connection_name, object_manager_path,
275 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
276}
277
278void findObjectManagerPathForEnumerate(
279 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600280 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700281{
Ed Tanous049a0512018-11-01 13:58:42 -0700282 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
283 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600285 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700286 const boost::system::error_code ec,
287 const boost::container::flat_map<
288 std::string, boost::container::flat_map<
289 std::string, std::vector<std::string>>>
290 &objects) {
291 if (ec)
292 {
Ed Tanous049a0512018-11-01 13:58:42 -0700293 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
294 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700295 return;
296 }
297
Ed Tanousf254ba72018-10-12 13:40:35 -0700298 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700299 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700300 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700301 {
302 if (connectionGroup.first == connection_name)
303 {
304 // Found the object manager path for this resource.
305 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700306 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600307 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700308 return;
309 }
310 }
311 }
312 },
313 "xyz.openbmc_project.ObjectMapper",
314 "/xyz/openbmc_project/object_mapper",
315 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
316 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700317}
Ed Tanous64530012018-02-06 17:08:16 -0800318
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600319// Uses GetObject to add the object info about the target /enumerate path to the
320// results of GetSubTree, as GetSubTree will not return info for the
321// target path, and then continues on enumerating the rest of the tree.
322void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
323{
324 using GetObjectType =
325 std::vector<std::pair<std::string, std::vector<std::string>>>;
326
327 crow::connections::systemBus->async_method_call(
328 [transaction](const boost::system::error_code ec,
329 const GetObjectType &objects) {
330 if (ec)
331 {
332 BMCWEB_LOG_ERROR << "GetObject for path "
333 << transaction->objectPath
334 << " failed with code " << ec;
335 return;
336 }
337
338 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
339 << " has " << objects.size() << " entries";
340 if (!objects.empty())
341 {
342 transaction->subtree->emplace_back(transaction->objectPath,
343 objects);
344 }
345
346 // Map indicating connection name, and the path where the object
347 // manager exists
348 boost::container::flat_map<std::string, std::string> connections;
349
350 for (const auto &object : *(transaction->subtree))
351 {
352 for (const auto &connection : object.second)
353 {
354 std::string &objectManagerPath =
355 connections[connection.first];
356 for (const auto &interface : connection.second)
357 {
358 BMCWEB_LOG_DEBUG << connection.first
359 << " has interface " << interface;
360 if (interface == "org.freedesktop.DBus.ObjectManager")
361 {
362 BMCWEB_LOG_DEBUG << "found object manager path "
363 << object.first;
364 objectManagerPath = object.first;
365 }
366 }
367 }
368 }
369 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
370
371 for (const auto &connection : connections)
372 {
373 // If we already know where the object manager is, we don't need
374 // to search for it, we can call directly in to
375 // getManagedObjects
376 if (!connection.second.empty())
377 {
378 getManagedObjectsForEnumerate(
379 transaction->objectPath, connection.second,
380 connection.first, transaction);
381 }
382 else
383 {
384 // otherwise we need to find the object manager path before
385 // we can continue
386 findObjectManagerPathForEnumerate(
387 transaction->objectPath, connection.first, transaction);
388 }
389 }
390 },
391 "xyz.openbmc_project.ObjectMapper",
392 "/xyz/openbmc_project/object_mapper",
393 "xyz.openbmc_project.ObjectMapper", "GetObject",
394 transaction->objectPath, std::array<const char *, 0>());
395}
Ed Tanous64530012018-02-06 17:08:16 -0800396
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700397// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398struct InProgressActionData
399{
400 InProgressActionData(crow::Response &res) : res(res){};
401 ~InProgressActionData()
402 {
Matt Spinler6db06242018-12-11 11:21:22 -0600403 // If still no JSON filled in, then we never found the method.
404 if (res.jsonValue.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 {
Matt Spinler6db06242018-12-11 11:21:22 -0600406 setErrorResponse(res, boost::beast::http::status::not_found,
407 methodNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700408 }
409 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700410 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700411
Matt Spinler6db06242018-12-11 11:21:22 -0600412 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 {
Matt Spinler6db06242018-12-11 11:21:22 -0600414 setErrorResponse(res, boost::beast::http::status::internal_server_error,
415 desc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 }
417 crow::Response &res;
418 std::string path;
419 std::string methodName;
420 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700421};
422
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423std::vector<std::string> dbusArgSplit(const std::string &string)
424{
425 std::vector<std::string> ret;
426 if (string.empty())
427 {
428 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700429 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700430 ret.push_back("");
431 int containerDepth = 0;
432
433 for (std::string::const_iterator character = string.begin();
434 character != string.end(); character++)
435 {
436 ret.back() += *character;
437 switch (*character)
438 {
439 case ('a'):
440 break;
441 case ('('):
442 case ('{'):
443 containerDepth++;
444 break;
445 case ('}'):
446 case (')'):
447 containerDepth--;
448 if (containerDepth == 0)
449 {
450 if (character + 1 != string.end())
451 {
452 ret.push_back("");
453 }
454 }
455 break;
456 default:
457 if (containerDepth == 0)
458 {
459 if (character + 1 != string.end())
460 {
461 ret.push_back("");
462 }
463 }
464 break;
465 }
466 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700467}
468
Ed Tanousd76323e2018-08-07 14:35:40 -0700469int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700470 const nlohmann::json &input_json)
471{
472 int r = 0;
473 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
474 << " to type: " << arg_type;
475 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700476
Ed Tanous1abe55e2018-09-05 08:30:59 -0700477 // Assume a single object for now.
478 const nlohmann::json *j = &input_json;
479 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700480
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700481 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700482 {
483 // If we are decoding multiple objects, grab the pointer to the
484 // iterator, and increment it for the next loop
485 if (argTypes.size() > 1)
486 {
487 if (jIt == input_json.end())
488 {
489 return -2;
490 }
491 j = &*jIt;
492 jIt++;
493 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700494 const int64_t *intValue = j->get_ptr<const int64_t *>();
495 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
496 const std::string *stringValue = j->get_ptr<const std::string *>();
497 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498 const bool *b = j->get_ptr<const bool *>();
499 int64_t v = 0;
500 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700501
Ed Tanous1abe55e2018-09-05 08:30:59 -0700502 // Do some basic type conversions that make sense. uint can be
503 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700504 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700506 v = static_cast<int64_t>(*uintValue);
507 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700509 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700511 d = static_cast<double>(*uintValue);
512 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700513 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700514 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700516 d = static_cast<double>(*intValue);
517 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700518 }
519
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700520 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700522 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 {
524 return -1;
525 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700526 r = sd_bus_message_append_basic(m, argCode[0],
527 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 if (r < 0)
529 {
530 return r;
531 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700532 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700533 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700534 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700535 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 {
537 return -1;
538 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700539 int32_t i = static_cast<int32_t>(*intValue);
540 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700541 if (r < 0)
542 {
543 return r;
544 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700545 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700546 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 {
548 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700549 int boolInt = false;
550 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 }
554 else if (b != nullptr)
555 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700556 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700558 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700560 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 }
562 else
563 {
564 return -1;
565 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700566 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 if (r < 0)
568 {
569 return r;
570 }
571 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700572 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700574 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 {
576 return -1;
577 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700578 int16_t n = static_cast<int16_t>(*intValue);
579 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 if (r < 0)
581 {
582 return r;
583 }
584 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700585 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700587 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588 {
589 return -1;
590 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700591 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592 if (r < 0)
593 {
594 return r;
595 }
596 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700597 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700599 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600 {
601 return -1;
602 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700603 uint8_t y = static_cast<uint8_t>(*uintValue);
604 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700608 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 {
610 return -1;
611 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700612 uint16_t q = static_cast<uint16_t>(*uintValue);
613 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700617 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 {
619 return -1;
620 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700621 uint32_t u = static_cast<uint32_t>(*uintValue);
622 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700624 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 {
628 return -1;
629 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700630 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700632 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700634 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700636 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700638 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700640 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 if (r < 0)
642 {
643 return r;
644 }
645
646 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
647 ++it)
648 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700649 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 if (r < 0)
651 {
652 return r;
653 }
654
655 it++;
656 }
657 sd_bus_message_close_container(m);
658 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700659 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700661 std::string containedType = argCode.substr(1);
662 BMCWEB_LOG_DEBUG << "variant type: " << argCode
663 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700665 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 if (r < 0)
667 {
668 return r;
669 }
670
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700671 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 if (r < 0)
673 {
674 return r;
675 }
676
677 r = sd_bus_message_close_container(m);
678 if (r < 0)
679 {
680 return r;
681 }
682 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700683 else if (boost::starts_with(argCode, "(") &&
684 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700688 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700690 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 {
692 if (it == j->end())
693 {
694 return -1;
695 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700696 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697 if (r < 0)
698 {
699 return r;
700 }
701 it++;
702 }
703 r = sd_bus_message_close_container(m);
704 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700705 else if (boost::starts_with(argCode, "{") &&
706 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700708 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700710 containedType.c_str());
711 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712 if (codes.size() != 2)
713 {
714 return -1;
715 }
716 const std::string &key_type = codes[0];
717 const std::string &value_type = codes[1];
718 for (auto it : j->items())
719 {
720 r = convertJsonToDbus(m, key_type, it.key());
721 if (r < 0)
722 {
723 return r;
724 }
725
726 r = convertJsonToDbus(m, value_type, it.value());
727 if (r < 0)
728 {
729 return r;
730 }
731 }
732 r = sd_bus_message_close_container(m);
733 }
734 else
735 {
736 return -2;
737 }
738 if (r < 0)
739 {
740 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700741 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700742
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 if (argTypes.size() > 1)
744 {
745 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700746 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700747 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700748}
749
Ed Tanousd76323e2018-08-07 14:35:40 -0700750void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751 const std::string &connectionName)
752{
753 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
754 << connectionName;
755 crow::connections::systemBus->async_method_call(
756 [transaction, connectionName{std::string(connectionName)}](
757 const boost::system::error_code ec,
758 const std::string &introspect_xml) {
759 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
760 if (ec)
761 {
762 BMCWEB_LOG_ERROR
763 << "Introspect call failed with error: " << ec.message()
764 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700765 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 else
767 {
768 tinyxml2::XMLDocument doc;
769
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700770 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
772 if (pRoot == nullptr)
773 {
774 BMCWEB_LOG_ERROR << "XML document failed to parse "
775 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700776 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700777 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700778 tinyxml2::XMLElement *interfaceNode =
779 pRoot->FirstChildElement("interface");
780 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700782 const char *thisInterfaceName =
783 interfaceNode->Attribute("name");
784 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700786 tinyxml2::XMLElement *methodNode =
787 interfaceNode->FirstChildElement("method");
788 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700789 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700790 const char *thisMethodName =
791 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700793 << thisMethodName;
794 if (thisMethodName != nullptr &&
795 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700797 BMCWEB_LOG_DEBUG
798 << "Found method named " << thisMethodName
799 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 sdbusplus::message::message m =
801 crow::connections::systemBus
802 ->new_method_call(
803 connectionName.c_str(),
804 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700805 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 transaction->methodName.c_str());
807
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700808 tinyxml2::XMLElement *argumentNode =
809 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700810
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700811 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 transaction->arguments.begin();
813
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700814 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700816 const char *argDirection =
817 argumentNode->Attribute("direction");
818 const char *argType =
819 argumentNode->Attribute("type");
820 if (argDirection != nullptr &&
821 argType != nullptr &&
822 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700824
825 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700826 transaction->arguments.end())
827 {
Matt Spinler6db06242018-12-11 11:21:22 -0600828 transaction->setErrorStatus(
829 "Invalid method args");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 return;
831 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700832 if (convertJsonToDbus(
833 m.get(), std::string(argType),
834 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 {
Matt Spinler6db06242018-12-11 11:21:22 -0600836 transaction->setErrorStatus(
837 "Invalid method arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 return;
839 }
840
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700841 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700842 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700843 argumentNode =
844 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700845 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700846
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 crow::connections::systemBus->async_send(
848 m, [transaction](
849 boost::system::error_code ec,
850 sdbusplus::message::message &m) {
851 if (ec)
852 {
Matt Spinler6db06242018-12-11 11:21:22 -0600853 transaction->setErrorStatus(
854 "Method call failed");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 return;
856 }
857 transaction->res.jsonValue = {
858 {"status", "ok"},
859 {"message", "200 OK"},
860 {"data", nullptr}};
861 });
862 break;
863 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700864 methodNode =
865 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700868 interfaceNode =
869 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700870 }
871 }
872 },
873 connectionName, transaction->path,
874 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700875}
876
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700877void handleAction(const crow::Request &req, crow::Response &res,
878 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700879{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700880 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
881 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 nlohmann::json requestDbusData =
883 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700884
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 if (requestDbusData.is_discarded())
886 {
Matt Spinler6db06242018-12-11 11:21:22 -0600887 setErrorResponse(res, boost::beast::http::status::bad_request,
888 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700889 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700890 return;
891 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700892 nlohmann::json::iterator data = requestDbusData.find("data");
893 if (data == requestDbusData.end())
894 {
Matt Spinler6db06242018-12-11 11:21:22 -0600895 setErrorResponse(res, boost::beast::http::status::bad_request,
896 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700897 res.end();
898 return;
899 }
900
901 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 {
Matt Spinler6db06242018-12-11 11:21:22 -0600903 setErrorResponse(res, boost::beast::http::status::bad_request,
904 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700905 res.end();
906 return;
907 }
908 auto transaction = std::make_shared<InProgressActionData>(res);
909
910 transaction->path = objectPath;
911 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700912 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700913 crow::connections::systemBus->async_method_call(
914 [transaction](
915 const boost::system::error_code ec,
916 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700917 &interfaceNames) {
918 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700919 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700920 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -0600921 setErrorResponse(transaction->res,
922 boost::beast::http::status::not_found,
923 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924 return;
925 }
926
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700927 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
928 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700929
930 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700931 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700932 {
933 findActionOnInterface(transaction, object.first);
934 }
935 },
936 "xyz.openbmc_project.ObjectMapper",
937 "/xyz/openbmc_project/object_mapper",
938 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
939 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700940}
941
Ed Tanousf839dfe2018-11-12 11:11:15 -0800942void handleList(crow::Response &res, const std::string &objectPath,
943 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700944{
945 crow::connections::systemBus->async_method_call(
946 [&res](const boost::system::error_code ec,
947 std::vector<std::string> &objectPaths) {
948 if (ec)
949 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -0600950 setErrorResponse(res, boost::beast::http::status::not_found,
951 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 }
953 else
954 {
955 res.jsonValue = {{"status", "ok"},
956 {"message", "200 OK"},
957 {"data", std::move(objectPaths)}};
958 }
959 res.end();
960 },
961 "xyz.openbmc_project.ObjectMapper",
962 "/xyz/openbmc_project/object_mapper",
963 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -0800964 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700966
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700967void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700968{
Ed Tanous049a0512018-11-01 13:58:42 -0700969 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
970 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
971
972 asyncResp->res.jsonValue = {{"message", "200 OK"},
973 {"status", "ok"},
974 {"data", nlohmann::json::object()}};
975
Ed Tanous1abe55e2018-09-05 08:30:59 -0700976 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600977 [objectPath, asyncResp](const boost::system::error_code ec,
978 GetSubTreeType &object_names) {
979 auto transaction = std::make_shared<InProgressEnumerateData>(
980 objectPath, asyncResp);
981
982 transaction->subtree =
983 std::make_shared<GetSubTreeType>(std::move(object_names));
984
Ed Tanous1abe55e2018-09-05 08:30:59 -0700985 if (ec)
986 {
Matt Spinler2ae60092018-12-06 10:35:36 -0600987 BMCWEB_LOG_ERROR << "GetSubTree failed on "
988 << transaction->objectPath;
989 setErrorResponse(transaction->asyncResp->res,
990 boost::beast::http::status::not_found,
991 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992 return;
993 }
Ed Tanous64530012018-02-06 17:08:16 -0800994
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600995 // Add the data for the path passed in to the results
996 // as if GetSubTree returned it, and continue on enumerating
997 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 },
999 "xyz.openbmc_project.ObjectMapper",
1000 "/xyz/openbmc_project/object_mapper",
1001 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001002 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001003}
Ed Tanous911ac312017-08-15 09:37:42 -07001004
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001005void handleGet(crow::Response &res, std::string &objectPath,
1006 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001007{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001008 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1009 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001010 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001011
Ed Tanous1abe55e2018-09-05 08:30:59 -07001012 std::shared_ptr<std::string> path =
1013 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001014
Ed Tanous1abe55e2018-09-05 08:30:59 -07001015 using GetObjectType =
1016 std::vector<std::pair<std::string, std::vector<std::string>>>;
1017 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001018 [&res, path, propertyName](const boost::system::error_code ec,
1019 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001020 if (ec || object_names.size() <= 0)
1021 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001022 setErrorResponse(res, boost::beast::http::status::not_found,
1023 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024 res.end();
1025 return;
1026 }
1027 std::shared_ptr<nlohmann::json> response =
1028 std::make_shared<nlohmann::json>(nlohmann::json::object());
1029 // The mapper should never give us an empty interface names list,
1030 // but check anyway
1031 for (const std::pair<std::string, std::vector<std::string>>
1032 connection : object_names)
1033 {
1034 const std::vector<std::string> &interfaceNames =
1035 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001036
Ed Tanous1abe55e2018-09-05 08:30:59 -07001037 if (interfaceNames.size() <= 0)
1038 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001039 setErrorResponse(res, boost::beast::http::status::not_found,
1040 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001041 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001042 return;
1043 }
1044
1045 for (const std::string &interface : interfaceNames)
1046 {
1047 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001048 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001049 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001050 const std::vector<std::pair<
1051 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001052 &properties) {
1053 if (ec)
1054 {
1055 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1056 << ec;
1057 }
1058 else
1059 {
James Feist5b4aa862018-08-16 14:07:01 -07001060 for (const std::pair<
1061 std::string,
1062 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001063 &property : properties)
1064 {
1065 // if property name is empty, or matches our
1066 // search query, add it to the response json
1067
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001068 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001069 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001070 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001071 [&response, &property](auto &&val) {
1072 (*response)[property.first] =
1073 val;
1074 },
1075 property.second);
1076 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001077 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001078 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001079 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001080 [&response](auto &&val) {
1081 (*response) = val;
1082 },
1083 property.second);
1084 }
1085 }
1086 }
1087 if (response.use_count() == 1)
1088 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001089 if (!propertyName->empty() && response->empty())
1090 {
1091 setErrorResponse(
1092 res,
1093 boost::beast::http::status::not_found,
1094 propNotFoundDesc, notFoundMsg);
1095 }
1096 else
1097 {
1098 res.jsonValue = {{"status", "ok"},
1099 {"message", "200 OK"},
1100 {"data", *response}};
1101 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001102 res.end();
1103 }
1104 },
1105 connection.first, *path,
1106 "org.freedesktop.DBus.Properties", "GetAll", interface);
1107 }
1108 }
1109 },
1110 "xyz.openbmc_project.ObjectMapper",
1111 "/xyz/openbmc_project/object_mapper",
1112 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1113 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001114}
1115
Ed Tanous1abe55e2018-09-05 08:30:59 -07001116struct AsyncPutRequest
1117{
1118 AsyncPutRequest(crow::Response &res) : res(res)
1119 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001120 }
1121 ~AsyncPutRequest()
1122 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001123 if (res.jsonValue.empty())
1124 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001125 setErrorResponse(res, boost::beast::http::status::forbidden,
1126 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001127 }
1128
1129 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001130 }
1131
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001132 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001133 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001134 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1135 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001136 }
1137
Ed Tanous1abe55e2018-09-05 08:30:59 -07001138 crow::Response &res;
1139 std::string objectPath;
1140 std::string propertyName;
1141 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001142};
1143
Ed Tanousd76323e2018-08-07 14:35:40 -07001144void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001145 const std::string &objectPath, const std::string &destProperty)
1146{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001147 if (destProperty.empty())
1148 {
1149 setErrorResponse(res, boost::beast::http::status::forbidden,
1150 forbiddenResDesc, forbiddenMsg);
1151 res.end();
1152 return;
1153 }
1154
Ed Tanous1abe55e2018-09-05 08:30:59 -07001155 nlohmann::json requestDbusData =
1156 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001157
Ed Tanous1abe55e2018-09-05 08:30:59 -07001158 if (requestDbusData.is_discarded())
1159 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001160 setErrorResponse(res, boost::beast::http::status::bad_request,
1161 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001162 res.end();
1163 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001165
Ed Tanous1abe55e2018-09-05 08:30:59 -07001166 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1167 if (propertyIt == requestDbusData.end())
1168 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001169 setErrorResponse(res, boost::beast::http::status::bad_request,
1170 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001171 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 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001188 setErrorResponse(transaction->res,
1189 boost::beast::http::status::not_found,
1190 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001191 return;
1192 }
Ed Tanous911ac312017-08-15 09:37:42 -07001193
Ed Tanous1abe55e2018-09-05 08:30:59 -07001194 for (const std::pair<std::string, std::vector<std::string>>
1195 connection : object_names)
1196 {
1197 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001198
Ed Tanous1abe55e2018-09-05 08:30:59 -07001199 crow::connections::systemBus->async_method_call(
1200 [connectionName{std::string(connectionName)},
1201 transaction](const boost::system::error_code ec,
1202 const std::string &introspectXml) {
1203 if (ec)
1204 {
1205 BMCWEB_LOG_ERROR
1206 << "Introspect call failed with error: "
1207 << ec.message()
1208 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001209 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001210 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001211 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001212 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001213
Ed Tanous1abe55e2018-09-05 08:30:59 -07001214 doc.Parse(introspectXml.c_str());
1215 tinyxml2::XMLNode *pRoot =
1216 doc.FirstChildElement("node");
1217 if (pRoot == nullptr)
1218 {
1219 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1220 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001221 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001222 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001223 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001224 tinyxml2::XMLElement *ifaceNode =
1225 pRoot->FirstChildElement("interface");
1226 while (ifaceNode != nullptr)
1227 {
1228 const char *interfaceName =
1229 ifaceNode->Attribute("name");
1230 BMCWEB_LOG_DEBUG << "found interface "
1231 << interfaceName;
1232 tinyxml2::XMLElement *propNode =
1233 ifaceNode->FirstChildElement("property");
1234 while (propNode != nullptr)
1235 {
1236 const char *propertyName =
1237 propNode->Attribute("name");
1238 BMCWEB_LOG_DEBUG << "Found property "
1239 << propertyName;
1240 if (propertyName == transaction->propertyName)
1241 {
1242 const char *argType =
1243 propNode->Attribute("type");
1244 if (argType != nullptr)
1245 {
1246 sdbusplus::message::message m =
1247 crow::connections::systemBus
1248 ->new_method_call(
1249 connectionName.c_str(),
1250 transaction->objectPath
1251 .c_str(),
1252 "org.freedesktop.DBus."
1253 "Properties",
1254 "Set");
1255 m.append(interfaceName,
1256 transaction->propertyName);
1257 int r = sd_bus_message_open_container(
1258 m.get(), SD_BUS_TYPE_VARIANT,
1259 argType);
1260 if (r < 0)
1261 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001262 transaction->setErrorStatus(
1263 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001264 return;
1265 }
1266 r = convertJsonToDbus(
1267 m.get(), argType,
1268 transaction->propertyValue);
1269 if (r < 0)
1270 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001271 transaction->setErrorStatus(
1272 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001273 return;
1274 }
1275 r = sd_bus_message_close_container(
1276 m.get());
1277 if (r < 0)
1278 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001279 transaction->setErrorStatus(
1280 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001281 return;
1282 }
Ed Tanous911ac312017-08-15 09:37:42 -07001283
Ed Tanous1abe55e2018-09-05 08:30:59 -07001284 crow::connections::systemBus
1285 ->async_send(
1286 m,
1287 [transaction](
1288 boost::system::error_code
1289 ec,
1290 sdbusplus::message::message
1291 &m) {
1292 BMCWEB_LOG_DEBUG << "sent";
1293 if (ec)
1294 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001295 setErrorResponse(
1296 transaction->res,
1297 boost::beast::http::
1298 status::
1299 forbidden,
1300 forbiddenPropDesc,
1301 ec.message());
1302 }
1303 else
1304 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001305 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001306 .jsonValue = {
1307 {"status", "ok"},
1308 {"message",
1309 "200 OK"},
1310 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001311 }
1312 });
1313 }
1314 }
1315 propNode =
1316 propNode->NextSiblingElement("property");
1317 }
1318 ifaceNode =
1319 ifaceNode->NextSiblingElement("interface");
1320 }
1321 },
1322 connectionName, transaction->objectPath,
1323 "org.freedesktop.DBus.Introspectable", "Introspect");
1324 }
1325 },
1326 "xyz.openbmc_project.ObjectMapper",
1327 "/xyz/openbmc_project/object_mapper",
1328 "xyz.openbmc_project.ObjectMapper", "GetObject",
1329 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001330}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001331
Ed Tanous049a0512018-11-01 13:58:42 -07001332inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1333 std::string &objectPath)
1334{
Ed Tanous049a0512018-11-01 13:58:42 -07001335
1336 // If accessing a single attribute, fill in and update objectPath,
1337 // otherwise leave destProperty blank
1338 std::string destProperty = "";
1339 const char *attrSeperator = "/attr/";
1340 size_t attrPosition = objectPath.find(attrSeperator);
1341 if (attrPosition != objectPath.npos)
1342 {
1343 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1344 objectPath.length());
1345 objectPath = objectPath.substr(0, attrPosition);
1346 }
1347
1348 if (req.method() == "POST"_method)
1349 {
1350 constexpr const char *actionSeperator = "/action/";
1351 size_t actionPosition = objectPath.find(actionSeperator);
1352 if (actionPosition != objectPath.npos)
1353 {
1354 std::string postProperty =
1355 objectPath.substr((actionPosition + strlen(actionSeperator)),
1356 objectPath.length());
1357 objectPath = objectPath.substr(0, actionPosition);
1358 handleAction(req, res, objectPath, postProperty);
1359 return;
1360 }
1361 }
1362 else if (req.method() == "GET"_method)
1363 {
1364 if (boost::ends_with(objectPath, "/enumerate"))
1365 {
1366 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1367 objectPath.end());
1368 handleEnumerate(res, objectPath);
1369 }
1370 else if (boost::ends_with(objectPath, "/list"))
1371 {
1372 objectPath.erase(objectPath.end() - sizeof("list"),
1373 objectPath.end());
1374 handleList(res, objectPath);
1375 }
1376 else
1377 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001378 // Trim any trailing "/" at the end
1379 if (boost::ends_with(objectPath, "/"))
1380 {
1381 objectPath.pop_back();
1382 handleList(res, objectPath, 1);
1383 }
1384 else
1385 {
1386 handleGet(res, objectPath, destProperty);
1387 }
Ed Tanous049a0512018-11-01 13:58:42 -07001388 }
1389 return;
1390 }
1391 else if (req.method() == "PUT"_method)
1392 {
1393 handlePut(req, res, objectPath, destProperty);
1394 return;
1395 }
1396
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001397 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1398 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001399 res.end();
1400}
1401
Ed Tanous1abe55e2018-09-05 08:30:59 -07001402template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1403{
1404 BMCWEB_ROUTE(app, "/bus/")
1405 .methods("GET"_method)(
1406 [](const crow::Request &req, crow::Response &res) {
1407 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1408 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001409 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001410 });
1411
1412 BMCWEB_ROUTE(app, "/bus/system/")
1413 .methods("GET"_method)(
1414 [](const crow::Request &req, crow::Response &res) {
1415 auto myCallback = [&res](const boost::system::error_code ec,
1416 std::vector<std::string> &names) {
1417 if (ec)
1418 {
1419 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1420 res.result(
1421 boost::beast::http::status::internal_server_error);
1422 }
1423 else
1424 {
1425 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001426 res.jsonValue = {{"status", "ok"}};
1427 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001428 for (auto &name : names)
1429 {
1430 objectsSub.push_back({{"name", name}});
1431 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001432 }
1433 res.end();
1434 };
1435 crow::connections::systemBus->async_method_call(
1436 std::move(myCallback), "org.freedesktop.DBus", "/",
1437 "org.freedesktop.DBus", "ListNames");
1438 });
1439
1440 BMCWEB_ROUTE(app, "/list/")
1441 .methods("GET"_method)(
1442 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001443 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001444 });
1445
1446 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous049a0512018-11-01 13:58:42 -07001447 .methods("GET"_method, "PUT"_method, "POST"_method)(
1448 [](const crow::Request &req, crow::Response &res,
1449 const std::string &path) {
1450 std::string objectPath = "/xyz/" + path;
1451 handleDBusUrl(req, res, objectPath);
1452 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001453
Ed Tanous049a0512018-11-01 13:58:42 -07001454 BMCWEB_ROUTE(app, "/org/<path>")
1455 .methods("GET"_method, "PUT"_method, "POST"_method)(
1456 [](const crow::Request &req, crow::Response &res,
1457 const std::string &path) {
1458 std::string objectPath = "/org/" + path;
1459 handleDBusUrl(req, res, objectPath);
1460 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001461
Ed Tanous1abe55e2018-09-05 08:30:59 -07001462 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1463 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1464 const std::string &dumpId) {
1465 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1466 if (!std::regex_match(dumpId, validFilename))
1467 {
1468 res.result(boost::beast::http::status::not_found);
1469 res.end();
1470 return;
1471 }
1472 std::experimental::filesystem::path loc(
1473 "/var/lib/phosphor-debug-collector/dumps");
1474
1475 loc += dumpId;
1476
1477 if (!std::experimental::filesystem::exists(loc) ||
1478 !std::experimental::filesystem::is_directory(loc))
1479 {
1480 res.result(boost::beast::http::status::not_found);
1481 res.end();
1482 return;
1483 }
1484 std::experimental::filesystem::directory_iterator files(loc);
1485 for (auto &file : files)
1486 {
1487 std::ifstream readFile(file.path());
1488 if (readFile.good())
1489 {
1490 continue;
1491 }
1492 res.addHeader("Content-Type", "application/octet-stream");
1493 res.body() = {std::istreambuf_iterator<char>(readFile),
1494 std::istreambuf_iterator<char>()};
1495 res.end();
1496 }
1497 res.result(boost::beast::http::status::not_found);
1498 res.end();
1499 return;
1500 });
1501
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001502 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001503 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001504 const std::string &Connection) {
1505 introspectObjects(Connection, "/",
1506 std::make_shared<bmcweb::AsyncResp>(res));
1507 });
1508
1509 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1510 .methods("GET"_method,
1511 "POST"_method)([](const crow::Request &req,
1512 crow::Response &res,
1513 const std::string &processName,
1514 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001515 std::vector<std::string> strs;
1516 boost::split(strs, requestedPath, boost::is_any_of("/"));
1517 std::string objectPath;
1518 std::string interfaceName;
1519 std::string methodName;
1520 auto it = strs.begin();
1521 if (it == strs.end())
1522 {
1523 objectPath = "/";
1524 }
1525 while (it != strs.end())
1526 {
1527 // Check if segment contains ".". If it does, it must be an
1528 // interface
1529 if (it->find(".") != std::string::npos)
1530 {
1531 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001532 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001533 // as part of our <path> specifier above, which causes the
1534 // normal trailing backslash redirector to fail.
1535 }
1536 else if (!it->empty())
1537 {
1538 objectPath += "/" + *it;
1539 }
1540 it++;
1541 }
1542 if (it != strs.end())
1543 {
1544 interfaceName = *it;
1545 it++;
1546
1547 // after interface, we might have a method name
1548 if (it != strs.end())
1549 {
1550 methodName = *it;
1551 it++;
1552 }
1553 }
1554 if (it != strs.end())
1555 {
1556 // if there is more levels past the method name, something went
1557 // wrong, return not found
1558 res.result(boost::beast::http::status::not_found);
1559 res.end();
1560 return;
1561 }
1562 if (interfaceName.empty())
1563 {
1564 crow::connections::systemBus->async_method_call(
1565 [&, processName,
1566 objectPath](const boost::system::error_code ec,
1567 const std::string &introspect_xml) {
1568 if (ec)
1569 {
1570 BMCWEB_LOG_ERROR
1571 << "Introspect call failed with error: "
1572 << ec.message()
1573 << " on process: " << processName
1574 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001575 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001576 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001577 tinyxml2::XMLDocument doc;
1578
1579 doc.Parse(introspect_xml.c_str());
1580 tinyxml2::XMLNode *pRoot =
1581 doc.FirstChildElement("node");
1582 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001583 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001584 BMCWEB_LOG_ERROR << "XML document failed to parse "
1585 << processName << " " << objectPath
1586 << "\n";
1587 res.jsonValue = {{"status", "XML parse error"}};
1588 res.result(boost::beast::http::status::
1589 internal_server_error);
1590 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001591 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001592
1593 BMCWEB_LOG_DEBUG << introspect_xml;
1594 res.jsonValue = {{"status", "ok"},
1595 {"bus_name", processName},
1596 {"object_path", objectPath}};
1597 nlohmann::json &interfacesArray =
1598 res.jsonValue["interfaces"];
1599 interfacesArray = nlohmann::json::array();
1600 tinyxml2::XMLElement *interface =
1601 pRoot->FirstChildElement("interface");
1602
1603 while (interface != nullptr)
1604 {
1605 const char *ifaceName =
1606 interface->Attribute("name");
1607 if (ifaceName != nullptr)
1608 {
1609 interfacesArray.push_back(
1610 {{"name", ifaceName}});
1611 }
1612
1613 interface =
1614 interface->NextSiblingElement("interface");
1615 }
1616
Ed Tanous1abe55e2018-09-05 08:30:59 -07001617 res.end();
1618 },
1619 processName, objectPath,
1620 "org.freedesktop.DBus.Introspectable", "Introspect");
1621 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001622 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001623 {
1624 crow::connections::systemBus->async_method_call(
1625 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001626 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001627 const boost::system::error_code ec,
1628 const std::string &introspect_xml) {
1629 if (ec)
1630 {
1631 BMCWEB_LOG_ERROR
1632 << "Introspect call failed with error: "
1633 << ec.message()
1634 << " on process: " << processName
1635 << " path: " << objectPath << "\n";
1636 }
1637 else
1638 {
1639 tinyxml2::XMLDocument doc;
1640
1641 doc.Parse(introspect_xml.c_str());
1642 tinyxml2::XMLNode *pRoot =
1643 doc.FirstChildElement("node");
1644 if (pRoot == nullptr)
1645 {
1646 BMCWEB_LOG_ERROR
1647 << "XML document failed to parse "
1648 << processName << " " << objectPath << "\n";
1649 res.result(boost::beast::http::status::
1650 internal_server_error);
1651 }
1652 else
1653 {
1654 tinyxml2::XMLElement *node =
1655 pRoot->FirstChildElement("node");
1656
1657 // if we know we're the only call, build the
1658 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001659 tinyxml2::XMLElement *interface =
1660 pRoot->FirstChildElement("interface");
1661
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001662 res.jsonValue = {
1663 {"status", "ok"},
1664 {"bus_name", processName},
1665 {"interface", interfaceName},
1666 {"object_path", objectPath},
1667 {"properties", nlohmann::json::object()}};
1668
1669 nlohmann::json &methodsArray =
1670 res.jsonValue["methods"];
1671 methodsArray = nlohmann::json::array();
1672
1673 nlohmann::json &signalsArray =
1674 res.jsonValue["signals"];
1675 signalsArray = nlohmann::json::array();
1676
Ed Tanous1abe55e2018-09-05 08:30:59 -07001677 while (interface != nullptr)
1678 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001679 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001680 interface->Attribute("name");
1681
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001682 if (ifaceName != nullptr &&
1683 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001684 {
1685 tinyxml2::XMLElement *methods =
1686 interface->FirstChildElement(
1687 "method");
1688 while (methods != nullptr)
1689 {
1690 nlohmann::json argsArray =
1691 nlohmann::json::array();
1692 tinyxml2::XMLElement *arg =
1693 methods->FirstChildElement(
1694 "arg");
1695 while (arg != nullptr)
1696 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001697 nlohmann::json thisArg;
1698 for (const char *fieldName :
1699 std::array<const char *,
1700 3>{"name",
1701 "direction",
1702 "type"})
1703 {
1704 const char *fieldValue =
1705 arg->Attribute(
1706 fieldName);
1707 if (fieldValue != nullptr)
1708 {
1709 thisArg[fieldName] =
1710 fieldValue;
1711 }
1712 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001713 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001714 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001715 arg = arg->NextSiblingElement(
1716 "arg");
1717 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001718
1719 const char *name =
1720 methods->Attribute("name");
1721 if (name != nullptr)
1722 {
1723 methodsArray.push_back(
1724 {{"name", name},
1725 {"uri", "/bus/system/" +
1726 processName +
1727 objectPath +
1728 "/" +
1729 interfaceName +
1730 "/" + name},
1731 {"args", argsArray}});
1732 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733 methods =
1734 methods->NextSiblingElement(
1735 "method");
1736 }
1737 tinyxml2::XMLElement *signals =
1738 interface->FirstChildElement(
1739 "signal");
1740 while (signals != nullptr)
1741 {
1742 nlohmann::json argsArray =
1743 nlohmann::json::array();
1744
1745 tinyxml2::XMLElement *arg =
1746 signals->FirstChildElement(
1747 "arg");
1748 while (arg != nullptr)
1749 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001750 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001751 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001752 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001753 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001754 if (name != nullptr &&
1755 type != nullptr)
1756 {
1757 argsArray.push_back({
1758 {"name", name},
1759 {"type", type},
1760 });
1761 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001762 arg = arg->NextSiblingElement(
1763 "arg");
1764 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001765 const char *name =
1766 signals->Attribute("name");
1767 if (name != nullptr)
1768 {
1769 signalsArray.push_back(
1770 {{"name", name},
1771 {"args", argsArray}});
1772 }
1773
Ed Tanous1abe55e2018-09-05 08:30:59 -07001774 signals =
1775 signals->NextSiblingElement(
1776 "signal");
1777 }
1778
Ed Tanous1abe55e2018-09-05 08:30:59 -07001779 break;
1780 }
1781
1782 interface = interface->NextSiblingElement(
1783 "interface");
1784 }
1785 if (interface == nullptr)
1786 {
1787 // if we got to the end of the list and
1788 // never found a match, throw 404
1789 res.result(
1790 boost::beast::http::status::not_found);
1791 }
1792 }
1793 }
1794 res.end();
1795 },
1796 processName, objectPath,
1797 "org.freedesktop.DBus.Introspectable", "Introspect");
1798 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001799 else
1800 {
1801 if (req.method() != "POST"_method)
1802 {
1803 res.result(boost::beast::http::status::not_found);
1804 res.end();
1805 return;
1806 }
1807
1808 nlohmann::json requestDbusData =
1809 nlohmann::json::parse(req.body, nullptr, false);
1810
1811 if (requestDbusData.is_discarded())
1812 {
1813 res.result(boost::beast::http::status::bad_request);
1814 res.end();
1815 return;
1816 }
1817 if (!requestDbusData.is_array())
1818 {
1819 res.result(boost::beast::http::status::bad_request);
1820 res.end();
1821 return;
1822 }
1823 auto transaction = std::make_shared<InProgressActionData>(res);
1824
1825 transaction->path = objectPath;
1826 transaction->methodName = methodName;
1827 transaction->arguments = std::move(requestDbusData);
1828
1829 findActionOnInterface(transaction, processName);
1830 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001831 });
1832}
1833} // namespace openbmc_mapper
1834} // namespace crow