blob: 52b751bbc28277459f0671980ba2effd2f07c580 [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#include <crow/app.h>
2
3#include <tinyxml2.h>
Ed Tanous911ac312017-08-15 09:37:42 -07004#include <dbus_singleton.hpp>
Ed Tanouse0d918b2018-03-27 17:41:04 -07005#include <boost/algorithm/string.hpp>
Ed Tanous64530012018-02-06 17:08:16 -08006#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -07007
8namespace crow {
9namespace openbmc_mapper {
Ed Tanousba9f9a62017-10-11 16:40:35 -070010
Ed Tanous911ac312017-08-15 09:37:42 -070011void introspect_objects(crow::response &res, std::string process_name,
Ed Tanous64530012018-02-06 17:08:16 -080012 std::string path,
13 std::shared_ptr<nlohmann::json> transaction) {
Ed Tanous911ac312017-08-15 09:37:42 -070014 crow::connections::system_bus->async_method_call(
Ed Tanousaa2e59c2018-04-12 12:17:20 -070015 [
16 &res, transaction, process_name{std::move(process_name)},
17 object_path{std::move(path)}
18 ](const boost::system::error_code ec, const std::string &introspect_xml) {
Ed Tanous911ac312017-08-15 09:37:42 -070019 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -080020 CROW_LOG_ERROR << "Introspect call failed with error: "
21 << ec.message() << " on process: " << process_name
22 << " path: " << object_path << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070023
24 } else {
Ed Tanous64530012018-02-06 17:08:16 -080025 transaction->push_back({{"path", object_path}});
Ed Tanous911ac312017-08-15 09:37:42 -070026
27 tinyxml2::XMLDocument doc;
28
29 doc.Parse(introspect_xml.c_str());
30 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
31 if (pRoot == nullptr) {
Ed Tanous64530012018-02-06 17:08:16 -080032 CROW_LOG_ERROR << "XML document failed to parse " << process_name
Ed Tanousaa2e59c2018-04-12 12:17:20 -070033 << " " << object_path << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070034
35 } else {
36 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
37 while (node != nullptr) {
38 std::string child_path = node->Attribute("name");
39 std::string newpath;
40 if (object_path != "/") {
41 newpath += object_path;
42 }
43 newpath += "/" + child_path;
Ed Tanous64530012018-02-06 17:08:16 -080044 // introspect the subobjects as well
45 introspect_objects(res, process_name, newpath, transaction);
Ed Tanous911ac312017-08-15 09:37:42 -070046
47 node = node->NextSiblingElement("node");
48 }
49 }
50 }
51 // if we're the last outstanding caller, finish the request
Ed Tanous64530012018-02-06 17:08:16 -080052 if (transaction.use_count() == 1) {
53 res.json_value = {{"status", "ok"},
54 {"bus_name", process_name},
55 {"objects", *transaction}};
Ed Tanous911ac312017-08-15 09:37:42 -070056 res.end();
57 }
58 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -070059 process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070060}
Ed Tanous64530012018-02-06 17:08:16 -080061
Ed Tanousaa2e59c2018-04-12 12:17:20 -070062// A smattering of common types to unpack. TODO(ed) this should really iterate
63// the sdbusplus object directly and build the json response
64using DbusRestVariantType = sdbusplus::message::variant<
65 std::vector<std::tuple<std::string, std::string, std::string>>, std::string,
66 int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t,
67 bool>;
68
69using ManagedObjectType = std::vector<std::pair<
70 sdbusplus::message::object_path,
71 boost::container::flat_map<
72 std::string,
73 boost::container::flat_map<std::string, DbusRestVariantType>>>>;
74
75void get_managed_objects_for_enumerate(
Ed Tanous64530012018-02-06 17:08:16 -080076 const std::string &object_name, const std::string &connection_name,
77 crow::response &res, std::shared_ptr<nlohmann::json> transaction) {
78 crow::connections::system_bus->async_method_call(
79 [&res, transaction](const boost::system::error_code ec,
80 const ManagedObjectType &objects) {
81 if (ec) {
82 CROW_LOG_ERROR << ec;
83 } else {
84 nlohmann::json &data_json = *transaction;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070085
Ed Tanous64530012018-02-06 17:08:16 -080086 for (auto &object_path : objects) {
Ed Tanousaa2e59c2018-04-12 12:17:20 -070087 CROW_LOG_DEBUG << "Reading object "
Ed Tanousa4e18f22018-04-27 10:25:29 -070088 << static_cast<const std::string &>(
89 object_path.first);
Ed Tanousaa2e59c2018-04-12 12:17:20 -070090 nlohmann::json &object_json =
Ed Tanousa4e18f22018-04-27 10:25:29 -070091 data_json[static_cast<const std::string &>(object_path.first)];
Ed Tanousaa2e59c2018-04-12 12:17:20 -070092 if (object_json.is_null()) {
93 object_json = nlohmann::json::object();
94 }
Ed Tanous64530012018-02-06 17:08:16 -080095 for (const auto &interface : object_path.second) {
96 for (const auto &property : interface.second) {
Ed Tanousaa2e59c2018-04-12 12:17:20 -070097 nlohmann::json &property_json = object_json[property.first];
98 mapbox::util::apply_visitor(
99 [&property_json](auto &&val) { property_json = val; },
Ed Tanous64530012018-02-06 17:08:16 -0800100 property.second);
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700101
102 // dbus-rest represents booleans as 1 or 0, implement to match
103 // TODO(ed) see if dbus-rest should be changed
104 const bool *property_bool =
105 property_json.get_ptr<const bool *>();
106 if (property_bool != nullptr) {
107 property_json = *property_bool ? 1 : 0;
108 }
Ed Tanous64530012018-02-06 17:08:16 -0800109 }
110 }
111 }
112 }
113
114 if (transaction.use_count() == 1) {
115 res.json_value = {{"message", "200 OK"},
116 {"status", "ok"},
117 {"data", std::move(*transaction)}};
118 res.end();
119 }
120 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700121 connection_name, object_name, "org.freedesktop.DBus.ObjectManager",
122 "GetManagedObjects");
123}
Ed Tanous64530012018-02-06 17:08:16 -0800124
125using GetSubTreeType = std::vector<
126 std::pair<std::string,
127 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
128
129void handle_enumerate(crow::response &res, const std::string &object_path) {
130 crow::connections::system_bus->async_method_call(
131 [&res, object_path{std::string(object_path)} ](
132 const boost::system::error_code ec,
133 const GetSubTreeType &object_names) {
134 if (ec) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700135 res.result(boost::beast::http::status::internal_server_error);
Ed Tanous64530012018-02-06 17:08:16 -0800136 res.end();
137 return;
138 }
139
140 boost::container::flat_set<std::string> connections;
141
142 for (const auto &object : object_names) {
143 for (const auto &connection : object.second) {
144 connections.insert(connection.first);
145 }
146 }
147
148 if (connections.size() <= 0) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700149 res.result(boost::beast::http::status::not_found);
Ed Tanous64530012018-02-06 17:08:16 -0800150 res.end();
151 return;
152 }
153 auto transaction =
154 std::make_shared<nlohmann::json>(nlohmann::json::object());
155 for (const std::string &connection : connections) {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700156 get_managed_objects_for_enumerate(object_path, connection, res,
157 transaction);
Ed Tanous64530012018-02-06 17:08:16 -0800158 }
159
160 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700161 "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper",
162 "xyz.openbmc_project.ObjectMapper", "GetSubTree", object_path, (int32_t)0,
163 std::array<std::string, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800164}
Ed Tanous911ac312017-08-15 09:37:42 -0700165
166template <typename... Middlewares>
167void request_routes(Crow<Middlewares...> &app) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700168 CROW_ROUTE(app, "/bus/")
169 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
170 res.json_value = {{"busses", {{{"name", "system"}}}}, {"status", "ok"}};
171 });
Ed Tanous911ac312017-08-15 09:37:42 -0700172
173 CROW_ROUTE(app, "/bus/system/")
174 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700175
176 auto myCallback = [&res](const boost::system::error_code ec,
177 std::vector<std::string> &names) {
178 if (ec) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700179 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700180 } else {
181 std::sort(names.begin(), names.end());
182 nlohmann::json j{{"status", "ok"}};
183 auto &objects_sub = j["objects"];
184 for (auto &name : names) {
185 objects_sub.push_back({{"name", name}});
186 }
187 res.json_value = std::move(j);
188 }
189 res.end();
190 };
Ed Tanous911ac312017-08-15 09:37:42 -0700191 crow::connections::system_bus->async_method_call(
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700192 std::move(myCallback), "org.freedesktop.DBus", "/",
193 "org.freedesktop.DBus", "ListNames");
Ed Tanous911ac312017-08-15 09:37:42 -0700194 });
195
Ed Tanousba9f9a62017-10-11 16:40:35 -0700196 CROW_ROUTE(app, "/list/")
197 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
198 crow::connections::system_bus->async_method_call(
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700199 [&res](const boost::system::error_code ec,
200 const std::vector<std::string> &object_paths) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700201 if (ec) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700202 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousba9f9a62017-10-11 16:40:35 -0700203 } else {
Ed Tanous64530012018-02-06 17:08:16 -0800204 res.json_value = {{"status", "ok"},
205 {"message", "200 OK"},
206 {"data", std::move(object_paths)}};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700207 }
208 res.end();
209 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700210 "xyz.openbmc_project.ObjectMapper",
211 "/xyz/openbmc_project/object_mapper",
212 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "",
213 static_cast<int32_t>(99), std::array<std::string, 0>());
Ed Tanousba9f9a62017-10-11 16:40:35 -0700214 });
215
Ed Tanous64530012018-02-06 17:08:16 -0800216 CROW_ROUTE(app, "/xyz/<path>")
shiyilei00b92f72017-11-12 16:21:16 +0800217 .methods("GET"_method,
218 "PUT"_method)([](const crow::request &req, crow::response &res,
Ed Tanousba9f9a62017-10-11 16:40:35 -0700219 const std::string &path) {
Ed Tanous64530012018-02-06 17:08:16 -0800220 std::shared_ptr<nlohmann::json> transaction =
221 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanousba9f9a62017-10-11 16:40:35 -0700222 using GetObjectType =
223 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanousa4e18f22018-04-27 10:25:29 -0700224 std::string object_path = "/xyz/" + path;
225
shiyilei00b92f72017-11-12 16:21:16 +0800226 std::string dest_property;
227 std::string property_set_value;
Ed Tanousa4e18f22018-04-27 10:25:29 -0700228 constexpr char *attr_seperator = "/attr/";
229 size_t attr_position = path.find(attr_seperator);
230 if (attr_position != path.npos) {
shiyilei00b92f72017-11-12 16:21:16 +0800231 object_path = "/xyz/" + path.substr(0, attr_position);
Ed Tanousa4e18f22018-04-27 10:25:29 -0700232 dest_property = path.substr((attr_position + strlen(attr_seperator)),
233 path.length());
234 // If this is attempting to write a property, parse the json in the
235 // request
236 if (req.method() == "PUT"_method) {
237 auto request_dbus_data =
238 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousba9f9a62017-10-11 16:40:35 -0700239
Ed Tanousa4e18f22018-04-27 10:25:29 -0700240 if (request_dbus_data.is_discarded()) {
241 res.result(boost::beast::http::status::bad_request);
242 res.end();
243 return;
244 }
shiyilei00b92f72017-11-12 16:21:16 +0800245
Ed Tanousa4e18f22018-04-27 10:25:29 -0700246 auto property_value_it = request_dbus_data.find("data");
247 if (property_value_it == request_dbus_data.end()) {
248 res.result(boost::beast::http::status::bad_request);
249 res.end();
250 return;
251 }
252
253 const std::string *ptr =
254 property_value_it->get_ptr<const std::string *>();
255 if (ptr == nullptr) {
256 res.result(boost::beast::http::status::bad_request);
257 res.end();
258 return;
259 }
260 property_set_value = *ptr;
shiyilei00b92f72017-11-12 16:21:16 +0800261 }
262 }
263
Ed Tanous64530012018-02-06 17:08:16 -0800264 if (boost::ends_with(object_path, "/enumerate")) {
265 object_path.erase(object_path.end() - 10, object_path.end());
266 handle_enumerate(res, object_path);
267 return;
268 }
269
Ed Tanousba9f9a62017-10-11 16:40:35 -0700270 crow::connections::system_bus->async_method_call(
shiyilei00b92f72017-11-12 16:21:16 +0800271 [
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700272 &res, &req, object_path{std::move(object_path)},
273 dest_property{std::move(dest_property)},
274 property_set_value{std::move(property_set_value)}, transaction
shiyilei00b92f72017-11-12 16:21:16 +0800275 ](const boost::system::error_code ec,
276 const GetObjectType &object_names) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700277 if (ec) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700278 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousba9f9a62017-10-11 16:40:35 -0700279 res.end();
280 return;
281 }
282 if (object_names.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700283 res.result(boost::beast::http::status::not_found);
Ed Tanousba9f9a62017-10-11 16:40:35 -0700284 res.end();
285 return;
286 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700287 if (req.method() == "GET"_method) {
shiyilei00b92f72017-11-12 16:21:16 +0800288 for (auto &interface : object_names[0].second) {
shiyilei00b92f72017-11-12 16:21:16 +0800289 crow::connections::system_bus->async_method_call(
Ed Tanousa4e18f22018-04-27 10:25:29 -0700290 [
291 &res, transaction,
292 dest_property{std::move(dest_property)}
293 ](const boost::system::error_code ec,
294 const std::vector<std::pair<
295 std::string, DbusRestVariantType>> &properties) {
shiyilei00b92f72017-11-12 16:21:16 +0800296 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800297 CROW_LOG_ERROR << "Bad dbus request error: " << ec;
shiyilei00b92f72017-11-12 16:21:16 +0800298 } else {
299 for (auto &property : properties) {
Ed Tanousa4e18f22018-04-27 10:25:29 -0700300 nlohmann::json &it = (*transaction)["data"];
301 // if we don't have a specific property, assume we
302 // are getting all properties
303 if (dest_property.empty()) {
304 nlohmann::json &it = it[property.first];
305 } else {
306 // if the property doesn't match our single
307 // property, nothing to do here
308 if (dest_property != property.first) {
309 continue;
310 }
311 }
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700312 mapbox::util::apply_visitor(
313 [&it](auto &&val) { it = val; },
shiyilei00b92f72017-11-12 16:21:16 +0800314 property.second);
315 }
316 }
Ed Tanous64530012018-02-06 17:08:16 -0800317 if (transaction.use_count() == 1) {
318 res.json_value = {{"status", "ok"},
319 {"message", "200 OK"},
320 {"data", *transaction}};
321
shiyilei00b92f72017-11-12 16:21:16 +0800322 res.end();
shiyilei00b92f72017-11-12 16:21:16 +0800323 }
324 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700325 object_names[0].first, object_path,
326 "org.freedesktop.DBus.Properties", "GetAll", interface);
shiyilei00b92f72017-11-12 16:21:16 +0800327 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700328 } else if (req.method() == "PUT"_method) {
shiyilei00b92f72017-11-12 16:21:16 +0800329 for (auto &interface : object_names[0].second) {
shiyilei00b92f72017-11-12 16:21:16 +0800330 crow::connections::system_bus->async_method_call(
331 [
332 &, interface{std::move(interface)},
333 object_names{std::move(object_names)},
334 object_path{std::move(object_path)},
335 dest_property{std::move(dest_property)},
Ed Tanous64530012018-02-06 17:08:16 -0800336 property_set_value{std::move(property_set_value)},
337 transaction
shiyilei00b92f72017-11-12 16:21:16 +0800338 ](const boost::system::error_code ec,
Ed Tanous64530012018-02-06 17:08:16 -0800339 const boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700340 std::string, DbusRestVariantType> &properties) {
shiyilei00b92f72017-11-12 16:21:16 +0800341 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800342 CROW_LOG_ERROR << "Bad dbus request error: " << ec;
shiyilei00b92f72017-11-12 16:21:16 +0800343 } else {
Ed Tanous64530012018-02-06 17:08:16 -0800344 auto it = properties.find(dest_property);
345 if (it != properties.end()) {
346 // find the matched property in the interface
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700347 DbusRestVariantType property_value(
348 property_set_value);
349 // create the dbus variant for dbus call
Ed Tanous64530012018-02-06 17:08:16 -0800350 crow::connections::system_bus->async_method_call(
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700351 [transaction](
352 const boost::system::error_code ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800353 // use the method "Set" to set the property
354 // value
355 if (ec) {
356 CROW_LOG_ERROR << "Bad dbus request error: "
357 << ec;
358 }
359 // find the matched property and send the
360 // response
361 *transaction = {{"status", "ok"},
362 {"message", "200 OK"},
363 {"data", nullptr}};
364
365 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700366 object_names[0].first, object_path,
367 "org.freedesktop.DBus.Properties", "Set",
Ed Tanous64530012018-02-06 17:08:16 -0800368 interface, dest_property, property_value);
shiyilei00b92f72017-11-12 16:21:16 +0800369 }
Ed Tanous64530012018-02-06 17:08:16 -0800370 }
371 // if we are the last caller, finish the transaction
372 if (transaction.use_count() == 1) {
373 // if nobody filled in the property, all calls either
374 // errored, or failed
375 if (transaction == nullptr) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700376 res.result(boost::beast::http::status::forbidden);
Ed Tanous64530012018-02-06 17:08:16 -0800377 res.json_value = {{"status", "error"},
378 {"message", "403 Forbidden"},
379 {"data",
380 {{"message",
381 "The specified property "
382 "cannot be created: " +
383 dest_property}}}};
384
385 } else {
386 res.json_value = *transaction;
shiyilei00b92f72017-11-12 16:21:16 +0800387 }
Ed Tanous64530012018-02-06 17:08:16 -0800388
389 res.end();
390 return;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700391 }
shiyilei00b92f72017-11-12 16:21:16 +0800392 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700393 object_names[0].first, object_path,
394 "org.freedesktop.DBus.Properties", "GetAll", interface);
shiyilei00b92f72017-11-12 16:21:16 +0800395 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700396 }
397 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700398 "xyz.openbmc_project.ObjectMapper",
399 "/xyz/openbmc_project/object_mapper",
400 "xyz.openbmc_project.ObjectMapper", "GetObject", object_path,
401 std::array<std::string, 0>());
Ed Tanousba9f9a62017-10-11 16:40:35 -0700402 });
shiyilei00b92f72017-11-12 16:21:16 +0800403
Ed Tanous911ac312017-08-15 09:37:42 -0700404 CROW_ROUTE(app, "/bus/system/<str>/")
405 .methods("GET"_method)([](const crow::request &req, crow::response &res,
406 const std::string &connection) {
Ed Tanous64530012018-02-06 17:08:16 -0800407 std::shared_ptr<nlohmann::json> transaction;
408 introspect_objects(res, connection, "/", transaction);
Ed Tanous911ac312017-08-15 09:37:42 -0700409 });
410
411 CROW_ROUTE(app, "/bus/system/<str>/<path>")
412 .methods("GET"_method)([](const crow::request &req, crow::response &res,
413 const std::string &process_name,
414 const std::string &requested_path) {
415
416 std::vector<std::string> strs;
417 boost::split(strs, requested_path, boost::is_any_of("/"));
418 std::string object_path;
419 std::string interface_name;
420 std::string method_name;
421 auto it = strs.begin();
422 if (it == strs.end()) {
423 object_path = "/";
424 }
425 while (it != strs.end()) {
426 // Check if segment contains ".". If it does, it must be an
427 // interface
428 if ((*it).find(".") != std::string::npos) {
429 break;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700430 // THis check is neccesary as the trailing slash gets parsed as
Ed Tanous64530012018-02-06 17:08:16 -0800431 // part of our <path> specifier above, which causes the normal
432 // trailing backslash redirector to fail.
Ed Tanous911ac312017-08-15 09:37:42 -0700433 } else if (!it->empty()) {
434 object_path += "/" + *it;
435 }
436 it++;
437 }
438 if (it != strs.end()) {
439 interface_name = *it;
440 it++;
441
442 // after interface, we might have a method name
443 if (it != strs.end()) {
444 method_name = *it;
445 it++;
446 }
447 }
448 if (it != strs.end()) {
449 // if there is more levels past the method name, something went
450 // wrong, throw an error
Ed Tanouse0d918b2018-03-27 17:41:04 -0700451 res.result(boost::beast::http::status::not_found);
Ed Tanous911ac312017-08-15 09:37:42 -0700452 res.end();
453 return;
454 }
Ed Tanous911ac312017-08-15 09:37:42 -0700455 if (interface_name.empty()) {
456 crow::connections::system_bus->async_method_call(
457 [
458 &, process_name{std::move(process_name)},
459 object_path{std::move(object_path)}
460 ](const boost::system::error_code ec,
461 const std::string &introspect_xml) {
462 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800463 CROW_LOG_ERROR
Ed Tanous911ac312017-08-15 09:37:42 -0700464 << "Introspect call failed with error: " << ec.message()
465 << " on process: " << process_name
466 << " path: " << object_path << "\n";
467
468 } else {
469 tinyxml2::XMLDocument doc;
470
471 doc.Parse(introspect_xml.c_str());
472 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
473 if (pRoot == nullptr) {
Ed Tanous64530012018-02-06 17:08:16 -0800474 CROW_LOG_ERROR << "XML document failed to parse "
475 << process_name << " " << object_path
476 << "\n";
Ed Tanouse0d918b2018-03-27 17:41:04 -0700477 res.json_value = {{"status", "XML parse error"}};
478 res.result(
479 boost::beast::http::status::internal_server_error);
Ed Tanous911ac312017-08-15 09:37:42 -0700480 } else {
481 nlohmann::json interfaces_array = nlohmann::json::array();
482 tinyxml2::XMLElement *interface =
483 pRoot->FirstChildElement("interface");
484
485 while (interface != nullptr) {
486 std::string iface_name = interface->Attribute("name");
487 interfaces_array.push_back({{"name", iface_name}});
488
489 interface = interface->NextSiblingElement("interface");
490 }
Ed Tanous64530012018-02-06 17:08:16 -0800491 res.json_value = {{"status", "ok"},
492 {"bus_name", process_name},
493 {"interfaces", interfaces_array},
494 {"object_path", object_path}};
Ed Tanous911ac312017-08-15 09:37:42 -0700495 }
496 }
497 res.end();
498 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700499 process_name, object_path, "org.freedesktop.DBus.Introspectable",
500 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700501 } else {
502 crow::connections::system_bus->async_method_call(
503 [
504 &, process_name{std::move(process_name)},
505 interface_name{std::move(interface_name)},
506 object_path{std::move(object_path)}
507 ](const boost::system::error_code ec,
508 const std::string &introspect_xml) {
509 if (ec) {
Ed Tanous64530012018-02-06 17:08:16 -0800510 CROW_LOG_ERROR
Ed Tanous911ac312017-08-15 09:37:42 -0700511 << "Introspect call failed with error: " << ec.message()
512 << " on process: " << process_name
513 << " path: " << object_path << "\n";
514
515 } else {
516 tinyxml2::XMLDocument doc;
517
518 doc.Parse(introspect_xml.c_str());
519 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
520 if (pRoot == nullptr) {
Ed Tanous64530012018-02-06 17:08:16 -0800521 CROW_LOG_ERROR << "XML document failed to parse "
522 << process_name << " " << object_path
523 << "\n";
Ed Tanouse0d918b2018-03-27 17:41:04 -0700524 res.result(
525 boost::beast::http::status::internal_server_error);
Ed Tanous911ac312017-08-15 09:37:42 -0700526
527 } else {
528 tinyxml2::XMLElement *node =
529 pRoot->FirstChildElement("node");
530
531 // if we know we're the only call, build the json directly
532 nlohmann::json methods_array = nlohmann::json::array();
533 nlohmann::json signals_array = nlohmann::json::array();
534 tinyxml2::XMLElement *interface =
535 pRoot->FirstChildElement("interface");
536
537 while (interface != nullptr) {
538 std::string iface_name = interface->Attribute("name");
539
540 if (iface_name == interface_name) {
541 tinyxml2::XMLElement *methods =
542 interface->FirstChildElement("method");
543 while (methods != nullptr) {
544 nlohmann::json args_array = nlohmann::json::array();
545 tinyxml2::XMLElement *arg =
546 methods->FirstChildElement("arg");
547 while (arg != nullptr) {
548 args_array.push_back(
549 {{"name", arg->Attribute("name")},
550 {"type", arg->Attribute("type")},
551 {"direction", arg->Attribute("direction")}});
552 arg = arg->NextSiblingElement("arg");
553 }
554 methods_array.push_back(
555 {{"name", methods->Attribute("name")},
Ed Tanous64530012018-02-06 17:08:16 -0800556 {"uri", "/bus/system/" + process_name +
557 object_path + "/" + interface_name +
558 "/" + methods->Attribute("name")},
Ed Tanous911ac312017-08-15 09:37:42 -0700559 {"args", args_array}});
560 methods = methods->NextSiblingElement("method");
561 }
562 tinyxml2::XMLElement *signals =
563 interface->FirstChildElement("signal");
564 while (signals != nullptr) {
565 nlohmann::json args_array = nlohmann::json::array();
566
567 tinyxml2::XMLElement *arg =
568 signals->FirstChildElement("arg");
569 while (arg != nullptr) {
570 std::string name = arg->Attribute("name");
571 std::string type = arg->Attribute("type");
572 args_array.push_back({
Ed Tanous64530012018-02-06 17:08:16 -0800573 {"name", name},
574 {"type", type},
Ed Tanous911ac312017-08-15 09:37:42 -0700575 });
576 arg = arg->NextSiblingElement("arg");
577 }
578 signals_array.push_back(
579 {{"name", signals->Attribute("name")},
580 {"args", args_array}});
581 signals = signals->NextSiblingElement("signal");
582 }
583
584 nlohmann::json j{
585 {"status", "ok"},
586 {"bus_name", process_name},
587 {"interface", interface_name},
588 {"methods", methods_array},
589 {"object_path", object_path},
590 {"properties", nlohmann::json::object()},
591 {"signals", signals_array}};
592
593 res.write(j.dump());
594 break;
595 }
596
597 interface = interface->NextSiblingElement("interface");
598 }
599 if (interface == nullptr) {
600 // if we got to the end of the list and never found a
601 // match, throw 404
Ed Tanouse0d918b2018-03-27 17:41:04 -0700602 res.result(boost::beast::http::status::not_found);
Ed Tanous911ac312017-08-15 09:37:42 -0700603 }
604 }
605 }
606 res.end();
607 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700608 process_name, object_path, "org.freedesktop.DBus.Introspectable",
609 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700610 }
611
612 });
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700613} // namespace openbmc_mapper
Ed Tanous911ac312017-08-15 09:37:42 -0700614} // namespace openbmc_mapper
615} // namespace crow