blob: 93488087ca1b0dbc403448a512a96ec3bc7aaf48 [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 {
13std::atomic<std::size_t> outstanding_async_calls(0);
14nlohmann::json object_paths;
15
16void introspect_objects(crow::response &res, std::string process_name,
17 std::string path) {
18 dbus::endpoint introspect_endpoint(
19 process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
20 outstanding_async_calls++;
21 crow::connections::system_bus->async_method_call(
22 [&, process_name{std::move(process_name)}, object_path{std::move(path)} ](
23 const boost::system::error_code ec,
24 const std::string &introspect_xml) {
25 outstanding_async_calls--;
26 if (ec) {
27 std::cerr << "Introspect call failed with error: " << ec.message()
28 << " on process: " << process_name
29 << " path: " << object_path << "\n";
30
31 } else {
32 object_paths.push_back({{"path", object_path}});
33
34 tinyxml2::XMLDocument doc;
35
36 doc.Parse(introspect_xml.c_str());
37 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
38 if (pRoot == nullptr) {
39 std::cerr << "XML document failed to parse " << process_name << " "
40 << path << "\n";
41
42 } else {
43 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
44 while (node != nullptr) {
45 std::string child_path = node->Attribute("name");
46 std::string newpath;
47 if (object_path != "/") {
48 newpath += object_path;
49 }
50 newpath += "/" + child_path;
51 // intropect the subobjects as well
52 introspect_objects(res, process_name, newpath);
53
54 node = node->NextSiblingElement("node");
55 }
56 }
57 }
58 // if we're the last outstanding caller, finish the request
59 if (outstanding_async_calls == 0) {
60 nlohmann::json j{{"status", "ok"},
61 {"bus_name", process_name},
62 {"objects", object_paths}};
63
64 res.write(j.dump());
65 object_paths.clear();
66 res.end();
67 }
68 },
69 introspect_endpoint);
70}
71
72template <typename... Middlewares>
73void request_routes(Crow<Middlewares...> &app) {
74 CROW_ROUTE(app, "/bus/").methods("GET"_method)([](const crow::request &req) {
75 return nlohmann::json{{"busses", {{{"name", "system"}}}}, {"status", "ok"}};
76
77 });
78
79 CROW_ROUTE(app, "/bus/system/")
80 .methods("GET"_method)([](const crow::request &req, crow::response &res) {
81 crow::connections::system_bus->async_method_call(
82 [&](const boost::system::error_code ec,
83 std::vector<std::string> &names) {
84 std::sort(names.begin(), names.end());
85 if (ec) {
86 res.code = 500;
87 } else {
88 nlohmann::json j{{"status", "ok"}};
89 auto &objects_sub = j["objects"];
90 for (auto &name : names) {
91 objects_sub.push_back({{"name", name}});
92 }
93
94 res.write(j.dump());
95 }
96
97 res.end();
98
99 },
100 {"org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"});
101
102 });
103
104 CROW_ROUTE(app, "/bus/system/<str>/")
105 .methods("GET"_method)([](const crow::request &req, crow::response &res,
106 const std::string &connection) {
107 // Can only do one call at a time (for now)
108 if (outstanding_async_calls == 0) {
109 // TODO(ed) sanitize paths
110 introspect_objects(res, connection, "/");
111 } else {
112 nlohmann::json j{{"status", "failed"}};
113 res.code = 500;
114 res.write(j.dump());
115 res.end();
116 }
117 });
118
119 CROW_ROUTE(app, "/bus/system/<str>/<path>")
120 .methods("GET"_method)([](const crow::request &req, crow::response &res,
121 const std::string &process_name,
122 const std::string &requested_path) {
123
124 std::vector<std::string> strs;
125 boost::split(strs, requested_path, boost::is_any_of("/"));
126 std::string object_path;
127 std::string interface_name;
128 std::string method_name;
129 auto it = strs.begin();
130 if (it == strs.end()) {
131 object_path = "/";
132 }
133 while (it != strs.end()) {
134 // Check if segment contains ".". If it does, it must be an
135 // interface
136 if ((*it).find(".") != std::string::npos) {
137 break;
138 // THis check is neccesary as the trailing slash gets parsed as part
139 // of our <path> specifier above, which causes the normal trailing
140 // backslash redirector to fail.
141 } else if (!it->empty()) {
142 object_path += "/" + *it;
143 }
144 it++;
145 }
146 if (it != strs.end()) {
147 interface_name = *it;
148 it++;
149
150 // after interface, we might have a method name
151 if (it != strs.end()) {
152 method_name = *it;
153 it++;
154 }
155 }
156 if (it != strs.end()) {
157 // if there is more levels past the method name, something went
158 // wrong, throw an error
159 res.code = 404;
160 res.end();
161 return;
162 }
163 dbus::endpoint introspect_endpoint(
164 process_name, object_path, "org.freedesktop.DBus.Introspectable",
165 "Introspect");
166 if (interface_name.empty()) {
167 crow::connections::system_bus->async_method_call(
168 [
169 &, process_name{std::move(process_name)},
170 object_path{std::move(object_path)}
171 ](const boost::system::error_code ec,
172 const std::string &introspect_xml) {
173 if (ec) {
174 std::cerr
175 << "Introspect call failed with error: " << ec.message()
176 << " on process: " << process_name
177 << " path: " << object_path << "\n";
178
179 } else {
180 tinyxml2::XMLDocument doc;
181
182 doc.Parse(introspect_xml.c_str());
183 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
184 if (pRoot == nullptr) {
185 std::cerr << "XML document failed to parse " << process_name
186 << " " << object_path << "\n";
187 res.write(nlohmann::json{{"status", "XML parse error"}});
188 res.code = 500;
189 } else {
190 nlohmann::json interfaces_array = nlohmann::json::array();
191 tinyxml2::XMLElement *interface =
192 pRoot->FirstChildElement("interface");
193
194 while (interface != nullptr) {
195 std::string iface_name = interface->Attribute("name");
196 interfaces_array.push_back({{"name", iface_name}});
197
198 interface = interface->NextSiblingElement("interface");
199 }
200 nlohmann::json j{{"status", "ok"},
201 {"bus_name", process_name},
202 {"interfaces", interfaces_array},
203 {"object_path", object_path}};
204 res.write(j.dump());
205 }
206 }
207 res.end();
208 },
209 introspect_endpoint);
210 } else {
211 crow::connections::system_bus->async_method_call(
212 [
213 &, process_name{std::move(process_name)},
214 interface_name{std::move(interface_name)},
215 object_path{std::move(object_path)}
216 ](const boost::system::error_code ec,
217 const std::string &introspect_xml) {
218 if (ec) {
219 std::cerr
220 << "Introspect call failed with error: " << ec.message()
221 << " on process: " << process_name
222 << " path: " << object_path << "\n";
223
224 } else {
225 tinyxml2::XMLDocument doc;
226
227 doc.Parse(introspect_xml.c_str());
228 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
229 if (pRoot == nullptr) {
230 std::cerr << "XML document failed to parse " << process_name
231 << " " << object_path << "\n";
232 res.code = 500;
233
234 } else {
235 tinyxml2::XMLElement *node =
236 pRoot->FirstChildElement("node");
237
238 // if we know we're the only call, build the json directly
239 nlohmann::json methods_array = nlohmann::json::array();
240 nlohmann::json signals_array = nlohmann::json::array();
241 tinyxml2::XMLElement *interface =
242 pRoot->FirstChildElement("interface");
243
244 while (interface != nullptr) {
245 std::string iface_name = interface->Attribute("name");
246
247 if (iface_name == interface_name) {
248 tinyxml2::XMLElement *methods =
249 interface->FirstChildElement("method");
250 while (methods != nullptr) {
251 nlohmann::json args_array = nlohmann::json::array();
252 tinyxml2::XMLElement *arg =
253 methods->FirstChildElement("arg");
254 while (arg != nullptr) {
255 args_array.push_back(
256 {{"name", arg->Attribute("name")},
257 {"type", arg->Attribute("type")},
258 {"direction", arg->Attribute("direction")}});
259 arg = arg->NextSiblingElement("arg");
260 }
261 methods_array.push_back(
262 {{"name", methods->Attribute("name")},
263 {"uri", "/bus/system/" + process_name +
264 object_path + "/" + interface_name +
265 "/" + methods->Attribute("name")},
266 {"args", args_array}});
267 methods = methods->NextSiblingElement("method");
268 }
269 tinyxml2::XMLElement *signals =
270 interface->FirstChildElement("signal");
271 while (signals != nullptr) {
272 nlohmann::json args_array = nlohmann::json::array();
273
274 tinyxml2::XMLElement *arg =
275 signals->FirstChildElement("arg");
276 while (arg != nullptr) {
277 std::string name = arg->Attribute("name");
278 std::string type = arg->Attribute("type");
279 args_array.push_back({
280 {"name", name}, {"type", type},
281 });
282 arg = arg->NextSiblingElement("arg");
283 }
284 signals_array.push_back(
285 {{"name", signals->Attribute("name")},
286 {"args", args_array}});
287 signals = signals->NextSiblingElement("signal");
288 }
289
290 nlohmann::json j{
291 {"status", "ok"},
292 {"bus_name", process_name},
293 {"interface", interface_name},
294 {"methods", methods_array},
295 {"object_path", object_path},
296 {"properties", nlohmann::json::object()},
297 {"signals", signals_array}};
298
299 res.write(j.dump());
300 break;
301 }
302
303 interface = interface->NextSiblingElement("interface");
304 }
305 if (interface == nullptr) {
306 // if we got to the end of the list and never found a
307 // match, throw 404
308 res.code = 404;
309 }
310 }
311 }
312 res.end();
313 },
314 introspect_endpoint);
315 }
316
317 });
318}
319} // namespace openbmc_mapper
320} // namespace crow