blob: 9e5e5401370bf0a330bf880037002a1fd7a43482 [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>
10
11namespace crow {
12namespace openbmc_mapper {
Ed Tanousba9f9a62017-10-11 16:40:35 -070013
14// TODO(ed) having these as scope globals, and as simple as they are limits the
15// ability to queue multiple async operations at once. Being able to register
16// "done" callbacks to a queue here that also had a count attached would allow
17// multiple requests to be running at once
Ed Tanous911ac312017-08-15 09:37:42 -070018std::atomic<std::size_t> outstanding_async_calls(0);
19nlohmann::json object_paths;
shiyilei00b92f72017-11-12 16:21:16 +080020bool property_matched=false;
Ed Tanous911ac312017-08-15 09:37:42 -070021void introspect_objects(crow::response &res, std::string process_name,
22 std::string path) {
23 dbus::endpoint introspect_endpoint(
24 process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
25 outstanding_async_calls++;
26 crow::connections::system_bus->async_method_call(
27 [&, process_name{std::move(process_name)}, object_path{std::move(path)} ](
28 const boost::system::error_code ec,
29 const std::string &introspect_xml) {
30 outstanding_async_calls--;
31 if (ec) {
32 std::cerr << "Introspect call failed with error: " << ec.message()
33 << " on process: " << process_name
34 << " path: " << object_path << "\n";
35
36 } else {
37 object_paths.push_back({{"path", object_path}});
38
39 tinyxml2::XMLDocument doc;
40
41 doc.Parse(introspect_xml.c_str());
42 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
43 if (pRoot == nullptr) {
44 std::cerr << "XML document failed to parse " << process_name << " "
45 << path << "\n";
46
47 } else {
48 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
49 while (node != nullptr) {
50 std::string child_path = node->Attribute("name");
51 std::string newpath;
52 if (object_path != "/") {
53 newpath += object_path;
54 }
55 newpath += "/" + child_path;
56 // intropect the subobjects as well
57 introspect_objects(res, process_name, newpath);
58
59 node = node->NextSiblingElement("node");
60 }
61 }
62 }
63 // if we're the last outstanding caller, finish the request
64 if (outstanding_async_calls == 0) {
65 nlohmann::json j{{"status", "ok"},
66 {"bus_name", process_name},
67 {"objects", object_paths}};
68
69 res.write(j.dump());
70 object_paths.clear();
71 res.end();
72 }
73 },
74 introspect_endpoint);
75}
76
77template <typename... Middlewares>
78void request_routes(Crow<Middlewares...> &app) {
79 CROW_ROUTE(app, "/bus/").methods("GET"_method)([](const crow::request &req) {
80 return nlohmann::json{{"busses", {{{"name", "system"}}}}, {"status", "ok"}};
81
82 });
83
84 CROW_ROUTE(app, "/bus/system/")
85 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
86 crow::connections::system_bus->async_method_call(
87 [&](const boost::system::error_code ec,
88 std::vector<std::string> &names) {
89 std::sort(names.begin(), names.end());
90 if (ec) {
91 res.code = 500;
92 } else {
93 nlohmann::json j{{"status", "ok"}};
94 auto &objects_sub = j["objects"];
95 for (auto &name : names) {
96 objects_sub.push_back({{"name", name}});
97 }
98
99 res.write(j.dump());
100 }
101
102 res.end();
103
104 },
105 {"org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"});
106
107 });
108
Ed Tanousba9f9a62017-10-11 16:40:35 -0700109 CROW_ROUTE(app, "/list/")
110 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
111 crow::connections::system_bus->async_method_call(
112 [&](const boost::system::error_code ec,
113 const std::vector<std::string> &object_paths) {
114
115 if (ec) {
116 res.code = 500;
117 } else {
118 nlohmann::json j{{"status", "ok"},
119 {"message", "200 OK"},
120 {"data", object_paths}};
121 res.body = j.dump();
122 }
123 res.end();
124 },
125 {"xyz.openbmc_project.ObjectMapper",
126 "/xyz/openbmc_project/object_mapper",
127 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths"},
128 "", static_cast<int32_t>(99), std::array<std::string, 0>());
129 });
130
shiyilei00b92f72017-11-12 16:21:16 +0800131 CROW_ROUTE(app, "/xyz/<path>")
132 .methods("GET"_method,
133 "PUT"_method)([](const crow::request &req, crow::response &res,
Ed Tanousba9f9a62017-10-11 16:40:35 -0700134 const std::string &path) {
135 if (outstanding_async_calls != 0) {
136 res.code = 500;
137 res.body = "request in progress";
138 res.end();
139 return;
140 }
141 using GetObjectType =
142 std::vector<std::pair<std::string, std::vector<std::string>>>;
shiyilei00b92f72017-11-12 16:21:16 +0800143 std::string object_path;
144 std::string dest_property;
145 std::string property_set_value;
146 size_t attr_position = path.find("/attr/");
147 if (attr_position == path.npos)
148 object_path = "/xyz/" + path;
149 else {
150 object_path = "/xyz/" + path.substr(0, attr_position);
151 dest_property =
152 path.substr((attr_position + strlen("/attr/")), path.length());
153 auto request_dbus_data =
154 nlohmann::json::parse(req.body, nullptr, false);
155 if (request_dbus_data.is_discarded()) {
156 res.code = 400;
157 res.end();
158 return;
159 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700160
shiyilei00b92f72017-11-12 16:21:16 +0800161 auto property_value_it = request_dbus_data.find("data");
162 if (property_value_it == request_dbus_data.end()) {
163 res.code = 400;
164 res.end();
165 return;
166 }
167
168 property_set_value = property_value_it->get<const std::string>();
169 if (property_set_value.empty()) {
170 res.code = 400;
171 res.end();
172 return;
173 }
174 }
175
Ed Tanousba9f9a62017-10-11 16:40:35 -0700176 crow::connections::system_bus->async_method_call(
177 // object_path intentially captured by value
shiyilei00b92f72017-11-12 16:21:16 +0800178 [
179 &, object_path, dest_property{std::move(dest_property)},
180 property_set_value{std::move(property_set_value)}
181 ](const boost::system::error_code ec,
182 const GetObjectType &object_names) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700183 if (ec) {
184 res.code = 500;
185 res.end();
186 return;
187 }
188 if (object_names.size() != 1) {
189 res.code = 404;
190 res.end();
191 return;
192 }
shiyilei00b92f72017-11-12 16:21:16 +0800193 if (req.method == "GET"_method) {
194 for (auto &interface : object_names[0].second) {
195 outstanding_async_calls++;
196 crow::connections::system_bus->async_method_call(
197 [&](const boost::system::error_code ec,
198 const std::vector<std::pair<
199 std::string, dbus::dbus_variant>> &properties) {
200 outstanding_async_calls--;
201 if (ec) {
202 std::cerr << "Bad dbus request error: " << ec;
203 } else {
204 for (auto &property : properties) {
205 boost::apply_visitor(
206 [&](auto val) {
207 object_paths[property.first] = val;
208 },
209 property.second);
210 }
211 }
212 if (outstanding_async_calls == 0) {
213 nlohmann::json j{{"status", "ok"},
214 {"message", "200 OK"},
215 {"data", object_paths}};
216 res.body = j.dump();
217 res.end();
218 object_paths.clear();
219 }
220 },
221 {object_names[0].first, object_path,
222 "org.freedesktop.DBus.Properties", "GetAll"},
223 interface);
224 }
225 } else if (req.method == "PUT"_method) {
226 for (auto &interface : object_names[0].second) {
227 outstanding_async_calls++;
228 crow::connections::system_bus->async_method_call(
229 [
230 &, interface{std::move(interface)},
231 object_names{std::move(object_names)},
232 object_path{std::move(object_path)},
233 dest_property{std::move(dest_property)},
234 property_set_value{std::move(property_set_value)}
235 ](const boost::system::error_code ec,
Ed Tanousba9f9a62017-10-11 16:40:35 -0700236 const std::vector<std::pair<
237 std::string, dbus::dbus_variant>> &properties) {
shiyilei00b92f72017-11-12 16:21:16 +0800238 outstanding_async_calls--;
239 if (ec) {
240 std::cerr << "Bad dbus request error: " << ec;
241 } else {
242 for (auto &property : properties) {
243 // search all the properties in the interfaces
244 if (dest_property.compare(property.first) == 0) {
245 // find the matched property in the interface
246 property_matched = true;
247 dbus::dbus_variant property_value(
248 property_set_value); // create the dbus
249 // variant for dbus call
250 crow::connections::system_bus->async_method_call(
251 [&](const boost::system::error_code ec) {
252 // use the method "Set" to set the property
253 // value
254 if (ec) {
255 std::cerr << "Bad dbus request error: "
256 << ec;
257 res.code = 500;
258 res.end();
259 } else {
260 // find the matched property and send the
261 // response
262 res.json_value = {{"status", "ok"},
263 {"message", "200 OK"},
264 {"data", NULL}};
265 res.end();
266 return;
267 }
268 },
269 {object_names[0].first, object_path,
270 "org.freedesktop.DBus.Properties", "Set"},
271 interface, dest_property, property_value);
272 }
273 }
274 if (outstanding_async_calls == 0) {
275 // all search has been finished
276 if (property_matched == false) {
277 nlohmann::json j{{"status", "error"},
278 {"message", "403 Forbidden"},
279 {"data",
280 {{"message",
281 "The specified property "
282 "cannot be created: " +
283 dest_property}}}};
284 res.json_value = j;
285 res.end();
286 return;
287 } else
288 property_matched = false;
289 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700290 }
shiyilei00b92f72017-11-12 16:21:16 +0800291 },
292 {object_names[0].first, object_path,
293 "org.freedesktop.DBus.Properties", "GetAll"},
294 interface);
295 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700296 }
297 },
298 {"xyz.openbmc_project.ObjectMapper",
299 "/xyz/openbmc_project/object_mapper",
300 "xyz.openbmc_project.ObjectMapper", "GetObject"},
301 object_path, std::array<std::string, 0>());
302 });
shiyilei00b92f72017-11-12 16:21:16 +0800303
Ed Tanous911ac312017-08-15 09:37:42 -0700304 CROW_ROUTE(app, "/bus/system/<str>/")
305 .methods("GET"_method)([](const crow::request &req, crow::response &res,
306 const std::string &connection) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700307 if (outstanding_async_calls != 0) {
Ed Tanous911ac312017-08-15 09:37:42 -0700308 res.code = 500;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700309 res.body = "request in progress";
Ed Tanous911ac312017-08-15 09:37:42 -0700310 res.end();
Ed Tanousba9f9a62017-10-11 16:40:35 -0700311 return;
Ed Tanous911ac312017-08-15 09:37:42 -0700312 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700313 introspect_objects(res, connection, "/");
Ed Tanous911ac312017-08-15 09:37:42 -0700314 });
315
316 CROW_ROUTE(app, "/bus/system/<str>/<path>")
317 .methods("GET"_method)([](const crow::request &req, crow::response &res,
318 const std::string &process_name,
319 const std::string &requested_path) {
320
321 std::vector<std::string> strs;
322 boost::split(strs, requested_path, boost::is_any_of("/"));
323 std::string object_path;
324 std::string interface_name;
325 std::string method_name;
326 auto it = strs.begin();
327 if (it == strs.end()) {
328 object_path = "/";
329 }
330 while (it != strs.end()) {
331 // Check if segment contains ".". If it does, it must be an
332 // interface
333 if ((*it).find(".") != std::string::npos) {
334 break;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700335 // THis check is neccesary as the trailing slash gets parsed as
336 // part
Ed Tanous911ac312017-08-15 09:37:42 -0700337 // of our <path> specifier above, which causes the normal trailing
338 // backslash redirector to fail.
339 } else if (!it->empty()) {
340 object_path += "/" + *it;
341 }
342 it++;
343 }
344 if (it != strs.end()) {
345 interface_name = *it;
346 it++;
347
348 // after interface, we might have a method name
349 if (it != strs.end()) {
350 method_name = *it;
351 it++;
352 }
353 }
354 if (it != strs.end()) {
355 // if there is more levels past the method name, something went
356 // wrong, throw an error
357 res.code = 404;
358 res.end();
359 return;
360 }
361 dbus::endpoint introspect_endpoint(
362 process_name, object_path, "org.freedesktop.DBus.Introspectable",
363 "Introspect");
364 if (interface_name.empty()) {
365 crow::connections::system_bus->async_method_call(
366 [
367 &, process_name{std::move(process_name)},
368 object_path{std::move(object_path)}
369 ](const boost::system::error_code ec,
370 const std::string &introspect_xml) {
371 if (ec) {
372 std::cerr
373 << "Introspect call failed with error: " << ec.message()
374 << " on process: " << process_name
375 << " path: " << object_path << "\n";
376
377 } else {
378 tinyxml2::XMLDocument doc;
379
380 doc.Parse(introspect_xml.c_str());
381 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
382 if (pRoot == nullptr) {
383 std::cerr << "XML document failed to parse " << process_name
384 << " " << object_path << "\n";
385 res.write(nlohmann::json{{"status", "XML parse error"}});
386 res.code = 500;
387 } else {
388 nlohmann::json interfaces_array = nlohmann::json::array();
389 tinyxml2::XMLElement *interface =
390 pRoot->FirstChildElement("interface");
391
392 while (interface != nullptr) {
393 std::string iface_name = interface->Attribute("name");
394 interfaces_array.push_back({{"name", iface_name}});
395
396 interface = interface->NextSiblingElement("interface");
397 }
398 nlohmann::json j{{"status", "ok"},
399 {"bus_name", process_name},
400 {"interfaces", interfaces_array},
401 {"object_path", object_path}};
402 res.write(j.dump());
403 }
404 }
405 res.end();
406 },
407 introspect_endpoint);
408 } else {
409 crow::connections::system_bus->async_method_call(
410 [
411 &, process_name{std::move(process_name)},
412 interface_name{std::move(interface_name)},
413 object_path{std::move(object_path)}
414 ](const boost::system::error_code ec,
415 const std::string &introspect_xml) {
416 if (ec) {
417 std::cerr
418 << "Introspect call failed with error: " << ec.message()
419 << " on process: " << process_name
420 << " path: " << object_path << "\n";
421
422 } else {
423 tinyxml2::XMLDocument doc;
424
425 doc.Parse(introspect_xml.c_str());
426 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
427 if (pRoot == nullptr) {
428 std::cerr << "XML document failed to parse " << process_name
429 << " " << object_path << "\n";
430 res.code = 500;
431
432 } else {
433 tinyxml2::XMLElement *node =
434 pRoot->FirstChildElement("node");
435
436 // if we know we're the only call, build the json directly
437 nlohmann::json methods_array = nlohmann::json::array();
438 nlohmann::json signals_array = nlohmann::json::array();
439 tinyxml2::XMLElement *interface =
440 pRoot->FirstChildElement("interface");
441
442 while (interface != nullptr) {
443 std::string iface_name = interface->Attribute("name");
444
445 if (iface_name == interface_name) {
446 tinyxml2::XMLElement *methods =
447 interface->FirstChildElement("method");
448 while (methods != nullptr) {
449 nlohmann::json args_array = nlohmann::json::array();
450 tinyxml2::XMLElement *arg =
451 methods->FirstChildElement("arg");
452 while (arg != nullptr) {
453 args_array.push_back(
454 {{"name", arg->Attribute("name")},
455 {"type", arg->Attribute("type")},
456 {"direction", arg->Attribute("direction")}});
457 arg = arg->NextSiblingElement("arg");
458 }
459 methods_array.push_back(
460 {{"name", methods->Attribute("name")},
Ed Tanousba9f9a62017-10-11 16:40:35 -0700461 {"uri",
462 "/bus/system/" + process_name + object_path +
463 "/" + interface_name + "/" +
464 methods->Attribute("name")},
Ed Tanous911ac312017-08-15 09:37:42 -0700465 {"args", args_array}});
466 methods = methods->NextSiblingElement("method");
467 }
468 tinyxml2::XMLElement *signals =
469 interface->FirstChildElement("signal");
470 while (signals != nullptr) {
471 nlohmann::json args_array = nlohmann::json::array();
472
473 tinyxml2::XMLElement *arg =
474 signals->FirstChildElement("arg");
475 while (arg != nullptr) {
476 std::string name = arg->Attribute("name");
477 std::string type = arg->Attribute("type");
478 args_array.push_back({
479 {"name", name}, {"type", type},
480 });
481 arg = arg->NextSiblingElement("arg");
482 }
483 signals_array.push_back(
484 {{"name", signals->Attribute("name")},
485 {"args", args_array}});
486 signals = signals->NextSiblingElement("signal");
487 }
488
489 nlohmann::json j{
490 {"status", "ok"},
491 {"bus_name", process_name},
492 {"interface", interface_name},
493 {"methods", methods_array},
494 {"object_path", object_path},
495 {"properties", nlohmann::json::object()},
496 {"signals", signals_array}};
497
498 res.write(j.dump());
499 break;
500 }
501
502 interface = interface->NextSiblingElement("interface");
503 }
504 if (interface == nullptr) {
505 // if we got to the end of the list and never found a
506 // match, throw 404
507 res.code = 404;
508 }
509 }
510 }
511 res.end();
512 },
513 introspect_endpoint);
514 }
515
516 });
517}
518} // namespace openbmc_mapper
519} // namespace crow