blob: d42c04b0d36add5feeddf1b3304408a5b9c2c625 [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#include <crow/app.h>
2
3#include <tinyxml2.h>
4#include <dbus/connection.hpp>
5#include <dbus/endpoint.hpp>
6#include <dbus/filter.hpp>
7#include <dbus/match.hpp>
8#include <dbus/message.hpp>
9#include <dbus_singleton.hpp>
Ed Tanous64530012018-02-06 17:08:16 -080010#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070011
12namespace crow {
13namespace openbmc_mapper {
Ed Tanousba9f9a62017-10-11 16:40:35 -070014
Ed Tanous911ac312017-08-15 09:37:42 -070015void introspect_objects(crow::response &res, std::string process_name,
Ed Tanous64530012018-02-06 17:08:16 -080016 std::string path,
17 std::shared_ptr<nlohmann::json> transaction) {
Ed Tanous911ac312017-08-15 09:37:42 -070018 dbus::endpoint introspect_endpoint(
19 process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070020 crow::connections::system_bus->async_method_call(
21 [&, process_name{std::move(process_name)}, object_path{std::move(path)} ](
22 const boost::system::error_code ec,
23 const std::string &introspect_xml) {
Ed Tanous911ac312017-08-15 09:37:42 -070024 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -080025 CROW_LOG_ERROR << "Introspect call failed with error: "
26 << ec.message() << " on process: " << process_name
27 << " path: " << object_path << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070028
29 } else {
Ed Tanous64530012018-02-06 17:08:16 -080030 transaction->push_back({{"path", object_path}});
Ed Tanous911ac312017-08-15 09:37:42 -070031
32 tinyxml2::XMLDocument doc;
33
34 doc.Parse(introspect_xml.c_str());
35 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
36 if (pRoot == nullptr) {
Ed Tanous64530012018-02-06 17:08:16 -080037 CROW_LOG_ERROR << "XML document failed to parse " << process_name
38 << " " << path << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070039
40 } else {
41 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
42 while (node != nullptr) {
43 std::string child_path = node->Attribute("name");
44 std::string newpath;
45 if (object_path != "/") {
46 newpath += object_path;
47 }
48 newpath += "/" + child_path;
Ed Tanous64530012018-02-06 17:08:16 -080049 // introspect the subobjects as well
50 introspect_objects(res, process_name, newpath, transaction);
Ed Tanous911ac312017-08-15 09:37:42 -070051
52 node = node->NextSiblingElement("node");
53 }
54 }
55 }
56 // if we're the last outstanding caller, finish the request
Ed Tanous64530012018-02-06 17:08:16 -080057 if (transaction.use_count() == 1) {
58 res.json_value = {{"status", "ok"},
59 {"bus_name", process_name},
60 {"objects", *transaction}};
Ed Tanous911ac312017-08-15 09:37:42 -070061 res.end();
62 }
63 },
64 introspect_endpoint);
65}
Ed Tanous64530012018-02-06 17:08:16 -080066using ManagedObjectType = std::vector<std::pair<
67 dbus::object_path, boost::container::flat_map<
68 std::string, boost::container::flat_map<
69 std::string, dbus::dbus_variant>>>>;
70
71void get_manged_objects_for_enumerate(
72 const std::string &object_name, const std::string &connection_name,
73 crow::response &res, std::shared_ptr<nlohmann::json> transaction) {
74 crow::connections::system_bus->async_method_call(
75 [&res, transaction](const boost::system::error_code ec,
76 const ManagedObjectType &objects) {
77 if (ec) {
78 CROW_LOG_ERROR << ec;
79 } else {
80 nlohmann::json &data_json = *transaction;
81 for (auto &object_path : objects) {
82 nlohmann::json &object_json = data_json[object_path.first.value];
83 for (const auto &interface : object_path.second) {
84 for (const auto &property : interface.second) {
85 boost::apply_visitor(
86 [&](auto &&val) { object_json[property.first] = val; },
87 property.second);
88 }
89 }
90 }
91 }
92
93 if (transaction.use_count() == 1) {
94 res.json_value = {{"message", "200 OK"},
95 {"status", "ok"},
96 {"data", std::move(*transaction)}};
97 res.end();
98 }
99 },
100 {connection_name, object_name, "org.freedesktop.DBus.ObjectManager",
101 "GetManagedObjects"});
102} // namespace openbmc_mapper
103
104using GetSubTreeType = std::vector<
105 std::pair<std::string,
106 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
107
108void handle_enumerate(crow::response &res, const std::string &object_path) {
109 crow::connections::system_bus->async_method_call(
110 [&res, object_path{std::string(object_path)} ](
111 const boost::system::error_code ec,
112 const GetSubTreeType &object_names) {
113 if (ec) {
114 res.code = 500;
115 res.end();
116 return;
117 }
118
119 boost::container::flat_set<std::string> connections;
120
121 for (const auto &object : object_names) {
122 for (const auto &connection : object.second) {
123 connections.insert(connection.first);
124 }
125 }
126
127 if (connections.size() <= 0) {
128 res.code = 404;
129 res.end();
130 return;
131 }
132 auto transaction =
133 std::make_shared<nlohmann::json>(nlohmann::json::object());
134 for (const std::string &connection : connections) {
135 get_manged_objects_for_enumerate(object_path, connection, res,
136 transaction);
137 }
138
139 },
140 {"xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper",
141 "xyz.openbmc_project.ObjectMapper", "GetSubTree"},
142 object_path, (int32_t)0, std::array<std::string, 0>());
143}
Ed Tanous911ac312017-08-15 09:37:42 -0700144
145template <typename... Middlewares>
146void request_routes(Crow<Middlewares...> &app) {
147 CROW_ROUTE(app, "/bus/").methods("GET"_method)([](const crow::request &req) {
148 return nlohmann::json{{"busses", {{{"name", "system"}}}}, {"status", "ok"}};
149
150 });
151
152 CROW_ROUTE(app, "/bus/system/")
153 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
154 crow::connections::system_bus->async_method_call(
155 [&](const boost::system::error_code ec,
156 std::vector<std::string> &names) {
Ed Tanous64530012018-02-06 17:08:16 -0800157
Ed Tanous911ac312017-08-15 09:37:42 -0700158 if (ec) {
159 res.code = 500;
160 } else {
Ed Tanous64530012018-02-06 17:08:16 -0800161 std::sort(names.begin(), names.end());
Ed Tanous911ac312017-08-15 09:37:42 -0700162 nlohmann::json j{{"status", "ok"}};
163 auto &objects_sub = j["objects"];
164 for (auto &name : names) {
165 objects_sub.push_back({{"name", name}});
166 }
Ed Tanous64530012018-02-06 17:08:16 -0800167 res.json_value = std::move(j);
Ed Tanous911ac312017-08-15 09:37:42 -0700168 }
Ed Tanous911ac312017-08-15 09:37:42 -0700169 res.end();
Ed Tanous911ac312017-08-15 09:37:42 -0700170 },
171 {"org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"});
172
173 });
174
Ed Tanousba9f9a62017-10-11 16:40:35 -0700175 CROW_ROUTE(app, "/list/")
176 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
177 crow::connections::system_bus->async_method_call(
178 [&](const boost::system::error_code ec,
179 const std::vector<std::string> &object_paths) {
180
181 if (ec) {
182 res.code = 500;
183 } else {
Ed Tanous64530012018-02-06 17:08:16 -0800184 res.json_value = {{"status", "ok"},
185 {"message", "200 OK"},
186 {"data", std::move(object_paths)}};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700187 }
188 res.end();
189 },
190 {"xyz.openbmc_project.ObjectMapper",
191 "/xyz/openbmc_project/object_mapper",
192 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths"},
193 "", static_cast<int32_t>(99), std::array<std::string, 0>());
194 });
195
Ed Tanous64530012018-02-06 17:08:16 -0800196 CROW_ROUTE(app, "/xyz/<path>")
shiyilei00b92f72017-11-12 16:21:16 +0800197 .methods("GET"_method,
198 "PUT"_method)([](const crow::request &req, crow::response &res,
Ed Tanousba9f9a62017-10-11 16:40:35 -0700199 const std::string &path) {
Ed Tanous64530012018-02-06 17:08:16 -0800200 std::shared_ptr<nlohmann::json> transaction =
201 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanousba9f9a62017-10-11 16:40:35 -0700202 using GetObjectType =
203 std::vector<std::pair<std::string, std::vector<std::string>>>;
shiyilei00b92f72017-11-12 16:21:16 +0800204 std::string object_path;
205 std::string dest_property;
206 std::string property_set_value;
207 size_t attr_position = path.find("/attr/");
Ed Tanous64530012018-02-06 17:08:16 -0800208 if (attr_position == path.npos) {
shiyilei00b92f72017-11-12 16:21:16 +0800209 object_path = "/xyz/" + path;
Ed Tanous64530012018-02-06 17:08:16 -0800210 } else {
shiyilei00b92f72017-11-12 16:21:16 +0800211 object_path = "/xyz/" + path.substr(0, attr_position);
212 dest_property =
213 path.substr((attr_position + strlen("/attr/")), path.length());
214 auto request_dbus_data =
215 nlohmann::json::parse(req.body, nullptr, false);
216 if (request_dbus_data.is_discarded()) {
217 res.code = 400;
218 res.end();
219 return;
220 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700221
shiyilei00b92f72017-11-12 16:21:16 +0800222 auto property_value_it = request_dbus_data.find("data");
223 if (property_value_it == request_dbus_data.end()) {
224 res.code = 400;
225 res.end();
226 return;
227 }
228
229 property_set_value = property_value_it->get<const std::string>();
230 if (property_set_value.empty()) {
231 res.code = 400;
232 res.end();
233 return;
234 }
235 }
236
Ed Tanous64530012018-02-06 17:08:16 -0800237 if (boost::ends_with(object_path, "/enumerate")) {
238 object_path.erase(object_path.end() - 10, object_path.end());
239 handle_enumerate(res, object_path);
240 return;
241 }
242
Ed Tanousba9f9a62017-10-11 16:40:35 -0700243 crow::connections::system_bus->async_method_call(
shiyilei00b92f72017-11-12 16:21:16 +0800244 [
Ed Tanous64530012018-02-06 17:08:16 -0800245 &, object_path{std::move(object_path)},
246 dest_property{std::move(dest_property)},
247 property_set_value{std::move(property_set_value)}, transaction
shiyilei00b92f72017-11-12 16:21:16 +0800248 ](const boost::system::error_code ec,
249 const GetObjectType &object_names) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700250 if (ec) {
251 res.code = 500;
252 res.end();
253 return;
254 }
255 if (object_names.size() != 1) {
256 res.code = 404;
257 res.end();
258 return;
259 }
shiyilei00b92f72017-11-12 16:21:16 +0800260 if (req.method == "GET"_method) {
261 for (auto &interface : object_names[0].second) {
shiyilei00b92f72017-11-12 16:21:16 +0800262 crow::connections::system_bus->async_method_call(
263 [&](const boost::system::error_code ec,
264 const std::vector<std::pair<
265 std::string, dbus::dbus_variant>> &properties) {
shiyilei00b92f72017-11-12 16:21:16 +0800266 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800267 CROW_LOG_ERROR << "Bad dbus request error: " << ec;
shiyilei00b92f72017-11-12 16:21:16 +0800268 } else {
269 for (auto &property : properties) {
270 boost::apply_visitor(
271 [&](auto val) {
Ed Tanous64530012018-02-06 17:08:16 -0800272 (*transaction)[property.first] = val;
shiyilei00b92f72017-11-12 16:21:16 +0800273 },
274 property.second);
275 }
276 }
Ed Tanous64530012018-02-06 17:08:16 -0800277 if (transaction.use_count() == 1) {
278 res.json_value = {{"status", "ok"},
279 {"message", "200 OK"},
280 {"data", *transaction}};
281
shiyilei00b92f72017-11-12 16:21:16 +0800282 res.end();
shiyilei00b92f72017-11-12 16:21:16 +0800283 }
284 },
285 {object_names[0].first, object_path,
286 "org.freedesktop.DBus.Properties", "GetAll"},
287 interface);
288 }
289 } else if (req.method == "PUT"_method) {
290 for (auto &interface : object_names[0].second) {
shiyilei00b92f72017-11-12 16:21:16 +0800291 crow::connections::system_bus->async_method_call(
292 [
293 &, interface{std::move(interface)},
294 object_names{std::move(object_names)},
295 object_path{std::move(object_path)},
296 dest_property{std::move(dest_property)},
Ed Tanous64530012018-02-06 17:08:16 -0800297 property_set_value{std::move(property_set_value)},
298 transaction
shiyilei00b92f72017-11-12 16:21:16 +0800299 ](const boost::system::error_code ec,
Ed Tanous64530012018-02-06 17:08:16 -0800300 const boost::container::flat_map<
301 std::string, dbus::dbus_variant> &properties) {
shiyilei00b92f72017-11-12 16:21:16 +0800302 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800303 CROW_LOG_ERROR << "Bad dbus request error: " << ec;
shiyilei00b92f72017-11-12 16:21:16 +0800304 } else {
Ed Tanous64530012018-02-06 17:08:16 -0800305 auto it = properties.find(dest_property);
306 if (it != properties.end()) {
307 // find the matched property in the interface
308 dbus::dbus_variant property_value(
309 property_set_value); // create the dbus
310 // variant for dbus call
311 crow::connections::system_bus->async_method_call(
312 [&](const boost::system::error_code ec) {
313 // use the method "Set" to set the property
314 // value
315 if (ec) {
316 CROW_LOG_ERROR << "Bad dbus request error: "
317 << ec;
318 }
319 // find the matched property and send the
320 // response
321 *transaction = {{"status", "ok"},
322 {"message", "200 OK"},
323 {"data", nullptr}};
324
325 },
326 {object_names[0].first, object_path,
327 "org.freedesktop.DBus.Properties", "Set"},
328 interface, dest_property, property_value);
shiyilei00b92f72017-11-12 16:21:16 +0800329 }
Ed Tanous64530012018-02-06 17:08:16 -0800330 }
331 // if we are the last caller, finish the transaction
332 if (transaction.use_count() == 1) {
333 // if nobody filled in the property, all calls either
334 // errored, or failed
335 if (transaction == nullptr) {
336 res.code = 403;
337 res.json_value = {{"status", "error"},
338 {"message", "403 Forbidden"},
339 {"data",
340 {{"message",
341 "The specified property "
342 "cannot be created: " +
343 dest_property}}}};
344
345 } else {
346 res.json_value = *transaction;
shiyilei00b92f72017-11-12 16:21:16 +0800347 }
Ed Tanous64530012018-02-06 17:08:16 -0800348
349 res.end();
350 return;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700351 }
shiyilei00b92f72017-11-12 16:21:16 +0800352 },
353 {object_names[0].first, object_path,
354 "org.freedesktop.DBus.Properties", "GetAll"},
355 interface);
356 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700357 }
358 },
359 {"xyz.openbmc_project.ObjectMapper",
360 "/xyz/openbmc_project/object_mapper",
361 "xyz.openbmc_project.ObjectMapper", "GetObject"},
362 object_path, std::array<std::string, 0>());
363 });
shiyilei00b92f72017-11-12 16:21:16 +0800364
Ed Tanous911ac312017-08-15 09:37:42 -0700365 CROW_ROUTE(app, "/bus/system/<str>/")
366 .methods("GET"_method)([](const crow::request &req, crow::response &res,
367 const std::string &connection) {
Ed Tanous64530012018-02-06 17:08:16 -0800368 std::shared_ptr<nlohmann::json> transaction;
369 introspect_objects(res, connection, "/", transaction);
Ed Tanous911ac312017-08-15 09:37:42 -0700370 });
371
372 CROW_ROUTE(app, "/bus/system/<str>/<path>")
373 .methods("GET"_method)([](const crow::request &req, crow::response &res,
374 const std::string &process_name,
375 const std::string &requested_path) {
376
377 std::vector<std::string> strs;
378 boost::split(strs, requested_path, boost::is_any_of("/"));
379 std::string object_path;
380 std::string interface_name;
381 std::string method_name;
382 auto it = strs.begin();
383 if (it == strs.end()) {
384 object_path = "/";
385 }
386 while (it != strs.end()) {
387 // Check if segment contains ".". If it does, it must be an
388 // interface
389 if ((*it).find(".") != std::string::npos) {
390 break;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700391 // THis check is neccesary as the trailing slash gets parsed as
Ed Tanous64530012018-02-06 17:08:16 -0800392 // part of our <path> specifier above, which causes the normal
393 // trailing backslash redirector to fail.
Ed Tanous911ac312017-08-15 09:37:42 -0700394 } else if (!it->empty()) {
395 object_path += "/" + *it;
396 }
397 it++;
398 }
399 if (it != strs.end()) {
400 interface_name = *it;
401 it++;
402
403 // after interface, we might have a method name
404 if (it != strs.end()) {
405 method_name = *it;
406 it++;
407 }
408 }
409 if (it != strs.end()) {
410 // if there is more levels past the method name, something went
411 // wrong, throw an error
412 res.code = 404;
413 res.end();
414 return;
415 }
416 dbus::endpoint introspect_endpoint(
417 process_name, object_path, "org.freedesktop.DBus.Introspectable",
418 "Introspect");
419 if (interface_name.empty()) {
420 crow::connections::system_bus->async_method_call(
421 [
422 &, process_name{std::move(process_name)},
423 object_path{std::move(object_path)}
424 ](const boost::system::error_code ec,
425 const std::string &introspect_xml) {
426 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800427 CROW_LOG_ERROR
Ed Tanous911ac312017-08-15 09:37:42 -0700428 << "Introspect call failed with error: " << ec.message()
429 << " on process: " << process_name
430 << " path: " << object_path << "\n";
431
432 } else {
433 tinyxml2::XMLDocument doc;
434
435 doc.Parse(introspect_xml.c_str());
436 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
437 if (pRoot == nullptr) {
Ed Tanous64530012018-02-06 17:08:16 -0800438 CROW_LOG_ERROR << "XML document failed to parse "
439 << process_name << " " << object_path
440 << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -0700441 res.write(nlohmann::json{{"status", "XML parse error"}});
442 res.code = 500;
443 } else {
444 nlohmann::json interfaces_array = nlohmann::json::array();
445 tinyxml2::XMLElement *interface =
446 pRoot->FirstChildElement("interface");
447
448 while (interface != nullptr) {
449 std::string iface_name = interface->Attribute("name");
450 interfaces_array.push_back({{"name", iface_name}});
451
452 interface = interface->NextSiblingElement("interface");
453 }
Ed Tanous64530012018-02-06 17:08:16 -0800454 res.json_value = {{"status", "ok"},
455 {"bus_name", process_name},
456 {"interfaces", interfaces_array},
457 {"object_path", object_path}};
Ed Tanous911ac312017-08-15 09:37:42 -0700458 }
459 }
460 res.end();
461 },
462 introspect_endpoint);
463 } else {
464 crow::connections::system_bus->async_method_call(
465 [
466 &, process_name{std::move(process_name)},
467 interface_name{std::move(interface_name)},
468 object_path{std::move(object_path)}
469 ](const boost::system::error_code ec,
470 const std::string &introspect_xml) {
471 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800472 CROW_LOG_ERROR
Ed Tanous911ac312017-08-15 09:37:42 -0700473 << "Introspect call failed with error: " << ec.message()
474 << " on process: " << process_name
475 << " path: " << object_path << "\n";
476
477 } else {
478 tinyxml2::XMLDocument doc;
479
480 doc.Parse(introspect_xml.c_str());
481 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
482 if (pRoot == nullptr) {
Ed Tanous64530012018-02-06 17:08:16 -0800483 CROW_LOG_ERROR << "XML document failed to parse "
484 << process_name << " " << object_path
485 << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -0700486 res.code = 500;
487
488 } else {
489 tinyxml2::XMLElement *node =
490 pRoot->FirstChildElement("node");
491
492 // if we know we're the only call, build the json directly
493 nlohmann::json methods_array = nlohmann::json::array();
494 nlohmann::json signals_array = nlohmann::json::array();
495 tinyxml2::XMLElement *interface =
496 pRoot->FirstChildElement("interface");
497
498 while (interface != nullptr) {
499 std::string iface_name = interface->Attribute("name");
500
501 if (iface_name == interface_name) {
502 tinyxml2::XMLElement *methods =
503 interface->FirstChildElement("method");
504 while (methods != nullptr) {
505 nlohmann::json args_array = nlohmann::json::array();
506 tinyxml2::XMLElement *arg =
507 methods->FirstChildElement("arg");
508 while (arg != nullptr) {
509 args_array.push_back(
510 {{"name", arg->Attribute("name")},
511 {"type", arg->Attribute("type")},
512 {"direction", arg->Attribute("direction")}});
513 arg = arg->NextSiblingElement("arg");
514 }
515 methods_array.push_back(
516 {{"name", methods->Attribute("name")},
Ed Tanous64530012018-02-06 17:08:16 -0800517 {"uri", "/bus/system/" + process_name +
518 object_path + "/" + interface_name +
519 "/" + methods->Attribute("name")},
Ed Tanous911ac312017-08-15 09:37:42 -0700520 {"args", args_array}});
521 methods = methods->NextSiblingElement("method");
522 }
523 tinyxml2::XMLElement *signals =
524 interface->FirstChildElement("signal");
525 while (signals != nullptr) {
526 nlohmann::json args_array = nlohmann::json::array();
527
528 tinyxml2::XMLElement *arg =
529 signals->FirstChildElement("arg");
530 while (arg != nullptr) {
531 std::string name = arg->Attribute("name");
532 std::string type = arg->Attribute("type");
533 args_array.push_back({
Ed Tanous64530012018-02-06 17:08:16 -0800534 {"name", name},
535 {"type", type},
Ed Tanous911ac312017-08-15 09:37:42 -0700536 });
537 arg = arg->NextSiblingElement("arg");
538 }
539 signals_array.push_back(
540 {{"name", signals->Attribute("name")},
541 {"args", args_array}});
542 signals = signals->NextSiblingElement("signal");
543 }
544
545 nlohmann::json j{
546 {"status", "ok"},
547 {"bus_name", process_name},
548 {"interface", interface_name},
549 {"methods", methods_array},
550 {"object_path", object_path},
551 {"properties", nlohmann::json::object()},
552 {"signals", signals_array}};
553
554 res.write(j.dump());
555 break;
556 }
557
558 interface = interface->NextSiblingElement("interface");
559 }
560 if (interface == nullptr) {
561 // if we got to the end of the list and never found a
562 // match, throw 404
563 res.code = 404;
564 }
565 }
566 }
567 res.end();
568 },
569 introspect_endpoint);
570 }
571
572 });
573}
574} // namespace openbmc_mapper
575} // namespace crow