blob: dfa618fd849eeca2c22fd9a60b222b54a905dc71 [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";
39
Matt Spinler2ae60092018-12-06 10:35:36 -060040const std::string notFoundDesc =
41 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060042const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060043const std::string noJsonDesc = "No JSON object could be decoded";
44const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinler2ae60092018-12-06 10:35:36 -060045
46void setErrorResponse(crow::Response &res, boost::beast::http::status result,
47 const std::string &desc, const std::string &msg)
48{
49 res.result(result);
50 res.jsonValue = {{"data", {{"description", desc}}},
51 {"message", msg},
52 {"status", "error"}};
53}
54
Ed Tanouse3cb5a32018-08-08 14:16:49 -070055void introspectObjects(const std::string &processName,
56 const std::string &objectPath,
57 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070058{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070059 if (transaction->res.jsonValue.is_null())
60 {
61 transaction->res.jsonValue = {{"status", "ok"},
62 {"bus_name", processName},
63 {"objects", nlohmann::json::array()}};
64 }
65
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070067 [transaction, processName{std::string(processName)},
68 objectPath{std::string(objectPath)}](
69 const boost::system::error_code ec,
70 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 if (ec)
72 {
73 BMCWEB_LOG_ERROR
74 << "Introspect call failed with error: " << ec.message()
75 << " on process: " << processName << " path: " << objectPath
76 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070077 return;
78 }
79 transaction->res.jsonValue["objects"].push_back(
80 {{"path", objectPath}});
81
82 tinyxml2::XMLDocument doc;
83
84 doc.Parse(introspect_xml.c_str());
85 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
86 if (pRoot == nullptr)
87 {
88 BMCWEB_LOG_ERROR << "XML document failed to parse "
89 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070090 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070091 else
92 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070093 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
94 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070096 const char *childPath = node->Attribute("name");
97 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070098 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 std::string newpath;
100 if (objectPath != "/")
101 {
102 newpath += objectPath;
103 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700104 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700106 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700108
109 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 }
111 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700113 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700115}
Ed Tanous64530012018-02-06 17:08:16 -0800116
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600117void getPropertiesForEnumerate(const std::string &objectPath,
118 const std::string &service,
119 const std::string &interface,
120 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
121{
122 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
123 << service << " " << interface;
124
125 crow::connections::systemBus->async_method_call(
126 [asyncResp, objectPath, service,
127 interface](const boost::system::error_code ec,
128 const std::vector<
129 std::pair<std::string, dbus::utility::DbusVariantType>>
130 &propertiesList) {
131 if (ec)
132 {
133 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
134 << interface << " service " << service
135 << " failed with code " << ec;
136 return;
137 }
138
139 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
140 nlohmann::json &objectJson = dataJson[objectPath];
141 if (objectJson.is_null())
142 {
143 objectJson = nlohmann::json::object();
144 }
145
146 for (const auto &[name, value] : propertiesList)
147 {
148 nlohmann::json &propertyJson = objectJson[name];
149 sdbusplus::message::variant_ns::visit(
150 [&propertyJson](auto &&val) { propertyJson = val; }, value);
151 }
152 },
153 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
154 interface);
155}
156
157// Find any results that weren't picked up by ObjectManagers, to be
158// called after all ObjectManagers are searched for and called.
159void findRemainingObjectsForEnumerate(
160 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
161 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
162{
163 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
164 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
165
166 for (const auto &[path, interface_map] : *subtree)
167 {
168 if (path == objectPath)
169 {
170 // An enumerate does not return the target path's properties
171 continue;
172 }
173 if (dataJson.find(path) == dataJson.end())
174 {
175 for (const auto &[service, interfaces] : interface_map)
176 {
177 for (const auto &interface : interfaces)
178 {
179 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
180 {
181 getPropertiesForEnumerate(path, service, interface,
182 asyncResp);
183 }
184 }
185 }
186 }
187 }
188}
189
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600190struct InProgressEnumerateData
191{
192 InProgressEnumerateData(const std::string &objectPath,
193 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
194 objectPath(objectPath),
195 asyncResp(asyncResp)
196 {
197 }
198
199 ~InProgressEnumerateData()
200 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600201 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600202 }
203
204 const std::string objectPath;
205 std::shared_ptr<GetSubTreeType> subtree;
206 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
207};
208
209void getManagedObjectsForEnumerate(
210 const std::string &object_name, const std::string &object_manager_path,
211 const std::string &connection_name,
212 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213{
Ed Tanous049a0512018-11-01 13:58:42 -0700214 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
215 << " object_manager_path " << object_manager_path
216 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600218 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700219 connection_name](const boost::system::error_code ec,
220 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221 if (ec)
222 {
Ed Tanous049a0512018-11-01 13:58:42 -0700223 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600224 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700225 << " failed with code " << ec;
226 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700227 }
Ed Tanous64530012018-02-06 17:08:16 -0800228
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600229 nlohmann::json &dataJson =
230 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700231
232 for (const auto &objectPath : objects)
233 {
234 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 {
Ed Tanous049a0512018-11-01 13:58:42 -0700236 BMCWEB_LOG_DEBUG << "Reading object "
237 << objectPath.first.str;
238 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700239 if (objectJson.is_null())
240 {
241 objectJson = nlohmann::json::object();
242 }
243 for (const auto &interface : objectPath.second)
244 {
245 for (const auto &property : interface.second)
246 {
247 nlohmann::json &propertyJson =
248 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700249 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 [&propertyJson](auto &&val) {
251 propertyJson = val;
252 },
253 property.second);
254 }
255 }
256 }
Ed Tanous049a0512018-11-01 13:58:42 -0700257 for (const auto &interface : objectPath.second)
258 {
259 if (interface.first == "org.freedesktop.DBus.ObjectManager")
260 {
261 getManagedObjectsForEnumerate(
262 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600263 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700264 }
265 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266 }
267 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700268 connection_name, object_manager_path,
269 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
270}
271
272void findObjectManagerPathForEnumerate(
273 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600274 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700275{
Ed Tanous049a0512018-11-01 13:58:42 -0700276 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
277 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700278 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600279 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700280 const boost::system::error_code ec,
281 const boost::container::flat_map<
282 std::string, boost::container::flat_map<
283 std::string, std::vector<std::string>>>
284 &objects) {
285 if (ec)
286 {
Ed Tanous049a0512018-11-01 13:58:42 -0700287 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
288 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700289 return;
290 }
291
Ed Tanousf254ba72018-10-12 13:40:35 -0700292 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700293 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700294 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700295 {
296 if (connectionGroup.first == connection_name)
297 {
298 // Found the object manager path for this resource.
299 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700300 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600301 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 return;
303 }
304 }
305 }
306 },
307 "xyz.openbmc_project.ObjectMapper",
308 "/xyz/openbmc_project/object_mapper",
309 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
310 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700311}
Ed Tanous64530012018-02-06 17:08:16 -0800312
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600313// Uses GetObject to add the object info about the target /enumerate path to the
314// results of GetSubTree, as GetSubTree will not return info for the
315// target path, and then continues on enumerating the rest of the tree.
316void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
317{
318 using GetObjectType =
319 std::vector<std::pair<std::string, std::vector<std::string>>>;
320
321 crow::connections::systemBus->async_method_call(
322 [transaction](const boost::system::error_code ec,
323 const GetObjectType &objects) {
324 if (ec)
325 {
326 BMCWEB_LOG_ERROR << "GetObject for path "
327 << transaction->objectPath
328 << " failed with code " << ec;
329 return;
330 }
331
332 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
333 << " has " << objects.size() << " entries";
334 if (!objects.empty())
335 {
336 transaction->subtree->emplace_back(transaction->objectPath,
337 objects);
338 }
339
340 // Map indicating connection name, and the path where the object
341 // manager exists
342 boost::container::flat_map<std::string, std::string> connections;
343
344 for (const auto &object : *(transaction->subtree))
345 {
346 for (const auto &connection : object.second)
347 {
348 std::string &objectManagerPath =
349 connections[connection.first];
350 for (const auto &interface : connection.second)
351 {
352 BMCWEB_LOG_DEBUG << connection.first
353 << " has interface " << interface;
354 if (interface == "org.freedesktop.DBus.ObjectManager")
355 {
356 BMCWEB_LOG_DEBUG << "found object manager path "
357 << object.first;
358 objectManagerPath = object.first;
359 }
360 }
361 }
362 }
363 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
364
365 for (const auto &connection : connections)
366 {
367 // If we already know where the object manager is, we don't need
368 // to search for it, we can call directly in to
369 // getManagedObjects
370 if (!connection.second.empty())
371 {
372 getManagedObjectsForEnumerate(
373 transaction->objectPath, connection.second,
374 connection.first, transaction);
375 }
376 else
377 {
378 // otherwise we need to find the object manager path before
379 // we can continue
380 findObjectManagerPathForEnumerate(
381 transaction->objectPath, connection.first, transaction);
382 }
383 }
384 },
385 "xyz.openbmc_project.ObjectMapper",
386 "/xyz/openbmc_project/object_mapper",
387 "xyz.openbmc_project.ObjectMapper", "GetObject",
388 transaction->objectPath, std::array<const char *, 0>());
389}
Ed Tanous64530012018-02-06 17:08:16 -0800390
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700391// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700392struct InProgressActionData
393{
394 InProgressActionData(crow::Response &res) : res(res){};
395 ~InProgressActionData()
396 {
Matt Spinler6db06242018-12-11 11:21:22 -0600397 // If still no JSON filled in, then we never found the method.
398 if (res.jsonValue.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 {
Matt Spinler6db06242018-12-11 11:21:22 -0600400 setErrorResponse(res, boost::beast::http::status::not_found,
401 methodNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 }
403 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700404 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700405
Matt Spinler6db06242018-12-11 11:21:22 -0600406 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 {
Matt Spinler6db06242018-12-11 11:21:22 -0600408 setErrorResponse(res, boost::beast::http::status::internal_server_error,
409 desc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 }
411 crow::Response &res;
412 std::string path;
413 std::string methodName;
414 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700415};
416
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417std::vector<std::string> dbusArgSplit(const std::string &string)
418{
419 std::vector<std::string> ret;
420 if (string.empty())
421 {
422 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700423 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424 ret.push_back("");
425 int containerDepth = 0;
426
427 for (std::string::const_iterator character = string.begin();
428 character != string.end(); character++)
429 {
430 ret.back() += *character;
431 switch (*character)
432 {
433 case ('a'):
434 break;
435 case ('('):
436 case ('{'):
437 containerDepth++;
438 break;
439 case ('}'):
440 case (')'):
441 containerDepth--;
442 if (containerDepth == 0)
443 {
444 if (character + 1 != string.end())
445 {
446 ret.push_back("");
447 }
448 }
449 break;
450 default:
451 if (containerDepth == 0)
452 {
453 if (character + 1 != string.end())
454 {
455 ret.push_back("");
456 }
457 }
458 break;
459 }
460 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700461}
462
Ed Tanousd76323e2018-08-07 14:35:40 -0700463int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700464 const nlohmann::json &input_json)
465{
466 int r = 0;
467 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
468 << " to type: " << arg_type;
469 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700470
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471 // Assume a single object for now.
472 const nlohmann::json *j = &input_json;
473 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700474
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700475 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 {
477 // If we are decoding multiple objects, grab the pointer to the
478 // iterator, and increment it for the next loop
479 if (argTypes.size() > 1)
480 {
481 if (jIt == input_json.end())
482 {
483 return -2;
484 }
485 j = &*jIt;
486 jIt++;
487 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700488 const int64_t *intValue = j->get_ptr<const int64_t *>();
489 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
490 const std::string *stringValue = j->get_ptr<const std::string *>();
491 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700492 const bool *b = j->get_ptr<const bool *>();
493 int64_t v = 0;
494 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700495
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 // Do some basic type conversions that make sense. uint can be
497 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700498 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700500 v = static_cast<int64_t>(*uintValue);
501 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700502 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700503 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700504 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700505 d = static_cast<double>(*uintValue);
506 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700508 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700510 d = static_cast<double>(*intValue);
511 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700512 }
513
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700514 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700516 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 {
518 return -1;
519 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700520 r = sd_bus_message_append_basic(m, argCode[0],
521 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 if (r < 0)
523 {
524 return r;
525 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700526 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700527 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700529 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 {
531 return -1;
532 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700533 int32_t i = static_cast<int32_t>(*intValue);
534 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700535 if (r < 0)
536 {
537 return r;
538 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700539 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700540 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700541 {
542 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700543 int boolInt = false;
544 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700546 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 }
548 else if (b != nullptr)
549 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700550 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700552 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700554 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 }
556 else
557 {
558 return -1;
559 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700560 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 if (r < 0)
562 {
563 return r;
564 }
565 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700566 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
570 return -1;
571 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700572 int16_t n = static_cast<int16_t>(*intValue);
573 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 if (r < 0)
575 {
576 return r;
577 }
578 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700579 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
583 return -1;
584 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700585 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 if (r < 0)
587 {
588 return r;
589 }
590 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700591 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700593 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 {
595 return -1;
596 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700597 uint8_t y = static_cast<uint8_t>(*uintValue);
598 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700602 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 {
604 return -1;
605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 uint16_t q = static_cast<uint16_t>(*uintValue);
607 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700609 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700611 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 {
613 return -1;
614 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 uint32_t u = static_cast<uint32_t>(*uintValue);
616 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700618 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700620 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
622 return -1;
623 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700624 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700626 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700628 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700630 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700632 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700634 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700635 if (r < 0)
636 {
637 return r;
638 }
639
640 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
641 ++it)
642 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700643 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 if (r < 0)
645 {
646 return r;
647 }
648
649 it++;
650 }
651 sd_bus_message_close_container(m);
652 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700653 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700655 std::string containedType = argCode.substr(1);
656 BMCWEB_LOG_DEBUG << "variant type: " << argCode
657 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700659 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 if (r < 0)
661 {
662 return r;
663 }
664
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700665 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 if (r < 0)
667 {
668 return r;
669 }
670
671 r = sd_bus_message_close_container(m);
672 if (r < 0)
673 {
674 return r;
675 }
676 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700677 else if (boost::starts_with(argCode, "(") &&
678 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 {
686 if (it == j->end())
687 {
688 return -1;
689 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700690 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 if (r < 0)
692 {
693 return r;
694 }
695 it++;
696 }
697 r = sd_bus_message_close_container(m);
698 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700699 else if (boost::starts_with(argCode, "{") &&
700 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700701 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700702 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 containedType.c_str());
705 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 if (codes.size() != 2)
707 {
708 return -1;
709 }
710 const std::string &key_type = codes[0];
711 const std::string &value_type = codes[1];
712 for (auto it : j->items())
713 {
714 r = convertJsonToDbus(m, key_type, it.key());
715 if (r < 0)
716 {
717 return r;
718 }
719
720 r = convertJsonToDbus(m, value_type, it.value());
721 if (r < 0)
722 {
723 return r;
724 }
725 }
726 r = sd_bus_message_close_container(m);
727 }
728 else
729 {
730 return -2;
731 }
732 if (r < 0)
733 {
734 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700735 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700736
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 if (argTypes.size() > 1)
738 {
739 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700740 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700741 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700742}
743
Ed Tanousd76323e2018-08-07 14:35:40 -0700744void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 const std::string &connectionName)
746{
747 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
748 << connectionName;
749 crow::connections::systemBus->async_method_call(
750 [transaction, connectionName{std::string(connectionName)}](
751 const boost::system::error_code ec,
752 const std::string &introspect_xml) {
753 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
754 if (ec)
755 {
756 BMCWEB_LOG_ERROR
757 << "Introspect call failed with error: " << ec.message()
758 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700759 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 else
761 {
762 tinyxml2::XMLDocument doc;
763
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700764 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700765 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
766 if (pRoot == nullptr)
767 {
768 BMCWEB_LOG_ERROR << "XML document failed to parse "
769 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700770 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700772 tinyxml2::XMLElement *interfaceNode =
773 pRoot->FirstChildElement("interface");
774 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700776 const char *thisInterfaceName =
777 interfaceNode->Attribute("name");
778 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700780 tinyxml2::XMLElement *methodNode =
781 interfaceNode->FirstChildElement("method");
782 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700783 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700784 const char *thisMethodName =
785 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700787 << thisMethodName;
788 if (thisMethodName != nullptr &&
789 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700791 BMCWEB_LOG_DEBUG
792 << "Found method named " << thisMethodName
793 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 sdbusplus::message::message m =
795 crow::connections::systemBus
796 ->new_method_call(
797 connectionName.c_str(),
798 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700799 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 transaction->methodName.c_str());
801
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700802 tinyxml2::XMLElement *argumentNode =
803 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700805 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 transaction->arguments.begin();
807
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700808 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700810 const char *argDirection =
811 argumentNode->Attribute("direction");
812 const char *argType =
813 argumentNode->Attribute("type");
814 if (argDirection != nullptr &&
815 argType != nullptr &&
816 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700818
819 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 transaction->arguments.end())
821 {
Matt Spinler6db06242018-12-11 11:21:22 -0600822 transaction->setErrorStatus(
823 "Invalid method args");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 return;
825 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700826 if (convertJsonToDbus(
827 m.get(), std::string(argType),
828 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 {
Matt Spinler6db06242018-12-11 11:21:22 -0600830 transaction->setErrorStatus(
831 "Invalid method arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 return;
833 }
834
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700835 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700836 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700837 argumentNode =
838 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700840
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 crow::connections::systemBus->async_send(
842 m, [transaction](
843 boost::system::error_code ec,
844 sdbusplus::message::message &m) {
845 if (ec)
846 {
Matt Spinler6db06242018-12-11 11:21:22 -0600847 transaction->setErrorStatus(
848 "Method call failed");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700849 return;
850 }
851 transaction->res.jsonValue = {
852 {"status", "ok"},
853 {"message", "200 OK"},
854 {"data", nullptr}};
855 });
856 break;
857 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700858 methodNode =
859 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700860 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700862 interfaceNode =
863 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700864 }
865 }
866 },
867 connectionName, transaction->path,
868 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700869}
870
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700871void handleAction(const crow::Request &req, crow::Response &res,
872 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700873{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700874 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
875 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 nlohmann::json requestDbusData =
877 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700878
Ed Tanous1abe55e2018-09-05 08:30:59 -0700879 if (requestDbusData.is_discarded())
880 {
Matt Spinler6db06242018-12-11 11:21:22 -0600881 setErrorResponse(res, boost::beast::http::status::bad_request,
882 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700883 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 return;
885 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700886 nlohmann::json::iterator data = requestDbusData.find("data");
887 if (data == requestDbusData.end())
888 {
Matt Spinler6db06242018-12-11 11:21:22 -0600889 setErrorResponse(res, boost::beast::http::status::bad_request,
890 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700891 res.end();
892 return;
893 }
894
895 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700896 {
Matt Spinler6db06242018-12-11 11:21:22 -0600897 setErrorResponse(res, boost::beast::http::status::bad_request,
898 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899 res.end();
900 return;
901 }
902 auto transaction = std::make_shared<InProgressActionData>(res);
903
904 transaction->path = objectPath;
905 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700906 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700907 crow::connections::systemBus->async_method_call(
908 [transaction](
909 const boost::system::error_code ec,
910 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700911 &interfaceNames) {
912 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700913 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700914 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -0600915 setErrorResponse(transaction->res,
916 boost::beast::http::status::not_found,
917 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700918 return;
919 }
920
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700921 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
922 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700923
924 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700925 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926 {
927 findActionOnInterface(transaction, object.first);
928 }
929 },
930 "xyz.openbmc_project.ObjectMapper",
931 "/xyz/openbmc_project/object_mapper",
932 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
933 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700934}
935
Ed Tanousf839dfe2018-11-12 11:11:15 -0800936void handleList(crow::Response &res, const std::string &objectPath,
937 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700938{
939 crow::connections::systemBus->async_method_call(
940 [&res](const boost::system::error_code ec,
941 std::vector<std::string> &objectPaths) {
942 if (ec)
943 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -0600944 setErrorResponse(res, boost::beast::http::status::not_found,
945 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700946 }
947 else
948 {
949 res.jsonValue = {{"status", "ok"},
950 {"message", "200 OK"},
951 {"data", std::move(objectPaths)}};
952 }
953 res.end();
954 },
955 "xyz.openbmc_project.ObjectMapper",
956 "/xyz/openbmc_project/object_mapper",
957 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -0800958 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700959}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700960
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700961void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700962{
Ed Tanous049a0512018-11-01 13:58:42 -0700963 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
964 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
965
966 asyncResp->res.jsonValue = {{"message", "200 OK"},
967 {"status", "ok"},
968 {"data", nlohmann::json::object()}};
969
Ed Tanous1abe55e2018-09-05 08:30:59 -0700970 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600971 [objectPath, asyncResp](const boost::system::error_code ec,
972 GetSubTreeType &object_names) {
973 auto transaction = std::make_shared<InProgressEnumerateData>(
974 objectPath, asyncResp);
975
976 transaction->subtree =
977 std::make_shared<GetSubTreeType>(std::move(object_names));
978
Ed Tanous1abe55e2018-09-05 08:30:59 -0700979 if (ec)
980 {
Matt Spinler2ae60092018-12-06 10:35:36 -0600981 BMCWEB_LOG_ERROR << "GetSubTree failed on "
982 << transaction->objectPath;
983 setErrorResponse(transaction->asyncResp->res,
984 boost::beast::http::status::not_found,
985 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700986 return;
987 }
Ed Tanous64530012018-02-06 17:08:16 -0800988
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600989 // Add the data for the path passed in to the results
990 // as if GetSubTree returned it, and continue on enumerating
991 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992 },
993 "xyz.openbmc_project.ObjectMapper",
994 "/xyz/openbmc_project/object_mapper",
995 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -0700996 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800997}
Ed Tanous911ac312017-08-15 09:37:42 -0700998
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700999void handleGet(crow::Response &res, std::string &objectPath,
1000 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001001{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001002 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1003 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001004 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001005
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 std::shared_ptr<std::string> path =
1007 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001008
Ed Tanous1abe55e2018-09-05 08:30:59 -07001009 using GetObjectType =
1010 std::vector<std::pair<std::string, std::vector<std::string>>>;
1011 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001012 [&res, path, propertyName](const boost::system::error_code ec,
1013 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001014 if (ec || object_names.size() <= 0)
1015 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001016 setErrorResponse(res, boost::beast::http::status::not_found,
1017 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018 res.end();
1019 return;
1020 }
1021 std::shared_ptr<nlohmann::json> response =
1022 std::make_shared<nlohmann::json>(nlohmann::json::object());
1023 // The mapper should never give us an empty interface names list,
1024 // but check anyway
1025 for (const std::pair<std::string, std::vector<std::string>>
1026 connection : object_names)
1027 {
1028 const std::vector<std::string> &interfaceNames =
1029 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001030
Ed Tanous1abe55e2018-09-05 08:30:59 -07001031 if (interfaceNames.size() <= 0)
1032 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001033 setErrorResponse(res, boost::beast::http::status::not_found,
1034 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001035 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001036 return;
1037 }
1038
1039 for (const std::string &interface : interfaceNames)
1040 {
1041 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001042 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001043 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001044 const std::vector<std::pair<
1045 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001046 &properties) {
1047 if (ec)
1048 {
1049 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1050 << ec;
1051 }
1052 else
1053 {
James Feist5b4aa862018-08-16 14:07:01 -07001054 for (const std::pair<
1055 std::string,
1056 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001057 &property : properties)
1058 {
1059 // if property name is empty, or matches our
1060 // search query, add it to the response json
1061
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001062 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001063 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001064 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001065 [&response, &property](auto &&val) {
1066 (*response)[property.first] =
1067 val;
1068 },
1069 property.second);
1070 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001071 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001072 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001073 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001074 [&response](auto &&val) {
1075 (*response) = val;
1076 },
1077 property.second);
1078 }
1079 }
1080 }
1081 if (response.use_count() == 1)
1082 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001083 if (!propertyName->empty() && response->empty())
1084 {
1085 setErrorResponse(
1086 res,
1087 boost::beast::http::status::not_found,
1088 propNotFoundDesc, notFoundMsg);
1089 }
1090 else
1091 {
1092 res.jsonValue = {{"status", "ok"},
1093 {"message", "200 OK"},
1094 {"data", *response}};
1095 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001096 res.end();
1097 }
1098 },
1099 connection.first, *path,
1100 "org.freedesktop.DBus.Properties", "GetAll", interface);
1101 }
1102 }
1103 },
1104 "xyz.openbmc_project.ObjectMapper",
1105 "/xyz/openbmc_project/object_mapper",
1106 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1107 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001108}
1109
Ed Tanous1abe55e2018-09-05 08:30:59 -07001110struct AsyncPutRequest
1111{
1112 AsyncPutRequest(crow::Response &res) : res(res)
1113 {
1114 res.jsonValue = {
1115 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
1116 }
1117 ~AsyncPutRequest()
1118 {
1119 if (res.result() == boost::beast::http::status::internal_server_error)
1120 {
1121 // Reset the json object to clear out any data that made it in
1122 // before the error happened todo(ed) handle error condition with
1123 // proper code
1124 res.jsonValue = nlohmann::json::object();
1125 }
1126
1127 if (res.jsonValue.empty())
1128 {
1129 res.result(boost::beast::http::status::forbidden);
1130 res.jsonValue = {
1131 {"status", "error"},
1132 {"message", "403 Forbidden"},
1133 {"data",
1134 {{"message", "The specified property cannot be created: " +
1135 propertyName}}}};
1136 }
1137
1138 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001139 }
1140
Ed Tanous1abe55e2018-09-05 08:30:59 -07001141 void setErrorStatus()
1142 {
1143 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001144 }
1145
Ed Tanous1abe55e2018-09-05 08:30:59 -07001146 crow::Response &res;
1147 std::string objectPath;
1148 std::string propertyName;
1149 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001150};
1151
Ed Tanousd76323e2018-08-07 14:35:40 -07001152void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153 const std::string &objectPath, const std::string &destProperty)
1154{
1155 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 {
1160 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001161 res.end();
1162 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001163 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001164
Ed Tanous1abe55e2018-09-05 08:30:59 -07001165 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1166 if (propertyIt == requestDbusData.end())
1167 {
1168 res.result(boost::beast::http::status::bad_request);
1169 res.end();
1170 return;
1171 }
1172 const nlohmann::json &propertySetValue = *propertyIt;
1173 auto transaction = std::make_shared<AsyncPutRequest>(res);
1174 transaction->objectPath = objectPath;
1175 transaction->propertyName = destProperty;
1176 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001177
Ed Tanous1abe55e2018-09-05 08:30:59 -07001178 using GetObjectType =
1179 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001180
Ed Tanous1abe55e2018-09-05 08:30:59 -07001181 crow::connections::systemBus->async_method_call(
1182 [transaction](const boost::system::error_code ec,
1183 const GetObjectType &object_names) {
1184 if (!ec && object_names.size() <= 0)
1185 {
1186 transaction->res.result(boost::beast::http::status::not_found);
1187 return;
1188 }
Ed Tanous911ac312017-08-15 09:37:42 -07001189
Ed Tanous1abe55e2018-09-05 08:30:59 -07001190 for (const std::pair<std::string, std::vector<std::string>>
1191 connection : object_names)
1192 {
1193 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001194
Ed Tanous1abe55e2018-09-05 08:30:59 -07001195 crow::connections::systemBus->async_method_call(
1196 [connectionName{std::string(connectionName)},
1197 transaction](const boost::system::error_code ec,
1198 const std::string &introspectXml) {
1199 if (ec)
1200 {
1201 BMCWEB_LOG_ERROR
1202 << "Introspect call failed with error: "
1203 << ec.message()
1204 << " on process: " << connectionName;
1205 transaction->setErrorStatus();
1206 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001207 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001208 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001209
Ed Tanous1abe55e2018-09-05 08:30:59 -07001210 doc.Parse(introspectXml.c_str());
1211 tinyxml2::XMLNode *pRoot =
1212 doc.FirstChildElement("node");
1213 if (pRoot == nullptr)
1214 {
1215 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1216 << introspectXml;
1217 transaction->setErrorStatus();
1218 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001219 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001220 tinyxml2::XMLElement *ifaceNode =
1221 pRoot->FirstChildElement("interface");
1222 while (ifaceNode != nullptr)
1223 {
1224 const char *interfaceName =
1225 ifaceNode->Attribute("name");
1226 BMCWEB_LOG_DEBUG << "found interface "
1227 << interfaceName;
1228 tinyxml2::XMLElement *propNode =
1229 ifaceNode->FirstChildElement("property");
1230 while (propNode != nullptr)
1231 {
1232 const char *propertyName =
1233 propNode->Attribute("name");
1234 BMCWEB_LOG_DEBUG << "Found property "
1235 << propertyName;
1236 if (propertyName == transaction->propertyName)
1237 {
1238 const char *argType =
1239 propNode->Attribute("type");
1240 if (argType != nullptr)
1241 {
1242 sdbusplus::message::message m =
1243 crow::connections::systemBus
1244 ->new_method_call(
1245 connectionName.c_str(),
1246 transaction->objectPath
1247 .c_str(),
1248 "org.freedesktop.DBus."
1249 "Properties",
1250 "Set");
1251 m.append(interfaceName,
1252 transaction->propertyName);
1253 int r = sd_bus_message_open_container(
1254 m.get(), SD_BUS_TYPE_VARIANT,
1255 argType);
1256 if (r < 0)
1257 {
1258 transaction->setErrorStatus();
1259 return;
1260 }
1261 r = convertJsonToDbus(
1262 m.get(), argType,
1263 transaction->propertyValue);
1264 if (r < 0)
1265 {
1266 transaction->setErrorStatus();
1267 return;
1268 }
1269 r = sd_bus_message_close_container(
1270 m.get());
1271 if (r < 0)
1272 {
1273 transaction->setErrorStatus();
1274 return;
1275 }
Ed Tanous911ac312017-08-15 09:37:42 -07001276
Ed Tanous1abe55e2018-09-05 08:30:59 -07001277 crow::connections::systemBus
1278 ->async_send(
1279 m,
1280 [transaction](
1281 boost::system::error_code
1282 ec,
1283 sdbusplus::message::message
1284 &m) {
1285 BMCWEB_LOG_DEBUG << "sent";
1286 if (ec)
1287 {
1288 transaction->res
1289 .jsonValue
1290 ["status"] =
1291 "error";
1292 transaction->res
1293 .jsonValue
1294 ["message"] =
1295 ec.message();
1296 }
1297 });
1298 }
1299 }
1300 propNode =
1301 propNode->NextSiblingElement("property");
1302 }
1303 ifaceNode =
1304 ifaceNode->NextSiblingElement("interface");
1305 }
1306 },
1307 connectionName, transaction->objectPath,
1308 "org.freedesktop.DBus.Introspectable", "Introspect");
1309 }
1310 },
1311 "xyz.openbmc_project.ObjectMapper",
1312 "/xyz/openbmc_project/object_mapper",
1313 "xyz.openbmc_project.ObjectMapper", "GetObject",
1314 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001315}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001316
Ed Tanous049a0512018-11-01 13:58:42 -07001317inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1318 std::string &objectPath)
1319{
Ed Tanous049a0512018-11-01 13:58:42 -07001320
1321 // If accessing a single attribute, fill in and update objectPath,
1322 // otherwise leave destProperty blank
1323 std::string destProperty = "";
1324 const char *attrSeperator = "/attr/";
1325 size_t attrPosition = objectPath.find(attrSeperator);
1326 if (attrPosition != objectPath.npos)
1327 {
1328 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1329 objectPath.length());
1330 objectPath = objectPath.substr(0, attrPosition);
1331 }
1332
1333 if (req.method() == "POST"_method)
1334 {
1335 constexpr const char *actionSeperator = "/action/";
1336 size_t actionPosition = objectPath.find(actionSeperator);
1337 if (actionPosition != objectPath.npos)
1338 {
1339 std::string postProperty =
1340 objectPath.substr((actionPosition + strlen(actionSeperator)),
1341 objectPath.length());
1342 objectPath = objectPath.substr(0, actionPosition);
1343 handleAction(req, res, objectPath, postProperty);
1344 return;
1345 }
1346 }
1347 else if (req.method() == "GET"_method)
1348 {
1349 if (boost::ends_with(objectPath, "/enumerate"))
1350 {
1351 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1352 objectPath.end());
1353 handleEnumerate(res, objectPath);
1354 }
1355 else if (boost::ends_with(objectPath, "/list"))
1356 {
1357 objectPath.erase(objectPath.end() - sizeof("list"),
1358 objectPath.end());
1359 handleList(res, objectPath);
1360 }
1361 else
1362 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001363 // Trim any trailing "/" at the end
1364 if (boost::ends_with(objectPath, "/"))
1365 {
1366 objectPath.pop_back();
1367 handleList(res, objectPath, 1);
1368 }
1369 else
1370 {
1371 handleGet(res, objectPath, destProperty);
1372 }
Ed Tanous049a0512018-11-01 13:58:42 -07001373 }
1374 return;
1375 }
1376 else if (req.method() == "PUT"_method)
1377 {
1378 handlePut(req, res, objectPath, destProperty);
1379 return;
1380 }
1381
1382 res.result(boost::beast::http::status::method_not_allowed);
1383 res.end();
1384}
1385
Ed Tanous1abe55e2018-09-05 08:30:59 -07001386template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1387{
1388 BMCWEB_ROUTE(app, "/bus/")
1389 .methods("GET"_method)(
1390 [](const crow::Request &req, crow::Response &res) {
1391 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1392 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001393 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001394 });
1395
1396 BMCWEB_ROUTE(app, "/bus/system/")
1397 .methods("GET"_method)(
1398 [](const crow::Request &req, crow::Response &res) {
1399 auto myCallback = [&res](const boost::system::error_code ec,
1400 std::vector<std::string> &names) {
1401 if (ec)
1402 {
1403 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1404 res.result(
1405 boost::beast::http::status::internal_server_error);
1406 }
1407 else
1408 {
1409 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001410 res.jsonValue = {{"status", "ok"}};
1411 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001412 for (auto &name : names)
1413 {
1414 objectsSub.push_back({{"name", name}});
1415 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001416 }
1417 res.end();
1418 };
1419 crow::connections::systemBus->async_method_call(
1420 std::move(myCallback), "org.freedesktop.DBus", "/",
1421 "org.freedesktop.DBus", "ListNames");
1422 });
1423
1424 BMCWEB_ROUTE(app, "/list/")
1425 .methods("GET"_method)(
1426 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001427 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001428 });
1429
1430 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous049a0512018-11-01 13:58:42 -07001431 .methods("GET"_method, "PUT"_method, "POST"_method)(
1432 [](const crow::Request &req, crow::Response &res,
1433 const std::string &path) {
1434 std::string objectPath = "/xyz/" + path;
1435 handleDBusUrl(req, res, objectPath);
1436 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001437
Ed Tanous049a0512018-11-01 13:58:42 -07001438 BMCWEB_ROUTE(app, "/org/<path>")
1439 .methods("GET"_method, "PUT"_method, "POST"_method)(
1440 [](const crow::Request &req, crow::Response &res,
1441 const std::string &path) {
1442 std::string objectPath = "/org/" + path;
1443 handleDBusUrl(req, res, objectPath);
1444 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001445
Ed Tanous1abe55e2018-09-05 08:30:59 -07001446 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1447 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1448 const std::string &dumpId) {
1449 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1450 if (!std::regex_match(dumpId, validFilename))
1451 {
1452 res.result(boost::beast::http::status::not_found);
1453 res.end();
1454 return;
1455 }
1456 std::experimental::filesystem::path loc(
1457 "/var/lib/phosphor-debug-collector/dumps");
1458
1459 loc += dumpId;
1460
1461 if (!std::experimental::filesystem::exists(loc) ||
1462 !std::experimental::filesystem::is_directory(loc))
1463 {
1464 res.result(boost::beast::http::status::not_found);
1465 res.end();
1466 return;
1467 }
1468 std::experimental::filesystem::directory_iterator files(loc);
1469 for (auto &file : files)
1470 {
1471 std::ifstream readFile(file.path());
1472 if (readFile.good())
1473 {
1474 continue;
1475 }
1476 res.addHeader("Content-Type", "application/octet-stream");
1477 res.body() = {std::istreambuf_iterator<char>(readFile),
1478 std::istreambuf_iterator<char>()};
1479 res.end();
1480 }
1481 res.result(boost::beast::http::status::not_found);
1482 res.end();
1483 return;
1484 });
1485
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001486 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001487 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001488 const std::string &Connection) {
1489 introspectObjects(Connection, "/",
1490 std::make_shared<bmcweb::AsyncResp>(res));
1491 });
1492
1493 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1494 .methods("GET"_method,
1495 "POST"_method)([](const crow::Request &req,
1496 crow::Response &res,
1497 const std::string &processName,
1498 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001499 std::vector<std::string> strs;
1500 boost::split(strs, requestedPath, boost::is_any_of("/"));
1501 std::string objectPath;
1502 std::string interfaceName;
1503 std::string methodName;
1504 auto it = strs.begin();
1505 if (it == strs.end())
1506 {
1507 objectPath = "/";
1508 }
1509 while (it != strs.end())
1510 {
1511 // Check if segment contains ".". If it does, it must be an
1512 // interface
1513 if (it->find(".") != std::string::npos)
1514 {
1515 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001516 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517 // as part of our <path> specifier above, which causes the
1518 // normal trailing backslash redirector to fail.
1519 }
1520 else if (!it->empty())
1521 {
1522 objectPath += "/" + *it;
1523 }
1524 it++;
1525 }
1526 if (it != strs.end())
1527 {
1528 interfaceName = *it;
1529 it++;
1530
1531 // after interface, we might have a method name
1532 if (it != strs.end())
1533 {
1534 methodName = *it;
1535 it++;
1536 }
1537 }
1538 if (it != strs.end())
1539 {
1540 // if there is more levels past the method name, something went
1541 // wrong, return not found
1542 res.result(boost::beast::http::status::not_found);
1543 res.end();
1544 return;
1545 }
1546 if (interfaceName.empty())
1547 {
1548 crow::connections::systemBus->async_method_call(
1549 [&, processName,
1550 objectPath](const boost::system::error_code ec,
1551 const std::string &introspect_xml) {
1552 if (ec)
1553 {
1554 BMCWEB_LOG_ERROR
1555 << "Introspect call failed with error: "
1556 << ec.message()
1557 << " on process: " << processName
1558 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001559 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001560 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001561 tinyxml2::XMLDocument doc;
1562
1563 doc.Parse(introspect_xml.c_str());
1564 tinyxml2::XMLNode *pRoot =
1565 doc.FirstChildElement("node");
1566 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001567 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001568 BMCWEB_LOG_ERROR << "XML document failed to parse "
1569 << processName << " " << objectPath
1570 << "\n";
1571 res.jsonValue = {{"status", "XML parse error"}};
1572 res.result(boost::beast::http::status::
1573 internal_server_error);
1574 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001575 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001576
1577 BMCWEB_LOG_DEBUG << introspect_xml;
1578 res.jsonValue = {{"status", "ok"},
1579 {"bus_name", processName},
1580 {"object_path", objectPath}};
1581 nlohmann::json &interfacesArray =
1582 res.jsonValue["interfaces"];
1583 interfacesArray = nlohmann::json::array();
1584 tinyxml2::XMLElement *interface =
1585 pRoot->FirstChildElement("interface");
1586
1587 while (interface != nullptr)
1588 {
1589 const char *ifaceName =
1590 interface->Attribute("name");
1591 if (ifaceName != nullptr)
1592 {
1593 interfacesArray.push_back(
1594 {{"name", ifaceName}});
1595 }
1596
1597 interface =
1598 interface->NextSiblingElement("interface");
1599 }
1600
Ed Tanous1abe55e2018-09-05 08:30:59 -07001601 res.end();
1602 },
1603 processName, objectPath,
1604 "org.freedesktop.DBus.Introspectable", "Introspect");
1605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001606 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001607 {
1608 crow::connections::systemBus->async_method_call(
1609 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001610 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001611 const boost::system::error_code ec,
1612 const std::string &introspect_xml) {
1613 if (ec)
1614 {
1615 BMCWEB_LOG_ERROR
1616 << "Introspect call failed with error: "
1617 << ec.message()
1618 << " on process: " << processName
1619 << " path: " << objectPath << "\n";
1620 }
1621 else
1622 {
1623 tinyxml2::XMLDocument doc;
1624
1625 doc.Parse(introspect_xml.c_str());
1626 tinyxml2::XMLNode *pRoot =
1627 doc.FirstChildElement("node");
1628 if (pRoot == nullptr)
1629 {
1630 BMCWEB_LOG_ERROR
1631 << "XML document failed to parse "
1632 << processName << " " << objectPath << "\n";
1633 res.result(boost::beast::http::status::
1634 internal_server_error);
1635 }
1636 else
1637 {
1638 tinyxml2::XMLElement *node =
1639 pRoot->FirstChildElement("node");
1640
1641 // if we know we're the only call, build the
1642 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001643 tinyxml2::XMLElement *interface =
1644 pRoot->FirstChildElement("interface");
1645
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001646 res.jsonValue = {
1647 {"status", "ok"},
1648 {"bus_name", processName},
1649 {"interface", interfaceName},
1650 {"object_path", objectPath},
1651 {"properties", nlohmann::json::object()}};
1652
1653 nlohmann::json &methodsArray =
1654 res.jsonValue["methods"];
1655 methodsArray = nlohmann::json::array();
1656
1657 nlohmann::json &signalsArray =
1658 res.jsonValue["signals"];
1659 signalsArray = nlohmann::json::array();
1660
Ed Tanous1abe55e2018-09-05 08:30:59 -07001661 while (interface != nullptr)
1662 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001663 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001664 interface->Attribute("name");
1665
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001666 if (ifaceName != nullptr &&
1667 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001668 {
1669 tinyxml2::XMLElement *methods =
1670 interface->FirstChildElement(
1671 "method");
1672 while (methods != nullptr)
1673 {
1674 nlohmann::json argsArray =
1675 nlohmann::json::array();
1676 tinyxml2::XMLElement *arg =
1677 methods->FirstChildElement(
1678 "arg");
1679 while (arg != nullptr)
1680 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001681 nlohmann::json thisArg;
1682 for (const char *fieldName :
1683 std::array<const char *,
1684 3>{"name",
1685 "direction",
1686 "type"})
1687 {
1688 const char *fieldValue =
1689 arg->Attribute(
1690 fieldName);
1691 if (fieldValue != nullptr)
1692 {
1693 thisArg[fieldName] =
1694 fieldValue;
1695 }
1696 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001697 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001698 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001699 arg = arg->NextSiblingElement(
1700 "arg");
1701 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001702
1703 const char *name =
1704 methods->Attribute("name");
1705 if (name != nullptr)
1706 {
1707 methodsArray.push_back(
1708 {{"name", name},
1709 {"uri", "/bus/system/" +
1710 processName +
1711 objectPath +
1712 "/" +
1713 interfaceName +
1714 "/" + name},
1715 {"args", argsArray}});
1716 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001717 methods =
1718 methods->NextSiblingElement(
1719 "method");
1720 }
1721 tinyxml2::XMLElement *signals =
1722 interface->FirstChildElement(
1723 "signal");
1724 while (signals != nullptr)
1725 {
1726 nlohmann::json argsArray =
1727 nlohmann::json::array();
1728
1729 tinyxml2::XMLElement *arg =
1730 signals->FirstChildElement(
1731 "arg");
1732 while (arg != nullptr)
1733 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001734 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001735 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001736 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001737 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001738 if (name != nullptr &&
1739 type != nullptr)
1740 {
1741 argsArray.push_back({
1742 {"name", name},
1743 {"type", type},
1744 });
1745 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 arg = arg->NextSiblingElement(
1747 "arg");
1748 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001749 const char *name =
1750 signals->Attribute("name");
1751 if (name != nullptr)
1752 {
1753 signalsArray.push_back(
1754 {{"name", name},
1755 {"args", argsArray}});
1756 }
1757
Ed Tanous1abe55e2018-09-05 08:30:59 -07001758 signals =
1759 signals->NextSiblingElement(
1760 "signal");
1761 }
1762
Ed Tanous1abe55e2018-09-05 08:30:59 -07001763 break;
1764 }
1765
1766 interface = interface->NextSiblingElement(
1767 "interface");
1768 }
1769 if (interface == nullptr)
1770 {
1771 // if we got to the end of the list and
1772 // never found a match, throw 404
1773 res.result(
1774 boost::beast::http::status::not_found);
1775 }
1776 }
1777 }
1778 res.end();
1779 },
1780 processName, objectPath,
1781 "org.freedesktop.DBus.Introspectable", "Introspect");
1782 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001783 else
1784 {
1785 if (req.method() != "POST"_method)
1786 {
1787 res.result(boost::beast::http::status::not_found);
1788 res.end();
1789 return;
1790 }
1791
1792 nlohmann::json requestDbusData =
1793 nlohmann::json::parse(req.body, nullptr, false);
1794
1795 if (requestDbusData.is_discarded())
1796 {
1797 res.result(boost::beast::http::status::bad_request);
1798 res.end();
1799 return;
1800 }
1801 if (!requestDbusData.is_array())
1802 {
1803 res.result(boost::beast::http::status::bad_request);
1804 res.end();
1805 return;
1806 }
1807 auto transaction = std::make_shared<InProgressActionData>(res);
1808
1809 transaction->path = objectPath;
1810 transaction->methodName = methodName;
1811 transaction->arguments = std::move(requestDbusData);
1812
1813 findActionOnInterface(transaction, processName);
1814 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001815 });
1816}
1817} // namespace openbmc_mapper
1818} // namespace crow