blob: 110e6ac25b9e840ed7424ff81b7e65dffd831b31 [file] [log] [blame]
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001#pragma once
2
Ed Tanous911ac312017-08-15 09:37:42 -07003#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -07004#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -07005
Ed Tanouse3cb5a32018-08-08 14:16:49 -07006#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07007#include <boost/algorithm/string.hpp>
8#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -07009#include <dbus_singleton.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070010#include <experimental/filesystem>
11#include <fstream>
Ed Tanous911ac312017-08-15 09:37:42 -070012
Ed Tanous1abe55e2018-09-05 08:30:59 -070013namespace crow
14{
15namespace openbmc_mapper
16{
Ed Tanousba9f9a62017-10-11 16:40:35 -070017
Ed Tanouse3cb5a32018-08-08 14:16:49 -070018void introspectObjects(const std::string &processName,
19 const std::string &objectPath,
20 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070021{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070022 if (transaction->res.jsonValue.is_null())
23 {
24 transaction->res.jsonValue = {{"status", "ok"},
25 {"bus_name", processName},
26 {"objects", nlohmann::json::array()}};
27 }
28
Ed Tanous1abe55e2018-09-05 08:30:59 -070029 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070030 [transaction, processName{std::string(processName)},
31 objectPath{std::string(objectPath)}](
32 const boost::system::error_code ec,
33 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070034 if (ec)
35 {
36 BMCWEB_LOG_ERROR
37 << "Introspect call failed with error: " << ec.message()
38 << " on process: " << processName << " path: " << objectPath
39 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070040 return;
41 }
42 transaction->res.jsonValue["objects"].push_back(
43 {{"path", objectPath}});
44
45 tinyxml2::XMLDocument doc;
46
47 doc.Parse(introspect_xml.c_str());
48 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
49 if (pRoot == nullptr)
50 {
51 BMCWEB_LOG_ERROR << "XML document failed to parse "
52 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070053 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 else
55 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070056 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
57 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070059 const char *childPath = node->Attribute("name");
60 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 std::string newpath;
63 if (objectPath != "/")
64 {
65 newpath += objectPath;
66 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070067 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -070069 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070071
72 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 }
74 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -070076 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070078}
Ed Tanous64530012018-02-06 17:08:16 -080079
Ed Tanousaa2e59c2018-04-12 12:17:20 -070080// A smattering of common types to unpack. TODO(ed) this should really iterate
81// the sdbusplus object directly and build the json response
82using DbusRestVariantType = sdbusplus::message::variant<
83 std::vector<std::tuple<std::string, std::string, std::string>>, std::string,
84 int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t,
85 bool>;
86
87using ManagedObjectType = std::vector<std::pair<
88 sdbusplus::message::object_path,
89 boost::container::flat_map<
90 std::string,
91 boost::container::flat_map<std::string, DbusRestVariantType>>>>;
92
Ed Tanous1abe55e2018-09-05 08:30:59 -070093void getManagedObjectsForEnumerate(const std::string &object_name,
Ed Tanouse3cb5a32018-08-08 14:16:49 -070094 const std::string &object_manager_path,
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 const std::string &connection_name,
96 crow::Response &res,
97 std::shared_ptr<nlohmann::json> transaction)
98{
99 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700100 [&res, transaction, object_name{std::string(object_name)}](
101 const boost::system::error_code ec,
102 const 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,
843 const std::vector<
844 std::pair<std::string, DbusRestVariantType>>
845 &properties) {
846 if (ec)
847 {
848 BMCWEB_LOG_ERROR << "Bad dbus request error: "
849 << ec;
850 }
851 else
852 {
853 for (const std::pair<std::string,
854 DbusRestVariantType>
855 &property : properties)
856 {
857 // if property name is empty, or matches our
858 // search query, add it to the response json
859
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700860 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 {
862 mapbox::util::apply_visitor(
863 [&response, &property](auto &&val) {
864 (*response)[property.first] =
865 val;
866 },
867 property.second);
868 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700869 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700870 {
871 mapbox::util::apply_visitor(
872 [&response](auto &&val) {
873 (*response) = val;
874 },
875 property.second);
876 }
877 }
878 }
879 if (response.use_count() == 1)
880 {
881 res.jsonValue = {{"status", "ok"},
882 {"message", "200 OK"},
883 {"data", *response}};
884
885 res.end();
886 }
887 },
888 connection.first, *path,
889 "org.freedesktop.DBus.Properties", "GetAll", interface);
890 }
891 }
892 },
893 "xyz.openbmc_project.ObjectMapper",
894 "/xyz/openbmc_project/object_mapper",
895 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
896 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700897}
898
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899struct AsyncPutRequest
900{
901 AsyncPutRequest(crow::Response &res) : res(res)
902 {
903 res.jsonValue = {
904 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
905 }
906 ~AsyncPutRequest()
907 {
908 if (res.result() == boost::beast::http::status::internal_server_error)
909 {
910 // Reset the json object to clear out any data that made it in
911 // before the error happened todo(ed) handle error condition with
912 // proper code
913 res.jsonValue = nlohmann::json::object();
914 }
915
916 if (res.jsonValue.empty())
917 {
918 res.result(boost::beast::http::status::forbidden);
919 res.jsonValue = {
920 {"status", "error"},
921 {"message", "403 Forbidden"},
922 {"data",
923 {{"message", "The specified property cannot be created: " +
924 propertyName}}}};
925 }
926
927 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700928 }
929
Ed Tanous1abe55e2018-09-05 08:30:59 -0700930 void setErrorStatus()
931 {
932 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700933 }
934
Ed Tanous1abe55e2018-09-05 08:30:59 -0700935 crow::Response &res;
936 std::string objectPath;
937 std::string propertyName;
938 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700939};
940
Ed Tanousd76323e2018-08-07 14:35:40 -0700941void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700942 const std::string &objectPath, const std::string &destProperty)
943{
944 nlohmann::json requestDbusData =
945 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700946
Ed Tanous1abe55e2018-09-05 08:30:59 -0700947 if (requestDbusData.is_discarded())
948 {
949 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700950 res.end();
951 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700953
Ed Tanous1abe55e2018-09-05 08:30:59 -0700954 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
955 if (propertyIt == requestDbusData.end())
956 {
957 res.result(boost::beast::http::status::bad_request);
958 res.end();
959 return;
960 }
961 const nlohmann::json &propertySetValue = *propertyIt;
962 auto transaction = std::make_shared<AsyncPutRequest>(res);
963 transaction->objectPath = objectPath;
964 transaction->propertyName = destProperty;
965 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -0700966
Ed Tanous1abe55e2018-09-05 08:30:59 -0700967 using GetObjectType =
968 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -0700969
Ed Tanous1abe55e2018-09-05 08:30:59 -0700970 crow::connections::systemBus->async_method_call(
971 [transaction](const boost::system::error_code ec,
972 const GetObjectType &object_names) {
973 if (!ec && object_names.size() <= 0)
974 {
975 transaction->res.result(boost::beast::http::status::not_found);
976 return;
977 }
Ed Tanous911ac312017-08-15 09:37:42 -0700978
Ed Tanous1abe55e2018-09-05 08:30:59 -0700979 for (const std::pair<std::string, std::vector<std::string>>
980 connection : object_names)
981 {
982 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -0700983
Ed Tanous1abe55e2018-09-05 08:30:59 -0700984 crow::connections::systemBus->async_method_call(
985 [connectionName{std::string(connectionName)},
986 transaction](const boost::system::error_code ec,
987 const std::string &introspectXml) {
988 if (ec)
989 {
990 BMCWEB_LOG_ERROR
991 << "Introspect call failed with error: "
992 << ec.message()
993 << " on process: " << connectionName;
994 transaction->setErrorStatus();
995 return;
Ed Tanous911ac312017-08-15 09:37:42 -0700996 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700997 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -0700998
Ed Tanous1abe55e2018-09-05 08:30:59 -0700999 doc.Parse(introspectXml.c_str());
1000 tinyxml2::XMLNode *pRoot =
1001 doc.FirstChildElement("node");
1002 if (pRoot == nullptr)
1003 {
1004 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1005 << introspectXml;
1006 transaction->setErrorStatus();
1007 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001008 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001009 tinyxml2::XMLElement *ifaceNode =
1010 pRoot->FirstChildElement("interface");
1011 while (ifaceNode != nullptr)
1012 {
1013 const char *interfaceName =
1014 ifaceNode->Attribute("name");
1015 BMCWEB_LOG_DEBUG << "found interface "
1016 << interfaceName;
1017 tinyxml2::XMLElement *propNode =
1018 ifaceNode->FirstChildElement("property");
1019 while (propNode != nullptr)
1020 {
1021 const char *propertyName =
1022 propNode->Attribute("name");
1023 BMCWEB_LOG_DEBUG << "Found property "
1024 << propertyName;
1025 if (propertyName == transaction->propertyName)
1026 {
1027 const char *argType =
1028 propNode->Attribute("type");
1029 if (argType != nullptr)
1030 {
1031 sdbusplus::message::message m =
1032 crow::connections::systemBus
1033 ->new_method_call(
1034 connectionName.c_str(),
1035 transaction->objectPath
1036 .c_str(),
1037 "org.freedesktop.DBus."
1038 "Properties",
1039 "Set");
1040 m.append(interfaceName,
1041 transaction->propertyName);
1042 int r = sd_bus_message_open_container(
1043 m.get(), SD_BUS_TYPE_VARIANT,
1044 argType);
1045 if (r < 0)
1046 {
1047 transaction->setErrorStatus();
1048 return;
1049 }
1050 r = convertJsonToDbus(
1051 m.get(), argType,
1052 transaction->propertyValue);
1053 if (r < 0)
1054 {
1055 transaction->setErrorStatus();
1056 return;
1057 }
1058 r = sd_bus_message_close_container(
1059 m.get());
1060 if (r < 0)
1061 {
1062 transaction->setErrorStatus();
1063 return;
1064 }
Ed Tanous911ac312017-08-15 09:37:42 -07001065
Ed Tanous1abe55e2018-09-05 08:30:59 -07001066 crow::connections::systemBus
1067 ->async_send(
1068 m,
1069 [transaction](
1070 boost::system::error_code
1071 ec,
1072 sdbusplus::message::message
1073 &m) {
1074 BMCWEB_LOG_DEBUG << "sent";
1075 if (ec)
1076 {
1077 transaction->res
1078 .jsonValue
1079 ["status"] =
1080 "error";
1081 transaction->res
1082 .jsonValue
1083 ["message"] =
1084 ec.message();
1085 }
1086 });
1087 }
1088 }
1089 propNode =
1090 propNode->NextSiblingElement("property");
1091 }
1092 ifaceNode =
1093 ifaceNode->NextSiblingElement("interface");
1094 }
1095 },
1096 connectionName, transaction->objectPath,
1097 "org.freedesktop.DBus.Introspectable", "Introspect");
1098 }
1099 },
1100 "xyz.openbmc_project.ObjectMapper",
1101 "/xyz/openbmc_project/object_mapper",
1102 "xyz.openbmc_project.ObjectMapper", "GetObject",
1103 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001104}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001105
1106template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1107{
1108 BMCWEB_ROUTE(app, "/bus/")
1109 .methods("GET"_method)(
1110 [](const crow::Request &req, crow::Response &res) {
1111 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1112 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001113 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001114 });
1115
1116 BMCWEB_ROUTE(app, "/bus/system/")
1117 .methods("GET"_method)(
1118 [](const crow::Request &req, crow::Response &res) {
1119 auto myCallback = [&res](const boost::system::error_code ec,
1120 std::vector<std::string> &names) {
1121 if (ec)
1122 {
1123 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1124 res.result(
1125 boost::beast::http::status::internal_server_error);
1126 }
1127 else
1128 {
1129 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001130 res.jsonValue = {{"status", "ok"}};
1131 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001132 for (auto &name : names)
1133 {
1134 objectsSub.push_back({{"name", name}});
1135 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001136 }
1137 res.end();
1138 };
1139 crow::connections::systemBus->async_method_call(
1140 std::move(myCallback), "org.freedesktop.DBus", "/",
1141 "org.freedesktop.DBus", "ListNames");
1142 });
1143
1144 BMCWEB_ROUTE(app, "/list/")
1145 .methods("GET"_method)(
1146 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001147 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001148 });
1149
1150 BMCWEB_ROUTE(app, "/xyz/<path>")
1151 .methods("GET"_method, "PUT"_method,
1152 "POST"_method)([](const crow::Request &req,
1153 crow::Response &res,
1154 const std::string &path) {
1155 std::string objectPath = "/xyz/" + path;
1156
1157 // Trim any trailing "/" at the end
1158 if (boost::ends_with(objectPath, "/"))
1159 {
1160 objectPath.pop_back();
1161 }
1162
1163 // If accessing a single attribute, fill in and update objectPath,
1164 // otherwise leave destProperty blank
1165 std::string destProperty = "";
1166 const char *attrSeperator = "/attr/";
1167 size_t attrPosition = path.find(attrSeperator);
1168 if (attrPosition != path.npos)
1169 {
1170 objectPath = "/xyz/" + path.substr(0, attrPosition);
1171 destProperty = path.substr(attrPosition + strlen(attrSeperator),
1172 path.length());
1173 }
1174
1175 if (req.method() == "POST"_method)
1176 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001177 constexpr const char *actionSeperator = "/action/";
1178 size_t actionPosition = path.find(actionSeperator);
1179 if (actionPosition != path.npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001181 objectPath = "/xyz/" + path.substr(0, actionPosition);
1182 std::string postProperty =
1183 path.substr((actionPosition + strlen(actionSeperator)),
1184 path.length());
1185 handleAction(req, res, objectPath, postProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001186 return;
1187 }
1188 }
1189 else if (req.method() == "GET"_method)
1190 {
1191 if (boost::ends_with(objectPath, "/enumerate"))
1192 {
1193 objectPath.erase(objectPath.end() - 10, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001194 handleEnumerate(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001195 }
1196 else if (boost::ends_with(objectPath, "/list"))
1197 {
1198 objectPath.erase(objectPath.end() - 5, objectPath.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001199 handleList(res, objectPath);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001200 }
1201 else
1202 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001203 handleGet(res, objectPath, destProperty);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001204 }
1205 return;
1206 }
1207 else if (req.method() == "PUT"_method)
1208 {
1209 handlePut(req, res, objectPath, destProperty);
1210 return;
1211 }
1212
1213 res.result(boost::beast::http::status::method_not_allowed);
1214 res.end();
1215 });
1216
Ed Tanous1abe55e2018-09-05 08:30:59 -07001217 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1218 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1219 const std::string &dumpId) {
1220 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1221 if (!std::regex_match(dumpId, validFilename))
1222 {
1223 res.result(boost::beast::http::status::not_found);
1224 res.end();
1225 return;
1226 }
1227 std::experimental::filesystem::path loc(
1228 "/var/lib/phosphor-debug-collector/dumps");
1229
1230 loc += dumpId;
1231
1232 if (!std::experimental::filesystem::exists(loc) ||
1233 !std::experimental::filesystem::is_directory(loc))
1234 {
1235 res.result(boost::beast::http::status::not_found);
1236 res.end();
1237 return;
1238 }
1239 std::experimental::filesystem::directory_iterator files(loc);
1240 for (auto &file : files)
1241 {
1242 std::ifstream readFile(file.path());
1243 if (readFile.good())
1244 {
1245 continue;
1246 }
1247 res.addHeader("Content-Type", "application/octet-stream");
1248 res.body() = {std::istreambuf_iterator<char>(readFile),
1249 std::istreambuf_iterator<char>()};
1250 res.end();
1251 }
1252 res.result(boost::beast::http::status::not_found);
1253 res.end();
1254 return;
1255 });
1256
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001257 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001258 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001259 const std::string &Connection) {
1260 introspectObjects(Connection, "/",
1261 std::make_shared<bmcweb::AsyncResp>(res));
1262 });
1263
1264 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1265 .methods("GET"_method,
1266 "POST"_method)([](const crow::Request &req,
1267 crow::Response &res,
1268 const std::string &processName,
1269 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001270 std::vector<std::string> strs;
1271 boost::split(strs, requestedPath, boost::is_any_of("/"));
1272 std::string objectPath;
1273 std::string interfaceName;
1274 std::string methodName;
1275 auto it = strs.begin();
1276 if (it == strs.end())
1277 {
1278 objectPath = "/";
1279 }
1280 while (it != strs.end())
1281 {
1282 // Check if segment contains ".". If it does, it must be an
1283 // interface
1284 if (it->find(".") != std::string::npos)
1285 {
1286 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001287 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001288 // as part of our <path> specifier above, which causes the
1289 // normal trailing backslash redirector to fail.
1290 }
1291 else if (!it->empty())
1292 {
1293 objectPath += "/" + *it;
1294 }
1295 it++;
1296 }
1297 if (it != strs.end())
1298 {
1299 interfaceName = *it;
1300 it++;
1301
1302 // after interface, we might have a method name
1303 if (it != strs.end())
1304 {
1305 methodName = *it;
1306 it++;
1307 }
1308 }
1309 if (it != strs.end())
1310 {
1311 // if there is more levels past the method name, something went
1312 // wrong, return not found
1313 res.result(boost::beast::http::status::not_found);
1314 res.end();
1315 return;
1316 }
1317 if (interfaceName.empty())
1318 {
1319 crow::connections::systemBus->async_method_call(
1320 [&, processName,
1321 objectPath](const boost::system::error_code ec,
1322 const std::string &introspect_xml) {
1323 if (ec)
1324 {
1325 BMCWEB_LOG_ERROR
1326 << "Introspect call failed with error: "
1327 << ec.message()
1328 << " on process: " << processName
1329 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001330 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001331 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001332 tinyxml2::XMLDocument doc;
1333
1334 doc.Parse(introspect_xml.c_str());
1335 tinyxml2::XMLNode *pRoot =
1336 doc.FirstChildElement("node");
1337 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001338 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001339 BMCWEB_LOG_ERROR << "XML document failed to parse "
1340 << processName << " " << objectPath
1341 << "\n";
1342 res.jsonValue = {{"status", "XML parse error"}};
1343 res.result(boost::beast::http::status::
1344 internal_server_error);
1345 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001346 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001347
1348 BMCWEB_LOG_DEBUG << introspect_xml;
1349 res.jsonValue = {{"status", "ok"},
1350 {"bus_name", processName},
1351 {"object_path", objectPath}};
1352 nlohmann::json &interfacesArray =
1353 res.jsonValue["interfaces"];
1354 interfacesArray = nlohmann::json::array();
1355 tinyxml2::XMLElement *interface =
1356 pRoot->FirstChildElement("interface");
1357
1358 while (interface != nullptr)
1359 {
1360 const char *ifaceName =
1361 interface->Attribute("name");
1362 if (ifaceName != nullptr)
1363 {
1364 interfacesArray.push_back(
1365 {{"name", ifaceName}});
1366 }
1367
1368 interface =
1369 interface->NextSiblingElement("interface");
1370 }
1371
Ed Tanous1abe55e2018-09-05 08:30:59 -07001372 res.end();
1373 },
1374 processName, objectPath,
1375 "org.freedesktop.DBus.Introspectable", "Introspect");
1376 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001377 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001378 {
1379 crow::connections::systemBus->async_method_call(
1380 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001381 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001382 const boost::system::error_code ec,
1383 const std::string &introspect_xml) {
1384 if (ec)
1385 {
1386 BMCWEB_LOG_ERROR
1387 << "Introspect call failed with error: "
1388 << ec.message()
1389 << " on process: " << processName
1390 << " path: " << objectPath << "\n";
1391 }
1392 else
1393 {
1394 tinyxml2::XMLDocument doc;
1395
1396 doc.Parse(introspect_xml.c_str());
1397 tinyxml2::XMLNode *pRoot =
1398 doc.FirstChildElement("node");
1399 if (pRoot == nullptr)
1400 {
1401 BMCWEB_LOG_ERROR
1402 << "XML document failed to parse "
1403 << processName << " " << objectPath << "\n";
1404 res.result(boost::beast::http::status::
1405 internal_server_error);
1406 }
1407 else
1408 {
1409 tinyxml2::XMLElement *node =
1410 pRoot->FirstChildElement("node");
1411
1412 // if we know we're the only call, build the
1413 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001414 tinyxml2::XMLElement *interface =
1415 pRoot->FirstChildElement("interface");
1416
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001417 res.jsonValue = {
1418 {"status", "ok"},
1419 {"bus_name", processName},
1420 {"interface", interfaceName},
1421 {"object_path", objectPath},
1422 {"properties", nlohmann::json::object()}};
1423
1424 nlohmann::json &methodsArray =
1425 res.jsonValue["methods"];
1426 methodsArray = nlohmann::json::array();
1427
1428 nlohmann::json &signalsArray =
1429 res.jsonValue["signals"];
1430 signalsArray = nlohmann::json::array();
1431
Ed Tanous1abe55e2018-09-05 08:30:59 -07001432 while (interface != nullptr)
1433 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001434 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001435 interface->Attribute("name");
1436
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001437 if (ifaceName != nullptr &&
1438 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001439 {
1440 tinyxml2::XMLElement *methods =
1441 interface->FirstChildElement(
1442 "method");
1443 while (methods != nullptr)
1444 {
1445 nlohmann::json argsArray =
1446 nlohmann::json::array();
1447 tinyxml2::XMLElement *arg =
1448 methods->FirstChildElement(
1449 "arg");
1450 while (arg != nullptr)
1451 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001452 nlohmann::json thisArg;
1453 for (const char *fieldName :
1454 std::array<const char *,
1455 3>{"name",
1456 "direction",
1457 "type"})
1458 {
1459 const char *fieldValue =
1460 arg->Attribute(
1461 fieldName);
1462 if (fieldValue != nullptr)
1463 {
1464 thisArg[fieldName] =
1465 fieldValue;
1466 }
1467 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001468 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001469 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001470 arg = arg->NextSiblingElement(
1471 "arg");
1472 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001473
1474 const char *name =
1475 methods->Attribute("name");
1476 if (name != nullptr)
1477 {
1478 methodsArray.push_back(
1479 {{"name", name},
1480 {"uri", "/bus/system/" +
1481 processName +
1482 objectPath +
1483 "/" +
1484 interfaceName +
1485 "/" + name},
1486 {"args", argsArray}});
1487 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001488 methods =
1489 methods->NextSiblingElement(
1490 "method");
1491 }
1492 tinyxml2::XMLElement *signals =
1493 interface->FirstChildElement(
1494 "signal");
1495 while (signals != nullptr)
1496 {
1497 nlohmann::json argsArray =
1498 nlohmann::json::array();
1499
1500 tinyxml2::XMLElement *arg =
1501 signals->FirstChildElement(
1502 "arg");
1503 while (arg != nullptr)
1504 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001505 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001506 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001507 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001508 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001509 if (name != nullptr &&
1510 type != nullptr)
1511 {
1512 argsArray.push_back({
1513 {"name", name},
1514 {"type", type},
1515 });
1516 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517 arg = arg->NextSiblingElement(
1518 "arg");
1519 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001520 const char *name =
1521 signals->Attribute("name");
1522 if (name != nullptr)
1523 {
1524 signalsArray.push_back(
1525 {{"name", name},
1526 {"args", argsArray}});
1527 }
1528
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529 signals =
1530 signals->NextSiblingElement(
1531 "signal");
1532 }
1533
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534 break;
1535 }
1536
1537 interface = interface->NextSiblingElement(
1538 "interface");
1539 }
1540 if (interface == nullptr)
1541 {
1542 // if we got to the end of the list and
1543 // never found a match, throw 404
1544 res.result(
1545 boost::beast::http::status::not_found);
1546 }
1547 }
1548 }
1549 res.end();
1550 },
1551 processName, objectPath,
1552 "org.freedesktop.DBus.Introspectable", "Introspect");
1553 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001554 else
1555 {
1556 if (req.method() != "POST"_method)
1557 {
1558 res.result(boost::beast::http::status::not_found);
1559 res.end();
1560 return;
1561 }
1562
1563 nlohmann::json requestDbusData =
1564 nlohmann::json::parse(req.body, nullptr, false);
1565
1566 if (requestDbusData.is_discarded())
1567 {
1568 res.result(boost::beast::http::status::bad_request);
1569 res.end();
1570 return;
1571 }
1572 if (!requestDbusData.is_array())
1573 {
1574 res.result(boost::beast::http::status::bad_request);
1575 res.end();
1576 return;
1577 }
1578 auto transaction = std::make_shared<InProgressActionData>(res);
1579
1580 transaction->path = objectPath;
1581 transaction->methodName = methodName;
1582 transaction->arguments = std::move(requestDbusData);
1583
1584 findActionOnInterface(transaction, processName);
1585 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001586 });
1587}
1588} // namespace openbmc_mapper
1589} // namespace crow