blob: aa6c95f8c7f045b30957a34fa2cc228e92067d7a [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>
Ed Tanous911ac312017-08-15 09:37:42 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace crow
28{
29namespace openbmc_mapper
30{
Ed Tanousba9f9a62017-10-11 16:40:35 -070031
Ed Tanouse3cb5a32018-08-08 14:16:49 -070032void introspectObjects(const std::string &processName,
33 const std::string &objectPath,
34 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070035{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070036 if (transaction->res.jsonValue.is_null())
37 {
38 transaction->res.jsonValue = {{"status", "ok"},
39 {"bus_name", processName},
40 {"objects", nlohmann::json::array()}};
41 }
42
Ed Tanous1abe55e2018-09-05 08:30:59 -070043 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070044 [transaction, processName{std::string(processName)},
45 objectPath{std::string(objectPath)}](
46 const boost::system::error_code ec,
47 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070048 if (ec)
49 {
50 BMCWEB_LOG_ERROR
51 << "Introspect call failed with error: " << ec.message()
52 << " on process: " << processName << " path: " << objectPath
53 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070054 return;
55 }
56 transaction->res.jsonValue["objects"].push_back(
57 {{"path", objectPath}});
58
59 tinyxml2::XMLDocument doc;
60
61 doc.Parse(introspect_xml.c_str());
62 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
63 if (pRoot == nullptr)
64 {
65 BMCWEB_LOG_ERROR << "XML document failed to parse "
66 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070067 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 else
69 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070070 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
71 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070073 const char *childPath = node->Attribute("name");
74 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070076 std::string newpath;
77 if (objectPath != "/")
78 {
79 newpath += objectPath;
80 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070081 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -070083 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -070084 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070085
86 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 }
88 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070089 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -070090 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -070091 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070092}
Ed Tanous64530012018-02-06 17:08:16 -080093
Ed Tanous1abe55e2018-09-05 08:30:59 -070094void getManagedObjectsForEnumerate(const std::string &object_name,
Ed Tanouse3cb5a32018-08-08 14:16:49 -070095 const std::string &object_manager_path,
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 const std::string &connection_name,
97 crow::Response &res,
98 std::shared_ptr<nlohmann::json> transaction)
99{
100 crow::connections::systemBus->async_method_call(
James Feist5b4aa862018-08-16 14:07:01 -0700101 [&res, transaction](const boost::system::error_code ec,
102 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 if (ec)
104 {
105 BMCWEB_LOG_ERROR << ec;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700106 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 else
108 {
109 nlohmann::json &dataJson = *transaction;
Ed Tanous64530012018-02-06 17:08:16 -0800110
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 for (auto &objectPath : objects)
112 {
113 BMCWEB_LOG_DEBUG
114 << "Reading object "
115 << static_cast<const std::string &>(objectPath.first);
116 nlohmann::json &objectJson =
117 dataJson[static_cast<const std::string &>(
118 objectPath.first)];
119 if (objectJson.is_null())
120 {
121 objectJson = nlohmann::json::object();
122 }
123 for (const auto &interface : objectPath.second)
124 {
125 for (const auto &property : interface.second)
126 {
127 nlohmann::json &propertyJson =
128 objectJson[property.first];
129 mapbox::util::apply_visitor(
130 [&propertyJson](auto &&val) {
131 propertyJson = val;
132 },
133 property.second);
134 }
135 }
136 }
137 }
138
139 if (transaction.use_count() == 1)
140 {
141 res.jsonValue = {{"message", "200 OK"},
142 {"status", "ok"},
143 {"data", std::move(*transaction)}};
144 res.end();
145 }
146 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700147 connection_name, object_manager_path,
148 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
149}
150
151void findObjectManagerPathForEnumerate(
152 const std::string &object_name, const std::string &connection_name,
153 crow::Response &res, std::shared_ptr<nlohmann::json> transaction)
154{
155 crow::connections::systemBus->async_method_call(
156 [&res, transaction, object_name{std::string(object_name)},
157 connection_name{std::string(connection_name)}](
158 const boost::system::error_code ec,
159 const boost::container::flat_map<
160 std::string, boost::container::flat_map<
161 std::string, std::vector<std::string>>>
162 &objects) {
163 if (ec)
164 {
165 BMCWEB_LOG_ERROR << ec;
166 return;
167 }
168
Ed Tanousf254ba72018-10-12 13:40:35 -0700169 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700170 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700171 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700172 {
173 if (connectionGroup.first == connection_name)
174 {
175 // Found the object manager path for this resource.
176 getManagedObjectsForEnumerate(
177 object_name, pathGroup.first, connection_name, res,
178 transaction);
179 return;
180 }
181 }
182 }
183 },
184 "xyz.openbmc_project.ObjectMapper",
185 "/xyz/openbmc_project/object_mapper",
186 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
187 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700188}
Ed Tanous64530012018-02-06 17:08:16 -0800189
190using GetSubTreeType = std::vector<
191 std::pair<std::string,
192 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
193
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700194// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195struct InProgressActionData
196{
197 InProgressActionData(crow::Response &res) : res(res){};
198 ~InProgressActionData()
199 {
200 if (res.result() == boost::beast::http::status::internal_server_error)
201 {
202 // Reset the json object to clear out any data that made it in
203 // before the error happened todo(ed) handle error condition with
204 // proper code
205 res.jsonValue = nlohmann::json::object();
206 }
207 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700208 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700209
Ed Tanous1abe55e2018-09-05 08:30:59 -0700210 void setErrorStatus()
211 {
212 res.result(boost::beast::http::status::internal_server_error);
213 }
214 crow::Response &res;
215 std::string path;
216 std::string methodName;
217 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700218};
219
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220std::vector<std::string> dbusArgSplit(const std::string &string)
221{
222 std::vector<std::string> ret;
223 if (string.empty())
224 {
225 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700226 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227 ret.push_back("");
228 int containerDepth = 0;
229
230 for (std::string::const_iterator character = string.begin();
231 character != string.end(); character++)
232 {
233 ret.back() += *character;
234 switch (*character)
235 {
236 case ('a'):
237 break;
238 case ('('):
239 case ('{'):
240 containerDepth++;
241 break;
242 case ('}'):
243 case (')'):
244 containerDepth--;
245 if (containerDepth == 0)
246 {
247 if (character + 1 != string.end())
248 {
249 ret.push_back("");
250 }
251 }
252 break;
253 default:
254 if (containerDepth == 0)
255 {
256 if (character + 1 != string.end())
257 {
258 ret.push_back("");
259 }
260 }
261 break;
262 }
263 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700264}
265
Ed Tanousd76323e2018-08-07 14:35:40 -0700266int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 const nlohmann::json &input_json)
268{
269 int r = 0;
270 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
271 << " to type: " << arg_type;
272 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700273
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 // Assume a single object for now.
275 const nlohmann::json *j = &input_json;
276 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700277
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700278 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 {
280 // If we are decoding multiple objects, grab the pointer to the
281 // iterator, and increment it for the next loop
282 if (argTypes.size() > 1)
283 {
284 if (jIt == input_json.end())
285 {
286 return -2;
287 }
288 j = &*jIt;
289 jIt++;
290 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700291 const int64_t *intValue = j->get_ptr<const int64_t *>();
292 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
293 const std::string *stringValue = j->get_ptr<const std::string *>();
294 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700295 const bool *b = j->get_ptr<const bool *>();
296 int64_t v = 0;
297 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700298
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 // Do some basic type conversions that make sense. uint can be
300 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700301 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700303 v = static_cast<int64_t>(*uintValue);
304 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700306 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700308 d = static_cast<double>(*uintValue);
309 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700311 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700312 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700313 d = static_cast<double>(*intValue);
314 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700315 }
316
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700317 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700319 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700320 {
321 return -1;
322 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700323 r = sd_bus_message_append_basic(m, argCode[0],
324 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 if (r < 0)
326 {
327 return r;
328 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700329 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700330 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700331 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700332 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700333 {
334 return -1;
335 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700336 int32_t i = static_cast<int32_t>(*intValue);
337 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 if (r < 0)
339 {
340 return r;
341 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700342 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700343 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700344 {
345 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700346 int boolInt = false;
347 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700348 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700349 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350 }
351 else if (b != nullptr)
352 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700353 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700355 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700357 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700358 }
359 else
360 {
361 return -1;
362 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700363 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700364 if (r < 0)
365 {
366 return r;
367 }
368 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700369 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700371 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 {
373 return -1;
374 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700375 int16_t n = static_cast<int16_t>(*intValue);
376 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700377 if (r < 0)
378 {
379 return r;
380 }
381 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700382 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700383 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700384 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700385 {
386 return -1;
387 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700388 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389 if (r < 0)
390 {
391 return r;
392 }
393 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700394 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700395 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700396 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397 {
398 return -1;
399 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700400 uint8_t y = static_cast<uint8_t>(*uintValue);
401 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700403 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700405 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406 {
407 return -1;
408 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700409 uint16_t q = static_cast<uint16_t>(*uintValue);
410 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700412 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700414 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700415 {
416 return -1;
417 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700418 uint32_t u = static_cast<uint32_t>(*uintValue);
419 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700421 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700423 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424 {
425 return -1;
426 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700427 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700429 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700430 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700431 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700432 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700433 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700435 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700436 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700437 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 if (r < 0)
439 {
440 return r;
441 }
442
443 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
444 ++it)
445 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700446 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 if (r < 0)
448 {
449 return r;
450 }
451
452 it++;
453 }
454 sd_bus_message_close_container(m);
455 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700456 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700458 std::string containedType = argCode.substr(1);
459 BMCWEB_LOG_DEBUG << "variant type: " << argCode
460 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700461 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700462 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463 if (r < 0)
464 {
465 return r;
466 }
467
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700468 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469 if (r < 0)
470 {
471 return r;
472 }
473
474 r = sd_bus_message_close_container(m);
475 if (r < 0)
476 {
477 return r;
478 }
479 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700480 else if (boost::starts_with(argCode, "(") &&
481 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700482 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700483 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700484 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700485 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700486 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700487 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700488 {
489 if (it == j->end())
490 {
491 return -1;
492 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700493 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 if (r < 0)
495 {
496 return r;
497 }
498 it++;
499 }
500 r = sd_bus_message_close_container(m);
501 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700502 else if (boost::starts_with(argCode, "{") &&
503 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700504 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700505 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700507 containedType.c_str());
508 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 if (codes.size() != 2)
510 {
511 return -1;
512 }
513 const std::string &key_type = codes[0];
514 const std::string &value_type = codes[1];
515 for (auto it : j->items())
516 {
517 r = convertJsonToDbus(m, key_type, it.key());
518 if (r < 0)
519 {
520 return r;
521 }
522
523 r = convertJsonToDbus(m, value_type, it.value());
524 if (r < 0)
525 {
526 return r;
527 }
528 }
529 r = sd_bus_message_close_container(m);
530 }
531 else
532 {
533 return -2;
534 }
535 if (r < 0)
536 {
537 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700538 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700539
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540 if (argTypes.size() > 1)
541 {
542 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700543 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700544 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700545}
546
Ed Tanousd76323e2018-08-07 14:35:40 -0700547void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700548 const std::string &connectionName)
549{
550 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
551 << connectionName;
552 crow::connections::systemBus->async_method_call(
553 [transaction, connectionName{std::string(connectionName)}](
554 const boost::system::error_code ec,
555 const std::string &introspect_xml) {
556 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
557 if (ec)
558 {
559 BMCWEB_LOG_ERROR
560 << "Introspect call failed with error: " << ec.message()
561 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700562 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 else
564 {
565 tinyxml2::XMLDocument doc;
566
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700567 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
569 if (pRoot == nullptr)
570 {
571 BMCWEB_LOG_ERROR << "XML document failed to parse "
572 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700573 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700575 tinyxml2::XMLElement *interfaceNode =
576 pRoot->FirstChildElement("interface");
577 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700579 const char *thisInterfaceName =
580 interfaceNode->Attribute("name");
581 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700583 tinyxml2::XMLElement *methodNode =
584 interfaceNode->FirstChildElement("method");
585 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700587 const char *thisMethodName =
588 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700590 << thisMethodName;
591 if (thisMethodName != nullptr &&
592 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700594 BMCWEB_LOG_DEBUG
595 << "Found method named " << thisMethodName
596 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 sdbusplus::message::message m =
598 crow::connections::systemBus
599 ->new_method_call(
600 connectionName.c_str(),
601 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700602 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 transaction->methodName.c_str());
604
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700605 tinyxml2::XMLElement *argumentNode =
606 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700608 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 transaction->arguments.begin();
610
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700611 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700613 const char *argDirection =
614 argumentNode->Attribute("direction");
615 const char *argType =
616 argumentNode->Attribute("type");
617 if (argDirection != nullptr &&
618 argType != nullptr &&
619 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700621
622 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 transaction->arguments.end())
624 {
625 transaction->setErrorStatus();
626 return;
627 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700628 if (convertJsonToDbus(
629 m.get(), std::string(argType),
630 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 {
632 transaction->setErrorStatus();
633 return;
634 }
635
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700636 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700638 argumentNode =
639 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700641
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 crow::connections::systemBus->async_send(
643 m, [transaction](
644 boost::system::error_code ec,
645 sdbusplus::message::message &m) {
646 if (ec)
647 {
648 transaction->setErrorStatus();
649 return;
650 }
651 transaction->res.jsonValue = {
652 {"status", "ok"},
653 {"message", "200 OK"},
654 {"data", nullptr}};
655 });
656 break;
657 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700658 methodNode =
659 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700662 interfaceNode =
663 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 }
665 }
666 },
667 connectionName, transaction->path,
668 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700669}
670
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700671void handleAction(const crow::Request &req, crow::Response &res,
672 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700674 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
675 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 nlohmann::json requestDbusData =
677 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700678
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 if (requestDbusData.is_discarded())
680 {
681 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700682 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 return;
684 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700685 nlohmann::json::iterator data = requestDbusData.find("data");
686 if (data == requestDbusData.end())
687 {
688 res.result(boost::beast::http::status::bad_request);
689 res.end();
690 return;
691 }
692
693 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 {
695 res.result(boost::beast::http::status::bad_request);
696 res.end();
697 return;
698 }
699 auto transaction = std::make_shared<InProgressActionData>(res);
700
701 transaction->path = objectPath;
702 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700703 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700704 crow::connections::systemBus->async_method_call(
705 [transaction](
706 const boost::system::error_code ec,
707 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700708 &interfaceNames) {
709 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700711 BMCWEB_LOG_ERROR << "Can't find object";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700712 transaction->setErrorStatus();
713 return;
714 }
715
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700716 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
717 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718
719 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700720 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 {
722 findActionOnInterface(transaction, object.first);
723 }
724 },
725 "xyz.openbmc_project.ObjectMapper",
726 "/xyz/openbmc_project/object_mapper",
727 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
728 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700729}
730
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700731void handleList(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732{
733 crow::connections::systemBus->async_method_call(
734 [&res](const boost::system::error_code ec,
735 std::vector<std::string> &objectPaths) {
736 if (ec)
737 {
738 res.result(boost::beast::http::status::internal_server_error);
739 }
740 else
741 {
742 res.jsonValue = {{"status", "ok"},
743 {"message", "200 OK"},
744 {"data", std::move(objectPaths)}};
745 }
746 res.end();
747 },
748 "xyz.openbmc_project.ObjectMapper",
749 "/xyz/openbmc_project/object_mapper",
750 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700751 static_cast<int32_t>(0), std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700753
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700754void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755{
756 crow::connections::systemBus->async_method_call(
757 [&res, objectPath{std::string(objectPath)}](
758 const boost::system::error_code ec,
759 const GetSubTreeType &object_names) {
760 if (ec)
761 {
762 res.jsonValue = {{"message", "200 OK"},
763 {"status", "ok"},
764 {"data", nlohmann::json::object()}};
Ed Tanous64530012018-02-06 17:08:16 -0800765
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 res.end();
767 return;
768 }
Ed Tanous64530012018-02-06 17:08:16 -0800769
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 boost::container::flat_set<std::string> connections;
Ed Tanous64530012018-02-06 17:08:16 -0800771
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 for (const auto &object : object_names)
773 {
774 for (const auto &Connection : object.second)
775 {
776 connections.insert(Connection.first);
777 }
778 }
779
780 if (connections.size() <= 0)
781 {
782 res.result(boost::beast::http::status::not_found);
783 res.end();
784 return;
785 }
786 auto transaction =
787 std::make_shared<nlohmann::json>(nlohmann::json::object());
788 for (const std::string &Connection : connections)
789 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700790 findObjectManagerPathForEnumerate(objectPath, Connection, res,
791 transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 }
793 },
794 "xyz.openbmc_project.ObjectMapper",
795 "/xyz/openbmc_project/object_mapper",
796 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
797 (int32_t)0, std::array<std::string, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800798}
Ed Tanous911ac312017-08-15 09:37:42 -0700799
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700800void handleGet(crow::Response &res, std::string &objectPath,
801 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700803 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
804 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -0700806
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 std::shared_ptr<std::string> path =
808 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -0700809
Ed Tanous1abe55e2018-09-05 08:30:59 -0700810 using GetObjectType =
811 std::vector<std::pair<std::string, std::vector<std::string>>>;
812 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700813 [&res, path, propertyName](const boost::system::error_code ec,
814 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 if (ec || object_names.size() <= 0)
816 {
817 res.result(boost::beast::http::status::not_found);
818 res.end();
819 return;
820 }
821 std::shared_ptr<nlohmann::json> response =
822 std::make_shared<nlohmann::json>(nlohmann::json::object());
823 // The mapper should never give us an empty interface names list,
824 // but check anyway
825 for (const std::pair<std::string, std::vector<std::string>>
826 connection : object_names)
827 {
828 const std::vector<std::string> &interfaceNames =
829 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700830
Ed Tanous1abe55e2018-09-05 08:30:59 -0700831 if (interfaceNames.size() <= 0)
832 {
833 res.result(boost::beast::http::status::not_found);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700834 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 return;
836 }
837
838 for (const std::string &interface : interfaceNames)
839 {
840 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700841 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700842 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -0700843 const std::vector<std::pair<
844 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700845 &properties) {
846 if (ec)
847 {
848 BMCWEB_LOG_ERROR << "Bad dbus request error: "
849 << ec;
850 }
851 else
852 {
James Feist5b4aa862018-08-16 14:07:01 -0700853 for (const std::pair<
854 std::string,
855 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700856 &property : properties)
857 {
858 // if property name is empty, or matches our
859 // search query, add it to the response json
860
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700861 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 {
863 mapbox::util::apply_visitor(
864 [&response, &property](auto &&val) {
865 (*response)[property.first] =
866 val;
867 },
868 property.second);
869 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700870 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871 {
872 mapbox::util::apply_visitor(
873 [&response](auto &&val) {
874 (*response) = val;
875 },
876 property.second);
877 }
878 }
879 }
880 if (response.use_count() == 1)
881 {
882 res.jsonValue = {{"status", "ok"},
883 {"message", "200 OK"},
884 {"data", *response}};
885
886 res.end();
887 }
888 },
889 connection.first, *path,
890 "org.freedesktop.DBus.Properties", "GetAll", interface);
891 }
892 }
893 },
894 "xyz.openbmc_project.ObjectMapper",
895 "/xyz/openbmc_project/object_mapper",
896 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
897 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700898}
899
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900struct AsyncPutRequest
901{
902 AsyncPutRequest(crow::Response &res) : res(res)
903 {
904 res.jsonValue = {
905 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
906 }
907 ~AsyncPutRequest()
908 {
909 if (res.result() == boost::beast::http::status::internal_server_error)
910 {
911 // Reset the json object to clear out any data that made it in
912 // before the error happened todo(ed) handle error condition with
913 // proper code
914 res.jsonValue = nlohmann::json::object();
915 }
916
917 if (res.jsonValue.empty())
918 {
919 res.result(boost::beast::http::status::forbidden);
920 res.jsonValue = {
921 {"status", "error"},
922 {"message", "403 Forbidden"},
923 {"data",
924 {{"message", "The specified property cannot be created: " +
925 propertyName}}}};
926 }
927
928 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700929 }
930
Ed Tanous1abe55e2018-09-05 08:30:59 -0700931 void setErrorStatus()
932 {
933 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700934 }
935
Ed Tanous1abe55e2018-09-05 08:30:59 -0700936 crow::Response &res;
937 std::string objectPath;
938 std::string propertyName;
939 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700940};
941
Ed Tanousd76323e2018-08-07 14:35:40 -0700942void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700943 const std::string &objectPath, const std::string &destProperty)
944{
945 nlohmann::json requestDbusData =
946 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700947
Ed Tanous1abe55e2018-09-05 08:30:59 -0700948 if (requestDbusData.is_discarded())
949 {
950 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700951 res.end();
952 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700953 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700954
Ed Tanous1abe55e2018-09-05 08:30:59 -0700955 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
956 if (propertyIt == requestDbusData.end())
957 {
958 res.result(boost::beast::http::status::bad_request);
959 res.end();
960 return;
961 }
962 const nlohmann::json &propertySetValue = *propertyIt;
963 auto transaction = std::make_shared<AsyncPutRequest>(res);
964 transaction->objectPath = objectPath;
965 transaction->propertyName = destProperty;
966 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -0700967
Ed Tanous1abe55e2018-09-05 08:30:59 -0700968 using GetObjectType =
969 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -0700970
Ed Tanous1abe55e2018-09-05 08:30:59 -0700971 crow::connections::systemBus->async_method_call(
972 [transaction](const boost::system::error_code ec,
973 const GetObjectType &object_names) {
974 if (!ec && object_names.size() <= 0)
975 {
976 transaction->res.result(boost::beast::http::status::not_found);
977 return;
978 }
Ed Tanous911ac312017-08-15 09:37:42 -0700979
Ed Tanous1abe55e2018-09-05 08:30:59 -0700980 for (const std::pair<std::string, std::vector<std::string>>
981 connection : object_names)
982 {
983 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -0700984
Ed Tanous1abe55e2018-09-05 08:30:59 -0700985 crow::connections::systemBus->async_method_call(
986 [connectionName{std::string(connectionName)},
987 transaction](const boost::system::error_code ec,
988 const std::string &introspectXml) {
989 if (ec)
990 {
991 BMCWEB_LOG_ERROR
992 << "Introspect call failed with error: "
993 << ec.message()
994 << " on process: " << connectionName;
995 transaction->setErrorStatus();
996 return;
Ed Tanous911ac312017-08-15 09:37:42 -0700997 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -0700999
Ed Tanous1abe55e2018-09-05 08:30:59 -07001000 doc.Parse(introspectXml.c_str());
1001 tinyxml2::XMLNode *pRoot =
1002 doc.FirstChildElement("node");
1003 if (pRoot == nullptr)
1004 {
1005 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1006 << introspectXml;
1007 transaction->setErrorStatus();
1008 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001009 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001010 tinyxml2::XMLElement *ifaceNode =
1011 pRoot->FirstChildElement("interface");
1012 while (ifaceNode != nullptr)
1013 {
1014 const char *interfaceName =
1015 ifaceNode->Attribute("name");
1016 BMCWEB_LOG_DEBUG << "found interface "
1017 << interfaceName;
1018 tinyxml2::XMLElement *propNode =
1019 ifaceNode->FirstChildElement("property");
1020 while (propNode != nullptr)
1021 {
1022 const char *propertyName =
1023 propNode->Attribute("name");
1024 BMCWEB_LOG_DEBUG << "Found property "
1025 << propertyName;
1026 if (propertyName == transaction->propertyName)
1027 {
1028 const char *argType =
1029 propNode->Attribute("type");
1030 if (argType != nullptr)
1031 {
1032 sdbusplus::message::message m =
1033 crow::connections::systemBus
1034 ->new_method_call(
1035 connectionName.c_str(),
1036 transaction->objectPath
1037 .c_str(),
1038 "org.freedesktop.DBus."
1039 "Properties",
1040 "Set");
1041 m.append(interfaceName,
1042 transaction->propertyName);
1043 int r = sd_bus_message_open_container(
1044 m.get(), SD_BUS_TYPE_VARIANT,
1045 argType);
1046 if (r < 0)
1047 {
1048 transaction->setErrorStatus();
1049 return;
1050 }
1051 r = convertJsonToDbus(
1052 m.get(), argType,
1053 transaction->propertyValue);
1054 if (r < 0)
1055 {
1056 transaction->setErrorStatus();
1057 return;
1058 }
1059 r = sd_bus_message_close_container(
1060 m.get());
1061 if (r < 0)
1062 {
1063 transaction->setErrorStatus();
1064 return;
1065 }
Ed Tanous911ac312017-08-15 09:37:42 -07001066
Ed Tanous1abe55e2018-09-05 08:30:59 -07001067 crow::connections::systemBus
1068 ->async_send(
1069 m,
1070 [transaction](
1071 boost::system::error_code
1072 ec,
1073 sdbusplus::message::message
1074 &m) {
1075 BMCWEB_LOG_DEBUG << "sent";
1076 if (ec)
1077 {
1078 transaction->res
1079 .jsonValue
1080 ["status"] =
1081 "error";
1082 transaction->res
1083 .jsonValue
1084 ["message"] =
1085 ec.message();
1086 }
1087 });
1088 }
1089 }
1090 propNode =
1091 propNode->NextSiblingElement("property");
1092 }
1093 ifaceNode =
1094 ifaceNode->NextSiblingElement("interface");
1095 }
1096 },
1097 connectionName, transaction->objectPath,
1098 "org.freedesktop.DBus.Introspectable", "Introspect");
1099 }
1100 },
1101 "xyz.openbmc_project.ObjectMapper",
1102 "/xyz/openbmc_project/object_mapper",
1103 "xyz.openbmc_project.ObjectMapper", "GetObject",
1104 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001105}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001106
1107template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1108{
1109 BMCWEB_ROUTE(app, "/bus/")
1110 .methods("GET"_method)(
1111 [](const crow::Request &req, crow::Response &res) {
1112 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1113 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001114 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001115 });
1116
1117 BMCWEB_ROUTE(app, "/bus/system/")
1118 .methods("GET"_method)(
1119 [](const crow::Request &req, crow::Response &res) {
1120 auto myCallback = [&res](const boost::system::error_code ec,
1121 std::vector<std::string> &names) {
1122 if (ec)
1123 {
1124 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1125 res.result(
1126 boost::beast::http::status::internal_server_error);
1127 }
1128 else
1129 {
1130 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001131 res.jsonValue = {{"status", "ok"}};
1132 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001133 for (auto &name : names)
1134 {
1135 objectsSub.push_back({{"name", name}});
1136 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001137 }
1138 res.end();
1139 };
1140 crow::connections::systemBus->async_method_call(
1141 std::move(myCallback), "org.freedesktop.DBus", "/",
1142 "org.freedesktop.DBus", "ListNames");
1143 });
1144
1145 BMCWEB_ROUTE(app, "/list/")
1146 .methods("GET"_method)(
1147 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001148 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001149 });
1150
1151 BMCWEB_ROUTE(app, "/xyz/<path>")
1152 .methods("GET"_method, "PUT"_method,
1153 "POST"_method)([](const crow::Request &req,
1154 crow::Response &res,
1155 const std::string &path) {
1156 std::string objectPath = "/xyz/" + path;
1157
1158 // Trim any trailing "/" at the end
1159 if (boost::ends_with(objectPath, "/"))
1160 {
1161 objectPath.pop_back();
1162 }
1163
1164 // If accessing a single attribute, fill in and update objectPath,
1165 // otherwise leave destProperty blank
1166 std::string destProperty = "";
1167 const char *attrSeperator = "/attr/";
1168 size_t attrPosition = path.find(attrSeperator);
1169 if (attrPosition != path.npos)
1170 {
1171 objectPath = "/xyz/" + path.substr(0, attrPosition);
1172 destProperty = path.substr(attrPosition + strlen(attrSeperator),
1173 path.length());
1174 }
1175
1176 if (req.method() == "POST"_method)
1177 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001178 constexpr const char *actionSeperator = "/action/";
1179 size_t actionPosition = path.find(actionSeperator);
1180 if (actionPosition != path.npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001181 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001182 objectPath = "/xyz/" + path.substr(0, actionPosition);
1183 std::string postProperty =
1184 path.substr((actionPosition + strlen(actionSeperator)),
1185 path.length());
1186 handleAction(req, res, objectPath, postProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001187 return;
1188 }
1189 }
1190 else if (req.method() == "GET"_method)
1191 {
1192 if (boost::ends_with(objectPath, "/enumerate"))
1193 {
1194 objectPath.erase(objectPath.end() - 10, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001195 handleEnumerate(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001196 }
1197 else if (boost::ends_with(objectPath, "/list"))
1198 {
1199 objectPath.erase(objectPath.end() - 5, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001200 handleList(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001201 }
1202 else
1203 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001204 handleGet(res, objectPath, destProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001205 }
1206 return;
1207 }
1208 else if (req.method() == "PUT"_method)
1209 {
1210 handlePut(req, res, objectPath, destProperty);
1211 return;
1212 }
1213
1214 res.result(boost::beast::http::status::method_not_allowed);
1215 res.end();
1216 });
1217
Ed Tanous1abe55e2018-09-05 08:30:59 -07001218 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1219 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1220 const std::string &dumpId) {
1221 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1222 if (!std::regex_match(dumpId, validFilename))
1223 {
1224 res.result(boost::beast::http::status::not_found);
1225 res.end();
1226 return;
1227 }
1228 std::experimental::filesystem::path loc(
1229 "/var/lib/phosphor-debug-collector/dumps");
1230
1231 loc += dumpId;
1232
1233 if (!std::experimental::filesystem::exists(loc) ||
1234 !std::experimental::filesystem::is_directory(loc))
1235 {
1236 res.result(boost::beast::http::status::not_found);
1237 res.end();
1238 return;
1239 }
1240 std::experimental::filesystem::directory_iterator files(loc);
1241 for (auto &file : files)
1242 {
1243 std::ifstream readFile(file.path());
1244 if (readFile.good())
1245 {
1246 continue;
1247 }
1248 res.addHeader("Content-Type", "application/octet-stream");
1249 res.body() = {std::istreambuf_iterator<char>(readFile),
1250 std::istreambuf_iterator<char>()};
1251 res.end();
1252 }
1253 res.result(boost::beast::http::status::not_found);
1254 res.end();
1255 return;
1256 });
1257
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001258 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001259 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001260 const std::string &Connection) {
1261 introspectObjects(Connection, "/",
1262 std::make_shared<bmcweb::AsyncResp>(res));
1263 });
1264
1265 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1266 .methods("GET"_method,
1267 "POST"_method)([](const crow::Request &req,
1268 crow::Response &res,
1269 const std::string &processName,
1270 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001271 std::vector<std::string> strs;
1272 boost::split(strs, requestedPath, boost::is_any_of("/"));
1273 std::string objectPath;
1274 std::string interfaceName;
1275 std::string methodName;
1276 auto it = strs.begin();
1277 if (it == strs.end())
1278 {
1279 objectPath = "/";
1280 }
1281 while (it != strs.end())
1282 {
1283 // Check if segment contains ".". If it does, it must be an
1284 // interface
1285 if (it->find(".") != std::string::npos)
1286 {
1287 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001288 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001289 // as part of our <path> specifier above, which causes the
1290 // normal trailing backslash redirector to fail.
1291 }
1292 else if (!it->empty())
1293 {
1294 objectPath += "/" + *it;
1295 }
1296 it++;
1297 }
1298 if (it != strs.end())
1299 {
1300 interfaceName = *it;
1301 it++;
1302
1303 // after interface, we might have a method name
1304 if (it != strs.end())
1305 {
1306 methodName = *it;
1307 it++;
1308 }
1309 }
1310 if (it != strs.end())
1311 {
1312 // if there is more levels past the method name, something went
1313 // wrong, return not found
1314 res.result(boost::beast::http::status::not_found);
1315 res.end();
1316 return;
1317 }
1318 if (interfaceName.empty())
1319 {
1320 crow::connections::systemBus->async_method_call(
1321 [&, processName,
1322 objectPath](const boost::system::error_code ec,
1323 const std::string &introspect_xml) {
1324 if (ec)
1325 {
1326 BMCWEB_LOG_ERROR
1327 << "Introspect call failed with error: "
1328 << ec.message()
1329 << " on process: " << processName
1330 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001331 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001332 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001333 tinyxml2::XMLDocument doc;
1334
1335 doc.Parse(introspect_xml.c_str());
1336 tinyxml2::XMLNode *pRoot =
1337 doc.FirstChildElement("node");
1338 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001339 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001340 BMCWEB_LOG_ERROR << "XML document failed to parse "
1341 << processName << " " << objectPath
1342 << "\n";
1343 res.jsonValue = {{"status", "XML parse error"}};
1344 res.result(boost::beast::http::status::
1345 internal_server_error);
1346 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001347 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001348
1349 BMCWEB_LOG_DEBUG << introspect_xml;
1350 res.jsonValue = {{"status", "ok"},
1351 {"bus_name", processName},
1352 {"object_path", objectPath}};
1353 nlohmann::json &interfacesArray =
1354 res.jsonValue["interfaces"];
1355 interfacesArray = nlohmann::json::array();
1356 tinyxml2::XMLElement *interface =
1357 pRoot->FirstChildElement("interface");
1358
1359 while (interface != nullptr)
1360 {
1361 const char *ifaceName =
1362 interface->Attribute("name");
1363 if (ifaceName != nullptr)
1364 {
1365 interfacesArray.push_back(
1366 {{"name", ifaceName}});
1367 }
1368
1369 interface =
1370 interface->NextSiblingElement("interface");
1371 }
1372
Ed Tanous1abe55e2018-09-05 08:30:59 -07001373 res.end();
1374 },
1375 processName, objectPath,
1376 "org.freedesktop.DBus.Introspectable", "Introspect");
1377 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001378 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001379 {
1380 crow::connections::systemBus->async_method_call(
1381 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001382 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001383 const boost::system::error_code ec,
1384 const std::string &introspect_xml) {
1385 if (ec)
1386 {
1387 BMCWEB_LOG_ERROR
1388 << "Introspect call failed with error: "
1389 << ec.message()
1390 << " on process: " << processName
1391 << " path: " << objectPath << "\n";
1392 }
1393 else
1394 {
1395 tinyxml2::XMLDocument doc;
1396
1397 doc.Parse(introspect_xml.c_str());
1398 tinyxml2::XMLNode *pRoot =
1399 doc.FirstChildElement("node");
1400 if (pRoot == nullptr)
1401 {
1402 BMCWEB_LOG_ERROR
1403 << "XML document failed to parse "
1404 << processName << " " << objectPath << "\n";
1405 res.result(boost::beast::http::status::
1406 internal_server_error);
1407 }
1408 else
1409 {
1410 tinyxml2::XMLElement *node =
1411 pRoot->FirstChildElement("node");
1412
1413 // if we know we're the only call, build the
1414 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001415 tinyxml2::XMLElement *interface =
1416 pRoot->FirstChildElement("interface");
1417
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001418 res.jsonValue = {
1419 {"status", "ok"},
1420 {"bus_name", processName},
1421 {"interface", interfaceName},
1422 {"object_path", objectPath},
1423 {"properties", nlohmann::json::object()}};
1424
1425 nlohmann::json &methodsArray =
1426 res.jsonValue["methods"];
1427 methodsArray = nlohmann::json::array();
1428
1429 nlohmann::json &signalsArray =
1430 res.jsonValue["signals"];
1431 signalsArray = nlohmann::json::array();
1432
Ed Tanous1abe55e2018-09-05 08:30:59 -07001433 while (interface != nullptr)
1434 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001435 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001436 interface->Attribute("name");
1437
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001438 if (ifaceName != nullptr &&
1439 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001440 {
1441 tinyxml2::XMLElement *methods =
1442 interface->FirstChildElement(
1443 "method");
1444 while (methods != nullptr)
1445 {
1446 nlohmann::json argsArray =
1447 nlohmann::json::array();
1448 tinyxml2::XMLElement *arg =
1449 methods->FirstChildElement(
1450 "arg");
1451 while (arg != nullptr)
1452 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001453 nlohmann::json thisArg;
1454 for (const char *fieldName :
1455 std::array<const char *,
1456 3>{"name",
1457 "direction",
1458 "type"})
1459 {
1460 const char *fieldValue =
1461 arg->Attribute(
1462 fieldName);
1463 if (fieldValue != nullptr)
1464 {
1465 thisArg[fieldName] =
1466 fieldValue;
1467 }
1468 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001469 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001470 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001471 arg = arg->NextSiblingElement(
1472 "arg");
1473 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001474
1475 const char *name =
1476 methods->Attribute("name");
1477 if (name != nullptr)
1478 {
1479 methodsArray.push_back(
1480 {{"name", name},
1481 {"uri", "/bus/system/" +
1482 processName +
1483 objectPath +
1484 "/" +
1485 interfaceName +
1486 "/" + name},
1487 {"args", argsArray}});
1488 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001489 methods =
1490 methods->NextSiblingElement(
1491 "method");
1492 }
1493 tinyxml2::XMLElement *signals =
1494 interface->FirstChildElement(
1495 "signal");
1496 while (signals != nullptr)
1497 {
1498 nlohmann::json argsArray =
1499 nlohmann::json::array();
1500
1501 tinyxml2::XMLElement *arg =
1502 signals->FirstChildElement(
1503 "arg");
1504 while (arg != nullptr)
1505 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001506 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001507 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001508 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001509 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001510 if (name != nullptr &&
1511 type != nullptr)
1512 {
1513 argsArray.push_back({
1514 {"name", name},
1515 {"type", type},
1516 });
1517 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001518 arg = arg->NextSiblingElement(
1519 "arg");
1520 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001521 const char *name =
1522 signals->Attribute("name");
1523 if (name != nullptr)
1524 {
1525 signalsArray.push_back(
1526 {{"name", name},
1527 {"args", argsArray}});
1528 }
1529
Ed Tanous1abe55e2018-09-05 08:30:59 -07001530 signals =
1531 signals->NextSiblingElement(
1532 "signal");
1533 }
1534
Ed Tanous1abe55e2018-09-05 08:30:59 -07001535 break;
1536 }
1537
1538 interface = interface->NextSiblingElement(
1539 "interface");
1540 }
1541 if (interface == nullptr)
1542 {
1543 // if we got to the end of the list and
1544 // never found a match, throw 404
1545 res.result(
1546 boost::beast::http::status::not_found);
1547 }
1548 }
1549 }
1550 res.end();
1551 },
1552 processName, objectPath,
1553 "org.freedesktop.DBus.Introspectable", "Introspect");
1554 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001555 else
1556 {
1557 if (req.method() != "POST"_method)
1558 {
1559 res.result(boost::beast::http::status::not_found);
1560 res.end();
1561 return;
1562 }
1563
1564 nlohmann::json requestDbusData =
1565 nlohmann::json::parse(req.body, nullptr, false);
1566
1567 if (requestDbusData.is_discarded())
1568 {
1569 res.result(boost::beast::http::status::bad_request);
1570 res.end();
1571 return;
1572 }
1573 if (!requestDbusData.is_array())
1574 {
1575 res.result(boost::beast::http::status::bad_request);
1576 res.end();
1577 return;
1578 }
1579 auto transaction = std::make_shared<InProgressActionData>(res);
1580
1581 transaction->path = objectPath;
1582 transaction->methodName = methodName;
1583 transaction->arguments = std::move(requestDbusData);
1584
1585 findActionOnInterface(transaction, processName);
1586 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001587 });
1588}
1589} // namespace openbmc_mapper
1590} // namespace crow